From 4c023d5215fb8451a8ada82d11ebe565a7ac4c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 25 Feb 2018 19:20:24 +0100 Subject: [PATCH 001/284] Enable CI backed by GitLab CI and CircleCI --- .ci/README.md | 9 ++ .ci/build-docker.sh | 46 ++++++++ .ci/protect-secrets.sh | 40 +++++++ .ci/pull-gitlab.sh | 33 ++++++ .ci/push-dockerhub.sh | 30 +++++ .ci/push-gitlab.sh | 27 +++++ .ci/setup-make-parallelity.sh | 26 ++++ .ci/test-cli.sh | 21 ++++ .ci/test-dev.sh | 35 ++++++ .ci/test-jupyter.sh | 23 ++++ .circleci/config.yml | 41 +++++++ .dockerignore | 1 + .gitignore | 6 + .gitlab-ci.yml | 99 ++++++++++++++++ Makefile | 26 ++++ configure.ac | 6 +- docker/.gitignore | 2 + docker/Dockerfile | 176 ++++++++++++++++++++++++++++ docker/README.md | 51 ++++++++ docker/entrypoint-dev.sh | 4 + docker/entrypoint.sh | 2 + docker/hooks/build | 1 + docker/hooks/push | 1 + src/sage_setup/docbuild/__init__.py | 7 +- 24 files changed, 709 insertions(+), 4 deletions(-) create mode 100644 .ci/README.md create mode 100755 .ci/build-docker.sh create mode 100755 .ci/protect-secrets.sh create mode 100755 .ci/pull-gitlab.sh create mode 100755 .ci/push-dockerhub.sh create mode 100755 .ci/push-gitlab.sh create mode 100644 .ci/setup-make-parallelity.sh create mode 100755 .ci/test-cli.sh create mode 100755 .ci/test-dev.sh create mode 100755 .ci/test-jupyter.sh create mode 100644 .circleci/config.yml create mode 120000 .dockerignore create mode 100644 .gitlab-ci.yml create mode 100644 docker/.gitignore create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100755 docker/entrypoint-dev.sh create mode 100755 docker/entrypoint.sh create mode 100644 docker/hooks/build create mode 100644 docker/hooks/push diff --git a/.ci/README.md b/.ci/README.md new file mode 100644 index 00000000000..e2b165cc518 --- /dev/null +++ b/.ci/README.md @@ -0,0 +1,9 @@ +# Continuous Integration (CI) + +We support several implementations of CI. All these implementations rely on +[docker](https://docker.com) in some way. This directory contains bits which +are shared between these CI implementations. The relevant docker files can be +found in `/docker/`. + +* [CircleCI](https://circleci.com) is configured in `/.circleci/`. +* [GitLab CI](https://gitlab.com) is configured in `/.gitlab-ci.yml`. diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh new file mode 100755 index 00000000000..11651e5dd38 --- /dev/null +++ b/.ci/build-docker.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# This script gets called from CI to build several flavours of docker images +# which contain Sage. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** +set -ex + +[[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none +[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest + +. .ci/setup-make-parallelity.sh + +# We speed up the build process by copying built artifacts from ARTIFACT_BASE +# during docker build. See /docker/Dockerfile for more details. +ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:latest} + +# Seed our cache with $ARTIFACT_BASE if it exists +docker pull $ARTIFACT_BASE || true + +function docker_build { + time docker build -f docker/Dockerfile --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +} + +# We use a multi-stage build /docker/Dockerfile. For the caching to be +# effective, we populate the cache by building the make-all target. (Just +# building the last target is not enough as intermediate targets would be +# discarded from the cache and therefore the caching would fail for our actual +# builds below.) +docker_build --pull --tag make-all --target make-all . + +# Build the release image without build artifacts. +DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG +docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . +# Build the developer image with the build artifacts intact. +# Note: It's important to build the dev image last because it might be tagged as ARTIFACT_BASE. +DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG +docker_build --target sagemath-dev --tag "$DOCKER_IMAGE_DEV" . diff --git a/.ci/protect-secrets.sh b/.ci/protect-secrets.sh new file mode 100755 index 00000000000..a3ae9239988 --- /dev/null +++ b/.ci/protect-secrets.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# This script protects all environment variables that start with "SECRET_". +# It puts them in a temporary file. The name of the variable contains the path +# of that file. This filename can then safely be used in `cat` even if `set +# -x` has been turned on. Also you can run "export" to understand the +# environment without danger. +# Be careful, however, not to use this like the following: +# docker login $DOCKER_USER $(cat $SECRET_DOCKER_PASS) +# as this would expose the password if `set -x` has been turned on. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -eo pipefail +set +x + +function encrypt { + RET=`mktemp` + eval " echo \$$1" > "$RET" + echo $RET +} + +for name in `awk 'END { for (name in ENVIRON) { print name; } }' < /dev/null`; do +case "$name" in + SECRET_*) + export $name="$(encrypt $name)" + echo "Protected $name" + ;; +esac +done + +unset encrypt diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh new file mode 100755 index 00000000000..01bb10cdd08 --- /dev/null +++ b/.ci/pull-gitlab.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# This script gets called from CI to pull the Sage docker images that were +# built during the "build" phase to pull all the connected docker daemon +# (likely a docker-in-docker.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. +# The variable $DOCKER_IMAGE is set to the full name of the pulled image; +# source this script to use it. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +[[ -z "$DOCKER_TAG" ]] && (echo "Can not pull untagged build."; exit 0) +[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest + +# Pull the built images from the gitlab registry and give them the original +# names they had after built. +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker pull $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" +docker tag $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG $DOCKER_IMAGE diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh new file mode 100755 index 00000000000..ca2c4906eec --- /dev/null +++ b/.ci/push-dockerhub.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This script gets called from CI to push our docker images to +# $DOCKER_USER/sagemath* on the Docker Hub. +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +[[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) +[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest + +# Push the built images to the docker hub (and fail silently if +# DOCKER_USER/SECRET_DOCKER_PASS have not been configured.) +if [[ -z "$DOCKER_USER" || -z "$SECRET_DOCKER_PASS" ]]; then + echo "DOCKER_USER/SECRET_DOCKER_PASS variables have not been configured in your Continuous Integration setup. Not pushing built images to Docker Hub." +else + cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin + docker push ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG +fi diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh new file mode 100755 index 00000000000..9d98dca7e49 --- /dev/null +++ b/.ci/push-gitlab.sh @@ -0,0 +1,27 @@ +#!/bin/bash +set -ex + +# This script gets called from CI to push our docker images to registry +# configured in GitLab. (Mostly, so we can pull them again to push them to the +# Docker Hub.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +[[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) +[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest + +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker tag ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +docker push $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh new file mode 100644 index 00000000000..12f3a252179 --- /dev/null +++ b/.ci/setup-make-parallelity.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** +set -ex + +# Determine the number of threads that can run simultaneously on this system +# (we might not have nproc available.) +# Note that this value is incorrect for some CI providers (notably CircleCI: +# https://circleci.com/docs/2.0/configuration-reference/#resource_class) which +# provision fewer vCPUs than shown in /proc/cpuinfo. Also, setting this value +# too high can lead to RAM being insufficient, so it's best to set this +# variable manually in your CI configuration. +[[ -z "$NTHREADS" ]] && NTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` || true +# Set -j and -l for make (though -l is probably stripped by Sage) +[[ -z "$MAKEOPTS" ]] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true +# Not all parts of Sage seem to honor MAKEOPTS, so the current way of telling +# the system which concurrency to use, seems to be setting $MAKE. +[[ -z "$MAKE" ]] && MAKE="make $MAKEOPTS" || true diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh new file mode 100755 index 00000000000..a9e8c059ffe --- /dev/null +++ b/.ci/test-cli.sh @@ -0,0 +1,21 @@ +#!/bin/bash + +# This script gets called from CI to run minimal tests on the sagemath image. + +# Usage: ./test-cli.sh sage-cli-image + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** +set -exo pipefail + +echo "Checking that Sage starts and can calculate 1+1…" +# Calculate 1+1 (remove startup messages and leading & trailing whitespace) +TWO=`docker run "$1" sage -c "'print(1+1)'" | tail -1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'` +[[ "x$TWO" = "x2" ]] diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh new file mode 100755 index 00000000000..60b59d6a5ca --- /dev/null +++ b/.ci/test-dev.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# This script gets called from CI to run minimal tests on the sagemath-dev image. +# This script expects a single argument, the full name of the docker image to +# test. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** +set -exo pipefail + +IMAGE="$1" + +. .ci/setup-make-parallelity.sh + +# Usage: timed_run limit args +# Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. +function timed_run { + START=`date +%s` + docker run -e MAKE="$MAKE" "$IMAGE" "$2" + END=`date +%s` + TOTAL=$((END-START)) + echo "Checking that \"$2\" was fast…" + [[ $TOTAL -lt $1 ]] +} + +timed_run 60 true # runs make build +# TODO: Can't we get this faster than that? +timed_run 300 make # runs make build and then make diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh new file mode 100755 index 00000000000..982f06fbeac --- /dev/null +++ b/.ci/test-jupyter.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# This script gets called from CI to run minimal tests on the sagemath-jupyter +# image. + +# Usage: ./test-jupyter.sh sage-jupyter-image [host] + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** +set -ex + +docker run --name sage-jupyter -p 8888:8888 -d "$1" "sage -n jupyter --no-browser --ip='*' --port=8888" +echo "Checking that the Jupyter notebook is running…" +sleep 10 # giving the server some time to start +docker logs sage-jupyter +wget --retry-connrefused --tries=10 --wait=3 "http://${2:-localhost}:8888" diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000..13f8b567f5f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,41 @@ +# This file configures automatic builds of Sage on [CircleCI](https://circleci.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:latest. When basic SPKGs change, this does not help much +# and the full build might exceed CircleCI's limits for open source projcets (five +# hours on 2 vCPUs as of early 2018.) +# You might want to try to build locally or with GitLab CI, see +# `.gitlab-ci.yml` for more options. +# Note that we do not use workflows because it was not clear whether the docker +# images sizes would not exceed the size limits of CircleCI workspaces. Also, +# workflows would not make things faster. We do not get more machines for free +# and the frequent loading of docker images probably exceeds the cost of the +# actual tests. + +version: 2 +jobs: + # As https://circleci.com/docs/2.0/docker-layer-caching/ is a paid feature, + # we do build & test & release in one step. + build: + machine: true + steps: + - checkout + - run: + # The docker commands sometimes take a while to produce output + no_output_timeout: 30m + command: | + # CircleCI has no mechanism to hide secret variables. + # Therefore we roll our own to protect $SECRET_* variables. + . .ci/protect-secrets.sh + + export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} + # Build docker images + # TODO: Change this line to sagemath/sagemath-dev:latest + export ARTIFACT_BASE=saraedum/sagemath-dev:gitlabci + . .ci/build-docker.sh + # Test that the images work + . .ci/test-dev.sh $DOCKER_IMAGE_DEV + . .ci/test-cli.sh $DOCKER_IMAGE_CLI + . .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + # Push docker images to dockerhub if a dockerhub user has been configured + . .ci/push-dockerhub.sh sagemath-dev + . .ci/push-dockerhub.sh sagemath diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 00000000000..3e4e48b0b5f --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 101d349690c..498b92c73b9 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,9 @@ $RECYCLE.BIN/ ########### .ipynb_checkpoints Untitled*.ipynb + +############################# +# GitLab CI generated files # +############################# +gitlab-build-docker.log + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..e256dd1effe --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,99 @@ +# This file configures automatic builds of Sage on [GitLab](https://gitlab.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:latest. When basic SPKGs changed, this does not help +# much and the full build might the set time limit in GitLab. You can increase +# that limit in Settings → CI/CD. +# You can also provision your own private more powerful runner in the same +# place; or set up your favourite cloud service to provide an on-demand +# autoscale runner. + +image: docker:latest + +stages: + - build + - test + - release + +variables: + DOCKER_TAG: $CI_COMMIT_REF_SLUG + # Builds are very I/O intensive; make sure we have a fast file system. + DOCKER_DRIVER: overlay2 + # TODO: Change this line to sagemath/sagemath-dev:latest + ARTIFACT_BASE: saraedum/sagemath-dev:gitlabci + +before_script: + # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 + # So we roll our own which protects all variables that start with SECRET_ + - . .ci/protect-secrets.sh + +# We use docker-in-docker to build our docker images. It can be faster to +# expose your outer docker daemon by mounting /var/run/docker.sock to +# /var/run/docker.sock and setting DOCKER_HOST in Settings -> CI/CD -> Secret +# variable to unix:///var/run/docker.sock +services: +- docker:dind + +# Build Sage and its documentation. +# The build starts from the build artifacts of ARTIFACT_BASE which is usually +# much faster than building from a clean checkout of Sage. +build-from-latest: &build + stage: build + artifacts: + when: always + paths: + - gitlab-build-docker.log + expire_in: 1 month + script: + # The output of the build can get larger than gitlab.com's limit; only print the first 3MB. + - . .ci/build-docker.sh | tee gitlab-build-docker.log | head -c 3m + - . .ci/push-gitlab.sh sagemath-dev + - . .ci/push-gitlab.sh sagemath + except: + - master + +# Build Sage and its documentation from a clean checkout of Sage. +# Note that this takes a very long time. You probably want to run this on your +# own gitlab-runner and increase the standard GitLab time limit for CI runs. +build-from-clean: + << : *build + variables: + ARTIFACT_BASE: "source-clean" + only: + - master + +test-dev: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath-dev + - . .ci/test-dev.sh "$DOCKER_IMAGE" + +test-cli: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath + - . .ci/test-cli.sh "$DOCKER_IMAGE" + +test-jupyter: + stage: test + script: + # Force usage of docker-in-docker (and don't start docker on a bind-mounted + # /var/run/docker.sock set through a proivate GitLab CI variable) so that + # the -p flag to docker run works as expected. + - export DOCKER_HOST='tcp://docker:2375' + - apk update + - apk add wget + - . .ci/pull-gitlab.sh sagemath + - . .ci/test-jupyter.sh "$DOCKER_IMAGE" docker + +# Pushes the built images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +dockerhub: + stage: release + only: + - branches + - tags + script: + - . .ci/pull-gitlab.sh sagemath-dev + - . .ci/push-dockerhub.sh sagemath-dev + - . .ci/pull-gitlab.sh sagemath + - . .ci/push-dockerhub.sh sagemath diff --git a/Makefile b/Makefile index 92457458fe5..d2dfc71256d 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,7 @@ misc-clean: rm -f build/make/Makefile build/make/Makefile-auto rm -f .BUILDSTART +# TODO: What is a "bdist"? A binary distribution? bdist-clean: clean $(MAKE) misc-clean @@ -89,6 +90,31 @@ maintainer-clean: distclean bootstrap-clean micro_release: bdist-clean sagelib-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 .py files that have a corresponding .pyc or .pyo file as they are not needed when running code with CPython..." + find local/lib/python* -name '*.py' | while IFS= read -r fname; do [ -e "$${fname}c" -o -e "$${fname}o" ] && rm "$$fname"; done || true + @echo "Removing sphinx artifacts..." + rm -rf local/share/doc/sage/doctrees local/share/doc/sage/inventory + @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" + @# We need src/sage/ for introspection with "??" + @# We need src/sage/bin/ for the scripts that invoke Sage + @# We need sage, the script to start Sage + @# We need local/, the dependencies and the built Sage library itself. + @# We keep VERSION.txt. + @# We keep COPYING.txt so we ship a license with this distribution. + find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; + cd src && find . -name . -o -prune ! -name sage ! -name bin -exec rm -rf \{\} \; + +# Leaves everything that is needed to make the next "make" fast but removes +# all the cheap build artifacts that can be quickly regenerated. +fast-rebuild-clean: misc-clean bdist-clean + rm -rf upstream/ + rm -rf src/build/temp.* + # Without site-packages/sage sage does not start but copying/compiling + # them from src/build is very fast. + rm -rf local/lib/python*/site-packages/sage + # The .py files in src/build are restored from src/sage without their + # mtimes changed. + find src/build -name '*.py' -exec rm \{\} \; TESTALL = ./sage -t --all PTESTALL = ./sage -t -p --all diff --git a/configure.ac b/configure.ac index 5c61a89704e..2d86803c6c9 100644 --- a/configure.ac +++ b/configure.ac @@ -285,9 +285,9 @@ fi AC_CHECK_PROG(found_latex, latex, yes, no) if test x$found_latex != xyes then - AC_MSG_WARN([You do not have 'latex', which is recommended, but not]) - AC_MSG_WARN([required. Latex is only really used for building pdf]) - AC_MSG_WARN([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) + AC_MSG_NOTICE([You do not have 'latex', which is recommended, but not]) + AC_MSG_NOTICE([required. Latex is only really used for building pdf]) + AC_MSG_NOTICE([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) fi # Check that perl is available, with version 5.8.0 or later. diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 00000000000..a38152cb5ee --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +# Stores the commit that was used to create a sagemath-dev image +commit diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000000..503f9eb579f --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,176 @@ +################################################################################ +# SageMath images for Docker # +################################################################################ +# This is a description of the layout of this Dockerfile; for details on the # +# created docker images, see the README.md please. # +# # +# This Dockerfile builds sagemath (for end-users) and sagemath-dev (for # +# developers) it consists of lots of intermediate targets, mostly to shrink # +# the resulting images but also to make this hopefully easier to maintain. # +# The aims of this Dockerfile are: # +# (1) Make it build in reasonable time. # +# (2) It should be self-contained and work on its own, i.e., just by invoking # +# docker build without any external orchestration script. # +# # +# The idea to achieve (1) is to reuse the build artifacts from the latest # +# master build. This is slightly against the philosophy of a Dockerfile (which # +# should produce perfectly reproducible outputs) but building Sage from scratch# +# just takes too long at the moment to do this all the time. ARTIFACT_BASE # +# controls which build artifacts are used. You probably want to set this to # +# sagemath/sagemath-dev:latest which takes the latest build from the official # +# master branch. The default is source-clean which builds Sage from scratch. # +# If you want to understand how this works, have a look at source-from-context # +# which merges ARTIFACT_BASE with the context, i.e., the contents of the sage # +# source directory. # +################################################################################ + + +ARG ARTIFACT_BASE=source-clean + +################################################################################ +# Image containing the run-time dependencies for Sage # +################################################################################ +FROM ubuntu:xenial as run-time-dependencies +LABEL maintainer="Erik M. Bray , Julian Rüth " +# Set sane defaults for common environment variables. +ENV LC_ALL C.UTF-8 +ENV LANG C.UTF-8 +ENV SHELL /bin/bash +# Create symlinks for sage and sagemath - we copy a built sage to the target of these symlinks later. +ARG SAGE_ROOT=/home/sage/sage +RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage +RUN ln -s /usr/bin/sage /usr/bin/sagemath +# Sage needs the fortran libraries at run-time because we do not build gfortran +# with Sage but use the system's. +# We also install sudo for the sage user, see below. +RUN apt-get update -qq \ + && apt-get install -y --no-install-recommends gfortran sudo \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* +# Sage refuses to build as root, so we add a "sage" user that can sudo without a password. +# We also want this user at runtime as some commands in sage know about the user that was used during build. +ARG HOME=/home/sage +RUN adduser --quiet --shell /bin/bash --gecos "Sage user,101,," --disabled-password --home "$HOME" sage \ + && echo "sage ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/01-sage \ + && chmod 0440 /etc/sudoers.d/01-sage +# Run everything from now on as the sage user in sage's home +USER sage +ENV HOME $HOME +WORKDIR $HOME + +################################################################################ +# Image containing everything so that a make in a clone of the Sage repository # +# completes without errors # +################################################################################ +FROM run-time-dependencies as build-time-dependencies +# Install the build time dependencies & git +RUN sudo apt-get update -qq \ + && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git \ + && sudo apt-get clean \ + && sudo rm -r /var/lib/apt/lists/* + +################################################################################ +# Image with an empty git repository in $SAGE_ROOT. # +################################################################################ +FROM build-time-dependencies as source-clean +ARG SAGE_ROOT=/home/sage/sage +RUN mkdir -p "$SAGE_ROOT" +WORKDIR $SAGE_ROOT +RUN git init +RUN git remote add trac git@trac.sagemath.org:sage.git + +################################################################################ +# Image with the build context added, i.e., the directory from which `docker # +# build` has been called in a separate directory so we can copy files from # +# there. # +# This blows up the size of this docker image significantly, but we only use # +# this image to create artifacts for our final image. # +# Warning: If you set ARTIFACT_BASE to something else than source-clean, the # +# build is not going to use the build-time-dependencies target but rely on # +# whatever tools are installed in ARTIFACT_BASE. # +################################################################################ +FROM $ARTIFACT_BASE as source-from-context +WORKDIR $HOME +COPY --chown=sage:sage . sage-context +# Checkout the commit that is in $HOME/sage-context +ARG SAGE_ROOT=/home/sage/sage +WORKDIR $SAGE_ROOT +RUN git fetch "$HOME/sage-context" HEAD \ + && git reset FETCH_HEAD \ + && git checkout -f FETCH_HEAD \ + && git log -1 --format=%H > docker/commit \ + && rm -rf .git +# Copy over all the untracked/staged/unstaged changes from sage-context +WORKDIR $HOME/sage-context +RUN if git status --porcelain | read CHANGES; then \ + git -c user.name=docker-build -c user.email=docker-build@sage.invalid stash -u \ + && git stash show -p > "$HOME"/sage-context.patch; \ + else \ + touch "$HOME"/sage-context.patch; \ + fi +WORKDIR $SAGE_ROOT +RUN patch -p1 < "$HOME"/sage-context.patch + +################################################################################ +# Image with a built sage but without sage's documentation. # +################################################################################ +FROM source-from-context as make-build +# Make sure that the results runs on most CPUs. +ENV SAGE_FAT_BINARY yes +# Just to be sure Sage doesn't try to build its own GCC (even though +# it shouldn't with a recent GCC package from the system and with gfortran) +ENV SAGE_INSTALL_GCC no +# Make everything in the build use multiple cores (this causes trouble for some packages outside Sage but it still seems to be they way Sage is doing this.) +ARG MAKE="make -j2" +ENV MAKE $MAKE +RUN make build + +################################################################################ +# Image with a full build of sage and its documentation. # +################################################################################ +FROM make-build as make-all +RUN make + +################################################################################ +# Image with a full build of sage and its documentation but everything # +# stripped that can be quickly rebuild by make. # +################################################################################ +FROM make-all as make-fast-rebuild-clean +RUN make fast-rebuild-clean + +################################################################################ +# A releasable (relatively small, but still huge) image of this build with all # +# the build artifacts intact so developers can make changes and rebuild # +# quickly # +################################################################################ +FROM source-clean as sagemath-dev +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=make-fast-rebuild-clean $SAGE_ROOT $SAGE_ROOT +COPY ./docker/entrypoint-dev.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +CMD ["bash"] + +################################################################################ +# Image with a full build of sage, ready to release, i.e., with stripped # +# binaries and some extras to run the jupyter notebook. # +################################################################################ +FROM make-all as make-release +RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" +RUN sage -i gap_jupyter singular_jupyter pari_jupyter +RUN make micro_release + +################################################################################ +# A releasable (relatively small) image which contains a copy of sage without # +# temporary build artifacts which is set up to start the command line # +# interface if no parameters are passed in. # +################################################################################ +FROM run-time-dependencies as sagemath +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ +# Put scripts to start gap, gp, maxima, ... in /usr/bin +WORKDIR $SAGE_ROOT +RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" +COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +EXPOSE 8888 +CMD ["sage"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000000..9b653689031 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,51 @@ +[![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/2018.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI](https://circleci.com/gh/saraedum/sage.svg?style=svg)](https://circleci.com/gh/saraedum/sage) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/gitlabci/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/gitlabci) + +# Supported tags + +* `latest` — the stable `master` branch +* `x.x.x` — all stable releases of Sage are tagged with their version number. +* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released + +# What is SageMath + +SageMath is a free open-source mathematics software system licensed under the GPL. It builds on top of many existing open-source packages: NumPy, SciPy, matplotlib, Sympy, Maxima, GAP, FLINT, R and many more. Access their combined power through a common, Python-based language or directly via interfaces or wrappers. + +**Mission**: *Creating a viable free open source alternative to Magma, Maple, Mathematica and Matlab.* + +# What's in this image + +There are several flavours of this image. + +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath.svg)](https://hub.docker.com/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: + ``` + docker run -it sagemath/sagemath:latest + ``` + You can start a graphical [Jupyter Notebook](https://jupyter.org) at http://localhost:8888 instead. To use the notebook, follow the instructions printed when you run: + ``` + docker run -p8888:8888 sagemath/sagemath:latest "sage -n jupyter --no-browser --ip='*' --port=8888" + ``` +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: + ``` + docker run -it sagemath/sagemath-dev:latest + ``` + This triggers a rebuild and drops you in a shell afterwards. Note that the git repository has been emptied to save space. If you want to use git, fetch from your git repository with `git fetch trac` and go to the commit that was used to create this image with + ``` + git reset $(cat docker/commit) + ``` + +# How to build your own SageMath images + +Run `docker build -f docker/Dockerfile --target TARGET .` in the Sage repository with `TARGET` one of `sage`, `sage-jupyter`, or `dev`. + +# How these images get updated + +Every push to our [github repository](https://github.com/sagemath/sage) triggers a build in [CircleCI](https://circleci.com) which builds and pushes the docker images. +A push to master also triggers a "build" on our [Docker Hub](https://hub.docker.com) repositories. The latter build is mostly disabled by the `hooks/` and only updates the `README.md`. + +Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) triggers a pipeline in GitLab CI. This build also pushes images to Docker Hub. + +Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup either continuous integration service for your own fork of the SageMath repository. + +# License + +The whole Sage software distribution is licensed under the General Public License, version 3. More details can be found in our [COPYING.txt](https://github.com/sagemath/sage/blob/master/COPYING.txt) diff --git a/docker/entrypoint-dev.sh b/docker/entrypoint-dev.sh new file mode 100755 index 00000000000..67299a5a92d --- /dev/null +++ b/docker/entrypoint-dev.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +make build +exec "$@" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 00000000000..692b5c83390 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,2 @@ +#!/bin/bash +exec sage -sh -c "$*" diff --git a/docker/hooks/build b/docker/hooks/build new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/build @@ -0,0 +1 @@ +#!/bin/true diff --git a/docker/hooks/push b/docker/hooks/push new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/push @@ -0,0 +1 @@ +#!/bin/true diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index b8f79c2269a..bf48bf57cdf 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -911,7 +911,12 @@ def get_new_and_updated_modules(self): except ImportError as err: logger.error("Warning: Could not import %s %s", module_name, err) raise - newtime = os.path.getmtime(sys.modules[module_name].__file__) + + module_filename = sys.modules[module_name].__file__ + if (module_filename.endswith('.pyc') or module_filename.endswith('.pyo')): + source_filename = module_filename[:-1] + if (os.path.exists(source_filename)): module_filename = source_filename + newtime = os.path.getmtime(module_filename) if newtime > mtime: updated_modules.append(module_name) From 6ccbaa3130cf397128b99dad75cbbe99c8abe745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 25 Feb 2018 20:44:29 +0100 Subject: [PATCH 002/284] Be more agressive about requiring the latest develop otherwise the builds will just take forever and it is good practice to merge develop frequently to prevent future conflicts. --- .ci/build-docker.sh | 2 +- .circleci/config.yml | 17 +++++++++++++---- .gitlab-ci.yml | 9 ++++++--- docker/Dockerfile | 33 ++++++++++++++++++++++++++------- docker/README.md | 2 +- 5 files changed, 47 insertions(+), 16 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 11651e5dd38..9ec55ab149a 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -21,7 +21,7 @@ set -ex # We speed up the build process by copying built artifacts from ARTIFACT_BASE # during docker build. See /docker/Dockerfile for more details. -ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:latest} +ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} # Seed our cache with $ARTIFACT_BASE if it exists docker pull $ARTIFACT_BASE || true diff --git a/.circleci/config.yml b/.circleci/config.yml index 13f8b567f5f..8a2cc7ca71d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,6 @@ # This file configures automatic builds of Sage on [CircleCI](https://circleci.com). # To make the build time not too excessive, we seed the build cache with -# sagemath/sagemath-dev:latest. When basic SPKGs change, this does not help much +# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much # and the full build might exceed CircleCI's limits for open source projcets (five # hours on 2 vCPUs as of early 2018.) # You might want to try to build locally or with GitLab CI, see @@ -27,15 +27,24 @@ jobs: # Therefore we roll our own to protect $SECRET_* variables. . .ci/protect-secrets.sh - export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} # Build docker images - # TODO: Change this line to sagemath/sagemath-dev:latest - export ARTIFACT_BASE=saraedum/sagemath-dev:gitlabci + export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} + case $DOCKER_TAG in + "develop" | "latest") + export ARTIFACT_BASE=source-clean + ;; + *) + # TODO: Change this line to sagemath/sagemath-dev:develop + export ARTIFACT_BASE=saraedum/sagemath-dev:develop + ;; + esac . .ci/build-docker.sh + # Test that the images work . .ci/test-dev.sh $DOCKER_IMAGE_DEV . .ci/test-cli.sh $DOCKER_IMAGE_CLI . .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + # Push docker images to dockerhub if a dockerhub user has been configured . .ci/push-dockerhub.sh sagemath-dev . .ci/push-dockerhub.sh sagemath diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e256dd1effe..d56fb0e28a9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ # This file configures automatic builds of Sage on [GitLab](https://gitlab.com). # To make the build time not too excessive, we seed the build cache with -# sagemath/sagemath-dev:latest. When basic SPKGs changed, this does not help +# sagemath/sagemath-dev:develop. When basic SPKGs changed, this does not help # much and the full build might the set time limit in GitLab. You can increase # that limit in Settings → CI/CD. # You can also provision your own private more powerful runner in the same @@ -18,8 +18,8 @@ variables: DOCKER_TAG: $CI_COMMIT_REF_SLUG # Builds are very I/O intensive; make sure we have a fast file system. DOCKER_DRIVER: overlay2 - # TODO: Change this line to sagemath/sagemath-dev:latest - ARTIFACT_BASE: saraedum/sagemath-dev:gitlabci + # TODO: Change this line to sagemath/sagemath-dev:develop + ARTIFACT_BASE: saraedum/sagemath-dev:develop before_script: # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 @@ -50,6 +50,7 @@ build-from-latest: &build - . .ci/push-gitlab.sh sagemath except: - master + - develop # Build Sage and its documentation from a clean checkout of Sage. # Note that this takes a very long time. You probably want to run this on your @@ -60,6 +61,8 @@ build-from-clean: ARTIFACT_BASE: "source-clean" only: - master + - develop + except: [] test-dev: stage: test diff --git a/docker/Dockerfile b/docker/Dockerfile index 503f9eb579f..d62f4c60cd3 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -13,12 +13,12 @@ # docker build without any external orchestration script. # # # # The idea to achieve (1) is to reuse the build artifacts from the latest # -# master build. This is slightly against the philosophy of a Dockerfile (which # +# develop build. This is slightly against the philosophy of a Dockerfile (which# # should produce perfectly reproducible outputs) but building Sage from scratch# # just takes too long at the moment to do this all the time. ARTIFACT_BASE # # controls which build artifacts are used. You probably want to set this to # -# sagemath/sagemath-dev:latest which takes the latest build from the official # -# master branch. The default is source-clean which builds Sage from scratch. # +# sagemath/sagemath-dev:develop which takes the latest build from the official # +# develop branch. The default is source-clean which builds Sage from scratch. # # If you want to understand how this works, have a look at source-from-context # # which merges ARTIFACT_BASE with the context, i.e., the contents of the sage # # source directory. # @@ -92,15 +92,34 @@ RUN git remote add trac git@trac.sagemath.org:sage.git FROM $ARTIFACT_BASE as source-from-context WORKDIR $HOME COPY --chown=sage:sage . sage-context -# Checkout the commit that is in $HOME/sage-context +# Checkout the commit that checked out in $HOME/sage-context +# This is a bit complicated because our local .git/ is empty and we want to +# make sure that we only change the mtimes of a minimal number of files. +# 1) Restore the git checkout ARTIFACT_BASE was built from, recorded in +# docker/commit. (Or go directly to FETCH_HEAD if there is no history to +# restore.) +# 2) Merge in FETCH_HEAD but only if it is a fast-forward, i.e., if it is an +# ancestor of the commit restored in 1. If we would not do that we would get +# a new commit hash in docker/commit that is not known outside of this build +# run. Since docker/commit was in the history of FETCH_HEAD this should +# automatically be a fast-forward. +# 3) Trash .git again to save some space. ARG SAGE_ROOT=/home/sage/sage WORKDIR $SAGE_ROOT RUN git fetch "$HOME/sage-context" HEAD \ - && git reset FETCH_HEAD \ - && git checkout -f FETCH_HEAD \ + && if [ -e docker/commit ]; then \ + git reset `cat docker/commit` \ + || ( echo "Could not find commit `cat docker/commit` in your local Git history. Please merge in the latest develop branch to fix this: git fetch trac && git merge trac/develop." && exit 1 ) \ + else \ + echo "You are building from $ARTIFACT_BASE which has no docker/commit file. That's a bug unless you are building from source-clean or something similar." \ + && git reset FETCH_HEAD \ + && git checkout -f FETCH_HEAD; \ + fi \ + && git merge --ff-only FETCH_HEAD \ && git log -1 --format=%H > docker/commit \ && rm -rf .git -# Copy over all the untracked/staged/unstaged changes from sage-context +# Copy over all the untracked/staged/unstaged changes from sage-context. This +# is relevant for non-CI invocations of this Dockerfile. WORKDIR $HOME/sage-context RUN if git status --porcelain | read CHANGES; then \ git -c user.name=docker-build -c user.email=docker-build@sage.invalid stash -u \ diff --git a/docker/README.md b/docker/README.md index 9b653689031..0e388d95e5a 100644 --- a/docker/README.md +++ b/docker/README.md @@ -26,7 +26,7 @@ There are several flavours of this image. ``` * [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` - docker run -it sagemath/sagemath-dev:latest + docker run -it sagemath/sagemath-dev:develop ``` This triggers a rebuild and drops you in a shell afterwards. Note that the git repository has been emptied to save space. If you want to use git, fetch from your git repository with `git fetch trac` and go to the commit that was used to create this image with ``` From 7f90d86a5716391952675f7ef45104ff4f85e925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 00:45:40 +0100 Subject: [PATCH 003/284] be more precise about failed latest merge --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d62f4c60cd3..ca3e40a6af8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -109,7 +109,7 @@ WORKDIR $SAGE_ROOT RUN git fetch "$HOME/sage-context" HEAD \ && if [ -e docker/commit ]; then \ git reset `cat docker/commit` \ - || ( echo "Could not find commit `cat docker/commit` in your local Git history. Please merge in the latest develop branch to fix this: git fetch trac && git merge trac/develop." && exit 1 ) \ + || ( echo "Could not find commit `cat docker/commit` in your local Git history. Please merge in the latest built develop branch to fix this: git fetch trac && git merge `cat docker/commit`." && exit 1 ) \ else \ echo "You are building from $ARTIFACT_BASE which has no docker/commit file. That's a bug unless you are building from source-clean or something similar." \ && git reset FETCH_HEAD \ From 8efe3a9d501d97b89575952166109bb770b9026f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 00:59:17 +0100 Subject: [PATCH 004/284] more and more specific badges --- docker/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docker/README.md b/docker/README.md index 0e388d95e5a..e64d90937a3 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,10 +1,11 @@ -[![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/2018.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI](https://circleci.com/gh/saraedum/sage.svg?style=svg)](https://circleci.com/gh/saraedum/sage) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/gitlabci/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/gitlabci) +[![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/2018.svg)](https://github.com/sagemath/sage/commits/master) # Supported tags -* `latest` — the stable `master` branch +* `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/saraedum/sage/master.svg)](https://github.com/saraedum/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/saraedum/sage/master.svg)](https://circleci.com/gh/saraedum/sage/tree/master) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/master/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/master) * `x.x.x` — all stable releases of Sage are tagged with their version number. -* `develop` — the current development version of Sage which gets merged into the `master` branch when a new version of Sage is released +* `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/saraedum/sage/develop.svg)](https://github.com/saraedum/sage/commits/develop) [![CircleCI branch](https://img.shields.io/circleci/project/github/saraedum/sage/master.svg)](https://circleci.com/gh/saraedum/sage/tree/master) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/develop/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/develop) + # What is SageMath @@ -16,7 +17,7 @@ SageMath is a free open-source mathematics software system licensed under the GP There are several flavours of this image. -* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath.svg)](https://hub.docker.com/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/saraedum/sagemath:latest.svg)](https://hub.docker.com/saraedum/sagemath) contains everything necessary to run Sage on the command line. Run it with: ``` docker run -it sagemath/sagemath:latest ``` @@ -24,7 +25,7 @@ There are several flavours of this image. ``` docker run -p8888:8888 sagemath/sagemath:latest "sage -n jupyter --no-browser --ip='*' --port=8888" ``` -* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/saraedum/sagemath-dev:develop.svg)](https://hub.docker.com/saraedum/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` docker run -it sagemath/sagemath-dev:develop ``` From 15f3ae761650c4d3c441e3ab2b68f26e290db9db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 01:16:42 +0100 Subject: [PATCH 005/284] fix "build your own" instructions --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index e64d90937a3..aa938e7122a 100644 --- a/docker/README.md +++ b/docker/README.md @@ -36,7 +36,7 @@ There are several flavours of this image. # How to build your own SageMath images -Run `docker build -f docker/Dockerfile --target TARGET .` in the Sage repository with `TARGET` one of `sage`, `sage-jupyter`, or `dev`. +Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=saraedum/sagemath-dev:develop --target TARGET .` in the Sage repository with `TARGET` one of `sagemath` or `sagemath-dev`. # How these images get updated From 595be9c6431f9e406b5cf14277b8ec0399966226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 09:07:44 +0100 Subject: [PATCH 006/284] POSIX scripts so they run on Alpine --- .ci/build-docker.sh | 3 ++- .ci/protect-secrets.sh | 2 +- .ci/pull-gitlab.sh | 2 +- .ci/push-dockerhub.sh | 2 +- .ci/push-gitlab.sh | 5 +++-- .ci/setup-make-parallelity.sh | 3 ++- .ci/test-cli.sh | 3 ++- .ci/test-dev.sh | 3 ++- .ci/test-jupyter.sh | 3 ++- 9 files changed, 16 insertions(+), 10 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 9ec55ab149a..d6d082db7fb 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to build several flavours of docker images # which contain Sage. @@ -12,6 +12,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + set -ex [[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none diff --git a/.ci/protect-secrets.sh b/.ci/protect-secrets.sh index a3ae9239988..ee781dddca8 100755 --- a/.ci/protect-secrets.sh +++ b/.ci/protect-secrets.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script protects all environment variables that start with "SECRET_". # It puts them in a temporary file. The name of the variable contains the path diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh index 01bb10cdd08..19be8c9ff7c 100755 --- a/.ci/pull-gitlab.sh +++ b/.ci/pull-gitlab.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to pull the Sage docker images that were # built during the "build" phase to pull all the connected docker daemon diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh index ca2c4906eec..5055fc056ac 100755 --- a/.ci/push-dockerhub.sh +++ b/.ci/push-dockerhub.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to push our docker images to # $DOCKER_USER/sagemath* on the Docker Hub. diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh index 9d98dca7e49..2e491e0a5b6 100755 --- a/.ci/push-gitlab.sh +++ b/.ci/push-gitlab.sh @@ -1,5 +1,4 @@ -#!/bin/bash -set -ex +#!/bin/sh # This script gets called from CI to push our docker images to registry # configured in GitLab. (Mostly, so we can pull them again to push them to the @@ -17,6 +16,8 @@ set -ex # http://www.gnu.org/licenses/ # **************************************************************************** +set -ex + [[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) [[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 12f3a252179..a69bdbc37f6 100644 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # **************************************************************************** # Copyright (C) 2018 Julian Rüth @@ -9,6 +9,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + set -ex # Determine the number of threads that can run simultaneously on this system diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh index a9e8c059ffe..ab58474f74f 100755 --- a/.ci/test-cli.sh +++ b/.ci/test-cli.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to run minimal tests on the sagemath image. @@ -13,6 +13,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + set -exo pipefail echo "Checking that Sage starts and can calculate 1+1…" diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 60b59d6a5ca..c8fd0af1be8 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to run minimal tests on the sagemath-dev image. # This script expects a single argument, the full name of the docker image to @@ -13,6 +13,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + set -exo pipefail IMAGE="$1" diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index 982f06fbeac..b2360b72722 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # This script gets called from CI to run minimal tests on the sagemath-jupyter # image. @@ -14,6 +14,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ # **************************************************************************** + set -ex docker run --name sage-jupyter -p 8888:8888 -d "$1" "sage -n jupyter --no-browser --ip='*' --port=8888" From e4010d8f91082ac1b4d9c2cd2955edda30c638f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 09:09:36 +0100 Subject: [PATCH 007/284] Fix branding of CI files and refer to sagemath instead of saraedum (that I had used for testing.) --- .circleci/config.yml | 3 +-- .gitlab-ci.yml | 3 +-- docker/README.md | 10 +++++----- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8a2cc7ca71d..504680deb98 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,8 +34,7 @@ jobs: export ARTIFACT_BASE=source-clean ;; *) - # TODO: Change this line to sagemath/sagemath-dev:develop - export ARTIFACT_BASE=saraedum/sagemath-dev:develop + export ARTIFACT_BASE=sagemath/sagemath-dev:develop ;; esac . .ci/build-docker.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d56fb0e28a9..1616e411980 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,8 +18,7 @@ variables: DOCKER_TAG: $CI_COMMIT_REF_SLUG # Builds are very I/O intensive; make sure we have a fast file system. DOCKER_DRIVER: overlay2 - # TODO: Change this line to sagemath/sagemath-dev:develop - ARTIFACT_BASE: saraedum/sagemath-dev:develop + ARTIFACT_BASE: sagemath/sagemath-dev:develop before_script: # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 diff --git a/docker/README.md b/docker/README.md index aa938e7122a..0a48435d9f9 100644 --- a/docker/README.md +++ b/docker/README.md @@ -2,9 +2,9 @@ # Supported tags -* `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/saraedum/sage/master.svg)](https://github.com/saraedum/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/saraedum/sage/master.svg)](https://circleci.com/gh/saraedum/sage/tree/master) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/master/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/master) +* `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) * `x.x.x` — all stable releases of Sage are tagged with their version number. -* `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/saraedum/sage/develop.svg)](https://github.com/saraedum/sage/commits/develop) [![CircleCI branch](https://img.shields.io/circleci/project/github/saraedum/sage/master.svg)](https://circleci.com/gh/saraedum/sage/tree/master) [![GitLab CI](https://gitlab.com/saraedum/sage/badges/develop/pipeline.svg)](https://gitlab.com/saraedum/sage/commits/develop) +* `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) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) # What is SageMath @@ -17,7 +17,7 @@ SageMath is a free open-source mathematics software system licensed under the GP There are several flavours of this image. -* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/saraedum/sagemath:latest.svg)](https://hub.docker.com/saraedum/sagemath) contains everything necessary to run Sage on the command line. Run it with: +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath:latest.svg)](https://hub.docker.com/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: ``` docker run -it sagemath/sagemath:latest ``` @@ -25,7 +25,7 @@ There are several flavours of this image. ``` docker run -p8888:8888 sagemath/sagemath:latest "sage -n jupyter --no-browser --ip='*' --port=8888" ``` -* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/saraedum/sagemath-dev:develop.svg)](https://hub.docker.com/saraedum/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev:develop.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` docker run -it sagemath/sagemath-dev:develop ``` @@ -36,7 +36,7 @@ There are several flavours of this image. # How to build your own SageMath images -Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=saraedum/sagemath-dev:develop --target TARGET .` in the Sage repository with `TARGET` one of `sagemath` or `sagemath-dev`. +Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=sagemath/sagemath-dev:develop --target TARGET .` in the Sage repository with `TARGET` one of `sagemath` or `sagemath-dev`. # How these images get updated From 10d2093e21b637f31f2f6fef0a994dd14f3cd48f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 13:24:35 +0100 Subject: [PATCH 008/284] Comment on timings and possible speedups --- .circleci/config.yml | 12 +++++++++--- .gitlab-ci.yml | 8 ++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 504680deb98..2c6354d9a86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,8 @@ # This file configures automatic builds of Sage on [CircleCI](https://circleci.com). # To make the build time not too excessive, we seed the build cache with -# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much -# and the full build might exceed CircleCI's limits for open source projcets (five -# hours on 2 vCPUs as of early 2018.) +# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much, +# still the full build does usually not exceed CircleCI's limits for open +# source projcets (five hours on 2 vCPUs as of early 2018.) # You might want to try to build locally or with GitLab CI, see # `.gitlab-ci.yml` for more options. # Note that we do not use workflows because it was not clear whether the docker @@ -11,6 +11,12 @@ # and the frequent loading of docker images probably exceeds the cost of the # actual tests. +# As of early 2018, a run on CircleCI takes usually about 35 minutes. Most of +# the time is spent pulling/pushing from/to Docker Hub and copying files +# locally during the docker build. We could probably save 5 minutes by not +# pushing the sagemath-dev image to Docker Hub. We could save another five +# minutes by not building and testing the sagemath-dev image in these cases. + version: 2 jobs: # As https://circleci.com/docs/2.0/docker-layer-caching/ is a paid feature, diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1616e411980..38241e6ecd0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,6 +7,14 @@ # place; or set up your favourite cloud service to provide an on-demand # autoscale runner. +# As of early 2018 a run on GitLab CI takes about 50 minutes (with +# build-from-latest.) We could probably save 5 minutes by not pushing the +# sagemath-dev image for branches other than develop and master. We could +# save another 10 minutes by not building/pushing/testing that image in these +# cases. +# Note that most of the time during CI is spent with pulling and pushing of +# docker images and copying files locally as part of the docker build. + image: docker:latest stages: From f1d33ecf2a9283aeef48379490e23b50f8cf5da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 15:06:40 +0100 Subject: [PATCH 009/284] Explain how docker can be used for local development --- docker/Dockerfile | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index ca3e40a6af8..2468c13b79f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -24,6 +24,22 @@ # source directory. # ################################################################################ +################################################################################ +# HOWTO use this file for local builds # +################################################################################ +# If you don't mind downloading a 2GB docker image from time to time, you # +# could use this file for local Sage development. As of early 2018 each build # +# takes about five minutes but you don't have to through the sadly frequent # +# rebuilds of Sage the whole Sage distribution... # +# To build Sage, run this command from your sage/ directory: # +# $ docker build --build-arg MAKE="make -j4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . +# To run Sage: # +# $ docker run -it sage # +# To run doctests: # +# $ docker run -e "MAKE=make -j4" -it sage sage -tp src/sage # +# Make sure that you always have the latest develop branch merged into your # +# local branch for this to work. # +################################################################################ ARG ARTIFACT_BASE=source-clean From d0b3d7fb1d288885fe42bc7988ed23bc513582a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 18:09:41 +0100 Subject: [PATCH 010/284] To make this truly a micro-release drop all documentation that is not accessible from IPython/Jupyter --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index d2dfc71256d..9f91b71e921 100644 --- a/Makefile +++ b/Makefile @@ -94,6 +94,8 @@ micro_release: bdist-clean sagelib-clean find local/lib/python* -name '*.py' | while IFS= read -r fname; do [ -e "$${fname}c" -o -e "$${fname}o" ] && rm "$$fname"; done || true @echo "Removing sphinx artifacts..." rm -rf local/share/doc/sage/doctrees local/share/doc/sage/inventory + @echo "Removing documentation. Inspection in IPython still works." + rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" @# We need src/sage/ for introspection with "??" @# We need src/sage/bin/ for the scripts that invoke Sage From 069e722679c5f5e612a7214e51d98cf89b07d137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 13:53:34 +0100 Subject: [PATCH 011/284] Allow user to override ARTIFACT_BASE on CircleCI --- .circleci/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2c6354d9a86..ae4fc6dc985 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -40,7 +40,10 @@ jobs: export ARTIFACT_BASE=source-clean ;; *) - export ARTIFACT_BASE=sagemath/sagemath-dev:develop + # Select sagemath/sagemath-dev:develop as the ARTIFACT_BASE + # unless the user has explicitly selected a differnt one as a + # CircleCI environment variable. + export ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} ;; esac . .ci/build-docker.sh From cd951b82acb8e9b2245a43cc319c8fe7038a749b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 26 Feb 2018 18:50:20 +0100 Subject: [PATCH 012/284] Allow overriding of ARTIFACT_BASE but still use source-clean on develop build --- .gitlab-ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 38241e6ecd0..f5848e84567 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,7 +26,7 @@ variables: DOCKER_TAG: $CI_COMMIT_REF_SLUG # Builds are very I/O intensive; make sure we have a fast file system. DOCKER_DRIVER: overlay2 - ARTIFACT_BASE: sagemath/sagemath-dev:develop + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop before_script: # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 @@ -45,6 +45,8 @@ services: # much faster than building from a clean checkout of Sage. build-from-latest: &build stage: build + variables: + ARTIFACT_BASE: $DEFAULT_ARTIFACT_BASE artifacts: when: always paths: From ffe50a74936f9f3df94b9f89282a2972276a4afd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 14:23:27 +0100 Subject: [PATCH 013/284] Include gcc in the docker images --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 2468c13b79f..262d156be50 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -58,9 +58,10 @@ RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage RUN ln -s /usr/bin/sage /usr/bin/sagemath # Sage needs the fortran libraries at run-time because we do not build gfortran # with Sage but use the system's. +# We need gcc to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get update -qq \ - && apt-get install -y --no-install-recommends gfortran sudo \ + && apt-get install -y --no-install-recommends gfortran gcc sudo \ && apt-get clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. From 09b8527363e8a33a34b31e755765f2d172dce0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 14:25:10 +0100 Subject: [PATCH 014/284] build-time-dependencies are not a requirement for ARTIFACT_BASE usually. --- .ci/build-docker.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index d6d082db7fb..2a1d3f8f4a8 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -32,10 +32,11 @@ function docker_build { } # We use a multi-stage build /docker/Dockerfile. For the caching to be -# effective, we populate the cache by building the make-all target. (Just -# building the last target is not enough as intermediate targets would be -# discarded from the cache and therefore the caching would fail for our actual -# builds below.) +# effective, we populate the cache by building the build-time-dependencies and +# the make-all target. (Just building the last target is not enough as +# intermediate targets would be discarded from the cache and therefore the +# caching would fail for our actual builds below.) +docker_build --pull --target build-time-dependencies . docker_build --pull --tag make-all --target make-all . # Build the release image without build artifacts. From 0e2aebfdfd12ce97984b87c165309d6137e3d550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 15:09:37 +0100 Subject: [PATCH 015/284] Are 2GB instances big enough for our build? --- .gitlab-ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5848e84567..3ee2fbbaacd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,8 @@ # that limit in Settings → CI/CD. # You can also provision your own private more powerful runner in the same # place; or set up your favourite cloud service to provide an on-demand -# autoscale runner. +# autoscale runner. (Make sure to tag them as "2gb", otherwise the build step +# is not going to run on them.) # As of early 2018 a run on GitLab CI takes about 50 minutes (with # build-from-latest.) We could probably save 5 minutes by not pushing the @@ -57,6 +58,11 @@ build-from-latest: &build - . .ci/build-docker.sh | tee gitlab-build-docker.log | head -c 3m - . .ci/push-gitlab.sh sagemath-dev - . .ci/push-gitlab.sh sagemath + tags: + # We need enough disk space for the build to work. + # The 2gb instances on gitlab.com have 50GB of disk: + # https://www.digitalocean.com/pricing/ + - 2gb except: - master - develop From 7b1f937c0018d22c95a56a64782af220904ca1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 15:18:30 +0100 Subject: [PATCH 016/284] Trying the do instances instead the 2gb instances do not seem to have --privileged enabled. *** WARNING: Service runner-9a6801bd-project-5193394-concurrent-0-docker-0 probably didn't start properly. Error response from daemon: Cannot link to a non running container: /runner-9a6801bd-project-5193394-concurrent-0-docker-0 AS /runner-9a6801bd-project-5193394-concurrent-0-docker-0-wait-for-service/runner-9a6801bd-project-5193394-concurrent-0-docker-0 2018-02-27T14:11:49.193457177Z mount: permission denied (are you root?) 2018-02-27T14:11:49.193483054Z Could not mount /sys/kernel/security. 2018-02-27T14:11:49.193487156Z AppArmor detection and --privileged mode might break. 2018-02-27T14:11:49.197419963Z mount: permission denied (are you root?) ********* --- .gitlab-ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ee2fbbaacd..ca4ee580f43 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,9 +60,9 @@ build-from-latest: &build - . .ci/push-gitlab.sh sagemath tags: # We need enough disk space for the build to work. - # The 2gb instances on gitlab.com have 50GB of disk: - # https://www.digitalocean.com/pricing/ - - 2gb + # The do(=digitalocean) instances on gitlab.com should be the 4GB instances + # with 80GB of disk: https://www.digitalocean.com/pricing/ + - do except: - master - develop From 30176eee0bbc2264464482501362d86faeb37914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 16:20:26 +0100 Subject: [PATCH 017/284] make doc-build depends heavily on the number of threads --- .ci/test-dev.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index c8fd0af1be8..75d1f0a812b 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -33,4 +33,4 @@ function timed_run { timed_run 60 true # runs make build # TODO: Can't we get this faster than that? -timed_run 300 make # runs make build and then make +timed_run $(( 600/$NTHREADS )) make # runs make build and then make From 8ac71a5b2d33104ecc3621985dc0f8f72537b8e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 27 Feb 2018 18:03:44 +0100 Subject: [PATCH 018/284] doc-build sometimes (?) needs pkg-config ``` [dochtml] Error building the documentation. [dochtml] Traceback (most recent call last): [dochtml] File "/home/sage/sage/local/lib/python2.7/runpy.py", line 174, in _run_module_as_main [dochtml] "__main__", fname, loader, pkg_name) [dochtml] File "/home/sage/sage/local/lib/python2.7/runpy.py", line 72, in _run_code [dochtml] exec code in run_globals [dochtml] File "/home/sage/sage/local/lib/python2.7/site-packages/sage_setup/docbuild/__main__.py", line 2, in [dochtml] main() [dochtml] File "/home/sage/sage/local/lib/python2.7/site-packages/sage_setup/docbuild/__init__.py", line 1680, in main [dochtml] builder() [dochtml] File "/home/sage/sage/local/lib/python2.7/site-packages/sage_setup/docbuild/__init__.py", line 314, in _wrapper [dochtml] getattr(get_builder(document), name)(*args, **kwds) [dochtml] File "/home/sage/sage/local/lib/python2.7/site-packages/sage_setup/docbuild/__init__.py", line 505, in _wrapper [dochtml] build_many(build_ref_doc, L) [dochtml] File "/home/sage/sage/local/lib/python2.7/site-packages/sage_setup/docbuild/__init__.py", line 246, in build_many [dochtml] ret = x.get(99999) [dochtml] File "/home/sage/sage/local/lib/python2.7/multiprocessing/pool.py", line 572, in get [dochtml] raise self._value [dochtml] EnvironmentError: pkg-config is not installed ``` --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 262d156be50..5bd9185c0df 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git RUN sudo apt-get update -qq \ - && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git \ + && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git pkg-config \ && sudo apt-get clean \ && sudo rm -r /var/lib/apt/lists/* From 711409f416fad2d9cad8d9a1ff3ae661976f1391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 00:21:32 +0100 Subject: [PATCH 019/284] slow CPUs need more time to make --- .ci/test-dev.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 75d1f0a812b..dcbf988abcb 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -33,4 +33,4 @@ function timed_run { timed_run 60 true # runs make build # TODO: Can't we get this faster than that? -timed_run $(( 600/$NTHREADS )) make # runs make build and then make +timed_run $(( 1200/$NTHREADS )) make # runs make build and then make From 29d7d9ab8a6f47f5d9b7883be93c3fa4b0b5aac5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 11:04:57 +0100 Subject: [PATCH 020/284] Update timings I started running these on "do" machines now, not on my own GCE machines that were faster. --- .gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ca4ee580f43..3a436999536 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,13 +8,16 @@ # autoscale runner. (Make sure to tag them as "2gb", otherwise the build step # is not going to run on them.) -# As of early 2018 a run on GitLab CI takes about 50 minutes (with +# As of early 2018 a run on GitLab CI takes about an hour (with # build-from-latest.) We could probably save 5 minutes by not pushing the # sagemath-dev image for branches other than develop and master. We could # save another 10 minutes by not building/pushing/testing that image in these # cases. # Note that most of the time during CI is spent with pulling and pushing of # docker images and copying files locally as part of the docker build. +# At the moment there is no way of passing the docker images to the following +# stages without explicit pushing/pulling or similar: +# https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 image: docker:latest From cf303a14641fbbeb83fbe5e9375d00010a6dec1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 15:48:50 +0100 Subject: [PATCH 021/284] Instead of deleting .py files in local hardlink them this also removes some 50MB in other places --- Makefile | 8 ++++++-- docker/Dockerfile | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9f91b71e921..c3b92d101bf 100644 --- a/Makefile +++ b/Makefile @@ -90,8 +90,6 @@ maintainer-clean: distclean bootstrap-clean micro_release: bdist-clean sagelib-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 .py files that have a corresponding .pyc or .pyo file as they are not needed when running code with CPython..." - find local/lib/python* -name '*.py' | while IFS= read -r fname; do [ -e "$${fname}c" -o -e "$${fname}o" ] && rm "$$fname"; done || true @echo "Removing sphinx artifacts..." rm -rf local/share/doc/sage/doctrees local/share/doc/sage/inventory @echo "Removing documentation. Inspection in IPython still works." @@ -105,6 +103,12 @@ micro_release: bdist-clean sagelib-clean @# We keep COPYING.txt so we ship a license with this distribution. find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; cd src && find . -name . -o -prune ! -name sage ! -name bin -exec rm -rf \{\} \; + if command -v rdfind > /dev/null; then \ + @echo "Hardlinking identical files."; \ + rdfind -makeresultsfile false -makehardlinks true .; \ + else \ + @echo "rdfind not installed. Not hardlinking identical files."; \ + fi # Leaves everything that is needed to make the next "make" fast but removes # all the cheap build artifacts that can be quickly regenerated. diff --git a/docker/Dockerfile b/docker/Dockerfile index 5bd9185c0df..7b7d4345324 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -193,6 +193,7 @@ CMD ["bash"] FROM make-all as make-release RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" RUN sage -i gap_jupyter singular_jupyter pari_jupyter +RUN apt-get update && apt-get install rdfind RUN make micro_release ################################################################################ From 34522cdb0109202286909a563265613254e32ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 16:46:01 +0100 Subject: [PATCH 022/284] We do not need pkg-config it's part of Sage-the-distribution --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 7b7d4345324..e8f144f82ef 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git RUN sudo apt-get update -qq \ - && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git pkg-config \ + && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git && sudo apt-get clean \ && sudo rm -r /var/lib/apt/lists/* From 13938095674f6c927ac012ff1c8f974db6a537e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 17:53:57 +0100 Subject: [PATCH 023/284] fix line terminator --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e8f144f82ef..c92d3f10024 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git RUN sudo apt-get update -qq \ - && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git + && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git \ && sudo apt-get clean \ && sudo rm -r /var/lib/apt/lists/* From 2fc2da6d443e66771f1da6bef9b122a0328a1aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Feb 2018 18:36:00 +0100 Subject: [PATCH 024/284] Add rdfind properly to the dependencies --- docker/Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index c92d3f10024..fbab7266d24 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git RUN sudo apt-get update -qq \ - && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git \ + && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ && sudo apt-get clean \ && sudo rm -r /var/lib/apt/lists/* @@ -193,7 +193,6 @@ CMD ["bash"] FROM make-all as make-release RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" RUN sage -i gap_jupyter singular_jupyter pari_jupyter -RUN apt-get update && apt-get install rdfind RUN make micro_release ################################################################################ From 8ce8f41ca3ec0a752628556150af1e670a7b390a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 01:06:27 +0100 Subject: [PATCH 025/284] add more debug info about the host we are running on --- .ci/describe-system.sh | 18 ++++++++++++++++++ .gitlab-ci.yml | 2 ++ 2 files changed, 20 insertions(+) create mode 100644 .ci/describe-system.sh diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh new file mode 100644 index 00000000000..c8cf7a3048c --- /dev/null +++ b/.ci/describe-system.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set +e -x + +uname -a +cat /proc/cpuinfo +cat /proc/meminfo +docker info diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a436999536..4af063fd93a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,6 +36,8 @@ before_script: # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 # So we roll our own which protects all variables that start with SECRET_ - . .ci/protect-secrets.sh + # Collect debug infos about the system we are running on + - . .ci/describe-system.sh # We use docker-in-docker to build our docker images. It can be faster to # expose your outer docker daemon by mounting /var/run/docker.sock to From e4d5e4e574387bca71e7290ccb5d7c9757425d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 01:35:39 +0100 Subject: [PATCH 026/284] fix doctest errors --- Makefile | 5 +++-- docker/Dockerfile | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index c3b92d101bf..72d5f03956b 100644 --- a/Makefile +++ b/Makefile @@ -95,14 +95,15 @@ micro_release: bdist-clean sagelib-clean @echo "Removing documentation. Inspection in IPython still works." rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" - @# We need src/sage/ for introspection with "??" + @# We need src/sage/ and src/doc/common for introspection with "??" @# We need src/sage/bin/ for the scripts that invoke Sage @# We need sage, the script to start Sage @# We need local/, the dependencies and the built Sage library itself. @# We keep VERSION.txt. @# We keep COPYING.txt so we ship a license with this distribution. find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; - cd src && find . -name . -o -prune ! -name sage ! -name bin -exec rm -rf \{\} \; + cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; + cd doc && find . -name . -o -prune ! -name common -exec rm -rf \{\} \; if command -v rdfind > /dev/null; then \ @echo "Hardlinking identical files."; \ rdfind -makeresultsfile false -makehardlinks true .; \ diff --git a/docker/Dockerfile b/docker/Dockerfile index fbab7266d24..5da501c23a1 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -58,10 +58,10 @@ RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage RUN ln -s /usr/bin/sage /usr/bin/sagemath # Sage needs the fortran libraries at run-time because we do not build gfortran # with Sage but use the system's. -# We need gcc to allow compilation of cython at run-time from the notebook. +# We need gcc and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get update -qq \ - && apt-get install -y --no-install-recommends gfortran gcc sudo \ + && apt-get install -y --no-install-recommends gfortran gcc libstdc++-5-dev sudo \ && apt-get clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. From bbe279e974ab3cd98cde207ed7cb757d360b2a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 01:46:41 +0100 Subject: [PATCH 027/284] same log info on CircleCI --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index ae4fc6dc985..2d38cef4204 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,6 +32,8 @@ jobs: # CircleCI has no mechanism to hide secret variables. # Therefore we roll our own to protect $SECRET_* variables. . .ci/protect-secrets.sh + # Collect debug infos about the system we are running on + . .ci/describe-system.sh # Build docker images export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} From 9aaca79bc7e99bc613e5a83016175fa418dcdb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 02:10:04 +0100 Subject: [PATCH 028/284] sh commands in Makefile do not modify the cwd --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 72d5f03956b..fffaac2f563 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ micro_release: bdist-clean sagelib-clean @# We keep COPYING.txt so we ship a license with this distribution. find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; - cd doc && find . -name . -o -prune ! -name common -exec rm -rf \{\} \; + cd src/doc && find . -name . -o -prune ! -name common -exec rm -rf \{\} \; if command -v rdfind > /dev/null; then \ @echo "Hardlinking identical files."; \ rdfind -makeresultsfile false -makehardlinks true .; \ From fa932819caf9cf56d2bba8dd41bf14ac23308908 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 13:53:37 +0100 Subject: [PATCH 029/284] Fix introspection it is easier to keep the full src/doc around. It's not worth the cherry-picking for just a few MB. --- Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Makefile b/Makefile index fffaac2f563..468302be943 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ micro_release: bdist-clean sagelib-clean @echo "Removing documentation. Inspection in IPython still works." rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" - @# We need src/sage/ and src/doc/common for introspection with "??" + @# We need src/sage/, src/doc/common, src/doc/en/introspect for introspection with "??" @# We need src/sage/bin/ for the scripts that invoke Sage @# We need sage, the script to start Sage @# We need local/, the dependencies and the built Sage library itself. @@ -103,7 +103,6 @@ micro_release: bdist-clean sagelib-clean @# We keep COPYING.txt so we ship a license with this distribution. find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; - cd src/doc && find . -name . -o -prune ! -name common -exec rm -rf \{\} \; if command -v rdfind > /dev/null; then \ @echo "Hardlinking identical files."; \ rdfind -makeresultsfile false -makehardlinks true .; \ From 5e387366d4078454ceb6dd144c4974c6b8c66706 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 13:58:19 +0100 Subject: [PATCH 030/284] Make tests requiring documentation optional as proposed by roed who seems to be the original author of some of this code. --- src/doc/common/conf.py | 2 +- src/sage/misc/sagedoc.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 97491ad8e1d..bb959ea04de 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -624,7 +624,7 @@ def call_intersphinx(app, env, node, contnode): sage: from sage.env import SAGE_DOC sage: thematic_index = os.path.join(SAGE_DOC, "html", "en", "thematic_tutorials", "index.html") - sage: for line in open(thematic_index).readlines(): + sage: for line in open(thematic_index).readlines(): # requires a built documentation, optional: doc ....: if "padics" in line: ....: sys.stdout.write(line)
  • Introduction to the -adics
  • diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index ebe1015364f..4649c116a43 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -20,7 +20,7 @@ sage: from sage.env import SAGE_DOC sage: docfilename = os.path.join(SAGE_DOC, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html') - sage: for line in open(docfilename): + sage: for line in open(docfilename): # requires a built documentation, optional: doc ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) numerical_approx(prec=None, digits=None, algorithm=None)... @@ -1331,7 +1331,7 @@ class _sage_doc: EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest + sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest, requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] 'Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring' @@ -1493,9 +1493,9 @@ def _open(self, name, testing=False): EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] + sage: browse_sage_doc._open("reference", testing=True)[0] # requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' - sage: browse_sage_doc._open("tutorial", testing=True)[1] + sage: browse_sage_doc._open("tutorial", testing=True)[1] # requires a built documentation, optional: doc '.../html/en/tutorial/index.html' """ url = self._base_url + os.path.join(name, "index.html") From fb22aa2984d5d2da6501dd422d0abf27b7ebc2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 14:02:54 +0100 Subject: [PATCH 031/284] Fix cython compilation errors during doctesting --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5da501c23a1..fc2664a13e9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -58,10 +58,10 @@ RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage RUN ln -s /usr/bin/sage /usr/bin/sagemath # Sage needs the fortran libraries at run-time because we do not build gfortran # with Sage but use the system's. -# We need gcc and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. +# We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get update -qq \ - && apt-get install -y --no-install-recommends gfortran gcc libstdc++-5-dev sudo \ + && apt-get install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ && apt-get clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. From 6aeebe66ce81c4122ba1bbee24a82d891dc70dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 4 Mar 2018 23:55:37 +0100 Subject: [PATCH 032/284] simplify environment variables --- .ci/build-docker.sh | 7 ------- .ci/pull-gitlab.sh | 3 --- .ci/push-gitlab.sh | 3 --- .ci/update-env.sh | 22 ++++++++++++++++++++++ .gitlab-ci.yml | 19 ++++++++++++++++--- 5 files changed, 38 insertions(+), 16 deletions(-) create mode 100644 .ci/update-env.sh diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 2a1d3f8f4a8..6ad77571efa 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -15,11 +15,6 @@ set -ex -[[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none -[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest - -. .ci/setup-make-parallelity.sh - # We speed up the build process by copying built artifacts from ARTIFACT_BASE # during docker build. See /docker/Dockerfile for more details. ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} @@ -40,9 +35,7 @@ docker_build --pull --target build-time-dependencies . docker_build --pull --tag make-all --target make-all . # Build the release image without build artifacts. -DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . # Build the developer image with the build artifacts intact. # Note: It's important to build the dev image last because it might be tagged as ARTIFACT_BASE. -DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG docker_build --target sagemath-dev --tag "$DOCKER_IMAGE_DEV" . diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh index 19be8c9ff7c..1aa316a625c 100755 --- a/.ci/pull-gitlab.sh +++ b/.ci/pull-gitlab.sh @@ -20,9 +20,6 @@ set -ex -[[ -z "$DOCKER_TAG" ]] && (echo "Can not pull untagged build."; exit 0) -[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest - # Pull the built images from the gitlab registry and give them the original # names they had after built. # Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh index 2e491e0a5b6..e4ceb30abcd 100755 --- a/.ci/push-gitlab.sh +++ b/.ci/push-gitlab.sh @@ -18,9 +18,6 @@ set -ex -[[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) -[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest - # Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it # automatically from the log output. docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY diff --git a/.ci/update-env.sh b/.ci/update-env.sh new file mode 100644 index 00000000000..9b4223fae3f --- /dev/null +++ b/.ci/update-env.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# This script gets called from CI to establish the name of the current docker +# tag to build from the name of the branch/tag provided by CI. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +[[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none +[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest + +DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG +DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4af063fd93a..7df98cc5710 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -38,6 +38,10 @@ before_script: - . .ci/protect-secrets.sh # Collect debug infos about the system we are running on - . .ci/describe-system.sh + # Set DOCKER_TAG according to the current branch/tag + - . .ci/update-env.sh + # Set MAKE and NTHREADS according to the machine we are running on + - . .ci/setup-make-parallelity.sh # We use docker-in-docker to build our docker images. It can be faster to # expose your outer docker daemon by mounting /var/run/docker.sock to @@ -110,13 +114,22 @@ test-jupyter: # Pushes the built images to Docker Hub if the Settings -> CI/CD -> Secret # variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. -dockerhub: +push-dockerhub: stage: release only: - branches - tags script: - - . .ci/pull-gitlab.sh sagemath-dev - - . .ci/push-dockerhub.sh sagemath-dev - . .ci/pull-gitlab.sh sagemath - . .ci/push-dockerhub.sh sagemath + +# Pushes the built dev images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +push-dockerhub-dev: + stage: release + only: + - master + - develop + script: + - . .ci/pull-gitlab.sh sagemath-dev + - . .ci/push-dockerhub.sh sagemath-dev From 4818f9bc54c2d71c12097f5b0ff10335718429e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 03:28:07 +0100 Subject: [PATCH 033/284] An attempt at incremental builds --- .gitignore | 5 +++++ docker/Dockerfile | 34 ++++++++++++++++++++++++++++++++-- src/.gitignore | 4 ---- 3 files changed, 37 insertions(+), 6 deletions(-) delete mode 100644 src/.gitignore diff --git a/.gitignore b/.gitignore index 498b92c73b9..c98ce30b813 100644 --- a/.gitignore +++ b/.gitignore @@ -87,3 +87,8 @@ Untitled*.ipynb ############################# gitlab-build-docker.log +/src/.cython_version +/src/build +/src/Makefile +/src/bin/sage-env-config + diff --git a/docker/Dockerfile b/docker/Dockerfile index fc2664a13e9..3273509bf7c 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -123,6 +123,7 @@ COPY --chown=sage:sage . sage-context # 3) Trash .git again to save some space. ARG SAGE_ROOT=/home/sage/sage WORKDIR $SAGE_ROOT +RUN find . -type f > $HOME/artifact-base.manifest RUN git fetch "$HOME/sage-context" HEAD \ && if [ -e docker/commit ]; then \ git reset `cat docker/commit` \ @@ -174,14 +175,43 @@ RUN make FROM make-all as make-fast-rebuild-clean RUN make fast-rebuild-clean +################################################################################ +# Depending on whether we built from source-clean or not, this image is either # +# identical to make-fast-rebuild-clean or contains a patch which can be used # +# to upgrade ARTIFACT_BASE to make-fast-rebuild-clean. # +################################################################################ +FROM make-fast-rebuild-clean as sagemath-dev-patch +ARG ARTIFACT_BASE=source-clean +ARG SAGE_ROOT=/home/sage/sage +# Build a patch containing of a tar file which contains all the modified files +# and a list of all modified files (added/updated/removed). +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + mkdir -p $HOME/patch \ + && find . -type f > $HOME/make-fast-rebuild-clean.manifest \ + && cat $HOME/make-fast-rebuild-clean.manifest $HOME/artifact-base.manifest | sort | uniq -u > $HOME/obsolete \ + && find . -type f -cnewer $HOME/artifact-base.manifest > $HOME/modified \ + && tar -cJf $HOME/patch/modified.tar.xz -T $HOME/modified \ + && cat $HOME/obsolete $HOME/modified | xz > $HOME/patch/modified.xz \ + && rm -rf $SAGE_ROOT \ + && mkdir -p $SAGE_ROOT \ + && mv $HOME/patch $SAGE_ROOT/; \ + fi + ################################################################################ # A releasable (relatively small, but still huge) image of this build with all # # the build artifacts intact so developers can make changes and rebuild # # quickly # ################################################################################ -FROM source-clean as sagemath-dev +FROM $ARTIFACT_BASE as sagemath-dev ARG SAGE_ROOT=/home/sage/sage -COPY --chown=sage:sage --from=make-fast-rebuild-clean $SAGE_ROOT $SAGE_ROOT +COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT +ARG ARTIFACT_BASE=source-clean +# Apply the patch from sagemath-dev-patch if we created one. +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + xzcat patch/modified.xz | xargs rm -rf \ + && tar -Jxf patch/modified.tar.xz \ + && rm -rf patch; \ + fi COPY ./docker/entrypoint-dev.sh /usr/local/bin/sage-entrypoint ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] CMD ["bash"] diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index e85aa7f89e4..00000000000 --- a/src/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.cython_version -/build -/Makefile -/bin/sage-env-config From c821ba052ef6b3c5bb625a1178663292e32f188b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 13:18:28 +0100 Subject: [PATCH 034/284] Fix build on CircleCI --- .circleci/config.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 2d38cef4204..8b13cbcd6ab 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,6 +34,10 @@ jobs: . .ci/protect-secrets.sh # Collect debug infos about the system we are running on . .ci/describe-system.sh + # Set DOCKER_TAG according to the current branch/tag + . .ci/update-env.sh + # Set MAKE and NTHREADS according to the machine we are running on + . .ci/setup-make-parallelity.sh # Build docker images export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} From 07602e5e137cdbe499733868f24628c2ee8449d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 14:07:03 +0100 Subject: [PATCH 035/284] fix CirceCI tag selection --- .ci/push-dockerhub.sh | 1 - .circleci/config.yml | 9 ++++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh index 5055fc056ac..1252b5f93ab 100755 --- a/.ci/push-dockerhub.sh +++ b/.ci/push-dockerhub.sh @@ -18,7 +18,6 @@ set -ex [[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) -[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest # Push the built images to the docker hub (and fail silently if # DOCKER_USER/SECRET_DOCKER_PASS have not been configured.) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8b13cbcd6ab..dec0d9763cd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -34,13 +34,14 @@ jobs: . .ci/protect-secrets.sh # Collect debug infos about the system we are running on . .ci/describe-system.sh - # Set DOCKER_TAG according to the current branch/tag - . .ci/update-env.sh # Set MAKE and NTHREADS according to the machine we are running on . .ci/setup-make-parallelity.sh - # Build docker images + # Set DOCKER_TAG according to the current branch/tag export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} + . .ci/update-env.sh + + # Select ARTIFACT_BASE depending on the current branch/tag case $DOCKER_TAG in "develop" | "latest") export ARTIFACT_BASE=source-clean @@ -52,6 +53,8 @@ jobs: export ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} ;; esac + + # Build docker images . .ci/build-docker.sh # Test that the images work From 3f088bfc5dce7211fd401566b4cc0907f8618369 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 15:31:00 +0100 Subject: [PATCH 036/284] enforce valid docker tags and make them the same on GitLab CI and CircleCI --- .ci/update-env.sh | 6 ++++++ .gitlab-ci.yml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.ci/update-env.sh b/.ci/update-env.sh index 9b4223fae3f..ab7db44f5ce 100644 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -15,6 +15,12 @@ set -ex +# From the docker documentation: "A tag name must be valid ASCII and may +# contain lowercase and uppercase letters, digits, underscores, periods and +# dashes. A tag name may not start with a period or a dash and may contain a +# maximum of 128 characters." +DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` + [[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none [[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7df98cc5710..9b2ef782023 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -27,7 +27,7 @@ stages: - release variables: - DOCKER_TAG: $CI_COMMIT_REF_SLUG + DOCKER_TAG: $CI_COMMIT_REF_NAME # Builds are very I/O intensive; make sure we have a fast file system. DOCKER_DRIVER: overlay2 DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop From 96a20eaa0cef2ce4a26867b86d16f1b813ec2f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 19:07:56 +0100 Subject: [PATCH 037/284] explain why "make" is not as fast as it could be --- .ci/test-dev.sh | 6 +++++- src/sage_setup/docbuild/ext/sage_autodoc.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index dcbf988abcb..4a3bac3ffbd 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -32,5 +32,9 @@ function timed_run { } timed_run 60 true # runs make build -# TODO: Can't we get this faster than that? +# Building the documentation is quite slow at the moment: +# Currently, this detects some dependencies as changed that have not changed. +# The parser in Sphinx fails to parse some .py files and adds the (more +# recently modified) .pyc files as dependencies instead. (Have a look the +# changeset that introduced this comment for more details.) timed_run $(( 1200/$NTHREADS )) make # runs make build and then make diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index 7590e229186..d5c45ba54a4 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -938,7 +938,12 @@ def generate(self, more_content=None, real_modname=None, self.analyzer.find_attr_docs() except PycodeError as err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) - # no source file -- e.g. for builtin and C modules + # A few things could have happened here: + # * there is no source file -- e.g. for builtin and C modules + # * the source file contains syntax that Sphinx can not parse, + # e.g., "print(1, end=' ')"; see + # https://github.com/sphinx-doc/sphinx/issues/1641, + # fixed in Sphinx 1.7. self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: From 3f9a0193369fe11dbe194a302e444b825a1de4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 19:47:40 +0100 Subject: [PATCH 038/284] minor documentation improvements --- docker/Dockerfile | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 3273509bf7c..d59b924b91d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -5,7 +5,7 @@ # created docker images, see the README.md please. # # # # This Dockerfile builds sagemath (for end-users) and sagemath-dev (for # -# developers) it consists of lots of intermediate targets, mostly to shrink # +# developers.) It consists of lots of intermediate targets, mostly to shrink # # the resulting images but also to make this hopefully easier to maintain. # # The aims of this Dockerfile are: # # (1) Make it build in reasonable time. # @@ -29,8 +29,8 @@ ################################################################################ # If you don't mind downloading a 2GB docker image from time to time, you # # could use this file for local Sage development. As of early 2018 each build # -# takes about five minutes but you don't have to through the sadly frequent # -# rebuilds of Sage the whole Sage distribution... # +# takes about five minutes but you don't have to go through the sadly frequent # +# rebuilds the whole Sage distribution... # # To build Sage, run this command from your sage/ directory: # # $ docker build --build-arg MAKE="make -j4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . # To run Sage: # @@ -123,6 +123,8 @@ COPY --chown=sage:sage . sage-context # 3) Trash .git again to save some space. ARG SAGE_ROOT=/home/sage/sage WORKDIR $SAGE_ROOT +# We create a list of all files present in the artifact-base (with a timestamp +# of now) so we can find out later which files were added/changed/removed. RUN find . -type f > $HOME/artifact-base.manifest RUN git fetch "$HOME/sage-context" HEAD \ && if [ -e docker/commit ]; then \ @@ -152,12 +154,14 @@ RUN patch -p1 < "$HOME"/sage-context.patch # Image with a built sage but without sage's documentation. # ################################################################################ FROM source-from-context as make-build -# Make sure that the results runs on most CPUs. +# Make sure that the result runs on most CPUs. ENV SAGE_FAT_BINARY yes # Just to be sure Sage doesn't try to build its own GCC (even though # it shouldn't with a recent GCC package from the system and with gfortran) ENV SAGE_INSTALL_GCC no -# Make everything in the build use multiple cores (this causes trouble for some packages outside Sage but it still seems to be they way Sage is doing this.) +# Make everything in the build use multiple cores (setting this variable causes +# trouble for some packages outside Sage but it still seems to be they way Sage +# is doing this.) ARG MAKE="make -j2" ENV MAKE $MAKE RUN make build @@ -177,7 +181,7 @@ RUN make fast-rebuild-clean ################################################################################ # Depending on whether we built from source-clean or not, this image is either # -# identical to make-fast-rebuild-clean or contains a patch which can be used # +# identical to make-fast-rebuild-clean or contains a "patch" which can be used # # to upgrade ARTIFACT_BASE to make-fast-rebuild-clean. # ################################################################################ FROM make-fast-rebuild-clean as sagemath-dev-patch From 2a57dd007a662286ec6de6c88770c67f72ed102b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 5 Mar 2018 21:04:45 +0100 Subject: [PATCH 039/284] update timings --- .circleci/config.yml | 7 +++---- .gitlab-ci.yml | 12 +++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dec0d9763cd..ba52319eb1f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -11,11 +11,10 @@ # and the frequent loading of docker images probably exceeds the cost of the # actual tests. -# As of early 2018, a run on CircleCI takes usually about 35 minutes. Most of +# As of early 2018, a run on CircleCI takes usually about 25 minutes. Most of # the time is spent pulling/pushing from/to Docker Hub and copying files -# locally during the docker build. We could probably save 5 minutes by not -# pushing the sagemath-dev image to Docker Hub. We could save another five -# minutes by not building and testing the sagemath-dev image in these cases. +# locally during the docker build. We could probably save five minutes by not +# building and testing the sagemath-dev image for most branches. version: 2 jobs: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9b2ef782023..39902bb09af 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,15 +8,13 @@ # autoscale runner. (Make sure to tag them as "2gb", otherwise the build step # is not going to run on them.) -# As of early 2018 a run on GitLab CI takes about an hour (with -# build-from-latest.) We could probably save 5 minutes by not pushing the -# sagemath-dev image for branches other than develop and master. We could -# save another 10 minutes by not building/pushing/testing that image in these -# cases. +# As of early 2018 a run on GitLab CI takes about 35 minutes (with +# build-from-latest.) We could probably save 10 minutes by not +# building/pushing/testing that image for branches other than master/develop. # Note that most of the time during CI is spent with pulling and pushing of # docker images and copying files locally as part of the docker build. -# At the moment there is no way of passing the docker images to the following -# stages without explicit pushing/pulling or similar: +# At the moment there is no reliable way of passing the docker images to the +# following stages without explicit pushing/pulling or similar: # https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 image: docker:latest From 28445f7107762401af831bf3e018fbbfdf6e6fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 8 Mar 2018 09:00:02 +0100 Subject: [PATCH 040/284] Fix comments in Makefile I still don't really understand what bdist is good for, so rather keep it uncommented. --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 468302be943..f0e01715f8c 100644 --- a/Makefile +++ b/Makefile @@ -68,7 +68,6 @@ misc-clean: rm -f build/make/Makefile build/make/Makefile-auto rm -f .BUILDSTART -# TODO: What is a "bdist"? A binary distribution? bdist-clean: clean $(MAKE) misc-clean @@ -87,6 +86,8 @@ bootstrap-clean: maintainer-clean: distclean bootstrap-clean rm -rf upstream +# Remove everything that is not necessary to run Sage and pass all its +# doctests. micro_release: bdist-clean sagelib-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 @@ -95,7 +96,9 @@ micro_release: bdist-clean sagelib-clean @echo "Removing documentation. Inspection in IPython still works." rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" - @# We need src/sage/, src/doc/common, src/doc/en/introspect for introspection with "??" + @# We need src/doc/common, src/doc/en/introspect for introspection with "??" + @# We keep src/sage for some doctests that it expect it to be there and + @# also because it does not add any weight with rdfind below. @# We need src/sage/bin/ for the scripts that invoke Sage @# We need sage, the script to start Sage @# We need local/, the dependencies and the built Sage library itself. From 2767adef1af044a105332a84e4f1370d94b50d48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 24 Mar 2018 00:39:42 +0200 Subject: [PATCH 041/284] Source only the scripts that set environment variables and run everything else with sh --- .circleci/config.yml | 12 ++++++------ .gitlab-ci.yml | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ba52319eb1f..3cdf69f7b27 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,7 +32,7 @@ jobs: # Therefore we roll our own to protect $SECRET_* variables. . .ci/protect-secrets.sh # Collect debug infos about the system we are running on - . .ci/describe-system.sh + sh .ci/describe-system.sh # Set MAKE and NTHREADS according to the machine we are running on . .ci/setup-make-parallelity.sh @@ -57,10 +57,10 @@ jobs: . .ci/build-docker.sh # Test that the images work - . .ci/test-dev.sh $DOCKER_IMAGE_DEV - . .ci/test-cli.sh $DOCKER_IMAGE_CLI - . .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + sh .ci/test-dev.sh $DOCKER_IMAGE_DEV + sh .ci/test-cli.sh $DOCKER_IMAGE_CLI + sh .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost # Push docker images to dockerhub if a dockerhub user has been configured - . .ci/push-dockerhub.sh sagemath-dev - . .ci/push-dockerhub.sh sagemath + sh .ci/push-dockerhub.sh sagemath-dev + sh .ci/push-dockerhub.sh sagemath diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 39902bb09af..241fed309a2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,7 +35,7 @@ before_script: # So we roll our own which protects all variables that start with SECRET_ - . .ci/protect-secrets.sh # Collect debug infos about the system we are running on - - . .ci/describe-system.sh + - sh .ci/describe-system.sh # Set DOCKER_TAG according to the current branch/tag - . .ci/update-env.sh # Set MAKE and NTHREADS according to the machine we are running on @@ -62,9 +62,9 @@ build-from-latest: &build expire_in: 1 month script: # The output of the build can get larger than gitlab.com's limit; only print the first 3MB. - - . .ci/build-docker.sh | tee gitlab-build-docker.log | head -c 3m - - . .ci/push-gitlab.sh sagemath-dev - - . .ci/push-gitlab.sh sagemath + - sh .ci/build-docker.sh | tee gitlab-build-docker.log | head -c 3m + - sh .ci/push-gitlab.sh sagemath-dev + - sh .ci/push-gitlab.sh sagemath tags: # We need enough disk space for the build to work. # The do(=digitalocean) instances on gitlab.com should be the 4GB instances @@ -90,13 +90,13 @@ test-dev: stage: test script: - . .ci/pull-gitlab.sh sagemath-dev - - . .ci/test-dev.sh "$DOCKER_IMAGE" + - sh .ci/test-dev.sh "$DOCKER_IMAGE" test-cli: stage: test script: - . .ci/pull-gitlab.sh sagemath - - . .ci/test-cli.sh "$DOCKER_IMAGE" + - sh .ci/test-cli.sh "$DOCKER_IMAGE" test-jupyter: stage: test @@ -108,7 +108,7 @@ test-jupyter: - apk update - apk add wget - . .ci/pull-gitlab.sh sagemath - - . .ci/test-jupyter.sh "$DOCKER_IMAGE" docker + - sh .ci/test-jupyter.sh "$DOCKER_IMAGE" docker # Pushes the built images to Docker Hub if the Settings -> CI/CD -> Secret # variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. @@ -119,7 +119,7 @@ push-dockerhub: - tags script: - . .ci/pull-gitlab.sh sagemath - - . .ci/push-dockerhub.sh sagemath + - sh .ci/push-dockerhub.sh sagemath # Pushes the built dev images to Docker Hub if the Settings -> CI/CD -> Secret # variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. @@ -130,4 +130,4 @@ push-dockerhub-dev: - develop script: - . .ci/pull-gitlab.sh sagemath-dev - - . .ci/push-dockerhub.sh sagemath-dev + - sh .ci/push-dockerhub.sh sagemath-dev From 834f66bd79845e4844c3f6a2b7d0b130d9ef2c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 24 Mar 2018 00:47:21 +0200 Subject: [PATCH 042/284] We need long lines in the README because dockerhub is not very smart --- docker/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/README.md b/docker/README.md index 0a48435d9f9..1d2ce27bb7f 100644 --- a/docker/README.md +++ b/docker/README.md @@ -50,3 +50,5 @@ Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup either con # License The whole Sage software distribution is licensed under the General Public License, version 3. More details can be found in our [COPYING.txt](https://github.com/sagemath/sage/blob/master/COPYING.txt) + +[//]: # (Please don't break long lines in this files as dockerhub then gets the formatting of this file wrong.) From 4ee63131bad067e53df4369dacb30461a58c97fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 24 Mar 2018 00:51:58 +0200 Subject: [PATCH 043/284] make usage easier to understand --- .ci/test-cli.sh | 2 +- .ci/test-dev.sh | 2 ++ .ci/test-jupyter.sh | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh index ab58474f74f..9048a9877cf 100755 --- a/.ci/test-cli.sh +++ b/.ci/test-cli.sh @@ -2,7 +2,7 @@ # This script gets called from CI to run minimal tests on the sagemath image. -# Usage: ./test-cli.sh sage-cli-image +# Usage: ./test-cli.sh IMAGE-NAME # **************************************************************************** # Copyright (C) 2018 Julian Rüth diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 4a3bac3ffbd..4ac3a4e7655 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -4,6 +4,8 @@ # This script expects a single argument, the full name of the docker image to # test. +# Usage: ./test-dev.sh IMAGE-NAME + # **************************************************************************** # Copyright (C) 2018 Julian Rüth # diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index b2360b72722..edcc0bcd44a 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -3,7 +3,7 @@ # This script gets called from CI to run minimal tests on the sagemath-jupyter # image. -# Usage: ./test-jupyter.sh sage-jupyter-image [host] +# Usage: ./test-jupyter.sh IMAGE-NAME [HOST] # **************************************************************************** # Copyright (C) 2018 Julian Rüth From 0892d35ba0c1a6dee2df8faa74cd1155e9964c4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Mar 2018 19:17:22 +0300 Subject: [PATCH 044/284] Explain the pros and cons of docker:dind --- .gitlab-ci.yml | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 241fed309a2..616e62dd383 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,10 +41,21 @@ before_script: # Set MAKE and NTHREADS according to the machine we are running on - . .ci/setup-make-parallelity.sh -# We use docker-in-docker to build our docker images. It can be faster to -# expose your outer docker daemon by mounting /var/run/docker.sock to -# /var/run/docker.sock and setting DOCKER_HOST in Settings -> CI/CD -> Secret -# variable to unix:///var/run/docker.sock +# We use docker-in-docker to build our docker images, i.e., we run a +# docker:dind "service" container and link to it from the container running the +# actual scripts below. +# Our scripts automatically connect to this service (unless you override it by +# setting DOCKER_HOST.) For example, each RUN statement in the Dockerfile +# spawns a docker container inside the docker:dind container to perform the RUN +# command there. +# It can be faster to expose your outer docker daemon by mounting +# /var/run/docker.sock to /var/run/docker.sock and setting DOCKER_HOST in +# Settings -> CI/CD -> Secret variable to unix:///var/run/docker.sock. (The +# speedup is mostly due to sharing layers of intermediate images.) However, +# this is only possible if you provision your own runners. Shared gitlab +# runners, do not bind mount /var/run/docker.sock. Also, docker:dind provides +# 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:dind From 290bec4ccc73a977fdff9d7539092fa2dc211d83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Mar 2018 19:43:00 +0300 Subject: [PATCH 045/284] clarify comment --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index d59b924b91d..cc157c7f409 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -114,7 +114,8 @@ COPY --chown=sage:sage . sage-context # make sure that we only change the mtimes of a minimal number of files. # 1) Restore the git checkout ARTIFACT_BASE was built from, recorded in # docker/commit. (Or go directly to FETCH_HEAD if there is no history to -# restore.) +# restore, i.e., set ARTIFACT_BASE=source-clean if you want to build from +# scratch.) # 2) Merge in FETCH_HEAD but only if it is a fast-forward, i.e., if it is an # ancestor of the commit restored in 1. If we would not do that we would get # a new commit hash in docker/commit that is not known outside of this build From c3eeb65d4adb6f77ecdb76f5c016c70973c935ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Mar 2018 19:47:38 +0300 Subject: [PATCH 046/284] do not source scripts without side effects --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3cdf69f7b27..f2c3cc16f3a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,7 +54,7 @@ jobs: esac # Build docker images - . .ci/build-docker.sh + sh .ci/build-docker.sh # Test that the images work sh .ci/test-dev.sh $DOCKER_IMAGE_DEV From bf56bcae1c28545666ed7ed489c8bcba3e3422ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 28 Mar 2018 20:13:05 +0300 Subject: [PATCH 047/284] howto provision your own runners --- .gitlab-ci.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 616e62dd383..048dae29e99 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,9 +4,11 @@ # much and the full build might the set time limit in GitLab. You can increase # that limit in Settings → CI/CD. # You can also provision your own private more powerful runner in the same -# place; or set up your favourite cloud service to provide an on-demand -# autoscale runner. (Make sure to tag them as "2gb", otherwise the build step -# is not going to run on them.) +# place +# https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; +# or set up your favourite cloud service to provide an on-demand autoscale +# runner. (Make sure to tag them as "do", otherwise the build step is not going +# to run on them.) # As of early 2018 a run on GitLab CI takes about 35 minutes (with # build-from-latest.) We could probably save 10 minutes by not From fa4599a6c6849e5a2ac2a1f738eed437843454fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 00:42:06 +0300 Subject: [PATCH 048/284] Rename commit to .commit so it does no show up in ls as we usually don't care about it. --- docker/.gitignore | 2 +- docker/Dockerfile | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docker/.gitignore b/docker/.gitignore index a38152cb5ee..579da245b87 100644 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -1,2 +1,2 @@ # Stores the commit that was used to create a sagemath-dev image -commit +.commit diff --git a/docker/Dockerfile b/docker/Dockerfile index cc157c7f409..1c28a0572c9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -113,13 +113,13 @@ COPY --chown=sage:sage . sage-context # This is a bit complicated because our local .git/ is empty and we want to # make sure that we only change the mtimes of a minimal number of files. # 1) Restore the git checkout ARTIFACT_BASE was built from, recorded in -# docker/commit. (Or go directly to FETCH_HEAD if there is no history to +# docker/.commit. (Or go directly to FETCH_HEAD if there is no history to # restore, i.e., set ARTIFACT_BASE=source-clean if you want to build from # scratch.) # 2) Merge in FETCH_HEAD but only if it is a fast-forward, i.e., if it is an # ancestor of the commit restored in 1. If we would not do that we would get -# a new commit hash in docker/commit that is not known outside of this build -# run. Since docker/commit was in the history of FETCH_HEAD this should +# a new commit hash in docker/.commit that is not known outside of this build +# run. Since docker/.commit was in the history of FETCH_HEAD this should # automatically be a fast-forward. # 3) Trash .git again to save some space. ARG SAGE_ROOT=/home/sage/sage @@ -128,16 +128,16 @@ WORKDIR $SAGE_ROOT # of now) so we can find out later which files were added/changed/removed. RUN find . -type f > $HOME/artifact-base.manifest RUN git fetch "$HOME/sage-context" HEAD \ - && if [ -e docker/commit ]; then \ - git reset `cat docker/commit` \ - || ( echo "Could not find commit `cat docker/commit` in your local Git history. Please merge in the latest built develop branch to fix this: git fetch trac && git merge `cat docker/commit`." && exit 1 ) \ + && if [ -e docker/.commit ]; then \ + git reset `cat docker/.commit` \ + || ( echo "Could not find commit `cat docker/.commit` in your local Git history. Please merge in the latest built develop branch to fix this: git fetch trac && git merge `cat docker/.commit`." && exit 1 ) \ else \ - echo "You are building from $ARTIFACT_BASE which has no docker/commit file. That's a bug unless you are building from source-clean or something similar." \ + echo "You are building from $ARTIFACT_BASE which has no docker/.commit file. That's a bug unless you are building from source-clean or something similar." \ && git reset FETCH_HEAD \ && git checkout -f FETCH_HEAD; \ fi \ && git merge --ff-only FETCH_HEAD \ - && git log -1 --format=%H > docker/commit \ + && git log -1 --format=%H > docker/.commit \ && rm -rf .git # Copy over all the untracked/staged/unstaged changes from sage-context. This # is relevant for non-CI invocations of this Dockerfile. From 71d9ce7391c6550ba0653bd5421a3745e28417d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 01:09:00 +0300 Subject: [PATCH 049/284] export variables in sourced scripts otherwise they do not get passed on the the subshells (before we sourced everything, so exported variables were essentially the same to non-exported ones.) --- .ci/pull-gitlab.sh | 2 +- .ci/setup-make-parallelity.sh | 3 +++ .ci/update-env.sh | 10 +++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh index 1aa316a625c..c8235e64068 100755 --- a/.ci/pull-gitlab.sh +++ b/.ci/pull-gitlab.sh @@ -26,5 +26,5 @@ set -ex # automatically from the log output. docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY docker pull $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG -DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" +export DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" docker tag $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG $DOCKER_IMAGE diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index a69bdbc37f6..761578a5b07 100644 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -20,8 +20,11 @@ set -ex # too high can lead to RAM being insufficient, so it's best to set this # variable manually in your CI configuration. [[ -z "$NTHREADS" ]] && NTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` || true +export NTHREADS="$NTHREADS" # Set -j and -l for make (though -l is probably stripped by Sage) [[ -z "$MAKEOPTS" ]] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true +export MAKEOPTS="$MAKEOPTS" # Not all parts of Sage seem to honor MAKEOPTS, so the current way of telling # the system which concurrency to use, seems to be setting $MAKE. [[ -z "$MAKE" ]] && MAKE="make $MAKEOPTS" || true +export MAKE="$MAKE" diff --git a/.ci/update-env.sh b/.ci/update-env.sh index ab7db44f5ce..ee6a2110a56 100644 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -19,10 +19,10 @@ set -ex # contain lowercase and uppercase letters, digits, underscores, periods and # dashes. A tag name may not start with a period or a dash and may contain a # maximum of 128 characters." -DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` +export DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` -[[ -z "$DOCKER_TAG" ]] && DOCKER_TAG=none -[[ "$DOCKER_TAG" = "master" ]] && DOCKER_TAG=latest +[[ -z "$DOCKER_TAG" ]] && export DOCKER_TAG=none +[[ "$DOCKER_TAG" = "master" ]] && export DOCKER_TAG=latest -DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG -DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG +export DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG +export DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG From 2493bb3e942ccfb4fa3fa0b11439062125d54445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 01:15:39 +0300 Subject: [PATCH 050/284] Fixup for fa4599a6c6849e5a2ac2a1f738eed437843454fa forgot to rename commit to .commit in the docker README --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index 1d2ce27bb7f..27fce3d8e18 100644 --- a/docker/README.md +++ b/docker/README.md @@ -31,7 +31,7 @@ There are several flavours of this image. ``` This triggers a rebuild and drops you in a shell afterwards. Note that the git repository has been emptied to save space. If you want to use git, fetch from your git repository with `git fetch trac` and go to the commit that was used to create this image with ``` - git reset $(cat docker/commit) + git reset $(cat docker/.commit) ``` # How to build your own SageMath images From b3c6d14e3c61efa0868e02bd481340aa408905c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 01:19:44 +0300 Subject: [PATCH 051/284] Make docker-build.sh POSIX compliant --- .ci/build-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 6ad77571efa..ee42df6830f 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -22,7 +22,7 @@ ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} # Seed our cache with $ARTIFACT_BASE if it exists docker pull $ARTIFACT_BASE || true -function docker_build { +docker_build() { time docker build -f docker/Dockerfile --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } From 183ddd40b2ebd9f53d242a112325bda119b8298c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 01:42:11 +0300 Subject: [PATCH 052/284] Make jupyter invocation easier to remember --- .ci/test-jupyter.sh | 2 +- docker/README.md | 2 +- docker/entrypoint.sh | 9 ++++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index edcc0bcd44a..a569013c99a 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -17,7 +17,7 @@ set -ex -docker run --name sage-jupyter -p 8888:8888 -d "$1" "sage -n jupyter --no-browser --ip='*' --port=8888" +docker run --name sage-jupyter -p 8888:8888 -d "$1" jupyter echo "Checking that the Jupyter notebook is running…" sleep 10 # giving the server some time to start docker logs sage-jupyter diff --git a/docker/README.md b/docker/README.md index 27fce3d8e18..314cdc91d8a 100644 --- a/docker/README.md +++ b/docker/README.md @@ -23,7 +23,7 @@ There are several flavours of this image. ``` You can start a graphical [Jupyter Notebook](https://jupyter.org) at http://localhost:8888 instead. To use the notebook, follow the instructions printed when you run: ``` - docker run -p8888:8888 sagemath/sagemath:latest "sage -n jupyter --no-browser --ip='*' --port=8888" + docker run -p8888:8888 sagemath/sagemath:latest jupyter ``` * [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev:develop.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 692b5c83390..52a67592834 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,2 +1,9 @@ #!/bin/bash -exec sage -sh -c "$*" +if [ x"$1" = jupyter ]; then + # If "jupyter" is given as a first argument, we start a jupyter notebook + # with reasonable default parameters for running it inside a container. + shift + exec sage -n jupyter --no-browser --ip='*' --port=8888 "$@" +else + exec sage -sh -c "$*" +fi From 59952f343884585bccaba2e2cb27e8393d3df985 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 19:50:21 +0300 Subject: [PATCH 053/284] Drop pipefail as it is not supported by CircleCI's "sh" and we actually do not need it here. --- .ci/test-cli.sh | 2 +- .ci/test-dev.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh index 9048a9877cf..fb0a4cb101a 100755 --- a/.ci/test-cli.sh +++ b/.ci/test-cli.sh @@ -14,7 +14,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** -set -exo pipefail +set -ex echo "Checking that Sage starts and can calculate 1+1…" # Calculate 1+1 (remove startup messages and leading & trailing whitespace) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 4ac3a4e7655..daf2c49794a 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -16,7 +16,7 @@ # http://www.gnu.org/licenses/ # **************************************************************************** -set -exo pipefail +set -ex IMAGE="$1" From a727ec548c321df24a6ac9bd789f700a8d31e2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 3 Apr 2018 19:54:30 +0300 Subject: [PATCH 054/284] Compilation sometimes takes a bit longer and the error message was a bit confusing --- .ci/test-dev.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index daf2c49794a..a5d852e7350 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -29,11 +29,14 @@ function timed_run { docker run -e MAKE="$MAKE" "$IMAGE" "$2" END=`date +%s` TOTAL=$((END-START)) - echo "Checking that \"$2\" was fast…" + echo "Checking whether running \"$2\" was fast…" [[ $TOTAL -lt $1 ]] } -timed_run 60 true # runs make build +# Most setup should be done in well under 120 seconds but some CI machines tend +# to be really slow so we better give them some space here. Better miss a +# regression at first than create a lot of noise. +timed_run 120 true # runs make build # Building the documentation is quite slow at the moment: # Currently, this detects some dependencies as changed that have not changed. # The parser in Sphinx fails to parse some .py files and adds the (more From 2e74da9f1e408d8f569151198b4c739b2208749f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Apr 2018 00:01:06 +0300 Subject: [PATCH 055/284] POSIX complaint tests so we can run test-dev.sh with a POSIX shell --- .ci/setup-make-parallelity.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 761578a5b07..655bd86b762 100644 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -19,12 +19,12 @@ set -ex # provision fewer vCPUs than shown in /proc/cpuinfo. Also, setting this value # too high can lead to RAM being insufficient, so it's best to set this # variable manually in your CI configuration. -[[ -z "$NTHREADS" ]] && NTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` || true +[ -z "$NTHREADS" ] && NTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` || true export NTHREADS="$NTHREADS" # Set -j and -l for make (though -l is probably stripped by Sage) -[[ -z "$MAKEOPTS" ]] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true +[ -z "$MAKEOPTS" ] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true export MAKEOPTS="$MAKEOPTS" # Not all parts of Sage seem to honor MAKEOPTS, so the current way of telling # the system which concurrency to use, seems to be setting $MAKE. -[[ -z "$MAKE" ]] && MAKE="make $MAKEOPTS" || true +[ -z "$MAKE" ] && MAKE="make $MAKEOPTS" || true export MAKE="$MAKE" From 410c5465a79f7204d1fd0e98d10a5f8879e627e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Apr 2018 00:07:25 +0300 Subject: [PATCH 056/284] use sage-jupyter instead of jupyter jupyter is actually a command in the PATH so we should not hide it --- .ci/test-jupyter.sh | 2 +- docker/entrypoint.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index a569013c99a..9df157b763b 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -17,7 +17,7 @@ set -ex -docker run --name sage-jupyter -p 8888:8888 -d "$1" jupyter +docker run --name sage-jupyter -p 8888:8888 -d "$1" sage-jupyter echo "Checking that the Jupyter notebook is running…" sleep 10 # giving the server some time to start docker logs sage-jupyter diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 52a67592834..bec95735ec8 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -if [ x"$1" = jupyter ]; then - # If "jupyter" is given as a first argument, we start a jupyter notebook +if [ x"$1" = sage-jupyter ]; then + # If "sage-jupyter" is given as a first argument, we start a jupyter notebook # with reasonable default parameters for running it inside a container. shift exec sage -n jupyter --no-browser --ip='*' --port=8888 "$@" From 93c98e0f1c4724a451b04c8a7230661b3b464911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 7 Apr 2018 00:43:12 +0300 Subject: [PATCH 057/284] POSIX compliant function call in sh --- .ci/test-dev.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index a5d852e7350..1c9519d778d 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -24,7 +24,7 @@ IMAGE="$1" # Usage: timed_run limit args # Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. -function timed_run { +timed_run() { START=`date +%s` docker run -e MAKE="$MAKE" "$IMAGE" "$2" END=`date +%s` From 474b181411500f59c21d1d58223a4edf510b4fc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 7 Apr 2018 01:13:34 +0300 Subject: [PATCH 058/284] fix typo --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 048dae29e99..9540316dceb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -115,7 +115,7 @@ test-jupyter: stage: test script: # Force usage of docker-in-docker (and don't start docker on a bind-mounted - # /var/run/docker.sock set through a proivate GitLab CI variable) so that + # /var/run/docker.sock set through a private GitLab CI variable) so that # the -p flag to docker run works as expected. - export DOCKER_HOST='tcp://docker:2375' - apk update From 4e714a2cbc26e623979912126fd1c6d2b897ff0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 7 Apr 2018 01:18:09 +0300 Subject: [PATCH 059/284] Add checks for binder binder needs jupyter to be on the path. --- .ci/test-cli.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh index fb0a4cb101a..dfcedfc5ec0 100755 --- a/.ci/test-cli.sh +++ b/.ci/test-cli.sh @@ -20,3 +20,11 @@ echo "Checking that Sage starts and can calculate 1+1…" # Calculate 1+1 (remove startup messages and leading & trailing whitespace) TWO=`docker run "$1" sage -c "'print(1+1)'" | tail -1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'` [[ "x$TWO" = "x2" ]] + +echo "Checking that some binaries that should be distributed with Sage are on the PATH…" +# We could also run minimal tests on these but we don't yet. +# Check that Singular and GAP are present +docker run "$1" which Singular +docker run "$1" which gap +# Check that jupyter is present (for binder) +docker run "$1" which jupyter From 84d541c289760839b99c5a471607058f3e1cb518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 7 Apr 2018 01:24:39 +0300 Subject: [PATCH 060/284] fix README the command is now called sage-jupyter not just jupyter as that one already exists --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index 314cdc91d8a..0028e8b9798 100644 --- a/docker/README.md +++ b/docker/README.md @@ -23,7 +23,7 @@ There are several flavours of this image. ``` You can start a graphical [Jupyter Notebook](https://jupyter.org) at http://localhost:8888 instead. To use the notebook, follow the instructions printed when you run: ``` - docker run -p8888:8888 sagemath/sagemath:latest jupyter + docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter ``` * [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev:develop.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` From 73b963b3f1013ed90aff9ca9776d515a19748b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 12 Apr 2018 19:49:18 +0200 Subject: [PATCH 061/284] POSIX compliant test, [ instead of [[. --- .ci/test-cli.sh | 2 +- .ci/test-dev.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh index dfcedfc5ec0..0ad6a8f3d0a 100755 --- a/.ci/test-cli.sh +++ b/.ci/test-cli.sh @@ -19,7 +19,7 @@ set -ex echo "Checking that Sage starts and can calculate 1+1…" # Calculate 1+1 (remove startup messages and leading & trailing whitespace) TWO=`docker run "$1" sage -c "'print(1+1)'" | tail -1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'` -[[ "x$TWO" = "x2" ]] +[ "x$TWO" = "x2" ] echo "Checking that some binaries that should be distributed with Sage are on the PATH…" # We could also run minimal tests on these but we don't yet. diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 1c9519d778d..3dde76bbbb0 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -30,7 +30,7 @@ timed_run() { END=`date +%s` TOTAL=$((END-START)) echo "Checking whether running \"$2\" was fast…" - [[ $TOTAL -lt $1 ]] + [ "$TOTAL" -lt "$1" ] } # Most setup should be done in well under 120 seconds but some CI machines tend From 0d5dd3d9f854ad6053ec527caacd737fe0e78023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 12 Apr 2018 19:57:25 +0200 Subject: [PATCH 062/284] Fix sage-jupyter command in entrypoint --- docker/entrypoint.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index bec95735ec8..bc841382eaf 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,5 +1,5 @@ #!/bin/bash -if [ x"$1" = sage-jupyter ]; then +if [ x"$1" = x"sage-jupyter" ]; then # If "sage-jupyter" is given as a first argument, we start a jupyter notebook # with reasonable default parameters for running it inside a container. shift From 7cb69d7b5a8bfa13e78a3c81ec7f864385505c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 12 Apr 2018 20:48:36 +0200 Subject: [PATCH 063/284] Mention that timings may vary --- .gitlab-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9540316dceb..46d209060cc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,8 +1,8 @@ # This file configures automatic builds of Sage on [GitLab](https://gitlab.com). # To make the build time not too excessive, we seed the build cache with # sagemath/sagemath-dev:develop. When basic SPKGs changed, this does not help -# much and the full build might the set time limit in GitLab. You can increase -# that limit in Settings → CI/CD. +# much and the full build might exceed the set time limit in GitLab. You can +# increase that limit in Settings → CI/CD. # You can also provision your own private more powerful runner in the same # place # https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; @@ -18,6 +18,10 @@ # At the moment there is no reliable way of passing the docker images to the # following stages without explicit pushing/pulling or similar: # https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 +# The timings mentioned above are typical values. The shared runners provided +# on gitlab.com are sometimes by a factor of two slower. It's unclear what is +# the source of this slowdown. There is nothing different in the logs, so it's +# probably just an overcommittment of virtual machines on hardware. image: docker:latest From 6aade395c5bb9cf92118c317942a7398f22ad08b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:12:28 +0200 Subject: [PATCH 064/284] Silence docker pull errors are still printed (to stderr.) Note that as of early 2018 there is no -q switch for docker pull yet. --- .ci/build-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index ee42df6830f..afd898e6fb2 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -20,7 +20,7 @@ set -ex ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} # Seed our cache with $ARTIFACT_BASE if it exists -docker pull $ARTIFACT_BASE || true +docker pull "$ARTIFACT_BASE" > /dev/null || true docker_build() { time docker build -f docker/Dockerfile --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ From 93a7ba6f9f07e6076bed9002a58b62a1f3c3beec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:13:28 +0200 Subject: [PATCH 065/284] Silence apt-get we only care about errors here --- docker/Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1c28a0572c9..2e3383a1ebe 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -60,9 +60,9 @@ RUN ln -s /usr/bin/sage /usr/bin/sagemath # with Sage but use the system's. # We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. -RUN apt-get update -qq \ - && apt-get install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ - && apt-get clean \ +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ + && apt-get -qq clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. # We also want this user at runtime as some commands in sage know about the user that was used during build. @@ -81,9 +81,9 @@ WORKDIR $HOME ################################################################################ FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git -RUN sudo apt-get update -qq \ - && sudo apt-get install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ - && sudo apt-get clean \ +RUN sudo apt-get -qq update \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ + && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* ################################################################################ From 158af15bf74e2604c6d3606867f0b6143bfe58ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:13:46 +0200 Subject: [PATCH 066/284] Silence setup.py The "cythonizing" messages, compiler/cython warnings are still shown. This mostly removes the messages that about byte-compilations of python files and copying of python files to site-packages. --- src/Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.in b/src/Makefile.in index 81a28b3758e..0233601cbbc 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -37,7 +37,7 @@ sage: SAGE_DOC_SRC=/doesnotexist \ SAGE_BUILD_DIR=/doesnotexist \ SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ - && sage-python23 -u setup.py --no-user-cfg build install + && sage-python23 -u setup.py --quiet --no-user-cfg build install if [ "$$UNAME" = "CYGWIN" ]; then \ sage-rebase.sh "$$SAGE_LOCAL" 2>/dev/null; \ fi From 3c78351fdcbfe93c2c353d0aef23bc2e976d7e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:14:45 +0200 Subject: [PATCH 067/284] Ignore much more useless chatter in sphinx build this currently makes CI builds exceed their output limit. I don't think we care about these "progress bars". Errors and warnings are still printed (and definitely detected.) --- src/sage_setup/docbuild/sphinxbuild.py | 52 ++++++++++++++++++-------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index 40aac600b36..a5b65baca0c 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -1,7 +1,7 @@ r""" This is Sage's version of the sphinx-build script -We redirect stdout to our own logger, and remove some unwanted chatter. +We redirect stdout and stderr to our own logger, and remove some unwanted chatter. """ # **************************************************************************** # Copyright (C) 2013-2014 Volker Braun @@ -31,8 +31,8 @@ def term_width_line(text): class SageSphinxLogger(object): r""" - This implements the file object interface to serve as sys.stdout - replacement. + This implements the file object interface to serve as + ``sys.stdout``/``sys.stderr`` replacement. """ ansi_color = re.compile(r'\x1b\[[0-9;]*m') ansi_reset = re.compile(r'\x1b\[39;49;00m') @@ -59,25 +59,41 @@ def __init__(self, stream, prefix): self._error = None def _init_chatter(self): - # useless_chatter: regular expressions to be filtered from - # Sphinx output. - self.useless_chatter = ( + # We drop any messages from the output that match these regular + # expressions. These just bloat the output and do not contain any + # information that we care about. + self._useless_chatter = ( re.compile('^$'), re.compile('^Running Sphinx v'), re.compile('^loading intersphinx inventory from '), + re.compile('^loading pickled environment... done'), + re.compile('^loading cross citations... done \([0-9]* citations\).'), re.compile('^Compiling a sub-document'), re.compile('^updating environment: 0 added, 0 changed, 0 removed'), re.compile('^looking for now-outdated files... none found'), re.compile('^building \[.*\]: targets for 0 source files that are out of date'), - re.compile('^loading pickled environment... done'), - re.compile('^loading cross citations... done \([0-9]* citations\).'), + re.compile('^building \[.*\]: targets for 0 po files that are out of date'), + re.compile('^building \[.*\]: targets for 0 mo files that are out of date'), + re.copmile('^pickling environment... done'), + re.copmile('^dumping object inventory... done'), + # We still have "Build finished." + re.compile('^build succeeded.'), + re.copmile('^checking consistency... done'), + re.copmile('^preparing documents... done'), + re.compile('^writing output... \[.*\] '), + re.compile('^reading sources... \[.*\] '), + re.compile('language "hu" not supported'), + ) + + # We fail whenever a line starts with "WARNING:", however, we ignore + # these warnings, as they are not relevant. + self._ignored_warnings = ( re.compile('WARNING: favicon file \'favicon.ico\' does not exist'), re.compile('WARNING: html_static_path entry .* does not exist'), re.compile('WARNING: while setting up extension'), re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), - re.compile('language "hu" not supported'), ) # replacements: pairs of regular expressions and their replacements, @@ -88,7 +104,7 @@ def _init_chatter(self): if 'inventory' in sys.argv: # When building the inventory, ignore warnings about missing # citations and the search index. - self.useless_chatter += ( + self._useless_chatter += ( re.compile('WARNING: citation not found:'), re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') ) @@ -119,7 +135,7 @@ def _filter_out(self, line): # swallow non-errors after an error occurred return True line = re.sub(self.ansi_color, '', line) - for regex in self.useless_chatter: + for regex in self._useless_chatter: if regex.search(line) is not None: return True return False @@ -143,10 +159,14 @@ def _check_errors(self, line): """ if self._error: return # we already have found an error - for regex in self._error_patterns: - if regex.search(line) is not None: - self._error = line - return + for error in self._error_patterns: + if error.search(line) is not None: + for ignored in self._ignored_warnings: + if ignored.search(line) is not None: + break + else: + self._error = line + return def _log_line(self, line): r""" @@ -182,6 +202,7 @@ def _log_line(self, line): [#25160 ] Exception: artificial exception """ + self._check_errors(line) if self._filter_out(line): return for (old, new) in self.replacements: @@ -191,7 +212,6 @@ def _log_line(self, line): line = self.ansi_color.sub('', line) self._stream.write(line) self._stream.flush() - self._check_errors(line) def raise_errors(self): r""" From 7d5f3898f3bff423efe3c3e97997de6af5ccd573 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:44:01 +0200 Subject: [PATCH 068/284] Smarter truncation of output this feels a bit overengineered but just using head makes the output stop without an additional message to the user. --- .ci/head-tail.sh | 45 +++++++++++++++++++++++++++++++++++++++++++++ .gitlab-ci.yml | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100755 .ci/head-tail.sh diff --git a/.ci/head-tail.sh b/.ci/head-tail.sh new file mode 100755 index 00000000000..8ecf9f879f0 --- /dev/null +++ b/.ci/head-tail.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +OFFSET=80 +# This script reads from stdin and prints to stdout as long as a the output +# does not exceed a certain number of bytes. When reading an EOF it prints the +# last $OFFSET lines if they have not been printed normally already. +# This script expects one argument, the number of bytes. + +# Heavily inspired by a simlar strategy in make, https://stackoverflow.com/a/44849696/812379. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +stdbuf -i0 -o0 -e0 awk -v limit=$1 -v firstMissingNR=-1 -v offset=$OFFSET -v bytes=0 \ +'{ + if (bytes < limit) { + # this probably gets multi-byte characters wrong, but that probably does + # not matter for our purposes. (We add 1 for a UNIX newline.) + bytes += length($0) + 1; + print; + } else { + if (firstMissingNR == -1){ + print "[…output truncated…]"; + firstMissingNR = NR; + } + a[NR] = $0; + delete a[NR-offset]; + printf "." > "/dev/stderr" + } +} +END { + if (firstMissingNR != -1) { + print "" > "/dev/stderr"; + for(i = NR-offset+1 > firstMissingNR ? NR-offset-1 : firstMissingNR; i<=NR ; i++){ print a[i]; } + } +} +' + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 46d209060cc..386a0e2ba81 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -79,7 +79,7 @@ build-from-latest: &build expire_in: 1 month script: # The output of the build can get larger than gitlab.com's limit; only print the first 3MB. - - sh .ci/build-docker.sh | tee gitlab-build-docker.log | head -c 3m + - sh .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 - sh .ci/push-gitlab.sh sagemath-dev - sh .ci/push-gitlab.sh sagemath tags: From 321658ed55349977819da30e83cbb1850aa05536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 21:51:47 +0200 Subject: [PATCH 069/284] head-tail.sh needs stdbuf from coreutils --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 386a0e2ba81..414810105c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,7 +78,8 @@ build-from-latest: &build - gitlab-build-docker.log expire_in: 1 month script: - # The output of the build can get larger than gitlab.com's limit; only print the first 3MB. + - apk update && apk add coreutils + # The output of the build can get larger than gitlab.com's limit; only print the first 3MB (and the last 80 lines.) - sh .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 - sh .ci/push-gitlab.sh sagemath-dev - sh .ci/push-gitlab.sh sagemath From 7735a2c0b60f71731c550544acb295300aee52bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 22:05:32 +0200 Subject: [PATCH 070/284] POSIX compliant push-dockerhub.sh script --- .ci/push-dockerhub.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh index 1252b5f93ab..c2c46062ffa 100755 --- a/.ci/push-dockerhub.sh +++ b/.ci/push-dockerhub.sh @@ -17,11 +17,11 @@ set -ex -[[ -z "$DOCKER_TAG" ]] && (echo "Can not push untagged build."; exit 0) +[ -z "$DOCKER_TAG" ] && (echo "Can not push untagged build."; exit 0) # Push the built images to the docker hub (and fail silently if # DOCKER_USER/SECRET_DOCKER_PASS have not been configured.) -if [[ -z "$DOCKER_USER" || -z "$SECRET_DOCKER_PASS" ]]; then +if [ -z "$DOCKER_USER" -o -z "$SECRET_DOCKER_PASS" ]; then echo "DOCKER_USER/SECRET_DOCKER_PASS variables have not been configured in your Continuous Integration setup. Not pushing built images to Docker Hub." else cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin From 3112236a66637c4ad27f69294c35277aca904968 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 22:07:15 +0200 Subject: [PATCH 071/284] Try to get rid of the explicit sh invocation --- .ci/describe-system.sh | 0 .ci/setup-make-parallelity.sh | 0 .ci/update-env.sh | 0 .circleci/config.yml | 14 +++++++------- .gitlab-ci.yml | 8 ++++---- 5 files changed, 11 insertions(+), 11 deletions(-) mode change 100644 => 100755 .ci/describe-system.sh mode change 100644 => 100755 .ci/setup-make-parallelity.sh mode change 100644 => 100755 .ci/update-env.sh diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh old mode 100644 new mode 100755 diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh old mode 100644 new mode 100755 diff --git a/.ci/update-env.sh b/.ci/update-env.sh old mode 100644 new mode 100755 diff --git a/.circleci/config.yml b/.circleci/config.yml index f2c3cc16f3a..0404cc6b8bb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,7 +32,7 @@ jobs: # Therefore we roll our own to protect $SECRET_* variables. . .ci/protect-secrets.sh # Collect debug infos about the system we are running on - sh .ci/describe-system.sh + .ci/describe-system.sh # Set MAKE and NTHREADS according to the machine we are running on . .ci/setup-make-parallelity.sh @@ -54,13 +54,13 @@ jobs: esac # Build docker images - sh .ci/build-docker.sh + .ci/build-docker.sh # Test that the images work - sh .ci/test-dev.sh $DOCKER_IMAGE_DEV - sh .ci/test-cli.sh $DOCKER_IMAGE_CLI - sh .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + .ci/test-dev.sh $DOCKER_IMAGE_DEV + .ci/test-cli.sh $DOCKER_IMAGE_CLI + .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost # Push docker images to dockerhub if a dockerhub user has been configured - sh .ci/push-dockerhub.sh sagemath-dev - sh .ci/push-dockerhub.sh sagemath + .ci/push-dockerhub.sh sagemath-dev + .ci/push-dockerhub.sh sagemath diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 414810105c3..8b9760b08c8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -41,7 +41,7 @@ before_script: # So we roll our own which protects all variables that start with SECRET_ - . .ci/protect-secrets.sh # Collect debug infos about the system we are running on - - sh .ci/describe-system.sh + - .ci/describe-system.sh # Set DOCKER_TAG according to the current branch/tag - . .ci/update-env.sh # Set MAKE and NTHREADS according to the machine we are running on @@ -80,9 +80,9 @@ build-from-latest: &build script: - apk update && apk add coreutils # The output of the build can get larger than gitlab.com's limit; only print the first 3MB (and the last 80 lines.) - - sh .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 - - sh .ci/push-gitlab.sh sagemath-dev - - sh .ci/push-gitlab.sh sagemath + - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 + - .ci/push-gitlab.sh sagemath-dev + - .ci/push-gitlab.sh sagemath tags: # We need enough disk space for the build to work. # The do(=digitalocean) instances on gitlab.com should be the 4GB instances From 05328a37ecc0705658327db56ba56c36a485f9f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 22:08:31 +0200 Subject: [PATCH 072/284] Fix typo in 3c78351fdcbfe93c2c353d0aef23bc2e976d7e17 --- src/sage_setup/docbuild/sphinxbuild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index a5b65baca0c..6bd5913d8ad 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -74,12 +74,12 @@ def _init_chatter(self): re.compile('^building \[.*\]: targets for 0 source files that are out of date'), re.compile('^building \[.*\]: targets for 0 po files that are out of date'), re.compile('^building \[.*\]: targets for 0 mo files that are out of date'), - re.copmile('^pickling environment... done'), - re.copmile('^dumping object inventory... done'), + re.compile('^pickling environment... done'), + re.compile('^dumping object inventory... done'), # We still have "Build finished." re.compile('^build succeeded.'), - re.copmile('^checking consistency... done'), - re.copmile('^preparing documents... done'), + re.compile('^checking consistency... done'), + re.compile('^preparing documents... done'), re.compile('^writing output... \[.*\] '), re.compile('^reading sources... \[.*\] '), re.compile('language "hu" not supported'), From 77eeea94b2b31328f3543a167e3440a22f19ab7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 13 Apr 2018 22:37:08 +0200 Subject: [PATCH 073/284] Do not print any of these ignored warnings we might want to add more ignored warnings that we actually print later --- src/sage_setup/docbuild/sphinxbuild.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index 6bd5913d8ad..ae0b6531dca 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -95,6 +95,7 @@ def _init_chatter(self): re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), ) + self._useless_chatter += self._ignored_warnings # replacements: pairs of regular expressions and their replacements, # to be applied to Sphinx output. @@ -104,10 +105,12 @@ def _init_chatter(self): if 'inventory' in sys.argv: # When building the inventory, ignore warnings about missing # citations and the search index. - self._useless_chatter += ( + ignored = ( re.compile('WARNING: citation not found:'), re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') ) + self._ignored_warnings += ignored + self._useless_chatter += ignored # Regular expressions indicating a problem with docbuilding. Raise an # exception if any of these occur. From aa13c840c2be80d905c243ff630a95d4516c3bcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 00:31:08 +0200 Subject: [PATCH 074/284] Ignore more chatter in docbuild --- src/sage_setup/docbuild/sphinxbuild.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index 45fbaae315b..efca70476d2 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -80,9 +80,20 @@ def _init_chatter(self): re.compile('^build succeeded.'), re.compile('^checking consistency... done'), re.compile('^preparing documents... done'), + re.compile('^copying extra files... done'), + re.compile('^writing additional pages... search'), + re.compile('^Writing js search indexes...writing additional pages... .*'), + re.compile('^generating indices... .*'), + re.compile('^dumping search index in .* ... done'), + re.compile('^linking _static directory'), + re.compile('^copying static files... done'), + re.compile('^copying extra files... done'), + re.compile('^loading transations \[.*\]... done'), re.compile('^writing output... \[.*\] '), + re.compile('^copying images... \[.*\] '), re.compile('^reading sources... \[.*\] '), re.compile('language "hu" not supported'), + re.compile('^WARNING:$'), ) # We fail whenever a line starts with "WARNING:", however, we ignore From 8884d9b2b809e9ddcf0c899508dbb84051e1c6b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 01:19:30 +0200 Subject: [PATCH 075/284] Print available disk space we need quite a bit of space for the CI to work --- .ci/describe-system.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh index c8cf7a3048c..b33a84e2428 100755 --- a/.ci/describe-system.sh +++ b/.ci/describe-system.sh @@ -13,6 +13,7 @@ set +e -x uname -a +df -h cat /proc/cpuinfo cat /proc/meminfo docker info From 090c25af45187150e501188715fb5943bcc91ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 01:20:36 +0200 Subject: [PATCH 076/284] Trying to find out which of the shared runners actually provide enough power --- .gitlab-ci.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8b9760b08c8..19b1d42939a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,8 +7,7 @@ # place # https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; # or set up your favourite cloud service to provide an on-demand autoscale -# runner. (Make sure to tag them as "do", otherwise the build step is not going -# to run on them.) +# runner. # As of early 2018 a run on GitLab CI takes about 35 minutes (with # build-from-latest.) We could probably save 10 minutes by not @@ -83,11 +82,6 @@ build-from-latest: &build - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 - .ci/push-gitlab.sh sagemath-dev - .ci/push-gitlab.sh sagemath - tags: - # We need enough disk space for the build to work. - # The do(=digitalocean) instances on gitlab.com should be the 4GB instances - # with 80GB of disk: https://www.digitalocean.com/pricing/ - - do except: - master - develop From a788043e2c0182c0cba45554ecd8b7ee5cb8c8dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 01:41:16 +0200 Subject: [PATCH 077/284] Make NTHREADS respect the available RAM sadly we need 2GB per thread for the docbuild to go through --- .ci/setup-make-parallelity.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 655bd86b762..18cfcf5675f 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -17,9 +17,11 @@ set -ex # Note that this value is incorrect for some CI providers (notably CircleCI: # https://circleci.com/docs/2.0/configuration-reference/#resource_class) which # provision fewer vCPUs than shown in /proc/cpuinfo. Also, setting this value -# too high can lead to RAM being insufficient, so it's best to set this +# too high can lead to RAM being insufficient, so it's best to set the NTHREADS # variable manually in your CI configuration. -[ -z "$NTHREADS" ] && NTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` || true +RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1024 / 1024 / 2 )) +CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` +[ -z "$NTHREADS" ] && NTHREADS=$([ $RAMTHREADS -le $CPUTHREADS ] && echo "$RAMTHREADS" || echo "$CPUTHREADS") || true export NTHREADS="$NTHREADS" # Set -j and -l for make (though -l is probably stripped by Sage) [ -z "$MAKEOPTS" ] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true From 9e317e6318275e971896f2dac14c3df483123132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 01:49:24 +0200 Subject: [PATCH 078/284] Allow at least one thread --- .ci/setup-make-parallelity.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 18cfcf5675f..cafe538cdc8 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -19,14 +19,25 @@ set -ex # provision fewer vCPUs than shown in /proc/cpuinfo. Also, setting this value # too high can lead to RAM being insufficient, so it's best to set the NTHREADS # variable manually in your CI configuration. -RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1024 / 1024 / 2 )) -CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` -[ -z "$NTHREADS" ] && NTHREADS=$([ $RAMTHREADS -le $CPUTHREADS ] && echo "$RAMTHREADS" || echo "$CPUTHREADS") || true +if [ -z "$NTHREADS" ]; then + CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` + RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1024 / 1024 / 2 )) + if [ $RAMTHREADS = 0 ];then + RAMTHREADS=1; + fi + NTHREADS=$([ $RAMTHREADS -le $CPUTHREADS ] && echo "$RAMTHREADS" || echo "$CPUTHREADS") +fi export NTHREADS="$NTHREADS" + # Set -j and -l for make (though -l is probably stripped by Sage) -[ -z "$MAKEOPTS" ] && MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" || true +if [ -z "$MAKEOPTS" ]; then + MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" +fi export MAKEOPTS="$MAKEOPTS" + # Not all parts of Sage seem to honor MAKEOPTS, so the current way of telling # the system which concurrency to use, seems to be setting $MAKE. -[ -z "$MAKE" ] && MAKE="make $MAKEOPTS" || true +if [ -z "$MAKE" ];then + MAKE="make $MAKEOPTS" +fi export MAKE="$MAKE" From d7e743ad626fd7a95046985bdca4b9ee0199af1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 02:02:32 +0200 Subject: [PATCH 079/284] Fixed comments about the do tag Apparently we don't need "do" for build-from-latest. --- .gitlab-ci.yml | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 19b1d42939a..39abb3a30f6 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,18 +9,18 @@ # or set up your favourite cloud service to provide an on-demand autoscale # runner. -# As of early 2018 a run on GitLab CI takes about 35 minutes (with -# build-from-latest.) We could probably save 10 minutes by not -# building/pushing/testing that image for branches other than master/develop. +# As of early 2018 a run on GitLab CI takes about 35 minutes. We could probably +# save 10 minutes by not building/pushing/testing that image for branches other +# than master/develop. # Note that most of the time during CI is spent with pulling and pushing of -# docker images and copying files locally as part of the docker build. -# At the moment there is no reliable way of passing the docker images to the -# following stages without explicit pushing/pulling or similar: +# docker images and copying files locally as part of the docker build. At the +# moment there is no reliable way of passing the docker images to the following +# stages without explicit pushing/pulling or similar: # https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 # The timings mentioned above are typical values. The shared runners provided -# on gitlab.com are sometimes by a factor of two slower. It's unclear what is -# the source of this slowdown. There is nothing different in the logs, so it's -# probably just an overcommittment of virtual machines on hardware. +# on gitlab.com are sometimes much slower depending on the runner you are +# scheduled on. Sometimes it's slower for no apparent reason, probably just an +# overcommittment of virtual machines on hardware. image: docker:latest @@ -87,8 +87,12 @@ build-from-latest: &build - develop # Build Sage and its documentation from a clean checkout of Sage. -# Note that this takes a very long time. You probably want to run this on your -# own gitlab-runner and increase the standard GitLab time limit for CI runs. +# Note that this takes several hours. You probably want to run this on your own +# gitlab-runner and increase the standard GitLab time limit for CI runs. +# Some of the shared runners provided by GitLab for free do not have enough +# disk space for this to work. If a build fails with "no space left on device", +# you could just retry it and hope to be scheduled on a machine with more disk +# space, or provision your own runner. build-from-clean: << : *build variables: From 49b21e16cbc288121fed109bc063629c1d285529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 02:43:24 +0200 Subject: [PATCH 080/284] Properly ignore undefined labels on first pass --- src/sage_setup/docbuild/sphinxbuild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index efca70476d2..ef539d6df56 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -138,11 +138,11 @@ def _init_chatter(self): # - undefined labels upon the first pass of the compilation: some # cross links may legitimately not yet be resolvable at this point. if 'latex' not in sys.argv: + self._error_patterns += (re.compile('WARNING:'),) if 'multidoc_first_pass=1' in sys.argv: - # Catch all warnings except 'WARNING: undefined label' - self._error_patterns += (re.compile('WARNING: (?!undefined label)'),) - else: - self._error_patterns += (re.compile('WARNING:'),) + ignore = (re.compile('WARNING: undefined label'),) + self._ignored_warnings += ignore + self._useless_chatter += ignore def _filter_out(self, line): if self._error and self._is_stdout: From bb1a40743fbfde18ecd949c0f504781729097534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 03:33:27 +0200 Subject: [PATCH 081/284] Fix incorrect @echo command --- Makefile | 4 ++-- docker/Dockerfile | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index d7e1671d4de..df8a7cbc101 100644 --- a/Makefile +++ b/Makefile @@ -114,10 +114,10 @@ micro_release: bdist-clean sagelib-clean find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; if command -v rdfind > /dev/null; then \ - @echo "Hardlinking identical files."; \ + echo "Hardlinking identical files."; \ rdfind -makeresultsfile false -makehardlinks true .; \ else \ - @echo "rdfind not installed. Not hardlinking identical files."; \ + echo "rdfind not installed. Not hardlinking identical files."; \ fi # Leaves everything that is needed to make the next "make" fast but removes diff --git a/docker/Dockerfile b/docker/Dockerfile index 2e3383a1ebe..7c061d944a8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -80,7 +80,7 @@ WORKDIR $HOME # completes without errors # ################################################################################ FROM run-time-dependencies as build-time-dependencies -# Install the build time dependencies & git +# Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ && sudo apt-get -qq clean \ From 52c2136eb4f7de55a5a1107bc29c58bc4652e912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 11:44:38 +0200 Subject: [PATCH 082/284] ignoring more chatter during docbuild --- src/sage_setup/docbuild/sphinxbuild.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index ef539d6df56..f55e25e46ac 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -88,11 +88,14 @@ def _init_chatter(self): re.compile('^linking _static directory'), re.compile('^copying static files... done'), re.compile('^copying extra files... done'), - re.compile('^loading transations \[.*\]... done'), + re.compile('^loading translations \[.*\]... done'), + re.compile('^Compiling the master document'), + re.compile('^Saved pickle file: citations.pickle'), re.compile('^writing output... \[.*\] '), re.compile('^copying images... \[.*\] '), re.compile('^reading sources... \[.*\] '), re.compile('language "hu" not supported'), + re.compile('^$'), re.compile('^WARNING:$'), ) @@ -149,6 +152,7 @@ def _filter_out(self, line): # swallow non-errors after an error occurred return True line = re.sub(self.ansi_color, '', line) + line = line.strip() for regex in self._useless_chatter: if regex.search(line) is not None: return True From f3bfa5fe9971fa2a04190b92da501cbf9724905b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 14 Apr 2018 12:18:34 +0200 Subject: [PATCH 083/284] Upgraded .gitlab-ci.yml to provide proper tags that actually work It's really unfortunate that GitLab CI machines are so underpowered. --- .gitlab-ci.yml | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 39abb3a30f6..fd776acf157 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,21 +7,35 @@ # place # https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; # or set up your favourite cloud service to provide an on-demand autoscale -# runner. +# runner. More details below. -# As of early 2018 a run on GitLab CI takes about 35 minutes. We could probably -# save 10 minutes by not building/pushing/testing that image for branches other +# As of early 2018 a run on GitLab CI takes about 45 minutes. We could probably +# save 10 minutes by not building/pushing/testing dev images for branches other # than master/develop. + # Note that most of the time during CI is spent with pulling and pushing of # docker images and copying files locally as part of the docker build. At the # moment there is no reliable way of passing the docker images to the following # stages without explicit pushing/pulling or similar: # https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 + # The timings mentioned above are typical values. The shared runners provided # on gitlab.com are sometimes much slower depending on the runner you are # scheduled on. Sometimes it's slower for no apparent reason, probably just an # overcommittment of virtual machines on hardware. +# GitLab provides several flavours of shared runners (as of early 2018): +# * runners tagged as "do" (digitalocean.com) provide about 60GB of HDD, two +# cores, but only 2GB of RAM. The RAM is often not sufficient to build the +# documentation. +# * runners tagged as "gce" (Google Compute Engine) provide about 22GB of HDD, +# a single core, 4GB of RAM. Since we are relying on OverlayFS, the disk +# space is not sufficient to build sage from scratch. + +# If you want to provide your own runners, make sure to tag them as follows: +# * "60hdd" (60GB of disk space are available) to make build-from-clean pass. +# * "gce" (4GB of RAM are available) to make build-from-latest pass. + image: docker:latest stages: @@ -85,6 +99,9 @@ build-from-latest: &build except: - master - develop + tags: + # 4GB of RAM are available + - gce # Build Sage and its documentation from a clean checkout of Sage. # Note that this takes several hours. You probably want to run this on your own @@ -101,6 +118,11 @@ build-from-clean: - master - develop except: [] + tags: + # 4 GB of RAM are available + - gce + # 60 GB of HDD are available + - 60hdd test-dev: stage: test From 5719a728d42ab8876b3e06c6bf7582f85e804591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 15 Apr 2018 23:51:55 +0200 Subject: [PATCH 084/284] Improve caching during build I do not fully understand why but somehow --cache-from works more reliably than just pulling an image and hoping for the docker daemon to realize that it can reuse some layers. It might be related to the docker version running or whether we are running docker:dind. --- .ci/build-docker.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index afd898e6fb2..75bad64e037 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -19,11 +19,12 @@ set -ex # during docker build. See /docker/Dockerfile for more details. ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} -# Seed our cache with $ARTIFACT_BASE if it exists -docker pull "$ARTIFACT_BASE" > /dev/null || true +# Seed our cache with $ARTIFACT_BASE if it exists (otherwise take alpine which is tiny) +(docker pull "$ARTIFACT_BASE" > /dev/null && docker tag "$ARTIFACT_BASE" cache) || \ + (docker pull alpine > /dev/null && docker tag alpine cache) docker_build() { - time docker build -f docker/Dockerfile --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ + time docker build -f docker/Dockerfile --cache-from cache --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } # We use a multi-stage build /docker/Dockerfile. For the caching to be From e4887e92aa0011d6debd6e59bca3e715fdf6fba1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 16 Apr 2018 01:22:26 +0200 Subject: [PATCH 085/284] Fix caching during build It does not matter to docker anymore whether --cache-from exists. So we can just --cache-from everything that we could possibly find. --- .ci/build-docker.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 75bad64e037..7f4ac7b7d3e 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -19,12 +19,16 @@ set -ex # during docker build. See /docker/Dockerfile for more details. ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} -# Seed our cache with $ARTIFACT_BASE if it exists (otherwise take alpine which is tiny) -(docker pull "$ARTIFACT_BASE" > /dev/null && docker tag "$ARTIFACT_BASE" cache) || \ - (docker pull alpine > /dev/null && docker tag alpine cache) +# Seed our cache with $ARTIFACT_BASE if it exists +docker pull "$ARTIFACT_BASE" > /dev/null || true docker_build() { - time docker build -f docker/Dockerfile --cache-from cache --build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ + # Docker's --cache-from does not really work with multi-stage builds: https://github.com/moby/moby/issues/34715 + # We work around that by specifying all possible tags (docker does not + # fail anymore if a cache-from tag can not be found.) + time docker build -f docker/Dockerfile \ +--cache-from "$ARTIFACT_BASE" --cache-from build-time-dependencies --cache-from make-all --cache-from "$DOCKER_IMAGE_CLI" --cache-from "$DOCKER_IMAGE_DEV" \ +--build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } # We use a multi-stage build /docker/Dockerfile. For the caching to be @@ -32,8 +36,8 @@ docker_build() { # the make-all target. (Just building the last target is not enough as # intermediate targets would be discarded from the cache and therefore the # caching would fail for our actual builds below.) -docker_build --pull --target build-time-dependencies . -docker_build --pull --tag make-all --target make-all . +docker_build --pull --target build-time-dependencies --tag build-time-dependencies . +docker_build --pull --target make-all --tag make-all . # Build the release image without build artifacts. docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . From be3a0f733950345bee7f4237db20558e1a06cefc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 19 Apr 2018 22:20:28 +0200 Subject: [PATCH 086/284] The docbuild crashes happen during an os.fork to spawn tachyon This is a cheap operation but it might exceed our overcommittment, let's check if that's the case. --- .ci/describe-system.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh index b33a84e2428..327243a4070 100755 --- a/.ci/describe-system.sh +++ b/.ci/describe-system.sh @@ -16,4 +16,6 @@ uname -a df -h cat /proc/cpuinfo cat /proc/meminfo +cat /proc/sys/vm/overcommit_memory +cat /proc/sys/vm/overcommit_ratio docker info From 7d85dc796c58c3de57401bc22d3587b94e205091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 19 Apr 2018 23:01:30 +0200 Subject: [PATCH 087/284] Something related to the sphinxbuild seems to be leaking memory and this might make us exceed the overcommit limit on an os.fork() call then. Using the Pool() even if we only want one thread, means that we get a constant penalty at the beginning but then every build runs in a clean process. --- src/sage_setup/docbuild/__init__.py | 44 ++++++++++------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 6940ebe5997..806018edcf4 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -264,35 +264,21 @@ def clean(self, *args): # import the customized builder for object.inv files inventory = builder_helper('inventory') -if NUM_THREADS > 1: - def build_many(target, args): - from multiprocessing import Pool - pool = Pool(NUM_THREADS, maxtasksperchild=1) - # map_async handles KeyboardInterrupt correctly. Plain map and - # apply_async does not, so don't use it. - x = pool.map_async(target, args, 1) - try: - ret = x.get(99999) - pool.close() - pool.join() - except Exception: - pool.terminate() - if ABORT_ON_ERROR: - raise - return ret -else: - def build_many(target, args): - results = [] - - for arg in args: - try: - results.append(target(arg)) - except Exception: - if ABORT_ON_ERROR: - raise - - return results - +def build_many(target, args): + from multiprocessing import Pool + pool = Pool(NUM_THREADS, maxtasksperchild=1) + # map_async handles KeyboardInterrupt correctly. Plain map and + # apply_async does not, so don't use it. + x = pool.map_async(target, args, 1) + try: + ret = x.get(99999) + pool.close() + pool.join() + except Exception: + pool.terminate() + if ABORT_ON_ERROR: + raise + return ret ########################################## # Parallel Building Ref Manual # From 50898feaef6499180ab2ec5b21e4b6b18032a77d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 19 Apr 2018 23:15:31 +0200 Subject: [PATCH 088/284] Try to build-from-scratch on GitLab's underpowered CI machines now that we are probably saving some (virtual) RAM in the docbuild. --- .gitlab-ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fd776acf157..7d4672f5500 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -119,10 +119,8 @@ build-from-clean: - develop except: [] tags: - # 4 GB of RAM are available - - gce # 60 GB of HDD are available - - 60hdd + - do test-dev: stage: test From 95c6275adac26be537c11a084114d8a8dc576392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 21 Apr 2018 12:04:42 +0200 Subject: [PATCH 089/284] Comment on fork logic in docbuilding --- src/sage_setup/docbuild/__init__.py | 8 ++++++++ src/sage_setup/docbuild/sphinxbuild.py | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 806018edcf4..3425a2686c7 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -265,6 +265,14 @@ def clean(self, *args): inventory = builder_helper('inventory') def build_many(target, args): + # Pool() uses an actual fork() to run each new instance. This is important + # for performance reasons, i.e., don't use a forkserver when it becomes + # available with Python 3: Here, sage is already initialized which is quite + # costly, with a forkserver we would have to reinitialize it for every + # document we build. At the same time, don't serialize this by taking the + # pool (and thus the call to fork()) out completely: The call to Sphinx + # leaks memory, so we need to build each document in its own process to + # control the RAM usage. from multiprocessing import Pool pool = Pool(NUM_THREADS, maxtasksperchild=1) # map_async handles KeyboardInterrupt correctly. Plain map and diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index f55e25e46ac..fbc66d4d28f 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -309,6 +309,11 @@ def runsphinx(): try: sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) + # Note that this call as of eraly 2018 leaks memory. So make sure that + # you don't call runsphinx() several times in a row. (i.e., you want to + # fork() somewhere before this call.) + # We don't use subprocess here, as we don't want to re-initialize Sage + # for every docbuild as this takes a while. sphinx.cmdline.main(sys.argv) sys.stderr.raise_errors() sys.stdout.raise_errors() From 329fdab86a1a3edf03c88780104a1471105fbcd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 23 Apr 2018 10:07:47 +0200 Subject: [PATCH 090/284] fix tags for build-from-latest --- .gitlab-ci.yml | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7d4672f5500..d06104fae3d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -26,15 +26,14 @@ # GitLab provides several flavours of shared runners (as of early 2018): # * runners tagged as "do" (digitalocean.com) provide about 60GB of HDD, two -# cores, but only 2GB of RAM. The RAM is often not sufficient to build the -# documentation. +# cores, but only 2GB of RAM. The RAM is sometimes not sufficient to build +# the documentation. # * runners tagged as "gce" (Google Compute Engine) provide about 22GB of HDD, # a single core, 4GB of RAM. Since we are relying on OverlayFS, the disk # space is not sufficient to build sage from scratch. # If you want to provide your own runners, make sure to tag them as follows: -# * "60hdd" (60GB of disk space are available) to make build-from-clean pass. -# * "gce" (4GB of RAM are available) to make build-from-latest pass. +# * "do" (60GB of disk space are available) to make build-from-clean pass. image: docker:latest @@ -99,9 +98,6 @@ build-from-latest: &build except: - master - develop - tags: - # 4GB of RAM are available - - gce # Build Sage and its documentation from a clean checkout of Sage. # Note that this takes several hours. You probably want to run this on your own From 048e2d8f0a25bada68693f9b22a8e0426eab2fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 27 Apr 2018 15:57:21 +0200 Subject: [PATCH 091/284] silence unicode warnings from the patchbot --- src/sage_setup/docbuild/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 3425a2686c7..7dc8ff783c5 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ The documentation builder From f9c772e5003fda874e82edd5c785eab2657cee1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 27 Apr 2018 16:21:33 +0200 Subject: [PATCH 092/284] Fix filtering logic in sphinx logger we still want to print the error lines, so we need to check for filter_out first. --- src/sage_setup/docbuild/sphinxbuild.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index fbc66d4d28f..bb3f49ae927 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -220,16 +220,16 @@ def _log_line(self, line): [#25160 ] Exception: artificial exception """ + skip_this_line = self._filter_out(line): self._check_errors(line) - if self._filter_out(line): - return for (old, new) in self.replacements: line = old.sub(new, line) line = self._prefix + ' ' + line.rstrip() + '\n' if not self._color: line = self.ansi_color.sub('', line) - self._stream.write(line) - self._stream.flush() + if not skip_this_line: + self._stream.write(line) + self._stream.flush() def raise_errors(self): r""" From f0431637a02819116781d87825523a068bf88ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 27 Apr 2018 16:30:31 +0200 Subject: [PATCH 093/284] Fixed some minor doctest issues --- src/sage_setup/docbuild/sphinxbuild.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index bb3f49ae927..a7a6afa81ec 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" This is Sage's version of the sphinx-build script @@ -172,7 +173,7 @@ def _check_errors(self, line): sage: logger.raise_errors() Traceback (most recent call last): ... - OSError: [doctestin] Segmentation fault! + OSError: Segmentation fault! """ if self._error: @@ -220,7 +221,7 @@ def _log_line(self, line): [#25160 ] Exception: artificial exception """ - skip_this_line = self._filter_out(line): + skip_this_line = self._filter_out(line) self._check_errors(line) for (old, new) in self.replacements: line = old.sub(new, line) @@ -246,7 +247,7 @@ def raise_errors(self): sage: logger.raise_errors() Traceback (most recent call last): ... - OSError: [doctestin] This is a SEVERE error + OSError: This is a SEVERE error """ if self._error: From 8f3480e71cafcef96b1c21aaf3219e8c81e5e9e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 2 May 2018 20:04:08 +0200 Subject: [PATCH 094/284] It seems that MAKEOPTS/SAGE_NUM_THREADS parsing has been fixed now let's not mangle MAKE anymore --- .ci/build-docker.sh | 2 +- .ci/setup-make-parallelity.sh | 8 +------- .ci/test-dev.sh | 2 +- docker/Dockerfile | 16 +++++++++------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 7f4ac7b7d3e..5bff3091814 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -28,7 +28,7 @@ docker_build() { # fail anymore if a cache-from tag can not be found.) time docker build -f docker/Dockerfile \ --cache-from "$ARTIFACT_BASE" --cache-from build-time-dependencies --cache-from make-all --cache-from "$DOCKER_IMAGE_CLI" --cache-from "$DOCKER_IMAGE_DEV" \ ---build-arg "MAKE=${MAKE}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +--build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } # We use a multi-stage build /docker/Dockerfile. For the caching to be diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index cafe538cdc8..a188d0d4269 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -28,16 +28,10 @@ if [ -z "$NTHREADS" ]; then NTHREADS=$([ $RAMTHREADS -le $CPUTHREADS ] && echo "$RAMTHREADS" || echo "$CPUTHREADS") fi export NTHREADS="$NTHREADS" +export SAGE_NUM_THREADS="$NTHREADS" # Set -j and -l for make (though -l is probably stripped by Sage) if [ -z "$MAKEOPTS" ]; then MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" fi export MAKEOPTS="$MAKEOPTS" - -# Not all parts of Sage seem to honor MAKEOPTS, so the current way of telling -# the system which concurrency to use, seems to be setting $MAKE. -if [ -z "$MAKE" ];then - MAKE="make $MAKEOPTS" -fi -export MAKE="$MAKE" diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index 3dde76bbbb0..b1216377049 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -26,7 +26,7 @@ IMAGE="$1" # Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. timed_run() { START=`date +%s` - docker run -e MAKE="$MAKE" "$IMAGE" "$2" + docker run -e MAKEOPTS="$MAKEOPTS" -e SAGE_NUM_THREADS="$SAGE_NUM_THREADS" "$IMAGE" "$2" END=`date +%s` TOTAL=$((END-START)) echo "Checking whether running \"$2\" was fast…" diff --git a/docker/Dockerfile b/docker/Dockerfile index 7c061d944a8..90266204d97 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -32,11 +32,11 @@ # takes about five minutes but you don't have to go through the sadly frequent # # rebuilds the whole Sage distribution... # # To build Sage, run this command from your sage/ directory: # -# $ docker build --build-arg MAKE="make -j4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . +# $ docker build --build-arg MAKEOPTS="-j4" --build-arg SAGE_NUM_THREADS="4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . # To run Sage: # # $ docker run -it sage # # To run doctests: # -# $ docker run -e "MAKE=make -j4" -it sage sage -tp src/sage # +# $ docker run -e "MAKEOPTS=-j4" -e "SAGE_NUM_THREADS=4" -it sage sage -tp src/sage # Make sure that you always have the latest develop branch merged into your # # local branch for this to work. # ################################################################################ @@ -160,11 +160,13 @@ ENV SAGE_FAT_BINARY yes # Just to be sure Sage doesn't try to build its own GCC (even though # it shouldn't with a recent GCC package from the system and with gfortran) ENV SAGE_INSTALL_GCC no -# Make everything in the build use multiple cores (setting this variable causes -# trouble for some packages outside Sage but it still seems to be they way Sage -# is doing this.) -ARG MAKE="make -j2" -ENV MAKE $MAKE +# Set MAKEOPTS and SAGE_NUM_THREADS to build things in parallel during the +# docker build. Note that these do not leak into the sagemath and sagemath-dev +# images. +ARG MAKEOPTS="-j2" +ENV MAKEOPTS $MAKEOPTS +ARG SAGE_NUM_THREADS="2" +ENV SAGE_NUM_THREADS $SAGE_NUM_THREADS RUN make build ################################################################################ From 99355ad8eb1402dd09ec4dd7bd0f898271147b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 May 2018 00:43:49 +0200 Subject: [PATCH 095/284] Build images in the same order in the Dockerfile and in build-docker.sh Otherwise we build sagemath-dev twice (probably depending on how aggresively exactly the docker daemon caches.) --- docker/Dockerfile | 50 +++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 90266204d97..e24f9e84bf2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -175,6 +175,31 @@ RUN make build FROM make-build as make-all RUN make +################################################################################ +# Image with a full build of sage, ready to release, i.e., with stripped # +# binaries and some extras to run the jupyter notebook. # +################################################################################ +FROM make-all as make-release +RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" +RUN sage -i gap_jupyter singular_jupyter pari_jupyter +RUN make micro_release + +################################################################################ +# A releasable (relatively small) image which contains a copy of sage without # +# temporary build artifacts which is set up to start the command line # +# interface if no parameters are passed in. # +################################################################################ +FROM run-time-dependencies as sagemath +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ +# Put scripts to start gap, gp, maxima, ... in /usr/bin +WORKDIR $SAGE_ROOT +RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" +COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +EXPOSE 8888 +CMD ["sage"] + ################################################################################ # Image with a full build of sage and its documentation but everything # # stripped that can be quickly rebuild by make. # @@ -222,28 +247,3 @@ RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ COPY ./docker/entrypoint-dev.sh /usr/local/bin/sage-entrypoint ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] CMD ["bash"] - -################################################################################ -# Image with a full build of sage, ready to release, i.e., with stripped # -# binaries and some extras to run the jupyter notebook. # -################################################################################ -FROM make-all as make-release -RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" -RUN sage -i gap_jupyter singular_jupyter pari_jupyter -RUN make micro_release - -################################################################################ -# A releasable (relatively small) image which contains a copy of sage without # -# temporary build artifacts which is set up to start the command line # -# interface if no parameters are passed in. # -################################################################################ -FROM run-time-dependencies as sagemath -ARG SAGE_ROOT=/home/sage/sage -COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ -# Put scripts to start gap, gp, maxima, ... in /usr/bin -WORKDIR $SAGE_ROOT -RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" -COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint -ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] -EXPOSE 8888 -CMD ["sage"] From 81babb8af3c494be4ce89c586c6168ae80682560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 May 2018 00:58:15 +0200 Subject: [PATCH 096/284] Simplify caching during docker build all the fancy --cache-from setup does not work with the latest docker:dind for some reason that I do not understand. If we also don't --pull caching seems to work fine on both GitLabCI (dind) and CircleCI. --- .ci/build-docker.sh | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 5bff3091814..cff26132d6f 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -19,25 +19,25 @@ set -ex # during docker build. See /docker/Dockerfile for more details. ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} -# Seed our cache with $ARTIFACT_BASE if it exists +# Seed our cache with $ARTIFACT_BASE if it exists. docker pull "$ARTIFACT_BASE" > /dev/null || true docker_build() { # Docker's --cache-from does not really work with multi-stage builds: https://github.com/moby/moby/issues/34715 - # We work around that by specifying all possible tags (docker does not - # fail anymore if a cache-from tag can not be found.) + # So we just have to rely on the local cache. time docker build -f docker/Dockerfile \ ---cache-from "$ARTIFACT_BASE" --cache-from build-time-dependencies --cache-from make-all --cache-from "$DOCKER_IMAGE_CLI" --cache-from "$DOCKER_IMAGE_DEV" \ --build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } # We use a multi-stage build /docker/Dockerfile. For the caching to be -# effective, we populate the cache by building the build-time-dependencies and -# the make-all target. (Just building the last target is not enough as -# intermediate targets would be discarded from the cache and therefore the -# caching would fail for our actual builds below.) -docker_build --pull --target build-time-dependencies --tag build-time-dependencies . -docker_build --pull --target make-all --tag make-all . +# effective, we populate the cache by building the run/build-time-dependencies +# and the make-all target. (Just building the last target is not enough as +# intermediate targets could be discarded from the cache [depending on the +# docker version] and therefore the caching would fail for our actual builds +# below.) +docker_build --target run-time-dependencies --tag run-time-dependencies:$DOCKER_TAG . +docker_build --target build-time-dependencies --tag build-time-dependencies:$DOCKER_TAG . +docker_build --target make-all --tag make-all:$DOCKER_TAG . # Build the release image without build artifacts. docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . From ad39cc9de9dd8a3f6092c7153278982393da57b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 May 2018 18:48:54 +0200 Subject: [PATCH 097/284] Complicate parallelization quite a bit to make the build fast and the docbuild not crash --- .ci/build-docker.sh | 2 +- .ci/setup-make-parallelity.sh | 55 ++++++++++++++++++++++++----------- .ci/test-dev.sh | 4 +-- .circleci/config.yml | 2 +- .gitlab-ci.yml | 2 +- docker/Dockerfile | 8 +++++ 6 files changed, 51 insertions(+), 22 deletions(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index cff26132d6f..404362cc1bb 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -26,7 +26,7 @@ docker_build() { # Docker's --cache-from does not really work with multi-stage builds: https://github.com/moby/moby/issues/34715 # So we just have to rely on the local cache. time docker build -f docker/Dockerfile \ ---build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +--build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg "MAKEOPTS_DOCBUILD=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS_DOCBUILD=${SAGE_NUM_THREADS_DOCBUILD}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ } # We use a multi-stage build /docker/Dockerfile. For the caching to be diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index a188d0d4269..1998b8bc423 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -1,5 +1,15 @@ #!/bin/sh +# Source this to set CPUTHREADS (the number of apparent cores) and +# RAMTHREADS (free RAM divided by the maximum amount needed per thread +# typically) +# From this this script exports reasonable defaults for SAGE_NUM_THREADS and +# MAKEOPTS. + +# We do exactly the same for CPUTHREADS_DOCBUILD, RAMTHREADS_DOCBUILD, +# SAGE_NUM_THREADS_DOCBUILD, MAKEOPTS_DOCBUILD. As the docbuild needs +# substantially more RAM as of May 2018. + # **************************************************************************** # Copyright (C) 2018 Julian Rüth # @@ -12,26 +22,37 @@ set -ex -# Determine the number of threads that can run simultaneously on this system -# (we might not have nproc available.) -# Note that this value is incorrect for some CI providers (notably CircleCI: -# https://circleci.com/docs/2.0/configuration-reference/#resource_class) which -# provision fewer vCPUs than shown in /proc/cpuinfo. Also, setting this value -# too high can lead to RAM being insufficient, so it's best to set the NTHREADS -# variable manually in your CI configuration. -if [ -z "$NTHREADS" ]; then +if [ -z "$CPUTHREADS" ]; then + # Determine the number of threads that can run simultaneously on this system + # (we might not have nproc available.) + # Note that this value is incorrect for some CI providers (notably CircleCI: + # https://circleci.com/docs/2.0/configuration-reference/#resource_class) which + # provision fewer vCPUs than shown in /proc/cpuinfo. So it is probably better + # to set CPUTHREADS manuall in your CI configuration. CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` - RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1024 / 1024 / 2 )) +fi +if [ -z "$CPUTHREADS_DOCBUILD" ]; then + CPUTHREADS_DOCBUILD=$CPUTHREADS +fi + +if [ -z "$RAMTHREADS" ]; then + RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1048576 )) if [ $RAMTHREADS = 0 ];then RAMTHREADS=1; fi - NTHREADS=$([ $RAMTHREADS -le $CPUTHREADS ] && echo "$RAMTHREADS" || echo "$CPUTHREADS") fi -export NTHREADS="$NTHREADS" -export SAGE_NUM_THREADS="$NTHREADS" - -# Set -j and -l for make (though -l is probably stripped by Sage) -if [ -z "$MAKEOPTS" ]; then - MAKEOPTS="-j $NTHREADS -l $((NTHREADS-1)).8" +if [ -z "$RAMTHREADS_DOCBUILD" ]; then + RAMTHREADS_DOCBUILD=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 2097152 )) + if [ $RAMTHREADS_DOCBUILD = 0 ];then + RAMTHREADS_DOCBUILD=1; + fi fi -export MAKEOPTS="$MAKEOPTS" + +# On CI machines with their virtual CPUs, it seems to be quite beneficial to +# overcommit on CPU usage. We only need to make sure that we do not exceed RAM +# (as there is no swap.) +export SAGE_NUM_THREADS=$RAMTHREADS +export SAGE_NUM_THREADS_DOCBUILD=$RAMTHREADS_DOCBUILD +# Set -j and -l for make (though -l is probably ignored by Sage) +export MAKEOPTS="-j $RAMTHREADS -l $((CPUTHREADS-1)).8" +export MAKEOPTS_DOCBUILD="-j $RAMTHREADS_DOCBUILD -l $((CPUTHREADS_DOCBUILD-1)).8" diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh index b1216377049..5ba2cce16ca 100755 --- a/.ci/test-dev.sh +++ b/.ci/test-dev.sh @@ -26,7 +26,7 @@ IMAGE="$1" # Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. timed_run() { START=`date +%s` - docker run -e MAKEOPTS="$MAKEOPTS" -e SAGE_NUM_THREADS="$SAGE_NUM_THREADS" "$IMAGE" "$2" + docker run -e MAKEOPTS="$MAKEOPTS_DOCBUILD" -e SAGE_NUM_THREADS="$SAGE_NUM_THREADS_DOCBUILD" "$IMAGE" "$2" END=`date +%s` TOTAL=$((END-START)) echo "Checking whether running \"$2\" was fast…" @@ -42,4 +42,4 @@ timed_run 120 true # runs make build # The parser in Sphinx fails to parse some .py files and adds the (more # recently modified) .pyc files as dependencies instead. (Have a look the # changeset that introduced this comment for more details.) -timed_run $(( 1200/$NTHREADS )) make # runs make build and then make +timed_run $(( 1200/$SAGE_NUM_THREADS_DOCBUILD )) make # runs make build and then make diff --git a/.circleci/config.yml b/.circleci/config.yml index 0404cc6b8bb..4869e5003dc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,7 +33,7 @@ jobs: . .ci/protect-secrets.sh # Collect debug infos about the system we are running on .ci/describe-system.sh - # Set MAKE and NTHREADS according to the machine we are running on + # Set MAKEOPTS and SAGE_NUM_THREADS . .ci/setup-make-parallelity.sh # Set DOCKER_TAG according to the current branch/tag diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d06104fae3d..b4f9dfb97a1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,7 +56,7 @@ before_script: - .ci/describe-system.sh # Set DOCKER_TAG according to the current branch/tag - . .ci/update-env.sh - # Set MAKE and NTHREADS according to the machine we are running on + # Set MAKEOPTS and SAGE_NUM_THREADS according to the machine we are running on - . .ci/setup-make-parallelity.sh # We use docker-in-docker to build our docker images, i.e., we run a diff --git a/docker/Dockerfile b/docker/Dockerfile index e24f9e84bf2..325e5d7f922 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -173,6 +173,14 @@ RUN make build # Image with a full build of sage and its documentation. # ################################################################################ FROM make-build as make-all +# The docbuild needs quite some RAM (as of May 2018). It sometimes calls +# os.fork() to spawn an external program which then exceeds easily the +# overcommit limit of the system (no RAM is actually used, but this limit is +# very low because there is not even swap on most CI systems.) +ARG MAKEOPTS_DOCBUILD=$MAKEOPTS +ENV MAKEOPTS_DOCBUILD $MAKEOPTS_DOCBUILD +ARG SAGE_NUM_THREADS_DOCBUILD=$SAGE_NUM_THREADS +ENV SAGE_NUM_THREADS_DOCBUILD $SAGE_NUM_THREADS_DOCBUILD RUN make ################################################################################ From ae119f23c62921e7e01e5169b52124a2243a8ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 3 May 2018 22:56:22 +0200 Subject: [PATCH 098/284] Do not overcommit that much on CircleCI we sometimes get single core but 8GB of RAM --- .ci/setup-make-parallelity.sh | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 1998b8bc423..77c1bbb9877 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -51,8 +51,16 @@ fi # On CI machines with their virtual CPUs, it seems to be quite beneficial to # overcommit on CPU usage. We only need to make sure that we do not exceed RAM # (as there is no swap.) -export SAGE_NUM_THREADS=$RAMTHREADS -export SAGE_NUM_THREADS_DOCBUILD=$RAMTHREADS_DOCBUILD +if [ $CPUTHREADS -lt $RAMTHREADS ]; then + export SAGE_NUM_THREADS=$((CPUTHREADS + 1)) +else + export SAGE_NUM_THREADS=$RAMTHREADS +fi +if [ $CPUTHREADS_DOCBUILD -lt $RAMTHREADS_DOCBUILD ]; then + export SAGE_NUM_THREADS_DOCBUILD=$((CPUTHREADS_DOCBUILD + 1)) +else + export SAGE_NUM_THREADS_DOCBUILD=$RAMTHREADS_DOCBUILD +fi # Set -j and -l for make (though -l is probably ignored by Sage) -export MAKEOPTS="-j $RAMTHREADS -l $((CPUTHREADS-1)).8" -export MAKEOPTS_DOCBUILD="-j $RAMTHREADS_DOCBUILD -l $((CPUTHREADS_DOCBUILD-1)).8" +export MAKEOPTS="-j $SAGE_NUM_THREADS -l $((CPUTHREADS - 1)).8" +export MAKEOPTS_DOCBUILD="-j $SAGE_NUM_THREADS_DOCBUILD -l $((CPUTHREADS_DOCBUILD - 1)).8" From a5914a4917e6dbbbc99c8424250f010ee54de809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 13 May 2018 20:13:49 +0200 Subject: [PATCH 099/284] Build tags from clean --- .circleci/config.yml | 9 +++------ .gitlab-ci.yml | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4869e5003dc..5fb65914ecb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,17 +41,14 @@ jobs: . .ci/update-env.sh # Select ARTIFACT_BASE depending on the current branch/tag - case $DOCKER_TAG in - "develop" | "latest") + if [ -n $CIRCLE_TAG ] || [ $DOCKER_TAG = master ] || [ $DOCKER_TAG = develop ];then export ARTIFACT_BASE=source-clean - ;; - *) + else # Select sagemath/sagemath-dev:develop as the ARTIFACT_BASE # unless the user has explicitly selected a differnt one as a # CircleCI environment variable. export ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} - ;; - esac + fi # Build docker images .ci/build-docker.sh diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b4f9dfb97a1..dcf46066619 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -98,6 +98,7 @@ build-from-latest: &build except: - master - develop + - tags # Build Sage and its documentation from a clean checkout of Sage. # Note that this takes several hours. You probably want to run this on your own @@ -113,6 +114,7 @@ build-from-clean: only: - master - develop + - tags except: [] tags: # 60 GB of HDD are available @@ -160,6 +162,7 @@ push-dockerhub-dev: only: - master - develop + - tags script: - . .ci/pull-gitlab.sh sagemath-dev - sh .ci/push-dockerhub.sh sagemath-dev From 49bb769a5adec5dae2e407c0e72d50d278f1a8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 14 May 2018 15:09:28 +0200 Subject: [PATCH 100/284] Work around CircleCI escaping -n does not work correctly in CircleCI's shell. Quoting might help. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5fb65914ecb..f8ad75a05ed 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -41,7 +41,7 @@ jobs: . .ci/update-env.sh # Select ARTIFACT_BASE depending on the current branch/tag - if [ -n $CIRCLE_TAG ] || [ $DOCKER_TAG = master ] || [ $DOCKER_TAG = develop ];then + if [ -n "$CIRCLE_TAG" ] || [ $DOCKER_TAG = "master" ] || [ $DOCKER_TAG = "develop" ];then export ARTIFACT_BASE=source-clean else # Select sagemath/sagemath-dev:develop as the ARTIFACT_BASE From f9c68a64834343db79cd5953c493728c30e71735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 14 May 2018 15:19:03 +0200 Subject: [PATCH 101/284] Tell CircleCI to build for all tags the branches bit is not necessary but for symmetry I think it looks better like that. --- .circleci/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f8ad75a05ed..27b27b1829c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,6 +22,12 @@ jobs: # we do build & test & release in one step. build: machine: true + # Build for all tags + tags: + only: /.*/ + # Build for all branches + branches: + only: /.*/ steps: - checkout - run: From b1508cf144c5b31fb7f4f1ff7a1f22155130900d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 15 May 2018 10:37:16 +0200 Subject: [PATCH 102/284] adapt to microbadger URL change --- docker/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/README.md b/docker/README.md index 0028e8b9798..a33ea0b81cc 100644 --- a/docker/README.md +++ b/docker/README.md @@ -17,7 +17,7 @@ SageMath is a free open-source mathematics software system licensed under the GP There are several flavours of this image. -* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath:latest.svg)](https://hub.docker.com/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath/latest.svg)](https://hub.docker.com/r/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: ``` docker run -it sagemath/sagemath:latest ``` @@ -25,7 +25,7 @@ There are several flavours of this image. ``` docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter ``` -* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev:develop.svg)](https://hub.docker.com/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: ``` docker run -it sagemath/sagemath-dev:develop ``` From a4487bf1fa369edb3084bcb9ba37755156eea770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 15 May 2018 10:47:56 +0200 Subject: [PATCH 103/284] Fix format of stable releases and mention betas --- docker/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index a33ea0b81cc..4b627c11d46 100644 --- a/docker/README.md +++ b/docker/README.md @@ -3,7 +3,8 @@ # Supported tags * `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) -* `x.x.x` — all stable releases of Sage are tagged with their version number. +* `x.x` — all stable releases of Sage are tagged with their version number. +* `x.x.{beta,rc}x` - betas and release candidadets 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) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) From 2d33a626429f5ae07dbe3dd7791b238341e37ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 15 May 2018 10:49:48 +0200 Subject: [PATCH 104/284] fix typo --- docker/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/README.md b/docker/README.md index 4b627c11d46..b9f9240f9e4 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,7 +4,7 @@ * `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) * `x.x` — all stable releases of Sage are tagged with their version number. -* `x.x.{beta,rc}x` - betas and release candidadets of Sage as [tagged in our git repository](https://github.com/sagemath/sage/tags). +* `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) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) From aa49196c4f0ed33da7a45b80c7a4a01bd46b7d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 15 May 2018 10:58:36 +0200 Subject: [PATCH 105/284] Backport https://trac.sagemath.org/ticket/24655 for automated docker builds --- .ci/README.md | 9 + .ci/build-docker.sh | 46 ++++ .ci/describe-system.sh | 21 ++ .ci/head-tail.sh | 45 ++++ .ci/protect-secrets.sh | 40 +++ .ci/pull-gitlab.sh | 30 +++ .ci/push-dockerhub.sh | 29 +++ .ci/push-gitlab.sh | 25 ++ .ci/setup-make-parallelity.sh | 66 +++++ .ci/test-cli.sh | 30 +++ .ci/test-dev.sh | 45 ++++ .ci/test-jupyter.sh | 24 ++ .ci/update-env.sh | 28 +++ .circleci/config.yml | 69 ++++++ .dockerignore | 1 + .gitignore | 11 + .gitlab-ci.yml | 168 +++++++++++++ Makefile | 35 +++ configure.ac | 6 +- docker/.gitignore | 2 + docker/Dockerfile | 257 ++++++++++++++++++++ docker/README.md | 55 +++++ docker/entrypoint-dev.sh | 4 + docker/entrypoint.sh | 9 + docker/hooks/build | 1 + docker/hooks/push | 1 + src/.gitignore | 4 - src/Makefile.in | 2 +- src/doc/common/conf.py | 2 +- src/sage/misc/sagedoc.py | 8 +- src/sage_setup/docbuild/__init__.py | 59 +++-- src/sage_setup/docbuild/ext/sage_autodoc.py | 7 +- src/sage_setup/docbuild/sphinxbuild.py | 231 +++++++++++++----- 33 files changed, 1264 insertions(+), 106 deletions(-) create mode 100644 .ci/README.md create mode 100755 .ci/build-docker.sh create mode 100755 .ci/describe-system.sh create mode 100755 .ci/head-tail.sh create mode 100755 .ci/protect-secrets.sh create mode 100755 .ci/pull-gitlab.sh create mode 100755 .ci/push-dockerhub.sh create mode 100755 .ci/push-gitlab.sh create mode 100755 .ci/setup-make-parallelity.sh create mode 100755 .ci/test-cli.sh create mode 100755 .ci/test-dev.sh create mode 100755 .ci/test-jupyter.sh create mode 100755 .ci/update-env.sh create mode 100644 .circleci/config.yml create mode 120000 .dockerignore create mode 100644 .gitlab-ci.yml create mode 100644 docker/.gitignore create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100755 docker/entrypoint-dev.sh create mode 100755 docker/entrypoint.sh create mode 100644 docker/hooks/build create mode 100644 docker/hooks/push delete mode 100644 src/.gitignore diff --git a/.ci/README.md b/.ci/README.md new file mode 100644 index 00000000000..e2b165cc518 --- /dev/null +++ b/.ci/README.md @@ -0,0 +1,9 @@ +# Continuous Integration (CI) + +We support several implementations of CI. All these implementations rely on +[docker](https://docker.com) in some way. This directory contains bits which +are shared between these CI implementations. The relevant docker files can be +found in `/docker/`. + +* [CircleCI](https://circleci.com) is configured in `/.circleci/`. +* [GitLab CI](https://gitlab.com) is configured in `/.gitlab-ci.yml`. diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh new file mode 100755 index 00000000000..404362cc1bb --- /dev/null +++ b/.ci/build-docker.sh @@ -0,0 +1,46 @@ +#!/bin/sh + +# This script gets called from CI to build several flavours of docker images +# which contain Sage. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# We speed up the build process by copying built artifacts from ARTIFACT_BASE +# during docker build. See /docker/Dockerfile for more details. +ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} + +# Seed our cache with $ARTIFACT_BASE if it exists. +docker pull "$ARTIFACT_BASE" > /dev/null || true + +docker_build() { + # Docker's --cache-from does not really work with multi-stage builds: https://github.com/moby/moby/issues/34715 + # So we just have to rely on the local cache. + time docker build -f docker/Dockerfile \ +--build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg "MAKEOPTS_DOCBUILD=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS_DOCBUILD=${SAGE_NUM_THREADS_DOCBUILD}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +} + +# We use a multi-stage build /docker/Dockerfile. For the caching to be +# effective, we populate the cache by building the run/build-time-dependencies +# and the make-all target. (Just building the last target is not enough as +# intermediate targets could be discarded from the cache [depending on the +# docker version] and therefore the caching would fail for our actual builds +# below.) +docker_build --target run-time-dependencies --tag run-time-dependencies:$DOCKER_TAG . +docker_build --target build-time-dependencies --tag build-time-dependencies:$DOCKER_TAG . +docker_build --target make-all --tag make-all:$DOCKER_TAG . + +# Build the release image without build artifacts. +docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . +# Build the developer image with the build artifacts intact. +# Note: It's important to build the dev image last because it might be tagged as ARTIFACT_BASE. +docker_build --target sagemath-dev --tag "$DOCKER_IMAGE_DEV" . diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh new file mode 100755 index 00000000000..327243a4070 --- /dev/null +++ b/.ci/describe-system.sh @@ -0,0 +1,21 @@ +#!/bin/sh + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set +e -x + +uname -a +df -h +cat /proc/cpuinfo +cat /proc/meminfo +cat /proc/sys/vm/overcommit_memory +cat /proc/sys/vm/overcommit_ratio +docker info diff --git a/.ci/head-tail.sh b/.ci/head-tail.sh new file mode 100755 index 00000000000..8ecf9f879f0 --- /dev/null +++ b/.ci/head-tail.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +OFFSET=80 +# This script reads from stdin and prints to stdout as long as a the output +# does not exceed a certain number of bytes. When reading an EOF it prints the +# last $OFFSET lines if they have not been printed normally already. +# This script expects one argument, the number of bytes. + +# Heavily inspired by a simlar strategy in make, https://stackoverflow.com/a/44849696/812379. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +stdbuf -i0 -o0 -e0 awk -v limit=$1 -v firstMissingNR=-1 -v offset=$OFFSET -v bytes=0 \ +'{ + if (bytes < limit) { + # this probably gets multi-byte characters wrong, but that probably does + # not matter for our purposes. (We add 1 for a UNIX newline.) + bytes += length($0) + 1; + print; + } else { + if (firstMissingNR == -1){ + print "[…output truncated…]"; + firstMissingNR = NR; + } + a[NR] = $0; + delete a[NR-offset]; + printf "." > "/dev/stderr" + } +} +END { + if (firstMissingNR != -1) { + print "" > "/dev/stderr"; + for(i = NR-offset+1 > firstMissingNR ? NR-offset-1 : firstMissingNR; i<=NR ; i++){ print a[i]; } + } +} +' + diff --git a/.ci/protect-secrets.sh b/.ci/protect-secrets.sh new file mode 100755 index 00000000000..ee781dddca8 --- /dev/null +++ b/.ci/protect-secrets.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# This script protects all environment variables that start with "SECRET_". +# It puts them in a temporary file. The name of the variable contains the path +# of that file. This filename can then safely be used in `cat` even if `set +# -x` has been turned on. Also you can run "export" to understand the +# environment without danger. +# Be careful, however, not to use this like the following: +# docker login $DOCKER_USER $(cat $SECRET_DOCKER_PASS) +# as this would expose the password if `set -x` has been turned on. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -eo pipefail +set +x + +function encrypt { + RET=`mktemp` + eval " echo \$$1" > "$RET" + echo $RET +} + +for name in `awk 'END { for (name in ENVIRON) { print name; } }' < /dev/null`; do +case "$name" in + SECRET_*) + export $name="$(encrypt $name)" + echo "Protected $name" + ;; +esac +done + +unset encrypt diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh new file mode 100755 index 00000000000..c8235e64068 --- /dev/null +++ b/.ci/pull-gitlab.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script gets called from CI to pull the Sage docker images that were +# built during the "build" phase to pull all the connected docker daemon +# (likely a docker-in-docker.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. +# The variable $DOCKER_IMAGE is set to the full name of the pulled image; +# source this script to use it. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# Pull the built images from the gitlab registry and give them the original +# names they had after built. +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker pull $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +export DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" +docker tag $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG $DOCKER_IMAGE diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh new file mode 100755 index 00000000000..c2c46062ffa --- /dev/null +++ b/.ci/push-dockerhub.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# This script gets called from CI to push our docker images to +# $DOCKER_USER/sagemath* on the Docker Hub. +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +[ -z "$DOCKER_TAG" ] && (echo "Can not push untagged build."; exit 0) + +# Push the built images to the docker hub (and fail silently if +# DOCKER_USER/SECRET_DOCKER_PASS have not been configured.) +if [ -z "$DOCKER_USER" -o -z "$SECRET_DOCKER_PASS" ]; then + echo "DOCKER_USER/SECRET_DOCKER_PASS variables have not been configured in your Continuous Integration setup. Not pushing built images to Docker Hub." +else + cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin + docker push ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG +fi diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh new file mode 100755 index 00000000000..e4ceb30abcd --- /dev/null +++ b/.ci/push-gitlab.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# This script gets called from CI to push our docker images to registry +# configured in GitLab. (Mostly, so we can pull them again to push them to the +# Docker Hub.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker tag ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +docker push $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh new file mode 100755 index 00000000000..77c1bbb9877 --- /dev/null +++ b/.ci/setup-make-parallelity.sh @@ -0,0 +1,66 @@ +#!/bin/sh + +# Source this to set CPUTHREADS (the number of apparent cores) and +# RAMTHREADS (free RAM divided by the maximum amount needed per thread +# typically) +# From this this script exports reasonable defaults for SAGE_NUM_THREADS and +# MAKEOPTS. + +# We do exactly the same for CPUTHREADS_DOCBUILD, RAMTHREADS_DOCBUILD, +# SAGE_NUM_THREADS_DOCBUILD, MAKEOPTS_DOCBUILD. As the docbuild needs +# substantially more RAM as of May 2018. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +if [ -z "$CPUTHREADS" ]; then + # Determine the number of threads that can run simultaneously on this system + # (we might not have nproc available.) + # Note that this value is incorrect for some CI providers (notably CircleCI: + # https://circleci.com/docs/2.0/configuration-reference/#resource_class) which + # provision fewer vCPUs than shown in /proc/cpuinfo. So it is probably better + # to set CPUTHREADS manuall in your CI configuration. + CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` +fi +if [ -z "$CPUTHREADS_DOCBUILD" ]; then + CPUTHREADS_DOCBUILD=$CPUTHREADS +fi + +if [ -z "$RAMTHREADS" ]; then + RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1048576 )) + if [ $RAMTHREADS = 0 ];then + RAMTHREADS=1; + fi +fi +if [ -z "$RAMTHREADS_DOCBUILD" ]; then + RAMTHREADS_DOCBUILD=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 2097152 )) + if [ $RAMTHREADS_DOCBUILD = 0 ];then + RAMTHREADS_DOCBUILD=1; + fi +fi + +# On CI machines with their virtual CPUs, it seems to be quite beneficial to +# overcommit on CPU usage. We only need to make sure that we do not exceed RAM +# (as there is no swap.) +if [ $CPUTHREADS -lt $RAMTHREADS ]; then + export SAGE_NUM_THREADS=$((CPUTHREADS + 1)) +else + export SAGE_NUM_THREADS=$RAMTHREADS +fi +if [ $CPUTHREADS_DOCBUILD -lt $RAMTHREADS_DOCBUILD ]; then + export SAGE_NUM_THREADS_DOCBUILD=$((CPUTHREADS_DOCBUILD + 1)) +else + export SAGE_NUM_THREADS_DOCBUILD=$RAMTHREADS_DOCBUILD +fi +# Set -j and -l for make (though -l is probably ignored by Sage) +export MAKEOPTS="-j $SAGE_NUM_THREADS -l $((CPUTHREADS - 1)).8" +export MAKEOPTS_DOCBUILD="-j $SAGE_NUM_THREADS_DOCBUILD -l $((CPUTHREADS_DOCBUILD - 1)).8" diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh new file mode 100755 index 00000000000..0ad6a8f3d0a --- /dev/null +++ b/.ci/test-cli.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath image. + +# Usage: ./test-cli.sh IMAGE-NAME + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +echo "Checking that Sage starts and can calculate 1+1…" +# Calculate 1+1 (remove startup messages and leading & trailing whitespace) +TWO=`docker run "$1" sage -c "'print(1+1)'" | tail -1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'` +[ "x$TWO" = "x2" ] + +echo "Checking that some binaries that should be distributed with Sage are on the PATH…" +# We could also run minimal tests on these but we don't yet. +# Check that Singular and GAP are present +docker run "$1" which Singular +docker run "$1" which gap +# Check that jupyter is present (for binder) +docker run "$1" which jupyter diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh new file mode 100755 index 00000000000..5ba2cce16ca --- /dev/null +++ b/.ci/test-dev.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath-dev image. +# This script expects a single argument, the full name of the docker image to +# test. + +# Usage: ./test-dev.sh IMAGE-NAME + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +IMAGE="$1" + +. .ci/setup-make-parallelity.sh + +# Usage: timed_run limit args +# Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. +timed_run() { + START=`date +%s` + docker run -e MAKEOPTS="$MAKEOPTS_DOCBUILD" -e SAGE_NUM_THREADS="$SAGE_NUM_THREADS_DOCBUILD" "$IMAGE" "$2" + END=`date +%s` + TOTAL=$((END-START)) + echo "Checking whether running \"$2\" was fast…" + [ "$TOTAL" -lt "$1" ] +} + +# Most setup should be done in well under 120 seconds but some CI machines tend +# to be really slow so we better give them some space here. Better miss a +# regression at first than create a lot of noise. +timed_run 120 true # runs make build +# Building the documentation is quite slow at the moment: +# Currently, this detects some dependencies as changed that have not changed. +# The parser in Sphinx fails to parse some .py files and adds the (more +# recently modified) .pyc files as dependencies instead. (Have a look the +# changeset that introduced this comment for more details.) +timed_run $(( 1200/$SAGE_NUM_THREADS_DOCBUILD )) make # runs make build and then make diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh new file mode 100755 index 00000000000..9df157b763b --- /dev/null +++ b/.ci/test-jupyter.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath-jupyter +# image. + +# Usage: ./test-jupyter.sh IMAGE-NAME [HOST] + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +docker run --name sage-jupyter -p 8888:8888 -d "$1" sage-jupyter +echo "Checking that the Jupyter notebook is running…" +sleep 10 # giving the server some time to start +docker logs sage-jupyter +wget --retry-connrefused --tries=10 --wait=3 "http://${2:-localhost}:8888" diff --git a/.ci/update-env.sh b/.ci/update-env.sh new file mode 100755 index 00000000000..ee6a2110a56 --- /dev/null +++ b/.ci/update-env.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# This script gets called from CI to establish the name of the current docker +# tag to build from the name of the branch/tag provided by CI. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# From the docker documentation: "A tag name must be valid ASCII and may +# contain lowercase and uppercase letters, digits, underscores, periods and +# dashes. A tag name may not start with a period or a dash and may contain a +# maximum of 128 characters." +export DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` + +[[ -z "$DOCKER_TAG" ]] && export DOCKER_TAG=none +[[ "$DOCKER_TAG" = "master" ]] && export DOCKER_TAG=latest + +export DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG +export DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000..27b27b1829c --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,69 @@ +# This file configures automatic builds of Sage on [CircleCI](https://circleci.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much, +# still the full build does usually not exceed CircleCI's limits for open +# source projcets (five hours on 2 vCPUs as of early 2018.) +# You might want to try to build locally or with GitLab CI, see +# `.gitlab-ci.yml` for more options. +# Note that we do not use workflows because it was not clear whether the docker +# images sizes would not exceed the size limits of CircleCI workspaces. Also, +# workflows would not make things faster. We do not get more machines for free +# and the frequent loading of docker images probably exceeds the cost of the +# actual tests. + +# As of early 2018, a run on CircleCI takes usually about 25 minutes. Most of +# the time is spent pulling/pushing from/to Docker Hub and copying files +# locally during the docker build. We could probably save five minutes by not +# building and testing the sagemath-dev image for most branches. + +version: 2 +jobs: + # As https://circleci.com/docs/2.0/docker-layer-caching/ is a paid feature, + # we do build & test & release in one step. + build: + machine: true + # Build for all tags + tags: + only: /.*/ + # Build for all branches + branches: + only: /.*/ + steps: + - checkout + - run: + # The docker commands sometimes take a while to produce output + no_output_timeout: 30m + command: | + # CircleCI has no mechanism to hide secret variables. + # Therefore we roll our own to protect $SECRET_* variables. + . .ci/protect-secrets.sh + # Collect debug infos about the system we are running on + .ci/describe-system.sh + # Set MAKEOPTS and SAGE_NUM_THREADS + . .ci/setup-make-parallelity.sh + + # Set DOCKER_TAG according to the current branch/tag + export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} + . .ci/update-env.sh + + # Select ARTIFACT_BASE depending on the current branch/tag + if [ -n "$CIRCLE_TAG" ] || [ $DOCKER_TAG = "master" ] || [ $DOCKER_TAG = "develop" ];then + export ARTIFACT_BASE=source-clean + else + # Select sagemath/sagemath-dev:develop as the ARTIFACT_BASE + # unless the user has explicitly selected a differnt one as a + # CircleCI environment variable. + export ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} + fi + + # Build docker images + .ci/build-docker.sh + + # Test that the images work + .ci/test-dev.sh $DOCKER_IMAGE_DEV + .ci/test-cli.sh $DOCKER_IMAGE_CLI + .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + + # Push docker images to dockerhub if a dockerhub user has been configured + .ci/push-dockerhub.sh sagemath-dev + .ci/push-dockerhub.sh sagemath diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 00000000000..3e4e48b0b5f --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 101d349690c..c98ce30b813 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,14 @@ $RECYCLE.BIN/ ########### .ipynb_checkpoints Untitled*.ipynb + +############################# +# GitLab CI generated files # +############################# +gitlab-build-docker.log + +/src/.cython_version +/src/build +/src/Makefile +/src/bin/sage-env-config + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..dcf46066619 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,168 @@ +# This file configures automatic builds of Sage on [GitLab](https://gitlab.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:develop. When basic SPKGs changed, this does not help +# much and the full build might exceed the set time limit in GitLab. You can +# increase that limit in Settings → CI/CD. +# You can also provision your own private more powerful runner in the same +# place +# https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; +# or set up your favourite cloud service to provide an on-demand autoscale +# runner. More details below. + +# As of early 2018 a run on GitLab CI takes about 45 minutes. We could probably +# save 10 minutes by not building/pushing/testing dev images for branches other +# than master/develop. + +# Note that most of the time during CI is spent with pulling and pushing of +# docker images and copying files locally as part of the docker build. At the +# moment there is no reliable way of passing the docker images to the following +# stages without explicit pushing/pulling or similar: +# https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 + +# The timings mentioned above are typical values. The shared runners provided +# on gitlab.com are sometimes much slower depending on the runner you are +# scheduled on. Sometimes it's slower for no apparent reason, probably just an +# overcommittment of virtual machines on hardware. + +# GitLab provides several flavours of shared runners (as of early 2018): +# * runners tagged as "do" (digitalocean.com) provide about 60GB of HDD, two +# cores, but only 2GB of RAM. The RAM is sometimes not sufficient to build +# the documentation. +# * runners tagged as "gce" (Google Compute Engine) provide about 22GB of HDD, +# a single core, 4GB of RAM. Since we are relying on OverlayFS, the disk +# space is not sufficient to build sage from scratch. + +# If you want to provide your own runners, make sure to tag them as follows: +# * "do" (60GB of disk space are available) to make build-from-clean pass. + +image: docker:latest + +stages: + - build + - test + - release + +variables: + DOCKER_TAG: $CI_COMMIT_REF_NAME + # Builds are very I/O intensive; make sure we have a fast file system. + DOCKER_DRIVER: overlay2 + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop + +before_script: + # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 + # So we roll our own which protects all variables that start with SECRET_ + - . .ci/protect-secrets.sh + # Collect debug infos about the system we are running on + - .ci/describe-system.sh + # Set DOCKER_TAG according to the current branch/tag + - . .ci/update-env.sh + # Set MAKEOPTS and SAGE_NUM_THREADS according to the machine we are running on + - . .ci/setup-make-parallelity.sh + +# We use docker-in-docker to build our docker images, i.e., we run a +# docker:dind "service" container and link to it from the container running the +# actual scripts below. +# Our scripts automatically connect to this service (unless you override it by +# setting DOCKER_HOST.) For example, each RUN statement in the Dockerfile +# spawns a docker container inside the docker:dind container to perform the RUN +# command there. +# It can be faster to expose your outer docker daemon by mounting +# /var/run/docker.sock to /var/run/docker.sock and setting DOCKER_HOST in +# Settings -> CI/CD -> Secret variable to unix:///var/run/docker.sock. (The +# speedup is mostly due to sharing layers of intermediate images.) However, +# this is only possible if you provision your own runners. Shared gitlab +# runners, do not bind mount /var/run/docker.sock. Also, docker:dind provides +# 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:dind + +# Build Sage and its documentation. +# The build starts from the build artifacts of ARTIFACT_BASE which is usually +# much faster than building from a clean checkout of Sage. +build-from-latest: &build + stage: build + variables: + ARTIFACT_BASE: $DEFAULT_ARTIFACT_BASE + artifacts: + when: always + paths: + - gitlab-build-docker.log + expire_in: 1 month + script: + - apk update && apk add coreutils + # The output of the build can get larger than gitlab.com's limit; only print the first 3MB (and the last 80 lines.) + - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 + - .ci/push-gitlab.sh sagemath-dev + - .ci/push-gitlab.sh sagemath + except: + - master + - develop + - tags + +# Build Sage and its documentation from a clean checkout of Sage. +# Note that this takes several hours. You probably want to run this on your own +# gitlab-runner and increase the standard GitLab time limit for CI runs. +# Some of the shared runners provided by GitLab for free do not have enough +# disk space for this to work. If a build fails with "no space left on device", +# you could just retry it and hope to be scheduled on a machine with more disk +# space, or provision your own runner. +build-from-clean: + << : *build + variables: + ARTIFACT_BASE: "source-clean" + only: + - master + - develop + - tags + except: [] + tags: + # 60 GB of HDD are available + - do + +test-dev: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath-dev + - sh .ci/test-dev.sh "$DOCKER_IMAGE" + +test-cli: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath + - sh .ci/test-cli.sh "$DOCKER_IMAGE" + +test-jupyter: + stage: test + script: + # Force usage of docker-in-docker (and don't start docker on a bind-mounted + # /var/run/docker.sock set through a private GitLab CI variable) so that + # the -p flag to docker run works as expected. + - export DOCKER_HOST='tcp://docker:2375' + - apk update + - apk add wget + - . .ci/pull-gitlab.sh sagemath + - sh .ci/test-jupyter.sh "$DOCKER_IMAGE" docker + +# Pushes the built images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +push-dockerhub: + stage: release + only: + - branches + - tags + script: + - . .ci/pull-gitlab.sh sagemath + - sh .ci/push-dockerhub.sh sagemath + +# Pushes the built dev images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +push-dockerhub-dev: + stage: release + only: + - master + - develop + - tags + script: + - . .ci/pull-gitlab.sh sagemath-dev + - sh .ci/push-dockerhub.sh sagemath-dev diff --git a/Makefile b/Makefile index d81d450ef4e..89ac3e4ecf1 100644 --- a/Makefile +++ b/Makefile @@ -104,9 +104,44 @@ bootstrap-clean: maintainer-clean: distclean bootstrap-clean rm -rf upstream +# Remove everything that is not necessary to run Sage and pass all its +# doctests. micro_release: bdist-clean sagelib-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..." + rm -rf local/share/doc/sage/doctrees local/share/doc/sage/inventory + @echo "Removing documentation. Inspection in IPython still works." + rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html + @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" + @# We need src/doc/common, src/doc/en/introspect for introspection with "??" + @# We keep src/sage for some doctests that it expect it to be there and + @# also because it does not add any weight with rdfind below. + @# We need src/sage/bin/ for the scripts that invoke Sage + @# We need sage, the script to start Sage + @# We need local/, the dependencies and the built Sage library itself. + @# We keep VERSION.txt. + @# We keep COPYING.txt so we ship a license with this distribution. + find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; + cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; + if command -v rdfind > /dev/null; then \ + echo "Hardlinking identical files."; \ + rdfind -makeresultsfile false -makehardlinks true .; \ + else \ + echo "rdfind not installed. Not hardlinking identical files."; \ + fi + +# Leaves everything that is needed to make the next "make" fast but removes +# all the cheap build artifacts that can be quickly regenerated. +fast-rebuild-clean: misc-clean bdist-clean + rm -rf upstream/ + rm -rf src/build/temp.* + # Without site-packages/sage sage does not start but copying/compiling + # them from src/build is very fast. + rm -rf local/lib/python*/site-packages/sage + # The .py files in src/build are restored from src/sage without their + # mtimes changed. + find src/build -name '*.py' -exec rm \{\} \; TESTALL = ./sage -t --all PTESTALL = ./sage -t -p --all diff --git a/configure.ac b/configure.ac index 569caf911cf..6a948395098 100644 --- a/configure.ac +++ b/configure.ac @@ -285,9 +285,9 @@ fi AC_CHECK_PROG(found_latex, latex, yes, no) if test x$found_latex != xyes then - AC_MSG_WARN([You do not have 'latex', which is recommended, but not]) - AC_MSG_WARN([required. Latex is only really used for building pdf]) - AC_MSG_WARN([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) + AC_MSG_NOTICE([You do not have 'latex', which is recommended, but not]) + AC_MSG_NOTICE([required. Latex is only really used for building pdf]) + AC_MSG_NOTICE([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) fi # Check that perl is available, with version 5.8.0 or later. diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 00000000000..579da245b87 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +# Stores the commit that was used to create a sagemath-dev image +.commit diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000000..325e5d7f922 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,257 @@ +################################################################################ +# SageMath images for Docker # +################################################################################ +# This is a description of the layout of this Dockerfile; for details on the # +# created docker images, see the README.md please. # +# # +# This Dockerfile builds sagemath (for end-users) and sagemath-dev (for # +# developers.) It consists of lots of intermediate targets, mostly to shrink # +# the resulting images but also to make this hopefully easier to maintain. # +# The aims of this Dockerfile are: # +# (1) Make it build in reasonable time. # +# (2) It should be self-contained and work on its own, i.e., just by invoking # +# docker build without any external orchestration script. # +# # +# The idea to achieve (1) is to reuse the build artifacts from the latest # +# develop build. This is slightly against the philosophy of a Dockerfile (which# +# should produce perfectly reproducible outputs) but building Sage from scratch# +# just takes too long at the moment to do this all the time. ARTIFACT_BASE # +# controls which build artifacts are used. You probably want to set this to # +# sagemath/sagemath-dev:develop which takes the latest build from the official # +# develop branch. The default is source-clean which builds Sage from scratch. # +# If you want to understand how this works, have a look at source-from-context # +# which merges ARTIFACT_BASE with the context, i.e., the contents of the sage # +# source directory. # +################################################################################ + +################################################################################ +# HOWTO use this file for local builds # +################################################################################ +# If you don't mind downloading a 2GB docker image from time to time, you # +# could use this file for local Sage development. As of early 2018 each build # +# takes about five minutes but you don't have to go through the sadly frequent # +# rebuilds the whole Sage distribution... # +# To build Sage, run this command from your sage/ directory: # +# $ docker build --build-arg MAKEOPTS="-j4" --build-arg SAGE_NUM_THREADS="4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . +# To run Sage: # +# $ docker run -it sage # +# To run doctests: # +# $ docker run -e "MAKEOPTS=-j4" -e "SAGE_NUM_THREADS=4" -it sage sage -tp src/sage +# Make sure that you always have the latest develop branch merged into your # +# local branch for this to work. # +################################################################################ + +ARG ARTIFACT_BASE=source-clean + +################################################################################ +# Image containing the run-time dependencies for Sage # +################################################################################ +FROM ubuntu:xenial as run-time-dependencies +LABEL maintainer="Erik M. Bray , Julian Rüth " +# Set sane defaults for common environment variables. +ENV LC_ALL C.UTF-8 +ENV LANG C.UTF-8 +ENV SHELL /bin/bash +# Create symlinks for sage and sagemath - we copy a built sage to the target of these symlinks later. +ARG SAGE_ROOT=/home/sage/sage +RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage +RUN ln -s /usr/bin/sage /usr/bin/sagemath +# Sage needs the fortran libraries at run-time because we do not build gfortran +# with Sage but use the system's. +# We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. +# We also install sudo for the sage user, see below. +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ + && apt-get -qq clean \ + && rm -r /var/lib/apt/lists/* +# Sage refuses to build as root, so we add a "sage" user that can sudo without a password. +# We also want this user at runtime as some commands in sage know about the user that was used during build. +ARG HOME=/home/sage +RUN adduser --quiet --shell /bin/bash --gecos "Sage user,101,," --disabled-password --home "$HOME" sage \ + && echo "sage ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/01-sage \ + && chmod 0440 /etc/sudoers.d/01-sage +# Run everything from now on as the sage user in sage's home +USER sage +ENV HOME $HOME +WORKDIR $HOME + +################################################################################ +# Image containing everything so that a make in a clone of the Sage repository # +# completes without errors # +################################################################################ +FROM run-time-dependencies as build-time-dependencies +# Install the build time dependencies & git & rdfind +RUN sudo apt-get -qq update \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ + && sudo apt-get -qq clean \ + && sudo rm -r /var/lib/apt/lists/* + +################################################################################ +# Image with an empty git repository in $SAGE_ROOT. # +################################################################################ +FROM build-time-dependencies as source-clean +ARG SAGE_ROOT=/home/sage/sage +RUN mkdir -p "$SAGE_ROOT" +WORKDIR $SAGE_ROOT +RUN git init +RUN git remote add trac git@trac.sagemath.org:sage.git + +################################################################################ +# Image with the build context added, i.e., the directory from which `docker # +# build` has been called in a separate directory so we can copy files from # +# there. # +# This blows up the size of this docker image significantly, but we only use # +# this image to create artifacts for our final image. # +# Warning: If you set ARTIFACT_BASE to something else than source-clean, the # +# build is not going to use the build-time-dependencies target but rely on # +# whatever tools are installed in ARTIFACT_BASE. # +################################################################################ +FROM $ARTIFACT_BASE as source-from-context +WORKDIR $HOME +COPY --chown=sage:sage . sage-context +# Checkout the commit that checked out in $HOME/sage-context +# This is a bit complicated because our local .git/ is empty and we want to +# make sure that we only change the mtimes of a minimal number of files. +# 1) Restore the git checkout ARTIFACT_BASE was built from, recorded in +# docker/.commit. (Or go directly to FETCH_HEAD if there is no history to +# restore, i.e., set ARTIFACT_BASE=source-clean if you want to build from +# scratch.) +# 2) Merge in FETCH_HEAD but only if it is a fast-forward, i.e., if it is an +# ancestor of the commit restored in 1. If we would not do that we would get +# a new commit hash in docker/.commit that is not known outside of this build +# run. Since docker/.commit was in the history of FETCH_HEAD this should +# automatically be a fast-forward. +# 3) Trash .git again to save some space. +ARG SAGE_ROOT=/home/sage/sage +WORKDIR $SAGE_ROOT +# We create a list of all files present in the artifact-base (with a timestamp +# of now) so we can find out later which files were added/changed/removed. +RUN find . -type f > $HOME/artifact-base.manifest +RUN git fetch "$HOME/sage-context" HEAD \ + && if [ -e docker/.commit ]; then \ + git reset `cat docker/.commit` \ + || ( echo "Could not find commit `cat docker/.commit` in your local Git history. Please merge in the latest built develop branch to fix this: git fetch trac && git merge `cat docker/.commit`." && exit 1 ) \ + else \ + echo "You are building from $ARTIFACT_BASE which has no docker/.commit file. That's a bug unless you are building from source-clean or something similar." \ + && git reset FETCH_HEAD \ + && git checkout -f FETCH_HEAD; \ + fi \ + && git merge --ff-only FETCH_HEAD \ + && git log -1 --format=%H > docker/.commit \ + && rm -rf .git +# Copy over all the untracked/staged/unstaged changes from sage-context. This +# is relevant for non-CI invocations of this Dockerfile. +WORKDIR $HOME/sage-context +RUN if git status --porcelain | read CHANGES; then \ + git -c user.name=docker-build -c user.email=docker-build@sage.invalid stash -u \ + && git stash show -p > "$HOME"/sage-context.patch; \ + else \ + touch "$HOME"/sage-context.patch; \ + fi +WORKDIR $SAGE_ROOT +RUN patch -p1 < "$HOME"/sage-context.patch + +################################################################################ +# Image with a built sage but without sage's documentation. # +################################################################################ +FROM source-from-context as make-build +# Make sure that the result runs on most CPUs. +ENV SAGE_FAT_BINARY yes +# Just to be sure Sage doesn't try to build its own GCC (even though +# it shouldn't with a recent GCC package from the system and with gfortran) +ENV SAGE_INSTALL_GCC no +# Set MAKEOPTS and SAGE_NUM_THREADS to build things in parallel during the +# docker build. Note that these do not leak into the sagemath and sagemath-dev +# images. +ARG MAKEOPTS="-j2" +ENV MAKEOPTS $MAKEOPTS +ARG SAGE_NUM_THREADS="2" +ENV SAGE_NUM_THREADS $SAGE_NUM_THREADS +RUN make build + +################################################################################ +# Image with a full build of sage and its documentation. # +################################################################################ +FROM make-build as make-all +# The docbuild needs quite some RAM (as of May 2018). It sometimes calls +# os.fork() to spawn an external program which then exceeds easily the +# overcommit limit of the system (no RAM is actually used, but this limit is +# very low because there is not even swap on most CI systems.) +ARG MAKEOPTS_DOCBUILD=$MAKEOPTS +ENV MAKEOPTS_DOCBUILD $MAKEOPTS_DOCBUILD +ARG SAGE_NUM_THREADS_DOCBUILD=$SAGE_NUM_THREADS +ENV SAGE_NUM_THREADS_DOCBUILD $SAGE_NUM_THREADS_DOCBUILD +RUN make + +################################################################################ +# Image with a full build of sage, ready to release, i.e., with stripped # +# binaries and some extras to run the jupyter notebook. # +################################################################################ +FROM make-all as make-release +RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" +RUN sage -i gap_jupyter singular_jupyter pari_jupyter +RUN make micro_release + +################################################################################ +# A releasable (relatively small) image which contains a copy of sage without # +# temporary build artifacts which is set up to start the command line # +# interface if no parameters are passed in. # +################################################################################ +FROM run-time-dependencies as sagemath +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ +# Put scripts to start gap, gp, maxima, ... in /usr/bin +WORKDIR $SAGE_ROOT +RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" +COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +EXPOSE 8888 +CMD ["sage"] + +################################################################################ +# Image with a full build of sage and its documentation but everything # +# stripped that can be quickly rebuild by make. # +################################################################################ +FROM make-all as make-fast-rebuild-clean +RUN make fast-rebuild-clean + +################################################################################ +# Depending on whether we built from source-clean or not, this image is either # +# identical to make-fast-rebuild-clean or contains a "patch" which can be used # +# to upgrade ARTIFACT_BASE to make-fast-rebuild-clean. # +################################################################################ +FROM make-fast-rebuild-clean as sagemath-dev-patch +ARG ARTIFACT_BASE=source-clean +ARG SAGE_ROOT=/home/sage/sage +# Build a patch containing of a tar file which contains all the modified files +# and a list of all modified files (added/updated/removed). +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + mkdir -p $HOME/patch \ + && find . -type f > $HOME/make-fast-rebuild-clean.manifest \ + && cat $HOME/make-fast-rebuild-clean.manifest $HOME/artifact-base.manifest | sort | uniq -u > $HOME/obsolete \ + && find . -type f -cnewer $HOME/artifact-base.manifest > $HOME/modified \ + && tar -cJf $HOME/patch/modified.tar.xz -T $HOME/modified \ + && cat $HOME/obsolete $HOME/modified | xz > $HOME/patch/modified.xz \ + && rm -rf $SAGE_ROOT \ + && mkdir -p $SAGE_ROOT \ + && mv $HOME/patch $SAGE_ROOT/; \ + fi + +################################################################################ +# A releasable (relatively small, but still huge) image of this build with all # +# the build artifacts intact so developers can make changes and rebuild # +# quickly # +################################################################################ +FROM $ARTIFACT_BASE as sagemath-dev +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT +ARG ARTIFACT_BASE=source-clean +# Apply the patch from sagemath-dev-patch if we created one. +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + xzcat patch/modified.xz | xargs rm -rf \ + && tar -Jxf patch/modified.tar.xz \ + && rm -rf patch; \ + fi +COPY ./docker/entrypoint-dev.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +CMD ["bash"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000000..b9f9240f9e4 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,55 @@ +[![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/2018.svg)](https://github.com/sagemath/sage/commits/master) + +# Supported tags + +* `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) +* `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) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) + + +# What is SageMath + +SageMath is a free open-source mathematics software system licensed under the GPL. It builds on top of many existing open-source packages: NumPy, SciPy, matplotlib, Sympy, Maxima, GAP, FLINT, R and many more. Access their combined power through a common, Python-based language or directly via interfaces or wrappers. + +**Mission**: *Creating a viable free open source alternative to Magma, Maple, Mathematica and Matlab.* + +# What's in this image + +There are several flavours of this image. + +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath/latest.svg)](https://hub.docker.com/r/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: + ``` + docker run -it sagemath/sagemath:latest + ``` + You can start a graphical [Jupyter Notebook](https://jupyter.org) at http://localhost:8888 instead. To use the notebook, follow the instructions printed when you run: + ``` + docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter + ``` +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: + ``` + docker run -it sagemath/sagemath-dev:develop + ``` + This triggers a rebuild and drops you in a shell afterwards. Note that the git repository has been emptied to save space. If you want to use git, fetch from your git repository with `git fetch trac` and go to the commit that was used to create this image with + ``` + git reset $(cat docker/.commit) + ``` + +# How to build your own SageMath images + +Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=sagemath/sagemath-dev:develop --target TARGET .` in the Sage repository with `TARGET` one of `sagemath` or `sagemath-dev`. + +# How these images get updated + +Every push to our [github repository](https://github.com/sagemath/sage) triggers a build in [CircleCI](https://circleci.com) which builds and pushes the docker images. +A push to master also triggers a "build" on our [Docker Hub](https://hub.docker.com) repositories. The latter build is mostly disabled by the `hooks/` and only updates the `README.md`. + +Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) triggers a pipeline in GitLab CI. This build also pushes images to Docker Hub. + +Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup either continuous integration service for your own fork of the SageMath repository. + +# License + +The whole Sage software distribution is licensed under the General Public License, version 3. More details can be found in our [COPYING.txt](https://github.com/sagemath/sage/blob/master/COPYING.txt) + +[//]: # (Please don't break long lines in this files as dockerhub then gets the formatting of this file wrong.) diff --git a/docker/entrypoint-dev.sh b/docker/entrypoint-dev.sh new file mode 100755 index 00000000000..67299a5a92d --- /dev/null +++ b/docker/entrypoint-dev.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +make build +exec "$@" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 00000000000..bc841382eaf --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash +if [ x"$1" = x"sage-jupyter" ]; then + # If "sage-jupyter" is given as a first argument, we start a jupyter notebook + # with reasonable default parameters for running it inside a container. + shift + exec sage -n jupyter --no-browser --ip='*' --port=8888 "$@" +else + exec sage -sh -c "$*" +fi diff --git a/docker/hooks/build b/docker/hooks/build new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/build @@ -0,0 +1 @@ +#!/bin/true diff --git a/docker/hooks/push b/docker/hooks/push new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/push @@ -0,0 +1 @@ +#!/bin/true diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index e85aa7f89e4..00000000000 --- a/src/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.cython_version -/build -/Makefile -/bin/sage-env-config diff --git a/src/Makefile.in b/src/Makefile.in index 81a28b3758e..0233601cbbc 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -37,7 +37,7 @@ sage: SAGE_DOC_SRC=/doesnotexist \ SAGE_BUILD_DIR=/doesnotexist \ SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ - && sage-python23 -u setup.py --no-user-cfg build install + && sage-python23 -u setup.py --quiet --no-user-cfg build install if [ "$$UNAME" = "CYGWIN" ]; then \ sage-rebase.sh "$$SAGE_LOCAL" 2>/dev/null; \ fi diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 9e57f8c761a..a9c4eb0fb6c 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -624,7 +624,7 @@ def call_intersphinx(app, env, node, contnode): sage: from sage.env import SAGE_DOC sage: thematic_index = os.path.join(SAGE_DOC, "html", "en", "thematic_tutorials", "index.html") - sage: for line in open(thematic_index).readlines(): + sage: for line in open(thematic_index).readlines(): # requires a built documentation, optional: doc ....: if "padics" in line: ....: sys.stdout.write(line)
  • Introduction to the -adics
  • diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 9255aa848ff..ec2c435cd8a 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -20,7 +20,7 @@ sage: from sage.env import SAGE_DOC sage: docfilename = os.path.join(SAGE_DOC, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html') - sage: with open(docfilename) as fobj: + sage: with open(docfilename) as fobj: # requires a built documentation, optional: doc ....: for line in fobj: ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) @@ -1332,7 +1332,7 @@ class _sage_doc: EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest + sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest, requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] 'Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring' @@ -1494,9 +1494,9 @@ def _open(self, name, testing=False): EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] + sage: browse_sage_doc._open("reference", testing=True)[0] # requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' - sage: browse_sage_doc._open("tutorial", testing=True)[1] + sage: browse_sage_doc._open("tutorial", testing=True)[1] # requires a built documentation, optional: doc '.../html/en/tutorial/index.html' """ url = self._base_url + os.path.join(name, "index.html") diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 5877b5b2a76..7dc8ff783c5 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -265,35 +265,29 @@ def clean(self, *args): # import the customized builder for object.inv files inventory = builder_helper('inventory') -if NUM_THREADS > 1: - def build_many(target, args): - from multiprocessing import Pool - pool = Pool(NUM_THREADS, maxtasksperchild=1) - # map_async handles KeyboardInterrupt correctly. Plain map and - # apply_async does not, so don't use it. - x = pool.map_async(target, args, 1) - try: - ret = x.get(99999) - pool.close() - pool.join() - except Exception: - pool.terminate() - if ABORT_ON_ERROR: - raise - return ret -else: - def build_many(target, args): - results = [] - - for arg in args: - try: - results.append(target(arg)) - except Exception: - if ABORT_ON_ERROR: - raise - - return results - +def build_many(target, args): + # Pool() uses an actual fork() to run each new instance. This is important + # for performance reasons, i.e., don't use a forkserver when it becomes + # available with Python 3: Here, sage is already initialized which is quite + # costly, with a forkserver we would have to reinitialize it for every + # document we build. At the same time, don't serialize this by taking the + # pool (and thus the call to fork()) out completely: The call to Sphinx + # leaks memory, so we need to build each document in its own process to + # control the RAM usage. + from multiprocessing import Pool + pool = Pool(NUM_THREADS, maxtasksperchild=1) + # map_async handles KeyboardInterrupt correctly. Plain map and + # apply_async does not, so don't use it. + x = pool.map_async(target, args, 1) + try: + ret = x.get(99999) + pool.close() + pool.join() + except Exception: + pool.terminate() + if ABORT_ON_ERROR: + raise + return ret ########################################## # Parallel Building Ref Manual # @@ -940,7 +934,12 @@ def get_new_and_updated_modules(self): except ImportError as err: logger.error("Warning: Could not import %s %s", module_name, err) raise - newtime = os.path.getmtime(sys.modules[module_name].__file__) + + module_filename = sys.modules[module_name].__file__ + if (module_filename.endswith('.pyc') or module_filename.endswith('.pyo')): + source_filename = module_filename[:-1] + if (os.path.exists(source_filename)): module_filename = source_filename + newtime = os.path.getmtime(module_filename) if newtime > mtime: updated_modules.append(module_name) diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index f241e09f8a5..2c399bad51b 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -988,7 +988,12 @@ def generate(self, more_content=None, real_modname=None, self.analyzer.find_attr_docs() except PycodeError as err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) - # no source file -- e.g. for builtin and C modules + # A few things could have happened here: + # * there is no source file -- e.g. for builtin and C modules + # * the source file contains syntax that Sphinx can not parse, + # e.g., "print(1, end=' ')"; see + # https://github.com/sphinx-doc/sphinx/issues/1641, + # fixed in Sphinx 1.7. self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index fda76a41748..a7a6afa81ec 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -1,8 +1,25 @@ -""" +# -*- coding: utf-8 -*- +r""" This is Sage's version of the sphinx-build script -We redirect stdout to our own logger, and remove some unwanted chatter. +We redirect stdout and stderr to our own logger, and remove some unwanted chatter. """ +# **************************************************************************** +# Copyright (C) 2013-2014 Volker Braun +# 2013-2017 J. H. Palmieri < +# 2013-2017 Jeroen Demeyer +# 2014 Christopher Schwan +# 2014 Nicolas M. Thiéry +# 2015 Marc Mezzarobba +# 2015 André Apitzsch +# 2018 Julian Rüth +# +# 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, sys, re, sphinx @@ -14,9 +31,9 @@ def term_width_line(text): class SageSphinxLogger(object): - """ - This implements the file object interface to serve as sys.stdout - replacement. + r""" + This implements the file object interface to serve as + ``sys.stdout``/``sys.stderr`` replacement. """ ansi_color = re.compile(r'\x1b\[[0-9;]*m') ansi_reset = re.compile(r'\x1b\[39;49;00m') @@ -37,28 +54,63 @@ def __init__(self, stream, prefix): else: color = 'lightgray' self._prefix = sphinx.util.console.colorize(color, prefix) + # When we see an error in the log, we store it here and raise it at the + # end of the file (sometimes the lines following the error still + # contain valuable information.) + self._error = None def _init_chatter(self): - # useless_chatter: regular expressions to be filtered from - # Sphinx output. - self.useless_chatter = ( + # We drop any messages from the output that match these regular + # expressions. These just bloat the output and do not contain any + # information that we care about. + self._useless_chatter = ( re.compile('^$'), re.compile('^Running Sphinx v'), re.compile('^loading intersphinx inventory from '), + re.compile('^loading pickled environment... done'), + re.compile('^loading cross citations... done \([0-9]* citations\).'), re.compile('^Compiling a sub-document'), re.compile('^updating environment: 0 added, 0 changed, 0 removed'), re.compile('^looking for now-outdated files... none found'), re.compile('^building \[.*\]: targets for 0 source files that are out of date'), - re.compile('^loading pickled environment... done'), - re.compile('^loading cross citations... done \([0-9]* citations\).'), + re.compile('^building \[.*\]: targets for 0 po files that are out of date'), + re.compile('^building \[.*\]: targets for 0 mo files that are out of date'), + re.compile('^pickling environment... done'), + re.compile('^dumping object inventory... done'), + # We still have "Build finished." + re.compile('^build succeeded.'), + re.compile('^checking consistency... done'), + re.compile('^preparing documents... done'), + re.compile('^copying extra files... done'), + re.compile('^writing additional pages... search'), + re.compile('^Writing js search indexes...writing additional pages... .*'), + re.compile('^generating indices... .*'), + re.compile('^dumping search index in .* ... done'), + re.compile('^linking _static directory'), + re.compile('^copying static files... done'), + re.compile('^copying extra files... done'), + re.compile('^loading translations \[.*\]... done'), + re.compile('^Compiling the master document'), + re.compile('^Saved pickle file: citations.pickle'), + re.compile('^writing output... \[.*\] '), + re.compile('^copying images... \[.*\] '), + re.compile('^reading sources... \[.*\] '), + re.compile('language "hu" not supported'), + re.compile('^$'), + re.compile('^WARNING:$'), + ) + + # We fail whenever a line starts with "WARNING:", however, we ignore + # these warnings, as they are not relevant. + self._ignored_warnings = ( re.compile('WARNING: favicon file \'favicon.ico\' does not exist'), re.compile('WARNING: html_static_path entry .* does not exist'), re.compile('WARNING: while setting up extension'), re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), - re.compile('language "hu" not supported'), ) + self._useless_chatter += self._ignored_warnings # replacements: pairs of regular expressions and their replacements, # to be applied to Sphinx output. @@ -68,14 +120,16 @@ def _init_chatter(self): if 'inventory' in sys.argv: # When building the inventory, ignore warnings about missing # citations and the search index. - self.useless_chatter += ( + ignored = ( re.compile('WARNING: citation not found:'), re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') ) + self._ignored_warnings += ignored + self._useless_chatter += ignored - # warnings: regular expressions (or strings) indicating a problem with - # docbuilding. Raise an exception if any of these occur. - self.warnings = (re.compile('Segmentation fault'), + # Regular expressions indicating a problem with docbuilding. Raise an + # exception if any of these occur. + self._error_patterns = (re.compile('Segmentation fault'), re.compile('SEVERE'), re.compile('ERROR'), re.compile('^make.*Error'), @@ -88,66 +142,121 @@ def _init_chatter(self): # - undefined labels upon the first pass of the compilation: some # cross links may legitimately not yet be resolvable at this point. if 'latex' not in sys.argv: + self._error_patterns += (re.compile('WARNING:'),) if 'multidoc_first_pass=1' in sys.argv: - # Catch all warnings except 'WARNING: undefined label' - self.warnings += (re.compile('WARNING: (?!undefined label)'),) - else: - self.warnings += (re.compile('WARNING:'),) + ignore = (re.compile('WARNING: undefined label'),) + self._ignored_warnings += ignore + self._useless_chatter += ignore def _filter_out(self, line): - if exception is not None and self._is_stdout: + if self._error and self._is_stdout: # swallow non-errors after an error occurred return True line = re.sub(self.ansi_color, '', line) - for regex in self.useless_chatter: + line = line.strip() + for regex in self._useless_chatter: if regex.search(line) is not None: return True return False - def _check_warnings(self, line): - global exception - if exception is not None: + def _check_errors(self, line): + r""" + Search for errors in line. + + EXAMPLES:: + + sage: from sys import stdout + sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: logger = SageSphinxLogger(stdout, "doctesting") + sage: logger._log_line("Segmentation fault!\n") # indirect doctest + [doctestin] Segmentation fault! + sage: logger.raise_errors() + Traceback (most recent call last): + ... + OSError: Segmentation fault! + + """ + if self._error: return # we already have found an error - for regex in self.warnings: - if regex.search(line) is not None: - exception = OSError(line) - return + for error in self._error_patterns: + if error.search(line) is not None: + for ignored in self._ignored_warnings: + if ignored.search(line) is not None: + break + else: + self._error = line + return def _log_line(self, line): - if self._filter_out(line): - return + r""" + Write ``line`` to the output stream with some mangling. + + EXAMPLES:: + + sage: from sys import stdout + sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: logger = SageSphinxLogger(stdout, "doctesting") + sage: logger._log_line("building documentation…\n") + [doctestin] building documentation… + + TESTS: + + Verify that :trac:`25160` has been resolved:: + + sage: logger = SageSphinxLogger(stdout, "#25160") + sage: raise Exception("artificial exception") + Traceback (most recent call last): + ... + Exception: artificial exception + sage: import traceback + sage: for line in traceback.format_exc().split('\n'): + ....: logger._log_line(line) + [#25160 ] Traceback (most recent call last): + [#25160 ] File ... + [#25160 ] self.compile_and_execute(example, compiler, test.globs) + [#25160 ] File ... + [#25160 ] exec(compiled, globs) + [#25160 ] File ... + [#25160 ] raise Exception("artificial exception") + [#25160 ] Exception: artificial exception + + """ + skip_this_line = self._filter_out(line) + self._check_errors(line) for (old, new) in self.replacements: line = old.sub(new, line) - line = self._prefix + ' ' + line.strip() + '\n' + line = self._prefix + ' ' + line.rstrip() + '\n' if not self._color: line = self.ansi_color.sub('', line) - self._stream.write(line) - self._stream.flush() - self._check_warnings(line) + if not skip_this_line: + self._stream.write(line) + self._stream.flush() + + def raise_errors(self): + r""" + Raise an exceptions if any errors have been found while parsing the + Sphinx output. + + EXAMPLES:: + + sage: from sys import stdout + sage: from sage_setup.docbuild.sphinxbuild import SageSphinxLogger + sage: logger = SageSphinxLogger(stdout, "doctesting") + sage: logger._log_line("This is a SEVERE error\n") + [doctestin] This is a SEVERE error + sage: logger.raise_errors() + Traceback (most recent call last): + ... + OSError: This is a SEVERE error - _line_buffer = '' - - def _break_long_lines(self): - """ - Break text that has been formated with string.ljust() back - into individual lines. Return partial output. Do nothing if - the filter rule matches, otherwise subsequent lines would be - not filtered out. """ - if self._filter_out(self._line_buffer): - return - cols = sphinx.util.console._tw - lines = [] - buf = self._line_buffer - while len(buf) > cols: - lines.append(buf[0:cols]) - buf = buf[cols:] - lines.append(buf) - self._line_buffer = '\n'.join(lines) + if self._error: + raise OSError(self._error) + + _line_buffer = '' def _write(self, string): self._line_buffer += string - #self._break_long_lines() lines = self._line_buffer.splitlines() for i, line in enumerate(lines): last = (i == len(lines)-1) @@ -193,12 +302,6 @@ def writelines(self, sequence): def runsphinx(): - # Do not error out at the first warning, sometimes there is more - # information. So we run until the end of the file and only then - # raise the error. - global exception - exception = None - output_dir = sys.argv[-1] saved_stdout = sys.stdout @@ -207,12 +310,16 @@ def runsphinx(): try: sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) + # Note that this call as of eraly 2018 leaks memory. So make sure that + # you don't call runsphinx() several times in a row. (i.e., you want to + # fork() somewhere before this call.) + # We don't use subprocess here, as we don't want to re-initialize Sage + # for every docbuild as this takes a while. sphinx.cmdline.main(sys.argv) + sys.stderr.raise_errors() + sys.stdout.raise_errors() finally: sys.stdout = saved_stdout sys.stderr = saved_stderr sys.stdout.flush() sys.stderr.flush() - - if exception is not None: - raise exception From 90ab8f67bf99b3fc988e562b8c82b051dbbc151a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sat, 19 May 2018 14:46:17 +0200 Subject: [PATCH 106/284] Put URL for anynomous access in the docker image so git fetch trac works without an SSH key --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 325e5d7f922..852dbb7af9d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -94,7 +94,7 @@ ARG SAGE_ROOT=/home/sage/sage RUN mkdir -p "$SAGE_ROOT" WORKDIR $SAGE_ROOT RUN git init -RUN git remote add trac git@trac.sagemath.org:sage.git +RUN git remote add trac git://trac.sagemath.org/sage.git ################################################################################ # Image with the build context added, i.e., the directory from which `docker # From 686fe86c7fb1f13f3d9d6ece7828107ff988e7c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 20 May 2018 22:56:23 +0200 Subject: [PATCH 107/284] An attempt to use workflows on CircleCI workflows do not really provide any advantage but they are necessary to build for a tagged branch, i.e., otherwise we can not tell CircleCI to build a for a git tag (such as 8.2) if we tell it to build for the git branch "master" at the same time. --- .ci/test-jupyter.sh | 5 +- .ci/update-env.sh | 6 ++- .circleci/before-script.sh | 26 ++++++++++ .circleci/config.yml | 103 ++++++++++++++++++++++--------------- .gitlab-ci.yml | 12 +---- 5 files changed, 96 insertions(+), 56 deletions(-) create mode 100644 .circleci/before-script.sh diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index 9df157b763b..b3ade38556a 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -16,9 +16,8 @@ # **************************************************************************** set -ex - -docker run --name sage-jupyter -p 8888:8888 -d "$1" sage-jupyter +docker run --name sage-jupyter -p -d "$1" sage-jupyter echo "Checking that the Jupyter notebook is running…" sleep 10 # giving the server some time to start docker logs sage-jupyter -wget --retry-connrefused --tries=10 --wait=3 "http://${2:-localhost}:8888" +docker run --link sage-jupyter alpine sh -c "apk --update add wget; wget --retry-connrefused --tries=10 --wait=3 http://sage-jupyter:8888" diff --git a/.ci/update-env.sh b/.ci/update-env.sh index ee6a2110a56..32ed5df6e8f 100755 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -1,7 +1,7 @@ #!/bin/sh # This script gets called from CI to establish the name of the current docker -# tag to build from the name of the branch/tag provided by CI. +# tag to build and also the image which is used to seed the cache. # **************************************************************************** # Copyright (C) 2018 Julian Rüth @@ -26,3 +26,7 @@ export DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' ' export DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG export DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG + +# 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/.circleci/before-script.sh b/.circleci/before-script.sh new file mode 100644 index 00000000000..51127362f15 --- /dev/null +++ b/.circleci/before-script.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Source this script during a CI run to set environment variables and print +# some informational messages about the system we are running on. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +# CircleCI has no mechanism to hide secret variables. +# Therefore we roll our own to protect $SECRET_* variables. +. .ci/protect-secrets.sh +# Collect debug infos about the system we are running on +.ci/describe-system.sh +# Set MAKEOPTS and SAGE_NUM_THREADS +. .ci/setup-make-parallelity.sh + +# Set DOCKER_TAG according to the current branch/tag +export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} +. .ci/update-env.sh diff --git a/.circleci/config.yml b/.circleci/config.yml index 27b27b1829c..65c00dcced9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,11 +5,6 @@ # source projcets (five hours on 2 vCPUs as of early 2018.) # You might want to try to build locally or with GitLab CI, see # `.gitlab-ci.yml` for more options. -# Note that we do not use workflows because it was not clear whether the docker -# images sizes would not exceed the size limits of CircleCI workspaces. Also, -# workflows would not make things faster. We do not get more machines for free -# and the frequent loading of docker images probably exceeds the cost of the -# actual tests. # As of early 2018, a run on CircleCI takes usually about 25 minutes. Most of # the time is spent pulling/pushing from/to Docker Hub and copying files @@ -18,52 +13,76 @@ version: 2 jobs: - # As https://circleci.com/docs/2.0/docker-layer-caching/ is a paid feature, - # we do build & test & release in one step. - build: - machine: true - # Build for all tags - tags: - only: /.*/ - # Build for all branches - branches: - only: /.*/ + build-test-release: &build-test-release + docker: + - image: docker:latest + environment: + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:latest steps: + - run: apk --update add git openssh - checkout - - run: + - setup_remote_docker + - run: &build # The docker commands sometimes take a while to produce output no_output_timeout: 30m + name: build command: | - # CircleCI has no mechanism to hide secret variables. - # Therefore we roll our own to protect $SECRET_* variables. - . .ci/protect-secrets.sh - # Collect debug infos about the system we are running on - .ci/describe-system.sh - # Set MAKEOPTS and SAGE_NUM_THREADS - . .ci/setup-make-parallelity.sh - - # Set DOCKER_TAG according to the current branch/tag - export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} - . .ci/update-env.sh - - # Select ARTIFACT_BASE depending on the current branch/tag - if [ -n "$CIRCLE_TAG" ] || [ $DOCKER_TAG = "master" ] || [ $DOCKER_TAG = "develop" ];then - export ARTIFACT_BASE=source-clean - else - # Select sagemath/sagemath-dev:develop as the ARTIFACT_BASE - # unless the user has explicitly selected a differnt one as a - # CircleCI environment variable. - export ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} - fi - - # Build docker images + . .circleci/before-script.sh .ci/build-docker.sh - - # Test that the images work + - run: &test-dev + name: test-dev + command: | + . .circleci/before-script.sh .ci/test-dev.sh $DOCKER_IMAGE_DEV + - run: &test-cli + name: test-cli + command: | + . .circleci/before-script.sh .ci/test-cli.sh $DOCKER_IMAGE_CLI + - run: &test-jupyter + name: test-jupyter + command: | + . .circleci/before-script.sh .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost - + - run: &release + # The docker commands sometimes take a while to produce output + no_output_timeout: 30m + name: release + command: | + . .circleci/before-script.sh # Push docker images to dockerhub if a dockerhub user has been configured .ci/push-dockerhub.sh sagemath-dev .ci/push-dockerhub.sh sagemath + build-from-latest-test-release: + <<: *build-test-release + build-from-clean-test-release: + <<: *build-test-release + environment: + ARTIFACT_BASE: source-clean + +workflows: + version: 2 + build-branch-from-clean: + jobs: + - build-from-clean-test-release: + filters: + branches: + only: + - master + - develop + build-tag-from-clean: + jobs: + - build-from-clean-test-release: + filters: + branches: + ignore: /.*/ + tags: + only: /.*/ + build-branch-from-latest: + jobs: + - build-from-latest-test-release: + filters: + branches: + ignore: + - master + - develop diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dcf46066619..b00a14498bf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,12 +78,10 @@ services: - docker:dind # Build Sage and its documentation. -# The build starts from the build artifacts of ARTIFACT_BASE which is usually -# much faster than building from a clean checkout of Sage. +# The build starts from the build artifacts of DEFAULT_ARTIFACT_BASE which is +# usually much faster than building from a clean checkout of Sage. build-from-latest: &build stage: build - variables: - ARTIFACT_BASE: $DEFAULT_ARTIFACT_BASE artifacts: when: always paths: @@ -135,12 +133,6 @@ test-cli: test-jupyter: stage: test script: - # Force usage of docker-in-docker (and don't start docker on a bind-mounted - # /var/run/docker.sock set through a private GitLab CI variable) so that - # the -p flag to docker run works as expected. - - export DOCKER_HOST='tcp://docker:2375' - - apk update - - apk add wget - . .ci/pull-gitlab.sh sagemath - sh .ci/test-jupyter.sh "$DOCKER_IMAGE" docker From 79003ab53cb5dcc4ade75573021dec91c763360c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 20 May 2018 23:27:46 +0200 Subject: [PATCH 108/284] Do not deduce CPUs/RAM by looking at the local machine but by looking at the limits of the machine running the docker containers --- .ci/setup-make-parallelity.sh | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh index 77c1bbb9877..4e33e11b006 100755 --- a/.ci/setup-make-parallelity.sh +++ b/.ci/setup-make-parallelity.sh @@ -1,9 +1,8 @@ #!/bin/sh -# Source this to set CPUTHREADS (the number of apparent cores) and -# RAMTHREADS (free RAM divided by the maximum amount needed per thread -# typically) -# From this this script exports reasonable defaults for SAGE_NUM_THREADS and +# Source this to set CPUTHREADS (the number of apparent cores) and RAMTHREADS +# (free RAM divided by the maximum amount needed per thread typically) +# From this this script infers reasonable defaults for SAGE_NUM_THREADS and # MAKEOPTS. # We do exactly the same for CPUTHREADS_DOCBUILD, RAMTHREADS_DOCBUILD, @@ -29,20 +28,20 @@ if [ -z "$CPUTHREADS" ]; then # https://circleci.com/docs/2.0/configuration-reference/#resource_class) which # provision fewer vCPUs than shown in /proc/cpuinfo. So it is probably better # to set CPUTHREADS manuall in your CI configuration. - CPUTHREADS=`grep -E '^processor' /proc/cpuinfo | wc -l` + CPUTHREADS=`docker run docker cat /proc/cpuinfo | grep -E '^processor' | wc -l` fi if [ -z "$CPUTHREADS_DOCBUILD" ]; then CPUTHREADS_DOCBUILD=$CPUTHREADS fi if [ -z "$RAMTHREADS" ]; then - RAMTHREADS=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 1048576 )) + RAMTHREADS=$(( `docker run docker cat /proc/meminfo | grep MemTotal | awk '{ print $2 }'` / 1048576 )) if [ $RAMTHREADS = 0 ];then RAMTHREADS=1; fi fi if [ -z "$RAMTHREADS_DOCBUILD" ]; then - RAMTHREADS_DOCBUILD=$(( `grep MemTotal /proc/meminfo | awk '{ print $2 }'` / 2097152 )) + RAMTHREADS_DOCBUILD=$(( `docker run docker cat /proc/meminfo | grep MemTotal | awk '{ print $2 }'` / 2097152 )) if [ $RAMTHREADS_DOCBUILD = 0 ];then RAMTHREADS_DOCBUILD=1; fi From 906ebeab69c1307582d6f299c8062837f9af30ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 20 May 2018 23:37:51 +0200 Subject: [PATCH 109/284] Collect system information on the docker host not on the local machine. For docker-in-docker (gitlab) this is the same but for CircleCI this is vastly different. --- .ci/describe-system.sh | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh index 327243a4070..6bd3b0efd4a 100755 --- a/.ci/describe-system.sh +++ b/.ci/describe-system.sh @@ -12,10 +12,12 @@ set +e -x -uname -a -df -h -cat /proc/cpuinfo -cat /proc/meminfo -cat /proc/sys/vm/overcommit_memory -cat /proc/sys/vm/overcommit_ratio docker info +docker run docker sh -c " + set -x + uname -a + df -h + cat /proc/cpuinfo + cat /proc/meminfo + cat /proc/sys/vm/overcommit_memory + cat /proc/sys/vm/overcommit_ratio" From 60ddb4b0486ee73fcb608d3efe60532fa5300fdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 20 May 2018 23:44:43 +0200 Subject: [PATCH 110/284] simplify apk add command for GitLab CI --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b00a14498bf..1caebb802db 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -88,7 +88,7 @@ build-from-latest: &build - gitlab-build-docker.log expire_in: 1 month script: - - apk update && apk add coreutils + - apk --update add coreutils # The output of the build can get larger than gitlab.com's limit; only print the first 3MB (and the last 80 lines.) - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 - .ci/push-gitlab.sh sagemath-dev From 25f934e5ec1bd8344b9e372f46387b569d379a8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 20 May 2018 23:54:27 +0200 Subject: [PATCH 111/284] Build from the latest develop image not from the latest (=master) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 65c00dcced9..bb28ca1b160 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,7 +17,7 @@ jobs: docker: - image: docker:latest environment: - DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:latest + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop steps: - run: apk --update add git openssh - checkout From 11f8a6cc24e9a1853a85f45d48210bdfb35254ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 May 2018 00:34:07 +0200 Subject: [PATCH 112/284] no port exposure needed for jupyter anymore --- .ci/test-jupyter.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh index b3ade38556a..4578fb5cbd8 100755 --- a/.ci/test-jupyter.sh +++ b/.ci/test-jupyter.sh @@ -16,7 +16,7 @@ # **************************************************************************** set -ex -docker run --name sage-jupyter -p -d "$1" sage-jupyter +docker run --name sage-jupyter -d "$1" sage-jupyter echo "Checking that the Jupyter notebook is running…" sleep 10 # giving the server some time to start docker logs sage-jupyter From 1960ba1e0674435f222af242df6252d2b7ea50c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 May 2018 02:59:17 +0200 Subject: [PATCH 113/284] add openssl/libssl-dev to the docker images so pip works --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 852dbb7af9d..2ac43bda981 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -61,7 +61,7 @@ RUN ln -s /usr/bin/sage /usr/bin/sagemath # We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo openssl \ && apt-get -qq clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ - && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind libssl-dev \ && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* From 14926cfe70ca7f26f97cbf69a4f6e7298f6fc3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 May 2018 14:59:09 +0200 Subject: [PATCH 114/284] Revert "add openssl/libssl-dev to the docker images" This reverts commit 1960ba1e0674435f222af242df6252d2b7ea50c8. sage -pip does not actually need this, so let's not install it. --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 2ac43bda981..852dbb7af9d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -61,7 +61,7 @@ RUN ln -s /usr/bin/sage /usr/bin/sagemath # We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo openssl \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ && apt-get -qq clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ - && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind libssl-dev \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* From f2c641a9d3a99fd0a1dc4f95c801c600e226f1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 21 May 2018 15:01:27 +0200 Subject: [PATCH 115/284] Add debug information on patch size somehow I am again at 4GB for the sagemath-dev image. Let's see why this happened. --- .ci/build-docker.sh | 4 ++++ docker/Dockerfile | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 404362cc1bb..82fb2c87c5a 100755 --- a/.ci/build-docker.sh +++ b/.ci/build-docker.sh @@ -41,6 +41,10 @@ docker_build --target make-all --tag make-all:$DOCKER_TAG . # Build the release image without build artifacts. docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . +# Display the layers of this image +docker history "$DOCKER_IMAGE_CLI" # Build the developer image with the build artifacts intact. # Note: It's important to build the dev image last because it might be tagged as ARTIFACT_BASE. docker_build --target sagemath-dev --tag "$DOCKER_IMAGE_DEV" . +# Display the layers of this image +docker history "$DOCKER_IMAGE_DEV" diff --git a/docker/Dockerfile b/docker/Dockerfile index 852dbb7af9d..0e772b987bd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -248,7 +248,8 @@ COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT ARG ARTIFACT_BASE=source-clean # Apply the patch from sagemath-dev-patch if we created one. RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ - xzcat patch/modified.xz | xargs rm -rf \ + echo "Applying `du -hs patch/modified.tar.xz` patch" \ + xzcat patch/modified.xz | xargs rm -rvf \ && tar -Jxf patch/modified.tar.xz \ && rm -rf patch; \ fi From 83f71e7948c8eb8a72e63d4d1451f9f20779bb8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 22 May 2018 22:32:20 +0200 Subject: [PATCH 116/284] Don't pipe "echo" into "rm" --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 0e772b987bd..cb9bf4230d9 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -249,7 +249,7 @@ ARG ARTIFACT_BASE=source-clean # Apply the patch from sagemath-dev-patch if we created one. RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ echo "Applying `du -hs patch/modified.tar.xz` patch" \ - xzcat patch/modified.xz | xargs rm -rvf \ + && xzcat patch/modified.xz | xargs rm -rvf \ && tar -Jxf patch/modified.tar.xz \ && rm -rf patch; \ fi From d28f601f53ddae2cecadc95854fbd91a525ac8f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 24 May 2018 23:55:25 +0200 Subject: [PATCH 117/284] Make sage -pip work I think I had tested this with the wrong sagemath docker image. pip does not work without these changes. (Probably it does not work with these changes either.) Revert "Revert "add openssl/libssl-dev to the docker images"" This reverts commit 14926cfe70ca7f26f97cbf69a4f6e7298f6fc3d3. --- docker/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index cb9bf4230d9..36f9d4ef76a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -61,7 +61,7 @@ RUN ln -s /usr/bin/sage /usr/bin/sagemath # We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. # We also install sudo for the sage user, see below. RUN apt-get -qq update \ - && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo openssl \ && apt-get -qq clean \ && rm -r /var/lib/apt/lists/* # Sage refuses to build as root, so we add a "sage" user that can sudo without a password. @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ - && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev sudo python libssl-dev git rdfind \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind libssl-dev \ && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* From b8e6077366e318a542299adfa1920424944e696c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Sun, 27 May 2018 01:33:43 +0200 Subject: [PATCH 118/284] Do not install libssl-dev twice --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 36f9d4ef76a..ad330cb7c13 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -82,7 +82,7 @@ WORKDIR $HOME FROM run-time-dependencies as build-time-dependencies # Install the build time dependencies & git & rdfind RUN sudo apt-get -qq update \ - && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind libssl-dev \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind \ && sudo apt-get -qq clean \ && sudo rm -r /var/lib/apt/lists/* From 4ddd34683e4e85c5121c720c0d790c4acfca1347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 28 May 2018 14:10:53 +0200 Subject: [PATCH 119/284] Backport #24655 to build docker images --- .ci/README.md | 9 + .ci/build-docker.sh | 50 ++++ .ci/describe-system.sh | 23 ++ .ci/head-tail.sh | 45 ++++ .ci/protect-secrets.sh | 40 +++ .ci/pull-gitlab.sh | 30 +++ .ci/push-dockerhub.sh | 29 +++ .ci/push-gitlab.sh | 25 ++ .ci/setup-make-parallelity.sh | 65 +++++ .ci/test-cli.sh | 30 +++ .ci/test-dev.sh | 45 ++++ .ci/test-jupyter.sh | 23 ++ .ci/update-env.sh | 32 +++ .circleci/before-script.sh | 26 ++ .circleci/config.yml | 88 +++++++ .dockerignore | 1 + .gitignore | 11 + .gitlab-ci.yml | 160 ++++++++++++ Makefile | 35 +++ configure.ac | 6 +- docker/.gitignore | 2 + docker/Dockerfile | 258 ++++++++++++++++++++ docker/README.md | 55 +++++ docker/entrypoint-dev.sh | 4 + docker/entrypoint.sh | 9 + docker/hooks/build | 1 + docker/hooks/push | 1 + src/.gitignore | 4 - src/Makefile.in | 2 +- src/doc/common/conf.py | 2 +- src/sage/misc/sagedoc.py | 8 +- src/sage_setup/docbuild/__init__.py | 59 +++-- src/sage_setup/docbuild/ext/sage_autodoc.py | 7 +- src/sage_setup/docbuild/sphinxbuild.py | 95 +++++-- 34 files changed, 1210 insertions(+), 70 deletions(-) create mode 100644 .ci/README.md create mode 100755 .ci/build-docker.sh create mode 100755 .ci/describe-system.sh create mode 100755 .ci/head-tail.sh create mode 100755 .ci/protect-secrets.sh create mode 100755 .ci/pull-gitlab.sh create mode 100755 .ci/push-dockerhub.sh create mode 100755 .ci/push-gitlab.sh create mode 100755 .ci/setup-make-parallelity.sh create mode 100755 .ci/test-cli.sh create mode 100755 .ci/test-dev.sh create mode 100755 .ci/test-jupyter.sh create mode 100755 .ci/update-env.sh create mode 100644 .circleci/before-script.sh create mode 100644 .circleci/config.yml create mode 120000 .dockerignore create mode 100644 .gitlab-ci.yml create mode 100644 docker/.gitignore create mode 100644 docker/Dockerfile create mode 100644 docker/README.md create mode 100755 docker/entrypoint-dev.sh create mode 100755 docker/entrypoint.sh create mode 100644 docker/hooks/build create mode 100644 docker/hooks/push delete mode 100644 src/.gitignore diff --git a/.ci/README.md b/.ci/README.md new file mode 100644 index 00000000000..e2b165cc518 --- /dev/null +++ b/.ci/README.md @@ -0,0 +1,9 @@ +# Continuous Integration (CI) + +We support several implementations of CI. All these implementations rely on +[docker](https://docker.com) in some way. This directory contains bits which +are shared between these CI implementations. The relevant docker files can be +found in `/docker/`. + +* [CircleCI](https://circleci.com) is configured in `/.circleci/`. +* [GitLab CI](https://gitlab.com) is configured in `/.gitlab-ci.yml`. diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh new file mode 100755 index 00000000000..82fb2c87c5a --- /dev/null +++ b/.ci/build-docker.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +# This script gets called from CI to build several flavours of docker images +# which contain Sage. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# We speed up the build process by copying built artifacts from ARTIFACT_BASE +# during docker build. See /docker/Dockerfile for more details. +ARTIFACT_BASE=${ARTIFACT_BASE:-sagemath/sagemath-dev:develop} + +# Seed our cache with $ARTIFACT_BASE if it exists. +docker pull "$ARTIFACT_BASE" > /dev/null || true + +docker_build() { + # Docker's --cache-from does not really work with multi-stage builds: https://github.com/moby/moby/issues/34715 + # So we just have to rely on the local cache. + time docker build -f docker/Dockerfile \ +--build-arg "MAKEOPTS=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg "MAKEOPTS_DOCBUILD=${MAKEOPTS}" --build-arg "SAGE_NUM_THREADS_DOCBUILD=${SAGE_NUM_THREADS_DOCBUILD}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +} + +# We use a multi-stage build /docker/Dockerfile. For the caching to be +# effective, we populate the cache by building the run/build-time-dependencies +# and the make-all target. (Just building the last target is not enough as +# intermediate targets could be discarded from the cache [depending on the +# docker version] and therefore the caching would fail for our actual builds +# below.) +docker_build --target run-time-dependencies --tag run-time-dependencies:$DOCKER_TAG . +docker_build --target build-time-dependencies --tag build-time-dependencies:$DOCKER_TAG . +docker_build --target make-all --tag make-all:$DOCKER_TAG . + +# Build the release image without build artifacts. +docker_build --target sagemath --tag "$DOCKER_IMAGE_CLI" . +# Display the layers of this image +docker history "$DOCKER_IMAGE_CLI" +# Build the developer image with the build artifacts intact. +# Note: It's important to build the dev image last because it might be tagged as ARTIFACT_BASE. +docker_build --target sagemath-dev --tag "$DOCKER_IMAGE_DEV" . +# Display the layers of this image +docker history "$DOCKER_IMAGE_DEV" diff --git a/.ci/describe-system.sh b/.ci/describe-system.sh new file mode 100755 index 00000000000..6bd3b0efd4a --- /dev/null +++ b/.ci/describe-system.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set +e -x + +docker info +docker run docker sh -c " + set -x + uname -a + df -h + cat /proc/cpuinfo + cat /proc/meminfo + cat /proc/sys/vm/overcommit_memory + cat /proc/sys/vm/overcommit_ratio" diff --git a/.ci/head-tail.sh b/.ci/head-tail.sh new file mode 100755 index 00000000000..8ecf9f879f0 --- /dev/null +++ b/.ci/head-tail.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +OFFSET=80 +# This script reads from stdin and prints to stdout as long as a the output +# does not exceed a certain number of bytes. When reading an EOF it prints the +# last $OFFSET lines if they have not been printed normally already. +# This script expects one argument, the number of bytes. + +# Heavily inspired by a simlar strategy in make, https://stackoverflow.com/a/44849696/812379. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +stdbuf -i0 -o0 -e0 awk -v limit=$1 -v firstMissingNR=-1 -v offset=$OFFSET -v bytes=0 \ +'{ + if (bytes < limit) { + # this probably gets multi-byte characters wrong, but that probably does + # not matter for our purposes. (We add 1 for a UNIX newline.) + bytes += length($0) + 1; + print; + } else { + if (firstMissingNR == -1){ + print "[…output truncated…]"; + firstMissingNR = NR; + } + a[NR] = $0; + delete a[NR-offset]; + printf "." > "/dev/stderr" + } +} +END { + if (firstMissingNR != -1) { + print "" > "/dev/stderr"; + for(i = NR-offset+1 > firstMissingNR ? NR-offset-1 : firstMissingNR; i<=NR ; i++){ print a[i]; } + } +} +' + diff --git a/.ci/protect-secrets.sh b/.ci/protect-secrets.sh new file mode 100755 index 00000000000..ee781dddca8 --- /dev/null +++ b/.ci/protect-secrets.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# This script protects all environment variables that start with "SECRET_". +# It puts them in a temporary file. The name of the variable contains the path +# of that file. This filename can then safely be used in `cat` even if `set +# -x` has been turned on. Also you can run "export" to understand the +# environment without danger. +# Be careful, however, not to use this like the following: +# docker login $DOCKER_USER $(cat $SECRET_DOCKER_PASS) +# as this would expose the password if `set -x` has been turned on. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -eo pipefail +set +x + +function encrypt { + RET=`mktemp` + eval " echo \$$1" > "$RET" + echo $RET +} + +for name in `awk 'END { for (name in ENVIRON) { print name; } }' < /dev/null`; do +case "$name" in + SECRET_*) + export $name="$(encrypt $name)" + echo "Protected $name" + ;; +esac +done + +unset encrypt diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh new file mode 100755 index 00000000000..c8235e64068 --- /dev/null +++ b/.ci/pull-gitlab.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script gets called from CI to pull the Sage docker images that were +# built during the "build" phase to pull all the connected docker daemon +# (likely a docker-in-docker.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. +# The variable $DOCKER_IMAGE is set to the full name of the pulled image; +# source this script to use it. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# Pull the built images from the gitlab registry and give them the original +# names they had after built. +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker pull $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +export DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" +docker tag $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG $DOCKER_IMAGE diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh new file mode 100755 index 00000000000..c2c46062ffa --- /dev/null +++ b/.ci/push-dockerhub.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +# This script gets called from CI to push our docker images to +# $DOCKER_USER/sagemath* on the Docker Hub. +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +[ -z "$DOCKER_TAG" ] && (echo "Can not push untagged build."; exit 0) + +# Push the built images to the docker hub (and fail silently if +# DOCKER_USER/SECRET_DOCKER_PASS have not been configured.) +if [ -z "$DOCKER_USER" -o -z "$SECRET_DOCKER_PASS" ]; then + echo "DOCKER_USER/SECRET_DOCKER_PASS variables have not been configured in your Continuous Integration setup. Not pushing built images to Docker Hub." +else + cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin + docker push ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG +fi diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh new file mode 100755 index 00000000000..e4ceb30abcd --- /dev/null +++ b/.ci/push-gitlab.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# This script gets called from CI to push our docker images to registry +# configured in GitLab. (Mostly, so we can pull them again to push them to the +# Docker Hub.) +# This script expects a single parameter, the base name of the docker image +# such as sagemath or sagemath-dev. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it +# automatically from the log output. +docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY +docker tag ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +docker push $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG diff --git a/.ci/setup-make-parallelity.sh b/.ci/setup-make-parallelity.sh new file mode 100755 index 00000000000..4e33e11b006 --- /dev/null +++ b/.ci/setup-make-parallelity.sh @@ -0,0 +1,65 @@ +#!/bin/sh + +# Source this to set CPUTHREADS (the number of apparent cores) and RAMTHREADS +# (free RAM divided by the maximum amount needed per thread typically) +# From this this script infers reasonable defaults for SAGE_NUM_THREADS and +# MAKEOPTS. + +# We do exactly the same for CPUTHREADS_DOCBUILD, RAMTHREADS_DOCBUILD, +# SAGE_NUM_THREADS_DOCBUILD, MAKEOPTS_DOCBUILD. As the docbuild needs +# substantially more RAM as of May 2018. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +if [ -z "$CPUTHREADS" ]; then + # Determine the number of threads that can run simultaneously on this system + # (we might not have nproc available.) + # Note that this value is incorrect for some CI providers (notably CircleCI: + # https://circleci.com/docs/2.0/configuration-reference/#resource_class) which + # provision fewer vCPUs than shown in /proc/cpuinfo. So it is probably better + # to set CPUTHREADS manuall in your CI configuration. + CPUTHREADS=`docker run docker cat /proc/cpuinfo | grep -E '^processor' | wc -l` +fi +if [ -z "$CPUTHREADS_DOCBUILD" ]; then + CPUTHREADS_DOCBUILD=$CPUTHREADS +fi + +if [ -z "$RAMTHREADS" ]; then + RAMTHREADS=$(( `docker run docker cat /proc/meminfo | grep MemTotal | awk '{ print $2 }'` / 1048576 )) + if [ $RAMTHREADS = 0 ];then + RAMTHREADS=1; + fi +fi +if [ -z "$RAMTHREADS_DOCBUILD" ]; then + RAMTHREADS_DOCBUILD=$(( `docker run docker cat /proc/meminfo | grep MemTotal | awk '{ print $2 }'` / 2097152 )) + if [ $RAMTHREADS_DOCBUILD = 0 ];then + RAMTHREADS_DOCBUILD=1; + fi +fi + +# On CI machines with their virtual CPUs, it seems to be quite beneficial to +# overcommit on CPU usage. We only need to make sure that we do not exceed RAM +# (as there is no swap.) +if [ $CPUTHREADS -lt $RAMTHREADS ]; then + export SAGE_NUM_THREADS=$((CPUTHREADS + 1)) +else + export SAGE_NUM_THREADS=$RAMTHREADS +fi +if [ $CPUTHREADS_DOCBUILD -lt $RAMTHREADS_DOCBUILD ]; then + export SAGE_NUM_THREADS_DOCBUILD=$((CPUTHREADS_DOCBUILD + 1)) +else + export SAGE_NUM_THREADS_DOCBUILD=$RAMTHREADS_DOCBUILD +fi +# Set -j and -l for make (though -l is probably ignored by Sage) +export MAKEOPTS="-j $SAGE_NUM_THREADS -l $((CPUTHREADS - 1)).8" +export MAKEOPTS_DOCBUILD="-j $SAGE_NUM_THREADS_DOCBUILD -l $((CPUTHREADS_DOCBUILD - 1)).8" diff --git a/.ci/test-cli.sh b/.ci/test-cli.sh new file mode 100755 index 00000000000..0ad6a8f3d0a --- /dev/null +++ b/.ci/test-cli.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath image. + +# Usage: ./test-cli.sh IMAGE-NAME + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +echo "Checking that Sage starts and can calculate 1+1…" +# Calculate 1+1 (remove startup messages and leading & trailing whitespace) +TWO=`docker run "$1" sage -c "'print(1+1)'" | tail -1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'` +[ "x$TWO" = "x2" ] + +echo "Checking that some binaries that should be distributed with Sage are on the PATH…" +# We could also run minimal tests on these but we don't yet. +# Check that Singular and GAP are present +docker run "$1" which Singular +docker run "$1" which gap +# Check that jupyter is present (for binder) +docker run "$1" which jupyter diff --git a/.ci/test-dev.sh b/.ci/test-dev.sh new file mode 100755 index 00000000000..5ba2cce16ca --- /dev/null +++ b/.ci/test-dev.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath-dev image. +# This script expects a single argument, the full name of the docker image to +# test. + +# Usage: ./test-dev.sh IMAGE-NAME + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +IMAGE="$1" + +. .ci/setup-make-parallelity.sh + +# Usage: timed_run limit args +# Runs $IMAGE with args and check that it terminates with a zero exit code in at most limit seconds. +timed_run() { + START=`date +%s` + docker run -e MAKEOPTS="$MAKEOPTS_DOCBUILD" -e SAGE_NUM_THREADS="$SAGE_NUM_THREADS_DOCBUILD" "$IMAGE" "$2" + END=`date +%s` + TOTAL=$((END-START)) + echo "Checking whether running \"$2\" was fast…" + [ "$TOTAL" -lt "$1" ] +} + +# Most setup should be done in well under 120 seconds but some CI machines tend +# to be really slow so we better give them some space here. Better miss a +# regression at first than create a lot of noise. +timed_run 120 true # runs make build +# Building the documentation is quite slow at the moment: +# Currently, this detects some dependencies as changed that have not changed. +# The parser in Sphinx fails to parse some .py files and adds the (more +# recently modified) .pyc files as dependencies instead. (Have a look the +# changeset that introduced this comment for more details.) +timed_run $(( 1200/$SAGE_NUM_THREADS_DOCBUILD )) make # runs make build and then make diff --git a/.ci/test-jupyter.sh b/.ci/test-jupyter.sh new file mode 100755 index 00000000000..4578fb5cbd8 --- /dev/null +++ b/.ci/test-jupyter.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +# This script gets called from CI to run minimal tests on the sagemath-jupyter +# image. + +# Usage: ./test-jupyter.sh IMAGE-NAME [HOST] + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex +docker run --name sage-jupyter -d "$1" sage-jupyter +echo "Checking that the Jupyter notebook is running…" +sleep 10 # giving the server some time to start +docker logs sage-jupyter +docker run --link sage-jupyter alpine sh -c "apk --update add wget; wget --retry-connrefused --tries=10 --wait=3 http://sage-jupyter:8888" diff --git a/.ci/update-env.sh b/.ci/update-env.sh new file mode 100755 index 00000000000..32ed5df6e8f --- /dev/null +++ b/.ci/update-env.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# This script gets called from CI to establish the name of the current docker +# tag to build and also the image which is used to seed the cache. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +set -ex + +# From the docker documentation: "A tag name must be valid ASCII and may +# contain lowercase and uppercase letters, digits, underscores, periods and +# dashes. A tag name may not start with a period or a dash and may contain a +# maximum of 128 characters." +export DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' '-' | sed 's/^[-.]*//' | cut -c1-128` + +[[ -z "$DOCKER_TAG" ]] && export DOCKER_TAG=none +[[ "$DOCKER_TAG" = "master" ]] && export DOCKER_TAG=latest + +export DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG +export DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG + +# 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/.circleci/before-script.sh b/.circleci/before-script.sh new file mode 100644 index 00000000000..51127362f15 --- /dev/null +++ b/.circleci/before-script.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Source this script during a CI run to set environment variables and print +# some informational messages about the system we are running on. + +# **************************************************************************** +# Copyright (C) 2018 Julian Rüth +# +# 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/ +# **************************************************************************** + +# CircleCI has no mechanism to hide secret variables. +# Therefore we roll our own to protect $SECRET_* variables. +. .ci/protect-secrets.sh +# Collect debug infos about the system we are running on +.ci/describe-system.sh +# Set MAKEOPTS and SAGE_NUM_THREADS +. .ci/setup-make-parallelity.sh + +# Set DOCKER_TAG according to the current branch/tag +export DOCKER_TAG=${CIRCLE_TAG:-$CIRCLE_BRANCH} +. .ci/update-env.sh diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000000..bb28ca1b160 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,88 @@ +# This file configures automatic builds of Sage on [CircleCI](https://circleci.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much, +# still the full build does usually not exceed CircleCI's limits for open +# source projcets (five hours on 2 vCPUs as of early 2018.) +# You might want to try to build locally or with GitLab CI, see +# `.gitlab-ci.yml` for more options. + +# As of early 2018, a run on CircleCI takes usually about 25 minutes. Most of +# the time is spent pulling/pushing from/to Docker Hub and copying files +# locally during the docker build. We could probably save five minutes by not +# building and testing the sagemath-dev image for most branches. + +version: 2 +jobs: + build-test-release: &build-test-release + docker: + - image: docker:latest + environment: + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop + steps: + - run: apk --update add git openssh + - checkout + - setup_remote_docker + - run: &build + # The docker commands sometimes take a while to produce output + no_output_timeout: 30m + name: build + command: | + . .circleci/before-script.sh + .ci/build-docker.sh + - run: &test-dev + name: test-dev + command: | + . .circleci/before-script.sh + .ci/test-dev.sh $DOCKER_IMAGE_DEV + - run: &test-cli + name: test-cli + command: | + . .circleci/before-script.sh + .ci/test-cli.sh $DOCKER_IMAGE_CLI + - run: &test-jupyter + name: test-jupyter + command: | + . .circleci/before-script.sh + .ci/test-jupyter.sh $DOCKER_IMAGE_CLI localhost + - run: &release + # The docker commands sometimes take a while to produce output + no_output_timeout: 30m + name: release + command: | + . .circleci/before-script.sh + # Push docker images to dockerhub if a dockerhub user has been configured + .ci/push-dockerhub.sh sagemath-dev + .ci/push-dockerhub.sh sagemath + build-from-latest-test-release: + <<: *build-test-release + build-from-clean-test-release: + <<: *build-test-release + environment: + ARTIFACT_BASE: source-clean + +workflows: + version: 2 + build-branch-from-clean: + jobs: + - build-from-clean-test-release: + filters: + branches: + only: + - master + - develop + build-tag-from-clean: + jobs: + - build-from-clean-test-release: + filters: + branches: + ignore: /.*/ + tags: + only: /.*/ + build-branch-from-latest: + jobs: + - build-from-latest-test-release: + filters: + branches: + ignore: + - master + - develop diff --git a/.dockerignore b/.dockerignore new file mode 120000 index 00000000000..3e4e48b0b5f --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/.gitignore b/.gitignore index 101d349690c..c98ce30b813 100644 --- a/.gitignore +++ b/.gitignore @@ -81,3 +81,14 @@ $RECYCLE.BIN/ ########### .ipynb_checkpoints Untitled*.ipynb + +############################# +# GitLab CI generated files # +############################# +gitlab-build-docker.log + +/src/.cython_version +/src/build +/src/Makefile +/src/bin/sage-env-config + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000000..1caebb802db --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,160 @@ +# This file configures automatic builds of Sage on [GitLab](https://gitlab.com). +# To make the build time not too excessive, we seed the build cache with +# sagemath/sagemath-dev:develop. When basic SPKGs changed, this does not help +# much and the full build might exceed the set time limit in GitLab. You can +# increase that limit in Settings → CI/CD. +# You can also provision your own private more powerful runner in the same +# place +# https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#use-docker-in-docker-executor; +# or set up your favourite cloud service to provide an on-demand autoscale +# runner. More details below. + +# As of early 2018 a run on GitLab CI takes about 45 minutes. We could probably +# save 10 minutes by not building/pushing/testing dev images for branches other +# than master/develop. + +# Note that most of the time during CI is spent with pulling and pushing of +# docker images and copying files locally as part of the docker build. At the +# moment there is no reliable way of passing the docker images to the following +# stages without explicit pushing/pulling or similar: +# https://gitlab.com/gitlab-org/gitlab-runner/issues/1107 + +# The timings mentioned above are typical values. The shared runners provided +# on gitlab.com are sometimes much slower depending on the runner you are +# scheduled on. Sometimes it's slower for no apparent reason, probably just an +# overcommittment of virtual machines on hardware. + +# GitLab provides several flavours of shared runners (as of early 2018): +# * runners tagged as "do" (digitalocean.com) provide about 60GB of HDD, two +# cores, but only 2GB of RAM. The RAM is sometimes not sufficient to build +# the documentation. +# * runners tagged as "gce" (Google Compute Engine) provide about 22GB of HDD, +# a single core, 4GB of RAM. Since we are relying on OverlayFS, the disk +# space is not sufficient to build sage from scratch. + +# If you want to provide your own runners, make sure to tag them as follows: +# * "do" (60GB of disk space are available) to make build-from-clean pass. + +image: docker:latest + +stages: + - build + - test + - release + +variables: + DOCKER_TAG: $CI_COMMIT_REF_NAME + # Builds are very I/O intensive; make sure we have a fast file system. + DOCKER_DRIVER: overlay2 + DEFAULT_ARTIFACT_BASE: sagemath/sagemath-dev:develop + +before_script: + # GitLab has no mechanism yet to hide secret variables: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 + # So we roll our own which protects all variables that start with SECRET_ + - . .ci/protect-secrets.sh + # Collect debug infos about the system we are running on + - .ci/describe-system.sh + # Set DOCKER_TAG according to the current branch/tag + - . .ci/update-env.sh + # Set MAKEOPTS and SAGE_NUM_THREADS according to the machine we are running on + - . .ci/setup-make-parallelity.sh + +# We use docker-in-docker to build our docker images, i.e., we run a +# docker:dind "service" container and link to it from the container running the +# actual scripts below. +# Our scripts automatically connect to this service (unless you override it by +# setting DOCKER_HOST.) For example, each RUN statement in the Dockerfile +# spawns a docker container inside the docker:dind container to perform the RUN +# command there. +# It can be faster to expose your outer docker daemon by mounting +# /var/run/docker.sock to /var/run/docker.sock and setting DOCKER_HOST in +# Settings -> CI/CD -> Secret variable to unix:///var/run/docker.sock. (The +# speedup is mostly due to sharing layers of intermediate images.) However, +# this is only possible if you provision your own runners. Shared gitlab +# runners, do not bind mount /var/run/docker.sock. Also, docker:dind provides +# 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:dind + +# Build Sage and its documentation. +# The build starts from the build artifacts of DEFAULT_ARTIFACT_BASE which is +# usually much faster than building from a clean checkout of Sage. +build-from-latest: &build + stage: build + artifacts: + when: always + paths: + - gitlab-build-docker.log + expire_in: 1 month + script: + - apk --update add coreutils + # The output of the build can get larger than gitlab.com's limit; only print the first 3MB (and the last 80 lines.) + - .ci/build-docker.sh | tee gitlab-build-docker.log | .ci/head-tail.sh 3145728 + - .ci/push-gitlab.sh sagemath-dev + - .ci/push-gitlab.sh sagemath + except: + - master + - develop + - tags + +# Build Sage and its documentation from a clean checkout of Sage. +# Note that this takes several hours. You probably want to run this on your own +# gitlab-runner and increase the standard GitLab time limit for CI runs. +# Some of the shared runners provided by GitLab for free do not have enough +# disk space for this to work. If a build fails with "no space left on device", +# you could just retry it and hope to be scheduled on a machine with more disk +# space, or provision your own runner. +build-from-clean: + << : *build + variables: + ARTIFACT_BASE: "source-clean" + only: + - master + - develop + - tags + except: [] + tags: + # 60 GB of HDD are available + - do + +test-dev: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath-dev + - sh .ci/test-dev.sh "$DOCKER_IMAGE" + +test-cli: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath + - sh .ci/test-cli.sh "$DOCKER_IMAGE" + +test-jupyter: + stage: test + script: + - . .ci/pull-gitlab.sh sagemath + - sh .ci/test-jupyter.sh "$DOCKER_IMAGE" docker + +# Pushes the built images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +push-dockerhub: + stage: release + only: + - branches + - tags + script: + - . .ci/pull-gitlab.sh sagemath + - sh .ci/push-dockerhub.sh sagemath + +# Pushes the built dev images to Docker Hub if the Settings -> CI/CD -> Secret +# variables DOCKER_USER and SECRET_DOCKER_PASS have been set up. +push-dockerhub-dev: + stage: release + only: + - master + - develop + - tags + script: + - . .ci/pull-gitlab.sh sagemath-dev + - sh .ci/push-dockerhub.sh sagemath-dev diff --git a/Makefile b/Makefile index d81d450ef4e..89ac3e4ecf1 100644 --- a/Makefile +++ b/Makefile @@ -104,9 +104,44 @@ bootstrap-clean: maintainer-clean: distclean bootstrap-clean rm -rf upstream +# Remove everything that is not necessary to run Sage and pass all its +# doctests. micro_release: bdist-clean sagelib-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..." + rm -rf local/share/doc/sage/doctrees local/share/doc/sage/inventory + @echo "Removing documentation. Inspection in IPython still works." + rm -rf local/share/doc local/share/*/doc local/share/*/examples local/share/singular/html + @echo "Removing unnecessary files & directories - make will not be functional afterwards anymore" + @# We need src/doc/common, src/doc/en/introspect for introspection with "??" + @# We keep src/sage for some doctests that it expect it to be there and + @# also because it does not add any weight with rdfind below. + @# We need src/sage/bin/ for the scripts that invoke Sage + @# We need sage, the script to start Sage + @# We need local/, the dependencies and the built Sage library itself. + @# We keep VERSION.txt. + @# We keep COPYING.txt so we ship a license with this distribution. + find . -name . -o -prune ! -name src ! -name sage ! -name local ! -name VERSION.txt ! -name COPYING.txt ! -name build -exec rm -rf \{\} \; + cd src && find . -name . -o -prune ! -name sage ! -name bin ! -name doc -exec rm -rf \{\} \; + if command -v rdfind > /dev/null; then \ + echo "Hardlinking identical files."; \ + rdfind -makeresultsfile false -makehardlinks true .; \ + else \ + echo "rdfind not installed. Not hardlinking identical files."; \ + fi + +# Leaves everything that is needed to make the next "make" fast but removes +# all the cheap build artifacts that can be quickly regenerated. +fast-rebuild-clean: misc-clean bdist-clean + rm -rf upstream/ + rm -rf src/build/temp.* + # Without site-packages/sage sage does not start but copying/compiling + # them from src/build is very fast. + rm -rf local/lib/python*/site-packages/sage + # The .py files in src/build are restored from src/sage without their + # mtimes changed. + find src/build -name '*.py' -exec rm \{\} \; TESTALL = ./sage -t --all PTESTALL = ./sage -t -p --all diff --git a/configure.ac b/configure.ac index 569caf911cf..6a948395098 100644 --- a/configure.ac +++ b/configure.ac @@ -285,9 +285,9 @@ fi AC_CHECK_PROG(found_latex, latex, yes, no) if test x$found_latex != xyes then - AC_MSG_WARN([You do not have 'latex', which is recommended, but not]) - AC_MSG_WARN([required. Latex is only really used for building pdf]) - AC_MSG_WARN([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) + AC_MSG_NOTICE([You do not have 'latex', which is recommended, but not]) + AC_MSG_NOTICE([required. Latex is only really used for building pdf]) + AC_MSG_NOTICE([documents and for %latex mode in the AC_PACKAGE_NAME notebook.]) fi # Check that perl is available, with version 5.8.0 or later. diff --git a/docker/.gitignore b/docker/.gitignore new file mode 100644 index 00000000000..579da245b87 --- /dev/null +++ b/docker/.gitignore @@ -0,0 +1,2 @@ +# Stores the commit that was used to create a sagemath-dev image +.commit diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 00000000000..ad330cb7c13 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,258 @@ +################################################################################ +# SageMath images for Docker # +################################################################################ +# This is a description of the layout of this Dockerfile; for details on the # +# created docker images, see the README.md please. # +# # +# This Dockerfile builds sagemath (for end-users) and sagemath-dev (for # +# developers.) It consists of lots of intermediate targets, mostly to shrink # +# the resulting images but also to make this hopefully easier to maintain. # +# The aims of this Dockerfile are: # +# (1) Make it build in reasonable time. # +# (2) It should be self-contained and work on its own, i.e., just by invoking # +# docker build without any external orchestration script. # +# # +# The idea to achieve (1) is to reuse the build artifacts from the latest # +# develop build. This is slightly against the philosophy of a Dockerfile (which# +# should produce perfectly reproducible outputs) but building Sage from scratch# +# just takes too long at the moment to do this all the time. ARTIFACT_BASE # +# controls which build artifacts are used. You probably want to set this to # +# sagemath/sagemath-dev:develop which takes the latest build from the official # +# develop branch. The default is source-clean which builds Sage from scratch. # +# If you want to understand how this works, have a look at source-from-context # +# which merges ARTIFACT_BASE with the context, i.e., the contents of the sage # +# source directory. # +################################################################################ + +################################################################################ +# HOWTO use this file for local builds # +################################################################################ +# If you don't mind downloading a 2GB docker image from time to time, you # +# could use this file for local Sage development. As of early 2018 each build # +# takes about five minutes but you don't have to go through the sadly frequent # +# rebuilds the whole Sage distribution... # +# To build Sage, run this command from your sage/ directory: # +# $ docker build --build-arg MAKEOPTS="-j4" --build-arg SAGE_NUM_THREADS="4" --build-arg ARTIFACT_BASE="sagemath/sagemath-dev:develop" -f docker/Dockerfile --target=make-build --tag sage . +# To run Sage: # +# $ docker run -it sage # +# To run doctests: # +# $ docker run -e "MAKEOPTS=-j4" -e "SAGE_NUM_THREADS=4" -it sage sage -tp src/sage +# Make sure that you always have the latest develop branch merged into your # +# local branch for this to work. # +################################################################################ + +ARG ARTIFACT_BASE=source-clean + +################################################################################ +# Image containing the run-time dependencies for Sage # +################################################################################ +FROM ubuntu:xenial as run-time-dependencies +LABEL maintainer="Erik M. Bray , Julian Rüth " +# Set sane defaults for common environment variables. +ENV LC_ALL C.UTF-8 +ENV LANG C.UTF-8 +ENV SHELL /bin/bash +# Create symlinks for sage and sagemath - we copy a built sage to the target of these symlinks later. +ARG SAGE_ROOT=/home/sage/sage +RUN ln -s "$SAGE_ROOT/sage" /usr/bin/sage +RUN ln -s /usr/bin/sage /usr/bin/sagemath +# Sage needs the fortran libraries at run-time because we do not build gfortran +# with Sage but use the system's. +# We need gcc/g++ and libstdc++-5-dev to allow compilation of cython at run-time from the notebook. +# We also install sudo for the sage user, see below. +RUN apt-get -qq update \ + && apt-get -qq install -y --no-install-recommends gfortran gcc g++ libstdc++-5-dev sudo openssl \ + && apt-get -qq clean \ + && rm -r /var/lib/apt/lists/* +# Sage refuses to build as root, so we add a "sage" user that can sudo without a password. +# We also want this user at runtime as some commands in sage know about the user that was used during build. +ARG HOME=/home/sage +RUN adduser --quiet --shell /bin/bash --gecos "Sage user,101,," --disabled-password --home "$HOME" sage \ + && echo "sage ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/01-sage \ + && chmod 0440 /etc/sudoers.d/01-sage +# Run everything from now on as the sage user in sage's home +USER sage +ENV HOME $HOME +WORKDIR $HOME + +################################################################################ +# Image containing everything so that a make in a clone of the Sage repository # +# completes without errors # +################################################################################ +FROM run-time-dependencies as build-time-dependencies +# Install the build time dependencies & git & rdfind +RUN sudo apt-get -qq update \ + && sudo apt-get -qq install -y wget build-essential automake m4 dpkg-dev python libssl-dev git rdfind \ + && sudo apt-get -qq clean \ + && sudo rm -r /var/lib/apt/lists/* + +################################################################################ +# Image with an empty git repository in $SAGE_ROOT. # +################################################################################ +FROM build-time-dependencies as source-clean +ARG SAGE_ROOT=/home/sage/sage +RUN mkdir -p "$SAGE_ROOT" +WORKDIR $SAGE_ROOT +RUN git init +RUN git remote add trac git://trac.sagemath.org/sage.git + +################################################################################ +# Image with the build context added, i.e., the directory from which `docker # +# build` has been called in a separate directory so we can copy files from # +# there. # +# This blows up the size of this docker image significantly, but we only use # +# this image to create artifacts for our final image. # +# Warning: If you set ARTIFACT_BASE to something else than source-clean, the # +# build is not going to use the build-time-dependencies target but rely on # +# whatever tools are installed in ARTIFACT_BASE. # +################################################################################ +FROM $ARTIFACT_BASE as source-from-context +WORKDIR $HOME +COPY --chown=sage:sage . sage-context +# Checkout the commit that checked out in $HOME/sage-context +# This is a bit complicated because our local .git/ is empty and we want to +# make sure that we only change the mtimes of a minimal number of files. +# 1) Restore the git checkout ARTIFACT_BASE was built from, recorded in +# docker/.commit. (Or go directly to FETCH_HEAD if there is no history to +# restore, i.e., set ARTIFACT_BASE=source-clean if you want to build from +# scratch.) +# 2) Merge in FETCH_HEAD but only if it is a fast-forward, i.e., if it is an +# ancestor of the commit restored in 1. If we would not do that we would get +# a new commit hash in docker/.commit that is not known outside of this build +# run. Since docker/.commit was in the history of FETCH_HEAD this should +# automatically be a fast-forward. +# 3) Trash .git again to save some space. +ARG SAGE_ROOT=/home/sage/sage +WORKDIR $SAGE_ROOT +# We create a list of all files present in the artifact-base (with a timestamp +# of now) so we can find out later which files were added/changed/removed. +RUN find . -type f > $HOME/artifact-base.manifest +RUN git fetch "$HOME/sage-context" HEAD \ + && if [ -e docker/.commit ]; then \ + git reset `cat docker/.commit` \ + || ( echo "Could not find commit `cat docker/.commit` in your local Git history. Please merge in the latest built develop branch to fix this: git fetch trac && git merge `cat docker/.commit`." && exit 1 ) \ + else \ + echo "You are building from $ARTIFACT_BASE which has no docker/.commit file. That's a bug unless you are building from source-clean or something similar." \ + && git reset FETCH_HEAD \ + && git checkout -f FETCH_HEAD; \ + fi \ + && git merge --ff-only FETCH_HEAD \ + && git log -1 --format=%H > docker/.commit \ + && rm -rf .git +# Copy over all the untracked/staged/unstaged changes from sage-context. This +# is relevant for non-CI invocations of this Dockerfile. +WORKDIR $HOME/sage-context +RUN if git status --porcelain | read CHANGES; then \ + git -c user.name=docker-build -c user.email=docker-build@sage.invalid stash -u \ + && git stash show -p > "$HOME"/sage-context.patch; \ + else \ + touch "$HOME"/sage-context.patch; \ + fi +WORKDIR $SAGE_ROOT +RUN patch -p1 < "$HOME"/sage-context.patch + +################################################################################ +# Image with a built sage but without sage's documentation. # +################################################################################ +FROM source-from-context as make-build +# Make sure that the result runs on most CPUs. +ENV SAGE_FAT_BINARY yes +# Just to be sure Sage doesn't try to build its own GCC (even though +# it shouldn't with a recent GCC package from the system and with gfortran) +ENV SAGE_INSTALL_GCC no +# Set MAKEOPTS and SAGE_NUM_THREADS to build things in parallel during the +# docker build. Note that these do not leak into the sagemath and sagemath-dev +# images. +ARG MAKEOPTS="-j2" +ENV MAKEOPTS $MAKEOPTS +ARG SAGE_NUM_THREADS="2" +ENV SAGE_NUM_THREADS $SAGE_NUM_THREADS +RUN make build + +################################################################################ +# Image with a full build of sage and its documentation. # +################################################################################ +FROM make-build as make-all +# The docbuild needs quite some RAM (as of May 2018). It sometimes calls +# os.fork() to spawn an external program which then exceeds easily the +# overcommit limit of the system (no RAM is actually used, but this limit is +# very low because there is not even swap on most CI systems.) +ARG MAKEOPTS_DOCBUILD=$MAKEOPTS +ENV MAKEOPTS_DOCBUILD $MAKEOPTS_DOCBUILD +ARG SAGE_NUM_THREADS_DOCBUILD=$SAGE_NUM_THREADS +ENV SAGE_NUM_THREADS_DOCBUILD $SAGE_NUM_THREADS_DOCBUILD +RUN make + +################################################################################ +# Image with a full build of sage, ready to release, i.e., with stripped # +# binaries and some extras to run the jupyter notebook. # +################################################################################ +FROM make-all as make-release +RUN sage -pip install terminado "notebook>=5" "ipykernel>=4.6" +RUN sage -i gap_jupyter singular_jupyter pari_jupyter +RUN make micro_release + +################################################################################ +# A releasable (relatively small) image which contains a copy of sage without # +# temporary build artifacts which is set up to start the command line # +# interface if no parameters are passed in. # +################################################################################ +FROM run-time-dependencies as sagemath +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ +# Put scripts to start gap, gp, maxima, ... in /usr/bin +WORKDIR $SAGE_ROOT +RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" +COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +EXPOSE 8888 +CMD ["sage"] + +################################################################################ +# Image with a full build of sage and its documentation but everything # +# stripped that can be quickly rebuild by make. # +################################################################################ +FROM make-all as make-fast-rebuild-clean +RUN make fast-rebuild-clean + +################################################################################ +# Depending on whether we built from source-clean or not, this image is either # +# identical to make-fast-rebuild-clean or contains a "patch" which can be used # +# to upgrade ARTIFACT_BASE to make-fast-rebuild-clean. # +################################################################################ +FROM make-fast-rebuild-clean as sagemath-dev-patch +ARG ARTIFACT_BASE=source-clean +ARG SAGE_ROOT=/home/sage/sage +# Build a patch containing of a tar file which contains all the modified files +# and a list of all modified files (added/updated/removed). +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + mkdir -p $HOME/patch \ + && find . -type f > $HOME/make-fast-rebuild-clean.manifest \ + && cat $HOME/make-fast-rebuild-clean.manifest $HOME/artifact-base.manifest | sort | uniq -u > $HOME/obsolete \ + && find . -type f -cnewer $HOME/artifact-base.manifest > $HOME/modified \ + && tar -cJf $HOME/patch/modified.tar.xz -T $HOME/modified \ + && cat $HOME/obsolete $HOME/modified | xz > $HOME/patch/modified.xz \ + && rm -rf $SAGE_ROOT \ + && mkdir -p $SAGE_ROOT \ + && mv $HOME/patch $SAGE_ROOT/; \ + fi + +################################################################################ +# A releasable (relatively small, but still huge) image of this build with all # +# the build artifacts intact so developers can make changes and rebuild # +# quickly # +################################################################################ +FROM $ARTIFACT_BASE as sagemath-dev +ARG SAGE_ROOT=/home/sage/sage +COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT +ARG ARTIFACT_BASE=source-clean +# Apply the patch from sagemath-dev-patch if we created one. +RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ + echo "Applying `du -hs patch/modified.tar.xz` patch" \ + && xzcat patch/modified.xz | xargs rm -rvf \ + && tar -Jxf patch/modified.tar.xz \ + && rm -rf patch; \ + fi +COPY ./docker/entrypoint-dev.sh /usr/local/bin/sage-entrypoint +ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] +CMD ["bash"] diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000000..b9f9240f9e4 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,55 @@ +[![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/2018.svg)](https://github.com/sagemath/sage/commits/master) + +# Supported tags + +* `latest` — the stable `master` branch [![GitHub last commit (branch)](https://img.shields.io/github/last-commit/sagemath/sage/master.svg)](https://github.com/sagemath/sage/commits/master) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/master/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/master) +* `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) [![CircleCI branch](https://img.shields.io/circleci/project/github/sagemath/sage/master.svg)](https://circleci.com/gh/sagemath/sage/tree/master) [![GitLab CI](https://gitlab.com/sagemath/sage/badges/develop/pipeline.svg)](https://gitlab.com/sagemath/sage/commits/develop) + + +# What is SageMath + +SageMath is a free open-source mathematics software system licensed under the GPL. It builds on top of many existing open-source packages: NumPy, SciPy, matplotlib, Sympy, Maxima, GAP, FLINT, R and many more. Access their combined power through a common, Python-based language or directly via interfaces or wrappers. + +**Mission**: *Creating a viable free open source alternative to Magma, Maple, Mathematica and Matlab.* + +# What's in this image + +There are several flavours of this image. + +* [`sagemath/sagemath`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath/latest.svg)](https://hub.docker.com/r/sagemath/sagemath) contains everything necessary to run Sage on the command line. Run it with: + ``` + docker run -it sagemath/sagemath:latest + ``` + You can start a graphical [Jupyter Notebook](https://jupyter.org) at http://localhost:8888 instead. To use the notebook, follow the instructions printed when you run: + ``` + docker run -p8888:8888 sagemath/sagemath:latest sage-jupyter + ``` +* [`sagemath/sagemath-dev`![image size](https://img.shields.io/microbadger/image-size/sagemath/sagemath-dev.svg)](https://hub.docker.com/r/sagemath/sagemath-dev) contains all the build artifacts to rebuild Sage quickly. This version is probably only relevant for Sage developers. Run this image with: + ``` + docker run -it sagemath/sagemath-dev:develop + ``` + This triggers a rebuild and drops you in a shell afterwards. Note that the git repository has been emptied to save space. If you want to use git, fetch from your git repository with `git fetch trac` and go to the commit that was used to create this image with + ``` + git reset $(cat docker/.commit) + ``` + +# How to build your own SageMath images + +Run `docker build -f docker/Dockerfile --build-arg ARTIFACT_BASE=sagemath/sagemath-dev:develop --target TARGET .` in the Sage repository with `TARGET` one of `sagemath` or `sagemath-dev`. + +# How these images get updated + +Every push to our [github repository](https://github.com/sagemath/sage) triggers a build in [CircleCI](https://circleci.com) which builds and pushes the docker images. +A push to master also triggers a "build" on our [Docker Hub](https://hub.docker.com) repositories. The latter build is mostly disabled by the `hooks/` and only updates the `README.md`. + +Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) triggers a pipeline in GitLab CI. This build also pushes images to Docker Hub. + +Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup either continuous integration service for your own fork of the SageMath repository. + +# License + +The whole Sage software distribution is licensed under the General Public License, version 3. More details can be found in our [COPYING.txt](https://github.com/sagemath/sage/blob/master/COPYING.txt) + +[//]: # (Please don't break long lines in this files as dockerhub then gets the formatting of this file wrong.) diff --git a/docker/entrypoint-dev.sh b/docker/entrypoint-dev.sh new file mode 100755 index 00000000000..67299a5a92d --- /dev/null +++ b/docker/entrypoint-dev.sh @@ -0,0 +1,4 @@ +#!/bin/bash +set -e +make build +exec "$@" diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 00000000000..bc841382eaf --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash +if [ x"$1" = x"sage-jupyter" ]; then + # If "sage-jupyter" is given as a first argument, we start a jupyter notebook + # with reasonable default parameters for running it inside a container. + shift + exec sage -n jupyter --no-browser --ip='*' --port=8888 "$@" +else + exec sage -sh -c "$*" +fi diff --git a/docker/hooks/build b/docker/hooks/build new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/build @@ -0,0 +1 @@ +#!/bin/true diff --git a/docker/hooks/push b/docker/hooks/push new file mode 100644 index 00000000000..b23e55619b2 --- /dev/null +++ b/docker/hooks/push @@ -0,0 +1 @@ +#!/bin/true diff --git a/src/.gitignore b/src/.gitignore deleted file mode 100644 index e85aa7f89e4..00000000000 --- a/src/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.cython_version -/build -/Makefile -/bin/sage-env-config diff --git a/src/Makefile.in b/src/Makefile.in index 81a28b3758e..0233601cbbc 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -37,7 +37,7 @@ sage: SAGE_DOC_SRC=/doesnotexist \ SAGE_BUILD_DIR=/doesnotexist \ SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ - && sage-python23 -u setup.py --no-user-cfg build install + && sage-python23 -u setup.py --quiet --no-user-cfg build install if [ "$$UNAME" = "CYGWIN" ]; then \ sage-rebase.sh "$$SAGE_LOCAL" 2>/dev/null; \ fi diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 9e57f8c761a..a9c4eb0fb6c 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -624,7 +624,7 @@ def call_intersphinx(app, env, node, contnode): sage: from sage.env import SAGE_DOC sage: thematic_index = os.path.join(SAGE_DOC, "html", "en", "thematic_tutorials", "index.html") - sage: for line in open(thematic_index).readlines(): + sage: for line in open(thematic_index).readlines(): # requires a built documentation, optional: doc ....: if "padics" in line: ....: sys.stdout.write(line)
  • Introduction to the -adics
  • diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 9255aa848ff..ec2c435cd8a 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -20,7 +20,7 @@ sage: from sage.env import SAGE_DOC sage: docfilename = os.path.join(SAGE_DOC, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html') - sage: with open(docfilename) as fobj: + sage: with open(docfilename) as fobj: # requires a built documentation, optional: doc ....: for line in fobj: ....: if "#sage.symbolic.expression.Expression.numerical_approx" in line: ....: print(line) @@ -1332,7 +1332,7 @@ class _sage_doc: EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest + sage: browse_sage_doc._open("reference", testing=True)[0] # indirect doctest, requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' sage: browse_sage_doc(identity_matrix, 'rst')[-107:-47] 'Full MatrixSpace of 3 by 3 sparse matrices over Integer Ring' @@ -1494,9 +1494,9 @@ def _open(self, name, testing=False): EXAMPLES:: - sage: browse_sage_doc._open("reference", testing=True)[0] + sage: browse_sage_doc._open("reference", testing=True)[0] # requires a built documentation, optional: doc 'http://localhost:8000/doc/live/reference/index.html' - sage: browse_sage_doc._open("tutorial", testing=True)[1] + sage: browse_sage_doc._open("tutorial", testing=True)[1] # requires a built documentation, optional: doc '.../html/en/tutorial/index.html' """ url = self._base_url + os.path.join(name, "index.html") diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 5877b5b2a76..7dc8ff783c5 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -265,35 +265,29 @@ def clean(self, *args): # import the customized builder for object.inv files inventory = builder_helper('inventory') -if NUM_THREADS > 1: - def build_many(target, args): - from multiprocessing import Pool - pool = Pool(NUM_THREADS, maxtasksperchild=1) - # map_async handles KeyboardInterrupt correctly. Plain map and - # apply_async does not, so don't use it. - x = pool.map_async(target, args, 1) - try: - ret = x.get(99999) - pool.close() - pool.join() - except Exception: - pool.terminate() - if ABORT_ON_ERROR: - raise - return ret -else: - def build_many(target, args): - results = [] - - for arg in args: - try: - results.append(target(arg)) - except Exception: - if ABORT_ON_ERROR: - raise - - return results - +def build_many(target, args): + # Pool() uses an actual fork() to run each new instance. This is important + # for performance reasons, i.e., don't use a forkserver when it becomes + # available with Python 3: Here, sage is already initialized which is quite + # costly, with a forkserver we would have to reinitialize it for every + # document we build. At the same time, don't serialize this by taking the + # pool (and thus the call to fork()) out completely: The call to Sphinx + # leaks memory, so we need to build each document in its own process to + # control the RAM usage. + from multiprocessing import Pool + pool = Pool(NUM_THREADS, maxtasksperchild=1) + # map_async handles KeyboardInterrupt correctly. Plain map and + # apply_async does not, so don't use it. + x = pool.map_async(target, args, 1) + try: + ret = x.get(99999) + pool.close() + pool.join() + except Exception: + pool.terminate() + if ABORT_ON_ERROR: + raise + return ret ########################################## # Parallel Building Ref Manual # @@ -940,7 +934,12 @@ def get_new_and_updated_modules(self): except ImportError as err: logger.error("Warning: Could not import %s %s", module_name, err) raise - newtime = os.path.getmtime(sys.modules[module_name].__file__) + + module_filename = sys.modules[module_name].__file__ + if (module_filename.endswith('.pyc') or module_filename.endswith('.pyo')): + source_filename = module_filename[:-1] + if (os.path.exists(source_filename)): module_filename = source_filename + newtime = os.path.getmtime(module_filename) if newtime > mtime: updated_modules.append(module_name) diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index f241e09f8a5..2c399bad51b 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -988,7 +988,12 @@ def generate(self, more_content=None, real_modname=None, self.analyzer.find_attr_docs() except PycodeError as err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) - # no source file -- e.g. for builtin and C modules + # A few things could have happened here: + # * there is no source file -- e.g. for builtin and C modules + # * the source file contains syntax that Sphinx can not parse, + # e.g., "print(1, end=' ')"; see + # https://github.com/sphinx-doc/sphinx/issues/1641, + # fixed in Sphinx 1.7. self.analyzer = None # at least add the module.__file__ as a dependency if hasattr(self.module, '__file__') and self.module.__file__: diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index fadc7ab0184..25257adbdd6 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -2,7 +2,7 @@ r""" This is Sage's version of the sphinx-build script -We redirect stdout to our own logger, and remove some unwanted chatter. +We redirect stdout and stderr to our own logger, and remove some unwanted chatter. """ # **************************************************************************** # Copyright (C) 2013-2014 Volker Braun @@ -32,8 +32,8 @@ def term_width_line(text): class SageSphinxLogger(object): r""" - This implements the file object interface to serve as sys.stdout - replacement. + This implements the file object interface to serve as + ``sys.stdout``/``sys.stderr`` replacement. """ ansi_color = re.compile(r'\x1b\[[0-9;]*m') ansi_reset = re.compile(r'\x1b\[39;49;00m') @@ -60,26 +60,57 @@ def __init__(self, stream, prefix): self._error = None def _init_chatter(self): - # useless_chatter: regular expressions to be filtered from - # Sphinx output. - self.useless_chatter = ( + # We drop any messages from the output that match these regular + # expressions. These just bloat the output and do not contain any + # information that we care about. + self._useless_chatter = ( re.compile('^$'), re.compile('^Running Sphinx v'), re.compile('^loading intersphinx inventory from '), + re.compile('^loading pickled environment... done'), + re.compile('^loading cross citations... done \([0-9]* citations\).'), re.compile('^Compiling a sub-document'), re.compile('^updating environment: 0 added, 0 changed, 0 removed'), re.compile('^looking for now-outdated files... none found'), re.compile('^building \[.*\]: targets for 0 source files that are out of date'), - re.compile('^loading pickled environment... done'), - re.compile('^loading cross citations... done \([0-9]* citations\).'), + re.compile('^building \[.*\]: targets for 0 po files that are out of date'), + re.compile('^building \[.*\]: targets for 0 mo files that are out of date'), + re.compile('^pickling environment... done'), + re.compile('^dumping object inventory... done'), + # We still have "Build finished." + re.compile('^build succeeded.'), + re.compile('^checking consistency... done'), + re.compile('^preparing documents... done'), + re.compile('^copying extra files... done'), + re.compile('^writing additional pages... search'), + re.compile('^Writing js search indexes...writing additional pages... .*'), + re.compile('^generating indices... .*'), + re.compile('^dumping search index in .* ... done'), + re.compile('^linking _static directory'), + re.compile('^copying static files... done'), + re.compile('^copying extra files... done'), + re.compile('^loading translations \[.*\]... done'), + re.compile('^Compiling the master document'), + re.compile('^Saved pickle file: citations.pickle'), + re.compile('^writing output... \[.*\] '), + re.compile('^copying images... \[.*\] '), + re.compile('^reading sources... \[.*\] '), + re.compile('language "hu" not supported'), + re.compile('^$'), + re.compile('^WARNING:$'), + ) + + # We fail whenever a line starts with "WARNING:", however, we ignore + # these warnings, as they are not relevant. + self._ignored_warnings = ( re.compile('WARNING: favicon file \'favicon.ico\' does not exist'), re.compile('WARNING: html_static_path entry .* does not exist'), re.compile('WARNING: while setting up extension'), re.compile('WARNING: Any IDs not assiend for figure node'), re.compile('WARNING: .* is not referenced'), re.compile('WARNING: Build finished'), - re.compile('language "hu" not supported'), ) + self._useless_chatter += self._ignored_warnings # replacements: pairs of regular expressions and their replacements, # to be applied to Sphinx output. @@ -89,10 +120,12 @@ def _init_chatter(self): if 'inventory' in sys.argv: # When building the inventory, ignore warnings about missing # citations and the search index. - self.useless_chatter += ( + ignored = ( re.compile('WARNING: citation not found:'), re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') ) + self._ignored_warnings += ignored + self._useless_chatter += ignored # Regular expressions indicating a problem with docbuilding. Raise an # exception if any of these occur. @@ -109,18 +142,19 @@ def _init_chatter(self): # - undefined labels upon the first pass of the compilation: some # cross links may legitimately not yet be resolvable at this point. if 'latex' not in sys.argv: + self._error_patterns += (re.compile('WARNING:'),) if 'multidoc_first_pass=1' in sys.argv: - # Catch all warnings except 'WARNING: undefined label' - self._error_patterns += (re.compile('WARNING: (?!undefined label)'),) - else: - self._error_patterns += (re.compile('WARNING:'),) + ignore = (re.compile('WARNING: undefined label'),) + self._ignored_warnings += ignore + self._useless_chatter += ignore def _filter_out(self, line): if self._error is not None and self._is_stdout: # swallow non-errors after an error occurred return True line = re.sub(self.ansi_color, '', line) - for regex in self.useless_chatter: + line = line.strip() + for regex in self._useless_chatter: if regex.search(line) is not None: return True return False @@ -139,15 +173,19 @@ def _check_errors(self, line): sage: logger.raise_errors() Traceback (most recent call last): ... - OSError: [doctestin] Segmentation fault! + OSError: Segmentation fault! """ if self._error is not None: return # we already have found an error - for regex in self._error_patterns: - if regex.search(line) is not None: - self._error = line - return + for error in self._error_patterns: + if error.search(line) is not None: + for ignored in self._ignored_warnings: + if ignored.search(line) is not None: + break + else: + self._error = line + return def _log_line(self, line): r""" @@ -183,16 +221,16 @@ def _log_line(self, line): [#25160 ] Exception: artificial exception """ - if self._filter_out(line): - return + skip_this_line = self._filter_out(line) + self._check_errors(line) for (old, new) in self.replacements: line = old.sub(new, line) line = self._prefix + ' ' + line.rstrip() + '\n' if not self._color: line = self.ansi_color.sub('', line) - self._stream.write(line) - self._stream.flush() - self._check_errors(line) + if not skip_this_line: + self._stream.write(line) + self._stream.flush() def raise_errors(self): r""" @@ -209,7 +247,7 @@ def raise_errors(self): sage: logger.raise_errors() Traceback (most recent call last): ... - OSError: [doctestin] This is a SEVERE error + OSError: This is a SEVERE error """ if self._error is not None: @@ -272,6 +310,11 @@ def runsphinx(): try: sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) + # Note that this call as of eraly 2018 leaks memory. So make sure that + # you don't call runsphinx() several times in a row. (i.e., you want to + # fork() somewhere before this call.) + # We don't use subprocess here, as we don't want to re-initialize Sage + # for every docbuild as this takes a while. sphinx.cmdline.main(sys.argv) sys.stderr.raise_errors() sys.stdout.raise_errors() From 47fe99c4b756b2e42d893e378e73a14fd491934e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 30 May 2018 12:00:46 +0200 Subject: [PATCH 120/284] switch the internal representation of the blocks from Set to frozenset --- src/sage/coding/code_constructions.py | 10 +-- src/sage/combinat/dyck_word.py | 2 +- src/sage/combinat/ncsym/ncsym.py | 4 +- src/sage/combinat/partition_algebra.py | 4 +- src/sage/combinat/posets/lattices.py | 7 +- src/sage/combinat/set_partition.py | 77 ++++++++++++++----- .../groups/perm_gps/symgp_conjugacy_class.py | 5 +- 7 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 3ed942a4c04..f2789a6f935 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -110,11 +110,11 @@ def _is_a_splitting(S1, S2, n, return_automorphism=False): sage: for P in SetPartitions(6,[3,3]): ....: res,aut= _is_a_splitting(P[0],P[1],7,return_automorphism=True) ....: if res: - ....: print((aut, P[0], P[1])) - (6, {1, 2, 3}, {4, 5, 6}) - (3, {1, 2, 4}, {3, 5, 6}) - (6, {1, 3, 5}, {2, 4, 6}) - (6, {1, 4, 5}, {2, 3, 6}) + ....: print((aut, P)) + (6, {{1, 2, 3}, {4, 5, 6}}) + (3, {{1, 2, 4}, {3, 5, 6}}) + (6, {{1, 3, 5}, {2, 4, 6}}) + (6, {{1, 4, 5}, {2, 3, 6}}) We illustrate now how to find idempotents in quotient rings:: diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index a8400d9929c..558fcdee74f 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -3787,7 +3787,7 @@ def from_noncrossing_partition(self, ncp): """ l = [0] * sum(len(v) for v in ncp) for v in ncp: - l[v[-1] - 1] = len(v) + l[max(v) - 1] = len(v) res = [] for i in l: diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 460bd827a0b..5c678b539c0 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -387,7 +387,7 @@ def lt(s, t): if s == t: return False for p in s: - if len([ z for z in list(t) if z.intersection(p) != Set([]) ]) != 1: + if len([ z for z in list(t) if len(z.intersection(p)) != 0 ]) != 1: return False return True @@ -1782,7 +1782,7 @@ def lt(s, t): if s == t: return False for p in s: - if len([ z for z in list(t) if z.intersection(p) != Set([]) ]) != 1: + if len([ z for z in list(t) if len(z.intersection(p)) != 0 ]) != 1: return False return True diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 6d66dd70911..16d2ca06e66 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -221,12 +221,12 @@ def __iter__(self): sage: all(ak.cardinality() == len(ak.list()) for ak in aks) True """ - kp = Set([-self.k-1]) + kp = frozenset([-self.k-1]) for sp in SetPartitions_set.__iter__(self): res = [] for part in sp: if self.k+1 in part: - res.append( part + kp ) + res.append( part.union(kp) ) else: res.append(part) yield self.element_class(self, res) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 1fbb6b8f568..30b0bd5c1d4 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -3889,8 +3889,9 @@ def is_subdirectly_reducible(self, certificate=False): if len(A) == 1: for a in A[0]: if len(a) > 1: - return (False, (self._vertex_to_element(a[0]), - self._vertex_to_element(a[1]))) + x, y = a + return (False, (self._vertex_to_element(x), + self._vertex_to_element(y))) H_closure = H.transitive_closure() a0 = [min(v) for v in A[0]] @@ -4531,7 +4532,7 @@ def congruence(self, S): ....: 6: [8], 3: [9], 7: [10], 8: [10], 9:[10]}) sage: cong = L.congruence([[1, 2]]) sage: cong[0] - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) .. SEEALSO:: :meth:`quotient` diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 122f08cf46f..5ee31376f37 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -296,7 +296,7 @@ def __mul__(self, other): sage: def mul2(s, t): ....: temp = [ss.intersection(ts) for ss in s for ts in t] - ....: temp = filter(lambda x: x != Set([]), temp) + ....: temp = filter(lambda x: x, temp) ....: return s.__class__(s.parent(), temp) Let us check that this gives the same as ``__mul__`` on set @@ -308,8 +308,8 @@ def __mul__(self, other): """ new_composition = [] for B in self: - for C in other: - BintC = B.intersection(C) + for C in other: + BintC = [b for b in B if b in C] if BintC: new_composition.append(BintC) return SetPartition(new_composition) @@ -339,10 +339,16 @@ def sup(self, t): sage: sp1.sup(sp2) == s True """ - res = Set(list(self)) + res = list(self) for p in t: - inters = Set([x for x in list(res) if x.intersection(p) != Set([])]) - res = res.difference(inters).union(_set_union(inters)) + # find blocks in res which intersect p + inters = [(i, b) for i, b in enumerate(res) + if any(a in res[i] for a in p)] + # remove these blocks from res + for i, _ in reversed(inters): + del res[i] + # add the union + res.append([e for _, b in inters for e in b]) return self.parent()(res) def standard_form(self): @@ -429,9 +435,9 @@ def union(s): # the elements of this part of s into a single part. ret = [] for part in s: - cur = Set([]) + cur = [] for i in part: - cur = cur.union(self[i-1]) # -1 for indexing + cur.extend(self[i-1]) # -1 for indexing ret.append(cur) return ret return [self.parent()(union(s)) for s in SP] @@ -490,11 +496,7 @@ class SetPartition(AbstractSetPartition): Here is the list of them:: sage: SetPartitions(3).list() - [{{1, 2, 3}}, - {{1}, {2, 3}}, - {{1, 3}, {2}}, - {{1, 2}, {3}}, - {{1}, {2}, {3}}] + [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] There are 6 set partitions of `\{1,2,3,4\}` whose underlying partition is `[2, 1, 1]`:: @@ -544,7 +546,9 @@ def __init__(self, parent, s, check=True): {} """ self._latex_options = {} - ClonableArray.__init__(self, parent, sorted(map(Set, s), key=min), check=check) + sets = map(frozenset, s) + blocks = sorted(sets, key=min) + ClonableArray.__init__(self, parent, blocks, check=check) def check(self): """ @@ -567,7 +571,7 @@ def check(self): sage: s = S([1, 2, 3]) Traceback (most recent call last): ... - TypeError: Element has no defined underlying set + TypeError: 'sage.rings.integer.Integer' object is not iterable """ if self not in self.parent(): raise ValueError("%s is not an element of %s"%(self, self.parent())) @@ -1722,7 +1726,7 @@ def is_less_than(self, s, t): return False for p in s: - x = p[0] + x = next(iter(p)) for t_ in t: if x in t_: break @@ -1817,8 +1821,16 @@ def __iter__(self): sage: it = SetPartitions().__iter__() sage: [next(it) for x in range(10)] - [{}, {{1}}, {{1, 2}}, {{1}, {2}}, {{1, 2, 3}}, {{1}, {2, 3}}, - {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}, {{1, 2, 3, 4}}] + [{}, + {{1}}, + {{1, 2}}, + {{1}, {2}}, + {{1, 2, 3}}, + {{1}, {2, 3}}, + {{1, 3}, {2}}, + {{1, 2}, {3}}, + {{1}, {2}, {3}}, + {{1, 2, 3, 4}}] """ n = 0 while True: @@ -1958,9 +1970,37 @@ def __iter__(self): sage: SetPartitions(3).list() [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] """ + for p in Partitions(len(self._set)): for sp in self._iterator_part(p): yield self.element_class(self, sp) +# base_set = list(self.base_set()) +# def from_word(w): +# sp = [] +# for i, b in zip(base_set, w): +# if len(sp) <= b: +# sp.append([i]) +# else: +# sp[b].append(i) +# return self.element_class(self, sp, check=False) +# +# # Ruskey, Combinatorial Generation, Algorithm 4.22 +# +# N = len(base_set) +# a = [0]*N +# def gen(l, m): +# if l >= N: +# yield from_word(a) +# else: +# for i in range(m+1): +# a[l] = i +# for P in gen(l+1, m): +# yield P +# if m < N-1: +# a[l] = m+1 +# for P in gen(l+1, m+1): +# yield P +# return gen(1, 0) def base_set(self): """ @@ -2335,4 +2375,3 @@ def cyclic_permutations_of_set_partition_iterator(set_part): for right in cyclic_permutations_of_set_partition_iterator(set_part[1:]): for perm in CyclicPermutations(set_part[0]): yield [perm] + right - diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index 3b55bcf178b..5e28f8d77d8 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -360,8 +360,9 @@ def conjugacy_class_iterator(part, S=None): m = len(part) for s in SetPartitions(S, part): - firsts = [t[0] for t in s] - rests = [t[1:] for t in s] + blocks = map(Set, s) + firsts = [t[0] for t in blocks] + rests = [t[1:] for t in blocks] iterator = tuple(itertools.permutations(r) for r in rests) for r in itertools.product(*iterator): yield [(firsts[i],) + r[i] for i in range(m)] From 5591b40f3375a1ea6d645adfb5dcfe6cf0c87fa9 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 30 May 2018 12:09:58 +0200 Subject: [PATCH 121/284] add faster iterator --- src/sage/combinat/set_partition.py | 55 ++++++++++++++---------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 5ee31376f37..77f94eb734d 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1970,37 +1970,32 @@ def __iter__(self): sage: SetPartitions(3).list() [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] """ + base_set = list(self.base_set()) + def from_word(w): + sp = [] + for i, b in zip(base_set, w): + if len(sp) <= b: + sp.append([i]) + else: + sp[b].append(i) + return self.element_class(self, sp, check=False) - for p in Partitions(len(self._set)): - for sp in self._iterator_part(p): - yield self.element_class(self, sp) -# base_set = list(self.base_set()) -# def from_word(w): -# sp = [] -# for i, b in zip(base_set, w): -# if len(sp) <= b: -# sp.append([i]) -# else: -# sp[b].append(i) -# return self.element_class(self, sp, check=False) -# -# # Ruskey, Combinatorial Generation, Algorithm 4.22 -# -# N = len(base_set) -# a = [0]*N -# def gen(l, m): -# if l >= N: -# yield from_word(a) -# else: -# for i in range(m+1): -# a[l] = i -# for P in gen(l+1, m): -# yield P -# if m < N-1: -# a[l] = m+1 -# for P in gen(l+1, m+1): -# yield P -# return gen(1, 0) + # Ruskey, Combinatorial Generation, Algorithm 4.22 + N = len(base_set) + a = [0]*N + def gen(l, m): + if l >= N: + yield from_word(a) + else: + for i in range(m+1): + a[l] = i + for P in gen(l+1, m): + yield P + if m < N-1: + a[l] = m+1 + for P in gen(l+1, m+1): + yield P + return gen(1, 0) def base_set(self): """ From 43be53b1dd431d86f7adb2bd73915a81393c4177 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Jun 2018 09:56:16 +0200 Subject: [PATCH 122/284] alternative, non-recursive iterator --- src/sage/combinat/set_partition.py | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 77f94eb734d..aa4c622b0f6 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1997,6 +1997,57 @@ def gen(l, m): yield P return gen(1, 0) + def _iter_Knuth(self): + """ + Iterate over ``self``. + + EXAMPLES:: + + sage: SetPartitions(3).list() + [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] + """ + base_set = list(self.base_set()) + def from_word(w): + sp = [] + for i, b in zip(base_set, w): + if len(sp) <= b: + sp.append([i]) + else: + sp[b].append(i) + return self.element_class(self, sp, check=False) + + # Knuth, TAOCP 4A 7.2.1.5, Algorithm H + N = len(base_set) + # H1: initialize + a = [0]*N + if N <= 1: + yield self.from_word(base_set, a) + return + b = [1]*N + while True: + # H2: visit + yield self.from_word(base_set, a) + if a[-1] == b[-1]: + # H4: find j + j = N-2 + while a[j] == b[j]: + j -= 1 + # H5: increase a_j + if j == 0: + break + a[j] += 1 + # H6: zero out a_{j+1},...,a_n + b[-1] = b[j] + (1 if a[j] == b[j] else 0) + j += 1 + while j < N-1: + a[j] = 0 + b[j] = b[-1] + j += 1 + a[-1] = 0 + else: + # increase a_n + a[-1] += 1 + def base_set(self): """ Return the base set of ``self``. From 7a2ba0a42dd2230f8beaa51cde386c059a5d02bc Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Jun 2018 12:50:50 +0200 Subject: [PATCH 123/284] add faster iterator for set partitions with given number of blocks --- src/sage/combinat/set_partition.py | 91 +++++++++++++----------------- 1 file changed, 40 insertions(+), 51 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index aa4c622b0f6..e8e7ea63b91 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1965,42 +1965,6 @@ def __iter__(self): """ Iterate over ``self``. - EXAMPLES:: - - sage: SetPartitions(3).list() - [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] - """ - base_set = list(self.base_set()) - def from_word(w): - sp = [] - for i, b in zip(base_set, w): - if len(sp) <= b: - sp.append([i]) - else: - sp[b].append(i) - return self.element_class(self, sp, check=False) - - # Ruskey, Combinatorial Generation, Algorithm 4.22 - N = len(base_set) - a = [0]*N - def gen(l, m): - if l >= N: - yield from_word(a) - else: - for i in range(m+1): - a[l] = i - for P in gen(l+1, m): - yield P - if m < N-1: - a[l] = m+1 - for P in gen(l+1, m+1): - yield P - return gen(1, 0) - - def _iter_Knuth(self): - """ - Iterate over ``self``. - EXAMPLES:: sage: SetPartitions(3).list() @@ -2021,12 +1985,12 @@ def from_word(w): # H1: initialize a = [0]*N if N <= 1: - yield self.from_word(base_set, a) + yield from_word(a) return b = [1]*N while True: # H2: visit - yield self.from_word(base_set, a) + yield from_word(a) if a[-1] == b[-1]: # H4: find j j = N-2 @@ -2036,7 +2000,7 @@ def from_word(w): if j == 0: break a[j] += 1 - # H6: zero out a_{j+1},...,a_n + # H6: zero out a_{j+1},...,a_{n-1} b[-1] = b[j] + (1 if a[j] == b[j] else 0) j += 1 while j < N-1: @@ -2045,7 +2009,7 @@ def from_word(w): j += 1 a[-1] = 0 else: - # increase a_n + # H3: increase a_{n-1} a[-1] += 1 def base_set(self): @@ -2189,7 +2153,7 @@ def __contains__(self, x): class SetPartitions_setn(SetPartitions_set): @staticmethod - def __classcall_private__(cls, s, n): + def __classcall_private__(cls, s, k): """ Normalize ``s`` to ensure a unique representation. @@ -2201,16 +2165,16 @@ def __classcall_private__(cls, s, n): sage: S1 is S2, S1 is S3 (True, True) """ - return super(SetPartitions_setn, cls).__classcall__(cls, frozenset(s), n) + return super(SetPartitions_setn, cls).__classcall__(cls, frozenset(s), k) - def __init__(self, s, n): + def __init__(self, s, k): """ TESTS:: sage: S = SetPartitions(5, 3) sage: TestSuite(S).run() """ - self.n = n + self._k = k SetPartitions_set.__init__(self, s) def _repr_(self): @@ -2220,7 +2184,7 @@ def _repr_(self): sage: SetPartitions(5, 3) Set partitions of {1, 2, 3, 4, 5} with 3 parts """ - return "Set partitions of %s with %s parts"%(Set(self._set), self.n) + return "Set partitions of %s with %s parts"%(Set(self._set), self._k) def cardinality(self): """ @@ -2234,7 +2198,7 @@ def cardinality(self): sage: stirling_number2(5,3) 25 """ - return stirling_number2(len(self._set), self.n) + return stirling_number2(len(self._set), self._k) def __iter__(self): """ @@ -2245,9 +2209,34 @@ def __iter__(self): sage: SetPartitions(3).list() [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] """ - for p in Partitions(len(self._set), length=self.n): - for sp in self._iterator_part(p): - yield self.element_class(self, sp) + base_set = list(self.base_set()) + def from_word(w): + sp = [] + for i, b in zip(base_set, w): + if len(sp) <= b: + sp.append([i]) + else: + sp[b].append(i) + return self.element_class(self, sp, check=False) + + # Ruskey, Combinatorial Generation, Algorithm 4.23 + n = len(base_set) + a = list(range(n)) + def gen(n, k): + if n == k: + yield from_word(a) + else: + for i in range(k): + a[n-1] = i + for P in gen(n-1, k): + yield P + a[n-1] = n-1 + if k > 1: + a[n-1] = k-1 + for P in gen(n-1, k-1): + yield P + a[n-1] = n-1 + return gen(n, self._k) def __contains__(self, x): """ @@ -2265,7 +2254,7 @@ def __contains__(self, x): """ if not SetPartitions_set.__contains__(self, x): return False - return len(x) == self.n + return len(x) == self._k def random_element(self): r""" @@ -2293,7 +2282,7 @@ def re(N, k): base_set = list(self.base_set()) N = len(base_set) - k = self.n + k = self._k p = re(N, k) return SetPartition([[base_set[e] for e in b] for b in p]) From d01f25dd69aa1788536c75bd6e8fa917548e075c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Jun 2018 16:28:46 +0200 Subject: [PATCH 124/284] adapt doctests --- src/sage/combinat/ncsym/ncsym.py | 6 ++--- src/sage/combinat/partition_algebra.py | 6 ++--- src/sage/combinat/set_partition.py | 31 +++++++++++++++++--------- src/sage/combinat/tutorial.py | 11 +++++---- src/sage/sandpiles/sandpile.py | 20 ++++++++--------- 5 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 5c678b539c0..17c1a941d6c 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -122,7 +122,7 @@ def nesting(la, nu): [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 1 0 0 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 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] @@ -387,7 +387,7 @@ def lt(s, t): if s == t: return False for p in s: - if len([ z for z in list(t) if len(z.intersection(p)) != 0 ]) != 1: + if len([z for z in t if z.intersection(p)]) != 1: return False return True @@ -1782,7 +1782,7 @@ def lt(s, t): if s == t: return False for p in s: - if len([ z for z in list(t) if len(z.intersection(p)) != 0 ]) != 1: + if len([z for z in t if z.intersection(p)]) != 1: return False return True diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 16d2ca06e66..5bcb4d9dcdc 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -81,11 +81,11 @@ def check(self): EXAMPLES:: sage: A2p5 = SetPartitionsAk(2.5) - sage: x = A2p5.first(); x # random - {{1, 2, 3, -1, -3, -2}} + sage: x = A2p5.first(); x + {{-3, -2, -1, 1, 2, 3}} sage: x.check() sage: y = A2p5.next(x); y - {{-3, -2, -1, 2, 3}, {1}} + {{-3, -1, 1, 2, 3}, {-2}} sage: y.check() """ #Check to make sure each element of x is a set diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index e8e7ea63b91..627a7ea2bc1 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -309,7 +309,7 @@ def __mul__(self, other): new_composition = [] for B in self: for C in other: - BintC = [b for b in B if b in C] + BintC = B.intersection(C) if BintC: new_composition.append(BintC) return SetPartition(new_composition) @@ -421,9 +421,9 @@ def coarsenings(self): [{{1, 2, 3, 4}}, {{1, 3}, {2, 4}}] sage: SetPartition([[1],[2,4],[3]]).coarsenings() [{{1, 2, 3, 4}}, - {{1}, {2, 3, 4}}, - {{1, 3}, {2, 4}}, {{1, 2, 4}, {3}}, + {{1, 3}, {2, 4}}, + {{1}, {2, 3, 4}}, {{1}, {2, 4}, {3}}] sage: SetPartition([]).coarsenings() [{}] @@ -496,7 +496,7 @@ class SetPartition(AbstractSetPartition): Here is the list of them:: sage: SetPartitions(3).list() - [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] + [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}] There are 6 set partitions of `\{1,2,3,4\}` whose underlying partition is `[2, 1, 1]`:: @@ -1826,9 +1826,9 @@ def __iter__(self): {{1, 2}}, {{1}, {2}}, {{1, 2, 3}}, - {{1}, {2, 3}}, - {{1, 3}, {2}}, {{1, 2}, {3}}, + {{1, 3}, {2}}, + {{1}, {2, 3}}, {{1}, {2}, {3}}, {{1, 2, 3, 4}}] """ @@ -1968,7 +1968,7 @@ def __iter__(self): EXAMPLES:: sage: SetPartitions(3).list() - [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] + [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}] """ base_set = list(self.base_set()) def from_word(w): @@ -2036,8 +2036,8 @@ def base_set_cardinality(self): class SetPartitions_setparts(SetPartitions_set): r""" - Class of all set partitions with fixed partition sizes corresponding to - an integer partition `\lambda`. + Set partitions with fixed partition sizes corresponding to an + integer partition `\lambda`. """ @staticmethod def __classcall_private__(cls, s, parts): @@ -2152,6 +2152,9 @@ def __contains__(self, x): return sorted(map(len, x)) == list(reversed(self.parts)) class SetPartitions_setn(SetPartitions_set): + """ + Set partitions with a given number of blocks. + """ @staticmethod def __classcall_private__(cls, s, k): """ @@ -2206,8 +2209,14 @@ def __iter__(self): EXAMPLES:: - sage: SetPartitions(3).list() - [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, {{1}, {2}, {3}}] + sage: SetPartitions(4, 2).list() + [{{1, 3, 4}, {2}}, + {{1, 4}, {2, 3}}, + {{1, 2, 4}, {3}}, + {{1, 3}, {2, 4}}, + {{1}, {2, 3, 4}}, + {{1, 2}, {3, 4}}, + {{1, 2, 3}, {4}}] """ base_set = list(self.base_set()) def from_word(w): diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index cf479065cfd..25d79509121 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -831,14 +831,17 @@ Set partitions:: - sage: C = SetPartitions([1,2,3]) + sage: C = SetPartitions(["a", "b", "c"]) sage: C - Set partitions of {1, 2, 3} + Set partitions of {'a', 'c', 'b'} sage: C.cardinality() 5 sage: C.list() - [{{1, 2, 3}}, {{1}, {2, 3}}, {{1, 3}, {2}}, {{1, 2}, {3}}, - {{1}, {2}, {3}}] + [{{'a', 'b', 'c'}}, + {{'a', 'c'}, {'b'}}, + {{'a', 'b'}, {'c'}}, + {{'a'}, {'b', 'c'}}, + {{'a'}, {'b'}, {'c'}}] Partial orders on a set of `8` elements, up to isomorphism:: diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index 12bf78bd440..95744ee0e23 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -6564,19 +6564,19 @@ def admissible_partitions(S, k): sage: from sage.sandpiles.sandpile import admissible_partitions sage: from sage.sandpiles.sandpile import partition_sandpile sage: S = sandpiles.Cycle(4) - sage: P = [admissible_partitions(S, i) for i in [2,3,4]] + sage: P = [set(admissible_partitions(S, i)) for i in [2,3,4]] sage: P - [[{{0}, {1, 2, 3}}, - {{0, 2, 3}, {1}}, - {{0, 1, 3}, {2}}, - {{0, 1, 2}, {3}}, + [{{{0}, {1, 2, 3}}, {{0, 1}, {2, 3}}, - {{0, 3}, {1, 2}}], - [{{0}, {1}, {2, 3}}, + {{0, 1, 2}, {3}}, + {{0, 1, 3}, {2}}, + {{0, 2, 3}, {1}}, + {{0, 3}, {1, 2}}}, + {{{0}, {1}, {2, 3}}, {{0}, {1, 2}, {3}}, - {{0, 3}, {1}, {2}}, - {{0, 1}, {2}, {3}}], - [{{0}, {1}, {2}, {3}}]] + {{0, 1}, {2}, {3}}, + {{0, 3}, {1}, {2}}}, + {{{0}, {1}, {2}, {3}}}] sage: for p in P: ....: sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p]) 6 From 511bf3ebfc3c5fef8320c5116592aca875971b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 1 Jun 2018 16:54:43 +0200 Subject: [PATCH 125/284] Introduce monkey-patching in CI --- .ci/update-env.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.ci/update-env.sh b/.ci/update-env.sh index 32ed5df6e8f..22c7a3a7ce3 100755 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -15,6 +15,18 @@ set -ex +# The maintainer of the CI environment, e.g., the people administrating the +# SageMath account on gitlab.com, can decide to inject an arbitrary +# base64-encoded script early into the CI execution. The script has access to +# all the CI variables, e.g., to find out which job is being executed, and +# it could do things such as "exit 1" to fail early, or "git merge" a fix for a +# known bug in CI. The CI_MONKEY_PATCH could of course also curl a more +# complicated script and execute that. +if [ -n "$CI_MONKEY_PATCH" ]; then + SCRIPT=$(base64 -d <<< "$CI_MONKEY_PATCH") + $SCRIPT +fi + # From the docker documentation: "A tag name must be valid ASCII and may # contain lowercase and uppercase letters, digits, underscores, periods and # dashes. A tag name may not start with a period or a dash and may contain a From eca8497a21a52fea2b29e1a7eac947ea9e6f0654 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 1 Jun 2018 21:51:31 +0200 Subject: [PATCH 126/284] fix more doctests --- src/sage/combinat/posets/lattices.py | 2 +- src/sage/combinat/tableau.py | 10 ++++++---- src/sage/tests/finite_poset.py | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 30b0bd5c1d4..077ed3b6bbc 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -3889,7 +3889,7 @@ def is_subdirectly_reducible(self, certificate=False): if len(A) == 1: for a in A[0]: if len(a) > 1: - x, y = a + x, y = min(a), max(a) return (False, (self._vertex_to_element(x), self._vertex_to_element(y))) diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index c66ef0234a9..c78124a1c4f 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -7429,8 +7429,8 @@ def random_element(self): EXAMPLES:: - sage: StandardTableaux(5).random_element() # random - [[1, 4, 5], [2], [3]] + sage: StandardTableaux(10).random_element() # random + [[1, 3, 6], [2, 5, 7], [4, 8], [9], [10]] sage: StandardTableaux(0).random_element() [] sage: StandardTableaux(1).random_element() @@ -7470,8 +7470,10 @@ def random_element(self): # singletons (corresponding to the fixed points of the # involution) and pairs (forming a perfect matching on the # remaining values). - permutation_cycle_rep = [(fixed_point,) for fixed_point in fixed_point_positions] + \ - list(PerfectMatchings(set(range(1, self.size + 1)) - set(fixed_point_positions)).random_element()) + matching = PerfectMatchings(set(range(1, self.size + 1)) + - set(fixed_point_positions)).random_element() + permutation_cycle_rep = ([(fixed_point,) for fixed_point in fixed_point_positions] + + [(a,b) for a,b in matching]) return from_cycles(self.size, permutation_cycle_rep).robinson_schensted()[0] diff --git a/src/sage/tests/finite_poset.py b/src/sage/tests/finite_poset.py index 6036901b1f1..48b59315614 100644 --- a/src/sage/tests/finite_poset.py +++ b/src/sage/tests/finite_poset.py @@ -309,7 +309,7 @@ def test_finite_lattice(L): c = L.is_regular(certificate=True)[1] if len(c[0]) == 1: raise ValueError("certificate error 1 in is_regular") - if Set(c[1]) not in c[0]: + if set(c[1]) not in c[0]: raise ValueError("certificate error 2 in is_regular") if L.congruence([c[1]]) == c[0]: raise ValueError("certificate error 3 in is_regular") From c27c580f7d1f9028e89f692b6cb7e97d42138724 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 2 Jun 2018 17:29:58 +0200 Subject: [PATCH 127/284] remove an expensive call to Set, adapt some doctests --- src/sage/combinat/set_partition.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 627a7ea2bc1..eb8197a02e6 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -296,7 +296,7 @@ def __mul__(self, other): sage: def mul2(s, t): ....: temp = [ss.intersection(ts) for ss in s for ts in t] - ....: temp = filter(lambda x: x, temp) + ....: temp = filter(bool, temp) ....: return s.__class__(s.parent(), temp) Let us check that this gives the same as ``__mul__`` on set @@ -342,13 +342,13 @@ def sup(self, t): res = list(self) for p in t: # find blocks in res which intersect p - inters = [(i, b) for i, b in enumerate(res) - if any(a in res[i] for a in p)] + inters = [(i, q) for i, q in enumerate(res) + if any(a in q for a in p)] # remove these blocks from res for i, _ in reversed(inters): del res[i] # add the union - res.append([e for _, b in inters for e in b]) + res.append([e for _, q in inters for e in q]) return self.parent()(res) def standard_form(self): @@ -452,10 +452,10 @@ def max_block_size(self): sage: pd = PartitionDiagram([[1,-3,-5],[2,4],[3,-1,-2],[5],[-4]]) sage: pd.max_block_size() 3 - sage: [d.max_block_size() for d in PartitionDiagrams(2)] - [4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1] - sage: [sp.max_block_size() for sp in SetPartitions(3)] - [3, 2, 2, 2, 1] + sage: sorted(d.max_block_size() for d in PartitionDiagrams(2)) + [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4] + sage: sorted(sp.max_block_size() for sp in SetPartitions(3)) + [1, 2, 2, 2, 3] """ return max(len(block) for block in self) @@ -1614,7 +1614,7 @@ def __contains__(self, x): return False # Check that all parts are disjoint - base_set = Set([e for p in x for e in p]) + base_set = set([e for p in x for e in p]) if len(base_set) != sum(map(len, x)): return False From 4ca5dfde0c7b3f6095d59a38af050a76beb7dfc3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 2 Jun 2018 17:31:09 +0200 Subject: [PATCH 128/284] revert a call to Set which works after #25496 --- src/sage/tests/finite_poset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tests/finite_poset.py b/src/sage/tests/finite_poset.py index 48b59315614..6036901b1f1 100644 --- a/src/sage/tests/finite_poset.py +++ b/src/sage/tests/finite_poset.py @@ -309,7 +309,7 @@ def test_finite_lattice(L): c = L.is_regular(certificate=True)[1] if len(c[0]) == 1: raise ValueError("certificate error 1 in is_regular") - if set(c[1]) not in c[0]: + if Set(c[1]) not in c[0]: raise ValueError("certificate error 2 in is_regular") if L.congruence([c[1]]) == c[0]: raise ValueError("certificate error 3 in is_regular") From 75a30435cc07e7414e096e88c14c5af2934a4690 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 2 Jun 2018 17:32:00 +0200 Subject: [PATCH 129/284] use the fast iterators and correct check --- src/sage/combinat/diagram_algebras.py | 29 +++++++++++++-------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 783019e569d..260ab2424ae 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -77,20 +77,12 @@ def partition_diagrams(k): """ if k in ZZ: S = SetPartitions(list(range(1, k+1)) + list(range(-k,0))) - for p in Partitions(2*k): - for i in S._iterator_part(p): - yield i elif k + ZZ(1)/ZZ(2) in ZZ: # Else k in 1/2 ZZ k = ZZ(k + ZZ(1) / ZZ(2)) S = SetPartitions(list(range(1, k+1)) + list(range(-k+1,0))) - for p in Partitions(2*k-1): - for sp in S._iterator_part(p): - sp = list(sp) - for i in range(len(sp)): - if k in sp[i]: - sp[i] += Set([-k]) - break - yield sp + else: + raise ValueError("argument %s must be a half-integer"%k) + return S def brauer_diagrams(k): r""" @@ -262,17 +254,25 @@ def check(self): Traceback (most recent call last): ... ValueError: {{-1}, {1}} does not represent two rows of vertices of order 2 + + sage: pd2 = da.AbstractPartitionDiagram(pd, [[[1,2],[-1,-2]]]) # indirect doctest + Traceback (most recent call last): + ... + ValueError: {{[-1, -2], [1, 2]}} does not represent two rows of vertices of order 2 """ if self._base_diagram: - tst = frozenset(flatten(self._base_diagram)) - if tst != self.parent()._set: + try: + tst = frozenset(e for B in self._base_diagram for e in B) + if tst != self.parent()._set: + raise TypeError + except TypeError: raise ValueError("{} does not represent two rows of vertices of order {}".format( self, self.parent().order)) def __hash__(self): """ Return the hash of ``self``. - + TESTS:: sage: import sage.combinat.diagram_algebras as da @@ -3888,4 +3888,3 @@ def set_partition_composition(sp1, sp2): ########################################################################## # END BORROWED CODE ########################################################################## - From 162fe52d0efcc665e71520d6aedb61faa1785a1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 5 Jun 2018 12:19:26 +0200 Subject: [PATCH 130/284] Reset WORKDIR to HOME nthiery reported that this used to be the case with our previous 8.1 images. So let's not break compatibility. --- docker/Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ad330cb7c13..18df023aa23 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -198,12 +198,13 @@ RUN make micro_release # interface if no parameters are passed in. # ################################################################################ FROM run-time-dependencies as sagemath +ARG HOME=/home/sage ARG SAGE_ROOT=/home/sage/sage COPY --chown=sage:sage --from=make-release $SAGE_ROOT/ $SAGE_ROOT/ # Put scripts to start gap, gp, maxima, ... in /usr/bin -WORKDIR $SAGE_ROOT -RUN sudo sage --nodotsage -c "install_scripts('/usr/bin')" +RUN sudo $SAGE_ROOT/sage --nodotsage -c "install_scripts('/usr/bin')" COPY ./docker/entrypoint.sh /usr/local/bin/sage-entrypoint +WORKDIR $HOME ENTRYPOINT ["/usr/local/bin/sage-entrypoint"] EXPOSE 8888 CMD ["sage"] From 4ef4e1830f78d45e9faf0ac48fa44f9d57da33ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 5 Jun 2018 12:26:55 +0200 Subject: [PATCH 131/284] Where to report bugs for the docker images --- docker/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/README.md b/docker/README.md index b9f9240f9e4..216fcc77364 100644 --- a/docker/README.md +++ b/docker/README.md @@ -48,6 +48,10 @@ Every push to our [GitLab repository](https://gitlab.com/sagemath/sage) triggers Have a look at `.circleci/` and `.gitlab-ci.yml` if you want to setup either continuous integration service for your own fork of the SageMath repository. +# Report bugs and issues + +Please tell us of any bugs or omissions at our [Issue Tracker](https://trac.sagemath.org) or contact us through the [sage-support](https://groups.google.com/forum/#!forum/sage-support) or the [sage-devel](https://groups.google.com/forum/#!forum/sage-devel) mailing lists. + # License The whole Sage software distribution is licensed under the General Public License, version 3. More details can be found in our [COPYING.txt](https://github.com/sagemath/sage/blob/master/COPYING.txt) From 6a3d5d0c70310ddcf28ac684c56bf279d189179c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 5 Jun 2018 12:31:11 +0200 Subject: [PATCH 132/284] drop trailing space --- .ci/protect-secrets.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/protect-secrets.sh b/.ci/protect-secrets.sh index ee781dddca8..527604106ca 100755 --- a/.ci/protect-secrets.sh +++ b/.ci/protect-secrets.sh @@ -29,7 +29,7 @@ function encrypt { } for name in `awk 'END { for (name in ENVIRON) { print name; } }' < /dev/null`; do -case "$name" in +case "$name" in SECRET_*) export $name="$(encrypt $name)" echo "Protected $name" From f7ea0f3c7a10baf475e96bd0bacd09f06039b14b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 5 Jun 2018 12:47:01 +0200 Subject: [PATCH 133/284] Make update-env posix compliant again --- .ci/update-env.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/update-env.sh b/.ci/update-env.sh index 22c7a3a7ce3..574595e48ac 100755 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -23,7 +23,7 @@ set -ex # known bug in CI. The CI_MONKEY_PATCH could of course also curl a more # complicated script and execute that. if [ -n "$CI_MONKEY_PATCH" ]; then - SCRIPT=$(base64 -d <<< "$CI_MONKEY_PATCH") + SCRIPT=$(echo "$CI_MONKEY_PATCH" | base64 -d) $SCRIPT fi From a7be1effe7a106c409958801c5d7875f353bf1e7 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 23 Jun 2018 00:12:10 +0200 Subject: [PATCH 134/284] fix (and partially improve) tests in diagram algebras --- src/sage/combinat/diagram_algebras.py | 308 +++++++++++++++----------- 1 file changed, 183 insertions(+), 125 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 260ab2424ae..4a6fd5e3076 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -65,21 +65,35 @@ def partition_diagrams(k): EXAMPLES:: sage: import sage.combinat.diagram_algebras as da - sage: [SetPartition(p) for p in da.partition_diagrams(2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, -1, 1}, {2}}, - {{-2, 1, 2}, {-1}}, {{-2}, {-1, 1, 2}}, {{-2, 1}, {-1, 2}}, - {{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, {{-2, 1}, {-1}, {2}}, - {{-2}, {-1, 1}, {2}}, {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] + sage: [p for p in da.partition_diagrams(2)] + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1, 2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1}, {2}}] sage: [SetPartition(p) for p in da.partition_diagrams(3/2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, 2}, {-1, 1}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}] """ if k in ZZ: S = SetPartitions(list(range(1, k+1)) + list(range(-k,0))) elif k + ZZ(1)/ZZ(2) in ZZ: # Else k in 1/2 ZZ k = ZZ(k + ZZ(1) / ZZ(2)) S = SetPartitions(list(range(1, k+1)) + list(range(-k+1,0))) + S = map(lambda p: [b.union([-k]) if k in b else b for b in p], S) else: raise ValueError("argument %s must be a half-integer"%k) return S @@ -146,17 +160,22 @@ def planar_diagrams(k): EXAMPLES:: sage: import sage.combinat.diagram_algebras as da - sage: [SetPartition(p) for p in da.planar_diagrams(2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, 1}, {-1}, {2}}, {{-2}, {-1, 1}, {2}}, - {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] - sage: [SetPartition(p) for p in da.planar_diagrams(3/2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, 2}, {-1, 1}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] + sage: all_diagrams = da.partition_diagrams(2) + sage: [p for p in all_diagrams if p not in da.planar_diagrams(2)] + [{{-2, 1}, {-1, 2}}] + sage: all_diagrams = da.partition_diagrams(5/2) + sage: [SetPartition(p) for p in all_diagrams if p not in da.planar_diagrams(5/2)] + [{{-3, -1, 3}, {-2, 1, 2}}, + {{-3, -2, 1, 3}, {-1, 2}}, + {{-3, -1, 1, 3}, {-2, 2}}, + {{-3, 1, 3}, {-2, -1, 2}}, + {{-3, 1, 3}, {-2, 2}, {-1}}, + {{-3, 1, 3}, {-2}, {-1, 2}}, + {{-3, -1, 2, 3}, {-2, 1}}, + {{-3, 3}, {-2, 1}, {-1, 2}}, + {{-3, -1, 3}, {-2, 1}, {2}}, + {{-3, -1, 3}, {-2, 2}, {1}}] + """ for i in partition_diagrams(k): if is_planar(i): @@ -172,15 +191,13 @@ def ideal_diagrams(k): EXAMPLES:: sage: import sage.combinat.diagram_algebras as da - sage: [SetPartition(p) for p in da.ideal_diagrams(2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, {{-2, -1, 1}, {2}}, - {{-2, 1, 2}, {-1}}, {{-2}, {-1, 1, 2}}, {{-2, -1}, {1, 2}}, - {{-2, -1}, {1}, {2}}, {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, 1}, {-1}, {2}}, {{-2}, {-1, 1}, {2}}, {{-2}, {-1}, {1, 2}}, - {{-2}, {-1}, {1}, {2}}] - sage: [SetPartition(p) for p in da.ideal_diagrams(3/2)] - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] + sage: all_diagrams = da.partition_diagrams(2) + sage: [SetPartition(p) for p in all_diagrams if p not in da.ideal_diagrams(2)] + [{{-2, 1}, {-1, 2}}, {{-2, 2}, {-1, 1}}] + + sage: all_diagrams = da.partition_diagrams(3/2) + sage: [SetPartition(p) for p in all_diagrams if p not in da.ideal_diagrams(3/2)] + [{{-2, 2}, {-1, 1}}] """ for i in partition_diagrams(k): if propagating_number(i) < k: @@ -501,15 +518,22 @@ class IdealDiagram(AbstractPartitionDiagram): sage: IDs(2) Ideal diagrams of order 2 sage: IDs(2).list() - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, -1}, {1, 2}}, - {{-2, -1}, {1}, {2}}, {{-2, 2}, {-1}, {1}}, - {{-2}, {-1, 2}, {1}}, {{-2, 1}, {-1}, {2}}, - {{-2}, {-1, 1}, {2}}, {{-2}, {-1}, {1, 2}}, + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, {{-2}, {-1}, {1}, {2}}] + sage: from sage.combinat.diagram_algebras import PartitionDiagrams as PDs - sage: PDs(4).cardinality() == factorial(4) + IDs(4).cardinality() # long time + sage: PDs(4).cardinality() == factorial(4) + IDs(4).cardinality() True """ @staticmethod @@ -566,13 +590,20 @@ class PlanarDiagram(AbstractPartitionDiagram): sage: PlanarDiagrams(2) Planar diagrams of order 2 sage: PlanarDiagrams(2).list() - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, 1}, {-1}, {2}}, {{-2}, {-1, 1}, {2}}, - {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1}, {2}}] """ @staticmethod def __classcall_private__(cls, diag): @@ -1056,19 +1087,19 @@ def __init__(self, order, category=None): Category of finite sets sage: pd = da.PartitionDiagrams(2) - sage: TestSuite(pd).run() # long time + sage: TestSuite(pd).run() sage: bd = da.BrauerDiagrams(2) - sage: TestSuite(bd).run() # long time + sage: TestSuite(bd).run() sage: td = da.TemperleyLiebDiagrams(2) - sage: TestSuite(td).run() # long time + sage: TestSuite(td).run() sage: pld = da.PlanarDiagrams(2) - sage: TestSuite(pld).run() # long time + sage: TestSuite(pld).run() sage: id = da.IdealDiagrams(2) - sage: TestSuite(id).run() # long time + sage: TestSuite(id).run() """ if category is None: category = FiniteEnumeratedSets() @@ -1097,22 +1128,28 @@ def __iter__(self): TESTS:: sage: import sage.combinat.diagram_algebras as da - sage: pd = da.PartitionDiagrams(2) sage: list(da.PartitionDiagrams(2)) - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, 1}, {-1, 2}}, - {{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}, - {{-2, -1}, {1}, {2}}, {{-2, 2}, {-1}, {1}}, - {{-2}, {-1, 2}, {1}}, {{-2, 1}, {-1}, {2}}, - {{-2}, {-1, 1}, {2}}, {{-2}, {-1}, {1, 2}}, + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1, 2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, {{-2}, {-1}, {1}, {2}}] sage: list(da.PartitionDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1, 1}}, {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, {{-2, 2}, {-1}, {1}}] sage: list(da.BrauerDiagrams(5/2)) @@ -1129,31 +1166,45 @@ def __iter__(self): sage: list(da.PlanarDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1, 1}}, {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, {{-2, 2}, {-1}, {1}}] + sage: list(da.PlanarDiagrams(2)) - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1}, {1, 2}}, {{-2, -1}, {1}, {2}}, - {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, 1}, {-1}, {2}}, {{-2}, {-1, 1}, {2}}, - {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1}, {2}}] sage: list(da.IdealDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, -1, 2}, {1}}, - {{-2, 1, 2}, {-1}}, - {{-2, 2}, {-1}, {1}}] + {{-2, 1, 2}, {-1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}] sage: list(da.IdealDiagrams(2)) - [{{-2, -1, 1, 2}}, {{-2, -1, 2}, {1}}, - {{-2, -1, 1}, {2}}, {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, {{-2, -1}, {1, 2}}, - {{-2, -1}, {1}, {2}}, {{-2, 2}, {-1}, {1}}, - {{-2}, {-1, 2}, {1}}, {{-2, 1}, {-1}, {2}}, - {{-2}, {-1, 1}, {2}}, {{-2}, {-1}, {1, 2}}, + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2}, {-1, 1, 2}}, + {{-2, -1}, {1, 2}}, + {{-2}, {-1}, {1, 2}}, + {{-2, -1, 1}, {2}}, + {{-2, 1}, {-1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1}, {2}}, + {{-2}, {-1, 2}, {1}}, + {{-2, -1}, {1}, {2}}, {{-2}, {-1}, {1}, {2}}] """ # The _diagram_func gets set as a method, but we want to @@ -1544,8 +1595,8 @@ class IdealDiagrams(AbstractPartitionDiagrams): True sage: da.IdealDiagrams(3/2).list() [{{-2, -1, 1, 2}}, - {{-2, -1, 2}, {1}}, {{-2, 1, 2}, {-1}}, + {{-2, -1, 2}, {1}}, {{-2, 2}, {-1}, {1}}] """ Element = IdealDiagram @@ -1585,13 +1636,20 @@ class DiagramAlgebra(CombinatorialFreeModule): sage: R. = QQ[] sage: D = da.DiagramAlgebra(2, x, R, 'P', da.PartitionDiagrams(2)) sage: list(D.basis()) - [P{{-2, -1, 1, 2}}, P{{-2, -1, 2}, {1}}, - P{{-2, -1, 1}, {2}}, P{{-2, 1, 2}, {-1}}, - P{{-2}, {-1, 1, 2}}, P{{-2, 1}, {-1, 2}}, - P{{-2, 2}, {-1, 1}}, P{{-2, -1}, {1, 2}}, - P{{-2, -1}, {1}, {2}}, P{{-2, 2}, {-1}, {1}}, - P{{-2}, {-1, 2}, {1}}, P{{-2, 1}, {-1}, {2}}, - P{{-2}, {-1, 1}, {2}}, P{{-2}, {-1}, {1, 2}}, + [P{{-2, -1, 1, 2}}, + P{{-2, 1, 2}, {-1}}, + P{{-2}, {-1, 1, 2}}, + P{{-2, -1}, {1, 2}}, + P{{-2}, {-1}, {1, 2}}, + P{{-2, -1, 1}, {2}}, + P{{-2, 1}, {-1, 2}}, + P{{-2, 1}, {-1}, {2}}, + P{{-2, 2}, {-1, 1}}, + P{{-2, -1, 2}, {1}}, + P{{-2, 2}, {-1}, {1}}, + P{{-2}, {-1, 1}, {2}}, + P{{-2}, {-1, 2}, {1}}, + P{{-2, -1}, {1}, {2}}, P{{-2}, {-1}, {1}, {2}}] """ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): @@ -2073,13 +2131,20 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: P.basis().keys()([[-2, 1, 2], [-1]]) {{-2, 1, 2}, {-1}} sage: P.basis().list() - [P{{-2, -1, 1, 2}}, P{{-2, -1, 2}, {1}}, - P{{-2, -1, 1}, {2}}, P{{-2, 1, 2}, {-1}}, - P{{-2}, {-1, 1, 2}}, P{{-2, 1}, {-1, 2}}, - P{{-2, 2}, {-1, 1}}, P{{-2, -1}, {1, 2}}, - P{{-2, -1}, {1}, {2}}, P{{-2, 2}, {-1}, {1}}, - P{{-2}, {-1, 2}, {1}}, P{{-2, 1}, {-1}, {2}}, - P{{-2}, {-1, 1}, {2}}, P{{-2}, {-1}, {1, 2}}, + [P{{-2, -1, 1, 2}}, + P{{-2, 1, 2}, {-1}}, + P{{-2}, {-1, 1, 2}}, + P{{-2, -1}, {1, 2}}, + P{{-2}, {-1}, {1, 2}}, + P{{-2, -1, 1}, {2}}, + P{{-2, 1}, {-1, 2}}, + P{{-2, 1}, {-1}, {2}}, + P{{-2, 2}, {-1, 1}}, + P{{-2, -1, 2}, {1}}, + P{{-2, 2}, {-1}, {1}}, + P{{-2}, {-1, 1}, {2}}, + P{{-2}, {-1, 2}, {1}}, + P{{-2, -1}, {1}, {2}}, P{{-2}, {-1}, {1}, {2}}] sage: E = P([[1,2],[-2,-1]]); E P{{-2, -1}, {1, 2}} @@ -2246,15 +2311,13 @@ def _element_constructor_(self, x): sage: B = BrauerAlgebra(3, x, R) sage: O = A.orbit_basis() sage: O2.an_element() - 3*O{{-2, -1, 1}, {2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, -1, 2}, {1}} + 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: A(O2.an_element()) - 3*P{{-3, 3}, {-2, -1, 1}, {2}} - 3*P{{-3, 3}, {-2, -1, 1, 2}} - + 2*P{{-3, 3}, {-2, -1, 2}, {1}} + 3*P{{-3, 3}, {-2}, {-1, 1, 2}} - 3*P{{-3, 3}, {-2, -1, 1, 2}} + 2*P{{-3, 3}, {-2, 1, 2}, {-1}} sage: A2.an_element() - 3*P{{-2, -1, 1}, {2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: A(A2.an_element()) - 3*P{{-3, 3}, {-2, -1, 1}, {2}} + 2*P{{-3, 3}, {-2, -1, 1, 2}} - + 2*P{{-3, 3}, {-2, -1, 2}, {1}} + 3*P{{-3, 3}, {-2}, {-1, 1, 2}} + 2*P{{-3, 3}, {-2, -1, 1, 2}} + 2*P{{-3, 3}, {-2, 1, 2}, {-1}} sage: S.an_element() [1, 2, 3] + 2*[1, 3, 2] + 3*[2, 1, 3] + [3, 1, 2] sage: A(S.an_element()) @@ -2267,11 +2330,9 @@ def _element_constructor_(self, x): 3*P{{-3, 1}, {-2, -1}, {2, 3}} + 2*P{{-3, 1}, {-2, 2}, {-1, 3}} + 2*P{{-3, 1}, {-2, 3}, {-1, 2}} sage: O.an_element() - 2*O{{-3, -2, -1, 1, 2, 3}} + 3*O{{-3, -2, -1, 1, 3}, {2}} - + 2*O{{-3, -2, -1, 2, 3}, {1}} + 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} sage: A(O.an_element()) - -3*P{{-3, -2, -1, 1, 2, 3}} + 3*P{{-3, -2, -1, 1, 3}, {2}} - + 2*P{{-3, -2, -1, 2, 3}, {1}} + 3*P{{-3}, {-2, -1, 1, 2, 3}} - 3*P{{-3, -2, -1, 1, 2, 3}} + 2*P{{-3, -1, 1, 2, 3}, {-2}} sage: A([]) P{{-3, 3}, {-2, 2}, {-1, 1}} sage: A(4) @@ -2398,12 +2459,9 @@ def _coerce_map_from_(self, R): TESTS:: sage: elt = O3.an_element(); elt - 2*O{{-3, -2, -1, 1, 2, 3}} + 3*O{{-3, -2, -1, 1, 3}, {2}} - + 2*O{{-3, -2, -1, 2, 3}, {1}} + 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} sage: A._coerce_map_from_(O3)(elt) - -3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} - + 3*P{{-4, 4}, {-3, -2, -1, 1, 3}, {2}} - + 2*P{{-4, 4}, {-3, -2, -1, 2, 3}, {1}} + 3*P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + 2*P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} """ # coerce from Orbit basis. if isinstance(R, OrbitBasis): @@ -2480,9 +2538,9 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) sage: pp = P.an_element(); pp - 3*P{{-2, -1, 1}, {2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: pp.to_orbit_basis() - 3*O{{-2, -1, 1}, {2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, -1, 2}, {1}} + 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} """ OP = self.parent().orbit_basis() return OP(self) @@ -2713,7 +2771,7 @@ def diagram_basis(self): Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field sage: P2(O2.an_element()) - 3*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} TESTS:: @@ -2789,7 +2847,7 @@ def product_on_basis(self, d1, d2): sage: o3 * o1 == o1 * o3 and o3 * o1 == o3 True sage: o3 * o3 - 6*O{{-2, -1, 1}, {2}} + 4*O{{-2, -1, 1, 2}} + 4*O{{-2, -1, 2}, {1}} + 6*O{{-2}, {-1, 1, 2}} + 4*O{{-2, -1, 1, 2}} + 4*O{{-2, 1, 2}, {-1}} We compute Examples 4.5 in [BH2017]_:: @@ -2888,12 +2946,12 @@ def to_diagram_basis(self): sage: P = PartitionAlgebra(2, x, R) sage: O = P.orbit_basis() sage: elt = O.an_element(); elt - 3*O{{-2, -1, 1}, {2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, -1, 2}, {1}} + 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: elt.to_diagram_basis() - 3*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: pp = P.an_element() sage: op = pp.to_orbit_basis(); op - 3*O{{-2, -1, 1}, {2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, -1, 2}, {1}} + 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: pp == op.to_diagram_basis() True """ @@ -3355,18 +3413,18 @@ class PlanarAlgebra(SubPartitionAlgebra, UnitDiagramMixin): {{-2, 2}, {-1, 1}} sage: Pl.basis().list() [Pl{{-2, -1, 1, 2}}, - Pl{{-2, -1, 2}, {1}}, - Pl{{-2, -1, 1}, {2}}, Pl{{-2, 1, 2}, {-1}}, Pl{{-2}, {-1, 1, 2}}, - Pl{{-2, 2}, {-1, 1}}, Pl{{-2, -1}, {1, 2}}, - Pl{{-2, -1}, {1}, {2}}, - Pl{{-2, 2}, {-1}, {1}}, - Pl{{-2}, {-1, 2}, {1}}, + Pl{{-2}, {-1}, {1, 2}}, + Pl{{-2, -1, 1}, {2}}, Pl{{-2, 1}, {-1}, {2}}, + Pl{{-2, 2}, {-1, 1}}, + Pl{{-2, -1, 2}, {1}}, + Pl{{-2, 2}, {-1}, {1}}, Pl{{-2}, {-1, 1}, {2}}, - Pl{{-2}, {-1}, {1, 2}}, + Pl{{-2}, {-1, 2}, {1}}, + Pl{{-2, -1}, {1}, {2}}, Pl{{-2}, {-1}, {1}, {2}}] sage: E = Pl([[1,2],[-1,-2]]) sage: E^2 == x*E @@ -3444,17 +3502,17 @@ class PropagatingIdeal(SubPartitionAlgebra): Ideal diagrams of order 2 sage: I.basis().list() [I{{-2, -1, 1, 2}}, - I{{-2, -1, 2}, {1}}, - I{{-2, -1, 1}, {2}}, I{{-2, 1, 2}, {-1}}, I{{-2}, {-1, 1, 2}}, I{{-2, -1}, {1, 2}}, - I{{-2, -1}, {1}, {2}}, - I{{-2, 2}, {-1}, {1}}, - I{{-2}, {-1, 2}, {1}}, + I{{-2}, {-1}, {1, 2}}, + I{{-2, -1, 1}, {2}}, I{{-2, 1}, {-1}, {2}}, + I{{-2, -1, 2}, {1}}, + I{{-2, 2}, {-1}, {1}}, I{{-2}, {-1, 1}, {2}}, - I{{-2}, {-1}, {1, 2}}, + I{{-2}, {-1, 2}, {1}}, + I{{-2, -1}, {1}, {2}}, I{{-2}, {-1}, {1}, {2}}] sage: E = I([[1,2],[-1,-2]]) sage: E^2 == x*E From ce6e63c5506a1e686310fc4fe2661bde4edac41e Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 23 Jun 2018 09:35:40 +0200 Subject: [PATCH 135/284] remove unused imports --- src/sage/combinat/diagram_algebras.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 4a6fd5e3076..46dfe568943 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -34,10 +34,8 @@ from sage.combinat.combinat import bell_number, catalan_number from sage.structure.global_options import GlobalOptions from sage.combinat.set_partition import SetPartitions, AbstractSetPartition -from sage.combinat.partition import Partitions from sage.combinat.symmetric_group_algebra import SymmetricGroupAlgebra_n from sage.combinat.permutation import Permutations -from sage.sets.set import Set from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute From 589f13596c3ab0f51fdc8792e5c5b3555a19803b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 24 Jun 2018 23:46:11 +0200 Subject: [PATCH 136/284] adress reviewer's comments: new method number_of_blocks and deprecation of n, partition_diagrams now returns iterator --- src/sage/combinat/diagram_algebras.py | 2 +- src/sage/combinat/set_partition.py | 29 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 46dfe568943..a1711ed8cd0 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -91,7 +91,7 @@ def partition_diagrams(k): elif k + ZZ(1)/ZZ(2) in ZZ: # Else k in 1/2 ZZ k = ZZ(k + ZZ(1) / ZZ(2)) S = SetPartitions(list(range(1, k+1)) + list(range(-k+1,0))) - S = map(lambda p: [b.union([-k]) if k in b else b for b in p], S) + S = itertools.imap(lambda p: [b.union([-k]) if k in b else b for b in p], S) else: raise ValueError("argument %s must be a half-integer"%k) return S diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index eb8197a02e6..e944d9c667c 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -2189,6 +2189,35 @@ def _repr_(self): """ return "Set partitions of %s with %s parts"%(Set(self._set), self._k) + @property + def n(self): + """ + ``self.n`` is deprecated; use :meth:`number_of_blocks` instead. + + TESTS:: + + sage: SetPartitions(5, 3).n + doctest:...: DeprecationWarning: The attribute n for the number of blocks is deprecated, use the method number_of_blocks instead. + See https://trac.sagemath.org/25462 for details. + 3 + + """ + from sage.misc.superseded import deprecation + deprecation(25462, 'The attribute n for the number of blocks is deprecated, use the method number_of_blocks instead.') + return self.number_of_blocks() + + def number_of_blocks(self): + """ + Return the number of blocks of the set partitions in this class. + + EXAMPLES:: + + sage: SetPartitions(5, 3).number_of_blocks() + 3 + + """ + return self._k + def cardinality(self): """ The Stirling number of the second kind is the number of partitions From ca293e395e482e3da3fe5e875b296eec4518cc8c Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 25 Jun 2018 10:08:58 +0200 Subject: [PATCH 137/284] avoid itertools and modify docstring --- src/sage/combinat/diagram_algebras.py | 6 ++++-- src/sage/combinat/set_partition.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index a1711ed8cd0..e44decfed8d 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -88,13 +88,15 @@ def partition_diagrams(k): """ if k in ZZ: S = SetPartitions(list(range(1, k+1)) + list(range(-k,0))) + for p in S: + yield p elif k + ZZ(1)/ZZ(2) in ZZ: # Else k in 1/2 ZZ k = ZZ(k + ZZ(1) / ZZ(2)) S = SetPartitions(list(range(1, k+1)) + list(range(-k+1,0))) - S = itertools.imap(lambda p: [b.union([-k]) if k in b else b for b in p], S) + for p in S: + yield [b.union([-k]) if k in b else b for b in p] else: raise ValueError("argument %s must be a half-integer"%k) - return S def brauer_diagrams(k): r""" diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index e944d9c667c..957df78ef0a 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -2208,7 +2208,7 @@ def n(self): def number_of_blocks(self): """ - Return the number of blocks of the set partitions in this class. + Return the number of blocks of the set partitions in ``self``. EXAMPLES:: From b7480975f2b66e2c8a13a9d1eafecc06605edc1f Mon Sep 17 00:00:00 2001 From: Robert Bradshaw Date: Wed, 30 Apr 2014 00:41:52 -0700 Subject: [PATCH 138/284] Fix hash for unreduced fraction field elements. --- src/sage/rings/fraction_field_element.pyx | 66 ++++++++++++++++++++--- 1 file changed, 60 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index bcb6509f0e3..524c91fcf8b 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -91,6 +91,7 @@ cdef class FractionFieldElement(FieldElement): """ cdef object __numerator cdef object __denominator + cdef bint _is_reduced def __init__(self, parent, numerator, denominator=1, coerce=True, reduce=True): @@ -157,7 +158,7 @@ cdef class FractionFieldElement(FieldElement): nden = codomain.coerce(self.__denominator._im_gens_(codomain, im_gens)) return codomain.coerce(nnum/nden) - def reduce(self): + cpdef reduce(self): """ Divides out the gcd of the numerator and denominator. @@ -173,6 +174,8 @@ cdef class FractionFieldElement(FieldElement): sage: f.reduce(); f x + 1.0 """ + if self._is_reduced: + return try: g = self.__numerator.gcd(self.__denominator) if not g.is_unit(): @@ -189,6 +192,7 @@ cdef class FractionFieldElement(FieldElement): pass self.__numerator = num self.__denominator = den + self._is_reduced = True except AttributeError: raise ArithmeticError("unable to reduce because lack of gcd or quo_rem algorithm") except TypeError: @@ -196,6 +200,22 @@ cdef class FractionFieldElement(FieldElement): except NotImplementedError: raise ArithmeticError("unable to reduce because gcd algorithm not implemented on input") + cpdef normalize(self): + """ + Returns a normalized representation of self. + + In particular, for any a == b, after normalization they will have the + same numerator and denominator. + + EXAMPLES:: + + sage: R. = Frac(ZZ['x']) + sage: s = (2*x + 2) / (4*x) + sage: s.normalize(); s + (x + 1)/(2*x) + """ + self.reduce() + def __copy__(self): """ Make a copy of ``self``. @@ -341,17 +361,31 @@ cdef class FractionFieldElement(FieldElement): 1 sage: hash(R(1)/R(2))==hash(1/2) True + + Ensure normalization is done before hashing the numerator and + denominator, fixing trac #16268:: + + sage: Ku. = FractionField(PolynomialRing(QQ,'u')) + sage: a = 27*u^2+81*u+243 + sage: b = 27*u-81 + sage: c = u^2 + 3*u + 9 + sage: d = u-3 + sage: s = a/b + sage: t = c/d + sage: s==t + True + sage: len(Set([s,t])) + 1 """ # This is same algorithm as used for members of QQ #cdef long n, d + self.normalize() n = hash(self.__numerator) d = hash(self.__denominator) if d == 1: return n - n = n ^ d - if n == -1: - return -2 - return n + else: + return n ^ d def __call__(self, *x, **kwds): """ @@ -1027,7 +1061,7 @@ cdef class FractionFieldElement(FieldElement): raise NotImplementedError -class FractionFieldElement_1poly_field(FractionFieldElement): +cdef class FractionFieldElement_1poly_field(FractionFieldElement): """ A fraction field element where the parent is the fraction field of a univariate polynomial ring. @@ -1070,6 +1104,26 @@ class FractionFieldElement_1poly_field(FractionFieldElement): L.sort() return L + cpdef normalize(self): + """ + Returns a normalized representation of self. + + In particular, for any a == b, after normalization they will have the + same numerator and denominator. + + EXAMPLES:: + + sage: R. = QQ[] + sage: s = (1 + x) / (2*x); s + (x + 1)/(2*x) + sage: s.normalize(); s + (1/2*x + 1/2)/x + """ + self.reduce() + leading = self.__denominator.leading_coefficient() + if leading != 1: + self.__numerator /= leading + self.__denominator /= leading def make_element(parent, numerator, denominator): """ From 2850652793ef87f2b261d267ff521e8d925b1f6f Mon Sep 17 00:00:00 2001 From: Erik Massop Date: Fri, 2 May 2014 15:57:02 +0200 Subject: [PATCH 139/284] Fix documentation of normalize --- src/sage/rings/fraction_field_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 524c91fcf8b..4341d2b8a5d 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -202,7 +202,7 @@ cdef class FractionFieldElement(FieldElement): cpdef normalize(self): """ - Returns a normalized representation of self. + Picks a normalized representation of self. In particular, for any a == b, after normalization they will have the same numerator and denominator. @@ -1106,7 +1106,7 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): cpdef normalize(self): """ - Returns a normalized representation of self. + Picks a normalized representation of self. In particular, for any a == b, after normalization they will have the same numerator and denominator. From 26c87d3bc0909edb33530f0b649f79b8d79871ac Mon Sep 17 00:00:00 2001 From: Erik Massop Date: Sun, 4 May 2014 18:51:45 +0200 Subject: [PATCH 140/284] Fix doctest FractionFieldElement_1poly_field is a cdef class now --- src/sage/rings/fraction_field.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 201e132bb73..a04957ec5fd 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -862,7 +862,7 @@ def __init__(self, R, sage: R. = QQ[]; K = R.fraction_field() sage: K._element_class - + """ FractionField_generic.__init__(self, R, element_class) From 78b59908725d9c98db66cdf2af8dadd2c719f5f0 Mon Sep 17 00:00:00 2001 From: Erik Massop Date: Sun, 4 May 2014 18:58:58 +0200 Subject: [PATCH 141/284] More careful hashing of FractionFieldElement * Remove normalize from general FractionFieldElement * Call reduce automatically only for exact rings * Refuse returning hash when independence of representative uncertain --- src/sage/rings/fraction_field_element.pyx | 81 ++++++++++++----------- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 4341d2b8a5d..c355d5954a7 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -162,6 +162,8 @@ cdef class FractionFieldElement(FieldElement): """ Divides out the gcd of the numerator and denominator. + If the denominator becomes a unit, it becomes 1. + Automatically called for exact rings, but because it may be numerically unstable for inexact rings it must be called manually in that case. @@ -200,22 +202,6 @@ cdef class FractionFieldElement(FieldElement): except NotImplementedError: raise ArithmeticError("unable to reduce because gcd algorithm not implemented on input") - cpdef normalize(self): - """ - Picks a normalized representation of self. - - In particular, for any a == b, after normalization they will have the - same numerator and denominator. - - EXAMPLES:: - - sage: R. = Frac(ZZ['x']) - sage: s = (2*x + 2) / (4*x) - sage: s.normalize(); s - (x + 1)/(2*x) - """ - self.reduce() - def __copy__(self): """ Make a copy of ``self``. @@ -362,30 +348,15 @@ cdef class FractionFieldElement(FieldElement): sage: hash(R(1)/R(2))==hash(1/2) True - Ensure normalization is done before hashing the numerator and - denominator, fixing trac #16268:: - - sage: Ku. = FractionField(PolynomialRing(QQ,'u')) - sage: a = 27*u^2+81*u+243 - sage: b = 27*u-81 - sage: c = u^2 + 3*u + 9 - sage: d = u-3 - sage: s = a/b - sage: t = c/d - sage: s==t - True - sage: len(Set([s,t])) - 1 """ - # This is same algorithm as used for members of QQ - #cdef long n, d - self.normalize() - n = hash(self.__numerator) - d = hash(self.__denominator) - if d == 1: - return n - else: - return n ^ d + if self._parent.is_exact(): + try: + self.reduce() + except ArithmeticError: + pass + if self.__denominator.is_one(): + return hash(self.__numerator) + raise NotImplementedError("Do not know how to hash elements of general fraction field") def __call__(self, *x, **kwds): """ @@ -1125,6 +1096,38 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): self.__numerator /= leading self.__denominator /= leading + def __hash__(self): + """ + This function hashes in a special way to ensure that elements of + a ring `R` and their images in a fraction field of `R` have the same + hash. This enables them to be used as keys interchangeably in a + dictionary (since ``==`` will claim them equal). + + Ensure normalization is done before hashing the numerator and + denominator, fixing trac #16268:: + + sage: Ku. = FractionField(PolynomialRing(QQ,'u')) + sage: a = 27*u^2+81*u+243 + sage: b = 27*u-81 + sage: c = u^2 + 3*u + 9 + sage: d = u-3 + sage: s = a/b + sage: t = c/d + sage: s==t + True + sage: len(Set([s,t])) + 1 + """ + # This is same algorithm as used for members of QQ + #cdef long n, d + self.normalize() + n = hash(self.__numerator) + d = hash(self.__denominator) + if d == 1: + return n + else: + return n ^ d + def make_element(parent, numerator, denominator): """ Used for unpickling :class:`FractionFieldElement` objects (and subclasses). From e7689732921af17a0f100651615e429d9711979e Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 23 Apr 2018 12:11:14 +0200 Subject: [PATCH 142/284] slightly simplify/robustify FractionFieldElement.reduce() --- src/sage/rings/fraction_field_element.pyx | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index c355d5954a7..507f4f56a0d 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -181,19 +181,8 @@ cdef class FractionFieldElement(FieldElement): try: g = self.__numerator.gcd(self.__denominator) if not g.is_unit(): - num, _ = self.__numerator.quo_rem(g) - den, _ = self.__denominator.quo_rem(g) - else: - num = self.__numerator - den = self.__denominator - if not den.is_one() and den.is_unit(): - try: - num *= den.inverse_of_unit() - den = den.parent().one() - except Exception: - pass - self.__numerator = num - self.__denominator = den + self.__numerator //= g + self.__denominator //= g self._is_reduced = True except AttributeError: raise ArithmeticError("unable to reduce because lack of gcd or quo_rem algorithm") @@ -201,6 +190,14 @@ cdef class FractionFieldElement(FieldElement): raise ArithmeticError("unable to reduce because gcd algorithm doesn't work on input") except NotImplementedError: raise ArithmeticError("unable to reduce because gcd algorithm not implemented on input") + if not self.__denominator.is_one() and self.__denominator.is_unit(): + try: + inv = self.__denominator.inverse_of_unit() + except Exception: + pass + else: + self.__numerator *= inv + self.__denominator = self.__denominator.parent().one() def __copy__(self): """ From 029346f6b30c052156b56572248145344e7e8bc9 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 23 Apr 2018 12:18:25 +0200 Subject: [PATCH 143/284] FractionFieldElement: merge reduce() and normalize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For elements of fields like ℚ(x,y)(t), it is typically much better for performance to always normalize the leading coefficients. Let's do it by default instead of requiring users to call a separate normalization method. --- src/sage/rings/fraction_field_element.pyx | 39 +++++++++++++---------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 507f4f56a0d..89ce2ef1bb7 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -160,9 +160,12 @@ cdef class FractionFieldElement(FieldElement): cpdef reduce(self): """ - Divides out the gcd of the numerator and denominator. + Reduce this fraction. - If the denominator becomes a unit, it becomes 1. + Divides out the gcd of the numerator and denominator. If the + denominator becomes a unit, it becomes 1. Additionally, depending on + the base ring, the leading coefficients of the numerator and the + denominator may be normalized to 1. Automatically called for exact rings, but because it may be numerically unstable for inexact rings it must be called manually @@ -1032,7 +1035,7 @@ cdef class FractionFieldElement(FieldElement): cdef class FractionFieldElement_1poly_field(FractionFieldElement): """ A fraction field element where the parent is the fraction field of a - univariate polynomial ring. + univariate polynomial ring over a field. Many of the functions here are included for coherence with number fields. """ @@ -1072,26 +1075,31 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): L.sort() return L - cpdef normalize(self): + cpdef reduce(self): """ - Picks a normalized representation of self. + Pick a normalized representation of self. In particular, for any a == b, after normalization they will have the same numerator and denominator. - EXAMPLES:: + EXAMPLES: + + For univariate rational functions over a field, we have:: sage: R. = QQ[] - sage: s = (1 + x) / (2*x); s - (x + 1)/(2*x) - sage: s.normalize(); s + sage: (2 + 2*x) / (4*x) # indirect doctest (1/2*x + 1/2)/x + + Compare with:: + + sage: R. = ZZ[] + sage: (2 + 2*x) / (4*x) + (x + 1)/(2*x) """ - self.reduce() - leading = self.__denominator.leading_coefficient() - if leading != 1: - self.__numerator /= leading - self.__denominator /= leading + super(self.__class__, self).reduce() + invlc = ~self.__denominator.leading_coefficient() + self.__denominator = self.__denominator.monic() + self.__numerator *= invlc def __hash__(self): """ @@ -1116,8 +1124,7 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): 1 """ # This is same algorithm as used for members of QQ - #cdef long n, d - self.normalize() + self.reduce() n = hash(self.__numerator) d = hash(self.__denominator) if d == 1: From 80f18f0f0e1936c821db76f9b02576dde26f9f97 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 23 Apr 2018 14:14:58 +0200 Subject: [PATCH 144/284] Tweak fraction field element hashing - merge back FractionFieldElement_1poly_field.__hash__() into FractionFieldElement.__hash__() (partial revert of 198c0596630bb4ecc200e62c55715441e6f38d5c); - let exceptions raised by reduce() propagate: this will typically be more informative than raising a new exception; - hash unreduced fractions without complaining over inexact rings, as a compatibility tradeoff. --- src/sage/rings/fraction_field_element.pyx | 68 +++++++++++------------ 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 89ce2ef1bb7..4d6c2d6c4af 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -348,15 +348,40 @@ cdef class FractionFieldElement(FieldElement): sage: hash(R(1)/R(2))==hash(1/2) True + Check that :trac:`16268` is fixed:: + + sage: ku. = FractionField(PolynomialRing(QQ,'u')) + sage: a = 27*u^2+81*u+243 + sage: b = 27*u-81 + sage: c = u^2 + 3*u + 9 + sage: d = u-3 + sage: s = a/b + sage: t = c/d + sage: s == t + True + sage: len(set([s,t])) + 1 """ - if self._parent.is_exact(): - try: - self.reduce() - except ArithmeticError: - pass if self.__denominator.is_one(): + # Handle this case even over rings that don't support reduction, to + # avoid breaking existing code that carelessly mixes p and p/1 return hash(self.__numerator) - raise NotImplementedError("Do not know how to hash elements of general fraction field") + if self._parent.is_exact(): + # May fail; let the exception propagate then. + # (In contrast, over inexact rings, we hash unreduced fractions + # without complaining. This is not ideal, but there is code in Sage + # that uses dictionaries indexed by rational functions with + # floating-point coefficients, and since the equality test involves + # potentially inexact operations, there would be compatibility + # issues even if we didn't...) + self.reduce() + # Same algorithm as for elements of QQ + n = hash(self.__numerator) + d = hash(self.__denominator) + if d == 1: + return n + else: + return n ^ d def __call__(self, *x, **kwds): """ @@ -1101,37 +1126,6 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): self.__denominator = self.__denominator.monic() self.__numerator *= invlc - def __hash__(self): - """ - This function hashes in a special way to ensure that elements of - a ring `R` and their images in a fraction field of `R` have the same - hash. This enables them to be used as keys interchangeably in a - dictionary (since ``==`` will claim them equal). - - Ensure normalization is done before hashing the numerator and - denominator, fixing trac #16268:: - - sage: Ku. = FractionField(PolynomialRing(QQ,'u')) - sage: a = 27*u^2+81*u+243 - sage: b = 27*u-81 - sage: c = u^2 + 3*u + 9 - sage: d = u-3 - sage: s = a/b - sage: t = c/d - sage: s==t - True - sage: len(Set([s,t])) - 1 - """ - # This is same algorithm as used for members of QQ - self.reduce() - n = hash(self.__numerator) - d = hash(self.__denominator) - if d == 1: - return n - else: - return n ^ d - def make_element(parent, numerator, denominator): """ Used for unpickling :class:`FractionFieldElement` objects (and subclasses). From 1153b5dfe3f26bf9b1a2b64e0a3c709a672370ad Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 23 Apr 2018 13:56:46 +0200 Subject: [PATCH 145/284] #16268 boring doctest updates --- src/sage/categories/functor.pyx | 2 +- src/sage/coding/linear_code.py | 2 +- src/sage/combinat/sf/dual.py | 2 +- src/sage/combinat/sf/jack.py | 40 +++++++------ src/sage/combinat/sf/sfa.py | 6 +- .../arithmetic_dynamics/projective_ds.py | 2 +- src/sage/groups/perm_gps/permgroup.py | 6 +- ...otics_multivariate_generating_functions.py | 3 +- src/sage/rings/cfinite_sequence.py | 56 +++++++++---------- src/sage/rings/fraction_field_element.pyx | 4 +- .../rings/polynomial/laurent_polynomial.pyx | 2 +- .../polynomial/polynomial_rational_flint.pyx | 2 +- src/sage/rings/polynomial/polynomial_ring.py | 2 +- .../schemes/elliptic_curves/constructor.py | 2 +- .../schemes/elliptic_curves/ell_generic.py | 2 +- .../schemes/projective/projective_space.py | 2 +- src/sage/structure/element.pyx | 2 +- src/sage/tests/french_book/polynomes.py | 2 +- 18 files changed, 72 insertions(+), 67 deletions(-) diff --git a/src/sage/categories/functor.pyx b/src/sage/categories/functor.pyx index 6d193f2971d..cf6314e47e5 100644 --- a/src/sage/categories/functor.pyx +++ b/src/sage/categories/functor.pyx @@ -262,7 +262,7 @@ cdef class Functor(SageObject): Ring endomorphism of Finite Field in a of size 5^2 Defn: a |--> 4*a + 1 sage: fF((a^2+a)*t^2/(a*t - a^2)) - 3*a*t^2/((4*a + 1)*t + a + 1) + ((4*a + 2)*t^2)/(t + a + 4) """ try: diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 89db3687501..cacbaabce93 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -3499,7 +3499,7 @@ def zeta_function(self, name="T"): sage: C = codes.HammingCode(GF(2), 3) sage: C.zeta_function() - (2/5*T^2 + 2/5*T + 1/5)/(2*T^2 - 3*T + 1) + (1/5*T^2 + 1/5*T + 1/10)/(T^2 - 3/2*T + 1/2) """ P = self.zeta_polynomial() q = (self.base_ring()).characteristic() diff --git a/src/sage/combinat/sf/dual.py b/src/sage/combinat/sf/dual.py index 77f22a58ccb..cb73aabc57a 100644 --- a/src/sage/combinat/sf/dual.py +++ b/src/sage/combinat/sf/dual.py @@ -735,7 +735,7 @@ def scalar_hl(self, x): sage: h = m.dual_basis(scalar=zee) sage: a = h([2,1]) sage: a.scalar_hl(a) - (t + 2)/(-t^4 + 2*t^3 - 2*t + 1) + (-t - 2)/(t^4 - 2*t^3 + 2*t - 1) """ return self._dual.scalar_hl(x) diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index 8655072da19..f58a4850173 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -214,9 +214,9 @@ def P(self): :: sage: JP(JQ([2,1])) - ((t+2)/(2*t^3+t^2))*JackP[2, 1] + ((1/2*t+1)/(t^3+1/2*t^2))*JackP[2, 1] sage: JP(JQ([3])) - ((2*t^2+3*t+1)/(6*t^3))*JackP[3] + ((1/3*t^2+1/2*t+1/6)/t^3)*JackP[3] sage: JP(JQ([1,1,1])) (6/(t^3+3*t^2+2*t))*JackP[1, 1, 1] @@ -266,13 +266,13 @@ def Q(self): sage: JQ = Sym.jack().Q() sage: JP = Sym.jack().P() sage: JQ(sum(JP(p) for p in Partitions(3))) - (1/6*t^3+1/2*t^2+1/3*t)*JackQ[1, 1, 1] + ((2*t^3+t^2)/(t+2))*JackQ[2, 1] + (6*t^3/(2*t^2+3*t+1))*JackQ[3] + (1/6*t^3+1/2*t^2+1/3*t)*JackQ[1, 1, 1] + ((2*t^3+t^2)/(t+2))*JackQ[2, 1] + (3*t^3/(t^2+3/2*t+1/2))*JackQ[3] :: sage: s = Sym.schur() sage: JQ(s([3])) # indirect doctest - (1/6*t^3-1/2*t^2+1/3*t)*JackQ[1, 1, 1] + ((2*t^3-2*t^2)/(t+2))*JackQ[2, 1] + (6*t^3/(2*t^2+3*t+1))*JackQ[3] + (1/6*t^3-1/2*t^2+1/3*t)*JackQ[1, 1, 1] + ((2*t^3-2*t^2)/(t+2))*JackQ[2, 1] + (3*t^3/(t^2+3/2*t+1/2))*JackQ[3] sage: JQ(s([2,1])) (1/3*t^3-1/3*t)*JackQ[1, 1, 1] + ((2*t^3+t^2)/(t+2))*JackQ[2, 1] sage: JQ(s([1,1,1])) @@ -329,15 +329,15 @@ def J(self): sage: JJ = Sym.jack().J() sage: JP = Sym.jack().P() sage: JJ(sum(JP(p) for p in Partitions(3))) - 1/6*JackJ[1, 1, 1] + (1/(t+2))*JackJ[2, 1] + (1/(2*t^2+3*t+1))*JackJ[3] + 1/6*JackJ[1, 1, 1] + (1/(t+2))*JackJ[2, 1] + (1/2/(t^2+3/2*t+1/2))*JackJ[3] :: sage: s = Sym.schur() sage: JJ(s([3])) # indirect doctest - ((t^2-3*t+2)/(6*t^2+18*t+12))*JackJ[1, 1, 1] + ((2*t-2)/(2*t^2+5*t+2))*JackJ[2, 1] + (1/(2*t^2+3*t+1))*JackJ[3] + ((1/6*t^2-1/2*t+1/3)/(t^2+3*t+2))*JackJ[1, 1, 1] + ((t-1)/(t^2+5/2*t+1))*JackJ[2, 1] + (1/2/(t^2+3/2*t+1/2))*JackJ[3] sage: JJ(s([2,1])) - ((t-1)/(3*t+6))*JackJ[1, 1, 1] + (1/(t+2))*JackJ[2, 1] + ((1/3*t-1/3)/(t+2))*JackJ[1, 1, 1] + (1/(t+2))*JackJ[2, 1] sage: JJ(s([1,1,1])) 1/6*JackJ[1, 1, 1] """ @@ -715,12 +715,12 @@ def _multiply(self, left, right): sage: JJ([1])^2 # indirect doctest (t/(t+1))*JackJ[1, 1] + (1/(t+1))*JackJ[2] sage: JJ([2])^2 - (2*t^2/(2*t^2+3*t+1))*JackJ[2, 2] + (4*t/(3*t^2+4*t+1))*JackJ[3, 1] + ((t+1)/(6*t^2+5*t+1))*JackJ[4] + (t^2/(t^2+3/2*t+1/2))*JackJ[2, 2] + (4/3*t/(t^2+4/3*t+1/3))*JackJ[3, 1] + ((1/6*t+1/6)/(t^2+5/6*t+1/6))*JackJ[4] sage: JQ = SymmetricFunctions(FractionField(QQ['t'])).jack().Q() sage: JQ([1])^2 # indirect doctest JackQ[1, 1] + (2/(t+1))*JackQ[2] sage: JQ([2])^2 - JackQ[2, 2] + (2/(t+1))*JackQ[3, 1] + ((6*t+6)/(6*t^2+5*t+1))*JackQ[4] + JackQ[2, 2] + (2/(t+1))*JackQ[3, 1] + ((t+1)/(t^2+5/6*t+1/6))*JackQ[4] """ return self( self._P(left)*self._P(right) ) @@ -889,11 +889,12 @@ def _m_cache(self, n): sage: l(JP._m_to_self_cache[3]) [([1, 1, 1], [([1, 1, 1], 1)]), ([2, 1], [([1, 1, 1], -6/(t + 2)), ([2, 1], 1)]), - ([3], [([1, 1, 1], 6/(t^2 + 3*t + 2)), ([2, 1], -3/(2*t + 1)), ([3], 1)])] + ([3], [([1, 1, 1], 6/(t^2 + 3*t + 2)), ([2, 1], -3/2/(t + 1/2)), ([3], 1)])] sage: l(JP._self_to_m_cache[3]) [([1, 1, 1], [([1, 1, 1], 1)]), ([2, 1], [([1, 1, 1], 6/(t + 2)), ([2, 1], 1)]), - ([3], [([1, 1, 1], 6/(2*t^2 + 3*t + 1)), ([2, 1], 3/(2*t + 1)), ([3], 1)])] + ([3], + [([1, 1, 1], 3/(t^2 + 3/2*t + 1/2)), ([2, 1], 3/2/(t + 1/2)), ([3], 1)])] """ if n in self._self_to_m_cache: return @@ -1002,9 +1003,9 @@ def scalar_jack_basis(self, part1, part2 = None): sage: JP.scalar_jack_basis(Partition([2,1]), Partition([1,1,1])) 0 sage: JP._normalize_coefficients(JP.scalar_jack_basis(Partition([3,2,1]), Partition([3,2,1]))) - (12*t^6 + 20*t^5 + 11*t^4 + 2*t^3)/(2*t^3 + 11*t^2 + 20*t + 12) + (6*t^6 + 10*t^5 + 11/2*t^4 + t^3)/(t^3 + 11/2*t^2 + 10*t + 6) sage: JJ(JP[3,2,1]).scalar_jack(JP[3,2,1]) - (12*t^6 + 20*t^5 + 11*t^4 + 2*t^3)/(2*t^3 + 11*t^2 + 20*t + 12) + (6*t^6 + 10*t^5 + 11/2*t^4 + t^3)/(t^3 + 11/2*t^2 + 10*t + 6) With a single argument, takes `part2 = part1`:: @@ -1035,7 +1036,7 @@ def scalar_jack(self, x, t=None): sage: JP = SymmetricFunctions(FractionField(QQ['t'])).jack().P() sage: l = [JP(p) for p in Partitions(3)] sage: matrix([[a.scalar_jack(b) for a in l] for b in l]) - [ 6*t^3/(2*t^2 + 3*t + 1) 0 0] + [3*t^3/(t^2 + 3/2*t + 1/2) 0 0] [ 0 (2*t^3 + t^2)/(t + 2) 0] [ 0 0 1/6*t^3 + 1/2*t^2 + 1/3*t] """ @@ -1195,9 +1196,14 @@ def _h_cache(self, n): [([1, 1], [([1, 1], 1), ([2], 2/(t + 1))]), ([2], [([2], 1)])] sage: JQp._h_cache(3) sage: l(JQp._h_to_self_cache[3]) - [([1, 1, 1], [([1, 1, 1], 1), ([2, 1], 6/(t + 2)), ([3], 6/(2*t^2 + 3*t + 1))]), ([2, 1], [([2, 1], 1), ([3], 3/(2*t + 1))]), ([3], [([3], 1)])] + [([1, 1, 1], + [([1, 1, 1], 1), ([2, 1], 6/(t + 2)), ([3], 3/(t^2 + 3/2*t + 1/2))]), + ([2, 1], [([2, 1], 1), ([3], 3/2/(t + 1/2))]), + ([3], [([3], 1)])] sage: l(JQp._self_to_h_cache[3]) - [([1, 1, 1], [([1, 1, 1], 1), ([2, 1], -6/(t + 2)), ([3], 6/(t^2 + 3*t + 2))]), ([2, 1], [([2, 1], 1), ([3], -3/(2*t + 1))]), ([3], [([3], 1)])] + [([1, 1, 1], [([1, 1, 1], 1), ([2, 1], -6/(t + 2)), ([3], 6/(t^2 + 3*t + 2))]), + ([2, 1], [([2, 1], 1), ([3], -3/2/(t + 1/2))]), + ([3], [([3], 1)])] """ if n in self._self_to_h_cache: return @@ -1291,7 +1297,7 @@ def coproduct_by_coercion( self, elt ): sage: Sym = SymmetricFunctions(QQ['t'].fraction_field()) sage: JQp = Sym.jack().Qp() sage: JQp[2,2].coproduct() #indirect doctest - JackQp[] # JackQp[2, 2] + (2*t/(t+1))*JackQp[1] # JackQp[2, 1] + JackQp[1, 1] # JackQp[1, 1] + ((4*t^3+8*t^2)/(2*t^3+5*t^2+4*t+1))*JackQp[2] # JackQp[2] + (2*t/(t+1))*JackQp[2, 1] # JackQp[1] + JackQp[2, 2] # JackQp[] + JackQp[] # JackQp[2, 2] + (2*t/(t+1))*JackQp[1] # JackQp[2, 1] + JackQp[1, 1] # JackQp[1, 1] + ((2*t^3+4*t^2)/(t^3+5/2*t^2+2*t+1/2))*JackQp[2] # JackQp[2] + (2*t/(t+1))*JackQp[2, 1] # JackQp[1] + JackQp[2, 2] # JackQp[] """ h = elt.parent().realization_of().h() parent = elt.parent() diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index b439700bea5..85da8a842c0 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -4468,9 +4468,9 @@ def scalar_jack(self, x, t=None): [ 0 0 0 0 384] sage: JQ = SymmetricFunctions(QQ['t'].fraction_field()).jack().Q() sage: matrix([[JQ(mu).scalar_jack(JQ(nu)) for nu in Partitions(3)] for mu in Partitions(3)]) - [(2*t^2 + 3*t + 1)/(6*t^3) 0 0] - [ 0 (t + 2)/(2*t^3 + t^2) 0] - [ 0 0 6/(t^3 + 3*t^2 + 2*t)] + [(1/3*t^2 + 1/2*t + 1/6)/t^3 0 0] + [ 0 (1/2*t + 1)/(t^3 + 1/2*t^2) 0] + [ 0 0 6/(t^3 + 3*t^2 + 2*t)] """ parent = self.parent() if t is None: diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 421fd8d2f6a..a05a211a6a0 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -2571,7 +2571,7 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(QQ,1) sage: f = DynamicalSystem_projective([x^2-2*x*y-2*y^2, -2*x^2-2*x*y+y^2]) sage: f.automorphism_group(return_functions=True) - [x, 2/(2*x), -x - 1, -2*x/(2*x + 2), (-x - 1)/x, -1/(x + 1)] + [x, 1/x, -x - 1, -x/(x + 1), (-x - 1)/x, -1/(x + 1)] :: diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 4c4958371ac..36648d97d6a 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -4058,10 +4058,10 @@ def molien_series(self): sage: G = SymmetricGroup(5) sage: G.molien_series() - 1/(-x^15 + x^14 + x^13 - x^10 - x^9 - x^8 + x^7 + x^6 + x^5 - x^2 - x + 1) + -1/(x^15 - x^14 - x^13 + x^10 + x^9 + x^8 - x^7 - x^6 - x^5 + x^2 + x - 1) sage: G = SymmetricGroup(3) sage: G.molien_series() - 1/(-x^6 + x^5 + x^4 - x^2 - x + 1) + -1/(x^6 - x^5 - x^4 + x^2 + x - 1) Some further tests (after :trac:`15817`):: @@ -4138,7 +4138,7 @@ def poincare_series(self, p=2, n=10): (x^2 + 1)/(x^4 - x^3 - x + 1) sage: G = SymmetricGroup(3) sage: G.poincare_series(2,10) # optional - gap_packages - 1/(-x + 1) + -1/(x - 1) AUTHORS: diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 14cfc0a324b..9629a44c58e 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -756,8 +756,7 @@ def univariate_decomposition(self): sage: FFPD = FractionWithFactoredDenominatorRing(R) sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1) sage: f - (15*x^7 - 15*x^6 + 5*x^5 - 5*x^4 + 6*x^3 - 2*x^2 + x - 1)/(3*x^4 - - 3*x^3 + x^2 - x) + (5*x^7 - 5*x^6 + 5/3*x^5 - 5/3*x^4 + 2*x^3 - 2/3*x^2 + 1/3*x - 1/3)/(x^4 - x^3 + 1/3*x^2 - 1/3*x) sage: decomp = FFPD(f).univariate_decomposition() sage: decomp (5*x^3, []) + diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index f2eb4611e30..f123c1e65c4 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -16,7 +16,7 @@ sage: fibo = CFiniteSequence(x/(1-x-x^2)) # the Fibonacci sequence sage: fibo - C-finite sequence, generated by x/(-x^2 - x + 1) + C-finite sequence, generated by -x/(x^2 + x - 1) sage: fibo.parent() The ring of C-Finite sequences in x over Rational Field sage: fibo.parent().category() @@ -27,13 +27,13 @@ sage: C The ring of C-Finite sequences in x over Rational Field sage: C(x/(1-x-x^2)) - C-finite sequence, generated by x/(-x^2 - x + 1) + C-finite sequence, generated by -x/(x^2 + x - 1) sage: C(x/(1-x-x^2)) == fibo True sage: var('y') y sage: CFiniteSequence(y/(1-y-y^2)) - C-finite sequence, generated by y/(-y^2 - y + 1) + C-finite sequence, generated by -y/(y^2 + y - 1) sage: CFiniteSequence(y/(1-y-y^2)) == fibo False @@ -135,7 +135,7 @@ def CFiniteSequences(base_ring, names = None, category = None): sage: C The ring of C-Finite sequences in x over Rational Field sage: C.an_element() - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: C.category() Category of commutative rings sage: C.one() @@ -147,7 +147,7 @@ def CFiniteSequences(base_ring, names = None, category = None): sage: C(1/x) Finite sequence [1], offset = -1 sage: C((-x + 2)/(-x^2 - x + 1)) - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) TESTS:: @@ -186,9 +186,9 @@ class CFiniteSequence(FieldElement): EXAMPLES:: sage: CFiniteSequence((2-x)/(1-x-x^2)) # the Lucas sequence - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: CFiniteSequence(x/(1-x)^3) # triangular numbers - C-finite sequence, generated by x/(-x^3 + 3*x^2 - 3*x + 1) + C-finite sequence, generated by -x/(x^3 - 3*x^2 + 3*x - 1) Polynomials are interpreted as finite sequences, or recurrences of degree 0:: @@ -208,12 +208,12 @@ class CFiniteSequence(FieldElement): sage: P = LaurentPolynomialRing(QQ.fraction_field(), 'X') sage: X=P.gen() sage: CFiniteSequence(1/(1-X)) - C-finite sequence, generated by 1/(-X + 1) + C-finite sequence, generated by -1/(X - 1) The o.g.f. is always normalized to get a denominator constant coefficient of `+1`:: sage: CFiniteSequence(1/(x-2)) - C-finite sequence, generated by -1/2/(-1/2*x + 1) + C-finite sequence, generated by 1/(x - 2) The given ``ogf`` is used to create an appropriate parent: it can be a symbolic expression, a polynomial , or a fraction field element @@ -240,7 +240,7 @@ class CFiniteSequence(FieldElement): sage: P. = QQ[] sage: CFiniteSequence(0.1/(1-x)) - C-finite sequence, generated by 1/10/(-x + 1) + C-finite sequence, generated by -1/10/(x - 1) sage: CFiniteSequence(pi/(1-x)) Traceback (most recent call last): ... @@ -270,14 +270,14 @@ def __classcall_private__(cls, ogf): sage: f1 = CFiniteSequence((2-x)/(1-x-x^2)) sage: f1 - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: C. = CFiniteSequences(QQ); sage: f2 = CFiniteSequence((2-x)/(1-x-x^2)) sage: f2 - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: f3 = C((2-x)/(1-x-x^2)) sage: f3 - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: f1 == f2 and f2 == f3 True sage: f1.parent() == f2.parent() and f2.parent() == f3.parent() @@ -298,7 +298,7 @@ def __classcall_private__(cls, ogf): y sage: f4 = CFiniteSequence((2-y)/(1-y-y^2)) sage: f4 - C-finite sequence, generated by (-y + 2)/(-y^2 - y + 1) + C-finite sequence, generated by (y - 2)/(y^2 + y - 1) sage: f4 == f1 False sage: f4.parent() == f1.parent() @@ -348,7 +348,7 @@ def __init__(self, parent, ogf): sage: C. = CFiniteSequences(QQ); sage: C((2-x)/(1-x-x^2)) # indirect doctest - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) """ br = parent.base_ring() @@ -517,7 +517,7 @@ def _div_(self, other): [1/2, 1, 3/2, 2, 5/2, 3] sage: s = C(x) sage: s/(s*-1 + 1) - C-finite sequence, generated by x/(-x + 1) + C-finite sequence, generated by -x/(x - 1) """ return CFiniteSequence(self.ogf() / (other.numerator() / other.denominator())) @@ -684,7 +684,7 @@ def ogf(self): sage: C. = CFiniteSequences(QQ) sage: r = C.from_recurrence([2],[1]) sage: r.ogf() - 1/(-2*x + 1) + -1/2/(x - 1/2) sage: C(0).ogf() 0 """ @@ -697,9 +697,9 @@ def numerator(self): EXAMPLES:: sage: f = CFiniteSequence((2-x)/(1-x-x^2)); f - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: f.numerator() - -x + 2 + x - 2 """ return self.ogf().numerator() @@ -710,9 +710,9 @@ def denominator(self): EXAMPLES:: sage: f = CFiniteSequence((2-x)/(1-x-x^2)); f - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: f.denominator() - -x^2 - x + 1 + x^2 + x - 1 """ return self.ogf().denominator() @@ -954,9 +954,9 @@ def _element_constructor_(self, ogf): sage: C. = CFiniteSequences(QQ) sage: C((2-x)/(1-x-x^2)) - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) sage: C(x/(1-x)^3) - C-finite sequence, generated by x/(-x^3 + 3*x^2 - 3*x + 1) + C-finite sequence, generated by -x/(x^3 - 3*x^2 + 3*x - 1) sage: C(x^2-4*x^5) Finite sequence [1, 0, 0, -4], offset = 2 sage: C(x^2+3/x) @@ -966,7 +966,7 @@ def _element_constructor_(self, ogf): sage: P = LaurentPolynomialRing(QQ.fraction_field(), 'X') sage: X = P.gen() sage: C(1/(1-X)) - C-finite sequence, generated by 1/(-x + 1) + C-finite sequence, generated by -1/(x - 1) sage: C = CFiniteSequences(QQ) sage: C(x) Finite sequence [1], offset = 1 @@ -1026,7 +1026,7 @@ def an_element(self): sage: C. = CFiniteSequences(QQ); sage: C.an_element() - C-finite sequence, generated by (-x + 2)/(-x^2 - x + 1) + C-finite sequence, generated by (x - 2)/(x^2 + x - 1) """ x = self.gen() return self((2-x)/(1-x-x**2)) @@ -1121,7 +1121,7 @@ def from_recurrence(self, coefficients, values): sage: C. = CFiniteSequences(QQ) sage: C.from_recurrence([1,1],[0,1]) # Fibonacci numbers - C-finite sequence, generated by x/(-x^2 - x + 1) + C-finite sequence, generated by -x/(x^2 + x - 1) sage: C.from_recurrence([-1,2],[0,1]) # natural numbers C-finite sequence, generated by x/(x^2 - 2*x + 1) sage: r = C.from_recurrence([-1],[1]) @@ -1180,7 +1180,7 @@ def guess(self, sequence, algorithm='sage'): sage: C. = CFiniteSequences(QQ) sage: C.guess([1,2,4,8,16,32]) - C-finite sequence, generated by 1/(-2*x + 1) + C-finite sequence, generated by -1/2/(x - 1/2) sage: r = C.guess([1,2,3,4,5]) Traceback (most recent call last): ... @@ -1190,7 +1190,7 @@ def guess(self, sequence, algorithm='sage'): So with an odd number of values the result may not generate the last value:: sage: r = C.guess([1,2,4,8,9], algorithm='bm'); r - C-finite sequence, generated by 1/(-2*x + 1) + C-finite sequence, generated by -1/2/(x - 1/2) sage: r[0:5] [1, 2, 4, 8, 16] """ diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 4d6c2d6c4af..833d6738e12 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -289,7 +289,7 @@ cdef class FractionFieldElement(FieldElement): sage: R. = QQ[] sage: a = 2*(x+1)^2 / (2*(x-1)^2); a - (2*x^2 + 4*x + 2)/(2*x^2 - 4*x + 2) + (x^2 + 2*x + 1)/(x^2 - 2*x + 1) sage: a.numerator().is_square() False sage: a.is_square() @@ -888,7 +888,7 @@ cdef class FractionFieldElement(FieldElement): sage: x = PolynomialRing(RationalField(),'x').gen() sage: f = (x^3 + x)/(x^2 - 2*x^3) sage: f - (x^2 + 1)/(-2*x^2 + x) + (-1/2*x^2 - 1/2)/(x^2 - 1/2*x) sage: f.valuation() -1 sage: f.valuation(x^2+1) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index f616ca104db..65333d3a84e 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1010,7 +1010,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: f / x 1 + x + 3*x^3 sage: f / g - (3*x^11 + x^9 + x^8)/(-x^11 + x^9 - x^8 + 1) + (-3*x^11 - x^9 - x^8)/(x^11 - x^9 + x^8 - 1) sage: (x^-2 + x)*(x^-2 + 1) / ((x^5 + x^8)*(x + 2)) (x^2 + 1)/(x^10 + 2*x^9) sage: (x^-2 + x)*(x^-2 + 1) / ((x^-5 + x^-8)*(x + 2)) diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 7255a54bc1f..1ec33804aa6 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -1143,7 +1143,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: f^3 -1/27*t^6 + 2/3*t^5 - 23/6*t^4 + 6*t^3 + 23/4*t^2 + 3/2*t + 1/8 sage: f^(-3) - 1/(-1/27*t^6 + 2/3*t^5 - 23/6*t^4 + 6*t^3 + 23/4*t^2 + 3/2*t + 1/8) + -27/(t^6 - 18*t^5 + 207/2*t^4 - 162*t^3 - 621/4*t^2 - 81/2*t - 27/8) TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index dbfbb58a1cb..39770c36046 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -632,7 +632,7 @@ def completion(self, p, prec=20, extras=None): sage: PP(f) 1 - x sage: 1/f - 1/(-x + 1) + -1/(x - 1) sage: 1/PP(f) 1 + x + x^2 + x^3 + x^4 + x^5 + x^6 + x^7 + x^8 + x^9 + x^10 + x^11 + x^12 + x^13 + x^14 + x^15 + x^16 + x^17 + x^18 + x^19 + O(x^20) """ diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 8a20cde0656..2aa3b20b3fb 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -1026,7 +1026,7 @@ def EllipticCurve_from_cubic(F, P=None, morphism=True): sage: R. = K[] sage: cubic = x^3+t*y^3+(1+t)*z^3 sage: EllipticCurve_from_cubic(cubic,[1,1,-1], morphism=False) - Elliptic Curve defined by y^2 + ((-236196*t^6-708588*t^5-1180980*t^4-1180980*t^3-708588*t^2-236196*t)/(-1458*t^6-17496*t^5+4374*t^4+29160*t^3+4374*t^2-17496*t-1458))*x*y + ((-459165024*t^14-5969145312*t^13-34207794288*t^12-113872925952*t^11-244304490582*t^10-354331909458*t^9-354331909458*t^8-244304490582*t^7-113872925952*t^6-34207794288*t^5-5969145312*t^4-459165024*t^3)/(-1458*t^14-58320*t^13-841266*t^12-5137992*t^11-11773350*t^10-7709904*t^9+12627738*t^8+25789104*t^7+12627738*t^6-7709904*t^5-11773350*t^4-5137992*t^3-841266*t^2-58320*t-1458))*y = x^3 + ((-118098*t^12-708588*t^11+944784*t^10+11219310*t^9+27871128*t^8+36374184*t^7+27871128*t^6+11219310*t^5+944784*t^4-708588*t^3-118098*t^2)/(-54*t^12-1296*t^11-7452*t^10+6048*t^9+25758*t^8-3888*t^7-38232*t^6-3888*t^5+25758*t^4+6048*t^3-7452*t^2-1296*t-54))*x^2 over Rational function field in t over Rational Field + Elliptic Curve defined by y^2 + ((162*t^6+486*t^5+810*t^4+810*t^3+486*t^2+162*t)/(t^6+12*t^5-3*t^4-20*t^3-3*t^2+12*t+1))*x*y + ((314928*t^14+4094064*t^13+23462136*t^12+78102144*t^11+167561379*t^10+243026001*t^9+243026001*t^8+167561379*t^7+78102144*t^6+23462136*t^5+4094064*t^4+314928*t^3)/(t^14+40*t^13+577*t^12+3524*t^11+8075*t^10+5288*t^9-8661*t^8-17688*t^7-8661*t^6+5288*t^5+8075*t^4+3524*t^3+577*t^2+40*t+1))*y = x^3 + ((2187*t^12+13122*t^11-17496*t^10-207765*t^9-516132*t^8-673596*t^7-516132*t^6-207765*t^5-17496*t^4+13122*t^3+2187*t^2)/(t^12+24*t^11+138*t^10-112*t^9-477*t^8+72*t^7+708*t^6+72*t^5-477*t^4-112*t^3+138*t^2+24*t+1))*x^2 over Rational function field in t over Rational Field TESTS: diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 6a260fea19e..0cf3be5c2f8 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2072,7 +2072,7 @@ def multiplication_by_m(self, m, x_only=False): Grab only the x-coordinate (less work):: sage: mx = E.multiplication_by_m(2, x_only=True); mx - (x^4 + 2*x^2 - 24*x + 1)/(4*x^3 - 4*x + 12) + (1/4*x^4 + 1/2*x^2 - 6*x + 1/4)/(x^3 - x + 3) sage: mx.parent() Fraction Field of Univariate Polynomial Ring in x over Rational Field diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 7f888c945fc..4c7a9ca4ae8 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -1042,7 +1042,7 @@ def Lattes_map(self, E, m): sage: P.Lattes_map(E, 2) Dynamical System of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to - (x^4 + 2*x^2*y^2 + y^4 : 4*x^3*y - 4*x*y^3) + (1/4*x^4 + 1/2*x^2*y^2 + 1/4*y^4 : x^3*y - x*y^3) """ if self.dimension_relative() != 1: raise TypeError("must be dimension 1") diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 88509b7b6f9..a8ef51026d7 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -3091,7 +3091,7 @@ cdef class CommutativeRingElement(RingElement): sage: R. = QQ[] sage: a = 2*(x+1)^2 / (2*(x-1)^2); a.sqrt() - (2*x + 2)/(2*x - 2) + (x + 1)/(x - 1) sage: sqrtx=(1/x).sqrt(name="y"); sqrtx y sage: sqrtx^2 diff --git a/src/sage/tests/french_book/polynomes.py b/src/sage/tests/french_book/polynomes.py index 4f5af0dde7c..9173d8bcb02 100644 --- a/src/sage/tests/french_book/polynomes.py +++ b/src/sage/tests/french_book/polynomes.py @@ -224,7 +224,7 @@ Sage example in ./polynomes.tex, line 864:: sage: r.reduce(); r - 1.00000000000000/(-x + 1.00000000000000) + -1.00000000000000/(x - 1.00000000000000) Sage example in ./polynomes.tex, line 907:: From b71ab2d566bef202899147a8368aecf663043150 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 23 Apr 2018 13:56:33 +0200 Subject: [PATCH 146/284] #16268 other doctest updates --- .../endPN_automorphism_group.py | 10 +++--- .../arithmetic_dynamics/projective_ds.py | 31 +++++++++++++++---- src/sage/rings/fraction_field_element.pyx | 2 -- .../function_field/function_field_element.pyx | 4 +-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index 4872b5ea66a..f44c5e3ee6a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -64,7 +64,7 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions=False, sage: rational_function = (z^2 - 2*z - 2)/(-2*z^2 - 2*z + 1) sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import automorphism_group_QQ_fixedpoints sage: automorphism_group_QQ_fixedpoints(rational_function, True) - [z, 2/(2*z), -z - 1, -2*z/(2*z + 2), (-z - 1)/z, -1/(z + 1)] + [z, 1/z, -z - 1, -z/(z + 1), (-z - 1)/z, -1/(z + 1)] :: @@ -342,8 +342,8 @@ def PGL_repn(rational_function): sage: f = ((2*z-1)/(3-z)) sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import PGL_repn sage: PGL_repn(f) - [ 2 -1] - [-1 3] + [-2 1] + [ 1 -3] """ if is_Matrix(rational_function): return rational_function @@ -1452,7 +1452,7 @@ def automorphisms_fixing_pair(rational_function, pair, quad): sage: L = [[4, 1], [2, 1]] sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import automorphisms_fixing_pair sage: automorphisms_fixing_pair(f, L, False) - [(6*z + 6)/z, 4/(3*z + 3)] + [(6*z + 6)/z, 6/(z + 1)] """ # define ground field and ambient function field if rational_function.parent().is_field(): @@ -1526,7 +1526,7 @@ def automorphism_group_FF_alg3(rational_function): sage: f = (3456*z^4) sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import automorphism_group_FF_alg3 sage: automorphism_group_FF_alg3(f) - [z, 3/(3*z)] + [z, 1/z] """ # define ground field and ambient function field if rational_function.parent().is_field(): diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index a05a211a6a0..dadbaa893f8 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -5520,12 +5520,31 @@ def automorphism_group(self, absolute=False, iso_type=False, return_functions=Fa sage: R. = ProjectiveSpace(GF(3^2,'t'),1) sage: f = DynamicalSystem_projective([x^3,y^3]) sage: f.automorphism_group(return_functions=True, iso_type=True) # long time - ([x, x/(x + 1), x/(2*x + 1), 2/(x + 2), (2*x + 1)/(2*x), (2*x + 2)/x, - 1/(2*x + 2), x + 1, x + 2, x/(x + 2), 2*x/(x + 1), 2*x, 1/x, 2*x + 1, - 2*x + 2, ((t + 2)*x + t + 2)/((2*t + 1)*x + t + 2), (t*x + 2*t)/(t*x + - t), 2/x, (x + 1)/(x + 2), (2*t*x + t)/(t*x), (2*t + 1)/((2*t + 1)*x + - 2*t + 1), ((2*t + 1)*x + 2*t + 1)/((2*t + 1)*x), t/(t*x + 2*t), (2*x + - 1)/(x + 1)], 'PGL(2,3)') + ([x, + x/(x + 1), + x/(2*x + 1), + 2/(x + 2), + (2*x + 1)/(2*x), + (2*x + 2)/x, + 1/(2*x + 2), + x + 1, + x + 2, + x/(x + 2), + 2*x/(x + 1), + 2*x, + 1/x, + 2*x + 1, + 2*x + 2, + (x + 2)/(x + 1), + 2/x, + (2*x + 2)/(x + 2), + (x + 1)/(x + 2), + (2*x + 1)/x, + 1/(x + 1), + 1/(x + 2), + (2*x + 1)/(x + 1), + (x + 1)/x], + 'PGL(2,3)') :: diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 833d6738e12..a736315f093 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -290,8 +290,6 @@ cdef class FractionFieldElement(FieldElement): sage: R. = QQ[] sage: a = 2*(x+1)^2 / (2*(x-1)^2); a (x^2 + 2*x + 1)/(x^2 - 2*x + 1) - sage: a.numerator().is_square() - False sage: a.is_square() True sage: (0/x).is_square() diff --git a/src/sage/rings/function_field/function_field_element.pyx b/src/sage/rings/function_field/function_field_element.pyx index 18c74ce02a0..df7111ed412 100644 --- a/src/sage/rings/function_field/function_field_element.pyx +++ b/src/sage/rings/function_field/function_field_element.pyx @@ -591,13 +591,13 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): sage: t.element() t sage: type(t.element()) - + <... 'sage.rings.fraction_field_FpT.FpTElement'> sage: K. = FunctionField(GF(131101)) sage: t.element() t sage: type(t.element()) - + <... 'sage.rings.fraction_field_element.FractionFieldElement_1poly_field'> """ return self._x From e663dbe550077471e93046163f63f6706a8a4fb7 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sat, 12 May 2018 08:23:43 +0200 Subject: [PATCH 147/284] fix normalization of leading coefficients --- .../quatalg/quaternion_algebra_element.pyx | 2 +- src/sage/combinat/sf/hall_littlewood.py | 10 +++--- src/sage/combinat/sf/macdonald.py | 4 +-- .../dynamics/arithmetic_dynamics/affine_ds.py | 7 ++-- .../endPN_automorphism_group.py | 16 +++++---- .../arithmetic_dynamics/projective_ds.py | 6 ++-- src/sage/matrix/matrix2.pyx | 2 +- ...otics_multivariate_generating_functions.py | 9 ++--- src/sage/rings/fraction_field_element.pyx | 34 ++++++++++++++++--- .../rings/function_field/function_field.py | 19 +++++------ .../function_field/function_field_element.pyx | 6 ++-- src/sage/rings/function_field/maps.py | 8 ++--- .../rings/polynomial/polynomial_element.pyx | 2 +- .../schemes/elliptic_curves/ell_generic.py | 2 +- 14 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 4b3bcba4881..fbfa2bf171c 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -843,7 +843,7 @@ cdef class QuaternionAlgebraElement_generic(QuaternionAlgebraElement_abstract): sage: type(theta) <... 'sage.algebras.quatalg.quaternion_algebra_element.QuaternionAlgebraElement_generic'> sage: theta._repr_() - '1/x + x*i + (-x - 1)*j + (2/(3*x^3 + 5))*k' + '1/x + x*i + (-x - 1)*j + (2/3/(x^3 + 5/3))*k' """ return self._do_print(self.x, self.y, self.z, self.w) diff --git a/src/sage/combinat/sf/hall_littlewood.py b/src/sage/combinat/sf/hall_littlewood.py index a096f7a9355..a46da3face0 100644 --- a/src/sage/combinat/sf/hall_littlewood.py +++ b/src/sage/combinat/sf/hall_littlewood.py @@ -662,7 +662,7 @@ def scalar_hl(self, x, t = None): sage: HLQ([2]).scalar_hl(HLQ([1,1])) 0 sage: HLP([2]).scalar_hl(HLP([2])) - 1/(-t + 1) + -1/(t - 1) """ parent = self.parent() if t is None: @@ -837,13 +837,13 @@ def __init__(self, hall_littlewood): sage: HLQp = Sym.hall_littlewood().Qp() sage: s = Sym.schur(); p = Sym.power() sage: HLQ( HLP([2,1]) + HLP([3]) ) - (1/(t^2-2*t+1))*HLQ[2, 1] + (1/(-t+1))*HLQ[3] + (1/(t^2-2*t+1))*HLQ[2, 1] - (1/(t-1))*HLQ[3] sage: HLQ(HLQp([2])) # indirect doctest - (t/(t^3-t^2-t+1))*HLQ[1, 1] + (1/(-t+1))*HLQ[2] + (t/(t^3-t^2-t+1))*HLQ[1, 1] - (1/(t-1))*HLQ[2] sage: HLQ(s([2])) - (t/(t^3-t^2-t+1))*HLQ[1, 1] + (1/(-t+1))*HLQ[2] + (t/(t^3-t^2-t+1))*HLQ[1, 1] - (1/(t-1))*HLQ[2] sage: HLQ(p([2])) - (1/(t^2-1))*HLQ[1, 1] + (1/(-t+1))*HLQ[2] + (1/(t^2-1))*HLQ[1, 1] - (1/(t-1))*HLQ[2] """ HallLittlewood_generic.__init__(self, hall_littlewood) diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index 254600dc88b..0d6ce7cd9e7 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -782,12 +782,12 @@ def _s_to_self(self, x): sage: J = Sym.macdonald(t=2).J() sage: s = Sym.schur() sage: J._s_to_self(s[2,1]) - ((-q+2)/(28*q-7))*McdJ[1, 1, 1] + (1/(-4*q+1))*McdJ[2, 1] + ((-1/28*q+1/14)/(q-1/4))*McdJ[1, 1, 1] - (1/4/(q-1/4))*McdJ[2, 1] This is for internal use only. Please use instead:: sage: J(s[2,1]) - ((-q+2)/(28*q-7))*McdJ[1, 1, 1] + (1/(-4*q+1))*McdJ[2, 1] + ((-1/28*q+1/14)/(q-1/4))*McdJ[1, 1, 1] - (1/4/(q-1/4))*McdJ[2, 1] """ return self._from_cache(x, self._s_cache, self._s_to_self_cache, q = self.q, t = self.t) diff --git a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py index 43261e8afd6..ebc01fe702f 100644 --- a/src/sage/dynamics/arithmetic_dynamics/affine_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/affine_ds.py @@ -699,9 +699,10 @@ def orbit(self, P, n): sage: A. = AffineSpace(FractionField(R), 2) sage: f = DynamicalSystem_affine([(x-t*y^2)/x, t*x*y]) sage: f.orbit(A(1, t), 3) - [(1, t), (-t^3 + 1, t^2), ((-t^5 - t^3 + 1)/(-t^3 + 1), -t^6 + t^3), - ((-t^16 + 3*t^13 - 3*t^10 + t^7 + t^5 + t^3 - 1)/(t^5 + t^3 - 1), -t^9 - - t^7 + t^4)] + [(1, t), + (-t^3 + 1, t^2), + ((t^5 + t^3 - 1)/(t^3 - 1), -t^6 + t^3), + ((-t^16 + 3*t^13 - 3*t^10 + t^7 + t^5 + t^3 - 1)/(t^5 + t^3 - 1), -t^9 - t^7 + t^4)] """ Q = P if isinstance(n, list) or isinstance(n, tuple): diff --git a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py index f44c5e3ee6a..7ee79dd4b48 100644 --- a/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py +++ b/src/sage/dynamics/arithmetic_dynamics/endPN_automorphism_group.py @@ -1083,7 +1083,7 @@ def three_stable_points(rational_function, invariant_list): sage: L = [[0,1],[4,1],[1,1],[1,0]] sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import three_stable_points sage: three_stable_points(f,L) - [z, 4*z, 2/(2*z), 3/(2*z)] + [z, 4*z, 1/z, 4/z] """ # define ground field and ambient function field if rational_function.parent().is_field(): @@ -1151,7 +1151,7 @@ def automorphism_group_FF_alg2(rational_function): sage: f = (3*z^3 - z^2)/(z-1) sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import automorphism_group_FF_alg2 sage: automorphism_group_FF_alg2(f) - [Univariate Polynomial Ring in w over Finite Field in b of size 7^2, [w, (3*b + 2)/((2*b + 6)*w)]] + [Univariate Polynomial Ring in w over Finite Field in b of size 7^2, [w, 5/w]] :: @@ -1159,11 +1159,13 @@ def automorphism_group_FF_alg2(rational_function): sage: f = (3456*z^(4)) sage: from sage.dynamics.arithmetic_dynamics.endPN_automorphism_group import automorphism_group_FF_alg2 sage: automorphism_group_FF_alg2(f) - [Univariate Polynomial Ring in w over Finite Field in b of size 5^6, [w, - (3*b^5 + 4*b^4 + 3*b^2 + 2*b + 1)*w, (2*b^5 + b^4 + 2*b^2 + 3*b + 3)*w, - (3*b^5 + 4*b^4 + 3*b^2 + 2*b)/((3*b^5 + 4*b^4 + 3*b^2 + 2*b)*w), (4*b^5 - + 2*b^4 + 4*b^2 + b + 2)/((3*b^5 + 4*b^4 + 3*b^2 + 2*b)*w), (3*b^5 + - 4*b^4 + 3*b^2 + 2*b + 3)/((3*b^5 + 4*b^4 + 3*b^2 + 2*b)*w)]] + [Univariate Polynomial Ring in w over Finite Field in b of size 5^6, + [w, + (3*b^5 + 4*b^4 + 3*b^2 + 2*b + 1)*w, + (2*b^5 + b^4 + 2*b^2 + 3*b + 3)*w, + 1/w, + (3*b^5 + 4*b^4 + 3*b^2 + 2*b + 1)/w, + (2*b^5 + b^4 + 2*b^2 + 3*b + 3)/w]] """ # define ground field and ambient function field if rational_function.parent().is_field(): diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index dadbaa893f8..3db56326e52 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -5522,11 +5522,11 @@ def automorphism_group(self, absolute=False, iso_type=False, return_functions=Fa sage: f.automorphism_group(return_functions=True, iso_type=True) # long time ([x, x/(x + 1), - x/(2*x + 1), + 2*x/(x + 2), 2/(x + 2), - (2*x + 1)/(2*x), + (x + 2)/x, (2*x + 2)/x, - 1/(2*x + 2), + 2/(x + 1), x + 1, x + 2, x/(x + 2), diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index e21d1d9185b..8509d63bbe0 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -331,7 +331,7 @@ cdef class Matrix(Matrix1): sage: v = vector([3,4*x - 2]) sage: X = A \ v sage: X - ((-8*x^2 + 4*x + 9)/(10*x^3 + x), (19*x^2 - 2*x - 3)/(10*x^3 + x)) + ((-4/5*x^2 + 2/5*x + 9/10)/(x^3 + 1/10*x), (19/10*x^2 - 1/5*x - 3/10)/(x^3 + 1/10*x)) sage: A * X == v True diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 9629a44c58e..1f40e6edbd5 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -802,10 +802,7 @@ def univariate_decomposition(self): sage: FFPD = FractionWithFactoredDenominatorRing(R) sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1) sage: f - (15.0000000000000*x^7 - 15.0000000000000*x^6 + 5.00000000000000*x^5 - - 5.00000000000000*x^4 + 6.00000000000000*x^3 - - 2.00000000000000*x^2 + x - 1.00000000000000)/(3.00000000000000*x^4 - - 3.00000000000000*x^3 + x^2 - x) + (5.00000000000000*x^7 - 5.00000000000000*x^6 + 1.66666666666667*x^5 - 1.66666666666667*x^4 + 2.00000000000000*x^3 - 0.666666666666667*x^2 + 0.333333333333333*x - 0.333333333333333)/(x^4 - x^3 + 0.333333333333333*x^2 - 0.333333333333333*x) sage: decomp = FFPD(f).univariate_decomposition() sage: decomp (5.00000000000000*x^3, []) + @@ -2762,9 +2759,7 @@ def smooth_critical_ideal(self, alpha): sage: F = FFPD(G, Hfac) sage: alpha = [7/3, var('a')] sage: F.smooth_critical_ideal(alpha) - Ideal (y^2 + 14/(3*a)*y - 1, x + (-3/7*a)*y + 3/7*a - 1) of - Multivariate Polynomial Ring in x, y over Fraction Field of - Univariate Polynomial Ring in a over Rational Field + Ideal (y^2 + 14/3/a*y - 1, x + (-3/7*a)*y + 3/7*a - 1) of Multivariate Polynomial Ring in x, y over Fraction Field of Univariate Polynomial Ring in a over Rational Field """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index a736315f093..c00751bbbdc 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -67,7 +67,7 @@ cdef class FractionFieldElement(FieldElement): True sage: x = K.gen() sage: f = (x^3 + x)/(17 - x^19); f - (x^3 + x)/(-x^19 + 17) + (-x^3 - x)/(x^19 - 17) sage: loads(f.dumps()) == f True @@ -1062,6 +1062,32 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): Many of the functions here are included for coherence with number fields. """ + + def __init__(self, parent, numerator, denominator=1, + coerce=True, reduce=True): + """ + TESTS: + + sage: P. = QQ[] + sage: a = (2*x^2)/x + sage: ~a + 1/2/x + sage: 1/a + 1/2/x + """ + FractionFieldElement.__init__(self, parent, numerator, denominator, + coerce, reduce) + if not reduce: + self.normalize_leading_coefficients() + + cdef normalize_leading_coefficients(self): + """ + See :meth:`reduce`. + """ + invlc = ~self.__denominator.leading_coefficient() + self.__denominator = self.__denominator.monic() + self.__numerator *= invlc + def is_integral(self): """ Returns whether this element is actually a polynomial. @@ -1119,10 +1145,10 @@ cdef class FractionFieldElement_1poly_field(FractionFieldElement): sage: (2 + 2*x) / (4*x) (x + 1)/(2*x) """ + if self._is_reduced: + return super(self.__class__, self).reduce() - invlc = ~self.__denominator.leading_coefficient() - self.__denominator = self.__denominator.monic() - self.__numerator *= invlc + self.normalize_leading_coefficients() def make_element(parent, numerator, denominator): """ diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 97eabec4a11..717b292769d 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -32,7 +32,7 @@ sage: y^3 2*x*y + (x^4 + 1)/x sage: a = 1/y; a - (4*x/(4*x^4 + 4))*y^2 + 2*x^2/(4*x^4 + 4) + (x/(x^4 + 1))*y^2 + 3*x^2/(x^4 + 1) sage: a * y 1 @@ -46,7 +46,7 @@ sage: t^2 x*y sage: 1/t - ((1/(x^4 + 1))*y^2 + 2*x/(4*x^4 + 4))*t + ((1/(x^4 + 1))*y^2 + 3*x/(x^4 + 1))*t sage: M.base_field() Function field in y defined by y^3 + 3*x*y + (4*x^4 + 4)/x sage: M.base_field().base_field() @@ -221,7 +221,7 @@ def some_elements(self): 1/x^2, x/(x^2 - 1), x/(x^2 + 1), - x/(2*x^2 + 2), + 1/2*x/(x^2 + 1), 0, 1/x, ...] @@ -234,14 +234,13 @@ def some_elements(self): [1, y, 1/x*y, - ((1/4*x + 1/4)/(1/4*x^2 - 1/2*x + 1/4))*y - 1/2*x/(1/4*x^2 - 1/2*x + 1/4), - -1/-x, + ((x + 1)/(x^2 - 2*x + 1))*y - 2*x/(x^2 - 2*x + 1), + 1/x, (1/(x - 1))*y, (1/(x + 1))*y, - (1/(2*x + 2))*y, + (1/2/(x + 1))*y, 0, ...] - """ elements = [] @@ -778,7 +777,7 @@ class FunctionField_polymod(FunctionField): sage: M. = L.extension(z^2 + y*z + y); M Function field in z defined by z^2 + y*z + y sage: 1/z - ((x/(-x^4 - 1))*y^4 - 2*x^2/(-x^4 - 1))*z - 1 + ((-x/(x^4 + 1))*y^4 + 2*x^2/(x^4 + 1))*z - 1 sage: z * (1/z) 1 @@ -1438,12 +1437,12 @@ def vector_space(self, base=None): We define an interesting element of the function field:: sage: a = 1/L.0; a - (-x/(-x^4 - 1))*y^4 + 2*x^2/(-x^4 - 1) + (x/(x^4 + 1))*y^4 - 2*x^2/(x^4 + 1) We convert it to the vector space, and get a vector over the base field:: sage: to_V(a) - (2*x^2/(-x^4 - 1), 0, 0, 0, -x/(-x^4 - 1)) + (-2*x^2/(x^4 + 1), 0, 0, 0, x/(x^4 + 1)) We convert to and back, and get the same element:: diff --git a/src/sage/rings/function_field/function_field_element.pyx b/src/sage/rings/function_field/function_field_element.pyx index df7111ed412..6ef8d6e2e5a 100644 --- a/src/sage/rings/function_field/function_field_element.pyx +++ b/src/sage/rings/function_field/function_field_element.pyx @@ -513,7 +513,7 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): sage: K. = FunctionField(QQ); R. = K[] sage: L. = K.extension(y^2 - x*y + 4*x^3) sage: a = ~(2*y + 1/x); a # indirect doctest - (-x^2/(8*x^5 + x^2 + 1/2))*y + (2*x^3 + x)/(16*x^5 + 2*x^2 + 1) + (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) sage: a*(2*y + 1/x) 1 """ @@ -534,9 +534,9 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): sage: K. = FunctionField(QQ); R. = K[] sage: L. = K.extension(y^2 - x*y + 4*x^3) sage: a = ~(2*y + 1/x); a - (-x^2/(8*x^5 + x^2 + 1/2))*y + (2*x^3 + x)/(16*x^5 + 2*x^2 + 1) + (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) sage: a.list() - [(2*x^3 + x)/(16*x^5 + 2*x^2 + 1), -x^2/(8*x^5 + x^2 + 1/2)] + [(1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16), -1/8*x^2/(x^5 + 1/8*x^2 + 1/16)] sage: (x*y).list() [0, x] """ diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index c09fb6db963..7a69dbbfb17 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -197,7 +197,7 @@ class FunctionFieldDerivation_separable(FunctionFieldDerivation): Derivation map: From: Function field in y defined by y^2 - x To: Function field in y defined by y^2 - x - Defn: y |--> (-1/2/-x)*y + Defn: y |--> 1/2/x*y """ def __init__(self, L, d): """ @@ -248,7 +248,7 @@ def _call_(self, x): sage: d(x) # indirect doctest 1 sage: d(y) - (-1/2/-x)*y + 1/2/x*y sage: d(y^2) 1 """ @@ -273,7 +273,7 @@ def _repr_defn(self): Derivation map: From: Function field in y defined by y^2 - x To: Function field in y defined by y^2 - x - Defn: y |--> (-1/2/-x)*y + Defn: y |--> 1/2/x*y sage: R. = L[] sage: M. = L.extension(z^2 - y) @@ -281,7 +281,7 @@ def _repr_defn(self): Derivation map: From: Function field in z defined by z^2 - y To: Function field in z defined by z^2 - y - Defn: y |--> (-1/2/-x)*y + Defn: y |--> 1/2/x*y z |--> 1/4/x*z """ base = self._d._repr_defn() diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 21a91601485..a401dbfff63 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -8401,7 +8401,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: m = z^9; sage: n, d = p.rational_reconstruct(m); sage: print((n ,d)) - ((1/-t)*z^4 - t*z + 1/-t, z + 1/-t) + (-1/t*z^4 - t*z - 1/t, z - 1/t) sage: print(((p*d - n) % m ).is_zero()) True sage: w = PowerSeriesRing(P.fraction_field(), 'w').gen() diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 0cf3be5c2f8..d4d9ebf09ea 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -813,7 +813,7 @@ def lift_x(self, x, all=False, extend=False): sage: -P (x : -y : 1) sage: 2*P - ((1/4*x^4 - 4*x)/(x^3 + 2) : ((-1/8*x^6 - 5*x^3 + 4)/(-x^6 - 4*x^3 - 4))*y : 1) + ((1/4*x^4 - 4*x)/(x^3 + 2) : ((1/8*x^6 + 5*x^3 - 4)/(x^6 + 4*x^3 + 4))*y : 1) AUTHOR: From db762fc4082cf6af913a7637b895f970a7492a74 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Sun, 10 Jun 2018 14:44:31 +0200 Subject: [PATCH 148/284] additional doctest update after #25440 --- src/sage/rings/fraction_field_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index c00751bbbdc..d80263e1660 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -1030,7 +1030,7 @@ cdef class FractionFieldElement(FieldElement): sage: L = R.fraction_field() sage: S. = L[] sage: y(K(1,1)/x) - ((1 + O(2)))/((1 + O(2^5))*x) + ((1 + O(2)))/((1 + O(2))*x) """ if self.numerator().is_one(): From ebd05e995a498f6a9c1bc87bec4da187fa6d96bd Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Jul 2018 17:33:17 +0200 Subject: [PATCH 149/284] Merging nq in gap_packages --- build/pkgs/gap_packages/checksums.ini | 6 +++--- build/pkgs/gap_packages/package-version.txt | 2 +- build/pkgs/gap_packages/spkg-install | 16 +++++++++++++++- build/pkgs/gap_packages/spkg-src | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini index 13b44b183c5..ea146206ed9 100644 --- a/build/pkgs/gap_packages/checksums.ini +++ b/build/pkgs/gap_packages/checksums.ini @@ -1,4 +1,4 @@ tarball=gap_packages-VERSION.tar.bz2 -sha1=d06a939b3e2f8be02d14d586f09290c48113c9a3 -md5=f14975358aae4123a9dac3383b0fc365 -cksum=3213385843 +sha1=f3d5cba0945c52053fdae3b5be4b0ef4a2ec229f +md5=3e89f61a6458a5a32934153bb9a5fb4f +cksum=3734734850 diff --git a/build/pkgs/gap_packages/package-version.txt b/build/pkgs/gap_packages/package-version.txt index 28fb9409ce1..e8dd34461ff 100644 --- a/build/pkgs/gap_packages/package-version.txt +++ b/build/pkgs/gap_packages/package-version.txt @@ -1 +1 @@ -4.8.6new.p0 +4.8.6new2.p0 diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index ec9ff979122..5e2bb705725 100644 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -27,7 +27,7 @@ cd src/ for p in \ aclib cohomolo-1.6.4 corelg crime ctbllib cryst crystcat design factint gbnp grape \ - guava-3.13 Hap1.11 HAPcryst happrime hecke laguna liealgdb liepring liering loops mapclass polymaking \ + guava-3.13 Hap1.11 HAPcryst happrime hecke laguna liealgdb liepring liering loops mapclass nq-2.5.3 polymaking \ QPA-1.25 quagroup repsn sla sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep do echo "Copying package $p" @@ -81,6 +81,20 @@ if [ $? -ne 0 ]; then exit 1 fi +# Build nq package +cd "$PKG_DIR/nq-2.5.3" +./configure --with-gaproot="$GAP_DIR" +if [ $? -ne 0 ]; then + echo >&2 "Error configuring nq package." + exit 1 +fi +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building nq package." + exit 1 +fi + + # Some GAP packages have weird permissions chmod -R u+rwX "$PKG_DIR" diff --git a/build/pkgs/gap_packages/spkg-src b/build/pkgs/gap_packages/spkg-src index bd56190e855..d063b97151a 100755 --- a/build/pkgs/gap_packages/spkg-src +++ b/build/pkgs/gap_packages/spkg-src @@ -26,7 +26,7 @@ chmod -R u+w "$GAP" mkdir src for pkg in \ aclib cohomolo corelg crime ctbllib design factint gbnp grape \ - guava Hap HAPcryst hecke laguna liealgdb liepring liering loops mapclass polymaking \ + guava Hap HAPcryst hecke laguna liealgdb liepring liering loops mapclass nq polymaking \ QPA quagroup repsn sla sonata toric polycyclic autpgrp Alnuth atlasrep do echo "Copying package $pkg" From 8ea81ebd5c455ecf741eb6cf95836212fa3630db Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Jul 2018 23:57:02 +0200 Subject: [PATCH 150/284] Updating gap_packages for nq with spkg-src, QPA downgraded --- build/pkgs/gap_packages/checksums.ini | 6 +++--- build/pkgs/gap_packages/spkg-install | 2 +- src/sage/interfaces/gap.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini index ea146206ed9..0b222043967 100644 --- a/build/pkgs/gap_packages/checksums.ini +++ b/build/pkgs/gap_packages/checksums.ini @@ -1,4 +1,4 @@ tarball=gap_packages-VERSION.tar.bz2 -sha1=f3d5cba0945c52053fdae3b5be4b0ef4a2ec229f -md5=3e89f61a6458a5a32934153bb9a5fb4f -cksum=3734734850 +sha1=e01de74900294c5a528ad39d1f1fbcee33f8f894 +md5=b7b4b1829a816e8262b46106bd9715e2 +cksum=207759899 diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index 5e2bb705725..ef9072cdd3b 100644 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -28,7 +28,7 @@ cd src/ for p in \ aclib cohomolo-1.6.4 corelg crime ctbllib cryst crystcat design factint gbnp grape \ guava-3.13 Hap1.11 HAPcryst happrime hecke laguna liealgdb liepring liering loops mapclass nq-2.5.3 polymaking \ - QPA-1.25 quagroup repsn sla sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep + QPA-1.24 quagroup repsn sla sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep do echo "Copying package $p" cp -r $p "$PKG_DIR" diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 2d0f889e330..770bb39318b 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1554,7 +1554,7 @@ def gap_reset_workspace(max_workspace_size=None, verbose=False): g.eval('SetUserPreference("HistoryMaxLines", 30)') for pkg in ['GAPDoc', 'ctbllib', 'sonata', 'guava', 'factint', \ 'gapdoc', 'grape', 'design', \ - 'toric', 'laguna', 'braid']: + 'toric', 'laguna', 'braid', 'polycyclic', 'nq']: # NOTE: Do *not* autoload hap - it screws up PolynomialRing(Rationals,2) try: g.load_package(pkg, verbose=verbose) From 5d9b1a5c9ec4588567a8f34bdb895082a6c1c26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 4 Jul 2018 17:32:08 +0200 Subject: [PATCH 151/284] Allow to push to another's user's namespace on the Docker Hub --- .ci/push-dockerhub.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/push-dockerhub.sh b/.ci/push-dockerhub.sh index c2c46062ffa..a685ae8dd6e 100755 --- a/.ci/push-dockerhub.sh +++ b/.ci/push-dockerhub.sh @@ -1,7 +1,7 @@ #!/bin/sh # This script gets called from CI to push our docker images to -# $DOCKER_USER/sagemath* on the Docker Hub. +# $DOCKER_NAMESPACE/sagemath* on the Docker Hub. # This script expects a single parameter, the base name of the docker image # such as sagemath or sagemath-dev. @@ -25,5 +25,5 @@ if [ -z "$DOCKER_USER" -o -z "$SECRET_DOCKER_PASS" ]; then echo "DOCKER_USER/SECRET_DOCKER_PASS variables have not been configured in your Continuous Integration setup. Not pushing built images to Docker Hub." else cat "$SECRET_DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin - docker push ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG + docker push ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG fi From 9c5298df63011683acac988f48c7b02990ea5379 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 5 Jul 2018 11:28:18 +0200 Subject: [PATCH 152/284] make doctests more independent of ordering --- src/sage/combinat/diagram_algebras.py | 144 +++++++++++++++----------- 1 file changed, 85 insertions(+), 59 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index e44decfed8d..451e1ef3a58 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -2304,45 +2304,67 @@ def _element_constructor_(self, x): EXAMPLES:: sage: R. = QQ[] + + Construct an element in the usual (diagram) basis of the partition algebra:: + sage: A2 = PartitionAlgebra(2, x, R) + sage: a2 = A2([[1,2],[-1,-2]]); a2 + P{{-2, -1}, {1, 2}} + + There is a natural embedding into partition algebras on more elements, by adding identity strands:: + + sage: A4 = PartitionAlgebra(4, x, R) + sage: A4(a2) + P{{-4, 4}, {-3, 3}, {-2, -1}, {1, 2}} + + Thus, the empty partition corresponds to the identity:: + + sage: A4([]) + P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} + sage: A4(5) + 5*P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} + + The group algebra of the symmetric group is a subalgebra:: + + sage: S3 = SymmetricGroupAlgebra(ZZ, 3) + sage: s3 = S3([2,3,1]); s3 + [2, 3, 1] + sage: A4(s3) + P{{-4, 4}, {-3, 2}, {-2, 1}, {-1, 3}} + sage: A4([2,1]) + P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} + + Be careful not to confuse the embedding of the group algebra + of the symmetric group with the embedding of partial set + partitions. The latter are embedded by adding the parts + `\{i,-i\}` if possible, and singletons sets for the remaining + parts:: + + sage: A4([[2,1]]) + P{{-4, 4}, {-3, 3}, {-2}, {-1}, {1, 2}} + sage: A4([[-1,3],[-2,-3,1]]) + P{{-4, 4}, {-3, -2, 1}, {-1, 3}, {2}} + + Another subalgebra is the Brauer algebra, which has perfect + matchings as basis elements. The group algebra of the + symmetric group is in fact a subalgebra of the Brauer + algebra:: + + sage: B3 = BrauerAlgebra(3, x, R) + sage: b3 = B3(s3); b3 + B{{-3, 2}, {-2, 1}, {-1, 3}} + + An important basis of the partition algebra is the orbit basis:: + sage: O2 = A2.orbit_basis() - sage: S = SymmetricGroupAlgebra(ZZ, 3) - sage: A = PartitionAlgebra(3, x, R) - sage: B = BrauerAlgebra(3, x, R) - sage: O = A.orbit_basis() - sage: O2.an_element() - 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} - sage: A(O2.an_element()) - 3*P{{-3, 3}, {-2}, {-1, 1, 2}} - 3*P{{-3, 3}, {-2, -1, 1, 2}} + 2*P{{-3, 3}, {-2, 1, 2}, {-1}} - sage: A2.an_element() - 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} - sage: A(A2.an_element()) - 3*P{{-3, 3}, {-2}, {-1, 1, 2}} + 2*P{{-3, 3}, {-2, -1, 1, 2}} + 2*P{{-3, 3}, {-2, 1, 2}, {-1}} - sage: S.an_element() - [1, 2, 3] + 2*[1, 3, 2] + 3*[2, 1, 3] + [3, 1, 2] - sage: A(S.an_element()) - P{{-3, 1}, {-2, 3}, {-1, 2}} + 2*P{{-3, 2}, {-2, 3}, {-1, 1}} - + 3*P{{-3, 3}, {-2, 1}, {-1, 2}} + P{{-3, 3}, {-2, 2}, {-1, 1}} - sage: B.an_element() - 3*B{{-3, 1}, {-2, -1}, {2, 3}} + 2*B{{-3, 1}, {-2, 2}, {-1, 3}} - + 2*B{{-3, 1}, {-2, 3}, {-1, 2}} - sage: A(B.an_element()) - 3*P{{-3, 1}, {-2, -1}, {2, 3}} + 2*P{{-3, 1}, {-2, 2}, {-1, 3}} - + 2*P{{-3, 1}, {-2, 3}, {-1, 2}} - sage: O.an_element() - 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} - sage: A(O.an_element()) - 3*P{{-3}, {-2, -1, 1, 2, 3}} - 3*P{{-3, -2, -1, 1, 2, 3}} + 2*P{{-3, -1, 1, 2, 3}, {-2}} - sage: A([]) - P{{-3, 3}, {-2, 2}, {-1, 1}} - sage: A(4) - 4*P{{-3, 3}, {-2, 2}, {-1, 1}} - sage: A([2,1]) - P{{-3, 3}, {-2, 1}, {-1, 2}} - sage: A([[2,1]]) - P{{-3, 3}, {-2}, {-1}, {1, 2}} - sage: A([[-1,3],[-2,-3,1]]) - P{{-3, -2, 1}, {-1, 3}, {2}} + sage: o2 = O2([[1,2],[-1,-2]]) + O2([[1,2,-1,-2]]); o2 + O{{-2, -1}, {1, 2}} + O{{-2, -1, 1, 2}} + + The diagram basis element corresponds to the sum of all orbit + basis elements indexed by coarser set partitions:: + + sage: A2(o2) + P{{-2, -1}, {1, 2}} TESTS:: @@ -2375,6 +2397,7 @@ def _element_constructor_(self, x): Traceback (most recent call last): ... ValueError: the diagram {{-2, 1}, {-1, 2}} must be planar + """ # coercion from basis keys if self.basis().keys().is_parent_of(x): @@ -2458,10 +2481,10 @@ def _coerce_map_from_(self, R): TESTS:: - sage: elt = O3.an_element(); elt - 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} + sage: elt = O3([[-3], [-2, -1, 1, 2, 3]]) + O3([[-3, -2, -1, 1, 2, 3]]) + O3([[-3, -1, 1, 2, 3], [-2]]); elt + O{{-3}, {-2, -1, 1, 2, 3}} + O{{-3, -2, -1, 1, 2, 3}} + O{{-3, -1, 1, 2, 3}, {-2}} sage: A._coerce_map_from_(O3)(elt) - 3*P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + 2*P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} + P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} """ # coerce from Orbit basis. if isinstance(R, OrbitBasis): @@ -2537,10 +2560,10 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) - sage: pp = P.an_element(); pp - 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + sage: pp = P([[-2], [-1, 1, 2]]) + P([[-2, -1, 1, 2]]) + P([[-2, 1, 2], [-1]]); pp + P{{-2}, {-1, 1, 2}} + P{{-2, -1, 1, 2}} + P{{-2, 1, 2}, {-1}} sage: pp.to_orbit_basis() - 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + O{{-2}, {-1, 1, 2}} + 3*O{{-2, -1, 1, 2}} + O{{-2, 1, 2}, {-1}} """ OP = self.parent().orbit_basis() return OP(self) @@ -2770,8 +2793,9 @@ def diagram_basis(self): sage: P2 = O2.diagram_basis(); P2 Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field - sage: P2(O2.an_element()) - 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + sage: o2 = O2([[-2, -1, 1], [2]]) + O2([[-2, -1, 1, 2]]) + O2([[-2, -1, 2], [1]]) + sage: P2(o2) + P{{-2, -1, 1}, {2}} - P{{-2, -1, 1, 2}} + P{{-2, -1, 2}, {1}} TESTS:: @@ -2836,8 +2860,8 @@ def product_on_basis(self, d1, d2): sage: R. = QQ[] sage: OP = PartitionAlgebra(2, x, R).orbit_basis() - sage: SP = OP.basis().keys() - sage: OP.product_on_basis(SP.an_element(), SP.an_element()) + sage: SP = OP.basis().keys(); sp = SP([[-2, -1, 1, 2]]) + sage: OP.product_on_basis(sp, sp) O{{-2, -1, 1, 2}} sage: o1 = OP.one(); o2 = OP([]); o3 = OP.an_element() sage: o2 == o1 @@ -2846,8 +2870,9 @@ def product_on_basis(self, d1, d2): True sage: o3 * o1 == o1 * o3 and o3 * o1 == o3 True - sage: o3 * o3 - 6*O{{-2}, {-1, 1, 2}} + 4*O{{-2, -1, 1, 2}} + 4*O{{-2, 1, 2}, {-1}} + sage: o4 = OP([[-2, -1, 1], [2]]) + OP([[-2, -1, 1, 2]]) + OP([[-2, -1, 2], [1]]) + sage: o4 * o4 + O{{-2, -1, 1}, {2}} + O{{-2, -1, 1, 2}} + O{{-2, -1, 2}, {1}} We compute Examples 4.5 in [BH2017]_:: @@ -2945,13 +2970,13 @@ def to_diagram_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) sage: O = P.orbit_basis() - sage: elt = O.an_element(); elt - 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + sage: elt = O([[-2], [-1, 1, 2]]) + O([[-2, -1, 1, 2]]) + O([[-2, 1, 2], [-1]]); elt + O{{-2}, {-1, 1, 2}} + O{{-2, -1, 1, 2}} + O{{-2, 1, 2}, {-1}} sage: elt.to_diagram_basis() - 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} - sage: pp = P.an_element() + P{{-2}, {-1, 1, 2}} - P{{-2, -1, 1, 2}} + P{{-2, 1, 2}, {-1}} + sage: pp = P([[-2], [-1, 1, 2]]) sage: op = pp.to_orbit_basis(); op - 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + O{{-2}, {-1, 1, 2}} + O{{-2, -1, 1, 2}} sage: pp == op.to_diagram_basis() True """ @@ -3040,11 +3065,12 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: B = BrauerAlgebra(2, x, R) - sage: bb = B.an_element(); bb - 3*B{{-2, -1}, {1, 2}} + 2*B{{-2, 1}, {-1, 2}} + 2*B{{-2, 2}, {-1, 1}} - sage: bb.to_orbit_basis() - 3*O{{-2, -1}, {1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1}, {-1, 2}} - + 2*O{{-2, 2}, {-1, 1}} + sage: b = B([[-2, -1], [1, 2]]); b + B{{-2, -1}, {1, 2}} + sage: o = b.to_orbit_basis(); o + O{{-2, -1}, {1, 2}} + O{{-2, -1, 1, 2}} + sage: o.parent() + Orbit basis of Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field """ P = self.parent().lift.codomain() OP = P.orbit_basis() From 4bdd32a4c7a9a434bc992be8d4e84ef7f2c89748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Jul 2018 17:01:52 +0200 Subject: [PATCH 153/284] Retry build-from-clean on github --- .gitlab-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1caebb802db..50445f8b16d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -117,6 +117,11 @@ build-from-clean: tags: # 60 GB of HDD are available - do + # This build takes several CPU hours. It is very unlikely that there are any + # actual build errors for a tagged release but the (discounted) cloud + # machines this is running on might be preempted during the long build time. + # So let's try three times before we give up. + retry: 2 test-dev: stage: test From 754c52bcf2ded4813b440e8f15e4ee2f295c290a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Jul 2018 17:23:51 +0200 Subject: [PATCH 154/284] Skip publication steps if SECRET_DOCKER_PASS has not been set Note that there is no way to check whether both SECRET_DOCKER_PASS && DOCKER_USER have been set. Also, this is an alpha feature of GitLab, so the syntax might change. As an alternative we could check this at the beginning of the script block and just "exit 0"; but currently a lot of time and in particular bandwidth is wasted by pulling the docker images and then not pushing them because of missing credentials. --- .gitlab-ci.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 50445f8b16d..ae5bd00de5e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -146,8 +146,11 @@ test-jupyter: push-dockerhub: stage: release only: - - branches - - tags + refs: + - branches + - tags + variables: + - $SECRET_DOCKER_PASS script: - . .ci/pull-gitlab.sh sagemath - sh .ci/push-dockerhub.sh sagemath @@ -157,9 +160,12 @@ push-dockerhub: push-dockerhub-dev: stage: release only: - - master - - develop - - tags + refs: + - master + - develop + - tags + variables: + - $SECRET_DOCKER_PASS script: - . .ci/pull-gitlab.sh sagemath-dev - sh .ci/push-dockerhub.sh sagemath-dev From 894c5b8b5c55324015e1d3c0664409bc60c8b992 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 5 Jul 2018 17:28:22 +0200 Subject: [PATCH 155/284] Fixup for 5d9b1a5c9ec4588567a8f34bdb895082a6c1c26d use DOCKER_NAMESPACE consistently --- .ci/pull-gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/pull-gitlab.sh b/.ci/pull-gitlab.sh index c8235e64068..58b17016b90 100755 --- a/.ci/pull-gitlab.sh +++ b/.ci/pull-gitlab.sh @@ -26,5 +26,5 @@ set -ex # automatically from the log output. docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY docker pull $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG -export DOCKER_IMAGE="${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG" +export DOCKER_IMAGE="${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG" docker tag $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG $DOCKER_IMAGE From e560da0590e38d45a54f40f8a9ec89700131a8c3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 6 Jul 2018 12:23:38 +0200 Subject: [PATCH 156/284] Correct gap version in spkg-src --- build/pkgs/gap_packages/checksums.ini | 6 +++--- build/pkgs/gap_packages/spkg-install | 2 +- build/pkgs/gap_packages/spkg-src | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini index 0b222043967..449d3d027f8 100644 --- a/build/pkgs/gap_packages/checksums.ini +++ b/build/pkgs/gap_packages/checksums.ini @@ -1,4 +1,4 @@ tarball=gap_packages-VERSION.tar.bz2 -sha1=e01de74900294c5a528ad39d1f1fbcee33f8f894 -md5=b7b4b1829a816e8262b46106bd9715e2 -cksum=207759899 +sha1=e2494ad7f27858b72b7d4d335bcda6e11ee3e528 +md5=b93f15c3a09349ee4035f1f5d2a8ad0b +cksum=4098558672 diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index ef9072cdd3b..5e2bb705725 100644 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -28,7 +28,7 @@ cd src/ for p in \ aclib cohomolo-1.6.4 corelg crime ctbllib cryst crystcat design factint gbnp grape \ guava-3.13 Hap1.11 HAPcryst happrime hecke laguna liealgdb liepring liering loops mapclass nq-2.5.3 polymaking \ - QPA-1.24 quagroup repsn sla sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep + QPA-1.25 quagroup repsn sla sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep do echo "Copying package $p" cp -r $p "$PKG_DIR" diff --git a/build/pkgs/gap_packages/spkg-src b/build/pkgs/gap_packages/spkg-src index d063b97151a..6e85520c691 100755 --- a/build/pkgs/gap_packages/spkg-src +++ b/build/pkgs/gap_packages/spkg-src @@ -13,7 +13,7 @@ shopt -s extglob # Remove old sources and download new rm -rf src if [ -z "$UPSTREAM_SOURCE_TARBALL" ]; then - tar xjf <( curl http://www.gap-system.org/pub/gap/gap48/tar.bz2/gap4r8p3_2016_03_19-22_17.tar.bz2) + tar xjf <( curl http://www.gap-system.org/pub/gap/gap48/tar.bz2/gap4r8p6_2016_11_12-14_25.tar.bz2) else tar xf "$UPSTREAM_SOURCE_TARBALL" fi From ce2962b001f088e28a2e568334c5d4daf58a5f32 Mon Sep 17 00:00:00 2001 From: zabrocki Date: Fri, 6 Jul 2018 08:25:54 -0400 Subject: [PATCH 157/284] restore richer doc tests --- src/sage/combinat/diagram_algebras.py | 54 ++++++++++++++------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 3baa8fa51d0..04b0ec8b50e 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -2308,14 +2308,15 @@ def _element_constructor_(self, x): Construct an element in the usual (diagram) basis of the partition algebra:: sage: A2 = PartitionAlgebra(2, x, R) - sage: a2 = A2([[1,2],[-1,-2]]); a2 - P{{-2, -1}, {1, 2}} + sage: a2 = 3*A2[[-2], [-1, 1, 2]] + 2*A2[[-2, -1, 1, 2]] + 2*A2[[-2, 1, 2], [-1]]; a2 + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} There is a natural embedding into partition algebras on more elements, by adding identity strands:: sage: A4 = PartitionAlgebra(4, x, R) sage: A4(a2) - P{{-4, 4}, {-3, 3}, {-2, -1}, {1, 2}} + 3*P{{-4, 4}, {-3, 3}, {-2}, {-1, 1, 2}} + 2*P{{-4, 4}, {-3, 3}, {-2, -1, 1, 2}} + + 2*P{{-4, 4}, {-3, 3}, {-2, 1, 2}, {-1}} Thus, the empty partition corresponds to the identity:: @@ -2327,10 +2328,11 @@ def _element_constructor_(self, x): The group algebra of the symmetric group is a subalgebra:: sage: S3 = SymmetricGroupAlgebra(ZZ, 3) - sage: s3 = S3([2,3,1]); s3 - [2, 3, 1] + sage: s3 = S3.an_element(); s3 + [1, 2, 3] + 2*[1, 3, 2] + 3*[2, 1, 3] + [3, 1, 2] sage: A4(s3) - P{{-4, 4}, {-3, 2}, {-2, 1}, {-1, 3}} + P{{-4, 4}, {-3, 1}, {-2, 3}, {-1, 2}} + 2*P{{-4, 4}, {-3, 2}, {-2, 3}, {-1, 1}} + + 3*P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} + P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} sage: A4([2,1]) P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} @@ -2352,7 +2354,8 @@ def _element_constructor_(self, x): sage: B3 = BrauerAlgebra(3, x, R) sage: b3 = B3(s3); b3 - B{{-3, 2}, {-2, 1}, {-1, 3}} + B{{-3, 1}, {-2, 3}, {-1, 2}} + 2*B{{-3, 2}, {-2, 3}, {-1, 1}} + + 3*B{{-3, 3}, {-2, 1}, {-1, 2}} + B{{-3, 3}, {-2, 2}, {-1, 1}} An important basis of the partition algebra is the orbit basis:: @@ -2481,10 +2484,10 @@ def _coerce_map_from_(self, R): TESTS:: - sage: elt = O3([[-3], [-2, -1, 1, 2, 3]]) + O3([[-3, -2, -1, 1, 2, 3]]) + O3([[-3, -1, 1, 2, 3], [-2]]); elt - O{{-3}, {-2, -1, 1, 2, 3}} + O{{-3, -2, -1, 1, 2, 3}} + O{{-3, -1, 1, 2, 3}, {-2}} + sage: elt = 3*O3([[-3], [-2, -1, 1, 2, 3]]) + 2*O3([[-3, -2, -1, 1, 2, 3]]) + 2*O3([[-3, -1, 1, 2, 3], [-2]]); elt + 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} sage: A._coerce_map_from_(O3)(elt) - P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} + 3*P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + 2*P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} """ # coerce from Orbit basis. if isinstance(R, OrbitBasis): @@ -2560,10 +2563,10 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) - sage: pp = P([[-2], [-1, 1, 2]]) + P([[-2, -1, 1, 2]]) + P([[-2, 1, 2], [-1]]); pp - P{{-2}, {-1, 1, 2}} + P{{-2, -1, 1, 2}} + P{{-2, 1, 2}, {-1}} + sage: pp = 3*P([[-2], [-1, 1, 2]]) + 2*P([[-2, -1, 1, 2]]) + 2*P([[-2, 1, 2], [-1]]); pp + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: pp.to_orbit_basis() - O{{-2}, {-1, 1, 2}} + 3*O{{-2, -1, 1, 2}} + O{{-2, 1, 2}, {-1}} + 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} """ OP = self.parent().orbit_basis() return OP(self) @@ -2793,9 +2796,9 @@ def diagram_basis(self): sage: P2 = O2.diagram_basis(); P2 Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field - sage: o2 = O2([[-2, -1, 1], [2]]) + O2([[-2, -1, 1, 2]]) + O2([[-2, -1, 2], [1]]) + sage: o2 = 3*O2([[-2, -1, 1], [2]]) + 2*O2([[-2, -1, 1, 2]]) + 2*O2([[-2, -1, 2], [1]]) sage: P2(o2) - P{{-2, -1, 1}, {2}} - P{{-2, -1, 1, 2}} + P{{-2, -1, 2}, {1}} + 3*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} TESTS:: @@ -2870,9 +2873,9 @@ def product_on_basis(self, d1, d2): True sage: o3 * o1 == o1 * o3 and o3 * o1 == o3 True - sage: o4 = OP([[-2, -1, 1], [2]]) + OP([[-2, -1, 1, 2]]) + OP([[-2, -1, 2], [1]]) + sage: o4 = 3*OP([[-2, -1, 1], [2]]) + 2*OP([[-2, -1, 1, 2]]) + 2*OP([[-2, -1, 2], [1]]) sage: o4 * o4 - O{{-2, -1, 1}, {2}} + O{{-2, -1, 1, 2}} + O{{-2, -1, 2}, {1}} + 6*O{{-2, -1, 1}, {2}} + 4*O{{-2, -1, 1, 2}} + 4*O{{-2, -1, 2}, {1}} We compute Examples 4.5 in [BH2017]_:: @@ -2970,13 +2973,13 @@ def to_diagram_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) sage: O = P.orbit_basis() - sage: elt = O([[-2], [-1, 1, 2]]) + O([[-2, -1, 1, 2]]) + O([[-2, 1, 2], [-1]]); elt - O{{-2}, {-1, 1, 2}} + O{{-2, -1, 1, 2}} + O{{-2, 1, 2}, {-1}} + sage: elt = 3*O([[-2], [-1, 1, 2]]) + 2*O([[-2, -1, 1, 2]]) + 2*O([[-2, 1, 2], [-1]]); elt + 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: elt.to_diagram_basis() - P{{-2}, {-1, 1, 2}} - P{{-2, -1, 1, 2}} + P{{-2, 1, 2}, {-1}} - sage: pp = P([[-2], [-1, 1, 2]]) + 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + sage: pp = 3*P[[-2],[-1, 1, 2]] + 2*P[[-2, -1, 1, 2]] + 2*P[[-2, 1, 2], [-1]] sage: op = pp.to_orbit_basis(); op - O{{-2}, {-1, 1, 2}} + O{{-2, -1, 1, 2}} + 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: pp == op.to_diagram_basis() True """ @@ -3065,10 +3068,11 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: B = BrauerAlgebra(2, x, R) - sage: b = B([[-2, -1], [1, 2]]); b - B{{-2, -1}, {1, 2}} + sage: b = 3*B[[-2, -1], [1, 2]] + 2*B[[-2, 1], [-1, 2]] + 2*B[[-2, 2], [-1, 1]]; b + 3*B{{-2, -1}, {1, 2}} + 2*B{{-2, 1}, {-1, 2}} + 2*B{{-2, 2}, {-1, 1}} sage: o = b.to_orbit_basis(); o - O{{-2, -1}, {1, 2}} + O{{-2, -1, 1, 2}} + 3*O{{-2, -1}, {1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1}, {-1, 2}} + + 2*O{{-2, 2}, {-1, 1}} sage: o.parent() Orbit basis of Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field """ From 39856158d913143f459fd55609b0372a92dff530 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 7 Jul 2018 00:35:15 +0200 Subject: [PATCH 158/284] Update to singular 4.1.1p2 --- build/pkgs/singular/checksums.ini | 6 +- build/pkgs/singular/package-version.txt | 2 +- build/pkgs/singular/patches/floor.patch | 33 ----- build/pkgs/singular/patches/floor2.patch | 37 ----- build/pkgs/singular/patches/pow.patch | 28 ---- build/pkgs/singular/patches/sunos.diff | 93 ------------ build/pkgs/singular/patches/sunos.patch | 180 ----------------------- 7 files changed, 4 insertions(+), 375 deletions(-) delete mode 100644 build/pkgs/singular/patches/floor.patch delete mode 100644 build/pkgs/singular/patches/floor2.patch delete mode 100644 build/pkgs/singular/patches/pow.patch delete mode 100644 build/pkgs/singular/patches/sunos.diff delete mode 100644 build/pkgs/singular/patches/sunos.patch diff --git a/build/pkgs/singular/checksums.ini b/build/pkgs/singular/checksums.ini index 49210700c6f..55751d34293 100644 --- a/build/pkgs/singular/checksums.ini +++ b/build/pkgs/singular/checksums.ini @@ -1,4 +1,4 @@ tarball=singular-VERSION.tar.gz -sha1=2ffb7064b106891ef71ea6087c44e28224af43c3 -md5=beff4f51fa3e3fd0ce4f449b415cf4a2 -cksum=3867411595 +sha1=5c6b6c3d2b5ebaca164967eec67e59ebb4e6142f +md5=cb50d64ab1b2b49a0c3f519e5c87639e +cksum=4294037094 diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index 8dfe4bffa7f..c86d8809c87 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.1.0p3.p2 +4.1.1p2 diff --git a/build/pkgs/singular/patches/floor.patch b/build/pkgs/singular/patches/floor.patch deleted file mode 100644 index f3b7230c5d5..00000000000 --- a/build/pkgs/singular/patches/floor.patch +++ /dev/null @@ -1,33 +0,0 @@ -commit 08fd9112fa9a9f938b637cbc4f50aa3f12a7013e -Author: Dima Pasechnik -Date: Sat Feb 3 04:08:10 2018 +0000 - - don't do floor(int) - - This is another catch from an attempt to build Singular on Solaris 11, - this time using gcc 5.4.0. It errors out saying - - walk.cc:4570:103: error: call of overloaded 'floor(int)' is ambiguous - (*next_weight2)[i] = 1 + (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); - - walk.cc:4574:99: error: call of overloaded 'floor(int)' is ambiguous - (*next_weight2)[i] = (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); - -diff --git a/Singular/walk.cc b/Singular/walk.cc -index 08b7658..0ece307 100644 ---- a/Singular/walk.cc -+++ b/Singular/walk.cc -@@ -4566,11 +4566,11 @@ static intvec* MWalkRandomNextWeight(ideal G, intvec* orig_M, intvec* target_wei - { - if((*next_weight2)[i] < 0) - { -- (*next_weight2)[i] = 1 + (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); -+ (*next_weight2)[i] = 1 + (*curr_weight)[i] + weight_rad*(*next_weight2)[i]/weight_norm; - } - else - { -- (*next_weight2)[i] = (*curr_weight)[i] + floor(weight_rad*(*next_weight2)[i]/weight_norm); -+ (*next_weight2)[i] = (*curr_weight)[i] + weight_rad*(*next_weight2)[i]/weight_norm; - } - } - if(test_w_in_ConeCC(G,next_weight2) == 1) diff --git a/build/pkgs/singular/patches/floor2.patch b/build/pkgs/singular/patches/floor2.patch deleted file mode 100644 index cc32ef1b146..00000000000 --- a/build/pkgs/singular/patches/floor2.patch +++ /dev/null @@ -1,37 +0,0 @@ -commit 1a13c7c54e8a386019b0bbf897243b395e01ebe5 -Author: Jeroen Demeyer -Date: Sat Feb 3 09:21:08 2018 +0100 - - Avoid some floor() calls - -diff --git a/Singular/walk.cc b/Singular/walk.cc -index 08b7658..bf692d9 100644 ---- a/Singular/walk.cc -+++ b/Singular/walk.cc -@@ -2302,7 +2302,7 @@ static intvec* MwalkNextWeightCC(intvec* curr_weight, intvec* target_weight, - } - for(j=0; j(sqrt(double(weight_norm))); - } - for(i=0; i -Date: Fri Feb 2 12:24:48 2018 +0100 - - fix: use pow(double,double) by explicit casting - -diff --git a/libpolys/coeffs/ffields.cc b/libpolys/coeffs/ffields.cc -index 61d8eff..612ed2d 100644 ---- a/libpolys/coeffs/ffields.cc -+++ b/libpolys/coeffs/ffields.cc -@@ -806,7 +806,7 @@ static BOOLEAN nfCoeffIsEqual (const coeffs r, n_coeffType n, void * parameter) - { - if (n==n_GF) { - GFInfo* p = (GFInfo *)(parameter); -- int c = pow (p->GFChar, p->GFDegree); -+ int c = (int)pow ((double)p->GFChar, (double)p->GFDegree); - if ((c == r->m_nfCharQ) && (strcmp(n_ParameterNames(r)[0], p->GFPar_name) == 0)) - return TRUE; - } -@@ -927,7 +927,7 @@ BOOLEAN nfInitChar(coeffs r, void * parameter) - return TRUE; - } - -- int c = pow (p->GFChar, p->GFDegree); -+ int c = (int)pow ((double)p->GFChar, (double)p->GFDegree); - - nfReadTable(c, r); - diff --git a/build/pkgs/singular/patches/sunos.diff b/build/pkgs/singular/patches/sunos.diff deleted file mode 100644 index fc9db417a1c..00000000000 --- a/build/pkgs/singular/patches/sunos.diff +++ /dev/null @@ -1,93 +0,0 @@ -Changes to source files to compile Singular on SunOS. - -This patch is not actually applied, only the changes to auto-generated -files in sunos.patch should be applied. - - -commit 6712e907767e5540613b217f792b5f42d2b39ac1 -Author: Dima Pasechnik -Date: Wed Jan 31 12:50:27 2018 +0000 - - link libraries sometimes needed by gethostbyname etc - - On Solaris, in particular, one needs -lsocket -lnsl in the libs - to be linked against. See https://trac.sagemath.org/ticket/24611 - This is taken care by this autoconf macro call. - -diff --git a/configure.ac b/configure.ac -index ecda2a4..a4a21a2 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -155,6 +155,7 @@ m4_ifval([$3], - fi[]dnl - ])]) - -+AX_LIB_SOCKET_NSL - - AC_DEFINE_UNQUOTED([CC],"$CC",[CC]) - AC_DEFINE_UNQUOTED([CXX],"$CXX",[CXX]) -diff --git a/m4/ax_lib_socket_nsl.m4 b/m4/ax_lib_socket_nsl.m4 -new file mode 100644 -index 0000000..54cad68 ---- /dev/null -+++ b/m4/ax_lib_socket_nsl.m4 -@@ -0,0 +1,40 @@ -+# =========================================================================== -+# https://www.gnu.org/software/autoconf-archive/ax_lib_socket_nsl.html -+# =========================================================================== -+# -+# SYNOPSIS -+# -+# AX_LIB_SOCKET_NSL -+# -+# DESCRIPTION -+# -+# This macro figures out what libraries are required on this platform to -+# link sockets programs. -+# -+# The common cases are not to need any extra libraries, or to need -+# -lsocket and -lnsl. We need to avoid linking with libnsl unless we need -+# it, though, since on some OSes where it isn't necessary it will totally -+# break networking. Unisys also includes gethostbyname() in libsocket but -+# needs libnsl for socket(). -+# -+# LICENSE -+# -+# Copyright (c) 2008 Russ Allbery -+# Copyright (c) 2008 Stepan Kasal -+# Copyright (c) 2008 Warren Young -+# -+# 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 7 -+ -+AU_ALIAS([LIB_SOCKET_NSL], [AX_LIB_SOCKET_NSL]) -+AC_DEFUN([AX_LIB_SOCKET_NSL], -+[ -+ AC_SEARCH_LIBS([gethostbyname], [nsl]) -+ AC_SEARCH_LIBS([socket], [socket], [], [ -+ AC_CHECK_LIB([socket], [socket], [LIBS="-lsocket -lnsl $LIBS"], -+ [], [-lnsl])]) -+]) -commit facbf5bceffaab92df81e58b805ceee5cdc1788e -Author: Hans Schoenemann -Date: Tue Jan 30 17:38:13 2018 +0100 - - fix: tr. #816 (P_PROCS_MODULE_LDFLAGS) - -diff --git a/libpolys/polys/Makefile.am b/libpolys/polys/Makefile.am -index 23d4cae..b222a7f 100644 ---- a/libpolys/polys/Makefile.am -+++ b/libpolys/polys/Makefile.am -@@ -65,7 +65,7 @@ p_Procs_FieldIndep_la_CPPFLAGS = -Dp_Procs_FieldIndep ${P_PROCS_CPPFLAGS_COMMON} - p_Procs_FieldQ_la_CPPFLAGS = -Dp_Procs_FieldQ ${P_PROCS_CPPFLAGS_COMMON} - p_Procs_FieldZp_la_CPPFLAGS = -Dp_Procs_FieldZp ${P_PROCS_CPPFLAGS_COMMON} - --P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -Wl,-undefined -Wl,dynamic_lookup -flat_namespace $(SINGULAR_LDFLAGS) -+P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -flat_namespace - - p_Procs_FieldGeneral_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} - p_Procs_FieldIndep_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} diff --git a/build/pkgs/singular/patches/sunos.patch b/build/pkgs/singular/patches/sunos.patch deleted file mode 100644 index 42c84d3713f..00000000000 --- a/build/pkgs/singular/patches/sunos.patch +++ /dev/null @@ -1,180 +0,0 @@ -Changes to relevant autogenerated-files as consequence of sunos.diff - -diff -ru singular-4.1.0//configure b/configure ---- singular-4.1.0//configure 2017-04-18 16:55:37.000000000 +0200 -+++ b/configure 2018-02-02 12:57:16.588312504 +0100 -@@ -23526,6 +23533,162 @@ - - - -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gethostbyname" >&5 -+$as_echo_n "checking for library containing gethostbyname... " >&6; } -+if ${ac_cv_search_gethostbyname+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_func_search_save_LIBS=$LIBS -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char gethostbyname (); -+int -+main () -+{ -+return gethostbyname (); -+ ; -+ return 0; -+} -+_ACEOF -+for ac_lib in '' nsl; do -+ if test -z "$ac_lib"; then -+ ac_res="none required" -+ else -+ ac_res=-l$ac_lib -+ LIBS="-l$ac_lib $ac_func_search_save_LIBS" -+ fi -+ if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_search_gethostbyname=$ac_res -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext -+ if ${ac_cv_search_gethostbyname+:} false; then : -+ break -+fi -+done -+if ${ac_cv_search_gethostbyname+:} false; then : -+ -+else -+ ac_cv_search_gethostbyname=no -+fi -+rm conftest.$ac_ext -+LIBS=$ac_func_search_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gethostbyname" >&5 -+$as_echo "$ac_cv_search_gethostbyname" >&6; } -+ac_res=$ac_cv_search_gethostbyname -+if test "$ac_res" != no; then : -+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" -+ -+fi -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 -+$as_echo_n "checking for library containing socket... " >&6; } -+if ${ac_cv_search_socket+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_func_search_save_LIBS=$LIBS -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char socket (); -+int -+main () -+{ -+return socket (); -+ ; -+ return 0; -+} -+_ACEOF -+for ac_lib in '' socket; do -+ if test -z "$ac_lib"; then -+ ac_res="none required" -+ else -+ ac_res=-l$ac_lib -+ LIBS="-l$ac_lib $ac_func_search_save_LIBS" -+ fi -+ if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_search_socket=$ac_res -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext -+ if ${ac_cv_search_socket+:} false; then : -+ break -+fi -+done -+if ${ac_cv_search_socket+:} false; then : -+ -+else -+ ac_cv_search_socket=no -+fi -+rm conftest.$ac_ext -+LIBS=$ac_func_search_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 -+$as_echo "$ac_cv_search_socket" >&6; } -+ac_res=$ac_cv_search_socket -+if test "$ac_res" != no; then : -+ test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" -+ -+else -+ -+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for socket in -lsocket" >&5 -+$as_echo_n "checking for socket in -lsocket... " >&6; } -+if ${ac_cv_lib_socket_socket+:} false; then : -+ $as_echo_n "(cached) " >&6 -+else -+ ac_check_lib_save_LIBS=$LIBS -+LIBS="-lsocket -lnsl $LIBS" -+cat confdefs.h - <<_ACEOF >conftest.$ac_ext -+/* end confdefs.h. */ -+ -+/* Override any GCC internal prototype to avoid an error. -+ Use char because int might match the return type of a GCC -+ builtin and then its argument prototype would still apply. */ -+#ifdef __cplusplus -+extern "C" -+#endif -+char socket (); -+int -+main () -+{ -+return socket (); -+ ; -+ return 0; -+} -+_ACEOF -+if ac_fn_c_try_link "$LINENO"; then : -+ ac_cv_lib_socket_socket=yes -+else -+ ac_cv_lib_socket_socket=no -+fi -+rm -f core conftest.err conftest.$ac_objext \ -+ conftest$ac_exeext conftest.$ac_ext -+LIBS=$ac_check_lib_save_LIBS -+fi -+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_socket_socket" >&5 -+$as_echo "$ac_cv_lib_socket_socket" >&6; } -+if test "x$ac_cv_lib_socket_socket" = xyes; then : -+ LIBS="-lsocket -lnsl $LIBS" -+fi -+ -+fi -+ -+ -+ - - cat >>confdefs.h <<_ACEOF - #define CC "$CC" -diff -ru singular-4.1.0//libpolys/polys/Makefile.in b/libpolys/polys/Makefile.in ---- singular-4.1.0//libpolys/polys/Makefile.in 2017-04-18 16:55:19.000000000 +0200 -+++ b/libpolys/polys/Makefile.in 2018-02-02 12:57:15.961312272 +0100 -@@ -709,7 +709,7 @@ - p_Procs_FieldIndep_la_CPPFLAGS = -Dp_Procs_FieldIndep ${P_PROCS_CPPFLAGS_COMMON} - p_Procs_FieldQ_la_CPPFLAGS = -Dp_Procs_FieldQ ${P_PROCS_CPPFLAGS_COMMON} - p_Procs_FieldZp_la_CPPFLAGS = -Dp_Procs_FieldZp ${P_PROCS_CPPFLAGS_COMMON} --P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -Wl,-undefined -Wl,dynamic_lookup -flat_namespace $(SINGULAR_LDFLAGS) -+P_PROCS_MODULE_LDFLAGS = -shared -module -dynamic -export-dynamic -avoid-version -weak_reference_mismatches weak -undefined dynamic_lookup -flat_namespace - p_Procs_FieldGeneral_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} - p_Procs_FieldIndep_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} - p_Procs_FieldQ_la_LDFLAGS = ${P_PROCS_MODULE_LDFLAGS} From 426644f6d085e053fa0fcf3457702eac685b7cba Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 7 Jul 2018 00:37:37 +0200 Subject: [PATCH 159/284] Adapt to singular 4.1.1 API changes --- src/sage/libs/singular/decl.pxd | 4 ++-- .../polynomial/multi_polynomial_libsingular.pyx | 16 ++++++++-------- src/sage/rings/polynomial/plural.pyx | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 4b658c48073..84476a9f827 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -170,7 +170,7 @@ cdef extern from "singular/Singular/libsingular.h": int n_NumberOfParameters(const n_Procs_s* r) - ctypedef struct poly "polyrec": + ctypedef struct poly "spolyrec": poly *next number *coef unsigned long exp[1] @@ -632,7 +632,7 @@ cdef extern from "singular/Singular/libsingular.h": # divide monomial p by monomial q, p,q const - poly *pDivide(poly *p,poly *q) + poly *pMDivide(poly *p,poly *q) # return the i-th power of p; p destroyed, requires global ring diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 2a8d9ae0218..f2a11a8e150 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -190,8 +190,8 @@ from sage.libs.singular.decl cimport ( n_IsUnit, n_Invers, p_ISet, rChangeCurrRing, p_Copy, p_Init, p_SetCoeff, p_Setm, p_SetExp, p_Add_q, p_NSet, p_GetCoeff, p_Delete, p_GetExp, pNext, rRingVar, omAlloc0, omStrDup, - omFree, pDivide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, - pDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, + omFree, pMDivide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, + pMDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, p_IsUnit, p_Series, p_Head, idInit, fast_map_common_subexp, id_Delete, p_IsHomogeneous, p_Homogen, p_Totaldegree,pLDeg1_Totaldegree, singclap_pdivide, singclap_factorize, idLift, IDELEMS, On, Off, SW_USE_CHINREM_GCD, SW_USE_EZGCD, @@ -1693,8 +1693,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if not g._poly: raise ZeroDivisionError - if r!=currRing: rChangeCurrRing(r) # pDivide - res = pDivide(f._poly, g._poly) + if r!=currRing: rChangeCurrRing(r) # pMDivide + res = pMDivide(f._poly, g._poly) if coeff: if r.cf.type == n_unknown or r.cf.cfDivBy(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf): n = r.cf.cfDiv( p_GetCoeff(f._poly, r) , p_GetCoeff(g._poly, r), r.cf) @@ -1853,8 +1853,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): and (g) \ and g.parent() is self \ and p_LmDivisibleBy((g)._poly, m, r): - if r!=currRing: rChangeCurrRing(r) # pDivide - flt = pDivide(f._poly, (g)._poly) + if r!=currRing: rChangeCurrRing(r) # pMDivide + flt = pMDivide(f._poly, (g)._poly) #p_SetCoeff(flt, n_Div( p_GetCoeff(f._poly, r) , p_GetCoeff((g)._poly, r), r), r) p_SetCoeff(flt, n_Init(1, r), r) return new_MP(self,flt), g @@ -4055,10 +4055,10 @@ cdef class MPolynomial_libsingular(MPolynomial): if _right.is_monomial(): p = _self._poly quo = p_ISet(0,r) - if r != currRing: rChangeCurrRing(r) # pDivide + if r != currRing: rChangeCurrRing(r) # pMDivide while p: if p_DivisibleBy(_right._poly, p, r): - temp = pDivide(p, _right._poly) + temp = pMDivide(p, _right._poly) p_SetCoeff0(temp, n_Copy(p_GetCoeff(p, r), r), r) quo = p_Add_q(quo, temp, r) p = pNext(p) diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index d5439f7f081..ad20ebcca0e 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -998,7 +998,7 @@ cdef class NCPolynomialRing_plural(Ring): if not g._poly: raise ZeroDivisionError - res = pDivide(f._poly,g._poly) + res = pMDivide(f._poly,g._poly) if coeff: if (r.cf.type == n_unknown) or r.cf.cfDivBy(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf): n = r.cf.cfDiv( p_GetCoeff(f._poly, r) , p_GetCoeff(g._poly, r), r.cf) @@ -1193,7 +1193,7 @@ cdef class NCPolynomialRing_plural(Ring): if isinstance(g, NCPolynomial_plural) \ and (g) \ and p_LmDivisibleBy((g)._poly, m, r): - flt = pDivide(f._poly, (g)._poly) + flt = pMDivide(f._poly, (g)._poly) #p_SetCoeff(flt, n_Div( p_GetCoeff(f._poly, r) , p_GetCoeff((g)._poly, r), r), r) p_SetCoeff(flt, n_Init(1, r), r) return new_NCP(self,flt), g From d5fc2a4bc640856507d743c2f67ad454874700e7 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 7 Jul 2018 00:38:56 +0200 Subject: [PATCH 160/284] Don't call singclap_pdivide for integer coefficients, it's no longer supported --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index f2a11a8e150..774fecd2f95 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4888,7 +4888,7 @@ cdef class MPolynomial_libsingular(MPolynomial): if right.is_zero(): raise ZeroDivisionError - if not self._parent._base.is_field() and not is_IntegerRing(self._parent._base): + if not self._parent._base.is_field(): py_quo = self//right py_rem = self - right*py_quo return py_quo, py_rem From 2d8f4bc9e6acd82f386a92ad087580c9b9d1d1fe Mon Sep 17 00:00:00 2001 From: Ximin Luo Date: Mon, 9 Jul 2018 11:21:51 +0200 Subject: [PATCH 161/284] Pass --no-readline to R otherwise Pexpect hangs If anyone wants to dig deeper on *why* readline is messing things up: By adding some debug-printing code to sage/interfaces/r.py and pexpect/spawnbase.py one gets the following trace: . $ ./sage -c 'r([10.4,5.6,3.1,6.4,21.7])' Write to R: sage0 <- 10.400000000000000 Pexpect reads: '> ' ---- > ---- Write to R: options(prompt="__SAGE__R__PROMPT__> ",continue="__SAGE__R__PROMPT__> ", width=100,pager="cat",device="png") Pexpect reads: '1+961840267;\r\n[1] 961840268\r\n> ' ---- 1+961840267; [1] 961840268 > ---- [ rest of the trace is irrelevant ] . This then messes up the rest of the logic which causes a hang as Pexpect waits for the "__SAGE__R__PROMPT__> " string which is what R is *supposed* to do. The second number is different every time, but it's always "1+$n\r\n[1] $sum". . The hang occurs both with and without Sage's pexpect patch so I kept the patch out for now. (Note that if you want to apply the patch to test it yourself, with Debian's pexpect 4.2.0 you must first replace "self.__select" with "select_ignore_interrupts", everywhere that it occurs.) --- src/sage/interfaces/r.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index c624e7e3316..136cab2a839 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -332,7 +332,7 @@ def __init__(self, prompt = '> ', #default, later comes the change # This is the command that starts up your program - command = "R --vanilla --quiet", + command = "R --no-readline --vanilla --quiet", server=server, server_tmpdir=server_tmpdir, From 9e6ea7e3a2eb5c9d28159942b6d794a6e830a148 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Mon, 9 Jul 2018 11:34:16 +0200 Subject: [PATCH 162/284] Link to #25806 to explain the --no-readline switch --- src/sage/interfaces/r.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 136cab2a839..de9872b1d3c 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -332,6 +332,7 @@ def __init__(self, prompt = '> ', #default, later comes the change # This is the command that starts up your program + # See #25806 for the --no-readline switch which fixes hangs for some command = "R --no-readline --vanilla --quiet", server=server, From 2cec8ac704cddc2c65cbc439a7b07c7bcc953f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 11 Jul 2018 06:56:12 +0200 Subject: [PATCH 163/284] Replace do tag with big Currently, the do runners are insufficient to build Sage from scratch so there is no point in using that confusing tag anymore. --- .gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ae5bd00de5e..6bf63f4240c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -31,9 +31,11 @@ # * runners tagged as "gce" (Google Compute Engine) provide about 22GB of HDD, # a single core, 4GB of RAM. Since we are relying on OverlayFS, the disk # space is not sufficient to build sage from scratch. +# The shared runners are terminated after three hours. Currently, this is often +# insufficient to build sage from scratch. # If you want to provide your own runners, make sure to tag them as follows: -# * "do" (60GB of disk space are available) to make build-from-clean pass. +# * "big" (60GB of disk space are available) to make build-from-clean pass. image: docker:latest @@ -116,7 +118,7 @@ build-from-clean: except: [] tags: # 60 GB of HDD are available - - do + - big # This build takes several CPU hours. It is very unlikely that there are any # actual build errors for a tagged release but the (discounted) cloud # machines this is running on might be preempted during the long build time. From 624101115b9af149f8bf0d7cfe2f63d9ccf5201d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 11 Jul 2018 10:56:51 +0200 Subject: [PATCH 164/284] Fix namespacing for CircleCI --- .ci/update-env.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.ci/update-env.sh b/.ci/update-env.sh index 574595e48ac..470529d742a 100755 --- a/.ci/update-env.sh +++ b/.ci/update-env.sh @@ -36,8 +36,8 @@ export DOCKER_TAG=`echo $DOCKER_TAG | tr -d '[:space:]' | tr -c '[:alnum:]_.-' ' [[ -z "$DOCKER_TAG" ]] && export DOCKER_TAG=none [[ "$DOCKER_TAG" = "master" ]] && export DOCKER_TAG=latest -export DOCKER_IMAGE_CLI=${DOCKER_USER:-sagemath}/sagemath:$DOCKER_TAG -export DOCKER_IMAGE_DEV=${DOCKER_USER:-sagemath}/sagemath-dev:$DOCKER_TAG +export DOCKER_IMAGE_CLI=${DOCKER_NAMESPACE:-sagemath}/sagemath:$DOCKER_TAG +export DOCKER_IMAGE_DEV=${DOCKER_NAMESPACE:-sagemath}/sagemath-dev:$DOCKER_TAG # Seed the build cache with this image (set to source-clean to build from # scratch.) From ba08ff35c1aec6cd454219653e2394fd256f396f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 11 Jul 2018 10:58:56 +0200 Subject: [PATCH 165/284] reviewer's comments --- src/sage/combinat/diagram_algebras.py | 12 +++++------- src/sage/groups/perm_gps/symgp_conjugacy_class.py | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 04b0ec8b50e..7566dcc0115 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -275,14 +275,11 @@ def check(self): sage: pd2 = da.AbstractPartitionDiagram(pd, [[[1,2],[-1,-2]]]) # indirect doctest Traceback (most recent call last): ... - ValueError: {{[-1, -2], [1, 2]}} does not represent two rows of vertices of order 2 + TypeError: unhashable type: 'list' """ if self._base_diagram: - try: - tst = frozenset(e for B in self._base_diagram for e in B) - if tst != self.parent()._set: - raise TypeError - except TypeError: + tst = frozenset(e for B in self._base_diagram for e in B) + if tst != self.parent()._set: raise ValueError("{} does not represent two rows of vertices of order {}".format( self, self.parent().order)) @@ -2311,7 +2308,8 @@ def _element_constructor_(self, x): sage: a2 = 3*A2[[-2], [-1, 1, 2]] + 2*A2[[-2, -1, 1, 2]] + 2*A2[[-2, 1, 2], [-1]]; a2 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} - There is a natural embedding into partition algebras on more elements, by adding identity strands:: + There is a natural embedding into partition algebras on more elements, + by adding identity strands:: sage: A4 = PartitionAlgebra(4, x, R) sage: A4(a2) diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index 5e28f8d77d8..96489dfd5c7 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -360,9 +360,9 @@ def conjugacy_class_iterator(part, S=None): m = len(part) for s in SetPartitions(S, part): - blocks = map(Set, s) - firsts = [t[0] for t in blocks] - rests = [t[1:] for t in blocks] + its = [iter(t) for t in s] + firsts = [next(t) for t in its] + rests = [list(t) for t in its] iterator = tuple(itertools.permutations(r) for r in rests) for r in itertools.product(*iterator): yield [(firsts[i],) + r[i] for i in range(m)] From 95ce20f8b875cbb2cc8848b09120544a895612b5 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 11 Jul 2018 11:53:30 +0200 Subject: [PATCH 166/284] provide iterators which return lists of lists --- src/sage/combinat/set_partition.py | 37 ++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index a552e5d965d..b9a5d04fbad 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1970,6 +1970,24 @@ def __iter__(self): sage: SetPartitions(3).list() [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}] """ + for sp in self._fast_iterator(): + yield self.element_class(self, sp, check=False) + + def _fast_iterator(self): + """ + A fast iterator for the set partitions of the base set, which + returns lists of lists instead of set partitions types. + + EXAMPLES:: + + sage: list(SetPartitions([1,-1,x])._fast_iterator()) + [[[1, -1, x]], + [[1, -1], [x]], + [[1, x], [-1]], + [[1], [-1, x]], + [[1], [-1], [x]]] + + """ base_set = list(self.base_set()) def from_word(w): sp = [] @@ -1978,7 +1996,7 @@ def from_word(w): sp.append([i]) else: sp[b].append(i) - return self.element_class(self, sp, check=False) + return sp # Knuth, TAOCP 4A 7.2.1.5, Algorithm H N = len(base_set) @@ -2247,6 +2265,21 @@ def __iter__(self): {{1, 2}, {3, 4}}, {{1, 2, 3}, {4}}] """ + for sp in self._fast_iterator(): + yield self.element_class(self, sp, check=False) + + def _fast_iterator(self): + """ + A fast iterator for the set partitions of the base set into the + specified number of blocks, which returns lists of lists + instead of set partitions types. + + EXAMPLES:: + + sage: list(SetPartitions([1,-1,x], 2)._fast_iterator()) + [[[1, x], [-1]], [[1], [-1, x]], [[1, -1], [x]]] + + """ base_set = list(self.base_set()) def from_word(w): sp = [] @@ -2255,7 +2288,7 @@ def from_word(w): sp.append([i]) else: sp[b].append(i) - return self.element_class(self, sp, check=False) + return sp # Ruskey, Combinatorial Generation, Algorithm 4.23 n = len(base_set) From 6e267b63dd1ce92e7cbd6ef0cdf99ed7be66ec53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Thu, 12 Jul 2018 10:59:35 +0200 Subject: [PATCH 167/284] Fix namespacing for GitLab CI --- .ci/push-gitlab.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/push-gitlab.sh b/.ci/push-gitlab.sh index e4ceb30abcd..a1609995eed 100755 --- a/.ci/push-gitlab.sh +++ b/.ci/push-gitlab.sh @@ -21,5 +21,5 @@ set -ex # Note that "set -x" prints the $CI_BUILD_TOKEN here but GitLab removes it # automatically from the log output. docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY -docker tag ${DOCKER_USER:-sagemath}/$1:$DOCKER_TAG $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG +docker tag ${DOCKER_NAMESPACE:-sagemath}/$1:$DOCKER_TAG $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG docker push $CI_REGISTRY_IMAGE/$1:$DOCKER_TAG From d2e0e6e2114e3fa758ddd7a9295f03a5792421c3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 12 Jul 2018 12:59:04 +0200 Subject: [PATCH 168/284] inline a computation by reviewer's request --- src/sage/combinat/set_partition.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index b9a5d04fbad..a537292205a 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -546,9 +546,7 @@ def __init__(self, parent, s, check=True): {} """ self._latex_options = {} - sets = map(frozenset, s) - blocks = sorted(sets, key=min) - ClonableArray.__init__(self, parent, blocks, check=check) + ClonableArray.__init__(self, parent, sorted(map(frozenset, s), key=min), check=check) def check(self): """ From 9af692e7a124bbdd7acbf39a23ad2a55fbf68979 Mon Sep 17 00:00:00 2001 From: Peter Bruin Date: Mon, 16 Jul 2018 11:42:01 +0200 Subject: [PATCH 169/284] Trac 6106: add examples --- src/sage/quadratic_forms/binary_qf.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index 2a501b4c6a7..e6b2521c540 100644 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -728,7 +728,7 @@ def reduced_form(self, transformation=False, algorithm="default"): sage: b.is_reduced() True - An example of reducing an indefinite form:: + Examples of reducing indefinite forms:: sage: f = BinaryQF(1, 0, -3) sage: f.is_reduced() @@ -738,6 +738,22 @@ def reduced_form(self, transformation=False, algorithm="default"): sage: g.is_reduced() True + sage: BinaryQF(1, 9, 4).reduced_form(transformation=True) + ( + [ 0 -1] + 4*x^2 + 7*x*y - y^2, [ 1 2] + ) + sage: BinaryQF(3, 7, -2).reduced_form(transformation=True) + ( + [1 0] + 3*x^2 + 7*x*y - 2*y^2, [0 1] + ) + sage: BinaryQF(-6, 6, -1).reduced_form(transformation=True) + ( + [ 0 -1] + -x^2 + 2*x*y + 2*y^2, [ 1 -4] + ) + """ if self.is_reduced(): if transformation: @@ -992,7 +1008,8 @@ def is_reduced(self): r""" Return if ``self`` is reduced. - Let `f = a x^2 + b xy + c y^2` be a binary quadratic form. + Let `f = a x^2 + b xy + c y^2` be a binary quadratic form of + discrimininant `D`. - If `f` is positive definite (`D < 0` and `a > 0`), then `f` is reduced if and only if `|b|\leq a \leq c`, and `b\geq 0` @@ -1025,11 +1042,15 @@ def is_reduced(self): sage: Q.is_reduced() True - An example using an indefinite form:: + Examples using indefinite forms:: sage: f = BinaryQF(-1, 2, 2) sage: f.is_reduced() True + sage: BinaryQF(1, 9, 4).is_reduced() + False + sage: BinaryQF(1, 5, -1).is_reduced() + True """ D = self.discriminant() From b12cd9ffba2c3e89c06f1051122a3bd3bd69167f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 17 Jul 2018 12:16:13 +0200 Subject: [PATCH 170/284] Do not use broken docker version on CircleCI CircleCI currently uses an old docker version. Let's use the one that we used on GitLab CI as we have done more extensive testing with that one. Strangely, this increases the size of the resulting docker image from 611MB to 635MB on CircleCI (same as on GitLab.) I have not checked what's the difference between the two but I guess it's safer to go with the later version. --- .circleci/config.yml | 3 ++- .gitlab-ci.yml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index bb28ca1b160..635ea14f16e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,8 @@ jobs: steps: - run: apk --update add git openssh - checkout - - setup_remote_docker + - setup_remote_docker: + version: 18.05.0-ce - run: &build # The docker commands sometimes take a while to produce output no_output_timeout: 30m diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6bf63f4240c..0319ba1c410 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -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:dind +- docker:18.05.0-ce-dind # Build Sage and its documentation. # The build starts from the build artifacts of DEFAULT_ARTIFACT_BASE which is From 220d948c93048bab2a5a909b377f012374c01b06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 17 Jul 2018 13:44:04 +0200 Subject: [PATCH 171/284] Warn about aufs as it creates 4GB sagemath-dev images --- docker/Dockerfile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index 18df023aa23..8b1b5110170 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -245,6 +245,11 @@ RUN if [ x"$ARTIFACT_BASE" != x"source-clean" ]; then \ ################################################################################ FROM $ARTIFACT_BASE as sagemath-dev ARG SAGE_ROOT=/home/sage/sage +# If docker is backed by aufs, then the following command adds the size of +# ARTIFACT_BASE to the image size. As of mid 2018 this is notably the case with +# the docker instances provided by setup_remote_docker on CircleCI. As a +# result, the sagemath-dev images that are "build-from-latest" are twice as big +# as the ones that are build on GitLab. COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT ARG ARTIFACT_BASE=source-clean # Apply the patch from sagemath-dev-patch if we created one. From c40bb81d324fc08f1ea6179626e0a5ddcddd5fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 17 Jul 2018 13:52:05 +0200 Subject: [PATCH 172/284] Add reference to known chown bug in docker/aufs --- docker/Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 8b1b5110170..4c8bc553959 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -249,7 +249,8 @@ ARG SAGE_ROOT=/home/sage/sage # ARTIFACT_BASE to the image size. As of mid 2018 this is notably the case with # the docker instances provided by setup_remote_docker on CircleCI. As a # result, the sagemath-dev images that are "build-from-latest" are twice as big -# as the ones that are build on GitLab. +# as the ones that are build on GitLab: +# https://github.com/moby/moby/issues/6119#issuecomment-268870519 COPY --chown=sage:sage --from=sagemath-dev-patch $SAGE_ROOT $SAGE_ROOT ARG ARTIFACT_BASE=source-clean # Apply the patch from sagemath-dev-patch if we created one. From 4763da19a9ea0be849ee3640fc39747d725cb5a1 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sat, 21 Jul 2018 11:31:02 +0200 Subject: [PATCH 173/284] Fix lcm of polynomials with integer coefficients and add test --- .../rings/polynomial/multi_polynomial_libsingular.pyx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 774fecd2f95..bb5aca6623d 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4794,6 +4794,8 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: p = -x*y + x*z + 54*x - 2 sage: (5*p^2).lcm(3*p) == 15*p^2 True + sage: lcm(2*x,2*x*y) + 2*x*y """ cdef ring *_ring = self._parent_ring cdef poly *ret @@ -4805,6 +4807,12 @@ cdef class MPolynomial_libsingular(MPolynomial): if _ring.cf.type != n_unknown: if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m : raise TypeError("LCM over non-integral domains not available.") + if _ring.cf.type == n_Z: + f_content = self.content() + g_content = g.content() + f_primitivepart = self / f_content + g_primitivepart = g / g_content + return f_primitivepart.change_ring(RationalField()).lcm(g_primitivepart.change_ring(RationalField())) * f_content.lcm(g_content) if self._parent is not g._parent: _g = self._parent._coerce_c(g) From 544f67d45ce00142d99cbca7ca1ef39df95d3551 Mon Sep 17 00:00:00 2001 From: Timo Kaufmann Date: Fri, 20 Jul 2018 16:58:48 +0200 Subject: [PATCH 174/284] =?UTF-8?q?Revert=20"Trac=20#25313:=20Speed=20up?= =?UTF-8?q?=20exact=20division=20in=20=E2=84=A4[x,y,...]"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5ef19ad3901910976878348ef2ec76637420010b, reversing changes made to 1c41c1d6cadf5bb863d5c0d125300a153f232fa5. --- .../polynomial/multi_polynomial_libsingular.pyx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index bb5aca6623d..92d6ed59fb1 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4023,16 +4023,6 @@ cdef class MPolynomial_libsingular(MPolynomial): Traceback (most recent call last): ... NotImplementedError: Division of multivariate polynomials over non fields by non-monomials not implemented. - - TESTS:: - - sage: P. = ZZ[] - sage: p = 3*(-x^8*y^2 - x*y^9 + 6*x^8*y + 17*x^2*y^6 - x^3*y^2) - sage: q = 7*(x^2 + x*y + y^2 + 1) - sage: p*q//q == p - True - sage: p*q//p == q - True """ cdef MPolynomialRing_libsingular parent = self._parent cdef ring *r = self._parent_ring @@ -4052,6 +4042,11 @@ cdef class MPolynomial_libsingular(MPolynomial): _right = right if r.cf.type != n_unknown: + if r.cf.type == n_Z: + P = parent.change_ring(RationalField()) + f = P(self)//P(right) + CM = list(f) + return parent(sum([c.floor()*m for c,m in CM])) if _right.is_monomial(): p = _self._poly quo = p_ISet(0,r) From 2cd8487ff5a7f74c6c75659b44be5f48d27639e6 Mon Sep 17 00:00:00 2001 From: Timo Kaufmann Date: Fri, 20 Jul 2018 18:32:20 +0200 Subject: [PATCH 175/284] singular: update doctests --- src/sage/rings/polynomial/multi_polynomial_ideal.py | 1 + .../rings/polynomial/multi_polynomial_libsingular.pyx | 2 +- .../rings/polynomial/polynomial_singular_interface.py | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 37dc45ce640..8bd53060821 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2199,6 +2199,7 @@ def variety(self, ring=None): sage: I.variety() verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: computations in the complex field are inexact; variety may be computed partially or incorrectly. verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: falling back to very slow toy implementation. + ... [{y: -0.86602540378443... - 0.500000000000000*I}, {y: -0.86602540378443... + 0.500000000000000*I}, {y: 0.86602540378443... - 0.500000000000000*I}, diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 92d6ed59fb1..619e7f04c6c 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4869,7 +4869,7 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: f.quo_rem(y) (2*x^2, x + 1) sage: f.quo_rem(3*x) - (2*x*y + 1, -4*x^2*y - 2*x + 1) + (0, 2*x^2*y + x + 1) TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_singular_interface.py b/src/sage/rings/polynomial/polynomial_singular_interface.py index 9331169f8bc..f753610fd3f 100644 --- a/src/sage/rings/polynomial/polynomial_singular_interface.py +++ b/src/sage/rings/polynomial/polynomial_singular_interface.py @@ -81,8 +81,8 @@ def _singular_(self, singular=singular): sage: R. = PolynomialRing(CC) sage: singular(R) polynomial ring, over a field, global ordering - // coefficients: float[I](complex:15 digits, additional 0 digits)/(I^2+1) - // number of vars : 2 + // coefficients: real[I](complex:15 digits, additional 0 digits)/(I^2+1) + // number of vars : 2 // block 1 : ordering dp // : names x y // block 2 : ordering C @@ -90,8 +90,8 @@ def _singular_(self, singular=singular): sage: R. = PolynomialRing(RealField(100)) sage: singular(R) polynomial ring, over a field, global ordering - // coefficients: float - // number of vars : 2 + // coefficients: Float() + // number of vars : 2 // block 1 : ordering dp // : names x y // block 2 : ordering C From d9dc104b4ff3d3cc34fa8ec9483f83fce033f627 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 22 Jul 2018 00:54:38 +0200 Subject: [PATCH 176/284] Make Singular error parser more granular --- src/sage/interfaces/singular.py | 2 +- src/sage/rings/polynomial/multi_polynomial_ideal.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 9d65c9fa6ce..031172eb0ce 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -654,7 +654,7 @@ def eval(self, x, allow_semicolon=True, strip=True, **kwds): s = Expect.eval(self, x, **kwds) - if s.find("error") != -1 or s.find("Segment fault") != -1: + if s.find("error occurred") != -1 or s.find("Segment fault") != -1: raise SingularError('Singular error:\n%s'%s) if get_verbose() > 0: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 8bd53060821..37dc45ce640 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -2199,7 +2199,6 @@ def variety(self, ring=None): sage: I.variety() verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: computations in the complex field are inexact; variety may be computed partially or incorrectly. verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: falling back to very slow toy implementation. - ... [{y: -0.86602540378443... - 0.500000000000000*I}, {y: -0.86602540378443... + 0.500000000000000*I}, {y: 0.86602540378443... - 0.500000000000000*I}, From ab851b1a2485c98b9f281e5bb5ea294e84f39040 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Sun, 22 Jul 2018 01:02:23 +0200 Subject: [PATCH 177/284] Update some tests output --- src/sage/interfaces/singular.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 031172eb0ce..f7972f55a6a 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1079,7 +1079,7 @@ def set_ring(self, R): sage: S = singular.ring('real', '(a,b)', 'lp') sage: singular.current_ring() polynomial ring, over a field, global ordering - // coefficients: float + // coefficients: Float() // number of vars : 2 // block 1 : ordering lp // : names a b @@ -1157,7 +1157,7 @@ def _tab_completion(self): sage: singular._tab_completion() ['exteriorPower', ... - 'flintZ'] + 'crossprod'] """ p = re.compile("// *([a-z0-9A-Z_]*).*") #compiles regular expression proclist = self.eval("listvar(proc)").splitlines() @@ -1183,7 +1183,7 @@ def version(self): EXAMPLES:: sage: singular.version() - "Singular ... version 4.1.0 ... + "Singular ... version 4.1.1 ... """ return singular_version() @@ -1992,7 +1992,7 @@ def set_ring(self): sage: S = singular.ring('real', '(a,b)', 'lp') sage: singular.current_ring() polynomial ring, over a field, global ordering - // coefficients: float + // coefficients: Float() // number of vars : 2 // block 1 : ordering lp // : names a b @@ -2072,7 +2072,7 @@ def _tab_completion(self): sage: R._tab_completion() ['exteriorPower', ... - 'flintZ'] + 'crossprod'] """ return self.parent()._tab_completion() @@ -2358,7 +2358,7 @@ def singular_version(): EXAMPLES:: sage: singular.version() - "Singular ... version 4.1.0 ... + "Singular ... version 4.1.1 ... """ return singular.eval('system("--version");') From 70cbd070487e8f1a3798015b665facea7d7492af Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 23 Jul 2018 17:01:57 +0200 Subject: [PATCH 178/284] Swap the arguments to SageDocTestParser Most examples of this class's usage don't really make use of the long argument in the first place, so having it first is a bit confusing. Putting it second also makes the API more amenable to adding additional arguments of a similar purpose. --- src/sage/doctest/parsing.py | 20 ++++++++++---------- src/sage/doctest/sources.py | 5 +++-- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 04212f20396..f442a9ff92d 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -611,17 +611,17 @@ class SageDocTestParser(doctest.DocTestParser): A version of the standard doctest parser which handles Sage's custom options and tolerances in floating point arithmetic. """ - def __init__(self, long=False, optional_tags=()): + def __init__(self, optional_tags=(), long=False): r""" INPUT: - - ``long`` -- boolean, whether to run doctests marked as taking a long time. - ``optional_tags`` -- a list or tuple of strings. + - ``long`` -- boolean, whether to run doctests marked as taking a long time. EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser - sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) + sage: DTP = SageDocTestParser(('sage','magma','guava')) sage: ex = DTP.parse("sage: 2 + 2\n")[1] sage: ex.sage_source '2 + 2\n' @@ -653,8 +653,8 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser - sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) - sage: DTP2 = SageDocTestParser(False, ('sage','magma','guava')) + sage: DTP = SageDocTestParser(('sage','magma','guava'), True) + sage: DTP2 = SageDocTestParser(('sage','magma','guava'), False) sage: DTP == DTP2 False """ @@ -669,8 +669,8 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser - sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) - sage: DTP2 = SageDocTestParser(False, ('sage','magma','guava')) + sage: DTP = SageDocTestParser(('sage','magma','guava'), True) + sage: DTP2 = SageDocTestParser(('sage','magma','guava'), False) sage: DTP != DTP2 True """ @@ -696,7 +696,7 @@ def parse(self, string, *args): EXAMPLES:: sage: from sage.doctest.parsing import SageDocTestParser - sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) + sage: DTP = SageDocTestParser(('sage','magma','guava')) sage: example = 'Explanatory text::\n\n sage: E = magma("EllipticCurve([1, 1, 1, -10, -10])") # optional: magma\n\nLater text' sage: parsed = DTP.parse(example) sage: parsed[0] @@ -710,7 +710,7 @@ def parse(self, string, *args): optional argument, the corresponding examples will just be removed:: - sage: DTP2 = SageDocTestParser(True, ('sage',)) + sage: DTP2 = SageDocTestParser(('sage',)) sage: parsed2 = DTP2.parse(example) sage: parsed2 ['Explanatory text::\n\n', '\nLater text'] @@ -816,7 +816,7 @@ class SageOutputChecker(doctest.OutputChecker): sage: from sage.doctest.parsing import SageOutputChecker, MarkedOutput, SageDocTestParser sage: import doctest sage: optflag = doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS - sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) + sage: DTP = SageDocTestParser(('sage','magma','guava')) sage: OC = SageOutputChecker() sage: example2 = 'sage: gamma(1.6) # tol 2.0e-11\n0.893515349287690' sage: ex = DTP.parse(example2)[1] diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index ac74b4cfb4e..d4b149d1891 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -272,7 +272,8 @@ def _create_doctests(self, namespace, tab_okay=None): tab_okay = isinstance(self,TexSource) self._init() self.line_shift = 0 - self.parser = SageDocTestParser(self.options.long, self.options.optional) + self.parser = SageDocTestParser(self.options.optional, + self.options.long) self.linking = False doctests = [] in_docstring = False @@ -1509,7 +1510,7 @@ def parse_docstring(self, docstring, namespace, start): sage: from sage.doctest.util import NestedName sage: filename = "sage_doc.rst" sage: FDS = FileDocTestSource(filename,DocTestDefaults()) - sage: FDS.parser = SageDocTestParser(False, set(['sage'])) + sage: FDS.parser = SageDocTestParser(set(['sage'])) sage: FDS.qualified_name = NestedName('sage_doc') sage: s = "Some text::\n\n def example_python_function(a, \ ....: b):\n '''\n Brief description \ From 4e7308bb5dd4a38a7e37fcb8a75e6bd9426d976a Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 23 Jul 2018 17:31:41 +0200 Subject: [PATCH 179/284] Add two enhancements to how memory limits and tests that eat up available memory are handled: * Add a --memlimit flag to sage-runtests; this sets the max amount of virtual memory that may be allocated for a given test worker. This still defaults to 3300 MB as before, but the memory limit may also be disabled by passing --memlimit=0 (or --memlimit=-N). * Adds a '# high mem' tag that can be added to individual test cases, indicting that they eat up a large amount of memory, if not all available. These tests are *only* run if a finite memory limit is set on the tests. If --memlimit=0, for example, then high mem tests are skipped. * memlimit=0 is enforced for platforms that don't properly set setrlimit(RLIMIT_AS); this includes Cygwin and OSX --- src/bin/sage-runtests | 29 +++++++++++++++++++++++------ src/sage/doctest/control.py | 3 ++- src/sage/doctest/parsing.py | 17 ++++++++++++++--- src/sage/doctest/reporting.py | 7 ++++++- src/sage/doctest/sources.py | 3 ++- 5 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 754081f3015..2045c5292f4 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -28,6 +28,11 @@ if __name__ == "__main__": callback=optional_argument, callback_args=(int, 0), nargs=0, metavar="N", help="tests in parallel using N threads with 0 interpreted as max(2, min(8, cpu_count()))") parser.add_option("-T", "--timeout", type=int, default=-1, help="timeout (in seconds) for doctesting one file, 0 for no timeout") + parser.add_option("-m", "--memlimit", type=int, default=3300, + help='maximum virtual memory to allow each test ' + 'process, in megabytes; no limit if zero or less, ' + 'but tests tagged "high mem" are 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") @@ -95,27 +100,39 @@ if __name__ == "__main__": parser.print_help() sys.exit(2) - # Ensure that all doctests can be run with virtual memory limited - # to 3300 MiB. We must set this limit before starting Sage. Note - # that this is a per-process limit, so we do not need to worry about - # running multiple doctest processes in parallel. It is in + # Ensure that all doctests can be run with virtual memory limited to 3300 + # MiB (or a user-provided value). We must set this limit before starting + # Sage. Note that this is a per-process limit, so we do not need to worry + # about running multiple doctest processes in parallel. It is in # particular doctests in src/sage/schemes/elliptic_curves/heegner.py # which need this much memory. - memlimit = 3300 << 20 + memlimit = options.memlimit << 20 + # Python's resource module only supports limits up to sys.maxsize, # even if the OS does support higher limits. - if memlimit <= sys.maxsize: + if memlimit > 0 and memlimit <= sys.maxsize: import resource lim, hard = resource.getrlimit(resource.RLIMIT_AS) if lim == resource.RLIM_INFINITY or lim > memlimit: try: resource.setrlimit(resource.RLIMIT_AS, (memlimit, hard)) except ValueError: + options.memlimit = -1 if sys.platform != 'cygwin': # RLIMIT_AS is not currently supported on Cygwin so # this will probably fail there: # https://trac.sagemath.org/ticket/23979 raise + else: + if resource.RLIMIT_AS == getattr(resource, 'RLIMIT_RSS', None): + # On some platforms (e.g. OSX) RLIMIT_AS is just an alias + # for RLIMIT_RSS (see + # https://trac.sagemath.org/ticket/24190) + # In this case we still call setrlimit, but then disable + # high mem tests since we don't want such tests to actually + # cause us to run out of physical memory, leading to system + # instability (as opposed to virtual memory allocs failing) + options.memlimit = -1 # Limit the number of threads to 2 to save system resources. # See Trac #23713 and #23892 diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 4236fd05e09..e4be46612fc 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -82,6 +82,7 @@ def __init__(self, **kwds): self.nthreads = 1 self.serial = False self.timeout = -1 + self.memlimit = 0 self.all = False self.logfile = None self.sagenb = False @@ -1001,7 +1002,7 @@ def _assemble_cmd(self): for o in ("all", "sagenb", "long", "force_lib", "verbose", "failed", "new"): if o in opt: cmd += "--%s "%o - for o in ("timeout", "randorder", "stats_path"): + for o in ("timeout", "memlimit", "randorder", "stats_path"): if o in opt: cmd += "--%s=%s "%(o, opt[o]) if "optional" in opt: diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index f442a9ff92d..7185c43bcc2 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -39,7 +39,7 @@ from .external import available_software float_regex = re.compile('\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)*))') +optional_regex = re.compile(r'(py2|py3|long time|high mem|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) random_marker = re.compile('.*random', re.I) @@ -267,6 +267,7 @@ def parse_optional_tags(string): set that occur in a comment on the first line of the input string. - 'long time' + - 'high mem' - 'not implemented' - 'not tested' - 'known bug' @@ -611,12 +612,15 @@ class SageDocTestParser(doctest.DocTestParser): A version of the standard doctest parser which handles Sage's custom options and tolerances in floating point arithmetic. """ - def __init__(self, optional_tags=(), long=False): + def __init__(self, optional_tags=(), long=False, highmem=False): r""" INPUT: - ``optional_tags`` -- a list or tuple of strings. - - ``long`` -- boolean, whether to run doctests marked as taking a long time. + - ``long`` -- boolean, whether to run doctests marked as taking a + long time. + - ``highmem`` -- boolean, whether to run doctests marked as using a + lot of memory. EXAMPLES:: @@ -634,6 +638,7 @@ def __init__(self, optional_tags=(), long=False): sage: TestSuite(DTP).run() """ self.long = long + self.highmem = highmem self.optionals = collections.defaultdict(int) # record skipped optional tests if optional_tags is True: # run all optional tests self.optional_tags = True @@ -788,6 +793,12 @@ def parse(self, string, *args): else: continue + if 'high mem' in optional_tags: + if self.highmem: + optional_tags.remove('high mem') + else: + continue + if self.optional_tags is not True: extra = optional_tags - self.optional_tags # set difference if extra: diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 957be443947..d80f2b56c2f 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -178,6 +178,7 @@ def report_head(self, source): cmd = "sage -t" if self.controller.options.long: cmd += " --long" + warnlong = self.controller.options.warn_long if warnlong is not None: cmd += " --warn-long" @@ -479,12 +480,16 @@ def report(self, source, timeout, return_code, results, output, pid=None): if self.controller.options.optional is not True: # if True we test all optional tags untested = 0 # Report not tested/implemented tests at the end seen_other = False - for tag in sorted(optionals.keys()): + for tag in sorted(optionals): nskipped = optionals[tag] if tag == "long time": if not self.controller.options.long: seen_other = True log(" %s not run"%(count_noun(nskipped, "long test"))) + elif tag == "high_mem": + if self.controller.options.memlimit <= 0: + seen_other = True + log(" %s not run"%(count_noun(nskipped, "high mem"))) elif tag in ("not tested", "not implemented"): untested += nskipped elif not self.have_optional_tag(tag): diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index d4b149d1891..54d67734796 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -273,7 +273,8 @@ def _create_doctests(self, namespace, tab_okay=None): self._init() self.line_shift = 0 self.parser = SageDocTestParser(self.options.optional, - self.options.long) + self.options.long, + self.options.memlimit > 0) self.linking = False doctests = [] in_docstring = False From 20a121c668843f7970568c5b1a987ec70da4a994 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 23 Jul 2018 17:32:18 +0200 Subject: [PATCH 180/284] Instead of explicitly checking about RLIMIT_AS (this check works on OSX but doesn't make sense for Cygwin), use the new 'high mem' tag in this test. --- src/sage/matrix/matrix_mod2_dense.pyx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index b5c10c13115..e99b86536b2 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -162,13 +162,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse Large matrices fail gracefully:: - sage: import resource - sage: if resource.RLIMIT_AS == getattr(resource, 'RLIMIT_RSS', None): - ....: # Skip this test if RLIMIT_AS is not properly - ....: # supported like on OS X, see Trac #24190 - ....: raise RuntimeError("matrix allocation failed") - ....: else: # Real test - ....: MatrixSpace(GF(2), 2^30)(1) + sage: MatrixSpace(GF(2), 2^30)(1) # high mem Traceback (most recent call last): ... RuntimeError: matrix allocation failed From 921cc310908e1880884af556dc9452c2b4ba8bc8 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 24 Jul 2018 18:45:26 +0200 Subject: [PATCH 181/284] Fix real base ring detection Singular prints it as 'Float()' instead of 'real' now --- src/sage/interfaces/singular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index f7972f55a6a..3815951a97c 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1562,7 +1562,7 @@ def sage_global_ring(self): elif charstr[0] in ['0', 'QQ']: from sage.all import QQ br = QQ - elif charstr[0]=='real': + elif charstr[0].startswith('Float'): from sage.all import RealField, ceil, log prec = singular.eval('ringlist(basering)[1][2][1]') br = RealField(ceil((ZZ(prec)+1)/log(2,10))) From 0f55b763c775bb0d652b53d50dc26f11e9f472a0 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 24 Jul 2018 19:16:20 +0200 Subject: [PATCH 182/284] More real -> Float porting --- src/sage/interfaces/singular.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 3815951a97c..04479200cd1 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1750,7 +1750,7 @@ def sage_poly(self, R=None, kcache=None): # Singular 4 puts parentheses around floats and sign outside them charstr = self.parent().eval('charstr(basering)').split(',',1) - if charstr[0] in ['real', 'complex']: + if charstr[0]=='complex' or charstr[0].startswith('Float'): for i in range(coeff_start, 2 * coeff_start): singular_poly_list[i] = singular_poly_list[i].replace('(','').replace(')','') From aea054407a41a517070eb06e8efcd02d6356269f Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Tue, 24 Jul 2018 19:38:55 +0200 Subject: [PATCH 183/284] Don't check for exact Singular version Should improve testing future Singular upgrades on distributions --- src/sage/interfaces/singular.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 04479200cd1..a028bbe719e 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -1183,7 +1183,7 @@ def version(self): EXAMPLES:: sage: singular.version() - "Singular ... version 4.1.1 ... + "Singular ... version 4... """ return singular_version() @@ -2358,7 +2358,7 @@ def singular_version(): EXAMPLES:: sage: singular.version() - "Singular ... version 4.1.1 ... + "Singular ... version 4... """ return singular.eval('system("--version");') From 4e52b5e3ca6bbb34666c25decbf9bab3e2b46e82 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 25 Jul 2018 16:17:38 +0200 Subject: [PATCH 184/284] Use p_Divide for lcm as suggested by upstream See https://www.singular.uni-kl.de/forum/viewtopic.php?f=10&t=2768 --- src/sage/libs/singular/decl.pxd | 4 ++++ .../polynomial/multi_polynomial_libsingular.pyx | 12 ++---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 84476a9f827..d8637404195 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -630,6 +630,10 @@ cdef extern from "singular/Singular/libsingular.h": # return p*q, destroys p and q poly *p_Mult_q(poly *p, poly *q, ring *r) + # polynomial division, ignoring the rest + # via singclap_pdivide resp. idLift, destroys p,q + poly *p_Divide(poly *p, poly *q, ring *r) + # divide monomial p by monomial q, p,q const poly *pMDivide(poly *p,poly *q) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 619e7f04c6c..aeff53af6cc 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -190,7 +190,7 @@ from sage.libs.singular.decl cimport ( n_IsUnit, n_Invers, p_ISet, rChangeCurrRing, p_Copy, p_Init, p_SetCoeff, p_Setm, p_SetExp, p_Add_q, p_NSet, p_GetCoeff, p_Delete, p_GetExp, pNext, rRingVar, omAlloc0, omStrDup, - omFree, pMDivide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, + omFree, pMDivide, p_Divide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, pMDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, p_IsUnit, p_Series, p_Head, idInit, fast_map_common_subexp, id_Delete, p_IsHomogeneous, p_Homogen, p_Totaldegree,pLDeg1_Totaldegree, singclap_pdivide, singclap_factorize, @@ -4802,12 +4802,6 @@ cdef class MPolynomial_libsingular(MPolynomial): if _ring.cf.type != n_unknown: if _ring.cf.type == n_Znm or _ring.cf.type == n_Zn or _ring.cf.type == n_Z2m : raise TypeError("LCM over non-integral domains not available.") - if _ring.cf.type == n_Z: - f_content = self.content() - g_content = g.content() - f_primitivepart = self / f_content - g_primitivepart = g / g_content - return f_primitivepart.change_ring(RationalField()).lcm(g_primitivepart.change_ring(RationalField())) * f_content.lcm(g_content) if self._parent is not g._parent: _g = self._parent._coerce_c(g) @@ -4824,9 +4818,7 @@ cdef class MPolynomial_libsingular(MPolynomial): if _ring!=currRing: rChangeCurrRing(_ring) # singclap_gcd gcd = singclap_gcd(p_Copy(self._poly, _ring), p_Copy(_g._poly, _ring), _ring ) prod = pp_Mult_qq(self._poly, _g._poly, _ring) - ret = singclap_pdivide(prod , gcd , _ring) - p_Delete(&prod, _ring) - p_Delete(&gcd, _ring) + ret = p_Divide(prod, gcd, _ring) if count >= 20: sig_off() return new_MP(self._parent, ret) From 50b9ae2fd233c30860e1cbb3e63a26f2cc10560a Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 25 Jul 2018 16:59:46 +0200 Subject: [PATCH 185/284] Backport patch to move singular's NTL handling out of libsingular --- build/pkgs/singular/package-version.txt | 2 +- .../patches/singular-ntl-error-handler.patch | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/singular/patches/singular-ntl-error-handler.patch diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index c86d8809c87..dc3219d462a 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -4.1.1p2 +4.1.1p2.p0 diff --git a/build/pkgs/singular/patches/singular-ntl-error-handler.patch b/build/pkgs/singular/patches/singular-ntl-error-handler.patch new file mode 100644 index 00000000000..2a3dac018bf --- /dev/null +++ b/build/pkgs/singular/patches/singular-ntl-error-handler.patch @@ -0,0 +1,71 @@ +From 502cf86d0bb2a96715be6764774b64a69c1ca34c Mon Sep 17 00:00:00 2001 +From: Hans Schoenemann +Date: Wed, 25 Jul 2018 11:03:32 +0200 +Subject: [PATCH] move error handler for factory,NTL to the non-libSingular part + +(see forum: "NTL error handling", for SAGE) + +diff --git a/Singular/cntrlc.cc b/Singular/cntrlc.cc +index 622495490c..874a5deb79 100644 +--- a/Singular/cntrlc.cc ++++ b/Singular/cntrlc.cc +@@ -20,6 +20,14 @@ + #include "Singular/links/silink.h" + #include "Singular/links/ssiLink.h" + ++#ifdef HAVE_NTL ++#include ++#include ++#ifdef NTL_CLIENT ++NTL_CLIENT ++#endif ++#endif ++ + /* undef, if you don't want GDB to come up on error */ + + #define CALL_GDB +@@ -549,11 +557,20 @@ static void stack_trace (char *const*args) + + # endif /* !__OPTIMIZE__ */ + +-/*2 +-* init signal handlers +-*/ ++/// init signal handlers and error handling for libraries: NTL, factory + void init_signals() + { ++// NTL error handling (>= 9.3.0) ---------------------------------------- ++#ifdef HAVE_NTL ++#if (((NTL_MAJOR_VERSION==9)&&(NTL_MINOR_VERSION>=3))||(NTL_MAJOR_VERSION>=10)) ++ ErrorMsgCallback=WerrorS; ++ ErrorCallback=HALT; ++#endif ++#endif ++// factory error handling: ----------------------------------------------- ++ factoryError=WerrorS; ++ ++// signal handler ------------------------------------------------------- + #ifdef SIGSEGV + si_set_signal(SIGSEGV,(si_hdl_typ)sigsegv_handler); + #endif +diff --git a/Singular/misc_ip.cc b/Singular/misc_ip.cc +index 49eddaae6f..3d5775edd7 100644 +--- a/Singular/misc_ip.cc ++++ b/Singular/misc_ip.cc +@@ -1316,16 +1316,6 @@ static BOOLEAN iiCrossProd(leftv res, leftv args) + On(SW_USE_EZGCD_P); + On(SW_USE_QGCD); + Off(SW_USE_NTL_SORT); // may be changed by an command line option +- factoryError=WerrorS; +- +-// NTL error handling (>= 9.3.0) +-#ifdef HAVE_NTL +-#if (((NTL_MAJOR_VERSION==9)&&(NTL_MINOR_VERSION>=3))||(NTL_MAJOR_VERSION>=10)) +- ErrorMsgCallback=WerrorS; +- ErrorCallback=HALT; +-#endif +-#endif +- + // memory initialization: ----------------------------------------------- + om_Opts.OutOfMemoryFunc = omSingOutOfMemoryFunc; + #ifndef OM_NDEBUG From 7b56ef2d85a79801916e7a8e831cb4a0f4997c74 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 25 Jul 2018 22:43:33 +0200 Subject: [PATCH 186/284] Document patch --- build/pkgs/singular/SPKG.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/pkgs/singular/SPKG.txt b/build/pkgs/singular/SPKG.txt index 6dc588577ba..9933e1a95aa 100644 --- a/build/pkgs/singular/SPKG.txt +++ b/build/pkgs/singular/SPKG.txt @@ -34,3 +34,8 @@ Other notes: 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. + +== Patches == + +singular-ntl-error-handler.patch: move NTL error handling out of libsingular so it doesn't conflict with Sage's + Backported from https://github.com/Singular/Sources/commit/502cf86d0bb2a96715be6764774b64a69c1ca34c From 930ba2eb1cacaf4e1e43733feb560dd105d531be Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Wed, 25 Jul 2018 22:45:15 +0200 Subject: [PATCH 187/284] Remove duplicate cimport --- src/sage/rings/polynomial/multi_polynomial_libsingular.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index aeff53af6cc..f0a174fcf04 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -190,7 +190,7 @@ from sage.libs.singular.decl cimport ( n_IsUnit, n_Invers, p_ISet, rChangeCurrRing, p_Copy, p_Init, p_SetCoeff, p_Setm, p_SetExp, p_Add_q, p_NSet, p_GetCoeff, p_Delete, p_GetExp, pNext, rRingVar, omAlloc0, omStrDup, - omFree, pMDivide, p_Divide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, + omFree, p_Divide, p_SetCoeff0, n_Init, p_DivisibleBy, pLcm, p_LmDivisibleBy, pMDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, p_IsUnit, p_Series, p_Head, idInit, fast_map_common_subexp, id_Delete, p_IsHomogeneous, p_Homogen, p_Totaldegree,pLDeg1_Totaldegree, singclap_pdivide, singclap_factorize, From 71ceb43e52cf2e132e5b35eae2e2728f2ae584a7 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Wed, 25 Jul 2018 17:00:27 -0400 Subject: [PATCH 188/284] Initial implementation --- src/sage/combinat/posets/poset_examples.py | 169 +++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 1f106810dad..def6e8a1795 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -37,6 +37,9 @@ :meth:`~posets.IntegerPartitionsDominanceOrder` | Return the lattice of integer partitions on the integer `n` ordered by dominance. :meth:`~posets.NoncrossingPartitions` | Return the poset of noncrossing partitions of a finite Coxeter group ``W``. :meth:`~posets.PentagonPoset` | Return the Pentagon poset. + :meth:`~posets.PermutationPattern` | Return the Permutation Pattern poset. + :meth:`~posets.PermutationPatternInterval` | Return an interval in the Permutation Pattern poset. + :meth:`~posets.PermutationPatternOccurenceInterval` | Return the occurence poset for a pair of comparable elements in the Permutation Pattern poset. :meth:`~posets.PowerPoset` | Return a power poset. :meth:`~posets.RandomLattice` | Return a random lattice on `n` elements. :meth:`~posets.RandomPoset` | Return a random poset on `n` elements. @@ -1500,6 +1503,172 @@ def YoungFibonacci(n): return FiniteMeetSemilattice(hasse_diagram=D, category=FinitePosets()) + + @staticmethod + def PermutationPattern(n): + """ + Return the poset of permutations under pattern containment up to rank `n`. + + INPUT: + + - ``n`` -- a positive integer + + A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' + if there is a (not necessarily consecutive) subsequence of 'u' + of length 'm' whose entries have the same relative order as 'v'. + + See :wikipedia:`Permutation Pattern`. + + EXAMPLES:: + + sage: P4 = posets.PermutationPattern(4); P4 + Finite poset containing 33 elements + sage: sorted(P4.lower_covers(Permutation([2,4,1,3]))) + [[1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]] + + .. SEEALSO:: :class:`~sage.combinat.posets.permutation.has_pattern`, + + + TESTS:: + + sage: posets.PermutationPattern(1) + Finite poset containing 1 elements + """ + try: + n = Integer(n) + except TypeError: + raise TypeError("number of elements must be an integer, not {0}".format(n)) + if n <= 0: + raise ValueError("number of elements must be nonnegative, not {0}".format(n)) + elem = [] + for i in range(1,n+1): + elem += Permutations(i) + return(Poset((elem,lambda a,b : b.has_pattern(a)))) + + + + + @staticmethod + def PermutationPatternInterval(bottom,top): + """ + Return the poset consisting of an interval in the poset of permutations + under pattern containment between 'bottom' and 'top' + + INPUT: + + - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` + as a pattern. + + A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' + if there is a (not necessarily consecutive) subsequence of 'u' + of length 'm' whose entries have the same relative order as 'v'. + + See :wikipedia:`Permutation Pattern`. + + EXAMPLES:: + + sage: R=posets.PermutationPatternInterval(Permutation([2,3,1]),Permutation([4,6,2,3,5,1]));R + Finite poset containing 14 elements + sage: R.moebius_function(R.bottom(),R.top()) + -4 + + .. SEEALSO:: :meth:`~sage.combinat.posets.permutation.has_pattern`, + :meth:`PermutationPattern`, + + + TESTS:: + + sage: posets.PermutationPatternInterval(Permutation([1]),Permutation([1])) + Finite poset containing 1 elements + """ + top = Permutation(top) + bottom = Permutation(bottom) + if not top.has_pattern(bottom): + raise ValueError("{0} doesn't contain {1} as a pattern".format(top,bottom)) + elem = [[top]] # Make a list of lists of elements in the interval divided by rank. + # List will be flattened at the end + level = 0 # Consider the top element to be level 0, and then go down from there. + rel = [] # List of covering relations to be fed into poset constructor. + while len(top)-len(bottom) >= level+1: + elem.append([]) # Add a new empty level + for upper in elem[level]: # Run through all permutations on current level + # find relations for which it is upper cover + for i in range(0,len(top)-level): # Try and remove the ith element from the permutation + lower = list(upper) + j = lower.pop(i) + for k in range(0,len(top)-level-1): #Standardize result + if lower[k]>j: + lower[k] = lower[k]-1 + if Permutation(lower).has_pattern(bottom):# Check to see if result is in interval + rel += [[Permutation(lower),Permutation(upper)]] + if lower not in elem[level+1]: + elem[level+1].append(Permutation(lower)) + level += 1 + elem = [item for sublist in elem for item in sublist] + return(Poset((elem,rel))) + + @staticmethod + def PermutationPatternOccurenceInterval(bottom, top, pos): + """ + Return the poset consisting of an interval in the poset of permutations + under pattern containment between 'bottom' and 'top', where a specified + instance of 'bottom' in 'top' must be maintained. + + INPUT: + + - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` + as a pattern. + - ``pos`` -- a list of indices indicating a distinguished copy of + ``bottom`` inside ``top`` (indexed starting at 0) + + A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' + if there is a (not necessarily consecutive) subsequence of 'u' + of length 'm' whose entries have the same relative order as 'v'. + + See :wikipedia:`Permutation Pattern`. + + EXAMPLES:: + + + + .. SEEALSO:: :meth:`~sage.combinat.posets.permutation.has_pattern`, + :meth:`PermutationPattern`, :meth:`PermutationPatternInterval`, + + + TESTS:: + + + """ + import sage.combinat.permutation as permutation + if not permutation.to_standard([top[z] for z in pos]) == list(a): # check input + print("error, or empty") + elem = [[(top,pos)]] + level = 0 + rel = [] + while len(top)-len(bottom) >= level+1: + elem.append([]) # Add a new empty level + for upper in elem[level]: + for i in range(0,len(top)-level): # Try and remove the ith element from the permutation + if i not in upper[1]: + lower_perm = list(upper[0]) + j = lower_perm.pop(i) + for e in range(0,len(top)-level-1): # + if lower_perm[e]>j: + lower_perm[e] = lower_perm[e]-1 + lower_pos=list(copy(upper[1])) + for f in range(len(upper[1])): + if upper[1][f]>i: + lower_pos[f] = upper[1][f] - 1 + rel += [[(Permutation(lower_perm),tuple(lower_pos)),(Permutation(upper[0]),upper[1])]] + if (Permutation(lower_perm),tuple(lower_pos)) not in elem[level+1]: + elem[level+1].append((Permutation(d),tuple(lower_pos))) + level += 1 + elem = [item for sublist in elem for item in sublist] + return(Poset([elem,rel])) + + + + ## RANDOM LATTICES # Following are helper functions for random lattice generation. From ed90ea94457d0b32850e8a2ffab827bdd929f36c Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Thu, 26 Jul 2018 15:04:26 +0200 Subject: [PATCH 189/284] use '# optional - memlimit' instead; this explicitly states that --memlimit was used and is working --- src/sage/doctest/control.py | 5 +++++ src/sage/doctest/parsing.py | 14 ++------------ src/sage/doctest/sources.py | 3 +-- src/sage/matrix/matrix_mod2_dense.pyx | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index e4be46612fc..6aec5a6c15d 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -347,6 +347,11 @@ def __init__(self, options, args): options.optional |= auto_optional_tags self.options = options + + if options.memlimit > 0: + # Allow tests that require a virtual memory limit to be set + options.optional.add('memlimit') + self.files = args if options.logfile: try: diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 7185c43bcc2..f2ff21da9e7 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -39,7 +39,7 @@ from .external import available_software float_regex = re.compile('\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') -optional_regex = re.compile(r'(py2|py3|long time|high mem|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') +optional_regex = re.compile(r'(py2|py3|long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) random_marker = re.compile('.*random', re.I) @@ -267,7 +267,6 @@ def parse_optional_tags(string): set that occur in a comment on the first line of the input string. - 'long time' - - 'high mem' - 'not implemented' - 'not tested' - 'known bug' @@ -612,15 +611,13 @@ class SageDocTestParser(doctest.DocTestParser): A version of the standard doctest parser which handles Sage's custom options and tolerances in floating point arithmetic. """ - def __init__(self, optional_tags=(), long=False, highmem=False): + def __init__(self, optional_tags=(), long=False): r""" INPUT: - ``optional_tags`` -- a list or tuple of strings. - ``long`` -- boolean, whether to run doctests marked as taking a long time. - - ``highmem`` -- boolean, whether to run doctests marked as using a - lot of memory. EXAMPLES:: @@ -638,7 +635,6 @@ def __init__(self, optional_tags=(), long=False, highmem=False): sage: TestSuite(DTP).run() """ self.long = long - self.highmem = highmem self.optionals = collections.defaultdict(int) # record skipped optional tests if optional_tags is True: # run all optional tests self.optional_tags = True @@ -793,12 +789,6 @@ def parse(self, string, *args): else: continue - if 'high mem' in optional_tags: - if self.highmem: - optional_tags.remove('high mem') - else: - continue - if self.optional_tags is not True: extra = optional_tags - self.optional_tags # set difference if extra: diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 54d67734796..d4b149d1891 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -273,8 +273,7 @@ def _create_doctests(self, namespace, tab_okay=None): self._init() self.line_shift = 0 self.parser = SageDocTestParser(self.options.optional, - self.options.long, - self.options.memlimit > 0) + self.options.long) self.linking = False doctests = [] in_docstring = False diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index e99b86536b2..65e8191418f 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -162,7 +162,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse Large matrices fail gracefully:: - sage: MatrixSpace(GF(2), 2^30)(1) # high mem + sage: MatrixSpace(GF(2), 2^30)(1) # optional - memlimit Traceback (most recent call last): ... RuntimeError: matrix allocation failed From f31d9da8861773c554079ee2a66922a40156a9a5 Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Thu, 26 Jul 2018 15:25:03 +0200 Subject: [PATCH 190/284] Move patch documentation inside patch itself --- build/pkgs/singular/SPKG.txt | 5 ----- build/pkgs/singular/patches/singular-ntl-error-handler.patch | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build/pkgs/singular/SPKG.txt b/build/pkgs/singular/SPKG.txt index 9933e1a95aa..6dc588577ba 100644 --- a/build/pkgs/singular/SPKG.txt +++ b/build/pkgs/singular/SPKG.txt @@ -34,8 +34,3 @@ Other notes: 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. - -== Patches == - -singular-ntl-error-handler.patch: move NTL error handling out of libsingular so it doesn't conflict with Sage's - Backported from https://github.com/Singular/Sources/commit/502cf86d0bb2a96715be6764774b64a69c1ca34c diff --git a/build/pkgs/singular/patches/singular-ntl-error-handler.patch b/build/pkgs/singular/patches/singular-ntl-error-handler.patch index 2a3dac018bf..3d495293239 100644 --- a/build/pkgs/singular/patches/singular-ntl-error-handler.patch +++ b/build/pkgs/singular/patches/singular-ntl-error-handler.patch @@ -1,3 +1,7 @@ +Move NTL error handler out of libsingular, otherwise it takes over Sage's error handler and makes it quit on NTL errors. +See https://www.singular.uni-kl.de/forum/viewtopic.php?f=10&t=2769 and https://trac.sagemath.org/ticket/24735#comment:29 +Rebased from upstream commit https://github.com/Singular/Sources/commit/502cf86d0bb2a96715be6764774b64a69c1ca34c + From 502cf86d0bb2a96715be6764774b64a69c1ca34c Mon Sep 17 00:00:00 2001 From: Hans Schoenemann Date: Wed, 25 Jul 2018 11:03:32 +0200 From 07ef11b3209f498dc771bf5cb02f1b3ce1264cbb Mon Sep 17 00:00:00 2001 From: Antonio Rojas Date: Thu, 26 Jul 2018 15:25:57 +0200 Subject: [PATCH 191/284] rest -> reminder --- src/sage/libs/singular/decl.pxd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index d8637404195..7f09aafd9ec 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -630,7 +630,7 @@ cdef extern from "singular/Singular/libsingular.h": # return p*q, destroys p and q poly *p_Mult_q(poly *p, poly *q, ring *r) - # polynomial division, ignoring the rest + # polynomial division, ignoring the remainder # via singclap_pdivide resp. idLift, destroys p,q poly *p_Divide(poly *p, poly *q, ring *r) From 8378080f16e2655e060c29bbb0081a15e2173084 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Thu, 26 Jul 2018 16:10:18 -0400 Subject: [PATCH 192/284] Added reference, added doc test to third poset, fixed variables names I hadn't renamed in third poset --- src/doc/en/reference/references/index.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 959994e73ce..c0de3495e61 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2570,6 +2570,10 @@ REFERENCES: Mathematical Society, Vol. 355, No. 12 (Dec., 2003), pp. 4807--4823 +.. [ST2010] Einar Steingrimmsson and Bridget Tenner + *The Moebius Function of the Permutation Pattern Poset*, + Journal of Combinatorics 1 (2010), 39-52 + .. [Sti2006] Douglas R. Stinson. *Cryptography: Theory and Practice*. 3rd edition, Chapman \& Hall/CRC, 2006. From 873dc0c6071853357af89e697a09aea4c6d43e85 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Thu, 26 Jul 2018 16:12:48 -0400 Subject: [PATCH 193/284] Forgot to add the other file --- src/sage/combinat/posets/poset_examples.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index def6e8a1795..913bb229e82 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1621,14 +1621,14 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): - ``pos`` -- a list of indices indicating a distinguished copy of ``bottom`` inside ``top`` (indexed starting at 0) - A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' - if there is a (not necessarily consecutive) subsequence of 'u' - of length 'm' whose entries have the same relative order as 'v'. + For futher information (and picture illustrating included example), + see [ST2010]_ . See :wikipedia:`Permutation Pattern`. EXAMPLES:: - + sage: A = PermutationPatternOccurenceInterval(Permutation([3,2,1]),Permutation([6,3,4,5,2,1]),(0,2,4));A + Finite poset containing 8 elements. .. SEEALSO:: :meth:`~sage.combinat.posets.permutation.has_pattern`, @@ -1639,8 +1639,9 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): """ + from copy import copy import sage.combinat.permutation as permutation - if not permutation.to_standard([top[z] for z in pos]) == list(a): # check input + if not permutation.to_standard([top[z] for z in pos]) == list(bottom): # check input print("error, or empty") elem = [[(top,pos)]] level = 0 @@ -1661,7 +1662,7 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): lower_pos[f] = upper[1][f] - 1 rel += [[(Permutation(lower_perm),tuple(lower_pos)),(Permutation(upper[0]),upper[1])]] if (Permutation(lower_perm),tuple(lower_pos)) not in elem[level+1]: - elem[level+1].append((Permutation(d),tuple(lower_pos))) + elem[level+1].append((Permutation(lower_perm),tuple(lower_pos))) level += 1 elem = [item for sublist in elem for item in sublist] return(Poset([elem,rel])) From ce5c10b4ea36729f4fa3d916e982e72df394cc4b Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 11:42:50 +0000 Subject: [PATCH 194/284] py3: miscellaneous fixes involving dict iterators (.keys(), .values()) being assumed to be lists --- .../cluster_algebra_quiver/cluster_seed.py | 46 ++++++++++--------- .../combinat/root_system/weyl_characters.py | 10 ++-- .../manifolds/differentiable/tensorfield.py | 4 +- .../riemann_surfaces/riemann_surface.py | 2 +- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e42a9dff759..6171eb456cc 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -32,6 +32,8 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function + +from six import itervalues from six.moves import range import itertools @@ -323,7 +325,7 @@ def __init__(self, data, frozen=None, is_principal=False, user_labels=None, user self._user_labels_prefix = user_labels_prefix # initialize the rest - + self._C = matrix.identity(self._n) self._use_c_vec = True @@ -759,7 +761,7 @@ def _sanitize_init_vars(self, user_labels, user_labels_prefix = 'x'): - ``user_labels`` -- The labels that need sanitizing - ``user_labels_prefix`` -- (default:'x') The prefix to use for labels if integers given for labels - + EXAMPLES:: sage: S = ClusterSeed(['A', 4]); S._init_vars @@ -815,7 +817,7 @@ def _sanitize_init_vars(self, user_labels, user_labels_prefix = 'x'): else: raise ValueError("the input 'user_labels' must be a dictionary or a list") - if len(self._init_vars.keys()) != self._n + self._m: + if len(self._init_vars) != self._n + self._m: raise ValueError("the number of user-defined labels is not the" " number of exchangeable and frozen variables") @@ -1307,7 +1309,7 @@ def cluster_variable(self, k): (x0*x2 + x1 + 1)/(x0*x1) """ if self._use_fpolys: - IE = self._init_exch.values() + IE = list(itervalues(self._init_exch)) if (k in range(self._n)) or (k in IE): if k in range(self._n): pass @@ -1380,7 +1382,7 @@ def _f_mutate( self, k): y0 + 1 """ if self._use_fpolys: - IE = self._init_exch.values() + IE = list(itervalues(self._init_exch)) else: IE = [] @@ -1433,7 +1435,7 @@ def f_polynomial(self,k): [y0 + 1, 1] """ if self._use_fpolys: - IE = self._init_exch.values() + IE = list(itervalues(self._init_exch)) if k in range(self._n): pass elif k in IE: @@ -2607,7 +2609,7 @@ def mutate(self, sequence, inplace=True, input_type=None): def cluster_index(self, cluster_str): r""" Return the index of a cluster if ``use_fpolys`` is on. - + INPUT: - ``cluster_str`` -- the string to look for in the cluster @@ -3101,7 +3103,7 @@ def principal_extension(self): [ 0 0 1 0 0] [ 0 0 0 1 0] [ 0 0 0 0 1] - + sage: S = ClusterSeed(['A', 4], user_labels=['a', 'b', 'c', 'd']) sage: T = S.principal_extension() sage: T.cluster() @@ -3251,12 +3253,12 @@ def reset_cluster( self ): sage: T.reset_cluster() sage: T.cluster() [x0, x1, x2] - + sage: S = ClusterSeed(['B',3],user_labels=[[1,2],[2,3],[3,4]],user_labels_prefix='p') sage: S.mutate([0,1]) sage: S.cluster() [(p_2_3 + 1)/p_1_2, (p_1_2*p_3_4^2 + p_2_3 + 1)/(p_1_2*p_2_3), p_3_4] - + sage: S.reset_cluster() sage: S.cluster() [p_1_2, p_2_3, p_3_4] @@ -3273,7 +3275,7 @@ def reset_cluster( self ): self._F = dict([(i,self._U(1)) for i in self._init_exch.values()]) if self._use_fpolys: self.set_cluster(self._R.gens(), force=True) - + def reset_coefficients( self ): r""" Resets the coefficients of ``self`` to the frozen variables but keeps the current cluster. @@ -3487,7 +3489,7 @@ def mutation_class_iter(self, depth=infinity, show_depth=False, gets_bigger = False # set the keys - keys = clusters.keys() + keys = list(clusters) # Our keys are cluster variables, so for each cluster: for key in keys: @@ -4436,7 +4438,7 @@ def get_upper_cluster_algebra_element(self,a): Returns an element in the upper cluster algebra. Depending on the input it may or may not be irreducible. EXAMPLES:: - + sage: B=matrix([[0,3,-3],[-3,0,3],[3,-3,0],[1,0,0],[0,1,0],[0,0,1]]) sage: C=ClusterSeed(B) sage: C.get_upper_cluster_algebra_element([1,1,0]) @@ -4461,7 +4463,7 @@ def get_upper_cluster_algebra_element(self,a): sage: C.get_upper_cluster_algebra_element([1,1,1]) x0^4*x1^2*x2^3 + x0^2*x1^3*x2^4 - + REFERENCES: .. [LeeLiM] Lee-Li-Mills, A combinatorial formula for certain elements in the upper cluster algebra, :arxiv:`1409.8177` @@ -4554,7 +4556,7 @@ def _compute_compatible_vectors(self,vd): [1, 0, 1, 1]], [[0, 0, 0, 0], [0, 0, 0, 1]]] - + sage: B=matrix([[0,1,1,0],[-1,0,1,1],[-1,-1,0,0],[0,-1,0,0]]) sage: C=ClusterSeed(B) sage: v=_vector_decomposition([2,-1,3,-2],4) @@ -4595,7 +4597,7 @@ def _compute_compatible_vectors(self,vd): while len(p) < len(vd[0][0]): p.append(0) psetvect.append(p) - + for a in vd: negative = False for m in range(len(a)): @@ -4641,7 +4643,7 @@ def _produce_upper_cluster_algebra_element(self, vd, cList): sage: c = C._compute_compatible_vectors(v) sage: C._produce_upper_cluster_algebra_element(v,c) (x0^2*x1^3*x4*x5^2*x6*x7^2 + x0*x1^2*x2*x3*x4*x5*x6*x7 + 2*x0^2*x1^2*x4*x5^2*x6*x7 + x0^2*x1^2*x4*x5^2*x7^2 + x0*x1*x2*x3*x4*x5*x6 + x0^2*x1*x4*x5^2*x6 + x0*x1^2*x2*x3*x5*x7 + 2*x0*x1*x2*x3*x4*x5*x7 + 2*x0^2*x1*x4*x5^2*x7 + x1*x2^2*x3^2 + x2^2*x3^2*x4 + x0*x1*x2*x3*x5 + 2*x0*x2*x3*x4*x5 + x0^2*x4*x5^2)/(x0*x1^2*x2*x3^2) - + sage: B = matrix([[0,1,1,0],[-1,0,1,1],[-1,-1,0,0],[0,-1,0,0]]) sage: C = ClusterSeed(B) sage: v = _vector_decomposition([2,-1,3,-2],4) @@ -4865,9 +4867,9 @@ def get_green_vertices(C): INPUT: - ``C`` -- The C matrix to check - + EXAMPLES:: - + sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import get_green_vertices sage: S = ClusterSeed(['A',4]); S.mutate([1,2,3,2,0,1,2,0,3]) sage: get_green_vertices(S.c_matrix()) @@ -4927,7 +4929,7 @@ def _vector_decomposition(a, length): sage: _vector_decomposition([3,2,3,4],4) [[(1, 1, 1, 1), 2], [(1, 0, 1, 1), 1], [(0, 0, 0, 1), 1]] """ - + multiList = [] a_plus=[] for i in range(len(a)): @@ -5011,7 +5013,7 @@ def _power_set(n): sage: _power_set(2) [[0, 0], [0, 1], [1, 0], [1, 1]] - + sage: _power_set(5) [[0, 0, 0, 0, 0], [0, 0, 0, 0, 1], @@ -5066,7 +5068,7 @@ def _multi_concatenate(l1, l2): OUTPUT: A 2-dimensional array. - + EXAMPLES:: sage: from sage.combinat.cluster_algebra_quiver.cluster_seed import _multi_concatenate diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 75a77d391fd..c32206e3c29 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -680,7 +680,7 @@ def _weight_multiplicities(self, x): d[l] += c*d1[l] else: d[l] = c*d1[l] - for k in d.keys(): + for k in list(d): if d[k] == 0: del d[k] else: @@ -1291,7 +1291,7 @@ def symmetric_square(self): # a generic product) in the weight ring to optimize by # running only through pairs of weights instead of couples. c = self.weight_multiplicities() - ckeys = c.keys() + ckeys = list(c) d = {} for j in range(len(ckeys)): for i in range(j+1): @@ -1306,7 +1306,7 @@ def symmetric_square(self): d[t] += coef else: d[t] = coef - for k in d.keys(): + for k in list(d): if d[k] == 0: del d[k] return self.parent().char_from_weights(d) @@ -1322,7 +1322,7 @@ def exterior_square(self): A2(0,1) """ c = self.weight_multiplicities() - ckeys = c.keys() + ckeys = list(c) d = {} for j in range(len(ckeys)): for i in range(j+1): @@ -1337,7 +1337,7 @@ def exterior_square(self): d[t] += coef else: d[t] = coef - for k in d.keys(): + for k in list(d): if d[k] == 0: del d[k] return self.parent().char_from_weights(d) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index 3b09433dbfd..69a95106d07 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -1271,7 +1271,7 @@ def add_expr_from_subdomain(self, frame, subdomain): One can see that ``v`` is not yet fully defined: the components (scalar fields) do not have values on the whole manifold:: - sage: v._components.values()[0]._comp[(0,)].display() + sage: sorted(v._components.values())[0]._comp[(0,)].display() S --> R on U: (x, y) |--> x @@ -1288,7 +1288,7 @@ def add_expr_from_subdomain(self, frame, subdomain): The definition of ``v`` is now complete:: - sage: v._components.values()[0]._comp[(2,)].display() + sage: sorted(v._components.values())[0]._comp[(2,)].display() S --> R on U: (x, y) |--> x^2 + y^2 on V: (xp, yp) |--> 1/(xp^2 + yp^2) diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index 80f49cc9265..411aca5d329 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -659,7 +659,7 @@ def homotopy_continuation(self, edge): sage: R. = QQ[] sage: f = z^3*w + w^3 + z sage: S = RiemannSurface(f) - sage: edge1 = S.edge_permutations().keys()[0] + sage: edge1 = sorted(S.edge_permutations())[0] sage: sigma = S.edge_permutations()[edge1] sage: continued_values = S.homotopy_continuation(edge1) sage: stored_values = S.w_values(S._vertices[edge1[1]]) From 5973ae603ce326cf42d76108b0286d0ecf350803 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 12:48:23 +0000 Subject: [PATCH 195/284] py3: a couple more minor dict iterator fixes --- .../algebras/lie_algebras/classical_lie_algebra.py | 6 +++--- src/sage/combinat/crystals/alcove_path.py | 2 +- src/sage/modular/arithgroup/congroup_gammaH.py | 2 +- src/sage/plot/colors.py | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index b2f46127d07..de62dbfbd4e 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -1072,10 +1072,10 @@ def e_coeff(r, s): s_coeffs[(-r, -s)] = {-a: -c} # Lastly, make sure a < b for all (a, b) in the coefficients and flip if necessary - for k in s_coeffs.keys(): - a,b = k[0], k[1] + for k in list(s_coeffs): + a, b = k[0], k[1] if self._basis_key(a) > self._basis_key(b): - s_coeffs[(b,a)] = [(index, -v) for index,v in s_coeffs[k].items()] + s_coeffs[(b, a)] = [(index, -v) for index, v in s_coeffs[k].items()] del s_coeffs[k] else: s_coeffs[k] = s_coeffs[k].items() diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 7842be4b0bf..7caa73ff8a1 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -827,7 +827,7 @@ def _folding_data(self, i): {(alpha[2], 0): 1, (alpha[1] + alpha[2], 1): 1, 'infinity': 1} sage: fd['infinity'] 1 - sage: fd.values() + sage: sorted(fd.values()) [1, 1, 1] """ Parent = self.parent() diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index da4b6acb120..390b2d16574 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -614,7 +614,7 @@ def _coset_reduction_data_second_coord(G): 240: [1, 7, 49, 103, 137, 191, 233, 239]} sage: G = GammaH(1200,[-1,7]); G Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199] - sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort() + sage: K = sorted(G._coset_reduction_data_second_coord()) sage: K == divisors(1200) True """ diff --git a/src/sage/plot/colors.py b/src/sage/plot/colors.py index 7d624b8d093..d260427353a 100644 --- a/src/sage/plot/colors.py +++ b/src/sage/plot/colors.py @@ -1151,7 +1151,7 @@ def __dir__(self): True """ methods = ['__dir__', '__getattr__'] - return dir(super(ColorsDict, self)) + methods + self.keys() + return dir(super(ColorsDict, self)) + methods + sorted(self) colors = ColorsDict() @@ -1285,7 +1285,7 @@ def float_to_integer(r, g, b): """ r, g, b = map(mod_one, (r, g, b)) return int(r * 255) << 16 | int(g * 255) << 8 | int(b * 255) - + def rainbow(n, format='hex'): """ @@ -1392,7 +1392,7 @@ def get_cmap(cmap): return cmap elif isinstance(cmap, six.string_types): - if not cmap in cm.datad.keys(): + 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] @@ -1441,7 +1441,7 @@ def load_maps(self): if not cm: from matplotlib import cm if not self.maps: - for cmap in cm.datad.keys(): + for cmap in cm.datad: self.maps[cmap] = cm.__getattribute__(cmap) def __dir__(self): @@ -1464,7 +1464,7 @@ def __dir__(self): methods = ['load_maps', '__dir__', '__len__', '__iter__', '__contains__', '__getitem__', '__getattr__', '__setitem__', '__delitem__'] - return dir(super(Colormaps, self)) + methods + self.keys() + return dir(super(Colormaps, self)) + methods + sorted(self) def __len__(self): """ From fa50082ba156219e581f58fc90ad1a4bb20712df Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 12:49:15 +0000 Subject: [PATCH 196/284] py3: return lists instead of dict iterators for these methods sort the test outputs so that they can be predictable --- src/sage/algebras/cluster_algebra.py | 48 +++++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 37e08ebfb80..af627cdee7e 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -94,12 +94,12 @@ and get all its g-vectors, F-polynomials, and cluster variables:: - sage: A.g_vectors_so_far() - [(0, 1), (0, -1), (1, 0), (-1, 1), (-1, 0)] - sage: A.F_polynomials_so_far() - [1, u1 + 1, 1, u0 + 1, u0*u1 + u0 + 1] - sage: A.cluster_variables_so_far() - [x1, (x0 + 1)/x1, x0, (x1 + 1)/x0, (x0 + x1 + 1)/(x0*x1)] + sage: sorted(A.g_vectors_so_far()) + [(-1, 0), (-1, 1), (0, -1), (0, 1), (1, 0)] + sage: sorted(A.F_polynomials_so_far()) + [1, 1, u1 + 1, u0 + 1, u0*u1 + u0 + 1] + sage: sorted(A.cluster_variables_so_far()) + [x0, x1, (x0 + 1)/x1, (x1 + 1)/x0, (x0 + x1 + 1)/(x0*x1)] Simple operations among cluster variables behave as expected:: @@ -345,6 +345,8 @@ # http://www.gnu.org/licenses/ # **************************************************************************** from __future__ import absolute_import + +from six import itervalues from six.moves import range, map from copy import copy @@ -1515,13 +1517,13 @@ def clear_computed_data(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() - sage: A.g_vectors_so_far() + sage: sorted(A.g_vectors_so_far()) [(0, 1), (1, 0)] sage: A.current_seed().mutate([1, 0]) - sage: A.g_vectors_so_far() - [(0, 1), (0, -1), (1, 0), (-1, 0)] + sage: sorted(A.g_vectors_so_far()) + [(-1, 0), (0, -1), (0, 1), (1, 0)] sage: A.clear_computed_data() - sage: A.g_vectors_so_far() + sage: sorted(A.g_vectors_so_far()) [(0, 1), (1, 0)] """ I = identity_matrix(self._n) @@ -1659,10 +1661,10 @@ def g_vectors_so_far(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() sage: A.current_seed().mutate(0) - sage: A.g_vectors_so_far() - [(0, 1), (1, 0), (-1, 1)] + sage: sorted(A.g_vectors_so_far()) + [(-1, 1), (0, 1), (1, 0)] """ - return self._path_dict.keys() + return list(self._path_dict) def cluster_variables_so_far(self): r""" @@ -1673,10 +1675,10 @@ def cluster_variables_so_far(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() sage: A.current_seed().mutate(0) - sage: A.cluster_variables_so_far() - [x1, x0, (x1 + 1)/x0] + sage: sorted(A.cluster_variables_so_far()) + [x0, x1, (x1 + 1)/x0] """ - return list(map(self.cluster_variable, self.g_vectors_so_far())) + return [self.cluster_variable(v) for v in self.g_vectors_so_far()] def F_polynomials_so_far(self): r""" @@ -1687,10 +1689,10 @@ def F_polynomials_so_far(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() sage: A.current_seed().mutate(0) - sage: A.F_polynomials_so_far() + sage: sorted(A.F_polynomials_so_far()) [1, 1, u0 + 1] """ - return self._F_poly_dict.values() + return list(itervalues(self._F_poly_dict)) @cached_method(key=lambda a, b: tuple(b)) def cluster_variable(self, g_vector): @@ -2012,15 +2014,15 @@ def seeds(self, **kwargs): sage: A.clear_computed_data() sage: seeds = A.seeds(allowed_directions=[3, 0, 1]) sage: _ = list(seeds) - sage: A.g_vectors_so_far() + sage: sorted(A.g_vectors_so_far()) [(-1, 0, 0, 0), - (1, 0, 0, 0), - (0, 0, 0, 1), + (-1, 1, 0, 0), (0, -1, 0, 0), + (0, 0, 0, -1), + (0, 0, 0, 1), (0, 0, 1, 0), (0, 1, 0, 0), - (-1, 1, 0, 0), - (0, 0, 0, -1)] + (1, 0, 0, 0)] """ # should we begin from the current seed? if kwargs.get('from_current_seed', False): From 664a969284b95766340df92a5f4307289cab2865 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 13:19:58 +0000 Subject: [PATCH 197/284] py3: more minor fixes involving dict iterators and dict sorting --- src/sage/graphs/isgci.py | 15 +++++++++------ src/sage/modular/etaproducts.py | 8 ++++---- src/sage/schemes/toric/weierstrass.py | 4 ++-- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 2d1e7b4ca23..916c8998891 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -52,9 +52,9 @@ sage: Chordal.description() Class of graphs : Chordal ------------------------- - type : base id : gc_32 name : chordal + type : base Problems : ----------- @@ -632,9 +632,9 @@ def description(self): sage: graph_classes.Chordal.description() Class of graphs : Chordal ------------------------- - type : base id : gc_32 name : chordal + type : base Problems : ----------- @@ -665,7 +665,7 @@ def description(self): print("Class of graphs : "+self._name) print("-" * (len(self._name)+18)) - for key, value in six.iteritems(cls): + for key, value in sorted(cls.items()): if value != "" and key != "problem": print("{:30} : {}".format(key, value)) @@ -976,7 +976,6 @@ def show_all(self): ... """ classes = self.classes() - classes_list = classes.values() # We want to print the different fields, and this dictionary stores the # maximal number of characters of each field. @@ -989,7 +988,11 @@ def show_all(self): # We sort the classes alphabetically, though we would like to display the # meaningful classes at the top of the list - classes_list.sort(key = lambda x:x.get("name","zzzzz")+"{0:4}".format(int(x["id"].split('_')[1]))) + def sort_key(x): + name = x.get("name","zzzzz") + return "{}{:4}".format(name, int(x["id"].split('_')[1])) + + classes_list = sorted(classes.values(), key=sort_key) # Maximum width of a field MAX_LEN = 40 @@ -1031,9 +1034,9 @@ def _XML_to_dict(root): sage: graph_classes.Perfect.description() # indirect doctest Class of graphs : Perfect ------------------------- - type : base id : gc_56 name : perfect + type : base ... """ ans = root.attrib.copy() diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index 740621b093f..74f14f41d4b 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -397,11 +397,11 @@ def __init__(self, parent, rdict): sumR = sumDR = sumNoverDr = 0 prod = 1 - for d in rdict.keys(): + for d in rdict: if N % d: raise ValueError("%s does not divide %s" % (d, N)) - for d in rdict.keys(): + for d in list(rdict): if rdict[d] == 0: rdict.pop(d) continue @@ -419,9 +419,9 @@ def __init__(self, parent, rdict): if not is_square(prod): raise ValueError("product (N/d)^(r_d) (=%s) is not a square" % prod) - self._sumDR = sumDR # this is useful to have around + self._sumDR = sumDR # this is useful to have around self._rdict = rdict - self._keys = rdict.keys() # avoid factoring N every time + self._keys = list(rdict) # avoid factoring N every time def _mul_(self, other): r""" diff --git a/src/sage/schemes/toric/weierstrass.py b/src/sage/schemes/toric/weierstrass.py index 61f3998201a..85a238b69e0 100644 --- a/src/sage/schemes/toric/weierstrass.py +++ b/src/sage/schemes/toric/weierstrass.py @@ -290,7 +290,7 @@ def Newton_polytope_vars_coeffs(polynomial, variables): (3, 0, 0): a30} sage: from sage.geometry.polyhedron.ppl_lattice_polytope import LatticePolytope_PPL - sage: polytope = LatticePolytope_PPL(p_data.keys()); polytope + sage: polytope = LatticePolytope_PPL(list(p_data)); polytope A 2-dimensional lattice polytope in ZZ^3 with 3 vertices sage: polytope.vertices() ((0, 0, 3), (3, 0, 0), (0, 3, 0)) @@ -359,7 +359,7 @@ def Newton_polygon_embedded(polynomial, variables): (s, t)) """ p_dict = Newton_polytope_vars_coeffs(polynomial, variables) - newton_polytope = LatticePolytope_PPL(p_dict.keys()) + newton_polytope = LatticePolytope_PPL(list(p_dict)) assert newton_polytope.affine_dimension() <= 2 embedding = newton_polytope.embed_in_reflexive_polytope('points') x, y = variables[0:2] From 75e0d2c3ac82d895d9153830490fd7f162173dc1 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 13:24:17 +0000 Subject: [PATCH 198/284] py3: one more dict.keys -> list fix --- src/sage/graphs/graph_editor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/graph_editor.py b/src/sage/graphs/graph_editor.py index 9c50404e3a9..dc5f86c3567 100644 --- a/src/sage/graphs/graph_editor.py +++ b/src/sage/graphs/graph_editor.py @@ -48,7 +48,7 @@ def graph_to_js(g): 'num_vertices=3;edges=[[0,1],[0,2]];pos=[[0.75,0.5],[1.0,0.0],[0.0,1.0]];' """ string = '' - vertex_list = g.get_vertices().keys() + vertex_list = list(g.get_vertices()) string += 'num_vertices=' + str(len(vertex_list)) + ';' string += 'edges=[' for i, e in enumerate(g.edges()): From b7266a050a1c34180f2981e092086bb6d60c1a35 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 13:37:15 +0000 Subject: [PATCH 199/284] py3: use WithEqualityById for ChowGroup_class This is already its MO, but it doesn't use the existing mix-in class to support it --- src/sage/schemes/toric/chow_group.py | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/src/sage/schemes/toric/chow_group.py b/src/sage/schemes/toric/chow_group.py index 011dbcc5a9d..a5c7bdc4d06 100644 --- a/src/sage/schemes/toric/chow_group.py +++ b/src/sage/schemes/toric/chow_group.py @@ -129,6 +129,7 @@ #***************************************************************************** from sage.misc.all import flatten +from sage.misc.fast_methods import WithEqualityById from sage.modules.fg_pid.fgp_module import FGP_Module_class from sage.modules.fg_pid.fgp_element import FGP_Element from sage.modules.free_module import FreeModule @@ -604,7 +605,7 @@ def create_object(self, version, key, **extra_args): #******************************************************************* -class ChowGroup_class(FGP_Module_class): +class ChowGroup_class(FGP_Module_class, WithEqualityById): r""" The Chow group of a toric variety. @@ -824,30 +825,6 @@ def _repr_(self): else: raise ValueError - - def __eq__(self, other): - r""" - Comparison of two Chow groups. - - INPUT: - - - ``other`` -- anything. - - OUTPUT: - - ``True`` or ``False``. - - EXAMPLES:: - - sage: P2 = toric_varieties.P2() - sage: P2.Chow_group() == P2.Chow_group() - True - sage: P2.Chow_group(ZZ) == P2.Chow_group(QQ) - False - """ - return self is other # ChowGroup_class is unique - - def _cone_to_V(self, cone): r""" Convert a cone into the corresponding vector in ``self._V`` From af8b427e4baa8dab4d61e9f5e94a121b2ecac389 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 13:57:48 +0000 Subject: [PATCH 200/284] py3: fix a couple AttributeErrors on dict.iteritems --- src/sage/algebras/lie_algebras/lie_algebra_element.pyx | 2 +- src/sage/categories/quantum_group_representations.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index 85407457d9c..1b4184c2650 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -1208,7 +1208,7 @@ class FreeLieAlgebraElement(LieAlgebraElement): [([x, y], -1), (x, 1)] """ k = lambda x: (-x[0]._grade, x[0]) if isinstance(x[0], GradedLieBracket) else (-1, x[0]) - return sorted(self._monomial_coefficients.iteritems(), key=k) + return sorted((self._monomial_coefficients).iteritems(), key=k) def _bracket_(self, y): """ diff --git a/src/sage/categories/quantum_group_representations.py b/src/sage/categories/quantum_group_representations.py index e4e52adc852..4efd05fd4aa 100644 --- a/src/sage/categories/quantum_group_representations.py +++ b/src/sage/categories/quantum_group_representations.py @@ -356,8 +356,8 @@ def K(self, i, power=1): """ F = self.parent() mc = self.monomial_coefficients(copy=False) - return F.linear_combination( (F.K_on_basis(i, m, power), c) - for m,c in mc.iteritems() ) + return F.linear_combination((F.K_on_basis(i, m, power), c) + for m, c in mc.items() ) class TensorProducts(TensorProductsCategory): """ From a95715d5492c4362505e086dc0e5c633eeaabb20 Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Fri, 27 Jul 2018 09:57:15 -0500 Subject: [PATCH 201/284] initial code --- src/sage/categories/finite_posets.py | 55 ++++++++++++++++++++++++++++ src/sage/combinat/posets/posets.py | 31 ++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 7480533a3e2..47786defbd9 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -18,6 +18,7 @@ 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""" @@ -1344,6 +1345,33 @@ def rowmotion_orbits(self, element_constructor = set): pan_orbits = self.panyushev_orbits(element_constructor = list) return [[element_constructor(self.order_ideal(oideal)) for oideal in orbit] for orbit in pan_orbits] + def rowmotion_orbits_plots(self): + r""" + Return plots of the rowmotion orbits of order ideals in ``self``. + + The rowmotion orbit of an order ideal is its orbit under + rowmotion (see :meth:`rowmotion`). + + EXAMPLES:: + + sage: P = Poset( {1: [2, 3], 2: [], 3: [], 4: [2]} ) + sage: P.rowmotion_orbits_plots() + [[Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives], [Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives]] + sage: P = Poset({}) + sage: P.rowmotion_orbits_plots() + Graphics Array of size 1 x 1 + + """ + plot_of_orb_plots=[] + for orb in self.rowmotion_orbits(): + orb_plots=[] + for oi in orb: + oiplot = self.order_ideal_plot(oi) + orb_plots.append(oiplot) + plot_of_orb_plots.append(orb_plots) + return graphics_array(plot_of_orb_plots) + + def toggling_orbits(self, vs, element_constructor = set): r""" Return the orbits of order ideals in ``self`` under the @@ -1398,6 +1426,33 @@ def toggling_orbits(self, vs, element_constructor = set): orbits.append([element_constructor(_) for _ in orbit]) return orbits + def toggling_orbits_plots(self, vs): + r""" + Return plots of the orbits of order ideals in ``self`` under the + operation of toggling the vertices ``vs[0], vs[1], ...`` + in this order. + + See :meth:`toggling_orbits` for more information. + + EXAMPLES:: + + sage: P = Poset( {1: [2, 3], 2: [], 3: [], 4: [2]} ) + sage: P.toggling_orbits_plots([1,2,3,4]) + [[Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives], [Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives]] + sage: P = Poset({}) + sage: P.toggling_orbits_plots([]) + Graphics Array of size 1 x 1 + + """ + plot_of_orb_plots=[] + for orb in self.toggling_orbits(vs): + orb_plots=[] + for oi in orb: + oiplot = self.order_ideal_plot(oi) + orb_plots.append(oiplot) + plot_of_orb_plots.append(orb_plots) + return graphics_array(plot_of_orb_plots) + def panyushev_orbit_iter(self, antichain, element_constructor=set, stop=True, check=True): r""" Iterate over the Panyushev orbit of an antichain diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 8ea97506d35..6886aedb2f1 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -5884,6 +5884,37 @@ def order_ideal(self, elements): oi = self._hasse_diagram.order_ideal(vertices) return [self._vertex_to_element(_) for _ in oi] + def order_ideal_plot(self, elements): + r""" + Return a plot of the order ideal generated by the elements of an + iterable ``elements``. + + `I` is an order ideal if, for any `x` in `I` and `y` such that + `y \le x`, then `y` is in `I`. This is also called lower set or + downset. + + EXAMPLES:: + + sage: P = Poset((divisors(1000), attrcall("divides"))) + sage: P.order_ideal_plot([20, 25]) + Graphics object consisting of 41 graphics primitives + + TESTS:: + + sage: P = Poset() # Test empty poset + sage: P.order_ideal_plot([]) + Graphics object consisting of 0 graphics primitives + sage: C = posets.ChainPoset(5) + sage: C.order_ideal_plot([]) + Graphics object consisting of 10 graphics primitives + """ + order_ideal = self.order_ideal(elements) + order_filer = self.order_filter(self.order_ideal_complement_generators(order_ideal)) + order_ideal_color_dictionary = {} + order_ideal_color_dictionary['green'] = order_ideal + order_ideal_color_dictionary['red']= order_filer + return self.plot(element_colors = order_ideal_color_dictionary) + def interval(self, x, y): r""" Return a list of the elements `z` such that `x \le z \le y`. From ba4a39e2cfada4f76547187097ac2b81dbae1bac Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Fri, 27 Jul 2018 11:32:43 -0400 Subject: [PATCH 202/284] Fixing doc strings. Links in html index not working. --- src/sage/combinat/posets/poset_examples.py | 44 +++++++++++----------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 913bb229e82..acc8eaac313 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -37,9 +37,9 @@ :meth:`~posets.IntegerPartitionsDominanceOrder` | Return the lattice of integer partitions on the integer `n` ordered by dominance. :meth:`~posets.NoncrossingPartitions` | Return the poset of noncrossing partitions of a finite Coxeter group ``W``. :meth:`~posets.PentagonPoset` | Return the Pentagon poset. - :meth:`~posets.PermutationPattern` | Return the Permutation Pattern poset. - :meth:`~posets.PermutationPatternInterval` | Return an interval in the Permutation Pattern poset. - :meth:`~posets.PermutationPatternOccurenceInterval` | Return the occurence poset for a pair of comparable elements in the Permutation Pattern poset. + :meth:`~posets.PermutationPattern` | Return the Permutation pattern poset. + :meth:`~posets.PermutationPatternInterval` | Return an interval in the Permutation pattern poset. + :meth:`~posets.PermutationPatternOccurenceInterval` | Return the occurence poset for a pair of comparable elements in the Permutation pattern poset. :meth:`~posets.PowerPoset` | Return a power poset. :meth:`~posets.RandomLattice` | Return a random lattice on `n` elements. :meth:`~posets.RandomPoset` | Return a random poset on `n` elements. @@ -1513,9 +1513,9 @@ def PermutationPattern(n): - ``n`` -- a positive integer - A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' - if there is a (not necessarily consecutive) subsequence of 'u' - of length 'm' whose entries have the same relative order as 'v'. + A permutation `u=u_1\ldots u_n` contains the pattern `v=v_1\ldots v_m` + if there is a (not necessarily consecutive) subsequence of `u` + of length `m` whose entries have the same relative order as `v`. See :wikipedia:`Permutation Pattern`. @@ -1526,7 +1526,7 @@ def PermutationPattern(n): sage: sorted(P4.lower_covers(Permutation([2,4,1,3]))) [[1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]] - .. SEEALSO:: :class:`~sage.combinat.posets.permutation.has_pattern`, + .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern` TESTS:: @@ -1552,16 +1552,15 @@ def PermutationPattern(n): def PermutationPatternInterval(bottom,top): """ Return the poset consisting of an interval in the poset of permutations - under pattern containment between 'bottom' and 'top' + under pattern containment between ``bottom`` and ``top`` INPUT: - - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` - as a pattern. + - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` as a pattern. - A permutation 'u=u_1\ldots u_n' contains the pattern 'v=v_1\ldots v_m' - if there is a (not necessarily consecutive) subsequence of 'u' - of length 'm' whose entries have the same relative order as 'v'. + A permutation `u=u_1\ldots u_n` contains the pattern `v=v_1\ldots v_m` + if there is a (not necessarily consecutive) subsequence of `u` + of length `m` whose entries have the same relative order as `v`. See :wikipedia:`Permutation Pattern`. @@ -1572,8 +1571,8 @@ def PermutationPatternInterval(bottom,top): sage: R.moebius_function(R.bottom(),R.top()) -4 - .. SEEALSO:: :meth:`~sage.combinat.posets.permutation.has_pattern`, - :meth:`PermutationPattern`, + .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern`, + :meth:`PermutationPattern` TESTS:: @@ -1611,8 +1610,8 @@ def PermutationPatternInterval(bottom,top): def PermutationPatternOccurenceInterval(bottom, top, pos): """ Return the poset consisting of an interval in the poset of permutations - under pattern containment between 'bottom' and 'top', where a specified - instance of 'bottom' in 'top' must be maintained. + under pattern containment between ``bottom`` and ``top``, where a specified + instance of 'bottom' in ``top`` must be maintained. INPUT: @@ -1627,12 +1626,13 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): See :wikipedia:`Permutation Pattern`. EXAMPLES:: - sage: A = PermutationPatternOccurenceInterval(Permutation([3,2,1]),Permutation([6,3,4,5,2,1]),(0,2,4));A - Finite poset containing 8 elements. + + sage: A = posets.PermutationPatternOccurenceInterval(Permutation([3,2,1]),Permutation([6,3,4,5,2,1]),(0,2,4));A + Finite poset containing 8 elements - .. SEEALSO:: :meth:`~sage.combinat.posets.permutation.has_pattern`, - :meth:`PermutationPattern`, :meth:`PermutationPatternInterval`, + .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern`, + :meth:`PermutationPattern`, :meth:`PermutationPatternInterval` TESTS:: @@ -1641,6 +1641,8 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): """ from copy import copy import sage.combinat.permutation as permutation + top = Permutation(top) + bottom = Permutation(bottom) if not permutation.to_standard([top[z] for z in pos]) == list(bottom): # check input print("error, or empty") elem = [[(top,pos)]] From 2467784ca7f07f51c81c07e131857a3c516bc1c7 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Fri, 27 Jul 2018 11:53:20 -0400 Subject: [PATCH 203/284] More minor doc changes. --- src/sage/combinat/posets/poset_examples.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index acc8eaac313..dee5867b1e9 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1506,7 +1506,7 @@ def YoungFibonacci(n): @staticmethod def PermutationPattern(n): - """ + r""" Return the poset of permutations under pattern containment up to rank `n`. INPUT: @@ -1546,11 +1546,9 @@ def PermutationPattern(n): return(Poset((elem,lambda a,b : b.has_pattern(a)))) - - @staticmethod def PermutationPatternInterval(bottom,top): - """ + r""" Return the poset consisting of an interval in the poset of permutations under pattern containment between ``bottom`` and ``top`` @@ -1608,7 +1606,7 @@ def PermutationPatternInterval(bottom,top): @staticmethod def PermutationPatternOccurenceInterval(bottom, top, pos): - """ + r""" Return the poset consisting of an interval in the poset of permutations under pattern containment between ``bottom`` and ``top``, where a specified instance of 'bottom' in ``top`` must be maintained. @@ -1633,11 +1631,6 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern`, :meth:`PermutationPattern`, :meth:`PermutationPatternInterval` - - - TESTS:: - - """ from copy import copy import sage.combinat.permutation as permutation From 1fc79e92c65c1bba7d9e34754e0153f247e243b2 Mon Sep 17 00:00:00 2001 From: Jessica Striker Date: Fri, 27 Jul 2018 13:16:15 -0500 Subject: [PATCH 204/284] fixed doctests and graphics array columns --- src/sage/categories/finite_posets.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 47786defbd9..a765fefe15a 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -1356,20 +1356,23 @@ def rowmotion_orbits_plots(self): sage: P = Poset( {1: [2, 3], 2: [], 3: [], 4: [2]} ) sage: P.rowmotion_orbits_plots() - [[Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives], [Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives]] + Graphics Array of size 2 x 5 sage: P = Poset({}) sage: P.rowmotion_orbits_plots() - Graphics Array of size 1 x 1 + Graphics Array of size 2 x 1 """ - plot_of_orb_plots=[] + plot_of_orb_plots=[] + max_orbit_size = 0 for orb in self.rowmotion_orbits(): - orb_plots=[] + orb_plots=[] + if len(orb) > max_orbit_size: + max_orbit_size = len(orb) for oi in orb: oiplot = self.order_ideal_plot(oi) orb_plots.append(oiplot) plot_of_orb_plots.append(orb_plots) - return graphics_array(plot_of_orb_plots) + return graphics_array(plot_of_orb_plots, ncols = max_orbit_size) def toggling_orbits(self, vs, element_constructor = set): @@ -1438,20 +1441,23 @@ def toggling_orbits_plots(self, vs): sage: P = Poset( {1: [2, 3], 2: [], 3: [], 4: [2]} ) sage: P.toggling_orbits_plots([1,2,3,4]) - [[Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives], [Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives, Graphics object consisting of 8 graphics primitives]] + Graphics Array of size 2 x 5 sage: P = Poset({}) sage: P.toggling_orbits_plots([]) - Graphics Array of size 1 x 1 + Graphics Array of size 2 x 1 """ - plot_of_orb_plots=[] + plot_of_orb_plots=[] + max_orbit_size = 0 for orb in self.toggling_orbits(vs): - orb_plots=[] + orb_plots=[] + if len(orb) > max_orbit_size: + max_orbit_size = len(orb) for oi in orb: oiplot = self.order_ideal_plot(oi) orb_plots.append(oiplot) plot_of_orb_plots.append(orb_plots) - return graphics_array(plot_of_orb_plots) + return graphics_array(plot_of_orb_plots, ncols = max_orbit_size) def panyushev_orbit_iter(self, antichain, element_constructor=set, stop=True, check=True): r""" From 331d4d5d47b1f4760750b069ad42c10b69052e5d Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 27 Jul 2018 17:11:44 +0200 Subject: [PATCH 205/284] Add test for --memlimit option --- src/sage/doctest/test.py | 12 ++++++++++++ src/sage/doctest/tests/memlimit.rst | 3 +++ 2 files changed, 15 insertions(+) create mode 100644 src/sage/doctest/tests/memlimit.rst diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index d43c91f2c8c..9f22451094d 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -495,4 +495,16 @@ ....: os.unlink(F) ....: except OSError: ....: pass + +Test the ``--memlimit`` option and ``# optional - memlimit`` +(but only on Linux):: + + sage: from platform import system + sage: ok = True + sage: if system() == "Linux": + ....: P = subprocess.Popen(["sage", "-t", "--warn-long", "0", "--memlimit=2000", "memlimit.rst"], stdout=subprocess.PIPE, **kwds) + ....: out, err = P.communicate() + ....: ok = ("MemoryError: failed to allocate" in out) + sage: ok or out + True """ diff --git a/src/sage/doctest/tests/memlimit.rst b/src/sage/doctest/tests/memlimit.rst new file mode 100644 index 00000000000..09b9a589402 --- /dev/null +++ b/src/sage/doctest/tests/memlimit.rst @@ -0,0 +1,3 @@ +This should raise a ``MemoryError``:: + + sage: 256 ^ (2000 << 20) # optional - memlimit From 91503870b530276d48bc43b0589abe0f8593f834 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 28 Jul 2018 06:14:58 +1000 Subject: [PATCH 206/284] Remove itervalues. --- src/sage/combinat/cluster_algebra_quiver/cluster_seed.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 6171eb456cc..6d078c93c4f 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -33,7 +33,6 @@ #***************************************************************************** from __future__ import print_function -from six import itervalues from six.moves import range import itertools @@ -1309,7 +1308,7 @@ def cluster_variable(self, k): (x0*x2 + x1 + 1)/(x0*x1) """ if self._use_fpolys: - IE = list(itervalues(self._init_exch)) + IE = list(self._init_exch.values()) if (k in range(self._n)) or (k in IE): if k in range(self._n): pass @@ -1382,7 +1381,7 @@ def _f_mutate( self, k): y0 + 1 """ if self._use_fpolys: - IE = list(itervalues(self._init_exch)) + IE = list(self._init_exch.values()) else: IE = [] @@ -1435,7 +1434,7 @@ def f_polynomial(self,k): [y0 + 1, 1] """ if self._use_fpolys: - IE = list(itervalues(self._init_exch)) + IE = list(self._init_exch.values()) if k in range(self._n): pass elif k in IE: From a9e5aed9215175f8cf49327c790ae153648a2513 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Sat, 28 Jul 2018 15:09:54 +0200 Subject: [PATCH 207/284] Minor fixes to Singular interface --- src/sage/interfaces/singular.py | 4 +- .../multi_polynomial_libsingular.pyx | 79 ++++++++++--------- src/sage/rings/polynomial/plural.pyx | 19 +++-- 3 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index a028bbe719e..3d563ffc047 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -654,6 +654,8 @@ def eval(self, x, allow_semicolon=True, strip=True, **kwds): s = Expect.eval(self, x, **kwds) + # "Segment fault" is not a typo: + # Singular actually does use that string if s.find("error occurred") != -1 or s.find("Segment fault") != -1: raise SingularError('Singular error:\n%s'%s) @@ -1750,7 +1752,7 @@ def sage_poly(self, R=None, kcache=None): # Singular 4 puts parentheses around floats and sign outside them charstr = self.parent().eval('charstr(basering)').split(',',1) - if charstr[0]=='complex' or charstr[0].startswith('Float'): + if charstr[0].startswith('Float') or charstr[0] == 'complex': for i in range(coeff_start, 2 * coeff_start): singular_poly_list[i] = singular_poly_list[i].replace('(','').replace(')','') diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index f0a174fcf04..043696ed868 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -181,8 +181,8 @@ from cysignals.signals cimport sig_on, sig_off from sage.cpython.string cimport char_to_str, str_to_bytes # singular types -from sage.libs.singular.decl cimport ring, poly, ideal, intvec, number, currRing -from sage.libs.singular.decl cimport n_unknown, n_Zp, n_Q, n_R, n_GF, n_long_R, n_algExt,n_transExt,n_long_C, n_Z, n_Zn, n_Znm, n_Z2m, n_CF +from sage.libs.singular.decl cimport (ring, poly, ideal, intvec, number, + currRing, n_unknown, n_Z, n_Zn, n_Znm, n_Z2m) # singular functions from sage.libs.singular.decl cimport ( @@ -198,7 +198,7 @@ from sage.libs.singular.decl cimport ( p_LmIsConstant, pTakeOutComp1, singclap_gcd, pp_Mult_qq, p_GetMaxExp, pLength, kNF, p_Neg, p_Minus_mm_Mult_qq, p_Plus_mm_Mult_qq, pDiff, singclap_resultant, p_Normalize, - prCopyR, prCopyR_NoSort ) + prCopyR, prCopyR_NoSort) # singular conversion routines from sage.libs.singular.singular cimport si2sa, sa2si, overflow_check @@ -225,7 +225,7 @@ from sage.rings.polynomial.polynomial_ring import is_PolynomialRing # base ring imports from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn from sage.rings.rational cimport Rational -from sage.rings.rational_field import RationalField +from sage.rings.rational_field import QQ from sage.rings.complex_field import is_ComplexField from sage.rings.real_mpfr import is_RealField from sage.rings.integer_ring import is_IntegerRing, ZZ @@ -1693,7 +1693,8 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): if not g._poly: raise ZeroDivisionError - if r!=currRing: rChangeCurrRing(r) # pMDivide + if r is not currRing: + rChangeCurrRing(r) res = pMDivide(f._poly, g._poly) if coeff: if r.cf.type == n_unknown or r.cf.cfDivBy(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf): @@ -1846,19 +1847,18 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_base): cdef poly *flt if not m: - return f,f + return (f, f) for g in G: - if isinstance(g, MPolynomial_libsingular) \ - and (g) \ - and g.parent() is self \ - and p_LmDivisibleBy((g)._poly, m, r): - if r!=currRing: rChangeCurrRing(r) # pMDivide - flt = pMDivide(f._poly, (g)._poly) - #p_SetCoeff(flt, n_Div( p_GetCoeff(f._poly, r) , p_GetCoeff((g)._poly, r), r), r) - p_SetCoeff(flt, n_Init(1, r), r) - return new_MP(self,flt), g - return self._zero_element,self._zero_element + if isinstance(g, MPolynomial_libsingular) and g: + h = g + if h._parent is self and p_LmDivisibleBy(h._poly, m, r): + if r is not currRing: + rChangeCurrRing(r) + flt = pMDivide(f._poly, h._poly) + p_SetCoeff(flt, n_Init(1, r), r) + return (new_MP(self, flt), h) + return (self._zero_element, self._zero_element) def monomial_pairwise_prime(self, MPolynomial_libsingular g, MPolynomial_libsingular h): """ @@ -4023,11 +4023,19 @@ cdef class MPolynomial_libsingular(MPolynomial): Traceback (most recent call last): ... NotImplementedError: Division of multivariate polynomials over non fields by non-monomials not implemented. + + TESTS:: + + sage: P. = ZZ[] + sage: p = 3*(-x^8*y^2 - x*y^9 + 6*x^8*y + 17*x^2*y^6 - x^3*y^2) + sage: q = 7*(x^2 + x*y + y^2 + 1) + sage: p*q//q == p + True + sage: p*q//p == q + True """ cdef MPolynomialRing_libsingular parent = self._parent cdef ring *r = self._parent_ring - if(r != currRing): rChangeCurrRing(r) - cdef MPolynomial_libsingular _self, _right cdef poly *quo cdef poly *temp cdef poly *p @@ -4038,19 +4046,19 @@ cdef class MPolynomial_libsingular(MPolynomial): if self._parent._base.is_finite() and self._parent._base.characteristic() > 1<<29: raise NotImplementedError("Division of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.") - _self = self _right = right + if r is not currRing: + rChangeCurrRing(r) + if r.cf.type != n_unknown: if r.cf.type == n_Z: - P = parent.change_ring(RationalField()) - f = P(self)//P(right) - CM = list(f) - return parent(sum([c.floor()*m for c,m in CM])) + P = parent.change_ring(QQ) + f = (P(self))._floordiv_(P(right)) + return parent(sum([c.floor() * m for c, m in f])) if _right.is_monomial(): - p = _self._poly + p = self._poly quo = p_ISet(0,r) - if r != currRing: rChangeCurrRing(r) # pMDivide while p: if p_DivisibleBy(_right._poly, p, r): temp = pMDivide(p, _right._poly) @@ -4061,16 +4069,13 @@ cdef class MPolynomial_libsingular(MPolynomial): if r.cf.type == n_Znm or r.cf.type == n_Zn or r.cf.type == n_Z2m : raise NotImplementedError("Division of multivariate polynomials over non fields by non-monomials not implemented.") - cdef int count = singular_polynomial_length_bounded(_self._poly,15) + count = singular_polynomial_length_bounded(self._poly, 15) if count >= 15: # note that _right._poly must be of shorter length than self._poly for us to care about this call sig_on() - if r!=currRing: rChangeCurrRing(r) # singclap_pdivide - quo = singclap_pdivide( _self._poly, _right._poly, r ) + quo = singclap_pdivide(self._poly, _right._poly, r) if count >= 15: sig_off() - f = new_MP(parent, quo) - - return f + return new_MP(parent, quo) def factor(self, proof=True): """ @@ -4789,7 +4794,9 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: p = -x*y + x*z + 54*x - 2 sage: (5*p^2).lcm(3*p) == 15*p^2 True - sage: lcm(2*x,2*x*y) + sage: lcm(2*x, 2*y) + 2*x*y + sage: lcm(2*x, 2*x*y) 2*x*y """ cdef ring *_ring = self._parent_ring @@ -5222,7 +5229,7 @@ cdef class MPolynomial_libsingular(MPolynomial): if ambient_ring is not self._parent: raise TypeError("the variable is not in the same ring as self") - if not ambient_ring.has_coerce_map_from(RationalField()): + if not ambient_ring.has_coerce_map_from(QQ): raise TypeError("the ring must contain the rational numbers") gens = ambient_ring.gens() @@ -5325,8 +5332,8 @@ cdef class MPolynomial_libsingular(MPolynomial): raise NotImplementedError("Resultants of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented.") if is_IntegerRing(self._parent._base): - ret = self.change_ring(RationalField()).resultant(other.change_ring(RationalField()), - variable.change_ring(RationalField())) + ret = self.change_ring(QQ).resultant(other.change_ring(QQ), + variable.change_ring(QQ)) return ret.change_ring(ZZ) elif not self._parent._base.is_field(): raise ValueError("Resultants require base fields or integer base ring.") @@ -5457,7 +5464,7 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: f.numerator().parent() is P True """ - if self.base_ring() == RationalField(): + if self.base_ring() is QQ: #This part is for compatibility with the univariate case, #where the numerator of a polynomial over RationalField #is a polynomial over IntegerRing diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index ad20ebcca0e..6dfcb4761d5 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -998,7 +998,7 @@ cdef class NCPolynomialRing_plural(Ring): if not g._poly: raise ZeroDivisionError - res = pMDivide(f._poly,g._poly) + res = pMDivide(f._poly, g._poly) if coeff: if (r.cf.type == n_unknown) or r.cf.cfDivBy(p_GetCoeff(f._poly, r), p_GetCoeff(g._poly, r), r.cf): n = r.cf.cfDiv( p_GetCoeff(f._poly, r) , p_GetCoeff(g._poly, r), r.cf) @@ -1187,17 +1187,16 @@ cdef class NCPolynomialRing_plural(Ring): cdef poly *flt if not m: - return f,f + return (f, f) for g in G: - if isinstance(g, NCPolynomial_plural) \ - and (g) \ - and p_LmDivisibleBy((g)._poly, m, r): - flt = pMDivide(f._poly, (g)._poly) - #p_SetCoeff(flt, n_Div( p_GetCoeff(f._poly, r) , p_GetCoeff((g)._poly, r), r), r) - p_SetCoeff(flt, n_Init(1, r), r) - return new_NCP(self,flt), g - return self._zero_element,self._zero_element + if isinstance(g, NCPolynomial_plural) and g: + h = g + if p_LmDivisibleBy(h._poly, m, r): + flt = pMDivide(f._poly, h._poly) + p_SetCoeff(flt, n_Init(1, r), r) + return (new_NCP(self,flt), h) + return (self._zero_element, self._zero_element) def monomial_pairwise_prime(self, NCPolynomial_plural g, NCPolynomial_plural h): """ From dd4400e38ae009ad6b638cb8f6c122d55a5d4501 Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Fri, 1 Jun 2018 18:14:22 -0400 Subject: [PATCH 208/284] fix more dokchitser --- src/ext/pari/dokchitser/computel.gp.template | 16 ++++++++-------- src/sage/lfunctions/dokchitser.py | 10 ++++++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/ext/pari/dokchitser/computel.gp.template b/src/ext/pari/dokchitser/computel.gp.template index 24beeb307af..572a328c3d1 100644 --- a/src/ext/pari/dokchitser/computel.gp.template +++ b/src/ext/pari/dokchitser/computel.gp.template @@ -238,7 +238,7 @@ fullgamma_$i(ss) = fullgammaseries_$i(ss,extraterms)= local(digts,GSD); - digts=default(realprecision); + digts=lfundigits_$i; if (lastFGSs_$i!=ss || lastFGSterms_$i!=extraterms, GSD=sum(j=1,numpoles_$i,(abs((ss+poles_$i[j])/2-round(real((ss+poles_$i[j])/2)))<10^(2-digts)) * PoleOrders_$i[j] )+extraterms; lastFGSs_$i=ss; @@ -571,12 +571,12 @@ LogInt_$i(i,j,logt,der)=\ MakeLogSum_$i(ss,der)= local(nn,V,logsum); - if(length(LogSum)==phiVnn_$i, \\ more phiV's necessary + if(length(LogSum_$i)==phiVnn_$i, \\ more phiV's necessary for (j=1,floor(phiVnn_$i/10)+1,RecursephiV_$i())); - for (nn=length(LogSum)+1,phiVnn_$i, \\ generate logsums + for (nn=length(LogSum_$i)+1,phiVnn_$i, \\ generate logsums V=phiV_$i[nn]; logsum=vector(numpoles_$i,j,sum(k=1,PoleOrders_$i[j],V[j,k]*LogInt_$i(poles_$i[j]+2*(nn-1)+ss,k,lt,der)))~; - LogSum=concat(LogSum,[logsum]); + LogSum_$i=concat(LogSum_$i,[logsum]); ); lastLSs_$i=[ss,der]; } @@ -587,7 +587,7 @@ G0_$i(t,ss,der)= \\ G(t,s,der) computed using Taylor series at 0 default(realprecision,taylordigits_$i); ss = precision(ss,taylordigits_$i); - if ([ss,der]!=lastLSs_$i,LogSum=[]); + if ([ss,der]!=lastLSs_$i,LogSum_$i=[]); t = precision(t,taylordigits_$i); t2 = t^2; \\ time LT = log(t); \\ = money @@ -597,8 +597,8 @@ G0_$i(t,ss,der)= \\ G(t,s,der) computed using Taylor series at 0 term = 1; until ((nn>3) && abs(term)<10^-(termdigits_$i+1), nn++; - if(nn>length(LogSum),MakeLogSum_$i(ss,der)); - term=TPower*subst(LogSum[nn],lt,LT); + if(nn>length(LogSum_$i),MakeLogSum_$i(ss,der)); + term=TPower*subst(LogSum_$i[nn],lt,LT); res+=term; TPower*=t2; ); @@ -698,8 +698,8 @@ Lseries_$i(ss,cutoff=1,der=0)= local(FGSeries,LGSeries,res); - default(realprecision,lfundigits_$i); FGSeries = fullgammaseries_$i(ss,der)*vA_$i^(ss+S); + default(realprecision,lfundigits_$i); if (length(Lpoles_$i) && (vecmin(abs(vector(length(Lpoles_$i),k,Lpoles_$i[k]-ss)))<10^(-answerdigits_$i) || vecmin(abs(vector(length(Lpoles_$i),k,weight_$i-Lpoles_$i[k]-ss)))<10^(-answerdigits_$i)), error("L*(s) has a pole at s=",ss)); diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index 43396991897..03e97b7745d 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -607,6 +607,16 @@ def taylor_series(self, a=0, k=6, var='z'): sage: L.taylor_series(-1, 3) 0.000000000000000 - 0.702565506265199*z + 0.638929001045535*z^2 + O(z^3) + Check that :trac:`25965` is fixed:: + + sage: L2 = EllipticCurve("37a1").modular_form().lseries(); L2 + L-series associated to the cusp form q - 2*q^2 - 3*q^3 + 2*q^4 - 2*q^5 + O(q^6) + sage: L2.taylor_series(0,4) + 0.000000000000000 - 0.357620466127498*z + 0.273373112603865*z^2 + 0.303362857047671*z^3 + O(z^4) + sage: L2.taylor_series(0,1) + O(z^1) + sage: L2(0) + 0.000000000000000 """ self.__check_init() a = self.__CC(a) From 4c97e9c376ac8971395d342b1ad132b29975526f Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Sat, 28 Jul 2018 19:34:41 +0200 Subject: [PATCH 209/284] update dokchitser and template LogSum --- src/ext/pari/dokchitser/computel.gp | 9 +++++++-- src/ext/pari/dokchitser/computel.gp.template | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ext/pari/dokchitser/computel.gp b/src/ext/pari/dokchitser/computel.gp index 85f8ce042a0..b3164ede3f6 100644 --- a/src/ext/pari/dokchitser/computel.gp +++ b/src/ext/pari/dokchitser/computel.gp @@ -1,8 +1,13 @@ -/************* ComputeL v1.3.4, 2001-2016, (c) Tim Dokchitser ************/ +/************* ComputeL v1.3.8, 2001-2018, (c) Tim Dokchitser ************/ /**************** computing special values of L-functions ****************/ /* arXiv.org/abs/math.NT/0207280, Exper. Math. 13 (2004), no. 2, 137-150 */ /****** Questions/comments welcome! -> tim.dokchitser@bristol.ac.uk ******/ +\\ ACKNOWLEDGEMENTS: I'd like to thank Mark Watkins, Steve Donnelly, +\\ William Stein, Anton Mellit, Almasa Odzak, Karim Belabas, Myoungil Kim, +\\ Chris King, F. Patrick Rabarison, Neil Dummigan, Maciej Radziejewski, +\\ François Brunault and Alex Best, for examples, bug fixes and suggestions + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\ Distributed under the terms of the GNU General Public License (GPL) \\ This code is distributed in the hope that it will be useful, @@ -238,7 +243,7 @@ fullgamma(ss) = fullgammaseries(ss,extraterms)= local(digts,GSD); - digts=default(realprecision); + digts=lfundigits; if (lastFGSs!=ss || lastFGSterms!=extraterms, GSD=sum(j=1,numpoles,(abs((ss+poles[j])/2-round(real((ss+poles[j])/2)))<10^(2-digts)) * PoleOrders[j] )+extraterms; lastFGSs=ss; diff --git a/src/ext/pari/dokchitser/computel.gp.template b/src/ext/pari/dokchitser/computel.gp.template index 572a328c3d1..077d84e9718 100644 --- a/src/ext/pari/dokchitser/computel.gp.template +++ b/src/ext/pari/dokchitser/computel.gp.template @@ -1,8 +1,13 @@ -/************* ComputeL v1.3.4, 2001-2016, (c) Tim Dokchitser ************/ +/************* ComputeL v1.3.8, 2001-2018, (c) Tim Dokchitser ************/ /**************** computing special values of L-functions ****************/ /* arXiv.org/abs/math.NT/0207280, Exper. Math. 13 (2004), no. 2, 137-150 */ /****** Questions/comments welcome! -> tim.dokchitser@bristol.ac.uk ******/ +\\ ACKNOWLEDGEMENTS: I'd like to thank Mark Watkins, Steve Donnelly, +\\ William Stein, Anton Mellit, Almasa Odzak, Karim Belabas, Myoungil Kim, +\\ Chris King, F. Patrick Rabarison, Neil Dummigan, Maciej Radziejewski, +\\ François Brunault and Alex Best, for examples, bug fixes and suggestions + \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \\ Distributed under the terms of the GNU General Public License (GPL) \\ This code is distributed in the hope that it will be useful, @@ -698,8 +703,8 @@ Lseries_$i(ss,cutoff=1,der=0)= local(FGSeries,LGSeries,res); - FGSeries = fullgammaseries_$i(ss,der)*vA_$i^(ss+S); default(realprecision,lfundigits_$i); + FGSeries = fullgammaseries_$i(ss,der)*vA_$i^(ss+S); if (length(Lpoles_$i) && (vecmin(abs(vector(length(Lpoles_$i),k,Lpoles_$i[k]-ss)))<10^(-answerdigits_$i) || vecmin(abs(vector(length(Lpoles_$i),k,weight_$i-Lpoles_$i[k]-ss)))<10^(-answerdigits_$i)), error("L*(s) has a pole at s=",ss)); From 69f8e03fa62eb8ed4ddc0c113486d0aab7553a12 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 31 Jul 2018 07:50:39 +1000 Subject: [PATCH 210/284] Making the output machine-independent. --- src/sage/algebras/cluster_algebra.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index af627cdee7e..1b4e66f0394 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -96,10 +96,10 @@ sage: sorted(A.g_vectors_so_far()) [(-1, 0), (-1, 1), (0, -1), (0, 1), (1, 0)] - sage: sorted(A.F_polynomials_so_far()) - [1, 1, u1 + 1, u0 + 1, u0*u1 + u0 + 1] - sage: sorted(A.cluster_variables_so_far()) - [x0, x1, (x0 + 1)/x1, (x1 + 1)/x0, (x0 + x1 + 1)/(x0*x1)] + sage: sorted(A.F_polynomials_so_far(), key=str) + [1, 1, u0 + 1, u0*u1 + u0 + 1, u1 + 1] + sage: sorted(A.cluster_variables_so_far(), key=str) + [(x0 + 1)/x1, (x0 + x1 + 1)/(x0*x1), (x1 + 1)/x0, x0, x1] Simple operations among cluster variables behave as expected:: @@ -346,7 +346,6 @@ # **************************************************************************** from __future__ import absolute_import -from six import itervalues from six.moves import range, map from copy import copy @@ -1675,8 +1674,8 @@ def cluster_variables_so_far(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() sage: A.current_seed().mutate(0) - sage: sorted(A.cluster_variables_so_far()) - [x0, x1, (x1 + 1)/x0] + sage: sorted(A.cluster_variables_so_far(), key=str) + [(x1 + 1)/x0, x0, x1] """ return [self.cluster_variable(v) for v in self.g_vectors_so_far()] @@ -1689,10 +1688,10 @@ def F_polynomials_so_far(self): sage: A = ClusterAlgebra(['A', 2]) sage: A.clear_computed_data() sage: A.current_seed().mutate(0) - sage: sorted(A.F_polynomials_so_far()) + sage: sorted(A.F_polynomials_so_far(), key=str) [1, 1, u0 + 1] """ - return list(itervalues(self._F_poly_dict)) + return list(self._F_poly_dict.values()) @cached_method(key=lambda a, b: tuple(b)) def cluster_variable(self, g_vector): From 4e009afc2906d7e1b96d85ad7c669fde7a7164d2 Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Tue, 31 Jul 2018 14:50:57 -0400 Subject: [PATCH 211/284] fixed wiki link --- src/sage/combinat/posets/poset_examples.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index dee5867b1e9..0e394b7a485 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -1517,7 +1517,7 @@ def PermutationPattern(n): if there is a (not necessarily consecutive) subsequence of `u` of length `m` whose entries have the same relative order as `v`. - See :wikipedia:`Permutation Pattern`. + See :wikipedia:`Permutation_pattern`. EXAMPLES:: @@ -1560,7 +1560,7 @@ def PermutationPatternInterval(bottom,top): if there is a (not necessarily consecutive) subsequence of `u` of length `m` whose entries have the same relative order as `v`. - See :wikipedia:`Permutation Pattern`. + See :wikipedia:`Permutation_pattern`. EXAMPLES:: @@ -1621,7 +1621,7 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): For futher information (and picture illustrating included example), see [ST2010]_ . - See :wikipedia:`Permutation Pattern`. + See :wikipedia:`Permutation_pattern`. EXAMPLES:: From 57a291e9eba1ff09e7899e43e681af70b35d9d39 Mon Sep 17 00:00:00 2001 From: Elisa Palezzato Date: Fri, 29 Jun 2018 19:29:32 +0200 Subject: [PATCH 212/284] upgrade to matplotlib 2.2.2 --- build/pkgs/kiwisolver/SPKG.txt | 31 +++++++++++++++++++ build/pkgs/kiwisolver/checksums.ini | 4 +++ build/pkgs/kiwisolver/dependencies | 5 +++ build/pkgs/kiwisolver/package-version.txt | 1 + build/pkgs/kiwisolver/spkg-install | 1 + build/pkgs/kiwisolver/type | 1 + build/pkgs/matplotlib/SPKG.txt | 1 + build/pkgs/matplotlib/checksums.ini | 6 ++-- build/pkgs/matplotlib/package-version.txt | 2 +- build/pkgs/matplotlib/spkg-src | 2 +- src/sage/plot/histogram.py | 16 +++++----- .../probability/probability_distribution.pyx | 6 ++-- 12 files changed, 60 insertions(+), 16 deletions(-) create mode 100644 build/pkgs/kiwisolver/SPKG.txt create mode 100644 build/pkgs/kiwisolver/checksums.ini create mode 100644 build/pkgs/kiwisolver/dependencies create mode 100644 build/pkgs/kiwisolver/package-version.txt create mode 100644 build/pkgs/kiwisolver/spkg-install create mode 100644 build/pkgs/kiwisolver/type diff --git a/build/pkgs/kiwisolver/SPKG.txt b/build/pkgs/kiwisolver/SPKG.txt new file mode 100644 index 00000000000..537b9aae123 --- /dev/null +++ b/build/pkgs/kiwisolver/SPKG.txt @@ -0,0 +1,31 @@ += 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/checksums.ini b/build/pkgs/kiwisolver/checksums.ini new file mode 100644 index 00000000000..1ca0ba338f4 --- /dev/null +++ b/build/pkgs/kiwisolver/checksums.ini @@ -0,0 +1,4 @@ +tarball=kiwisolver-VERSION.tar.gz +sha1=093c2348a53fe18d42983ddbd823911b21781928 +md5=e2a1718b837e2cd001f7c06934616fcd +cksum=4236707026 diff --git a/build/pkgs/kiwisolver/dependencies b/build/pkgs/kiwisolver/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/kiwisolver/dependencies @@ -0,0 +1,5 @@ +$(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/kiwisolver/package-version.txt b/build/pkgs/kiwisolver/package-version.txt new file mode 100644 index 00000000000..7dea76edb3d --- /dev/null +++ b/build/pkgs/kiwisolver/package-version.txt @@ -0,0 +1 @@ +1.0.1 diff --git a/build/pkgs/kiwisolver/spkg-install b/build/pkgs/kiwisolver/spkg-install new file mode 100644 index 00000000000..deba1bb42bb --- /dev/null +++ b/build/pkgs/kiwisolver/spkg-install @@ -0,0 +1 @@ +cd src && sdh_pip_install . diff --git a/build/pkgs/kiwisolver/type b/build/pkgs/kiwisolver/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/kiwisolver/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/matplotlib/SPKG.txt b/build/pkgs/matplotlib/SPKG.txt index 9480a03150d..5b8e0e6ea63 100644 --- a/build/pkgs/matplotlib/SPKG.txt +++ b/build/pkgs/matplotlib/SPKG.txt @@ -33,6 +33,7 @@ The matplotlib mailing lists: see http://sourceforge.net/projects/matplotlib * dateutil * pyparsing * tornado + * kiwisolver == Build Instructions/Changes == diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index ff8a8f84834..ae9567ba538 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,4 +1,4 @@ tarball=matplotlib-VERSION.tar.bz2 -sha1=4fa346a0ea9a9c2b7e59045c5947230c07067e03 -md5=9583848157456284620f78277b531f9f -cksum=1698365803 +sha1=891e723c88ec2f5003d09692e9591c66a3c664c9 +md5=7f244740247cec0455b56e4749a99fec +cksum=2094355640 diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 7ec1d6db408..b1b25a5ffae 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -2.1.0 +2.2.2 diff --git a/build/pkgs/matplotlib/spkg-src b/build/pkgs/matplotlib/spkg-src index 8ad76b7c268..8dc3fd7202b 100755 --- a/build/pkgs/matplotlib/spkg-src +++ b/build/pkgs/matplotlib/spkg-src @@ -7,7 +7,7 @@ set -e # Set the version and its download URL. -VERSION=2.1.0 +VERSION=2.2.2 DOWNLOAD_URL="https://github.com/matplotlib/matplotlib/archive/v${VERSION}.tar.gz" # fetch and unpack latest version diff --git a/src/sage/plot/histogram.py b/src/sage/plot/histogram.py index 6f2b8631976..b174cb959ff 100644 --- a/src/sage/plot/histogram.py +++ b/src/sage/plot/histogram.py @@ -66,8 +66,8 @@ def get_minmax_data(self): EXAMPLES:: - sage: H = histogram([10,3,5], normed=True); h = H[0] - sage: h.get_minmax_data() + sage: H = histogram([10,3,5], density=True); h = H[0] + sage: h.get_minmax_data() # rel tol 1e-15 {'xmax': 10.0, 'xmin': 3.0, 'ymax': 0.4761904761904765, 'ymin': 0} sage: G = histogram([random() for _ in range(500)]); g = G[0] sage: g.get_minmax_data() # random output @@ -83,7 +83,7 @@ def get_minmax_data(self): options=self.options() opt=dict(range = options.pop('range',None), bins = options.pop('bins',None), - normed = options.pop('normed',None), + density = options.pop('density',None), weights = options.pop('weights', None)) #check to see if a list of datasets @@ -140,7 +140,7 @@ def _allowed_options(self): 'rwidth': 'The relative width of the bars as a fraction of the bin width', 'cumulative': '(True or False) If True, then a histogram is computed in which each bin gives the counts in that bin plus all bins for smaller values. Negative values give a reversed direction of accumulation.', 'range': 'A list [min, max] which define the range of the histogram. Values outside of this range are treated as outliers and omitted from counts.', - 'normed': '(True or False) If True, the counts are normalized to form a probability density. (n/(len(x)*dbin)', + 'density': '(True or False) If True, the counts are normalized to form a probability density. (n/(len(x)*dbin)', 'weights': 'A sequence of weights the same length as the data list. If supplied, then each value contributes its associated weight to the bin count.', 'stacked': '(True or False) If True, multiple data are stacked on top of each other.', 'label': 'A string label for each data list given.'} @@ -186,7 +186,7 @@ def _render_on_subplot(self, subplot): subplot.hist(self.datalist.transpose(), **options) -@options(aspect_ratio='automatic',align='mid', weights=None, range=None, bins=10, normed=False, edgecolor='black') +@options(aspect_ratio='automatic',align='mid', weights=None, range=None, bins=10, density=False, edgecolor='black') def histogram(datalist, **options): """ Computes and draws the histogram for list(s) of numerical data. @@ -218,7 +218,7 @@ def histogram(datalist, **options): - ``linewidth`` -- (float) width of the lines defining the bars - ``linestyle`` -- (default: 'solid') Style of the line. One of 'solid' or '-', 'dashed' or '--', 'dotted' or ':', 'dashdot' or '-.' - - ``normed`` -- (boolean - default: False) If True, the counts are + - ``density`` -- (boolean - default: False) If True, the counts are normalized to form a probability density. - ``range`` -- A list [min, max] which define the range of the histogram. Values outside of this range are treated as outliers and @@ -245,11 +245,11 @@ def histogram(datalist, **options): Graphics object consisting of 1 graphics primitive We can see how the histogram compares to various distributions. - Note the use of the ``normed`` keyword to guarantee the plot + Note the use of the ``density`` keyword to guarantee the plot looks like the probability density function:: sage: nv = normalvariate - sage: H = histogram([nv(0,1) for _ in range(1000)], bins=20, normed=True, range=[-5,5]) + sage: H = histogram([nv(0,1) for _ in range(1000)], bins=20, density=True, range=[-5,5]) sage: P = plot( 1/sqrt(2*pi)*e^(-x^2/2), (x,-5,5), color='red', linestyle='--') sage: H+P Graphics object consisting of 2 graphics primitives diff --git a/src/sage/probability/probability_distribution.pyx b/src/sage/probability/probability_distribution.pyx index 797ca571bc2..1b119e323fe 100644 --- a/src/sage/probability/probability_distribution.pyx +++ b/src/sage/probability/probability_distribution.pyx @@ -134,7 +134,7 @@ cdef class ProbabilityDistribution: """ import pylab l = [float(self.get_random_element()) for _ in range(num_samples)] - S = pylab.hist(l, bins, normed=True) + S = pylab.hist(l, bins, density=True) return [list(S[0]), list(S[1])] def generate_histogram_plot(self, name, num_samples = 1000, bins = 50): @@ -167,7 +167,7 @@ cdef class ProbabilityDistribution: """ import pylab l = [float(self.get_random_element()) for _ in range(num_samples)] - pylab.hist(l, bins, normed=True) + pylab.hist(l, bins, density=True) pylab.savefig(name) @@ -186,7 +186,7 @@ cdef class SphericalDistribution(ProbabilityDistribution): sage: T = SphericalDistribution() sage: T.get_random_element() # rel tol 1e-14 (-0.2922296724828204, -0.9563459345927822, 0.0020668595602153454) - sage: T = SphericalDistribution(dimension = 4, rng = 'luxury') + sage: T = SphericalDistribution(dimension=4, rng='luxury') sage: T.get_random_element() # rel tol 1e-14 (-0.0363300434761631, 0.6459885817544098, 0.24825817345598158, 0.7209346430129753) From 082479d5b7cd9571a3a8e353db4a980f2346d2ec Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 6 Aug 2018 09:33:15 +0200 Subject: [PATCH 213/284] Keep supporting "normed" in addition to "density" --- src/sage/plot/histogram.py | 49 ++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/sage/plot/histogram.py b/src/sage/plot/histogram.py index b174cb959ff..5d284737319 100644 --- a/src/sage/plot/histogram.py +++ b/src/sage/plot/histogram.py @@ -78,35 +78,49 @@ def get_minmax_data(self): sage: Z = histogram([[1,3,2,0], [4,4,3,3]]); z = Z[0] sage: z.get_minmax_data() {'xmax': 4.0, 'xmin': 0, 'ymax': 2, 'ymin': 0} + + TESTS:: + + sage: h = histogram([10,3,5], normed=True)[0] + sage: h.get_minmax_data() # rel tol 1e-15 + {'xmax': 10.0, 'xmin': 3.0, 'ymax': 0.4761904761904765, 'ymin': 0} """ import numpy - options=self.options() - opt=dict(range = options.pop('range',None), - bins = options.pop('bins',None), - density = options.pop('density',None), - weights = options.pop('weights', None)) - + + # Extract these options (if they are not None) and pass them to + # histogram() + options = self.options() + opt = {} + for key in ('range', 'bins', 'normed', 'density', 'weights'): + try: + value = options[key] + except KeyError: + pass + else: + if value is not None: + opt[key] = value + #check to see if a list of datasets - if not hasattr(self.datalist[0],'__contains__' ): - ydata,xdata=numpy.histogram(self.datalist, **opt) + if not hasattr(self.datalist[0], '__contains__'): + ydata, xdata = numpy.histogram(self.datalist, **opt) return minmax_data(xdata,[0]+list(ydata), dict=True) else: m = { 'xmax': 0, 'xmin':0, 'ymax':0, 'ymin':0} - if not options.pop('stacked',None): + if not options.get('stacked'): for d in self.datalist: - ydata, xdata = numpy.histogram(d,**opt) + ydata, xdata = numpy.histogram(d, **opt) m['xmax'] = max([m['xmax']] + list(xdata)) m['xmin'] = min([m['xmin']] + list(xdata)) m['ymax'] = max([m['ymax']] + list(ydata)) return m else: for d in self.datalist: - ydata, xdata = numpy.histogram(d,**opt) + ydata, xdata = numpy.histogram(d, **opt) m['xmax'] = max([m['xmax']] + list(xdata)) m['xmin'] = min([m['xmin']] + list(xdata)) m['ymax'] = m['ymax'] + max(list(ydata)) return m - + def _allowed_options(self): """ Return the allowed options with descriptions for this graphics @@ -123,8 +137,6 @@ def _allowed_options(self): 'How the bars align inside of each bin. Acceptable values are "left", "right" or "mid".') sage: L[-1] ('zorder', 'The layer level to draw the histogram') - sage: len(L) - 18 """ return {'color': 'The color of the face of the bars or list of colors if multiple data sets are given.', 'edgecolor':'The color of the border of each bar.', @@ -135,12 +147,13 @@ def _allowed_options(self): 'linewidth':'Width of the lines defining the bars', 'linestyle': "One of 'solid' or '-', 'dashed' or '--', 'dotted' or ':', 'dashdot' or '-.'", 'zorder':'The layer level to draw the histogram', - 'bins': 'The number of sections in which to divide the range. Also can be a sequence of points within the range that create the partition.', + 'bins': 'The number of sections in which to divide the range. Also can be a sequence of points within the range that create the partition.', 'align': 'How the bars align inside of each bin. Acceptable values are "left", "right" or "mid".', 'rwidth': 'The relative width of the bars as a fraction of the bin width', 'cumulative': '(True or False) If True, then a histogram is computed in which each bin gives the counts in that bin plus all bins for smaller values. Negative values give a reversed direction of accumulation.', - 'range': 'A list [min, max] which define the range of the histogram. Values outside of this range are treated as outliers and omitted from counts.', - 'density': '(True or False) If True, the counts are normalized to form a probability density. (n/(len(x)*dbin)', + 'range': 'A list [min, max] which define the range of the histogram. Values outside of this range are treated as outliers and omitted from counts.', + 'normed': 'Deprecated alias for density', + 'density': '(True or False) If True, the counts are normalized to form a probability density. (n/(len(x)*dbin)', 'weights': 'A sequence of weights the same length as the data list. If supplied, then each value contributes its associated weight to the bin count.', 'stacked': '(True or False) If True, multiple data are stacked on top of each other.', 'label': 'A string label for each data list given.'} @@ -186,7 +199,7 @@ def _render_on_subplot(self, subplot): subplot.hist(self.datalist.transpose(), **options) -@options(aspect_ratio='automatic',align='mid', weights=None, range=None, bins=10, density=False, edgecolor='black') +@options(aspect_ratio='automatic',align='mid', weights=None, range=None, bins=10, edgecolor='black') def histogram(datalist, **options): """ Computes and draws the histogram for list(s) of numerical data. From 197cabbd430f5d6f51f8007cb8b6ea6f18f83256 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 6 Aug 2018 13:19:17 +0200 Subject: [PATCH 214/284] Split database_pari --- build/pkgs/database_pari/SPKG.txt | 25 ------------------- build/pkgs/database_pari/checksums.ini | 4 --- build/pkgs/database_pari/spkg-check | 7 ------ build/pkgs/pari_elldata/SPKG.txt | 18 +++++++++++++ build/pkgs/pari_elldata/checksums.ini | 4 +++ .../dependencies | 2 +- .../package-version.txt | 0 .../spkg-install | 0 .../pkgs/{database_pari => pari_elldata}/type | 0 build/pkgs/pari_galdata/spkg-install | 2 +- build/pkgs/pari_galpol/SPKG.txt | 20 +++++++++++++++ build/pkgs/pari_galpol/checksums.ini | 4 +++ build/pkgs/pari_galpol/dependencies | 5 ++++ build/pkgs/pari_galpol/package-version.txt | 1 + build/pkgs/pari_galpol/spkg-install | 1 + build/pkgs/pari_galpol/type | 1 + build/pkgs/pari_nftables/SPKG.txt | 19 ++++++++++++++ build/pkgs/pari_nftables/checksums.ini | 4 +++ build/pkgs/pari_nftables/dependencies | 5 ++++ build/pkgs/pari_nftables/package-version.txt | 1 + build/pkgs/pari_nftables/spkg-install | 1 + build/pkgs/pari_nftables/type | 1 + build/pkgs/pari_seadata/SPKG.txt | 23 +++++++++++++++++ build/pkgs/pari_seadata/checksums.ini | 4 +++ build/pkgs/pari_seadata/dependencies | 5 ++++ build/pkgs/pari_seadata/package-version.txt | 1 + build/pkgs/pari_seadata/spkg-install | 1 + build/pkgs/pari_seadata/type | 1 + build/pkgs/pari_seadata_small/spkg-install | 2 +- src/sage/tests/parigp.py | 16 ++++++------ 30 files changed, 131 insertions(+), 47 deletions(-) delete mode 100644 build/pkgs/database_pari/SPKG.txt delete mode 100644 build/pkgs/database_pari/checksums.ini delete mode 100644 build/pkgs/database_pari/spkg-check create mode 100644 build/pkgs/pari_elldata/SPKG.txt create mode 100644 build/pkgs/pari_elldata/checksums.ini rename build/pkgs/{database_pari => pari_elldata}/dependencies (89%) rename build/pkgs/{database_pari => pari_elldata}/package-version.txt (100%) rename build/pkgs/{database_pari => pari_elldata}/spkg-install (100%) rename build/pkgs/{database_pari => pari_elldata}/type (100%) mode change 120000 => 100644 build/pkgs/pari_galdata/spkg-install create mode 100644 build/pkgs/pari_galpol/SPKG.txt create mode 100644 build/pkgs/pari_galpol/checksums.ini create mode 100644 build/pkgs/pari_galpol/dependencies create mode 100644 build/pkgs/pari_galpol/package-version.txt create mode 100644 build/pkgs/pari_galpol/spkg-install create mode 100644 build/pkgs/pari_galpol/type create mode 100644 build/pkgs/pari_nftables/SPKG.txt create mode 100644 build/pkgs/pari_nftables/checksums.ini create mode 100644 build/pkgs/pari_nftables/dependencies create mode 100644 build/pkgs/pari_nftables/package-version.txt create mode 100644 build/pkgs/pari_nftables/spkg-install create mode 100644 build/pkgs/pari_nftables/type create mode 100644 build/pkgs/pari_seadata/SPKG.txt create mode 100644 build/pkgs/pari_seadata/checksums.ini create mode 100644 build/pkgs/pari_seadata/dependencies create mode 100644 build/pkgs/pari_seadata/package-version.txt create mode 100644 build/pkgs/pari_seadata/spkg-install create mode 100644 build/pkgs/pari_seadata/type mode change 120000 => 100644 build/pkgs/pari_seadata_small/spkg-install diff --git a/build/pkgs/database_pari/SPKG.txt b/build/pkgs/database_pari/SPKG.txt deleted file mode 100644 index 48f0bf8a214..00000000000 --- a/build/pkgs/database_pari/SPKG.txt +++ /dev/null @@ -1,25 +0,0 @@ -= database_pari = - -== Description == - -The collection of optional PARI packages elldata, seadata, galpol, -nftables (galdata is included in the standard PARI spkg). -See http://pari.math.u-bordeaux.fr/packages.html - -== 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 - -== Special Update/Build Instructions == - -Download the four mentioned tarballs and extract them, then move them -out of the top-level directory data. diff --git a/build/pkgs/database_pari/checksums.ini b/build/pkgs/database_pari/checksums.ini deleted file mode 100644 index 4a2a4db76c8..00000000000 --- a/build/pkgs/database_pari/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=database_pari-VERSION.tar.bz2 -sha1=dd2b168da033a9fca98ce5d9d81ad76fbc1739fb -md5=0fb372a3c8de787f84c066f81511a276 -cksum=3988008667 diff --git a/build/pkgs/database_pari/spkg-check b/build/pkgs/database_pari/spkg-check deleted file mode 100644 index babbdda597a..00000000000 --- a/build/pkgs/database_pari/spkg-check +++ /dev/null @@ -1,7 +0,0 @@ -if [ -z "$SAGE_SRC" ]; then - echo >&2 "SAGE_SRC undefined ... exiting" - echo >&2 "Maybe run 'sage --sh'?" - exit 1 -fi - -sage -tp --long --optional=database_pari,sage "$SAGE_SRC/sage/tests/parigp.py" "$SAGE_SRC/sage/libs/pari" diff --git a/build/pkgs/pari_elldata/SPKG.txt b/build/pkgs/pari_elldata/SPKG.txt new file mode 100644 index 00000000000..a51f439e39e --- /dev/null +++ b/build/pkgs/pari_elldata/SPKG.txt @@ -0,0 +1,18 @@ += 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/checksums.ini b/build/pkgs/pari_elldata/checksums.ini new file mode 100644 index 00000000000..d3858e5dde1 --- /dev/null +++ b/build/pkgs/pari_elldata/checksums.ini @@ -0,0 +1,4 @@ +tarball=elldata.tgz +sha1=85f51ef85390f0f737fba10567d7d47f99249780 +md5=0ae49b7aa6a012ccd2804bfb831d3686 +cksum=1365963558 diff --git a/build/pkgs/database_pari/dependencies b/build/pkgs/pari_elldata/dependencies similarity index 89% rename from build/pkgs/database_pari/dependencies rename to build/pkgs/pari_elldata/dependencies index f1e43e6a8d3..c1b713883fe 100644 --- a/build/pkgs/database_pari/dependencies +++ b/build/pkgs/pari_elldata/dependencies @@ -1,4 +1,4 @@ -pari +| $(SAGERUNTIME) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/database_pari/package-version.txt b/build/pkgs/pari_elldata/package-version.txt similarity index 100% rename from build/pkgs/database_pari/package-version.txt rename to build/pkgs/pari_elldata/package-version.txt diff --git a/build/pkgs/database_pari/spkg-install b/build/pkgs/pari_elldata/spkg-install similarity index 100% rename from build/pkgs/database_pari/spkg-install rename to build/pkgs/pari_elldata/spkg-install diff --git a/build/pkgs/database_pari/type b/build/pkgs/pari_elldata/type similarity index 100% rename from build/pkgs/database_pari/type rename to build/pkgs/pari_elldata/type diff --git a/build/pkgs/pari_galdata/spkg-install b/build/pkgs/pari_galdata/spkg-install deleted file mode 120000 index 7229eb863f5..00000000000 --- a/build/pkgs/pari_galdata/spkg-install +++ /dev/null @@ -1 +0,0 @@ -../database_pari/spkg-install \ No newline at end of file diff --git a/build/pkgs/pari_galdata/spkg-install b/build/pkgs/pari_galdata/spkg-install new file mode 100644 index 00000000000..45141f24b56 --- /dev/null +++ b/build/pkgs/pari_galdata/spkg-install @@ -0,0 +1 @@ +sdh_install src/* "$GP_DATA_DIR" diff --git a/build/pkgs/pari_galpol/SPKG.txt b/build/pkgs/pari_galpol/SPKG.txt new file mode 100644 index 00000000000..a05341cd1bd --- /dev/null +++ b/build/pkgs/pari_galpol/SPKG.txt @@ -0,0 +1,20 @@ += 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/checksums.ini b/build/pkgs/pari_galpol/checksums.ini new file mode 100644 index 00000000000..530a5a72f09 --- /dev/null +++ b/build/pkgs/pari_galpol/checksums.ini @@ -0,0 +1,4 @@ +tarball=galpol.tgz +sha1=b072d752207bf598c86c896dc2ce35314fd9aff4 +md5=6ebd6057f1b252f7f9b77525c27f447d +cksum=547352040 diff --git a/build/pkgs/pari_galpol/dependencies b/build/pkgs/pari_galpol/dependencies new file mode 100644 index 00000000000..c1b713883fe --- /dev/null +++ b/build/pkgs/pari_galpol/dependencies @@ -0,0 +1,5 @@ +| $(SAGERUNTIME) + +---------- +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/pari_galpol/package-version.txt b/build/pkgs/pari_galpol/package-version.txt new file mode 100644 index 00000000000..00064c890bc --- /dev/null +++ b/build/pkgs/pari_galpol/package-version.txt @@ -0,0 +1 @@ +20180625 diff --git a/build/pkgs/pari_galpol/spkg-install b/build/pkgs/pari_galpol/spkg-install new file mode 100644 index 00000000000..45141f24b56 --- /dev/null +++ b/build/pkgs/pari_galpol/spkg-install @@ -0,0 +1 @@ +sdh_install src/* "$GP_DATA_DIR" diff --git a/build/pkgs/pari_galpol/type b/build/pkgs/pari_galpol/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pari_galpol/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pari_nftables/SPKG.txt b/build/pkgs/pari_nftables/SPKG.txt new file mode 100644 index 00000000000..310faf0a446 --- /dev/null +++ b/build/pkgs/pari_nftables/SPKG.txt @@ -0,0 +1,19 @@ += 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/checksums.ini b/build/pkgs/pari_nftables/checksums.ini new file mode 100644 index 00000000000..3463064475d --- /dev/null +++ b/build/pkgs/pari_nftables/checksums.ini @@ -0,0 +1,4 @@ +tarball=nftables.tgz +sha1=90df66222346b0dbca68925671a374f5e286daeb +md5=82788524f36bbbba886785c940c859db +cksum=1478870095 diff --git a/build/pkgs/pari_nftables/dependencies b/build/pkgs/pari_nftables/dependencies new file mode 100644 index 00000000000..3546cda4614 --- /dev/null +++ b/build/pkgs/pari_nftables/dependencies @@ -0,0 +1,5 @@ +# no dependencies + +---------- +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/pari_nftables/package-version.txt b/build/pkgs/pari_nftables/package-version.txt new file mode 100644 index 00000000000..13731bb57cf --- /dev/null +++ b/build/pkgs/pari_nftables/package-version.txt @@ -0,0 +1 @@ +20080929 diff --git a/build/pkgs/pari_nftables/spkg-install b/build/pkgs/pari_nftables/spkg-install new file mode 100644 index 00000000000..b0dde4fa426 --- /dev/null +++ b/build/pkgs/pari_nftables/spkg-install @@ -0,0 +1 @@ +sdh_install src/* "$GP_DATA_DIR/nftables" diff --git a/build/pkgs/pari_nftables/type b/build/pkgs/pari_nftables/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pari_nftables/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pari_seadata/SPKG.txt b/build/pkgs/pari_seadata/SPKG.txt new file mode 100644 index 00000000000..ececd8350a0 --- /dev/null +++ b/build/pkgs/pari_seadata/SPKG.txt @@ -0,0 +1,23 @@ += 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/checksums.ini b/build/pkgs/pari_seadata/checksums.ini new file mode 100644 index 00000000000..097c3a558d7 --- /dev/null +++ b/build/pkgs/pari_seadata/checksums.ini @@ -0,0 +1,4 @@ +tarball=seadata.tgz +sha1=fa3deb36df0ce71a466eb0ff0d4a18d48d44e8b9 +md5=6e9c119ccb3c65916a48e1a8cd899558 +cksum=3015139491 diff --git a/build/pkgs/pari_seadata/dependencies b/build/pkgs/pari_seadata/dependencies new file mode 100644 index 00000000000..c1b713883fe --- /dev/null +++ b/build/pkgs/pari_seadata/dependencies @@ -0,0 +1,5 @@ +| $(SAGERUNTIME) + +---------- +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/pari_seadata/package-version.txt b/build/pkgs/pari_seadata/package-version.txt new file mode 100644 index 00000000000..5a0327d9bc6 --- /dev/null +++ b/build/pkgs/pari_seadata/package-version.txt @@ -0,0 +1 @@ +20090618 diff --git a/build/pkgs/pari_seadata/spkg-install b/build/pkgs/pari_seadata/spkg-install new file mode 100644 index 00000000000..45141f24b56 --- /dev/null +++ b/build/pkgs/pari_seadata/spkg-install @@ -0,0 +1 @@ +sdh_install src/* "$GP_DATA_DIR" diff --git a/build/pkgs/pari_seadata/type b/build/pkgs/pari_seadata/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pari_seadata/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pari_seadata_small/spkg-install b/build/pkgs/pari_seadata_small/spkg-install deleted file mode 120000 index 7229eb863f5..00000000000 --- a/build/pkgs/pari_seadata_small/spkg-install +++ /dev/null @@ -1 +0,0 @@ -../database_pari/spkg-install \ No newline at end of file diff --git a/build/pkgs/pari_seadata_small/spkg-install b/build/pkgs/pari_seadata_small/spkg-install new file mode 100644 index 00000000000..45141f24b56 --- /dev/null +++ b/build/pkgs/pari_seadata_small/spkg-install @@ -0,0 +1 @@ +sdh_install src/* "$GP_DATA_DIR" diff --git a/src/sage/tests/parigp.py b/src/sage/tests/parigp.py index 45cf398dae1..c118b6eb374 100644 --- a/src/sage/tests/parigp.py +++ b/src/sage/tests/parigp.py @@ -41,13 +41,13 @@ Check that the optional PARI databases work:: - sage: gp.ellinit('"299998a1"') # optional -- database_pari + sage: gp.ellinit('"299998a1"') # optional -- pari_elldata [1, 0, 1, 110, -3660, ...] sage: E = EllipticCurve("1728ba1") - sage: gp(E).ellidentify() # optional -- database_pari + sage: gp(E).ellidentify() # optional -- pari_elldata [["1728ba1", [0, 0, 0, -6, 6], [[1, 1]]], [1, 0, 0, 0]] - sage: pari("ellmodulareqn(211)") # optional -- database_pari + sage: pari("ellmodulareqn(211)") # optional -- pari_seadata [x^212 + (-y^7 + 5207*y^6 - 10241606*y^5 + 9430560101*y^4 - 4074860204015*y^3 + 718868274900397*y^2 - 34897101275826114*y + 104096378056356968)*x^211... The following requires the modular polynomials up to degree 223, while @@ -55,18 +55,18 @@ sage: p = next_prime(2^328) sage: E = EllipticCurve(GF(p), [6,1]) - sage: E.cardinality() # long time (108s on sage.math, 2013), optional -- database_pari + sage: E.cardinality() # long time (108s on sage.math, 2013), optional -- pari_seadata 546812681195752981093125556779405341338292357723293496548601032930284335897180749997402596957976244 Create a number field with Galois group `A4`. Group `A4` corresponds to transitive group `(12,3)` in GAP:: sage: R. = PolynomialRing(ZZ) - sage: pol = pari("galoisgetpol(12,3)[1]") # optional -- database_pari - sage: K. = NumberField(R(pol)) # optional -- database_pari - sage: factor(K.discriminant()) # optional -- database_pari + sage: pol = pari("galoisgetpol(12,3)[1]") # optional -- pari_galpol + sage: K. = NumberField(R(pol)) # optional -- pari_galpol + sage: factor(K.discriminant()) # optional -- pari_galpol 163^8 - sage: [F.degree() for F,a,b in K.subfields()] # optional -- database_pari + sage: [F.degree() for F,a,b in K.subfields()] # optional -- pari_galpol [1, 3, 4, 4, 4, 4, 6, 6, 6, 12] sage: sorted([12/H.cardinality() for H in AlternatingGroup(4).subgroups()]) [1, 3, 4, 4, 4, 4, 6, 6, 6, 12] From 8592da73198b34872a6d45692f1f06c30fa43d2a Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 7 Aug 2018 10:32:04 +0000 Subject: [PATCH 215/284] py3: use the repr() of floats instead of the str() for consistency between python 2 and 3 this is a follow up to #25247 --- src/sage/rings/polynomial/real_roots.pyx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index bd5d9f90b9a..459eabfe25d 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -349,9 +349,9 @@ cdef class interval_bernstein_polynomial: sage: bp = mk_ibpf([0.5, 0.2, -0.9, -0.7, 0.99], neg_err=-0.1, pos_err=0.01) sage: bp1, bp2, _ = bp.try_split(mk_context(), None) sage: bp1 - + sage: bp2 - + """ (p1, p2, ok) = self.de_casteljau(ctx, QQ_1_2) ctx.dc_log_append(("half" + self._type_code(), self.scale_log2, self.bitsize, ok, logging_note)) @@ -387,9 +387,9 @@ cdef class interval_bernstein_polynomial: sage: bp = mk_ibpf([0.5, 0.2, -0.9, -0.7, 0.99], neg_err=-0.1, pos_err=0.01) sage: bp1, bp2, _ = bp.try_rand_split(mk_context(), None) sage: bp1 # rel tol - + sage: bp2 # rel tol - + """ # We want a split point which is a dyadic rational (denominator @@ -758,7 +758,7 @@ cdef class interval_bernstein_polynomial_integer(interval_bernstein_polynomial): sage: print(bp.as_float()) degree 4 IBP with floating-point coefficients sage: bp.as_float() - + """ (fcoeffs, neg_err, pos_err, scale_log2_delta) = intvec_to_doublevec(self.coeffs, self.error) cdef interval_bernstein_polynomial_float fbp = interval_bernstein_polynomial_float(fcoeffs, self.lower, self.upper, self.lsign, self.usign, neg_err, pos_err, self.scale_log2 + scale_log2_delta, self.level, self.slope_err) @@ -1429,7 +1429,7 @@ cdef class interval_bernstein_polynomial_float(interval_bernstein_polynomial): sage: repr(bp) '' """ - base = "%s + [%s .. %s]" % (self.coeffs, self.neg_err, self.pos_err) + base = "%s + [%r .. %r]" % (self.coeffs, self.neg_err, self.pos_err) if self.scale_log2 != 0: base = "(%s) * 2^%d" % (base, self.scale_log2) s = " + sage: bp2 - + sage: bp1, bp2, ok = bp.de_casteljau(ctx, 2/3) sage: bp1 # rel tol 2e-16 - + sage: bp2 # rel tol 3e-15 - + sage: bp1, bp2, ok = bp.de_casteljau(ctx, 7/39) sage: bp1 # rel tol - + sage: bp2 # rel tol - + """ (c1_, c2_, err_inc) = de_casteljau_doublevec(self.coeffs, mid) cdef Vector_real_double_dense c1 = c1_ From bec801e3b085e866a88906561c912d6b03ef52a3 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 7 Aug 2018 10:33:20 +0000 Subject: [PATCH 216/284] py3: use randstate.python_random instead of using the random module directly If nothing else this gives more consistent test results in Python 2 and 3. We still re-seed the Python Random with the seed passed to 'context' so that the pseudo-random results are consistent with what they were before this change --- src/sage/rings/polynomial/real_roots.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 459eabfe25d..abd41ee40d0 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -133,11 +133,11 @@ from https://wiki.sagemath.org/days4schedule . from __future__ import print_function, absolute_import from copy import copy -from random import Random import time from sage.rings.all import ZZ, QQ, RR, AA, RealField, RealIntervalField, RIF, RDF, infinity from sage.arith.all import binomial, factorial +from sage.misc.randstate import randstate from sage.modules.all import vector, FreeModule from sage.matrix.all import MatrixSpace from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -4273,7 +4273,7 @@ cdef class context: Initialize a context class. """ self.seed = seed # saved to make context printable - self.random = Random() + self.random = randstate().python_random() self.random.seed(seed) self.do_logging = do_logging self.wordsize = wordsize From 6fa8bc6a16cfb7ce61598f38052729775cf898c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Tue, 7 Aug 2018 20:59:48 +0200 Subject: [PATCH 217/284] Builds fail with 8.4.beta0 but the output is truncated and it is not clear what's the issue. In general it makes sense to keep a bitter chunk of the output as the part towards the end is usually more interesting. --- .ci/head-tail.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/head-tail.sh b/.ci/head-tail.sh index 8ecf9f879f0..036161dbc37 100755 --- a/.ci/head-tail.sh +++ b/.ci/head-tail.sh @@ -1,6 +1,6 @@ #!/bin/sh -OFFSET=80 +OFFSET=1024 # This script reads from stdin and prints to stdout as long as a the output # does not exceed a certain number of bytes. When reading an EOF it prints the # last $OFFSET lines if they have not been printed normally already. From a7e3891517a22245bcb51494340056ce4265f1b2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 8 Aug 2018 13:01:27 +1000 Subject: [PATCH 218/284] Fixed _latex_ doctest in quantum_group_gap.py by adding "# random". --- src/sage/algebras/quantum_groups/quantum_group_gap.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index 4502ba255d2..1dbd2f1b113 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -1547,11 +1547,9 @@ def _latex_(self): sage: V = Q.highest_weight_module([1,0]) # optional - gap_packages sage: T = tensor([V,V]) # optional - gap_packages sage: S = T.highest_weight_decomposition()[0] # optional - gap_packages - sage: latex(S) # optional - gap_packages - \begin{tikzpicture}... - %% + sage: latex(S) # optional - gap_packages # random (depends on dot2tex) + \begin{tikzpicture} ... - % \end{tikzpicture} """ from sage.misc.latex import latex From 2a716bde30812c41c2906806b2b8d8041155d153 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 8 Aug 2018 21:37:39 +1000 Subject: [PATCH 219/284] Some reviewer changes. --- src/doc/en/reference/references/index.rst | 8 +- src/sage/combinat/posets/poset_examples.py | 159 ++++++++++++--------- 2 files changed, 93 insertions(+), 74 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3039e3c8359..6c3466a3859 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2551,6 +2551,10 @@ REFERENCES: *The 128-bit blockcipher CLEFIA (extended abstract)*; in FSE, (2007), pp. 181-195. +.. [ST2010] Einar Steingrimmsson and Bridget Tenner. + *The Moebius Function of the Permutation Pattern Poset*, + Journal of Combinatorics 1 (2010), 39-52 + .. [ST2011] \A. Schilling, P. Tingley. *Demazure crystals, Kirillov-Reshetikhin crystals, and the energy function*. Electronic Journal of Combinatorics. **19(2)**. 2012. @@ -2587,10 +2591,6 @@ REFERENCES: Mathematical Society, Vol. 355, No. 12 (Dec., 2003), pp. 4807--4823 -.. [ST2010] Einar Steingrimmsson and Bridget Tenner - *The Moebius Function of the Permutation Pattern Poset*, - Journal of Combinatorics 1 (2010), 39-52 - .. [Sti2006] Douglas R. Stinson. *Cryptography: Theory and Practice*. 3rd edition, Chapman \& Hall/CRC, 2006. diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 3a4ffdd9573..fdf31349463 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -83,7 +83,7 @@ from sage.misc.classcall_metaclass import ClasscallMetaclass import sage.categories.posets -from sage.combinat.permutation import Permutations, Permutation +from sage.combinat.permutation import Permutations, Permutation, to_standard from sage.combinat.posets.posets import Poset, FinitePoset, FinitePosets_n from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, JoinSemilattice, FiniteLatticePoset) @@ -1502,20 +1502,20 @@ def YoungFibonacci(n): D.relabel(lambda v: Word(v), inplace=True) return FiniteMeetSemilattice(hasse_diagram=D, category=FinitePosets()) - - @staticmethod def PermutationPattern(n): r""" - Return the poset of permutations under pattern containment up to rank `n`. + Return the poset of permutations under pattern containment + up to rank ``n``. INPUT: - ``n`` -- a positive integer - A permutation `u=u_1\ldots u_n` contains the pattern `v=v_1\ldots v_m` - if there is a (not necessarily consecutive) subsequence of `u` - of length `m` whose entries have the same relative order as `v`. + A permutation `u = u_1 \cdots u_n` contains the pattern + `v = v_1 \cdots v_m` if there is a (not necessarily consecutive) + subsequence of `u` of length `m` whose entries have the same + relative order as `v`. See :wikipedia:`Permutation_pattern`. @@ -1526,97 +1526,111 @@ def PermutationPattern(n): sage: sorted(P4.lower_covers(Permutation([2,4,1,3]))) [[1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2]] - .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern` + .. SEEALSO:: + :meth:`~sage.combinat.permutation.Permutation.has_pattern` TESTS:: sage: posets.PermutationPattern(1) Finite poset containing 1 elements + sage: posets.PermutationPattern(2) + Finite poset containing 3 elements """ try: n = Integer(n) except TypeError: - raise TypeError("number of elements must be an integer, not {0}".format(n)) + raise TypeError("number of elements must be an integer, not {}".format(n)) if n <= 0: - raise ValueError("number of elements must be nonnegative, not {0}".format(n)) + raise ValueError("number of elements must be nonnegative, not {}".format(n)) elem = [] - for i in range(1,n+1): + for i in range(1, n+1): elem += Permutations(i) - return(Poset((elem,lambda a,b : b.has_pattern(a)))) - + return Poset((elem, lambda a,b: b.has_pattern(a))) @staticmethod - def PermutationPatternInterval(bottom,top): + def PermutationPatternInterval(bottom, top): r""" Return the poset consisting of an interval in the poset of permutations - under pattern containment between ``bottom`` and ``top`` + under pattern containment between ``bottom`` and ``top``. INPUT: - - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` as a pattern. + - ``bottom``, ``top`` -- permutations where ``top`` contains + ``bottom`` as a pattern - A permutation `u=u_1\ldots u_n` contains the pattern `v=v_1\ldots v_m` - if there is a (not necessarily consecutive) subsequence of `u` - of length `m` whose entries have the same relative order as `v`. + A permutation `u = u_1 \cdots u_n` contains the pattern + `v = v_1 \cdots v_m` if there is a (not necessarily consecutive) + subsequence of `u` of length `m` whose entries have the same + relative order as `v`. See :wikipedia:`Permutation_pattern`. EXAMPLES:: - sage: R=posets.PermutationPatternInterval(Permutation([2,3,1]),Permutation([4,6,2,3,5,1]));R + sage: t = Permutation([2,3,1]) + sage: b = Permutation([4,6,2,3,5,1]) + sage: R = posets.PermutationPatternInterval(t, b); R Finite poset containing 14 elements sage: R.moebius_function(R.bottom(),R.top()) -4 - .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern`, - :meth:`PermutationPattern` + .. SEEALSO:: + :meth:`~sage.combinat.permutation.Permutation.has_pattern`, + :meth:`PermutationPattern` TESTS:: - sage: posets.PermutationPatternInterval(Permutation([1]),Permutation([1])) + sage: p = Permutation([1]) + sage: posets.PermutationPatternInterval(p, p) Finite poset containing 1 elements """ - top = Permutation(top) - bottom = Permutation(bottom) + P = Permutations() + top = P(top) + bottom = P(bottom) if not top.has_pattern(bottom): - raise ValueError("{0} doesn't contain {1} as a pattern".format(top,bottom)) + raise ValueError("{} doesn't contain {} as a pattern".format(top, bottom)) elem = [[top]] # Make a list of lists of elements in the interval divided by rank. - # List will be flattened at the end + # List will be flattened at the end level = 0 # Consider the top element to be level 0, and then go down from there. rel = [] # List of covering relations to be fed into poset constructor. - while len(top)-len(bottom) >= level+1: + while len(top) - len(bottom) >= level + 1: elem.append([]) # Add a new empty level - for upper in elem[level]: # Run through all permutations on current level - # find relations for which it is upper cover - for i in range(0,len(top)-level): # Try and remove the ith element from the permutation + for upper in elem[level]: + # Run through all permutations on current level + # and find relations for which it is upper cover + upper_perm = P(upper) + for i in range(len(top)-level): + # Try and remove the ith element from the permutation lower = list(upper) j = lower.pop(i) - for k in range(0,len(top)-level-1): #Standardize result - if lower[k]>j: - lower[k] = lower[k]-1 - if Permutation(lower).has_pattern(bottom):# Check to see if result is in interval - rel += [[Permutation(lower),Permutation(upper)]] + for k in range(len(top)-level-1): # Standardize result + if lower[k] > j: + lower[k] = lower[k] - 1 + lower_perm = P(lower) + if lower_perm.has_pattern(bottom): # Check to see if result is in interval + rel += [[lower_perm, upper_perm]] if lower not in elem[level+1]: - elem[level+1].append(Permutation(lower)) + elem[level+1].append(lower_perm) level += 1 elem = [item for sublist in elem for item in sublist] - return(Poset((elem,rel))) + return Poset((elem,rel)) @staticmethod def PermutationPatternOccurenceInterval(bottom, top, pos): r""" - Return the poset consisting of an interval in the poset of permutations - under pattern containment between ``bottom`` and ``top``, where a specified - instance of 'bottom' in ``top`` must be maintained. + Return the poset consisting of an interval in the poset of + permutations under pattern containment between ``bottom`` and + ``top``, where a specified instance of ``bottom`` in ``top`` + must be maintained. INPUT: - - ``bottom``, ``top`` -- permutations where ``top`` contains ``bottom`` - as a pattern. + - ``bottom``, ``top`` -- permutations where ``top`` contains + ``bottom`` as a pattern - ``pos`` -- a list of indices indicating a distinguished copy of - ``bottom`` inside ``top`` (indexed starting at 0) + ``bottom`` inside ``top`` (indexed starting at 0) For futher information (and picture illustrating included example), see [ST2010]_ . @@ -1625,42 +1639,47 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): EXAMPLES:: - sage: A = posets.PermutationPatternOccurenceInterval(Permutation([3,2,1]),Permutation([6,3,4,5,2,1]),(0,2,4));A + sage: t = Permutation([3,2,1]) + sage: b = Permutation([6,3,4,5,2,1]) + sage: A = posets.PermutationPatternOccurenceInterval(t, b, (0,2,4)); A Finite poset containing 8 elements + .. SEEALSO:: - .. SEEALSO:: :meth:`~sage.combinat.permutation.Permutation.has_pattern`, - :meth:`PermutationPattern`, :meth:`PermutationPatternInterval` + :meth:`~sage.combinat.permutation.Permutation.has_pattern`, + :meth:`PermutationPattern`, :meth:`PermutationPatternInterval` """ - from copy import copy - import sage.combinat.permutation as permutation - top = Permutation(top) - bottom = Permutation(bottom) - if not permutation.to_standard([top[z] for z in pos]) == list(bottom): # check input - print("error, or empty") - elem = [[(top,pos)]] + P = Permutations() + top = P(top) + bottom = P(bottom) + if not to_standard([top[z] for z in pos]) == list(bottom): # check input + raise ValueError("cannot find 'bottom' in 'top' given by 'pos'") + elem = [[(top, pos)]] level = 0 rel = [] - while len(top)-len(bottom) >= level+1: + while len(top) - len(bottom) >= level + 1: elem.append([]) # Add a new empty level for upper in elem[level]: - for i in range(0,len(top)-level): # Try and remove the ith element from the permutation - if i not in upper[1]: - lower_perm = list(upper[0]) - j = lower_perm.pop(i) - for e in range(0,len(top)-level-1): # - if lower_perm[e]>j: - lower_perm[e] = lower_perm[e]-1 - lower_pos=list(copy(upper[1])) - for f in range(len(upper[1])): - if upper[1][f]>i: - lower_pos[f] = upper[1][f] - 1 - rel += [[(Permutation(lower_perm),tuple(lower_pos)),(Permutation(upper[0]),upper[1])]] - if (Permutation(lower_perm),tuple(lower_pos)) not in elem[level+1]: - elem[level+1].append((Permutation(lower_perm),tuple(lower_pos))) + for i in range(len(top)-level): + # Try and remove the ith element from the permutation + if i in upper[1]: + continue + lower_perm = list(upper[0]) + j = lower_perm.pop(i) + for e in range(len(top)-level-1): + if lower_perm[e] > j: + lower_perm[e] = lower_perm[e] - 1 + lower_pos = list(upper[1]) + for f in range(len(upper[1])): + if upper[1][f] > i: + lower_pos[f] = upper[1][f] - 1 + rel += [[(P(lower_perm), tuple(lower_pos)), + (P(upper[0]), upper[1])]] + if (P(lower_perm), tuple(lower_pos)) not in elem[level+1]: + elem[level+1].append((P(lower_perm), tuple(lower_pos))) level += 1 elem = [item for sublist in elem for item in sublist] - return(Poset([elem,rel])) + return Poset([elem,rel]) From 52b4b8b8ad5d3f937078158f099353cf1b48160d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 14:00:52 +0200 Subject: [PATCH 220/284] fixing invalid escape sequences in groups (final) --- .../algebras/quantum_groups/quantum_group_gap.py | 2 +- src/sage/crypto/mq/sr.py | 14 +++++++------- src/sage/doctest/parsing.py | 2 +- src/sage/doctest/sources.py | 4 ++-- src/sage/groups/abelian_gps/abelian_group.py | 4 ++-- src/sage/groups/abelian_gps/dual_abelian_group.py | 3 +-- src/sage/groups/abelian_gps/values.py | 5 ++--- src/sage/groups/affine_gps/affine_group.py | 4 ++-- src/sage/groups/affine_gps/group_element.py | 2 +- src/sage/groups/braid.py | 2 +- src/sage/groups/matrix_gps/matrix_group.py | 2 +- src/sage/groups/matrix_gps/orthogonal.py | 2 +- src/sage/groups/perm_gps/permgroup.py | 8 ++++---- src/sage/groups/perm_gps/permgroup_named.py | 11 ++++++----- .../semimonomial_transformation_group.py | 2 +- 15 files changed, 33 insertions(+), 34 deletions(-) diff --git a/src/sage/algebras/quantum_groups/quantum_group_gap.py b/src/sage/algebras/quantum_groups/quantum_group_gap.py index 4502ba255d2..cac1d48817a 100644 --- a/src/sage/algebras/quantum_groups/quantum_group_gap.py +++ b/src/sage/algebras/quantum_groups/quantum_group_gap.py @@ -854,7 +854,7 @@ def _mul_(self, other): return self.__class__(self.parent(), self._libgap * other._libgap) def bar(self): - """ + r""" Return the bar involution on ``self``. The bar involution is defined by diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 7b1263d3f11..6457d918054 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1080,7 +1080,7 @@ def random_state_array(self, *args, **kwds): return random_matrix(self.base_ring(), self._r, self._c, *args, **kwds) def random_vector(self, *args, **kwds): - """ + r""" Return a random vector as it might appear in the algebraic expression of self. @@ -1534,7 +1534,7 @@ def _insert_matrix_into_matrix(self, dst, src, row, col): def varformatstr(self, name, n=None, rc=None, e=None): - """ + r""" Return a format string which is understood by print et al. If a numerical value is omitted, the default value of ``self`` @@ -1913,7 +1913,7 @@ def round_polynomials(self, i, plaintext=None, ciphertext=None): return tuple(lin + sbox) def key_schedule_polynomials(self, i): - """ + r""" Return polynomials for the `i`-th round of the key schedule. @@ -2251,7 +2251,7 @@ def phi(self, l): raise TypeError def antiphi(self, l): - """ + r""" The operation `\phi^{-1}` from [MR2002]_ or the inverse of ``self.phi``. INPUT: @@ -2472,7 +2472,7 @@ def inversion_polynomials(self, xi, wi, length): return [xi[j, 0]*wi[j, 0] + 1 for j in range(length)] def field_polynomials(self, name, i, l=None): - """ + r""" Return list of conjugacy polynomials for a given round ``i`` and name ``name``. @@ -2657,7 +2657,7 @@ def phi(self, l, diffusion_matrix=False): else: raise TypeError def antiphi(self, l): - """ + r""" The operation `\phi^{-1}` from [MR2002]_ or the inverse of ``self.phi``. INPUT: @@ -3197,7 +3197,7 @@ def inversion_polynomials(self, xi, wi, length): return l def field_polynomials(self, name, i, l=None): - """ + r""" Return list of field polynomials for a given round ``i`` and name ``name``. diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 04212f20396..3bf6b60c522 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -38,7 +38,7 @@ from .external import available_software -float_regex = re.compile('\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') +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)*))') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) find_sage_continuation = re.compile(r"^(\s*)\.\.\.\.:", re.M) diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index ac74b4cfb4e..113e0f0bdad 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -36,7 +36,7 @@ from sage.env import SAGE_SRC, SAGE_LOCAL # Python file parsing -triple_quotes = re.compile("\s*[rRuU]*((''')|(\"\"\"))") +triple_quotes = re.compile(r"\s*[rRuU]*((''')|(\"\"\"))") name_regex = re.compile(r".*\s(\w+)([(].*)?:") # LaTeX file parsing @@ -50,7 +50,7 @@ link_all = re.compile(r"^\s*\.\.\s+linkall\s*$") double_colon = re.compile(r"^(\s*).*::\s*$") -whitespace = re.compile("\s*") +whitespace = re.compile(r"\s*") bitness_marker = re.compile('#.*(32|64)-bit') bitness_value = '64' if sys.maxsize > (1 << 32) else '32' diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index e19199b949f..26054e7fae9 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -869,8 +869,8 @@ def _latex_(self): sage: F._latex_() '$\\mathrm{AbelianGroup}( 10, (2, 2, 2, 2, 2, 2, 2, 2, 2, 2) )$' """ - s = "$\mathrm{AbelianGroup}( %s, %s )$"%(self.ngens(), self.gens_orders()) - return s + return r"$\mathrm{AbelianGroup}( %s, %s )$" % (self.ngens(), + self.gens_orders()) def _gap_init_(self): r""" diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 1ec4b94c2a7..80c580f8e3b 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -208,8 +208,7 @@ def _latex_(self): sage: Fd._latex_() '$\\mathrm{DualAbelianGroup}( AbelianGroup ( 3, (2, 2, 2) ) )$' """ - s = "$\mathrm{DualAbelianGroup}( AbelianGroup ( %s, %s ) )$"%(self.ngens(), self.gens_orders()) - return s + return r"$\mathrm{DualAbelianGroup}( AbelianGroup ( %s, %s ) )$" % (self.ngens(), self.gens_orders()) def random_element(self): """ diff --git a/src/sage/groups/abelian_gps/values.py b/src/sage/groups/abelian_gps/values.py index 8ff2c8544c3..288fb95b93c 100644 --- a/src/sage/groups/abelian_gps/values.py +++ b/src/sage/groups/abelian_gps/values.py @@ -1,4 +1,4 @@ -""" +r""" Multiplicative Abelian Groups With Values Often, one ends up with a set that forms an Abelian group. It would be @@ -78,9 +78,8 @@ from sage.groups.abelian_gps.abelian_group_element import AbelianGroupElement - def AbelianGroupWithValues(values, n, gens_orders=None, names='f', check=False, values_group=None): - """ + r""" Construct an Abelian group with values associated to the generators. INPUT: diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index b310cb83911..a04968138fc 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -351,7 +351,7 @@ def linear_space(self): return MatrixSpace(self.base_ring(), dp, dp) def linear(self, A): - """ + r""" Construct the general linear transformation by ``A``. INPUT: @@ -374,7 +374,7 @@ def linear(self, A): return self.element_class(self, A, self.vector_space().zero(), check=True, convert=False) def translation(self, b): - """ + r""" Construct the translation by ``b``. INPUT: diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index fbb84068d85..bc8257b5471 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -47,7 +47,7 @@ class AffineGroupElement(MultiplicativeGroupElement): - """ + r""" An affine group element. INPUT: diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index d62a85d97b8..48c0022e7c6 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -172,7 +172,7 @@ def components_in_closure(self): return self.strands() - sum(len(c)-1 for c in cycles) def burau_matrix(self, var='t', reduced=False): - """ + r""" Return the Burau matrix of the braid. INPUT: diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 6debf1c3d95..aaf2340c42d 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -589,7 +589,7 @@ def _check_matrix(self, x_sage, x_gap): TypeError: matrix is not in the finitely generated group """ from sage.libs.gap.libgap import libgap - libgap_contains = libgap.eval('\in') + libgap_contains = libgap.eval(r'\in') is_contained = libgap_contains(x_gap, self.gap()) if not is_contained.sage(): raise TypeError('matrix is not in the finitely generated group') diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index c188b4714cf..071520aadf5 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -400,7 +400,7 @@ def invariant_bilinear_form(self): @cached_method def invariant_quadratic_form(self): - """ + r""" Return the quadratic form preserved by the orthogonal group. OUTPUT: diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 6ee5639a769..06facceb9f1 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -1363,7 +1363,7 @@ def gap_to_output(x, depth, container): return tuple(result) def transversals(self, point): - """ + r""" If G is a permutation group acting on the set `X = \{1, 2, ...., n\}` and H is the stabilizer subgroup of , a right (respectively left) transversal is a set containing exactly @@ -1522,7 +1522,7 @@ def base(self, seed=None): return [self._domain_from_gap[x] for x in self._gap_().StabChain(seed).BaseStabChain().sage()] def strong_generating_system(self, base_of_group=None): - """ + r""" Return a Strong Generating System of ``self`` according the given base for the right action of ``self`` on itself. @@ -2736,8 +2736,8 @@ def cohomology(self, n, p = 0): @hap_decorator def cohomology_part(self, n, p = 0): - """ - Computes the p-part of the group cohomology `H^n(G, F)`, + r""" + Compute the p-part of the group cohomology `H^n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if `p > 0` is a prime. diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index da1774e96c5..f4602cd3708 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -463,7 +463,7 @@ def major_index(self, parameter=None): return q_factorial(self.degree(), parameter) def conjugacy_classes_representatives(self): - """ + r""" Return a complete list of representatives of conjugacy classes in a permutation group `G`. @@ -568,7 +568,7 @@ def conjugacy_class(self, g): return SymmetricGroupConjugacyClass(self, g) def algebra(self, base_ring, category=None): - """ + r""" Return the symmetric group algebra associated to ``self``. INPUT: @@ -626,6 +626,7 @@ def algebra(self, base_ring, category=None): Element = SymmetricGroupElement + class AlternatingGroup(PermutationGroup_symalt): def __init__(self, domain=None): """ @@ -1368,7 +1369,7 @@ def _repr_(self): class DihedralGroup(PermutationGroup_unique): def __init__(self, n): - """ + r""" The Dihedral group of order `2n` for any integer `n\geq 1`. INPUT: @@ -2692,10 +2693,10 @@ def __str__(self): return "The projective special linear group of degree %s over %s"%(self._n, self.base_ring()) def ramification_module_decomposition_hurwitz_curve(self): - """ + r""" Helps compute the decomposition of the ramification module for the Hurwitz curves X (over CC say) with automorphism group - G = PSL(2,q), q a "Hurwitz prime" (ie, p is $\pm 1 \pmod 7$). + G = PSL(2,q), q a "Hurwitz prime" (ie, p is `\pm 1 \pmod 7`). Using this computation and Borne's formula helps determine the G-module structure of the RR spaces of equivariant divisors can be determined explicitly. diff --git a/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py b/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py index f0ad1ea5c16..cd54e9cf576 100644 --- a/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py +++ b/src/sage/groups/semimonomial_transformations/semimonomial_transformation_group.py @@ -392,7 +392,7 @@ def _latex_(self): ring_latex = self.base_ring()._latex_() return ('\\left(' + ring_latex + '^' + str(self.degree()) + '\\wr' + SymmetricGroup(self.degree())._latex_() + - ' \\right) \\rtimes \operatorname{Aut}(' + ring_latex + ')') + ' \\right) \\rtimes \\operatorname{Aut}(' + ring_latex + ')') class SemimonomialActionVec(Action): From 7532096e8fbfdc710803d55c76736726d871433c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 14:14:57 +0200 Subject: [PATCH 221/284] fixing invalid escape sequences in matroids (final) --- src/sage/interacts/library.py | 29 ++++++++++++---------- src/sage/libs/coxeter3/coxeter_group.py | 4 +-- src/sage/libs/eclib/interface.py | 3 +-- src/sage/libs/gap/operations.py | 2 +- src/sage/matroids/catalog.py | 12 ++++----- src/sage/matroids/dual_matroid.py | 2 +- src/sage/matroids/matroids_plot_helpers.py | 6 ++--- src/sage/matroids/minor_matroid.py | 10 ++++---- src/sage/matroids/utilities.py | 3 ++- 9 files changed, 36 insertions(+), 35 deletions(-) diff --git a/src/sage/interacts/library.py b/src/sage/interacts/library.py index 35ba7ea10f6..51394652aa3 100644 --- a/src/sage/interacts/library.py +++ b/src/sage/interacts/library.py @@ -138,10 +138,12 @@ def taylor_polynomial( dot = point((x0,f(x=x0)),pointsize=80,rgbcolor=(1,0,0)) ft = f.taylor(x,x0,order) pt = plot(ft,(-1, 5), color='green', thickness=2) - html('$f(x)\;=\;%s$'%latex(f)) - html('$\hat{f}(x;%s)\;=\;%s+\mathcal{O}(x^{%s})$'%(x0,latex(ft),order+1)) + html(r'$f(x)\;=\;%s$' % latex(f)) + html(r'$\hat{f}(x;%s)\;=\;%s+\mathcal{O}(x^{%s})$' % (x0, latex(ft), + order + 1)) show(dot + p + pt, ymin = -.5, ymax = 1) + @library_interact def definite_integral( title = text_control('

    Definite integral

    '), @@ -256,9 +258,10 @@ def function_derivative( else: show(plots, xmin=x_range[0], xmax=x_range[1], ymin=y_range[0], ymax=y_range[1]) - html("
    $\color{Blue}{f(x) = %s}$
    "%latex(f(x))) - html("
    $\color{Green}{f'(x) = %s}$
    "%latex(df(x))) - html("
    $\color{Red}{f''(x) = %s}$
    "%latex(ddf(x))) + html(r"
    $\color{Blue}{f(x) = %s}$
    " % latex(f(x))) + html(r"
    $\color{Green}{f'(x) = %s}$
    " % latex(df(x))) + html(r"
    $\color{Red}{f''(x) = %s}$
    " % latex(ddf(x))) + @library_interact def difference_quotient( @@ -922,7 +925,7 @@ def _newton_method(f, c, maxn, h): html(r"${f(c) = }%s"%latex(f(c))) html(r"$%s \text{ iterations}"%len(midpoints)) if list_steps: - s = [["$n$","$x_n$","$f(x_n)$", "$f(x_n-h)\,f(x_n+h)$"]] + s = [["$n$", "$x_n$", "$f(x_n)$", r"$f(x_n-h)\,f(x_n+h)$"]] for i, c in enumerate(midpoints): s.append([i+1, c, f(c), (c-h)*f(c+h)]) pretty_print(table(s, header_row=True)) @@ -1012,7 +1015,7 @@ def trapezoid_integration( sum_values_html = r"\frac{%.2f}{2} \cdot \left[%.2f + %s + %.2f\right]" % ( h, N(ys[0], digits=5), - ' + '.join([ "2\cdot %.2f" % N(i, digits=5) for i in ys[1:-1]]), + ' + '.join([ r"2\cdot %.2f" % N(i, digits=5) for i in ys[1:-1]]), N(ys[n], digits=5) ) @@ -1032,7 +1035,7 @@ def trapezoid_integration( N(approx, digits=7) )) elif output_form == 'table': - s = [['$i$','$x_i$','$f(x_i)$','$m$','$m\cdot f(x_i)$']] + s = [['$i$', '$x_i$', '$f(x_i)$', '$m$', r'$m\cdot f(x_i)$']] for i in range(0,n+1): if i==0 or i==n: j = 1 @@ -1151,14 +1154,14 @@ def parabola(a, b, c): N(approx,digits=7) )) elif output_form == 'table': - s = [['$i$','$x_i$','$f(x_i)$','$m$','$m\cdot f(x_i)$']] + s = [['$i$', '$x_i$', '$f(x_i)$', '$m$', r'$m\cdot f(x_i)$']] for i in range(0,n+1): if i==0 or i==n: j = 1 else: j = (i+1)%2*(-2)+4 s.append([i, xs[i], ys[i],j,N(j*ys[i])]) - s.append(['','','','$\sum$','$%s$'%latex(3/dx*approx)]) + s.append(['', '', '', r'$\sum$', '$%s$' % latex(3/dx*approx)]) pretty_print(table(s, header_row=True)) html(r'$\int_{%.2f}^{%.2f} {f(x) \, \mathrm{d}x}\approx\frac {%.2f}{3}\cdot %s=%s$'% (interval[0], interval[1],dx,latex(3/dx*approx),latex(approx))) @@ -1230,14 +1233,14 @@ def riemann_sum( delka_intervalu=[division[i+1]-division[i] for i in range(n)] if list_table: pretty_print(table([ - ["$i$", "$[x_{i-1},x_i]$", "$\eta_i$", "$f(\eta_i)$", "$x_{i}-x_{i-1}$"] + ["$i$", "$[x_{i-1},x_i]$", r"$\eta_i$", r"$f(\eta_i)$", "$x_{i}-x_{i-1}$"] ] + [ [i+1,[division[i],division[i+1]],xs[i],ys[i],delka_intervalu[i]] for i in range(n) ], header_row=True)) - html('Riemann sum: $\displaystyle\sum_{i=1}^{%s} f(\eta_i)(x_i-x_{i-1})=%s$ '% + html(r'Riemann sum: $\displaystyle\sum_{i=1}^{%s} f(\eta_i)(x_i-x_{i-1})=%s$ '% (latex(n),latex(sum([ys[i]*delka_intervalu[i] for i in range(n)])))) - html('Exact value of the integral $\displaystyle\int_{%s}^{%s}%s\,\mathrm{d}x=%s$'% + html(r'Exact value of the integral $\displaystyle\int_{%s}^{%s}%s\,\mathrm{d}x=%s$'% (latex(a),latex(b),latex(func(x)),latex(integral_numerical(func(x),a,b)[0]))) diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index e9a22d5256f..a480e1243f2 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -341,7 +341,7 @@ def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True): return ZZq(d) def parabolic_kazhdan_lusztig_polynomial(self, u, v, J, constant_term_one=True): - """ + r""" Return the parabolic Kazhdan-Lusztig polynomial `P_{u,v}^{-,J}`. INPUT: @@ -540,7 +540,7 @@ def __len__(self): length = __len__ def bruhat_le(self, v): - """ + r""" Return whether ``self`` `\le` ``v`` in Bruhat order. EXAMPLES:: diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index f77000c478e..985f37b9f1c 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -335,7 +335,7 @@ def two_descent(self, second_limit = 8, n_aux = -1, second_descent = True): - """ + r""" Compute 2-descent data for this curve. INPUT: @@ -410,7 +410,6 @@ def two_descent(self, True sage: E.two_descent(verbose=False) True - """ from sage.libs.eclib.mwrank import _two_descent # import here to save time first_limit = int(first_limit) diff --git a/src/sage/libs/gap/operations.py b/src/sage/libs/gap/operations.py index 6f1d9ecf85d..92ffc3d58d4 100644 --- a/src/sage/libs/gap/operations.py +++ b/src/sage/libs/gap/operations.py @@ -22,7 +22,7 @@ NameFunction = libgap.function_factory('NameFunction') -NAME_RE = re.compile('(Setter|Getter|Tester)\((.*)\)') +NAME_RE = re.compile(r'(Setter|Getter|Tester)\((.*)\)') class OperationInspector(SageObject): diff --git a/src/sage/matroids/catalog.py b/src/sage/matroids/catalog.py index ab3a68a352d..8c5702320db 100644 --- a/src/sage/matroids/catalog.py +++ b/src/sage/matroids/catalog.py @@ -155,7 +155,7 @@ def R6(): def Fano(): - """ + r""" Return the Fano matroid, represented over `GF(2)`. The Fano matroid, or Fano plane, or `F_7`, is a 7-element matroid of @@ -774,7 +774,7 @@ def CompleteGraphic(n): def Wheel(n, field=None, ring=None): - """ + r""" Return the rank-`n` wheel. INPUT: @@ -979,7 +979,7 @@ def PG(n, q, x=None): def AG(n, q, x=None): - """ + r""" Return the affine geometry of dimension ``n`` over the finite field of order ``q``. @@ -1233,7 +1233,7 @@ def TicTacToe(): def Q10(): - """ + r""" Return the matroid `Q_{10}`, represented over `\GF{4}`. `Q_{10}` is a 10-element, rank-5, self-dual matroid. It is representable @@ -1273,7 +1273,7 @@ def Q10(): def N1(): - """ + r""" Return the matroid `N_1`, represented over `\GF{3}`. `N_1` is an excluded minor for the dyadic matroids. See [Oxl2011]_, p. 554. @@ -1300,7 +1300,7 @@ def N1(): def N2(): - """ + r""" Return the matroid `N_2`, represented over `\GF{3}`. `N_2` is an excluded minor for the dyadic matroids. See [Oxl2011]_, p. 554. diff --git a/src/sage/matroids/dual_matroid.py b/src/sage/matroids/dual_matroid.py index ac5859eb8b9..d4ee0674b1c 100644 --- a/src/sage/matroids/dual_matroid.py +++ b/src/sage/matroids/dual_matroid.py @@ -355,7 +355,7 @@ def _minor(self, contractions=None, deletions=None): return DualMatroid(self._matroid._minor(contractions=deletions, deletions=contractions)) def dual(self): - """ + r""" Return the dual of the matroid. Let `M` be a matroid with ground set `E`. If `B` is the set of bases diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index c8a45eba7af..d3e9544a06a 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -81,7 +81,7 @@ def it(M, B1, nB1, lps): - """ + r""" Return points on and off the triangle and lines to be drawn for a rank 3 matroid. @@ -133,10 +133,8 @@ def it(M, B1, nB1, lps): .. NOTE:: - This method does NOT do any checks. - + This method does NOT do any checks. """ - tripts = [(0, 0), (1, 2), (2, 0)] pts = {} j = 0 diff --git a/src/sage/matroids/minor_matroid.py b/src/sage/matroids/minor_matroid.py index c902612f198..a5bace13e3b 100644 --- a/src/sage/matroids/minor_matroid.py +++ b/src/sage/matroids/minor_matroid.py @@ -359,10 +359,10 @@ def _repr_(self): 4: {{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'}}}' """ s = "M" - if len(self._contractions) > 0: - s = s + " / " + setprint_s(self._contractions, toplevel=True) - if len(self._deletions) > 0: - s = s + " \ " + setprint_s(self._deletions, toplevel=True) + if self._contractions: + s += r" / " + setprint_s(self._contractions, toplevel=True) + if self._deletions: + s += r" \ " + setprint_s(self._deletions, toplevel=True) s += ", where M is " + repr(self._matroid) return s @@ -511,7 +511,7 @@ def __deepcopy__(self, memo={}): return N def __reduce__(self): - """ + r""" Save the matroid for later reloading. EXAMPLES:: diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py index 448c7ba2adf..c85158756d4 100644 --- a/src/sage/matroids/utilities.py +++ b/src/sage/matroids/utilities.py @@ -650,8 +650,9 @@ def lift_cross_ratios(A, lift_map = None): return Z + def lift_map(target): - """ + r""" Create a lift map, to be used for lifting the cross ratios of a matroid representation. From 9508f3953d66d83724b0c55b792892a1eabfbe04 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Thu, 9 Aug 2018 14:28:41 +0200 Subject: [PATCH 222/284] Upgrade to Sphinx 1.7.6 --- build/pkgs/sphinx/checksums.ini | 6 +- build/pkgs/sphinx/package-version.txt | 2 +- build/pkgs/sphinx/patches/GH-5040.patch | 146 ------------------------ 3 files changed, 4 insertions(+), 150 deletions(-) delete mode 100644 build/pkgs/sphinx/patches/GH-5040.patch diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 353219f5388..fbc54a8db05 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,4 +1,4 @@ tarball=Sphinx-VERSION.tar.gz -sha1=515cd0dd74168622c2ab770679b65ac524e34c6a -md5=478a4d94506febd4786f47cc8ae297bb -cksum=2983614061 +sha1=3caa8c2d3f9b283e32df259baee885c0340c5d02 +md5=8fbd77d80c8e0966964751ab31a0204a +cksum=1266150694 diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index 122e92a1e81..3f7f430a6d4 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -1.7.5.p0 +1.7.6.p0 diff --git a/build/pkgs/sphinx/patches/GH-5040.patch b/build/pkgs/sphinx/patches/GH-5040.patch deleted file mode 100644 index 8e7aa75eb48..00000000000 --- a/build/pkgs/sphinx/patches/GH-5040.patch +++ /dev/null @@ -1,146 +0,0 @@ -See https://github.com/sphinx-doc/sphinx/pull/5040 - -commit 24cecfd0bc4bc3e9910d09e8bd1326cbff41e525 -Author: jfbu -Date: Tue Jun 5 09:56:36 2018 +0200 - - LaTeX: update list of language codes with " active - - cf docutils.writers.latex2e and LaTeX babel package main documentation, - section \babelshorthand which gives a list of shorthands used per - language. - -diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py -index bd88ca8..84c80cc 100644 ---- a/sphinx/writers/latex.py -+++ b/sphinx/writers/latex.py -@@ -201,9 +201,36 @@ class ExtBabel(Babel): - def get_shorthandoff(self): - # type: () -> unicode - shortlang = self.language.split('_')[0] -- if shortlang in ('de', 'ngerman', 'sl', 'slovene', 'pt', 'portuges', -- 'es', 'spanish', 'nl', 'dutch', 'pl', 'polish', 'it', -- 'italian', 'pt-BR', 'brazil'): -+ if shortlang in ('br', 'breton', -+ 'bg', 'bulgarian', -+ 'ca', 'catalan', -+ 'cs', 'czech', -+ 'da', 'danish', -+ 'de', 'ngerman', -+ 'de-1901', 'german', -+ 'de-AT', 'naustrian', -+ 'de-AT-1901', 'austrian', -+ 'es', 'spanish', -+ 'et', 'estonian', -+ 'eu', 'basque', -+ 'hsb', 'uppersorbian', -+ 'gl', 'galician', -+ 'is', 'icelandic', -+ 'it', 'italian', -+ 'fi', 'finnish', -+ 'nn', 'nynorsk', -+ 'no', 'norsk', -+ 'nl', 'dutch', -+ 'pl', 'polish', -+ 'pt', 'pt-PT', 'portuges', -+ 'pt-BR', 'brazil', -+ 'ru', 'russian', -+ 'sh-Latn', 'serbian', -+ 'sk', 'slovak', -+ 'sl', 'slovene', -+ 'sq', 'albanian', -+ 'sv', 'swedish', -+ 'uk', 'ukrainian'): - return '\\ifnum\\catcode`\\"=\\active\\shorthandoff{"}\\fi' - elif shortlang in ('tr', 'turkish'): - # memo: if ever Sphinx starts supporting 'Latin', do as for Turkish -commit 66741b1ee9040d7f4d30e4ee5f79e87b2d2a3074 -Author: jfbu -Date: Tue Jun 5 10:08:40 2018 +0200 - - LaTeX: make deactivation of = and " systematic - - This avoids maintaining list of languages making " (or worse =) active - TeX characters. - -diff --git a/sphinx/writers/latex.py b/sphinx/writers/latex.py -index 84c80cc..0e5462e 100644 ---- a/sphinx/writers/latex.py -+++ b/sphinx/writers/latex.py -@@ -200,42 +200,10 @@ class ExtBabel(Babel): - - def get_shorthandoff(self): - # type: () -> unicode -- shortlang = self.language.split('_')[0] -- if shortlang in ('br', 'breton', -- 'bg', 'bulgarian', -- 'ca', 'catalan', -- 'cs', 'czech', -- 'da', 'danish', -- 'de', 'ngerman', -- 'de-1901', 'german', -- 'de-AT', 'naustrian', -- 'de-AT-1901', 'austrian', -- 'es', 'spanish', -- 'et', 'estonian', -- 'eu', 'basque', -- 'hsb', 'uppersorbian', -- 'gl', 'galician', -- 'is', 'icelandic', -- 'it', 'italian', -- 'fi', 'finnish', -- 'nn', 'nynorsk', -- 'no', 'norsk', -- 'nl', 'dutch', -- 'pl', 'polish', -- 'pt', 'pt-PT', 'portuges', -- 'pt-BR', 'brazil', -- 'ru', 'russian', -- 'sh-Latn', 'serbian', -- 'sk', 'slovak', -- 'sl', 'slovene', -- 'sq', 'albanian', -- 'sv', 'swedish', -- 'uk', 'ukrainian'): -- return '\\ifnum\\catcode`\\"=\\active\\shorthandoff{"}\\fi' -- elif shortlang in ('tr', 'turkish'): -- # memo: if ever Sphinx starts supporting 'Latin', do as for Turkish -- return '\\ifnum\\catcode`\\=\\string=\\active\\shorthandoff{=}\\fi' -- return '' -+ return ('\\ifdefined\\shorthandoff\n' -+ ' \\ifnum\\catcode`\\=\\string=\\active\\shorthandoff{=}\\fi\n' -+ ' \\ifnum\\catcode`\\"=\\active\\shorthandoff{"}\\fi\n' -+ '\\fi') - - def uses_cyrillic(self): - # type: () -> bool -@@ -595,6 +563,7 @@ class LaTeXTranslator(nodes.NodeVisitor): - self.elements['classoptions'] = ',dvipdfmx' - # disable babel which has not publishing quality in Japanese - self.elements['babel'] = '' -+ self.elements['shorthandoff'] = '' - self.elements['multilingual'] = '' - # disable fncychap in Japanese documents - self.elements['fncychap'] = '' -diff --git a/tests/test_build_latex.py b/tests/test_build_latex.py -index 96ae140..68704d9 100644 ---- a/tests/test_build_latex.py -+++ b/tests/test_build_latex.py -@@ -467,7 +467,7 @@ def test_babel_with_language_ru(app, status, warning): - assert '\\addto\\captionsrussian{\\renewcommand{\\tablename}{Table.}}\n' in result - assert (u'\\addto\\extrasrussian{\\def\\pageautorefname' - u'{\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430}}\n' in result) -- assert '\\shorthandoff' not in result -+ assert '\\shorthandoff' in result - - - @pytest.mark.sphinx( -@@ -529,7 +529,7 @@ def test_babel_with_unknown_language(app, status, warning): - assert '\\addto\\captionsenglish{\\renewcommand{\\figurename}{Fig.}}\n' in result - assert '\\addto\\captionsenglish{\\renewcommand{\\tablename}{Table.}}\n' in result - assert '\\addto\\extrasenglish{\\def\\pageautorefname{page}}\n' in result -- assert '\\shorthandoff' not in result -+ assert '\\shorthandoff' in result - - assert "WARNING: no Babel option known for language 'unknown'" in warning.getvalue() - From 486d25d7ad3a550e70affd248cc6652e2b066191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 14:28:54 +0200 Subject: [PATCH 223/284] fixing invalid escape sequences in rings (final) --- src/sage/misc/dev_tools.py | 4 ++-- src/sage/misc/latex.py | 5 +++-- src/sage/misc/sagedoc.py | 4 ++-- src/sage/misc/sphinxify.py | 6 +++--- src/sage/numerical/optimize.py | 8 ++++---- src/sage/rings/number_field/number_field_rel.py | 6 +++--- src/sage/rings/padics/padic_base_generic.py | 2 +- src/sage/rings/padics/padic_base_leaves.py | 2 +- src/sage/rings/padics/padic_generic.py | 3 +-- .../rings/padics/unramified_extension_generic.py | 15 +++++++-------- 10 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/sage/misc/dev_tools.py b/src/sage/misc/dev_tools.py index 448a7309434..21e6253ea0d 100644 --- a/src/sage/misc/dev_tools.py +++ b/src/sage/misc/dev_tools.py @@ -177,7 +177,7 @@ def load_submodules(module=None, exclude_pattern=None): if module is None: import sage module = sage - exclude_pattern = "^sage\.libs|^sage\.tests|tests$|^sage\.all_|all$|sage\.interacts$|^sage\.misc\.benchmark$" + exclude_pattern = r"^sage\.libs|^sage\.tests|tests$|^sage\.all_|all$|sage\.interacts$|^sage\.misc\.benchmark$" if exclude_pattern: import re @@ -314,7 +314,7 @@ def find_object_modules(obj): # if the object is an instance, we try to guess where it is defined if sageinspect.isclassinstance(obj): - dec_pattern = re.compile("^(\w[\w0-9\_]*)\s*=", re.MULTILINE) + dec_pattern = re.compile(r"^(\w[\w0-9\_]*)\s*=", re.MULTILINE) module_to_obj2 = {} for module_name, obj_names in iteritems(module_to_obj): module_to_obj2[module_name] = [] diff --git a/src/sage/misc/latex.py b/src/sage/misc/latex.py index a417908c96f..0c202d0f7bf 100644 --- a/src/sage/misc/latex.py +++ b/src/sage/misc/latex.py @@ -258,8 +258,9 @@ def builtin_constant_function(x): sage: sage.misc.latex.EMBEDDED_MODE = False """ if EMBEDDED_MODE: - return "{\\rm %s}"%x - return "\mbox{\\rm %s}"%x + return "{\\rm %s}" % x + return "\\mbox{\\rm %s}" % x + def None_function(x): r""" diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 3f45b587b43..79db2a03048 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -324,7 +324,7 @@ def skip_TESTS_block(docstring): # underscores. # Also match uppercase text followed by a colon, like # "REFERENCES:" or "ALGORITHM:". - end_of_block = re.compile('[ ]*(\.\.[ ]+[-_A-Za-z]+|[A-Z]+):') + end_of_block = re.compile(r'[ ]*(\.\.[ ]+[-_A-Za-z]+|[A-Z]+):') # header: match a string of hyphens, or other characters which are # valid markers for reST headers: - = ` : ' " ~ _ ^ * + # < > header = re.compile(r'^[ ]*([-=`:\'"~_^*+#><])\1+[ ]*$') @@ -925,7 +925,7 @@ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='', # done with preparation; ready to start search for dirpath, dirs, files in os.walk(os.path.join(base_path, module)): for f in files: - if not f.startswith('.') and re.search("\.(" + "|".join(exts) + ")$", f): + if not f.startswith('.') and re.search(r"\.(" + "|".join(exts) + ")$", f): filename = os.path.join(dirpath, f) if re.search(path_re, filename): if multiline: diff --git a/src/sage/misc/sphinxify.py b/src/sage/misc/sphinxify.py index 8f426b59893..5b5b72edc7e 100644 --- a/src/sage/misc/sphinxify.py +++ b/src/sage/misc/sphinxify.py @@ -108,9 +108,9 @@ def sphinxify(docstring, format='html'): # "/media/...path.../blah.png" # to # "/doc/static/reference/media/...path.../blah.png" - output = re.sub("""src=['"](/?\.\.)*/?media/([^"']*)['"]""", - 'src="/doc/static/reference/media/\\2"', - output) + output = re.sub(r"""src=['"](/?\.\.)*/?media/([^"']*)['"]""", + 'src="/doc/static/reference/media/\\2"', + output) # Remove spurious \(, \), \[, \]. output = output.replace('\\(', '').replace('\\)', '').replace('\\[', '').replace('\\]', '') else: diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index 343863b013c..e7d73f1d375 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -19,7 +19,7 @@ def find_root(f, a, b, xtol=10e-13, rtol=2.0**-50, maxiter=100, full_output=False): - """ + r""" Numerically find a root of ``f`` on the closed interval `[a,b]` (or `[b,a]`) if possible, where ``f`` is a function in the one variable. Note: this function only works in fixed (machine) precision, it is not @@ -492,9 +492,9 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) return vector(RDF, min) -def linear_program(c,G,h,A=None,b=None,solver=None): - """ - Solves the dual linear programs: +def linear_program(c, G, h, A=None, b=None, solver=None): + r""" + Solve the dual linear programs: - Minimize `c'x` subject to `Gx + s = h`, `Ax = b`, and `s \geq 0` where `'` denotes transpose. diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index b3f9c9e5311..8a4e752ea5a 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -1387,9 +1387,9 @@ def relative_vector_space(self): return self.__relative_vector_space def absolute_vector_space(self): - """ - Return vector space over `\QQ` of self and isomorphisms from - the vector space to self and in the other direction. + r""" + Return vector space over `\QQ` of ``self`` and isomorphisms from + the vector space to ``self`` and in the other direction. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index 3d743027dfd..1196d05c20e 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -1,4 +1,4 @@ -""" +r""" `p`-Adic Base Generic A superclass for implementations of `\mathbb{Z}_p` and `\mathbb{Q}_p`. diff --git a/src/sage/rings/padics/padic_base_leaves.py b/src/sage/rings/padics/padic_base_leaves.py index 4aabe2f0b29..ff4e8f33a38 100644 --- a/src/sage/rings/padics/padic_base_leaves.py +++ b/src/sage/rings/padics/padic_base_leaves.py @@ -1,4 +1,4 @@ -""" +r""" `p`-Adic Base Leaves Implementations of `\mathbb{Z}_p` and `\mathbb{Q}_p` diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 327136c9488..4e3752c5260 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -997,7 +997,7 @@ def _test_convert_residue_field(self, **options): @cached_method def _log_unit_part_p(self): - """ + r""" Compute the logarithm of the unit-part of `p`. If `\pi` is the uniformizer in this ring, then we can uniquely write @@ -1021,7 +1021,6 @@ def _log_unit_part_p(self): sage: W. = R.extension(x^3-3*x-3) sage: W._log_unit_part_p() 2 + pi + 2*pi^2 + pi^4 + pi^5 + 2*pi^7 + 2*pi^8 + pi^9 + 2*pi^10 + pi^11 + pi^12 + 2*pi^14 + O(pi^15) - """ return self(self.prime()).unit_part().log() diff --git a/src/sage/rings/padics/unramified_extension_generic.py b/src/sage/rings/padics/unramified_extension_generic.py index 84acd7f72fc..f2344b02135 100644 --- a/src/sage/rings/padics/unramified_extension_generic.py +++ b/src/sage/rings/padics/unramified_extension_generic.py @@ -332,19 +332,18 @@ def has_pth_root(self): return self.ground_ring().has_pth_root() def has_root_of_unity(self, n): - """ - Returns whether or not `\ZZ_p` has a primitive `n^{\mbox{th}}` + r""" + Return whether or not `\ZZ_p` has a primitive `n^{\mbox{th}}` root of unity. INPUT: - - self -- a p-adic ring - - n -- an integer + - ``self`` -- a p-adic ring + - ``n`` -- an integer OUTPUT: - - boolean -- whether self has primitive `n^{\mbox{th}}` - root of unity + - boolean EXAMPLES:: @@ -356,7 +355,7 @@ def has_root_of_unity(self, n): sage: R.has_root_of_unity(11) False """ - if (self.prime() == 2): - return n.divides(2*(self.residue_class_field().order()-1)) + if self.prime() == 2: + return n.divides(2 * (self.residue_class_field().order() - 1)) else: return n.divides(self.residue_class_field().order() - 1) From df5c6bec1d26a62501a9737389330a370f523bd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 13:40:14 +0200 Subject: [PATCH 224/284] fixing invalid escape sequences in combinat (final step) --- src/sage/combinat/matrices/hadamard_matrix.py | 10 ++++++---- src/sage/combinat/matrices/latin.py | 17 ++++++++++------- src/sage/combinat/ncsf_qsym/ncsf.py | 2 +- src/sage/combinat/ncsf_qsym/qsym.py | 6 +++--- .../rigged_configurations/bij_type_D.py | 8 ++++---- .../rigged_configurations/kleber_tree.py | 7 ++++--- .../rigged_configurations/rc_infinity.py | 4 ++-- .../rigged_configuration_element.py | 6 +++--- .../rigged_configurations.py | 3 ++- .../species/combinatorial_logarithm.py | 2 +- src/sage/combinat/species/generating_series.py | 18 ++++++++++-------- src/sage/combinat/species/library.py | 6 ++++-- src/sage/combinat/species/series.py | 4 ++-- src/sage/combinat/species/species.py | 4 ++-- src/sage/combinat/species/structure.py | 2 +- 15 files changed, 55 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 0ecde78c50d..bec5e98bcda 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -88,9 +88,10 @@ def normalise_hadamard(H): H.rescale_row(i, -1) return H + def hadamard_matrix_paleyI(n, normalize=True): - """ - Implements the Paley type I construction. + r""" + Implement the Paley type I construction. The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a prime `p` (see [Hora]_). @@ -154,9 +155,10 @@ def hadamard_matrix_paleyI(n, normalize=True): H = normalise_hadamard(H) return H + def hadamard_matrix_paleyII(n): - """ - Implements the Paley type II construction. + r""" + Implement the Paley type II construction. The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a prime `p` (see [Hora]_). diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 03d31dae72e..1d53b66c4dc 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -1332,9 +1332,12 @@ def genus(T1, T2): cells_map, t1, t2, t3 = tau123(T1, T2) return (len(t1.to_cycles()) + len(t2.to_cycles()) + len(t3.to_cycles()) - T1.nr_filled_cells() - 2) // (-2) + def tau123(T1, T2): - """ - Compute the tau_i representation for a bitrade (T1, T2). See the + r""" + Compute the tau_i representation for a bitrade (T1, T2). + + See the functions tau1, tau2, and tau3 for the mathematical definitions. OUTPUT: @@ -1438,7 +1441,6 @@ def tau123(T1, T2): sage: len((t1*t2*t3).fixed_points()) == T1.nr_filled_cells() True """ - assert is_bitrade(T1, T2) cells_map = T1.filled_cells_map() @@ -1449,6 +1451,7 @@ def tau123(T1, T2): return (cells_map, t1, t2, t3) + def isotopism(p): """ Return a Permutation object that represents an isotopism (for rows, @@ -2332,7 +2335,7 @@ def group_to_LatinSquare(G): def alternating_group_bitrade_generators(m): - """ + r""" Construct generators a, b, c for the alternating group on 3m+1 points, such that a\*b\*c = 1. @@ -2359,7 +2362,6 @@ def alternating_group_bitrade_generators(m): [ 2 1 3 -1] [ 0 3 -1 2] """ - assert m >= 1 a = tuple(range(1, 2*m+1 + 1)) @@ -2464,7 +2466,7 @@ def p3_group_bitrade_generators(p): def check_bitrade_generators(a, b, c): - """ + r""" Three group elements a, b, c will generate a bitrade if a\*b\*c = 1 and the subgroups a, b, c intersect (pairwise) in just the identity. @@ -2483,7 +2485,8 @@ def check_bitrade_generators(a, b, c): B = PermutationGroup([b]) C = PermutationGroup([c]) - if a*b != c**(-1): return False + if a*b != c**(-1): + return False X = gap.Intersection(gap.Intersection(A, B), C) return X.Size() == 1 diff --git a/src/sage/combinat/ncsf_qsym/ncsf.py b/src/sage/combinat/ncsf_qsym/ncsf.py index d03d11aa9f5..d0d99241bb6 100644 --- a/src/sage/combinat/ncsf_qsym/ncsf.py +++ b/src/sage/combinat/ncsf_qsym/ncsf.py @@ -2416,7 +2416,7 @@ def C(i): return self._indices([i]) if i else self._indices([]) return T.sum_of_monomials( (C(j), C(i-j)) for j in range(0,i+1) ) class MultiplicativeBasesOnPrimitiveElements(Category_realization_of_parent): - """ + r""" Category of multiplicative bases of the non-commutative symmetric functions whose generators are primitive elements. diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index be1a2838f9f..e7070d8e8cb 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -600,7 +600,7 @@ def _repr_(self): sage: M._repr_() 'Quasisymmetric functions over the Integer Ring in the Monomial basis' """ - return "Quasisymmetric functions over the %s"%self.base_ring() + return "Quasisymmetric functions over the %s" % self.base_ring() def a_realization(self): r""" @@ -2730,7 +2730,7 @@ def coproduct_on_basis(self, compo): for i in range(0,len(compo)+1)) def product_on_basis(self, I, J): - """ + r""" The product on Essential basis elements. The product of the basis elements indexed by two compositions @@ -3363,7 +3363,7 @@ def __init_extra__(self): self._M_inverse_transition_matrices = {} def _precompute_cache(self, n, to_self_cache, from_self_cache, transition_matrices, inverse_transition_matrices, from_self_gen_function): - """ + r""" Compute the transition matrices between ``self`` and the monomial basis in the homogeneous components of degree `n`. The results are not returned, but rather stored in the caches. diff --git a/src/sage/combinat/rigged_configurations/bij_type_D.py b/src/sage/combinat/rigged_configurations/bij_type_D.py index 5b660866e24..b2b02e7ab70 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_D.py +++ b/src/sage/combinat/rigged_configurations/bij_type_D.py @@ -378,7 +378,7 @@ def doubling_map(self): self.ret_rig_con[i].vacancy_numbers[j] *= 2 def halving_map(self): - """ + r""" Perform the halving map of the rigged configuration at the current state of the bijection. @@ -677,7 +677,7 @@ def next_state(self, height): return(b) def doubling_map(self): - """ + r""" Perform the doubling map of the rigged configuration at the current state of the bijection. @@ -714,7 +714,7 @@ def doubling_map(self): partition.vacancy_numbers[j] *= 2 def halving_map(self): - """ + r""" Perform the halving map of the rigged configuration at the current state of the bijection. @@ -742,7 +742,7 @@ def halving_map(self): partition.vacancy_numbers[j] //= 2 def _correct_vacancy_nums(self): - """ + r""" Correct the vacancy numbers with special considerations for spinor columns. diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index e1faccd538a..78d14155876 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -462,9 +462,9 @@ def _latex_(self): ret_str = repr(self.multiplicity()) + ret_str for pair in self.weight: if pair[1] > 1: - ret_str += repr(pair[1]) + "\omega_{" + repr(pair[0]) + "}+" + ret_str += repr(pair[1]) + r"\omega_{" + repr(pair[0]) + "}+" elif pair[1] == 1: - ret_str += "\omega_{" + repr(pair[0]) + "}+" + ret_str += r"\omega_{" + repr(pair[0]) + "}+" if ret_str[-1] == '{': ret_str += "0}" @@ -1065,8 +1065,9 @@ def _element_constructor_(self, node_weight, dominant_root, parent_node=None): Element = KleberTreeNode + class VirtualKleberTree(KleberTree): - """ + r""" A virtual Kleber tree. We can use a modified version of the Kleber algorithm called the virtual diff --git a/src/sage/combinat/rigged_configurations/rc_infinity.py b/src/sage/combinat/rigged_configurations/rc_infinity.py index 1839acddac0..0efcff22810 100644 --- a/src/sage/combinat/rigged_configurations/rc_infinity.py +++ b/src/sage/combinat/rigged_configurations/rc_infinity.py @@ -288,7 +288,7 @@ def weight_lattice_realization(self): return R.weight_lattice() class Element(RiggedConfigurationElement): - """ + r""" A rigged configuration in `\mathcal{B}(\infty)` in simply-laced types. TESTS:: @@ -492,7 +492,7 @@ def from_virtual(self, vrc): rigging_list=riggings) class Element(RCNonSimplyLacedElement): - """ + r""" A rigged configuration in `\mathcal{B}(\infty)` in non-simply-laced types. diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 441bde47c9e..ccd73f984f9 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -410,7 +410,7 @@ def _latex_(self): ret_string = self[0]._latex_() for partition in self[1:]: - ret_string += "\n\quad\n" + partition._latex_() + ret_string += "\n\\quad\n" + partition._latex_() return ret_string @@ -923,7 +923,7 @@ def to_virtual_configuration(self): return self.parent().to_virtual(self) def e(self, a): - """ + r""" Return the action of `e_a` on ``self``. This works by lifting into the virtual configuration, then applying @@ -959,7 +959,7 @@ def e(self, a): return self.parent().from_virtual(virtual_rc) def f(self, a): - """ + r""" Return the action of `f_a` on ``self``. This works by lifting into the virtual configuration, then applying diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index a736877c001..8c2ce283a99 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -39,9 +39,10 @@ KRRCTypeA2DualElement) from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition + # Used in the KR crystals catalog so that there is a common interface def KirillovReshetikhinCrystal(cartan_type, r, s): - """ + r""" Return the KR crystal `B^{r,s}` using :class:`rigged configurations `. diff --git a/src/sage/combinat/species/combinatorial_logarithm.py b/src/sage/combinat/species/combinatorial_logarithm.py index f2ef8c11841..89a69f3117d 100644 --- a/src/sage/combinat/species/combinatorial_logarithm.py +++ b/src/sage/combinat/species/combinatorial_logarithm.py @@ -1,4 +1,4 @@ -""" +r""" Combinatorial Logarithm This file provides the cycle index series for the virtual species `\Omega`, diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index e4b410447d0..361ee8b6dc0 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -586,7 +586,7 @@ def _egs_gen(self, ao): yield self.coefficient(i).coefficient([1]*i) def __invert__(self): - """ + r""" Return the multiplicative inverse of ``self``. This algorithm is derived from [BLL]_. @@ -701,7 +701,7 @@ def _functorial_compose_gen(self, g, ao): n += 1 def arithmetic_product(self, g, check_input = True): - """ + r""" Return the arithmetic product of ``self`` with ``g``. For species `M` and `N` such that `M[\\varnothing] = N[\\varnothing] = \\varnothing`, @@ -1289,9 +1289,10 @@ def ExponentialCycleIndexSeries(R = RationalField()): CIS = CycleIndexSeriesRing(R) return CIS(_exp_gen(R)) + @cached_function def _cl_term(n, R = RationalField()): - """ + r""" Compute the order-n term of the cycle index series of the virtual species `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets. @@ -1301,8 +1302,7 @@ def _cl_term(n, R = RationalField()): sage: [_cl_term(i) for i in range(4)] [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] """ - - n = Integer(n) #check that n is an integer + n = Integer(n) # check that n is an integer p = SymmetricFunctions(R).power() @@ -1310,12 +1310,13 @@ def _cl_term(n, R = RationalField()): if n == 1: res = p([1]) elif n > 1: - res = 1/n * ((-1)**(n-1) * p([1])**n - sum(d * p([Integer(n/d)]).plethysm(_cl_term(d, R)) for d in divisors(n)[:-1])) + res = 1/n * ((-1)**(n-1) * p([1])**n - sum(d * p([n // d]).plethysm(_cl_term(d, R)) for d in divisors(n)[:-1])) return res + def _cl_gen (R = RationalField()): - """ + r""" Produce a generator which yields the terms of the cycle index series of the virtual species `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets. @@ -1328,9 +1329,10 @@ def _cl_gen (R = RationalField()): """ return (_cl_term(i, R) for i in _integers_from(0)) + @cached_function def LogarithmCycleIndexSeries(R = RationalField()): - """ + r""" Return the cycle index series of the virtual species `\Omega`, the compositional inverse of the species `E^{+}` of nonempty sets. diff --git a/src/sage/combinat/species/library.py b/src/sage/combinat/species/library.py index 0e5749e018d..711617299aa 100644 --- a/src/sage/combinat/species/library.py +++ b/src/sage/combinat/species/library.py @@ -73,8 +73,10 @@ def SimpleGraphSpecies(): @cached_function def BinaryTreeSpecies(): - """ - Returns the species of binary trees on n leaves. The species of + r""" + Return the species of binary trees on n leaves. + + The species of binary trees B is defined by B = X + B\*B where X is the singleton species. diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index 2a5f966432d..664fef9a6bc 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -1128,8 +1128,8 @@ def _mul_(self, y): times = _mul_ def _times_gen(self, y, ao): - """ - Returns an iterator for the coefficients of self \* y. + r""" + Return an iterator for the coefficients of self \* y. EXAMPLES:: diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index d97104fc1cb..2d766872e67 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -178,7 +178,7 @@ def __ne__(self, other): return not (self == other) def __getstate__(self): - """ + r""" This is used during the pickling process and returns a dictionary of the data needed to create this object during the unpickling process. It returns an (\*args, \*\*kwds) tuple which is to be @@ -195,7 +195,7 @@ def __getstate__(self): sage: sorted(kwds.items()) [('max', None), ('min', None), ('weight', 1)] """ - kwds = {'weight':self._weight, 'min':self._min, 'max':self._max} + kwds = {'weight': self._weight, 'min': self._min, 'max': self._max} try: return (dict(enumerate(self._state_info)), kwds) except AttributeError: diff --git a/src/sage/combinat/species/structure.py b/src/sage/combinat/species/structure.py index 871e7eb05eb..7776777bb1a 100644 --- a/src/sage/combinat/species/structure.py +++ b/src/sage/combinat/species/structure.py @@ -1,4 +1,4 @@ -""" +r""" Species structures We will illustrate the use of the structure classes using the From 5a6d66a77df336c30d9817e5d83e46dd31d64040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 17:09:50 +0200 Subject: [PATCH 225/284] some py3 details in polyhedron/ --- src/sage/geometry/polyhedron/backend_cdd.py | 8 ++++---- src/sage/geometry/polyhedron/representation.py | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index b4672e8f1b6..b7d340bf962 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -14,8 +14,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import from six import PY2 from subprocess import Popen, PIPE @@ -27,6 +26,7 @@ from .base_QQ import Polyhedron_QQ from .base_RDF import Polyhedron_RDF + class Polyhedron_cdd(Polyhedron_base): r""" Base class for the cdd backend. @@ -247,8 +247,8 @@ def parse_H_representation(intro, data): assert self.ambient_dim() == dimension - 1, "Unexpected ambient dimension" assert len(data) == count, "Unexpected number of lines" for i, line in enumerate(data): - coefficients = map(self.base_ring(), line) - if coefficients[0] != 0 and all([e == 0 for e in coefficients[1:]]): + coefficients = list(map(self.base_ring(), line)) + if coefficients[0] != 0 and all(e == 0 for e in coefficients[1:]): # cddlib sometimes includes an implicit plane at infinity: 1 0 0 ... 0 # We do not care about this entry. self._cdd_H_to_sage_H[i+1] = None diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 699c56e066f..5416b07076e 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -349,6 +349,7 @@ def _set_data(self, polyhedron, data): sage: TestSuite(pH).run(skip='_test_pickling') """ assert polyhedron.parent() is self._polyhedron_parent + data = list(data) if len(data) != self._vector.degree(): raise ValueError('H-representation data requires a list of length ambient_dim+1') From 1264a7add22a1061c6b44501d20dac1a62b2214b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Aug 2018 17:17:50 +0200 Subject: [PATCH 226/284] one detail --- src/sage/geometry/polyhedron/backend_cdd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index b7d340bf962..63fd020d066 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -218,7 +218,7 @@ def _init_from_cdd_output(self, cddout): cddout = cddout.splitlines() def parse_indices(count, cdd_indices, cdd_indices_to_sage_indices=None): - cdd_indices = map(int, cdd_indices) + cdd_indices = list(map(int, cdd_indices)) if cdd_indices_to_sage_indices is None: cdd_indices_to_sage_indices = {i:i-1 for i in cdd_indices} if count < 0: From 2a8dd57e65b0ba22f80ab9afbcd8aa6e1c595513 Mon Sep 17 00:00:00 2001 From: Sebastian Oehms Date: Thu, 9 Aug 2018 18:29:57 +0200 Subject: [PATCH 227/284] initial implementation --- src/sage/groups/matrix_gps/matrix_group.py | 10 +- src/sage/groups/matrix_gps/named_group.py | 78 +++++- src/sage/groups/matrix_gps/orthogonal.py | 287 ++++++++++++++++----- src/sage/groups/matrix_gps/symplectic.py | 124 +++++++-- src/sage/groups/matrix_gps/unitary.py | 258 +++++++++++++++--- 5 files changed, 638 insertions(+), 119 deletions(-) diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 6debf1c3d95..b50c8e7519f 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -126,12 +126,14 @@ def _check_matrix(self, x, *args): EXAMPLES:: - sage: G = SU(2,GF(5)) - sage: G._check_matrix(identity_matrix(GF(5),2)) - sage: G._check_matrix(matrix(GF(5),[[1,1],[0,1]])) + sage: G = SU(2,GF(5)); F = G.base_ring() # this is GF(5^2,'a') + sage: G._check_matrix(identity_matrix(F,2)) + sage: G._check_matrix(matrix(F,[[1,1],[0,1]])) Traceback (most recent call last): ... - TypeError: matrix must be unitary + TypeError: matrix must be unitary with respect to the hermitian form + [0 1] + [1 0] """ if not x.is_invertible(): raise TypeError('matrix is not invertible') diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index 5ae0db6c7c9..47fa5f7e8cd 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -118,10 +118,73 @@ def normalize_args_vectorspace(*args, **kwds): return (ZZ(degree), ring) +def normalize_args_invariant_form(R, d, invariant_form): + """ + function to normalize the input of a user defined invariant + bilinear form for orthogonal, unitary and symplectic groups. + Further informations and examples can be found in the defining + functions (GU, SU, Sp,...) for unitary and symplectic groups + + INPUT: + + - ``R`` -- instance of the integral domain which should become + the base_ring of the classical group + + - ``d`` -- integer giving the dimension of the module the classical + group is operating on + + - ``invariant_form`` -- instances being accepted by the + matrix-constructor which define a d x d square matrix + over R describing the bilinear form to be kept invariant + by the classical group + + OUTPUT: an instance of sage.matrix.matrix2.Matrix if the + normalization was possible. Else, an error is raised except the + the imput has been None. + + TESTS: + + sage: from sage.groups.matrix_gps.named_group import normalize_args_invariant_form + sage: CF3 = CyclotomicField(3) + sage: m = normalize_args_invariant_form(CF3, 3, (1,2,3,0,2,0,0,2,1)); m + [1 2 3] + [0 2 0] + [0 2 1] + sage: m.base_ring() == CF3 + True + + sage: normalize_args_invariant_form(ZZ, 3, (1,2,3,0,2,0,0,2)) + Traceback (most recent call last): + ... + ValueError: invariant_form must be a 3 x 3 matrix over Integer Ring + + sage: normalize_args_invariant_form(QQ, 3, (1,2,3,0,2,0,0,2,0)) + Traceback (most recent call last): + ... + ValueError: invariant_form must be non degenerated + + AUTHORS: + + - Sebastian Oehms (2018-8) (see :trac:`26028`) + """ + if invariant_form == None: + return invariant_form + + from sage.matrix.constructor import matrix + try: + m = matrix(R, d, d, invariant_form) + except: + raise ValueError("invariant_form must be a %s x %s matrix over %s" %(d, d, R)) + + if m.is_singular(): + raise ValueError("invariant_form must be non degenerated") + return m + + class NamedMatrixGroup_generic(CachedRepresentation, MatrixGroup_generic): def __init__(self, degree, base_ring, special, sage_name, latex_string, - category=None): + category=None, invariant_form=None): """ Base class for "named" matrix groups @@ -135,9 +198,18 @@ def __init__(self, degree, base_ring, special, sage_name, latex_string, - ``special`` -- boolean. Whether the matrix group is special, that is, elements have determinant one. + - ``sage_name`` -- string. the name displayed in a sage session. + - ``latex_string`` -- string. The latex representation. - EXAMPLES:: + - ``category`` -- (optional) instance of sage.categories.groups.Groups + passed to the constructor of MatrixGroup_generic. + + - ``invariant_form`` -- (optional) square-matrix of the given + degree over the given base_ring describing a bilinear form + to be kept invariant by the group. + + EXAMPLES (see the examples for GU, SU, Sp,.., as well):: sage: G = GL(2, QQ) sage: from sage.groups.matrix_gps.named_group import NamedMatrixGroup_generic @@ -148,6 +220,7 @@ def __init__(self, degree, base_ring, special, sage_name, latex_string, self._special = special self._name_string = sage_name self._latex_string = latex_string + self._user_invariant_form_ = invariant_form def _an_element_(self): """ @@ -220,6 +293,7 @@ def __richcmp__(self, other, op): return MatrixGroup_generic.__richcmp__(self, other, op) + class NamedMatrixGroup_gap(NamedMatrixGroup_generic, MatrixGroup_gap): def __init__(self, degree, base_ring, special, sage_name, latex_string, diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index c188b4714cf..66b1482c385 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -66,6 +66,10 @@ - William Stein (2006-12-09): rewrite - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. + +- Sebastian Oehms (2018-8) add :meth:`invariant_form` (as alias), :func:`_OG`, + option for user defined invariant bilinear form and bug-fix in cmd-string + for calling GAP (see :trac:`26028`) """ #***************************************************************************** @@ -81,7 +85,8 @@ from sage.misc.latex import latex from sage.misc.cachefunc import cached_method from sage.groups.matrix_gps.named_group import ( - normalize_args_vectorspace, NamedMatrixGroup_generic, NamedMatrixGroup_gap ) + normalize_args_vectorspace, normalize_args_invariant_form, + NamedMatrixGroup_generic, NamedMatrixGroup_gap) def normalize_args_e(degree, ring, e): """ @@ -127,12 +132,66 @@ def normalize_args_e(degree, ring, e): +############################################################################### +# Orthogonal Group: common Code for both GO and SO +############################################################################### + +def _OG(n, R, special, e=0, var='a', invariant_form=None): + r""" + This function is commonly used by the functions GO and SO to avoid uneccessarily + duplicated code. For documentation and examples see the individual functions. + + TEST: + + sage: GO(3,25).order() # check that :trac:`????` is fixed + 31200 + """ + prefix = 'General' + ltx_prefix ='G' + if special: + prefix = 'Special' + ltx_prefix ='S' + + degree, ring = normalize_args_vectorspace(n, R, var=var) + e = normalize_args_e(degree, ring, e) + + if e == 0: + if invariant_form != None: + if is_FiniteField(ring): + raise NotImplementedError("invariant_form for finite Groups is fixed") + + invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) + if not invariant_form.is_symmetric(): + raise ValueError("invariant_form must be symmetric") + + inserted_text = 'with respect to symmetrc form' + try: + if not invariant_form.is_positive_definite(): + inserted_text = 'with respect to non positive definite symmetrc form' + except: + pass + + name = '{0} Orthogonal Group of degree {1} over {2} {3}\n{4}'.format(prefix, degree, ring, inserted_text,invariant_form) + ltx = r'\text{{{0}O}}_{{{1}}}({2})\text{{ {3} }}{4}'.format(ltx_prefix, degree, latex(ring), inserted_text, latex(invariant_form)) + else: + name = '{0} Orthogonal Group of degree {1} over {2}'.format(prefix, degree, ring) + ltx = r'\text{{{0}O}}_{{{1}}}({2})'.format(ltx_prefix, degree, latex(ring)) + else: + name = '{0} Orthogonal Group of degree {1} and form parameter {2} over {3}'.format(prefix, degree, e, ring) + ltx = r'\text{{{0}O}}_{{{1}}}({2}, {3})'.format(ltx_prefix, degree, latex(ring), '+' if e == 1 else '-') + if is_FiniteField(ring): + cmd = '{0}O({1}, {2}, {3})'.format(ltx_prefix, e, degree, ring.order()) + return OrthogonalMatrixGroup_gap(degree, ring, False, name, ltx, cmd) + else: + return OrthogonalMatrixGroup_generic(degree, ring, False, name, ltx, invariant_form=invariant_form) + + ######################################################################## # General Orthogonal Group ######################################################################## -def GO(n, R, e=0, var='a'): +def GO(n, R, e=0, var='a', invariant_form=None): """ Return the general orthogonal group. @@ -161,6 +220,15 @@ def GO(n, R, e=0, var='a'): for finite fields and if the degree is even. A parameter that distinguishes inequivalent invariant forms. + - ``var`` -- (optional, default='a') variable used to represent + generator of the finite field, if needed. + + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a n x n square matrix + over R describing the symmetric form to be kept invariant + by the orthogonal group. The form is checked to be non + degenerated and symmetric but not to be positive definite. + OUTPUT: The general orthogonal group of given degree, base ring, and @@ -179,25 +247,47 @@ def GO(n, R, e=0, var='a'): [0 0 1], [0 2 1] ) - TESTS:: + using the invariant_form option:: + + sage: m = matrix(QQ, 3,3, [[0, 1, 0], [1, 0, 0], [0, 0, 3]]) + sage: GO3 = GO(3,QQ) + sage: GO3m = GO(3,QQ, invariant_form=m) + sage: GO3 == GO3m + False + sage: GO3.invariant_form() + [1 0 0] + [0 1 0] + [0 0 1] + sage: GO3m.invariant_form() + [0 1 0] + [1 0 0] + [0 0 3] + sage: pm = Permutation([2,3,1]).to_matrix() + sage: g = GO3(pm); g in GO3; g + True + [0 0 1] + [1 0 0] + [0 1 0] + sage: GO3m(pm) + Traceback (most recent call last): + ... + TypeError: matrix must be orthogonal with respect to the symmetric form + [0 1 0] + [1 0 0] + [0 0 3] + + sage GO(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) + Traceback (most recent call last): + ... + NotImplementedError: invariant_form for finite Groups is fixed + + TESTS: + sage: TestSuite(GO3).run() sage: groups.matrix.GO(2, 3, e=-1) General Orthogonal Group of degree 2 and form parameter -1 over Finite Field of size 3 """ - degree, ring = normalize_args_vectorspace(n, R, var=var) - e = normalize_args_e(degree, ring, e) - if e == 0: - name = 'General Orthogonal Group of degree {0} over {1}'.format(degree, ring) - ltx = r'\text{{GO}}_{{{0}}}({1})'.format(degree, latex(ring)) - else: - name = 'General Orthogonal Group of degree' + \ - ' {0} and form parameter {1} over {2}'.format(degree, e, ring) - ltx = r'\text{{GO}}_{{{0}}}({1}, {2})'.format(degree, latex(ring), '+' if e == 1 else '-') - if is_FiniteField(ring): - cmd = 'GO({0}, {1}, {2})'.format(e, degree, ring.characteristic()) - return OrthogonalMatrixGroup_gap(degree, ring, False, name, ltx, cmd) - else: - return OrthogonalMatrixGroup_generic(degree, ring, False, name, ltx) + return _OG(n, R, False, e=e, var=var, invariant_form=invariant_form) @@ -205,7 +295,7 @@ def GO(n, R, e=0, var='a'): # Special Orthogonal Group ######################################################################## -def SO(n, R, e=None, var='a'): +def SO(n, R, e=None, var='a', invariant_form=None): """ Return the special orthogonal group. @@ -229,6 +319,14 @@ def SO(n, R, e=None, var='a'): - ``e`` -- ``+1`` or ``-1``, and ignored by default. Only relevant for finite fields and if the degree is even. A parameter that distinguishes inequivalent invariant forms. + - ``var`` -- (optional, default='a') variable used to represent + generator of the finite field, if needed. + + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a n x n square matrix + over R describing the symmetric form to be kept invariant + by the orthogonal group. The form is checked to be non + degenerated and symmetric but not to be positive definite. OUTPUT: @@ -256,25 +354,48 @@ def SO(n, R, e=None, var='a'): [0 0 1], [0 3 1], [2 0 4] ) - TESTS:: + using the invariant_form option:: + + sage: CF3 = CyclotomicField(3); e3 = CF3.gen() + sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3,2,0],[0,0,1]]) + sage: SO3 = SO(3, CF3) + sage: SO3m = SO(3, CF3, invariant_form=m) + sage: SO3 == SO3m + False + sage: SO3.invariant_form() + [1 0 0] + [0 1 0] + [0 0 1] + sage: SO3m.invariant_form() + [ 1 zeta3 0] + [zeta3 2 0] + [ 0 0 1] + sage: pm = Permutation([2,3,1]).to_matrix() + sage: g = SO3(pm); g in SO3; g + True + [0 0 1] + [1 0 0] + [0 1 0] + sage: SO3m(pm) + Traceback (most recent call last): + ... + TypeError: matrix must be orthogonal with respect to the symmetric form + [ 1 zeta3 0] + [zeta3 2 0] + [ 0 0 1] + sage SO(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) + Traceback (most recent call last): + ... + NotImplementedError: invariant_form for finite Groups is fixed + + TESTS: + + sage: TestSuite(SO3m).run() sage: groups.matrix.SO(2, 3, e=1) Special Orthogonal Group of degree 2 and form parameter 1 over Finite Field of size 3 """ - degree, ring = normalize_args_vectorspace(n, R, var=var) - e = normalize_args_e(degree, ring, e) - if e == 0: - name = 'Special Orthogonal Group of degree {0} over {1}'.format(degree, ring) - ltx = r'\text{{SO}}_{{{0}}}({1})'.format(degree, latex(ring)) - else: - name = 'Special Orthogonal Group of degree' + \ - ' {0} and form parameter {1} over {2}'.format(degree, e, ring) - ltx = r'\text{{SO}}_{{{0}}}({1}, {2})'.format(degree, latex(ring), '+' if e == 1 else '-') - if is_FiniteField(ring): - cmd = 'SO({0}, {1}, {2})'.format(e, degree, ring.characteristic()) - return OrthogonalMatrixGroup_gap(degree, ring, True, name, ltx, cmd) - else: - return OrthogonalMatrixGroup_generic(degree, ring, True, name, ltx) + return _OG(n, R, True, e=e, var=var, invariant_form=invariant_form) @@ -283,6 +404,31 @@ def SO(n, R, e=None, var='a'): ######################################################################## class OrthogonalMatrixGroup_generic(NamedMatrixGroup_generic): + r""" + General Orthogonal Group over arbitrary rings. + + EXAMPLES:: + + sage: G = GO(3, GF(7)); G + General Orthogonal Group of degree 3 over Finite Field of size 7 + sage: latex(G) + \text{GO}_{3}(\Bold{F}_{7}) + + sage: G = SO(3, GF(5)); G + Special Orthogonal Group of degree 3 over Finite Field of size 5 + sage: latex(G) + \text{SO}_{3}(\Bold{F}_{5}) + + sage: CF3 = CyclotomicField(3); e3 = CF3.gen() + sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3,2,0],[0,0,1]]) + sage: G = SO(3, CF3, invariant_form=m) + sage: latex(G) + \text{SO}_{3}(\Bold{Q}(\zeta_{3}))\text{ with respect to non positive definite symmetrc form }\left(\begin{array}{rrr} + 1 & \zeta_{3} & 0 \\ + \zeta_{3} & 2 & 0 \\ + 0 & 0 & 1 + \end{array}\right) + """ @cached_method def invariant_bilinear_form(self): @@ -302,38 +448,39 @@ def invariant_bilinear_form(self): sage: GO(2,3,-1).invariant_bilinear_form() [2 1] [1 1] + sage: G = GO(4, QQ) + sage: G.invariant_bilinear_form() + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + sage: GO3m = GO(3,QQ, invariant_form=(1,0,0,0,2,0,0,0,3)) + sage: GO3m.invariant_bilinear_form() + [1 0 0] + [0 2 0] + [0 0 3] + + TESTS: + + sage: GO3m.invariant_form() # test alias + [1 0 0] + [0 2 0] + [0 0 3] """ - from sage.matrix.constructor import identity_matrix - m = identity_matrix(self.base_ring(), self.degree()) - m.set_immutable() - return m - - @cached_method - def invariant_quadratic_form(self): - """ - Return the quadratic form preserved by the orthogonal group. - - OUTPUT: - - A matrix. - - EXAMPLES:: + if self._user_invariant_form_ != None: + return self._user_invariant_form_ - sage: GO(2,3,+1).invariant_quadratic_form() - [0 1] - [0 0] - sage: GO(2,3,-1).invariant_quadratic_form() - [1 1] - [0 2] - """ from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) m.set_immutable() return m + invariant_quadratic_form = invariant_bilinear_form # this is identical in the generic case + invariant_form = invariant_bilinear_form # alias (analogues to symplectc and unitary cases) + def _check_matrix(self, x, *args): """a - Check whether the matrix ``x`` is symplectic. + Check whether the matrix ``x`` is orthogonal. See :meth:`~sage.groups.matrix_gps.matrix_group._check_matrix` for details. @@ -347,7 +494,10 @@ def _check_matrix(self, x, *args): raise TypeError('matrix must have determinant one') F = self.invariant_bilinear_form() if x * F * x.transpose() != F: - raise TypeError('matrix must be orthogonal with respect to the invariant form') + if F == self.one().matrix(): + raise TypeError('matrix must be orthogonal') + else: + raise TypeError('matrix must be orthogonal with respect to the symmetric form\n%s' %(F)) # TODO: check that quadratic form is preserved in characteristic two class OrthogonalMatrixGroup_gap(OrthogonalMatrixGroup_generic, NamedMatrixGroup_gap): @@ -380,15 +530,16 @@ def invariant_bilinear_form(self): [0 0 6 0] [0 0 0 2] - sage: G = GO(4, QQ) + sage: G = SO(4, GF(7), -1) sage: G.invariant_bilinear_form() - [1 0 0 0] [0 1 0 0] - [0 0 1 0] - [0 0 0 1] + [1 0 0 0] + [0 0 2 0] + [0 0 0 2] - sage: G = SO(4, GF(7), -1) - sage: G.invariant_bilinear_form() + TESTS: + + sage: G.invariant_form() # test alias [0 1 0 0] [1 0 0 0] [0 0 2 0] @@ -398,6 +549,8 @@ def invariant_bilinear_form(self): m.set_immutable() return m + invariant_form = invariant_bilinear_form # alias (analogues to symplectc and unitary cases) + @cached_method def invariant_quadratic_form(self): """ @@ -440,6 +593,14 @@ def invariant_quadratic_form(self): [0 0 0 0] [0 0 1 0] [0 0 0 1] + + TESTS: + + sage: GO(4, GF(7), -1).invariant_form() # test alias + [0 1 0 0] + [1 0 0 0] + [0 0 2 0] + [0 0 0 2] """ m = self.gap().InvariantQuadraticForm()['matrix'].matrix() m.set_immutable() diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index 7f944ef0947..441ec4cdad5 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -22,6 +22,10 @@ special_linear (by W. Stein) - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. + +- Sebastian Oehms (2018-8) add option for user defined invariant bilinear + form and bug-fix in :meth:`invariant_form` of :class:`SymplecticMatrixGroup_generic` + (see :trac:`26028`) """ #***************************************************************************** @@ -34,8 +38,10 @@ from sage.misc.latex import latex from sage.misc.cachefunc import cached_method +from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.groups.matrix_gps.named_group import ( - normalize_args_vectorspace, NamedMatrixGroup_generic, NamedMatrixGroup_gap ) + normalize_args_vectorspace, normalize_args_invariant_form, + NamedMatrixGroup_generic, NamedMatrixGroup_gap) @@ -43,7 +49,7 @@ # Symplectic Group ############################################################################### -def Sp(n, R, var='a'): +def Sp(n, R, var='a', invariant_form=None): r""" Return the symplectic group. @@ -62,8 +68,13 @@ def Sp(n, R, var='a'): - ``R`` -- ring or an integer. If an integer is specified, the corresponding finite field is used. - - ``var`` -- variable used to represent generator of the finite - field, if needed. + - ``var`` -- (optional, default='a') variable used to represent + generator of the finite field, if needed. + + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a n x n square matrix + over R describing the alternating form to be kept invariant + by the symplectic group EXAMPLES:: @@ -78,8 +89,47 @@ def Sp(n, R, var='a'): ... ValueError: the degree must be even - TESTS:: + using the invariant_form option:: + + sage: m = matrix(QQ, 4,4, [[0, 0, 1, 0], [0, 0, 0, 2], [-1, 0, 0, 0], [0, -2, 0, 0]]) + sage: Sp4m = Sp(4, QQ, invariant_form=m) + sage: Sp4 = Sp(4, QQ) + sage: Sp4 == Sp4m + False + sage: Sp4.invariant_form() + [ 0 0 0 1] + [ 0 0 1 0] + [ 0 -1 0 0] + [-1 0 0 0] + sage: Sp4m.invariant_form() + [ 0 0 1 0] + [ 0 0 0 2] + [-1 0 0 0] + [ 0 -2 0 0] + sage: pm = Permutation([2,1,4,3]).to_matrix() + sage: g = Sp4(pm); g in Sp4; g + True + [0 1 0 0] + [1 0 0 0] + [0 0 0 1] + [0 0 1 0] + sage: Sp4m(pm) + Traceback (most recent call last): + ... + TypeError: matrix must be symplectic with respect to the alternating form + [ 0 0 1 0] + [ 0 0 0 2] + [-1 0 0 0] + [ 0 -2 0 0] + sage Sp(4,3, invariant_form=[[0,0,0,1],[0,0,1,0],[0,2,0,0], [2,0,0,0]]) + Traceback (most recent call last): + ... + NotImplementedError: invariant_form for finite Groups is fixed + + TESTS:: + sage: TestSuite(Sp4).run() + sage: TestSuite(Sp4m).run() sage: groups.matrix.Sp(2, 3) Symplectic Group of degree 2 over Finite Field of size 3 @@ -89,18 +139,55 @@ def Sp(n, R, var='a'): degree, ring = normalize_args_vectorspace(n, R, var=var) if degree % 2 != 0: raise ValueError('the degree must be even') - name = 'Symplectic Group of degree {0} over {1}'.format(degree, ring) - ltx = r'\text{{Sp}}_{{{0}}}({1})'.format(degree, latex(ring)) + + if invariant_form != None: + if is_FiniteField(ring): + raise NotImplementedError("invariant_form for finite Groups is fixed") + + invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) + if not invariant_form.is_alternating(): + raise ValueError("invariant_form must be alternating") + + name = 'Symplectic Group of degree {0} over {1} with respect to alternating bilinear form\n{2}'.format(degree, ring, invariant_form) + ltx = r'\text{{Sp}}_{{{0}}}({1})\text{{ with respect to alternating bilinear form}}{2}'.format(degree, latex(ring), latex(invariant_form)) + else: + name = 'Symplectic Group of degree {0} over {1}'.format(degree, ring) + ltx = r'\text{{Sp}}_{{{0}}}({1})'.format(degree, latex(ring)) + from sage.libs.gap.libgap import libgap try: cmd = 'Sp({0}, {1})'.format(degree, ring._gap_init_()) return SymplecticMatrixGroup_gap(degree, ring, True, name, ltx, cmd) except ValueError: - return SymplecticMatrixGroup_generic(degree, ring, True, name, ltx) + return SymplecticMatrixGroup_generic(degree, ring, True, name, ltx, invariant_form=invariant_form) class SymplecticMatrixGroup_generic(NamedMatrixGroup_generic): + r""" + Symplectic Group over arbitrary rings. + + EXAMPLES:: + + sage: Sp43 = Sp(4,3); Sp43 + Symplectic Group of degree 4 over Finite Field of size 3 + sage: latex(Sp43) + \text{Sp}_{4}(\Bold{F}_{3}) + + sage: Sp4m = Sp(4,QQ, invariant_form=(0, 0, 1, 0, 0, 0, 0, 2, -1, 0, 0, 0, 0, -2, 0, 0)); Sp4m + Symplectic Group of degree 4 over Rational Field with respect to alternating bilinear form + [ 0 0 1 0] + [ 0 0 0 2] + [-1 0 0 0] + [ 0 -2 0 0] + sage: latex(Sp4m) + \text{Sp}_{4}(\Bold{Q})\text{ with respect to alternating bilinear form}\left(\begin{array}{rrrr} + 0 & 0 & 1 & 0 \\ + 0 & 0 & 0 & 2 \\ + -1 & 0 & 0 & 0 \\ + 0 & -2 & 0 & 0 + \end{array}\right) + """ @cached_method def invariant_form(self): @@ -114,15 +201,20 @@ def invariant_form(self): EXAMPLES:: sage: Sp(4, QQ).invariant_form() - [0 0 0 1] - [0 0 1 0] - [0 1 0 0] - [1 0 0 0] + [ 0 0 0 1] + [ 0 0 1 0] + [ 0 -1 0 0] + [-1 0 0 0] """ + if self._user_invariant_form_ != None: + return self._user_invariant_form_ + + R = self.base_ring() + d = self.degree() from sage.matrix.constructor import zero_matrix - m = zero_matrix(self.base_ring(), self.degree()) - for i in range(self.degree()): - m[i, self.degree()-i-1] = 1 + m = zero_matrix(R, d) + for i in range(d): + m[i, d-i-1] = 1 if i < d/2 else -1 m.set_immutable() return m @@ -140,7 +232,7 @@ def _check_matrix(self, x, *args): """ F = self.invariant_form() if x * F * x.transpose() != F: - raise TypeError('matrix must be symplectic') + raise TypeError('matrix must be symplectic with respect to the alternating form\n%s' %(F)) class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_gap): diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index 237e37b3462..c9f6c399513 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -31,6 +31,10 @@ - William Stein (2006-12): rewrite - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. + +- Sebastian Oehms (2018-8) add :meth:`invariant_form`, :func:`_UG`, + option for user defined invariant bilinear form and bug-fix in + :meth:`_check_matrix` (see :trac:`26028`) """ #********************************************************************************* @@ -44,8 +48,10 @@ from sage.rings.all import ZZ, GF from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.misc.latex import latex +from sage.misc.cachefunc import cached_method from sage.groups.matrix_gps.named_group import ( - normalize_args_vectorspace, NamedMatrixGroup_generic, NamedMatrixGroup_gap ) + normalize_args_vectorspace, normalize_args_invariant_form, + NamedMatrixGroup_generic, NamedMatrixGroup_gap ) def finite_field_sqrt(ring): @@ -74,11 +80,61 @@ def finite_field_sqrt(ring): return q +############################################################################### +# Unitary Group: common Code for both GU and SU +############################################################################### + +def _UG(n, R, special, var='a', invariant_form=None): + r""" + This function is commonly used by the functions GU and SU to avoid uneccessarily + duplicated code. For documnentation and examples see the individual functions. + """ + prefix = 'General' + latex_prefix ='G' + if special: + prefix = 'Special' + latex_prefix ='S' + + degree, ring = normalize_args_vectorspace(n, R, var=var) + if is_FiniteField(ring): + q = ring.cardinality() + ring = GF(q ** 2, name=var) + if invariant_form != None: + raise NotImplementedError("invariant_form for finite Groups is fixed") + + if invariant_form != None: + invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) + if not invariant_form.is_hermitian(): + raise ValueError("invariant_form must be hermitian") + + inserted_text = 'with respect to hermitian form' + try: + if not invariant_form.is_positive_definite(): + inserted_text = 'with respect to non positive definite hermitian form' + except: + pass + + name = '{0} Unitary Group of degree {1} over {2} {3}\n{4}'.format(prefix, degree, ring, inserted_text,invariant_form) + ltx = r'\text{{{0}U}}_{{{1}}}({2})\text{{ {3} }}{4}'.format(latex_prefix, degree, latex(ring), inserted_text, latex(invariant_form)) + else: + name = '{0} Unitary Group of degree {1} over {2}'.format(prefix, degree, ring) + ltx = r'\text{{{0}U}}_{{{1}}}({2})'.format(latex_prefix, degree, latex(ring)) + + if is_FiniteField(ring): + cmd = '{0}U({1}, {2})'.format(latex_prefix, degree, q) + return UnitaryMatrixGroup_gap(degree, ring, special, name, ltx, cmd) + else: + return UnitaryMatrixGroup_generic(degree, ring, special, name, ltx, invariant_form=invariant_form) + + + + + ############################################################################### # General Unitary Group ############################################################################### -def GU(n, R, var='a'): +def GU(n, R, var='a', invariant_form=None): r""" Return the general unitary group. @@ -104,8 +160,14 @@ def GU(n, R, var='a'): - ``R`` -- ring or an integer. If an integer is specified, the corresponding finite field is used. - - ``var`` -- variable used to represent generator of the finite - field, if needed. + - ``var`` -- (optional, default='a') variable used to represent + generator of the finite field, if needed. + + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a n x n square matrix + over R describing the hermitian form to be kept invariant + by the unitary group. The form is checked to be non + degenerated and hermitian but not to be positive definite. OUTPUT: @@ -134,22 +196,53 @@ def GU(n, R, var='a'): [ 0 0 3*beta], [ 1 0 0] ) - TESTS:: - + using the invariant_form option:: + + sage: UCF = UniversalCyclotomicField(); e5=UCF.gen(5) + sage: m=matrix(UCF, 3,3, [[1,e5,0],[e5.conjugate(),2,0],[0,0,1]]) + sage: G = GU(3, UCF) + sage: Gm = GU(3, UCF, invariant_form=m) + sage: G == Gm + False + sage: G.invariant_form() + [1 0 0] + [0 1 0] + [0 0 1] + sage: Gm.invariant_form() + [ 1 E(5) 0] + [E(5)^4 2 0] + [ 0 0 1] + sage: pm=Permutation((1,2,3)).to_matrix() + sage: g = G(pm); g in G; g + True + [0 0 1] + [1 0 0] + [0 1 0] + sage: Gm(pm) + Traceback (most recent call last): + ... + TypeError: matrix must be unitary with respect to the hermitian form + [ 1 E(5) 0] + [E(5)^4 2 0] + [ 0 0 1] + + sage GU(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) + Traceback (most recent call last): + ... + NotImplementedError: invariant_form for finite Groups is fixed + + sage: GU(2,QQ, invariant_form=[[1,0],[2,0]]) + Traceback (most recent call last): + ... + ValueError: invariant_form must be non degenerated + + TESTS: + + sage: TestSuite(G).run() sage: groups.matrix.GU(2, 3) General Unitary Group of degree 2 over Finite Field in a of size 3^2 """ - degree, ring = normalize_args_vectorspace(n, R, var=var) - if is_FiniteField(ring): - q = ring.cardinality() - ring = GF(q ** 2, name=var) - name = 'General Unitary Group of degree {0} over {1}'.format(degree, ring) - ltx = r'\text{{GU}}_{{{0}}}({1})'.format(degree, latex(ring)) - if is_FiniteField(ring): - cmd = 'GU({0}, {1})'.format(degree, q) - return UnitaryMatrixGroup_gap(degree, ring, False, name, ltx, cmd) - else: - return UnitaryMatrixGroup_generic(degree, ring, False, name, ltx) + return _UG(n, R, False, var=var, invariant_form=invariant_form) @@ -157,7 +250,7 @@ def GU(n, R, var='a'): # Special Unitary Group ############################################################################### -def SU(n, R, var='a'): +def SU(n, R, var='a', invariant_form=None): """ The special unitary group `SU( d, R )` consists of all `d \times d` matrices that preserve a nondegenerate sesquilinear form over the @@ -181,8 +274,14 @@ def SU(n, R, var='a'): - ``R`` -- ring or an integer. If an integer is specified, the corresponding finite field is used. - - ``var`` -- variable used to represent generator of the finite - field, if needed. + - ``var`` -- (optional, default='a') variable used to represent + generator of the finite field, if needed. + + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a n x n square matrix + over R describing the hermitian form to be kept invariant + by the unitary group. The form is checked to be non + degenerated and hermitian but not to be positive definite. OUTPUT: @@ -197,22 +296,48 @@ def SU(n, R, var='a'): sage: SU(3,QQ) Special Unitary Group of degree 3 over Rational Field + using the invariant_form option:: + + sage: CF3 = CyclotomicField(3); e3 = CF3.gen() + sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3.conjugate(),2,0],[0,0,1]]) + sage: G = SU(3, CF3) + sage: Gm = SU(3, CF3, invariant_form=m) + sage: G == Gm + False + sage: G.invariant_form() + [1 0 0] + [0 1 0] + [0 0 1] + sage: Gm.invariant_form() + [ 1 zeta3 0] + [-zeta3 - 1 2 0] + [ 0 0 1] + sage: pm=Permutation((1,2,3)).to_matrix() + sage: G(pm) + [0 0 1] + [1 0 0] + [0 1 0] + sage: Gm(pm) + Traceback (most recent call last): + ... + TypeError: matrix must be unitary with respect to the hermitian form + [ 1 zeta3 0] + [-zeta3 - 1 2 0] + [ 0 0 1] + + sage SU(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) + Traceback (most recent call last): + ... + NotImplementedError: invariant_form for finite Groups is fixed + TESTS:: + sage: TestSuite(Gm).run() sage: groups.matrix.SU(2, 3) Special Unitary Group of degree 2 over Finite Field in a of size 3^2 """ - degree, ring = normalize_args_vectorspace(n, R, var=var) - if is_FiniteField(ring): - q = ring.cardinality() - ring = GF(q ** 2, name=var) - name = 'Special Unitary Group of degree {0} over {1}'.format(degree, ring) - ltx = r'\text{{SU}}_{{{0}}}({1})'.format(degree, latex(ring)) - if is_FiniteField(ring): - cmd = 'SU({0}, {1})'.format(degree, q) - return UnitaryMatrixGroup_gap(degree, ring, True, name, ltx, cmd) - else: - return UnitaryMatrixGroup_generic(degree, ring, True, name, ltx) + return _UG(n, R, True, var=var, invariant_form=invariant_form) + ######################################################################## @@ -234,8 +359,46 @@ class UnitaryMatrixGroup_generic(NamedMatrixGroup_generic): Special Unitary Group of degree 3 over Finite Field in a of size 5^2 sage: latex(G) \text{SU}_{3}(\Bold{F}_{5^{2}}) + + sage: CF3 = CyclotomicField(3); e3 = CF3.gen() + sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3.conjugate(),2,0],[0,0,1]]) + sage: G = SU(3, CF3, invariant_form=m) + sage: latex(G) + \text{SU}_{3}(\Bold{Q}(\zeta_{3}))\text{ with respect to hermitian form }\left(\begin{array}{rrr} + 1 & \zeta_{3} & 0 \\ + -\zeta_{3} - 1 & 2 & 0 \\ + 0 & 0 & 1 + \end{array}\right) """ + @cached_method + def invariant_form(self): + """ + Return the hermitian form preserved by the unitary + group. + + OUTPUT: + + A square matrix describing the bilinear form + + EXAMPLES:: + + sage: SU4 = SU(4,QQ) + sage: SU4.invariant_form() + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + """ + if self._user_invariant_form_ != None: + return self._user_invariant_form_ + + from sage.matrix.constructor import identity_matrix + m = identity_matrix(self.base_ring(), self.degree()) + m.set_immutable() + return m + + def _check_matrix(self, x, *args): """a Check whether the matrix ``x`` is unitary. @@ -252,12 +415,39 @@ def _check_matrix(self, x, *args): """ if self._special and x.determinant() != 1: raise TypeError('matrix must have determinant one') - if not x.is_unitary(): - raise TypeError('matrix must be unitary') + + H = self.invariant_form() + if x * H * x.conjugate_transpose() != H: + if H == self.one().matrix(): + raise TypeError('matrix must be unitary') + else: + raise TypeError('matrix must be unitary with respect to the hermitian form\n%s' %(H)) class UnitaryMatrixGroup_gap(UnitaryMatrixGroup_generic, NamedMatrixGroup_gap): - pass + @cached_method + def invariant_form(self): + """ + Return the hermitian form preserved by the unitary group. + + OUTPUT: + A square matrix describing the bilinear form + EXAMPLES:: + + sage: G32=GU(3,2) + sage: G32.invariant_form() + [0 0 1] + [0 1 0] + [1 0 0] + """ + d = self.degree() + R = self.base_ring() + # note that self.gap().InvariantSesquilinearForm()['matrix'].matrix().base_ring() != R for example for self = GU(3.2) + # therefore we have to coerce into the right matrix space + from sage.matrix.constructor import matrix + m = matrix(R, d, d, self.gap().InvariantSesquilinearForm()['matrix'].matrix()) + m.set_immutable() + return m From e57d3dfd0a4a89660dc3b6e5c81ae52188189547 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 9 Aug 2018 10:37:41 -0700 Subject: [PATCH 228/284] trac 26035: use list comprehension instead of list(map(...)) --- src/sage/geometry/polyhedron/backend_cdd.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 63fd020d066..5ecab3aa76f 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -218,7 +218,7 @@ def _init_from_cdd_output(self, cddout): cddout = cddout.splitlines() def parse_indices(count, cdd_indices, cdd_indices_to_sage_indices=None): - cdd_indices = list(map(int, cdd_indices)) + cdd_indices = [int(_) for _ in cdd_indices] if cdd_indices_to_sage_indices is None: cdd_indices_to_sage_indices = {i:i-1 for i in cdd_indices} if count < 0: @@ -247,7 +247,7 @@ def parse_H_representation(intro, data): assert self.ambient_dim() == dimension - 1, "Unexpected ambient dimension" assert len(data) == count, "Unexpected number of lines" for i, line in enumerate(data): - coefficients = list(map(self.base_ring(), line)) + coefficients = [self.base_ring()(x) for x in line] if coefficients[0] != 0 and all(e == 0 for e in coefficients[1:]): # cddlib sometimes includes an implicit plane at infinity: 1 0 0 ... 0 # We do not care about this entry. From 8c1c081c439e6f38e3f08fe8d95630014b10b1d2 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 9 Aug 2018 11:14:18 -0700 Subject: [PATCH 229/284] trac 26035: Don't use _ in list comprehension. Convert 'data' to a list in Vrepresentation, not Hrepresentation. --- src/sage/geometry/polyhedron/backend_cdd.py | 2 +- src/sage/geometry/polyhedron/representation.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 5ecab3aa76f..64902c76f9c 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -218,7 +218,7 @@ def _init_from_cdd_output(self, cddout): cddout = cddout.splitlines() def parse_indices(count, cdd_indices, cdd_indices_to_sage_indices=None): - cdd_indices = [int(_) for _ in cdd_indices] + cdd_indices = [int(x) for x in cdd_indices] if cdd_indices_to_sage_indices is None: cdd_indices_to_sage_indices = {i:i-1 for i in cdd_indices} if count < 0: diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 5416b07076e..e53fee09212 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -349,7 +349,6 @@ def _set_data(self, polyhedron, data): sage: TestSuite(pH).run(skip='_test_pickling') """ assert polyhedron.parent() is self._polyhedron_parent - data = list(data) if len(data) != self._vector.degree(): raise ValueError('H-representation data requires a list of length ambient_dim+1') @@ -921,6 +920,7 @@ def _set_data(self, polyhedron, data): sage: TestSuite(pV).run(skip='_test_pickling') """ assert polyhedron.parent() is self._polyhedron_parent + data = list(data) if len(data) != self._vector.degree(): raise ValueError('V-representation data requires a list of length ambient_dim') From 3b381f1cbc29205f83097e5484c7c531b5ad93f4 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 9 Aug 2018 11:28:04 -0700 Subject: [PATCH 230/284] trac 26037: fix typo in geometry/polyhedron/backend_ppl.py --- src/sage/geometry/polyhedron/backend_ppl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/backend_ppl.py b/src/sage/geometry/polyhedron/backend_ppl.py index c5ecfc4c6b4..fb028ea78ed 100644 --- a/src/sage/geometry/polyhedron/backend_ppl.py +++ b/src/sage/geometry/polyhedron/backend_ppl.py @@ -259,7 +259,7 @@ class Polyhedron_ZZ_ppl(Polyhedron_ppl, Polyhedron_ZZ): EXAMPLES:: - sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], rays=[(1,1)], lines=[]) + sage: p = Polyhedron(vertices=[(0,0),(1,0),(0,1)], rays=[(1,1)], lines=[], ....: backend='ppl', base_ring=ZZ) sage: TestSuite(p).run(skip='_test_pickling') """ From b357154f847d38a85f54423e8f24af6998802166 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Thu, 9 Aug 2018 18:05:42 -0700 Subject: [PATCH 231/284] trac 26035: minor optimization --- src/sage/geometry/polyhedron/backend_cdd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 64902c76f9c..b97f77c71d6 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -246,8 +246,9 @@ def parse_H_representation(intro, data): count, dimension = map(int, data.pop(0)) assert self.ambient_dim() == dimension - 1, "Unexpected ambient dimension" assert len(data) == count, "Unexpected number of lines" + R = self.base_ring() for i, line in enumerate(data): - coefficients = [self.base_ring()(x) for x in line] + coefficients = [R(x) for x in line] if coefficients[0] != 0 and all(e == 0 for e in coefficients[1:]): # cddlib sometimes includes an implicit plane at infinity: 1 0 0 ... 0 # We do not care about this entry. From acad1de09068e2b664e35f5ac1f912bda64a813a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 12 Jul 2018 10:07:05 +0200 Subject: [PATCH 232/284] py3: fix use of automethod for pdf docbuild --- src/sage/categories/category.py | 40 +++++++++++----------- src/sage/categories/category_with_axiom.py | 14 ++++---- src/sage/combinat/permutation.py | 4 +-- src/sage/sets/cartesian_product.py | 2 +- src/sage_setup/docbuild/ext/multidocs.py | 2 +- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index d797df635fe..4f82568303a 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -396,22 +396,22 @@ class inheritance from ``C.parent_class``. sage: loads(dumps(Ds().element_class)) is Ds().element_class True - .. automethod:: _super_categories - .. automethod:: _super_categories_for_classes - .. automethod:: _all_super_categories - .. automethod:: _all_super_categories_proper - .. automethod:: _set_of_super_categories - .. automethod:: _make_named_class - .. automethod:: _repr_ - .. automethod:: _repr_object_names - .. automethod:: _test_category - .. automethod:: _with_axiom - .. automethod:: _with_axiom_as_tuple - .. automethod:: _without_axioms - .. automethod:: _sort - .. automethod:: _sort_uniq - .. automethod:: __classcall__ - .. automethod:: __init__ + .. automethod:: Category._super_categories + .. automethod:: Category._super_categories_for_classes + .. automethod:: Category._all_super_categories + .. automethod:: Category._all_super_categories_proper + .. automethod:: Category._set_of_super_categories + .. automethod:: Category._make_named_class + .. automethod:: Category._repr_ + .. automethod:: Category._repr_object_names + .. automethod:: Category._test_category + .. automethod:: Category._with_axiom + .. automethod:: Category._with_axiom_as_tuple + .. automethod:: Category._without_axioms + .. automethod:: Category._sort + .. automethod:: Category._sort_uniq + .. automethod:: Category.__classcall__ + .. automethod:: Category.__init__ """ @staticmethod def __classcall__(cls, *args, **options): @@ -2701,7 +2701,7 @@ class CategoryWithParameters(Category): sage: C1.parent_class is C3.parent_class False - .. automethod:: _make_named_class + .. automethod:: Category._make_named_class """ def _make_named_class(self, name, method_provider, cache = False, **options): @@ -2927,9 +2927,9 @@ class JoinCategory(CategoryWithParameters): sage: type(A3) is type(A5) True - .. automethod:: _repr_object_names - .. automethod:: _repr_ - .. automethod:: _without_axioms + .. automethod:: Category._repr_object_names + .. automethod:: Category._repr_ + .. automethod:: Category._without_axioms """ def __init__(self, super_categories, **kwds): diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index 69f7c63cc3f..741420e7331 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1855,13 +1855,13 @@ class CategoryWithAxiom(Category): how to implement axioms and the documentation of the axiom infrastructure. - .. automethod:: __classcall__ - .. automethod:: __classget__ - .. automethod:: __init__ - .. automethod:: _repr_object_names - .. automethod:: _repr_object_names_static - .. automethod:: _test_category_with_axiom - .. automethod:: _without_axioms + .. automethod:: CategoryWithAxiom.__classcall__ + .. automethod:: CategoryWithAxiom.__classget__ + .. automethod:: CategoryWithAxiom.__init__ + .. automethod:: CategoryWithAxiom._repr_object_names + .. automethod:: CategoryWithAxiom._repr_object_names_static + .. automethod:: CategoryWithAxiom._test_category_with_axiom + .. automethod:: CategoryWithAxiom._without_axioms """ @lazy_class_attribute diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 7290a0013d7..52fe8392753 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -419,8 +419,8 @@ class Permutation(CombinatorialElement): sage: Permutation( [[], []] ) [] - .. automethod:: _left_to_right_multiply_on_right - .. automethod:: _left_to_right_multiply_on_left + .. automethod:: Permutation.left_action_product + .. automethod:: Permutation.right_action_product """ @staticmethod def __classcall_private__(cls, l, check_input = True): diff --git a/src/sage/sets/cartesian_product.py b/src/sage/sets/cartesian_product.py index c84c44747b5..f3354107fb6 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -46,7 +46,7 @@ class CartesianProduct(UniqueRepresentation, Parent): and Category of Cartesian products of monoids and Category of Cartesian products of finite enumerated sets - .. automethod:: _cartesian_product_of_elements + .. automethod:: CartesianProduct._cartesian_product_of_elements """ def __init__(self, sets, category, flatten=False): r""" diff --git a/src/sage_setup/docbuild/ext/multidocs.py b/src/sage_setup/docbuild/ext/multidocs.py index 5dec2010107..62d39461df2 100644 --- a/src/sage_setup/docbuild/ext/multidocs.py +++ b/src/sage_setup/docbuild/ext/multidocs.py @@ -236,7 +236,7 @@ def write_citations(app, citations): """ from sage.misc.temporary_file import atomic_write outdir = citation_dir(app) - with atomic_write(os.path.join(outdir, CITE_FILENAME)) as f: + with atomic_write(os.path.join(outdir, CITE_FILENAME), binary=True) as f: cPickle.dump(citations, f) app.info("Saved pickle file: %s" % CITE_FILENAME) From ff5b7ccbaf5ae79b27d856d9862d056fe07e6aad Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 10 Aug 2018 21:01:57 +0200 Subject: [PATCH 233/284] Stop using 3-argument version of MethodType() --- src/sage/graphs/bipartite_graph.py | 24 ++++++------------- .../numerical/backends/logging_backend.py | 12 ++++------ 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 738f571157d..461606bb52a 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -315,14 +315,10 @@ def __init__(self, data=None, partition=None, check=True, *args, **kwds): # need to turn off partition checking for Graph.__init__() adding # vertices and edges; methods are restored ad the end of big "if" # statement below - import types - self.add_vertex = types.MethodType(Graph.add_vertex, - self, - BipartiteGraph) - self.add_vertices = types.MethodType(Graph.add_vertices, - self, - BipartiteGraph) - self.add_edge = types.MethodType(Graph.add_edge, self, BipartiteGraph) + from types import MethodType + self.add_vertex = MethodType(Graph.add_vertex, self) + self.add_vertices = MethodType(Graph.add_vertices, self) + self.add_edge = MethodType(Graph.add_edge, self) from sage.structure.element import is_Matrix if isinstance(data, BipartiteGraph): @@ -432,15 +428,9 @@ def __init__(self, data=None, partition=None, check=True, *args, **kwds): raise TypeError("Input graph is not bipartite!") # restore vertex partition checking - self.add_vertex = types.MethodType(BipartiteGraph.add_vertex, - self, - BipartiteGraph) - self.add_vertices = types.MethodType(BipartiteGraph.add_vertices, - self, - BipartiteGraph) - self.add_edge = types.MethodType(BipartiteGraph.add_edge, - self, - BipartiteGraph) + del self.add_vertex + del self.add_vertices + del self.add_edge # post-processing if isinstance(data, str): diff --git a/src/sage/numerical/backends/logging_backend.py b/src/sage/numerical/backends/logging_backend.py index b2c56e9a6c9..774e210c11e 100644 --- a/src/sage/numerical/backends/logging_backend.py +++ b/src/sage/numerical/backends/logging_backend.py @@ -194,22 +194,18 @@ def base_ring(self): else: return self._backend.base_ring() + # Override all methods that we inherited from GenericBackend # by delegating methods def _override_attr(attr): """ Override a method by a delegating method. - - TESTS:: - - sage: from sage.numerical.backends.logging_backend import _override_attr """ a = getattr(LoggingBackend, attr) if callable(a): - # make an unbound method - import types - _mm = types.MethodType(_make_wrapper(GenericBackend(), attr), None, LoggingBackend) - setattr(LoggingBackend, attr, _mm) + m = _make_wrapper(GenericBackend(), attr) + setattr(LoggingBackend, attr, m) + for attr in dir(LoggingBackend): if not attr.startswith("_") and attr not in ("zero", "base_ring"): From 15e13d737821a0024a0c7e7ddc187711fe2ccc89 Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Fri, 10 Aug 2018 21:16:23 +0200 Subject: [PATCH 234/284] Delete unused "six" from mac app --- src/mac-app/tools/createDSStore/six.py | 891 ------------------------- 1 file changed, 891 deletions(-) delete mode 100644 src/mac-app/tools/createDSStore/six.py diff --git a/src/mac-app/tools/createDSStore/six.py b/src/mac-app/tools/createDSStore/six.py deleted file mode 100644 index 6bf4fd38104..00000000000 --- a/src/mac-app/tools/createDSStore/six.py +++ /dev/null @@ -1,891 +0,0 @@ -# Copyright (c) 2010-2017 Benjamin Peterson -# -# 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. - -"""Utilities for writing code that runs on Python 2 and 3""" - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.11.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("getoutput", "commands", "subprocess"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("email_mime_image", "email.MIMEImage", "email.mime.image"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote_to_bytes", "urllib", "urllib.parse", "unquote", "unquote_to_bytes"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("splitvalue", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), - MovedAttribute("parse_http_list", "urllib2", "urllib.request"), - MovedAttribute("parse_keqv_list", "urllib2", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - try: - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - finally: - value = None - tb = None - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - try: - raise tp, value, tb - finally: - tb = None -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - try: - if from_value is None: - raise value - raise value from from_value - finally: - value = None -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - try: - raise value from from_value - finally: - value = None -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) From 100ad84b38ff59a16eb83307a8c13b6b29f6808d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 11 Aug 2018 10:19:17 +0200 Subject: [PATCH 235/284] in links, do not sort the connected components --- src/sage/knots/link.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index be449d98963..263602cdc10 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -1032,7 +1032,7 @@ def _enhanced_states(self): G.add_edge((cr[2], cr[3], n), cr[2]) G.add_edge((cr[2], cr[3], n), cr[3]) sm = set(tuple(sorted(x for x in b if isinstance(x, tuple))) - for b in G.connected_components()) + for b in G.connected_components(sort=False)) iindex = (writhe - ncross + 2 * sum(v)) // 2 jmin = writhe + iindex - len(sm) jmax = writhe + iindex + len(sm) @@ -2439,10 +2439,11 @@ def _isolated_components(self): V = G.vertices() setV = [set(c) for c in V] for i in range(len(V) - 1): - for j in range(i+1, len(V)): + for j in range(i + 1, len(V)): if setV[i].intersection(setV[j]): G.add_edge(V[i], V[j]) - return [[list(i) for i in j] for j in G.connected_components()] + return [[list(i) for i in j] + for j in G.connected_components(sort=False)] def homfly_polynomial(self, var1='L', var2='M', normalization = 'lm'): r""" From 19f82d12c5f522f4f9b0ad05a30c492ffaaac2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 11 Aug 2018 13:08:44 +0200 Subject: [PATCH 236/284] various fixes for py3 in combinat and root_systems --- src/sage/combinat/designs/orthogonal_arrays.py | 2 +- src/sage/combinat/root_system/coxeter_matrix.py | 5 +++-- src/sage/combinat/root_system/pieri_factors.py | 5 +++-- .../combinat/root_system/reflection_group_c.pyx | 13 +++++++------ src/sage/combinat/root_system/type_affine.py | 7 +++++++ src/sage/combinat/root_system/type_reducible.py | 6 +++--- src/sage/combinat/root_system/weyl_characters.py | 8 ++++---- 7 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 99daa1f415d..56c6d39ce49 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1331,7 +1331,7 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): IOA.remove(h2) holes = sum(holes,[]) - holes = map(list, list(zip(*holes))) + holes = list(map(list, zip(*holes))) # Building the relabel matrix for l in holes: diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index c9981228450..97af4010bf8 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -1100,8 +1100,9 @@ def recognize_coxeter_type_from_matrix(coxeter_matrix, index_set): # First, we build the Coxeter graph of the group without the edge labels n = ZZ(coxeter_matrix.nrows()) G = Graph([[index_set[i], index_set[j], coxeter_matrix[i, j]] - for i in range(n) for j in range(i,n) - if coxeter_matrix[i, j] not in [1, 2]]) + for i in range(n) for j in range(i, n) + if coxeter_matrix[i, j] not in [1, 2]], + format='list_of_edges') G.add_vertices(index_set) types = [] diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index d442dc18aa9..16c8ab98f71 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -62,8 +62,9 @@ class PieriFactors(UniqueRepresentation, Parent): sage: PF = W.pieri_factors() sage: PF.generating_series() 6*z^6 + 14*z^5 + 18*z^4 + 15*z^3 + 9*z^2 + 4*z + 1 - sage: [w.reduced_word() for w in PF if w.length() == 2] - [[2, 3], [1, 0], [2, 0], [0, 1], [2, 1], [3, 1], [3, 0], [3, 2], [1, 2]] + sage: sorted(w.reduced_word() for w in PF if w.length() == 2) + [[0, 1], [1, 0], [1, 2], [2, 0], [2, 1], + [2, 3], [3, 0], [3, 1], [3, 2]] REFERENCES: diff --git a/src/sage/combinat/root_system/reflection_group_c.pyx b/src/sage/combinat/root_system/reflection_group_c.pyx index 9816485a19e..a1c4ef54ac9 100644 --- a/src/sage/combinat/root_system/reflection_group_c.pyx +++ b/src/sage/combinat/root_system/reflection_group_c.pyx @@ -362,14 +362,14 @@ cdef class Iterator(object): cdef list elts = [W.one()] for i in range(1, self.n): - coset_reps = reduced_coset_repesentatives(W, self.order[:i], - self.order[:i-1], True) + coset_reps = reduced_coset_representatives(W, self.order[:i], + self.order[:i-1], True) elts = [_new_mul_(w, v) for w in elts for v in coset_reps] # the list ``elts`` now contains all prods of red coset reps - coset_reps = reduced_coset_repesentatives(W, self.order, - self.order[:len(self.order)-1], True) + coset_reps = reduced_coset_representatives(W, self.order, + self.order[:len(self.order)-1], True) for w in elts: for v in coset_reps: @@ -499,7 +499,7 @@ cpdef PermutationGroupElement reduce_in_coset(PermutationGroupElement w, tuple S si = (S[i]) w = _new_mul_(w, si) -cdef list reduced_coset_repesentatives(W, list parabolic_big, list parabolic_small, +cdef list reduced_coset_representatives(W, list parabolic_big, list parabolic_small, bint right): cdef tuple S = tuple(W.simple_reflections()) cdef int N = W.number_of_reflections() @@ -550,7 +550,8 @@ def parabolic_iteration_application(W, f): True """ cdef int i - cdef list coset_reps = [reduced_coset_repesentatives(W, range(i+1), range(i), True) + cdef list coset_reps = [reduced_coset_representatives(W, list(range(i+1)), + list(range(i)), True) for i in range(W.rank())] parabolic_recursive(W.one(), coset_reps, f) diff --git a/src/sage/combinat/root_system/type_affine.py b/src/sage/combinat/root_system/type_affine.py index 3c409648b7f..0dcfddc7bfd 100644 --- a/src/sage/combinat/root_system/type_affine.py +++ b/src/sage/combinat/root_system/type_affine.py @@ -14,6 +14,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from .weight_lattice_realizations import WeightLatticeRealizations + class AmbientSpace(CombinatorialFreeModule): r""" Ambient space for affine types. @@ -106,6 +107,11 @@ class AmbientSpace(CombinatorialFreeModule): ['G', 2, 1]^* ['BC', 1, 2]^* ['BC', 5, 2]^* + + TESTS:: + + sage: Lambda[1] + e[0] + e['deltacheck'] """ @classmethod def smallest_base_ring(cls, cartan_type): @@ -153,6 +159,7 @@ def __init__(self, root_system, base_ring): basis_keys, prefix = "e", latex_prefix = "e", + sorting_key=str, category = WeightLatticeRealizations(base_ring)) self._weight_space = self.root_system.weight_space(base_ring=base_ring,extended=True) self.classical().module_morphism(self.monomial, codomain=self).register_as_coercion() diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index d951e790a41..96c46e0f495 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -35,12 +35,12 @@ class CartanType(SageObject, CartanType_abstract): INPUT: - - ``types`` - a list of simple Cartan types + - ``types`` -- a list of simple Cartan types EXAMPLES:: - sage: [t1,t2]=[CartanType(x) for x in ['A',1],['B',2]] - sage: CartanType([t1,t2]) + sage: t1, t2 = [CartanType(x) for x in (['A',1], ['B',2])] + sage: CartanType([t1, t2]) A1xB2 sage: t = CartanType("A2xB2") diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 25f09c3392a..d8be98e6e86 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -1291,7 +1291,7 @@ def symmetric_square(self): # a generic product) in the weight ring to optimize by # running only through pairs of weights instead of couples. c = self.weight_multiplicities() - ckeys = c.keys() + ckeys = list(c) d = {} for j in range(len(ckeys)): for i in range(j+1): @@ -1306,7 +1306,7 @@ def symmetric_square(self): d[t] += coef else: d[t] = coef - for k in d.keys(): + for k in list(d): if d[k] == 0: del d[k] return self.parent().char_from_weights(d) @@ -1322,7 +1322,7 @@ def exterior_square(self): A2(0,1) """ c = self.weight_multiplicities() - ckeys = c.keys() + ckeys = list(c) d = {} for j in range(len(ckeys)): for i in range(j+1): @@ -1337,7 +1337,7 @@ def exterior_square(self): d[t] += coef else: d[t] = coef - for k in d.keys(): + for k in list(d): if d[k] == 0: del d[k] return self.parent().char_from_weights(d) From 45ff3719d4f9059a0e85d142cd1e31cb87efa11c Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 8 Aug 2018 10:31:27 +0200 Subject: [PATCH 237/284] Fix debug build of Singular --- build/pkgs/singular/spkg-install | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/build/pkgs/singular/spkg-install b/build/pkgs/singular/spkg-install index 41dcad1be25..5a4d8def9ee 100644 --- a/build/pkgs/singular/spkg-install +++ b/build/pkgs/singular/spkg-install @@ -6,16 +6,10 @@ SRC=`pwd`/src cd "$SRC" if [ "x$SAGE_DEBUG" = "xyes" ]; then + # This used to disable omalloc but that is not really supported + # by upstream SINGULAR_CONFIGURE="$SINGULAR_CONFIGURE --enable-debug --disable-optimizationflags" - # --disable-omalloc is broken: linking fails because of missing flags - #SINGULAR_CONFIGURE="$SINGULAR_CONFIGURE --disable-omalloc" - - # Replace omalloc by xalloc in places unaffected by --disable-omalloc - # See xalloc/README, altough here we just replace the folder for simplicity - rm -rf "$SRC/omalloc" - mv "$SRC/xalloc" "$SRC/omalloc" - CFLAGS="$CFLAGS -O0 -g" CXXFLAGS="$CXXFLAGS -O0 -g" else From 7456780bfb55ddedbe65b6bc4f31f724c00e7cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 11 Aug 2018 16:16:40 +0200 Subject: [PATCH 238/284] pyflakes cleanup in one file --- src/sage/combinat/designs/orthogonal_arrays.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 56c6d39ce49..588695b0f04 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -61,13 +61,13 @@ from six import itervalues, iteritems from six.moves import range -from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from .designs_pyx import is_orthogonal_array from .group_divisible_designs import GroupDivisibleDesign from .designs_pyx import _OA_cache_set, _OA_cache_get, _OA_cache_construction_available + def transversal_design(k,n,resolvable=False,check=True,existence=False): r""" Return a transversal design of parameters `k,n`. @@ -347,8 +347,6 @@ def transversal_design(k,n,resolvable=False,check=True,existence=False): if existence and _OA_cache_get(k,n) is not None: return _OA_cache_get(k,n) - may_be_available = _OA_cache_construction_available(k,n) is not False - if n == 1: if existence: return True @@ -381,6 +379,7 @@ def transversal_design(k,n,resolvable=False,check=True,existence=False): return TransversalDesign(TD,k,n,check=check) + class TransversalDesign(GroupDivisibleDesign): r""" Class for Transversal Designs @@ -871,7 +870,6 @@ def orthogonal_array(k,n,t=2,resolvable=False, check=True,existence=False,explai return _OA_cache_get(k,n) from .block_design import projective_plane - from .latin_squares import mutually_orthogonal_latin_squares from .database import OA_constructions, MOLS_constructions, QDM from .orthogonal_arrays_find_recursive import find_recursive_construction from .difference_matrices import difference_matrix @@ -1664,7 +1662,6 @@ def OA_n_times_2_pow_c_from_matrix(k,c,G,A,Y,check=True): The Australasian Journal of Combinatorics, vol 10 (1994) """ from sage.rings.finite_rings.finite_field_constructor import FiniteField - from sage.rings.integer import Integer from itertools import combinations from .designs_pyx import is_difference_matrix @@ -1862,11 +1859,10 @@ def OA_from_Vmt(m,t,V): sage: _ = designs.orthogonal_arrays.build(6,46) # indirect doctest """ - from sage.rings.finite_rings.finite_field_constructor import FiniteField - q = m*t+1 Fq, M = QDM_from_Vmt(m,t,V) return OA_from_quasi_difference_matrix(M,Fq,add_col = False) + def QDM_from_Vmt(m,t,V): r""" Return a QDM from a `V(m,t)` From f2e2002f4742d600698af5423bcc7c75a7e2db67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 10 Aug 2018 08:49:22 +0200 Subject: [PATCH 239/284] various changes in pyx files, using iterators rather than lists --- src/sage/categories/map.pyx | 4 ++-- src/sage/geometry/triangulation/base.pyx | 17 +++++++++-------- src/sage/libs/pynac/pynac.pyx | 16 +++++++++------- src/sage/matrix/matrix2.pyx | 6 +++--- .../function_field/function_field_element.pyx | 3 ++- .../rings/number_field/number_field_element.pyx | 2 +- .../polynomial/multi_polynomial_libsingular.pyx | 2 +- .../polynomial/multi_polynomial_ring_base.pyx | 4 ++-- .../rings/polynomial/polynomial_element.pyx | 2 +- .../polynomial_integer_dense_flint.pyx | 3 +-- .../polynomial/polynomial_integer_dense_ntl.pyx | 3 +-- src/sage/rings/real_double.pyx | 2 +- 12 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index 4ec1ddc7acd..a9790dcff4b 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -1896,7 +1896,7 @@ cdef class FormalCompositeMap(Map): else: return True - if all([f.is_surjective() for f in injectives]): + if all(f.is_surjective() for f in injectives): return False raise NotImplementedError("Not enough information to deduce injectivity.") @@ -1962,7 +1962,7 @@ cdef class FormalCompositeMap(Map): else: return True - if all([f.is_injective() for f in surjectives]): + if all(f.is_injective() for f in surjectives): return False raise NotImplementedError("Not enough information to deduce surjectivity.") diff --git a/src/sage/geometry/triangulation/base.pyx b/src/sage/geometry/triangulation/base.pyx index 1512c5a06d4..1ca2f15ddb8 100644 --- a/src/sage/geometry/triangulation/base.pyx +++ b/src/sage/geometry/triangulation/base.pyx @@ -436,7 +436,7 @@ cdef class PointConfiguration_base(Parent): # We now are sure that projective_points is not empty self._ambient_dim = len(projective_points[0])-1 - assert all([ len(p)==self._ambient_dim+1 for p in projective_points ]), \ + assert all(len(p) == self._ambient_dim+1 for p in projective_points), \ 'The given point coordinates must all have the same length.' assert len(uniq(projective_points)) == len(projective_points), \ 'Not all points are pairwise distinct.' @@ -444,14 +444,14 @@ cdef class PointConfiguration_base(Parent): proj = matrix(projective_points).transpose() self._base_ring = proj.base_ring() - if all([ x==1 for x in proj.row(self.ambient_dim()) ]): - aff = proj.submatrix(0,0,nrows=self.ambient_dim()) + if all(x == 1 for x in proj.row(self.ambient_dim())): + aff = proj.submatrix(0, 0, nrows=self.ambient_dim()) else: - raise NotImplementedError # TODO + raise NotImplementedError # TODO if n>1: # shift first point to origin - red = matrix([ aff.column(i)-aff.column(0) for i in range(0,n) ]).transpose() + red = matrix([ aff.column(i)-aff.column(0) for i in range(n) ]).transpose() # pick linearly independent rows red = matrix([ red.row(i) for i in red.pivot_rows()]) else: @@ -461,8 +461,9 @@ cdef class PointConfiguration_base(Parent): from sage.modules.free_module import VectorSpace self._reduced_affine_vector_space = VectorSpace(self._base_ring.fraction_field(), self._dim) self._reduced_projective_vector_space = VectorSpace(self._base_ring.fraction_field(), self._dim+1) - self._pts = tuple([ Point(self, i, proj.column(i), aff.column(i), red.column(i)) - for i in range(0,n) ]) + self._pts = tuple([Point(self, i, proj.column(i), + aff.column(i), red.column(i)) + for i in range(n)]) def __hash__(self): r""" @@ -972,7 +973,7 @@ cdef class ConnectedTriangulationsIterator(SageObject): try: enumerated_simplices_seed = seed.enumerated_simplices() except AttributeError: - enumerated_simplices_seed = tuple([ int(t) for t in seed ]) + enumerated_simplices_seed = tuple([int(t) for t in seed]) assert self._tp == NULL self._tp = init_triangulations(point_configuration.n_points(), point_configuration.dim()+1, diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 87340fd380c..84e4d17b170 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -617,7 +617,7 @@ cdef stdstring* py_print_fderivative(unsigned id, params, derivative. - args -- arguments of the function. """ - if all([tolerant_is_symbol(a) for a in args]) and len(set(args))==len(args): + if all(tolerant_is_symbol(a) for a in args) and len(set(args)) == len(args): diffvarstr = ', '.join([repr(args[i]) for i in params]) py_res = ''.join(['diff(',py_print_function_pystring(id,args,False),', ',diffvarstr,')']) else: @@ -626,6 +626,7 @@ cdef stdstring* py_print_fderivative(unsigned id, params, py_res = ostr + fstr return string_from_pystr(py_res) + def py_print_fderivative_for_doctests(id, params, args): """ Used for testing a cdef'd function. @@ -662,6 +663,7 @@ def py_print_fderivative_for_doctests(id, params, args): print(char_to_str(ostr.c_str())) del ostr + cdef stdstring* py_latex_fderivative(unsigned id, params, args): """ @@ -671,14 +673,14 @@ cdef stdstring* py_latex_fderivative(unsigned id, params, See documentation of py_print_fderivative for more information. """ - if all([tolerant_is_symbol(a) for a in args]) and len(set(args))==len(args): - param_iter=iter(params) - v=next(param_iter) - nv=1 - diff_args=[] + if all(tolerant_is_symbol(a) for a in args) and len(set(args)) == len(args): + param_iter = iter(params) + v = next(param_iter) + nv = 1 + diff_args = [] for next_v in param_iter: if next_v == v: - nv+=1 + nv += 1 else: if nv == 1: diff_args.append(r"\partial %s"%(args[v]._latex_(),)) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ecf4a1a428a..d9176b3be07 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -10235,8 +10235,8 @@ cdef class Matrix(Matrix1): evals = eigenvalues else: evals = A.charpoly().roots() - if sum([mult for (_,mult) in evals]) < n: - raise RuntimeError("Some eigenvalue does not exist in %s." %(A.base_ring())) + if sum(mult for (_, mult) in evals) < n: + raise RuntimeError("Some eigenvalue does not exist in %s." % (A.base_ring())) # Compute the block information. Here, ``blocks`` is a list of pairs, # each first entry a root and each second entry the size of a block. @@ -10490,7 +10490,7 @@ cdef class Matrix(Matrix1): raise ValueError('matrix entries must be from a field, not {0}'.format(self.base_ring())) evals = self.charpoly().roots() - if sum([mult for (_,mult) in evals]) < self._nrows: + if sum(mult for (_, mult) in evals) < self._nrows: raise RuntimeError('an eigenvalue of the matrix is not contained in {0}'.format(self.base_ring())) # Obtaining a generic minimal polynomial requires much more diff --git a/src/sage/rings/function_field/function_field_element.pyx b/src/sage/rings/function_field/function_field_element.pyx index 18c74ce02a0..74d61a7789e 100644 --- a/src/sage/rings/function_field/function_field_element.pyx +++ b/src/sage/rings/function_field/function_field_element.pyx @@ -316,7 +316,8 @@ cdef class FunctionFieldElement(FieldElement): W^2 - W + 4*x """ R = self.parent().base_field().maximal_order() - return all([a in R for a in self.minimal_polynomial()]) + return all(a in R for a in self.minimal_polynomial()) + cdef class FunctionFieldElement_polymod(FunctionFieldElement): """ diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 4d5910cb4c6..876d719545d 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -3488,7 +3488,7 @@ cdef class NumberFieldElement(FieldElement): sage: ((a-b)/2).is_integral() False """ - return all([a in ZZ for a in self.absolute_minpoly()]) + return all(a in ZZ for a in self.absolute_minpoly()) def matrix(self, base=None): r""" diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 2a8d9ae0218..f1183d4b124 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4843,7 +4843,7 @@ cdef class MPolynomial_libsingular(MPolynomial): False """ # TODO: Use Singular (4.x) intrinsics. (Temporary solution from #17254.) - return all([ e == 1 for (f, e) in self.factor() ]) + return all(e == 1 for (f, e) in self.factor()) @coerce_binop def quo_rem(self, MPolynomial_libsingular right): diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index e324c1adced..5e38862ebf9 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -1306,9 +1306,9 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): if len(flist) <= 0: raise TypeError('input list should contain at least 1 polynomial') - if not all([f.is_homogeneous() for f in flist]): + if not all(f.is_homogeneous() for f in flist): raise TypeError('resultant for non-homogeneous polynomials is not supported') - if not all([self.is_parent_of(f) for f in flist]): + if not all(self.is_parent_of(f) for f in flist): raise TypeError('not all inputs are polynomials in the calling ring') sparse = kwds.pop('sparse', False) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 8476f3a87a6..9f2dcb6faa1 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -9034,7 +9034,7 @@ cdef class Polynomial(CommutativeAlgebraElement): # - AttributeError in case p-th roots are not (or do not exist) except (NotImplementedError, AttributeError): F = self.factor() - return all([e<=1 for (f,e) in F]) + return all(e <= 1 for (f, e) in F) def radical(self): """ diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index bda1bc4d9c1..1a38d8410ca 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -1639,7 +1639,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): p = Integer(p) if not p.is_prime(): raise ValueError("p must be prime") - if all([c%p==0 for c in self.coefficients()]): + if not any(c % p for c in self.coefficients()): raise ArithmeticError("factorization of 0 is not defined") f = self.__pari__() G = f.factormod(p) @@ -1647,7 +1647,6 @@ cdef class Polynomial_integer_dense_flint(Polynomial): R = k[self.parent().variable_name()] return R(1)._factor_pari_helper(G, unit=R(self).leading_coefficient()) - def factor_padic(self, p, prec=10): """ Return `p`-adic factorization of self to given precision. diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index d0985591d68..a095717ef14 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -1023,7 +1023,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): p = Integer(p) if not p.is_prime(): raise ValueError("p must be prime") - if all([c%p==0 for c in self.coefficients()]): + if not any(c % p for c in self.coefficients()): raise ArithmeticError("factorization of 0 is not defined") f = self.__pari__() G = f.factormod(p) @@ -1031,7 +1031,6 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): R = k[self.parent().variable_name()] return R(1)._factor_pari_helper(G, unit=R(self).leading_coefficient()) - def factor_padic(self, p, prec=10): """ Return `p`-adic factorization of self to given precision. diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 09c76cfdf16..f2a58c58af6 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -665,7 +665,7 @@ cdef class RealDoubleField_class(Field): # collect real roots and conjugate pairs of non-real roots real_roots = [(r, e) for r, e in roots if r.imag().is_zero()] non_real_roots = {r: e for r, e in roots if not r.imag().is_zero()} - assert all([non_real_roots[r.conj()] == e for r, e in non_real_roots.items()]), "Bug in root finding code over RDF - roots must always come in conjugate pairs" + assert all(non_real_roots[r.conj()] == e for r, e in non_real_roots.items()), "Bug in root finding code over RDF - roots must always come in conjugate pairs" non_real_roots = [(r, e) for r, e in non_real_roots.items() if r.imag() > 0] # turn the roots into irreducible factors From bf817f4554309268ea10ae4dbc140d2bd79cee76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 11 Aug 2018 17:08:44 +0200 Subject: [PATCH 240/284] py3 fixes for oeis --- src/sage/databases/oeis.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index 37ca61d006d..aa4ead63ef0 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -219,7 +219,7 @@ def _urls(html_string): """ urls = [] - from HTMLParser import HTMLParser + from html.parser import HTMLParser class MyHTMLParser(HTMLParser): def handle_starttag(self, tag, attrs): @@ -369,7 +369,7 @@ def __call__(self, query, max_results=3, first_result=0): sage: oeis() Traceback (most recent call last): ... - TypeError: __call__() takes at least 2 arguments (1 given) + TypeError: __call__() ... """ if isinstance(query, str): if re.match('^A[0-9]{6}$', query): @@ -662,7 +662,7 @@ def __init__(self, entry): self._fields = defaultdict(list) for line in entry.splitlines(): self._fields[line[1]].append(line[11:]) - if 'dead' in self.keywords(): + if 'dead' in self.keywords(): from warnings import warn warn('This sequence is dead: "{}: {}"'.format(self.id(), self.name()), RuntimeWarning) @@ -1795,8 +1795,8 @@ class FancyTuple(tuple): """ def __repr__(self): r""" - Prints the tuple with one value per line, each line begins with the - index of the value in ``self``. + Print the tuple with one value per line, where each line + begins with the index of the value in ``self``. EXAMPLES:: @@ -1814,7 +1814,7 @@ def __repr__(self): def __getslice__(self, i, j): r""" The slice of a FancyTuple remains a FancyTuple. - + EXAMPLES:: sage: from sage.databases.oeis import FancyTuple @@ -1822,7 +1822,7 @@ def __getslice__(self, i, j): sage: t[-2:] 0: three 1: 4 - + TESTS:: sage: t = ('é', 'è', 'à', 'ç') @@ -1834,4 +1834,5 @@ def __getslice__(self, i, j): """ return FancyTuple(tuple(self).__getslice__(i, j)) + oeis = OEIS() From 232b415be6a3f5d3423175bc0671b265ac5f27b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 11 Aug 2018 21:32:14 +0200 Subject: [PATCH 241/284] pyflakes cleanup in species akes inl --- src/sage/combinat/species/cycle_species.py | 2 +- src/sage/combinat/species/product_species.py | 9 +++---- src/sage/combinat/species/series.py | 26 +++++++------------- src/sage/combinat/species/species.py | 15 ++++++----- src/sage/combinat/species/stream.py | 7 +++--- 5 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/sage/combinat/species/cycle_species.py b/src/sage/combinat/species/cycle_species.py index 45168c11203..cab160af3a6 100644 --- a/src/sage/combinat/species/cycle_species.py +++ b/src/sage/combinat/species/cycle_species.py @@ -56,7 +56,7 @@ def permutation_group_element(self): sage: a.permutation_group_element() (1,2,3) """ - from sage.groups.all import PermutationGroupElement, SymmetricGroup + from sage.groups.all import PermutationGroupElement return PermutationGroupElement(tuple(self._list)) def transport(self, perm): diff --git a/src/sage/combinat/species/product_species.py b/src/sage/combinat/species/product_species.py index 9b09abbcf75..90b9733e434 100644 --- a/src/sage/combinat/species/product_species.py +++ b/src/sage/combinat/species/product_species.py @@ -22,11 +22,11 @@ from sage.structure.unique_representation import UniqueRepresentation -class ProductSpeciesStructure(GenericSpeciesStructure): +class ProductSpeciesStructure(GenericSpeciesStructure): def __init__(self, parent, labels, subset, left, right): """ TESTS:: - + sage: S = species.SetSpecies() sage: F = S * S sage: a = F.structures(['a','b','c']).random_element() @@ -182,7 +182,6 @@ def automorphism_group(self): from sage.combinat.species.misc import change_support left, right = self._list - n = len(self._labels) #Get the supports for each of the sides l_support = self._subset._list @@ -237,7 +236,7 @@ def __init__(self, F, G, min=None, max=None, weight=None): def left_factor(self): """ Returns the left factor of this product. - + EXAMPLES:: sage: P = species.PermutationSpecies() @@ -251,7 +250,7 @@ def left_factor(self): def right_factor(self): """ Returns the right factor of this product. - + EXAMPLES:: sage: P = species.PermutationSpecies() diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index 2a5f966432d..4baae057c44 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -36,7 +36,6 @@ from sage.misc.all import prod from functools import partial from sage.misc.misc import repr_lincomb, is_iterator -from sage.misc.superseded import deprecated_function_alias from sage.algebras.algebra import Algebra import sage.structure.parent_base @@ -61,12 +60,8 @@ def __init__(self, R, element_class = None, names=None): """ #Make sure R is a ring with unit element - if not R in Rings(): + if R not in Rings(): raise TypeError("Argument R must be a ring.") - try: - z = R(Integer(1)) - except Exception: - raise ValueError("R must have a unit element") #Take care of the names if names is None: @@ -98,7 +93,7 @@ def __repr__(self): return "Lazy Power Series Ring over %s"%self.base_ring() def __eq__(self, x): - """ + """ Check whether ``self`` is equal to ``x``. EXAMPLES:: @@ -535,9 +530,9 @@ def _get_repr_info(self, x): """ n = len(self._stream) m = ['1', x] - m += [x+"^"+str(i) for i in range(2, n)] - c = [ self._stream[i] for i in range(n) ] - return [ (m,c) for m,c in zip(m,c) if c != 0] + m += [x + "^" + str(i) for i in range(2, n)] + c = [self._stream[i] for i in range(n)] + return [(mo, co) for mo, co in zip(m, c) if co != 0] def __repr__(self): """ @@ -1232,7 +1227,7 @@ def __call__(self, y): def _compose_gen(self, y, ao): """ - Returns a iterator for the coefficients of the composition of this + Return a iterator for the coefficients of the composition of this power series with the power series y. EXAMPLES:: @@ -1246,15 +1241,12 @@ def _compose_gen(self, y, ao): """ assert y.coefficient(0) == 0 yield self._stream[0] - z = self.tail().compose(y)*y - c = z.coefficient(1) - + z = self.tail().compose(y) * y n = 1 while True: yield z._stream[n] n += 1 - def tail(self): """ Returns the power series whose coefficients obtained by subtracting @@ -1514,9 +1506,9 @@ def _integral_nonzero_gen(self, integration_constant): for _ in range(ao-1): yield self._zero - n = max(1,ao) + n = max(1, ao) while True: - c = self.coefficient(n-1) + self.coefficient(n - 1) #Check to see if the stream is finite if self.is_finite(n-1): diff --git a/src/sage/combinat/species/species.py b/src/sage/combinat/species/species.py index d97104fc1cb..1468bdc2d2f 100644 --- a/src/sage/combinat/species/species.py +++ b/src/sage/combinat/species/species.py @@ -260,15 +260,14 @@ def __repr__(self): else: name = "Combinatorial species" - optional = False options = [] if self._min is not None: - options.append('min=%s'%self._min) + options.append('min=%s' % self._min) if self._max is not None: - options.append('max=%s'%self._max) + options.append('max=%s' % self._max) if self._weight != 1: - options.append('weight=%s'%self._weight) + options.append('weight=%s' % self._weight) if options: name += " with " + ", ".join(options) @@ -788,12 +787,13 @@ def algebraic_equation_system(self): Qz = QQ['z'].fraction_field() #Generate the variable names and the corresponding polynomial rings - var_names = ["node%s"%i for i in range(d.num_verts())] + var_names = ["node%s" % i for i in range(d.num_verts())] R = Qz[", ".join(var_names)] R_gens_dict = R.gens_dict() #A dictionary mapping the nodes to variables - var_mapping = dict((node, R_gens_dict[name]) for node, name in zip(d.vertices(), var_names)) + var_mapping = {node: R_gens_dict[name] + for node, name in zip(d.vertices(), var_names)} var_mapping['z'] = Qz.gen() eqns = [] @@ -807,5 +807,4 @@ def algebraic_equation_system(self): eqns.append(var_mapping[species] - eqn) except AttributeError: raise NotImplementedError - eqns = [eqn.subs(subs) for eqn in eqns] - return eqns + return [eq.subs(subs) for eq in eqns] diff --git a/src/sage/combinat/species/stream.py b/src/sage/combinat/species/stream.py index 1040411f1b2..67ec514e815 100644 --- a/src/sage/combinat/species/stream.py +++ b/src/sage/combinat/species/stream.py @@ -177,7 +177,7 @@ def __init__(self, gen=None, const=None, func=None): def __setitem__(self, i, t): """ - Sets the ith entry of self to t. + Set the i-th entry of self to t. EXAMPLES:: @@ -207,8 +207,8 @@ def __setitem__(self, i, t): sage: s.data() [0, 1, -1, 2, -2, 3, -3, 4, -4, 5, 10] """ - #Compute all of the coefficients up to (and including) the ith one - test = self[i] + # Compute all of the coefficients up to (and including) the ith one + self[i] if i < len(self._list): #If we are here, we can just change the entry in self._list @@ -221,7 +221,6 @@ def __setitem__(self, i, t): self._last_index = i self._list[i] = t - def set_gen(self, gen): """ EXAMPLES:: From a79e30251c4757969d0b849ce47c4fc07fcc3456 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 12 Aug 2018 10:06:49 +1000 Subject: [PATCH 242/284] Reverted to an_element() and added some additional reviewer changes. --- src/sage/combinat/diagram_algebras.py | 251 +++++++++++++------------- src/sage/combinat/set_partition.py | 23 +-- src/sage/sandpiles/sandpile.py | 20 +- 3 files changed, 143 insertions(+), 151 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 0a3dd7bbaed..5abbf12a0ec 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -2118,44 +2118,110 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): True We now define the partition algebra of rank `2` with parameter ``x`` - over `\ZZ`:: + over `\ZZ` in the usual (diagram) basis:: sage: R. = ZZ[] - sage: P = PartitionAlgebra(2, x, R) - sage: P + sage: A2 = PartitionAlgebra(2, x, R) + sage: A2 Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Integer Ring - sage: P.basis().keys() + sage: A2.basis().keys() Partition diagrams of order 2 - sage: P.basis().keys()([[-2, 1, 2], [-1]]) + sage: A2.basis().keys()([[-2, 1, 2], [-1]]) {{-2, 1, 2}, {-1}} - sage: P.basis().list() - [P{{-2, -1, 1, 2}}, - P{{-2, 1, 2}, {-1}}, - P{{-2}, {-1, 1, 2}}, - P{{-2, -1}, {1, 2}}, - P{{-2}, {-1}, {1, 2}}, - P{{-2, -1, 1}, {2}}, - P{{-2, 1}, {-1, 2}}, - P{{-2, 1}, {-1}, {2}}, - P{{-2, 2}, {-1, 1}}, - P{{-2, -1, 2}, {1}}, - P{{-2, 2}, {-1}, {1}}, - P{{-2}, {-1, 1}, {2}}, - P{{-2}, {-1, 2}, {1}}, - P{{-2, -1}, {1}, {2}}, + sage: A2.basis().list() + [P{{-2, -1, 1, 2}}, P{{-2, 1, 2}, {-1}}, + P{{-2}, {-1, 1, 2}}, P{{-2, -1}, {1, 2}}, + P{{-2}, {-1}, {1, 2}}, P{{-2, -1, 1}, {2}}, + P{{-2, 1}, {-1, 2}}, P{{-2, 1}, {-1}, {2}}, + P{{-2, 2}, {-1, 1}}, P{{-2, -1, 2}, {1}}, + P{{-2, 2}, {-1}, {1}}, P{{-2}, {-1, 1}, {2}}, + P{{-2}, {-1, 2}, {1}}, P{{-2, -1}, {1}, {2}}, P{{-2}, {-1}, {1}, {2}}] - sage: E = P([[1,2],[-2,-1]]); E + sage: E = A2([[1,2],[-2,-1]]); E P{{-2, -1}, {1, 2}} - sage: E in P.basis().list() + sage: E in A2.basis().list() True sage: E^2 x*P{{-2, -1}, {1, 2}} sage: E^5 x^4*P{{-2, -1}, {1, 2}} - sage: (P([[2,-2],[-1,1]]) - 2*P([[1,2],[-1,-2]]))^2 + sage: (A2([[2,-2],[-1,1]]) - 2*A2([[1,2],[-1,-2]]))^2 (4*x-4)*P{{-2, -1}, {1, 2}} + P{{-2, 2}, {-1, 1}} + Next, we construct an element:: + + sage: a2 = A2.an_element(); a2 + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + + There is a natural embedding into partition algebras on more + elements, by adding identity strands:: + + sage: A4 = PartitionAlgebra(4, x, R) + sage: A4(a2) + 3*P{{-4, 4}, {-3, 3}, {-2}, {-1, 1, 2}} + + 2*P{{-4, 4}, {-3, 3}, {-2, -1, 1, 2}} + + 2*P{{-4, 4}, {-3, 3}, {-2, 1, 2}, {-1}} + + Thus, the empty partition corresponds to the identity:: + + sage: A4([]) + P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} + sage: A4(5) + 5*P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} + + The group algebra of the symmetric group is a subalgebra:: + + sage: S3 = SymmetricGroupAlgebra(ZZ, 3) + sage: s3 = S3.an_element(); s3 + [1, 2, 3] + 2*[1, 3, 2] + 3*[2, 1, 3] + [3, 1, 2] + sage: A4(s3) + P{{-4, 4}, {-3, 1}, {-2, 3}, {-1, 2}} + + 2*P{{-4, 4}, {-3, 2}, {-2, 3}, {-1, 1}} + + 3*P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} + + P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} + sage: A4([2,1]) + P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} + + Be careful not to confuse the embedding of the group algebra of + the symmetric group with the embedding of partial set partitions. + The latter are embedded by adding the parts `\{i,-i\}` if + possible, and singletons sets for the remaining parts:: + + sage: A4([[2,1]]) + P{{-4, 4}, {-3, 3}, {-2}, {-1}, {1, 2}} + sage: A4([[-1,3],[-2,-3,1]]) + P{{-4, 4}, {-3, -2, 1}, {-1, 3}, {2}} + + Another subalgebra is the Brauer algebra, which has perfect + matchings as basis elements. The group algebra of the + symmetric group is in fact a subalgebra of the Brauer algebra:: + + sage: B3 = BrauerAlgebra(3, x, R) + sage: b3 = B3(s3); b3 + B{{-3, 1}, {-2, 3}, {-1, 2}} + 2*B{{-3, 2}, {-2, 3}, {-1, 1}} + + 3*B{{-3, 3}, {-2, 1}, {-1, 2}} + B{{-3, 3}, {-2, 2}, {-1, 1}} + + An important basis of the partition algebra is the + :meth:`orbit basis `:: + + sage: O2 = A2.orbit_basis() + sage: o2 = O2([[1,2],[-1,-2]]) + O2([[1,2,-1,-2]]); o2 + O{{-2, -1}, {1, 2}} + O{{-2, -1, 1, 2}} + + The diagram basis element corresponds to the sum of all orbit + basis elements indexed by coarser set partitions:: + + sage: A2(o2) + P{{-2, -1}, {1, 2}} + + We can convert back from the orbit basis to the diagram basis:: + + sage: o2 = O2.an_element(); o2 + 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + sage: A2(o2) + 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + One can work with partition algebras using a symbol for the parameter, leaving the base ring unspecified. This implies that the underlying base ring is Sage's symbolic ring. @@ -2176,9 +2242,9 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: P = PA.basis().list() sage: PA.one() P{{-2, 2}, {-1, 1}} - sage: PA.one()*P[7] == P[7] + sage: PA.one() * P[7] == P[7] True - sage: P[7]*PA.one() == P[7] + sage: P[7] * PA.one() == P[7] True We now give some further examples of the use of the other arguments. @@ -2198,21 +2264,9 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: (PA([[2, -2], [1, -1]]) - 2*PA([[-2, -1], [1, 2]]))^2 == 16*PA([[-2, -1], [1, 2]]) + PA([[2, -2], [1, -1]]) True - TESTS: - - A computation that returned an incorrect result until :trac:`15958`:: - - sage: A = PartitionAlgebra(1,17) - sage: g = SetPartitionsAk(1).list() - sage: a = A[g[1]] - sage: a - P{{-1}, {1}} - sage: a*a - 17*P{{-1}, {1}} - - Symmetric group algebra elements and elements from other subalgebras of the - partition algebra (e.g., ``BrauerAlgebra`` and ``TemperleyLiebAlgebra``) can also - be coerced into the partition algebra:: + Symmetric group algebra elements and elements from other subalgebras + of the partition algebra (e.g., ``BrauerAlgebra`` and + ``TemperleyLiebAlgebra``) can also be coerced into the partition algebra:: sage: S = SymmetricGroupAlgebra(SR, 2) sage: B = BrauerAlgebra(2, x, SR) @@ -2224,8 +2278,9 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: A([[1],[-1],[2,-2]]) * B([[-1,-2],[2,1]]) P{{-2, -1}, {1}, {2}} - The same is true if the elements come from a subalgebra of a partition algebra of - smaller order, or if they are defined over a different base ring:: + The same is true if the elements come from a subalgebra of a partition + algebra of smaller order, or if they are defined over a different + base ring:: sage: R = FractionField(ZZ['q']); q = R.gen() sage: S = SymmetricGroupAlgebra(ZZ, 2) @@ -2236,6 +2291,18 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: A(B([[-1,-2],[2,1]])) P{{-3, 3}, {-2, -1}, {1, 2}} + TESTS: + + A computation that returned an incorrect result until :trac:`15958`:: + + sage: A = PartitionAlgebra(1,17) + sage: g = SetPartitionsAk(1).list() + sage: a = A[g[1]] + sage: a + P{{-1}, {1}} + sage: a*a + 17*P{{-1}, {1}} + Shorthands for working with basis elements are as follows:: sage: S = SymmetricGroupAlgebra(ZZ, 3) @@ -2300,75 +2367,6 @@ def _element_constructor_(self, x): r""" Construct an element of ``self``. - EXAMPLES:: - - sage: R. = QQ[] - - Construct an element in the usual (diagram) basis of the partition algebra:: - - sage: A2 = PartitionAlgebra(2, x, R) - sage: a2 = 3*A2[[-2], [-1, 1, 2]] + 2*A2[[-2, -1, 1, 2]] + 2*A2[[-2, 1, 2], [-1]]; a2 - 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} - - There is a natural embedding into partition algebras on more elements, - by adding identity strands:: - - sage: A4 = PartitionAlgebra(4, x, R) - sage: A4(a2) - 3*P{{-4, 4}, {-3, 3}, {-2}, {-1, 1, 2}} + 2*P{{-4, 4}, {-3, 3}, {-2, -1, 1, 2}} - + 2*P{{-4, 4}, {-3, 3}, {-2, 1, 2}, {-1}} - - Thus, the empty partition corresponds to the identity:: - - sage: A4([]) - P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} - sage: A4(5) - 5*P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} - - The group algebra of the symmetric group is a subalgebra:: - - sage: S3 = SymmetricGroupAlgebra(ZZ, 3) - sage: s3 = S3.an_element(); s3 - [1, 2, 3] + 2*[1, 3, 2] + 3*[2, 1, 3] + [3, 1, 2] - sage: A4(s3) - P{{-4, 4}, {-3, 1}, {-2, 3}, {-1, 2}} + 2*P{{-4, 4}, {-3, 2}, {-2, 3}, {-1, 1}} - + 3*P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} + P{{-4, 4}, {-3, 3}, {-2, 2}, {-1, 1}} - sage: A4([2,1]) - P{{-4, 4}, {-3, 3}, {-2, 1}, {-1, 2}} - - Be careful not to confuse the embedding of the group algebra - of the symmetric group with the embedding of partial set - partitions. The latter are embedded by adding the parts - `\{i,-i\}` if possible, and singletons sets for the remaining - parts:: - - sage: A4([[2,1]]) - P{{-4, 4}, {-3, 3}, {-2}, {-1}, {1, 2}} - sage: A4([[-1,3],[-2,-3,1]]) - P{{-4, 4}, {-3, -2, 1}, {-1, 3}, {2}} - - Another subalgebra is the Brauer algebra, which has perfect - matchings as basis elements. The group algebra of the - symmetric group is in fact a subalgebra of the Brauer - algebra:: - - sage: B3 = BrauerAlgebra(3, x, R) - sage: b3 = B3(s3); b3 - B{{-3, 1}, {-2, 3}, {-1, 2}} + 2*B{{-3, 2}, {-2, 3}, {-1, 1}} - + 3*B{{-3, 3}, {-2, 1}, {-1, 2}} + B{{-3, 3}, {-2, 2}, {-1, 1}} - - An important basis of the partition algebra is the orbit basis:: - - sage: O2 = A2.orbit_basis() - sage: o2 = O2([[1,2],[-1,-2]]) + O2([[1,2,-1,-2]]); o2 - O{{-2, -1}, {1, 2}} + O{{-2, -1, 1, 2}} - - The diagram basis element corresponds to the sum of all orbit - basis elements indexed by coarser set partitions:: - - sage: A2(o2) - P{{-2, -1}, {1, 2}} - TESTS:: sage: import sage.combinat.diagram_algebras as da @@ -2400,7 +2398,6 @@ def _element_constructor_(self, x): Traceback (most recent call last): ... ValueError: the diagram {{-2, 1}, {-1, 2}} must be planar - """ # coercion from basis keys if self.basis().keys().is_parent_of(x): @@ -2484,7 +2481,7 @@ def _coerce_map_from_(self, R): TESTS:: - sage: elt = 3*O3([[-3], [-2, -1, 1, 2, 3]]) + 2*O3([[-3, -2, -1, 1, 2, 3]]) + 2*O3([[-3, -1, 1, 2, 3], [-2]]); elt + sage: elt = O3.an_element(); elt 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} sage: A._coerce_map_from_(O3)(elt) 3*P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + 2*P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} @@ -2563,7 +2560,11 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) - sage: pp = 3*P([[-2], [-1, 1, 2]]) + 2*P([[-2, -1, 1, 2]]) + 2*P([[-2, 1, 2], [-1]]); pp + sage: pp = P.an_element(); + sage: pp.to_orbit_basis() + 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + sage: pp = (3*P([[-2], [-1, 1, 2]]) + 2*P([[-2, -1, 1, 2]]) + ....: + 2*P([[-2, 1, 2], [-1]])); pp 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: pp.to_orbit_basis() 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} @@ -2796,9 +2797,10 @@ def diagram_basis(self): sage: P2 = O2.diagram_basis(); P2 Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field - sage: o2 = 3*O2([[-2, -1, 1], [2]]) + 2*O2([[-2, -1, 1, 2]]) + 2*O2([[-2, -1, 2], [1]]) + sage: o2 = O2.an_element(); o2 + 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: P2(o2) - 3*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, -1, 2}, {1}} + 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} TESTS:: @@ -2873,7 +2875,8 @@ def product_on_basis(self, d1, d2): True sage: o3 * o1 == o1 * o3 and o3 * o1 == o3 True - sage: o4 = 3*OP([[-2, -1, 1], [2]]) + 2*OP([[-2, -1, 1, 2]]) + 2*OP([[-2, -1, 2], [1]]) + sage: o4 = (3*OP([[-2, -1, 1], [2]]) + 2*OP([[-2, -1, 1, 2]]) + ....: + 2*OP([[-2, -1, 2], [1]])) sage: o4 * o4 6*O{{-2, -1, 1}, {2}} + 4*O{{-2, -1, 1, 2}} + 4*O{{-2, -1, 2}, {1}} @@ -2966,18 +2969,20 @@ def matchings(A, B): class Element(PartitionAlgebra.Element): def to_diagram_basis(self): """ - Expand ``self`` in the natural diagram basis of the partition algebra. + Expand ``self`` in the natural diagram basis of the + partition algebra. EXAMPLES:: sage: R. = QQ[] sage: P = PartitionAlgebra(2, x, R) sage: O = P.orbit_basis() - sage: elt = 3*O([[-2], [-1, 1, 2]]) + 2*O([[-2, -1, 1, 2]]) + 2*O([[-2, 1, 2], [-1]]); elt + sage: elt = O.an_element(); elt 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: elt.to_diagram_basis() 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} - sage: pp = 3*P[[-2],[-1, 1, 2]] + 2*P[[-2, -1, 1, 2]] + 2*P[[-2, 1, 2], [-1]] + sage: pp = P.an_element(); pp + 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} sage: op = pp.to_orbit_basis(); op 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} sage: pp == op.to_diagram_basis() @@ -3068,13 +3073,11 @@ def to_orbit_basis(self): sage: R. = QQ[] sage: B = BrauerAlgebra(2, x, R) - sage: b = 3*B[[-2, -1], [1, 2]] + 2*B[[-2, 1], [-1, 2]] + 2*B[[-2, 2], [-1, 1]]; b + sage: bb = B.an_element(); bb 3*B{{-2, -1}, {1, 2}} + 2*B{{-2, 1}, {-1, 2}} + 2*B{{-2, 2}, {-1, 1}} - sage: o = b.to_orbit_basis(); o + sage: bb.to_orbit_basis() 3*O{{-2, -1}, {1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1}, {-1, 2}} + 2*O{{-2, 2}, {-1, 1}} - sage: o.parent() - Orbit basis of Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field """ P = self.parent().lift.codomain() OP = P.orbit_basis() diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index a537292205a..043da2384a6 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -1819,16 +1819,8 @@ def __iter__(self): sage: it = SetPartitions().__iter__() sage: [next(it) for x in range(10)] - [{}, - {{1}}, - {{1, 2}}, - {{1}, {2}}, - {{1, 2, 3}}, - {{1, 2}, {3}}, - {{1, 3}, {2}}, - {{1}, {2, 3}}, - {{1}, {2}, {3}}, - {{1, 2, 3, 4}}] + [{}, {{1}}, {{1, 2}}, {{1}, {2}}, {{1, 2, 3}}, {{1, 2}, {3}}, + {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}, {{1, 2, 3, 4}}] """ n = 0 while True: @@ -1984,7 +1976,6 @@ def _fast_iterator(self): [[1, x], [-1]], [[1], [-1, x]], [[1], [-1], [x]]] - """ base_set = list(self.base_set()) def from_word(w): @@ -2207,7 +2198,7 @@ def _repr_(self): @property def n(self): - """ + r""" ``self.n`` is deprecated; use :meth:`number_of_blocks` instead. TESTS:: @@ -2216,21 +2207,20 @@ def n(self): doctest:...: DeprecationWarning: The attribute n for the number of blocks is deprecated, use the method number_of_blocks instead. See https://trac.sagemath.org/25462 for details. 3 - """ from sage.misc.superseded import deprecation - deprecation(25462, 'The attribute n for the number of blocks is deprecated, use the method number_of_blocks instead.') + deprecation(25462, "The attribute n for the number of blocks is deprecated," + " use the method number_of_blocks instead.") return self.number_of_blocks() def number_of_blocks(self): - """ + r""" Return the number of blocks of the set partitions in ``self``. EXAMPLES:: sage: SetPartitions(5, 3).number_of_blocks() 3 - """ return self._k @@ -2276,7 +2266,6 @@ def _fast_iterator(self): sage: list(SetPartitions([1,-1,x], 2)._fast_iterator()) [[[1, x], [-1]], [[1], [-1, x]], [[1, -1], [x]]] - """ base_set = list(self.base_set()) def from_word(w): diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index 8619a2f753f..271515e7a78 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -6564,19 +6564,19 @@ def admissible_partitions(S, k): sage: from sage.sandpiles.sandpile import admissible_partitions sage: from sage.sandpiles.sandpile import partition_sandpile sage: S = sandpiles.Cycle(4) - sage: P = [set(admissible_partitions(S, i)) for i in [2,3,4]] + sage: P = [admissible_partitions(S, i) for i in [2,3,4]] sage: P - [{{{0}, {1, 2, 3}}, - {{0, 1}, {2, 3}}, - {{0, 1, 2}, {3}}, + [[{{0, 2, 3}, {1}}, + {{0, 3}, {1, 2}}, {{0, 1, 3}, {2}}, - {{0, 2, 3}, {1}}, - {{0, 3}, {1, 2}}}, - {{{0}, {1}, {2, 3}}, + {{0}, {1, 2, 3}}, + {{0, 1}, {2, 3}}, + {{0, 1, 2}, {3}}], + [{{0, 3}, {1}, {2}}, + {{0}, {1}, {2, 3}}, {{0}, {1, 2}, {3}}, - {{0, 1}, {2}, {3}}, - {{0, 3}, {1}, {2}}}, - {{{0}, {1}, {2}, {3}}}] + {{0, 1}, {2}, {3}}], + [{{0}, {1}, {2}, {3}}]] sage: for p in P: ....: sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p]) 6 From 87bf535461a19ba4d8730fe08e91c55bab042003 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 12 Aug 2018 11:12:53 +1000 Subject: [PATCH 243/284] Cythonizing iterator. --- src/sage/combinat/combinat_cython.pxd | 3 + src/sage/combinat/combinat_cython.pyx | 135 +++++++++++++++++++++++++- src/sage/combinat/set_partition.py | 102 +------------------ 3 files changed, 141 insertions(+), 99 deletions(-) diff --git a/src/sage/combinat/combinat_cython.pxd b/src/sage/combinat/combinat_cython.pxd index 3df09e4797e..a973fd6a9f7 100644 --- a/src/sage/combinat/combinat_cython.pxd +++ b/src/sage/combinat/combinat_cython.pxd @@ -1,3 +1,6 @@ from sage.libs.gmp.all cimport mpz_t cdef mpz_stirling_s2(mpz_t s, unsigned long n, unsigned long k) + +cdef list from_word(list w, list base_set) + diff --git a/src/sage/combinat/combinat_cython.pyx b/src/sage/combinat/combinat_cython.pyx index 0d87fabcc04..f42b5c90902 100644 --- a/src/sage/combinat/combinat_cython.pyx +++ b/src/sage/combinat/combinat_cython.pyx @@ -2,13 +2,18 @@ Fast computation of combinatorial functions (Cython + mpz). Currently implemented: + - Stirling numbers of the second kind +- iterators for set partitions AUTHORS: -- Fredrik Johansson (2010-10): Stirling numbers of second kind +- Fredrik Johansson (2010-10): Stirling numbers of second kind +- Martin Rubey and Travis Scrimshaw (2018): iterators for set partitions """ +cimport cython + from cysignals.memory cimport check_allocarray, sig_free from sage.libs.gmp.all cimport * @@ -133,3 +138,131 @@ def _stirling_number2(n, k): cdef Integer s = Integer.__new__(Integer) mpz_stirling_s2(s.value, n, k) return s + +##################################################################### +## Set partition iterators + +@cython.wraparound(False) +@cython.boundscheck(False) +cdef list from_word(list w, list base_set): + cdef list sp = [] + cdef Py_ssize_t i + cdef Py_ssize_t b + for i in range(len(w)): + b = (w[i]) + x = base_set[i] + if len(sp) <= b: + sp.append([x]) + else: + sp[b].append(x) + return sp + +@cython.wraparound(False) +@cython.boundscheck(False) +def set_partition_iterator(base_set): + """ + A fast iterator for the set partitions of the base set, which + returns lists of lists instead of set partitions types. + + EXAMPLES:: + + sage: from sage.combinat.combinat_cython import set_partition_iterator + sage: list(set_partition_iterator([1,-1,x])) + [[[1, -1, x]], + [[1, -1], [x]], + [[1, x], [-1]], + [[1], [-1, x]], + [[1], [-1], [x]]] + """ + cdef list base = list(base_set) + + # Knuth, TAOCP 4A 7.2.1.5, Algorithm H + cdef Py_ssize_t N = len(base) + # H1: initialize + cdef list a = [0] * N + if N <= 1: + yield from_word(a, base) + return + + cdef list b = [1] * N + cdef Py_ssize_t j + cdef Py_ssize_t last = N - 1 + while True: + # H2: visit + yield from_word(a, base) + if a[last] == b[last]: + # H4: find j + j = N - 2 + while a[j] == b[j]: + j -= 1 + # H5: increase a_j + if j == 0: + break + a[j] += 1 + # H6: zero out a_{j+1},...,a_{n-1} + b[last] = b[j] + int(a[j] == b[j]) + j += 1 + while j < N - 1: + a[j] = 0 + b[j] = b[last] + j += 1 + a[last] = 0 + else: + # H3: increase a_{n-1} + a[last] += 1 + +@cython.wraparound(False) +@cython.boundscheck(False) +def _set_partition_block_gen(Py_ssize_t n, Py_ssize_t k, list a): + r""" + Recursively generate set partitions of ``n`` with fixed block + size ``k`` using Algorithm 4.23 from *Combinatorial Generation* + by Ruskey. ``a`` is a list of size ``n``. + + EXAMPLES:: + + sage: from sage.combinat.combinat_cython import _set_partition_block_gen + sage: a = list(range(3)) + sage: for p in _set_partition_block_gen(3, 2, a): + ....: print(p) + [0, 1, 0] + [0, 1, 1] + [0, 0, 1] + """ + cdef Py_ssize_t i + if n == k: + yield a + return + + for i in range(k): + a[n-1] = i + for P in _set_partition_block_gen(n-1, k, a): + yield P + a[n-1] = n-1 + if k > 1: + a[n-1] = k-1 + for P in _set_partition_block_gen(n-1, k-1, a): + yield P + a[n-1] = n-1 + +@cython.wraparound(False) +@cython.boundscheck(False) +def set_partition_iterator_blocks(base_set, Py_ssize_t k): + """ + A fast iterator for the set partitions of the base set into the + specified number of blocks, which returns lists of lists + instead of set partitions types. + + EXAMPLES:: + + sage: from sage.combinat.combinat_cython import set_partition_iterator_blocks + sage: list(set_partition_iterator_blocks([1,-1,x], 2)) + [[[1, x], [-1]], [[1], [-1, x]], [[1, -1], [x]]] + """ + cdef list base = list(base_set) + cdef Py_ssize_t n = len(base) + cdef list a = list(range(n)) + # TODO: implement _set_partition_block_gen as an iterative algorithm + for P in _set_partition_block_gen(n, k, a): + yield from_word( P, base) + diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 043da2384a6..c84ec092db8 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -48,6 +48,8 @@ from sage.rings.integer import Integer from sage.combinat.misc import IterableFunctionCall from sage.combinat.combinatorial_map import combinatorial_map +from sage.combinat.combinat_cython import (set_partition_iterator, + set_partition_iterator_blocks) import sage.combinat.subset as subset from sage.combinat.partition import Partition, Partitions from sage.combinat.set_partition_ordered import OrderedSetPartitions @@ -1960,65 +1962,9 @@ def __iter__(self): sage: SetPartitions(3).list() [{{1, 2, 3}}, {{1, 2}, {3}}, {{1, 3}, {2}}, {{1}, {2, 3}}, {{1}, {2}, {3}}] """ - for sp in self._fast_iterator(): + for sp in set_partition_iterator(sorted(self._set)): yield self.element_class(self, sp, check=False) - def _fast_iterator(self): - """ - A fast iterator for the set partitions of the base set, which - returns lists of lists instead of set partitions types. - - EXAMPLES:: - - sage: list(SetPartitions([1,-1,x])._fast_iterator()) - [[[1, -1, x]], - [[1, -1], [x]], - [[1, x], [-1]], - [[1], [-1, x]], - [[1], [-1], [x]]] - """ - base_set = list(self.base_set()) - def from_word(w): - sp = [] - for i, b in zip(base_set, w): - if len(sp) <= b: - sp.append([i]) - else: - sp[b].append(i) - return sp - - # Knuth, TAOCP 4A 7.2.1.5, Algorithm H - N = len(base_set) - # H1: initialize - a = [0]*N - if N <= 1: - yield from_word(a) - return - b = [1]*N - while True: - # H2: visit - yield from_word(a) - if a[-1] == b[-1]: - # H4: find j - j = N-2 - while a[j] == b[j]: - j -= 1 - # H5: increase a_j - if j == 0: - break - a[j] += 1 - # H6: zero out a_{j+1},...,a_{n-1} - b[-1] = b[j] + (1 if a[j] == b[j] else 0) - j += 1 - while j < N-1: - a[j] = 0 - b[j] = b[-1] - j += 1 - a[-1] = 0 - else: - # H3: increase a_{n-1} - a[-1] += 1 - def base_set(self): """ Return the base set of ``self``. @@ -2253,49 +2199,9 @@ def __iter__(self): {{1, 2}, {3, 4}}, {{1, 2, 3}, {4}}] """ - for sp in self._fast_iterator(): + for sp in set_partition_iterator_blocks(sorted(self._set), self._k): yield self.element_class(self, sp, check=False) - def _fast_iterator(self): - """ - A fast iterator for the set partitions of the base set into the - specified number of blocks, which returns lists of lists - instead of set partitions types. - - EXAMPLES:: - - sage: list(SetPartitions([1,-1,x], 2)._fast_iterator()) - [[[1, x], [-1]], [[1], [-1, x]], [[1, -1], [x]]] - """ - base_set = list(self.base_set()) - def from_word(w): - sp = [] - for i, b in zip(base_set, w): - if len(sp) <= b: - sp.append([i]) - else: - sp[b].append(i) - return sp - - # Ruskey, Combinatorial Generation, Algorithm 4.23 - n = len(base_set) - a = list(range(n)) - def gen(n, k): - if n == k: - yield from_word(a) - else: - for i in range(k): - a[n-1] = i - for P in gen(n-1, k): - yield P - a[n-1] = n-1 - if k > 1: - a[n-1] = k-1 - for P in gen(n-1, k-1): - yield P - a[n-1] = n-1 - return gen(n, self._k) - def __contains__(self, x): """ Check containment. From a6e99a00a84465585537d7d0dc7fbe5b61a7ba3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 12 Aug 2018 09:46:42 +0200 Subject: [PATCH 244/284] pyflakes for species: fix --- src/sage/combinat/species/series.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index 4baae057c44..8e60005de25 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -1242,6 +1242,7 @@ def _compose_gen(self, y, ao): assert y.coefficient(0) == 0 yield self._stream[0] z = self.tail().compose(y) * y + z.coefficient(1) n = 1 while True: yield z._stream[n] From 3c25ec35eeca78c629996eda6bae24dc201d72b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 12 Aug 2018 10:03:54 +0200 Subject: [PATCH 245/284] fix sorting key for affine root systems --- src/sage/combinat/root_system/type_affine.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/root_system/type_affine.py b/src/sage/combinat/root_system/type_affine.py index 0dcfddc7bfd..0a47d1ba6cc 100644 --- a/src/sage/combinat/root_system/type_affine.py +++ b/src/sage/combinat/root_system/type_affine.py @@ -110,8 +110,8 @@ class AmbientSpace(CombinatorialFreeModule): TESTS:: - sage: Lambda[1] - e[0] + e['deltacheck'] + sage: Lambda[1] + e[0] + e['deltacheck'] """ @classmethod def smallest_base_ring(cls, cartan_type): @@ -154,12 +154,15 @@ def __init__(self, root_system, base_ring): """ self.root_system = root_system classical = root_system.cartan_type().classical().root_system().ambient_space(base_ring) - basis_keys = tuple(classical.basis().keys()) + ("delta","deltacheck") + basis_keys = tuple(classical.basis().keys()) + ("delta", "deltacheck") + + def sortkey(x): + return (1 if isinstance(x, str) else 0, x) CombinatorialFreeModule.__init__(self, base_ring, basis_keys, prefix = "e", latex_prefix = "e", - sorting_key=str, + sorting_key=sortkey, category = WeightLatticeRealizations(base_ring)) self._weight_space = self.root_system.weight_space(base_ring=base_ring,extended=True) self.classical().module_morphism(self.monomial, codomain=self).register_as_coercion() From 2a553edb2a7ac44af4d65633de554ffea673119b Mon Sep 17 00:00:00 2001 From: Kevin Dilks Date: Sun, 12 Aug 2018 11:04:56 -0400 Subject: [PATCH 246/284] Fixed spelling of occurrence --- src/sage/combinat/posets/poset_examples.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index fdf31349463..0236c38e9a2 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -39,7 +39,7 @@ :meth:`~posets.PentagonPoset` | Return the Pentagon poset. :meth:`~posets.PermutationPattern` | Return the Permutation pattern poset. :meth:`~posets.PermutationPatternInterval` | Return an interval in the Permutation pattern poset. - :meth:`~posets.PermutationPatternOccurenceInterval` | Return the occurence poset for a pair of comparable elements in the Permutation pattern poset. + :meth:`~posets.PermutationPatternOccurrenceInterval` | Return the occurrence poset for a pair of comparable elements in the Permutation pattern poset. :meth:`~posets.PowerPoset` | Return a power poset. :meth:`~posets.RandomLattice` | Return a random lattice on `n` elements. :meth:`~posets.RandomPoset` | Return a random poset on `n` elements. @@ -1443,7 +1443,7 @@ def YoungFibonacci(n): Elements of the (infinite) lattice are words with letters '1' and '2'. The covers of a word are the words with another '1' - added somewhere not after the first occurence of an existing + added somewhere not after the first occurrence of an existing '1' and, additionally, the words where the first '1' is replaced by a '2'. The lattice is truncated to have rank `n`. @@ -1618,7 +1618,7 @@ def PermutationPatternInterval(bottom, top): return Poset((elem,rel)) @staticmethod - def PermutationPatternOccurenceInterval(bottom, top, pos): + def PermutationPatternOccurrenceInterval(bottom, top, pos): r""" Return the poset consisting of an interval in the poset of permutations under pattern containment between ``bottom`` and @@ -1641,7 +1641,7 @@ def PermutationPatternOccurenceInterval(bottom, top, pos): sage: t = Permutation([3,2,1]) sage: b = Permutation([6,3,4,5,2,1]) - sage: A = posets.PermutationPatternOccurenceInterval(t, b, (0,2,4)); A + sage: A = posets.PermutationPatternOccurrenceInterval(t, b, (0,2,4)); A Finite poset containing 8 elements .. SEEALSO:: From 94b87e9eec1c7cf3257e64eb42a748c24e217c2e Mon Sep 17 00:00:00 2001 From: Bryan Gillespie Date: Sun, 12 Aug 2018 15:24:08 -0600 Subject: [PATCH 247/284] Fix issue with pickling of BinaryMatroid objects Issue is with the __reduce__ method, which returns a groundset list and current basis list which are not compatible with the contract of the __init__ method of BinaryMatroid. This issue can also be found in the classes TernaryMatroid and QuaternaryMatroid, and is fixed. See also: Trac ticket \#23437, https://trac.sagemath.org/ticket/23437. --- src/sage/matroids/linear_matroid.pyx | 40 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 06b97965a79..8a5d310abca 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -472,7 +472,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): Let `M` be a matroid on `n` elements with rank `r`. Let `E` be an ordering of the groundset, as output by :func:`M.groundset_list() `. - A *representation* of the matroid is an `r \times n` matrix with the + A *representation* of the matroid is an `r \\times n` matrix with the following property. Consider column ``i`` to be labeled by ``E[i]``, and denote by `A[F]` the submatrix formed by the columns labeled by the subset `F \subseteq E`. Then for all `F \subseteq E`, the columns @@ -480,7 +480,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): independent set in the matroid. A *reduced representation* is a matrix `D` such that `[I\ \ D]` is a - representation of the matroid, where `I` is an `r \times r` identity + representation of the matroid, where `I` is an `r \\times r` identity matrix. In this case, the rows of `D` are considered to be labeled by the first `r` elements of the list ``E``, and the columns by the remaining `n - r` elements. @@ -3944,20 +3944,30 @@ cdef class BinaryMatroid(LinearMatroid): sage: loads(dumps(M)).representation() [1 0 1] [1 0 1] + + TESTS:: + + From :trac:`23437` and comments: + + sage: M = matroids.named_matroids.Fano().dual() + sage: _ = list(M.bases()) + sage: N = loads(dumps(M)) + sage: N.closure(frozenset({'d'})) + frozenset({'d'}) + sage: N.is_isomorphic(M) + True """ import sage.matroids.unpickling version = 0 + gs = self._E cdef list basis = [0] * self.full_rank() if self._representation is not None: A = self._representation - gs = self._E basis = None else: A = self._A - for e in self.basis(): - basis[self._prow[self._idx[e]]] = e - rows, cols = self._current_rows_cols() - gs = rows + cols + # current basis ordered so matrix cols form identity matrix: + basis, _ = self._current_rows_cols() data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_binary_matroid, (version, data) @@ -4833,17 +4843,15 @@ cdef class TernaryMatroid(LinearMatroid): """ import sage.matroids.unpickling version = 0 + gs = self._E cdef list basis = [0] * self.full_rank() if self._representation is not None: A = self._representation - gs = self._E basis = None else: A = self._A - for e in self.basis(): - basis[self._prow[self._idx[e]]] = e - rows, cols = self._current_rows_cols() - gs = rows + cols + # current basis ordered so matrix cols form identity matrix: + basis, _ = self._current_rows_cols() data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_ternary_matroid, (version, data) @@ -5540,17 +5548,15 @@ cdef class QuaternaryMatroid(LinearMatroid): """ import sage.matroids.unpickling version = 0 + gs = self._E cdef list basis = [0] * self.full_rank() if self._representation is not None: A = self._representation - gs = self._E basis = None else: A = self._A - for e in self.basis(): - basis[self._prow[self._idx[e]]] = e - rows, cols = self._current_rows_cols() - gs = rows + cols + # current basis ordered so matrix cols form identity matrix: + basis, _ = self._current_rows_cols() data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_quaternary_matroid, (version, data) From ba6a115f30e3b7db0b972c0ec2bf3f511457dad3 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Aug 2018 13:53:30 +1000 Subject: [PATCH 248/284] Fixing doctests due to ordering change. --- src/sage/combinat/diagram_algebras.py | 192 +++++++++++++------------ src/sage/combinat/partition_algebra.py | 2 +- src/sage/combinat/tutorial.py | 2 +- 3 files changed, 99 insertions(+), 97 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 5abbf12a0ec..8fb4ae4bd8e 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -66,25 +66,25 @@ def partition_diagrams(k): sage: import sage.combinat.diagram_algebras as da sage: [p for p in da.partition_diagrams(2)] [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, {{-2, 1}, {-1, 2}}, {{-2, 1}, {-1}, {2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] sage: [SetPartition(p) for p in da.partition_diagrams(3/2)] [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] """ if k in ZZ: @@ -166,17 +166,16 @@ def planar_diagrams(k): [{{-2, 1}, {-1, 2}}] sage: all_diagrams = da.partition_diagrams(5/2) sage: [SetPartition(p) for p in all_diagrams if p not in da.planar_diagrams(5/2)] - [{{-3, -1, 3}, {-2, 1, 2}}, + [{{-3, 1, 3}, {-2, -1, 2}}, + {{-3, -1, 3}, {-2, 1, 2}}, {{-3, -2, 1, 3}, {-1, 2}}, - {{-3, -1, 1, 3}, {-2, 2}}, - {{-3, 1, 3}, {-2, -1, 2}}, - {{-3, 1, 3}, {-2, 2}, {-1}}, - {{-3, 1, 3}, {-2}, {-1, 2}}, {{-3, -1, 2, 3}, {-2, 1}}, {{-3, 3}, {-2, 1}, {-1, 2}}, {{-3, -1, 3}, {-2, 1}, {2}}, - {{-3, -1, 3}, {-2, 2}, {1}}] - + {{-3, -1, 1, 3}, {-2, 2}}, + {{-3, -1, 3}, {-2, 2}, {1}}, + {{-3, 1, 3}, {-2, 2}, {-1}}, + {{-3, 1, 3}, {-2}, {-1, 2}}] """ for i in partition_diagrams(k): if is_planar(i): @@ -517,17 +516,17 @@ class IdealDiagram(AbstractPartitionDiagram): Ideal diagrams of order 2 sage: IDs(2).list() [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, - {{-2, 1}, {-1}, {2}}, {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, + {{-2, 1}, {-1}, {2}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] sage: from sage.combinat.diagram_algebras import PartitionDiagrams as PDs @@ -589,18 +588,18 @@ class PlanarDiagram(AbstractPartitionDiagram): Planar diagrams of order 2 sage: PlanarDiagrams(2).list() [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, {{-2, 1}, {-1}, {2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] """ @staticmethod @@ -1128,26 +1127,26 @@ def __iter__(self): sage: import sage.combinat.diagram_algebras as da sage: list(da.PartitionDiagrams(2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, {{-2, 1}, {-1, 2}}, {{-2, 1}, {-1}, {2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] sage: list(da.PartitionDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] sage: list(da.BrauerDiagrams(5/2)) @@ -1164,45 +1163,45 @@ def __iter__(self): sage: list(da.PlanarDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] sage: list(da.PlanarDiagrams(2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, + {{-2, -1, 2}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, {{-2, 1}, {-1}, {2}}, {{-2, 2}, {-1, 1}}, - {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] sage: list(da.IdealDiagrams(3/2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] sage: list(da.IdealDiagrams(2)) [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, - {{-2}, {-1, 1, 2}}, - {{-2, -1}, {1, 2}}, - {{-2}, {-1}, {1, 2}}, {{-2, -1, 1}, {2}}, - {{-2, 1}, {-1}, {2}}, {{-2, -1, 2}, {1}}, - {{-2, 2}, {-1}, {1}}, + {{-2, -1}, {1, 2}}, + {{-2, -1}, {1}, {2}}, + {{-2, 1, 2}, {-1}}, + {{-2, 1}, {-1}, {2}}, + {{-2}, {-1, 1, 2}}, {{-2}, {-1, 1}, {2}}, + {{-2, 2}, {-1}, {1}}, {{-2}, {-1, 2}, {1}}, - {{-2, -1}, {1}, {2}}, + {{-2}, {-1}, {1, 2}}, {{-2}, {-1}, {1}, {2}}] """ # The _diagram_func gets set as a method, but we want to @@ -1594,8 +1593,8 @@ class IdealDiagrams(AbstractPartitionDiagrams): True sage: da.IdealDiagrams(3/2).list() [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] """ Element = IdealDiagram @@ -1636,19 +1635,19 @@ class DiagramAlgebra(CombinatorialFreeModule): sage: D = da.DiagramAlgebra(2, x, R, 'P', da.PartitionDiagrams(2)) sage: list(D.basis()) [P{{-2, -1, 1, 2}}, - P{{-2, 1, 2}, {-1}}, - P{{-2}, {-1, 1, 2}}, - P{{-2, -1}, {1, 2}}, - P{{-2}, {-1}, {1, 2}}, P{{-2, -1, 1}, {2}}, + P{{-2, -1, 2}, {1}}, + P{{-2, -1}, {1, 2}}, + P{{-2, -1}, {1}, {2}}, + P{{-2, 1, 2}, {-1}}, P{{-2, 1}, {-1, 2}}, P{{-2, 1}, {-1}, {2}}, P{{-2, 2}, {-1, 1}}, - P{{-2, -1, 2}, {1}}, - P{{-2, 2}, {-1}, {1}}, + P{{-2}, {-1, 1, 2}}, P{{-2}, {-1, 1}, {2}}, + P{{-2, 2}, {-1}, {1}}, P{{-2}, {-1, 2}, {1}}, - P{{-2, -1}, {1}, {2}}, + P{{-2}, {-1}, {1, 2}}, P{{-2}, {-1}, {1}, {2}}] """ def __init__(self, k, q, base_ring, prefix, diagrams, category=None): @@ -2130,13 +2129,13 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): sage: A2.basis().keys()([[-2, 1, 2], [-1]]) {{-2, 1, 2}, {-1}} sage: A2.basis().list() - [P{{-2, -1, 1, 2}}, P{{-2, 1, 2}, {-1}}, - P{{-2}, {-1, 1, 2}}, P{{-2, -1}, {1, 2}}, - P{{-2}, {-1}, {1, 2}}, P{{-2, -1, 1}, {2}}, + [P{{-2, -1, 1, 2}}, P{{-2, -1, 1}, {2}}, + P{{-2, -1, 2}, {1}}, P{{-2, -1}, {1, 2}}, + P{{-2, -1}, {1}, {2}}, P{{-2, 1, 2}, {-1}}, P{{-2, 1}, {-1, 2}}, P{{-2, 1}, {-1}, {2}}, - P{{-2, 2}, {-1, 1}}, P{{-2, -1, 2}, {1}}, - P{{-2, 2}, {-1}, {1}}, P{{-2}, {-1, 1}, {2}}, - P{{-2}, {-1, 2}, {1}}, P{{-2, -1}, {1}, {2}}, + P{{-2, 2}, {-1, 1}}, P{{-2}, {-1, 1, 2}}, + P{{-2}, {-1, 1}, {2}}, P{{-2, 2}, {-1}, {1}}, + P{{-2}, {-1, 2}, {1}}, P{{-2}, {-1}, {1, 2}}, P{{-2}, {-1}, {1}, {2}}] sage: E = A2([[1,2],[-2,-1]]); E P{{-2, -1}, {1, 2}} @@ -2152,16 +2151,16 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): Next, we construct an element:: sage: a2 = A2.an_element(); a2 - 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + 2*P{{-2, -1, 1}, {2}} + 2*P{{-2, -1, 1, 2}} + 3*P{{-2, -1, 2}, {1}} There is a natural embedding into partition algebras on more elements, by adding identity strands:: sage: A4 = PartitionAlgebra(4, x, R) sage: A4(a2) - 3*P{{-4, 4}, {-3, 3}, {-2}, {-1, 1, 2}} + 2*P{{-4, 4}, {-3, 3}, {-2, -1, 1}, {2}} + 2*P{{-4, 4}, {-3, 3}, {-2, -1, 1, 2}} - + 2*P{{-4, 4}, {-3, 3}, {-2, 1, 2}, {-1}} + + 3*P{{-4, 4}, {-3, 3}, {-2, -1, 2}, {1}} Thus, the empty partition corresponds to the identity:: @@ -2218,9 +2217,9 @@ class PartitionAlgebra(DiagramBasis, UnitDiagramMixin): We can convert back from the orbit basis to the diagram basis:: sage: o2 = O2.an_element(); o2 - 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + 2*O{{-2, -1, 1}, {2}} + 2*O{{-2, -1, 1, 2}} + 3*O{{-2, -1, 2}, {1}} sage: A2(o2) - 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + 2*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 3*P{{-2, -1, 2}, {1}} One can work with partition algebras using a symbol for the parameter, leaving the base ring unspecified. This implies that the underlying @@ -2482,9 +2481,12 @@ def _coerce_map_from_(self, R): TESTS:: sage: elt = O3.an_element(); elt - 3*O{{-3}, {-2, -1, 1, 2, 3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + 2*O{{-3, -1, 1, 2, 3}, {-2}} + 2*O{{-3, -2, -1, 1, 2}, {3}} + 2*O{{-3, -2, -1, 1, 2, 3}} + + 3*O{{-3, -2, -1, 1, 3}, {2}} sage: A._coerce_map_from_(O3)(elt) - 3*P{{-4, 4}, {-3}, {-2, -1, 1, 2, 3}} - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + 2*P{{-4, 4}, {-3, -1, 1, 2, 3}, {-2}} + 2*P{{-4, 4}, {-3, -2, -1, 1, 2}, {3}} + - 3*P{{-4, 4}, {-3, -2, -1, 1, 2, 3}} + + 3*P{{-4, 4}, {-3, -2, -1, 1, 3}, {2}} """ # coerce from Orbit basis. if isinstance(R, OrbitBasis): @@ -2562,7 +2564,7 @@ def to_orbit_basis(self): sage: P = PartitionAlgebra(2, x, R) sage: pp = P.an_element(); sage: pp.to_orbit_basis() - 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + 2*O{{-2, -1, 1}, {2}} + 7*O{{-2, -1, 1, 2}} + 3*O{{-2, -1, 2}, {1}} sage: pp = (3*P([[-2], [-1, 1, 2]]) + 2*P([[-2, -1, 1, 2]]) ....: + 2*P([[-2, 1, 2], [-1]])); pp 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} @@ -2798,9 +2800,9 @@ def diagram_basis(self): Partition Algebra of rank 2 with parameter x over Univariate Polynomial Ring in x over Rational Field sage: o2 = O2.an_element(); o2 - 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + 2*O{{-2, -1, 1}, {2}} + 2*O{{-2, -1, 1, 2}} + 3*O{{-2, -1, 2}, {1}} sage: P2(o2) - 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + 2*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 3*P{{-2, -1, 2}, {1}} TESTS:: @@ -2978,13 +2980,13 @@ def to_diagram_basis(self): sage: P = PartitionAlgebra(2, x, R) sage: O = P.orbit_basis() sage: elt = O.an_element(); elt - 3*O{{-2}, {-1, 1, 2}} + 2*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + 2*O{{-2, -1, 1}, {2}} + 2*O{{-2, -1, 1, 2}} + 3*O{{-2, -1, 2}, {1}} sage: elt.to_diagram_basis() - 3*P{{-2}, {-1, 1, 2}} - 3*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + 2*P{{-2, -1, 1}, {2}} - 3*P{{-2, -1, 1, 2}} + 3*P{{-2, -1, 2}, {1}} sage: pp = P.an_element(); pp - 3*P{{-2}, {-1, 1, 2}} + 2*P{{-2, -1, 1, 2}} + 2*P{{-2, 1, 2}, {-1}} + 2*P{{-2, -1, 1}, {2}} + 2*P{{-2, -1, 1, 2}} + 3*P{{-2, -1, 2}, {1}} sage: op = pp.to_orbit_basis(); op - 3*O{{-2}, {-1, 1, 2}} + 7*O{{-2, -1, 1, 2}} + 2*O{{-2, 1, 2}, {-1}} + 2*O{{-2, -1, 1}, {2}} + 7*O{{-2, -1, 1, 2}} + 3*O{{-2, -1, 2}, {1}} sage: pp == op.to_diagram_basis() True """ @@ -3447,18 +3449,18 @@ class PlanarAlgebra(SubPartitionAlgebra, UnitDiagramMixin): {{-2, 2}, {-1, 1}} sage: Pl.basis().list() [Pl{{-2, -1, 1, 2}}, - Pl{{-2, 1, 2}, {-1}}, - Pl{{-2}, {-1, 1, 2}}, - Pl{{-2, -1}, {1, 2}}, - Pl{{-2}, {-1}, {1, 2}}, Pl{{-2, -1, 1}, {2}}, + Pl{{-2, -1, 2}, {1}}, + Pl{{-2, -1}, {1, 2}}, + Pl{{-2, -1}, {1}, {2}}, + Pl{{-2, 1, 2}, {-1}}, Pl{{-2, 1}, {-1}, {2}}, Pl{{-2, 2}, {-1, 1}}, - Pl{{-2, -1, 2}, {1}}, - Pl{{-2, 2}, {-1}, {1}}, + Pl{{-2}, {-1, 1, 2}}, Pl{{-2}, {-1, 1}, {2}}, + Pl{{-2, 2}, {-1}, {1}}, Pl{{-2}, {-1, 2}, {1}}, - Pl{{-2, -1}, {1}, {2}}, + Pl{{-2}, {-1}, {1, 2}}, Pl{{-2}, {-1}, {1}, {2}}] sage: E = Pl([[1,2],[-1,-2]]) sage: E^2 == x*E @@ -3536,17 +3538,17 @@ class PropagatingIdeal(SubPartitionAlgebra): Ideal diagrams of order 2 sage: I.basis().list() [I{{-2, -1, 1, 2}}, - I{{-2, 1, 2}, {-1}}, - I{{-2}, {-1, 1, 2}}, - I{{-2, -1}, {1, 2}}, - I{{-2}, {-1}, {1, 2}}, I{{-2, -1, 1}, {2}}, - I{{-2, 1}, {-1}, {2}}, I{{-2, -1, 2}, {1}}, - I{{-2, 2}, {-1}, {1}}, + I{{-2, -1}, {1, 2}}, + I{{-2, -1}, {1}, {2}}, + I{{-2, 1, 2}, {-1}}, + I{{-2, 1}, {-1}, {2}}, + I{{-2}, {-1, 1, 2}}, I{{-2}, {-1, 1}, {2}}, + I{{-2, 2}, {-1}, {1}}, I{{-2}, {-1, 2}, {1}}, - I{{-2, -1}, {1}, {2}}, + I{{-2}, {-1}, {1, 2}}, I{{-2}, {-1}, {1}, {2}}] sage: E = I([[1,2],[-1,-2]]) sage: E^2 == x*E diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 19d095f0720..5496588a292 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -86,7 +86,7 @@ def check(self): {{-3, -2, -1, 1, 2, 3}} sage: x.check() sage: y = A2p5.next(x); y - {{-3, -1, 1, 2, 3}, {-2}} + {{-3, 3}, {-2, -1, 1, 2}} sage: y.check() """ #Check to make sure each element of x is a set diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index b32d1a8c999..786aa6c39f9 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -838,8 +838,8 @@ 5 sage: C.list() [{{'a', 'b', 'c'}}, - {{'a', 'c'}, {'b'}}, {{'a', 'b'}, {'c'}}, + {{'a', 'c'}, {'b'}}, {{'a'}, {'b', 'c'}}, {{'a'}, {'b'}, {'c'}}] From 588b4d37dc33a628839bc1aff55e73567a1e93f0 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Aug 2018 14:50:06 +1000 Subject: [PATCH 249/284] Did some formatting fixes and other small reviewer tweaks. --- src/sage/groups/matrix_gps/matrix_group.py | 1 - src/sage/groups/matrix_gps/named_group.py | 90 +++++++------- src/sage/groups/matrix_gps/orthogonal.py | 137 +++++++++++---------- src/sage/groups/matrix_gps/symplectic.py | 59 ++++----- src/sage/groups/matrix_gps/unitary.py | 123 +++++++++--------- 5 files changed, 220 insertions(+), 190 deletions(-) diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index b50c8e7519f..42b49bb2b8b 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -54,7 +54,6 @@ from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.matrix.matrix_space import MatrixSpace from sage.misc.latex import latex -from sage.structure.sequence import Sequence from sage.structure.richcmp import (richcmp_not_equal, rich_to_bool, richcmp_method, richcmp) from sage.misc.cachefunc import cached_method diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index 47fa5f7e8cd..38f7de9b02c 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -107,7 +107,6 @@ def normalize_args_vectorspace(*args, **kwds): ring = V.base_ring() if len(args) == 2: degree, ring = args - from sage.rings.integer import is_Integer try: ring = ZZ(ring) from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -119,31 +118,35 @@ def normalize_args_vectorspace(*args, **kwds): def normalize_args_invariant_form(R, d, invariant_form): - """ - function to normalize the input of a user defined invariant - bilinear form for orthogonal, unitary and symplectic groups. + r""" + Normalize the input of a user defined invariant bilinear form + for orthogonal, unitary and symplectic groups. + Further informations and examples can be found in the defining - functions (GU, SU, Sp,...) for unitary and symplectic groups + functions (:func:`GU`, :func:`SU`, :func:`Sp`, etc.) for unitary, + symplectic groups, etc. INPUT: - - ``R`` -- instance of the integral domain which should become - the base_ring of the classical group + - ``R`` -- instance of the integral domain which should become + the ``base_ring`` of the classical group - - ``d`` -- integer giving the dimension of the module the classical - group is operating on + - ``d`` -- integer giving the dimension of the module the classical + group is operating on - - ``invariant_form`` -- instances being accepted by the - matrix-constructor which define a d x d square matrix - over R describing the bilinear form to be kept invariant - by the classical group + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor that define a `d \times d` square matrix + over R describing the bilinear form to be kept invariant + by the classical group - OUTPUT: an instance of sage.matrix.matrix2.Matrix if the - normalization was possible. Else, an error is raised except the - the imput has been None. + OUTPUT: + + ``None`` if ``invariant_form`` was not specified (or ``None``). + A matrix if the normalization was possible; otherwise an error + is raised. + + TESTS:: - TESTS: - sage: from sage.groups.matrix_gps.named_group import normalize_args_invariant_form sage: CF3 = CyclotomicField(3) sage: m = normalize_args_invariant_form(CF3, 3, (1,2,3,0,2,0,0,2,1)); m @@ -156,28 +159,25 @@ def normalize_args_invariant_form(R, d, invariant_form): sage: normalize_args_invariant_form(ZZ, 3, (1,2,3,0,2,0,0,2)) Traceback (most recent call last): ... - ValueError: invariant_form must be a 3 x 3 matrix over Integer Ring + ValueError: sequence too short (expected length 9, got 8) sage: normalize_args_invariant_form(QQ, 3, (1,2,3,0,2,0,0,2,0)) Traceback (most recent call last): ... - ValueError: invariant_form must be non degenerated + ValueError: invariant_form must be non-degenerate AUTHORS: - Sebastian Oehms (2018-8) (see :trac:`26028`) """ - if invariant_form == None: + if invariant_form is None: return invariant_form - + from sage.matrix.constructor import matrix - try: - m = matrix(R, d, d, invariant_form) - except: - raise ValueError("invariant_form must be a %s x %s matrix over %s" %(d, d, R)) + m = matrix(R, d, d, invariant_form) if m.is_singular(): - raise ValueError("invariant_form must be non degenerated") + raise ValueError("invariant_form must be non-degenerate") return m @@ -190,37 +190,44 @@ def __init__(self, degree, base_ring, special, sage_name, latex_string, INPUT: - - ``degree`` -- integer. The degree (number of rows/columns of - matrices). + - ``degree`` -- integer; the degree (number of rows/columns of + matrices) - - ``base_ring`` -- ring. The base ring of the matrices. + - ``base_ring`` -- ring; the base ring of the matrices - - ``special`` -- boolean. Whether the matrix group is special, - that is, elements have determinant one. + - ``special`` -- boolean; whether the matrix group is special, + that is, elements have determinant one - - ``sage_name`` -- string. the name displayed in a sage session. + - ``sage_name`` -- string; the name of the group - - ``latex_string`` -- string. The latex representation. + - ``latex_string`` -- string; the latex representation - - ``category`` -- (optional) instance of sage.categories.groups.Groups - passed to the constructor of MatrixGroup_generic. + - ``category`` -- (optional) a subcategory of + :class:`sage.categories.groups.Groups` passed to + the constructor of + :class:`sage.groups.matrix_gps.matrix_group.MatrixGroup_generic` - - ``invariant_form`` -- (optional) square-matrix of the given - degree over the given base_ring describing a bilinear form - to be kept invariant by the group. + - ``invariant_form`` -- (optional) square-matrix of the given + degree over the given base_ring describing a bilinear form + to be kept invariant by the group - EXAMPLES (see the examples for GU, SU, Sp,.., as well):: + EXAMPLES:: sage: G = GL(2, QQ) sage: from sage.groups.matrix_gps.named_group import NamedMatrixGroup_generic sage: isinstance(G, NamedMatrixGroup_generic) True + + .. SEEALSO:: + + See the examples for :func:`GU`, :func:`SU`, :func:`Sp`, etc. + as well. """ MatrixGroup_generic.__init__(self, degree, base_ring, category=category) self._special = special self._name_string = sage_name self._latex_string = latex_string - self._user_invariant_form_ = invariant_form + self._invariant_form = invariant_form def _an_element_(self): """ @@ -293,7 +300,6 @@ def __richcmp__(self, other, op): return MatrixGroup_generic.__richcmp__(self, other, op) - class NamedMatrixGroup_gap(NamedMatrixGroup_generic, MatrixGroup_gap): def __init__(self, degree, base_ring, special, sage_name, latex_string, diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index 66b1482c385..37e2fef0bfc 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -29,7 +29,7 @@ d, q)``, ``SO([e,] d, q)``. * Sage notation: The optional ``e`` comes last, the standard Python - convention: ``GO(d, GF(q), e=0)``, ``SO( d, GF(q), e=0)``. + convention: ``GO(d, GF(q), e=0)``, ``SO(d, GF(q), e=0)``. EXAMPLES:: @@ -67,16 +67,20 @@ - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. -- Sebastian Oehms (2018-8) add :meth:`invariant_form` (as alias), :func:`_OG`, - option for user defined invariant bilinear form and bug-fix in cmd-string - for calling GAP (see :trac:`26028`) +- Sebastian Oehms (2018-8) add + :meth:`~sage.groups.matrix_gps.orthogonal.OrthogonalMatrixGroup_generic.invariant_form` + (as alias), ``_OG``, option for user defined invariant bilinear form, + and bug-fix in cmd-string for calling GAP (see :trac:`26028`) """ #***************************************************************************** # Copyright (C) 2006 David Joyner and William Stein # Copyright (C) 2013 Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) +# 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/ #***************************************************************************** @@ -141,9 +145,11 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): This function is commonly used by the functions GO and SO to avoid uneccessarily duplicated code. For documentation and examples see the individual functions. - TEST: + TESTS: + + Check that :trac:`26028` is fixed:: - sage: GO(3,25).order() # check that :trac:`????` is fixed + sage: GO(3,25).order() # indirect doctest 31200 """ prefix = 'General' @@ -156,9 +162,9 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): e = normalize_args_e(degree, ring, e) if e == 0: - if invariant_form != None: + if invariant_form is not None: if is_FiniteField(ring): - raise NotImplementedError("invariant_form for finite Groups is fixed") + raise NotImplementedError("invariant_form for finite groups is fixed by GAP") invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) if not invariant_form.is_symmetric(): @@ -171,14 +177,20 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): except: pass - name = '{0} Orthogonal Group of degree {1} over {2} {3}\n{4}'.format(prefix, degree, ring, inserted_text,invariant_form) - ltx = r'\text{{{0}O}}_{{{1}}}({2})\text{{ {3} }}{4}'.format(ltx_prefix, degree, latex(ring), inserted_text, latex(invariant_form)) + name = '{0} Orthogonal Group of degree {1} over {2} {3}\n{4}'.format( + prefix, degree, ring, inserted_text,invariant_form) + ltx = r'\text{{{0}O}}_{{{1}}}({2})\text{{ {3} }}{4}'.format( + ltx_prefix, degree, latex(ring), inserted_text, + latex(invariant_form)) else: name = '{0} Orthogonal Group of degree {1} over {2}'.format(prefix, degree, ring) ltx = r'\text{{{0}O}}_{{{1}}}({2})'.format(ltx_prefix, degree, latex(ring)) else: name = '{0} Orthogonal Group of degree {1} and form parameter {2} over {3}'.format(prefix, degree, e, ring) - ltx = r'\text{{{0}O}}_{{{1}}}({2}, {3})'.format(ltx_prefix, degree, latex(ring), '+' if e == 1 else '-') + ltx = r'\text{{{0}O}}_{{{1}}}({2}, {3})'.format(ltx_prefix, degree, + latex(ring), + '+' if e == 1 else '-') + if is_FiniteField(ring): cmd = '{0}O({1}, {2}, {3})'.format(ltx_prefix, e, degree, ring.order()) return OrthogonalMatrixGroup_gap(degree, ring, False, name, ltx, cmd) @@ -192,7 +204,7 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): ######################################################################## def GO(n, R, e=0, var='a', invariant_form=None): - """ + r""" Return the general orthogonal group. The general orthogonal group `GO(n,R)` consists of all `n \times n` @@ -205,29 +217,29 @@ def GO(n, R, e=0, var='a', invariant_form=None): there are two inequivalent quadratic forms and a third parameter ``e`` must be specified to disambiguate these two possibilities. - .. note:: + .. NOTE:: This group is also available via ``groups.matrix.GO()``. - INPUT: + INPUT: - - ``n`` -- integer. The degree. + - ``n`` -- integer; the degree - - ``R`` -- ring or an integer. If an integer is specified, the - corresponding finite field is used. + - ``R`` -- ring or an integer; if an integer is specified, the + corresponding finite field is used - - ``e`` -- ``+1`` or ``-1``, and ignored by default. Only relevant - for finite fields and if the degree is even. A parameter that - distinguishes inequivalent invariant forms. + - ``e`` -- ``+1`` or ``-1``, and ignored by default; only relevant + for finite fields and if the degree is even: a parameter that + distinguishes inequivalent invariant forms - - ``var`` -- (optional, default='a') variable used to represent - generator of the finite field, if needed. + - ``var`` -- (optional, default: ``'a'``) variable used to + represent generator of the finite field, if needed - - ``invariant_form`` -- (optional) instances being accepted by - the matrix-constructor which define a n x n square matrix - over R describing the symmetric form to be kept invariant - by the orthogonal group. The form is checked to be non - degenerated and symmetric but not to be positive definite. + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a `n \times n` square matrix + over ``R`` describing the symmetric form to be kept invariant + by the orthogonal group; the form is checked to be + non-degenerate and symmetric but not to be positive definite OUTPUT: @@ -247,7 +259,7 @@ def GO(n, R, e=0, var='a', invariant_form=None): [0 0 1], [0 2 1] ) - using the invariant_form option:: + Using the ``invariant_form`` option:: sage: m = matrix(QQ, 3,3, [[0, 1, 0], [1, 0, 0], [0, 0, 3]]) sage: GO3 = GO(3,QQ) @@ -280,8 +292,8 @@ def GO(n, R, e=0, var='a', invariant_form=None): Traceback (most recent call last): ... NotImplementedError: invariant_form for finite Groups is fixed - - TESTS: + + TESTS:: sage: TestSuite(GO3).run() sage: groups.matrix.GO(2, 3, e=-1) @@ -296,7 +308,7 @@ def GO(n, R, e=0, var='a', invariant_form=None): ######################################################################## def SO(n, R, e=None, var='a', invariant_form=None): - """ + r""" Return the special orthogonal group. The special orthogonal group `GO(n,R)` consists of all `n \times n` @@ -305,28 +317,29 @@ def SO(n, R, e=None, var='a', invariant_form=None): multiple non-isomorphic quadratic forms, additional data needs to be specified to disambiguate. - .. note:: + .. NOTE:: This group is also available via ``groups.matrix.SO()``. INPUT: - - ``n`` -- integer. The degree. + - ``n`` -- integer; the degree - - ``R`` -- ring or an integer. If an integer is specified, the - corresponding finite field is used. + - ``R`` -- ring or an integer; if an integer is specified, the + corresponding finite field is used - - ``e`` -- ``+1`` or ``-1``, and ignored by default. Only relevant - for finite fields and if the degree is even. A parameter that - distinguishes inequivalent invariant forms. - - ``var`` -- (optional, default='a') variable used to represent - generator of the finite field, if needed. + - ``e`` -- ``+1`` or ``-1``, and ignored by default; only relevant + for finite fields and if the degree is even: a parameter that + distinguishes inequivalent invariant forms + + - ``var`` -- (optional, default: ``'a'``) variable used to + represent generator of the finite field, if needed - - ``invariant_form`` -- (optional) instances being accepted by - the matrix-constructor which define a n x n square matrix - over R describing the symmetric form to be kept invariant - by the orthogonal group. The form is checked to be non - degenerated and symmetric but not to be positive definite. + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a `n \times n` square matrix + over ``R`` describing the symmetric form to be kept invariant + by the orthogonal group; the form is checked to be + non-degenerate and symmetric but not to be positive definite OUTPUT: @@ -354,7 +367,7 @@ def SO(n, R, e=None, var='a', invariant_form=None): [0 0 1], [0 3 1], [2 0 4] ) - using the invariant_form option:: + Using the ``invariant_form`` option:: sage: CF3 = CyclotomicField(3); e3 = CF3.gen() sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3,2,0],[0,0,1]]) @@ -389,7 +402,7 @@ def SO(n, R, e=None, var='a', invariant_form=None): ... NotImplementedError: invariant_form for finite Groups is fixed - TESTS: + TESTS:: sage: TestSuite(SO3m).run() sage: groups.matrix.SO(2, 3, e=1) @@ -433,8 +446,7 @@ class OrthogonalMatrixGroup_generic(NamedMatrixGroup_generic): @cached_method def invariant_bilinear_form(self): """ - Return the symmetric bilinear form preserved by the orthogonal - group. + Return the symmetric bilinear form preserved by ``self``. OUTPUT: @@ -460,15 +472,15 @@ def invariant_bilinear_form(self): [0 2 0] [0 0 3] - TESTS: - - sage: GO3m.invariant_form() # test alias + TESTS:: + + sage: GO3m.invariant_form() [1 0 0] [0 2 0] [0 0 3] """ - if self._user_invariant_form_ != None: - return self._user_invariant_form_ + if self._invariant_form is not None: + return self._invariant_form from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) @@ -537,9 +549,9 @@ def invariant_bilinear_form(self): [0 0 2 0] [0 0 0 2] - TESTS: - - sage: G.invariant_form() # test alias + TESTS:: + + sage: G.invariant_form() [0 1 0 0] [1 0 0 0] [0 0 2 0] @@ -553,7 +565,7 @@ def invariant_bilinear_form(self): @cached_method def invariant_quadratic_form(self): - """ + r""" Return the quadratic form preserved by the orthogonal group. OUTPUT: @@ -594,9 +606,9 @@ def invariant_quadratic_form(self): [0 0 1 0] [0 0 0 1] - TESTS: - - sage: GO(4, GF(7), -1).invariant_form() # test alias + TESTS:: + + sage: GO(4, GF(7), -1).invariant_form() [0 1 0 0] [1 0 0 0] [0 0 2 0] @@ -605,3 +617,4 @@ def invariant_quadratic_form(self): m = self.gap().InvariantQuadraticForm()['matrix'].matrix() m.set_immutable() return m + diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index 441ec4cdad5..f2f20e6930a 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -24,7 +24,8 @@ - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. - Sebastian Oehms (2018-8) add option for user defined invariant bilinear - form and bug-fix in :meth:`invariant_form` of :class:`SymplecticMatrixGroup_generic` + form and bug-fix in + :meth:`~sage.groups.matrix_gps.symplectic.SymplecticMatrixGroup_generic.invariant_form` (see :trac:`26028`) """ @@ -32,7 +33,10 @@ # Copyright (C) 2006 David Joyner and William Stein # Copyright (C) 2013 Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) +# 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/ #***************************************************************************** @@ -54,26 +58,25 @@ def Sp(n, R, var='a', invariant_form=None): Return the symplectic group. The special linear group `GL( d, R )` consists of all `d \times d` - matrices that are invertible over the ring `R` with determinant - one. + matrices that are invertible over the ring `R` with determinant one. - .. note:: + .. NOTE:: This group is also available via ``groups.matrix.Sp()``. INPUT: - - ``n`` -- a positive integer. + - ``n`` -- a positive integer - - ``R`` -- ring or an integer. If an integer is specified, the - corresponding finite field is used. + - ``R`` -- ring or an integer; if an integer is specified, the + corresponding finite field is used - - ``var`` -- (optional, default='a') variable used to represent - generator of the finite field, if needed. + - ``var`` -- (optional, default: ``'a'``) variable used to + represent generator of the finite field, if needed - ``invariant_form`` -- (optional) instances being accepted by - the matrix-constructor which define a n x n square matrix - over R describing the alternating form to be kept invariant + the matrix-constructor which define a `n \times n` square matrix + over ``R`` describing the alternating form to be kept invariant by the symplectic group EXAMPLES:: @@ -89,7 +92,7 @@ def Sp(n, R, var='a', invariant_form=None): ... ValueError: the degree must be even - using the invariant_form option:: + Using the ``invariant_form`` option:: sage: m = matrix(QQ, 4,4, [[0, 0, 1, 0], [0, 0, 0, 2], [-1, 0, 0, 0], [0, -2, 0, 0]]) sage: Sp4m = Sp(4, QQ, invariant_form=m) @@ -128,6 +131,7 @@ def Sp(n, R, var='a', invariant_form=None): NotImplementedError: invariant_form for finite Groups is fixed TESTS:: + sage: TestSuite(Sp4).run() sage: TestSuite(Sp4m).run() sage: groups.matrix.Sp(2, 3) @@ -140,23 +144,24 @@ def Sp(n, R, var='a', invariant_form=None): if degree % 2 != 0: raise ValueError('the degree must be even') - if invariant_form != None: + if invariant_form is not None: if is_FiniteField(ring): - raise NotImplementedError("invariant_form for finite Groups is fixed") + raise NotImplementedError("invariant_form for finite groups is fixed by GAP") invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) if not invariant_form.is_alternating(): raise ValueError("invariant_form must be alternating") - name = 'Symplectic Group of degree {0} over {1} with respect to alternating bilinear form\n{2}'.format(degree, ring, invariant_form) - ltx = r'\text{{Sp}}_{{{0}}}({1})\text{{ with respect to alternating bilinear form}}{2}'.format(degree, latex(ring), latex(invariant_form)) + name = 'Symplectic Group of degree {0} over {1} with respect to alternating bilinear form\n{2}'.format( + degree, ring, invariant_form) + ltx = r'\text{{Sp}}_{{{0}}}({1})\text{{ with respect to alternating bilinear form}}{2}'.format( + degree, latex(ring), latex(invariant_form)) else: name = 'Symplectic Group of degree {0} over {1}'.format(degree, ring) ltx = r'\text{{Sp}}_{{{0}}}({1})'.format(degree, latex(ring)) - from sage.libs.gap.libgap import libgap try: - cmd = 'Sp({0}, {1})'.format(degree, ring._gap_init_()) + cmd = 'Sp({0}, {1})'.format(degree, ring._gap_init_()) return SymplecticMatrixGroup_gap(degree, ring, True, name, ltx, cmd) except ValueError: return SymplecticMatrixGroup_generic(degree, ring, True, name, ltx, invariant_form=invariant_form) @@ -192,7 +197,7 @@ class SymplecticMatrixGroup_generic(NamedMatrixGroup_generic): @cached_method def invariant_form(self): """ - Return the quadratic form preserved by the orthogonal group. + Return the quadratic form preserved by the symplectic group. OUTPUT: @@ -206,8 +211,8 @@ def invariant_form(self): [ 0 -1 0 0] [-1 0 0 0] """ - if self._user_invariant_form_ != None: - return self._user_invariant_form_ + if self._invariant_form is not None: + return self._invariant_form R = self.base_ring() d = self.degree() @@ -232,12 +237,12 @@ def _check_matrix(self, x, *args): """ F = self.invariant_form() if x * F * x.transpose() != F: - raise TypeError('matrix must be symplectic with respect to the alternating form\n%s' %(F)) + raise TypeError('matrix must be symplectic with respect to the alternating form\n{}'.format(F)) class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_gap): r""" - Symplectic group in GAP + Symplectic group in GAP. EXAMPLES:: @@ -251,7 +256,7 @@ class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_ @cached_method def invariant_form(self): """ - Return the quadratic form preserved by the orthogonal group. + Return the quadratic form preserved by the symplectic group. OUTPUT: @@ -269,7 +274,3 @@ def invariant_form(self): m.set_immutable() return m - - - - diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index c9f6c399513..2776927c8a9 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -32,20 +32,24 @@ - Volker Braun (2013-1) port to new Parent, libGAP, extreme refactoring. -- Sebastian Oehms (2018-8) add :meth:`invariant_form`, :func:`_UG`, - option for user defined invariant bilinear form and bug-fix in - :meth:`_check_matrix` (see :trac:`26028`) +- Sebastian Oehms (2018-8) add ``_UG``, + :meth:`~sage.groups.matrix_gps.unitary.UnitaryMatrixGroup_generic.invariant_form`, + option for user defined invariant bilinear form, and bug-fix in + ``_check_matrix`` (see :trac:`26028`) """ #********************************************************************************* # Copyright (C) 2006 David Joyner and William Stein # Copyright (C) 2013 Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) +# 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.all import ZZ, GF +from sage.rings.all import GF from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.misc.latex import latex from sage.misc.cachefunc import cached_method @@ -86,8 +90,14 @@ def finite_field_sqrt(ring): def _UG(n, R, special, var='a', invariant_form=None): r""" - This function is commonly used by the functions GU and SU to avoid uneccessarily - duplicated code. For documnentation and examples see the individual functions. + This function is commonly used by the functions :func:`GU` and :func:`SU` + to avoid uneccessarily duplicated code. For documnentation and examples + see the individual functions. + + TESTS:: + + sage: GU(3,25).order() # indirect doctest + 3961191000000 """ prefix = 'General' latex_prefix ='G' @@ -98,38 +108,38 @@ def _UG(n, R, special, var='a', invariant_form=None): degree, ring = normalize_args_vectorspace(n, R, var=var) if is_FiniteField(ring): q = ring.cardinality() - ring = GF(q ** 2, name=var) - if invariant_form != None: - raise NotImplementedError("invariant_form for finite Groups is fixed") + ring = GF(q**2, name=var) + if invariant_form is not None: + raise NotImplementedError("invariant_form for finite groups is fixed by GAP") - if invariant_form != None: + if invariant_form is not None: invariant_form = normalize_args_invariant_form(ring, degree, invariant_form) if not invariant_form.is_hermitian(): raise ValueError("invariant_form must be hermitian") - inserted_text = 'with respect to hermitian form' + inserted_text = 'with respect to hermitian form' try: if not invariant_form.is_positive_definite(): - inserted_text = 'with respect to non positive definite hermitian form' + inserted_text = 'with respect to non positive definite hermitian form' except: pass - name = '{0} Unitary Group of degree {1} over {2} {3}\n{4}'.format(prefix, degree, ring, inserted_text,invariant_form) - ltx = r'\text{{{0}U}}_{{{1}}}({2})\text{{ {3} }}{4}'.format(latex_prefix, degree, latex(ring), inserted_text, latex(invariant_form)) + name = '{0} Unitary Group of degree {1} over {2} {3}\n{4}'.format(prefix, + degree, ring, inserted_text,invariant_form) + ltx = r'\text{{{0}U}}_{{{1}}}({2})\text{{ {3} }}{4}'.format(latex_prefix, + degree, latex(ring), inserted_text, latex(invariant_form)) else: name = '{0} Unitary Group of degree {1} over {2}'.format(prefix, degree, ring) ltx = r'\text{{{0}U}}_{{{1}}}({2})'.format(latex_prefix, degree, latex(ring)) if is_FiniteField(ring): - cmd = '{0}U({1}, {2})'.format(latex_prefix, degree, q) + cmd = '{0}U({1}, {2})'.format(latex_prefix, degree, q) return UnitaryMatrixGroup_gap(degree, ring, special, name, ltx, cmd) else: return UnitaryMatrixGroup_generic(degree, ring, special, name, ltx, invariant_form=invariant_form) - - ############################################################################### # General Unitary Group ############################################################################### @@ -138,36 +148,36 @@ def GU(n, R, var='a', invariant_form=None): r""" Return the general unitary group. - The general unitary group `GU( d, R )` consists of all `d \times - d` matrices that preserve a nondegenerate sesquilinear form over - the ring `R`. + The general unitary group `GU( d, R )` consists of all `d \times d` + matrices that preserve a nondegenerate sesquilinear form over the + ring `R`. - .. note:: + .. NOTE:: For a finite field the matrices that preserve a sesquilinear form over `F_q` live over `F_{q^2}`. So ``GU(n,q)`` for integer ``q`` constructs the matrix group over the base ring ``GF(q^2)``. - .. note:: + .. NOTE:: This group is also available via ``groups.matrix.GU()``. INPUT: - - ``n`` -- a positive integer. + - ``n`` -- a positive integer - - ``R`` -- ring or an integer. If an integer is specified, the - corresponding finite field is used. + - ``R`` -- ring or an integer; if an integer is specified, the + corresponding finite field is used - - ``var`` -- (optional, default='a') variable used to represent - generator of the finite field, if needed. + - ``var`` -- (optional, default: ``'a'``) variable used to + represent generator of the finite field, if needed - - ``invariant_form`` -- (optional) instances being accepted by - the matrix-constructor which define a n x n square matrix - over R describing the hermitian form to be kept invariant - by the unitary group. The form is checked to be non - degenerated and hermitian but not to be positive definite. + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a `n \times n` square matrix + over R describing the hermitian form to be kept invariant + by the unitary group; the form is checked to be + non-degenerate and hermitian but not to be positive definite OUTPUT: @@ -196,7 +206,7 @@ def GU(n, R, var='a', invariant_form=None): [ 0 0 3*beta], [ 1 0 0] ) - using the invariant_form option:: + Using the ``invariant_form`` option:: sage: UCF = UniversalCyclotomicField(); e5=UCF.gen(5) sage: m=matrix(UCF, 3,3, [[1,e5,0],[e5.conjugate(),2,0],[0,0,1]]) @@ -234,9 +244,9 @@ def GU(n, R, var='a', invariant_form=None): sage: GU(2,QQ, invariant_form=[[1,0],[2,0]]) Traceback (most recent call last): ... - ValueError: invariant_form must be non degenerated + ValueError: invariant_form must be non-degenerate - TESTS: + TESTS:: sage: TestSuite(G).run() sage: groups.matrix.GU(2, 3) @@ -251,37 +261,37 @@ def GU(n, R, var='a', invariant_form=None): ############################################################################### def SU(n, R, var='a', invariant_form=None): - """ + r""" The special unitary group `SU( d, R )` consists of all `d \times d` matrices that preserve a nondegenerate sesquilinear form over the - ring `R` and have determinant one. + ring `R` and have determinant `1`. - .. note:: + .. NOTE:: For a finite field the matrices that preserve a sesquilinear form over `F_q` live over `F_{q^2}`. So ``SU(n,q)`` for integer ``q`` constructs the matrix group over the base ring ``GF(q^2)``. - .. note:: + .. NOTE:: This group is also available via ``groups.matrix.SU()``. INPUT: - - ``n`` -- a positive integer. + - ``n`` -- a positive integer - - ``R`` -- ring or an integer. If an integer is specified, the - corresponding finite field is used. + - ``R`` -- ring or an integer; if an integer is specified, the + corresponding finite field is used - - ``var`` -- (optional, default='a') variable used to represent - generator of the finite field, if needed. + - ``var`` -- (optional, default: ``'a'``) variable used to + represent generator of the finite field, if needed - - ``invariant_form`` -- (optional) instances being accepted by - the matrix-constructor which define a n x n square matrix - over R describing the hermitian form to be kept invariant - by the unitary group. The form is checked to be non - degenerated and hermitian but not to be positive definite. + - ``invariant_form`` -- (optional) instances being accepted by + the matrix-constructor which define a `n \times n` square matrix + over R describing the hermitian form to be kept invariant + by the unitary group; the form is checked to be + non-degenerate and hermitian but not to be positive definite OUTPUT: @@ -296,7 +306,7 @@ def SU(n, R, var='a', invariant_form=None): sage: SU(3,QQ) Special Unitary Group of degree 3 over Rational Field - using the invariant_form option:: + Using the ``invariant_form`` option:: sage: CF3 = CyclotomicField(3); e3 = CF3.gen() sage: m=matrix(CF3, 3,3, [[1,e3,0],[e3.conjugate(),2,0],[0,0,1]]) @@ -390,8 +400,8 @@ def invariant_form(self): [0 0 1 0] [0 0 0 1] """ - if self._user_invariant_form_ != None: - return self._user_invariant_form_ + if self._invariant_form is not None: + return self._invariant_form from sage.matrix.constructor import identity_matrix m = identity_matrix(self.base_ring(), self.degree()) @@ -400,7 +410,7 @@ def invariant_form(self): def _check_matrix(self, x, *args): - """a + """ Check whether the matrix ``x`` is unitary. See :meth:`~sage.groups.matrix_gps.matrix_group._check_matrix` @@ -421,7 +431,7 @@ def _check_matrix(self, x, *args): if H == self.one().matrix(): raise TypeError('matrix must be unitary') else: - raise TypeError('matrix must be unitary with respect to the hermitian form\n%s' %(H)) + raise TypeError('matrix must be unitary with respect to the hermitian form\n{}'.format(H)) class UnitaryMatrixGroup_gap(UnitaryMatrixGroup_generic, NamedMatrixGroup_gap): @@ -451,3 +461,4 @@ def invariant_form(self): m = matrix(R, d, d, self.gap().InvariantSesquilinearForm()['matrix'].matrix()) m.set_immutable() return m + From 2a736ba820a97619818be6eb0d60e667ea2de16f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 12 Aug 2018 10:33:07 +0200 Subject: [PATCH 250/284] implement Dedekind psi function --- src/sage/arith/misc.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 5e349013559..09a1a538d68 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -5063,6 +5063,7 @@ def squarefree_divisors(x): for a in powerset(prime_divisors(x)): yield prod(a, ZZ.one()) + def dedekind_sum(p, q, algorithm='default'): r""" Return the Dedekind sum `s(p,q)` defined for integers `p`, `q` as @@ -5273,3 +5274,33 @@ def gauss_sum(char_value, finite_field): gen_power *= gen zq_power *= zeta_q return resu + + +def dedekind_psi(N): + r""" + Return the value of the Dedekind psi function at ``N``. + + INPUT: + + - ``N`` -- a positive integer + + OUTPUT: + + an integer + + The Dedekind psi function is the multiplicative function defined by + + .. MATH:: + + \psi(n) = n \prod_{p|n, p prime} (1 + 1/p). + + See :wikipedia:`Dedekind_psi_function` and :oeis:`A001615`. + + EXAMPLES:: + + sage: from sage.arith.misc import dedekind_psi + sage: [dedekind_psi(d) for d in range(1, 12)] + [1, 3, 4, 6, 6, 12, 8, 12, 12, 18, 12] + """ + N = Integer(N) + return Integer(N * prod(1 + 1 / p for p in N.prime_divisors())) From 3649619b5420f9de957687076c28e0e7cac6ddb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 13 Aug 2018 10:18:18 +0200 Subject: [PATCH 251/284] cleanup of misc/misc.py (remove deprecated stuff + pep8) --- src/sage/misc/all.py | 8 +- src/sage/misc/misc.py | 330 ++++++++++-------------------------------- 2 files changed, 79 insertions(+), 259 deletions(-) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index 5cd23caaedd..acaa4248dda 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -2,17 +2,17 @@ from .lazy_attribute import lazy_attribute, lazy_class_attribute from .lazy_import import lazy_import -from .misc import (BackslashOperator, getitem, +from .misc import (BackslashOperator, cputime, verbose, set_verbose, set_verbose_files, get_verbose_files, unset_verbose_files, get_verbose, union, uniq, powerset, subsets, exists, forall, is_iterator, - random_sublist, walltime, generic_cmp, + random_sublist, walltime, repr_lincomb, pad_zeros, attrcall, SAGE_DB, SAGE_TMP, - newton_method_sizes, compose, - self_compose, nest) + newton_method_sizes, compose, + nest) from .banner import version, banner diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index fc9eaea6437..d4443db9d1a 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -28,7 +28,7 @@ False """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein # # This program is free software: you can redistribute it and/or modify @@ -36,7 +36,7 @@ # 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, absolute_import from six.moves import range from six import integer_types @@ -46,18 +46,21 @@ import sys import time import resource -import sage.misc.prandom as random +import pdb import warnings +import sage.misc.prandom as random from .lazy_string import lazy_string +import sage.server.support from sage.env import DOT_SAGE, HOSTNAME -LOCAL_IDENTIFIER = '%s.%s'%(HOSTNAME , os.getpid()) +LOCAL_IDENTIFIER = '%s.%s' % (HOSTNAME, os.getpid()) ################################################################# # File and directory utilities ################################################################# + def sage_makedirs(dir): """ Python version of ``mkdir -p``: try to create a directory, and also @@ -174,6 +177,7 @@ def SAGE_TMP_INTERFACE(): sage_makedirs(d) return d + SAGE_DB = os.path.join(DOT_SAGE, 'db') sage_makedirs(SAGE_DB) @@ -183,19 +187,11 @@ def SAGE_TMP_INTERFACE(): except KeyError: pass -################################################################# -# Functions to help with interfacing with CXX code that -# uses the GMP library -################################################################# -def to_gmp_hex(n): - from sage.misc.superseded import deprecation - deprecation(21926, "to_gmp_hex() is deprecated") - return hex(n).replace("L","").replace("0x","") - ################################################################# # timing ################################################################# + def cputime(t=0, subprocesses=False): """ Return the time in CPU seconds since Sage started, or with @@ -250,15 +246,15 @@ def cputime(t=0, subprocesses=False): started and terminated at any given time. """ if isinstance(t, GlobalCputime): - subprocesses=True + subprocesses = True if not subprocesses: try: t = float(t) except TypeError: t = 0.0 - u,s = resource.getrusage(resource.RUSAGE_SELF)[:2] - return u+s - t + u, s = resource.getrusage(resource.RUSAGE_SELF)[:2] + return u + s - t else: if t == 0: ret = GlobalCputime(cputime()) @@ -287,6 +283,7 @@ def cputime(t=0, subprocesses=False): pass return ret + class GlobalCputime: """ Container for CPU times of subprocesses. @@ -390,6 +387,7 @@ def __float__(self): """ return float(self.total) + def walltime(t=0): """ Return the wall time in second, or with optional argument t, return @@ -419,10 +417,11 @@ def walltime(t=0): ################################################################# # simple verbosity system ################################################################# -LEVEL=0 # default +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. @@ -455,13 +454,13 @@ def verbose(mesg="", t=0, level=1, caller_name=None): VERBOSE1 (william): This is Sage. (time = 0.0) sage: set_verbose(0) """ - if level>LEVEL: + 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: + if 'all' in verbose_files or level <= 0: show = True else: show = False @@ -473,7 +472,7 @@ def verbose(mesg="", t=0, level=1, caller_name=None): if not show: return cputime() - if t != 0 and mesg=="": + if t != 0 and mesg == "": mesg = "Finished." # see recipe 14.7 in Python Cookbook @@ -483,20 +482,16 @@ def verbose(mesg="", t=0, level=1, caller_name=None): 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) + 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) + 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 todo(mesg=""): - from sage.misc.superseded import deprecation - deprecation(21926, "todo() is deprecated") - caller_name = sys._getframe(1).f_code.co_name - raise NotImplementedError("{}: todo -- {}".format(caller_name, mesg)) def set_verbose(level, files='all'): """ @@ -531,6 +526,7 @@ def set_verbose(level, files='all'): files = [files] set_verbose_files(files) + def set_verbose_files(file_name): """ @@ -540,12 +536,14 @@ def set_verbose_files(file_name): global verbose_files verbose_files = file_name + def get_verbose_files(): """ """ return verbose_files + def unset_verbose_files(file_name): """ @@ -577,23 +575,6 @@ def get_verbose(): return LEVEL - -def generic_cmp(x,y): - """ - Compare x and y and return -1, 0, or 1. - - This is similar to x.__cmp__(y), but works even in some cases - when a .__cmp__ method is not defined. - """ - from sage.misc.superseded import deprecation - deprecation(21926, "generic_cmp() is deprecated") - if x < y: - return -1 - elif x == y: - return 0 - return 1 - - def cmp_props(left, right, props): from sage.misc.superseded import deprecation deprecation(23149, "cmp_props is deprecated") @@ -637,6 +618,7 @@ def union(x, y=None): return list(set(x)) return list(set(x).union(y)) + def uniq(x): """ Return the sublist of all elements in the list x that is sorted and @@ -665,15 +647,17 @@ def coeff_repr(c, is_latex=False): if is_latex and hasattr(c, '_latex_'): s = c._latex_() else: - s = str(c).replace(' ','') + s = str(c).replace(' ', '') if s.find("+") != -1 or s.find("-") != -1: if is_latex: - return "\\left(%s\\right)"%s + return "\\left(%s\\right)" % s else: - return "(%s)"%s + return "(%s)" % s return s -def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, repr_monomial = None, latex_scalar_mult = None): + +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. @@ -760,7 +744,9 @@ def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, repr_m if repr_monomial is None: if is_latex: - repr_monomial = lambda monomial: monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) + + def repr_monomial(monomial): + return monomial._latex_() if hasattr(monomial, '_latex_') else str(monomial) else: repr_monomial = str @@ -770,11 +756,11 @@ def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, repr_m if scalar_mult is None: scalar_mult = "" if is_latex else "*" - for (monomial,c) in terms: + for (monomial, c) in terms: if c != 0: coeff = coeff_repr(c) negative = False - if len(coeff)>0 and coeff[0] == "-": + if len(coeff) and coeff[0] == "-": negative = True try: if c < 0: @@ -791,32 +777,33 @@ def repr_lincomb(terms, is_latex=False, scalar_mult="*", strip_one=False, repr_m if coeff != "0": if negative: if first: - sign = "-" # add trailing space? + sign = "-" # add trailing space? else: sign = " - " else: if first: sign = "" else: - sign= " + " + sign = " + " b = repr_monomial(monomial) - if len(b) > 0: - if coeff != "": - if b =="1" and strip_one: + if len(b): + if coeff != "": + if b == "1" and strip_one: b = "" else: b = scalar_mult + b - s += "%s%s%s"%(sign, coeff, 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? + 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): +def strunc(s, n=60): """ Truncate at first space after position n, adding '...' if nontrivial truncation. @@ -828,11 +815,9 @@ def strunc(s, n = 60): while i < len(s) and s[i] != ' ': i += 1 return s[:i] + " ..." - #return s[:n-4] + " ..." return s - def newton_method_sizes(N): r""" Returns a sequence of integers @@ -884,26 +869,8 @@ def newton_method_sizes(N): ################################################################# -def assert_attribute(x, attr, init=None): - """ - If the object x has the attribute attr, do nothing. If not, set - x.attr to init. - """ - from sage.misc.superseded import deprecation - deprecation(21926, "assert_attribute() is deprecated") - if attr in x.__dict__: return - if attr[:2] == "__": - z = str(x.__class__).split("'") - if len(z) > 1: - z = z[1] - else: - z = z[0] - attr = "_" + z[len(x.__module__)+1:] + attr - x.__dict__[attr] = init - - def compose(f, g): - """ + r""" Return the composition of one-variable functions: `f \circ g` See also :func:`nest()` @@ -938,56 +905,6 @@ def compose(f, g): return lambda x: f(g(x)) -def self_compose(f, n): - """ - Return the function `f` composed with itself `n` times. - - See :func:`nest()` if you want `f(f(...(f(x))...))` for - known `x`. - - - INPUT: - - `f` -- a function of one variable - - `n` -- a nonnegative integer - - OUTPUT: - A function, the result of composing `f` with itself `n` times - - EXAMPLES:: - - sage: def f(x): return x^2 + 1 - sage: g = self_compose(f, 3) - doctest:... DeprecationWarning: self_compose() is deprecated, use nest() instead - See http://trac.sagemath.org/21926 for details. - sage: x = var('x') - sage: g(x) - ((x^2 + 1)^2 + 1)^2 + 1 - - :: - - sage: def f(x): return x + 1 - sage: g = self_compose(f, 10000) - sage: g(0) - 10000 - - :: - - sage: x = var('x') - sage: self_compose(sin, 0)(x) - x - - """ - from sage.misc.superseded import deprecation - deprecation(21926, "self_compose() is deprecated, use nest() instead") - from sage.rings.all import Integer - n = Integer(n) - - if n < 0: - raise ValueError("n must be a nonnegative integer, not {}.".format(n)) - - return lambda x: nest(f, n, x) - - def nest(f, n, x): """ Return `f(f(...f(x)...))`, where the composition occurs n times. @@ -1040,7 +957,7 @@ def nest(f, n, x): ################################################################# class BackslashOperator: - """ + r""" Implements Matlab-style backslash operator for solving systems:: A \\ b @@ -1080,7 +997,7 @@ def __rmul__(self, left): return self def __mul__(self, right): - """ + r""" EXAMPLES:: sage: A = matrix(RDF, 5, 5, 2) @@ -1236,6 +1153,7 @@ def some_tuples(elements, repeat, bound, max_samples=None): return elements if repeat is None else product(elements, repeat=repeat) return _some_tuples_sampling(elements, repeat, max_samples, n) + def _some_tuples_sampling(elements, repeat, max_samples, n): """ Internal function for :func:`some_tuples`. @@ -1257,6 +1175,7 @@ def _some_tuples_sampling(elements, repeat, max_samples, n): else: yield tuple(elements[j] for j in Integer(a).digits(n, padto=repeat)) + def powerset(X): r""" Iterator over the *list* of all subsets of the iterable X, in no @@ -1301,7 +1220,6 @@ def powerset(X): sets must be hashable and many structures on which one wants the powerset consist of non-hashable objects. - AUTHORS: - William Stein @@ -1312,67 +1230,12 @@ def powerset(X): yield [] pairs = [] for x in X: - pairs.append((2**len(pairs),x)) + pairs.append((2**len(pairs), x)) for w in range(2**(len(pairs)-1), 2**(len(pairs))): yield [x for m, x in pairs if m & w] -subsets = powerset - -################################################################# -# Type checking -################################################################# -def typecheck(x, C, var="x"): - """ - Check that x is of instance C. If not raise a TypeError with an - error message. - """ - from sage.misc.superseded import deprecation - deprecation(21926, "typecheck is deprecated, use isinstance instead") - if not isinstance(x, C): - raise TypeError("{} (={}) must be of type {}.".format(var, x, C)) -################################################################# -# This will likely eventually be useful. -################################################################# - -# From the Python Cookbook Ver 2, Recipe 20.4 -class cached_attribute(object): - """ - Computes attribute value and caches it in the instance. - """ - def __init__(self, method, name=None): - from sage.misc.superseded import deprecation - deprecation(21926, "cached_attribute is deprecated") - # record the unbound-method and the name - self.method = method - self.name = name or method.__name__ - def __get__(self, inst, cls): - if inst is None: - # instance attribute accessed on class, return self - return self - # compute, cache and return the instance's attribute value - result = self.method(inst) - setattr(inst, self.name, result) - return result - -class lazy_prop(object): - def __init__(self, calculate_function): - from sage.misc.superseded import deprecation - deprecation(21926, "lazy_prop is deprecated") - self._calculate = calculate_function - self.__doc__ = calculate_function.__doc__ - - def __call__(self, obj, _=None): - if obj is None: - return self - value = self._calculate(obj) - setattr(obj, self._calculate.__name__, value) - return value - -def prop(f): - from sage.misc.superseded import deprecation - deprecation(21926, "prop() is deprecated") - return property(f, None, None, f.__doc__) +subsets = powerset ################################################################# @@ -1426,9 +1289,11 @@ def exists(S, P): (False, None) """ for x in S: - if P(x): return True, x + if P(x): + return True, x return False, None + def forall(S, P): """ If P(x) is true every x in S, return True and None. If there is @@ -1483,30 +1348,17 @@ def forall(S, P): (True, None) """ for x in S: - if not P(x): return False, x + if not P(x): + return False, x return True, None -################################################################# -# which source file? -################################################################# -import inspect -def sourcefile(object): - """ - Work out which source or compiled file an object was defined in. - """ - from sage.misc.superseded import deprecation - deprecation(21926, "sourcefile(x) is deprecated, use inspect.getfile(x) instead") - return inspect.getfile(object) - ################################################################# # debug tracing ################################################################# -import pdb set_trace = pdb.set_trace - ################################################################# # Word wrap lines ################################################################# @@ -1529,7 +1381,7 @@ def word_wrap(s, ncols=85): end = '' t.append(x[:k] + end) x = x[k:] - k=0 + k = 0 while k < len(x) and x[k] == ' ': k += 1 x = x[k:] @@ -1537,42 +1389,6 @@ def word_wrap(s, ncols=85): return '\n'.join(t) -def getitem(v, n): - r""" - Variant of getitem that coerces to an int if a TypeError is - raised. - - (This is not needed anymore - classes should define an - __index__ method.) - - Thus, e.g., ``getitem(v,n)`` will work even if - `v` is a Python list and `n` is a Sage integer. - - EXAMPLES:: - - sage: v = [1,2,3] - - The following used to fail in Sage <= 1.3.7. Now it works fine:: - - sage: v[ZZ(1)] - 2 - - This always worked. - - :: - - sage: getitem(v, ZZ(1)) - doctest:... DeprecationWarning: getitem(v, n) is deprecated, use v[n] instead - See http://trac.sagemath.org/21926 for details. - 2 - """ - from sage.misc.superseded import deprecation - deprecation(21926, "getitem(v, n) is deprecated, use v[n] instead") - try: - return v[n] - except TypeError: - return v[int(n)] - def pad_zeros(s, size=3): """ EXAMPLES:: @@ -1588,9 +1404,8 @@ def pad_zeros(s, size=3): sage: pad_zeros(389, 10) '0000000389' """ - return "0"*(size-len(str(s))) + str(s) + return "0" * (size - len(str(s))) + str(s) -import sage.server.support def embedded(): """ @@ -1641,8 +1456,9 @@ def __call__(self, x, *args): def __repr__(self): """ - Returns a string representation of this object. The star in the - output represents the object passed into self. + Return a string representation of this object. + + The star in the output represents the object passed into ``self``. EXAMPLES:: @@ -1653,11 +1469,11 @@ def __repr__(self): sage: attrcall('hooks', 3, flatten=True) *.hooks(3, flatten=True) """ - s = "*.%s(%s"%(self.name, ", ".join(map(repr, self.args))) + s = "*.%s(%s" % (self.name, ", ".join(map(repr, self.args))) if self.kwds: - if len(self.args) > 0: + if self.args: s += ", " - s += ", ".join("%s=%s"%keyvalue for keyvalue in self.kwds.items()) + s += ", ".join("%s=%s" % keyvalue for keyvalue in self.kwds.items()) s += ")" return s @@ -1727,6 +1543,7 @@ def __hash__(self): """ return hash((self.args, tuple(self.kwds.items()))) + def attrcall(name, *args, **kwds): """ Returns a callable which takes in an object, gets the method named @@ -1750,6 +1567,7 @@ def attrcall(name, *args, **kwds): """ return AttrCallObject(name, args, kwds) + def call_method(obj, name, *args, **kwds): """ Call the method ``name`` on ``obj``. @@ -1766,6 +1584,7 @@ def call_method(obj, name, *args, **kwds): """ return getattr(obj, name)(*args, **kwds) + def is_in_string(line, pos): r""" Returns True if the character at position pos in line occurs @@ -1915,7 +1734,8 @@ def inject_variable(name, value, warn=True): # also from functions in various modules. G = get_main_globals() if name in G and warn: - warnings.warn("redefining global value `%s`"%name, RuntimeWarning, stacklevel = 2) + warnings.warn("redefining global value `%s`" % name, + RuntimeWarning, stacklevel=2) G[name] = value From 321c1b19986799f3a725251728918fc42544810b Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 23 Jun 2018 23:05:48 +0200 Subject: [PATCH 252/284] fix cardinality for PartitionDiagrams, BrauerDiagrams, TemperleyLiebDiagrams and PlanarDiagrams, improve doctests --- src/sage/combinat/diagram_algebras.py | 165 ++++++++++++++++++++++---- 1 file changed, 142 insertions(+), 23 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 8fb4ae4bd8e..04bee68c89d 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -41,7 +41,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.misc.flatten import flatten from sage.misc.misc_c import prod -from sage.rings.all import ZZ +from sage.rings.all import ZZ, QQ from sage.functions.other import ceil import itertools @@ -119,6 +119,7 @@ def brauer_diagrams(k): [{{-3, 3}, {-2, 1}, {-1, 2}}, {{-3, 3}, {-2, 2}, {-1, 1}}, {{-3, 3}, {-2, -1}, {1, 2}}] """ if k in ZZ: + k = ZZ(k) S = SetPartitions(list(range(1,k+1)) + list(range(-k,0)), [2]*k) for i in S._iterator_part(S.parts): yield list(i) @@ -1101,11 +1102,12 @@ def __init__(self, order, category=None): if category is None: category = FiniteEnumeratedSets() Parent.__init__(self, category=category) - self.order = order if order in ZZ: + self.order = ZZ(order) base_set = frozenset(list(range(1,order+1)) + list(range(-order,0))) else: #order is a half-integer. + self.order = QQ(order) base_set = frozenset(list(range(1,ZZ(ZZ(1)/ZZ(2) + order)+1)) + list(range(ZZ(-ZZ(1)/ZZ(2) - order),0))) self._set = base_set @@ -1264,12 +1266,35 @@ class PartitionDiagrams(AbstractPartitionDiagrams): EXAMPLES:: + sage: import sage.combinat.diagram_algebras as da + sage: pd = da.PartitionDiagrams(1); pd + Partition diagrams of order 1 + sage: pd.list() + [{{-1, 1}}, {{-1}, {1}}] + + sage: pd = da.PartitionDiagrams(3/2); pd + Partition diagrams of order 3/2 + sage: pd.list() + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}] + + TESTS:: + sage: import sage.combinat.diagram_algebras as da sage: pd = da.PartitionDiagrams(3) sage: pd.an_element() in pd True sage: pd.cardinality() == len(pd.list()) True + + sage: pd = da.PartitionDiagrams(5/2) + sage: pd.an_element() in pd + True + sage: pd.cardinality() == len(pd.list()) + True """ Element = PartitionDiagram _name = "Partition" @@ -1277,7 +1302,7 @@ class PartitionDiagrams(AbstractPartitionDiagrams): def cardinality(self): r""" - The cardinality of partition diagrams of integer order `n` is + The cardinality of partition diagrams of half-integer order `n` is the `2n`-th Bell number. EXAMPLES:: @@ -1286,11 +1311,12 @@ def cardinality(self): sage: pd = da.PartitionDiagrams(3) sage: pd.cardinality() 203 - """ - if self.order in ZZ: - return bell_number(2 * self.order) - return bell_number(2 * self.order - 1) + sage: pd = da.PartitionDiagrams(7/2) + sage: pd.cardinality() + 877 + """ + return bell_number(ZZ(2 * self.order)) class BrauerDiagrams(AbstractPartitionDiagrams): r""" @@ -1300,6 +1326,21 @@ class BrauerDiagrams(AbstractPartitionDiagrams): EXAMPLES:: + sage: import sage.combinat.diagram_algebras as da + sage: bd = da.BrauerDiagrams(2); bd + Brauer diagrams of order 2 + sage: bd.list() + [{{-2, 1}, {-1, 2}}, {{-2, 2}, {-1, 1}}, {{-2, -1}, {1, 2}}] + + sage: bd = da.BrauerDiagrams(5/2); bd + Brauer diagrams of order 5/2 + sage: bd.list() + [{{-3, 3}, {-2, 1}, {-1, 2}}, + {{-3, 3}, {-2, 2}, {-1, 1}}, + {{-3, 3}, {-2, -1}, {1, 2}}] + + TESTS:: + sage: import sage.combinat.diagram_algebras as da sage: bd = da.BrauerDiagrams(3) sage: bd.an_element() in bd @@ -1307,6 +1348,12 @@ class BrauerDiagrams(AbstractPartitionDiagrams): sage: bd.cardinality() == len(bd.list()) True + sage: bd = da.BrauerDiagrams(5/2) + sage: bd.an_element() in bd + True + sage: bd.cardinality() == len(bd.list()) + True + These diagrams also come equipped with a compact representation based on their bipartition triple representation. See the :meth:`from_involution_permutation_triple` method for more information. @@ -1350,8 +1397,16 @@ def __contains__(self, obj): True sage: [[1,2,-1,-2]] in bd False + sage: bd = da.BrauerDiagrams(3/2) + sage: bd.an_element() in bd + True + """ - return super(BrauerDiagrams, self).__contains__(obj) and [len(i) for i in obj] == [2]*self.order + if self.order in ZZ: + r = ZZ(self.order) + else: + r = ZZ(self.order + ZZ(1)/ZZ(2)) + return super(BrauerDiagrams, self).__contains__(obj) and [len(i) for i in obj] == [2]*r def cardinality(self): r""" @@ -1365,11 +1420,15 @@ def cardinality(self): sage: bd = da.BrauerDiagrams(3) sage: bd.cardinality() 15 + + sage: bd = da.BrauerDiagrams(7/2) + sage: bd.cardinality() + 15 """ if self.order in ZZ: - return (2 * self.order - 1).multifactorial(2) + return (2 * ZZ(self.order) -1 ).multifactorial(2) else: - return (2 * self.order - 2).multifactorial(2) + return (2 * ZZ(self.order - 1/2) - 1).multifactorial(2) def symmetric_diagrams(self, l=None, perm=None): r""" @@ -1380,17 +1439,28 @@ def symmetric_diagrams(self, l=None, perm=None): sage: import sage.combinat.diagram_algebras as da sage: bd = da.BrauerDiagrams(4) - sage: bd.symmetric_diagrams(l=1,perm=[2,1]) + sage: bd.symmetric_diagrams(l=1, perm=[2,1]) [{{-4, -3}, {-2, 1}, {-1, 2}, {3, 4}}, {{-4, -2}, {-3, 1}, {-1, 3}, {2, 4}}, {{-4, 1}, {-3, -2}, {-1, 4}, {2, 3}}, {{-4, -1}, {-3, 2}, {-2, 3}, {1, 4}}, {{-4, 2}, {-3, -1}, {-2, 4}, {1, 3}}, {{-4, 3}, {-3, 4}, {-2, -1}, {1, 2}}] + + TESTS:: + + sage: import sage.combinat.diagram_algebras as da + sage: bd = da.BrauerDiagrams(3/2) + sage: bd.symmetric_diagrams(l=1, perm=[2,1]) + Traceback (most recent call last): + ... + NotImplementedError: symmetric_diagrams is only implemented for Brauer diagrams of integer order, not for order 3/2 """ # perm = permutation on free nodes # l = number of arcs - n = self.order + if self.order not in ZZ: + raise NotImplementedError("symmetric_diagrams is only implemented for Brauer diagrams of integer order, not for order %s" %(self.order)) + n = ZZ(self.order) if l is None: l = 0 if perm is None: @@ -1433,7 +1503,18 @@ def from_involution_permutation_triple(self, D1_D2_pi): sage: bd = da.BrauerDiagrams(4) sage: bd.from_involution_permutation_triple([[[1,2]],[[3,4]],[2,1]]) {{-4, -3}, {-2, 3}, {-1, 4}, {1, 2}} + + TESTS:: + + sage: import sage.combinat.diagram_algebras as da + sage: bd = da.BrauerDiagrams(5/2) + sage: bd.from_involution_permutation_triple([[[1,2]],[[3,4]],[2,1]]) + Traceback (most recent call last): + ... + NotImplementedError: from_involution_permutation_triple is only implemented for Brauer diagrams of integer order, not for order 5/2 """ + if self.order not in ZZ: + raise NotImplementedError("from_involution_permutation_triple is only implemented for Brauer diagrams of integer order, not for order %s" %(self.order)) try: (D1,D2,pi) = tuple(D1_D2_pi) except ValueError: @@ -1462,12 +1543,35 @@ class TemperleyLiebDiagrams(AbstractPartitionDiagrams): EXAMPLES:: + sage: import sage.combinat.diagram_algebras as da + sage: td = da.TemperleyLiebDiagrams(3); td + Temperley Lieb diagrams of order 3 + sage: td.list() + [{{-3, 1}, {-2, -1}, {2, 3}}, + {{-3, 3}, {-2, 2}, {-1, 1}}, + {{-3, 3}, {-2, -1}, {1, 2}}, + {{-3, -2}, {-1, 1}, {2, 3}}, + {{-3, -2}, {-1, 3}, {1, 2}}] + + sage: td = da.TemperleyLiebDiagrams(5/2); td + Temperley Lieb diagrams of order 5/2 + sage: td.list() + [{{-3, 3}, {-2, 2}, {-1, 1}}, {{-3, 3}, {-2, -1}, {1, 2}}] + + TESTS:: + sage: import sage.combinat.diagram_algebras as da sage: td = da.TemperleyLiebDiagrams(3) sage: td.an_element() in td True sage: td.cardinality() == len(td.list()) True + + sage: td = da.TemperleyLiebDiagrams(7/2) + sage: td.an_element() in td + True + sage: td.cardinality() == len(td.list()) + True """ Element = TemperleyLiebDiagram _name = "Temperley Lieb" @@ -1488,9 +1592,9 @@ def cardinality(self): 5 """ if self.order in ZZ: - return catalan_number(self.order) + return catalan_number(ZZ(self.order)) else: - return catalan_number(self.order - ZZ.one() / 2) + return catalan_number(ZZ(self.order - 1/2)) def __contains__(self, obj): r""" @@ -1512,11 +1616,7 @@ def __contains__(self, obj): obj = self._element_constructor_(obj) except (ValueError, TypeError): return False - if obj not in BrauerDiagrams(self.order): - return False - if not obj.is_planar(): - return False - return True + return obj in BrauerDiagrams(self.order) and obj.is_planar() class PlanarDiagrams(AbstractPartitionDiagrams): r""" @@ -1524,12 +1624,34 @@ class PlanarDiagrams(AbstractPartitionDiagrams): EXAMPLES:: + sage: import sage.combinat.diagram_algebras as da + sage: pld = da.PlanarDiagrams(1); pld + Planar diagrams of order 1 + sage: pld.list() + [{{-1, 1}}, {{-1}, {1}}] + + sage: pld = da.PlanarDiagrams(3/2); pld + Planar diagrams of order 3/2 + sage: pld.list() + [{{-2, -1, 1, 2}}, + {{-2, 1, 2}, {-1}}, + {{-2, 2}, {-1, 1}}, + {{-2, -1, 2}, {1}}, + {{-2, 2}, {-1}, {1}}] + + TESTS:: + sage: import sage.combinat.diagram_algebras as da sage: pld = da.PlanarDiagrams(3) sage: pld.an_element() in pld True sage: pld.cardinality() == len(pld.list()) True + sage: pld = da.PlanarDiagrams(5/2) + sage: pld.an_element() in pld + True + sage: pld.cardinality() == len(pld.list()) + True """ Element = PlanarDiagram _name = "Planar" @@ -1549,10 +1671,7 @@ def cardinality(self): sage: pld.cardinality() 132 """ - if self.order in ZZ: - return catalan_number(2*self.order) - else: - return catalan_number(2*self.order-1) + return catalan_number(2*self.order) def __contains__(self, obj): r""" From 9835998a9d1a64792ec6a0b70fd8abb66eeb1d6f Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Mon, 13 Aug 2018 10:37:06 +0000 Subject: [PATCH 253/284] fix the output of these tests to be consistent with #25462 --- src/sage/combinat/diagram_algebras.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 04bee68c89d..fa1cd4918a1 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1276,9 +1276,9 @@ class PartitionDiagrams(AbstractPartitionDiagrams): Partition diagrams of order 3/2 sage: pd.list() [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] TESTS:: @@ -1634,9 +1634,9 @@ class PlanarDiagrams(AbstractPartitionDiagrams): Planar diagrams of order 3/2 sage: pld.list() [{{-2, -1, 1, 2}}, - {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1, 1}}, {{-2, -1, 2}, {1}}, + {{-2, 1, 2}, {-1}}, {{-2, 2}, {-1}, {1}}] TESTS:: From f54b339d87f9d1ac39dfc95512835c41b518c533 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 13 Aug 2018 13:53:28 +0200 Subject: [PATCH 254/284] shorter error message per reviewer's request --- src/sage/combinat/diagram_algebras.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index fa1cd4918a1..c070c5b0528 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -1454,12 +1454,13 @@ def symmetric_diagrams(self, l=None, perm=None): sage: bd.symmetric_diagrams(l=1, perm=[2,1]) Traceback (most recent call last): ... - NotImplementedError: symmetric_diagrams is only implemented for Brauer diagrams of integer order, not for order 3/2 + NotImplementedError: only implemented for integer order, not for order 3/2 """ # perm = permutation on free nodes # l = number of arcs if self.order not in ZZ: - raise NotImplementedError("symmetric_diagrams is only implemented for Brauer diagrams of integer order, not for order %s" %(self.order)) + raise NotImplementedError("only implemented for integer order," + " not for order %s" % (self.order)) n = ZZ(self.order) if l is None: l = 0 @@ -1511,10 +1512,11 @@ def from_involution_permutation_triple(self, D1_D2_pi): sage: bd.from_involution_permutation_triple([[[1,2]],[[3,4]],[2,1]]) Traceback (most recent call last): ... - NotImplementedError: from_involution_permutation_triple is only implemented for Brauer diagrams of integer order, not for order 5/2 + NotImplementedError: only implemented for integer order, not for order 5/2 """ if self.order not in ZZ: - raise NotImplementedError("from_involution_permutation_triple is only implemented for Brauer diagrams of integer order, not for order %s" %(self.order)) + raise NotImplementedError("only implemented for integer order," + " not for order %s" % (self.order)) try: (D1,D2,pi) = tuple(D1_D2_pi) except ValueError: From c4e964e24a4e85f07550e9e4a9a3f5d375805768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 13 Aug 2018 16:17:40 +0200 Subject: [PATCH 255/284] add some missing doctests inside combinat --- src/sage/combinat/baxter_permutations.py | 12 ++++-- src/sage/combinat/grossman_larson_algebras.py | 2 + src/sage/combinat/hall_polynomial.py | 6 +++ .../combinat/posets/incidence_algebras.py | 12 ++++++ src/sage/combinat/posets/linear_extensions.py | 6 +++ src/sage/combinat/posets/moebius_algebra.py | 8 ++++ .../combinat/root_system/associahedron.py | 11 ++++- src/sage/combinat/sf/kfpoly.py | 40 +++++++++++++++---- src/sage/combinat/sine_gordon.py | 8 ++-- src/sage/combinat/tableau_residues.py | 11 +++-- 10 files changed, 97 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 5be7ae1e518..52826149079 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Baxter permutations """ @@ -187,9 +188,9 @@ def __iter__(self): REFERENCES: - .. [BBF08] \N. Bonichon, M. Bousquet-Melou, E. Fusy. - Baxter permutations and plane bipolar orientations. - Seminaire Lotharingien de combinatoire 61A, article B61Ah, 2008. + .. [BBF08] \N. Bonichon, M. Bousquet-Mélou, E. Fusy. + *Baxter permutations and plane bipolar orientations*. + Séminaire Lotharingien de combinatoire 61A, article B61Ah, 2008. """ if self._n == 0: yield Permutations(0)([]) @@ -305,6 +306,11 @@ def __contains__(self, x): False sage: Permutation([4, 3, 6, 9, 7, 5, 1, 2, 8]) in BaxterPermutations() True + + TESTS:: + + sage: 42 in BaxterPermutations() + False """ if not x in Permutations(): return False diff --git a/src/sage/combinat/grossman_larson_algebras.py b/src/sage/combinat/grossman_larson_algebras.py index d9b3109fa87..f942268a767 100644 --- a/src/sage/combinat/grossman_larson_algebras.py +++ b/src/sage/combinat/grossman_larson_algebras.py @@ -556,6 +556,8 @@ def _element_constructor_(self, x): ValueError: incorrect root label sage: R. = algebras.GrossmanLarson(QQ) + sage: R(x) is x + True sage: S. = algebras.GrossmanLarson(GF(3)) sage: R(z) Traceback (most recent call last): diff --git a/src/sage/combinat/hall_polynomial.py b/src/sage/combinat/hall_polynomial.py index 481da790d12..575087d7335 100644 --- a/src/sage/combinat/hall_polynomial.py +++ b/src/sage/combinat/hall_polynomial.py @@ -21,6 +21,7 @@ from sage.combinat.partition import Partition from sage.combinat.q_analogues import q_binomial + def hall_polynomial(nu, mu, la, q=None): r""" Return the (classical) Hall polynomial `P^{\nu}_{\mu,\lambda}` @@ -143,6 +144,11 @@ def hall_polynomial(nu, mu, la, q=None): 2*q^3 + q^2 - q - 1 sage: hall_polynomial([4,2], [2,1], [2,1], 0) 1 + + TESTS:: + + sage: hall_polynomial([3], [1], [1], 0) + 0 """ if q is None: q = ZZ['q'].gen() diff --git a/src/sage/combinat/posets/incidence_algebras.py b/src/sage/combinat/posets/incidence_algebras.py index a4933671fc6..d8e709b2581 100644 --- a/src/sage/combinat/posets/incidence_algebras.py +++ b/src/sage/combinat/posets/incidence_algebras.py @@ -644,6 +644,18 @@ def __getitem__(self, A): R[(0, 0)] sage: R[3, 11] R[(0, 1)] + + TESTS: + + sage: R[2, 5] + Traceback (most recent call last): + ... + ValueError: not an interval + + sage: R[-1] + Traceback (most recent call last): + ... + ValueError: not an element of the poset """ if not isinstance(A, (list, tuple)): if A not in self._ambient._poset.list(): diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 8adef42bb51..be5a115e756 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -112,10 +112,15 @@ def __classcall_private__(cls, linear_extension, poset): Finite poset containing 4 elements sage: TestSuite(p).run() + TESTS:: + sage: LinearExtensionOfPoset([4,3,2,1], P) Traceback (most recent call last): ... ValueError: [4, 3, 2, 1] is not a linear extension of Finite poset containing 4 elements + + sage: p is LinearExtensionOfPoset(p, P) + True """ if isinstance(linear_extension, cls): return linear_extension @@ -400,6 +405,7 @@ def jump_count(self): n += 1 return n + class LinearExtensionsOfPoset(UniqueRepresentation, Parent): """ The set of all linear extensions of a finite poset diff --git a/src/sage/combinat/posets/moebius_algebra.py b/src/sage/combinat/posets/moebius_algebra.py index 79ae50ab287..fc58937aea3 100644 --- a/src/sage/combinat/posets/moebius_algebra.py +++ b/src/sage/combinat/posets/moebius_algebra.py @@ -361,6 +361,7 @@ def __getitem__(self, x): idempotent = I + class QuantumMoebiusAlgebra(Parent, UniqueRepresentation): r""" The quantum Möbius algebra of a lattice. @@ -388,6 +389,13 @@ def __init__(self, L, q=None): sage: L = posets.BooleanLattice(4) sage: M = L.quantum_moebius_algebra() sage: TestSuite(M).run() # long time + + sage: from sage.combinat.posets.moebius_algebra import QuantumMoebiusAlgebra + sage: L = posets.Crown(2) + sage: QuantumMoebiusAlgebra(L) + Traceback (most recent call last): + ... + ValueError: L must be a lattice """ if not L.is_lattice(): raise ValueError("L must be a lattice") diff --git a/src/sage/combinat/root_system/associahedron.py b/src/sage/combinat/root_system/associahedron.py index 658dc6920c9..a58aefd3615 100644 --- a/src/sage/combinat/root_system/associahedron.py +++ b/src/sage/combinat/root_system/associahedron.py @@ -65,12 +65,12 @@ def Associahedron(cartan_type): sage: Asso = polytopes.associahedron(['A',3]); Asso Generalized associahedron of type ['A', 3] with 14 vertices - sage: Asso.plot() + sage: Asso.plot() # long time Graphics3d Object sage: Asso = polytopes.associahedron(['B',3]); Asso Generalized associahedron of type ['B', 3] with 20 vertices - sage: Asso.plot() + sage: Asso.plot() # long time Graphics3d Object TESTS:: @@ -205,6 +205,13 @@ def _element_constructor_(self, cartan_type, **kwds): Generalized associahedron of type ['A', 2] with 5 vertices sage: parent._element_constructor_(['A',2]) Generalized associahedron of type ['A', 2] with 5 vertices + + TESTS:: + + sage: parent(['A', 2, 1]) + Traceback (most recent call last): + ... + ValueError: the Cartan type must be finite """ cartan_type = CartanType(cartan_type) if not cartan_type.is_finite(): diff --git a/src/sage/combinat/sf/kfpoly.py b/src/sage/combinat/sf/kfpoly.py index 661fc3cc4fd..f39d4245100 100644 --- a/src/sage/combinat/sf/kfpoly.py +++ b/src/sage/combinat/sf/kfpoly.py @@ -59,20 +59,34 @@ def KostkaFoulkesPolynomial(mu, nu, t=None): sage: q = PolynomialRing(QQ,'q').gen() sage: KostkaFoulkesPolynomial([2,2],[2,1,1],q) q + + TESTS:: + + sage: KostkaFoulkesPolynomial([2,4],[2,2]) + Traceback (most recent call last): + ... + ValueError: mu must be a partition + sage: KostkaFoulkesPolynomial([2,2],[2,4]) + Traceback (most recent call last): + ... + ValueError: nu must be a partition + sage: KostkaFoulkesPolynomial([3,2],[2,1]) + Traceback (most recent call last): + ... + ValueError: mu and nu must be partitions of the same size """ if mu not in _Partitions: raise ValueError("mu must be a partition") if nu not in _Partitions: raise ValueError("nu must be a partition") - if sum(mu) != sum(nu): raise ValueError("mu and nu must be partitions of the same size") - return kfpoly(mu, nu, t) + def kfpoly(mu, nu, t=None): r""" - Returns the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)` + Return the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)` by generating all rigging sequences for the shape `\mu`, and then selecting those of content `\nu`. @@ -98,11 +112,14 @@ def kfpoly(mu, nu, t=None): t^2 sage: kfpoly([1,1,1,1], [2,2]) 0 + + TESTS:: + + sage: kfpoly([], []) + 1 """ if mu == nu: return 1 - elif mu == []: - return 0 if t is None: t = polygen(ZZ, 't') @@ -111,8 +128,8 @@ def kfpoly(mu, nu, t=None): f = lambda x: weight(x, t) if x[0] == nuc else 0 - res = sum(f(rg) for rg in riggings(mu)) - return res + return sum(f(rg) for rg in riggings(mu)) + def schur_to_hl(mu, t=None): r""" @@ -277,6 +294,7 @@ def compat(n, mu, nu): return [] # _Partitions([]) + def dom(mup, snu): """ Return ``True`` if ``sum(mu[:i+1]) >= snu[i]`` for all @@ -302,6 +320,11 @@ def dom(mup, snu): False sage: dom([3,2,1],[4,4,4]) False + + TESTS:: + + sage: dom([],[]) + True """ if not mup: # mup is empty: return not snu # True if and only if snu is empty @@ -322,7 +345,8 @@ def dom(mup, snu): if sa < snu[pos]: return False pos += 1 - return all(sa >= snu[j] for j in range(pos,l)) + return all(sa >= snu[j] for j in range(pos, l)) + def weight(rg, t=None): r""" diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index 18509b27237..3dbc1584182 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -461,7 +461,8 @@ def plot(self, **kwds): EXAMPLES:: sage: Y = SineGordonYsystem('A',(6,4,3)); - sage: Y.plot() # not tested + sage: Y.plot() # long time 2s + Graphics object consisting of 219 graphics primitives """ # Set up plotting options if 'radius' in kwds: @@ -504,13 +505,14 @@ def plot(self, **kwds): def triangle(x): (a, b) = sorted(x[:2]) - for p in self.vertex_iterator(): + for p in self.vertices(): if (p, a) in self.triangulation() or (a, p) in self.triangulation(): if (p, b) in self.triangulation() or (b, p) in self.triangulation(): if p < a or p > b: return sorted((a, b, p)) def plot_arc(radius, p, q, **opts): + # TODO: THIS SHOULD USE THE EXISTING PLOT OF ARCS! # plot the arc from p to q differently depending on the type of self p = ZZ(p) q = ZZ(q) @@ -596,7 +598,7 @@ def vertex_to_angle(v): # Vertices v_points = {x: (radius * cos(vertex_to_angle(x)), radius * sin(vertex_to_angle(x))) - for x in self.vertex_iterator()} + for x in self.vertices()} for v in v_points: P += point(v_points[v], zorder=len(P), **points_opts) # Reflection axes diff --git a/src/sage/combinat/tableau_residues.py b/src/sage/combinat/tableau_residues.py index 8056d67c1a8..9e72cd14d12 100644 --- a/src/sage/combinat/tableau_residues.py +++ b/src/sage/combinat/tableau_residues.py @@ -452,14 +452,20 @@ def swap_residues(self, i, j): sage: res == ser False + TESTS:: + + sage: res.swap_residues(22,26) + Traceback (most recent call last): + ... + IndexError: 22 and 26 must be between 1 and 8 """ with self.clone() as swap: try: # we have overridden __getitem__ so that indices are 1-based but # __setitem__ is still 0-based so we need to renormalise the LHS - swap[i-1],swap[j-1] = self[j], self[i] + swap[i-1], swap[j-1] = self[j], self[i] except IndexError: - raise IndexError('%s and %s must be between 1 and %s' % (i,j,self.size)) + raise IndexError('%s and %s must be between 1 and %s' % (i, j, self.size())) return swap def standard_tableaux(self, shape=None): @@ -846,4 +852,3 @@ def check_element(self, element): """ if any(r not in self._base_ring for r in element): raise ValueError('not a {}-residue sequence {}'.format(self._quantum_characteristic)) - From c3337c9f966af14a88ae8f79d40b937cbe770813 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 13 Aug 2018 22:17:49 +0200 Subject: [PATCH 256/284] hash for Bruhat Tits etc --- src/sage/modular/btquotients/btquotient.py | 6 ++-- .../modular/btquotients/pautomorphicform.py | 28 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/btquotients/btquotient.py b/src/sage/modular/btquotients/btquotient.py index d0966dc2088..95afec7e2f0 100644 --- a/src/sage/modular/btquotients/btquotient.py +++ b/src/sage/modular/btquotients/btquotient.py @@ -1528,7 +1528,7 @@ def __init__(self, p, Nminus, Nplus=1, character=None, def _cache_key(self): r""" - Return a hash of self, for using in caching. + Return a hash of ``self``, for using in caching. EXAMPLES:: @@ -1541,8 +1541,10 @@ def _cache_key(self): sage: Y._cache_key() == X._cache_key() # optional - magma False """ - return hash((self._p, self._Nminus, self._Nplus, self._character, self._use_magma)) + + __hash__ = _cache_key + def _repr_(self): r""" Return the representation of self as a string. diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 6bffb4795d5..5ca5a816b42 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -1038,6 +1038,20 @@ def __ne__(self, other): """ return not self.__eq__(other) + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: X = BruhatTitsQuotient(5,7) + sage: H1 = X.harmonic_cocycles(2,prec=10) + sage: H2 = X.harmonic_cocycles(2,prec=10) + sage: hash(H1) == hash(H2) + True + """ + return hash((self.base_ring(), self._X, self._k)) + def _element_constructor_(self, x): r""" Constructor for harmonic cocycles. @@ -2391,6 +2405,20 @@ def __ne__(self, other): """ return not self.__eq__(other) + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: X = BruhatTitsQuotient(5,7) + sage: H1 = X.padic_automorphic_forms(2,prec = 10) + sage: H2 = X.padic_automorphic_forms(2,prec = 10) + sage: hash(H1) == hash(H2) + True + """ + return hash((self.base_ring(), self._source, self._U)) + def _repr_(self): r""" Return the representation of self as a string. From 331e5cbc6327a37bb9b29af7aba236999e8d72b5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 14 Aug 2018 16:49:38 +1000 Subject: [PATCH 257/284] Fixing doctest issues. --- src/sage/groups/matrix_gps/orthogonal.py | 12 ++++++++---- src/sage/groups/matrix_gps/symplectic.py | 4 ++-- src/sage/groups/matrix_gps/unitary.py | 8 ++++---- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index 37e2fef0bfc..bee84f6af6d 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -288,10 +288,12 @@ def GO(n, R, e=0, var='a', invariant_form=None): [1 0 0] [0 0 3] - sage GO(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) + sage: GO(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) Traceback (most recent call last): ... - NotImplementedError: invariant_form for finite Groups is fixed + NotImplementedError: invariant_form for finite groups is fixed by GAP + sage: 5+5 + 10 TESTS:: @@ -397,10 +399,12 @@ def SO(n, R, e=None, var='a', invariant_form=None): [zeta3 2 0] [ 0 0 1] - sage SO(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) + sage: SO(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) Traceback (most recent call last): ... - NotImplementedError: invariant_form for finite Groups is fixed + NotImplementedError: invariant_form for finite groups is fixed by GAP + sage: 5+5 + 10 TESTS:: diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index f2f20e6930a..783c2e4ab14 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -125,10 +125,10 @@ def Sp(n, R, var='a', invariant_form=None): [-1 0 0 0] [ 0 -2 0 0] - sage Sp(4,3, invariant_form=[[0,0,0,1],[0,0,1,0],[0,2,0,0], [2,0,0,0]]) + sage: Sp(4,3, invariant_form=[[0,0,0,1],[0,0,1,0],[0,2,0,0], [2,0,0,0]]) Traceback (most recent call last): ... - NotImplementedError: invariant_form for finite Groups is fixed + NotImplementedError: invariant_form for finite groups is fixed by GAP TESTS:: diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index 2776927c8a9..34e8f5d8895 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -236,10 +236,10 @@ def GU(n, R, var='a', invariant_form=None): [E(5)^4 2 0] [ 0 0 1] - sage GU(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) + sage: GU(3,3, invariant_form=[[1,0,0],[0,2,0],[0,0,1]]) Traceback (most recent call last): ... - NotImplementedError: invariant_form for finite Groups is fixed + NotImplementedError: invariant_form for finite groups is fixed by GAP sage: GU(2,QQ, invariant_form=[[1,0],[2,0]]) Traceback (most recent call last): @@ -335,10 +335,10 @@ def SU(n, R, var='a', invariant_form=None): [-zeta3 - 1 2 0] [ 0 0 1] - sage SU(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) + sage: SU(3,5, invariant_form=[[1,0,0],[0,2,0],[0,0,3]]) Traceback (most recent call last): ... - NotImplementedError: invariant_form for finite Groups is fixed + NotImplementedError: invariant_form for finite groups is fixed by GAP TESTS:: From 4acc84bca754c2a4edee82128193730796cf0289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 14 Aug 2018 09:19:20 +0200 Subject: [PATCH 258/284] py3: hash and richcmp for crystals of alcove paths --- src/sage/combinat/crystals/alcove_path.py | 62 +++++++++++------------ 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index fbb149a8e05..d0750c65ac3 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -476,6 +476,7 @@ def digraph_fast(self, depth=None): return super(CrystalOfAlcovePaths, self).digraph() return super(CrystalOfAlcovePaths, self).digraph(depth=depth) + class CrystalOfAlcovePathsElement(ElementWrapper): """ Crystal of alcove paths element. @@ -746,57 +747,56 @@ def plot(self): affine_ambient_space = RootSystem(ct.affine()).ambient_space() return affine_ambient_space.plot() + affine_ambient_space.plot_alcove_walk( word, foldings=foldings, labels=False) - def __eq__(self, other): + def _richcmp_(self, other, op): r""" - Test equality of ``self.value`` and ``other.value``. + Comparison of ``self.value`` and ``other.value``. + + For inequalities, ``self.value`` is compared to + ``other.value`` in dictionary order. EXAMPLES:: - sage: C=crystals.AlcovePaths(['B',2],[1,0]) - sage: lst=list(C) + sage: C = crystals.AlcovePaths(['B',2],[1,0]) + sage: lst = list(C) sage: lst[2] == lst[2] True sage: lst[2] == lst[1] False - """ - #note: may want to use _eq_ for coercion - try: - return self.value == other.value - except (NameError, AttributeError): - return False - - def __lt__(self, other): - r""" - Test if ``self.value`` is less than ``other.value`` in dictionary order. + sage: lst[2] != lst[2] + False + sage: lst[2] != lst[1] + True - EXAMPLES:: + sage: C = crystals.AlcovePaths(['A',2],[2,0]) + sage: x = C(()) + sage: x < x.f(1) + True + sage: a = x.f(1) ; b = x.f(1).f(1).f(2) + sage: a < b + False sage: C = crystals.AlcovePaths(['A',2],[2,0]) sage: x = C( () ) - sage: x.__lt__(x.f(1)) - True - sage: a=x.f(1) ; b = x.f(1).f(1).f(2) - sage: a.__lt__(b) + sage: x > x.f(1) False + sage: a = x.f(1) ; b = x.f(1).f(1).f(2) + sage: a > b + True """ - return self.value < other.value + return richcmp(self.value, other.value, op) - def __gt__(self, other): - r""" - Test if ``self.value`` is greater than ``other.value`` in dictionary - order. + def __hash__(self): + """ + Return the hash of ``self``. EXAMPLES:: - sage: C = crystals.AlcovePaths(['A',2],[2,0]) - sage: x = C( () ) - sage: x.__gt__(x.f(1)) - False - sage: a=x.f(1) ; b = x.f(1).f(1).f(2) - sage: a.__gt__(b) + sage: C = crystals.AlcovePaths(['B',2],[1,0]) + sage: lst = list(C) + sage: hash(lst[2]) == hash(lst[2]) True """ - return self.value > other.value + return hash(self.value) def _folding_data(self, i): r""" From 2000c4cdddd7df76a6c6f7d746d60cc24c79caea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 14 Aug 2018 09:32:46 +0200 Subject: [PATCH 259/284] py3: hash for shifted prime tableaux u --- src/sage/combinat/shifted_primed_tableau.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/shifted_primed_tableau.py b/src/sage/combinat/shifted_primed_tableau.py index 4c2868456ce..9e1d6772e5a 100644 --- a/src/sage/combinat/shifted_primed_tableau.py +++ b/src/sage/combinat/shifted_primed_tableau.py @@ -27,8 +27,6 @@ from sage.combinat.integer_vector import IntegerVectors from sage.rings.integer import Integer from sage.rings.rational_field import QQ -from sage.rings.rational import Rational -from sage.rings.integer_ring import ZZ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.lazy_attribute import lazy_attribute @@ -262,6 +260,18 @@ def __ne__(self, other): """ return not (self == other) + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: t = ShiftedPrimedTableau([[1,"2p"]]) + sage: hash(t) == hash(ShiftedPrimedTableaux([2])([[1,3/2]])) + True + """ + return hash((self._skew, tuple(self))) + def _repr_(self): """ Return a string representation of ``self``. From 2f61309028eb88900cfcc88b7526ac93befe3f18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 14 Aug 2018 09:39:28 +0200 Subject: [PATCH 260/284] py3: hash for Hamming code --- src/sage/coding/hamming_code.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sage/coding/hamming_code.py b/src/sage/coding/hamming_code.py index 771a0329673..5e25ba5cb13 100644 --- a/src/sage/coding/hamming_code.py +++ b/src/sage/coding/hamming_code.py @@ -91,6 +91,19 @@ def __eq__(self, other): and self.length() == other.length()\ and self.dimension() == other.dimension() + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: C1 = codes.HammingCode(GF(7), 3) + sage: C2 = codes.HammingCode(GF(7), 3) + sage: hash(C1) == hash(C2) + True + """ + return hash((self.length(), self.dimension())) + def _repr_(self): r""" Returns a string representation of ``self``. From 2931a96c5737676dee3cb8606384708cecabcdcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 14 Aug 2018 09:46:25 +0200 Subject: [PATCH 261/284] py3: hash for Symbolic subrings --- src/sage/symbolic/subring.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/sage/symbolic/subring.py b/src/sage/symbolic/subring.py index a8d4024273c..c2722c296ac 100644 --- a/src/sage/symbolic/subring.py +++ b/src/sage/symbolic/subring.py @@ -99,9 +99,10 @@ #***************************************************************************** from .ring import SymbolicRing, SR +from sage.categories.pushout import ConstructionFunctor +from sage.structure.factory import UniqueFactory -from sage.structure.factory import UniqueFactory class SymbolicSubringFactory(UniqueFactory): r""" A factory creating a symbolic subring. @@ -472,8 +473,23 @@ def __ne__(self, other): """ return not self == other + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: from sage.symbolic.subring import SymbolicSubring + sage: A = SymbolicSubring(accepting_variables=('a',)) + sage: B = SymbolicSubring(accepting_variables=('b',)) + sage: hash(A) == hash(A) + True + sage: hash(A) == hash(B) + False + """ + return hash(tuple(sorted(self._vars_))) + -from sage.categories.pushout import ConstructionFunctor class GenericSymbolicSubringFunctor(ConstructionFunctor): r""" A base class for the functors constructing symbolic subrings. @@ -775,7 +791,6 @@ def merge(self, other): if not (self.vars & other.vars): return other - def _apply_functor(self, R): """ Apply this functor to the given symbolic ring `R`. @@ -1037,7 +1052,6 @@ def _repr_(self): """ return 'Symbolic Constants Subring' - def has_valid_variable(self, variable): r""" Return whether the given ``variable`` is valid in this subring. @@ -1063,7 +1077,6 @@ def has_valid_variable(self, variable): """ return False - def _an_element_(self): r""" Return an element of this symbolic subring. From 32b543b455d5b1e7d28336841bd1e769b5e3d145 Mon Sep 17 00:00:00 2001 From: "Alex J. Best" Date: Tue, 14 Aug 2018 18:07:52 +0530 Subject: [PATCH 262/284] add prec argument in local_analytic_coord --- .../hyperelliptic_padic_field.py | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py index 6b30e9352f0..b1acfa9ea12 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py @@ -96,6 +96,18 @@ def local_analytic_interpolation(self, P, Q): ... ValueError: (5^-2 + O(5^6) : 5^-3 + 4*5^2 + 5^3 + 3*5^4 + O(5^5) : 1 + O(5^8)) and (1 + O(5^8) : 0 : 1 + O(5^8)) are not in the same residue disc + TESTS: + + Check that :trac:`26005` is fixed:: + + sage: L = Qp(5, 100) + sage: HL = H.change_ring(L) + sage: P = HL.lift_x(1 + 2*5^2) + sage: Q = HL.lift_x(1 + 3*5^2) + sage: x,y,z=HL.local_analytic_interpolation(P, Q) + sage: x.polynomial().degree() + 98 + AUTHORS: - Robert Bradshaw (2007-03) @@ -106,20 +118,20 @@ def local_analytic_interpolation(self, P, Q): raise ValueError("%s and %s are not in the same residue disc"%(P,Q)) disc = self.residue_disc(P) t = PowerSeriesRing(self.base_ring(), 't', prec).gen(0) - if disc == self.change_ring(self.base_ring().residue_field())(0,1,0): + if disc == self.change_ring(self.base_ring().residue_field())(0,1,0): # Infinite disc x,y = self.local_coordinates_at_infinity(2*prec) g = self.genus() return (x*t**(2*g+1),y*t**(2*g+1),t**(2*g+1)) - if disc[1] !=0: + if disc[1] != 0: # non-Weierstrass disc x = P[0]+t*(Q[0]-P[0]) pts = self.lift_x(x, all=True) if pts[0][1][0] == P[1]: return pts[0] else: return pts[1] - else: + else: # Weierstrass disc S = self.find_char_zero_weier_point(P) - x,y = self.local_coord(S) + x,y = self.local_coord(S, prec) a = P[1] b = Q[1] - P[1] y = a + b*t From db22bc7951f4a054166b99f5cb8f74af587f178f Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Tue, 14 Aug 2018 13:05:19 +0000 Subject: [PATCH 263/284] add an optional 'seed' argument to randstate.python_random this allows it to directly set the seed for the Python RNG without first consuming a random element from ZZ --- src/sage/misc/randstate.pyx | 11 +++++++++-- src/sage/rings/polynomial/real_roots.pyx | 3 +-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/sage/misc/randstate.pyx b/src/sage/misc/randstate.pyx index 9ce847cdb31..598622365ff 100644 --- a/src/sage/misc/randstate.pyx +++ b/src/sage/misc/randstate.pyx @@ -563,7 +563,7 @@ cdef class randstate: """ return self._seed - def python_random(self, cls=None): + def python_random(self, cls=None, seed=None): r""" Return a :class:`random.Random` object. The first time it is called on a given :class:`randstate`, a new :class:`random.Random` @@ -580,6 +580,10 @@ cdef class randstate: Python RNG interface. Otherwise the standard :class:`random.Random` is used. + - ``seed`` -- (optional) an integer to seed the :class:`random.Random` + instance with upon creation; if not specified it is seeded using + ``ZZ.random_element(1 << 128)``. + EXAMPLES:: sage: set_random_seed(5) @@ -598,7 +602,10 @@ cdef class randstate: from sage.rings.integer_ring import ZZ rand = cls() - rand.seed(long(ZZ.random_element(long(1)<<128))) + if seed is None: + rand.seed(long(ZZ.random_element(long(1)<<128))) + else: + rand.seed(long(seed)) self._python_random = rand return rand diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index abd41ee40d0..1a6e4434bd4 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -4273,8 +4273,7 @@ cdef class context: Initialize a context class. """ self.seed = seed # saved to make context printable - self.random = randstate().python_random() - self.random.seed(seed) + self.random = randstate().python_random(seed=seed) self.do_logging = do_logging self.wordsize = wordsize self.dc_log = [] From f8687f7d8800aede97e36d4da30ae79da895b5f7 Mon Sep 17 00:00:00 2001 From: "Erik M. Bray" Date: Fri, 27 Jul 2018 10:22:44 +0000 Subject: [PATCH 264/284] py3: comparison cleanup for hyperelliptic curves * also makes the comparison of ideals in polynomial rings more robust * also adds an explicit cast to int for range --- .../polynomial/multi_polynomial_ideal.py | 10 ++- .../hyperelliptic_generic.py | 80 +++++++++---------- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 6eef5136dc4..9baf6b0ea2b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -3240,8 +3240,14 @@ def __richcmp__(self, other, op): else: other_new = other - if set(self.gens()) == set(other_new.gens()): - return rich_to_bool(op, 0) + s_gens = self.gens() + o_gens = other_new.gens() + try: + if (s_gens == o_gens) or (set(s_gens) == set(o_gens)): + # the first clause works in the non-hashable case + return rich_to_bool(op, 0) + except TypeError: + pass # comparison for <= # needs just the Groebner basis for other diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py index 9d820ffab0e..abeebb8b507 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_generic.py @@ -54,7 +54,46 @@ def is_HyperellipticCurve(C): """ return isinstance(C,HyperellipticCurve_generic) + class HyperellipticCurve_generic(plane_curve.ProjectivePlaneCurve): + """ + TESTS:: + + sage: P. = QQ[] + sage: f0 = 4*x^5 - 30*x^3 + 45*x - 22 + sage: C0 = HyperellipticCurve(f0) + sage: f1 = x^5 - x^3 + x - 22 + sage: C1 = HyperellipticCurve(f1) + sage: C0 == C1 + False + sage: C0 == C0 + True + + sage: P. = QQ[] + sage: f0 = 4*x^5 - 30*x^3 + 45*x - 22 + sage: C0 = HyperellipticCurve(f0) + sage: f1 = x^5 - x^3 + x - 22 + sage: C1 = HyperellipticCurve(f1) + sage: C0 != C1 + True + sage: C0 != C0 + False + + sage: P. = QQ[] + sage: f0 = 4*x^5 - 30*x^3 + 45*x - 22 + sage: C0 = HyperellipticCurve(f0) + sage: f1 = x^5 - x^3 + x - 22 + sage: C1 = HyperellipticCurve(f1) + sage: Q. = GF(5)[] + sage: f2 = y^5 - y^3 + y - 22 + sage: C2 = HyperellipticCurve(f2) + sage: hash(C0) == hash(C0) + True + sage: hash(C0) == hash(C1) + False + sage: hash(C1) == hash(C2) + False + """ def __init__(self, PP, f, h=None, names=None, genus=None): x, y, z = PP.gens() df = f.degree() @@ -130,45 +169,6 @@ def _repr_(self): else: return "Hyperelliptic Curve over %s defined by %s + %s = %s" % (R, y**2, h(x)*y, f(x)) - def __eq__(self, other): - """ - Test of equality. - - EXAMPLES:: - - sage: P. = QQ[] - sage: f0 = 4*x^5 - 30*x^3 + 45*x - 22 - sage: C0 = HyperellipticCurve(f0) - sage: f1 = x^5 - x^3 + x - 22 - sage: C1 = HyperellipticCurve(f1) - sage: C0 == C1 - False - sage: C0 == C0 - True - """ - if not isinstance(other, HyperellipticCurve_generic): - return False - return (self._hyperelliptic_polynomials == - other._hyperelliptic_polynomials) - - def __ne__(self, other): - """ - Test of not equality. - - EXAMPLES:: - - sage: P. = QQ[] - sage: f0 = 4*x^5 - 30*x^3 + 45*x - 22 - sage: C0 = HyperellipticCurve(f0) - sage: f1 = x^5 - x^3 + x - 22 - sage: C1 = HyperellipticCurve(f1) - sage: C0 != C1 - True - sage: C0 != C0 - False - """ - return not self == other - def hyperelliptic_polynomials(self, K=None, var='x'): """ EXAMPLES:: @@ -513,7 +513,7 @@ def local_coordinates_at_weierstrass(self, P, prec=20, name='t'): t2 = t**2 c = b + t2/pol_prime(b) c = c.add_bigoh(prec) - for _ in range(1 + log(prec, 2)): + for _ in range(int(1 + log(prec, 2))): c -= (pol(c) - t2)/pol_prime(c) return (c, t.add_bigoh(prec)) From 4075773a6ec6da8a915d3e7a2706570909ca5990 Mon Sep 17 00:00:00 2001 From: Bryan Gillespie Date: Tue, 14 Aug 2018 12:49:58 -0600 Subject: [PATCH 265/284] Incorporate small style changes and add doctests --- src/sage/matroids/linear_matroid.pyx | 50 ++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 8a5d310abca..6e9c00ef137 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -466,21 +466,21 @@ cdef class LinearMatroid(BasisExchangeMatroid): # representations cpdef representation(self, B=None, reduced=False, labels=None, order=None, lift_map=None): - """ + r""" Return a matrix representing the matroid. Let `M` be a matroid on `n` elements with rank `r`. Let `E` be an ordering of the groundset, as output by :func:`M.groundset_list() `. - A *representation* of the matroid is an `r \\times n` matrix with the - following property. Consider column ``i`` to be labeled by ``E[i]``, + A *representation* of the matroid is an `r \times n` matrix with the + following property. Consider column `i` to be labeled by `E[i]`, and denote by `A[F]` the submatrix formed by the columns labeled by the subset `F \subseteq E`. Then for all `F \subseteq E`, the columns of `A[F]` are linearly independent if and only if `F` is an independent set in the matroid. A *reduced representation* is a matrix `D` such that `[I\ \ D]` is a - representation of the matroid, where `I` is an `r \\times r` identity + representation of the matroid, where `I` is an `r \times r` identity matrix. In this case, the rows of `D` are considered to be labeled by the first `r` elements of the list ``E``, and the columns by the remaining `n - r` elements. @@ -3945,12 +3945,12 @@ cdef class BinaryMatroid(LinearMatroid): [1 0 1] [1 0 1] - TESTS:: + TESTS: - From :trac:`23437` and comments: + Check that :trac:`23437` is fixed:: sage: M = matroids.named_matroids.Fano().dual() - sage: _ = list(M.bases()) + sage: B = list(M.bases()) sage: N = loads(dumps(M)) sage: N.closure(frozenset({'d'})) frozenset({'d'}) @@ -3967,7 +3967,7 @@ cdef class BinaryMatroid(LinearMatroid): else: A = self._A # current basis ordered so matrix cols form identity matrix: - basis, _ = self._current_rows_cols() + basis = self._current_rows_cols()[0] data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_binary_matroid, (version, data) @@ -4840,6 +4840,21 @@ cdef class TernaryMatroid(LinearMatroid): sage: loads(dumps(M)).representation() [1 0 1] [1 0 1] + + TESTS: + + Check that :trac:`23437` is fixed:: + + sage: from sage.matroids.advanced import * + sage: X_bin = matroids.named_matroids.Fano().representation() + sage: X = Matrix(GF(3), X_bin) + sage: M = TernaryMatroid(matrix=X).dual() + sage: B = list(M.bases()) + sage: N = loads(dumps(M)) + sage: N.closure(frozenset({3})) + frozenset({3}) + sage: N.is_isomorphic(M) + True """ import sage.matroids.unpickling version = 0 @@ -4851,7 +4866,7 @@ cdef class TernaryMatroid(LinearMatroid): else: A = self._A # current basis ordered so matrix cols form identity matrix: - basis, _ = self._current_rows_cols() + basis = self._current_rows_cols()[0] data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_ternary_matroid, (version, data) @@ -5545,6 +5560,21 @@ cdef class QuaternaryMatroid(LinearMatroid): sage: M.rename("U34") sage: loads(dumps(M)) U34 + + TESTS: + + Check that :trac:`23437` is fixed:: + + sage: from sage.matroids.advanced import QuaternaryMatroid + sage: X_bin = matroids.named_matroids.Fano().representation() + sage: X = Matrix(GF(4), X_bin) + sage: M = QuaternaryMatroid(matrix=X).dual() + sage: B = list(M.bases()) + sage: N = loads(dumps(M)) + sage: N.closure(frozenset({3})) + frozenset({3}) + sage: N.is_isomorphic(M) + True """ import sage.matroids.unpickling version = 0 @@ -5556,7 +5586,7 @@ cdef class QuaternaryMatroid(LinearMatroid): else: A = self._A # current basis ordered so matrix cols form identity matrix: - basis, _ = self._current_rows_cols() + basis = self._current_rows_cols()[0] data = (A, gs, basis, getattr(self, '__custom_name')) return sage.matroids.unpickling.unpickle_quaternary_matroid, (version, data) From 09f770d86cd9c1564075e376d4f2d072ae78b041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 14 Aug 2018 21:59:41 +0200 Subject: [PATCH 266/284] some details (pyflakes, pep, etc) --- src/sage/coding/hamming_code.py | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/sage/coding/hamming_code.py b/src/sage/coding/hamming_code.py index 5e25ba5cb13..1247ccc5081 100644 --- a/src/sage/coding/hamming_code.py +++ b/src/sage/coding/hamming_code.py @@ -12,23 +12,22 @@ """ from __future__ import absolute_import -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2016 David Lucas # # 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 .linear_code import AbstractLinearCode from sage.matrix.matrix_space import MatrixSpace from sage.schemes.projective.projective_space import ProjectiveSpace from sage.misc.cachefunc import cached_method from sage.rings.integer import Integer -from sage.rings.ring import Field -from copy import copy + class HammingCode(AbstractLinearCode): r""" @@ -78,7 +77,7 @@ def __init__(self, base_field, order): def __eq__(self, other): r""" - Tests equality of Hamming Code objects. + Test equality of Hamming Code objects. EXAMPLES:: @@ -103,10 +102,10 @@ def __hash__(self): True """ return hash((self.length(), self.dimension())) - + def _repr_(self): r""" - Returns a string representation of ``self``. + Return a string representation of ``self``. EXAMPLES:: @@ -119,7 +118,7 @@ def _repr_(self): def _latex_(self): r""" - Returns a latex representation of ``self``. + Return a latex representation of ``self``. EXAMPLES:: @@ -130,11 +129,10 @@ def _latex_(self): return "[%s, %s] \\textnormal{ Hamming Code over }%s"\ % (self.length(), self.dimension(), self.base_field()._latex_()) - @cached_method def parity_check_matrix(self): r""" - Returns a parity check matrix of ``self``. + Return a parity check matrix of ``self``. The construction of the parity check matrix in case ``self`` is not a binary code is not really well documented. @@ -155,8 +153,8 @@ def parity_check_matrix(self): n = self.length() F = self.base_field() m = n - self.dimension() - MS = MatrixSpace(F,n,m) - X = ProjectiveSpace(m-1,F) + MS = MatrixSpace(F, n, m) + X = ProjectiveSpace(m - 1, F) PFn = [list(p) for p in X.point_set(F).points()] H = MS(PFn).transpose() @@ -166,7 +164,8 @@ def parity_check_matrix(self): def minimum_distance(self): r""" - Returns the minimum distance of ``self``. + Return the minimum distance of ``self``. + It is always 3 as ``self`` is a Hamming Code. EXAMPLES:: @@ -176,6 +175,3 @@ def minimum_distance(self): 3 """ return 3 - - -####################### registration ############################### From 579e8250a431e3153b921a5d446af68ef8467912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Wed, 15 Aug 2018 12:33:16 +0200 Subject: [PATCH 267/284] Allow repeated key polynomials the comment in the code was only true for a full run of mac_lane_approximants starting from a GaussValuation. That's the code I had this originally written for. I do not think that the block is necessary (it might have been in some broken p-adics setting but if it is then, that should be fixed in the p-adics code.) --- .../rings/valuation/inductive_valuation.py | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/valuation/inductive_valuation.py b/src/sage/rings/valuation/inductive_valuation.py index b49fbec395d..fc8e3c232a2 100644 --- a/src/sage/rings/valuation/inductive_valuation.py +++ b/src/sage/rings/valuation/inductive_valuation.py @@ -30,7 +30,7 @@ """ #***************************************************************************** -# Copyright (C) 2016-2017 Julian Rüth +# Copyright (C) 2016-2018 Julian Rüth # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -712,6 +712,15 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a sage: eta2 [ Gauss valuation induced by Valuation on rational function field induced by [ Gauss valuation induced by 3-adic valuation, v(x) = 1/3 ], v(y + x) = 2/3 ] + Check that :trac:`26066` has been resolved:: + + sage: R. = QQ[] + sage: v = QQ.valuation(2) + sage: v = GaussValuation(R, v).augmentation(x+1, 1/2) + sage: f = x^4 - 30*x^2 - 75 + sage: v.mac_lane_step(f) + [[ Gauss valuation induced by 2-adic valuation, v(x + 1) = 3/4 ]] + """ G = self.domain().coerce(G) @@ -771,18 +780,6 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a assert len(F) == 1 break - if phi == self.phi(): - # a factor phi in the equivalence decomposition means that we - # found an actual factor of G, i.e., we can set - # v(phi)=infinity - # However, this should already have happened in the last step - # (when this polynomial had -infinite slope in the Newton - # polygon.) - if self.is_gauss_valuation(): # unless in the first step - pass - else: - continue - verbose("Determining the augmentation of %s for %s"%(self, phi), level=11) old_mu = self(phi) w = self.augmentation(phi, old_mu, check=False) @@ -842,7 +839,7 @@ def mac_lane_step(self, G, principal_part_bound=None, assume_squarefree=False, a assert degree_bound >= phi.degree() ret.append((w, degree_bound, multiplicities[slope], w_coefficients, new_valuations)) - assert ret + assert ret, "a MacLane step produced no augmentations" if not report_degree_bounds_and_caches: ret = [v for v,_,_,_,_ in ret] return ret From b86c9212836e5bef1dd6ca46cef7df7c886ac39f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 16 Aug 2018 15:31:21 +1000 Subject: [PATCH 268/284] Adding FinitelyGeneratedMatrixGroup_gap to finite matrix groups. --- src/sage/groups/matrix_gps/linear.py | 15 ++++++++++++++- src/sage/groups/matrix_gps/orthogonal.py | 14 +++++++++++++- src/sage/groups/matrix_gps/symplectic.py | 13 +++++++++++-- src/sage/groups/matrix_gps/unitary.py | 14 +++++++++++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 368e7041e48..224afaab704 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -63,6 +63,7 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, NamedMatrixGroup_generic, NamedMatrixGroup_gap ) from sage.categories.groups import Groups +from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap ############################################################################### @@ -277,5 +278,17 @@ def _check_matrix(self, x, *args): raise TypeError('matrix must non-zero determinant') -class LinearMatrixGroup_gap(NamedMatrixGroup_gap, LinearMatrixGroup_generic): +class LinearMatrixGroup_gap(NamedMatrixGroup_gap, LinearMatrixGroup_generic, FinitelyGeneratedMatrixGroup_gap): + r""" + The general or special linear group in GAP. + + TESTS: + + Check that :trac:`20867` is fixed:: + + sage: from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap + sage: G = GL(3,3) + sage: isinstance(G, FinitelyGeneratedMatrixGroup_gap) + True + """ pass diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index bee84f6af6d..35c8101112d 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -91,6 +91,7 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, normalize_args_invariant_form, NamedMatrixGroup_generic, NamedMatrixGroup_gap) +from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap def normalize_args_e(degree, ring, e): """ @@ -516,8 +517,19 @@ def _check_matrix(self, x, *args): raise TypeError('matrix must be orthogonal with respect to the symmetric form\n%s' %(F)) # TODO: check that quadratic form is preserved in characteristic two -class OrthogonalMatrixGroup_gap(OrthogonalMatrixGroup_generic, NamedMatrixGroup_gap): +class OrthogonalMatrixGroup_gap(OrthogonalMatrixGroup_generic, NamedMatrixGroup_gap, FinitelyGeneratedMatrixGroup_gap): + r""" + The general or special orthogonal group in GAP. + + TESTS: + Check that :trac:`20867` is fixed:: + + sage: from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap + sage: G = GO(3,3) + sage: isinstance(G, FinitelyGeneratedMatrixGroup_gap) + True + """ @cached_method def invariant_bilinear_form(self): """ diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index 783c2e4ab14..b3c5ad1b95a 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -46,7 +46,7 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, normalize_args_invariant_form, NamedMatrixGroup_generic, NamedMatrixGroup_gap) - +from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap ############################################################################### @@ -240,7 +240,7 @@ def _check_matrix(self, x, *args): raise TypeError('matrix must be symplectic with respect to the alternating form\n{}'.format(F)) -class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_gap): +class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_gap, FinitelyGeneratedMatrixGroup_gap): r""" Symplectic group in GAP. @@ -251,6 +251,15 @@ class SymplecticMatrixGroup_gap(SymplecticMatrixGroup_generic, NamedMatrixGroup_ sage: latex(Sp(4,5)) \text{Sp}_{4}(\Bold{F}_{5}) + + TESTS: + + Check that :trac:`20867` is fixed:: + + sage: from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap + sage: G = Sp(4,3) + sage: isinstance(G, FinitelyGeneratedMatrixGroup_gap) + True """ @cached_method diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index 34e8f5d8895..87dd6b22263 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -56,6 +56,7 @@ from sage.groups.matrix_gps.named_group import ( normalize_args_vectorspace, normalize_args_invariant_form, NamedMatrixGroup_generic, NamedMatrixGroup_gap ) +from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap def finite_field_sqrt(ring): @@ -433,8 +434,19 @@ def _check_matrix(self, x, *args): else: raise TypeError('matrix must be unitary with respect to the hermitian form\n{}'.format(H)) +class UnitaryMatrixGroup_gap(UnitaryMatrixGroup_generic, NamedMatrixGroup_gap, FinitelyGeneratedMatrixGroup_gap): + r""" + The general or special unitary group in GAP. + + TESTS: + + Check that :trac:`20867` is fixed:: -class UnitaryMatrixGroup_gap(UnitaryMatrixGroup_generic, NamedMatrixGroup_gap): + sage: from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_gap + sage: G = GU(3,3) + sage: isinstance(G, FinitelyGeneratedMatrixGroup_gap) + True + """ @cached_method def invariant_form(self): From b61380f42beaa7bfe7337401d7104d67b1b2aebd Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 16 Aug 2018 17:58:40 +1000 Subject: [PATCH 269/284] Fixed comment by Dima; added missing "r" to doc with latex. --- src/sage/groups/matrix_gps/linear.py | 2 +- src/sage/groups/matrix_gps/unitary.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 224afaab704..280938ad5df 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -71,7 +71,7 @@ ############################################################################### def GL(n, R, var='a'): - """ + r""" Return the general linear group. The general linear group `GL( d, R )` consists of all `d \times d` diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index 87dd6b22263..357d0403d39 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -157,8 +157,8 @@ def GU(n, R, var='a', invariant_form=None): For a finite field the matrices that preserve a sesquilinear form over `F_q` live over `F_{q^2}`. So ``GU(n,q)`` for - integer ``q`` constructs the matrix group over the base ring - ``GF(q^2)``. + a prime power ``q`` constructs the matrix group over the base + ring ``GF(q^2)``. .. NOTE:: @@ -271,8 +271,8 @@ def SU(n, R, var='a', invariant_form=None): For a finite field the matrices that preserve a sesquilinear form over `F_q` live over `F_{q^2}`. So ``SU(n,q)`` for - integer ``q`` constructs the matrix group over the base ring - ``GF(q^2)``. + a prime power ``q`` constructs the matrix group over the base + ring ``GF(q^2)``. .. NOTE:: From 4e9896a293b23598b971d5b2c7a3f02dc16c971b Mon Sep 17 00:00:00 2001 From: Vincent Klein Date: Thu, 16 Aug 2018 16:19:28 +0200 Subject: [PATCH 270/284] Trac #26072: Fix ``inlist.pyx`` doctests for py3 --- src/sage/stats/intlist.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/stats/intlist.pyx b/src/sage/stats/intlist.pyx index 0fa9262cfb9..48bcfd03672 100644 --- a/src/sage/stats/intlist.pyx +++ b/src/sage/stats/intlist.pyx @@ -572,13 +572,13 @@ def unpickle_intlist_v1(bytes v, Py_ssize_t n): sage: v = stats.IntList([1,2,3]) sage: s = v.__reduce__()[1][0] - sage: type(s) - <... 'str'> + sage: type(s) == type(b'') + True sage: sage.stats.intlist.unpickle_intlist_v1(s, 3) [1, 2, 3] sage: sage.stats.intlist.unpickle_intlist_v1(s+s,6) [1, 2, 3, 1, 2, 3] - sage: sage.stats.intlist.unpickle_intlist_v1('',0) + sage: sage.stats.intlist.unpickle_intlist_v1(b'',0) [] """ cdef IntList t = new_int_list(n) From 0827d8762ef057b142e66decb39c78d52cae8324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 17 Aug 2018 10:41:57 +0200 Subject: [PATCH 271/284] trac 26082 switching pip using sage-python23 --- src/bin/sage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/sage b/src/bin/sage index 8e18776480b..19ca81c6bfd 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -587,7 +587,7 @@ fi if [ "$1" = '-pip' -o "$1" = '--pip' ]; then shift - exec pip "$@" + exec sage-python23 -m pip "$@" fi if [ "$1" = '-fix-pkg-checksums' -o "$1" = '--fix-pkg-checksums' ]; then From ac6201a942aa19b6181c463cac8e9588e92b3d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 17 Aug 2018 15:39:32 +0200 Subject: [PATCH 272/284] Timeouts DO happen on CircleCI I am not sure when that changed. The builds used to take 4:30 hours and now the take almost exactly 5 hours. Sometimes more, sometimes less. --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 635ea14f16e..2b425783ce6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,8 @@ # This file configures automatic builds of Sage on [CircleCI](https://circleci.com). # To make the build time not too excessive, we seed the build cache with # sagemath/sagemath-dev:develop. When basic SPKGs change, this does not help much, -# still the full build does usually not exceed CircleCI's limits for open -# source projcets (five hours on 2 vCPUs as of early 2018.) +# as the full build will often exceed CircleCI's limits for open source +# projcets (five hours on 2 vCPUs as of early 2018.) # You might want to try to build locally or with GitLab CI, see # `.gitlab-ci.yml` for more options. From efde68752f69d0453b1fe29bb8af75f652a632fa Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 17 Aug 2018 22:36:41 +0200 Subject: [PATCH 273/284] Handle precision correctly in DefPolyConversion --- .../rings/padics/padic_extension_generic.py | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 13285023169..1a72bd86321 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -25,6 +25,7 @@ from sage.rings.number_field.number_field_base import NumberField from sage.rings.number_field.order import Order from sage.rings.rational_field import QQ +from sage.rings.infinity import Infinity from sage.structure.richcmp import op_EQ from functools import reduce from sage.categories.morphism import Morphism @@ -592,17 +593,32 @@ def _call_(self, x): sage: z = W.random_element() sage: repr(W.change(print_mode='digits')(z)) '...20112102111011011200001212210222202220100111100200011222122121202100210120010120' + + TESTS: + + We check that :trac:`25990` has been resolved:: + + sage: R.
    = Zp(2).extension(x^3 - 2) + sage: K = R.fraction_field() + sage: u = K(1,10); u + 1 + O(a^10) + sage: R(u) + 1 + O(a^10) + + sage: R(K(0)) + 0 + """ S = self.codomain() Sbase = S.base_ring() L = x.polynomial().list() - if L and not (len(L) == 1 and L[0].is_zero()): - return S([Sbase(c) for c in L]) - # Inexact zeros need to be handled separately - elif isinstance(x.parent(), pAdicExtensionGeneric): - return S(0, x.precision_absolute()) - else: - return S(0) + while L and L[-1].is_zero(): + del L[-1] + if isinstance(x.parent(), pAdicExtensionGeneric): + absprec = x.precision_absolute() + if absprec is not Infinity: + return S([Sbase(c) for c in L], absprec) + return S([Sbase(c) for c in L]) def _call_with_args(self, x, args=(), kwds={}): """ @@ -616,22 +632,37 @@ def _call_with_args(self, x, args=(), kwds={}): sage: z = W.random_element() sage: repr(W.change(print_mode='digits')(z, absprec=8)) # indirect doctest '...20010120' + + TESTS:: + + sage: R. = Zp(2).extension(x^3 - 2) + sage: K = R.fraction_field() + sage: R(K(0), 10) + O(a^10) + + sage: R(K(0,10), Infinity) + O(a^10) + + sage: R(K(0,10), Infinity, absprec=30) + Traceback (most recent call last): + ... + TypeError: _call_with_args() got multiple values for keyword argument 'absprec' + """ S = self.codomain() Sbase = S.base_ring() L = x.polynomial().list() - if L and not (len(L) == 1 and L[0].is_zero()): - return S([Sbase(c) for c in L], *args, **kwds) - # Inexact zeros need to be handled separately - elif isinstance(x.parent(), pAdicExtensionGeneric): + while L and L[-1].is_zero(): + del L[-1] + if isinstance(x.parent(), pAdicExtensionGeneric): if args: if 'absprec' in kwds: raise TypeError("_call_with_args() got multiple values for keyword argument 'absprec'") absprec = args[0] args = args[1:] else: - absprec = kwds.pop('absprec',x.precision_absolute()) + absprec = kwds.pop('absprec', Infinity) absprec = min(absprec, x.precision_absolute()) - return S(0, absprec, *args, **kwds) - else: - return S(0, *args, **kwds) + if absprec is not Infinity: + return S([Sbase(c) for c in L], absprec, *args, **kwds) + return S([Sbase(c) for c in L], *args, **kwds) From b4ed23677ddbb0cc8fc0f2e68b9cbc68c25588a0 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 17 Aug 2018 23:58:15 +0200 Subject: [PATCH 274/284] Handle precision even better --- src/sage/rings/padics/padic_extension_generic.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 1a72bd86321..336d460ec61 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -605,6 +605,11 @@ def _call_(self, x): sage: R(u) 1 + O(a^10) + sage: u += a^4 + a^5 + a^7 + a^8; u + 1 + a^4 + a^5 + a^7 + a^8 + O(a^10) + sage: R(u) + 1 + a^4 + a^5 + a^7 + a^8 + O(a^10) + sage: R(K(0)) 0 @@ -617,7 +622,7 @@ def _call_(self, x): if isinstance(x.parent(), pAdicExtensionGeneric): absprec = x.precision_absolute() if absprec is not Infinity: - return S([Sbase(c) for c in L], absprec) + return S([Sbase(c).lift_to_precision() for c in L], absprec) return S([Sbase(c) for c in L]) def _call_with_args(self, x, args=(), kwds={}): @@ -664,5 +669,5 @@ def _call_with_args(self, x, args=(), kwds={}): absprec = kwds.pop('absprec', Infinity) absprec = min(absprec, x.precision_absolute()) if absprec is not Infinity: - return S([Sbase(c) for c in L], absprec, *args, **kwds) + return S([Sbase(c).lift_to_precision() for c in L], absprec, *args, **kwds) return S([Sbase(c) for c in L], *args, **kwds) From d8da8742bd98efe1695397157c47378589650078 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jori=20M=C3=A4ntysalo?= Date: Sat, 18 Aug 2018 08:26:53 +0300 Subject: [PATCH 275/284] Faster algorithm. --- src/sage/combinat/posets/lattices.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 4e2466370df..23f4bbecbe3 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -1772,24 +1772,19 @@ def is_sectionally_complemented(self, certificate=False): if not certificate and not self.is_atomic(): return False - n = self.cardinality() H = self._hasse_diagram mt = H._meet - jn = H._join - bottom = 0 - - for top in range(n): - interval = H.principal_order_ideal(top) - for e in interval: - for f in interval: - if mt[e, f] == bottom and jn[e, f] == top: - break - else: - if certificate: - return (False, (self._vertex_to_element(top), - self._vertex_to_element(e))) - return False - + n = H.order()-1 + for e in range(2, n+1): + t = n + for lc in H.neighbors_in(e): + t = mt[t, lc] + if t == 0: + break + else: + if certificate: + return (False, (self[e], self[t])) + return False return (True, None) if certificate else True def breadth(self, certificate=False): From 61e6555e7faaa8a78ee736c76b3a0843284cc730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 18 Aug 2018 08:36:35 +0200 Subject: [PATCH 276/284] py3: hash for Cycle index ring --- src/sage/combinat/species/series.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/species/series.py b/src/sage/combinat/species/series.py index 2a5f966432d..1c79233d7fe 100644 --- a/src/sage/combinat/species/series.py +++ b/src/sage/combinat/species/series.py @@ -43,8 +43,9 @@ from sage.categories.all import Rings from sage.structure.element import Element, parent, AlgebraElement + class LazyPowerSeriesRing(Algebra): - def __init__(self, R, element_class = None, names=None): + def __init__(self, R, element_class=None, names=None): """ TESTS:: @@ -129,6 +130,22 @@ def __ne__(self, other): """ return not (self == other) + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: LQ = LazyPowerSeriesRing(QQ) + sage: LZ = LazyPowerSeriesRing(ZZ) + sage: hash(LQ) == hash(LQ) + True + sage: hash(LZ) == hash(LQ) + False + """ + # with a random number, so that the hash is not that of the base ring + return hash((16079305, self.base_ring())) + def _coerce_impl(self, x): """ EXAMPLES:: From c77483393efcd40f484d696cd32e8399f1310dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 18 Aug 2018 09:12:31 +0200 Subject: [PATCH 277/284] py3: adding hash for smooth character groups --- src/sage/modular/local_comp/smoothchar.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/local_comp/smoothchar.py b/src/sage/modular/local_comp/smoothchar.py index e1fb18b38e0..68b7ee19ba2 100644 --- a/src/sage/modular/local_comp/smoothchar.py +++ b/src/sage/modular/local_comp/smoothchar.py @@ -425,9 +425,26 @@ def __ne__(self, other): """ return not (self == other) + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: from sage.modular.local_comp.smoothchar import SmoothCharacterGroupQp + sage: G = SmoothCharacterGroupQp(3, QQ) + sage: hash(G) == hash(SmoothCharacterGroupQp(3, QQ[I])) + False + sage: hash(G) == hash(SmoothCharacterGroupQp(7, QQ)) + False + sage: hash(G) == hash(SmoothCharacterGroupQp(3, QQ)) + True + """ + return hash((self.prime(), self.number_field(), self.base_ring())) + def _coerce_map_from_(self, other): r""" - Return True if self has a canonical coerce map from other. + Return ``True`` if ``self`` has a canonical coerce map from ``other``. EXAMPLES:: From 0e90f15ea53276882092398e54db4437de3492a1 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sat, 18 Aug 2018 08:58:33 +0000 Subject: [PATCH 278/284] removed unneeded import, cf pyflakes patchbot plugin --- src/sage/groups/matrix_gps/linear.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 280938ad5df..1ef21d696a6 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -241,7 +241,6 @@ def SL(n, R, var='a'): cat = Groups() name = 'Special Linear Group of degree {0} over {1}'.format(degree, ring) ltx = 'SL({0}, {1})'.format(degree, latex(ring)) - from sage.libs.gap.libgap import libgap try: cmd = 'SL({0}, {1})'.format(degree, ring._gap_init_()) return LinearMatrixGroup_gap(degree, ring, True, name, ltx, cmd, From 794f02d3c7fffa9db10feb851c3e145ebcaa33d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 19 Aug 2018 09:16:34 +0200 Subject: [PATCH 279/284] py3: hash for subsets --- src/sage/combinat/subset.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/subset.py b/src/sage/combinat/subset.py index 12b648257e6..bb5b0879231 100644 --- a/src/sage/combinat/subset.py +++ b/src/sage/combinat/subset.py @@ -290,6 +290,21 @@ def __ne__(self, other): """ return not self == other + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: hash(Subsets([0,1,2])) == hash(Subsets([1,2,3])) + False + sage: hash(Subsets([0,1,2])) == hash(Subsets([0,1,2])) + True + sage: hash(Subsets([0,1,2])) == hash(Subsets([0,1,2],2)) + False + """ + return hash(self._s) + def _repr_(self): """ TESTS:: @@ -615,6 +630,19 @@ def __ne__(self, other): """ return not self == other + def __hash__(self): + """ + Return the hash of ``self``. + + TESTS:: + + sage: hash(Subsets(5,3)) == hash(Subsets(5,3)) + True + sage: hash(Subsets(4,2)) == hash(Subsets(5,2)) + False + """ + return hash((self._s, self._k)) + def cardinality(self): """ EXAMPLES:: @@ -1376,4 +1404,3 @@ def _element_constructor_(self, x): [(), (0,), (1,), (2,), (0, 1), (0, 2), (1, 2), (0, 1, 2)] """ return self.element_class(sorted(set(x))) - From 715f0994633176b2d697498992825eb6ce9633b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 19 Aug 2018 10:01:59 +0200 Subject: [PATCH 280/284] py3: fixing hash for spaces of modular symbols --- src/sage/modular/modsym/ambient.py | 50 ++++++++---------------------- 1 file changed, 13 insertions(+), 37 deletions(-) diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index bd63ef84a2d..f78bca3d10c 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -126,6 +126,19 @@ class ModularSymbolsAmbient(ModularSymbolsSpace, hecke.AmbientHeckeModule): - ``custom_init`` - a function that is called with self as input before any computations are done using self; this could be used to set a custom modular symbols presentation. + + TESTS:: + + sage: ModularSymbols(11,2) == ModularSymbols(11,2) + True + sage: ModularSymbols(11,2) == ModularSymbols(11,4) + False + sage: ModularSymbols(11,2) != ModularSymbols(11,2) + False + sage: ModularSymbols(11,2) != ModularSymbols(11,4) + True + sage: hash(ModularSymbols(11,2)) != hash(ModularSymbols(11,4)) + True """ def __init__(self, group, weight, sign, base_ring, character=None, custom_init=None, category=None): @@ -182,43 +195,6 @@ def __init__(self, group, weight, sign, base_ring, hecke.AmbientHeckeModule.__init__(self, base_ring, rank, group.level(), weight, category=category) - def __eq__(self, other): - """ - Check that ``self`` is equal to ``other``. - - EXAMPLES:: - - sage: ModularSymbols(11,2) == ModularSymbols(11,2) - True - sage: ModularSymbols(11,2) == ModularSymbols(11,4) - False - """ - if not isinstance(other, ModularSymbolsSpace): - return False - - if isinstance(other, ModularSymbolsAmbient): - return (self.group() == other.group() and - self.weight() == other.weight() and - self.sign() == other.sign() and - self.base_ring() == other.base_ring() and - self.character() == other.character()) - - return (self == other.ambient_hecke_module() and - self.free_module() == other.free_module()) - - def __ne__(self, other): - """ - Check that ``self`` is not equal to ``other``. - - EXAMPLES:: - - sage: ModularSymbols(11,2) != ModularSymbols(11,2) - False - sage: ModularSymbols(11,2) != ModularSymbols(11,4) - True - """ - return not (self == other) - def new_submodule(self, p=None): r""" Returns the new or `p`-new submodule of this modular symbols ambient space. From f555b9d183a9ffb1e334c9350011cc2066918a5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 19 Aug 2018 14:34:00 +0200 Subject: [PATCH 281/284] details in plural rings ; hash for noncommutative ideals --- src/sage/rings/ideal_monoid.py | 18 ++++++++++++ src/sage/rings/noncommutative_ideals.pyx | 19 +++++++++++-- src/sage/rings/polynomial/plural.pyx | 36 +++++++++++++----------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/ideal_monoid.py b/src/sage/rings/ideal_monoid.py index 2d050a3bd2e..45b3fee2172 100644 --- a/src/sage/rings/ideal_monoid.py +++ b/src/sage/rings/ideal_monoid.py @@ -165,3 +165,21 @@ def __ne__(self, other): False """ return not (self == other) + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: R = QuadraticField(-23, 'a') + sage: M = R.ideal_monoid() + sage: hash(M) == hash(QQ) + False + sage: hash(M) == 17 + False + sage: hash(M) == hash(R.ideal_monoid()) + True + """ + # uses a random number, to have a distinct hash + return hash((1580963238588124931699, self.ring())) diff --git a/src/sage/rings/noncommutative_ideals.pyx b/src/sage/rings/noncommutative_ideals.pyx index cd5cfb54365..9b3093d025c 100644 --- a/src/sage/rings/noncommutative_ideals.pyx +++ b/src/sage/rings/noncommutative_ideals.pyx @@ -271,7 +271,6 @@ class Ideal_nc(Ideal_generic): False sage: IR == [A.1+A.2,A.1^2]*A True - """ if not isinstance(right, Ideal_nc): return False @@ -298,10 +297,26 @@ class Ideal_nc(Ideal_generic): True sage: IR != [A.1+A.2,A.1^2]*A False - """ return not self.__eq__(right) + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: A = SteenrodAlgebra(2) + sage: IR = [A.1+A.2,A.1^2]*A + sage: IL = A*[A.1+A.2,A.1^2] + sage: IT = A*[A.1+A.2,A.1^2]*A + sage: hash(IT) == hash(IL) + False + sage: hash(IR) == hash([A.1^2,A.1+A.2]*A) + True + """ + return hash((self.parent(), self.__side, frozenset(self.gens()))) + def side(self): """ Return a string that describes the sidedness of this ideal. diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index d5439f7f081..2b1b4ad3166 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -105,6 +105,7 @@ TESTS:: from __future__ import print_function, absolute_import from cysignals.memory cimport sig_malloc, sig_free +from sage.cpython.string cimport bytes_to_str from sage.categories.algebras import Algebras @@ -136,6 +137,7 @@ from sage.structure.parent cimport Parent from sage.structure.parent_gens cimport ParentWithGens from sage.rings.polynomial.term_order import TermOrder + class G_AlgFactory(UniqueFactory): """ A factory for the creation of g-algebras as unique parents. @@ -173,8 +175,8 @@ class G_AlgFactory(UniqueFactory): return NCPolynomialRing_plural(base_ring, names, c, d, order, category, check) - def create_key_and_extra_args(self, base_ring, c,d, names=None, order=None, - category=None,check=None): + def create_key_and_extra_args(self, base_ring, c, d, names=None, order=None, + category=None, check=None): """ Create a unique key for g-algebras. @@ -214,15 +216,17 @@ class G_AlgFactory(UniqueFactory): d.set_immutable() # Get the correct category - category=check_default_category(Algebras(base_ring),category) + category = check_default_category(Algebras(base_ring), category) # Extra arg if check is None: - return (base_ring,names,c,d,order,category),{} - return (base_ring,names,c,d,order,category),{'check':check} + return (base_ring, names, c, d, order, category), {} + return (base_ring, names, c, d, order, category), {'check':check} + g_Algebra = G_AlgFactory('sage.rings.polynomial.plural.g_Algebra') + cdef class NCPolynomialRing_plural(Ring): """ A non-commutative polynomial ring. @@ -245,7 +249,7 @@ cdef class NCPolynomialRing_plural(Ring): True """ - def __init__(self, base_ring, names, c, d, order, category, check = True): + def __init__(self, base_ring, names, c, d, order, category, check=True): """ Construct a noncommutative polynomial G-algebra subject to the following conditions: @@ -371,7 +375,7 @@ cdef class NCPolynomialRing_plural(Ring): sage: loads(dumps(g)) == g True """ - return g_Algebra, (self.base_ring(),self._c,self._d, + return g_Algebra, (self.base_ring(), self._c, self._d, self.variable_names(), self.term_order(), self.category()) @@ -473,7 +477,6 @@ cdef class NCPolynomialRing_plural(Ring): if(_ring != currRing): rChangeCurrRing(_ring) - if isinstance(element, NCPolynomial_plural): if element.parent() is self: @@ -508,9 +511,9 @@ cdef class NCPolynomialRing_plural(Ring): # Accepting int elif isinstance(element, int): if isinstance(base_ring, FiniteField_prime_modn): - _p = p_ISet(int(element) % _ring.cf.ch,_ring) + _p = p_ISet(int(element) % _ring.cf.ch, _ring) else: - _n = sa2si(base_ring(element),_ring) + _n = sa2si(base_ring(element), _ring) _p = p_NSet(_n, _ring) # and longs @@ -548,8 +551,6 @@ cdef class NCPolynomialRing_plural(Ring): if self.base_ring().has_coerce_map_from(S): return True - - def __hash__(self): """ Return a hash for this noncommutative ring, that is, a hash of the string @@ -669,10 +670,10 @@ cdef class NCPolynomialRing_plural(Ring): sage: y*x -x*y """ -#TODO: print the relations - varstr = ", ".join([ rRingVar(i,self._ring) for i in range(self.__ngens) ]) - return "Noncommutative Multivariate Polynomial Ring in %s over %s, nc-relations: %s"%(varstr,self.base_ring(), self.relations()) - + # TODO: print the relations + varstr = ", ".join([bytes_to_str(rRingVar(i, self._ring)) + for i in range(self.__ngens) ]) + return "Noncommutative Multivariate Polynomial Ring in %s over %s, nc-relations: %s" % (varstr, self.base_ring(), self.relations()) def _ringlist(self): """ @@ -700,7 +701,7 @@ cdef class NCPolynomialRing_plural(Ring): result = ringlist(self, ring=self) return result - def relations(self, add_commutative = False): + def relations(self, add_commutative=False): """ Return the relations of this g-algebra. @@ -1315,6 +1316,7 @@ cdef class NCPolynomialRing_plural(Ring): M.append(new_NCP(self, p_Copy(tempvector,_ring))) return M + def unpickle_NCPolynomial_plural(NCPolynomialRing_plural R, d): """ Auxiliary function to unpickle a non-commutative polynomial. From 1b3371c51467148d63a62134fbc577504feb9f2b Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Wed, 22 Aug 2018 17:01:41 +0200 Subject: [PATCH 282/284] PARI databases have no dependencies --- build/pkgs/pari_elldata/dependencies | 2 +- build/pkgs/pari_galpol/dependencies | 2 +- build/pkgs/pari_seadata/dependencies | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/pari_elldata/dependencies b/build/pkgs/pari_elldata/dependencies index c1b713883fe..3546cda4614 100644 --- a/build/pkgs/pari_elldata/dependencies +++ b/build/pkgs/pari_elldata/dependencies @@ -1,4 +1,4 @@ -| $(SAGERUNTIME) +# no dependencies ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pari_galpol/dependencies b/build/pkgs/pari_galpol/dependencies index c1b713883fe..3546cda4614 100644 --- a/build/pkgs/pari_galpol/dependencies +++ b/build/pkgs/pari_galpol/dependencies @@ -1,4 +1,4 @@ -| $(SAGERUNTIME) +# no dependencies ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pari_seadata/dependencies b/build/pkgs/pari_seadata/dependencies index c1b713883fe..3546cda4614 100644 --- a/build/pkgs/pari_seadata/dependencies +++ b/build/pkgs/pari_seadata/dependencies @@ -1,4 +1,4 @@ -| $(SAGERUNTIME) +# no dependencies ---------- All lines of this file are ignored except the first. From dd20582d28372ffc38d16444b3f81d5c2885289a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20R=C3=BCth?= Date: Fri, 24 Aug 2018 17:20:53 +0200 Subject: [PATCH 283/284] Set OPENBLAS_NUM_THREADS=1 to make sage -tp fast again. --- src/bin/sage-env | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/bin/sage-env b/src/bin/sage-env index db2640fa701..e30ed07f588 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -615,6 +615,16 @@ SAGE_NUM_THREADS_PARALLEL=${sage_num_threads_array[1]} export SAGE_NUM_THREADS export SAGE_NUM_THREADS_PARALLEL +# Multithreading in OpenBLAS does not seem to play well with Sage's attempts to +# spawn new processes, see #26118. Apparently, OpenBLAS sets the thread +# affinity and, e.g., parallel doctest jobs, remain on the same core. +# Disabling that thread-affinity with OPENBLAS_MAIN_FREE=1 leads to hangs in +# some computations. +# So we disable OpenBLAS' threading completely; we might loose some performance +# here but strangely the opposite seems to be the case. Note that callers such +# as LinBox use a single-threaded OpenBLAS anyway. +export OPENBLAS_NUM_THREADS=1 + if [ "$MAKE" = "" ]; then MAKE="make" fi From 8b4f9a08855bbc92f09fcc7de15c86459d84ede6 Mon Sep 17 00:00:00 2001 From: Volker Braun Date: Sun, 26 Aug 2018 00:44:41 +0200 Subject: [PATCH 284/284] Updated SageMath version to 8.4.beta2 --- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/VERSION.txt b/VERSION.txt index 98049bded4d..708b78d2f1d 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.4.beta1, Release Date: 2018-08-14 +SageMath version 8.4.beta2, Release Date: 2018-08-25 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 8d046fa0eee..73d467a77bd 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=667d7519a357ef427d102680ec7c6cdd3eecfa31 -md5=7481b39886ab0931671be11b2c0a7aba -cksum=1575778832 +sha1=f70c9bf20fd9362f4e8b9d57b1e657313583a7d6 +md5=55828f7543d5316724e3ce539e127a43 +cksum=4162376827 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 3d242f55505..bbb81cf1257 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -278 +279 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 01b49cf352f..fbe3f80220c 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='8.4.beta1' -SAGE_RELEASE_DATE='2018-08-14' -SAGE_VERSION_BANNER='SageMath version 8.4.beta1, Release Date: 2018-08-14' +SAGE_VERSION='8.4.beta2' +SAGE_RELEASE_DATE='2018-08-25' +SAGE_VERSION_BANNER='SageMath version 8.4.beta2, Release Date: 2018-08-25' diff --git a/src/sage/version.py b/src/sage/version.py index b0172043197..aaf10ef694f 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '8.4.beta1' -date = '2018-08-14' -banner = 'SageMath version 8.4.beta1, Release Date: 2018-08-14' +version = '8.4.beta2' +date = '2018-08-25' +banner = 'SageMath version 8.4.beta2, Release Date: 2018-08-25'