diff --git a/.ci/build-docker.sh b/.ci/build-docker.sh index 321f92ddba4..24c3405e39b 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 "WITH_PYTHON=${WITH_PYTHON}" --build-arg "MAKEFLAGS=${MAKEFLAGS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg "MAKEFLAGS_DOCBUILD=${MAKEFLAGS}" --build-arg "SAGE_NUM_THREADS_DOCBUILD=${SAGE_NUM_THREADS_DOCBUILD}" --build-arg ARTIFACT_BASE=$ARTIFACT_BASE $@ +--build-arg "--build-arg "MAKEFLAGS=${MAKEFLAGS}" --build-arg "SAGE_NUM_THREADS=${SAGE_NUM_THREADS}" --build-arg "MAKEFLAGS_DOCBUILD=${MAKEFLAGS}" --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/.homebrew-build-env b/.homebrew-build-env index 2db3df54034..ccaab934ba5 100644 --- a/.homebrew-build-env +++ b/.homebrew-build-env @@ -2,7 +2,7 @@ # that activate keg-only homebrew package installations HOMEBREW=`brew --prefix` || return 1 -for l in gettext; do +for l in gettext bzip2; do if [ -d "$HOMEBREW/opt/$l/bin" ]; then PATH="$HOMEBREW/opt/$l/bin:$PATH" fi @@ -23,7 +23,7 @@ export PKG_CONFIG_PATH LIBRARY_PATH="$HOMEBREW/lib$LIBRARY_PATH" [ -z "$CPATH" ] || CPATH=":${CPATH}" CPATH="$HOMEBREW/include$CPATH" -for l in readline ; do +for l in readline bzip2; do if [ -d "$HOMEBREW/opt/$l/lib" ]; then LIBRARY_PATH="$HOMEBREW/opt/$l/lib:$LIBRARY_PATH" fi diff --git a/README.md b/README.md index 62d99fb397e..f22a6e96c46 100644 --- a/README.md +++ b/README.md @@ -218,7 +218,7 @@ Guide](https://doc.sagemath.org/html/en/installation). ``ExtUtils::MakeMaker``), `ranlib`, `git`, `tar`, `bc` * Any version of `python` (full installation including `urllib`), - but ideally version 3.7.x, which will avoid having to build Sage's + but ideally version 3.7.x or 3.8.x, which will avoid having to build Sage's own copy of Python 3. We have collected lists of system packages that provide these build diff --git a/VERSION.txt b/VERSION.txt index 77511d19eb9..50c1a7ab3c0 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.2.beta13, Release Date: 2020-09-21 +SageMath version 9.2.beta14, Release Date: 2020-09-30 diff --git a/bootstrap b/bootstrap index 0a34d892258..4865c2053b7 100755 --- a/bootstrap +++ b/bootstrap @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh ######################################################################## # Regenerate auto-generated files (e.g. configure) @@ -17,6 +17,9 @@ # will download http://host/path/configure-$CONFVERSION.tar.gz to # upstream/configure-$CONFVERSION.tar.gz. This is used by the buildbot # to download tarballs that are not published. +# +# The -q (quiet) flag hides all "informational" output. +# ######################################################################## # Set SAGE_ROOT to the path to this file and then cd into it @@ -66,7 +69,9 @@ install_config_rpath() { return 179 fi - echo "bootstrap:$LINENO: installing 'config/config.rpath'" + if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo "bootstrap:$LINENO: installing 'config/config.rpath'" + fi cp "$config_rpath" config/ } @@ -97,10 +102,24 @@ SAGE_SPKG_CONFIGURE_$(echo ${pkgname} | tr '[a-z]' '[A-Z]')" done echo "$spkg_configures" >> m4/sage_spkg_configures.m4 - SAGE_ROOT="$SAGE_ROOT" src/doc/bootstrap && \ + # Default to no filter if "-q" was not passed. + QUIET_SED_FILTER="" + if [ "${BOOTSTRAP_QUIET}" = "yes" ]; then + # Otherwise, this filters the expected output from automake. + QUIET_SED_FILTER='/configure\.ac:[0-9][0-9]*: installing /d' + fi + + # The insanity with automake's descriptors is intended to filter + # ONLY stderr, and to re-output the results back to stderr leaving + # stdout alone. Basically we swap the two descriptors using a + # third, filter, and then swap them back. + BOOTSTRAP_QUIET="${BOOTSTRAP_QUIET}" \ + SAGE_ROOT="$SAGE_ROOT" \ + src/doc/bootstrap && \ install_config_rpath && \ aclocal -I m4 && \ - automake --add-missing --copy build/make/Makefile-auto && \ + automake --add-missing --copy build/make/Makefile-auto 3>&1 1>&2 2>&3 \ + | sed "${QUIET_SED_FILTER}" 3>&1 1>&2 2>&3 && \ autoconf st=$? @@ -110,7 +129,7 @@ SAGE_SPKG_CONFIGURE_$(echo ${pkgname} | tr '[a-z]' '[A-Z]')" 179|16|63|127) # install_config_rpath failed|no m4 for pkg-config|autotools not installed|or version too old if [ $DOWNLOAD = yes ]; then echo >&2 "Bootstrap failed, downloading required files instead." - bootstrap-download || exit $? + bootstrap_download || exit $? else if [ $st -eq 127 ]; then verb="install" @@ -129,8 +148,11 @@ SAGE_SPKG_CONFIGURE_$(echo ${pkgname} | tr '[a-z]' '[A-Z]')" } # Bootstrap by downloading the auto-generated files -bootstrap-download () { - sage-download-file configure-$CONFVERSION.tar.gz +bootstrap_download () { + SAGE_DL_LOGLEVEL="" + [ "${BOOTSTRAP_QUIET}" = "yes" ] && SAGE_DL_LOGLEVEL="--log=WARNING" + sage-download-file ${SAGE_DL_LOGLEVEL} configure-$CONFVERSION.tar.gz + if [ $? -ne 0 ]; then echo >&2 "Error: downloading configure-$CONFVERSION.tar.gz failed" exit 1 @@ -156,22 +178,46 @@ save () { NEWCONFVERSION=`git rev-parse HEAD` NEWCONFBALL="upstream/configure-$NEWCONFVERSION.tar.gz" - + # Create configure tarball - echo "Creating $NEWCONFBALL..." + if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo "Creating $NEWCONFBALL..." + fi mkdir -p upstream - tar zcf "$NEWCONFBALL" configure config/* build/make/Makefile-auto.in src/doc/en/installation/*.txt src/doc/en/reference/spkg/*.rst src/doc/en/reference/repl/*.txt - + tar zcf "$NEWCONFBALL" \ + configure \ + config/* \ + build/make/Makefile-auto.in \ + src/doc/en/installation/*.txt \ + src/doc/en/reference/spkg/*.rst \ + src/doc/en/reference/repl/*.txt + # Update version echo "$NEWCONFVERSION" >$PKG/package-version.txt - + # Compute checksum - ./sage --package fix-checksum configure + if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + ./sage --package fix-checksum configure + else + # Hide the "Updating checksum..." message + ./sage --package fix-checksum configure > /dev/null + fi } usage () { - echo >&2 "Usage: $0 [-d|-D|-s] [-u ] [-h]" + echo >&2 "Usage: $0 [-d|-D|-s] [-u ] [-h] [-q]" + echo >&2 "" + echo >&2 "Options:" + echo >&2 " -d fall back to downloading (released versions only)" + echo >&2 " or using a pre-generated configure script" + echo >&2 " -D download and use a pre-generated configure script" + echo >&2 " (released versions only); overrides -d" + echo >&2 " -s save the generated configure script under upstream/" + echo >&2 " for later use with -d or -D" + echo >&2 " -u like -D, but downloads from the specified base URL" + echo >&2 " -h display this help and exit" + echo >&2 " -q hide informational output (be quiet)" } @@ -180,7 +226,8 @@ SAVE=no DOWNLOAD=no ALWAYSDOWNLOAD=no CONFTARBALL_URL="" -while getopts "Ddshu:" OPTION +BOOTSTRAP_QUIET=no +while getopts "Ddsu:hq" OPTION do case "$OPTION" in D) ALWAYSDOWNLOAD=yes; DOWNLOAD=yes;; @@ -188,6 +235,7 @@ do s) SAVE=yes;; u) CONFTARBALL_URL="$OPTARG"; ALWAYSDOWNLOAD=yes; DOWNLOAD=yes;; h) usage; exit 0;; + q) BOOTSTRAP_QUIET=yes;; ?) usage; exit 2;; esac done @@ -200,26 +248,34 @@ if [ $DOWNLOAD$SAVE = yesyes ]; then fi # Start cleanly (it's not a problem if this fails) -$MAKE bootstrap-clean 2>/dev/null +# POSIX supports two separate incompatible formats for the MAKEFLAGS +# variable, so instead of guessing, we simply define our own variable +# to optionally pass an "-s" (silent) flag to Make. +MAKE_SILENT="" +[ "${BOOTSTRAP_QUIET}" = "yes" ] && MAKE_SILENT="-s" +$MAKE ${MAKE_SILENT} bootstrap-clean 2>/dev/null mkdir config 2>/dev/null # If Sage has not been built yet, this will fail due to a missing -# sage-env-config. We just ignore that error. -source src/bin/sage-env-config 2>/dev/null -source src/bin/sage-env 2>/dev/null - +# sage-env-config. +if [ -f src/bin/sage-env-config ]; then + . src/bin/sage-env-config + . src/bin/sage-env +fi if [ $ALWAYSDOWNLOAD = yes ]; then if [ -n "$CONFTARBALL_URL" ]; then URL="$CONFTARBALL_URL"/configure-$CONFVERSION.tar.gz - sage-download-file "$URL" upstream/configure-$CONFVERSION.tar.gz + SAGE_DL_LOGLEVEL="" + [ "${BOOTSTRAP_QUIET}" = "yes" ] && SAGE_DL_LOGLEVEL="--log=WARNING" + sage-download-file ${SAGE_DL_LOGLEVEL} "$URL" upstream/configure-$CONFVERSION.tar.gz if [ $? -ne 0 ]; then echo >&2 "Error: downloading configure-$CONFVERSION.tar.gz from $CONFTARBALL_URL failed" exit 1 fi echo >&2 "Downloaded configure-$CONFVERSION.tar.gz from $CONFTARBALL_URL " else - bootstrap-download || exit $? + bootstrap_download || exit $? fi else bootstrap diff --git a/build/bin/sage-python23 b/build/bin/sage-python23 index 83a5668def4..99df0a2596f 100755 --- a/build/bin/sage-python23 +++ b/build/bin/sage-python23 @@ -11,11 +11,7 @@ # using for installing Python packages if at all possible. -if [ "$SAGE_PYTHON3" = yes ]; then - PYTHON="$SAGE_LOCAL/bin/python3" -else - PYTHON="$SAGE_LOCAL/bin/python2" -fi +PYTHON="$SAGE_LOCAL/bin/python3" # Check that Python is actually installed and issue an error message if not--in # particular if this was run from an spkg-install before Python is installed diff --git a/build/bin/sage-site b/build/bin/sage-site index ef5762d3cdc..035a1e2bce5 100755 --- a/build/bin/sage-site +++ b/build/bin/sage-site @@ -156,7 +156,15 @@ if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then # tends to ask interactive questions if something goes wrong. These # cause the build to hang. If stdin is /dev/null, TeX just aborts. shift - export LANG=C # to ensure it is possible to scrape out non-EN locale warnings + + # Trac #30002: ensure an English locale so that it is possible to + # scrape out warnings by pattern matching. + # Trac #30576: But we have to avoid the C locale, which disables + # proper UTF-8 operation in Python 3.6 or older. + LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) + LANG=$LC_ALL + export LC_ALL + export LANG # See #30351: bugs in macOS implementations of openblas/libgopm can cause # docbuild to hang if multiple OpenMP threads are allowed. diff --git a/build/bin/sage-system-python b/build/bin/sage-system-python index a72b1527df5..fdf0ad7444d 100755 --- a/build/bin/sage-system-python +++ b/build/bin/sage-system-python @@ -28,6 +28,16 @@ fi # is accessible by this python; this is to guard on Cygwin against Pythons # installed somewhere else in Windows. +# Trac #30008: Make it work even if the environment tries to sabotage UTF-8 +# operation in Python 3.0.x-3.6.x by setting LC_ALL=C or similar. + +if [ "$LC_ALL" = "C" -o "$LANG" = "C" -o "$LC_CTYPE" = "C" ]; then + LC_ALL=$(locale -a | grep -E -i '^(c|en_us)[-.]utf-?8$' | head -n 1) + LANG=$LC_ALL + export LC_ALL + export LANG +fi + PYTHONS="python python3 python3.8 python3.7 python2.7 python3.6 python2" for PY in $PYTHONS; do PYTHON="$(PATH="$SAGE_ORIG_PATH" command -v $PY)" diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 6b7beb3252e..99961f39c65 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -31,7 +31,7 @@ INST = $(SAGE_SPKG_INST) # Aliases for mutually exclusive standard packages selected at configure time TOOLCHAIN = @SAGE_TOOLCHAIN@ -PYTHON = python@SAGE_PYTHON_VERSION@ +PYTHON = python3 MP_LIBRARY = @SAGE_MP_LIBRARY@ BLAS = @SAGE_BLAS@ diff --git a/build/pkgs/conda-bootstrap.txt b/build/pkgs/conda-bootstrap.txt index 55f45bfd276..5405f70cf3d 100644 --- a/build/pkgs/conda-bootstrap.txt +++ b/build/pkgs/conda-bootstrap.txt @@ -1,2 +1,2 @@ # Packages needed for ./bootstrap -gettext autoconf automake libtool pkg-config +gettext autoconf automake libtool diff --git a/build/pkgs/conda.txt b/build/pkgs/conda.txt index 8d8ac6480f4..f564afa159a 100644 --- a/build/pkgs/conda.txt +++ b/build/pkgs/conda.txt @@ -2,6 +2,7 @@ compilers make m4 perl -"python<3.8" +"python<3.9" tar bc +pkg-config diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index e97928a7e40..3364afa3fa7 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=e8cac32102815b6ff83bbb455c5d4cacc5b4ee8f -md5=caa431f870eb76bf36b0bfd47a01ba5d -cksum=2384868099 +sha1=2d1d5ab6f12d7181f246eacec7b2fb1e1338b9ed +md5=4c9d6a5ed4f1b2494187fe043a15214b +cksum=3219488233 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 945076d0d30..91fd5591327 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -85a3dc5389b6670934938f705a9366b4ba8573b8 +be1a62b3bc99666c909f3672a8bf7baf32a15404 diff --git a/build/pkgs/cryptominisat/spkg-install.in b/build/pkgs/cryptominisat/spkg-install.in index 371e7b05896..2018fa38896 100644 --- a/build/pkgs/cryptominisat/spkg-install.in +++ b/build/pkgs/cryptominisat/spkg-install.in @@ -1,10 +1,5 @@ cd src -if [ $SAGE_PYTHON_VERSION -eq 2 ] -then - EXTRA_OPTS="-DFORCE_PYTHON2='ON'" -fi - sdh_cmake -DUSE_GAUSS='ON' $EXTRA_OPTS sdh_make VERBOSE=ON sdh_make_install VERBOSE=ON diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini deleted file mode 100644 index 148ab832b76..00000000000 --- a/build/pkgs/gap_packages/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=gap-VERSION.tar.bz2 -sha1=ceeb3dfbc0c26031ad53b0256ed04ca9d532566d -md5=36a141e05bb55be5d6ead7ccda969c5e -cksum=3891403157 diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini new file mode 120000 index 00000000000..b747449a062 --- /dev/null +++ b/build/pkgs/gap_packages/checksums.ini @@ -0,0 +1 @@ +../gap/checksums.ini \ No newline at end of file diff --git a/build/pkgs/gap_packages/package-version.txt b/build/pkgs/gap_packages/package-version.txt deleted file mode 100644 index 5cd045ae73e..00000000000 --- a/build/pkgs/gap_packages/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -4.10.2.p1 diff --git a/build/pkgs/gap_packages/package-version.txt b/build/pkgs/gap_packages/package-version.txt new file mode 120000 index 00000000000..9f0140a8413 --- /dev/null +++ b/build/pkgs/gap_packages/package-version.txt @@ -0,0 +1 @@ +../gap/package-version.txt \ No newline at end of file diff --git a/build/pkgs/gc/distros/conda.txt b/build/pkgs/gc/distros/conda.txt index 3c7c99fba98..f09f27a449a 100644 --- a/build/pkgs/gc/distros/conda.txt +++ b/build/pkgs/gc/distros/conda.txt @@ -1,2 +1 @@ -# needs rectificatation from conda people -# libgc +bdw-gc diff --git a/build/pkgs/gmpy2/checksums.ini b/build/pkgs/gmpy2/checksums.ini index 71bc24d1412..0db353f62f9 100644 --- a/build/pkgs/gmpy2/checksums.ini +++ b/build/pkgs/gmpy2/checksums.ini @@ -1,4 +1,5 @@ tarball=gmpy2-VERSION.tar.gz -sha1=b78f6e6d06d6406f7896e71ff7dca4c43c7368f1 -md5=500cbece0fedbd13598b1ddc52c893c7 -cksum=3080201027 +sha1=ef3cfb93ce0ea8b5ad4cebef5bf3c55d8fb5821a +md5=1504652fcab1cd8ce3e42661d42f9f73 +cksum=3152134051 +upstream_url=https://pypi.io/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz diff --git a/build/pkgs/gmpy2/package-version.txt b/build/pkgs/gmpy2/package-version.txt index 2225cc5e333..326c35e0969 100644 --- a/build/pkgs/gmpy2/package-version.txt +++ b/build/pkgs/gmpy2/package-version.txt @@ -1 +1 @@ -2.1.0b1 +2.1.0b5 diff --git a/build/pkgs/libatomic_ops/distros/conda.txt b/build/pkgs/libatomic_ops/distros/conda.txt index 6c53eb4c3a0..a6f2a51c512 100644 --- a/build/pkgs/libatomic_ops/distros/conda.txt +++ b/build/pkgs/libatomic_ops/distros/conda.txt @@ -1,2 +1 @@ -# this needs a rectification from Conda people -# libatomic_ops +libatomic_ops diff --git a/build/pkgs/nose/spkg-check.in b/build/pkgs/nose/spkg-check.in index 59090720043..6863374a12e 100644 --- a/build/pkgs/nose/spkg-check.in +++ b/build/pkgs/nose/spkg-check.in @@ -1,7 +1,5 @@ cd src -if SAGE_PYTHON3=yes; then - python3 setup.py build_tests -fi +python3 setup.py build_tests sage-python23 selftest.py diff --git a/build/pkgs/openblas/patches/lapack_h.patch b/build/pkgs/openblas/patches/lapack_h.patch new file mode 100644 index 00000000000..80e4de365ba --- /dev/null +++ b/build/pkgs/openblas/patches/lapack_h.patch @@ -0,0 +1,154 @@ +diff --git a/lapack-netlib/LAPACKE/include/lapack.h b/lapack-netlib/LAPACKE/include/lapack.h +index 36e53ec2..4f48b7c8 100644 +--- a/lapack-netlib/LAPACKE/include/lapack.h ++++ b/lapack-netlib/LAPACKE/include/lapack.h +@@ -3650,45 +3650,45 @@ void LAPACK_zggrqf( + lapack_int* info ); + + #define LAPACK_sggsvd LAPACK_GLOBAL(sggsvd,SGGSVD) +-lapack_int LAPACKE_sggsvd( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int n, lapack_int p, ++lapack_int LAPACK_sggsvd( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* n, lapack_int* p, + lapack_int* k, lapack_int* l, float* a, +- lapack_int lda, float* b, lapack_int ldb, +- float* alpha, float* beta, float* u, lapack_int ldu, +- float* v, lapack_int ldv, float* q, lapack_int ldq, +- lapack_int* iwork ); ++ lapack_int* lda, float* b, lapack_int* ldb, ++ float* alpha, float* beta, float* u, lapack_int* ldu, ++ float* v, lapack_int* ldv, float* q, lapack_int* ldq, ++ float* work, lapack_int* iwork, lapack_int* info ); + + #define LAPACK_dggsvd LAPACK_GLOBAL(dggsvd,DGGSVD) +-lapack_int LAPACKE_dggsvd( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int n, lapack_int p, ++lapack_int LAPACK_dggsvd( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* n, lapack_int* p, + lapack_int* k, lapack_int* l, double* a, +- lapack_int lda, double* b, lapack_int ldb, ++ lapack_int* lda, double* b, lapack_int* ldb, + double* alpha, double* beta, double* u, +- lapack_int ldu, double* v, lapack_int ldv, double* q, +- lapack_int ldq, lapack_int* iwork ); ++ lapack_int* ldu, double* v, lapack_int* ldv, double* q, ++ lapack_int* ldq, float* work, lapack_int* iwork, lapack_int* info ); + + #define LAPACK_cggsvd LAPACK_GLOBAL(cggsvd,CGGSVD) +-lapack_int LAPACKE_cggsvd( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int n, lapack_int p, ++lapack_int LAPACK_cggsvd( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* n, lapack_int* p, + lapack_int* k, lapack_int* l, +- lapack_complex_float* a, lapack_int lda, +- lapack_complex_float* b, lapack_int ldb, ++ lapack_complex_float* a, lapack_int* lda, ++ lapack_complex_float* b, lapack_int* ldb, + float* alpha, float* beta, lapack_complex_float* u, +- lapack_int ldu, lapack_complex_float* v, +- lapack_int ldv, lapack_complex_float* q, +- lapack_int ldq, lapack_int* iwork ); ++ lapack_int* ldu, lapack_complex_float* v, ++ lapack_int* ldv, lapack_complex_float* q, ++ lapack_int* ldq, float* work, lapack_int* rwork, lapack_int* iwork, lapack_int *info ); + + #define LAPACK_zggsvd LAPACK_GLOBAL(zggsvd,ZGGSVD) +-lapack_int LAPACKE_zggsvd( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int n, lapack_int p, ++lapack_int LAPACK_zggsvd( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* n, lapack_int* p, + lapack_int* k, lapack_int* l, +- lapack_complex_double* a, lapack_int lda, +- lapack_complex_double* b, lapack_int ldb, ++ lapack_complex_double* a, lapack_int* lda, ++ lapack_complex_double* b, lapack_int* ldb, + double* alpha, double* beta, +- lapack_complex_double* u, lapack_int ldu, +- lapack_complex_double* v, lapack_int ldv, +- lapack_complex_double* q, lapack_int ldq, +- lapack_int* iwork ); ++ lapack_complex_double* u, lapack_int* ldu, ++ lapack_complex_double* v, lapack_int* ldv, ++ lapack_complex_double* q, lapack_int* ldq, ++ float* work, lapack_int* rwork, lapack_int* iwork, lapack_int* info ); + + #define LAPACK_cggsvd3 LAPACK_GLOBAL(cggsvd3,CGGSVD3) + void LAPACK_cggsvd3( +@@ -3753,41 +3753,49 @@ void LAPACK_zggsvd3( + lapack_int* info ); + + #define LAPACK_sggsvp LAPACK_GLOBAL(sggsvp,SGGSVP) +-lapack_int LAPACKE_sggsvp( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int p, lapack_int n, float* a, +- lapack_int lda, float* b, lapack_int ldb, float tola, +- float tolb, lapack_int* k, lapack_int* l, float* u, +- lapack_int ldu, float* v, lapack_int ldv, float* q, +- lapack_int ldq ); ++lapack_int LAPACK_sggsvp( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* p, lapack_int* n, float* a, ++ lapack_int* lda, float* b, lapack_int* ldb, float* tola, ++ float* tolb, lapack_int* k, lapack_int* l, float* u, ++ lapack_int* ldu, float* v, lapack_int* ldv, float* q, ++ lapack_int* ldq, lapack_int* iwork, float* tau, ++ float* work, lapack_int* info); + + #define LAPACK_dggsvp LAPACK_GLOBAL(dggsvp,DGGSVP) +-lapack_int LAPACKE_dggsvp( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int p, lapack_int n, double* a, +- lapack_int lda, double* b, lapack_int ldb, +- double tola, double tolb, lapack_int* k, +- lapack_int* l, double* u, lapack_int ldu, double* v, +- lapack_int ldv, double* q, lapack_int ldq ); ++lapack_int LAPACK_dggsvp( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* p, lapack_int* n, double* a, ++ lapack_int* lda, double* b, lapack_int* ldb, ++ double* tola, double* tolb, lapack_int* k, ++ lapack_int* l, double* u, lapack_int* ldu, double* v, ++ lapack_int* ldv, double* q, lapack_int* ldq, ++ lapack_int* iwork, double* tau, double* work, ++ lapack_int* info); + + #define LAPACK_cggsvp LAPACK_GLOBAL(cggsvp,CGGSVP) +-lapack_int LAPACKE_cggsvp( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int p, lapack_int n, +- lapack_complex_float* a, lapack_int lda, +- lapack_complex_float* b, lapack_int ldb, float tola, +- float tolb, lapack_int* k, lapack_int* l, +- lapack_complex_float* u, lapack_int ldu, +- lapack_complex_float* v, lapack_int ldv, +- lapack_complex_float* q, lapack_int ldq ); ++lapack_int LAPACK_cggsvp( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* p, lapack_int* n, ++ lapack_complex_float* a, lapack_int* lda, ++ lapack_complex_float* b, lapack_int* ldb, float* tola, ++ float* tolb, lapack_int* k, lapack_int* l, ++ lapack_complex_float* u, lapack_int* ldu, ++ lapack_complex_float* v, lapack_int* ldv, ++ lapack_complex_float* q, lapack_int* ldq, ++ lapack_int* iwork, lapack_int* rwork, ++ lapack_complex_float* tau, lapack_complex_float* work, ++ lapack_int* info); + + #define LAPACK_zggsvp LAPACK_GLOBAL(zggsvp,ZGGSVP) +-lapack_int LAPACKE_zggsvp( int matrix_layout, char jobu, char jobv, char jobq, +- lapack_int m, lapack_int p, lapack_int n, +- lapack_complex_double* a, lapack_int lda, +- lapack_complex_double* b, lapack_int ldb, +- double tola, double tolb, lapack_int* k, ++lapack_int LAPACK_zggsvp( char const* jobu, char const* jobv, char const* jobq, ++ lapack_int* m, lapack_int* p, lapack_int* n, ++ lapack_complex_double* a, lapack_int* lda, ++ lapack_complex_double* b, lapack_int* ldb, ++ double* tola, double* tolb, lapack_int* k, + lapack_int* l, lapack_complex_double* u, +- lapack_int ldu, lapack_complex_double* v, +- lapack_int ldv, lapack_complex_double* q, +- lapack_int ldq ); ++ lapack_int* ldu, lapack_complex_double* v, ++ lapack_int* ldv, lapack_complex_double* q, ++ lapack_int* ldq, lapack_int* iwork, lapack_int* rwork, ++ lapack_complex_double* tau, lapack_complex_double* work, ++ lapack_int* info); + + #define LAPACK_cggsvp3 LAPACK_GLOBAL(cggsvp3,CGGSVP3) + void LAPACK_cggsvp3( diff --git a/build/pkgs/pillow/spkg-install.in b/build/pkgs/pillow/spkg-install.in index e01a3aee112..d131fc6bf8e 100644 --- a/build/pkgs/pillow/spkg-install.in +++ b/build/pkgs/pillow/spkg-install.in @@ -20,10 +20,10 @@ fi # Note: Avoid shared libraries inside egg files, Trac #19467 sage-python23 setup.py \ --no-user-cfg \ + bdist_wheel \ build_ext \ --debug \ --disable-jpeg \ - $extra_build_ext \ - bdist_wheel || sdh_die "Error building/installing Pillow" + $extra_build_ext || sdh_die "Error building/installing Pillow" sdh_store_and_pip_install_wheel . diff --git a/build/pkgs/pkgconf/distros/conda.txt b/build/pkgs/pkgconf/distros/conda.txt new file mode 100644 index 00000000000..6d2c214eb6d --- /dev/null +++ b/build/pkgs/pkgconf/distros/conda.txt @@ -0,0 +1 @@ +pkg-config diff --git a/build/pkgs/pkgconf/spkg-configure.m4 b/build/pkgs/pkgconf/spkg-configure.m4 index b55b8222ccb..87795bf8cde 100644 --- a/build/pkgs/pkgconf/spkg-configure.m4 +++ b/build/pkgs/pkgconf/spkg-configure.m4 @@ -1,16 +1,7 @@ SAGE_SPKG_CONFIGURE( [pkgconf], [ - AC_CACHE_CHECK([for pkg-config >= 0.29], [ac_cv_path_PKGCONF], [ - AC_PATH_PROGS_FEATURE_CHECK([PKGCONF], [pkg-config], [ - pkgconf_version=`$ac_path_PKGCONF --version 2>&1` - AS_IF([test -n "$pkgconf_version"], [ - AX_COMPARE_VERSION([$pkgconf_version], [ge], [0.29], [ - ac_cv_path_PKGCONF="$ac_path_PKGCONF" - ]) - ]) - ]) - ]) - AS_IF([test -z "$ac_cv_path_PKGCONF"], [ + dnl Check is in configure.ac + AS_IF([test -z "$PKG_CONFIG"], [ sage_spkg_install_pkgconf=yes AC_SUBST(SAGE_PKG_CONFIG_PATH, ['']) AC_MSG_RESULT([installing pkgconf spkg])], [ diff --git a/build/pkgs/python3/spkg-configure.m4 b/build/pkgs/python3/spkg-configure.m4 index 78b4d8bda85..f9701c13f39 100644 --- a/build/pkgs/python3/spkg-configure.m4 +++ b/build/pkgs/python3/spkg-configure.m4 @@ -1,148 +1,59 @@ SAGE_SPKG_CONFIGURE([python3], [ - SAGE_SPKG_DEPCHECK([sqlite libpng bzip2 xz libffi], [ - AS_IF([test $SAGE_PYTHON_VERSION = 2], [ - dnl If we use Python 2 for Sage, we install Python 3 too and do NOT attempt to do - dnl venv using system python3 over SAGE_LOCAL. - dnl (In particular, the setuptools and pip install scripts are not prepared for - dnl handling this situation.) - sage_spkg_install_python3=yes - ], [ - dnl Using Python 3 for Sage. Check if we can do venv with a system python3 - dnl instead of building our own copy. - check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" - AC_CACHE_CHECK([for python3 >= 3.7, < 3.9 with modules $check_modules], [ac_cv_path_PYTHON3], [ - AC_MSG_RESULT([]) - AC_PATH_PROGS_FEATURE_CHECK([PYTHON3], [python3.8 python3.7 python3], [ - AC_MSG_CHECKING([... whether $ac_path_PYTHON3 is good]) - python3_version=`"$ac_path_PYTHON3" --version 2>&1 \ - | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` - AS_IF([test -n "$python3_version"], [ - AX_COMPARE_VERSION([$python3_version], [ge], [3.7.0], [ - AX_COMPARE_VERSION([$python3_version], [lt], [3.9.0], [ - dnl Because the system python is not used directly but rather in a venv without site-packages, - dnl we test whether the module will be available in a venv. - dnl Otherwise, some system site-package may be providing this module to the system python. - dnl m4_define([conftest_venv], [config-venv]) .... for debugging only - rm -rf conftest_venv - AS_IF(["$ac_path_PYTHON3" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import $check_modules"], [ - AC_LANG_PUSH([C]) - AC_LANG_CONFTEST([ - AC_LANG_SOURCE([[ -#define PY_SSIZE_T_CLEAN -#include -static PyMethodDef SpamMethods[] = { - {NULL, NULL, 0, NULL} /* Sentinel */ -}; -static struct PyModuleDef spammodule = { - PyModuleDef_HEAD_INIT, - "spam", /* name of module */ - NULL, /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module, - or -1 if the module keeps state in global variables. */ - SpamMethods -}; -PyMODINIT_FUNC -PyInit_spam(void) -{ - PyObject *m; - - m = PyModule_Create(&spammodule); - return m; -} - ]]) - ]) - AC_LANG_POP([C]) - cat > conftest.py <& AS_MESSAGE_LOG_FD - echo CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD - AS_IF([CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ - rm -rf conftest.* - AC_LANG_PUSH([C++]) - AC_LANG_CONFTEST([ - AC_LANG_SOURCE([[ -#define PY_SSIZE_T_CLEAN -#include -static PyMethodDef SpamMethods[] = { - {NULL, NULL, 0, NULL} /* Sentinel */ -}; -static struct PyModuleDef spammodule = { - PyModuleDef_HEAD_INIT, - "spam", /* name of module */ - NULL, /* module documentation, may be NULL */ - -1, /* size of per-interpreter state of the module, - or -1 if the module keeps state in global variables. */ - SpamMethods -}; -PyMODINIT_FUNC -PyInit_spam(void) -{ - PyObject *m; - - m = PyModule_Create(&spammodule); - return m; -} -// Partial C++11 test, from ax_cxx_compile_stdcxx.m4 - - namespace test_noexcept - { + AC_ARG_WITH([python], + [AS_HELP_STRING([--with-python=PYTHON3], + [Python 3 executable to use for the Sage venv; default: python3])]) + AS_IF([test x"$with_python" = x2], [AC_MSG_ERROR([Sage cannot be built on Python 2. Exiting.])]) + AS_IF([test x"$with_python" = x3], [ + AC_MSG_NOTICE([The meaning of the option --with-python has changed in Sage 9.2. Ignoring.]) + with_python='' + ]) + AS_IF([test x"$with_python" = x"no"], + [AC_MSG_ERROR([building Sage --without-python is not supported])]) + ac_path_PYTHON3="$with_python" - int f() { return 0; } - int g() noexcept { return 0; } - - static_assert(noexcept(f()) == false, ""); - static_assert(noexcept(g()) == true, ""); - - } - - ]]) - ]) - AC_LANG_POP([C++]) - cat > conftest.py <& AS_MESSAGE_LOG_FD 2>&1 ], [ - ac_cv_path_PYTHON3="$ac_path_PYTHON3" - ac_path_PYTHON3_found=: - AC_MSG_RESULT([yes]) - dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK - AC_MSG_CHECKING([for python3 >= 3.6, < 3.9 with modules $check_modules]) - ], [ - AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C++ 11 extension]) - ]) - ], [ - AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C extension]) - ]) - ], [ - AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: $check_modules]) - ]) - ], [ - AC_MSG_RESULT([no, $python3_version is too recent]) - ]) - ], [ - AC_MSG_RESULT([no, $python3_version is too old]) - ]) - ], [ - AC_MSG_RESULT([no, "$ac_path_PYTHON3 --version" does not work]) + SAGE_SPKG_DEPCHECK([sqlite libpng bzip2 xz libffi], [ + dnl Check if we can do venv with a system python3 + dnl instead of building our own copy. + check_modules="sqlite3, ctypes, math, hashlib, crypt, readline, socket, zlib, distutils.core" + m4_pushdef([MIN_VERSION], [3.6.0]) + m4_pushdef([LT_VERSION], [3.9.0]) + AC_CACHE_CHECK([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules], [ac_cv_path_PYTHON3], [ + AS_IF([test x"$ac_path_PYTHON3" != x], [dnl checking explicitly specified $with_python + AC_MSG_RESULT([]) + AC_PATH_PROG([ac_path_PYTHON3], [$ac_path_PYTHON3]) + SAGE_CHECK_PYTHON_FOR_VENV([$ac_path_PYTHON3], + MIN_VERSION, LT_VERSION, + $check_modules, [ + dnl It is good + ac_cv_path_PYTHON3="$ac_path_PYTHON3" + ac_path_PYTHON3_found=: + AC_MSG_RESULT([yes]) + dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK + AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules]) + ]) + AS_IF([test -z "$ac_cv_path_PYTHON3"], [ + AC_MSG_ERROR([the python3 selected using --with-python=$with_python is not suitable]) + ]) + ], [dnl checking the default system python3 + AC_MSG_RESULT([]) + AC_PATH_PROGS_FEATURE_CHECK([PYTHON3], [python3], [ + SAGE_CHECK_PYTHON_FOR_VENV([$ac_path_PYTHON3], + MIN_VERSION, LT_VERSION, + $check_modules, [ + dnl It is good + ac_cv_path_PYTHON3="$ac_path_PYTHON3" + ac_path_PYTHON3_found=: + AC_MSG_RESULT([yes]) + dnl introduction for AC_MSG_RESULT printed by AC_CACHE_CHECK + AC_MSG_CHECKING([for python3 >= ]MIN_VERSION[, < ]LT_VERSION[ with modules $check_modules]) ]) ]) - ]) - AS_IF([test -z "$ac_cv_path_PYTHON3"], - [sage_spkg_install_python3=yes]) + ]) ]) - ]) + AS_IF([test -z "$ac_cv_path_PYTHON3"], [sage_spkg_install_python3=yes]) + m4_popdef([MIN_VERSION]) + m4_popdef([LT_VERSION]) + ]) ],, [ dnl PRE ], [ diff --git a/build/pkgs/r/patches/configure_bzlibtest.patch b/build/pkgs/r/patches/configure_bzlibtest.patch new file mode 100644 index 00000000000..b4d3f61ef9e --- /dev/null +++ b/build/pkgs/r/patches/configure_bzlibtest.patch @@ -0,0 +1,13 @@ +diff --git a/configure b/configure +index 976de50..14b6b4c 100755 +--- a/configure ++++ b/configure +@@ -42239,6 +42239,8 @@ else + + #ifdef HAVE_BZLIB_H + #include ++#include ++#include + #endif + int main() { + char *ver = BZ2_bzlibVersion(); diff --git a/build/pkgs/readline/SPKG.rst b/build/pkgs/readline/SPKG.rst index 8553446ae64..278f946b102 100644 --- a/build/pkgs/readline/SPKG.rst +++ b/build/pkgs/readline/SPKG.rst @@ -46,5 +46,3 @@ Patches - 0002-ltinfo.patch: We build readline using ncurses, and for that it needs to be told to link with libtinfo (part of ncurses). - -- sigsetjmp.patch: Correctly define sigsetjmp and friends on Cygwin. diff --git a/build/pkgs/readline/checksums.ini b/build/pkgs/readline/checksums.ini index b5697e24ad6..5e4e180686f 100644 --- a/build/pkgs/readline/checksums.ini +++ b/build/pkgs/readline/checksums.ini @@ -1,4 +1,5 @@ tarball=readline-VERSION.tar.gz -sha1=d050d1ad41e876cab82e1a407258096ffbed9202 -md5=7d0e4b56c577c8c12cf4f1d919d05210 -cksum=931813369 +sha1=d58041c2143595dc001d2777ae9a200be30198b0 +md5=7e6c1f16aee3244a69aba6e438295ca3 +cksum=3826776229 +upstream_url=https://ftp.gnu.org/gnu/readline/readline-VERSION.tar.gz diff --git a/build/pkgs/readline/package-version.txt b/build/pkgs/readline/package-version.txt index 27065bf51e0..657c5dea53b 100644 --- a/build/pkgs/readline/package-version.txt +++ b/build/pkgs/readline/package-version.txt @@ -1 +1,2 @@ -6.3.008.p0 +8.0 + diff --git a/build/pkgs/readline/patches/0001-macports.patch b/build/pkgs/readline/patches/0001-macports.patch index 92c3966954f..d9154a38443 100644 --- a/build/pkgs/readline/patches/0001-macports.patch +++ b/build/pkgs/readline/patches/0001-macports.patch @@ -4,8 +4,8 @@ darwin[1-7].*) SHOBJ_STATUS=unsupported SHOBJ_LDFLAGS='-dynamic' -- SHLIB_XLDFLAGS='-arch_only `/usr/bin/arch` -install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR) -v' -+ SHLIB_XLDFLAGS='-install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR) -v' +- SHLIB_XLDFLAGS='-arch_only `/usr/bin/arch` -install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -v' ++ SHLIB_XLDFLAGS='-install_name $(libdir)/`echo $@ | sed "s:\\..*::"`.$(SHLIB_MAJOR).$(SHLIB_LIBSUFF) -current_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -compatibility_version $(SHLIB_MAJOR)$(SHLIB_MINOR) -v' ;; # Darwin 8 == Mac OS X 10.4; Mac OS X 10.N == Darwin N+4 *) diff --git a/build/pkgs/readline/patches/sigsetjmp.patch b/build/pkgs/readline/patches/sigsetjmp.patch deleted file mode 100644 index 9fa5974cc7a..00000000000 --- a/build/pkgs/readline/patches/sigsetjmp.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- readline-6.3/posixjmp.h 2012-12-23 21:20:50.000000000 -0600 -+++ readline-6.3/posixjmp.h 2014-03-13 02:53:44.793652900 -0500 -@@ -27,7 +27,7 @@ - - #if defined (HAVE_POSIX_SIGSETJMP) - # define procenv_t sigjmp_buf --# if !defined (__OPENNT) -+# if !defined (__OPENNT) && !defined(__CYGWIN__) - # undef setjmp - # define setjmp(x) sigsetjmp((x), 1) - # define setjmp_nosigs(x) sigsetjmp((x), 0) ---- readline-6.3/rltty.c 2013-08-25 15:57:05.000000000 -0500 -+++ readline-6.3/rltty.c 2014-03-13 03:28:49.575426200 -0500 -@@ -37,9 +37,9 @@ - - #include "rldefs.h" - --#if defined (GWINSZ_IN_SYS_IOCTL) -+#if defined (GWINSZ_IN_SYS_IOCTL) || defined(STRUCT_WINSIZE_IN_SYS_IOCTL) - # include --#endif /* GWINSZ_IN_SYS_IOCTL */ -+#endif /* GWINSZ_IN_SYS_IOCTL || STRUCT_WINSIZE_IN_SYS_IOCTL */ - - #include "rltty.h" - #include "readline.h" diff --git a/build/pkgs/sagelib/package-version.txt b/build/pkgs/sagelib/package-version.txt index d441d9a4cbe..c2e47d9a8ef 100644 --- a/build/pkgs/sagelib/package-version.txt +++ b/build/pkgs/sagelib/package-version.txt @@ -1 +1 @@ -9.2.beta13 +9.2.beta14 diff --git a/build/pkgs/sagelib/src/requirements.txt b/build/pkgs/sagelib/src/requirements.txt index 1e5647c8608..7d059be408a 100644 --- a/build/pkgs/sagelib/src/requirements.txt +++ b/build/pkgs/sagelib/src/requirements.txt @@ -4,7 +4,7 @@ six # use of six should be removed from sage_setup Cython==0.29.17 pkgconfig cysignals -gmpy2==2.1.0b1 +gmpy2==2.1.0b5 numpy # already needed by sage.env jinja2 # sage_setup.autogen.interpreters diff --git a/build/pkgs/scipy/patches/extern_decls.patch b/build/pkgs/scipy/patches/extern_decls.patch new file mode 100644 index 00000000000..a99d5b11db8 --- /dev/null +++ b/build/pkgs/scipy/patches/extern_decls.patch @@ -0,0 +1,129 @@ +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/clacon2.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/clacon2.c +index 107bb64..3756149 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/clacon2.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/clacon2.c +@@ -106,6 +106,7 @@ clacon2_(int *n, complex *v, complex *x, float *est, int *kase, int isave[3]) + extern float smach(char *); + extern int icmax1_slu(int *, complex *, int *); + extern double scsum1_slu(int *, complex *, int *); ++ extern void ccopy_(int *, complex [], int *, complex [], int *); + + safmin = smach("Safe minimum"); /* lamch_("Safe minimum"); */ + if ( *kase == 0 ) { +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/dmach.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/dmach.c +index 73beacb..cafdf1c 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/dmach.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/dmach.c +@@ -11,6 +11,7 @@ at the top-level directory. + #include + #include + #include ++#include + + double dmach(char *cmach) + { +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_cdrop_row.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_cdrop_row.c +index 4987548..4056b5d 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_cdrop_row.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_cdrop_row.c +@@ -23,6 +23,7 @@ at the top-level directory. + #include + #include "slu_cdefs.h" + ++extern void scopy_(int *, float [], int *, float [], int *); + extern void cswap_(int *, complex [], int *, complex [], int *); + extern void caxpy_(int *, complex *, complex [], int *, complex [], int *); + extern void ccopy_(int *, complex [], int *, complex [], int *); +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_zdrop_row.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_zdrop_row.c +index f434dd9..a8dda39 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_zdrop_row.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/ilu_zdrop_row.c +@@ -23,6 +23,7 @@ at the top-level directory. + #include + #include "slu_zdefs.h" + ++extern void dcopy_(int *, double [], int *, double [], int *); + extern void zswap_(int *, doublecomplex [], int *, doublecomplex [], int *); + extern void zaxpy_(int *, doublecomplex *, doublecomplex [], int *, doublecomplex [], int *); + extern void zcopy_(int *, doublecomplex [], int *, doublecomplex [], int *); +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/slacon2.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/slacon2.c +index 7c93341..ade669e 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/slacon2.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/slacon2.c +@@ -100,10 +100,12 @@ slacon2_(int *n, float *v, float *x, int *isgn, float *est, int *kase, int isave + float temp; + #ifdef _CRAY + extern int ISAMAX(int *, float *, int *); ++ extern int IDAMAX(int *, float *, int *); + extern float SASUM(int *, float *, int *); + extern int SCOPY(int *, float *, int *, float *, int *); + #else + extern int isamax_(int *, float *, int *); ++ extern int idamax_(int *, float *, int *); + extern float sasum_(int *, float *, int *); + extern int scopy_(int *, float *, int *, float *, int *); + #endif +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/smach.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/smach.c +index fff6c5f..0b69991 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/smach.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/smach.c +@@ -11,6 +11,7 @@ at the top-level directory. + #include + #include + #include ++#include + + float smach(char *cmach) + { +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/sp_ienv.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/sp_ienv.c +index 855d901..e9aac41 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/sp_ienv.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/sp_ienv.c +@@ -68,6 +68,7 @@ at the top-level directory. + int + sp_ienv(int ispec) + { ++ extern int input_error(char *, int *); + int i; + + switch (ispec) { +diff --git a/scipy/sparse/linalg/dsolve/SuperLU/SRC/zlacon2.c b/scipy/sparse/linalg/dsolve/SuperLU/SRC/zlacon2.c +index b43c619..873678d 100755 +--- a/scipy/sparse/linalg/dsolve/SuperLU/SRC/zlacon2.c ++++ b/scipy/sparse/linalg/dsolve/SuperLU/SRC/zlacon2.c +@@ -106,6 +106,7 @@ zlacon2_(int *n, doublecomplex *v, doublecomplex *x, double *est, int *kase, int + extern double dmach(char *); + extern int izmax1_slu(int *, doublecomplex *, int *); + extern double dzsum1_slu(int *, doublecomplex *, int *); ++ extern void zcopy_(int *, doublecomplex [], int *, doublecomplex [], int *); + + safmin = dmach("Safe minimum"); /* lamch_("Safe minimum"); */ + if ( *kase == 0 ) { +diff --git a/scipy/spatial/qhull_src/src/libqhull_r.h b/scipy/spatial/qhull_src/src/libqhull_r.h +index a48dd4e..6da002d 100755 +--- a/scipy/spatial/qhull_src/src/libqhull_r.h ++++ b/scipy/spatial/qhull_src/src/libqhull_r.h +@@ -37,6 +37,7 @@ + #include + #include + #include ++#include + + #ifndef __STDC__ + #ifndef __cplusplus +@@ -1176,6 +1177,7 @@ void qh_initqhull_start2(qhT *qh, FILE *infile, FILE *outfile, FILE *errfile) + void qh_initthresholds(qhT *qh, char *command); + void qh_lib_check(int qhullLibraryType, int qhTsize, int vertexTsize, int ridgeTsize, int facetTsize, int setTsize, int qhmemTsize); + void qh_option(qhT *qh, const char *option, int *i, realT *r); ++void qh_setfeasible(qhT *qh, int dim); + void qh_zero(qhT *qh, FILE *errfile); + + /***** -io_r.c prototypes (duplicated from io_r.h) ***********************/ +@@ -1184,6 +1186,7 @@ void qh_dfacet(qhT *qh, unsigned int id); + void qh_dvertex(qhT *qh, unsigned int id); + void qh_printneighborhood(qhT *qh, FILE *fp, qh_PRINT format, facetT *facetA, facetT *facetB, boolT printall); + void qh_produce_output(qhT *qh); ++void qh_prepare_output(qhT *qh); + coordT *qh_readpoints(qhT *qh, int *numpoints, int *dimension, boolT *ismalloc); + + diff --git a/build/pkgs/symmetrica/SPKG.rst b/build/pkgs/symmetrica/SPKG.rst index bd82eda45f7..ccd3b34a913 100644 --- a/build/pkgs/symmetrica/SPKG.rst +++ b/build/pkgs/symmetrica/SPKG.rst @@ -18,8 +18,10 @@ University of Bayreuth. It has routines to handle the following topics - operations of finite groups. - ordinary representation theory of Hecke algebras of type A_n -For more details check http://www.symmetrica.de (currently redirects to -http://www.algorithm.uni-bayreuth.de/en/research/SYMMETRICA) +For more details check http://www.algorithm.uni-bayreuth.de/en/research/SYMMETRICA + +Updated package on https://gitlab.com/sagemath/symmetrica/-/releases +with changes to modernize the source and the build system. License ------- @@ -30,28 +32,4 @@ Public Domain (see the above web site) Upstream Contact ---------------- -- Axel Kohnert - see http://www.mathe2.uni-bayreuth.de/axel/ - -Dependencies ------------- - -- GNU patch (for applying the patches to upstream) - - -Special Update/Build Instructions ---------------------------------- - -The following patches are applied in spkg-install: - -- ``bruch.patch``: store integers in a temporary variable before freeing - memory -- ``de.patch``: turn off banner -- ``int32.patch``: use ``int32_t`` and ``uint32_t`` for type INT. -- ``sort_sum_rename.patch``: rename ``sort`` to ``sym_sort``, ``sum`` to ``sym_sum`` -- We copy over our own ``Makefile``: - - ``patches/makefile`` (Fix compiler, i.e., use ``$CC``, and let it use - ``$CFLAGS``.) - -Permissions in the upstream tarball are funky, please run ``chmod 644 -src/*`` after unpacking. +- (passed away in 2013) Axel Kohnert - see http://www.mathe2.uni-bayreuth.de/axel/ diff --git a/build/pkgs/symmetrica/checksums.ini b/build/pkgs/symmetrica/checksums.ini index bab7c48d1c1..5f70ed5363c 100644 --- a/build/pkgs/symmetrica/checksums.ini +++ b/build/pkgs/symmetrica/checksums.ini @@ -1,4 +1,5 @@ -tarball=symmetrica-VERSION.tar.bz2 -sha1=946111801dc98a11bb50d8a02ebc18b3e6d2ee28 -md5=2baff354c087a29db772af57512cc84d -cksum=2849267806 +tarball=symmetrica-VERSION.tar.xz +sha1=0044cc087ff04267c246e730c6570d89f6e593af +md5=cd4716c26b5c625a012c22656113ef6f +cksum=1186250347 +upstream_url=http://users.ox.ac.uk/~coml0531/sage/symmetrica-3.0.1.tar.xz diff --git a/build/pkgs/symmetrica/package-version.txt b/build/pkgs/symmetrica/package-version.txt index 7197fe846bb..cb2b00e4f7a 100644 --- a/build/pkgs/symmetrica/package-version.txt +++ b/build/pkgs/symmetrica/package-version.txt @@ -1 +1 @@ -2.0.p11 +3.0.1 diff --git a/build/pkgs/symmetrica/patches/bruch.patch b/build/pkgs/symmetrica/patches/bruch.patch deleted file mode 100644 index ae88b792068..00000000000 --- a/build/pkgs/symmetrica/patches/bruch.patch +++ /dev/null @@ -1,38 +0,0 @@ ---- src/bruch.c 2007-12-06 11:30:00.000000000 -0500 -+++ b/bruch.c 2013-10-22 08:37:43.000000000 -0400 -@@ -975,14 +975,16 @@ - ggterg = ggt_i(S_B_UI(bruch),S_B_OI(bruch)); - - if (ggterg == S_B_UI(bruch)) { -+ INT tmp = S_B_OI(bruch); - freeself_bruch(bruch); -- M_I_I(S_B_OI(bruch) / ggterg,bruch); -+ M_I_I(tmp / ggterg,bruch); - goto ende; - } - - if (-ggterg == S_B_UI(bruch)) { -+ INT tmp = S_B_OI(bruch); - freeself_bruch(bruch); -- M_I_I(- S_B_OI(bruch) / ggterg,bruch); -+ M_I_I(- tmp / ggterg,bruch); - goto ende; - } - -@@ -1032,12 +1034,14 @@ - - if (S_O_K(S_B_U(bruch)) == INTEGER) - if (S_B_UI(bruch) == 1) { -+ INT tmp = S_B_OI(bruch); - freeself_bruch(bruch); -- M_I_I(S_B_OI(bruch),bruch); -+ M_I_I(tmp,bruch); - goto ende; } - else if (S_B_UI(bruch) == -1) { -+ INT tmp = S_B_OI(bruch); - freeself_bruch(bruch); -- M_I_I( - S_B_OI(bruch),bruch); -+ M_I_I( - tmp,bruch); - goto ende; } - if (NEGP(S_B_O(bruch)) && NEGP(S_B_U(bruch))) - { diff --git a/build/pkgs/symmetrica/patches/de.patch b/build/pkgs/symmetrica/patches/de.patch deleted file mode 100644 index 1df23166145..00000000000 --- a/build/pkgs/symmetrica/patches/de.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ru src/de.c b/de.c ---- src/de.c 2007-12-06 17:30:00.000000000 +0100 -+++ b/de.c 2013-10-19 18:36:11.098758179 +0200 -@@ -24,7 +24,7 @@ - OP cons_negeins;/* global INTEGER variable -1 */ - OP cons_null; /* global INTEGER variable 0 */ - FILE *texout; /* global variable for texoutput */ --INT no_banner = FALSE; /* AK 281293 */ -+INT no_banner = TRUE; /* AK 281293 */ - INT no_mem_check=TRUE; /* AK 100893 */ - INT english_tableau=FALSE; /* AK 290995 */ - diff --git a/build/pkgs/symmetrica/patches/int32.patch b/build/pkgs/symmetrica/patches/int32.patch deleted file mode 100644 index b64f27d51ea..00000000000 --- a/build/pkgs/symmetrica/patches/int32.patch +++ /dev/null @@ -1,37 +0,0 @@ -diff -ru src/def.h c/def.h ---- src/def.h 2007-12-06 17:30:56.000000000 +0100 -+++ c/def.h 2013-10-19 18:42:55.118745730 +0200 -@@ -2,14 +2,9 @@ - /* INT should always be 4 byte */ - #ifndef DEF_H - -- --#ifdef __alpha --typedef int INT; --typedef unsigned int UINT; --#else /* __alpha */ --typedef long INT; --typedef unsigned long UINT; --#endif /* __alpha */ -+#include -+typedef int32_t INT; -+typedef uint32_t UINT; - - #include - #include -@@ -65,10 +60,13 @@ - - - /* definitionen fuer object.c */ --typedef INT OBJECTKIND; /* 4 byte */ -+/* NOTE: partition code assumes that there is no unused space in the -+ * object struct when an INT is stored. This requires both OBJECTKIND -+ * and OBJECTSELF to have a size equal to a machine word. */ -+typedef intptr_t OBJECTKIND; - - typedef union { -- INT ob_INT; -+ intptr_t ob_INT; - INT * ob_INTpointer; - char *ob_charpointer; - struct bruch *ob_bruch; diff --git a/build/pkgs/symmetrica/patches/makefile b/build/pkgs/symmetrica/patches/makefile deleted file mode 100644 index 133981b86c1..00000000000 --- a/build/pkgs/symmetrica/patches/makefile +++ /dev/null @@ -1,23 +0,0 @@ -SOURCES:=bar.c bi.c boe.c bruch.c classical.c de.c di.c ff.c galois.c \ - ga.c gra.c hash.c hiccup.c io.c ko.c list.c lo.c ma.c mee.c \ - mem.c mes.c mhe.c mhh.c mhm.c mhp.c mhs.c mmm.c mms.c \ - mod_dg_sbd.c mo.c mpp.c mps.c mse.c msh.c msm.c mss.c muir.c \ - na.c nb.c nc.c nu.c part.c pee.c peh.c pem.c perm.c pes.c \ - phe.c phh.c phm.c phs.c plet.c pme.c pmh.c poly.c ppe.c pph.c \ - ppm.c ppp.c pps.c pr.c pse.c psh.c psm.c pss.c rest.c rh.c \ - sab.c sb.c sc.c sr.c ta.c teh.c tem.c tep.c tes.c the.c thm.c \ - thp.c ths.c tme.c tmh.c tmp.c tms.c tpe.c tph.c tpm.c tps.c \ - tse.c tsh.c tsm.c tsp.c vc.c zo.c zykelind.c zyk.c - -OBJS:=$(SOURCES:%.c=%.o) - -all: libsymmetrica.a - -libsymmetrica.a: $(OBJS) makefile - $(AR) crs $@ $(OBJS) - $(RANLIB) $@ - -# Built and run from spkg-check: -test: test.o libsymmetrica.a makefile - $(CC) test.o -o $@ -L. $(LDFLAGS) -lsymmetrica -lm - diff --git a/build/pkgs/symmetrica/patches/return_values.patch b/build/pkgs/symmetrica/patches/return_values.patch deleted file mode 100644 index e09c389337a..00000000000 --- a/build/pkgs/symmetrica/patches/return_values.patch +++ /dev/null @@ -1,14 +0,0 @@ -diff -ru src/part.c b/part.c ---- src/part.c Thu May 14 06:01:00 2009 -0400 -+++ b/part.c Thu May 14 06:01:00 2009 -0400 -@@ -1767,8 +1767,8 @@ - /* to compute number of partitions */ - { - INT erg = OK; -- if (ni<0) return; -- if (not EMPTYP(S_V_I(vec,ni))) return; -+ if (ni<0) return 0; -+ if (not EMPTYP(S_V_I(vec,ni))) return 0; - else if (ni<=1) M_I_I(1,S_V_I(vec,ni)); - else { - diff --git a/build/pkgs/symmetrica/patches/sort_sum_rename.patch b/build/pkgs/symmetrica/patches/sort_sum_rename.patch deleted file mode 100644 index 2d9a2dae068..00000000000 --- a/build/pkgs/symmetrica/patches/sort_sum_rename.patch +++ /dev/null @@ -1,340 +0,0 @@ -diff -r 5cd656a07aa5 src/bar.c ---- a/bar.c Thu May 14 06:01:00 2009 -0400 -+++ b/bar.c Thu May 14 07:38:17 2009 -0400 -@@ -237,7 +237,7 @@ - g = callocobject(); - e = S_V_I(a,0L); - f = S_V_I(a,1L); -- erg += sum(f,g); -+ erg += sym_sum(f,g); - j=0L; - for (i=0L;iob_kind = (OBJECTKIND)(b)) ---- a/nc.c Thu May 14 06:01:00 2009 -0400 -+++ b/nc.c Thu May 14 07:38:17 2009 -0400 -@@ -204,7 +204,7 @@ - erg += mult(S_NC_C(a),S_NC_C(b),d); - erg += m_gl_co(S_NC_GL(a),e); - erg += mult_apply(e,d); -- erg += sum(d,e); -+ erg += sym_sum(d,e); - erg += m_gl_go(S_NC_GL(a),d); - erg += div(e,d,c); - erg += freeall(e); -@@ -617,7 +617,7 @@ - erg += mult_nc_kranz(c,a,e); - erg += mult(S_V_I(e,1L),f,c); - erg += div(c,g,c); -- erg += sum(c,S_V_I(S_NC_C(b),S_I_I(d))); -+ erg += sym_sum(c,S_V_I(S_NC_C(b),S_I_I(d))); - } - erg += freeall(c); - erg += freeall(d); -@@ -712,7 +712,7 @@ - for(j = 0L; j=0;j--) -diff -r 5cd656a07aa5 src/perm.c ---- a/perm.c Thu May 14 06:01:00 2009 -0400 -+++ b/perm.c Thu May 14 07:38:17 2009 -0400 -@@ -833,9 +833,9 @@ - /* s = Anzahl der spalten */ - - s = S_V_LI(S_V_I(a,0L)); -- sum(S_V_I(a,0L),summe);/* composition ist vector */ -+ sym_sum(S_V_I(a,0L),summe);/* composition ist vector */ - z = S_I_I(summe); -- FREEALL(summe); -+ FREEALL(summe); - m_ilih_nm(s,z,b); - C_O_K(b,KRANZTYPUS); - for (i=0L;i&2 "Error building Symmetrica's test program." - exit 1 -fi - -echo 123 | ./test > spkg-check.actual - -cat < spkg-check.expected - 12.146304.367025.329675.766243.241881.295855.454217.088483.382315. - 328918.161829.235892.362167.668831.156960.612640.202170.735835.221294. - 047782.591091.570411.651472.186029.519906.261646.730733.907419.814952. - 960000.000000.000000.000000.000000 -EOF - -if ! diff -b spkg-check.actual spkg-check.expected; then - echo >&2 "Error: The Symmetrica check failed." - exit 1 -fi -echo "The Symmetrica check passed." +sdh_make_check diff --git a/build/pkgs/symmetrica/spkg-install.in b/build/pkgs/symmetrica/spkg-install.in index bc5d8e5f26c..17d240e98e0 100644 --- a/build/pkgs/symmetrica/spkg-install.in +++ b/build/pkgs/symmetrica/spkg-install.in @@ -1,11 +1,6 @@ cd src -# Patching the upstream makefile doesn't make sense, -# as it has (syntactically) nothing in common with ours. -cp -f ../patches/makefile makefile || exit $? - export CFLAGS="-O2 -g $CFLAGS -fPIC -DFAST -DALLTRUE" -sdh_make -sdh_install libsymmetrica.a "$SAGE_LOCAL/lib" -sdh_install *.h "$SAGE_LOCAL/include/symmetrica" +sdh_configure +sdh_make_install diff --git a/build/pkgs/tox/SPKG.rst b/build/pkgs/tox/SPKG.rst new file mode 100644 index 00000000000..9197632ae0a --- /dev/null +++ b/build/pkgs/tox/SPKG.rst @@ -0,0 +1,21 @@ +tox automation project +====================== + +Description +----------- + +Command line driven CI frontend and development task automation tool. + +The Sage library uses tox as an entry point for testing and linting. See ``src/tox.ini`` and ``sage --advanced``. + +Sage-the-distribution uses tox for portability testing. See ``SAGE_ROOT/tox.ini``. + +License +------- + +- MIT License + +Upstream Contact +---------------- + +https://pypi.org/project/tox/ diff --git a/build/pkgs/tox/dependencies b/build/pkgs/tox/dependencies new file mode 100644 index 00000000000..7b139dc904c --- /dev/null +++ b/build/pkgs/tox/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) packaging six | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/tox/spkg-configure.m4 b/build/pkgs/tox/spkg-configure.m4 new file mode 100644 index 00000000000..a0b68027f1a --- /dev/null +++ b/build/pkgs/tox/spkg-configure.m4 @@ -0,0 +1,18 @@ +SAGE_SPKG_CONFIGURE([tox], [ + dnl src/tox.ini has only minimal version requirements. We use 2.5.0 just to set a baseline. + dnl (SAGE_ROOT/tox.ini needs negated factor conditions introduced in 3.0.0, but it is + dnl best to run it with system tox anyway.) + m4_pushdef([TOX_MIN_VERSION], [2.5.0]) + AC_CACHE_CHECK([for tox >= ]TOX_MIN_VERSION, [ac_cv_path_TOX], [ + AC_PATH_PROGS_FEATURE_CHECK([TOX], [tox], [ + tox_version=$($ac_path_TOX --version 2> /dev/null | tail -1) + AS_IF([test -n "$tox_version"], [ + AX_COMPARE_VERSION([$tox_version], [ge], TOX_MIN_VERSION, [ + ac_cv_path_TOX="$ac_path_TOX" + ]) + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_TOX"], + [sage_spkg_install_tox=yes]) +]) diff --git a/build/sage_bootstrap/uncompress/tar_file.py b/build/sage_bootstrap/uncompress/tar_file.py index cf707e78a3a..9777d265b4d 100644 --- a/build/sage_bootstrap/uncompress/tar_file.py +++ b/build/sage_bootstrap/uncompress/tar_file.py @@ -28,8 +28,10 @@ class SageBaseTarFile(tarfile.TarFile): """ - Sage as tarfile.TarFile, but applies a reasonable umask (0022) to the - permissions of all extracted files and directories. + Same as tarfile.TarFile, but applies a reasonable umask (0022) to the + permissions of all extracted files and directories, and fixes + the encoding of file names in the tarball to be 'utf-8' instead of + depending on locale settings. Previously this applied the user's current umask per the default behavior of the ``tar`` utility, but this did not provide sufficiently reliable @@ -46,6 +48,9 @@ class SageBaseTarFile(tarfile.TarFile): umask = 0o022 def __init__(self, *args, **kwargs): + + kwargs['encoding'] = 'utf-8' + # Unfortunately the only way to get the current umask is to set it # and then restore it super(SageBaseTarFile, self).__init__(*args, **kwargs) diff --git a/configure.ac b/configure.ac index 212896af045..9cb20bdd3e0 100644 --- a/configure.ac +++ b/configure.ac @@ -201,7 +201,7 @@ dnl Exit autoconf with exit code 16 in this case. This will be dnl caught by the bootstrap script. m4_exit(16)]) -PKG_PROG_PKG_CONFIG +PKG_PROG_PKG_CONFIG([0.29]) AC_CHECK_PROG(found_ranlib, ranlib, yes, no) if test x$found_ranlib != xyes @@ -366,21 +366,6 @@ SAGE_CHECK_OSX_SUPPORTED() # Collect substitutions for build/make/Makefile.in ############################################################################### -# Python version -AC_ARG_WITH([python], -[AS_HELP_STRING([--with-python=3], - [build and use Python 3 (default)])]) - -case "$with_python" in - 3) SAGE_PYTHON_VERSION=3;; - 3*) AC_MSG_WARN([the only allowed value for --with-python is 3; specific Python 3.x versions cannot be requested using --with-python. For compatibility reasons, this is only a warning. Use './configure PYTHON3=/path/to/python3' to use a specific Python 3 binary for the Sage venv.]) - SAGE_PYTHON_VERSION=3;; - "") SAGE_PYTHON_VERSION=3;; - *) - AC_MSG_ERROR([the only allowed value for --with-python is 3. Support for Python 2 has been removed in Sage 9.2.]);; -esac -AC_SUBST(SAGE_PYTHON_VERSION) - # $(TOOLCHAIN) variable containing prerequisites for the build SAGE_TOOLCHAIN=gcc if test "$SAGE_INSTALL_CCACHE" = yes ; then diff --git a/docker/Dockerfile b/docker/Dockerfile index e1bc5aeec2f..fb4a3cf6719 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -163,14 +163,12 @@ ENV SAGE_INSTALL_GCC no # Set MAKEFLAGS 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 WITH_PYTHON=2 -ENV WITH_PYTHON $WITH_PYTHON ARG MAKEFLAGS="-j2" ENV MAKEFLAGS $MAKEFLAGS ARG SAGE_NUM_THREADS="2" ENV SAGE_NUM_THREADS $SAGE_NUM_THREADS RUN make configure -RUN ./configure --with-python=$WITH_PYTHON +RUN ./configure RUN make build ################################################################################ diff --git a/m4/sage_check_conda_compilers.m4 b/m4/sage_check_conda_compilers.m4 index 05cdf8773f2..03e43e77573 100644 --- a/m4/sage_check_conda_compilers.m4 +++ b/m4/sage_check_conda_compilers.m4 @@ -5,12 +5,30 @@ AC_DEFUN([SAGE_CHECK_CONDA_COMPILERS], [ AS_IF([test $have_conda_active = yes], [ dnl A conda environment is active. dnl #27699: Conda compiler packages must be installed - need_pkgs="c-compiler cxx-compiler fortran-compiler" + dnl #30662: Do not allow system pkg-config to give us library paths that bypass conda + AS_VAR_SET([reasons], []) + AS_VAR_SET([need_pkgs], []) AS_IF([test -z "$CC" -o -z "$CXX" -o -z "$FC" ], [ - AC_MSG_ERROR([A conda environment ($CONDA_DEFAULT_ENV) is active, but -at least one of the environment variables CC, CXX, FC is not set, which indicates -that the conda environment is missing the following conda packages required -for building Sage: + AS_VAR_APPEND([reasons], + [", but +at least one of the environment variables CC, CXX, FC is not set"]) + AS_VAR_APPEND([need_pkgs], + [" c-compiler cxx-compiler fortran-compiler"]) + ]) + AS_IF([test "$CONDA_PREFIX/bin/pkg-config" != "$PKG_CONFIG"], [ + AS_IF([test -z "$reasons"], + [AS_VAR_APPEND([reasons], [", but"])], + [AS_VAR_APPEND([reasons], [" and"])]) + AS_VAR_APPEND([reasons], + [" +the pkg-config command is not provided by the conda environment"]) + AS_VAR_APPEND([need_pkgs], + [" pkg-config"]) + ]) + AS_IF([test -n "$need_pkgs"], [ + AC_MSG_ERROR([A conda environment ($CONDA_DEFAULT_ENV) is active$reasons, +which indicates that the conda environment is missing +the following conda packages required for building Sage: $need_pkgs For building Sage, either: - activate a conda environment that has these packages, using: diff --git a/m4/sage_check_python_for_venv.m4 b/m4/sage_check_python_for_venv.m4 new file mode 100644 index 00000000000..0f8082bae3f --- /dev/null +++ b/m4/sage_check_python_for_venv.m4 @@ -0,0 +1,137 @@ +# SAGE_CHECK_PYTHON_FOR_VENV(PYTHON_EXE, MIN_VERSION, LT_VERSION, REQUIRED_MODULES, COMMANDS_IF_GOOD) + +AC_DEFUN([SAGE_CHECK_PYTHON_FOR_VENV], [ + m4_pushdef([PYTHON_EXE], [$1]) + m4_pushdef([MIN_VERSION], [$2]) + m4_pushdef([LT_VERSION], [$3]) + m4_pushdef([REQUIRED_MODULES], [$4]) + m4_pushdef([COMMANDS_IF_GOOD], [$5]) + + AC_MSG_CHECKING([... whether ]PYTHON_EXE[ is good]) + python3_version=`"PYTHON_EXE" --version 2>&1 \ + | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` + AS_IF([test -n "$python3_version"], [ + AX_COMPARE_VERSION([$python3_version], [ge], MIN_VERSION, [ + AX_COMPARE_VERSION([$python3_version], [lt], LT_VERSION, [ + dnl Because the system python is not used directly but rather in a venv without site-packages, + dnl we test whether the module will be available in a venv. + dnl Otherwise, some system site-package may be providing this module to the system python. + dnl m4_define([conftest_venv], [config-venv]) .... for debugging only + rm -rf conftest_venv + AS_IF(["]PYTHON_EXE[" build/bin/sage-venv conftest_venv && conftest_venv/bin/python3 -c "import ]REQUIRED_MODULES["], [ + AC_LANG_PUSH([C]) + AC_LANG_CONFTEST([ + AC_LANG_SOURCE([[ +#define PY_SSIZE_T_CLEAN +#include +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +static struct PyModuleDef spammodule = { + PyModuleDef_HEAD_INIT, + "spam", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + SpamMethods +}; +PyMODINIT_FUNC +PyInit_spam(void) +{ + PyObject *m; + + m = PyModule_Create(&spammodule); + return m; +} + ]]) + ]) + AC_LANG_POP([C]) + cat > conftest.py <& AS_MESSAGE_LOG_FD + echo CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD + AS_IF([CC="$CC" CXX="$CXX" conftest_venv/bin/python3 conftest.py --verbose build --build-base=conftest.dir >& AS_MESSAGE_LOG_FD 2>&1 ], [ + rm -rf conftest.* + AC_LANG_PUSH([C++]) + AC_LANG_CONFTEST([ + AC_LANG_SOURCE([[ +#define PY_SSIZE_T_CLEAN +#include +static PyMethodDef SpamMethods[] = { + {NULL, NULL, 0, NULL} /* Sentinel */ +}; +static struct PyModuleDef spammodule = { + PyModuleDef_HEAD_INIT, + "spam", /* name of module */ + NULL, /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + SpamMethods +}; +PyMODINIT_FUNC +PyInit_spam(void) +{ + PyObject *m; + + m = PyModule_Create(&spammodule); + return m; +} +// Partial C++11 test, from ax_cxx_compile_stdcxx.m4 + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + ]]) + ]) + AC_LANG_POP([C++]) + cat > conftest.py <& AS_MESSAGE_LOG_FD 2>&1 ], + [COMMANDS_IF_GOOD], [ + AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C++ 11 extension]) + ]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range, and the modules can be imported, but distutils cannot build a C extension]) + ]) + ], [ + AC_MSG_RESULT([no, the version is in the supported range but cannot import one of the required modules: ]REQUIRED_MODULES) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too recent]) + ]) + ], [ + AC_MSG_RESULT([no, $python3_version is too old]) + ]) + ], [ + AC_MSG_RESULT([no, "]PYTHON_EXE[ --version" does not work]) + ]) + + + m4_popdef([PYTHON_EXE]) + m4_popdef([MIN_VERSION]) + m4_popdef([LT_VERSION]) + m4_popdef([REQUIRED_MODULES]) + m4_popdef([COMMANDS_IF_GOOD]) + +]) diff --git a/src/.codespell-dictionary.txt b/src/.codespell-dictionary.txt new file mode 100644 index 00000000000..5eba2d5784d --- /dev/null +++ b/src/.codespell-dictionary.txt @@ -0,0 +1,3 @@ +ans->answer, and, +numer->numerator, number, +numers->numerators, numbers, diff --git a/src/.codespell-ignore.txt b/src/.codespell-ignore.txt new file mode 100644 index 00000000000..270d2da903d --- /dev/null +++ b/src/.codespell-ignore.txt @@ -0,0 +1,2 @@ +Hart +PRing diff --git a/src/.relint.yml b/src/.relint.yml index 2addd3f4d8a..eec47836e17 100644 --- a/src/.relint.yml +++ b/src/.relint.yml @@ -53,3 +53,9 @@ hint: | Use "Sage" or "SageMath" instead pattern: '\bSAGE\b' + +# https://trac.sagemath.org/ticket/30585 +- name: 'typo "homogenous" detected' + hint: | + in mathematics it should be "homogeneous" + pattern: 'homogenous' diff --git a/src/bin/sage b/src/bin/sage index a1a6319e4ec..5ac6b4d4ce7 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -405,8 +405,8 @@ usage_advanced() { echo " --tox [options] -- general entry point for testing" echo " and linting of the Sage library" echo " -e -- run specific test environments (default: run all)" - tox -c "$SAGE_SRC" --listenvs -v 2>/dev/null | sed -n '/->/s/^/ /;s/(same as/\ - (same as/;s/->/ --/p;' + tox -c "$SAGE_SRC" --listenvs -v 2>/dev/null | sed -n '/->/s/^/ /;s/(/\ + (/;s/->/ --/p;' echo " -p auto -- run test environments in parallel" echo " --help -- show tox help" command -v tox &>/dev/null || \ @@ -529,11 +529,7 @@ fi if [ "$1" = '-python' -o "$1" = '--python' ]; then shift - if [ "$SAGE_PYTHON3" = 'yes' ]; then - exec "$SAGE_LOCAL"/bin/python3 "$@" - else - exec "$SAGE_LOCAL"/bin/python2 "$@" - fi + exec "$SAGE_LOCAL"/bin/python3 "$@" fi if [ "$1" = '-python3' -o "$1" = '--python3' ]; then @@ -612,7 +608,11 @@ fi if [ "$1" = '-maxima' -o "$1" = '--maxima' ]; then shift - exec maxima "$@" + maxima_cmd=$(sage-config MAXIMA 2>/dev/null) + if [ -z "${maxima_cmd}" ]; then + maxima_cmd="maxima -l ecl" + fi + exec $maxima_cmd "$@" fi if [ "$1" = '-mwrank' -o "$1" = '--mwrank' ]; then diff --git a/src/bin/sage-env b/src/bin/sage-env index d6d51ffc5e4..e175b431a24 100644 --- a/src/bin/sage-env +++ b/src/bin/sage-env @@ -227,10 +227,6 @@ if [ "$UNAME" = "Darwin" ]; then export OBJC OBJCXX fi -if [ "$SAGE_PYTHON_VERSION" = 3 ]; then - export SAGE_PYTHON3=yes -fi - # Set other Fortran-related compiler variables export F77="$FC" export F90="$FC" # Needed for SciPy diff --git a/src/bin/sage-env-config.in b/src/bin/sage-env-config.in index 7872a930dc9..fbd88585ffd 100644 --- a/src/bin/sage-env-config.in +++ b/src/bin/sage-env-config.in @@ -46,7 +46,6 @@ CONFIGURED_OBJCXX="@OBJCXX@" ####################################### # Other configuration (exported) ####################################### -export SAGE_PYTHON_VERSION=@SAGE_PYTHON_VERSION@ export PYTHON_FOR_VENV="@PYTHON_FOR_VENV@" ####################################### diff --git a/src/bin/sage-valgrind b/src/bin/sage-valgrind index b2452f0c7a8..ae0d5dae389 100755 --- a/src/bin/sage-valgrind +++ b/src/bin/sage-valgrind @@ -5,12 +5,8 @@ if [ -z $SAGE_EXTCODE ]; then fi SUPP="" -# Our python2/3 spkgs and also Debian install these files as python.supp and python3.supp -if [ "$SAGE_PYTHON_VERSION" = 2 ]; then - PYTHON_SUPP_FILENAME="python.supp" -else - PYTHON_SUPP_FILENAME="python$SAGE_PYTHON_VERSION.supp" -fi +# Our python spkgs and also Debian install this file as python3.supp +PYTHON_SUPP_FILENAME="python3.supp" for dir in "$SAGE_LOCAL/lib/valgrind" "/usr/local/lib/valgrind" "/usr/lib64/valgrind" "/usr/lib/valgrind" ; do if [ -f "$dir"/"$PYTHON_SUPP_FILENAME" ]; then SUPP=" --suppressions=$dir/$PYTHON_SUPP_FILENAME" diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index a3f8ec3ed0a..1265983e01e 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.2.beta13' -SAGE_RELEASE_DATE='2020-09-21' -SAGE_VERSION_BANNER='SageMath version 9.2.beta13, Release Date: 2020-09-21' +SAGE_VERSION='9.2.beta14' +SAGE_RELEASE_DATE='2020-09-30' +SAGE_VERSION_BANNER='SageMath version 9.2.beta14, Release Date: 2020-09-30' diff --git a/src/doc/bootstrap b/src/doc/bootstrap index 4577463c06b..616ae6c9913 100755 --- a/src/doc/bootstrap +++ b/src/doc/bootstrap @@ -5,6 +5,9 @@ # # This script is run by SAGE_ROOT/bootstrap as part of the bootstrapping phase # (before configure, before creating source distributions). +# +# The BOOTSTRAP_QUIET variable is set by the top-level +# bootstrap script and controls how verbose we are. ######################################################################## set -e @@ -49,14 +52,18 @@ for SYSTEM in arch debian fedora cygwin homebrew; do fi fi done - echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/$SYSTEM.txt and "$OUTPUT_DIR"/$SYSTEM-optional.txt + if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/$SYSTEM.txt and "$OUTPUT_DIR"/$SYSTEM-optional.txt + fi echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM.txt echo "$(sage-print-system-package-command $SYSTEM --prompt --sudo install $(echo $(echo $OPTIONAL_SYSTEM_PACKAGES | xargs -n 1 echo | sort)))" > "$OUTPUT_DIR"/$SYSTEM-optional.txt done OUTPUT_DIR="src/doc/en/reference/spkg" mkdir -p "$OUTPUT_DIR" -echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/"*.rst" +if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo >&2 $0:$LINENO: installing "$OUTPUT_DIR"/"*.rst" +fi OUTPUT_INDEX="$OUTPUT_DIR"/index.rst cat > "$OUTPUT_INDEX" <&2 $0:$LINENO: installing "$OUTPUT" +if [ "${BOOTSTRAP_QUIET}" = "no" ]; then + echo >&2 $0:$LINENO: installing "$OUTPUT" +fi ./sage -advanced > "$OUTPUT" diff --git a/src/doc/en/constructions/plotting.rst b/src/doc/en/constructions/plotting.rst index 6bf1958a31f..ef8ea7c12b8 100644 --- a/src/doc/en/constructions/plotting.rst +++ b/src/doc/en/constructions/plotting.rst @@ -214,7 +214,7 @@ Terminal application.) :: sage: maxima.eval('load("plotdf");') - '".../share/maxima/.../share/dynamics/plotdf.lisp"' + '".../share/maxima.../share/dynamics/plotdf.lisp"' sage: maxima.eval('plotdf(x+y,[trajectory_at,2,-0.1]); ') # not tested This plots a direction field (the plotdf Maxima package was also diff --git a/src/doc/en/developer/portability_testing.rst b/src/doc/en/developer/portability_testing.rst index 07eecb3858e..9cfad03dc4a 100644 --- a/src/doc/en/developer/portability_testing.rst +++ b/src/doc/en/developer/portability_testing.rst @@ -371,7 +371,7 @@ command ``docker build``. For example:: [mkoeppe@sage sage]$ docker build . -f Dockerfile \ --build-arg BASE_IMAGE=ubuntu:latest \ --build-arg NUMPROC=4 \ - --build-arg EXTRA_CONFIGURE_ARGS="--with-python=2" + --build-arg EXTRA_CONFIGURE_ARGS="--with-python=/usr/bin/python3.42" These arguments (and their default values) are defined using ``ARG`` commands in the ``Dockerfile``. @@ -616,9 +616,6 @@ such as ``debian-buster-standard`` and ``centos-7-i386-minimal``. Finally, the **configuration** factor (which is allowed to be empty) controls how the ``configure`` script is run. -- ``python2`` adds the argument ``--with-python=2`` to the - ``configure`` run. - The factors are connected by a hyphen to name a tox environment. (The order of the factors does not matter; however, for consistency and because the ordered name is used for caching purposes, we recommend to @@ -627,7 +624,7 @@ use the factors in the listed order.) To run an environment:: [mkoeppe@sage sage]$ tox -e docker-slackware-14.2-minimal - [mkoeppe@sage sage]$ tox -e docker-ubuntu-bionic-standard-python2 + [mkoeppe@sage sage]$ tox -e docker-ubuntu-bionic-standard Arbitrary extra arguments to ``docker build`` can be supplied through the environment variable ``EXTRA_DOCKER_BUILD_ARGS``. For example, @@ -779,8 +776,7 @@ keep the source tree clean to the extent possible. In particular: tox environment directory, and a symbolic link. This makes it possible for advanced users to test several ``local`` -tox environments (such as ``local-direct`` and -``local-direct-python2``) out of one worktree. However, because a +tox environments (such as ``local-direct``) out of one worktree. However, because a build still writes configuration scripts and build artefacts (such as ``config.status``) into the worktree, only one ``local`` build can run at a time in a given worktree. diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 6553f603c8c..e687562aaa7 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -88,7 +88,7 @@ computer: - **perl**: version 5.8.0 or later. - **ar** and **ranlib**: can be obtained as part of GNU binutils. - **tar**: GNU tar version 1.17 or later, or BSD tar. -- **python**: Python 3, 3.3 or later, or Python 2 (deprecated), 2.6 or later. +- **python**: Python 3, 3.6 or later, or Python 2.7 (deprecated). Other versions of these may work, but they are untested. @@ -167,6 +167,20 @@ that your assembler understands all instructions for your processor. On Linux, this means you need a recent version of ``binutils``; on macOS you need a recent version of Xcode. +Python for venv +^^^^^^^^^^^^^^^ + +By default, Sage will try to use system's `python3` to set up a virtual +environment, a.k.a. `venv `_ +rather than building a Python 3 installation from scratch. +Use the configure option ``--without-system-python3`` in case you want Python 3 +built from scratch. + +You can also use ``--with-python=/path/to/python3_binary`` to tell Sage to use +``/path/to/python3_binary`` to set up the venv. Note that setting up venv requires +a number of Python modules to be availabe within the Python in question. Currently, +for Sage 9.2, these modules are as follows: sqlite3, ctypes, math, hashlib, crypt, +readline, socket, zlib, distutils.core - they will be checked for by configure. Other notes ^^^^^^^^^^^ diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index e61b5e38f1f..f31986b8385 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -138,13 +138,6 @@ PARI sage/libs/pari/convert_sage sage/rings/pari_ring -PPL ---- -.. toctree:: - :maxdepth: 2 - - sage/libs/ppl - ratpoints --------- .. toctree:: diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 2b0273da06e..12304e4bac6 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -1339,6 +1339,9 @@ default implementation. Hence: - Next, we implement a new version of the "usual" fraction field functor, having the same rank, but returning our new implementation. - We make our new implementation the default, by virtue of a merge method. +- Since our fraction fields accept an optional argument ``category``, we pass + the optional arguments to the construction functor, which will in turn use + it to create a fraction field. .. WARNING:: @@ -1350,10 +1353,12 @@ default implementation. Hence: sage: from sage.categories.pushout import ConstructionFunctor sage: class MyFracFunctor(ConstructionFunctor): ....: rank = 5 - ....: def __init__(self): + ....: def __init__(self, args=None, kwds=None): + ....: self.args = args or () + ....: self.kwds = kwds or {} ....: ConstructionFunctor.__init__(self, IntegralDomains(), Fields()) ....: def _apply_functor(self, R): - ....: return MyFrac(R) + ....: return MyFrac(R,*self.args,**self.kwds) ....: def merge(self, other): ....: if isinstance(other, (type(self), sage.categories.pushout.FractionField)): ....: return self @@ -1392,14 +1397,18 @@ We verify that our functor can really be used to construct our implementation of .. end of output -There remains to let our new fraction fields know about the new construction functor: - +There remains to let our new fraction fields know about the new construction +functor. The arguments that were used when creating the fraction field are +stored as an attribute---this is a feature provided by +:class:`~sage.structure.unique_representation.CachedRepresentation`. We pass +all but the first of these arguments to the construction functor, such that +the construction functor is able to reconstruct the fraction field. :: sage: class MyFrac(MyFrac): ....: def construction(self): - ....: return MyFracFunctor(), self.base() + ....: return MyFracFunctor(self._reduction[1][1:], self._reduction[2]), self.base() .. end of output @@ -1537,6 +1546,7 @@ Here are the tests that form the test suite of quotient fields:: '_test_cardinality', '_test_characteristic', '_test_characteristic_fields', + '_test_construction', '_test_distributivity', '_test_divides', '_test_elements', @@ -1585,6 +1595,7 @@ Let us see what tests are actually performed:: running ._test_category() . . . pass running ._test_characteristic() . . . pass running ._test_characteristic_fields() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_divides() . . . pass running ._test_elements() . . . @@ -1758,6 +1769,7 @@ interesting. running ._test_category() . . . pass running ._test_characteristic() . . . pass running ._test_characteristic_fields() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_divides() . . . pass running ._test_elements() . . . diff --git a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst index d60a3dce097..c8cbd71ca83 100644 --- a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst +++ b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst @@ -196,6 +196,7 @@ Ok, let's run the tests:: running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() diff --git a/src/sage/__init__.py b/src/sage/__init__.py index 5be0bace31a..eb317071816 100644 --- a/src/sage/__init__.py +++ b/src/sage/__init__.py @@ -84,7 +84,8 @@ def patch_sqlite3(): # then return - import sqlite3, functools + import sqlite3 + import functools orig_sqlite3_connect = sqlite3.connect @functools.wraps(orig_sqlite3_connect) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 3c71f07c980..4f011edf68a 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -99,6 +99,7 @@ from sage.rings.quotient_ring import QuotientRing_nc from sage.rings.quotient_ring_element import QuotientRingElement from sage.misc.cachefunc import cached_function +from sage.misc.superseded import deprecated_function_alias def sorting_keys(element): @@ -1505,9 +1506,9 @@ def is_homogeneous(self, total=False): return False return True - def homogenous_parts(self): + def homogeneous_parts(self): r""" - Return the homogenous parts of the element. The result is given as + Return the homogeneous parts of the element. The result is given as a dictionary indexed by degree. @@ -1515,7 +1516,7 @@ def homogenous_parts(self): sage: A. = GradedCommutativeAlgebra(QQ) sage: a = e1*e3*e5-3*e2*e3*e5 + e1*e2 -2*e3 + e5 - sage: a.homogenous_parts() + sage: a.homogeneous_parts() {1: -2*e3 + e5, 2: e1*e2, 3: e1*e3*e5 - 3*e2*e3*e5} """ dic = self.dict() @@ -1529,6 +1530,8 @@ def homogenous_parts(self): res[deg] = term return {i: res[i] for i in sorted(res.keys())} + homogenous_parts = deprecated_function_alias(30585, homogeneous_parts) + def dict(self): r""" A dictionary that determines the element. @@ -1667,7 +1670,7 @@ def __init__(self, base, degrees, names=None, R=None, I=None): sage: A. = GradedCommutativeAlgebra(QQ, degrees=((1,0), (0,1), (1,1))) sage: TestSuite(A).run() sage: B. = GradedCommutativeAlgebra(GF(2), degrees=((3,2),)) - sage: TestSuite(B).run() + sage: TestSuite(B).run(skip=['_test_construction']) sage: C = GradedCommutativeAlgebra(GF(7), degrees=((3,2),)) sage: TestSuite(C).run() """ @@ -2930,7 +2933,7 @@ def is_cohomologous_to(self, other): def cohomology_class(self): r""" - Return the cohomology class of an homogenous cycle, as an element + Return the cohomology class of an homogeneous cycle, as an element of the corresponding cohomology group. EXAMPLES:: @@ -2967,7 +2970,7 @@ def cohomology_class(self): True """ if not self.is_homogeneous(): - raise ValueError("The element is not homogenous") + raise ValueError("The element is not homogeneous") if not self.differential().is_zero(): raise ValueError("The element is not closed") d = self.degree() @@ -3005,7 +3008,7 @@ def _cohomology_class_dict(self): raise ValueError("The element is not closed") if not self.is_homogeneous(): res = {} - for d in self.homogenous_parts().values(): + for d in self.homogeneous_parts().values(): res.update(d._cohomology_class_dict()) return res d = self.degree() diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index a16cb9b3d29..7391dd98123 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -192,7 +192,7 @@ class FreeAlgebraFactory(UniqueFactory): By :trac:`7797`, we provide a different implementation of free algebras, based on Singular's "letterplace rings". Our letterplace wrapper allows for choosing positive integral degree weights for the - generators of the free algebra. However, only (weighted) homogenous + generators of the free algebra. However, only (weighted) homogeneous elements are supported. Of course, isomorphic algebras in different implementations are not identical:: @@ -747,7 +747,7 @@ def product_on_basis(self, x, y): """ return self.monomial(x * y) - def quotient(self, mons, mats=None, names=None): + def quotient(self, mons, mats=None, names=None, **args): """ Return a quotient algebra. diff --git a/src/sage/algebras/free_zinbiel_algebra.py b/src/sage/algebras/free_zinbiel_algebra.py index 63cd7023e57..c8eed9ff226 100644 --- a/src/sage/algebras/free_zinbiel_algebra.py +++ b/src/sage/algebras/free_zinbiel_algebra.py @@ -495,7 +495,11 @@ def construction(self): sage: F(QQ) Free Zinbiel algebra on generators (Z[x], Z[y]) over Rational Field """ - return ZinbielFunctor(self.variable_names()), self.base_ring() + if self._n is None: + A = self._indices.alphabet() + else: + A = self.variable_names() + return ZinbielFunctor(A), self.base_ring() class ZinbielFunctor(ConstructionFunctor): @@ -526,7 +530,7 @@ class ZinbielFunctor(ConstructionFunctor): """ rank = 9 - def __init__(self, vars): + def __init__(self, variables): """ EXAMPLES:: @@ -537,7 +541,8 @@ def __init__(self, vars): Free Zinbiel algebra on generators (Z[x], Z[y]) over Integer Ring """ Functor.__init__(self, Rings(), Magmas()) - self.vars = vars + self.vars = variables + self._finite_vars = bool(isinstance(variables, (list, tuple)) or variables in Sets().Finite()) def _apply_functor(self, R): """ @@ -552,9 +557,17 @@ def _apply_functor(self, R): sage: F(ZZ) # indirect doctest Free Zinbiel algebra on generators (Z[x], Z[y], Z[z]) - over Integer Ring + over Integer Ring + + sage: R = algebras.FreeZinbiel(QQ, ZZ) + sage: F = R.construction()[0]; F + Zinbiel[Integer Ring] + sage: F(ZZ) # indirect doctest + Free Zinbiel algebra on generators indexed by Integer Ring over Integer Ring """ - return FreeZinbielAlgebra(R, len(self.vars), self.vars) + if self._finite_vars: + return FreeZinbielAlgebra(R, len(self.vars), self.vars) + return FreeZinbielAlgebra(R, self.vars) def _apply_functor_to_morphism(self, f): """ @@ -637,9 +650,7 @@ def __mul__(self, other): if isinstance(other, IdentityConstructionFunctor): return self if isinstance(other, ZinbielFunctor): - def check(x): - return isinstance(x, (list, tuple)) or x in Sets().Finite() - if not check(self.vars) or not check(other.vars): + if not self._finite_vars or not other._finite_vars: raise CoercionException("Unable to determine overlap for infinite sets") if set(self.vars).intersection(other.vars): raise CoercionException("Overlapping variables (%s,%s)" % @@ -716,6 +727,11 @@ def _repr_(self): sage: algebras.FreeZinbiel(QQ,'x,y,z,t').construction()[0] Zinbiel[x,y,z,t] + + sage: algebras.FreeZinbiel(QQ, ZZ).construction()[0] + Zinbiel[Integer Ring] """ - return "Zinbiel[%s]" % ','.join(self.vars) + if self._finite_vars: + return "Zinbiel[%s]" % ','.join(self.vars) + return "Zinbiel[{}]".format(self.vars) diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 8b7777cad05..0cfb7bb3d79 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -1153,14 +1153,14 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): EXAMPLES:: sage: x = var('x') - sage: f = (1+1/x)^x - sage: f.limit(x = oo) + sage: f = (1 + 1/x)^x + sage: f.limit(x=oo) e - sage: f.limit(x = 5) + sage: f.limit(x=5) 7776/3125 - sage: f.limit(x = 1.2) + sage: f.limit(x=1.2) 2.06961575467... - sage: f.limit(x = I, taylor=True) + sage: f.limit(x=I, taylor=True) (-I + 1)^I sage: f(x=1.2) 2.0696157546720... @@ -1168,7 +1168,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): (-I + 1)^I sage: CDF(f(x=I)) 2.0628722350809046 + 0.7450070621797239*I - sage: CDF(f.limit(x = I)) + sage: CDF(f.limit(x=I)) 2.0628722350809046 + 0.7450070621797239*I Notice that Maxima may ask for more information:: @@ -1196,7 +1196,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): more details) Is a an integer? sage: assume(a,'integer') - sage: limit(x^a,x=0) + sage: limit(x^a, x=0) Traceback (most recent call last): ... ValueError: Computation failed since Maxima requested additional @@ -1204,26 +1204,26 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): (example of legal syntax is 'assume(a>0)', see `assume?` for more details) Is a an even number? - sage: assume(a,'even') - sage: limit(x^a,x=0) + sage: assume(a, 'even') + sage: limit(x^a, x=0) 0 sage: forget() More examples:: - sage: limit(x*log(x), x = 0, dir='+') + sage: limit(x*log(x), x=0, dir='+') 0 - sage: lim((x+1)^(1/x), x = 0) + sage: lim((x+1)^(1/x), x=0) e - sage: lim(e^x/x, x = oo) + sage: lim(e^x/x, x=oo) +Infinity - sage: lim(e^x/x, x = -oo) + sage: lim(e^x/x, x=-oo) 0 - sage: lim(-e^x/x, x = oo) + sage: lim(-e^x/x, x=oo) -Infinity - sage: lim((cos(x))/(x^2), x = 0) + sage: lim((cos(x))/(x^2), x=0) +Infinity - sage: lim(sqrt(x^2+1) - x, x = oo) + sage: lim(sqrt(x^2+1) - x, x=oo) 0 sage: lim(x^2/(sec(x)-1), x=0) 2 @@ -1238,8 +1238,8 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): :: - sage: f = log(log(x))/log(x) - sage: forget(); assume(x<-2); lim(f, x=0, taylor=True) + sage: f = log(log(x)) / log(x) + sage: forget(); assume(x < -2); lim(f, x=0, taylor=True) 0 sage: forget() @@ -1283,7 +1283,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): Maxima gives the right answer here, too, showing that :trac:`4142` is fixed:: - sage: f = sqrt(1-x^2) + sage: f = sqrt(1 - x^2) sage: g = diff(f, x); g -x/sqrt(-x^2 + 1) sage: limit(g, x=1, dir='-') @@ -1300,24 +1300,24 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): Check that :trac:`8942` is fixed:: - sage: f(x) = (cos(pi/4-x) - tan(x)) / (1 - sin(pi/4+x)) - sage: limit(f(x), x = pi/4, dir='minus') + sage: f(x) = (cos(pi/4 - x) - tan(x)) / (1 - sin(pi/4 + x)) + sage: limit(f(x), x=pi/4, dir='minus') +Infinity - sage: limit(f(x), x = pi/4, dir='plus') + sage: limit(f(x), x=pi/4, dir='plus') -Infinity - sage: limit(f(x), x = pi/4) + sage: limit(f(x), x=pi/4) Infinity Check that :trac:`12708` is fixed:: - sage: limit(tanh(x),x=0) + sage: limit(tanh(x), x=0) 0 Check that :trac:`15386` is fixed:: sage: n = var('n') sage: assume(n>0) - sage: sequence = -(3*n^2 + 1)*(-1)^n/sqrt(n^5 + 8*n^3 + 8) + sage: sequence = -(3*n^2 + 1)*(-1)^n / sqrt(n^5 + 8*n^3 + 8) sage: limit(sequence, n=infinity) 0 @@ -1328,7 +1328,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): From :trac:`14677`:: - sage: f = (x^x-sin(x)^sin(x))/(x^3*log(x)) + sage: f = (x^x - sin(x)^sin(x)) / (x^3*log(x)) sage: limit(f, x=0, algorithm='fricas') # optional - fricas und @@ -1342,6 +1342,10 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): sage: limit(f, y=0, algorithm='fricas') # optional - fricas 0 + From :trac:`26060`:: + + sage: limit(x / (x + 2^x + cos(x)), x=-infinity) + 1 """ if not isinstance(ex, Expression): ex = SR(ex) diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 5606ab39da0..5bee0b2518c 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -69,6 +69,7 @@ class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring): running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() diff --git a/src/sage/categories/cartesian_product.py b/src/sage/categories/cartesian_product.py index bb351b34588..b6ad5e7cf5b 100644 --- a/src/sage/categories/cartesian_product.py +++ b/src/sage/categories/cartesian_product.py @@ -20,7 +20,7 @@ class CartesianProductFunctor(CovariantFunctorialConstruction, MultivariateConstructionFunctor): """ - A singleton class for the Cartesian product functor. + The Cartesian product functor. EXAMPLES:: @@ -109,7 +109,7 @@ class CartesianProductFunctor(CovariantFunctorialConstruction, MultivariateConst _functor_category = "CartesianProducts" symbol = " (+) " - def __init__(self): + def __init__(self, category=None): r""" Constructor. See :class:`CartesianProductFunctor` for details. @@ -120,8 +120,13 @@ def __init__(self): The cartesian_product functorial construction """ CovariantFunctorialConstruction.__init__(self) + self._forced_category = category from sage.categories.sets_cat import Sets - MultivariateConstructionFunctor.__init__(self, Sets(), Sets()) + if self._forced_category is not None: + codomain = self._forced_category + else: + codomain = Sets() + MultivariateConstructionFunctor.__init__(self, Sets(), codomain) def __call__(self, args, **kwds): r""" @@ -175,12 +180,41 @@ def __call__(self, args, **kwds): S = Sets() args = [S(a, enumerated_set=True) for a in args] elif not args: - from sage.categories.sets_cat import Sets + if self._forced_category is None: + from sage.categories.sets_cat import Sets + cat = Sets().CartesianProducts() + else: + cat = self._forced_category from sage.sets.cartesian_product import CartesianProduct - return CartesianProduct((), Sets().CartesianProducts()) + return CartesianProduct((), cat) + elif self._forced_category is not None: + return super(CartesianProductFunctor, self).__call__(args, category=self._forced_category, **kwds) return super(CartesianProductFunctor, self).__call__(args, **kwds) + def __eq__(self, other): + r""" + Comparison ignores the ``category`` parameter. + + TESTS:: + + sage: from sage.categories.cartesian_product import CartesianProductFunctor + sage: cartesian_product([ZZ, ZZ]).construction()[0] == CartesianProductFunctor() + True + """ + return isinstance(other, CartesianProductFunctor) + + def __ne__(self, other): + r""" + Comparison ignores the ``category`` parameter. + + TESTS:: + + sage: from sage.categories.cartesian_product import CartesianProductFunctor + sage: cartesian_product([ZZ, ZZ]).construction()[0] != CartesianProductFunctor() + False + """ + return not (self == other) class CartesianProductsCategory(CovariantConstructionCategory): r""" diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 4e8f58c84f9..4948c5c0d3f 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -2898,7 +2898,6 @@ def _subcategory_hook_(self, C): return False return Unknown - ############################################################# # Join of several categories ############################################################# diff --git a/src/sage/categories/classical_crystals.py b/src/sage/categories/classical_crystals.py index 40360ac4d22..177e95ff1ba 100644 --- a/src/sage/categories/classical_crystals.py +++ b/src/sage/categories/classical_crystals.py @@ -41,6 +41,7 @@ class ClassicalCrystals(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -265,6 +266,7 @@ def __iter__(self): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -293,6 +295,7 @@ def __iter__(self): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -346,6 +349,7 @@ def __iter__(self): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index 4b12ce0d8f7..69a0140c08c 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -78,6 +78,7 @@ class Crystals(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/commutative_additive_monoids.py b/src/sage/categories/examples/commutative_additive_monoids.py index 0e3821e30e8..c7e01d6e5c6 100644 --- a/src/sage/categories/examples/commutative_additive_monoids.py +++ b/src/sage/categories/examples/commutative_additive_monoids.py @@ -44,6 +44,7 @@ class FreeCommutativeAdditiveMonoid(FreeCommutativeAdditiveSemigroup): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/commutative_additive_semigroups.py b/src/sage/categories/examples/commutative_additive_semigroups.py index 9f2ed73394f..a278d781285 100644 --- a/src/sage/categories/examples/commutative_additive_semigroups.py +++ b/src/sage/categories/examples/commutative_additive_semigroups.py @@ -45,6 +45,7 @@ class FreeCommutativeAdditiveSemigroup(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/crystals.py b/src/sage/categories/examples/crystals.py index 60ea90688d8..576f8edc795 100644 --- a/src/sage/categories/examples/crystals.py +++ b/src/sage/categories/examples/crystals.py @@ -70,6 +70,7 @@ class HighestWeightCrystalOfTypeA(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/facade_sets.py b/src/sage/categories/examples/facade_sets.py index d54a10566dd..737ea7ae8d0 100644 --- a/src/sage/categories/examples/facade_sets.py +++ b/src/sage/categories/examples/facade_sets.py @@ -37,6 +37,7 @@ class PositiveIntegerMonoid(UniqueRepresentation, Parent): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -138,6 +139,7 @@ class IntegersCompletion(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/finite_enumerated_sets.py b/src/sage/categories/examples/finite_enumerated_sets.py index 3d1e17f0546..1d5f9a99f03 100644 --- a/src/sage/categories/examples/finite_enumerated_sets.py +++ b/src/sage/categories/examples/finite_enumerated_sets.py @@ -40,6 +40,7 @@ class Example(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/finite_monoids.py b/src/sage/categories/examples/finite_monoids.py index 9e1724669ef..52e473ee6a1 100644 --- a/src/sage/categories/examples/finite_monoids.py +++ b/src/sage/categories/examples/finite_monoids.py @@ -38,6 +38,7 @@ class IntegerModMonoid(UniqueRepresentation, Parent): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/finite_semigroups.py b/src/sage/categories/examples/finite_semigroups.py index 306775ce53e..25ffa464bd3 100644 --- a/src/sage/categories/examples/finite_semigroups.py +++ b/src/sage/categories/examples/finite_semigroups.py @@ -81,6 +81,7 @@ class LeftRegularBand(UniqueRepresentation, Parent): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/infinite_enumerated_sets.py b/src/sage/categories/examples/infinite_enumerated_sets.py index e91aacbf739..850bb4385e3 100644 --- a/src/sage/categories/examples/infinite_enumerated_sets.py +++ b/src/sage/categories/examples/infinite_enumerated_sets.py @@ -53,6 +53,7 @@ class NonNegativeIntegers(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/monoids.py b/src/sage/categories/examples/monoids.py index f316b459721..5859de22561 100644 --- a/src/sage/categories/examples/monoids.py +++ b/src/sage/categories/examples/monoids.py @@ -53,6 +53,7 @@ class FreeMonoid(FreeSemigroup): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/posets.py b/src/sage/categories/examples/posets.py index e4d706eb786..4d864c9cef5 100644 --- a/src/sage/categories/examples/posets.py +++ b/src/sage/categories/examples/posets.py @@ -32,6 +32,7 @@ class FiniteSetsOrderedByInclusion(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/semigroups.py b/src/sage/categories/examples/semigroups.py index f36cf457b29..180cef37fca 100644 --- a/src/sage/categories/examples/semigroups.py +++ b/src/sage/categories/examples/semigroups.py @@ -47,6 +47,7 @@ class LeftZeroSemigroup(UniqueRepresentation, Parent): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -333,6 +334,7 @@ class QuotientOfLeftZeroSemigroup(UniqueRepresentation, Parent): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -557,6 +559,7 @@ def __init__(self, category = None): ------------------------------------------------------------ running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/semigroups_cython.pyx b/src/sage/categories/examples/semigroups_cython.pyx index e615d2248dd..931ce2fdc03 100644 --- a/src/sage/categories/examples/semigroups_cython.pyx +++ b/src/sage/categories/examples/semigroups_cython.pyx @@ -167,6 +167,7 @@ class LeftZeroSemigroup(LeftZeroSemigroupPython): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index d8cc742f982..5a691d46792 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -60,6 +60,7 @@ class PrimeNumbers(UniqueRepresentation, Parent): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -352,6 +353,7 @@ class PrimeNumbers_Inherits(PrimeNumbers_Abstract): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -635,6 +637,7 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/finite_crystals.py b/src/sage/categories/finite_crystals.py index b06f45ad454..8db61ffe940 100644 --- a/src/sage/categories/finite_crystals.py +++ b/src/sage/categories/finite_crystals.py @@ -35,6 +35,7 @@ class FiniteCrystals(CategoryWithAxiom): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index e3d10f50924..8c9827dc8b8 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -49,6 +49,7 @@ class FinitePermutationGroups(CategoryWithAxiom): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index dc4441abe46..214b9d22733 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -40,6 +40,7 @@ class HighestWeightCrystals(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -763,7 +764,21 @@ def highest_weight_vectors_iterator(self): Traceback (most recent call last): ... NotImplementedError: not implemented for infinite crystals + + Check that :trac:`30493` is fixed:: + + sage: CW = CartanType("G", 2) + sage: C = crystals.Letters(CW) + sage: C.highest_weight_vectors() + (1,) + sage: T = crystals.TensorProduct(C) + sage: T.highest_weight_vectors() + ([1],) """ + if len(self.crystals) == 1: + for b in self.crystals[0].highest_weight_vectors(): + yield self.element_class(self, [b]) + return I = self.index_set() try: T_elts = [C.list() for C in self.crystals[:-1]] diff --git a/src/sage/categories/hopf_algebras_with_basis.py b/src/sage/categories/hopf_algebras_with_basis.py index 468999347e5..e5e0e05fe57 100644 --- a/src/sage/categories/hopf_algebras_with_basis.py +++ b/src/sage/categories/hopf_algebras_with_basis.py @@ -77,6 +77,7 @@ class HopfAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 5bc194ae2d3..7dc89010d2f 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -803,6 +803,7 @@ class SubcategoryMethods: running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 3e27a5dc0e4..3c99b2ae266 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -754,10 +754,7 @@ def common_base(self, other_functor, self_bases, other_bases): sage: pushout(cartesian_product([ZZ]), cartesian_product([ZZ, QQ])) # indirect doctest Traceback (most recent call last): ... - CoercionException: No common base ("join") found for - The cartesian_product functorial construction(Integer Ring) and - The cartesian_product functorial construction(Integer Ring, Rational Field): - Functors need the same number of arguments. + CoercionException: No common base ("join") found ... """ if self != other_functor: self._raise_common_base_exception_( @@ -2369,7 +2366,8 @@ def __init__(self, p, prec, extras=None): In the ``lattice-cap`` precision case, ``prec`` will be a tuple instead. - ``extras`` (optional dictionary): Information on how to print elements, etc. - If 'type' is given as a key, the corresponding value should be a string among the following: + If 'type' is given as a key, the corresponding value should be a string among + the following: - 'RDF', 'Interval', 'RLF', or 'RR' for completions at infinity @@ -2384,11 +2382,17 @@ def __init__(self, p, prec, extras=None): 5-adic Field with capped relative precision 100 sage: F1(ZZ) 5-adic Ring with capped relative precision 100 + sage: F1.type is None + True + sage: sorted(F1.extras.items()) + [] sage: F2 = RR.construction()[0] sage: F2 Completion[+Infinity, prec=53] + sage: F2.type + 'MPFR' sage: F2.extras - {'rnd': 0, 'sci_not': False, 'type': 'MPFR'} + {'rnd': 0, 'sci_not': False} """ Functor.__init__(self, Rings(), Rings()) self.p = p @@ -2399,7 +2403,7 @@ def __init__(self, p, prec, extras=None): self.type = None else: self.extras = dict(extras) - self.type = extras.get('type', None) + self.type = self.extras.pop('type', None) from sage.rings.infinity import Infinity if self.p == Infinity: if self.type not in self._real_types: @@ -2445,7 +2449,8 @@ def _apply_functor(self, R): return R.completion(self.p, self.prec, {'type': self.type}) else: extras = self.extras.copy() - extras['type'] = self.type + if self.type is not None: + extras['type'] = self.type return R.completion(self.p, self.prec, extras) except (NotImplementedError, AttributeError): if R.construction() is None: @@ -2665,7 +2670,9 @@ class QuotientFunctor(ConstructionFunctor): .. NOTE:: - The functor keeps track of variable names. + The functor keeps track of variable names. Optionally, it may + keep track of additional properties of the quotient, such as + its category or its implementation. EXAMPLES:: @@ -2687,13 +2694,23 @@ class QuotientFunctor(ConstructionFunctor): """ rank = 4.5 - def __init__(self, I, names=None, as_field=False): + def __init__(self, I, names=None, as_field=False, domain=None, + codomain=None, **kwds): """ INPUT: - ``I``, an ideal (the modulus) - - ``names`` (optional string or list of strings), the names for the quotient ring generators - - ``as_field`` (optional bool, default false), return the quotient ring as field (if available). + - ``names`` (optional string or list of strings), the names for the + quotient ring generators + - ``as_field`` (optional bool, default false), return the quotient + ring as field (if available). + - ``domain`` (optional category, default ``Rings()``), the domain of + this functor. + - ``codomain`` (optional category, default ``Rings()``), the codomain + of this functor. + - Further named arguments. In particular, an implementation of the + quotient can be suggested here. These named arguments are passed to + the quotient construction. TESTS:: @@ -2717,7 +2734,12 @@ def __init__(self, I, names=None, as_field=False): Ring of integers modulo 5 """ - Functor.__init__(self, Rings(), Rings()) # much more general... + if domain is None: + domain = Rings() + if codomain is None: + codomain = Rings() + Functor.__init__(self, domain, codomain) + self.I = I if names is None: self.names = None @@ -2726,6 +2748,7 @@ def __init__(self, I, names=None, as_field=False): else: self.names = tuple(names) self.as_field = as_field + self.kwds = kwds def _apply_functor(self, R): """ @@ -2764,10 +2787,10 @@ def _apply_functor(self, R): R = pushout(R, I.ring().base_ring()) I = [R.one() * t for t in I.gens()] * R try: - Q = R.quo(I, names=self.names) + Q = R.quo(I, names=self.names, **self.kwds) except IndexError: # That may happen! raise CoercionException("Can not apply this quotient functor to %s" % R) - if self.as_field:# and hasattr(Q, 'field'): + if self.as_field: try: Q = Q.field() except AttributeError: @@ -2776,7 +2799,7 @@ def _apply_functor(self, R): def __eq__(self, other): """ - The types, the names and the moduli are compared. + The types, domain, codomain, names and moduli are compared. TESTS:: @@ -2793,7 +2816,10 @@ def __eq__(self, other): """ if not isinstance(other, QuotientFunctor): return False - return (self.names == other.names and + return (type(self) == type(other) and + self.domain() == other.domain() and + self.codomain() == other.codomain() and + self.names == other.names and self.I == other.I) def __ne__(self, other): @@ -2819,7 +2845,12 @@ def __ne__(self, other): def merge(self, other): """ - Two quotient functors with coinciding names are merged by taking the gcd of their moduli. + Two quotient functors with coinciding names are merged by taking the gcd + of their moduli, the meet of their domains, and the join of their codomains. + + In particular, if one of the functors being merged knows that the quotient + is going to be a field, then the merged functor will return fields as + well. EXAMPLES:: @@ -2842,23 +2873,47 @@ def merge(self, other): return None if self == other: if self.as_field == other.as_field: + # The two functors are *really* equal return self - return QuotientFunctor(self.I, names=self.names, as_field=True) # one of them yields a field! - try: - gcd = self.I + other.I - except (TypeError, NotImplementedError): + # They are equal up to the additional arguments + I = self.I + domain = self.domain() + codomain = self.codomain() + else: try: - gcd = self.I.gcd(other.I) + I = self.I + other.I except (TypeError, NotImplementedError): + try: + I = self.I.gcd(other.I) + except (TypeError, NotImplementedError): + return None + domain = self.domain().meet([self.domain(), other.domain()]) + codomain = self.codomain().join([self.codomain(), other.codomain()]) + # Get the optional arguments: + as_field = self.as_field or other.as_field + kwds = {} + for k,v in self.kwds.items(): + kwds[k] = v + for k,v in other.kwds.items(): + if k=='category': + if kwds[k] is not None: + kwds[k] = v.join([v,kwds[k]]) + else: + kwds[k] = v + continue + if k in kwds and kwds[k] is not None and v!=kwds[k]: + # Don't know what default to choose. Hence: No merge! return None - if gcd.is_trivial() and not gcd.is_zero(): - # quotient by gcd would result in the trivial ring/group/... + kwds[k] = v + if I.is_trivial() and not I.is_zero(): + # quotient by I would result in the trivial ring/group/... # Rather than create the zero ring, we claim they can't be merged # TODO: Perhaps this should be detected at a higher level... raise TypeError("Trivial quotient intersection.") # GF(p) has a coercion from Integers(p). Hence, merging should # yield a field if either self or other yields a field. - return QuotientFunctor(gcd, names=self.names, as_field=self.as_field or other.as_field) + return QuotientFunctor(I, names=self.names, as_field=as_field, + domain=domain, codomain=codomain, **kwds) class AlgebraicExtensionFunctor(ConstructionFunctor): @@ -2897,12 +2952,42 @@ class AlgebraicExtensionFunctor(ConstructionFunctor): sage: O.ambient() is K True + Special cases are made for cyclotomic fields and residue fields:: + + sage: C = CyclotomicField(8) + sage: F,R = C.construction() + sage: F + AlgebraicExtensionFunctor + sage: R + Rational Field + sage: F(R) + Cyclotomic Field of order 8 and degree 4 + sage: F(ZZ) + Maximal Order in Cyclotomic Field of order 8 and degree 4 + + :: + + sage: K. = CyclotomicField(7) + sage: P = K.factor(17)[0][0] + sage: k = K.residue_field(P) + sage: F, R = k.construction() + sage: F + AlgebraicExtensionFunctor + sage: R + Cyclotomic Field of order 7 and degree 6 + sage: F(R) is k + True + sage: F(ZZ) + Residue field of Integers modulo 17 + sage: F(CyclotomicField(49)) + Residue field in zbar of Fractional ideal (17) + """ rank = 3 def __init__(self, polys, names, embeddings=None, structures=None, cyclotomic=None, precs=None, implementations=None, - *, latex_names=None, **kwds): + *, residue=None, latex_names=None, **kwds): """ INPUT: @@ -2932,6 +3017,12 @@ def __init__(self, polys, names, embeddings=None, structures=None, If it is provided, it is used to determine an implementation in the p-adic case. + - ``residue`` -- (optional) prime ideal of an order in a number + field, determining a residue field. If it is provided, + application of the functor to a number field yields the + residue field with respect to the given prime ideal + (coerced into the number field). + - ``latex_names`` -- (optional) list of strings of the same length as the list ``polys`` @@ -3008,7 +3099,6 @@ def __init__(self, polys, names, embeddings=None, structures=None, sage: L1 = (b*x).parent().base_ring() sage: L1 is L0 True - """ Functor.__init__(self, Rings(), Rings()) if not (isinstance(polys, (list, tuple)) and isinstance(names, (list, tuple))): @@ -3033,6 +3123,7 @@ def __init__(self, polys, names, embeddings=None, structures=None, self.cyclotomic = int(cyclotomic) if cyclotomic is not None else None self.precs = list(precs) self.implementations = list(implementations) + self.residue = residue # Normalize latex_names: Use None when latex_name does not override the default. latex_names = list(latex_names) for i, name in enumerate(self.names): @@ -3073,6 +3164,8 @@ def _apply_functor(self, R): return CyclotomicField(self.cyclotomic) if R == ZZ: return CyclotomicField(self.cyclotomic).maximal_order() + elif self.residue is not None: + return R.residue_field(R*self.residue, names=tuple(self.names)) if len(self.polys) == 1: return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0], structure=self.structures[0], prec=self.precs[0], @@ -3255,12 +3348,18 @@ def merge(self, other): # Finite fields and unramified local extensions may use # integers to encode degrees of extensions. from sage.rings.integer import Integer + kwds_self = dict(self.kwds.items()) + if 'impl' in kwds_self: + del kwds_self['impl'] + kwds_other = dict(other.kwds.items()) + if 'impl' in kwds_other: + del kwds_other['impl'] if (isinstance(self.polys[0], Integer) and isinstance(other.polys[0], Integer) and self.embeddings == other.embeddings == [None] and self.structures == other.structures == [None] - and self.kwds == other.kwds): - return AlgebraicExtensionFunctor([self.polys[0].lcm(other.polys[0])], [None], **self.kwds) + and kwds_self == kwds_other): + return AlgebraicExtensionFunctor([self.polys[0].lcm(other.polys[0])], [None], **kwds_self) def __mul__(self, other): """ diff --git a/src/sage/categories/regular_crystals.py b/src/sage/categories/regular_crystals.py index f9466c2fa23..6b365b0c7cd 100644 --- a/src/sage/categories/regular_crystals.py +++ b/src/sage/categories/regular_crystals.py @@ -58,6 +58,7 @@ class RegularCrystals(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/regular_supercrystals.py b/src/sage/categories/regular_supercrystals.py index a4cc095f51c..0d51530d989 100644 --- a/src/sage/categories/regular_supercrystals.py +++ b/src/sage/categories/regular_supercrystals.py @@ -68,6 +68,7 @@ class RegularSuperCrystals(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 007c38652d7..9a1acdd02ac 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -760,7 +760,7 @@ def _ideal_class_(self,n=0): ## # Quotient rings # Again, this is defined in sage.rings.ring.pyx - def quotient(self, I, names=None): + def quotient(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. @@ -769,6 +769,8 @@ def quotient(self, I, names=None): - ``I``: A twosided ideal of this ring. - ``names``: a list of strings to be used as names for the variables in the quotient ring. + - further named arguments that may be passed to the + quotient ring constructor. EXAMPLES: @@ -801,9 +803,9 @@ def quotient(self, I, names=None): 0 """ from sage.rings.quotient_ring import QuotientRing - return QuotientRing(self, I, names=names) + return QuotientRing(self, I, names=names, **kwds) - def quo(self, I, names=None): + def quo(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. @@ -843,9 +845,9 @@ def quo(self, I, names=None): ) """ - return self.quotient(I,names=names) + return self.quotient(I,names=names,**kwds) - def quotient_ring(self, I, names=None): + def quotient_ring(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. @@ -883,7 +885,7 @@ def quotient_ring(self, I, names=None): ) """ - return self.quotient(I,names=names) + return self.quotient(I,names=names, **kwds) def __truediv__(self, I): """ diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index f8b1ed19a3f..ba1862b64f3 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -134,6 +134,7 @@ class Sets(Category_singleton): running ._test_an_element() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -1450,6 +1451,65 @@ def construction(self): """ return None + def _test_construction(self, **options): + """ + Test that the construction returned by self really yields self. + + :meth:`construction` either returns None or a pair ``(F,O)``, + and if it returns the latter, then it is supposed that ``F(O)==self`. + The test verifies this assumption. + + EXAMPLE: + + We create a parent that returns a wrong construction (its construction + returns the rational field rather than the parent itself):: + + sage: class P(Parent): + ....: Element = ElementWrapper + ....: def __init__(self): + ....: Parent.__init__(self, category=Sets()) + ....: def __eq__(self, P): + ....: return type(self) == type(P) + ....: def __hash__(self): + ....: return hash(type(self)) + ....: def construction(self): + ....: return sage.categories.pushout.FractionField(), ZZ + ....: + sage: import __main__ + sage: __main__.P = P # this is to enable pickling in doctests + sage: p = P() + sage: F,R = p.construction() + sage: F(R) + Rational Field + sage: TestSuite(p).run() + Failure in _test_construction: + Traceback (most recent call last): + ... + AssertionError: the object's construction does not recreate this object + ... + The following tests failed: _test_construction + + If the parent returns the empty construction, the test will not complain:: + + sage: ZZ.construction() is None + True + sage: TestSuite(ZZ).run() # indirect doctest + + If the construction works as expected, the test will not complain + either:: + + sage: F,R = QQ.construction() + sage: F(R) == QQ + True + sage: TestSuite(QQ).run() # indirect doctest + + """ + tester = self._tester(**options) + FO = self.construction() + if FO is None: + return + tester.assertEqual(FO[0](FO[1]), self, "the object's construction does not recreate this object") + CartesianProduct = CartesianProduct def cartesian_product(*parents, **kwargs): @@ -1731,7 +1791,7 @@ def is_injective(self): Topological = LazyImport('sage.categories.topological_spaces', 'TopologicalSpaces', 'Topological', at_startup=True) Metric = LazyImport('sage.categories.metric_spaces', 'MetricSpaces', - 'Mertic', at_startup=True) + 'Metric', at_startup=True) class Infinite(CategoryWithAxiom): diff --git a/src/sage/categories/with_realizations.py b/src/sage/categories/with_realizations.py index ec63236afd8..7f59b92d72b 100644 --- a/src/sage/categories/with_realizations.py +++ b/src/sage/categories/with_realizations.py @@ -293,5 +293,3 @@ def _repr_(self): """ s = repr(self.base_category()) return s+" with realizations" - - diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 7f3c24d4587..056e3a51e63 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -49,6 +49,7 @@ from sage.structure.element import is_Matrix from sage.misc.misc import cputime from sage.rings.integer cimport Integer from copy import copy +from sage.data_structures.bitset_base cimport * WORD_SIZE = sizeof(codeword) << 3 @@ -84,7 +85,6 @@ cdef int *hamming_weights(): ham_wts[i] = ham_wts[i & 255] + ham_wts[(i>>8) & 255] return ham_wts -include 'sage/data_structures/bitset.pxi' def weight_dist(M): """ Computes the weight distribution of the row space of M. diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index abccedfdf47..98022553c29 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -101,7 +101,7 @@ from sage.groups.perm_gps.permgroup import PermutationGroup cimport sage.groups.perm_gps.partn_ref2.refinement_generic from sage.modules.finite_submodule_iter cimport FiniteFieldsubspace_projPoint_iterator as FFSS_projPoint from sage.groups.perm_gps.partn_ref.data_structures cimport * -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * cdef class InnerGroup: @@ -759,6 +759,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): """ from sage.matrix.constructor import matrix cdef FFSS_projPoint iter = FFSS_projPoint(self._matrix) + cdef mp_bitcnt_t i,j ambient_space = (self._matrix.base_ring()) ** (self._n) weights2size = [0] * (self.len() + 1) diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index f550c1413e0..5da8030f454 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -2,16 +2,14 @@ Affine Permutations """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013 Tom Denton # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -from __future__ import print_function, division - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.misc.constant_function import ConstantFunction @@ -709,11 +707,15 @@ def maximal_cyclic_factor(self, typ='decreasing', side='right', verbose=False): y = self.clone().apply_simple_reflection(i,side) T = [i] j = i - for count in range(1,self.k): - if (typ[0],side[0]) == ('d','r'): j=(j+1)%(k+1) - if (typ[0],side[0]) == ('i','r'): j=(j-1)%(k+1) - if (typ[0],side[0]) == ('d','l'): j=(j-1)%(k+1) - if (typ[0],side[0]) == ('i','l'): j=(j+1)%(k+1) + for count in range(1, self.k): + if (typ[0],side[0]) == ('d', 'r'): + j=(j+1)%(k+1) + if (typ[0],side[0]) == ('i', 'r'): + j=(j-1)%(k+1) + if (typ[0],side[0]) == ('d', 'l'): + j=(j-1)%(k+1) + if (typ[0],side[0]) == ('i', 'l'): + j=(j+1)%(k+1) if y.has_descent(j, side): y=y.apply_simple_reflection(j,side) T.append(j%(k+1)) @@ -853,7 +855,8 @@ def to_lehmer_code(self, typ='decreasing', side='right'): b=self(j) #A small rotation is necessary for the reduced word from #the lehmer code to match the element. - if a= n: a,b = M[i,j],-M[i,j] - else: a,b = M[i,j],M[j,i] + if i >= n: + a, b = M[i, j], -M[i, j] + else: + a, b = M[i, j], M[j, i] if a > 0: dg._backend.add_edge(i,j,(a,b),True) elif i >= n: diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 693c5f3ea88..bc301c67b83 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -853,7 +853,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): if type_tmp[0].letter() == 'A' and type_tmp[0].is_finite(): if v in type_tmp[1]: type_tmp[1].remove(v) - if n == 4: type_tmp[1].extend( dead_neighbors[:2] ) + if n == 4: + type_tmp[1].extend(dead_neighbors[:2]) if ret_conn_vert: return [ QuiverMutationType( ['D',n] ), type_tmp[1] ] else: @@ -1448,19 +1449,27 @@ def _random_tests(mt, k, mut_class=None, nr_mut=5): a,b = M[i,j],M[j,i] skew_sym = False while not skew_sym: - ran = random.randint(1,2) + ran = random.randint(1, 2) if ran == 1: - M[i,j], M[j,i] = -M[j,i], -M[i,j] + M[i, j], M[j, i] = -M[j, i], -M[i, j] elif ran == 2: - ran2 = random.randint(1,8) - if ran2 == 1: c,d = 1,-1 - elif ran2 == 2: c,d = 1,-2 - elif ran2 == 3: c,d = 2,-1 - elif ran2 == 4: c,d = 1,-3 - elif ran2 == 5: c,d = 3,-1 - elif ran2 == 6: c,d = 2,-2 - elif ran2 == 7: c,d = 1,-4 - elif ran2 == 8: c,d = 4,-1 + ran2 = random.randint(1, 8) + if ran2 == 1: + c, d = 1, -1 + elif ran2 == 2: + c, d = 1, -2 + elif ran2 == 3: + c, d = 2, -1 + elif ran2 == 4: + c, d = 1, -3 + elif ran2 == 5: + c, d = 3, -1 + elif ran2 == 6: + c, d = 2, -2 + elif ran2 == 7: + c, d = 1, -4 + elif ran2 == 8: + c, d = 4, -1 M[i, j], M[j, i] = c, d if M.is_skew_symmetrizable(positive=True): skew_sym = True diff --git a/src/sage/combinat/debruijn_sequence.pyx b/src/sage/combinat/debruijn_sequence.pyx index db8fda9e5c0..9dbf16238ee 100644 --- a/src/sage/combinat/debruijn_sequence.pyx +++ b/src/sage/combinat/debruijn_sequence.pyx @@ -64,7 +64,7 @@ AUTHOR: # http://www.gnu.org/licenses/ #******************************************************************************* -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * def debruijn_sequence(int k, int n): """ diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 9334786ecbd..65f49d9a864 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -867,8 +867,9 @@ def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True): B = BlockDesign(len(points), blocks, name="AffineGeometryDesign", check=check) if point_coordinates: - rd = {i:p[0][1:] for p,i in points.items()} - for v in rd.values(): v.set_immutable() + rd = {i: p[0][1:] for p, i in points.items()} + for v in rd.values(): + v.set_immutable() B.relabel(rd) if check: @@ -877,6 +878,7 @@ def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True): "construction. Please e-mail sage-devel@googlegroups.com") return B + def CremonaRichmondConfiguration(): r""" Return the Cremona-Richmond configuration diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index 2098f33b6aa..c72baf64b6b 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -7,7 +7,7 @@ Functions --------- """ -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from libc.string cimport memset diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index a296d22d8eb..91269089a45 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -1666,12 +1666,15 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch elif explain_construction: return "Hadamard difference set product from N1={} and N2={}".format(N1,N2) else: - v1 = 4*N1*N1; v2 = 4*N2*N2 - k1 = 2*N1*N1 - N1; k2 = 2*N2*N2 - N2 - l1 = N1*N1 - N1; l2 = N2*N2 - N2 - G1,D1 = difference_family(v1,k1,l1) - G2,D2 = difference_family(v2,k2,l2) - G,D = hadamard_difference_set_product(G1,D1,G2,D2) + v1 = 4*N1*N1 + v2 = 4*N2*N2 + k1 = 2*N1*N1 - N1 + k2 = 2*N2*N2 - N2 + l1 = N1*N1 - N1 + l2 = N2*N2 - N2 + G1, D1 = difference_family(v1,k1,l1) + G2, D2 = difference_family(v2,k2,l2) + G, D = hadamard_difference_set_product(G1,D1,G2,D2) elif are_hadamard_difference_set_parameters(v,k,l) and (k-2*l).is_prime(): if existence: diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index e942ec51631..361c12c2cb9 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -219,10 +219,14 @@ def kirkman_triple_system(v,existence=False): # First parallel class first_class = [[(0,1),(0,2),'inf']] - b0 = K.one(); b1 = a**t; b2 = a**m + b0 = K.one() + b1 = a**t + b2 = a**m first_class.extend([(b0*a**i,1),(b1*a**i,1),(b2*a**i,2)] for i in list(range(t))+list(range(2*t,3*t))+list(range(4*t,5*t))) - b0 = a**(m+t); b1=a**(m+3*t); b2=a**(m+5*t) + b0 = a**(m+t) + b1 = a**(m+3*t) + b2 = a**(m+5*t) first_class.extend([[(b0*a**i,2),(b1*a**i,2),(b2*a**i,2)] for i in range(t)]) @@ -318,15 +322,23 @@ def kirkman_triple_system(v,existence=False): # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: - for i,b in enumerate(B): - if 8 in b: j = min(b); del B[i]; B.insert(0,j); break + for i, b in enumerate(B): + if 8 in b: + j = min(b) + del B[i] + B.insert(0, j) + break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: - for i,b in enumerate(B): - if 14 in b: j = min(b); del B[i]; B.insert(0,j); break + for i, b in enumerate(B): + if 14 in b: + j = min(b) + del B[i] + B.insert(0, j) + break gdd7.sort() for B in gdd7: B.pop(0) diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index fbcad03d19d..b5175a9ace2 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -122,14 +122,23 @@ def _element_constructor_(self, lst): EXAMPLES:: sage: WIV = WeightedIntegerVectors(3, [2,1,1]) - sage: elt = WIV([1, 2, 0]); elt - [1, 2, 0] + sage: elt = WIV([1, 1, 0]); elt + [1, 1, 0] sage: elt.parent() is WIV True + sage: WIV([1, 1, 0]) + [1, 1, 0] + sage: WIV([1, 2, 0]) + Traceback (most recent call last): + ... + ValueError: cannot convert [1, 2, 0] into Integer vectors of 3 + weighted by [2, 1, 1] + """ if isinstance(lst, IntegerVector): if lst.parent() is self: return lst + if lst not in self: raise ValueError("cannot convert %s into %s" % (lst, self)) return self.element_class(self, lst) diff --git a/src/sage/combinat/multiset_partition_into_sets_ordered.py b/src/sage/combinat/multiset_partition_into_sets_ordered.py index 46b317565d3..0d588045fd1 100755 --- a/src/sage/combinat/multiset_partition_into_sets_ordered.py +++ b/src/sage/combinat/multiset_partition_into_sets_ordered.py @@ -951,15 +951,16 @@ def minimaj_blocks(self): return () C = [sorted(self[-1])] - for i in range(1,len(self)): - lower = []; upper = [] - for j in self[-1-i]: + for i in range(1, len(self)): + lower = [] + upper = [] + for j in self[-1 - i]: if j <= C[0][0]: lower.append(j) else: upper.append(j) - C = [sorted(upper)+sorted(lower)] + C - return tuple(map(tuple,C)) + C = [sorted(upper) + sorted(lower)] + C + return tuple(map(tuple, C)) def to_tableaux_words(self): r""" @@ -1415,13 +1416,14 @@ def __classcall_private__(self, *args, **constraints): if "alphabet" in constraints: A = constraints["alphabet"] if A in ZZ: - A = range(1, A+1) + A = range(1, A + 1) constraints["alphabet"] = frozenset(A) - if len(args) == 2: # treat as `alphabet` & `order` - alph = args[0]; order = args[1] + if len(args) == 2: # treat as `alphabet` & `order` + alph = args[0] + order = args[1] if alph in ZZ: - alph = range(1,alph+1) + alph = range(1, alph + 1) if (alph and len(set(alph)) == len(alph)) and (order in ZZ and order >= 0): if "alphabet" in constraints: raise ValueError("cannot pass alphabet as first argument and keyword argument") @@ -1817,11 +1819,12 @@ def _from_list_with_zeros(self, lst_with_zeros): [{'a','b','c'}, {'a'}, {'b'}] """ from_zero_lst = list(lst_with_zeros) - if from_zero_lst[-1] not in {0,'0'}: + if from_zero_lst[-1] not in {0, '0'}: from_zero_lst += [0] - co = []; block=[] + co = [] + block = [] for a in from_zero_lst: - if a in {0,'0'}: + if a in {0, '0'}: if block: co.append(block) block = [] diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index 8e65e9c9cc5..547f1753840 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -4,7 +4,7 @@ A perfect matching of a set `S` is a partition into 2-element sets. If `S` is the set `\{1,...,n\}`, it is equivalent to fixpoint-free involutions. These -simple combinatorial objects appear in different domains such as combinatoric +simple combinatorial objects appear in different domains such as combinatorics of orthogonal polynomials and of the hyperoctaedral groups (see [MV]_, [McD]_ and also [CM]_): diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 766fcb3175c..f7b9812ef5e 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -4512,11 +4512,13 @@ def increasing_tree(self, compare=min): 4[3[2[., .], 1[., .]], .] """ from sage.combinat.binary_tree import LabelledBinaryTree as LBT + def rec(perm): - if len(perm) == 0: return LBT(None) + if len(perm) == 0: + return LBT(None) mn = compare(perm) k = perm.index(mn) - return LBT([rec(perm[:k]), rec(perm[k+1:])], label = mn) + return LBT([rec(perm[:k]), rec(perm[k + 1:])], label = mn) return rec(self) @combinatorial_map(name="Increasing tree") @@ -5472,9 +5474,10 @@ def __contains__(self, x): sage: [3,1] in Permutations(3,2) True """ - if len(x) != self._k: return False + if len(x) != self._k: + return False - r = list(range(1, self.n+1)) + r = list(range(1, self.n + 1)) for i in x: if i in r: r.remove(i) diff --git a/src/sage/combinat/posets/cartesian_product.py b/src/sage/combinat/posets/cartesian_product.py index 7738c875e1a..303bd5dfb5b 100644 --- a/src/sage/combinat/posets/cartesian_product.py +++ b/src/sage/combinat/posets/cartesian_product.py @@ -71,11 +71,11 @@ class CartesianProductPoset(CartesianProduct): sage: Cl.category() Join of Category of finite posets and Category of Cartesian products of finite enumerated sets - sage: TestSuite(Cl).run() + sage: TestSuite(Cl).run(skip=['_test_construction']) sage: Cp.category() Join of Category of finite posets and Category of Cartesian products of finite enumerated sets - sage: TestSuite(Cp).run() + sage: TestSuite(Cp).run(skip=['_test_construction']) .. SEEALSO:: diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index a788371292d..c385a16649b 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -12,7 +12,7 @@ Classes and methods ------------------- """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2012 Anne Schilling # # Distributed under the terms of the GNU General Public License (GPL) @@ -24,8 +24,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#**************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from __future__ import print_function from sage.rings.rational_field import QQ @@ -36,10 +36,10 @@ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.graphs.dot2tex_utils import have_dot2tex from sage.structure.list_clone import ClonableArray -from sage.misc.misc_c import prod from sage.functions.other import factorial from sage.matrix.constructor import matrix + class LinearExtensionOfPoset(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index e391955783d..468d80b832a 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -10,9 +10,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import print_function - -import functools, operator +import functools +import operator from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import from sage.misc.misc_c import prod diff --git a/src/sage/combinat/sf/new_kschur.py b/src/sage/combinat/sf/new_kschur.py index 61f9a5db317..5e6ba3eff42 100644 --- a/src/sage/combinat/sf/new_kschur.py +++ b/src/sage/combinat/sf/new_kschur.py @@ -109,24 +109,24 @@ def __init__(self, Sym, k, t='t'): self.t = R(t) category = GradedHopfAlgebras(R) if t == 1 else GradedCoalgebras(R) - Parent.__init__(self, category = category.Subobjects().WithRealizations()) + Parent.__init__(self, category=category.Subobjects().WithRealizations()) ks = self.kschur() # Coercions if t == 1: s = ks.ambient() - kh = self.khomogeneous(); h = kh.ambient() - h_to_s = s._internal_coerce_map_from(h) + kh = self.khomogeneous() + h = kh.ambient() + h_to_s = s._internal_coerce_map_from(h) kh_to_ks = ks.retract * h_to_s * kh.lift ks.register_coercion(kh_to_ks) - s_to_h = h._internal_coerce_map_from(s) + s_to_h = h._internal_coerce_map_from(s) ks_to_kh = kh.retract * s_to_h * ks.lift kh.register_coercion(ks_to_kh) # temporary workaround until handled by trac 125959 self.one = ConstantFunction(ks.one()) self.zero = ConstantFunction(ks.zero()) - def retract(self, sym): r""" Return the retract of ``sym`` from the ring of symmetric functions to ``self``. diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index dca0cfc3914..2f5e8ec7f34 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -2273,7 +2273,8 @@ def _gram_schmidt(self, n, source, scalar, cache, leading_coeff=None, upper_tria ([2, 1], [([1, 1, 1], 2), ([2, 1], 1)]), ([3], [([1, 1, 1], 1), ([2, 1], 1), ([3], 1)])] """ - BR = self.base_ring(); one = BR.one() + BR = self.base_ring() + one = BR.one() p = self.realization_of().p() # Create a function which converts x and y to the power-sum basis and applies diff --git a/src/sage/combinat/shuffle.py b/src/sage/combinat/shuffle.py index cd8c3c5516c..4f6f59e18d7 100644 --- a/src/sage/combinat/shuffle.py +++ b/src/sage/combinat/shuffle.py @@ -340,27 +340,35 @@ def __iter__(self): ############ Gray code ############# def swap(i, j): - l[i-1], l[j-1] = l[j-1], l[i-1] + l[i - 1], l[j - 1] = l[j - 1], l[i - 1] def gen(n, k): if 0 < k < n: - for _ in gen(n-1, k): yield + for _ in gen(n - 1, k): + yield - if k == 1: swap(n, n-1) - else: swap(n, k-1) + if k == 1: + swap(n, n - 1) + else: + swap(n, k - 1) yield - for _ in neg(n-1, k-1): yield + for _ in neg(n - 1, k - 1): + yield def neg(n, k): if 0 < k < n: - for _ in gen(n-1, k-1): yield + for _ in gen(n - 1, k - 1): + yield - if k == 1: swap(n, n-1) - else: swap(n, k-1) + if k == 1: + swap(n, n - 1) + else: + swap(n, k - 1) yield - for _ in neg(n-1, k): yield + for _ in neg(n - 1, k): + yield #################################### diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 28db604f0ed..c04128ce691 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -8,7 +8,7 @@ - Travis Scrimshaw, Arthur Lubovsky (2013-02-11): Factored out ``CombinatorialClass`` """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen , # Copyright (C) 2013 Travis Scrimshaw # Copyright (C) 2013 Arthur Lubovsky @@ -22,9 +22,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** -from __future__ import print_function, absolute_import +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.parent import Parent @@ -46,9 +45,11 @@ from sage.combinat.integer_vector import IntegerVectors from sage.combinat.words.words import Words +from sage.misc.persist import register_unpickle_override + class SkewTableau(ClonableList, - metaclass=InheritComparisonClasscallMetaclass): + metaclass=InheritComparisonClasscallMetaclass): r""" A skew tableau. @@ -265,7 +266,8 @@ def _repr_diagram(self): . 4 5 """ - none_str = lambda x: " ." if x is None else "%3s"%str(x) + def none_str(x): + return " ." if x is None else "%3s" % str(x) if self.parent().options('convention') == "French": new_rows = ["".join(map(none_str, row)) for row in reversed(self)] else: @@ -285,7 +287,9 @@ def _repr_compact(self): """ if not self: return '-' - str_rep = lambda x: '%s'%x if x is not None else '.' + + def str_rep(x): + return '%s' % x if x is not None else '.' return '/'.join(','.join(str_rep(r) for r in row) for row in self) def pp(self): @@ -446,7 +450,7 @@ def conjugate(self): """ conj_shape = self.outer_shape().conjugate() - conj = [[None]*row_length for row_length in conj_shape] + conj = [[None] * row_length for row_length in conj_shape] for i in range(len(conj)): for j in range(len(conj[i])): @@ -617,7 +621,7 @@ def is_standard(self): sage: SkewTableau([[None, 2], [2, 4]]).is_standard() False """ - #Check to make sure that it is filled with 1...size + # Check to make sure that it is filled with 1...size w = [i for row in self for i in row if i is not None] if sorted(w) != list(range(1, len(w) + 1)): return False @@ -655,7 +659,8 @@ def is_semistandard(self): # Is it weakly increasing along the rows? for row in self: - if any(row[c] is not None and row[c] > row[c+1] for c in range(len(row)-1)): + if any(row[c] is not None and row[c] > row[c + 1] + for c in range(len(row) - 1)): return False # Is it strictly increasing down columns? @@ -820,7 +825,7 @@ def to_chain(self, max_entry=None): max_entry = 0 else: max_entry = max(c for row in self for c in row if c is not None) - return [self.restriction_outer_shape(x) for x in range(max_entry+1)] + return [self.restriction_outer_shape(x) for x in range(max_entry + 1)] def slide(self, corner=None, return_vacated=False): """ @@ -864,39 +869,39 @@ def slide(self, corner=None, return_vacated=False): spotl, spotc = corner while (spotl, spotc) not in outer_corners: - #Check to see if there is nothing to the right + # Check to see if there is nothing to the right if spotc == len(new_st[spotl]) - 1: - #Swap the hole with the cell below - new_st[spotl][spotc] = new_st[spotl+1][spotc] - new_st[spotl+1][spotc] = None + # Swap the hole with the cell below + new_st[spotl][spotc] = new_st[spotl + 1][spotc] + new_st[spotl + 1][spotc] = None spotl += 1 continue - #Check to see if there is nothing below - if spotl == len(new_st) - 1 or len(new_st[spotl+1]) <= spotc: - #Swap the hole with the cell to the right - new_st[spotl][spotc] = new_st[spotl][spotc+1] - new_st[spotl][spotc+1] = None + # Check to see if there is nothing below + if spotl == len(new_st) - 1 or len(new_st[spotl + 1]) <= spotc: + # Swap the hole with the cell to the right + new_st[spotl][spotc] = new_st[spotl][spotc + 1] + new_st[spotl][spotc + 1] = None spotc += 1 continue - #If we get to this stage, we need to compare - below = new_st[spotl+1][spotc] - right = new_st[spotl][spotc+1] + # If we get to this stage, we need to compare + below = new_st[spotl + 1][spotc] + right = new_st[spotl][spotc + 1] if below <= right: - #Swap with the cell below - new_st[spotl][spotc] = new_st[spotl+1][spotc] - new_st[spotl+1][spotc] = None + # Swap with the cell below + new_st[spotl][spotc] = new_st[spotl + 1][spotc] + new_st[spotl + 1][spotc] = None spotl += 1 continue - #Otherwise swap with the cell to the right - new_st[spotl][spotc] = new_st[spotl][spotc+1] - new_st[spotl][spotc+1] = None + # Otherwise swap with the cell to the right + new_st[spotl][spotc] = new_st[spotl][spotc + 1] + new_st[spotl][spotc + 1] = None spotc += 1 - #Clean up to remove the "None" at an outside corner - #Remove the last row if there is nothing left in it + # Clean up to remove the "None" at an outside corner + # Remove the last row if there is nothing left in it new_st[spotl].pop() if not new_st[spotl]: new_st.pop() @@ -1089,7 +1094,7 @@ def shuffle(self, t2): t2_new = t2 # make a blank copy of t2 (to fill in iteratively), which will become t1_new - t1_new = [[None]*len(x) for x in list(t2)] + t1_new = [[None] * len(x) for x in list(t2)] # perform reverse slides according to the entries of t1, # from largest to smallest @@ -1293,15 +1298,15 @@ def bender_knuth_involution(self, k, rows=None, check=True): if i == 0: prev_row = [None] * len(result_tab[i]) else: - prev_row = result_tab[i-1] + prev_row = result_tab[i - 1] if i == l - 1: next_row = [None] * len(result_tab[i]) else: - next_row = result_tab[i+1] + [None] * (len(result_tab[i]) - len(result_tab[i+1])) + next_row = result_tab[i + 1] + [None] * (len(result_tab[i]) - len(result_tab[i + 1])) a = 0 b = 0 - sk = None # The first entry of k - sk1 = None # The first entry of k+1 + sk = None # The first entry of k + sk1 = None # The first entry of k+1 for j, val in enumerate(result_tab[i]): if val == k and next_row[j] != k + 1: if sk is None: @@ -1313,16 +1318,16 @@ def bender_knuth_involution(self, k, rows=None, check=True): b += 1 if sk1 is not None: if a > b: - for j in range(sk1-(a-b), sk1): + for j in range(sk1 - (a - b), sk1): result_tab[i][j] = k + 1 elif a < b: - for j in range(sk1, sk1+b-a): + for j in range(sk1, sk1 + b - a): result_tab[i][j] = k elif sk is not None: - for j in range(sk, sk+a): + for j in range(sk, sk + a): result_tab[i][j] = k + 1 - return SkewTableau(result_tab) # This should be a SkewSemistandardTableau + return SkewTableau(result_tab) # This should be a SkewSemistandardTableau def to_expr(self): """ @@ -1405,7 +1410,7 @@ def is_ribbon(self): mu = list(self.inner_shape()) l_out = len(lam) l_in = len(mu) - mu += [0]*(l_out-l_in) + mu += [0] * (l_out - l_in) if l_out == 0: return True @@ -1425,7 +1430,7 @@ def is_ribbon(self): v = u + 1 v_test = True while v_test: - if v >= l_out or lam[v] != mu[v-1] + 1: + if v >= l_out or lam[v] != mu[v - 1] + 1: v_test = False else: v += 1 @@ -1495,18 +1500,18 @@ def cells_by_content(self, c): if c >= 0: if c >= len(self[0]): return [] - i,j = 0,c + i, j = 0, c else: c = -c if c >= len(self): return [] - i,j = c,0 + i, j = c, 0 res = [] while True: if self[i][j] is not None: - res.append((i,j)) - i,j = i+1, j+1 + res.append((i, j)) + i, j = i + 1, j + 1 if i >= len(self) or j >= len(self[i]): break return res @@ -1529,7 +1534,7 @@ def entries_by_content(self, c): sage: s.entries_by_content(-2) [6] """ - return [self[i][j] for i,j in self.cells_by_content(c)] + return [self[i][j] for i, j in self.cells_by_content(c)] def cells(self): """ @@ -1545,7 +1550,7 @@ def cells(self): for i in range(len(self)): for j in range(len(self[i])): if self[i][j] is not None: - res.append( (i,j) ) + res.append((i, j)) return res def cells_containing(self, i): @@ -1583,11 +1588,11 @@ def cells_containing(self, i): [] """ cell_list = [] - for r in range(len(self)-1, -1, -1): + for r in range(len(self) - 1, -1, -1): rth_row = self[r] - for c,val in enumerate(rth_row): + for c, val in enumerate(rth_row): if val == i: - cell_list.append((r,c)) + cell_list.append((r, c)) return cell_list def is_k_tableau(self, k): @@ -1604,8 +1609,9 @@ def is_k_tableau(self, k): False """ shapes = self.to_chain() - kshapes = [ la.k_conjugate(k) for la in shapes ] - return all( kshapes[i+1].contains(kshapes[i]) for i in range(len(shapes)-1) ) + kshapes = [la.k_conjugate(k) for la in shapes] + return all(kshapes[i + 1].contains(kshapes[i]) + for i in range(len(shapes) - 1)) def _label_skew(list_of_cells, sk): @@ -1625,10 +1631,11 @@ def _label_skew(list_of_cells, sk): i = 1 skew = [list(row) for row in sk] for row, column in list_of_cells: - skew[row][column] = i - i += 1 + skew[row][column] = i + i += 1 return skew + class SkewTableaux(UniqueRepresentation, Parent): r""" Class of all skew tableaux. @@ -1711,10 +1718,10 @@ def from_expr(self, expr): """ skp = [] outer = expr[1] - inner = expr[0]+[0]*(len(outer)-len(expr[0])) + inner = expr[0] + [0] * (len(outer) - len(expr[0])) for i in range(len(outer)): - skp.append( [None]*(inner[i]) + outer[-(i+1)] ) + skp.append([None] * (inner[i]) + outer[-(i + 1)]) return self.element_class(self, skp) @@ -1729,10 +1736,10 @@ def from_chain(self, chain): """ shape = chain[-1] T = [[None for _ in range(r)] for r in shape] - for i in range(1,len(chain)): + for i in range(1, len(chain)): la = chain[i] - mu = chain[i-1] - mu += [0]*(len(la) - len(mu)) + mu = chain[i - 1] + mu += [0] * (len(la) - len(mu)) for r in range(len(la)): for c in range(mu[r], la[r]): @@ -1753,7 +1760,7 @@ def from_shape_and_word(self, shape, word): sage: SkewTableaux().from_shape_and_word(shape, word) [[None, 1, 3], [None, 2], [4]] """ - st = [ [None]*row_length for row_length in shape[0] ] + st = [[None] * row_length for row_length in shape[0]] w_count = 0 for i in reversed(range(len(shape[0]))): for j in range(shape[0][i]): @@ -1762,6 +1769,7 @@ def from_shape_and_word(self, shape, word): w_count += 1 return self.element_class(self, st) + class StandardSkewTableaux(SkewTableaux): """ Standard skew tableaux. @@ -1831,6 +1839,7 @@ def __contains__(self, x): return SkewTableau(x).is_standard() + class StandardSkewTableaux_all(StandardSkewTableaux): """ Class of all standard skew tableaux. @@ -1874,6 +1883,7 @@ def __iter__(self): yield self.element_class(self, st) n += 1 + class StandardSkewTableaux_size(StandardSkewTableaux): """ Standard skew tableaux of a fixed size `n`. @@ -1895,7 +1905,7 @@ def _repr_(self): sage: StandardSkewTableaux(3) Standard skew tableaux of size 3 """ - return "Standard skew tableaux of size %s"%self.n + return "Standard skew tableaux of size %s" % self.n def cardinality(self): """ @@ -1948,6 +1958,7 @@ def __iter__(self): for sst in StandardSkewTableaux_shape(skp): yield self.element_class(self, sst) + class StandardSkewTableaux_shape(StandardSkewTableaux): r""" Standard skew tableaux of a fixed skew shape `\lambda / \mu`. @@ -1985,7 +1996,7 @@ def _repr_(self): sage: StandardSkewTableaux([[3, 2, 1], [1, 1]]) Standard skew tableaux of shape [3, 2, 1] / [1, 1] """ - return "Standard skew tableaux of shape %s"%repr(self.skp) + return "Standard skew tableaux of shape %s" % repr(self.skp) def cardinality(self): """ @@ -2002,15 +2013,15 @@ def cardinality(self): m = len(outer) n = sum(outer) - sum(inner) outer = list(outer) - inner = list(inner) + [0]*(m-len(inner)) + inner = list(inner) + [0] * (m - len(inner)) a = zero_matrix(QQ, m) for i in range(m): for j in range(m): v = outer[i] - inner[j] - i + j if v < 0: - a[i,j] = 0 + a[i, j] = 0 else: - a[i,j] = 1/factorial(v) + a[i, j] = 1 / factorial(v) return ZZ(factorial(n) * a.det()) def __iter__(self): @@ -2035,11 +2046,12 @@ def __iter__(self): dag = self.skp.to_dag(format="tuple") le_list = list(dag.topological_sort_generator()) - empty = [[None]*row_length for row_length in self.skp.outer()] + empty = [[None] * row_length for row_length in self.skp.outer()] for le in le_list: yield self.element_class(self, _label_skew(le, empty)) + class SemistandardSkewTableaux(SkewTableaux): r""" Semistandard skew tableaux. @@ -2192,6 +2204,7 @@ def __contains__(self, x): return False return x.is_semistandard() + class SemistandardSkewTableaux_all(SemistandardSkewTableaux): """ Class of all semistandard skew tableaux, possibly with a given @@ -2279,6 +2292,7 @@ def __iter__(self): yield self.element_class(self, ssst) n += 1 + class SemistandardSkewTableaux_size(SemistandardSkewTableaux): """ Class of all semistandard skew tableaux of a fixed size `n`, @@ -2307,7 +2321,7 @@ def _repr_(self): sage: SemistandardSkewTableaux(3, max_entry=8) Semistandard skew tableaux of size 3 and maximum entry 8 """ - return "Semistandard skew tableaux of size %s and maximum entry %s"%(repr(self.n), repr(self.max_entry)) + return "Semistandard skew tableaux of size %s and maximum entry %s" % (repr(self.n), repr(self.max_entry)) def cardinality(self): """ @@ -2339,6 +2353,7 @@ def __iter__(self): for ssst in SemistandardSkewTableaux_shape(p, self.max_entry): yield self.element_class(self, ssst) + class SemistandardSkewTableaux_size_weight(SemistandardSkewTableaux): r""" Class of semistandard tableaux of a fixed size `n` and weight `\mu`. @@ -2375,7 +2390,7 @@ def _repr_(self): sage: SemistandardSkewTableaux(3,[2,1]) Semistandard skew tableaux of size 3 and weight [2, 1] """ - return "Semistandard skew tableaux of size %s and weight %s"%(repr(self.n),list(self.mu)) + return "Semistandard skew tableaux of size %s and weight %s" % (repr(self.n), list(self.mu)) def cardinality(self): """ @@ -2400,6 +2415,7 @@ def __iter__(self): for ssst in SemistandardSkewTableaux_shape_weight(p, self.mu): yield self.element_class(self, ssst) + class SemistandardSkewTableaux_shape(SemistandardSkewTableaux): r""" Class of semistandard skew tableaux of a fixed skew shape @@ -2433,7 +2449,7 @@ def __classcall_private__(cls, p, max_entry=None): True """ if max_entry is None: - max_entry = sum(p[0])-sum(p[1]) + max_entry = sum(p[0]) - sum(p[1]) return super(SemistandardSkewTableaux_shape, cls).__classcall__(cls, SkewPartition(p), max_entry) def __init__(self, p, max_entry): @@ -2456,7 +2472,7 @@ def _repr_(self): sage: SemistandardSkewTableaux([[2,1],[]]) Semistandard skew tableaux of shape [2, 1] / [] and maximum entry 3 """ - return "Semistandard skew tableaux of shape %s and maximum entry %s"%(repr(self.p), repr(self.max_entry)) + return "Semistandard skew tableaux of shape %s and maximum entry %s" % (repr(self.p), repr(self.max_entry)) def cardinality(self): """ @@ -2493,6 +2509,7 @@ def __iter__(self): for ssst in SemistandardSkewTableaux_shape_weight(self.p, mu): yield self.element_class(self, ssst) + class SemistandardSkewTableaux_shape_weight(SemistandardSkewTableaux): r""" Class of semistandard skew tableaux of a fixed skew shape `\lambda / \nu` @@ -2534,7 +2551,7 @@ def _repr_(self): sage: SemistandardSkewTableaux([[2,1],[]],[2,1]) Semistandard skew tableaux of shape [2, 1] / [] and weight [2, 1] """ - return "Semistandard skew tableaux of shape %s and weight %s"%(repr(self.p), list(self.mu)) + return "Semistandard skew tableaux of shape %s and weight %s" % (repr(self.p), list(self.mu)) def __iter__(self): """ @@ -2549,6 +2566,7 @@ def __iter__(self): for x in RibbonTableaux_shape_weight_length(self.p, self.mu, 1): yield self.element_class(self, x) + class SkewTableau_class(SkewTableau): """ This exists solely for unpickling ``SkewTableau_class`` objects. @@ -2567,13 +2585,15 @@ def __setstate__(self, state): self.__class__ = SkewTableau self.__init__(SkewTableaux(), state['_list']) + # October 2012: fixing outdated pickles which use the classes being deprecated -from sage.misc.persist import register_unpickle_override -register_unpickle_override('sage.combinat.skew_tableau', 'StandardSkewTableaux_n', StandardSkewTableaux_size) -register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_n', SemistandardSkewTableaux_size) -register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_nmu', SemistandardSkewTableaux_size_weight) -register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_p', SemistandardSkewTableaux_shape) -register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_pmu', SemistandardSkewTableaux_shape_weight) + + +register_unpickle_override('sage.combinat.skew_tableau', 'StandardSkewTableaux_n', StandardSkewTableaux_size) +register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_n', SemistandardSkewTableaux_size) +register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_nmu', SemistandardSkewTableaux_size_weight) +register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_p', SemistandardSkewTableaux_shape) +register_unpickle_override('sage.combinat.skew_tableau', 'SemistandardSkewTableaux_pmu', SemistandardSkewTableaux_shape_weight) # July 2013: But wait, there more! -register_unpickle_override('sage.combinat.skew_tableau', 'StandardSkewTableaux_skewpartition', StandardSkewTableaux_shape) -register_unpickle_override('sage.combinat.skew_tableau', 'SkewTableau_class', SkewTableau_class) +register_unpickle_override('sage.combinat.skew_tableau', 'StandardSkewTableaux_skewpartition', StandardSkewTableaux_shape) +register_unpickle_override('sage.combinat.skew_tableau', 'SkewTableau_class', SkewTableau_class) diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 32c7cac054e..ada6bf23094 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -6751,10 +6751,10 @@ def _eval(self, n): -# inhomogenous second order recurrences +# inhomogeneous second order recurrences def recur_gen2b(a0,a1,a2,a3,b): r""" - inhomogenous second-order linear recurrence generator with fixed + inhomogeneous second-order linear recurrence generator with fixed coefficients and `b = f(n)` `a(0) = a0`, `a(1) = a1`, diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index 131f7eabe6a..2657f5c1ec6 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -178,7 +178,7 @@ def SymmetricGroupAlgebra(R, W, category=None): sage: QS3 = SymmetricGroupAlgebra(QQ, 3, category=Monoids()) sage: QS3.category() Category of finite dimensional cellular monoid algebras over Rational Field - sage: TestSuite(QS3).run() + sage: TestSuite(QS3).run(skip=['_test_construction']) TESTS:: @@ -201,7 +201,7 @@ def SymmetricGroupAlgebra(R, W, category=None): sage: SGA = SymmetricGroupAlgebra(QQ, W) sage: SGA.group() is W True - sage: TestSuite(SGA).run(skip="_test_cellular") + sage: TestSuite(SGA).run(skip=["_test_cellular", "_test_construction"]) sage: W = WeylGroup(["A",2]) sage: SGA = SymmetricGroupAlgebra(QQ, W) sage: SGA._test_cellular() diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 6c52ab59c46..2044c63a7da 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -84,7 +84,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** -from __future__ import print_function, absolute_import from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 49dec045a86..66f0dd3c09e 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -1238,12 +1238,13 @@ def restrict(self, m=None): sage: TableauTuples(level=2)([[[5]],[[1,2],[3,4]]]).restrict(3).category() Category of elements of Tableau tuples of level 2 """ - if m is None: m=self.size()-1 + if m is None: + m = self.size() - 1 # We are lucky in that currently restriction is defined for arbitrary # (level one) tableau and not just standard ones. If this ever changes # we will have to treat the cases where the components restrict to # empty lists of the form [[]] separately. - tab=[t.restrict(m) for t in self] + tab = [t.restrict(m) for t in self] try: return self.parent()(tab) except ValueError: diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index d6eac286031..4f02ff27bd5 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -2686,54 +2686,6 @@ def palindromic_lacunas_study(self, f=None): return lengths_lps, lacunas, palindromes - def lengths_lps(self, f=None): - r""" - Return the list of the length of the longest palindromic - suffix (lps) for each non-empty prefix of ``self``. - - It corresponds to the function `G_w` defined in [BMBFLR2008]_. - - INPUT: - - - ``f`` -- involution (default: ``None``) on the alphabet of ``self``. It must - be callable on letters as well as words (e.g. ``WordMorphism``). - - OUTPUT: - - a list -- list of the length of the longest palindromic - suffix (lps) for each non-empty prefix of ``self`` - - EXAMPLES:: - - sage: Word().lengths_lps() - doctest:warning - ... - DeprecationWarning: This method is deprecated. Use lps_lengths - See http://trac.sagemath.org/19154 for details. - [] - sage: Word('a').lengths_lps() - [1] - sage: Word('aaa').lengths_lps() - [1, 2, 3] - sage: Word('abbabaabbaab').lengths_lps() - [1, 1, 2, 4, 3, 3, 2, 4, 2, 4, 6, 8] - - :: - - sage: f = WordMorphism('a->b,b->a') - sage: Word('abbabaabbaab').lengths_lps(f) - [0, 2, 0, 2, 2, 4, 6, 8, 4, 6, 4, 6] - - :: - - sage: f = WordMorphism({5:[8],8:[5]}) - sage: Word([5,8,5,5,8,8,5,5,8,8,5,8,5]).lengths_lps(f) - [0, 2, 2, 0, 2, 4, 6, 4, 6, 8, 10, 12, 4] - """ - from sage.misc.superseded import deprecation - deprecation(19154, 'This method is deprecated. Use lps_lengths') - return self.lps_lengths(f)[1:] - def lacunas(self, f=None): r""" Return the list of all the lacunas of ``self``. @@ -3006,16 +2958,15 @@ def lps_lengths(self, f=None): [0, 0, 2, 0, 2, 2, 4, 6, 8] """ LPC = self.lengths_maximal_palindromes(f) - LPS = [] # lengths of the longest palindromic suffix of prefixes - k = 0 - LPS.append(0) + Nk = LPC[0] + LPS = [0] # lengths of the longest palindromic suffix of prefixes - for j in range(1, 2*len(self)+1): - if j + LPC[j] > k + LPC[k]: - for i in range(k + LPC[k] + 1, j + LPC[j] + 1): - if i % 2 == 0: - LPS.append(i-j) - k = j + for j in range(1, 2 * len(self) + 1): + Nj = j + LPC[j] + if Nj > Nk: + for i in range(Nk + 2 - (Nk % 2), Nj + 1, 2): + LPS.append(i - j) + Nk = Nj return LPS def palindromes(self, f=None): diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index d9ebfc97679..79c02de5f0e 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -13,7 +13,7 @@ Fast word datatype using an array of unsigned char from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport sig_on, sig_off -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * cimport cython from cpython.object cimport Py_EQ, Py_NE diff --git a/src/sage/combinat/words/words.py b/src/sage/combinat/words/words.py index 47327fab0cf..34a2eae2465 100644 --- a/src/sage/combinat/words/words.py +++ b/src/sage/combinat/words/words.py @@ -27,7 +27,7 @@ sage: InfiniteWords('natural numbers') Infinite words over Non negative integers """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Arnaud Bergeron , # Sébastien Labbé , # Franco Saliola @@ -37,14 +37,14 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** -from __future__ import print_function +# https://www.gnu.org/licenses/ +# **************************************************************************** import itertools from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.persist import register_unpickle_override from sage.structure.parent import Parent @@ -2328,35 +2328,8 @@ def iterate_by_length(self, length): ############### # old pickles # ############### -class Words_all(FiniteOrInfiniteWords): - r""" - Deprecated class used for unpickle support only! - """ - _alphabet = build_alphabet() - - def __init__(self): - r""" - TESTS:: - - sage: from sage.combinat.words.words import Words_all - sage: Words_all() - doctest:...: DeprecationWarning: Words_all is deprecated, use - FiniteOrInfiniteWords instead - See http://trac.sagemath.org/19619 for details. - Finite and infinite words over Set of Python objects of class 'object' - """ - from sage.misc.superseded import deprecation - deprecation(19619, "Words_all is deprecated, use FiniteOrInfiniteWords instead") - FiniteOrInfiniteWords.__init__(self, None) - - def _element_constructor_(self): - r""" - This method exists to make (old) unpickling work. - """ - pass -from sage.misc.persist import register_unpickle_override register_unpickle_override("sage.combinat.words.words", "Words_over_OrderedAlphabet", FiniteOrInfiniteWords) register_unpickle_override("sage.combinat.words.words", "Words_over_Alphabet", FiniteOrInfiniteWords) register_unpickle_override("sage.combinat.words.words", "FiniteWords_length_k_over_OrderedAlphabet", Words_n) diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 820f4510708..64064672466 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -44,7 +44,7 @@ from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.misc.superseded import deprecated_function_alias -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * # for details about the implementation of hamming_weight_int, # walsh_hadamard transform, reed_muller transform, and a lot @@ -294,6 +294,7 @@ cdef class BooleanFunction(SageObject): ... ValueError: the length of the truth table must be a power of 2 """ + cdef mp_bitcnt_t i if isinstance(x, str): L = ZZ(len(x)) if L.is_power_of(2): @@ -502,6 +503,7 @@ cdef class BooleanFunction(SageObject): [0, 1, 1, 0, 1, 0, 1, 1] """ cdef bitset_t anf + cdef mp_bitcnt_t i, inf, sup, j bitset_init(anf, (1<= self._truth_table.size: raise IndexError("index out of bound") - return bitset_in(self._truth_table,x) + return bitset_in(self._truth_table, x) elif isinstance(x, list): if len(x) != self._nvariables: raise ValueError("bad number of inputs") @@ -704,6 +706,7 @@ cdef class BooleanFunction(SageObject): (0, -4, 0, 4, 0, 4, 0, 4) """ cdef long *temp + cdef mp_bitcnt_t i,n if self._walsh_hadamard_transform is None: n = self._truth_table.size @@ -775,6 +778,7 @@ cdef class BooleanFunction(SageObject): sage: B.is_symmetric() True """ + cdef mp_bitcnt_t i cdef list T = [ self(2**i-1) for i in xrange(self._nvariables+1) ] for i in xrange(2**self._nvariables): sig_check() @@ -1010,6 +1014,8 @@ cdef class BooleanFunction(SageObject): cdef BooleanFunction t + cdef mp_bitcnt_t v + for i,m in enumerate(r): t = BooleanFunction(m) for j,v in enumerate(s): diff --git a/src/sage/data_structures/binary_matrix.pxi b/src/sage/data_structures/binary_matrix.pxi index f3185f9533b..b4818fb1515 100644 --- a/src/sage/data_structures/binary_matrix.pxi +++ b/src/sage/data_structures/binary_matrix.pxi @@ -17,7 +17,7 @@ A ``binary_matrix_t`` structure contains: """ from sage.data_structures.binary_matrix cimport * -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * cdef inline binary_matrix_init(binary_matrix_t m, Py_ssize_t n_rows, Py_ssize_t n_cols): diff --git a/src/sage/data_structures/bitset.pxd b/src/sage/data_structures/bitset.pxd index fd12fc98b45..08394ffcf5f 100644 --- a/src/sage/data_structures/bitset.pxd +++ b/src/sage/data_structures/bitset.pxd @@ -1,6 +1,3 @@ -""" -Cython bitset types -""" #***************************************************************************** # Copyright (C) 2008 Robert Bradshaw # @@ -10,45 +7,7 @@ Cython bitset types # http://www.gnu.org/licenses/ #***************************************************************************** -# This file declares the bitset types. The implementation of the basic -# Cython type bitset_t (inline functions) is in bitset.pxi, the -# implementation of the Python class is in bitset.pyx. The latter file -# also contains all doctests. - -cdef extern from *: - # Given an element index n in a set, (n >> index_shift) gives the - # corresponding limb number. - int index_shift "(sizeof(mp_limb_t) == 8 ? 6 : 5)" - -from sage.libs.gmp.types cimport * - -cdef struct bitset_s: - # The size of a bitset B counts the maximum number of bits that B can - # hold. This size is independent of how many elements of B are toggled to - # 1. For example, say B is the bitset 1001. Then B has size 4, with the - # first and fourth elements toggled to 1, reading from left to right. - # We can also think of the size of a bitset as its capacity. - mp_bitcnt_t size - - # A limb is that part of a bitset that can fit into an mp_limb_t - # (typically, 32 bits on a 32-bit machine and 64 bits on a 64-bit - # machine). This counts the number of limbs to represent a bitset. - # If a bitset has size <= n, then the whole bitset fits into a limb - # and we only require one limb to represent the bitset. However, if - # the bitset has size > n, we require more than one limb to - # represent the bitset. For example, if a limb is 64 bits in length - # and the bitset has size 96 bits, then we require at most two limbs - # to represent the bitset. - # - # NOTE: some code assumes that mp_limb_t is an unsigned long - # (this assumption is always true in practice). - mp_size_t limbs - - # The individual bits of a bitset. - mp_limb_t* bits - -ctypedef bitset_s bitset_t[1] - +from .bitset_base cimport bitset_t # Python layer over bitset_t cdef class FrozenBitset: diff --git a/src/sage/data_structures/bitset.pyx b/src/sage/data_structures/bitset.pyx index 708eae1cb05..94a32a737ad 100644 --- a/src/sage/data_structures/bitset.pyx +++ b/src/sage/data_structures/bitset.pyx @@ -31,7 +31,7 @@ linear in ``capacity``. # http://www.gnu.org/licenses/ #***************************************************************************** -include "bitset.pxi" +from .bitset_base cimport * from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE diff --git a/src/sage/data_structures/bitset.pxi b/src/sage/data_structures/bitset_base.pxd similarity index 67% rename from src/sage/data_structures/bitset.pxi rename to src/sage/data_structures/bitset_base.pxd index d6e6e10d6af..65bcfe2a7e3 100644 --- a/src/sage/data_structures/bitset.pxi +++ b/src/sage/data_structures/bitset_base.pxd @@ -1,3 +1,5 @@ +# distutils: depends = bitset_intrinsics.h +# distutils: libraries = gmp """ A fast bitset datatype in Cython @@ -25,16 +27,85 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +# This file declares the bitset types and the (inline functions). +# Few functions that are not inline are in the `pyx` file. +# The python wrapper and all the doctests are in bitset.pyx/bitset.pxd. + from libc.string cimport strlen from cysignals.memory cimport check_calloc, check_reallocarray, sig_malloc, sig_free from sage.cpython.string cimport char_to_str, str_to_bytes, bytes_to_str from sage.libs.gmp.mpn cimport * -from sage.data_structures.bitset cimport * +from sage.libs.gmp.types cimport * +from sage.data_structures.sparse_bitset cimport sparse_bitset_t from cython.operator import preincrement as preinc +from sage.ext.memory_allocator cimport MemoryAllocator + + +cdef extern from *: + # Given an element index n in a set, (n >> index_shift) gives the + # corresponding limb number. + int index_shift "(sizeof(mp_limb_t) == 8 ? 6 : 5)" + + +cdef struct bitset_s: + # The size of a bitset B counts the maximum number of bits that B can + # hold. This size is independent of how many elements of B are toggled to + # 1. For example, say B is the bitset 1001. Then B has size 4, with the + # first and fourth elements toggled to 1, reading from left to right. + # We can also think of the size of a bitset as its capacity. + mp_bitcnt_t size + + # A limb is that part of a bitset that can fit into an mp_limb_t + # (typically, 32 bits on a 32-bit machine and 64 bits on a 64-bit + # machine). This counts the number of limbs to represent a bitset. + # If a bitset has size <= n, then the whole bitset fits into a limb + # and we only require one limb to represent the bitset. However, if + # the bitset has size > n, we require more than one limb to + # represent the bitset. For example, if a limb is 64 bits in length + # and the bitset has size 96 bits, then we require at most two limbs + # to represent the bitset. + # + # NOTE: some code assumes that mp_limb_t is an unsigned long + # (this assumption is always true in practice). + mp_size_t limbs + + # The individual bits of a bitset. + mp_limb_t* bits + +ctypedef bitset_s bitset_t[1] + +ctypedef fused fused_bitset_t: + bitset_t + sparse_bitset_t # Doctests for the functions in this file are in sage/data_structures/bitset.pyx +############################################################################# +# Functions that can be optimized by intrinsics. +############################################################################# + +cdef extern from "bitset_intrinsics.h": + cdef const mp_bitcnt_t LIMB_SIZE + cdef const mp_bitcnt_t ALIGNMENT + + # Bitset Comparison + cdef bint _bitset_isempty(mp_limb_t* bits, mp_bitcnt_t limbs) nogil + cdef bint _bitset_eq(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + cdef bint _bitset_issubset(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + cdef bint _bitset_are_disjoint(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + + # Bitset Searching + cdef long _bitset_first_in_limb(mp_limb_t limb) nogil + cdef long _bitset_first_in_limb_nonzero(mp_limb_t) nogil + cdef long _bitset_len(mp_limb_t* bits, mp_bitcnt_t limbs) nogil + + # Bitset Arithmetic + cdef void _bitset_intersection(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + cdef void _bitset_union(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + cdef void _bitset_difference(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + cdef void _bitset_symmetric_difference(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs) nogil + ############################################################################# # Creating limb patterns ############################################################################# @@ -81,8 +152,28 @@ cdef inline bint bitset_init(bitset_t bits, mp_bitcnt_t size) except -1: raise ValueError("bitset capacity must be greater than 0") bits.size = size - bits.limbs = (size - 1) / (8 * sizeof(mp_limb_t)) + 1 - bits.bits = check_calloc(bits.limbs, sizeof(mp_limb_t)) + bits.limbs = (size - 1) / (8 * LIMB_SIZE) + 1 + bits.bits = check_calloc(bits.limbs, LIMB_SIZE) + +cdef inline bint bitset_init_with_allocator(fused_bitset_t bits, mp_bitcnt_t size, MemoryAllocator mem) except -1: + """ + Allocate an empty bitset of size ``size``. + + Size must be at least 1. + """ + if size <= 0: + raise ValueError("bitset capacity must be greater than 0") + + bits.size = size + bits.limbs = ((size - 1) / (8*ALIGNMENT) + 1) * (ALIGNMENT/LIMB_SIZE) + bits.bits = mem.aligned_calloc(ALIGNMENT, bits.limbs, LIMB_SIZE) + +cdef inline bint bitset_check_alignment(fused_bitset_t bits): + """ + Return whether the bitset is aligned correctly. + """ + cdef size_t address = bits.bits + return address == (address & ~(ALIGNMENT - 1)) cdef inline int bitset_realloc(bitset_t bits, mp_bitcnt_t size) except -1: """ @@ -96,8 +187,8 @@ cdef inline int bitset_realloc(bitset_t bits, mp_bitcnt_t size) except -1: if size <= 0: raise ValueError("bitset capacity must be greater than 0") - cdef mp_size_t limbs_new = (size - 1) / (8 * sizeof(mp_limb_t)) + 1 - bits.bits = check_reallocarray(bits.bits, limbs_new, sizeof(mp_limb_t)) + cdef mp_size_t limbs_new = (size - 1) / (8 * LIMB_SIZE) + 1 + bits.bits = check_reallocarray(bits.bits, limbs_new, LIMB_SIZE) bits.size = size bits.limbs = limbs_new @@ -114,13 +205,13 @@ cdef inline void bitset_free(bitset_t bits): """ sig_free(bits.bits) -cdef inline void bitset_clear(bitset_t bits): +cdef inline void bitset_clear(fused_bitset_t bits): """ Remove all elements from the set. """ mpn_zero(bits.bits, bits.limbs) -cdef inline void bitset_zero(bitset_t bits): +cdef inline void bitset_zero(fused_bitset_t bits): """ Remove all elements from the set. @@ -128,7 +219,7 @@ cdef inline void bitset_zero(bitset_t bits): """ mpn_zero(bits.bits, bits.limbs) -cdef inline void bitset_copy(bitset_t dst, bitset_t src): +cdef inline void bitset_copy(fused_bitset_t dst, fused_bitset_t src): """ Copy the bitset src over to the bitset dst, overwriting dst. @@ -136,7 +227,18 @@ cdef inline void bitset_copy(bitset_t dst, bitset_t src): """ mpn_copyi(dst.bits, src.bits, src.limbs) -cdef inline void bitset_fix(bitset_t bits): +cdef inline void bitset_copy_flex(fused_bitset_t dst, fused_bitset_t src): + """ + Copy the bitset src over to the bitset dst, overwriting dst. + + Set additional limbs in dst to zero. + + We assume ``dst.limbs >= src.limbs``. + """ + mpn_copyi(dst.bits, src.bits, src.limbs) + mpn_zero(dst.bits+src.limbs, src.limbs-dst.limbs) + +cdef inline void bitset_fix(fused_bitset_t bits): """ Clear upper bits in upper limb which should be zero. """ @@ -161,7 +263,7 @@ cdef inline bint mpn_equal_bits(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t n): cdef mp_limb_t b2h = b2[nlimbs] return (b1h ^ b2h) & mask == 0 -cdef bint mpn_equal_bits_shifted(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t n, mp_bitcnt_t offset): +cdef inline bint mpn_equal_bits_shifted(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t n, mp_bitcnt_t offset): """ Return ``True`` iff the first n bits of *b1 and the bits ranging from offset to offset+n of *b2 agree. @@ -176,7 +278,7 @@ cdef bint mpn_equal_bits_shifted(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t n, mp_b # bits of an additional limb of b1 to be considered cdef mp_limb_t tmp_limb cdef mp_size_t i1 - for i1 from 0 <= i1 < nlimbs: + for i1 in range(nlimbs): tmp_limb = (b2[i2] >> bit_offset) tmp_limb |= (b2[preinc(i2)] << neg_bit_offset) if tmp_limb != b1[i1]: @@ -192,21 +294,14 @@ cdef bint mpn_equal_bits_shifted(mp_srcptr b1, mp_srcptr b2, mp_bitcnt_t n, mp_b tmp_limb |= (b2[preinc(i2)] << neg_bit_offset) return (b1h ^ tmp_limb) & mask == 0 -cdef inline bint bitset_isempty(bitset_t bits): +cdef inline bint bitset_isempty(fused_bitset_t bits) nogil: """ Test whether bits is empty. Return True (i.e., 1) if the set is empty, False (i.e., 0) otherwise. """ - # First check lowest limb - if bits.bits[0]: - return False - if bits.limbs == 1: - return True - # Compare bits to itself shifted by 1 limb. If these compare equal, - # all limbs must be 0. - return mpn_cmp(bits.bits+1, bits.bits, bits.limbs-1) == 0 + return _bitset_isempty(bits.bits, bits.limbs) -cdef inline bint bitset_is_zero(bitset_t bits): +cdef inline bint bitset_is_zero(fused_bitset_t bits): """ Test whether bits is empty (i.e., zero). Return True (1) if the set is empty, False (0) otherwise. @@ -215,16 +310,16 @@ cdef inline bint bitset_is_zero(bitset_t bits): """ return bitset_isempty(bits) -cdef inline bint bitset_eq(bitset_t a, bitset_t b): +cdef inline bint bitset_eq(fused_bitset_t a, fused_bitset_t b): """ Compare bitset a and b. Return True (i.e., 1) if the sets are equal, and False (i.e., 0) otherwise. We assume ``a.limbs >= b.limbs``. """ - return mpn_cmp(a.bits, b.bits, b.limbs) == 0 + return _bitset_eq(a.bits, b.bits, b.limbs) -cdef inline int bitset_cmp(bitset_t a, bitset_t b): +cdef inline int bitset_cmp(fused_bitset_t a, fused_bitset_t b): """ Compare bitsets a and b. Return 0 if the two sets are identical, and consistently return -1 or 1 for two sets that are @@ -234,7 +329,7 @@ cdef inline int bitset_cmp(bitset_t a, bitset_t b): """ return mpn_cmp(a.bits, b.bits, b.limbs) -cdef inline int bitset_lex_cmp(bitset_t a, bitset_t b): +cdef inline int bitset_lex_cmp(fused_bitset_t a, fused_bitset_t b): """ Compare bitsets ``a`` and ``b`` using lexicographical ordering. @@ -263,20 +358,16 @@ cdef inline int bitset_lex_cmp(bitset_t a, bitset_t b): else: return -1 -cdef inline bint bitset_issubset(bitset_t a, bitset_t b): +cdef inline bint bitset_issubset(fused_bitset_t a, fused_bitset_t b) nogil: """ Test whether a is a subset of b (i.e., every element in a is also in b). We assume ``a.limbs <= b.limbs``. """ - cdef mp_size_t i - for i from 0 <= i < a.limbs: - if (a.bits[i] & ~b.bits[i]) != 0: - return False - return True + return _bitset_issubset(a.bits, b.bits, a.limbs) -cdef inline bint bitset_issuperset(bitset_t a, bitset_t b): +cdef inline bint bitset_issuperset(fused_bitset_t a, fused_bitset_t b) nogil: """ Test whether a is a superset of b (i.e., every element in b is also in a). @@ -285,32 +376,27 @@ cdef inline bint bitset_issuperset(bitset_t a, bitset_t b): """ return bitset_issubset(b, a) - -cdef inline bint bitset_are_disjoint(bitset_t a, bitset_t b): +cdef inline bint bitset_are_disjoint(fused_bitset_t a, fused_bitset_t b): """ Tests whether ``a`` and ``b`` have an empty intersection. We assume ``a.limbs <= b.limbs``. """ - cdef mp_size_t i - for i from 0 <= i < a.limbs: - if (a.bits[i]&b.bits[i]) != 0: - return False - return True + return _bitset_are_disjoint(a.bits, b.bits, a.limbs) ############################################################################# # Bitset Bit Manipulation ############################################################################# -cdef inline bint bitset_in(bitset_t bits, mp_bitcnt_t n): +cdef inline bint bitset_in(fused_bitset_t bits, mp_bitcnt_t n): """ Check if n is in bits. Return True (i.e., 1) if n is in the set, False (i.e., 0) otherwise. """ return (bits.bits[n >> index_shift] >> (n % GMP_LIMB_BITS)) & 1 -cdef inline bint bitset_check(bitset_t bits, mp_bitcnt_t n): +cdef inline bint bitset_check(fused_bitset_t bits, mp_bitcnt_t n): """ Check if n is in bits. Return True (i.e., 1) if n is in the set, False (i.e., 0) otherwise. @@ -319,14 +405,14 @@ cdef inline bint bitset_check(bitset_t bits, mp_bitcnt_t n): """ return bitset_in(bits, n) -cdef inline bint bitset_not_in(bitset_t bits, mp_bitcnt_t n): +cdef inline bint bitset_not_in(fused_bitset_t bits, mp_bitcnt_t n): """ Check if n is not in bits. Return True (i.e., 1) if n is not in the set, False (i.e., 0) otherwise. """ return not bitset_in(bits, n) -cdef inline bint bitset_remove(bitset_t bits, mp_bitcnt_t n) except -1: +cdef inline bint bitset_remove(fused_bitset_t bits, mp_bitcnt_t n) except -1: """ Remove n from bits. Raise KeyError if n is not contained in bits. """ @@ -334,13 +420,13 @@ cdef inline bint bitset_remove(bitset_t bits, mp_bitcnt_t n) except -1: raise KeyError(n) bitset_discard(bits, n) -cdef inline void bitset_discard(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_discard(fused_bitset_t bits, mp_bitcnt_t n): """ Remove n from bits. """ bits.bits[n >> index_shift] &= limb_one_zero_bit(n) -cdef inline void bitset_unset(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_unset(fused_bitset_t bits, mp_bitcnt_t n): """ Remove n from bits. @@ -349,13 +435,13 @@ cdef inline void bitset_unset(bitset_t bits, mp_bitcnt_t n): bitset_discard(bits, n) -cdef inline void bitset_add(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_add(fused_bitset_t bits, mp_bitcnt_t n): """ Add n to bits. """ bits.bits[n >> index_shift] |= limb_one_set_bit(n) -cdef inline void bitset_set(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_set(fused_bitset_t bits, mp_bitcnt_t n): """ Add n to bits. @@ -370,65 +456,49 @@ cdef inline void bitset_set_to(bitset_t bits, mp_bitcnt_t n, bint b): bitset_unset(bits, n) bits.bits[n >> index_shift] |= (b) << (n % GMP_LIMB_BITS) -cdef inline void bitset_flip(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_flip(fused_bitset_t bits, mp_bitcnt_t n): """ If n is in bits, remove n from bits. If n is not in bits, add n to bits. """ bits.bits[n >> index_shift] ^= limb_one_set_bit(n) -cdef inline void bitset_set_first_n(bitset_t bits, mp_bitcnt_t n): +cdef inline void bitset_set_first_n(fused_bitset_t bits, mp_bitcnt_t n): """ Set exactly the first n bits. """ cdef mp_size_t i cdef mp_size_t index = n >> index_shift - for i from 0 <= i < index: + for i in range(index): bits.bits[i] = -1 if index < bits.limbs: bits.bits[index] = limb_lower_bits_down(n) - for i from index < i < bits.limbs: + for i in range(index+1, bits.limbs): bits.bits[i] = 0 ############################################################################# # Bitset Searching ############################################################################# -cdef inline long _bitset_first_in_limb_nonzero(mp_limb_t limb): - """ - Given a non-zero limb of a bitset, return the index of the first - nonzero bit. - """ - return mpn_scan1(&limb, 0) - -cdef inline long _bitset_first_in_limb(mp_limb_t limb): - """ - Given a limb of a bitset, return the index of the first nonzero - bit. If there are no bits set in the limb, return -1. - """ - if limb == 0: - return -1 - return mpn_scan1(&limb, 0) - -cdef inline long bitset_first(bitset_t a): +cdef inline long bitset_first(fused_bitset_t a): """ Calculate the index of the first element in the set. If the set is empty, returns -1. """ cdef mp_size_t i - for i from 0 <= i < a.limbs: + for i in range(a.limbs): if a.bits[i]: return (i << index_shift) | _bitset_first_in_limb_nonzero(a.bits[i]) return -1 -cdef inline long bitset_first_in_complement(bitset_t a): +cdef inline long bitset_first_in_complement(fused_bitset_t a): """ Calculate the index of the first element not in the set. If the set is full, returns -1. """ cdef mp_size_t i cdef mp_bitcnt_t j - for i from 0 <= i < a.limbs: + for i in range(a.limbs): if ~a.bits[i]: j = (i << index_shift) | _bitset_first_in_limb_nonzero(~a.bits[i]) if j >= a.size: @@ -436,7 +506,7 @@ cdef inline long bitset_first_in_complement(bitset_t a): return j return -1 -cdef inline long bitset_pop(bitset_t a) except -1: +cdef inline long bitset_pop(fused_bitset_t a) except -1: """ Remove and return an arbitrary element from the set. Raise KeyError if the set is empty. @@ -447,7 +517,7 @@ cdef inline long bitset_pop(bitset_t a) except -1: bitset_discard(a, i) return i -cdef inline long bitset_first_diff(bitset_t a, bitset_t b): +cdef inline long bitset_first_diff(fused_bitset_t a, fused_bitset_t b): """ Calculate the index of the first difference between a and b. If a and b are equal, then return -1. @@ -455,12 +525,12 @@ cdef inline long bitset_first_diff(bitset_t a, bitset_t b): We assume ``a.limbs == b.limbs``. """ cdef mp_size_t i - for i from 0 <= i < a.limbs: + for i in range(a.limbs): if a.bits[i] != b.bits[i]: return (i << index_shift) | _bitset_first_in_limb_nonzero(a.bits[i] ^ b.bits[i]) return -1 -cdef inline long bitset_next(bitset_t a, mp_bitcnt_t n): +cdef inline long bitset_next(fused_bitset_t a, mp_bitcnt_t n): """ Calculate the index of the next element in the set, starting at (and including) n. Return -1 if there are no elements from n @@ -473,12 +543,12 @@ cdef inline long bitset_next(bitset_t a, mp_bitcnt_t n): cdef long ret = _bitset_first_in_limb(limb) if ret != -1: return (i << index_shift) | ret - for i from (n >> index_shift) < i < a.limbs: + for i in range((n >> index_shift) + 1, a.limbs): if a.bits[i]: return (i << index_shift) | _bitset_first_in_limb_nonzero(a.bits[i]) return -1 -cdef inline long bitset_next_diff(bitset_t a, bitset_t b, mp_bitcnt_t n): +cdef inline long bitset_next_diff(fused_bitset_t a, fused_bitset_t b, mp_bitcnt_t n): """ Calculate the index of the next element that differs between a and b, starting at (and including) n. Return -1 if there are no @@ -493,18 +563,18 @@ cdef inline long bitset_next_diff(bitset_t a, bitset_t b, mp_bitcnt_t n): cdef long ret = _bitset_first_in_limb(limb) if ret != -1: return (i << index_shift) | ret - for i from (n >> index_shift) < i < a.limbs: + for i in range((n >> index_shift) + 1, a.limbs): if a.bits[i] != b.bits[i]: return (i << index_shift) | _bitset_first_in_limb(a.bits[i] ^ b.bits[i]) return -1 -cdef inline long bitset_len(bitset_t bits): +cdef inline long bitset_len(fused_bitset_t bits) nogil: """ Calculate the number of items in the set (i.e., the number of nonzero bits). """ - return mpn_popcount(bits.bits, bits.limbs) + return _bitset_len(bits.bits, bits.limbs) -cdef inline long bitset_hash(bitset_t bits): +cdef inline long bitset_hash(fused_bitset_t bits): """ Calculate a (very naive) hash function. @@ -513,7 +583,7 @@ cdef inline long bitset_hash(bitset_t bits): """ cdef mp_limb_t hash = 0 cdef mp_size_t i - for i from 0 <= i < bits.limbs: + for i in range(bits.limbs): hash += bits.bits[i] return hash @@ -521,7 +591,7 @@ cdef inline long bitset_hash(bitset_t bits): # Bitset Arithmetic ############################################################################# -cdef inline void bitset_complement(bitset_t r, bitset_t a): +cdef inline void bitset_complement(fused_bitset_t r, fused_bitset_t a): """ Set r to be the complement of a, overwriting r. @@ -530,7 +600,7 @@ cdef inline void bitset_complement(bitset_t r, bitset_t a): mpn_com(r.bits, a.bits, a.limbs) bitset_fix(r) -cdef inline void bitset_not(bitset_t r, bitset_t a): +cdef inline void bitset_not(fused_bitset_t r, fused_bitset_t a): """ Set r to be the complement of a, overwriting r. @@ -540,15 +610,15 @@ cdef inline void bitset_not(bitset_t r, bitset_t a): """ bitset_complement(r, a) -cdef inline void bitset_intersection(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_intersection(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b) nogil: """ Set r to the intersection of a and b, overwriting r. We assume ``a.limbs >= r.limbs == b.limbs``. """ - mpn_and_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_intersection(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_and(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_and(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b): """ Set r to the intersection of a and b, overwriting r. @@ -556,18 +626,18 @@ cdef inline void bitset_and(bitset_t r, bitset_t a, bitset_t b): This function is the same as bitset_intersection(r, a, b). """ - mpn_and_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_intersection(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_union(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_union(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b) nogil: """ Set r to the union of a and b, overwriting r. We assume ``r.limbs >= a.limbs >= b.limbs`` and either ``r is a`` or ``r.limbs == b.limbs``. """ - mpn_ior_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_union(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_or(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_or(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b): """ Set r to the union of a and b, overwriting r. @@ -576,9 +646,9 @@ cdef inline void bitset_or(bitset_t r, bitset_t a, bitset_t b): This function is the same as bitset_union(r, a, b). """ - mpn_ior_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_union(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_difference(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_difference(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b): """ Set r to the difference of a and b (i.e., things in a that are not in b), overwriting r. @@ -586,18 +656,18 @@ cdef inline void bitset_difference(bitset_t r, bitset_t a, bitset_t b): We assume ``r.limbs >= a.limbs >= b.limbs`` and either ``r is a`` or ``r.limbs == b.limbs``. """ - mpn_andn_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_difference(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_symmetric_difference(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_symmetric_difference(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b): """ Set r to the symmetric difference of a and b, overwriting r. We assume ``r.limbs >= a.limbs >= b.limbs`` and either ``r is a`` or ``r.limbs == b.limbs``. """ - mpn_xor_n(r.bits, a.bits, b.bits, b.limbs) + _bitset_symmetric_difference(r.bits, a.bits, b.bits, b.limbs) -cdef inline void bitset_xor(bitset_t r, bitset_t a, bitset_t b): +cdef inline void bitset_xor(fused_bitset_t r, fused_bitset_t a, fused_bitset_t b): """ Set r to the symmetric difference of a and b, overwriting r. @@ -606,10 +676,9 @@ cdef inline void bitset_xor(bitset_t r, bitset_t a, bitset_t b): This function is the same as bitset_symmetric_difference(r, a, b). """ - mpn_xor_n(r.bits, a.bits, b.bits, b.limbs) - + _bitset_symmetric_difference(r.bits, a.bits, b.bits, b.limbs) -cdef void bitset_rshift(bitset_t r, bitset_t a, mp_bitcnt_t n): +cdef inline void bitset_rshift(fused_bitset_t r, fused_bitset_t a, mp_bitcnt_t n): """ Shift the bitset ``a`` right by ``n`` bits and store the result in ``r``. @@ -649,7 +718,7 @@ cdef void bitset_rshift(bitset_t r, bitset_t a, mp_bitcnt_t n): # Clear bits outside bitset in top limb bitset_fix(r) -cdef void bitset_lshift(bitset_t r, bitset_t a, mp_bitcnt_t n): +cdef inline void bitset_lshift(fused_bitset_t r, fused_bitset_t a, mp_bitcnt_t n): """ Shift the bitset ``a`` left by ``n`` bits and store the result in ``r``. @@ -691,8 +760,7 @@ cdef void bitset_lshift(bitset_t r, bitset_t a, mp_bitcnt_t n): # Clear bottom limbs mpn_zero(r.bits, nlimbs) - -cdef int bitset_map(bitset_t r, bitset_t a, m) except -1: +cdef inline int bitset_map(fused_bitset_t r, fused_bitset_t a, m) except -1: """ Fill bitset ``r`` so ``r == {m[i] for i in a}``. @@ -704,7 +772,7 @@ cdef int bitset_map(bitset_t r, bitset_t a, m) except -1: bitset_clear(r) i = bitset_first(a) while i >= 0: - bitset_add(r, m[i]) + bitset_add(r, m[i]) i = bitset_next(a, i + 1) return 0 @@ -712,118 +780,25 @@ cdef int bitset_map(bitset_t r, bitset_t a, m) except -1: # Hamming Weights ############################################################################# -cdef inline long bitset_hamming_weight(bitset_t a): +cdef inline long bitset_hamming_weight(fused_bitset_t a): return bitset_len(a) ############################################################################# # Bitset Conversion ############################################################################# -cdef char* bitset_chars(char* s, bitset_t bits, char zero=c'0', char one=c'1'): - """ - Return a string representation of the bitset in s, using zero for - the character representing the items not in the bitset and one for - the character representing the items in the bitset. - - The string is both stored in s and returned. If s is NULL, then a - new string is allocated. - """ - cdef mp_bitcnt_t i - if s == NULL: - s = sig_malloc(bits.size + 1) - for i from 0 <= i < bits.size: - s[i] = one if bitset_in(bits, i) else zero - s[bits.size] = 0 - return s - - -cdef int bitset_from_char(bitset_t bits, char* s, char zero=c'0', char one=c'1') except -1: - """ - Initialize a bitset with a set derived from the C string s, where one - represents the character indicating set membership. - """ - bitset_init(bits, strlen(s)) - cdef mp_bitcnt_t i - for i from 0 <= i < bits.size: - bitset_set_to(bits, i, s[i] == one) - return 0 - - -cdef int bitset_from_str(bitset_t bits, object s, char zero=c'0', char one=c'1') except -1: - """ - Initialize a bitset with a set derived from the Python str s, where one - represents the character indicating set membership. - """ - cdef bytes b = str_to_bytes(s) - return bitset_from_char(bits, b, zero, one) - - -cdef bitset_string(bitset_t bits): - """ - Return a python string representing the bitset. - """ - return bytes_to_str(bitset_bytes(bits)) - - -cdef bitset_bytes(bitset_t bits): - """ - Return a python bytes string representing the bitset. +cdef char* bitset_chars(char* s, fused_bitset_t bits, char zero=*, char one=*) - On Python 2 this is equivalent to bitset_string. - """ +cdef int bitset_from_char(bitset_t bits, char* s, char zero=*, char one=*) except -1 - cdef char* s = bitset_chars(NULL, bits) - cdef object py_s - py_s = s - sig_free(s) - return py_s +cdef int bitset_from_str(bitset_t bits, object s, char zero=*, char one=*) except -1 +cdef bitset_string(fused_bitset_t bits) -cdef list bitset_list(bitset_t bits): - """ - Return a list of elements in the bitset. - """ - cdef list elts = [] - cdef long elt = bitset_first(bits) - while elt >= 0: - elts.append(elt) - elt = bitset_next(bits, elt + 1) - return elts +cdef bitset_bytes(fused_bitset_t bits) -cdef bitset_pickle(bitset_t bs): - """ - Convert ``bs`` to a reasonably compact Python structure. +cdef list bitset_list(fused_bitset_t bits) - Useful for pickling objects using bitsets as internal data structure. - To ensure this works on 32-bit and 64-bit machines, the size of a long - is stored too. - """ - version = 0 - data = [] - for i from 0 <= i < bs.limbs: - data.append(bs.bits[i]) - return (version, bs.size, bs.limbs, sizeof(unsigned long), tuple(data)) +cdef bitset_pickle(bitset_t bs) -cdef bitset_unpickle(bitset_t bs, tuple input): - """ - Convert the data into a bitset. - - Companion of ``bitset_pickle()``. Assumption: ``bs`` has been initialized. - """ - version, size, limbs, longsize, data = input - if version != 0: - raise TypeError("bitset was saved with newer version of Sage. Please upgrade.") - if bs.size != size: - bitset_realloc(bs, size) - if sizeof(unsigned long) == longsize and bs.limbs == limbs: - for i from 0 <= i < bs.limbs: - bs.bits[i] = data[i] - else: - storage = 8 * longsize # number of elements encoded in one limb - adder = 0 - bitset_clear(bs) - for i from 0 <= i < limbs: - for j from 0 <= j < storage: - if (data[i] >> j) & 1: - bitset_add(bs, j + adder) - adder += storage +cdef bitset_unpickle(bitset_t bs, tuple input) diff --git a/src/sage/data_structures/bitset_base.pyx b/src/sage/data_structures/bitset_base.pyx new file mode 100644 index 00000000000..6a527a8ebfe --- /dev/null +++ b/src/sage/data_structures/bitset_base.pyx @@ -0,0 +1,116 @@ +""" +Few functions from ``bitset_base.pxd`` that are not inlined. +""" + +#***************************************************************************** +# Copyright (C) 2008 Robert Bradshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +cdef char* bitset_chars(char* s, fused_bitset_t bits, char zero=c'0', char one=c'1'): + """ + Return a string representation of the bitset in s, using zero for + the character representing the items not in the bitset and one for + the character representing the items in the bitset. + + The string is both stored in s and returned. If s is NULL, then a + new string is allocated. + """ + cdef mp_bitcnt_t i + if s == NULL: + s = sig_malloc(bits.size + 1) + for i in range(bits.size): + s[i] = one if bitset_in(bits, i) else zero + s[bits.size] = 0 + return s + +cdef int bitset_from_char(bitset_t bits, char* s, char zero=c'0', char one=c'1') except -1: + """ + Initialize a bitset with a set derived from the C string s, where one + represents the character indicating set membership. + """ + bitset_init(bits, strlen(s)) + cdef mp_bitcnt_t i + for i in range(bits.size): + bitset_set_to(bits, i, s[i] == one) + return 0 + +cdef int bitset_from_str(bitset_t bits, object s, char zero=c'0', char one=c'1') except -1: + """ + Initialize a bitset with a set derived from the Python str s, where one + represents the character indicating set membership. + """ + cdef bytes b = str_to_bytes(s) + return bitset_from_char(bits, b, zero, one) + +cdef bitset_string(fused_bitset_t bits): + """ + Return a python string representing the bitset. + """ + return bytes_to_str(bitset_bytes(bits)) + +cdef bitset_bytes(fused_bitset_t bits): + """ + Return a python bytes string representing the bitset. + + On Python 2 this is equivalent to bitset_string. + """ + + cdef char* s = bitset_chars(NULL, bits) + cdef object py_s + py_s = s + sig_free(s) + return py_s + +cdef list bitset_list(fused_bitset_t bits): + """ + Return a list of elements in the bitset. + """ + cdef list elts = [] + cdef long elt = bitset_first(bits) + while elt >= 0: + elts.append(elt) + elt = bitset_next(bits, elt + 1) + return elts + +cdef bitset_pickle(bitset_t bs): + """ + Convert ``bs`` to a reasonably compact Python structure. + + Useful for pickling objects using bitsets as internal data structure. + To ensure this works on 32-bit and 64-bit machines, the size of a long + is stored too. + """ + version = 0 + data = [] + for i in range(bs.limbs): + data.append(bs.bits[i]) + return (version, bs.size, bs.limbs, sizeof(unsigned long), tuple(data)) + +cdef bitset_unpickle(bitset_t bs, tuple input): + """ + Convert the data into a bitset. + + Companion of ``bitset_pickle()``. Assumption: ``bs`` has been initialized. + """ + version, size, limbs, longsize, data = input + if version != 0: + raise TypeError("bitset was saved with newer version of Sage. Please upgrade.") + if bs.size != size: + bitset_realloc(bs, size) + if sizeof(unsigned long) == longsize and bs.limbs == limbs: + for i in range(bs.limbs): + bs.bits[i] = data[i] + else: + storage = 8 * longsize # number of elements encoded in one limb + adder = 0 + bitset_clear(bs) + for i in range(limbs): + for j in range(storage): + if (data[i] >> j) & 1: + bitset_add(bs, (j + adder)) + adder += storage diff --git a/src/sage/data_structures/bitset_intrinsics.h b/src/sage/data_structures/bitset_intrinsics.h new file mode 100644 index 00000000000..07b94a9fe29 --- /dev/null +++ b/src/sage/data_structures/bitset_intrinsics.h @@ -0,0 +1,125 @@ +#include "gmp.h" + +// This file contains functions of ``bitset_base.pxd`` +// that can be optimized using intrinsics. + +/* +############################################################################# +# Bitset Comparison +############################################################################# +*/ + +const mp_bitcnt_t LIMB_SIZE = sizeof(mp_limb_t); +const mp_bitcnt_t ALIGNMENT = sizeof(mp_limb_t); + +inline int _bitset_isempty(mp_limb_t* bits, mp_bitcnt_t limbs){ + /* + Test whether bits is empty. Return True (i.e., 1) if the set is + empty, False (i.e., 0) otherwise. + */ + // First check lowest limb + if (bits[0]) + return 0; + if (limbs == 1) + return 1; + // Compare bits to itself shifted by 1 limb. If these compare equal, + // all limbs must be 0. + return mpn_cmp(bits+1, bits, limbs-1) == 0; +} + +inline int _bitset_eq(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Compare bitset a and b. Return True (i.e., 1) if the sets are + equal, and False (i.e., 0) otherwise. + */ + return mpn_cmp(a, b, limbs) == 0; +} + +inline int _bitset_issubset(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Test whether a is a subset of b (i.e., every element in a is also + in b). + */ + for(mp_bitcnt_t i = 0; i < limbs; i++){ + if ((a[i] & ~b[i]) != 0) + return 0; + } + return 1; +} + +inline int _bitset_are_disjoint(mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Tests whether ``a`` and ``b`` have an empty intersection. + */ + for(mp_bitcnt_t i = 0; i < limbs; i++){ + if (a[i] & b[i]) + return 0; + } + return 1; +} + +/* +############################################################################# +# Bitset Searching +############################################################################# +*/ + +inline long _bitset_first_in_limb(mp_limb_t limb){ + /* + Given a limb of a bitset, return the index of the first nonzero + bit. If there are no bits set in the limb, return -1. + */ + if (limb == 0) + return -1; + return mpn_scan1(&limb, 0); +} + +inline long _bitset_first_in_limb_nonzero(mp_limb_t limb){ + /* + Given a non-zero limb of a bitset, return the index of the first + nonzero bit. + */ + return mpn_scan1(&limb, 0); +} + +inline long _bitset_len(mp_limb_t* bits, mp_bitcnt_t limbs){ + /* + Calculate the number of items in the set (i.e., the number of nonzero bits). + */ + return mpn_popcount(bits, limbs); +} + +/* +############################################################################# +# Bitset Arithmetic +############################################################################# +*/ + +inline void _bitset_intersection(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Set dst to the intersection of a and b, overwriting dst. + */ + mpn_and_n(dst, a, b, limbs); +} + +inline void _bitset_union(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Set dst to the union of a and b, overwriting dst. + */ + mpn_ior_n(dst, a, b, limbs); +} + +inline void _bitset_difference(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Set dst to the difference of a and b (i.e., things in a that are not + in b), overwriting dst. + */ + mpn_andn_n(dst, a, b, limbs); +} + +inline void _bitset_symmetric_difference(mp_limb_t* dst, mp_limb_t* a, mp_limb_t* b, mp_bitcnt_t limbs){ + /* + Set dst to the symmetric difference of a and b, overwriting dst. + */ + mpn_xor_n(dst, a, b, limbs); +} diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index ded2429a2ea..af9c15952fd 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -108,7 +108,7 @@ AUTHORS: # **************************************************************************** from cysignals.signals cimport sig_check, sig_on, sig_off -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * from cpython.int cimport PyInt_FromSize_t from cpython.slice cimport PySlice_GetIndicesEx diff --git a/src/sage/data_structures/sparse_bitset.pxd b/src/sage/data_structures/sparse_bitset.pxd new file mode 100644 index 00000000000..6da9183b301 --- /dev/null +++ b/src/sage/data_structures/sparse_bitset.pxd @@ -0,0 +1,45 @@ +""" +Sparse bitset. + +This is a regular bitset to which we we will add additional structure. + +In particular some representation of which limbs even contain data. +""" +# **************************************************************************** +# Copyright (C) 2020 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.libs.gmp.types cimport * + +cdef struct sparse_bitset_s: + # The size of a bitset B counts the maximum number of bits that B can + # hold. This size is independent of how many elements of B are toggled to + # 1. For example, say B is the bitset 1001. Then B has size 4, with the + # first and fourth elements toggled to 1, reading from left to right. + # We can also think of the size of a bitset as its capacity. + mp_bitcnt_t size + + # A limb is that part of a bitset that can fit into an mp_limb_t + # (typically, 32 bits on a 32-bit machine and 64 bits on a 64-bit + # machine). This counts the number of limbs to represent a bitset. + # If a bitset has size <= n, then the whole bitset fits into a limb + # and we only require one limb to represent the bitset. However, if + # the bitset has size > n, we require more than one limb to + # represent the bitset. For example, if a limb is 64 bits in length + # and the bitset has size 96 bits, then we require at most two limbs + # to represent the bitset. + # + # NOTE: some code assumes that mp_limb_t is an unsigned long + # (this assumption is always true in practice). + mp_size_t limbs + + # The individual bits of a bitset. + mp_limb_t* bits + +ctypedef sparse_bitset_s sparse_bitset_t[1] diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index c6d9840ccc2..bf2024ce5b6 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -1,4 +1,6 @@ -import sys, os, sphinx +import sys +import os +import sphinx from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC, THEBE_DIR, PPLPY_DOCS, MATHJAX_DIR import sage.version from sage.misc.sagedoc import extlinks diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index a03f6cb08ee..14e9dfc6f2a 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -44,7 +44,11 @@ optionaltag_regex = re.compile(r'^\w+$') # Optional tags which are always automatically added -auto_optional_tags = set(['py3']) + +from sage.libs.arb.arb_version import version as arb_vers +arb_tag = 'arb2' + arb_vers().split('.')[1] + +auto_optional_tags = set(['py3', arb_tag]) class DocTestDefaults(SageObject): @@ -771,8 +775,8 @@ def expand_files_into_sources(self): sage: DD = DocTestDefaults(optional='magma,guava') sage: DC = DocTestController(DD, [dirname]) sage: DC.expand_files_into_sources() - sage: sorted(DC.sources[0].options.optional) # abs tol 1 - ['guava', 'magma', 'py2'] + sage: all(t in DC.sources[0].options.optional for t in ['magma','guava']) + True We check that files are skipped appropriately:: diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 3a96fa605f1..4e1a6b32e41 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -34,7 +34,7 @@ from .external import available_software 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)*))') +optional_regex = re.compile(r'(arb216|arb218|py2|py3|long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') # Version 4.65 of glpk prints the warning "Long-step dual simplex will # be used" frequently. When Sage uses a system installation of glpk # which has not been patched, we need to ignore that message. @@ -297,6 +297,8 @@ def parse_optional_tags(string): - 'known bug' - 'py2' - 'py3' + - 'arb216' + - 'arb218' - 'optional: PKG_NAME' -- the set will just contain 'PKG_NAME' EXAMPLES:: diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 68ba38fcd1a..b87a86142a3 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -2335,7 +2335,7 @@ def _multipliermod(self, P, n, p, k): def _nth_preimage_tree_helper(self, Q, n, m, **kwds): r""" - A recusive method to fill in ``n``-th preimage tree. + A recursive method to fill in ``n``-th preimage tree. This helper function is used by ``nth_preimage_tree`` below to actually compute the points of the tree and populate the dictionary used to create a ``DiGraph`` @@ -2419,7 +2419,7 @@ def nth_preimage_tree(self, Q, n, **kwds): - ``numerical`` -- (default: ``False``) boolean; calculate pre-images numerically. Note if this is set to ``True``, preimage points are displayed as complex numbers - - ``prec`` -- (default: 100) postive integer; the precision of the ``ComplexField`` if + - ``prec`` -- (default: 100) positive integer; the precision of the ``ComplexField`` if we compute the preimage points numerically - ``display_labels`` -- (default: ``True``) boolean; whether to display vertex labels. Since labels @@ -5391,7 +5391,7 @@ def all_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ, 1) sage: f = DynamicalSystem([x^2+ y^2, x*y]) - sage: f.all_periodic_points(algorithm="dnyatomic") + sage: f.all_periodic_points(algorithm="banana") Traceback (most recent call last): ... ValueError: algorithm must be 'dynatomic' or 'lifting' diff --git a/src/sage/ext_data/nbconvert/postprocess.py b/src/sage/ext_data/nbconvert/postprocess.py index d2ef5c5b76f..f36497fb73f 100755 --- a/src/sage/ext_data/nbconvert/postprocess.py +++ b/src/sage/ext_data/nbconvert/postprocess.py @@ -9,10 +9,11 @@ AUTHORS: - - Thierry Monteil (2018): initial version. +- Thierry Monteil (2018): initial version. """ -import sys, re +import sys +import re file_name = sys.argv[1] diff --git a/src/sage/ext_data/pari/simon/qfsolve.gp b/src/sage/ext_data/pari/simon/qfsolve.gp index ccaa66ae835..501fb50828d 100644 --- a/src/sage/ext_data/pari/simon/qfsolve.gp +++ b/src/sage/ext_data/pari/simon/qfsolve.gp @@ -802,7 +802,7 @@ return([G,H]); } \\ p a prime number. -\\ finds a solution mod p for the quadatic form G +\\ finds a solution mod p for the quadratic form G \\ such that det(G) !=0 mod p and dim G = n>=3; {qfsolvemodp(G,p) = my(vdet,G2,sol,x1,x2,x3,N1,N2,N3,s); diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 060aaf8fc2f..598ecf495e7 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -151,7 +151,7 @@ 0.403652637676806 """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Fredrik Johansson # Copyright (C) 2013 Eviatar Bach # @@ -159,8 +159,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ @@ -216,8 +216,9 @@ def rational_param_as_tuple(x): class Hypergeometric(BuiltinFunction): r""" - Represents a (formal) generalized infinite hypergeometric series. It is - defined as + Represent a (formal) generalized infinite hypergeometric series. + + It is defined as .. MATH:: @@ -235,11 +236,20 @@ def __init__(self): sage: maxima(hypergeometric) hypergeometric + + TESTS:: + + sage: F = hypergeometric([-4,2],[1],1) # optional - maple + sage: G = maple(F); G # optional - maple + hypergeom([-4, 2],[1],1) + sage: G.simplify() # optional - maple + 0 """ BuiltinFunction.__init__(self, 'hypergeometric', nargs=3, conversions={'mathematica': 'HypergeometricPFQ', 'maxima': 'hypergeometric', + 'maple': 'hypergeom', 'sympy': 'hyper', 'fricas': 'hypergeometricF'}) @@ -346,7 +356,7 @@ def _evalf_(self, a, b, z, parent, algorithm=None): 2.7182818284590452353602874714 """ - if not isinstance(a,tuple) or not isinstance(b,tuple): + if not isinstance(a, tuple) or not isinstance(b, tuple): raise TypeError("The first two parameters must be of type list") from mpmath import hyper aa = [rational_param_as_tuple(c) for c in a] @@ -472,9 +482,10 @@ def eliminate_parameters(self, a, b, z): def is_termwise_finite(self, a, b, z): """ - Determine whether all terms of ``self`` are finite. Any infinite - terms or ambiguous terms beyond the first zero, if one exists, - are ignored. + Determine whether all terms of ``self`` are finite. + + Any infinite terms or ambiguous terms beyond the first + zero, if one exists, are ignored. Ambiguous cases (where a term is the product of both zero and an infinity) are not considered finite. @@ -520,8 +531,10 @@ def is_termwise_finite(self, a, b, z): def is_terminating(self, a, b, z): r""" - Determine whether the series represented by self terminates - after a finite number of terms, i.e. whether any of the + Determine whether the series represented by ``self`` terminates + after a finite number of terms. + + This happens if any of the numerator parameters are nonnegative integers (with no preceding nonnegative denominator parameters), or `z = 0`. @@ -738,6 +751,7 @@ def _deflated(self, a, b, z): return terms return ((1, new),) + hypergeometric = Hypergeometric() @@ -913,8 +927,7 @@ def _2f1(a, b, c, z): if z == 1: return (gamma(cc) * gamma(cc - aa - bb) / gamma(cc - aa) / gamma(cc - bb)) - if ((aa * 2) in ZZ and (bb * 2) in ZZ and (cc * 2) in ZZ and - aa > 0 and bb > 0 and cc > 0): + if all((cf * 2) in ZZ and cf > 0 for cf in (aa, bb, cc)): try: return _2f1(aa, bb, cc, z) except NotImplementedError: @@ -922,6 +935,7 @@ def _2f1(a, b, c, z): return hyp return sum([coeff * _closed_form(pfq) for coeff, pfq in new._deflated()]) + class Hypergeometric_M(BuiltinFunction): r""" The confluent hypergeometric function of the first kind, @@ -930,15 +944,15 @@ class Hypergeometric_M(BuiltinFunction): .. MATH:: - zy'' + (b-z)y' - ay = 0. + zy'' + (b-z)y' - ay = 0. This is not the same as Kummer's `U`-hypergeometric function, though it satisfies the same DE that `M` does. .. warning:: - In the literature, both are called "Kummer confluent - hypergeometric" functions. + In the literature, both are called "Kummer confluent + hypergeometric" functions. EXAMPLES:: @@ -967,6 +981,7 @@ def __init__(self): BuiltinFunction.__init__(self, 'hypergeometric_M', nargs=3, conversions={'mathematica': 'Hypergeometric1F1', + 'maple': 'KummerM', 'maxima': 'kummer_m', 'fricas': 'kummerM'}, latex_name='M') @@ -1012,7 +1027,7 @@ def _derivative_(self, a, b, z, diff_param): class EvaluationMethods(object): def generalized(self, a, b, z): """ - Return as a generalized hypergeometric function + Return as a generalized hypergeometric function. EXAMPLES:: @@ -1024,8 +1039,10 @@ def generalized(self, a, b, z): """ return hypergeometric([a], [b], z) + hypergeometric_M = Hypergeometric_M() + class Hypergeometric_U(BuiltinFunction): r""" The confluent hypergeometric function of the second kind, @@ -1078,6 +1095,7 @@ def __init__(self): BuiltinFunction.__init__(self, 'hypergeometric_U', nargs=3, conversions={'mathematica': 'HypergeometricU', + 'maple': 'KummerU', 'maxima': 'kummer_u', 'fricas': 'kummerU'}, latex_name='U') @@ -1114,7 +1132,7 @@ def _derivative_(self, a, b, z, diff_param): class EvaluationMethods(object): def generalized(self, a, b, z): """ - Return in terms of the generalized hypergeometric function + Return in terms of the generalized hypergeometric function. EXAMPLES:: @@ -1130,4 +1148,5 @@ def generalized(self, a, b, z): """ return z ** (-a) * hypergeometric([a, a - b + 1], [], -z ** (-1)) + hypergeometric_U = Hypergeometric_U() diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index 8d86b1b8408..eb909343efa 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -424,6 +424,11 @@ def log(*args, **kwds): Traceback (most recent call last): ... TypeError: Symbolic function log takes at most 2 arguments (3 given) + + Check if :trac:`29164` is fixed:: + + sage: log(0, 2) + -Infinity """ base = kwds.pop('base', None) if base: diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index c389a7a383c..33b28dbedec 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -209,7 +209,7 @@ from sage.matrix.all import column_matrix, matrix, MatrixSpace from sage.misc.all import cached_method, flatten, latex from sage.modules.all import span, vector, VectorSpace -from sage.rings.all import QQ, RR, ZZ +from sage.rings.all import QQ, ZZ from sage.structure.all import SageObject, parent from sage.structure.richcmp import richcmp_method, richcmp from ppl import (C_Polyhedron, Generator_System, Constraint_System, @@ -526,9 +526,10 @@ def _ambient_space_point(body, data): OUTPUT: - - integral, rational or numeric point of the ambient space of ``body`` - if ``data`` were successfully interpreted in such a way, otherwise a - ``TypeError`` exception is raised + An integral, rational, real algebraic, or numeric point of the + ambient space of ``body`` is returned if ``data`` were + successfully interpreted in such a way. A ``TypeError`` is raised + otherwise. TESTS:: @@ -536,6 +537,8 @@ def _ambient_space_point(body, data): sage: c = Cone([(1,0), (0,1)]) sage: _ambient_space_point(c, [1,1]) N(1, 1) + sage: _ambient_space_point(c, vector(ZZ,[1,1])) + N(1, 1) sage: _ambient_space_point(c, c.dual_lattice()([1,1])) Traceback (most recent call last): ... @@ -543,30 +546,65 @@ def _ambient_space_point(body, data): 2-d cone in 2-d lattice N have incompatible lattices sage: _ambient_space_point(c, [1,1/3]) (1, 1/3) + sage: _ambient_space_point(c, vector(QQ,[1,1/3])) + (1, 1/3) sage: _ambient_space_point(c, [1/2,1/sqrt(3)]) - (0.500000000000000, 0.577350269189626) + (1/2, 0.5773502691896258?) + sage: _ambient_space_point(c, vector(AA,[1/2,1/sqrt(3)])) + (1/2, 0.5773502691896258?) sage: _ambient_space_point(c, [1,1,3]) Traceback (most recent call last): ... TypeError: [1, 1, 3] does not represent a valid point - in the ambient space of 2-d cone in 2-d lattice N + in the ambient space of 2-d cone in 2-d lattice N + sage: _ambient_space_point(c, vector(ZZ,[1,1,3])) + Traceback (most recent call last): + ... + TypeError: (1, 1, 3) does not represent a valid point + in the ambient space of 2-d cone in 2-d lattice N + + Ensure that transcendental elements can, at the very least, be + represented numerically:: + + sage: from sage.geometry.cone import _ambient_space_point + sage: c = Cone([(1,0), (0,1)]) + sage: _ambient_space_point(c, [1, pi]) + (1.00000000000000, 3.14159265358979) + sage: _ambient_space_point(c, vector(SR,[1, pi])) + (1.00000000000000, 3.14159265358979) + """ + from sage.rings.all import AA, RR + L = body.lattice() - try: # to make a lattice element... - return L(data) - except TypeError: - # Special treatment for toric lattice elements - if is_ToricLattice(parent(data)): - raise TypeError("the point %s and %s have incompatible " - "lattices" % (data, body)) - try: # ... or an exact point... - return L.base_extend(QQ)(data) - except TypeError: - pass - try: # ... or at least a numeric one - return L.base_extend(RR)(data) - except TypeError: - pass + + def try_base_extend(ring): + # Factor out the "try this ring..." code that's repeated four + # times. + try: + return L.base_extend(ring)(data) + except TypeError: + pass + except ValueError as ex: + if str(ex).startswith("Cannot coerce"): + pass + + # Special treatment for toric lattice elements + p = try_base_extend(ZZ) + if p is not None: + return p + if is_ToricLattice(parent(data)): + raise TypeError("the point %s and %s have incompatible " + "lattices" % (data, body)) + + # If we don't have a lattice element, try successively + # less-desirable ambient spaces until (as a last resort) we + # attempt a numerical representation. + for ring in [QQ, AA, RR]: + p = try_base_extend(ring) + if p is not None: + return p + # Raise TypeError with our own message raise TypeError("%s does not represent a valid point in the ambient " "space of %s" % (data, body)) @@ -1546,27 +1584,38 @@ def _contains(self, point, region='whole cone'): This function is called by :meth:`__contains__` and :meth:`contains` to ensure the same call depth for warning messages. + By default, a point on the boundary of the cone is considered + part of the cone. If you want to test whether the + **interior** of the cone contains the point, you need to pass + the optional argument ``'interior'``. If you want to test + whether the **relative interior** of the cone contains the + point, you need to pass the optional argument + ``'relative_interior'``. + + .. WARNING:: + + The boundary of a closed convex cone is determined by a + set of inequalities. If your ``point`` has entries in an + inexact ring, it will sometimes be impossible to say (with + confidence) if that point lies on the boundary of the cone + or slightly inside it. + INPUT: - - ``point`` -- anything. An attempt will be made to convert it into a - single element of the ambient space of ``self``. If it fails, - ``False`` is returned; + - ``point`` -- anything; an attempt will be made to convert it + into an element compatible with the ambient space of ``self``. - - ``region`` -- string. Can be either 'whole cone' (default), - 'interior', or 'relative interior'. By default, a point on - the boundary of the cone is considered part of the cone. If - you want to test whether the **interior** of the cone - contains the point, you need to pass the optional argument - ``'interior'``. If you want to test whether the **relative - interior** of the cone contains the point, you need to pass - the optional argument ``'relative_interior'``. + - ``region`` -- a string (default: 'whole cone'); can be + either 'whole cone', 'interior', or 'relative interior'. OUTPUT: - - ``True`` if ``point`` is contained in the specified ``region`` of - ``self``, ``False`` otherwise. + ``True`` is returned if ``point`` is contained in the + specified ``region`` of ``self``. ``False`` is returned + otherwise, in particular when ``point`` is incompatible with + the ambient space. - Raises a ``ValueError`` if ``region`` is not one of the + A ``ValueError`` is raised if ``region`` is not one of the three allowed values. TESTS:: @@ -1574,6 +1623,28 @@ def _contains(self, point, region='whole cone'): sage: c = Cone([(1,0), (0,1)]) sage: c._contains((1,1)) True + + We can test vectors with irrational components:: + + sage: c = Cone([(1,0), (0,1)]) + sage: c._contains((1,sqrt(2))) + True + sage: c._contains(vector(SR, [1,pi])) + True + + Ensure that complex vectors are not contained in a real cone:: + + sage: c = Cone([(1,0), (0,1)]) + sage: c._contains((1,I)) + False + sage: c._contains(vector(QQbar,[1,I])) + False + + And we refuse to coerce elements of another lattice into ours:: + + sage: c = Cone([(1,0), (0,1)]) + sage: c._contains(c.dual().ray(0)) + False """ try: point = _ambient_space_point(self, point) diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 2484f45ca41..43e9bcadcbf 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -660,7 +660,7 @@ def nmz_ieqs_eqns_QQ(ieqs, eqns): data["number_field"] = number_field_data self._init_from_normaliz_data(data, normaliz_field=normaliz_field, verbose=verbose) - def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, ieqs, eqns=None, verbose=False): + def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, ieqs, eqns=None, verbose=False, homogeneous=False): r""" Construct cone from V-representation data and H-representation data. @@ -685,6 +685,9 @@ def _cone_from_Vrepresentation_and_Hrepresentation(self, vertices, rays, lines, - ``verbose`` -- boolean (default: ``False``); whether to print verbose output for debugging purposes + - ``homogeneous`` -- boolean (default: ``False``); if ``True`` set + up the cone without explicit inhomogenization + EXAMPLES:: sage: P = polytopes.hypercube(4,backend='normaliz') * Polyhedron(rays=[[0,1]]) * Polyhedron(lines=[[1,0]]) # optional - pynormaliz @@ -852,7 +855,8 @@ def rays_subspace_lattice_ieqs_NF(vertices, rays, lines, ieqs): "support_hyperplanes": nmz_ieqs} ambient_dim = len(data["extreme_rays"][0]) - data["dehomogenization"] = [[0]*(ambient_dim-1) + [1]] + if not homogeneous: + data["dehomogenization"] = [[0]*(ambient_dim-1) + [1]] number_field_data = self._number_field_triple(normaliz_field) if number_field_data: @@ -1113,36 +1117,6 @@ def _make_normaliz_cone(data, verbose=False): assert cone, "NmzCone(**{}) did not return a cone".format(data) return cone - @staticmethod - def _cone_generators(pynormaliz_cone): - r""" - Returns the generators of a pynormaliz cone. - - This is particularly useful to get the reordering of the vertices (or - rays) that is internally used by normaliz. - - INPUT: - - - ``pynormaliz_cone`` -- a pynormaliz cone object. - - OUTPUT: - - - a tuple of generators for the cone. - - TESTS:: - - sage: from sage.geometry.polyhedron.backend_normaliz import Polyhedron_normaliz # optional - pynormaliz - sage: data = {'inhom_inequalities': [[-1, 2, 0], [0, 0, 1], [2, -1, 0]]} # optional - pynormaliz - sage: nmz_cone = Polyhedron_normaliz._make_normaliz_cone(data,verbose=False) # optional - pynormaliz - sage: Polyhedron_normaliz._cone_generators(nmz_cone) # py2 # optional - pynormaliz - [[1L, 2L, 0L], [0L, 0L, 1L], [2L, 1L, 0L]] - sage: Polyhedron_normaliz._cone_generators(nmz_cone) # py3 # optional - pynormaliz - [[1, 2, 0], [0, 0, 1], [2, 1, 0]] - """ - PythonModule("PyNormaliz", spkg="pynormaliz").require() - import PyNormaliz - return PyNormaliz.NmzResult(pynormaliz_cone, "Generators") - def _get_nmzcone_data(self): r""" Get the data necessary to reproduce the normaliz cone. @@ -1541,12 +1515,15 @@ def _volume_normaliz(self, measure='euclidean'): def _triangulate_normaliz(self): r""" - Gives a triangulation of the polyhedron using normaliz + Give a triangulation of the polyhedron using normaliz. OUTPUT: - A tuple of pairs ``(simplex,simplex_volume)`` used in the - triangulation. + For compact polyhedra a list of simplices + each represented by indices of their vertices. + + For cones a list of simplicial cones + each represented by indices of their rays. .. NOTE:: @@ -1564,47 +1541,101 @@ def _triangulate_normaliz(self): sage: C2 = Polyhedron(rays=[[1,0,1],[0,0,1],[0,1,1],[1,1,10/9]],backend='normaliz') # optional - pynormaliz sage: C2._triangulate_normaliz() # optional - pynormaliz [(0, 1, 2), (1, 2, 3)] + + Works only for cones and compact polyhedra:: + + sage: P = polytopes.cube(backend='normaliz') # optional - pynormaliz + sage: Q = Polyhedron(rays=[[0,1]], backend='normaliz') # optional - pynormaliz + sage: R = Polyhedron(lines=[[0,1]], backend='normaliz') # optional - pynormaliz + sage: (P*Q)._triangulate_normaliz() # optional - pynormaliz + Traceback (most recent call last): + ... + NotImplementedError: triangulation of non-compact polyhedra that are not cones is not supported + sage: (P*R)._triangulate_normaliz() # optional - pynormaliz + Traceback (most recent call last): + ... + NotImplementedError: triangulation of non-compact not pointed polyhedron is not supported + + TESTS: + + Check that :trac:`30531` is fixed:: + + sage: P = polytopes.cube(backend='normaliz')*AA(2).sqrt() # optional - pynormaliz + sage: P._triangulate_normaliz() # optional - pynormaliz + [(0, 1, 2, 4), + (1, 2, 4, 3), + (1, 3, 4, 5), + (3, 5, 6, 7), + (6, 2, 4, 3), + (6, 3, 4, 5)] + + :: + + sage: C1 = Polyhedron(rays=[[0,0,1],[1,0,AA(2).sqrt()],[0,1,1],[1,1,1]], backend='normaliz') # optional - pynormaliz + sage: C1._triangulate_normaliz() # optional - pynormaliz + [(0, 1, 3), (0, 3, 2)] """ - cone = self._normaliz_cone - assert cone if self.lines(): raise NotImplementedError("triangulation of non-compact not pointed polyhedron is not supported") if len(self.vertices_list()) >= 2 and self.rays_list(): # A mix of polytope and cone raise NotImplementedError("triangulation of non-compact polyhedra that are not cones is not supported") - data = self._get_nmzcone_data() - # Recreates a pointed cone. This is a hack and should be fixed once - # Normaliz accepts compact polyhedron - # For now, we lose the information about the volume? - # if self.is_compact(): - # data['cone'] = data['vertices'] - if not self.is_compact(): - data.pop('vertices', None) - data.pop('inhom_equations', None) - data.pop('inhom_inequalities', None) - cone = self._make_normaliz_cone(data) - - nmz_triangulation = self._nmz_result(cone, "Triangulation") - triang_indices = tuple(vector(ZZ, s[0]) for s in nmz_triangulation) - - # Get the Normaliz ordering of generators if self.is_compact(): - generators = [list(vector(ZZ, g)[:-1]) for g in self._cone_generators(cone)] + cone = self._normaliz_cone else: - generators = [list(vector(ZZ, g)) for g in self._cone_generators(cone)] + # Make a inhomogeneous copy of the cone. + cone = self._cone_from_Vrepresentation_and_Hrepresentation( + self.vertices(), self.rays(), self.lines(), + self.inequalities(), self.equations(), homogeneous=True) - # Get the Sage ordering of generators - if self.is_compact(): - poly_gen = self.vertices_list() - else: - poly_gen = self.rays_list() + # Compute the triangulation. + assert cone + nmz_triangulation = self._nmz_result(cone, "Triangulation") - # When triangulating, Normaliz uses the indexing of 'Generators' and - # not necessarily the indexing of the V-representation. So we apply the - # appropriate relabeling into the V-representation inside sage. - triangulation = [tuple(sorted([poly_gen.index(generators[i]) for i in s])) for s in triang_indices] + # Normaliz does not guarantee that the order of generators is kept during + # computation of the triangulation. + # Those are the generators that the indices of the triangulation correspond to: + nmz_new_generators = self._nmz_result(cone, "Generators") - return triangulation + base_ring = self.base_ring() + v_list = self.vertices_list() + r_list = self.rays_list() + + new_to_old = {} + for i,g in enumerate(nmz_new_generators): + if self.is_compact(): + d = base_ring(g[-1]) + vertex = [base_ring(x)/d for x in g[:-1]] + new_to_old[i] = v_list.index(vertex) + pass + else: + if g[-1] > 0: + new_to_old[i] = None + else: + try: + new_to_old[i] = r_list.index([base_ring(x) for x in g[:-1]]) + except ValueError: + # Rays are only unique up to scaling. + new_ray = vector(base_ring, g[:-1]) + + for j,r in enumerate(self.rays()): + ray = r.vector() + try: + # Check for colinearity. + _ = new_ray/ray + new_to_old[i] = j + break + except (TypeError, ArithmeticError): + pass + else: + raise ValueError("could not match rays after computing triangulation with original rays") + + def new_indices(old_indices): + for i in old_indices: + if new_to_old[i] is not None: + yield new_to_old[i] + + return [tuple(new_indices(x[0])) for x in nmz_triangulation] ######################################################################### diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index aead5396a28..48ae60afeb7 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -2994,7 +2994,7 @@ def slack_matrix(self): Vrep_matrix = matrix(self.base_ring(), self.Vrepresentation()) Hrep_matrix = matrix(self.base_ring(), self.Hrepresentation()) - # Getting homogenous coordinates of the Vrepresentation. + # Getting homogeneous coordinates of the Vrepresentation. hom_helper = matrix(self.base_ring(), [1 if v.is_vertex() else 0 for v in self.Vrepresentation()]) hom_Vrep = hom_helper.stack(Vrep_matrix.transpose()) @@ -5262,32 +5262,32 @@ def linear_transformation(self, linear_transf, new_base_ring=None): new_lines = () if self.is_compact() and self.n_vertices() and self.n_inequalities(): - homogenous_basis = matrix(R, ( [1] + list(v) for v in self.an_affine_basis() )).transpose() + homogeneous_basis = matrix(R, ( [1] + list(v) for v in self.an_affine_basis() )).transpose() # To convert first to a list and then to a matrix seems to be necesarry to obtain a meaningful error, # in case the number of columns doesn't match the dimension. - new_homogenous_basis = matrix(list( [1] + list(linear_transf*vector(R, v)) for v in self.an_affine_basis()) ).transpose() + new_homogeneous_basis = matrix(list( [1] + list(linear_transf*vector(R, v)) for v in self.an_affine_basis()) ).transpose() - if self.dim() + 1 == new_homogenous_basis.rank(): + if self.dim() + 1 == new_homogeneous_basis.rank(): # The transformation is injective on the polytope. is_injective = True - # Let V be the homogenous vertex matrix (each vertex a column) + # Let V be the homogeneous vertex matrix (each vertex a column) # and M the linear transformation. - # Then M*V is the new homogenous vertex matrix. + # Then M*V is the new homogeneous vertex matrix. # Let H be the inequalities matrix (each inequality a row). # If we find N such that N*M*V = V than the new inequalities are # given by H*N. # Note that such N must exist, as our map is injective on the polytope. - # It is uniquely defined by considering a basis of the homogenous vertices. - N = new_homogenous_basis.solve_left(homogenous_basis) + # It is uniquely defined by considering a basis of the homogeneous vertices. + N = new_homogeneous_basis.solve_left(homogeneous_basis) new_inequalities = ( h for h in matrix(R, self.inequalities())*N ) - # The equations are the left kernel matrix of the homogenous vertices + # The equations are the left kernel matrix of the homogeneous vertices # or equivalently a basis thereof. - new_equations = (new_homogenous_basis.transpose()).right_kernel_matrix() + new_equations = (new_homogeneous_basis.transpose()).right_kernel_matrix() else: new_vertices = [[] for v in self.vertex_generator() ] diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx index f950a67b2e6..3dc05a4cd4e 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx @@ -74,6 +74,7 @@ from libc.string cimport memset from .list_of_faces cimport ListOfFaces from sage.misc.superseded import deprecated_function_alias from sage.matrix.matrix_integer_dense cimport Matrix_integer_dense +from sage.ext.memory_allocator cimport MemoryAllocator cdef extern from "Python.h": int unlikely(int) nogil # Defined by Cython @@ -86,6 +87,24 @@ cdef inline uint64_t vertex_to_bit_dictionary(size_t i): """ return (1) << (64 - i - 1) +def _Vrep_list_to_bit_rep_wrapper(tup, size_t face_length=-1): + r""" + A function to allow doctesting of :func:`Vrep_list_to_bit_rep`. + + TESTS:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _Vrep_list_to_bit_rep_wrapper + sage: _Vrep_list_to_bit_rep_wrapper((60, 63)) + (9,) + """ + if face_length == -1: + face_length = max(tup)//64 + 1 + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint64_t *output = mem.allocarray(face_length, 8) + Vrep_list_to_bit_rep(tup, output, face_length) + return tuple(output[i] for i in range(face_length)) + cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, size_t face_length) except -1: r""" @@ -107,41 +126,21 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, EXAMPLES:: - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport Vrep_list_to_bit_rep - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from sage.rings.integer cimport smallInteger - ....: - ....: def Vrep_list_to_bit_rep_wrapper(tup): - ....: cdef size_t face_length = max(tup)//64 + 1 - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef uint64_t *output = mem.allocarray(face_length, 8) - ....: Vrep_list_to_bit_rep(tup, output, face_length) - ....: return tuple(smallInteger(output[i]) for i in range(face_length)) - ....: - ....: def Vrep_list_to_bit_rep_wrong_size(tup): - ....: cdef size_t face_length = 1 - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef uint64_t *output = mem.allocarray(face_length, 8) - ....: Vrep_list_to_bit_rep(tup, output, face_length) - ....: return tuple(smallInteger(output[i]) for i in range(face_length)) - ....: ''') # long time - - sage: Vrep_list_to_bit_rep_wrapper((62, 63)) # long time + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _Vrep_list_to_bit_rep_wrapper + sage: _Vrep_list_to_bit_rep_wrapper((62, 63)) (3,) - sage: Vrep_list_to_bit_rep_wrapper((61, 63, 125)) # long time + sage: _Vrep_list_to_bit_rep_wrapper((61, 63, 125)) (5, 4) - sage: Vrep_list_to_bit_rep_wrong_size((62, 70)) # long time + sage: _Vrep_list_to_bit_rep_wrapper((62, 70), face_length=1) Traceback (most recent call last): ... IndexError: output too small to represent 70 - sage: Vrep_list_to_bit_rep_wrapper((-1, 12)) # long time + sage: _Vrep_list_to_bit_rep_wrapper((-1, 12)) Traceback (most recent call last): ... OverflowError: can...t convert negative value to size_t - sage: Vrep_list_to_bit_rep_wrapper((0, 0)) # long time + sage: _Vrep_list_to_bit_rep_wrapper((0, 0)) Traceback (most recent call last): ... ValueError: entries of ``tup`` are not distinct @@ -160,6 +159,25 @@ cdef int Vrep_list_to_bit_rep(tuple Vrep_list, uint64_t *output, raise IndexError("output too small to represent %s"%entry) output[position] += vertex_to_bit_dictionary(value) +def _incidences_to_bit_rep_wrapper(tup, size_t face_length=-1): + r""" + A function to allow doctesting of :func:`incidences_to_bit_rep`. + + TESTS:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _incidences_to_bit_rep_wrapper + sage: _incidences_to_bit_rep_wrapper((0,) * 60 + (1,0,0,1)) + (9,) + """ + if face_length == -1: + face_length = (len(tup)-1)//64 + 1 + cdef MemoryAllocator mem = MemoryAllocator() + cdef uint64_t *output = \ + mem.allocarray(face_length, 8) + incidences_to_bit_rep(tup, output, face_length) + return tuple(output[i] for i in range(face_length)) + cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, size_t face_length) except -1: @@ -182,38 +200,16 @@ cdef int incidences_to_bit_rep(tuple incidences, uint64_t *output, EXAMPLES:: - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport incidences_to_bit_rep - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from sage.rings.integer cimport smallInteger - ....: - ....: def incidences_to_bit_reps_wrapper(tup): - ....: cdef size_t face_length = (len(tup)-1)//64 + 1 - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef uint64_t *output = \ - ....: mem.allocarray(face_length, 8) - ....: incidences_to_bit_rep(tup, output, face_length) - ....: return tuple(smallInteger(output[i]) for i in range(face_length)) - ....: - ....: def incidences_to_bit_reps_wrong_size(tup): - ....: cdef size_t face_length = 1 - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef uint64_t *output = \ - ....: mem.allocarray(face_length, 8) - ....: incidences_to_bit_rep(tup, output, face_length) - ....: return tuple(smallInteger(output[i]) for i in range(face_length)) - ....: ''') # long time - - sage: incidences_to_bit_reps_wrapper((0,) * 62 + (1,1)) # long time + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _incidences_to_bit_rep_wrapper + sage: _incidences_to_bit_rep_wrapper((0,) * 62 + (1,1)) (3,) - sage: incidences_to_bit_reps_wrapper((0,) * 61 + (1,0,1) + # long time + sage: _incidences_to_bit_rep_wrapper((0,) * 61 + (1,0,1) + ....: (0,) * 61 + (1,)) (5, 4) - sage: incidences_to_bit_reps_wrapper((1,) * 64) # long time - (-1,) - sage: incidences_to_bit_reps_wrong_size((1,) * 70) # long time + sage: _incidences_to_bit_rep_wrapper((1,) * 64) == (2**64-1,) + True + sage: _incidences_to_bit_rep_wrapper((1,) * 70, face_length=1) Traceback (most recent call last): ... IndexError: output too small to represent all incidences @@ -250,28 +246,9 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): EXAMPLES:: - sage: cython(''' - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list - ....: from sage.rings.integer cimport smallInteger - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from libc.stdint cimport uint64_t - ....: - ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef size_t *output - ....: output = mem.allocarray(faces.n_atoms, - ....: sizeof(size_t)) - ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_rep_to_Vrep_list( - ....: data, output, faces.face_length) - ....: return tuple(smallInteger(output[i]) for i in range(length)) - ....: ''') # long time - sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_rep_of_facets + ....: import incidence_matrix_to_bit_rep_of_facets, \ + ....: _bit_rep_to_Vrep_list_wrapper sage: P = polytopes.permutahedron(4) sage: inc = P.incidence_matrix() sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) @@ -280,8 +257,8 @@ def incidence_matrix_to_bit_rep_of_facets(Matrix_integer_dense matrix): 14 sage: facets.n_atoms 24 - sage: for i in range(facets.n_faces): # long time - ....: print(bit_rep_to_Vrep_list_wrapper(facets, i)) + sage: for i in range(facets.n_faces): + ....: print(_bit_rep_to_Vrep_list_wrapper(facets, i)) (18, 19, 20, 21, 22, 23) (3, 5, 8, 10, 12, 17) (2, 7, 11, 13, 20, 21) @@ -329,7 +306,7 @@ def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): r""" Initialize Vrepresentatives in Bit-representation as :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces`. - Each Vrepresenative is represented as the facets it is contained in. + Each Vrepresentative is represented as the facets it is contained in. Those are the facets of the polar polyhedron, if it exists. INPUT: @@ -345,28 +322,9 @@ def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): EXAMPLES:: - sage: cython(''' - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list - ....: from sage.rings.integer cimport smallInteger - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from libc.stdint cimport uint64_t - ....: - ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef size_t *output - ....: output = mem.allocarray(faces.n_atoms, - ....: sizeof(size_t)) - ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_rep_to_Vrep_list( - ....: data, output, faces.face_length) - ....: return tuple(smallInteger(output[i]) for i in range(length)) - ....: ''') - sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import incidence_matrix_to_bit_rep_of_Vrep + ....: import incidence_matrix_to_bit_rep_of_Vrep, \ + ....: _bit_rep_to_Vrep_list_wrapper sage: P = polytopes.permutahedron(4) sage: inc = P.incidence_matrix() sage: mod_inc = inc.delete_columns([i for i,V in enumerate(P.Hrepresentation()) if V.is_equation()]) @@ -376,7 +334,7 @@ def incidence_matrix_to_bit_rep_of_Vrep(Matrix_integer_dense matrix): sage: vertices.n_atoms 14 sage: for i in range(vertices.n_faces): - ....: print(bit_rep_to_Vrep_list_wrapper(vertices, i)) + ....: print(_bit_rep_to_Vrep_list_wrapper(vertices, i)) (8, 9, 11) (8, 10, 11) (2, 3, 7) @@ -421,33 +379,14 @@ def facets_tuple_to_bit_rep_of_facets(tuple facets_input, size_t n_Vrep): EXAMPLES:: - sage: cython(''' - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list - ....: from sage.rings.integer cimport smallInteger - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from libc.stdint cimport uint64_t - ....: - ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef size_t *output - ....: output = mem.allocarray(faces.n_atoms, - ....: sizeof(size_t)) - ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_rep_to_Vrep_list( - ....: data, output, faces.face_length) - ....: return tuple(smallInteger(output[i]) for i in range(length)) - ....: ''') # long time - sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_rep_of_facets + ....: import facets_tuple_to_bit_rep_of_facets, \ + ....: _bit_rep_to_Vrep_list_wrapper sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) sage: facets = facets_tuple_to_bit_rep_of_facets(bi_pyr, 6) - sage: for i in range(8): # long time - ....: print(bit_rep_to_Vrep_list_wrapper(facets, i)) + sage: for i in range(8): + ....: print(_bit_rep_to_Vrep_list_wrapper(facets, i)) (0, 1, 4) (1, 2, 4) (2, 3, 4) @@ -487,33 +426,14 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): EXAMPLES:: - sage: cython(''' - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list - ....: from sage.rings.integer cimport smallInteger - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from libc.stdint cimport uint64_t - ....: - ....: def bit_rep_to_Vrep_list_wrapper(ListOfFaces faces, index): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef size_t *output - ....: output = mem.allocarray(faces.n_atoms, - ....: sizeof(size_t)) - ....: cdef uint64_t * data = faces.data[index] - ....: length = bit_rep_to_Vrep_list( - ....: data, output, faces.face_length) - ....: return tuple(smallInteger(output[i]) for i in range(length)) - ....: ''') - sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: import facets_tuple_to_bit_rep_of_Vrep + ....: import facets_tuple_to_bit_rep_of_Vrep, \ + ....: _bit_rep_to_Vrep_list_wrapper sage: bi_pyr = ((0,1,4), (1,2,4), (2,3,4), (3,0,4), ....: (0,1,5), (1,2,5), (2,3,5), (3,0,5)) sage: vertices = facets_tuple_to_bit_rep_of_Vrep(bi_pyr, 6) sage: for i in range(6): - ....: print(bit_rep_to_Vrep_list_wrapper(vertices, i)) + ....: print(_bit_rep_to_Vrep_list_wrapper(vertices, i)) (0, 3, 4, 7) (0, 1, 4, 5) (1, 2, 5, 6) @@ -551,6 +471,52 @@ def facets_tuple_to_bit_rep_of_Vrep(tuple facets_input, size_t n_Vrep): return Vrep facets_tuple_to_bit_repr_of_Vrepr = deprecated_function_alias(28608, facets_tuple_to_bit_rep_of_Vrep) +def _bit_rep_to_Vrep_list_wrapper(data, index=0): + r""" + A function to test :func:`bit_rep_to_Vrep_list`. + + INPUT: + + - ``data`` -- either a :class:`~sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces` + or a tuple of integers in ``range(0,2**64)`` + - ``index`` -- ``0`` if ``data`` is a tuple, otherwise the index of the ``face`` + to convert + + OUTPUT: A tuple of integers. + + If the input is a tuple, it will be interpreted as a list of faces over with `64` atoms per element in the + tuple. Each number in the tuple corresponds to an ``uint64_t``. + + The list of faces is then translated into a tuple of the integers with set bits. + + EXAMPLES:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import facets_tuple_to_bit_rep_of_facets, \ + ....: _bit_rep_to_Vrep_list_wrapper + sage: _bit_rep_to_Vrep_list_wrapper((1, 1)) + (63, 127) + sage: faces = facets_tuple_to_bit_rep_of_facets(((1,5,123,1054),), 1055) + sage: _bit_rep_to_Vrep_list_wrapper(faces, 0) + (1, 5, 123, 1054) + """ + cdef ListOfFaces faces + if isinstance(data, ListOfFaces): + faces = data + else: + assert isinstance(data, tuple) + faces = ListOfFaces(1, 64*len(data)) + for i in range(len(data)): + faces.data[0][i] = data[i] + + cdef MemoryAllocator mem = MemoryAllocator() + cdef size_t *output + output = mem.allocarray(faces.n_atoms, + sizeof(size_t)) + length = bit_rep_to_Vrep_list( + faces.data[index], output, faces.face_length) + return tuple(output[i] for i in range(length)) + cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, size_t face_length) except -1: r""" @@ -573,35 +539,13 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, EXAMPLES:: - sage: cython(''' - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep - ....: from sage.rings.integer cimport smallInteger - ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: from libc.stdint cimport uint64_t - ....: - ....: def bit_rep_to_Vrep_list_wrapper(tup): - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef size_t *output - ....: cdef length = len(tup) - ....: output = mem.allocarray(length*64, - ....: sizeof(size_t)) - ....: cdef uint64_t * data - ....: data = mem.allocarray(length, 8) - ....: for i in range(len(tup)): - ....: data[i] = tup[i] - ....: outputlength = bit_rep_to_Vrep_list( - ....: data, output, length) - ....: return tuple(smallInteger(output[i]) for i in range(outputlength)) - ....: ''') # long time - - sage: bit_rep_to_Vrep_list_wrapper((17, 31)) # long time + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _bit_rep_to_Vrep_list_wrapper + sage: _bit_rep_to_Vrep_list_wrapper((17, 31)) (59, 63, 123, 124, 125, 126, 127) - sage: bit_rep_to_Vrep_list_wrapper((13,)) # long time + sage: _bit_rep_to_Vrep_list_wrapper((13,)) (60, 61, 63) - sage: bit_rep_to_Vrep_list_wrapper((0, 61)) # long time + sage: _bit_rep_to_Vrep_list_wrapper((0, 61)) (122, 123, 124, 125, 127) TESTS: @@ -609,26 +553,17 @@ cdef inline size_t bit_rep_to_Vrep_list(uint64_t *face, size_t *output, Testing that :meth`bit_rep_to_Vrep_list` is the inverse to :meth:`Vrep_list_to_bit_rep`:: - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ - ....: cimport bit_rep_to_Vrep_list, Vrep_list_to_bit_rep - ....: from sage.misc.prandom import randint - ....: - ....: cdef uint64_t[2] face - ....: cdef size_t length - ....: cdef size_t[128] output - ....: - ....: for _ in range(10): + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ + ....: import _bit_rep_to_Vrep_list_wrapper, \ + ....: _Vrep_list_to_bit_rep_wrapper + sage: for _ in range(10): ....: st = set(randint(0,127) for i in range(40)) ....: tup = tuple(sorted(tuple(st))) - ....: Vrep_list_to_bit_rep(tup, face, 2) - ....: length = bit_rep_to_Vrep_list(face, output, 2) - ....: tup2 = tuple(output[i] for i in range(length)) - ....: if not tup == tup2: + ....: faces = _Vrep_list_to_bit_rep_wrapper(tup) + ....: output = _bit_rep_to_Vrep_list_wrapper(faces, 0) + ....: if not tup == output: ....: print('``bit_rep_to_Vrep_list`` does not behave', ....: 'as the inverse of ``Vrep_list_to_bit_rep``') - ....: ''') # long time """ cdef size_t i, j cdef size_t output_length = 0 diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx index 0fb3ac9925f..e3f4f28a6f8 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx @@ -55,7 +55,7 @@ Obtain the facets of a polyhedron as :class:`ListOfFaces` from a facet list:: sage: facets = ((0,1,2), (0,1,3), (0,2,3), (1,2,3)) sage: face_list = facets_tuple_to_bit_rep_of_facets(facets, 4) -Likewise for the Vrepresenatives as facet-incidences:: +Likewise for the Vrepresentatives as facet-incidences:: sage: from sage.geometry.polyhedron.combinatorial_polyhedron.conversions \ ....: import facets_tuple_to_bit_rep_of_Vrep @@ -186,26 +186,7 @@ cdef class ListOfFaces: See :class:`ListOfFaces`. - TESTS: - - Checking for correct alignment of the data:: - - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ - ....: cimport ListOfFaces - ....: - ....: cdef ListOfFaces facets - ....: cdef size_t address - ....: cdef size_t required_alignment - ....: - ....: facets = ListOfFaces(10, 13) - ....: required_alignment = facets.face_length*8 - ....: for i in range(10): - ....: address = facets.data[i] - ....: if not address == address & ~(required_alignment - 1): - ....: print('Alignment not correct') - ....: ''') + TESTS:: sage: TestSuite(sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces.ListOfFaces).run() """ @@ -231,6 +212,26 @@ cdef class ListOfFaces: self.data[i] = \ self._mem.aligned_malloc(chunksize//8, self.face_length*8) + def _test_alignment(self): + r""" + Check the correct alignment. + + TESTS:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.list_of_faces \ + ....: import ListOfFaces + sage: facets = ListOfFaces(10, 13) + sage: facets._test_alignment() + """ + cdef size_t address + cdef size_t required_alignment + cdef size_t i + + required_alignment = chunksize/8 + for i in range(self.n_faces): + address = self.data[i] + assert address == address & ~(required_alignment - 1) + def __copy__(self): r""" Return a copy of self. diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd index 9f90d0cac33..250fa8328ad 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd @@ -51,7 +51,7 @@ cdef class PolyhedronFaceLattice: cdef inline bint is_smaller(self, uint64_t *one, uint64_t *two) cdef inline int is_equal(self, int dimension, size_t index, uint64_t *face) except -1 - cdef CombinatorialFace get_face(self, int dimension, size_t index) + cpdef CombinatorialFace get_face(self, int dimension, size_t index) cdef size_t set_coatom_rep(self, int dimension, size_t index) except -1 cdef size_t set_atom_rep(self, int dimension, size_t index) except -1 cdef void incidence_init(self, int dimension_one, int dimension_two) diff --git a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx index dd4b8335c80..506a21ef045 100644 --- a/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +++ b/src/sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx @@ -337,6 +337,30 @@ cdef class PolyhedronFaceLattice: j += 1 counter += 1 + def _find_face_from_combinatorial_face(self, CombinatorialFace face): + r""" + A method to test :meth:`find_face`. + + ``f`` must be a face in dual mode if and only if ``self`` is in dual mode. + + TESTS:: + + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice \ + ....: import PolyhedronFaceLattice + sage: P = polytopes.hypercube(4) + sage: C = CombinatorialPolyhedron(P) + sage: F = PolyhedronFaceLattice(C) + sage: it = C.face_iter() + sage: face = next(it) + sage: F._find_face_from_combinatorial_face(face) + Traceback (most recent call last): + ... + ValueError: cannot find a facet, as those are not sorted + """ + if not (self.dual == face._dual): + raise ValueError("iterator and allfaces not in same mode") + return self.find_face(face.dimension(), face.face) + cdef inline size_t find_face(self, int dimension, uint64_t *face) except -1: r""" Return the index of ``face``, if it is of dimension ``dimension``. @@ -348,28 +372,13 @@ cdef class PolyhedronFaceLattice: EXAMPLES:: - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.base \ - ....: cimport CombinatorialPolyhedron, FaceIterator, PolyhedronFaceLattice - ....: - ....: def find_face_from_iterator(FaceIterator it, CombinatorialPolyhedron C): - ....: C._record_all_faces() - ....: cdef PolyhedronFaceLattice all_faces = C._all_faces - ....: if not (all_faces.dual == it.dual): - ....: raise ValueError("iterator and allfaces not in same mode") - ....: return all_faces.find_face(it.structure.current_dimension, it.structure.face) - ....: ''') + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice \ + ....: import PolyhedronFaceLattice sage: P = polytopes.permutahedron(4) sage: C = CombinatorialPolyhedron(P) - sage: it = C.face_iter() - sage: face = next(it) - sage: find_face_from_iterator(it, C) - Traceback (most recent call last): - ... - ValueError: cannot find a facet, as those are not sorted + sage: F = PolyhedronFaceLattice(C) sage: it = C.face_iter(dimension=1) - sage: S = set(find_face_from_iterator(it, C) for _ in it) + sage: S = set(F._find_face_from_combinatorial_face(f) for f in it) sage: S == set(range(36)) True """ @@ -419,7 +428,7 @@ cdef class PolyhedronFaceLattice: cdef size_t i return (0 == memcmp(face, face2, self.face_length*8)) - cdef CombinatorialFace get_face(self, int dimension, size_t index): + cpdef CombinatorialFace get_face(self, int dimension, size_t index): r""" Return the face of dimension ``dimension`` and index ``index``. @@ -432,42 +441,36 @@ cdef class PolyhedronFaceLattice: EXAMPLES:: - sage: cython(''' - ....: from libc.stdint cimport uint64_t - ....: from sage.geometry.polyhedron.combinatorial_polyhedron.base \ - ....: cimport CombinatorialPolyhedron, FaceIterator, PolyhedronFaceLattice - ....: - ....: def face_via_all_faces_from_iterator(FaceIterator it, CombinatorialPolyhedron C): - ....: cdef int dimension = it.structure.current_dimension - ....: C._record_all_faces() - ....: cdef PolyhedronFaceLattice all_faces = C._all_faces - ....: if not (all_faces.dual == it.dual): - ....: raise ValueError("iterator and allfaces not in same mode") - ....: index = all_faces.find_face(dimension, it.structure.face) - ....: return all_faces.get_face(dimension, index) - ....: ''') + sage: from sage.geometry.polyhedron.combinatorial_polyhedron.polyhedron_face_lattice \ + ....: import PolyhedronFaceLattice sage: P = polytopes.permutahedron(4) sage: C = CombinatorialPolyhedron(P) + sage: F = PolyhedronFaceLattice(C) sage: it = C.face_iter(dimension=1) sage: face = next(it) - sage: face_via_all_faces_from_iterator(it, C).ambient_Vrepresentation() + sage: index = F._find_face_from_combinatorial_face(face) + sage: F.get_face(face.dimension(), index).ambient_Vrepresentation() (A vertex at (2, 1, 4, 3), A vertex at (1, 2, 4, 3)) sage: face.ambient_Vrepresentation() (A vertex at (2, 1, 4, 3), A vertex at (1, 2, 4, 3)) - sage: all(face_via_all_faces_from_iterator(it, C).ambient_Vrepresentation() == + sage: all(F.get_face(face.dimension(), + ....: F._find_face_from_combinatorial_face(face)).ambient_Vrepresentation() == ....: face.ambient_Vrepresentation() for face in it) True sage: P = polytopes.twenty_four_cell() sage: C = CombinatorialPolyhedron(P) + sage: F = PolyhedronFaceLattice(C) sage: it = C.face_iter() sage: face = next(it) sage: while (face.dimension() == 3): face = next(it) - sage: face_via_all_faces_from_iterator(it, C).ambient_Vrepresentation() + sage: index = F._find_face_from_combinatorial_face(face) + sage: F.get_face(face.dimension(), index).ambient_Vrepresentation() (A vertex at (-1/2, 1/2, -1/2, -1/2), A vertex at (-1/2, 1/2, 1/2, -1/2), A vertex at (0, 0, 0, -1)) - sage: all(face_via_all_faces_from_iterator(it, C).ambient_V_indices() == + sage: all(F.get_face(face.dimension(), + ....: F._find_face_from_combinatorial_face(face)).ambient_V_indices() == ....: face.ambient_V_indices() for face in it) True """ diff --git a/src/sage/geometry/toric_lattice.py b/src/sage/geometry/toric_lattice.py index 6b0b340c46f..5b85da8d545 100644 --- a/src/sage/geometry/toric_lattice.py +++ b/src/sage/geometry/toric_lattice.py @@ -636,7 +636,7 @@ def intersection(self, other): return I def quotient(self, sub, check=True, - positive_point=None, positive_dual_point=None): + positive_point=None, positive_dual_point=None, **kwds): """ Return the quotient of ``self`` by the given sublattice ``sub``. @@ -664,6 +664,9 @@ def quotient(self, sub, check=True, ``sub``, then the notion of positivity will depend on the choice of lift! + Further named arguments are passed to the constructor of a toric lattice + quotient. + EXAMPLES:: sage: N = ToricLattice(3) @@ -730,7 +733,7 @@ def quotient(self, sub, check=True, """ return ToricLattice_quotient(self, sub, check, - positive_point, positive_dual_point) + positive_point, positive_dual_point, **kwds) def saturation(self): r""" @@ -1387,6 +1390,9 @@ class ToricLattice_quotient(FGP_Module_class): ``positive_dual_point`` is not zero on the sublattice ``sub``, then the notion of positivity will depend on the choice of lift! + Further given named arguments are passed to the constructor of an FGP + module. + OUTPUT: - quotient of ``V`` by ``W``. @@ -1429,7 +1435,7 @@ class ToricLattice_quotient(FGP_Module_class): True """ - def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=None): + def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=None, **kwds): r""" The constructor @@ -1461,7 +1467,7 @@ def __init__(self, V, W, check=True, positive_point=None, positive_dual_point=No W = V.submodule(W) except (TypeError, ArithmeticError): raise ArithmeticError("W must be a sublattice of V") - super(ToricLattice_quotient, self).__init__(V, W, check) + super(ToricLattice_quotient, self).__init__(V, W, check, **kwds) if (positive_point, positive_dual_point) == (None, None): self._flip_sign_of_generator = False return diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index 36964722536..8a86d969f4c 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -63,8 +63,7 @@ Functions from libc.stdint cimport uint32_t from cysignals.signals cimport sig_on, sig_off -include "sage/data_structures/bitset.pxi" - +from sage.data_structures.bitset_base cimport * from sage.graphs.base.static_sparse_graph cimport short_digraph, init_short_digraph, free_short_digraph from sage.ext.memory_allocator cimport MemoryAllocator diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index 2a4a6b58851..bb9a97a3be1 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -43,8 +43,7 @@ method :meth:`realloc `. # https://www.gnu.org/licenses/ # **************************************************************************** -include "sage/data_structures/bitset.pxi" - +from sage.data_structures.bitset_base cimport * from sage.rings.integer cimport Integer from sage.arith.long cimport pyobject_to_long from libcpp.queue cimport priority_queue @@ -1530,7 +1529,7 @@ cdef class CGraphBackend(GenericGraphBackend): name = 0 while name in self.vertex_ints or ( name not in self.vertex_labels and - bitset_in(self.cg().active_vertices, name)): + bitset_in(self.cg().active_vertices, name)): name += 1 retval = name @@ -3308,6 +3307,8 @@ cdef class CGraphBackend(GenericGraphBackend): bitset_init(activated, self.cg().active_vertices.size) bitset_set_first_n(activated, self.cg().active_vertices.size) + cdef mp_bitcnt_t uu, u, v + # Vertices whose neighbors have already been added to the stack cdef bitset_t tried bitset_init(tried, self.cg().active_vertices.size) diff --git a/src/sage/graphs/base/dense_graph.pyx b/src/sage/graphs/base/dense_graph.pyx index 585d78ceca5..b14c3c78b98 100644 --- a/src/sage/graphs/base/dense_graph.pyx +++ b/src/sage/graphs/base/dense_graph.pyx @@ -117,7 +117,7 @@ vertices. For more details about this, refer to the documentation for # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * from libc.string cimport memcpy diff --git a/src/sage/graphs/base/sparse_graph.pyx b/src/sage/graphs/base/sparse_graph.pyx index 501d0e3f56a..3a19b434c20 100644 --- a/src/sage/graphs/base/sparse_graph.pyx +++ b/src/sage/graphs/base/sparse_graph.pyx @@ -193,7 +193,8 @@ for both of these uses. from libc.string cimport memset from cysignals.memory cimport check_malloc, check_allocarray, sig_free -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * +from sage.data_structures.bitset cimport * cdef enum: BT_REORDERING_CONSTANT = 145533211 diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 3f91dc21b8d..9b8478cb9e9 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -46,7 +46,7 @@ from sage.graphs.base.static_sparse_graph cimport (init_short_digraph, from .c_graph cimport CGraphBackend from sage.data_structures.bitset cimport FrozenBitset from libc.stdint cimport uint32_t -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * cdef class StaticSparseCGraph(CGraph): """ diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 0cb3de02cbb..9d6519b6a36 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -182,7 +182,6 @@ with C arguments). # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/data_structures/bitset.pxi" cimport cpython from libc.string cimport memset from libc.limits cimport INT_MAX @@ -191,6 +190,7 @@ from libcpp.vector cimport vector from cysignals.memory cimport check_allocarray, check_calloc, sig_free from cysignals.signals cimport sig_on, sig_off +from sage.data_structures.bitset_base cimport * from sage.graphs.base.c_graph cimport CGraph from .static_sparse_backend cimport StaticSparseCGraph from .static_sparse_backend cimport StaticSparseBackend @@ -1094,7 +1094,7 @@ def spectral_radius(G, prec=1e-10): sage: G.spectral_radius() Traceback (most recent call last): ... - ValueError: the graph must be aperiodic + ValueError: the graph must be aperiodic """ if not G: raise ValueError("empty graph") @@ -1103,7 +1103,7 @@ def spectral_radius(G, prec=1e-10): raise ValueError("G must be strongly connected") elif not G.is_connected(): raise ValueError("G must be connected") - + cdef double e_min, e_max if G.num_verts() == 1: diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index cd69e1628aa..420f264e209 100755 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -21,7 +21,7 @@ from libc.stdint cimport uint32_t from cysignals.memory cimport check_allocarray, sig_free from cysignals.signals cimport sig_check -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from sage.graphs.base.static_sparse_graph cimport * from sage.libs.gmp.mpq cimport * from sage.rings.rational cimport Rational @@ -916,7 +916,7 @@ def centrality_closeness_random_k(G, int k=1): # Shuffle the vertices cdef list l = list(range(n)) random.shuffle(l) - + if G.weighted(): # For all random nodes take as a source then run Dijstra and # calculate closeness centrality for k random vertices from l. diff --git a/src/sage/graphs/convexity_properties.pyx b/src/sage/graphs/convexity_properties.pyx index 8e49d228ed2..cd618b40d1c 100644 --- a/src/sage/graphs/convexity_properties.pyx +++ b/src/sage/graphs/convexity_properties.pyx @@ -302,7 +302,7 @@ cdef class ConvexityProperties: bitset_set_first_n(bs, 0) for v in vertices: - bitset_add(bs, self._dict_vertices_to_integers[v]) + bitset_add(bs, self._dict_vertices_to_integers[v]) self._bitset_convex_hull(bs) diff --git a/src/sage/graphs/generators/distance_regular.pyx b/src/sage/graphs/generators/distance_regular.pyx index 83be8b2891a..be743cf28e8 100644 --- a/src/sage/graphs/generators/distance_regular.pyx +++ b/src/sage/graphs/generators/distance_regular.pyx @@ -421,7 +421,7 @@ def shortened_00_11_binary_Golay_code_graph(): EXAMPLES:: - sage: G = graphs.shortened_00_11_binary_Golay_code_graph() # long time (25 s) + sage: G = graphs.shortened_00_11_binary_Golay_code_graph() # long time (9 s) sage: G.is_distance_regular(True) # long time ([21, 20, 16, 6, 2, 1, None], [None, 1, 2, 6, 16, 20, 21]) @@ -460,7 +460,7 @@ def shortened_000_111_extended_binary_Golay_code_graph(): EXAMPLES:: - sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # long time (2 min) + sage: G = graphs.shortened_000_111_extended_binary_Golay_code_graph() # long time (25 s) sage: G.is_distance_regular(True) # long time ([21, 20, 16, 9, 2, 1, None], [None, 1, 2, 3, 16, 20, 21]) @@ -544,7 +544,8 @@ def LeonardGraph(): M = hadamard_matrix(12) edges = [] for i, j, k, l in itertools.product(range(12), repeat=4): - if i == k or j == l: continue + if i == k or j == l: + continue if M[i, j] * M[i, l] * M[k, j] * M[k, l] == -1: edges.append(((i, j), (k, l))) @@ -651,11 +652,14 @@ def BilinearFormsGraph(const int d, const int e, const int q): sage: K.is_isomorphic(G) True """ - from sage.combinat.integer_vector import IntegerVectors + from itertools import product as cartprod Fq = GF(q) Fqelems = list(Fq) - matricesOverq = IntegerVectors(k=d*e, max_part=q-1) + FqToInt = {x: n for n, x in enumerate(Fqelems)} + dim = d * e + matricesOverq = cartprod(range(q), repeat=dim) + qto = [int(q**jj) for jj in range(dim)] rank1Matrices = [] for u in VectorSpace(Fq, d): @@ -667,7 +671,7 @@ def BilinearFormsGraph(const int d, const int e, const int q): continue sig_check() - M = [0] * (d*e) + M = [0] * dim for row in range(d): for col in range(e): M[e*row + col] = u[row] * v[col] @@ -676,11 +680,17 @@ def BilinearFormsGraph(const int d, const int e, const int q): edges = [] for m1 in matricesOverq: - m1 = tuple(map(lambda x: Fqelems[x], m1)) + intM1 = 0 # represents vector m1 as integer base q + for jj in range(dim): + intM1 += m1[jj] * qto[jj] + for m2 in rank1Matrices: sig_check() - m3 = tuple([m1[i] + m2[i] for i in range(d*e)]) - edges.append((m1, m3)) + intM3 = 0 + for jj in range(dim): + intM3 += FqToInt[Fqelems[m1[jj]] + Fqelems[m2[jj]]] * qto[jj] + + edges.append((intM1, intM3)) G = Graph(edges, format='list_of_edges') G.name("Bilinear forms graphs over F_%d with parameters (%d, %d)"%(q, d, e)) @@ -986,35 +996,37 @@ def HalfCube(const int n): sage: G1.is_isomorphic(G2) True """ - from sage.graphs.graph_generators import graphs - - def hamming_distance(str v, str w): - cdef int i, counter + from sage.functions.trig import cos, sin - counter = 0 - for i in range(len(v)): - if (v[i] != w[i]): - counter = counter + 1 + if n < 2: + raise ValueError("the dimension must be n > 1") - return counter + cdef int u, uu, v, i, j + cdef list E = [] + cdef dict pos = {} # dictionary of positions + cdef float theta = 3.14159265 / (n - 1) + cdef list cosi = [cos(i*theta) for i in range(n - 1)] + cdef list sini = [sin(i*theta) for i in range(n - 1)] - if n <= 2: - raise ValueError("we need n > 2") - - G = graphs.CubeGraph(n-1) - # we use the fact that the vertices are strings - # and their distance is their hamming_distance - for v, w in itertools.combinations(G, 2): + for u in range(2**(n - 1)): sig_check() - if hamming_distance(v, w) == 2: - G.add_edge(v, w) - - G.relabel() # relabel vertices to 0,1,2,... - + pos[u] = (sum(((u >> (n-2-i)) & 1) * cosi[i] for i in range(n - 1)), + sum(((u >> (n-2-i)) & 1) * sini[i] for i in range(n - 1))) + + for i in range(n - 1): + uu = u ^ (1 << i) + if u < uu: + E.append((u, uu)) + for j in range(i + 1, n - 1): + v = uu ^ (1 << j) + if u < v: + E.append((u, v)) + + G = Graph([range(2**(n - 1)), E], format='vertices_and_edges') + G.set_pos(pos) G.name("Half %d Cube"%n) return G - def GrassmannGraph(const int q, const int n, const int input_e): r""" Return the Grassmann graph with parameters `(q, n, e)`. @@ -1124,3 +1136,124 @@ def DoubleGrassmannGraph(const int q, const int e): G = Graph(edges, format='list_of_edges') G.name("Double Grassmann graph (%d, %d, %d)"%(n, e, q)) return G + + +def is_from_GQ_spread(list arr): + r""" + Return a pair `(s, t)` if the graph obtained from a GQ of order `(s, t)` + with a spread has the intersection array passed. We also require that such + GQ can be built by Sage. + If no such pair exists, then return ``False``. + + INPUT: + + - ``arr`` -- list; an intersection array + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: is_from_GQ_spread, graph_from_GQ_spread + sage: is_from_GQ_spread([125, 120, 1, 1, 24, 125]) + (5, 25) + sage: G = graph_from_GQ_spread(5, 25) + sage: G.is_distance_regular(True) + ([125, 120, 1, None], [None, 1, 24, 125]) + + REFERENCES: + + The graphs we are looking for are antipodal covers of complete graphs. + See [BCN1989]_ pp. 385, 386 for a discussion on these particular case. + + TESTS:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: is_from_GQ_spread + sage: is_from_GQ_spread([343, 336, 1, 1, 48, 343]) + (7, 49) + sage: is_from_GQ_spread([343, 336, 1, 2, 48, 343]) + False + + Check that we don't get ``True`` for inexisting GQs:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: is_from_GQ_spread + sage: s = 5 + sage: t = 6 + sage: [s * t, s * (t-1), 1, 1, t - 1, s * t] + [30, 25, 1, 1, 5, 30] + sage: is_from_GQ_spread([30, 25, 1, 1, 5, 30]) + False + """ + from sage.combinat.designs import design_catalog as designs + + if len(arr) != 6: + return False + + t = arr[4] + 1 + if t <= 1: # avoid division by 0 + return False + + s = arr[1] // (t-1) + if s == 1 and t == 1: # in this case we don't get a connected graph + return False + + if arr != [s * t, s * (t-1), 1, 1, t - 1, s * t]: + return False + + # check Sage can build it (it may not exist) + if designs.generalised_quadrangle_with_spread(s, t, existence=True) \ + is not True: + return False + + return (s,t) + +def graph_from_GQ_spread(const int s, const int t): + r""" + Return the point graph of the generalised quandrangle with + order `(s, t)` after removing one of its spreads. + + These graphs are antipodal covers of complete graphs and, in particular, + distance-regular graphs of diameter 3. + + INPUT: + + - ``s, t`` -- integers; order of the generalised quadrangle + + EXAMPLES:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: graph_from_GQ_spread + sage: G = graph_from_GQ_spread(4, 16) + sage: G.is_distance_regular(True) + ([64, 60, 1, None], [None, 1, 15, 64]) + + REFERENCES: + + The graphs constructed here follow [BCN1989]_ pp. 385, 386. + + TESTS:: + + sage: from sage.graphs.generators.distance_regular import \ + ....: graph_from_GQ_spread, is_from_GQ_spread + sage: is_from_GQ_spread([64, 60, 1, 1, 15, 64]) + (4, 16) + sage: graph_from_GQ_spread(*is_from_GQ_spread([27, 24, 1, 1, 8, 27])) + Graph on 112 vertices + sage: _.is_distance_regular(True) + ([27, 24, 1, None], [None, 1, 8, 27]) + """ + from sage.combinat.designs import design_catalog as designs + + (GQ, S) = designs.generalised_quadrangle_with_spread(s, t, check=False) + + k = len(GQ.blocks()[0]) + edges = [] + for b in GQ.blocks(): + if b in S: # skip blocks in spread + continue + for p1, p2 in itertools.combinations(b, 2): + sig_check() + edges.append((p1, p2)) + + G = Graph(edges, format="list_of_edges") + return G diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 4a75f579a96..4796522e31a 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -12438,7 +12438,7 @@ def subgraph(self, vertices=None, edges=None, inplace=False, sage: J = G.subgraph(edges=[(0, 1)]) sage: J.edges(labels=False) [(0, 1)] - sage: J.vertices() == G.vertices() + sage: set(J) == set(G) True sage: G.subgraph([0, 1, 2], inplace=True); G Subgraph of (Complete graph): Graph on 3 vertices @@ -12453,7 +12453,7 @@ def subgraph(self, vertices=None, edges=None, inplace=False, sage: H = D.subgraph(edges=[(0, 1), (0, 2)]) sage: H.edges(labels=False) [(0, 1), (0, 2)] - sage: H.vertices() == D.vertices() + sage: set(H) == set(D) True sage: D Complete digraph: Digraph on 9 vertices @@ -12527,7 +12527,7 @@ def subgraph(self, vertices=None, edges=None, inplace=False, {3: 'v3', 4: 'v4', 5: 'v5'} """ if vertices is None: - vertices = list(self) + vertices = self elif vertices in self: vertices = [vertices] else: @@ -12559,7 +12559,9 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm INPUT: - - ``vertices`` -- a list of vertices + - ``vertices`` -- (default: ``None``); an iterable container of + vertices, e.g. a list, set, graph, file or numeric array. If not + passed (i.e., ``None``), defaults to the entire graph. - ``edges`` -- a single edge or an iterable container of edges (e.g., a list, set, file, numeric array, etc.). By default (``edges=None``), @@ -12585,7 +12587,7 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm sage: J = G._subgraph_by_adding(vertices=G, edges=[(0, 1)]) sage: J.edges(labels=False) [(0, 1)] - sage: J.vertices() == G.vertices() + sage: set(J) == set(G) True sage: G._subgraph_by_adding(vertices=G) == G True @@ -12598,7 +12600,7 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm sage: H = D._subgraph_by_adding(vertices=D, edges=[(0, 1), (0, 2)]) sage: H.edges(labels=False) [(0, 1), (0, 2)] - sage: H.vertices() == D.vertices() + sage: set(H) == set(D) True sage: D Complete digraph: Digraph on 9 vertices @@ -12657,9 +12659,8 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm G = self.__class__(weighted=self._weighted, loops=self.allows_loops(), multiedges=self.allows_multiple_edges()) G.name("Subgraph of (%s)"%self.name()) - G.add_vertices(vertices) + G.add_vertices(self if vertices is None else vertices) - vertices = set(vertices) if edges is not None: edges_to_keep_labeled = frozenset(e for e in edges if len(e) == 3) edges_to_keep_unlabeled = frozenset(e for e in edges if len(e) == 2) @@ -12667,12 +12668,12 @@ def _subgraph_by_adding(self, vertices=None, edges=None, edge_property=None, imm edges_to_keep = [] if self._directed: for u, v, l in self.edges(vertices=vertices, sort=False): - if (v in vertices and ((u, v, l) in edges_to_keep_labeled - or (u, v) in edges_to_keep_unlabeled)): + if (v in G and ((u, v, l) in edges_to_keep_labeled + or (u, v) in edges_to_keep_unlabeled)): edges_to_keep.append((u, v, l)) else: for u, v, l in self.edges(vertices=vertices, sort=False): - if (u in vertices and v in vertices + if (u in G and v in G and ((u, v, l) in edges_to_keep_labeled or (v, u, l) in edges_to_keep_labeled or (u, v) in edges_to_keep_unlabeled @@ -12709,7 +12710,9 @@ def _subgraph_by_deleting(self, vertices=None, edges=None, inplace=False, INPUT: - - ``vertices`` -- a list of vertices + - ``vertices`` -- (default: ``None``); an iterable container of + vertices, e.g. a list, set, graph, file or numeric array. If not + passed (i.e., ``None``), defaults to the entire graph. - ``edges`` -- a single edge or an iterable container of edges (e.g., a list, set, file, numeric array, etc.). By default (``edges=None``), @@ -12739,7 +12742,7 @@ def _subgraph_by_deleting(self, vertices=None, edges=None, inplace=False, sage: J = G._subgraph_by_deleting(vertices=G, edges=[(0, 1)]) sage: J.edges(labels=False) [(0, 1)] - sage: J.vertices() == G.vertices() + sage: set(J) == set(G) True sage: G._subgraph_by_deleting([0, 1, 2], inplace=True); G Subgraph of (Complete graph): Graph on 3 vertices @@ -12754,7 +12757,7 @@ def _subgraph_by_deleting(self, vertices=None, edges=None, inplace=False, sage: H = D._subgraph_by_deleting(vertices=D, edges=[(0, 1), (0, 2)]) sage: H.edges(labels=False) [(0, 1), (0, 2)] - sage: H.vertices() == D.vertices() + sage: set(H) == set(D) True sage: D Complete digraph: Digraph on 9 vertices @@ -12823,7 +12826,9 @@ def _subgraph_by_deleting(self, vertices=None, edges=None, inplace=False, G = copy(self) G.name("Subgraph of (%s)"%self.name()) - G.delete_vertices([v for v in G if v not in vertices]) + if vertices is not None: + vertices = set(vertices) + G.delete_vertices([v for v in G if v not in vertices]) edges_to_delete = [] if edges is not None: diff --git a/src/sage/graphs/graph_database.py b/src/sage/graphs/graph_database.py index 9f63bae48f2..e83ebddb75c 100644 --- a/src/sage/graphs/graph_database.py +++ b/src/sage/graphs/graph_database.py @@ -512,7 +512,8 @@ class located in :mod:`sage.databases.sql_db` to make the query # construct sql syntax substring for display cols disp_list = ['SELECT graph_data.graph6, '] for col in graph_data_disp[1:]: - if col != 'graph6': disp_list.append('graph_data.%s, '%col) + if col != 'graph6': + disp_list.append('graph_data.%s, '%col) for col in aut_grp_disp[1:]: disp_list.append('aut_grp.%s, '%col) for col in degrees_disp[1:]: @@ -670,7 +671,8 @@ def show(self, max_field_size=20, with_picture=False): if re.search('SELECT .*degree_sequence.* FROM', self.__query_string__): format_cols = {'degree_sequence': (lambda x, y: data_to_degseq(x, y))} - else: format_cols = {} + else: + format_cols = {} if with_picture: SQLQuery.show(self, max_field_size=max_field_size, plot_cols={'graph6': (lambda x: graph6_to_plot(x))}, diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index e7741b7f6f7..479dfd40616 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -164,7 +164,7 @@ from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from libc.stdint cimport uint16_t, uint32_t, uint64_t -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * # Defining a pair of vertices as a C struct diff --git a/src/sage/graphs/weakly_chordal.pyx b/src/sage/graphs/weakly_chordal.pyx index 4036412aeb3..fed5ced007d 100644 --- a/src/sage/graphs/weakly_chordal.pyx +++ b/src/sage/graphs/weakly_chordal.pyx @@ -32,7 +32,7 @@ Methods # http://www.gnu.org/licenses/ ############################################################################## -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph diff --git a/src/sage/groups/cubic_braid.py b/src/sage/groups/cubic_braid.py index 12137eb841b..1a81e69a251 100644 --- a/src/sage/groups/cubic_braid.py +++ b/src/sage/groups/cubic_braid.py @@ -1071,7 +1071,7 @@ def set_classical_realization(self, base_group, proj_group, centralizing_matrix, # ------------------------------------------------------------------------------- # local methods to set up the classical group (specific part) # ------------------------------------------------------------------------------- - # Case for symlectic groups + # Case for symplectic groups # ------------------------------------------------------------------------------- def create_sympl_realization(self, m): r""" diff --git a/src/sage/groups/fqf_orthogonal.py b/src/sage/groups/fqf_orthogonal.py index 4294048425f..a88da297c7a 100644 --- a/src/sage/groups/fqf_orthogonal.py +++ b/src/sage/groups/fqf_orthogonal.py @@ -78,7 +78,7 @@ class FqfIsometry(AbelianGroupAutomorphism): def _repr_(self): r""" - Return the string represenation of ``self``. + Return the string representation of ``self``. EXAMPLES:: @@ -114,6 +114,7 @@ def __call__(self, x): else: return AbelianGroupAutomorphism.__call__(self, x) + class FqfOrthogonalGroup(AbelianGroupAutomorphismGroup_subgroup): r""" Return a group of isometries of this torsion quadratic form. diff --git a/src/sage/groups/free_group.py b/src/sage/groups/free_group.py index 743bf66d72e..e3c3f785b7b 100644 --- a/src/sage/groups/free_group.py +++ b/src/sage/groups/free_group.py @@ -907,7 +907,7 @@ def abelian_invariants(self): """ return (0,) * self.ngens() - def quotient(self, relations): + def quotient(self, relations, **kwds): """ Return the quotient of ``self`` by the normal subgroup generated by the given elements. @@ -920,6 +920,8 @@ def quotient(self, relations): - ``relations`` -- A list/tuple/iterable with the elements of the free group. + - further named arguments, that are passed to the constructor + of a finitely presented group. OUTPUT: @@ -951,6 +953,6 @@ def quotient(self, relations): """ from sage.groups.finitely_presented import FinitelyPresentedGroup - return FinitelyPresentedGroup(self, tuple(map(self, relations) ) ) + return FinitelyPresentedGroup(self, tuple(map(self, relations) ), **kwds) __truediv__ = quotient diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index 362eaf7c226..9590eba4e0f 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -221,7 +221,7 @@ cdef class Group(Parent): from sage.misc.all import prod return prod(self.gens()) - def quotient(self, H): + def quotient(self, H, **kwds): """ Return the quotient of this group by the normal subgroup `H`. diff --git a/src/sage/groups/group_semidirect_product.py b/src/sage/groups/group_semidirect_product.py index d5b8b6b62de..e7e87530d1c 100644 --- a/src/sage/groups/group_semidirect_product.py +++ b/src/sage/groups/group_semidirect_product.py @@ -443,5 +443,21 @@ def opposite_semidirect_product(self): """ return GroupSemidirectProduct(self.cartesian_factors()[1], self.cartesian_factors()[0], twist=self._twist, act_to_right = not self.act_to_right(), prefix0 = self._prefix1, prefix1 = self._prefix0, print_tuple = self._print_tuple, category=self._category) + def construction(self): + r""" + Return ``None``. + + This overrides the construction functor inherited from ``CartesianProduct``. + + EXAMPLES:: + + sage: def twist(x,y): + ....: return y + sage: H = GroupSemidirectProduct(WeylGroup(['A',2],prefix="s"), WeylGroup(['A',3],prefix="t"), twist) + sage: H.construction() + + """ + return None + GroupSemidirectProduct.Element = GroupSemidirectProductElement diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx index 7b522f0cb15..6243307fba1 100644 --- a/src/sage/groups/old.pyx +++ b/src/sage/groups/old.pyx @@ -189,7 +189,7 @@ cdef class Group(sage.structure.parent_gens.ParentWithGens): """ raise NotImplementedError - def quotient(self, H): + def quotient(self, H, **kwds): """ Return the quotient of this group by the normal subgroup `H`. diff --git a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx index 92443eda909..eb3263d914d 100644 --- a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +++ b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx @@ -113,7 +113,7 @@ from libc.string cimport memcmp, memcpy from cysignals.memory cimport sig_malloc, sig_realloc, sig_free from .data_structures cimport * -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * cdef inline int agcl_cmp(int a, int b): if a < b: return -1 diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd index bb1f8157d5c..b6af8190d51 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd @@ -8,7 +8,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.data_structures.bitset cimport bitset_s, bitset_t +from sage.data_structures.bitset_base cimport * from libc.string cimport memcpy from libc.stdlib cimport rand diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx index cc0bc55e3a1..b3b2bfe1d68 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx @@ -31,7 +31,7 @@ from libc.string cimport memcpy, memset from libc.stdlib cimport rand from cysignals.memory cimport sig_malloc, sig_calloc, sig_realloc, sig_free -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from sage.rings.integer cimport Integer from sage.libs.flint.ulong_extras cimport n_is_prime @@ -291,7 +291,7 @@ cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=NULL, bitset_zero(b) while 1: if PS.levels[i] <= PS.depth: - if i != j and n > i - j + 1 and (partn_ref_alg is None or + if i != j and n > i - j + 1 and (partn_ref_alg is None or partn_ref_alg._minimization_allowed_on_col(PS.entries[j])): n = i - j + 1 location = j diff --git a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx index 4231c3b6d3a..b197321e084 100644 --- a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx +++ b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx @@ -96,8 +96,7 @@ REFERENCE: from cysignals.memory cimport sig_calloc from .data_structures cimport * -include "sage/data_structures/bitset.pxi" - +from sage.data_structures.bitset_base cimport * # Functions diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx index 59eadef85e2..541cf8ddd23 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx @@ -26,7 +26,7 @@ REFERENCE: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from .data_structures cimport * from sage.rings.integer cimport Integer from sage.structure.element import is_Matrix @@ -1086,10 +1086,10 @@ def random_tests(num=50, n_max=50, k_max=6, nwords_max=200, perms_per_code=10, d C_n = NonlinearBinaryCodeStruct( matrix(GF(2), B_n.nwords, B_n.degree) ) for j from 0 <= j < B.dimension: for h from 0 <= h < B.degree: - bitset_set_to(&C.basis[j], perm[h], bitset_check(&B.basis[j], h)) + bitset_set_to(&C.basis[j], perm[h], bitset_check(&B.basis[j], h)) for j from 0 <= j < B_n.nwords: for h from 0 <= h < B_n.degree: - bitset_set_to(&C_n.words[j], perm[h], bitset_check(&B_n.words[j], h)) + bitset_set_to(&C_n.words[j], perm[h], bitset_check(&B_n.words[j], h)) # now C is a random permutation of B, and C_n of B_n C.run() C_n.run() diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index fae908884ea..53e1c620c69 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -22,7 +22,7 @@ REFERENCE: # **************************************************************************** from .data_structures cimport * -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from sage.rings.integer cimport Integer from sage.graphs.base.sparse_graph cimport SparseGraph from sage.graphs.base.dense_graph cimport DenseGraph diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx index d1123e426d0..736659318d6 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx @@ -29,7 +29,7 @@ REFERENCE: from libc.string cimport memcmp from .data_structures cimport * -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * from sage.rings.integer cimport Integer from sage.matrix.constructor import Matrix from .refinement_binary cimport NonlinearBinaryCodeStruct, refine_by_bip_degree diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx index 22cc10c4862..1ae52e88426 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx @@ -28,7 +28,7 @@ REFERENCE: from .data_structures cimport * from .double_coset cimport double_coset -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * def set_stab_py(generators, sett, relab=False): diff --git a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx index 2af474f9401..813218c2e49 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx @@ -186,7 +186,7 @@ from copy import copy from cysignals.memory cimport sig_malloc, sig_realloc, sig_free from sage.groups.perm_gps.partn_ref.data_structures cimport * -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * cdef tuple PS_refinement(PartitionStack * part, long *refine_vals, long *best, diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 334d90ac328..c411b1e4fac 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -2944,11 +2944,13 @@ def as_finitely_presented_group(self, reduced=False): ret_fp = ret_fp.simplified() return ret_fp - def quotient(self, N): + def quotient(self, N, **kwds): """ Returns the quotient of this permutation group by the normal subgroup `N`, as a permutation group. + Further named arguments are passed to the permutation group constructor. + Wraps the GAP operator "/". EXAMPLES:: @@ -2965,7 +2967,7 @@ def quotient(self, N): # This is currently done using the right regular representation # FIXME: GAP certainly knows of a better way! phi = Q.RegularActionHomomorphism() - return PermutationGroup(gap_group=phi.Image()) + return PermutationGroup(gap_group=phi.Image(), **kwds) def commutator(self, other=None): r""" diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index cce13898753..177cfe2f714 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -608,7 +608,7 @@ def quit(self, verbose=False): sage: a = maxima('y') sage: maxima.quit(verbose=True) - Exiting Maxima with PID ... running .../bin/maxima ... + Exiting Maxima with PID ... running .../bin/maxima... sage: a._check_valid() Traceback (most recent call last): ... diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index ee8de094311..f3755802d9b 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2665,7 +2665,7 @@ def sub(self, gens): """ return self.parent().bar_call(self, 'sub', gens) - def quo(self, gens): + def quo(self, gens, **args): """ Return the quotient of self by the given object or list of generators. @@ -2674,6 +2674,7 @@ def quo(self, gens): - ``gens`` - object or list/tuple of generators + - further named arguments that are ignored OUTPUT: diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index 9a0343dc52b..ec45d445892 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -500,7 +500,21 @@ def _install_hints(self): (1) You might have to buy Mathematica (http://www.wolfram.com/). - (2) * LINUX: The math script comes standard with your Mathematica install. + (2) * LINUX: The math script usually comes standard with your Mathematica install. + However, on some systems it may be called wolfram, while math is absent. + In this case, assuming wolfram is in your PATH, + (a) create a file called math (in your PATH): + #!/bin/sh + /usr/bin/env wolfram $@ + + (b) Make the file executable. + chmod +x math + + * WINDOWS: + + Install Mathematica for Linux into the VMware virtual machine (sorry, + that's the only way at present). + * APPLE OS X: (a) create a file called math (in your PATH): diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 70e885c0930..b0e0de05ec0 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -56,7 +56,7 @@ import sys import subprocess -from sage.env import DOT_SAGE +from sage.env import DOT_SAGE, MAXIMA COMMANDS_CACHE = '%s/maxima_commandlist_cache.sobj' % DOT_SAGE from sage.cpython.string import bytes_to_str @@ -167,7 +167,7 @@ def _command_runner(self, command, s, redirect=True): -- Function: gcd (, , , ...) ... """ - cmd = 'maxima --very-quiet --batch-string="%s(%s);" '%(command, s) + cmd = '{} --very-quiet --batch-string="{}({});" '.format(MAXIMA, command, s) if sage.server.support.EMBEDDED_MODE: cmd += '< /dev/null' @@ -2218,7 +2218,7 @@ def maxima_version(): sage: maxima_version() # random '5.41.0' """ - with os.popen('maxima --version') as p: + with os.popen('{} --version'.format(MAXIMA)) as p: return p.read().split()[-1] @@ -2236,4 +2236,4 @@ def maxima_console(): from sage.repl.rich_output.display_manager import get_display_manager if not get_display_manager().is_in_terminal(): raise RuntimeError('Can use the console only in the terminal. Try %%maxima magics instead.') - os.system('maxima') + os.system('{}'.format(MAXIMA)) diff --git a/src/sage/interfaces/psage.py b/src/sage/interfaces/psage.py index f12aff7c3eb..11bc5c60b50 100644 --- a/src/sage/interfaces/psage.py +++ b/src/sage/interfaces/psage.py @@ -113,10 +113,21 @@ def __del__(self): sage: PSage().__del__() """ - if os.path.exists(self.__tmp_dir): - for x in os.listdir(self.__tmp_dir): - os.remove(os.path.join(self.__tmp_dir, x)) + try: + files = os.listdir(self.__tmp_dir) + except OSError: + pass + else: + for x in files: + try: + os.remove(os.path.join(self.__tmp_dir, x)) + except OSError: + pass + try: os.removedirs(self.__tmp_dir) + except OSError: + pass + if not (self._expect is None): cmd = 'kill -9 %s'%self._expect.pid os.system(cmd) diff --git a/src/sage/libs/arb/arb.pxd b/src/sage/libs/arb/arb.pxd index 06827a174d9..17d685ffe4b 100644 --- a/src/sage/libs/arb/arb.pxd +++ b/src/sage/libs/arb/arb.pxd @@ -29,6 +29,7 @@ cdef extern from "arb_wrap.h": void arb_set_fmpq(arb_t y, const fmpq_t x, long prec) int arb_set_str(arb_t res, const char * inp, long prec) char * arb_get_str(const arb_t x, long n, unsigned long flags) + char * arb_version void arb_zero(arb_t x) void arb_one(arb_t f) diff --git a/src/sage/libs/arb/arb_version.pyx b/src/sage/libs/arb/arb_version.pyx new file mode 100644 index 00000000000..6ad567e67ce --- /dev/null +++ b/src/sage/libs/arb/arb_version.pyx @@ -0,0 +1,19 @@ +# -*- coding: utf-8 +from sage.libs.arb.arb cimport arb_version +from sage.cpython.string cimport char_to_str + +def version(): + """ + Get arb version + + TESTS:: + + sage: from sage.libs.arb.arb_version import version + sage: version().split('.')[0] + '2' + """ + try: + py_string = char_to_str(arb_version) + finally: + pass + return py_string diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx index 4b92c256771..92752b45203 100644 --- a/src/sage/libs/fes.pyx +++ b/src/sage/libs/fes.pyx @@ -38,7 +38,7 @@ Random Degree-2 System:: sage: len(sols) # optional - FES 1 -Cylic benchmark:: +Cyclic benchmark:: sage: from sage.rings.ideal import Cyclic # optional - FES sage: from sage.libs.fes import exhaustive_search # optional - FES diff --git a/src/sage/libs/ppl.pyx b/src/sage/libs/ppl.pyx deleted file mode 100644 index f1c617576ff..00000000000 --- a/src/sage/libs/ppl.pyx +++ /dev/null @@ -1,7250 +0,0 @@ -# distutils: sources = sage/libs/ppl_shim.cc -# distutils: language = c++ -# distutils: libraries = ppl m -r""" -Cython wrapper for the Parma Polyhedra Library (PPL) - -The Parma Polyhedra Library (PPL) is a library for polyhedral -computations over `\QQ`. This interface tries to reproduce the C++ API -as faithfully as possible in Cython/Sage. For example, the following -C++ excerpt: - -.. code-block:: c++ - - Variable x(0); - Variable y(1); - Constraint_System cs; - cs.insert(x >= 0); - cs.insert(x <= 3); - cs.insert(y >= 0); - cs.insert(y <= 3); - C_Polyhedron poly_from_constraints(cs); - -translates into:: - - sage: from sage.libs.ppl import Variable, Constraint_System, C_Polyhedron - doctest:warning - ... - DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert(x >= 0) - sage: cs.insert(x <= 3) - sage: cs.insert(y >= 0) - sage: cs.insert(y <= 3) - sage: poly_from_constraints = C_Polyhedron(cs) - -The same polyhedron constructed from generators:: - - sage: from sage.libs.ppl import Variable, Generator_System, C_Polyhedron, point - sage: gs = Generator_System() - sage: gs.insert(point(0*x + 0*y)) - sage: gs.insert(point(0*x + 3*y)) - sage: gs.insert(point(3*x + 0*y)) - sage: gs.insert(point(3*x + 3*y)) - sage: poly_from_generators = C_Polyhedron(gs) - -Rich comparisons test equality/inequality and strict/non-strict -containment:: - - sage: poly_from_generators == poly_from_constraints - True - sage: poly_from_generators >= poly_from_constraints - True - sage: poly_from_generators < poly_from_constraints - False - sage: poly_from_constraints.minimized_generators() - Generator_System {point(0/1, 0/1), point(0/1, 3/1), point(3/1, 0/1), point(3/1, 3/1)} - sage: poly_from_constraints.minimized_constraints() - Constraint_System {-x0+3>=0, -x1+3>=0, x0>=0, x1>=0} - -As we see above, the library is generally easy to use. There are a few -pitfalls that are not entirely obvious without consulting the -documentation, in particular: - -* There are no vectors used to describe :class:`Generator` (points, - closure points, rays, lines) or :class:`Constraint` (strict - inequalities, non-strict inequalities, or equations). Coordinates - are always specified via linear polynomials in :class:`Variable` - -* All coordinates of rays and lines as well as all coefficients of - constraint relations are (arbitrary precision) integers. Only the - generators :func:`point` and :func:`closure_point` allow one to - specify an overall divisor of the otherwise integral - coordinates. For example:: - - sage: from sage.libs.ppl import Variable, point - sage: x = Variable(0); y = Variable(1) - sage: p = point( 2*x+3*y, 5 ); p - point(2/5, 3/5) - sage: p.coefficient(x) - 2 - sage: p.coefficient(y) - 3 - sage: p.divisor() - 5 - -* PPL supports (topologically) closed polyhedra - (:class:`C_Polyhedron`) as well as not necessarily closed polyhedra - (:class:`NNC_Polyhedron`). Only the latter allows closure points - (=points of the closure but not of the actual polyhedron) and strict - inequalities (``>`` and ``<``) - -The naming convention for the C++ classes is that they start with -``PPL_``, for example, the original ``Linear_Expression`` becomes -``PPL_Linear_Expression``. The Python wrapper has the same name as the -original library class, that is, just ``Linear_Expression``. In short: - -* If you are using the Python wrapper (if in doubt: thats you), then - you use the same names as the PPL C++ class library. - -* If you are writing your own Cython code, you can access the - underlying C++ classes by adding the prefix ``PPL_``. - -Finally, PPL is fast. For example, here is the permutahedron of 5 -basis vectors:: - - sage: from sage.libs.ppl import Variable, Generator_System, point, C_Polyhedron - sage: basis = list(range(5)) - sage: x = [ Variable(i) for i in basis ] - sage: gs = Generator_System() - sage: for coeff in Permutations(basis): - ....: gs.insert(point( sum( (coeff[i]+1)*x[i] for i in basis ) )) - sage: C_Polyhedron(gs) - A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 120 points - -The same computation with cddlib which is slightly slower:: - - sage: basis = list(range(5)) - sage: gs = [ tuple(coeff) for coeff in Permutations(basis) ] - sage: Polyhedron(vertices=gs, backend='cdd') - A 4-dimensional polyhedron in QQ^5 defined as the convex hull of 120 vertices - -DIFFERENCES VS. C++ - -Since Python and C++ syntax are not always compatible, there are -necessarily some differences. The main ones are: - -* The :class:`Linear_Expression` also accepts an iterable as input for - the homogeneous coefficients. - -* :class:`Polyhedron` and its subclasses as well as - :class:`Generator_System` and :class:`Constraint_System` can be set - immutable via a ``set_immutable()`` method. This is the analog of - declaring a C++ instance ``const``. All other classes are immutable - by themselves. - -AUTHORS: - -- Volker Braun (2010-10-08): initial version. -- Risan (2012-02-19): extension for MIP_Problem class -- Vincent Klein (2017-12-21) : Deprecate this module. - Future change should be done in the standalone package pplpy - (https://github.com/videlec/pplpy). -""" - -#***************************************************************************** -# Copyright (C) 2010 Volker Braun -# -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at youroption) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.misc.superseded import deprecation -from cysignals.signals cimport sig_on, sig_off - -from sage.structure.sage_object cimport SageObject -from sage.libs.gmp.mpz cimport * -from sage.libs.gmpxx cimport mpz_class -from sage.rings.integer cimport Integer -from sage.rings.rational cimport Rational - -from libcpp cimport bool as cppbool -from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE - -deprecation(23024, "The Sage wrappers around ppl are now superseded " + - "by the standalone pplpy. Please use import 'ppl' instead" + - " of 'sage.libs.ppl'.") - -#################################################### -# Potentially expensive operations: -# - compute dual description -# - solve linear program -# These can only be triggered by methods in the Polyhedron class -# they need to be wrapped in sig_on() / sig_off() -#################################################### - -#################################################### -# PPL can use floating-point arithmetic to compute integers -cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": - cdef void set_rounding_for_PPL() - cdef void restore_pre_PPL_rounding() - -# but with PPL's rounding the gsl will be very unhappy; must turn off! -restore_pre_PPL_rounding() - -#################################################### -# Cython does not support ctypedef within cppclass; Hack around this restriction: -cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library::Generator": - ctypedef enum PPL_GeneratorType "Parma_Polyhedra_Library::Generator::Type": - LINE, RAY, POINT, CLOSURE_POINT - -cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library::Constraint": - ctypedef enum PPL_ConstraintType "Parma_Polyhedra_Library::Constraint::Type": - EQUALITY, NONSTRICT_INEQUALITY, STRICT_INEQUALITY - -cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library::MIP_Problem": - ctypedef enum PPL_MIP_Problem_Control_Parameter_Name: - PRICING - ctypedef enum PPL_MIP_Problem_Control_Parameter_Value: - PRICING_STEEPEST_EDGE_FLOAT, PRICING_STEEPEST_EDGE_EXACT, PRICING_TEXTBOOK - - -#################################################### -cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": - - ctypedef size_t PPL_dimension_type "Parma_Polyhedra_Library::dimension_type" - ctypedef mpz_class PPL_Coefficient "Parma_Polyhedra_Library::Coefficient" - cdef cppclass PPL_Variable "Parma_Polyhedra_Library::Variable" - cdef cppclass PPL_Variables_Set "Parma_Polyhedra_Library::Variables_Set" - cdef cppclass PPL_Linear_Expression "Parma_Polyhedra_Library::Linear_Expression" - cdef cppclass PPL_Generator "Parma_Polyhedra_Library::Generator" - cdef cppclass PPL_Generator_System "Parma_Polyhedra_Library::Generator_System" - cdef cppclass PPL_Constraint "Parma_Polyhedra_Library::Constraint" - cdef cppclass PPL_Constraint_System "Parma_Polyhedra_Library::Constraint_System" - cdef cppclass PPL_Polyhedron "Parma_Polyhedra_Library::Polyhedron" - cdef cppclass PPL_C_Polyhedron "Parma_Polyhedra_Library::C_Polyhedron" (PPL_Polyhedron) - cdef cppclass PPL_NNC_Polyhedron "Parma_Polyhedra_Library::NNC_Polyhedron" (PPL_Polyhedron) - cdef cppclass PPL_Poly_Gen_Relation "Parma_Polyhedra_Library::Poly_Gen_Relation" - cdef cppclass PPL_Poly_Con_Relation "Parma_Polyhedra_Library::Poly_Con_Relation" - cdef cppclass PPL_MIP_Problem "Parma_Polyhedra_Library::MIP_Problem" - - cdef cppclass PPL_Variable: - PPL_Variable(PPL_dimension_type i) - PPL_dimension_type id() - bint OK() - PPL_dimension_type space_dimension() - - cdef cppclass PPL_Variables_Set: - PPL_Variables_Set() - PPL_Variables_Set(PPL_Variable v) - PPL_Variables_Set(PPL_Variable v, PPL_Variable w) - PPL_dimension_type space_dimension() - void insert(PPL_Variable v) - size_t size() - void ascii_dump() - bint OK() - - cdef cppclass PPL_Linear_Expression: - PPL_Linear_Expression() - PPL_Linear_Expression(PPL_Linear_Expression &e) - PPL_Linear_Expression(PPL_Coefficient n) - PPL_Linear_Expression(PPL_Variable v) - PPL_dimension_type space_dimension() - PPL_Coefficient coefficient(PPL_Variable v) - PPL_Coefficient inhomogeneous_term() - bint is_zero() - bint all_homogeneous_terms_are_zero() - void ascii_dump() - bint OK() - PPL_Linear_Expression operator+(PPL_Linear_Expression& e) - PPL_Linear_Expression operator-(PPL_Linear_Expression& e) - PPL_Linear_Expression operator*(PPL_Coefficient n) - PPL_Constraint operator> (PPL_Linear_Expression& e) - PPL_Constraint operator>=(PPL_Linear_Expression& e) - PPL_Constraint operator==(PPL_Linear_Expression& e) - PPL_Constraint operator<=(PPL_Linear_Expression& e) - PPL_Constraint operator< (PPL_Linear_Expression& e) - - cdef cppclass PPL_Generator: - PPL_Generator(PPL_Generator &g) - # Cython does not support static members - #PPL_Generator line(PPL_Linear_Expression &e) - #PPL_Generator ray(PPL_Linear_Expression &e) - #PPL_Generator point(PPL_Linear_Expression &e, PPL_Coefficient d) - #PPL_Generator closure_point(PPL_Linear_Expression &e) - PPL_dimension_type space_dimension() - PPL_GeneratorType type() - bint is_line() - bint is_ray() - bint is_line_or_ray() - bint is_point() - bint is_closure_point() - PPL_Coefficient coefficient(PPL_Variable v) - PPL_Coefficient divisor() except + - bint is_equivalent_to(PPL_Generator &y) - void ascii_dump() - bint OK() - - cdef cppclass PPL_Constraint: - PPL_Constraint() - PPL_Constraint(PPL_Constraint &g) - PPL_dimension_type space_dimension() - PPL_ConstraintType type() - bint is_equality() - bint is_inequality() - bint is_nonstrict_inequality() - bint is_strict_inequality() - PPL_Coefficient coefficient(PPL_Variable v) - PPL_Coefficient inhomogeneous_term() - bint is_tautological() - bint is_inconsistent() - bint is_equivalent_to(PPL_Constraint &y) - void ascii_dump() - bint OK() - - cdef cppclass PPL_Generator_System: - # This seems to not work in cython - #cppclass PPL_const_iterator "const_iterator": - # PPL_Generator operator*() - # PPL_const_iterator operator++() - # bint operator==(PPL_const_iterator&) - # bint operator!=(PPL_const_iterator&) - #PPL_const_iterator begin() - #PPL_const_iterator end() - PPL_Generator_System() - PPL_Generator_System(PPL_Generator &g) - PPL_Generator_System(PPL_Generator_System &gs) - PPL_dimension_type space_dimension() - void clear() - void insert(PPL_Generator &g) - bint empty() - void ascii_dump() - bint OK() - - cdef cppclass PPL_Constraint_System: - # This seems to not work in cython - #cppclass PPL_const_iterator "const_iterator": - # PPL_Constraint operator*() - # PPL_const_iterator operator++() - # bint operator==(PPL_const_iterator&) - # bint operator!=(PPL_const_iterator&) - #PPL_const_iterator begin() - #PPL_const_iterator end() - PPL_Constraint_System() - PPL_Constraint_System(PPL_Constraint &g) - PPL_Constraint_System(PPL_Constraint_System &gs) - PPL_dimension_type space_dimension() - bint has_equalities() - bint has_strict_inequalities() - void clear() - void insert(PPL_Constraint &g) - bint empty() - void ascii_dump() - bint OK() - - cdef enum PPL_Degenerate_Element: - UNIVERSE, EMPTY - - cdef enum PPL_Optimization_Mode "Parma_Polyhedra_Library::Optimization_Mode": - MINIMIZATION, MAXIMIZATION - - cdef enum MIP_Problem_Status: - UNFEASIBLE_MIP_PROBLEM, UNBOUNDED_MIP_PROBLEM, OPTIMIZED_MIP_PROBLEM - - cdef cppclass PPL_Polyhedron: - PPL_dimension_type space_dimension() - PPL_dimension_type affine_dimension() - PPL_Constraint_System& constraints() - PPL_Constraint_System& minimized_constraints() - PPL_Generator_System& generators() - PPL_Generator_System& minimized_generators() - PPL_Poly_Con_Relation relation_with(PPL_Constraint &c) except +ValueError - PPL_Poly_Gen_Relation relation_with(PPL_Generator &g) except +ValueError - bint is_empty() - bint is_universe() - bint is_topologically_closed() - bint is_disjoint_from(PPL_Polyhedron &y) except +ValueError - bint is_discrete() - bint is_bounded() - bint contains_integer_point() - bint constrains(PPL_Variable var) except +ValueError - bint bounds_from_above(PPL_Linear_Expression &expr) except +ValueError - bint bounds_from_below(PPL_Linear_Expression &expr) except +ValueError - bint maximize(PPL_Linear_Expression &expr, PPL_Coefficient &sup_n, PPL_Coefficient &sup_d, - cppbool &maximum) - bint maximize(PPL_Linear_Expression &expr, PPL_Coefficient &sup_n, PPL_Coefficient &sup_d, - cppbool &maximum, PPL_Generator &g) - bint minimize(PPL_Linear_Expression &expr, PPL_Coefficient &inf_n, PPL_Coefficient &inf_d, - cppbool &minimum) - bint minimize(PPL_Linear_Expression &expr, PPL_Coefficient &inf_n, PPL_Coefficient &inf_d, - cppbool &minimum, PPL_Generator &g) - bint frequency(PPL_Linear_Expression &expr, PPL_Coefficient &freq_n, PPL_Coefficient &freq_d, - PPL_Coefficient &val_n, PPL_Coefficient &val_d) - bint contains(PPL_Polyhedron &y) except +ValueError - bint strictly_contains(PPL_Polyhedron &y) except +ValueError - void add_constraint(PPL_Constraint &c) except +ValueError - void add_generator(PPL_Generator &g) except +ValueError - void add_constraints(PPL_Constraint_System &cs) except +ValueError - void add_generators(PPL_Generator_System &gs) except +ValueError - void refine_with_constraint(PPL_Constraint &c) except +ValueError - void refine_with_constraints(PPL_Constraint_System &cs) except +ValueError - void unconstrain(PPL_Variable var) except +ValueError - void intersection_assign(PPL_Polyhedron &y) except +ValueError - void poly_hull_assign(PPL_Polyhedron &y) except +ValueError - void upper_bound_assign(PPL_Polyhedron &y) except +ValueError - void poly_difference_assign(PPL_Polyhedron &y) except +ValueError - void difference_assign(PPL_Polyhedron &y) except +ValueError - void drop_some_non_integer_points() - void topological_closure_assign() - void add_space_dimensions_and_embed(PPL_dimension_type m) except +ValueError - void add_space_dimensions_and_project(PPL_dimension_type m) except +ValueError - void concatenate_assign(PPL_Polyhedron &y) except +ValueError - void remove_higher_space_dimensions(PPL_dimension_type new_dimension) except +ValueError - void ascii_dump() - int hash_code() - PPL_dimension_type max_space_dimension() - bint OK(cppbool check_not_empty=false) - bint operator!=(PPL_Polyhedron &y) - bint operator==(PPL_Polyhedron &y) - - cdef cppclass PPL_C_Polyhedron(PPL_Polyhedron): - PPL_C_Polyhedron(PPL_dimension_type num_dimensions, PPL_Degenerate_Element) - PPL_C_Polyhedron(PPL_Constraint_System &cs) except +ValueError - PPL_C_Polyhedron(PPL_Generator_System &gs) except +ValueError - PPL_C_Polyhedron(PPL_C_Polyhedron &y) - - cdef cppclass PPL_NNC_Polyhedron(PPL_Polyhedron): - PPL_NNC_Polyhedron(PPL_dimension_type num_dimensions, PPL_Degenerate_Element kind) - PPL_NNC_Polyhedron(PPL_Constraint_System &cs) except +ValueError - PPL_NNC_Polyhedron(PPL_Generator_System &gs) except +ValueError - PPL_NNC_Polyhedron(PPL_NNC_Polyhedron &y) - PPL_NNC_Polyhedron(PPL_C_Polyhedron &y) - - cdef cppclass PPL_Poly_Gen_Relation: - PPL_Poly_Gen_Relation(PPL_Poly_Gen_Relation &cpy_from) - bint implies(PPL_Poly_Gen_Relation &y) - void ascii_dump() - bint OK() - - cdef cppclass PPL_Poly_Con_Relation: - PPL_Poly_Con_Relation(PPL_Poly_Con_Relation &cpy_from) - bint implies(PPL_Poly_Con_Relation &y) - void ascii_dump() - bint OK() - - cdef cppclass PPL_MIP_Problem: - PPL_MIP_Problem(PPL_MIP_Problem &cpy_from) - PPL_MIP_Problem(PPL_dimension_type dim) except +ValueError - PPL_MIP_Problem(PPL_dimension_type dim, PPL_Constraint_System &cs, PPL_Linear_Expression &obj, PPL_Optimization_Mode) except +ValueError - PPL_dimension_type space_dimension() - PPL_Linear_Expression& objective_function() - void clear() - void add_space_dimensions_and_embed(PPL_dimension_type m) except +ValueError - void add_constraint(PPL_Constraint &c) except +ValueError - void add_constraints(PPL_Constraint_System &cs) except +ValueError - void add_to_integer_space_dimensions(PPL_Variables_Set &i_vars) except +ValueError - void set_objective_function(PPL_Linear_Expression &obj) except +ValueError - void set_optimization_mode(PPL_Optimization_Mode mode) - PPL_Optimization_Mode optimization_mode() - bint is_satisfiable() - MIP_Problem_Status solve() - void evaluate_objective_function(PPL_Generator evaluating_point, PPL_Coefficient &num, PPL_Coefficient &den) except +ValueError - PPL_Generator& feasible_point() - PPL_Generator optimizing_point() except +ValueError - void optimal_value(PPL_Coefficient &num, PPL_Coefficient &den) except +ValueError - bint OK() - PPL_MIP_Problem_Control_Parameter_Value get_control_parameter(PPL_MIP_Problem_Control_Parameter_Name name) - void set_control_parameter(PPL_MIP_Problem_Control_Parameter_Value value) - -cdef extern from "ppl.hh": - PPL_Generator PPL_line "Parma_Polyhedra_Library::line" (PPL_Linear_Expression &e) except +ValueError - PPL_Generator PPL_ray "Parma_Polyhedra_Library::ray" (PPL_Linear_Expression &e) except +ValueError - PPL_Generator PPL_point "Parma_Polyhedra_Library::point" (PPL_Linear_Expression &e, PPL_Coefficient &d) except +ValueError - PPL_Generator PPL_closure_point "Parma_Polyhedra_Library::closure_point" (PPL_Linear_Expression &e, PPL_Coefficient &d) except +ValueError - - -#################################################### -# Cython does not support static methods; hack around -cdef extern from "ppl.hh": - - PPL_Poly_Gen_Relation PPL_Poly_Gen_Relation_nothing "Parma_Polyhedra_Library::Poly_Gen_Relation::nothing" () - PPL_Poly_Gen_Relation PPL_Poly_Gen_Relation_subsumes "Parma_Polyhedra_Library::Poly_Gen_Relation::subsumes" () - - PPL_Poly_Con_Relation PPL_Poly_Con_Relation_nothing "Parma_Polyhedra_Library::Poly_Con_Relation::nothing" () - PPL_Poly_Con_Relation PPL_Poly_Con_Relation_is_disjoint "Parma_Polyhedra_Library::Poly_Con_Relation::is_disjoint" () - PPL_Poly_Con_Relation PPL_Poly_Con_Relation_strictly_intersects "Parma_Polyhedra_Library::Poly_Con_Relation::strictly_intersects" () - PPL_Poly_Con_Relation PPL_Poly_Con_Relation_is_included "Parma_Polyhedra_Library::Poly_Con_Relation::is_included" () - PPL_Poly_Con_Relation PPL_Poly_Con_Relation_saturates "Parma_Polyhedra_Library::Poly_Con_Relation::saturates" () - - - -#################################################### -# Workaround for private constructors -cdef extern from "ppl_shim.hh": - PPL_Poly_Gen_Relation* new_relation_with(PPL_Polyhedron &p, PPL_Generator &g) except +ValueError - PPL_Poly_Con_Relation* new_relation_with(PPL_Polyhedron &p, PPL_Constraint &c) except +ValueError - - -### Forward declarations ########################### -cdef class _mutable_or_immutable(SageObject) -cdef class Variable(object) -cdef class Variables_Set(object) -cdef class Linear_Expression(object) -cdef class Generator(object) -cdef class Generator_System(_mutable_or_immutable) -cdef class Generator_System_iterator(object) -cdef class Constraint(object) -cdef class Constraint_System(_mutable_or_immutable) -cdef class Constraint_System_iterator(object) -cdef class Polyhedron(_mutable_or_immutable) -cdef class C_Polyhedron(Polyhedron) -cdef class NNC_Polyhedron(Polyhedron) -cdef class Poly_Gen_Relation(object) -cdef class Poly_Con_Relation(object) -cdef class MIP_Problem(_mutable_or_immutable) - - -#################################################### -### _mutable_or_immutable ########################## -#################################################### -cdef class _mutable_or_immutable(SageObject): - r""" - A base class for mutable or immutable objects. - - By default, any object is mutable. It can then be - :meth:`set_immutable`. - - EXAMPLES:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() - sage: x.is_mutable() - True - sage: x.is_immutable() - False - sage: x.set_immutable() - sage: x.is_mutable() - False - """ - - cdef bint _is_mutable - - def __cinit__(self): - """ - The Cython constructor. - - TESTS:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() # indirect doctest - sage: x.is_mutable() - True - """ - self._is_mutable = True - - - def set_immutable(self): - """ - Make this object immutable. - - This operation cannot be undone. - - EXAMPLES:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() - sage: x.is_mutable() - True - sage: x.set_immutable() - sage: x.is_mutable() - False - """ - self._is_mutable = False - - - def is_mutable(self): - """ - Return whether this object is mutable. - - The data members of the object can only be modified if the - object is mutable. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() - sage: x.is_mutable() - True - """ - return self._is_mutable - - - def is_immutable(self): - """ - Return whether this object is immutable. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() - sage: x.is_immutable() - False - """ - return not self._is_mutable - - - def assert_mutable(self, msg): - r""" - Raise ``ValueError`` if the object is not mutable. - - INPUT: - - - ``msg`` -- a string. The message to be returned together - with the ``ValueError`` - - OUTPUT: - - This method returns no output. A ``ValueError``` is raised if - the object is not mutable. - - EXAMPLES:: - - sage: from sage.libs.ppl import _mutable_or_immutable as ExampleObj - sage: x = ExampleObj() - sage: x.assert_mutable("this will not trigger") - sage: x.set_immutable() - sage: x.assert_mutable("this will trigger") - Traceback (most recent call last): - ... - ValueError: this will trigger - """ - if not self._is_mutable: - raise ValueError(msg) - -#################################################### -### MIP_Problem #################################### -#################################################### -cdef class MIP_Problem(_mutable_or_immutable): - r""" - wrapper for PPL's MIP_Problem class - - An object of the class MIP_Problem represents a Mixed Integer - (Linear) Program problem. - - INPUT: - - - ``dim`` -- integer - - ``args`` -- an array of the defining data of the MIP_Problem. - For each element, any one of the following is accepted: - - * A :class:`Constraint_System`. - - * A :class:`Linear_Expression`. - - OUTPUT: - - A :class:`MIP_Problem`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.optimal_value() - 10/3 - sage: m.optimizing_point() - point(10/3, 0/3) - """ - cdef PPL_MIP_Problem *thisptr - - def __repr__(self): - """ - String representation of MIP Problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0 ) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m - A MIP_Problem - Maximize: x0+x1 - Subject to constraints - """ - ret = 'A MIP_Problem\n' - if self.optimization_mode() == 'maximization': - ret += 'Maximize' - else: - ret += 'Minimize' - ret += ': ' + str(self.objective_function()) + '\n' - ret += 'Subject to constraints\n' - - return ret - - def __cinit__(self, PPL_dimension_type dim = 0, *args): - """ - Constructor - - TESTS:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: MIP_Problem(0) - A MIP_Problem - Maximize: 0 - Subject to constraints - - Check that :trac:`19903` is fixed:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert(x + y <= 2) - sage: _ = MIP_Problem(2, cs, 0) - sage: _ = MIP_Problem(2, cs, x) - sage: _ = MIP_Problem(2, None, None) - Traceback (most recent call last): - ... - TypeError: Cannot convert NoneType to sage.libs.ppl.Constraint_System - sage: _ = MIP_Problem(2, cs, 'hey') - Traceback (most recent call last): - ... - TypeError: unable to convert 'hey' to an integer - sage: _ = MIP_Problem(2, cs, x, 'middle') - Traceback (most recent call last): - ... - ValueError: unknown mode 'middle' - """ - cdef Constraint_System cs - cdef Linear_Expression obj - cdef PPL_Optimization_Mode mode - - if not args: - self.thisptr = new PPL_MIP_Problem(dim) - - elif 2 <= len(args) <= 3: - cs = args[0] - try: - obj = args[1] - except TypeError: - obj = Linear_Expression(args[1]) - - mode = MAXIMIZATION - if len(args) == 3: - if args[2] == 'maximization': - mode = MAXIMIZATION - elif args[2] == 'minimization': - mode = MINIMIZATION - else: - raise ValueError('unknown mode {!r}'.format(args[2])) - self.thisptr = new PPL_MIP_Problem(dim, cs.thisptr[0], obj.thisptr[0], mode) - - else: - raise ValueError('cannot initialize from {!r}'.format(args)) - - def __dealloc__(self): - """ - The Cython destructor - """ - del self.thisptr - - def optimization_mode(self): - """ - Return the optimization mode used in the MIP_Problem. - - It will return "maximization" if the MIP_Problem was set - to MAXIMIZATION mode, and "minimization" otherwise. - - EXAMPLES:: - - sage: from sage.libs.ppl import MIP_Problem - sage: m = MIP_Problem() - sage: m.optimization_mode() - 'maximization' - """ - if self.thisptr.optimization_mode() == MAXIMIZATION: - return "maximization" - elif self.thisptr.optimization_mode() == MINIMIZATION: - return "minimization" - - def optimal_value(self): - """ - Return the optimal value of the MIP_Problem. ValueError thrown if self does not - have an optimizing point, i.e., if the MIP problem is unbounded or not satisfiable. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0 ) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.optimal_value() - 10/3 - sage: cs = Constraint_System() - sage: cs.insert( x >= 0 ) - sage: m = MIP_Problem(1, cs, x + x ) - sage: m.optimal_value() - Traceback (most recent call last): - ... - ValueError: PPL::MIP_Problem::optimizing_point(): - *this does not have an optimizing point. - """ - cdef PPL_Coefficient sup_n - cdef PPL_Coefficient sup_d - - sig_on() - try: - self.thisptr.optimal_value(sup_n, sup_d) - finally: - sig_off() - - cdef Integer Int_sup_n = Integer(0) - mpz_set(Int_sup_n.value, sup_n.get_mpz_t()) - cdef Integer Int_sup_d = Integer(0) - mpz_set(Int_sup_d.value, sup_d.get_mpz_t()) - - return Rational((Int_sup_n, Int_sup_d)) - - def space_dimension(self): - """ - Return the space dimension of the MIP_Problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.space_dimension() - 2 - """ - return self.thisptr.space_dimension() - - def objective_function(self): - """ - Return the optimal value of the MIP_Problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.objective_function() - x0+x1 - """ - rc = Linear_Expression() - rc.thisptr[0] = self.thisptr.objective_function() - return rc - - def clear(self): - """ - Reset the MIP_Problem to be equal to the trivial MIP_Problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.objective_function() - x0+x1 - sage: m.clear() - sage: m.objective_function() - 0 - """ - self.thisptr.clear() - - def add_space_dimensions_and_embed(self, PPL_dimension_type m): - """ - Adds m new space dimensions and embeds the old MIP problem in the new vector space. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2, cs, x + y) - sage: m.add_space_dimensions_and_embed(5) - sage: m.space_dimension() - 7 - """ - self.assert_mutable("The MIP_Problem is not mutable!") - sig_on() - self.thisptr.add_space_dimensions_and_embed(m) - sig_off() - - def _add_rational_constraint(self, e, denom, lower, upper): - """ - Helper function for adding constraints: add the constraint - ``lower <= e/denom <= upper``. - - INPUT: - - - ``e`` -- a linear expression (type ``Linear_Expression``) - - - ``denom`` -- a positive integer - - - ``lower``, ``upper`` -- a rational number or ``None``, where - ``None`` means that there is no constraint - - TESTS: - - Create a linear system with only equalities as constraints:: - - sage: p = MixedIntegerLinearProgram(solver="PPL") - sage: x = p.new_variable(nonnegative=False) - sage: n = 40 - sage: v = random_vector(QQ, n) - sage: M = random_matrix(QQ, 2*n, n) - sage: for j in range(2*n): # indirect doctest - ....: lhs = p.sum(M[j,i]*x[i] for i in range(n)) - ....: rhs = M.row(j).inner_product(v) - ....: p.add_constraint(lhs == rhs) - sage: p.solve() # long time - 0 - - """ - cdef Rational rhs - - if lower == upper: - if lower is not None: - rhs = Rational(lower * denom) - self.add_constraint(e * rhs.denominator() == rhs.numerator()) - else: - if lower is not None: - rhs = Rational(lower * denom) - self.add_constraint(e * rhs.denominator() >= rhs.numerator()) - if upper is not None: - rhs = Rational(upper * denom) - self.add_constraint(e * rhs.denominator() <= rhs.numerator()) - - def add_constraint(self, Constraint c): - """ - Adds a copy of constraint c to the MIP problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.set_objective_function(x + y) - sage: m.optimal_value() - 10/3 - - TESTS:: - - sage: z = Variable(2) - sage: m.add_constraint(z >= -3) - Traceback (most recent call last): - ... - ValueError: PPL::MIP_Problem::add_constraint(c): - c.space_dimension() == 3 exceeds this->space_dimension == 2. - """ - self.assert_mutable("The MIP_Problem is not mutable!") - sig_on() - try: - self.thisptr.add_constraint(c.thisptr[0]) - finally: - sig_off() - - def add_constraints(self, Constraint_System cs): - """ - Adds a copy of the constraints in cs to the MIP problem. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2) - sage: m.set_objective_function(x + y) - sage: m.add_constraints(cs) - sage: m.optimal_value() - 10/3 - - TESTS:: - - sage: p = Variable(9) - sage: cs.insert(p >= -3) - sage: m.add_constraints(cs) - Traceback (most recent call last): - ... - ValueError: PPL::MIP_Problem::add_constraints(cs): - cs.space_dimension() == 10 exceeds this->space_dimension() == 2. - """ - self.assert_mutable("The MIP_Problem is not mutable!") - sig_on() - try: - self.thisptr.add_constraints(cs.thisptr[0]) - finally: - sig_off() - - def add_to_integer_space_dimensions(self, Variables_Set i_vars): - """ - Sets the variables whose indexes are in set `i_vars` to be integer space dimensions. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Variables_Set, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0) - sage: cs.insert( y >= 0 ) - sage: cs.insert( 3 * x + 5 * y <= 10 ) - sage: m = MIP_Problem(2) - sage: m.set_objective_function(x + y) - sage: m.add_constraints(cs) - sage: i_vars = Variables_Set(x, y) - sage: m.add_to_integer_space_dimensions(i_vars) - sage: m.optimal_value() - 3 - """ - self.assert_mutable("The MIP_Problem is not mutable!") - sig_on() - try: - self.thisptr.add_to_integer_space_dimensions(i_vars.thisptr[0]) - finally: - sig_off() - - def set_objective_function(self, Linear_Expression obj): - """ - Sets the objective function to obj. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.set_objective_function(x + y) - sage: m.optimal_value() - 10/3 - - TESTS:: - - sage: z = Variable(2) - sage: m.set_objective_function(x + y + z) - Traceback (most recent call last): - ... - ValueError: PPL::MIP_Problem::set_objective_function(obj): - obj.space_dimension() == 3 exceeds this->space_dimension == 2. - """ - self.assert_mutable("The MIP_Problem is not mutable!") - self.thisptr.set_objective_function(obj.thisptr[0]) - - def set_optimization_mode(self, mode): - """ - Sets the optimization mode to mode. - - EXAMPLES:: - - sage: from sage.libs.ppl import MIP_Problem - sage: m = MIP_Problem() - sage: m.optimization_mode() - 'maximization' - sage: m.set_optimization_mode('minimization') - sage: m.optimization_mode() - 'minimization' - - TESTS:: - - sage: m.set_optimization_mode('max') - Traceback (most recent call last): - ... - ValueError: Unknown value: mode=max. - """ - if mode == 'minimization': - self.thisptr.set_optimization_mode(MINIMIZATION) - elif mode == 'maximization': - self.thisptr.set_optimization_mode(MAXIMIZATION) - else: - raise ValueError('Unknown value: mode={}.'.format(mode)) - - def is_satisfiable(self): - """ - Check if the MIP_Problem is satisfiable - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.is_satisfiable() - True - """ - ret = self.thisptr.is_satisfiable() - - return ret - - def evaluate_objective_function(self, Generator evaluating_point): - """ - Return the result of evaluating the objective function on evaluating_point. ValueError thrown - if self and evaluating_point are dimension-incompatible or if the generator evaluating_point is not a point. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem, Generator - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.set_objective_function(x + y) - sage: g = Generator.point(5 * x - 2 * y, 7) - sage: m.evaluate_objective_function(g) - 3/7 - sage: z = Variable(2) - sage: g = Generator.point(5 * x - 2 * z, 7) - sage: m.evaluate_objective_function(g) - Traceback (most recent call last): - ... - ValueError: PPL::MIP_Problem::evaluate_objective_function(p, n, d): - *this and p are dimension incompatible. - """ - cdef PPL_Coefficient sup_n - cdef PPL_Coefficient sup_d - - sig_on() - try: - self.thisptr.evaluate_objective_function(evaluating_point.thisptr[0], sup_n, sup_d) - finally: - sig_off() - - cdef Integer Int_sup_n = Integer(0) - mpz_set(Int_sup_n.value, sup_n.get_mpz_t()) - cdef Integer Int_sup_d = Integer(0) - mpz_set(Int_sup_d.value, sup_d.get_mpz_t()) - - return Rational((Int_sup_n, Int_sup_d)) - - def solve(self): - """ - Optimizes the MIP_Problem - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.set_objective_function(x + y) - sage: m.solve() - {'status': 'optimized'} - """ - sig_on() - try: - tmp = self.thisptr.solve() - finally: - sig_off() - if tmp == UNFEASIBLE_MIP_PROBLEM: - return { 'status':'unfeasible' } - elif tmp == UNBOUNDED_MIP_PROBLEM: - return { 'status':'unbounded' } - else: - return { 'status':'optimized' } - - def optimizing_point(self): - """ - Returns an optimal point for the MIP_Problem, if it exists. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.add_constraint(y >= 0) - sage: m.add_constraint(3 * x + 5 * y <= 10) - sage: m.set_objective_function(x + y) - sage: m.optimizing_point() - point(10/3, 0/3) - """ - cdef PPL_Generator *g - sig_on() - try: - g = new_MIP_optimizing_point(self.thisptr[0]) - finally: - sig_off() - return _wrap_Generator(g[0]) - - def OK(self): - """ - Check if all the invariants are satisfied. - - OUTPUT: - - ``True`` if and only if ``self`` satisfies all the invariants. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem - sage: x = Variable(0) - sage: y = Variable(1) - sage: m = MIP_Problem() - sage: m.add_space_dimensions_and_embed(2) - sage: m.add_constraint(x >= 0) - sage: m.OK() - True - """ - return self.thisptr.OK() - -#################################################### -### Polyhedron ##################################### -#################################################### -cdef class Polyhedron(_mutable_or_immutable): - r""" - Wrapper for PPL's ``Polyhedron`` class. - - An object of the class Polyhedron represents a convex polyhedron - in the vector space. - - A polyhedron can be specified as either a finite system of - constraints or a finite system of generators (see Section - Representations of Convex Polyhedra) and it is always possible to - obtain either representation. That is, if we know the system of - constraints, we can obtain from this the system of generators that - define the same polyhedron and vice versa. These systems can - contain redundant members: in this case we say that they are not - in the minimal form. - - INPUT/OUTPUT: - - This is an abstract base for :class:`C_Polyhedron` and - :class:`NNC_Polyhedron`. You cannot instantiate this class. - """ - - cdef PPL_Polyhedron *thisptr - - - def __init__(self): - r""" - The Python constructor. - - See also :class:`C_Polyhedron` and - :class:`NNC_Polyhedron`. You must not instantiate - :class:`Polyhedron` objects. - - TESTS:: - - sage: from sage.libs.ppl import Polyhedron - sage: Polyhedron() - Traceback (most recent call last): - ... - NotImplementedError: The Polyhedron class is abstract, you must not instantiate it. - """ - raise NotImplementedError('The Polyhedron class is abstract, you must not instantiate it.') - - - def _repr_(self): - """ - Return a string representation. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: C_Polyhedron( 5*x-2*y >= x+y-1 )._repr_() - 'A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray, 1 line' - - Special cases:: - - sage: C_Polyhedron(3, 'empty')._repr_() - 'The empty polyhedron in QQ^3' - sage: C_Polyhedron(3, 'universe')._repr_() - 'The space-filling polyhedron in QQ^3' - """ - dim = self.affine_dimension() - ambient_dim = self.space_dimension() - gs = self.minimized_generators() - n_points = 0 - n_closure_points = 0 - n_lines = 0 - n_rays = 0 - for g in gs: - if g.is_line(): - n_lines += 1 - elif g.is_ray(): - n_rays += 1 - elif g.is_point(): - n_points += 1 - elif g.is_closure_point(): - n_closure_points += 1 - else: - assert False - if self.is_empty(): - return 'The empty polyhedron in QQ^'+str(ambient_dim) - if self.is_universe(): - return 'The space-filling polyhedron in QQ^'+str(ambient_dim) - desc = 'A ' + str(dim) + '-dimensional polyhedron' - desc += ' in QQ' - desc += '^' + str(ambient_dim) - desc += ' defined as the convex hull of ' - first = True - if n_points>0: - if not first: - desc += ", " - first = False - desc += str(n_points) - if n_points==1: desc += ' point' - else: desc += ' points' - if n_closure_points>0: - if not first: - desc += ", " - first = False - desc += str(n_closure_points) - if n_closure_points==1: desc += ' closure_point' - else: desc += ' closure_points' - if n_rays>0: - if not first: - desc += ", " - first = False - desc += str(n_rays) - if n_rays==1: desc += ' ray' - else: desc += ' rays' - if n_lines>0: - if not first: - desc += ", " - first = False - desc += repr(n_lines) - if n_lines==1: desc +=' line' - else: desc +=' lines' - return desc; - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( 5*x-2*y >= x+y-1 ) - sage: p.space_dimension() - 2 - """ - return self.thisptr.space_dimension() - - - def affine_dimension(self): - r""" - Return the affine dimension of ``self``. - - OUTPUT: - - An integer. Returns 0 if ``self`` is empty. Otherwise, returns - the affine dimension of ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( 5*x-2*y == x+y-1 ) - sage: p.affine_dimension() - 1 - """ - sig_on() - cdef size_t dim = self.thisptr.affine_dimension() - sig_off() - return dim - - - def constraints(self): - r""" - Returns the system of constraints. - - See also :meth:`minimized_constraints`. - - OUTPUT: - - A :class:`Constraint_System`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( y>=0 ) - sage: p.add_constraint( x>=0 ) - sage: p.add_constraint( x+y>=0 ) - sage: p.constraints() - Constraint_System {x1>=0, x0>=0, x0+x1>=0} - sage: p.minimized_constraints() - Constraint_System {x1>=0, x0>=0} - """ - sig_on() - cdef PPL_Constraint_System cs = self.thisptr.constraints() - sig_off() - return _wrap_Constraint_System(cs) - - - def minimized_constraints(self): - r""" - Returns the minimized system of constraints. - - See also :meth:`constraints`. - - OUTPUT: - - A :class:`Constraint_System`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( y>=0 ) - sage: p.add_constraint( x>=0 ) - sage: p.add_constraint( x+y>=0 ) - sage: p.constraints() - Constraint_System {x1>=0, x0>=0, x0+x1>=0} - sage: p.minimized_constraints() - Constraint_System {x1>=0, x0>=0} - """ - sig_on() - cdef PPL_Constraint_System cs = self.thisptr.minimized_constraints() - sig_off() - return _wrap_Constraint_System(cs) - - - def generators(self): - r""" - Returns the system of generators. - - See also :meth:`minimized_generators`. - - OUTPUT: - - A :class:`Generator_System`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron(3,'empty') - sage: p.add_generator( point(-x-y) ) - sage: p.add_generator( point(0) ) - sage: p.add_generator( point(+x+y) ) - sage: p.generators() - Generator_System {point(-1/1, -1/1, 0/1), point(0/1, 0/1, 0/1), point(1/1, 1/1, 0/1)} - sage: p.minimized_generators() - Generator_System {point(-1/1, -1/1, 0/1), point(1/1, 1/1, 0/1)} - """ - sig_on() - cdef PPL_Generator_System gs = self.thisptr.generators() - sig_off() - return _wrap_Generator_System(gs) - - - def minimized_generators(self): - r""" - Returns the minimized system of generators. - - See also :meth:`generators`. - - OUTPUT: - - A :class:`Generator_System`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron(3,'empty') - sage: p.add_generator( point(-x-y) ) - sage: p.add_generator( point(0) ) - sage: p.add_generator( point(+x+y) ) - sage: p.generators() - Generator_System {point(-1/1, -1/1, 0/1), point(0/1, 0/1, 0/1), point(1/1, 1/1, 0/1)} - sage: p.minimized_generators() - Generator_System {point(-1/1, -1/1, 0/1), point(1/1, 1/1, 0/1)} - """ - sig_on() - cdef PPL_Generator_System gs = self.thisptr.minimized_generators() - sig_off() - return _wrap_Generator_System(gs) - - - cdef _relation_with_generator(Polyhedron self, Generator g): - r""" - Helper method for :meth:`relation_with`. - """ - rel = Poly_Gen_Relation(True) - try: - sig_on() - try: - rel.thisptr = new_relation_with(self.thisptr[0], g.thisptr[0]) - finally: - sig_off() - except BaseException: - # rel.thisptr must be set to something valid or rel.__dealloc__() will segfault - rel.thisptr = new PPL_Poly_Gen_Relation(PPL_Poly_Gen_Relation_nothing()) - raise - return rel - - - cdef _relation_with_constraint(Polyhedron self, Constraint c): - r""" - Helper method for :meth:`relation_with`. - """ - rel = Poly_Con_Relation(True) - try: - sig_on() - try: - rel.thisptr = new_relation_with(self.thisptr[0], c.thisptr[0]) - finally: - sig_off() - except BaseException: - # rel.thisptr must be set to something valid or rel.__dealloc__() will segfault - rel.thisptr = new PPL_Poly_Con_Relation(PPL_Poly_Con_Relation_nothing()) - raise - return rel - - - def relation_with(self, arg): - r""" - Return the relations holding between the polyhedron ``self`` - and the generator or constraint ``arg``. - - INPUT: - - - ``arg`` -- a :class:`Generator` or a :class:`Constraint`. - - OUTPUT: - - A :class:`Poly_Gen_Relation` or a :class:`Poly_Con_Relation` - according to the type of the input. - - Raises ``ValueError`` if ``self`` and the generator/constraint - ``arg`` are dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point, ray, Poly_Con_Relation - sage: x = Variable(0); y = Variable(1) - sage: p = C_Polyhedron(2, 'empty') - sage: p.add_generator( point(1*x+0*y) ) - sage: p.add_generator( point(0*x+1*y) ) - sage: p.minimized_constraints() - Constraint_System {x0+x1-1==0, -x1+1>=0, x1>=0} - sage: p.relation_with( point(1*x+1*y) ) - nothing - sage: p.relation_with( point(1*x+1*y, 2) ) - subsumes - sage: p.relation_with( x+y==-1 ) - is_disjoint - sage: p.relation_with( x==y ) - strictly_intersects - sage: p.relation_with( x+y<=1 ) - is_included, saturates - sage: p.relation_with( x+y<1 ) - is_disjoint, saturates - - In a Sage program you will usually use :meth:`relation_with` - together with :meth:`~sage.libs.ppl.Poly_Gen_Relation.implies` - or :meth:`~sage.libs.ppl.Poly_Con_Relation.implies`, for - example:: - - sage: p.relation_with( x+y<1 ).implies(Poly_Con_Relation.saturates()) - True - - You can only get relations with dimension-compatible - generators or constraints:: - - sage: z = Variable(2) - sage: p.relation_with( point(x+y+z) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::relation_with(g): - this->space_dimension() == 2, g.space_dimension() == 3. - sage: p.relation_with( z>0 ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::relation_with(c): - this->space_dimension() == 2, c.space_dimension() == 3. - """ - if isinstance(arg, Generator): - return self._relation_with_generator(arg) - if isinstance(arg, Constraint): - return self._relation_with_constraint(arg) - else: - raise TypeError('Argument must be Generator or a Constraint') - - - def is_empty(self): - """ - Test if ``self`` is an empty polyhedron. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import C_Polyhedron - sage: C_Polyhedron(3, 'empty').is_empty() - True - sage: C_Polyhedron(3, 'universe').is_empty() - False - """ - sig_on() - cdef bint result = self.thisptr.is_empty() - sig_off() - return result - - - def is_universe(self): - """ - Test if ``self`` is a universe (space-filling) polyhedron. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import C_Polyhedron - sage: C_Polyhedron(3, 'empty').is_universe() - False - sage: C_Polyhedron(3, 'universe').is_universe() - True - """ - sig_on() - cdef bint result = self.thisptr.is_universe() - sig_off() - return result - - - def is_topologically_closed(self): - """ - Tests if ``self`` is topologically closed. - - OUTPUT: - - Returns ``True`` if and only if ``self`` is a topologically - closed subset of the ambient vector space. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron - sage: x = Variable(0); y = Variable(1) - sage: C_Polyhedron(3, 'universe').is_topologically_closed() - True - sage: C_Polyhedron( x>=1 ).is_topologically_closed() - True - sage: NNC_Polyhedron( x>1 ).is_topologically_closed() - False - """ - sig_on() - cdef bint result = self.thisptr.is_topologically_closed() - sig_off() - return result - - - def is_disjoint_from(self, Polyhedron y): - r""" - Tests whether ``self`` and ``y`` are disjoint. - - INPUT: - - - ``y`` -- a :class:`Polyhedron`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` and ``y`` - are disjoint. - - Rayises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron - sage: x = Variable(0); y = Variable(1) - sage: C_Polyhedron(x<=0).is_disjoint_from( C_Polyhedron(x>=1) ) - True - - This is not allowed:: - - sage: x = Variable(0); y = Variable(1) - sage: poly_1d = C_Polyhedron(x<=0) - sage: poly_2d = C_Polyhedron(x+0*y>=1) - sage: poly_1d.is_disjoint_from(poly_2d) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::intersection_assign(y): - this->space_dimension() == 1, y.space_dimension() == 2. - - Nor is this:: - - sage: x = Variable(0); y = Variable(1) - sage: c_poly = C_Polyhedron( x<=0 ) - sage: nnc_poly = NNC_Polyhedron( x >0 ) - sage: c_poly.is_disjoint_from(nnc_poly) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::intersection_assign(y): - y is a NNC_Polyhedron. - sage: NNC_Polyhedron(c_poly).is_disjoint_from(nnc_poly) - True - """ - cdef bint result - sig_on() - try: - result = self.thisptr.is_disjoint_from(y.thisptr[0]) - finally: - sig_off() - return result - - - def is_discrete(self): - r""" - Test whether ``self`` is discrete. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is discrete. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point, ray - sage: x = Variable(0); y = Variable(1) - sage: p = C_Polyhedron( point(1*x+2*y) ) - sage: p.is_discrete() - True - sage: p.add_generator( point(x) ) - sage: p.is_discrete() - False - """ - sig_on() - cdef bint result = self.thisptr.is_discrete() - sig_off() - return result - - - def is_bounded(self): - r""" - Test whether ``self`` is bounded. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is a bounded polyhedron. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, NNC_Polyhedron, point, closure_point, ray - sage: x = Variable(0) - sage: p = NNC_Polyhedron( point(0*x) ) - sage: p.add_generator( closure_point(1*x) ) - sage: p.is_bounded() - True - sage: p.add_generator( ray(1*x) ) - sage: p.is_bounded() - False - """ - sig_on() - cdef bint result = self.thisptr.is_bounded() - sig_off() - return result - - - def contains_integer_point(self): - r""" - Test whether ``self`` contains an integer point. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` contains an - integer point. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, NNC_Polyhedron - sage: x = Variable(0) - sage: p = NNC_Polyhedron(x>0) - sage: p.add_constraint(x<1) - sage: p.contains_integer_point() - False - sage: p.topological_closure_assign() - sage: p.contains_integer_point() - True - """ - sig_on() - cdef bint result = self.thisptr.contains_integer_point() - sig_off() - return result - - - def constrains(self, Variable var): - r""" - Test whether ``var`` is constrained in ``self``. - - INPUT: - - - ``var`` -- a :class:`Variable`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``var`` is - constrained in ``self``. - - Raises a ``ValueError`` if ``var`` is not a space dimension of - ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: p = C_Polyhedron(1, 'universe') - sage: p.constrains(x) - False - sage: p = C_Polyhedron(x>=0) - sage: p.constrains(x) - True - sage: y = Variable(1) - sage: p.constrains(y) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::constrains(v): - this->space_dimension() == 1, v.space_dimension() == 2. - """ - cdef bint result - sig_on() - try: - result = self.thisptr.constrains(var.thisptr[0]) - finally: - sig_off() - return result - - - def bounds_from_above(self, Linear_Expression expr): - r""" - Test whether the ``expr`` is bounded from above. - - INPUT: - - - ``expr`` -- a :class:`Linear_Expression` - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``expr`` is bounded - from above in ``self``. - - Raises a ``ValueError`` if ``expr`` and ``this`` are - dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, Linear_Expression - sage: x = Variable(0); y = Variable(1) - sage: p = C_Polyhedron(y<=0) - sage: p.bounds_from_above(x+1) - False - sage: p.bounds_from_above(Linear_Expression(y)) - True - sage: p = C_Polyhedron(x<=0) - sage: p.bounds_from_above(y+1) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::bounds_from_above(e): - this->space_dimension() == 1, e.space_dimension() == 2. - """ - cdef bint result - sig_on() - try: - result = self.thisptr.bounds_from_above(expr.thisptr[0]) - finally: - sig_off() - return result - - - def bounds_from_below(self, Linear_Expression expr): - r""" - Test whether the ``expr`` is bounded from above. - - INPUT: - - - ``expr`` -- a :class:`Linear_Expression` - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``expr`` is bounded - from above in ``self``. - - Raises a ``ValueError`` if ``expr`` and ``this`` are - dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, Linear_Expression - sage: x = Variable(0); y = Variable(1) - sage: p = C_Polyhedron(y>=0) - sage: p.bounds_from_below(x+1) - False - sage: p.bounds_from_below(Linear_Expression(y)) - True - sage: p = C_Polyhedron(x<=0) - sage: p.bounds_from_below(y+1) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::bounds_from_below(e): - this->space_dimension() == 1, e.space_dimension() == 2. - """ - cdef bint result - sig_on() - try: - result = self.thisptr.bounds_from_below(expr.thisptr[0]) - finally: - sig_off() - return result - - - def maximize(self, Linear_Expression expr): - r""" - Maximize ``expr``. - - INPUT: - - - ``expr`` -- a :class:`Linear_Expression`. - - OUTPUT: - - A dictionary with the following keyword:value pair: - - * ``'bounded'``: Boolean. Whether the linear expression - ``expr`` is bounded from above on ``self``. - - If ``expr`` is bounded from above, the following additional - keyword:value pairs are set to provide information about the - supremum: - - * ``'sup_n'``: Integer. The numerator of the supremum value. - - * ``'sup_d'``: Non-zero integer. The denominator of the supremum - value. - - * ``'maximum'``: Boolean. ``True`` if and only if the supremum - is also the maximum value. - - * ``'generator'``: a :class:`Generator`. A point or closure - point where expr reaches its supremum value. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron, Constraint_System, Linear_Expression - sage: x = Variable(0); y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x>=0 ) - sage: cs.insert( y>=0 ) - sage: cs.insert( 3*x+5*y<=10 ) - sage: p = C_Polyhedron(cs) - sage: p.maximize( x+y ) - {'bounded': True, - 'generator': point(10/3, 0/3), - 'maximum': True, - 'sup_d': 3, - 'sup_n': 10} - - Unbounded case:: - - sage: cs = Constraint_System() - sage: cs.insert( x>0 ) - sage: p = NNC_Polyhedron(cs) - sage: p.maximize( +x ) - {'bounded': False} - sage: p.maximize( -x ) - {'bounded': True, - 'generator': closure_point(0/1), - 'maximum': False, - 'sup_d': 1, - 'sup_n': 0} - """ - cdef PPL_Coefficient sup_n - cdef PPL_Coefficient sup_d - cdef Generator g = point() - cdef cppbool maximum - sig_on() - rc = self.thisptr.maximize(expr.thisptr[0], sup_n, sup_d, maximum, g.thisptr[0]) - sig_off() - - cdef Integer Int_sup_n = Integer(0) - mpz_set(Int_sup_n.value, sup_n.get_mpz_t()) - cdef Integer Int_sup_d = Integer(0) - mpz_set(Int_sup_d.value, sup_d.get_mpz_t()) - - if rc: - return { 'bounded':True, 'sup_n':Int_sup_n, 'sup_d':Int_sup_d, 'maximum':maximum, 'generator':g } - else: - return { 'bounded':False } - - - def minimize(self, Linear_Expression expr): - r""" - Minimize ``expr``. - - INPUT: - - - ``expr`` -- a :class:`Linear_Expression`. - - OUTPUT: - - A dictionary with the following keyword:value pair: - - * ``'bounded'``: Boolean. Whether the linear expression - ``expr`` is bounded from below on ``self``. - - If ``expr`` is bounded from below, the following additional - keyword:value pairs are set to provide information about the - infimum: - - * ``'inf_n'``: Integer. The numerator of the infimum value. - - * ``'inf_d'``: Non-zero integer. The denominator of the infimum - value. - - * ``'minimum'``: Boolean. ``True`` if and only if the infimum - is also the minimum value. - - * ``'generator'``: a :class:`Generator`. A point or closure - point where expr reaches its infimum value. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron, Constraint_System, Linear_Expression - sage: x = Variable(0); y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x>=0 ) - sage: cs.insert( y>=0 ) - sage: cs.insert( 3*x+5*y<=10 ) - sage: p = C_Polyhedron(cs) - sage: p.minimize( x+y ) - {'bounded': True, - 'generator': point(0/1, 0/1), - 'inf_d': 1, - 'inf_n': 0, - 'minimum': True} - - Unbounded case:: - - sage: cs = Constraint_System() - sage: cs.insert( x>0 ) - sage: p = NNC_Polyhedron(cs) - sage: p.minimize( +x ) - {'bounded': True, - 'generator': closure_point(0/1), - 'inf_d': 1, - 'inf_n': 0, - 'minimum': False} - sage: p.minimize( -x ) - {'bounded': False} - """ - cdef PPL_Coefficient inf_n - cdef PPL_Coefficient inf_d - cdef Generator g = point() - cdef cppbool minimum - sig_on() - rc = self.thisptr.minimize(expr.thisptr[0], inf_n, inf_d, minimum, g.thisptr[0]) - sig_off() - - cdef Integer Int_inf_n = Integer(0) - mpz_set(Int_inf_n.value, inf_n.get_mpz_t()) - cdef Integer Int_inf_d = Integer(0) - mpz_set(Int_inf_d.value, inf_d.get_mpz_t()) - - if rc: - return { 'bounded':True, 'inf_n':Int_inf_n, 'inf_d':Int_inf_d, 'minimum':minimum, 'generator':g } - else: - return { 'bounded':False } - - - def contains(self, Polyhedron y): - r""" - Test whether ``self`` contains ``y``. - - INPUT: - - - ``y`` -- a :class:`Polyhedron`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` contains ``y``. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p0 = C_Polyhedron( x>=0 ) - sage: p1 = C_Polyhedron( x>=1 ) - sage: p0.contains(p1) - True - sage: p1.contains(p0) - False - - Errors are raised if the dimension or topology is not compatible:: - - sage: p0.contains(C_Polyhedron(y>=0)) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::contains(y): - this->space_dimension() == 1, y.space_dimension() == 2. - sage: p0.contains(NNC_Polyhedron(x>0)) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::contains(y): - y is a NNC_Polyhedron. - """ - cdef bint result - sig_on() - try: - result = self.thisptr.contains(y.thisptr[0]) - finally: - sig_off() - return result - - - def strictly_contains(self, Polyhedron y): - r""" - Test whether ``self`` strictly contains ``y``. - - INPUT: - - - ``y`` -- a :class:`Polyhedron`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` contains - ``y`` and ``self`` does not equal ``y``. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p0 = C_Polyhedron( x>=0 ) - sage: p1 = C_Polyhedron( x>=1 ) - sage: p0.strictly_contains(p1) - True - sage: p1.strictly_contains(p0) - False - - Errors are raised if the dimension or topology is not compatible:: - - sage: p0.strictly_contains(C_Polyhedron(y>=0)) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::contains(y): - this->space_dimension() == 1, y.space_dimension() == 2. - sage: p0.strictly_contains(NNC_Polyhedron(x>0)) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::contains(y): - y is a NNC_Polyhedron. - """ - cdef bint result - sig_on() - try: - result = self.thisptr.strictly_contains(y.thisptr[0]) - finally: - sig_off() - return result - - - def add_constraint(self, Constraint c): - r""" - Add a constraint to the polyhedron. - - Adds a copy of constraint ``c`` to the system of constraints - of ``self``, without minimizing the result. - - See alse :meth:`add_constraints`. - - INPUT: - - - ``c`` -- the :class:`Constraint` that will be added to the - system of constraints of ``self``. - - OUTPUT: - - This method modifies the polyhedron ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and the constraint ``c`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( y>=0 ) - sage: p.add_constraint( x>=0 ) - - We just added a 1-d constraint to a 2-d polyhedron, this is - fine. The other way is not:: - - sage: p = C_Polyhedron( x>=0 ) - sage: p.add_constraint( y>=0 ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_constraint(c): - this->space_dimension() == 1, c.space_dimension() == 2. - - The constraint must also be topology-compatible, that is, - :class:`C_Polyhedron` only allows non-strict inequalities:: - - sage: p = C_Polyhedron( x>=0 ) - sage: p.add_constraint( x< 1 ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_constraint(c): - c is a strict inequality. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.add_constraint(c.thisptr[0]) - finally: - sig_off() - - - def add_generator(self, Generator g): - r""" - Add a generator to the polyhedron. - - Adds a copy of constraint ``c`` to the system of generators - of ``self``, without minimizing the result. - - INPUT: - - - ``g`` -- the :class:`Generator` that will be added to the - system of Generators of ``self``. - - OUTPUT: - - This method modifies the polyhedron ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and the generator ``g`` - are topology-incompatible or dimension-incompatible, or if - ``self`` is an empty polyhedron and ``g`` is not a point. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point, closure_point, ray - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron(1, 'empty') - sage: p.add_generator( point(0*x) ) - - We just added a 1-d generator to a 2-d polyhedron, this is - fine. The other way is not:: - - sage: p = C_Polyhedron(1, 'empty') - sage: p.add_generator( point(0*y) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_generator(g): - this->space_dimension() == 1, g.space_dimension() == 2. - - The constraint must also be topology-compatible, that is, - :class:`C_Polyhedron` does not allow :func:`closure_point` - generators:: - - sage: p = C_Polyhedron( point(0*x+0*y) ) - sage: p.add_generator( closure_point(0*x) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_generator(g): - g is a closure point. - - Finally, ever non-empty polyhedron must have at least one - point generator:: - - sage: p = C_Polyhedron(3, 'empty') - sage: p.add_generator( ray(x) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_generator(g): - *this is an empty polyhedron and g is not a point. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.add_generator(g.thisptr[0]) - finally: - sig_off() - - - def add_constraints(self, Constraint_System cs): - r""" - Add constraints to the polyhedron. - - Adds a copy of constraints in ``cs`` to the system of constraints - of ``self``, without minimizing the result. - - See alse :meth:`add_constraint`. - - INPUT: - - - ``cs`` -- the :class:`Constraint_System` that will be added - to the system of constraints of ``self``. - - OUTPUT: - - This method modifies the polyhedron ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and the constraints in - ``cs`` are topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, Constraint_System - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert(x>=0) - sage: cs.insert(y>=0) - sage: p = C_Polyhedron( y<=1 ) - sage: p.add_constraints(cs) - - We just added a 1-d constraint to a 2-d polyhedron, this is - fine. The other way is not:: - - sage: p = C_Polyhedron( x<=1 ) - sage: p.add_constraints(cs) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_recycled_constraints(cs): - this->space_dimension() == 1, cs.space_dimension() == 2. - - The constraints must also be topology-compatible, that is, - :class:`C_Polyhedron` only allows non-strict inequalities:: - - sage: p = C_Polyhedron( x>=0 ) - sage: p.add_constraints( Constraint_System(x<0) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_recycled_constraints(cs): - cs contains strict inequalities. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.add_constraints(cs.thisptr[0]) - finally: - sig_off() - - - def add_generators(self, Generator_System gs): - r""" - Add generators to the polyhedron. - - Adds a copy of the generators in ``gs`` to the system of - generators of ``self``, without minimizing the result. - - See alse :meth:`add_generator`. - - INPUT: - - - ``gs`` -- the :class:`Generator_System` that will be added - to the system of constraints of ``self``. - - OUTPUT: - - This method modifies the polyhedron ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and one of the generators - in ``gs`` are topology-incompatible or dimension-incompatible, - or if ``self`` is an empty polyhedron and ``gs`` does not - contain a point. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, Generator_System, point, ray, closure_point - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System() - sage: gs.insert(point(0*x+0*y)) - sage: gs.insert(point(1*x+1*y)) - sage: p = C_Polyhedron(2, 'empty') - sage: p.add_generators(gs) - - We just added a 1-d constraint to a 2-d polyhedron, this is - fine. The other way is not:: - - sage: p = C_Polyhedron(1, 'empty') - sage: p.add_generators(gs) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_recycled_generators(gs): - this->space_dimension() == 1, gs.space_dimension() == 2. - - The constraints must also be topology-compatible, that is, - :class:`C_Polyhedron` does not allow :func:`closure_point` - generators:: - - sage: p = C_Polyhedron( point(0*x+0*y) ) - sage: p.add_generators( Generator_System(closure_point(x) )) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_recycled_generators(gs): - gs contains closure points. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.add_generators(gs.thisptr[0]) - finally: - sig_off() - - - def unconstrain(self, Variable var): - r""" - Compute the cylindrification of ``self`` with respect to space - dimension ``var``. - - INPUT: - - - ``var`` -- a :class:`Variable`. The space dimension that - will be unconstrained. Exceptions: - - OUTPUT: - - This method assigns the cylindrification to ``self`` and does - not return anything. - - Raises a ``ValueError`` if ``var`` is not a space dimension of - ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( point(x+y) ); p - A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point - sage: p.unconstrain(x); p - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 line - sage: z = Variable(2) - sage: p.unconstrain(z) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::unconstrain(var): - this->space_dimension() == 2, required space dimension == 3. - """ - sig_on() - try: - self.thisptr.unconstrain(var.thisptr[0]) - finally: - sig_off() - - - def intersection_assign(self, Polyhedron y): - r""" - Assign to ``self`` the intersection of ``self`` and ``y``. - - INPUT: - - - ``y`` -- a :class:`Polyhedron` - - OUTPUT: - - This method assigns the intersection to ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( 1*x+0*y >= 0 ) - sage: p.intersection_assign( C_Polyhedron(y>=0) ) - sage: p.constraints() - Constraint_System {x0>=0, x1>=0} - sage: z = Variable(2) - sage: p.intersection_assign( C_Polyhedron(z>=0) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::intersection_assign(y): - this->space_dimension() == 2, y.space_dimension() == 3. - sage: p.intersection_assign( NNC_Polyhedron(x+y<1) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::intersection_assign(y): - y is a NNC_Polyhedron. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.intersection_assign(y.thisptr[0]) - finally: - sig_off() - - - def poly_hull_assign(self, Polyhedron y): - r""" - Assign to ``self`` the poly-hull of ``self`` and ``y``. - - For any pair of NNC polyhedra `P_1` and `P_2`, the convex - polyhedral hull (or poly-hull) of is the smallest NNC - polyhedron that includes both `P_1` and `P_2`. The poly-hull - of any pair of closed polyhedra in is also closed. - - INPUT: - - - ``y`` -- a :class:`Polyhedron` - - OUTPUT: - - This method assigns the poly-hull to ``self`` and does not - return anything. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point, NNC_Polyhedron - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron( point(1*x+0*y) ) - sage: p.poly_hull_assign(C_Polyhedron( point(0*x+1*y) )) - sage: p.generators() - Generator_System {point(0/1, 1/1), point(1/1, 0/1)} - - ``self`` and ``y`` must be dimension- and topology-compatible, - or an exception is raised:: - - sage: z = Variable(2) - sage: p.poly_hull_assign( C_Polyhedron(z>=0) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::poly_hull_assign(y): - this->space_dimension() == 2, y.space_dimension() == 3. - sage: p.poly_hull_assign( NNC_Polyhedron(x+y<1) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::poly_hull_assign(y): - y is a NNC_Polyhedron. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.poly_hull_assign(y.thisptr[0]) - finally: - sig_off() - - - upper_bound_assign = poly_hull_assign - - - def poly_difference_assign(self, Polyhedron y): - r""" - Assign to ``self`` the poly-difference of ``self`` and ``y``. - - For any pair of NNC polyhedra `P_1` and `P_2` the convex - polyhedral difference (or poly-difference) of `P_1` and `P_2` - is defined as the smallest convex polyhedron containing the - set-theoretic difference `P_1\setminus P_2` of `P_1` and - `P_2`. - - In general, even if `P_1` and `P_2` are topologically closed - polyhedra, their poly-difference may be a convex polyhedron - that is not topologically closed. For this reason, when - computing the poly-difference of two :class:`C_Polyhedron`, - the library will enforce the topological closure of the - result. - - INPUT: - - - ``y`` -- a :class:`Polyhedron` - - OUTPUT: - - This method assigns the poly-difference to ``self`` and does - not return anything. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or dimension-incompatible. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point, closure_point, NNC_Polyhedron - sage: x = Variable(0) - sage: p = NNC_Polyhedron( point(0*x) ) - sage: p.add_generator( point(1*x) ) - sage: p.poly_difference_assign(NNC_Polyhedron( point(0*x) )) - sage: p.minimized_constraints() - Constraint_System {-x0+1>=0, x0>0} - - The poly-difference of :class:`C_polyhedron` is really its closure:: - - sage: p = C_Polyhedron( point(0*x) ) - sage: p.add_generator( point(1*x) ) - sage: p.poly_difference_assign(C_Polyhedron( point(0*x) )) - sage: p.minimized_constraints() - Constraint_System {x0>=0, -x0+1>=0} - - ``self`` and ``y`` must be dimension- and topology-compatible, - or an exception is raised:: - - sage: y = Variable(1) - sage: p.poly_difference_assign( C_Polyhedron(y>=0) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::poly_difference_assign(y): - this->space_dimension() == 1, y.space_dimension() == 2. - sage: p.poly_difference_assign( NNC_Polyhedron(x+y<1) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::poly_difference_assign(y): - y is a NNC_Polyhedron. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.poly_difference_assign(y.thisptr[0]) - finally: - sig_off() - - - difference_assign = poly_difference_assign - - - def drop_some_non_integer_points(self): - r""" - Possibly tighten ``self`` by dropping some points with - non-integer coordinates. - - The modified polyhedron satisfies: - - * it is (not necessarily strictly) contained in the original - polyhedron. - - * integral vertices (generating points with integer - coordinates) of the original polyhedron are not removed. - - .. NOTE:: - - The modified polyhedron is not necessarily a lattice - polyhedron; Some vertices will, in general, still be - rational. Lattice points interior to the polyhedron may be - lost in the process. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, NNC_Polyhedron, Constraint_System - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x>=0 ) - sage: cs.insert( y>=0 ) - sage: cs.insert( 3*x+2*y<5 ) - sage: p = NNC_Polyhedron(cs) - sage: p.minimized_generators() - Generator_System {point(0/1, 0/1), closure_point(0/2, 5/2), closure_point(5/3, 0/3)} - sage: p.drop_some_non_integer_points() - sage: p.minimized_generators() - Generator_System {point(0/1, 0/1), point(0/1, 2/1), point(4/3, 0/3)} - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - self.thisptr.drop_some_non_integer_points() - sig_off() - - - def topological_closure_assign(self): - r""" - Assign to ``self`` its topological closure. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, NNC_Polyhedron - sage: x = Variable(0) - sage: p = NNC_Polyhedron(x>0) - sage: p.is_topologically_closed() - False - sage: p.topological_closure_assign() - sage: p.is_topologically_closed() - True - sage: p.minimized_constraints() - Constraint_System {x0>=0} - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - self.thisptr.topological_closure_assign() - sig_off() - - - def add_space_dimensions_and_embed(self, m): - r""" - Add ``m`` new space dimensions and embed ``self`` in the new - vector space. - - The new space dimensions will be those having the highest - indexes in the new polyhedron, which is characterized by a - system of constraints in which the variables running through - the new dimensions are not constrained. For instance, when - starting from the polyhedron `P` and adding a third space - dimension, the result will be the polyhedron - - .. MATH:: - - \Big\{ - (x,y,z)^T \in \RR^3 - \Big| - (x,y)^T \in P - \Big\} - - INPUT: - - - ``m`` -- integer. - - OUTPUT: - - This method assigns the embedded polyhedron to ``self`` and - does not return anything. - - Raises a ``ValueError`` if adding ``m`` new space dimensions - would cause the vector space to exceed dimension - ``self.max_space_dimension()``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point - sage: x = Variable(0) - sage: p = C_Polyhedron( point(3*x) ) - sage: p.add_space_dimensions_and_embed(1) - sage: p.minimized_generators() - Generator_System {line(0, 1), point(3/1, 0/1)} - sage: p.add_space_dimensions_and_embed( p.max_space_dimension() ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_space_dimensions_and_embed(m): - adding m new space dimensions exceeds the maximum allowed space dimension. - """ - self.assert_mutable('The Polyhedron is not mutable!') - m = int(m) - sig_on() - try: - self.thisptr.add_space_dimensions_and_embed(m) - finally: - sig_off() - - - def add_space_dimensions_and_project(self, m): - r""" - Add ``m`` new space dimensions and embed ``self`` in the new - vector space. - - The new space dimensions will be those having the highest - indexes in the new polyhedron, which is characterized by a - system of constraints in which the variables running through - the new dimensions are all constrained to be equal to `0`. - For instance, when starting from the polyhedron `P` and adding - a third space dimension, the result will be the polyhedron - - .. MATH:: - - \Big\{ - (x,y,0)^T \in \RR^3 - \Big| - (x,y)^T \in P - \Big\} - - INPUT: - - - ``m`` -- integer. - - OUTPUT: - - This method assigns the projected polyhedron to ``self`` and - does not return anything. - - Raises a ``ValueError`` if adding ``m`` new space dimensions - would cause the vector space to exceed dimension - ``self.max_space_dimension()``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, point - sage: x = Variable(0) - sage: p = C_Polyhedron( point(3*x) ) - sage: p.add_space_dimensions_and_project(1) - sage: p.minimized_generators() - Generator_System {point(3/1, 0/1)} - sage: p.add_space_dimensions_and_project( p.max_space_dimension() ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::add_space_dimensions_and_project(m): - adding m new space dimensions exceeds the maximum allowed space dimension. - """ - self.assert_mutable('The Polyhedron is not mutable!') - m = int(m) - sig_on() - try: - self.thisptr.add_space_dimensions_and_project(m) - finally: - sig_off() - - - def concatenate_assign(self, Polyhedron y): - r""" - Assign to ``self`` the concatenation of ``self`` and ``y``. - - This function returns the Cartesian product of ``self`` and - ``y``. - - Viewing a polyhedron as a set of tuples (its points), it is - sometimes useful to consider the set of tuples obtained by - concatenating an ordered pair of polyhedra. Formally, the - concatenation of the polyhedra `P` and `Q` (taken in this - order) is the polyhedron such that - - .. MATH:: - - R = - \Big\{ - (x_0,\dots,x_{n-1},y_0,\dots,y_{m-1})^T \in \RR^{n+m} - \Big| - (x_0,\dots,x_{n-1})^T \in P - ,~ - (y_0,\dots,y_{m-1})^T \in Q - \Big\} - - Another way of seeing it is as follows: first embed polyhedron - `P` into a vector space of dimension `n+m` and then add a - suitably renamed-apart version of the constraints defining - `Q`. - - INPUT: - - - ``m`` -- integer. - - OUTPUT: - - This method assigns the concatenated polyhedron to ``self`` and - does not return anything. - - Raises a ``ValueError`` if ``self`` and ``y`` are - topology-incompatible or if adding ``y.space_dimension()`` new - space dimensions would cause the vector space to exceed - dimension ``self.max_space_dimension()``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron, NNC_Polyhedron, point - sage: x = Variable(0) - sage: p1 = C_Polyhedron( point(1*x) ) - sage: p2 = C_Polyhedron( point(2*x) ) - sage: p1.concatenate_assign(p2) - sage: p1.minimized_generators() - Generator_System {point(1/1, 2/1)} - - The polyhedra must be topology-compatible and not exceed the - maximum space dimension:: - - sage: p1.concatenate_assign( NNC_Polyhedron(1, 'universe') ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::concatenate_assign(y): - y is a NNC_Polyhedron. - sage: p1.concatenate_assign( C_Polyhedron(p1.max_space_dimension(), 'empty') ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::concatenate_assign(y): - concatenation exceeds the maximum allowed space dimension. - """ - self.assert_mutable('The Polyhedron is not mutable!') - sig_on() - try: - self.thisptr.concatenate_assign(y.thisptr[0]) - finally: - sig_off() - - - def remove_higher_space_dimensions(self, new_dimension): - r""" - Remove the higher dimensions of the vector space so that the - resulting space will have dimension ``new_dimension``. - - OUTPUT: - - This method modifies ``self`` and does not return anything. - - Raises a ``ValueError`` if ``new_dimensions`` is greater than - the space dimension of ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import C_Polyhedron, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: p = C_Polyhedron(3*x+0*y==2) - sage: p.remove_higher_space_dimensions(1) - sage: p.minimized_constraints() - Constraint_System {3*x0-2==0} - sage: p.remove_higher_space_dimensions(2) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::remove_higher_space_dimensions(nd): - this->space_dimension() == 1, required space dimension == 2. - """ - self.assert_mutable('The Polyhedron is not mutable!') - new_dimension = int(new_dimension) - sig_on() - try: - self.thisptr.remove_higher_space_dimensions(new_dimension) - finally: - sig_off() - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import C_Polyhedron, Variable\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'p = C_Polyhedron(3*x+2*y==1)\n' - sage: sage_cmd += 'p.minimized_generators()\n' - sage: sage_cmd += 'p.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - space_dim 2 - -ZE -EM +CM +GM +CS +GS -CP -GP -SC +SG - con_sys (up-to-date) - topology NECESSARILY_CLOSED - 2 x 2 SPARSE (sorted) - index_first_pending 2 - size 3 -1 3 2 = (C) - size 3 1 0 0 >= (C) - - gen_sys (up-to-date) - topology NECESSARILY_CLOSED - 2 x 2 DENSE (not_sorted) - index_first_pending 2 - size 3 0 2 -3 L (C) - size 3 2 0 1 P (C) - - sat_c - 0 x 0 - - sat_g - 2 x 2 - 0 0 - 0 1 - sage: print(err) # long time py3 - space_dim 2 - -ZE -EM +CM +GM +CS +GS -CP -GP -SC +SG - con_sys (up-to-date) - topology NECESSARILY_CLOSED - 2 x 2 SPARSE (sorted) - index_first_pending 2 - size 3 -1 3 2 = (C) - size 3 1 0 0 >= (C) - - gen_sys (up-to-date) - topology NECESSARILY_CLOSED - 2 x 2 DENSE (not_sorted) - index_first_pending 2 - size 3 0 2 -3 L (C) - size 3 2 0 1 P (C) - - sat_c - 0 x 0 - - sat_g - 2 x 2 - 0 0 - 0 1 - - /...: DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - sig_on() - self.thisptr.ascii_dump() - sig_off() - - - def max_space_dimension(self): - r""" - Return the maximum space dimension all kinds of Polyhedron can handle. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import C_Polyhedron - sage: C_Polyhedron(1, 'empty').max_space_dimension() # random output - 1152921504606846974 - sage: C_Polyhedron(1, 'empty').max_space_dimension() - 357913940 # 32-bit - 1152921504606846974 # 64-bit - """ - return self.thisptr.max_space_dimension() - - - def OK(self, check_non_empty=False): - """ - Check if all the invariants are satisfied. - - The check is performed so as to intrude as little as - possible. If the library has been compiled with run-time - assertions enabled, error messages are written on std::cerr in - case invariants are violated. This is useful for the purpose - of debugging the library. - - INPUT: - - - ``check_not_empty`` -- boolean. ``True`` if and only if, in - addition to checking the invariants, ``self`` must be - checked to be not empty. - - OUTPUT: - - ``True`` if and only if ``self`` satisfies all the invariants - and either ``check_not_empty`` is ``False`` or ``self`` is not - empty. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: e = 3*x+2*y+1 - sage: e.OK() - True - """ - sig_on() - cdef bint result = self.thisptr.OK() - sig_off() - return result - - - def __hash__(self): - r""" - Hash value for polyhedra. - - TESTS:: - - sage: from sage.libs.ppl import Constraint_System, Variable, C_Polyhedron - sage: x = Variable(0) - sage: p = C_Polyhedron( 5*x >= 3 ) - sage: p.set_immutable() - sage: hash(p) - 1 - - sage: y = Variable(1) - sage: cs = Constraint_System() - sage: cs.insert( x >= 0 ) - sage: cs.insert( y >= 0 ) - sage: p = C_Polyhedron(cs) - sage: p.set_immutable() - sage: hash(p) - 2 - - sage: hash(C_Polyhedron(x >= 0)) - Traceback (most recent call last): - ... - TypeError: mutable polyhedra are unhashable - """ - if self.is_mutable(): - raise TypeError("mutable polyhedra are unhashable") - # TODO: the hash code from PPL looks like being the dimension! - return self.thisptr[0].hash_code() - - def __richcmp__(Polyhedron lhs, Polyhedron rhs, op): - r""" - Comparison for polyhedra. - - INPUT: - - - ``lhs``, ``rhs`` -- :class:`Polyhedron`. - - - ``op`` -- integer. The comparison operation to be performed. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, C_Polyhedron - sage: x = Variable(0) - sage: C_Polyhedron(x>=0) > C_Polyhedron(x>=1) # indirect doctest - True - """ - cdef result - sig_on() - if op == Py_LT: - result = rhs.strictly_contains(lhs) - elif op == Py_LE: - result = rhs.contains(lhs) - elif op == Py_EQ: - result = (lhs.thisptr[0] == rhs.thisptr[0]) - elif op == Py_GT: - result = lhs.strictly_contains(rhs) - elif op == Py_GE: - result = lhs.contains(rhs) - elif op == Py_NE: - result = (lhs.thisptr[0] != rhs.thisptr[0]) - else: - assert False # unreachable - sig_off() - return result - - - -#################################################### -### C_Polyhedron ################################### -#################################################### -cdef class C_Polyhedron(Polyhedron): - r""" - Wrapper for PPL's ``C_Polyhedron`` class. - - An object of the class :class:`C_Polyhedron` represents a - topologically closed convex polyhedron in the vector space. See - :class:`NNC_Polyhedron` for more general (not necessarily closed) - polyhedra. - - When building a closed polyhedron starting from a system of - constraints, an exception is thrown if the system contains a - strict inequality constraint. Similarly, an exception is thrown - when building a closed polyhedron starting from a system of - generators containing a closure point. - - INPUT: - - - ``arg`` -- the defining data of the polyhedron. Any one of the - following is accepted: - - * A non-negative integer. Depending on ``degenerate_element``, - either the space-filling or the empty polytope in the given - dimension ``arg`` is constructed. - - * A :class:`Constraint_System`. - - * A :class:`Generator_System`. - - * A single :class:`Constraint`. - - * A single :class:`Generator`. - - * A :class:`C_Polyhedron`. - - - ``degenerate_element`` -- string, either ``'universe'`` or - ``'empty'``. Only used if ``arg`` is an integer. - - OUTPUT: - - A :class:`C_Polyhedron`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint, Constraint_System, Generator, Generator_System, Variable, C_Polyhedron, point, ray - sage: x = Variable(0) - sage: y = Variable(1) - sage: C_Polyhedron( 5*x-2*y >= x+y-1 ) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray, 1 line - sage: cs = Constraint_System() - sage: cs.insert( x >= 0 ) - sage: cs.insert( y >= 0 ) - sage: C_Polyhedron(cs) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 2 rays - sage: C_Polyhedron( point(x+y) ) - A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point - sage: gs = Generator_System() - sage: gs.insert( point(-x-y) ) - sage: gs.insert( ray(x) ) - sage: C_Polyhedron(gs) - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray - - The empty and universe polyhedra are constructed like this:: - - sage: C_Polyhedron(3, 'empty') - The empty polyhedron in QQ^3 - sage: C_Polyhedron(3, 'empty').constraints() - Constraint_System {-1==0} - sage: C_Polyhedron(3, 'universe') - The space-filling polyhedron in QQ^3 - sage: C_Polyhedron(3, 'universe').constraints() - Constraint_System {} - - Note that, by convention, the generator system of a polyhedron is - either empty or contains at least one point. In particular, if you - define a polyhedron via a non-empty :class:`Generator_System` it - must contain a point (at any position). If you start with a single - generator, this generator must be a point:: - - sage: C_Polyhedron( ray(x) ) - Traceback (most recent call last): - ... - ValueError: PPL::C_Polyhedron::C_Polyhedron(gs): - *this is an empty polyhedron and - the non-empty generator system gs contains no points. - """ - - - def __cinit__(self, arg, degenerate_element='universe'): - """ - The Cython constructor. - - See :class:`C_Polyhedron` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import C_Polyhedron - sage: C_Polyhedron(3, 'empty') # indirect doctest - The empty polyhedron in QQ^3 - """ - if isinstance(arg, C_Polyhedron): - ph = arg - self.thisptr = new PPL_C_Polyhedron(ph.thisptr[0]) - return - if isinstance(arg, Generator): - arg = Generator_System(arg) - if isinstance(arg, Constraint): - arg = Constraint_System(arg) - if isinstance(arg, Generator_System): - gs = arg - self.thisptr = new PPL_C_Polyhedron(gs.thisptr[0]) - return - if isinstance(arg, Constraint_System): - cs = arg - self.thisptr = new PPL_C_Polyhedron(cs.thisptr[0]) - return - try: - dim = int(arg) - assert dim>=0 - except ValueError: - raise ValueError('Cannot initialize C_Polyhedron with '+str(arg)+'.') - degenerate_element = degenerate_element.lower() - if degenerate_element=='universe': - self.thisptr = new PPL_C_Polyhedron(dim, UNIVERSE) - return - elif degenerate_element=='empty': - self.thisptr = new PPL_C_Polyhedron(dim, EMPTY) - return - else: - raise ValueError('Unknown value: degenerate_element='+str(degenerate_element)+'.') - - - def __init__(self, *args): - """ - The Python destructor. - - See :class:`C_Polyhedron` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import C_Polyhedron - sage: C_Polyhedron(3, 'empty') # indirect doctest - The empty polyhedron in QQ^3 - """ - # override Polyhedron.__init__ - pass - - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def __reduce__(self): - """ - Pickle object - - TESTS:: - - sage: from sage.libs.ppl import C_Polyhedron, Variable - sage: P = C_Polyhedron(3, 'empty') - sage: loads(dumps(P)) - The empty polyhedron in QQ^3 - - sage: Q = C_Polyhedron(5, 'universe') - sage: loads(dumps(Q)) - The space-filling polyhedron in QQ^5 - - sage: x = Variable(0) - sage: y = Variable(1) - sage: H = C_Polyhedron( 5*x-2*y >= x+y-1 ) - sage: loads(dumps(H)) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 ray, 1 line - """ - if self.is_empty(): - return (C_Polyhedron, (self.space_dimension(), 'empty')) - elif self.is_universe(): - return (C_Polyhedron, (self.space_dimension(), 'universe')) - else: - return (C_Polyhedron, (self.generators(),)) - - -#################################################### -### NNC_Polyhedron ################################# -#################################################### -cdef class NNC_Polyhedron(Polyhedron): - r""" - Wrapper for PPL's ``NNC_Polyhedron`` class. - - An object of the class ``NNC_Polyhedron`` represents a not - necessarily closed (NNC) convex polyhedron in the vector space. - - Note: Since NNC polyhedra are a generalization of closed - polyhedra, any object of the class :class:`C_Polyhedron` can be - (explicitly) converted into an object of the class - :class:`NNC_Polyhedron`. The reason for defining two different - classes is that objects of the class :class:`C_Polyhedron` are - characterized by a more efficient implementation, requiring less - time and memory resources. - - INPUT: - - - ``arg`` -- the defining data of the polyhedron. Any one of the - following is accepted: - - * An non-negative integer. Depending on ``degenerate_element``, - either the space-filling or the empty polytope in the given - dimension ``arg`` is constructed. - - * A :class:`Constraint_System`. - - * A :class:`Generator_System`. - - * A single :class:`Constraint`. - - * A single :class:`Generator`. - - * A :class:`NNC_Polyhedron`. - - * A :class:`C_Polyhedron`. - - - ``degenerate_element`` -- string, either ``'universe'`` or - ``'empty'``. Only used if ``arg`` is an integer. - - OUTPUT: - - A :class:`C_Polyhedron`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint, Constraint_System, Generator, Generator_System, Variable, NNC_Polyhedron, point, ray, closure_point - sage: x = Variable(0) - sage: y = Variable(1) - sage: NNC_Polyhedron( 5*x-2*y > x+y-1 ) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 1 ray, 1 line - sage: cs = Constraint_System() - sage: cs.insert( x > 0 ) - sage: cs.insert( y > 0 ) - sage: NNC_Polyhedron(cs) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 2 rays - sage: NNC_Polyhedron( point(x+y) ) - A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point - sage: gs = Generator_System() - sage: gs.insert( point(-y) ) - sage: gs.insert( closure_point(-x-y) ) - sage: gs.insert( ray(x) ) - sage: p = NNC_Polyhedron(gs); p - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, 1 closure_point, 1 ray - sage: p.minimized_constraints() - Constraint_System {x1+1==0, x0+1>0} - - Note that, by convention, every polyhedron must contain a point:: - - sage: NNC_Polyhedron( closure_point(x+y) ) - Traceback (most recent call last): - ... - ValueError: PPL::NNC_Polyhedron::NNC_Polyhedron(gs): - *this is an empty polyhedron and - the non-empty generator system gs contains no points. - """ - - - def __cinit__(self, arg, degenerate_element='universe'): - """ - The Cython constructor. - - See :class:`NNC_Polyhedron` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import NNC_Polyhedron - sage: NNC_Polyhedron(3, 'empty') # indirect doctest - The empty polyhedron in QQ^3 - """ - if isinstance(arg, NNC_Polyhedron): - p_nnc = arg - self.thisptr = new PPL_NNC_Polyhedron(p_nnc.thisptr[0]) - return - if isinstance(arg, C_Polyhedron): - p_c = arg - self.thisptr = new PPL_NNC_Polyhedron(p_c.thisptr[0]) - return - if isinstance(arg, Generator): - arg = Generator_System(arg) - if isinstance(arg, Constraint): - arg = Constraint_System(arg) - if isinstance(arg, Generator_System): - gs = arg - self.thisptr = new PPL_NNC_Polyhedron(gs.thisptr[0]) - return - if isinstance(arg, Constraint_System): - cs = arg - self.thisptr = new PPL_NNC_Polyhedron(cs.thisptr[0]) - return - try: - dim = int(arg) - assert dim>=0 - except ValueError: - raise ValueError('Cannot initialize NNC_Polyhedron with '+str(arg)+'.') - degenerate_element = degenerate_element.lower() - if degenerate_element=='universe': - self.thisptr = new PPL_NNC_Polyhedron(dim, UNIVERSE) - return - elif degenerate_element=='empty': - self.thisptr = new PPL_NNC_Polyhedron(dim, EMPTY) - return - else: - raise ValueError('Unknown value: degenerate_element='+str(degenerate_element)+'.') - - - def __init__(self, *args): - """ - The Python destructor. - - See :class:`NNC_Polyhedron` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import NNC_Polyhedron - sage: NNC_Polyhedron(3, 'empty') # indirect doctest - The empty polyhedron in QQ^3 - """ - # override Polyhedron.__init__ - pass - - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def __reduce__(self): - """ - Pickle object - - TESTS:: - - sage: from sage.libs.ppl import NNC_Polyhedron, Variable - sage: P = NNC_Polyhedron(3, 'empty') - sage: loads(dumps(P)) - The empty polyhedron in QQ^3 - - sage: Q = NNC_Polyhedron(5, 'universe') - sage: loads(dumps(Q)) - The space-filling polyhedron in QQ^5 - - sage: x = Variable(0) - sage: y = Variable(1) - sage: H = NNC_Polyhedron( 5*x-2*y > x+y-1 ) - sage: loads(dumps(H)) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 point, - 1 closure_point, 1 ray, 1 line - """ - if self.is_empty(): - return (NNC_Polyhedron, (self.space_dimension(), 'empty')) - elif self.is_universe(): - return (NNC_Polyhedron, (self.space_dimension(), 'universe')) - else: - return (NNC_Polyhedron, (self.generators(),)) - - -#################################################### -### Variable ####################################### -#################################################### -cdef class Variable(object): - r""" - Wrapper for PPL's ``Variable`` class. - - A dimension of the vector space. - - An object of the class Variable represents a dimension of the - space, that is one of the Cartesian axes. Variables are used as - basic blocks in order to build more complex linear - expressions. Each variable is identified by a non-negative - integer, representing the index of the corresponding Cartesian - axis (the first axis has index 0). The space dimension of a - variable is the dimension of the vector space made by all the - Cartesian axes having an index less than or equal to that of the - considered variable; thus, if a variable has index `i`, its space - dimension is `i+1`. - - INPUT: - - - ``i`` -- integer. The index of the axis. - - OUTPUT: - - A :class:`Variable` - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(123) - sage: x.id() - 123 - sage: x - x123 - - Note that the "meaning" of an object of the class Variable is - completely specified by the integer index provided to its - constructor: be careful not to be mislead by C++ language variable - names. For instance, in the following example the linear - expressions ``e1`` and ``e2`` are equivalent, since the two - variables ``x`` and ``z`` denote the same Cartesian axis:: - - sage: x = Variable(0) - sage: y = Variable(1) - sage: z = Variable(0) - sage: e1 = x + y; e1 - x0+x1 - sage: e2 = y + z; e2 - x0+x1 - sage: e1 - e2 - 0 - """ - - cdef PPL_Variable *thisptr - - - def __cinit__(self, PPL_dimension_type i): - """ - The Cython constructor. - - See :class:`Variable` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Variable - sage: Variable(123) # indirect doctest - x123 - """ - self.thisptr = new PPL_Variable(i) - - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def id(self): - """ - Return the index of the Cartesian axis associated to the variable. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(123) - sage: x.id() - 123 - """ - return self.thisptr.id() - - - def OK(self): - """ - Checks if all the invariants are satisfied. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: x.OK() - True - """ - return self.thisptr.OK() - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. The returned value is ``self.id()+1``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: x.space_dimension() - 1 - """ - return self.thisptr.space_dimension() - - - def __repr__(self): - """ - Return a string representation. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: x.__repr__() - 'x0' - """ - return 'x{0}'.format(self.id()) - - - def __add__(self, other): - r""" - Return the sum ``self`` + ``other``. - - INPUT: - - - ``self``, ``other`` -- anything convertible to - ``Linear_Expression``: An integer, a :class:`Variable`, or a - :class:`Linear_Expression`. - - OUTPUT: - - A :class:`Linear_Expression` representing ``self`` + ``other``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); y = Variable(1) - sage: x + 15 - x0+15 - sage: 15 + y - x1+15 - """ - return Linear_Expression(self)+Linear_Expression(other) - - - def __sub__(self, other): - r""" - Return the difference ``self`` - ``other``. - - INPUT: - - - ``self``, ``other`` -- anything convertible to - ``Linear_Expression``: An integer, a :class:`Variable`, or a - :class:`Linear_Expression`. - - OUTPUT: - - A :class:`Linear_Expression` representing ``self`` - ``other``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); y = Variable(1) - sage: x - 15 - x0-15 - sage: 15 - y - -x1+15 - """ - return Linear_Expression(self)-Linear_Expression(other) - - - def __mul__(self, other): - r""" - Return the product ``self`` * ``other``. - - INPUT: - - - ``self``, ``other`` -- One must be an integer, the other a - :class:`Variable`. - - OUTPUT: - - A :class:`Linear_Expression` representing ``self`` * ``other``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); y = Variable(1) - sage: x * 15 - 15*x0 - sage: 15 * y - 15*x1 - """ - if isinstance(self, Variable): - return Linear_Expression(self) * other - else: - return Linear_Expression(other) * self - - - def __pos__(self): - r""" - Return ``self`` as :class:`Linear_Expression` - - OUTPUT: - - The :class:`Linear_Expression` ``+self`` - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); x - x0 - sage: +x - x0 - """ - return Linear_Expression(self) - - - def __neg__(self): - r""" - Return -``self`` as :class:`Linear_Expression` - - OUTPUT: - - The :class:`Linear_Expression` ``-self`` - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); x - x0 - sage: -x - -x0 - """ - return Linear_Expression(self)*(-1) - - - def __richcmp__(self, other, op): - """ - Construct :class:`Constraint` from equalities or inequalities. - - INPUT: - - - ``self``, ``other`` -- anything convertible to a - :class:`Linear_Expression` - - - ``op`` -- the operation. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: x < y - -x0+x1>0 - sage: x <= 0 - -x0>=0 - sage: x == y-y - x0==0 - sage: x >= -2 - x0+2>=0 - sage: x > 0 - x0>0 - sage: 0 == 1 # watch out! - False - sage: 0*x == 1 - -1==0 - """ - return _make_Constraint_from_richcmp(self, other, op) - - -#################################################### -### Variables_Set ################################## -#################################################### - -cdef class Variables_Set(object): - r""" - Wrapper for PPL's ``Variables_Set`` class. - - A set of variables' indexes. - - EXAMPLES: - - Build the empty set of variable indexes:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: Variables_Set() - Variables_Set of cardinality 0 - - Build the singleton set of indexes containing the index of the variable:: - - sage: v123 = Variable(123) - sage: Variables_Set(v123) - Variables_Set of cardinality 1 - - Build the set of variables' indexes in the range from one variable to - another variable:: - - sage: v127 = Variable(127) - sage: Variables_Set(v123,v127) - Variables_Set of cardinality 5 - """ - - cdef PPL_Variables_Set *thisptr - - def __cinit__(self, *args): - """ - The Cython constructor. - - See :class:`Variables_Set` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: Variables_Set() - Variables_Set of cardinality 0 - """ - if not args: - self.thisptr = new PPL_Variables_Set() - elif len(args)==1: - v = args[0] - self.thisptr = new PPL_Variables_Set(v.thisptr[0]) - elif len(args)==2: - v = args[0] - w = args[1] - self.thisptr = new PPL_Variables_Set(v.thisptr[0], w.thisptr[0]) - - def __dealloc__(self): - """ - The Cython destructor - """ - del self.thisptr - - def OK(self): - """ - Checks if all the invariants are satisfied. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: v123 = Variable(123) - sage: S = Variables_Set(v123) - sage: S.OK() - True - """ - return self.thisptr.OK() - - def space_dimension(self): - r""" - Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in the set. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: v123 = Variable(123) - sage: S = Variables_Set(v123) - sage: S.space_dimension() - 124 - """ - return self.thisptr.space_dimension() - - def insert(self, Variable v): - r""" - Inserts the index of variable `v` into the set. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: S = Variables_Set() - sage: v123 = Variable(123) - sage: S.insert(v123) - sage: S.space_dimension() - 124 - """ - self.thisptr.insert(v.thisptr[0]) - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Variable, Variables_Set\n' - sage: sage_cmd += 'v123 = Variable(123)\n' - sage: sage_cmd += 'S = Variables_Set(v123)\n' - sage: sage_cmd += 'S.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - - variables( 1 ) - 123 - sage: print(err) # long time py3 - variables( 1 ) - 123 /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - def __repr__(self): - """ - Return a string representation. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Variables_Set - sage: S = Variables_Set() - sage: S.__repr__() - 'Variables_Set of cardinality 0' - """ - return 'Variables_Set of cardinality {}'.format(self.thisptr.size()) - -#################################################### -### Linear_Expression ############################## -#################################################### -cdef class Linear_Expression(object): - r""" - Wrapper for PPL's ``PPL_Linear_Expression`` class. - - INPUT: - - The constructor accepts zero, one, or two arguments. - - If there are two arguments ``Linear_Expression(a,b)``, they are - interpreted as - - - ``a`` -- an iterable of integer coefficients, for example a - list. - - - ``b`` -- an integer. The inhomogeneous term. - - A single argument ``Linear_Expression(arg)`` is interpreted as - - - ``arg`` -- something that determines a linear - expression. Possibilities are: - - * a :class:`Variable`: The linear expression given by that - variable. - - * a :class:`Linear_Expression`: The copy constructor. - - * an integer: Constructs the constant linear expression. - - No argument is the default constructor and returns the zero linear - expression. - - OUTPUT: - - A :class:`Linear_Expression` - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: Linear_Expression([1,2,3,4],5) - x0+2*x1+3*x2+4*x3+5 - sage: Linear_Expression(10) - 10 - sage: Linear_Expression() - 0 - sage: Linear_Expression(10).inhomogeneous_term() - 10 - sage: x = Variable(123) - sage: expr = x+1; expr - x123+1 - sage: expr.OK() - True - sage: expr.coefficient(x) - 1 - sage: expr.coefficient( Variable(124) ) - 0 - """ - - cdef PPL_Linear_Expression *thisptr - - - def __cinit__(self, *args): - """ - The Cython constructor. - - See :class:`Linear_Expression` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Linear_Expression - sage: Linear_Expression(10) # indirect doctest - 10 - """ - if len(args)==2: - a = args[0] - b = args[1] - ex = Linear_Expression(0) - for i in range(len(a)): - ex += Variable(i) * Integer(a[i]) - arg = ex + b - elif len(args)==1: - arg = args[0] - elif not args: - self.thisptr = new PPL_Linear_Expression() - return - else: - assert False, 'Cannot initialize with more than 2 arguments.' - - if isinstance(arg, Variable): - v = arg - self.thisptr = new PPL_Linear_Expression(v.thisptr[0]) - return - if isinstance(arg, Linear_Expression): - e = arg - self.thisptr = new PPL_Linear_Expression(e.thisptr[0]) - return - try: - c = Integer(arg) - self.thisptr = new PPL_Linear_Expression(PPL_Coefficient(c.value)) - return - except ValueError: - raise ValueError('Cannot initialize with {}.'.format(args)) - - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def space_dimension(self): - """ - Return the dimension of the vector space necessary for the - linear expression. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: ( x+y+1 ).space_dimension() - 2 - sage: ( x+y ).space_dimension() - 2 - sage: ( y+1 ).space_dimension() - 2 - sage: ( x +1 ).space_dimension() - 1 - sage: ( y+1-y ).space_dimension() - 2 - """ - return self.thisptr.space_dimension() - - - def coefficient(self, Variable v): - """ - Return the coefficient of the variable ``v``. - - INPUT: - - - ``v`` -- a :class:`Variable`. - - OUTPUT: - - An integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: e = 3*x+1 - sage: e.coefficient(x) - 3 - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.coefficient(v.thisptr[0]).get_mpz_t()) - return c - - - def coefficients(self): - """ - Return the coefficients of the linear expression. - - OUTPUT: - - A tuple of integers of length :meth:`space_dimension`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); y = Variable(1) - sage: e = 3*x+5*y+1 - sage: e.coefficients() - (3, 5) - """ - cdef int d = self.space_dimension() - cdef int i - cdef Integer c = Integer(0) - coeffs = [] - for i in range(0,d): - mpz_set(c.value, self.thisptr.coefficient(PPL_Variable(i)).get_mpz_t()) - coeffs.append(Integer(c)) - return tuple(coeffs) - - - def inhomogeneous_term(self): - """ - Return the inhomogeneous term of the linear expression. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: Linear_Expression(10).inhomogeneous_term() - 10 - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.inhomogeneous_term().get_mpz_t()) - return c - - - def is_zero(self): - """ - Test if ``self`` is the zero linear expression. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: Linear_Expression(0).is_zero() - True - sage: Linear_Expression(10).is_zero() - False - """ - return self.thisptr.is_zero() - - - def all_homogeneous_terms_are_zero(self): - """ - Test if ``self`` is a constant linear expression. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: Linear_Expression(10).all_homogeneous_terms_are_zero() - True - """ - return self.thisptr.all_homogeneous_terms_are_zero() - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Linear_Expression, Variable\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'e = 3*x+2*y+1\n' - sage: sage_cmd += 'e.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - size 3 1 3 2 - sage: print(err) # long time py3 - size 3 1 3 2/... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: e = 3*x+2*y+1 - sage: e.OK() - True - """ - return self.thisptr.OK() - - - def __repr__(self): - r""" - Return a string representation of the linear expression. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: x+1 - x0+1 - sage: x+1-x - 1 - sage: 2*x - 2*x0 - sage: x-x-1 - -1 - sage: x-x - 0 - """ - s = '' - first = True - for i in range(0,self.space_dimension()): - x = Variable(i) - coeff = self.coefficient(x) - if coeff==0: continue - if first and coeff==1: - s += '%r' % x - first = False - elif first and coeff==-1: - s += '-%r' % x - first = False - elif first and coeff!=1: - s += '%d*%r' % (coeff, x) - first = False - elif coeff==1: - s += '+%r' % x - elif coeff==-1: - s += '-%r' % x - else: - s += '%+d*%r' % (coeff, x) - inhomog = self.inhomogeneous_term() - if inhomog!=0: - if first: - s += '%d' % inhomog - first = False - else: - s += '%+d' % inhomog - if first: - s = '0' - return s - - - def __add__(self, other): - r""" - Add ``self`` and ``other``. - - INPUT: - - - ``self``, ``other`` -- anything that can be used to - construct a :class:`Linear_Expression`. One of them, not - necessarily ``self``, is guaranteed to be a - :class:`Linear_Expression`, otherwise Python would not - have called this method. - - OUTPUT: - - The sum as a :class:`Linear_Expression` - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: 9+x+y+(1+x)+y+y - 2*x0+3*x1+10 - """ - cdef Linear_Expression lhs = Linear_Expression(self) - cdef Linear_Expression rhs = Linear_Expression(other) - cdef Linear_Expression result = Linear_Expression() - result.thisptr[0] = lhs.thisptr[0] + rhs.thisptr[0] - return result - - - def __sub__(self, other): - r""" - Subtract ``other`` from ``self``. - - INPUT: - - - ``self``, ``other`` -- anything that can be used to - construct a :class:`Linear_Expression`. One of them, not - necessarily ``self``, is guaranteed to be a - :class:`Linear_Expression`, otherwise Python would not - have called this method. - - OUTPUT: - - The difference as a :class:`Linear_Expression` - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: 9-x-y-(1-x)-y-y - -3*x1+8 - """ - cdef Linear_Expression lhs = Linear_Expression(self) - cdef Linear_Expression rhs = Linear_Expression(other) - cdef Linear_Expression result = Linear_Expression() - result.thisptr[0] = lhs.thisptr[0] - rhs.thisptr[0] - return result - - - def __mul__(self, other): - r""" - Multiply ``self`` with ``other``. - - INPUT: - - - ``self``, ``other`` -- anything that can be used to - construct a :class:`Linear_Expression`. One of them, not - necessarily ``self``, is guaranteed to be a - :class:`Linear_Expression`, otherwise Python would not - have called this method. - - OUTPUT: - - The product as a :class:`Linear_Expression` - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: 8*(x+1) - 8*x0+8 - sage: y*8 - 8*x1 - """ - cdef Linear_Expression e - cdef Integer c - if isinstance(self, Linear_Expression): - e = self - c = Integer(other) - else: - e = other - c = Integer(self) - - cdef Linear_Expression result = Linear_Expression() - result.thisptr[0] = e.thisptr[0] * PPL_Coefficient(c.value) - return result - - - def __pos__(self): - """ - Return ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: +Linear_Expression(1) - 1 - sage: x = Variable(0) - sage: +(x+1) - x0+1 - """ - return self - - - def __neg__(self): - """ - Return the negative of ``self``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: -Linear_Expression(1) - -1 - sage: x = Variable(0) - sage: -(x+1) - -x0-1 - """ - return self*(-1) - - - def __richcmp__(self, other, int op): - """ - Construct :class:`Constraint`s - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: x+1 < y-2 - -x0+x1-3>0 - sage: x+1 <= y-2 - -x0+x1-3>=0 - sage: x+1 == y-2 - x0-x1+3==0 - sage: x+1 >= y-2 - x0-x1+3>=0 - sage: x+1 > y-2 - x0-x1+3>0 - """ - return _make_Constraint_from_richcmp(self, other, op) - - - def __reduce__(self): - """ - Pickle object - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression - sage: le = loads(dumps(Linear_Expression([1,2,3],4))) - sage: le.coefficients() == (1,2,3) - True - sage: le.inhomogeneous_term() == 4 - True - """ - return (Linear_Expression, (self.coefficients(), self.inhomogeneous_term())) - - -#################################################### -### Generator ###################################### -#################################################### -cdef _wrap_Generator(PPL_Generator generator): - """ - Wrap a C++ ``PPL_Generator`` into a Cython ``Generator``. - """ - cdef Generator g = Generator(True) - g.thisptr = new PPL_Generator(generator) - return g - - -#################################################### -# C++ static methods not supported -# Note that the PPL_Generator default constructor is private, hence we must return pointers -cdef extern from "ppl_shim.hh": - PPL_Generator* new_line(PPL_Linear_Expression &e) except +ValueError - PPL_Generator* new_ray(PPL_Linear_Expression &e) except +ValueError - PPL_Generator* new_point(PPL_Linear_Expression &e, PPL_Coefficient d) except +ValueError - PPL_Generator* new_closure_point(PPL_Linear_Expression &e, PPL_Coefficient d) except +ValueError - PPL_Generator* new_MIP_optimizing_point(PPL_MIP_Problem &problem) except +ValueError - - -#################################################### -cdef class Generator(object): - r""" - Wrapper for PPL's ``Generator`` class. - - An object of the class Generator is one of the following: - - * a line `\ell = (a_0, \dots, a_{n-1})^T` - - * a ray `r = (a_0, \dots, a_{n-1})^T` - - * a point `p = (\tfrac{a_0}{d}, \dots, \tfrac{a_{n-1}}{d})^T` - - * a closure point `c = (\tfrac{a_0}{d}, \dots, \tfrac{a_{n-1}}{d})^T` - - where `n` is the dimension of the space and, for points and - closure points, `d` is the divisor. - - INPUT/OUTPUT: - - Use the helper functions :func:`line`, :func:`ray`, :func:`point`, - and :func:`closure_point` to construct generators. Analogous class - methods are also available, see :meth:`Generator.line`, - :meth:`Generator.ray`, :meth:`Generator.point`, - :meth:`Generator.closure_point`. Do not attempt to construct - generators manually. - - .. NOTE:: - - The generators are constructed from linear expressions. The - inhomogeneous term is always silently discarded. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: Generator.line(5*x-2*y) - line(5, -2) - sage: Generator.ray(5*x-2*y) - ray(5, -2) - sage: Generator.point(5*x-2*y, 7) - point(5/7, -2/7) - sage: Generator.closure_point(5*x-2*y, 7) - closure_point(5/7, -2/7) - """ - - cdef PPL_Generator *thisptr - - - def __cinit__(self, do_not_construct_manually=False): - """ - The Cython constructor. - - See :class:`Variable` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Variable, line - sage: x = Variable(0) - sage: line(x) # indirect doctest - line(1) - """ - assert(do_not_construct_manually) - self.thisptr = NULL - - - def __dealloc__(self): - """ - The Cython destructor. - """ - assert self.thisptr!=NULL, 'Do not construct Generators manually!' - del self.thisptr - - - @classmethod - def line(cls, expression): - """ - Construct a line. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression` or something - convertible to it (:class:`Variable` or integer). - - OUTPUT: - - A new :class:`Generator` representing the line. - - Raises a ``ValueError` if the homogeneous part of - ``expression`` represents the origin of the vector space. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: y = Variable(1) - sage: Generator.line(2*y) - line(0, 1) - sage: Generator.line(y) - line(0, 1) - sage: Generator.line(1) - Traceback (most recent call last): - ... - ValueError: PPL::line(e): - e == 0, but the origin cannot be a line. - """ - cdef Linear_Expression e = Linear_Expression(expression) - # This does not work as Cython gets confused by the private default ctor - # return _wrap_Generator(PPL_line(e.thisptr[0])) - # workaround follows - cdef Generator g = Generator(True) - try: - g.thisptr = new_line(e.thisptr[0]) - except BaseException: - # g.thisptr must be set to something valid or g.__dealloc__() will segfault - g.thisptr = new_point(e.thisptr[0],PPL_Coefficient(1)) - raise - return g - - - @classmethod - def ray(cls, expression): - """ - Construct a ray. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression` or something - convertible to it (:class:`Variable` or integer). - - OUTPUT: - - A new :class:`Generator` representing the ray. - - Raises a ``ValueError` if the homogeneous part of - ``expression`` represents the origin of the vector space. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: y = Variable(1) - sage: Generator.ray(2*y) - ray(0, 1) - sage: Generator.ray(y) - ray(0, 1) - sage: Generator.ray(1) - Traceback (most recent call last): - ... - ValueError: PPL::ray(e): - e == 0, but the origin cannot be a ray. - """ - cdef Linear_Expression e = Linear_Expression(expression) - # This does not work as Cython gets confused by the private default ctor - # return _wrap_Generator(PPL_ray(e.thisptr[0])) - # workaround follows - cdef Generator g = Generator(True) - try: - g.thisptr = new_ray(e.thisptr[0]) - except BaseException: - # g.thisptr must be set to something valid or g.__dealloc__() will segfault - g.thisptr = new_point(e.thisptr[0],PPL_Coefficient(1)) - raise - return g - - - @classmethod - def point(cls, expression=0, divisor=1): - """ - Construct a point. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression` or something - convertible to it (:class:`Variable` or integer). - - - ``divisor`` -- an integer. - - OUTPUT: - - A new :class:`Generator` representing the point. - - Raises a ``ValueError` if ``divisor==0``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: y = Variable(1) - sage: Generator.point(2*y+7, 3) - point(0/3, 2/3) - sage: Generator.point(y+7, 3) - point(0/3, 1/3) - sage: Generator.point(7, 3) - point() - sage: Generator.point(0, 0) - Traceback (most recent call last): - ... - ValueError: PPL::point(e, d): - d == 0. - """ - cdef Linear_Expression e = Linear_Expression(expression) - cdef Integer d = Integer(divisor) - # This does not work as Cython gets confused by the private default ctor - # return _wrap_Generator(PPL_point(e.thisptr[0], PPL_Coefficient(d.value))) - # workaround follows - cdef Generator g = Generator(True) - try: - g.thisptr = new_point(e.thisptr[0], PPL_Coefficient(d.value)) - except BaseException: - # g.thisptr must be set to something valid or g.__dealloc__() will segfault - g.thisptr = new_point(e.thisptr[0],PPL_Coefficient(1)) - raise - return g - - - @classmethod - def closure_point(cls, expression=0, divisor=1): - """ - Construct a closure point. - - A closure point is a point of the topological closure of a - polyhedron that is not a point of the polyhedron itself. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression` or something - convertible to it (:class:`Variable` or integer). - - - ``divisor`` -- an integer. - - OUTPUT: - - A new :class:`Generator` representing the point. - - Raises a ``ValueError` if ``divisor==0``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: y = Variable(1) - sage: Generator.closure_point(2*y+7, 3) - closure_point(0/3, 2/3) - sage: Generator.closure_point(y+7, 3) - closure_point(0/3, 1/3) - sage: Generator.closure_point(7, 3) - closure_point() - sage: Generator.closure_point(0, 0) - Traceback (most recent call last): - ... - ValueError: PPL::closure_point(e, d): - d == 0. - """ - cdef Linear_Expression e = Linear_Expression(expression) - cdef Integer d = Integer(divisor) - # This does not work as Cython gets confused by the private default ctor - # return _wrap_Generator(PPL_closure_point(e.thisptr[0], PPL_Coefficient(d.value))) - # workaround follows - cdef Generator g = Generator(True) - try: - g.thisptr = new_closure_point(e.thisptr[0], PPL_Coefficient(d.value)) - except BaseException: - # g.thisptr must be set to something valid or g.__dealloc__() will segfault - g.thisptr = new_point(e.thisptr[0],PPL_Coefficient(1)) - raise - return g - - - def __repr__(self): - """ - Return a string representation of the generator. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: e = 2*x-y+5 - sage: Generator.line(e) - line(2, -1) - sage: Generator.ray(e) - ray(2, -1) - sage: Generator.point(e, 3) - point(2/3, -1/3) - sage: Generator.closure_point(e, 3) - closure_point(2/3, -1/3) - """ - t = self.type() - if t=='line': - s = 'line(' - div = '' - elif t=='ray': - s = 'ray(' - div = '' - elif t=='point': - s = 'point(' - div = '/'+str(self.divisor()) - elif t=='closure_point': - s = 'closure_point(' - div = '/'+str(self.divisor()) - else: - assert(False) - - for i in range(0,self.space_dimension()): - if i>0: - s += ', ' - s += str(self.coefficient(Variable(i))) + div - - s += ')' - return s - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: point(x).space_dimension() - 1 - sage: point(y).space_dimension() - 2 - """ - return self.thisptr.space_dimension() - - - def type(self): - r""" - Return the generator type of ``self``. - - OUTPUT: - - String. One of ``'line'``, ``'ray'``, ``'point'``, or - ``'closure_point'``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).type() - 'line' - sage: ray(x).type() - 'ray' - sage: point(x,2).type() - 'point' - sage: closure_point(x,2).type() - 'closure_point' - """ - t = self.thisptr.type() - if t==LINE: - return 'line' - elif t==RAY: - return 'ray' - elif t==POINT: - return 'point' - elif t==CLOSURE_POINT: - return 'closure_point' - assert False - - - def is_line(self): - r""" - Test whether ``self`` is a line. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).is_line() - True - sage: ray(x).is_line() - False - sage: point(x,2).is_line() - False - sage: closure_point(x,2).is_line() - False - """ - return self.thisptr.is_line() - - - def is_ray(self): - r""" - Test whether ``self`` is a ray. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).is_ray() - False - sage: ray(x).is_ray() - True - sage: point(x,2).is_ray() - False - sage: closure_point(x,2).is_ray() - False - """ - return self.thisptr.is_ray() - - - def is_line_or_ray(self): - r""" - Test whether ``self`` is a line or a ray. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).is_line_or_ray() - True - sage: ray(x).is_line_or_ray() - True - sage: point(x,2).is_line_or_ray() - False - sage: closure_point(x,2).is_line_or_ray() - False - """ - return self.thisptr.is_line_or_ray() - - - def is_point(self): - r""" - Test whether ``self`` is a point. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).is_point() - False - sage: ray(x).is_point() - False - sage: point(x,2).is_point() - True - sage: closure_point(x,2).is_point() - False - """ - return self.thisptr.is_point() - - - def is_closure_point(self): - r""" - Test whether ``self`` is a closure point. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point, closure_point, ray, line - sage: x = Variable(0) - sage: line(x).is_closure_point() - False - sage: ray(x).is_closure_point() - False - sage: point(x,2).is_closure_point() - False - sage: closure_point(x,2).is_closure_point() - True - """ - return self.thisptr.is_closure_point() - - - def coefficient(self, Variable v): - """ - Return the coefficient of the variable ``v``. - - INPUT: - - - ``v`` -- a :class:`Variable`. - - OUTPUT: - - An integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, line - sage: x = Variable(0) - sage: line = line(3*x+1) - sage: line - line(1) - sage: line.coefficient(x) - 1 - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.coefficient(v.thisptr[0]).get_mpz_t()) - return c - - - def coefficients(self): - """ - Return the coefficients of the generator. - - See also :meth:`coefficient`. - - OUTPUT: - - A tuple of integers of length :meth:`space_dimension`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point - sage: x = Variable(0); y = Variable(1) - sage: p = point(3*x+5*y+1, 2); p - point(3/2, 5/2) - sage: p.coefficients() - (3, 5) - """ - cdef int d = self.space_dimension() - cdef int i - cdef Integer c = Integer(0) - coeffs = [] - for i in range(0,d): - mpz_set(c.value, self.thisptr.coefficient(PPL_Variable(i)).get_mpz_t()) - coeffs.append(Integer(c)) - return tuple(coeffs) - - - def divisor(self): - """ - If ``self`` is either a point or a closure point, return its - divisor. - - OUTPUT: - - An integer. If ``self`` is a ray or a line, raises - ``ValueError``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: point = Generator.point(2*x-y+5) - sage: point.divisor() - 1 - sage: line = Generator.line(2*x-y+5) - sage: line.divisor() - Traceback (most recent call last): - ... - ValueError: PPL::Generator::divisor(): - *this is neither a point nor a closure point. - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.divisor().get_mpz_t()) - return c - - - def is_equivalent_to(self, Generator g): - r""" - Test whether ``self`` and ``g`` are equivalent. - - INPUT: - - - ``g`` -- a :class:`Generator`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` and ``g`` - are equivalent generators. - - Note that generators having different space dimensions are not - equivalent. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator, Variable, point, line - sage: x = Variable(0) - sage: y = Variable(1) - sage: point(2*x , 2).is_equivalent_to( point(x) ) - True - sage: point(2*x+0*y, 2).is_equivalent_to( point(x) ) - False - sage: line(4*x).is_equivalent_to(line(x)) - True - """ - return self.thisptr.is_equivalent_to(g.thisptr[0]) - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Linear_Expression, Variable, point\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'p = point(3*x+2*y)\n' - sage: sage_cmd += 'p.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - size 3 1 3 2 P (C) - sage: print(err) # long time py3 - size 3 1 3 2 P (C) - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: e = 3*x+2*y+1 - sage: e.OK() - True - """ - return self.thisptr.OK() - - - def __reduce__(self): - """ - Pickle object. - - TESTS:: - - sage: from sage.libs.ppl import Generator, Variable, line, ray, point, closure_point - sage: x = Variable(0); y = Variable(1) - sage: loads(dumps(Generator.point(2*x+7*y, 3))) - point(2/3, 7/3) - sage: loads(dumps(Generator.closure_point(2*x+7*y, 3))) - closure_point(2/3, 7/3) - sage: loads(dumps(Generator.line(2*x+7*y))) - line(2, 7) - sage: loads(dumps(Generator.ray(2*x+7*y))) - ray(2, 7) - """ - t = self.thisptr.type() - le = Linear_Expression(self.coefficients(), 0) - if t==LINE: - return (line, (le,)) - elif t==RAY: - return (ray, (le,)) - elif t==POINT: - return (point, (le, self.divisor())) - elif t==CLOSURE_POINT: - return (closure_point, (le, self.divisor())) - assert False - - - -#################################################### -def line(expression): - """ - Constuct a line. - - See :meth:`Generator.line` for documentation. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, line - sage: y = Variable(1) - sage: line(2*y) - line(0, 1) - """ - return Generator.line(expression) - - -#################################################### -def ray(expression): - """ - Constuct a ray. - - See :meth:`Generator.ray` for documentation. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, ray - sage: y = Variable(1) - sage: ray(2*y) - ray(0, 1) - """ - return Generator.ray(expression) - - -#################################################### -def point(expression=0, divisor=1): - """ - Constuct a point. - - See :meth:`Generator.point` for documentation. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, point - sage: y = Variable(1) - sage: point(2*y, 5) - point(0/5, 2/5) - """ - return Generator.point(expression, divisor) - - -#################################################### -def closure_point(expression=0, divisor=1): - """ - Constuct a closure point. - - See :meth:`Generator.closure_point` for documentation. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, closure_point - sage: y = Variable(1) - sage: closure_point(2*y, 5) - closure_point(0/5, 2/5) - """ - return Generator.closure_point(expression, divisor) - - - -#################################################### -### Generator_System ############################## -#################################################### -cdef _wrap_Generator_System(PPL_Generator_System generator_system): - """ - Wrap a C++ ``PPL_Generator_System`` into a Cython ``Generator_System``. - """ - cdef Generator_System gs = Generator_System() - del gs.thisptr - gs.thisptr = new PPL_Generator_System(generator_system) - return gs - - -#################################################### -cdef class Generator_System(_mutable_or_immutable): - """ - Wrapper for PPL's ``Generator_System`` class. - - An object of the class Generator_System is a system of generators, - i.e., a multiset of objects of the class Generator (lines, rays, - points and closure points). When inserting generators in a system, - space dimensions are automatically adjusted so that all the - generators in the system are defined on the same vector space. A - system of generators which is meant to define a non-empty - polyhedron must include at least one point: the reason is that - lines, rays and closure points need a supporting point (lines and - rays only specify directions while closure points only specify - points in the topological closure of the NNC polyhedron). - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, line, ray, point, closure_point - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System( line(5*x-2*y) ) - sage: gs.insert( ray(6*x-3*y) ) - sage: gs.insert( point(2*x-7*y, 5) ) - sage: gs.insert( closure_point(9*x-1*y, 2) ) - sage: gs - Generator_System {line(5, -2), ray(2, -1), point(2/5, -7/5), closure_point(9/2, -1/2)} - """ - - cdef PPL_Generator_System *thisptr - - - def __cinit__(self, arg=None): - """ - The Cython constructor. - - See :class:`Generator_System` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Generator_System - sage: Generator_System() # indirect doctest - Generator_System {} - """ - if arg is None: - self.thisptr = new PPL_Generator_System() - return - if isinstance(arg, Generator): - g = arg - self.thisptr = new PPL_Generator_System(g.thisptr[0]) - return - if isinstance(arg, Generator_System): - gs = arg - self.thisptr = new PPL_Generator_System(gs.thisptr[0]) - return - if isinstance(arg, (list,tuple)): - self.thisptr = new PPL_Generator_System() - for generator in arg: - self.insert(generator) - return - raise ValueError('Cannot initialize with '+str(arg)+'.') - - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: gs = Generator_System( point(3*x) ) - sage: gs.space_dimension() - 1 - """ - return self.thisptr.space_dimension() - - - def clear(self): - r""" - Removes all generators from the generator system and sets its - space dimension to 0. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: gs = Generator_System( point(3*x) ); gs - Generator_System {point(3/1)} - sage: gs.clear() - sage: gs - Generator_System {} - """ - self.assert_mutable('The Generator_System is not mutable!') - self.thisptr.clear() - - - def insert(self, Generator g): - """ - Insert ``g`` into the generator system. - - The number of space dimensions of ``self`` is increased, if needed. - - INPUT: - - - ``g`` -- a :class:`Generator`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: gs = Generator_System( point(3*x) ) - sage: gs.insert( point(-3*x) ) - sage: gs - Generator_System {point(3/1), point(-3/1)} - """ - self.assert_mutable('The Generator_System is not mutable!') - self.thisptr.insert(g.thisptr[0]) - - - def empty(self): - """ - Return ``True`` if and only if ``self`` has no generators. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: gs = Generator_System() - sage: gs.empty() - True - sage: gs.insert( point(-3*x) ) - sage: gs.empty() - False - """ - return self.thisptr.empty() - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Generator_System, point, Variable\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'gs = Generator_System( point(3*x+2*y+1) )\n' - sage: sage_cmd += 'gs.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - topology NECESSARILY_CLOSED - 1 x 2 SPARSE (sorted) - index_first_pending 1 - size 3 1 3 2 P (C) - sage: print(err) # long time py3 - topology NECESSARILY_CLOSED - 1 x 2 SPARSE (sorted) - index_first_pending 1 - size 3 1 3 2 P (C) - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System( point(3*x+2*y+1) ) - sage: gs.OK() - True - """ - return self.thisptr.OK() - - - def __len__(self): - """ - Return the number of generators in the system. - - sage: from sage.libs.ppl import Variable, Generator_System, point - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System() - sage: gs.insert(point(3*x+2*y)) - sage: gs.insert(point(x)) - sage: gs.insert(point(y)) - sage: len(gs) - 3 - """ - return sum([1 for g in self]) - - - def __iter__(self): - """ - Iterate through the generators of the system. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, point - sage: x = Variable(0) - sage: gs = Generator_System(point(3*x)) - sage: iter = gs.__iter__() - sage: next(iter) - point(3/1) - """ - return Generator_System_iterator(self) - - - def __getitem__(self, int k): - """ - Return the ``k``-th generator. - - The correct way to read the individual generators is to - iterate over the generator system. This method is for - convenience only. - - INPUT: - - - ``k`` -- integer. The index of the generator. - - OUTPUT: - - The ``k``-th constraint of the generator system. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, point - sage: x = Variable(0) - sage: gs = Generator_System() - sage: gs.insert(point(3*x)) - sage: gs.insert(point(-2*x)) - sage: gs - Generator_System {point(3/1), point(-2/1)} - sage: gs[0] - point(3/1) - sage: gs[1] - point(-2/1) - """ - if k < 0: - raise IndexError('index must be nonnegative') - iterator = iter(self) - try: - for i in range(k): - next(iterator) - except StopIteration: - raise IndexError('index is past-the-end') - return next(iterator) - - - def __repr__(self): - r""" - Return a string representation of the generator system. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, point, ray - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System(point(3*x+2*y+1)) - sage: gs.insert(ray(x)) - sage: gs.__repr__() - 'Generator_System {point(3/1, 2/1), ray(1, 0)}' - """ - s = 'Generator_System {' - s += ', '.join([ repr(g) for g in self ]) - s += '}' - return s - - - def __reduce__(self): - """ - Pickle object. - - TESTS:: - - sage: from sage.libs.ppl import Generator_System, Variable, point, ray - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System((point(3*x+2*y+1), ray(x))); gs - Generator_System {point(3/1, 2/1), ray(1, 0)} - sage: loads(dumps(gs)) - Generator_System {point(3/1, 2/1), ray(1, 0)} - """ - return (Generator_System, (tuple(self), )) - - - -#################################################### -### Generator_System_iterator ###################### -#################################################### -cdef extern from "ppl_shim.hh": - ctypedef void* gs_iterator_ptr - cdef gs_iterator_ptr init_gs_iterator(PPL_Generator_System &gs) - cdef PPL_Generator next_gs_iterator(gs_iterator_ptr) - cdef bint is_end_gs_iterator(PPL_Generator_System &gs, gs_iterator_ptr gsi_ptr) - cdef void delete_gs_iterator(gs_iterator_ptr) - - -#################################################### -cdef class Generator_System_iterator(object): - """ - Wrapper for PPL's ``Generator_System::const_iterator`` class. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, line, ray, point, closure_point, Generator_System_iterator - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System( line(5*x-2*y) ) - sage: gs.insert( ray(6*x-3*y) ) - sage: gs.insert( point(2*x-7*y, 5) ) - sage: gs.insert( closure_point(9*x-1*y, 2) ) - sage: next(Generator_System_iterator(gs)) - line(5, -2) - sage: list(gs) - [line(5, -2), ray(2, -1), point(2/5, -7/5), closure_point(9/2, -1/2)] - """ - - cdef Generator_System gs - cdef gs_iterator_ptr gsi_ptr - - - def __cinit__(self, Generator_System gs): - r""" - The Cython constructor. - - TESTS:: - - sage: from sage.libs.ppl import Generator_System, Generator_System_iterator - sage: iter = Generator_System_iterator(Generator_System()) # indirect doctest - """ - self.gs = gs - self.gsi_ptr = init_gs_iterator(gs.thisptr[0]) - - - def __dealloc__(self): - """ - The Cython destructor. - """ - delete_gs_iterator(self.gsi_ptr) - - - def __next__(Generator_System_iterator self): - r""" - The next iteration. - - OUTPUT: - - A :class:`Generator`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Generator_System, Variable, point, Generator_System_iterator - sage: x = Variable(0) - sage: y = Variable(1) - sage: gs = Generator_System( point(5*x-2*y) ) - sage: next(Generator_System_iterator(gs)) - point(5/1, -2/1) - """ - if is_end_gs_iterator((self.gs).thisptr[0], self.gsi_ptr): - raise StopIteration - return _wrap_Generator(next_gs_iterator(self.gsi_ptr)) - - -#################################################### -### Constraint ###################################### -#################################################### -cdef _wrap_Constraint(PPL_Constraint constraint): - """ - Wrap a C++ ``PPL_Constraint`` into a Cython ``Constraint``. - - Check that :trac:`27278` is fixed:: - - sage: from sage.libs.ppl import Variable, Constraint - sage: x=Variable(0) - sage: c = x == 0 - sage: type(Constraint(c)) - - """ - cdef Constraint c = Constraint.__new__(Constraint) - c.thisptr = new PPL_Constraint(constraint) - return c - - -#################################################### -cdef _make_Constraint_from_richcmp(lhs_, rhs_, op): - cdef Linear_Expression lhs = Linear_Expression(lhs_) - cdef Linear_Expression rhs = Linear_Expression(rhs_) - if op == Py_LT: - return _wrap_Constraint(lhs.thisptr[0] < rhs.thisptr[0]) - elif op == Py_LE: - return _wrap_Constraint(lhs.thisptr[0] <= rhs.thisptr[0]) - elif op == Py_EQ: - return _wrap_Constraint(lhs.thisptr[0] == rhs.thisptr[0]) - elif op == Py_GT: - return _wrap_Constraint(lhs.thisptr[0] > rhs.thisptr[0]) - elif op == Py_GE: - return _wrap_Constraint(lhs.thisptr[0] >= rhs.thisptr[0]) - elif op == Py_NE: - raise NotImplementedError - else: - assert(False) - - -#################################################### -cdef class Constraint(object): - """ - Wrapper for PPL's ``Constraint`` class. - - An object of the class ``Constraint`` is either: - - * an equality `\sum_{i=0}^{n-1} a_i x_i + b = 0` - - * a non-strict inequality `\sum_{i=0}^{n-1} a_i x_i + b \geq 0` - - * a strict inequality `\sum_{i=0}^{n-1} a_i x_i + b > 0` - - where `n` is the dimension of the space, `a_i` is the integer - coefficient of variable `x_i`, and `b_i` is the integer - inhomogeneous term. - - INPUT/OUTPUT: - - You construct constraints by writing inequalities in - :class:`Linear_Expression`. Do not attempt to manually construct - constraints. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint, Variable, Linear_Expression - sage: x = Variable(0) - sage: y = Variable(1) - sage: 5*x-2*y > x+y-1 - 4*x0-3*x1+1>0 - sage: 5*x-2*y >= x+y-1 - 4*x0-3*x1+1>=0 - sage: 5*x-2*y == x+y-1 - 4*x0-3*x1+1==0 - sage: 5*x-2*y <= x+y-1 - -4*x0+3*x1-1>=0 - sage: 5*x-2*y < x+y-1 - -4*x0+3*x1-1>0 - sage: x > 0 - x0>0 - - Special care is needed if the left hand side is a constant:: - - sage: 0 == 1 # watch out! - False - sage: Linear_Expression(0) == 1 - -1==0 - """ - - cdef PPL_Constraint *thisptr - - def __init__(self, arg=None): - if arg is None: - self.thisptr = new PPL_Constraint() - elif isinstance(arg, Constraint): - self.thisptr = new PPL_Constraint(( arg).thisptr[0]) - else: - raise TypeError("invalid argument for Constraint") - - def __cinit__(self): - """ - The Cython constructor. - - See :class:`Constraint` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Constraint, Variable, Linear_Expression - sage: x = Variable(0) - sage: x>0 # indirect doctest - x0>0 - """ - self.thisptr = NULL - - - def __dealloc__(self): - """ - The Cython destructor. - """ - assert self.thisptr!=NULL, 'Do not construct Constraints manually!' - del self.thisptr - - - def __repr__(self): - """ - Return a string representation of the constraint. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: (2*x-y+5 > x).__repr__() - 'x0-x1+5>0' - sage: (2*x-y+5 == x).__repr__() - 'x0-x1+5==0' - sage: (2*x-y+5 >= x).__repr__() - 'x0-x1+5>=0' - """ - e = sum([ self.coefficient(x)*x - for x in [Variable(i) - for i in range(0,self.space_dimension())] ]) - e += self.inhomogeneous_term() - s = repr(e) - t = self.type() - if t=='equality': - s += '==0' - elif t=='nonstrict_inequality': - s += '>=0' - elif t=='strict_inequality': - s += '>0' - else: - assert(False) - return s - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: (x>=0).space_dimension() - 1 - sage: (y==1).space_dimension() - 2 - """ - return self.thisptr.space_dimension() - - - def type(self): - r""" - Return the constraint type of ``self``. - - OUTPUT: - - String. One of ``'equality'``, ``'nonstrict_inequality'``, or - ``'strict_inequality'``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).type() - 'equality' - sage: (x>=0).type() - 'nonstrict_inequality' - sage: (x>0).type() - 'strict_inequality' - """ - t = self.thisptr.type() - if t==EQUALITY: - return 'equality' - elif t==NONSTRICT_INEQUALITY: - return 'nonstrict_inequality' - elif t==STRICT_INEQUALITY: - return 'strict_inequality' - - - def is_equality(self): - r""" - Test whether ``self`` is an equality. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is an - equality constraint. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).is_equality() - True - sage: (x>=0).is_equality() - False - sage: (x>0).is_equality() - False - """ - return self.thisptr.is_equality() - - - def is_inequality(self): - r""" - Test whether ``self`` is an inequality. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is an - inequality constraint, either strict or non-strict. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).is_inequality() - False - sage: (x>=0).is_inequality() - True - sage: (x>0).is_inequality() - True - """ - return self.thisptr.is_inequality() - - - def is_nonstrict_inequality(self): - r""" - Test whether ``self`` is a non-strict inequality. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is an - non-strict inequality constraint. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).is_nonstrict_inequality() - False - sage: (x>=0).is_nonstrict_inequality() - True - sage: (x>0).is_nonstrict_inequality() - False - """ - return self.thisptr.is_nonstrict_inequality() - - - def is_strict_inequality(self): - r""" - Test whether ``self`` is a strict inequality. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is an - strict inequality constraint. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).is_strict_inequality() - False - sage: (x>=0).is_strict_inequality() - False - sage: (x>0).is_strict_inequality() - True - """ - return self.thisptr.is_strict_inequality() - - - def coefficient(self, Variable v): - """ - Return the coefficient of the variable ``v``. - - INPUT: - - - ``v`` -- a :class:`Variable`. - - OUTPUT: - - An integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: ineq = (3*x+1 > 0) - sage: ineq.coefficient(x) - 3 - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.coefficient(v.thisptr[0]).get_mpz_t()) - return c - - - def coefficients(self): - """ - Return the coefficients of the constraint. - - See also :meth:`coefficient`. - - OUTPUT: - - A tuple of integers of length :meth:`space_dimension`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0); y = Variable(1) - sage: ineq = ( 3*x+5*y+1 == 2); ineq - 3*x0+5*x1-1==0 - sage: ineq.coefficients() - (3, 5) - """ - cdef int d = self.space_dimension() - cdef int i - cdef Integer c = Integer(0) - coeffs = [] - for i in range(0,d): - mpz_set(c.value, self.thisptr.coefficient(PPL_Variable(i)).get_mpz_t()) - coeffs.append(Integer(c)) - return tuple(coeffs) - - - def inhomogeneous_term(self): - """ - Return the inhomogeneous term of the constraint. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: y = Variable(1) - sage: ineq = ( 10+y > 9 ) - sage: ineq - x1+1>0 - sage: ineq.inhomogeneous_term() - 1 - """ - cdef Integer c = Integer(0) - mpz_set(c.value, self.thisptr.inhomogeneous_term().get_mpz_t()) - return c - - - def is_tautological(self): - r""" - Test whether ``self`` is a tautological constraint. - - A tautology can have either one of the following forms: - - * an equality: `\sum 0 x_i + 0 = 0`, - - * a non-strict inequality: `\sum 0 x_i + b \geq 0` with `b\geq 0`, or - - * a strict inequality: `\sum 0 x_i + b > 0` with `b> 0`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is a - tautological constraint. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==0).is_tautological() - False - sage: (0*x>=0).is_tautological() - True - """ - return self.thisptr.is_tautological() - - - def is_inconsistent(self): - r""" - Test whether ``self`` is an inconsistent constraint, that is, always false. - - An inconsistent constraint can have either one of the - following forms: - - * an equality: `\sum 0 x_i + b = 0` with `b\not=0`, - - * a non-strict inequality: `\sum 0 x_i + b \geq 0` with `b< 0`, or - - * a strict inequality: `\sum 0 x_i + b > 0` with `b\leq 0`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` is an - inconsistent constraint. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable - sage: x = Variable(0) - sage: (x==1).is_inconsistent() - False - sage: (0*x>=1).is_inconsistent() - True - """ - return self.thisptr.is_inconsistent() - - - def is_equivalent_to(self, Constraint c): - r""" - Test whether ``self`` and ``c`` are equivalent. - - INPUT: - - - ``c`` -- a :class:`Constraint`. - - OUTPUT: - - Boolean. Returns ``True`` if and only if ``self`` and ``c`` - are equivalent constraints. - - Note that constraints having different space dimensions are - not equivalent. However, constraints having different types - may nonetheless be equivalent, if they both are tautologies or - inconsistent. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Linear_Expression - sage: x = Variable(0) - sage: y = Variable(1) - sage: ( x>0 ).is_equivalent_to( Linear_Expression(0)0 ).is_equivalent_to( 0*y1 ).is_equivalent_to( 0*x==-2 ) - True - """ - return self.thisptr.is_equivalent_to(c.thisptr[0]) - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Linear_Expression, Variable\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'e = (3*x+2*y+1 > 0)\n' - sage: sage_cmd += 'e.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - size 4 1 3 2 -1 > (NNC) - sage: print(err) # long time py3 - size 4 1 3 2 -1 > (NNC) - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: ineq = (3*x+2*y+1>=0) - sage: ineq.OK() - True - """ - return self.thisptr.OK() - - - def __reduce__(self): - """ - Pickle object. - - EXAMPLES:: - - sage: from sage.libs.ppl import Linear_Expression, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: loads(dumps(3*x+2*y+1>=5)) - 3*x0+2*x1-4>=0 - sage: loads(dumps(3*x+2*y+1>5)) - 3*x0+2*x1-4>0 - sage: loads(dumps(3*x+2*y+1==5)) - 3*x0+2*x1-4==0 - """ - le = Linear_Expression(self.coefficients(), self.inhomogeneous_term()) - if self.is_nonstrict_inequality(): - return (inequality, (le, )) - elif self.is_strict_inequality(): - return (strict_inequality, (le, )) - elif self.is_equality(): - return (equation, (le, )) - else: - assert False - - - -#################################################### -def inequality(expression): - """ - Constuct an inequality. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression`. - - OUTPUT: - - The inequality ``expression`` >= 0. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, inequality - sage: y = Variable(1) - sage: 2*y+1 >= 0 - 2*x1+1>=0 - sage: inequality(2*y+1) - 2*x1+1>=0 - """ - return expression >= 0 - - -#################################################### -def strict_inequality(expression): - """ - Constuct a strict inequality. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression`. - - OUTPUT: - - The inequality ``expression`` > 0. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, strict_inequality - sage: y = Variable(1) - sage: 2*y+1 > 0 - 2*x1+1>0 - sage: strict_inequality(2*y+1) - 2*x1+1>0 - """ - return expression > 0 - - -#################################################### -def equation(expression): - """ - Constuct an equation. - - INPUT: - - - ``expression`` -- a :class:`Linear_Expression`. - - OUTPUT: - - The equation ``expression`` == 0. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, equation - sage: y = Variable(1) - sage: 2*y+1 == 0 - 2*x1+1==0 - sage: equation(2*y+1) - 2*x1+1==0 - """ - return expression == 0 - - - -#################################################### -### Constraint_System ############################## -#################################################### -cdef _wrap_Constraint_System(PPL_Constraint_System constraint_system): - """ - Wrap a C++ ``PPL_Constraint_System`` into a Cython ``Constraint_System``. - """ - cdef Constraint_System cs = Constraint_System.__new__(Constraint_System) - cs.thisptr = new PPL_Constraint_System(constraint_system) - return cs - - -#################################################### -cdef class Constraint_System(object): - """ - Wrapper for PPL's ``Constraint_System`` class. - - An object of the class Constraint_System is a system of - constraints, i.e., a multiset of objects of the class - Constraint. When inserting constraints in a system, space - dimensions are automatically adjusted so that all the constraints - in the system are defined on the same vector space. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint_System, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System( 5*x-2*y > 0 ) - sage: cs.insert( 6*x<3*y ) - sage: cs.insert( x >= 2*x-7*y ) - sage: cs - Constraint_System {5*x0-2*x1>0, -2*x0+x1>0, -x0+7*x1>=0} - """ - - cdef PPL_Constraint_System *thisptr - - - def __init__(self, arg=None): - if arg is None: - self.thisptr = new PPL_Constraint_System() - return - if isinstance(arg, Constraint): - g = arg - self.thisptr = new PPL_Constraint_System(g.thisptr[0]) - return - if isinstance(arg, Constraint_System): - gs = arg - self.thisptr = new PPL_Constraint_System(gs.thisptr[0]) - return - if isinstance(arg, (list,tuple)): - self.thisptr = new PPL_Constraint_System() - for constraint in arg: - self.insert(constraint) - return - raise ValueError('Cannot initialize with '+str(arg)+'.') - - def __cinit__(self, arg=None): - """ - The Cython constructor. - - See :class:`Constraint_System` for documentation. - - Tests: - - >>> from ppl import Constraint_System - >>> Constraint_System() - Constraint_System {} - """ - self.thisptr = NULL - - def __dealloc__(self): - """ - The Cython destructor. - """ - del self.thisptr - - - def space_dimension(self): - r""" - Return the dimension of the vector space enclosing ``self``. - - OUTPUT: - - Integer. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System( x>0 ) - sage: cs.space_dimension() - 1 - """ - return self.thisptr.space_dimension() - - - def has_equalities(self): - r""" - Tests whether ``self`` contains one or more equality constraints. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System() - sage: cs.insert( x>0 ) - sage: cs.insert( x<0 ) - sage: cs.has_equalities() - False - sage: cs.insert( x==0 ) - sage: cs.has_equalities() - True - """ - return self.thisptr.has_equalities() - - - def has_strict_inequalities(self): - r""" - Tests whether ``self`` contains one or more strict inequality constraints. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System() - sage: cs.insert( x>=0 ) - sage: cs.insert( x==-1 ) - sage: cs.has_strict_inequalities() - False - sage: cs.insert( x>0 ) - sage: cs.has_strict_inequalities() - True - """ - return self.thisptr.has_strict_inequalities() - - - def clear(self): - r""" - Removes all constraints from the constraint system and sets its - space dimension to 0. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System(x>0) - sage: cs - Constraint_System {x0>0} - sage: cs.clear() - sage: cs - Constraint_System {} - """ - self.assert_mutable('The Constraint_System is not mutable!') - self.thisptr.clear() - - - def insert(self, Constraint c): - """ - Insert ``c`` into the constraint system. - - INPUT: - - - ``c`` -- a :class:`Constraint`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System() - sage: cs.insert( x>0 ) - sage: cs - Constraint_System {x0>0} - """ - self.assert_mutable('The Constraint_System is not mutable!') - self.thisptr.insert(c.thisptr[0]) - - - def empty(self): - """ - Return ``True`` if and only if ``self`` has no constraints. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System, point - sage: x = Variable(0) - sage: cs = Constraint_System() - sage: cs.empty() - True - sage: cs.insert( x>0 ) - sage: cs.empty() - False - """ - return self.thisptr.empty() - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Constraint_System, Variable\n' - sage: sage_cmd += 'x = Variable(0)\n' - sage: sage_cmd += 'y = Variable(1)\n' - sage: sage_cmd += 'cs = Constraint_System( 3*x > 2*y+1 )\n' - sage: sage_cmd += 'cs.ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - ... - topology NOT_NECESSARILY_CLOSED - 1 x 2 SPARSE (sorted) - index_first_pending 1 - size 4 -1 3 -2 -1 > (NNC) - sage: print(err) # long time py3 - topology NOT_NECESSARILY_CLOSED - 1 x 2 SPARSE (sorted) - index_first_pending 1 - size 4 -1 3 -2 -1 > (NNC) - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System( 3*x+2*y+1 <= 10 ) - sage: cs.OK() - True - """ - return self.thisptr.OK() - - - def __len__(self): - """ - Return the number of constraints in the system. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System( x>0 ) - sage: cs.insert( x<1 ) - sage: len(cs) - 2 - """ - return sum([1 for c in self]) - - - def __iter__(self): - """ - Iterate through the constraints of the system. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System( x>0 ) - sage: iter = cs.__iter__() - sage: next(iter) - x0>0 - sage: list(cs) # uses __iter__() internally - [x0>0] - """ - return Constraint_System_iterator(self) - - - def __getitem__(self, int k): - """ - Return the k-th constraint. - - The correct way to read the individual constraints is to - iterate over the constraint system. This method is for - convenience only. - - INPUT: - - - ``k`` -- integer. The index of the constraint. - - OUTPUT: - - The `k`-th constraint of the constraint system. - - EXAMPLES:: - - sage: from sage.libs.ppl import Variable, Constraint_System - sage: x = Variable(0) - sage: cs = Constraint_System( x>0 ) - sage: cs.insert( x<1 ) - sage: cs - Constraint_System {x0>0, -x0+1>0} - sage: cs[0] - x0>0 - sage: cs[1] - -x0+1>0 - """ - if k < 0: - raise IndexError('index must be nonnegative') - iterator = iter(self) - try: - for i in range(k): - next(iterator) - except StopIteration: - raise IndexError('index is past-the-end') - return next(iterator) - - - def __repr__(self): - r""" - Return a string representation of the constraint system. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint_System, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System([3*x+2*y+1 < 3, 0*x>x+1]) - sage: cs.__repr__() - 'Constraint_System {-3*x0-2*x1+2>0, -x0-1>0}' - """ - s = 'Constraint_System {' - s += ', '.join([ repr(c) for c in self ]) - s += '}' - return s - - - def __reduce__(self): - """ - Pickle object. - - TESTS:: - - sage: from sage.libs.ppl import Constraint_System, Variable - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System([3*x+2*y+1 < 3, 0*x>x+1]); cs - Constraint_System {-3*x0-2*x1+2>0, -x0-1>0} - sage: loads(dumps(cs)) - Constraint_System {-3*x0-2*x1+2>0, -x0-1>0} - """ - return (Constraint_System, (tuple(self), )) - - -#################################################### -### Constraint_System_iterator ##################### -#################################################### -cdef extern from "ppl_shim.hh": - ctypedef void* cs_iterator_ptr - cdef cs_iterator_ptr init_cs_iterator(PPL_Constraint_System &cs) - cdef PPL_Constraint next_cs_iterator(cs_iterator_ptr) - cdef bint is_end_cs_iterator(PPL_Constraint_System &cs, cs_iterator_ptr csi_ptr) - cdef void delete_cs_iterator(cs_iterator_ptr) - - -#################################################### -cdef class Constraint_System_iterator(object): - """ - Wrapper for PPL's ``Constraint_System::const_iterator`` class. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint_System, Variable, Constraint_System_iterator - sage: x = Variable(0) - sage: y = Variable(1) - sage: cs = Constraint_System( 5*x < 2*y ) - sage: cs.insert( 6*x-3*y==0 ) - sage: cs.insert( x >= 2*x-7*y ) - sage: next(Constraint_System_iterator(cs)) - -5*x0+2*x1>0 - sage: list(cs) - [-5*x0+2*x1>0, 2*x0-x1==0, -x0+7*x1>=0] - """ - - cdef Constraint_System cs - cdef cs_iterator_ptr csi_ptr - - - def __cinit__(self, Constraint_System cs): - """ - The Cython constructor. - - See :class:`Constraint_System_iterator` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Constraint_System, Constraint_System_iterator - sage: iter = Constraint_System_iterator( Constraint_System() ) # indirect doctest - """ - self.cs = cs - self.csi_ptr = init_cs_iterator(cs.thisptr[0]) - - - def __dealloc__(self): - """ - The Cython destructor. - """ - delete_cs_iterator(self.csi_ptr) - - - def __next__(Constraint_System_iterator self): - r""" - The next iteration. - - OUTPUT: - - A :class:`Generator`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Constraint_System, Variable, Constraint_System_iterator - sage: x = Variable(0) - sage: cs = Constraint_System( 5*x > 0 ) - sage: next(Constraint_System_iterator(cs)) - x0>0 - """ - if is_end_cs_iterator((self.cs).thisptr[0], self.csi_ptr): - raise StopIteration - return _wrap_Constraint(next_cs_iterator(self.csi_ptr)) - - - -#################################################### -### Poly_Gen_Relation ############################## -#################################################### -cdef _wrap_Poly_Gen_Relation(PPL_Poly_Gen_Relation relation): - """ - Wrap a C++ ``PPL_Poly_Gen_Relation`` into a Cython ``Poly_Gen_Relation``. - """ - cdef Poly_Gen_Relation rel = Poly_Gen_Relation(True) - rel.thisptr = new PPL_Poly_Gen_Relation(relation) - return rel - - -#################################################### -cdef class Poly_Gen_Relation(object): - r""" - Wrapper for PPL's ``Poly_Con_Relation`` class. - - INPUT/OUTPUT: - - You must not construct :class:`Poly_Gen_Relation` objects - manually. You will usually get them from - :meth:`~sage.libs.ppl.Polyhedron.relation_with`. You can also get - pre-defined relations from the class methods :meth:`nothing` and - :meth:`subsumes`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: nothing = Poly_Gen_Relation.nothing(); nothing - nothing - sage: subsumes = Poly_Gen_Relation.subsumes(); subsumes - subsumes - sage: nothing.implies( subsumes ) - False - sage: subsumes.implies( nothing ) - True - """ - - cdef PPL_Poly_Gen_Relation *thisptr - - - def __cinit__(self, do_not_construct_manually=False): - """ - The Cython constructor. - - See :class:`Poly_Gen_Relation` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: Poly_Gen_Relation.nothing() - nothing - """ - assert(do_not_construct_manually) - self.thisptr = NULL - - - def __dealloc__(self): - """ - The Cython destructor. - """ - assert self.thisptr!=NULL, 'Do not construct Poly_Gen_Relation objects manually!' - del self.thisptr - - - def implies(self, Poly_Gen_Relation y): - r""" - Test whether ``self`` implies ``y``. - - INPUT: - - - ``y`` -- a :class:`Poly_Gen_Relation`. - - OUTPUT: - - Boolean. ``True`` if and only if ``self`` implies ``y``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: nothing = Poly_Gen_Relation.nothing() - sage: nothing.implies( nothing ) - True - """ - return self.thisptr.implies(y.thisptr[0]) - - - @classmethod - def nothing(cls): - r""" - Return the assertion that says nothing. - - OUTPUT: - - A :class:`Poly_Gen_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: Poly_Gen_Relation.nothing() - nothing - """ - return _wrap_Poly_Gen_Relation(PPL_Poly_Gen_Relation_nothing()) - - - @classmethod - def subsumes(cls): - r""" - Return the assertion "Adding the generator would not change - the polyhedron". - - OUTPUT: - - A :class:`Poly_Gen_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: Poly_Gen_Relation.subsumes() - subsumes - """ - return _wrap_Poly_Gen_Relation(PPL_Poly_Gen_Relation_subsumes()) - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Poly_Gen_Relation\n' - sage: sage_cmd += 'Poly_Gen_Relation.nothing().ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - NOTHING - sage: print(err) # long time py3 - NOTHING/... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self, check_non_empty=False): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: Poly_Gen_Relation.nothing().OK() - True - """ - return self.thisptr.OK() - - - def __repr__(self): - r""" - Return a string representation. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Gen_Relation - sage: Poly_Gen_Relation.nothing().__repr__() - 'nothing' - """ - if self.implies(Poly_Gen_Relation.subsumes()): - return 'subsumes' - else: - return 'nothing' - - -#################################################### -### Poly_Con_Relation ############################## -#################################################### -cdef _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation relation): - """ - Wrap a C++ ``PPL_Poly_Con_Relation`` into a Cython ``Poly_Con_Relation``. - """ - cdef Poly_Con_Relation rel = Poly_Con_Relation(True) - rel.thisptr = new PPL_Poly_Con_Relation(relation) - return rel - - -#################################################### -cdef class Poly_Con_Relation(object): - r""" - Wrapper for PPL's ``Poly_Con_Relation`` class. - - INPUT/OUTPUT: - - You must not construct :class:`Poly_Con_Relation` objects - manually. You will usually get them from - :meth:`~sage.libs.ppl.Polyhedron.relation_with`. You can also get - pre-defined relations from the class methods :meth:`nothing`, - :meth:`is_disjoint`, :meth:`strictly_intersects`, - :meth:`is_included`, and :meth:`saturates`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: saturates = Poly_Con_Relation.saturates(); saturates - saturates - sage: is_included = Poly_Con_Relation.is_included(); is_included - is_included - sage: is_included.implies(saturates) - False - sage: saturates.implies(is_included) - False - sage: rels = [] - sage: rels.append( Poly_Con_Relation.nothing() ) - sage: rels.append( Poly_Con_Relation.is_disjoint() ) - sage: rels.append( Poly_Con_Relation.strictly_intersects() ) - sage: rels.append( Poly_Con_Relation.is_included() ) - sage: rels.append( Poly_Con_Relation.saturates() ) - sage: rels - [nothing, is_disjoint, strictly_intersects, is_included, saturates] - sage: from sage.matrix.constructor import matrix - sage: m = matrix(5,5) - sage: for i, rel_i in enumerate(rels): - ....: for j, rel_j in enumerate(rels): - ....: m[i,j] = rel_i.implies(rel_j) - sage: m - [1 0 0 0 0] - [1 1 0 0 0] - [1 0 1 0 0] - [1 0 0 1 0] - [1 0 0 0 1] - """ - - cdef PPL_Poly_Con_Relation *thisptr - - - def __cinit__(self, do_not_construct_manually=False): - """ - The Cython constructor. - - See :class:`Poly_Con_Relation` for documentation. - - TESTS:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.nothing() - nothing - """ - assert(do_not_construct_manually) - self.thisptr = NULL - - - def __dealloc__(self): - """ - The Cython destructor. - """ - assert self.thisptr!=NULL, 'Do not construct Poly_Con_Relation objects manually!' - del self.thisptr - - - def implies(self, Poly_Con_Relation y): - r""" - Test whether ``self`` implies ``y``. - - INPUT: - - - ``y`` -- a :class:`Poly_Con_Relation`. - - OUTPUT: - - Boolean. ``True`` if and only if ``self`` implies ``y``. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: nothing = Poly_Con_Relation.nothing() - sage: nothing.implies( nothing ) - True - """ - return self.thisptr.implies(y.thisptr[0]) - - - @classmethod - def nothing(cls): - r""" - Return the assertion that says nothing. - - OUTPUT: - - A :class:`Poly_Con_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.nothing() - nothing - """ - return _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation_nothing()) - - - @classmethod - def is_disjoint(cls): - r""" - Return the assertion "The polyhedron and the set of points - satisfying the constraint are disjoint". - - OUTPUT: - - A :class:`Poly_Con_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.is_disjoint() - is_disjoint - """ - return _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation_is_disjoint()) - - - @classmethod - def strictly_intersects(cls): - r""" - Return the assertion "The polyhedron intersects the set of - points satisfying the constraint, but it is not included in - it". - - OUTPUT: - - A :class:`Poly_Con_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.strictly_intersects() - strictly_intersects - """ - return _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation_strictly_intersects()) - - - @classmethod - def is_included(cls): - r""" - Return the assertion "The polyhedron is included in the set of - points satisfying the constraint". - - OUTPUT: - - A :class:`Poly_Con_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.is_included() - is_included - """ - return _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation_is_included()) - - - @classmethod - def saturates(cls): - r""" - Return the assertion "". - - OUTPUT: - - A :class:`Poly_Con_Relation`. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.saturates() - saturates - """ - return _wrap_Poly_Con_Relation(PPL_Poly_Con_Relation_saturates()) - - - def ascii_dump(self): - r""" - Write an ASCII dump to stderr. - - EXAMPLES:: - - sage: sage_cmd = 'from sage.libs.ppl import Poly_Con_Relation\n' - sage: sage_cmd += 'Poly_Con_Relation.nothing().ascii_dump()\n' - sage: from sage.tests.cmdline import test_executable - sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest - sage: print(err) # long time py2 - /... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - NOTHING - sage: print(err) # long time py3 - NOTHING/... DeprecationWarning: The Sage wrappers around ppl are now superseded by the standalone pplpy. - Please use import 'ppl' instead of 'sage.libs.ppl'. - See http://trac.sagemath.org/23024 for details. - ... - """ - self.thisptr.ascii_dump() - - - def OK(self, check_non_empty=False): - """ - Check if all the invariants are satisfied. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.nothing().OK() - True - """ - return self.thisptr.OK() - - - def __repr__(self): - r""" - Return a string representation. - - OUTPUT: - - String. - - EXAMPLES:: - - sage: from sage.libs.ppl import Poly_Con_Relation - sage: Poly_Con_Relation.nothing().__repr__() - 'nothing' - """ - rel = [] - if self.implies(Poly_Con_Relation.is_disjoint()): - rel.append('is_disjoint') - if self.implies(Poly_Con_Relation.strictly_intersects()): - rel.append('strictly_intersects') - if self.implies(Poly_Con_Relation.is_included()): - rel.append('is_included') - if self.implies(Poly_Con_Relation.saturates()): - rel.append('saturates') - - if rel: - return ', '.join(rel) - else: - return 'nothing' - - -#################################################### -#################################################### -#################################################### -#################################################### diff --git a/src/sage/libs/ppl_shim.cc b/src/sage/libs/ppl_shim.cc deleted file mode 100644 index ba8bb290eec..00000000000 --- a/src/sage/libs/ppl_shim.cc +++ /dev/null @@ -1,93 +0,0 @@ -#include "ppl_shim.hh" - - - - -// /************************************************************/ -Generator* new_line(const Linear_Expression& e) -{ - return new Generator(Generator::line(e)); -} - -Generator* new_ray(const Linear_Expression& e) -{ - return new Generator(Generator::ray(e)); -} - -Generator* new_point(const Linear_Expression& e, Coefficient d) -{ - return new Generator(Generator::point(e, d)); -} - -Generator* new_closure_point(const Linear_Expression& e, Coefficient d) -{ - return new Generator(Generator::closure_point(e, d)); -} - - -/************************************************************/ -Poly_Gen_Relation* new_relation_with(const Polyhedron &p, const Generator &g) -{ - return new Poly_Gen_Relation(p.relation_with(g)); -} - -Poly_Con_Relation* new_relation_with(const Polyhedron &p, const Constraint &c) -{ - return new Poly_Con_Relation(p.relation_with(c)); -} - - -/************************************************************/ -typedef Generator_System::const_iterator* gs_iterator_ptr; - -gs_iterator_ptr init_gs_iterator(const Generator_System &gs) -{ - return new Generator_System::const_iterator(gs.begin()); -} - -Generator next_gs_iterator(gs_iterator_ptr gsi_ptr) -{ - return *(*gsi_ptr)++; -} - -bool is_end_gs_iterator(const Generator_System &gs, gs_iterator_ptr gsi_ptr) -{ - return (*gsi_ptr) == gs.end(); -} - -void delete_gs_iterator(gs_iterator_ptr gsi_ptr) -{ - delete gsi_ptr; -} - - -/************************************************************/ -typedef Constraint_System::const_iterator* cs_iterator_ptr; - -cs_iterator_ptr init_cs_iterator(const Constraint_System &cs) -{ - return new Constraint_System::const_iterator(cs.begin()); -} - -Constraint next_cs_iterator(cs_iterator_ptr csi_ptr) -{ - return *(*csi_ptr)++; -} - -bool is_end_cs_iterator(const Constraint_System &cs, cs_iterator_ptr csi_ptr) -{ - return (*csi_ptr) == cs.end(); -} - -void delete_cs_iterator(cs_iterator_ptr csi_ptr) -{ - delete csi_ptr; -} - - -/************************************************************/ -Generator* new_MIP_optimizing_point(const MIP_Problem& problem) -{ - return new Generator(problem.optimizing_point()); -} - diff --git a/src/sage/libs/ppl_shim.hh b/src/sage/libs/ppl_shim.hh deleted file mode 100644 index eef289c71ea..00000000000 --- a/src/sage/libs/ppl_shim.hh +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef PPL_SHIM__H -#define PPL_SHIM__H - - - -#include - -using namespace Parma_Polyhedra_Library; - - -// access Generator's static methods -Generator* new_line(const Linear_Expression& e); -Generator* new_ray(const Linear_Expression& e); -Generator* new_point(const Linear_Expression& e, Coefficient d); -Generator* new_closure_point(const Linear_Expression& e, Coefficient d); - -// Poly_Gen_Relation/Poly_Con_Relation have no default constructor -Poly_Gen_Relation* new_relation_with(const Polyhedron &p, const Generator &g); -Poly_Con_Relation* new_relation_with(const Polyhedron &p, const Constraint &c); - - -// Iterator for Generator_System -typedef Generator_System::const_iterator* gs_iterator_ptr; -gs_iterator_ptr init_gs_iterator(const Generator_System &gs); -Generator next_gs_iterator(gs_iterator_ptr); -bool is_end_gs_iterator(const Generator_System &gs, gs_iterator_ptr gsi_ptr); -void delete_gs_iterator(gs_iterator_ptr); - - -// Iterator for Constraint_System -typedef Constraint_System::const_iterator* cs_iterator_ptr; -cs_iterator_ptr init_cs_iterator(const Constraint_System &cs); -Constraint next_cs_iterator(cs_iterator_ptr); -bool is_end_cs_iterator(const Constraint_System &cs, cs_iterator_ptr csi_ptr); -void delete_cs_iterator(cs_iterator_ptr); - - -// workarounds for Cython issues with exceptions while returning references -Generator* new_MIP_optimizing_point(const MIP_Problem& problem); - - - -#endif diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 0ac4169402c..94db9fd9f09 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -578,7 +578,7 @@ cdef extern from "singular/Singular/libsingular.h": poly *p_Homogen (poly *p, int varnum, ring *r) - # return whether a polynomial is homogenous + # return whether a polynomial is homogeneous int p_IsHomogeneous(poly *p, const ring *r) @@ -1024,10 +1024,10 @@ cdef extern from "singular/polys/sbuckets.h": #sBucket is actually a class, but we handle it opaquely, so we call it a "struct" here. ctypedef struct sBucket: pass - + #create an sBucket sBucket *sBucketCreate(ring *r) - + #destroy an sBucket (note: pointer to pointer) void sBucketDestroy(sBucket **bucket); @@ -1035,7 +1035,7 @@ cdef extern from "singular/polys/sbuckets.h": #(use when monomials are distinct). #assumes length <= 0 || pLength(p) == length void sBucketClearMerge(sBucket *bucket, poly **p, int *length) - + #add contents of sBucket into polynomial an clear bucket #(can handle repeated monomials) void sBucketClearAdd(sBucket *bucket, poly **p, int *length) @@ -1043,10 +1043,10 @@ cdef extern from "singular/polys/sbuckets.h": #inline versions that in addition clear the pointer bucket afterwards void sBucketDestroyMerge(sBucket *bucket, poly **p, int *length) void sBucketDestroyAdd(sBucket *bucket, poly *p, int *length) - + #delete bucket constant and clear pointer void sBucketDeleteAndDestroy(sBucket **bucket_pt); - + #merge p into bucket (distinct monomials assumed) #destroys poly in the process void sBucket_Merge_p(sBucket *bucket, poly *p, int lp); diff --git a/src/sage/libs/singular/option.pyx b/src/sage/libs/singular/option.pyx index edfcb2ef44b..fc3b576ff71 100644 --- a/src/sage/libs/singular/option.pyx +++ b/src/sage/libs/singular/option.pyx @@ -353,7 +353,7 @@ cdef class LibSingularOptions(LibSingularOptions_abstract): is ring dependent. By default, it is set for rings with global degree orderings and not set for all other rings. - - ``red_through`` or ``redThrough`` - for inhomogenous input, + - ``red_through`` or ``redThrough`` - for inhomogeneous input, polynomial reductions during standard basis computations are never postponed, but always finished through. This option is ring dependent. By default, it is set for rings with global diff --git a/src/sage/libs/symmetrica/schur.pxi b/src/sage/libs/symmetrica/schur.pxi index 209ce5af4d2..501acf5b7e0 100644 --- a/src/sage/libs/symmetrica/schur.pxi +++ b/src/sage/libs/symmetrica/schur.pxi @@ -182,7 +182,7 @@ def compute_schur_with_alphabet_symmetrica(part, length, alphabet='x'): def compute_homsym_with_alphabet_symmetrica(n, length, alphabet='x'): """ - computes the expansion of a homogenous(=complete) symmetric + computes the expansion of a homogeneous(=complete) symmetric function labeled by a INTEGER number as a POLYNOM erg. The object number may also be a PARTITION or a HOM_SYM object. The INTEGER laenge specifies the length of the alphabet. diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index 05f82dbd7db..bca17099360 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -17,7 +17,7 @@ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger # Copyright (C) 2016 Travis Scrimshaw @@ -26,8 +26,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.categories.homset import Hom from sage.categories.morphism import Morphism @@ -1246,8 +1246,10 @@ def coord_functions(self, chart1=None, chart2=None): - 8*U + V**3 + 4*V**2 - 8*V)/(4*(U - V))) on the Chart (M, (U, V)) """ - dom1 = self._domain; dom2 = self._codomain - def_chart1 = dom1._def_chart; def_chart2 = dom2._def_chart + dom1 = self._domain + dom2 = self._codomain + def_chart1 = dom1._def_chart + def_chart2 = dom2._def_chart if chart1 is None: chart1 = def_chart1 if chart2 is None: @@ -1268,7 +1270,8 @@ def coord_functions(self, chart1=None, chart2=None): chart1.multifunction(*coord_functions) return self._coord_expression[(chart1, chart2)] # Some change of coordinates must be performed - change_start = [] ; change_arrival = [] + change_start = [] + change_arrival = [] for (ochart1, ochart2) in self._coord_expression: if chart1 == ochart1: change_arrival.append(ochart2) diff --git a/src/sage/manifolds/differentiable/automorphismfield.py b/src/sage/manifolds/differentiable/automorphismfield.py index b0fe7f1641c..ac6e63cf4af 100644 --- a/src/sage/manifolds/differentiable/automorphismfield.py +++ b/src/sage/manifolds/differentiable/automorphismfield.py @@ -1214,7 +1214,8 @@ def __invert__(self): inv_latex_name = r'\left(' + self._latex_name + \ r'\right)^{-1}' fmodule = self._fmodule - si = fmodule._sindex ; nsi = fmodule._rank + si + si = fmodule._sindex + nsi = fmodule._rank + si self._inverse = fmodule.automorphism(name=inv_name, latex_name=inv_latex_name) for frame in self._components: diff --git a/src/sage/manifolds/differentiable/degenerate_submanifold.py b/src/sage/manifolds/differentiable/degenerate_submanifold.py index 2aada97535b..eae84a65fa8 100644 --- a/src/sage/manifolds/differentiable/degenerate_submanifold.py +++ b/src/sage/manifolds/differentiable/degenerate_submanifold.py @@ -602,7 +602,7 @@ def induced_metric(self): OUTPUT: - - induced mettric, as an instance of + - induced metric, as an instance of :class:`~sage.manifolds.differentiable.metric.DegenerateMetric` EXAMPLES: diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index c5375f06231..b2a9531caf2 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -21,20 +21,21 @@ """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Eric Gourgoulhon # Copyright (C) 2015 Michal Bejger # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.manifolds.continuous_map import ContinuousMap from sage.parallel.decorate import parallel from sage.parallel.parallelism import Parallelism + class DiffMap(ContinuousMap): r""" Differentiable map between two differentiable manifolds. @@ -762,7 +763,8 @@ def differential_functions(self, chart1=None, chart2=None): True """ - dom1 = self._domain; dom2 = self._codomain + dom1 = self._domain + dom2 = self._codomain if chart1 is None: chart1 = dom1._def_chart if chart2 is None: @@ -1084,7 +1086,8 @@ def paral_comp(tcomp,chart1,chart2,coord2_1,jacob, "fields on {}".format(dom2)) resu_rst = [] for chart_pair in self._coord_expression: - chart1 = chart_pair[0]; chart2 = chart_pair[1] + chart1 = chart_pair[0] + chart2 = chart_pair[1] ch2dom = chart2._domain if ch2dom.is_subset(tdom): self_r = self.restrict(chart1._domain, subcodomain=ch2dom) @@ -1194,7 +1197,8 @@ def pushforward(self, tensor): # A pair of charts (chart1, chart2) where the computation # is feasible is searched, privileging the default chart of the # map's domain for chart1 - chart1 = None; chart2 = None + chart1 = None + chart2 = None def_chart1 = dom1.default_chart() def_chart2 = self._codomain.default_chart() if (def_chart1._frame in tensor._components diff --git a/src/sage/manifolds/differentiable/metric.py b/src/sage/manifolds/differentiable/metric.py index 46124515da2..4455493500a 100644 --- a/src/sage/manifolds/differentiable/metric.py +++ b/src/sage/manifolds/differentiable/metric.py @@ -2444,7 +2444,8 @@ def inverse(self, expansion_symbol=None, order=1): if frame not in self._inverse._components: # the computation is necessary fmodule = self._fmodule - si = fmodule._sindex ; nsi = fmodule._rank + si + si = fmodule._sindex + nsi = fmodule._rank + si dom = self._domain cinv = CompFullySym(fmodule._ring, frame, 2, start_index=si, output_formatter=fmodule._output_formatter) diff --git a/src/sage/manifolds/differentiable/vectorframe.py b/src/sage/manifolds/differentiable/vectorframe.py index b8f8ff8eba8..3482c9872dd 100644 --- a/src/sage/manifolds/differentiable/vectorframe.py +++ b/src/sage/manifolds/differentiable/vectorframe.py @@ -206,7 +206,7 @@ """ -#****************************************************************************** +# ***************************************************************************** # Copyright (C) 2013-2018 Eric Gourgoulhon # Copyright (C) 2013, 2014 Michal Bejger # Copyright (C) 2016 Travis Scrimshaw @@ -214,14 +214,15 @@ # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.tensor.modules.free_module_basis import (FreeModuleBasis, FreeModuleCoBasis) from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule from sage.misc.cachefunc import cached_method + class CoFrame(FreeModuleCoBasis): r""" Coframe on a differentiable manifold. @@ -1466,7 +1467,8 @@ def at(self, point): ts_frame_bases[self] = basis # Update of the change of bases in the tangent space: for frame_pair, automorph in self._domain._frame_changes.items(): - frame1 = frame_pair[0]; frame2 = frame_pair[1] + frame1 = frame_pair[0] + frame2 = frame_pair[1] if frame1 is self: fr2 = None for frame in ts_frame_bases: diff --git a/src/sage/manifolds/local_frame.py b/src/sage/manifolds/local_frame.py index 85c28d01850..0b29c9ab2f6 100644 --- a/src/sage/manifolds/local_frame.py +++ b/src/sage/manifolds/local_frame.py @@ -1136,7 +1136,8 @@ def at(self, point): vbf_frame_bases[self] = basis # Update of the change of bases in the fiber: for frame_pair, automorph in self._vbundle._frame_changes.items(): - frame1 = frame_pair[0]; frame2 = frame_pair[1] + frame1 = frame_pair[0] + frame2 = frame_pair[1] if frame1 is self: fr2 = None for frame in vbf_frame_bases: diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index f5875dc7291..73a0843ed83 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -2825,7 +2825,7 @@ def _lmul_(self, number): # different chart chart_coords = chart[:] var_not_in_chart = [s for s in var - if not s in chart_coords] + if s not in chart_coords] any_in_other_chart = False if var_not_in_chart: for other_chart in self._domain.atlas(): diff --git a/src/sage/manifolds/utilities.py b/src/sage/manifolds/utilities.py index 2dc01e28149..c45fe0ca506 100644 --- a/src/sage/manifolds/utilities.py +++ b/src/sage/manifolds/utilities.py @@ -12,7 +12,7 @@ """ -#****************************************************************************** +# ***************************************************************************** # # Copyright (C) 2015 Michal Bejger # Copyright (C) 2015, 2017 Eric Gourgoulhon @@ -22,10 +22,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from __future__ import division +# https://www.gnu.org/licenses/ +# **************************************************************************** from operator import pow as _pow from sage.symbolic.expression import Expression @@ -167,7 +165,8 @@ def arithmetic(self, ex, operator): minus_one_half = -one_half if (power == one_half) or (power == minus_one_half): # This is a square root or the inverse of a square root - w0 = SR.wild(0); w1 = SR.wild(1) + w0 = SR.wild(0) + w1 = SR.wild(1) sqrt_pattern = w0**one_half inv_sqrt_pattern = w0**minus_one_half sqrt_ratio_pattern1 = w0**one_half * w1**minus_one_half diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 02096919fc8..025100786ac 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -54,6 +54,10 @@ AUTHORS: - Mario Pernici (2014-07-01): modified ``rook_vector`` method - Rob Beezer (2015-05-25): modified ``is_similar`` method + +- Samuel Lelièvre (2020-09-18): improved method ``LLL_gram`` based on a patch + by William Stein posted at :trac:`5178`, moving the method from its initial + location in ``sage.matrix.integer_matrix_dense`` """ # **************************************************************************** @@ -16158,6 +16162,127 @@ cdef class Matrix(Matrix1): return all(s * (self * x) == 0 for (x, s) in K.discrete_complementarity_set()) + def LLL_gram(self, flag=0): + """ + Return the LLL transformation matrix for this Gram matrix. + + That is, the transformation matrix U over ZZ of determinant 1 + that transforms the lattice with this matrix as Gram matrix + to a lattice that is LLL-reduced. + + Always works when ``self`` is positive definite, + might work in some semidefinite and indefinite cases. + + INPUT: + + - ``self`` -- the Gram matrix of a quadratic form or of + a lattice equipped with a bilinear form + + - ``flag`` -- an optional flag passed to ``qflllgram``. + According to :pari:`qflllgram`'s documentation the options are: + + - ``0`` -- (default), assume that ``self`` has either exact + (integral or rational) or real floating point entries. + The matrix is rescaled, converted to integers and the + behavior is then as in ``flag=1``. + + - ``1`` -- assume that G is integral. + Computations involving Gram-Schmidt vectors are + approximate, with precision varying as needed. + + OUTPUT: + + A dense matrix ``U`` over the integers with determinant 1 + such that ``U.T * M * U`` is LLL-reduced. + + ALGORITHM: + + Calls PARI's :pari:`qflllgram`. + + EXAMPLES: + + Create a Gram matrix and LLL-reduce it:: + + sage: M = Matrix(ZZ, 2, 2, [5, 3, 3, 2]) + sage: U = M.LLL_gram() + sage: MM = U.transpose() * M * U + sage: M, U, MM + ( + [5 3] [-1 1] [1 0] + [3 2], [ 1 -2], [0 1] + ) + + For a Gram matrix over RR with a length one first vector and + a very short second vector, the LLL-reduced basis is obtained + by swapping the two basis vectors (and changing sign to + preserve orientation). :: + + sage: M = Matrix(RDF, 2, 2, [1, 0, 0, 1e-5]) + sage: M.LLL_gram() + [ 0 -1] + [ 1 0] + + The algorithm might work for some semidefinite and indefinite forms:: + + sage: Matrix(ZZ, 2, 2, [2, 6, 6, 3]).LLL_gram() + [-3 -1] + [ 1 0] + sage: Matrix(ZZ, 2, 2, [1, 0, 0, -1]).LLL_gram() + [ 0 -1] + [ 1 0] + + However, it might fail for others, either raising a ``ValueError``:: + + sage: Matrix(ZZ, 1, 1, [0]).LLL_gram() + Traceback (most recent call last): + ... + ValueError: qflllgram did not return a square matrix, + perhaps the matrix is not positive definite + + sage: Matrix(ZZ, 2, 2, [0, 1, 1, 0]).LLL_gram() + Traceback (most recent call last): + ... + ValueError: qflllgram did not return a square matrix, + perhaps the matrix is not positive definite + + or running forever:: + + sage: Matrix(ZZ, 2, 2, [-5, -1, -1, -5]).LLL_gram() # not tested + Traceback (most recent call last): + ... + RuntimeError: infinite loop while calling qflllgram + + Nonreal input leads to a value error:: + + sage: Matrix(2, 2, [CDF(1, 1), 0, 0, 1]).LLL_gram() + Traceback (most recent call last): + ... + ValueError: qflllgram failed, perhaps the matrix is not positive definite + """ + if self._nrows != self._ncols: + raise ArithmeticError("self must be a square matrix") + n = self.nrows() + P = self.__pari__() + try: + if self.base_ring() == ZZ: + U = P.lllgramint() + else: + U = P.qflllgram(flag) + except (RuntimeError, ArithmeticError) as msg: + raise ValueError("qflllgram failed, " + "perhaps the matrix is not positive definite") + if U.matsize() != [n, n]: + raise ValueError("qflllgram did not return a square matrix, " + "perhaps the matrix is not positive definite") + from sage.matrix.matrix_space import MatrixSpace + MS = MatrixSpace(ZZ, n) + U = MS(U.sage()) + # Fix last column so that det = +1 + from sage.rings.finite_rings.finite_field_constructor import FiniteField + if U.change_ring(FiniteField(3)).det() != 1: # p = 3 is enough to decide + U.rescale_col(n - 1, -1) + return U + # a limited number of access-only properties are provided for matrices @property def T(self): diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx index 92d7da402d2..0fff7a1f006 100644 --- a/src/sage/matrix/matrix_gap.pyx +++ b/src/sage/matrix/matrix_gap.pyx @@ -63,9 +63,9 @@ cdef class Matrix_gap(Matrix_dense): sage: for ring in [ZZ, QQ, UniversalCyclotomicField(), GF(2), GF(3)]: ....: M = MatrixSpace(ring, 2, implementation='gap') - ....: TestSuite(M).run() + ....: TestSuite(M).run(skip=['_test_construction']) ....: M = MatrixSpace(ring, 2, 3, implementation='gap') - ....: TestSuite(M).run() + ....: TestSuite(M).run(skip=['_test_construction']) """ def __init__(self, parent, entries=None, copy=None, bint coerce=True): r""" diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 7753468c3f4..fa6971504bc 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -2713,109 +2713,6 @@ cdef class Matrix_integer_dense(Matrix_dense): #################################################################################### # LLL #################################################################################### - def LLL_gram(self, flag = 0): - """ - LLL reduction of the lattice whose gram matrix is ``self``, - assuming that ``self`` is positive definite. - - .. WARNING:: - - The algorithm does not check if ``self`` is positive definite. - - INPUT: - - - ``self`` -- a gram matrix of a positive definite quadratic form - - - ``flag`` -- an optional flag passed to ``qflllgram``. - According to :pari:`qflllgram`'s documentation - the options are: - - - ``0`` -- (default), assume that ``self`` has either exact - (integral or rational) or real floating point entries. - The matrix is rescaled, converted to integers and the - behavior is then as in ``flag = 1``. - - - ``1`` -- assume that G is integral. - Computations involving Gram-Schmidt vectors are - approximate, with precision varying as needed. - - - OUTPUT: - - A dense matrix ``U`` over the integers that represents a unimodular - transformation matrix such that ``U.T * M * U`` is LLL-reduced. - - ALGORITHM: - - Calls PARI's :pari:`qflllgram`. - - EXAMPLES:: - - sage: M = Matrix(ZZ, 2, 2, [5,3,3,2]) ; M - [5 3] - [3 2] - sage: U = M.LLL_gram(); U - [-1 1] - [ 1 -2] - sage: U.transpose() * M * U - [1 0] - [0 1] - - The algorithm might work for some semidefinite and indefinite forms:: - - sage: Matrix(ZZ,2,2,[2,6,6,3]).LLL_gram() - [-3 -1] - [ 1 0] - sage: Matrix(ZZ,2,2,[1,0,0,-1]).LLL_gram() - [ 0 -1] - [ 1 0] - - However, it might fail for others by raising a ``ValueError``:: - - sage: Matrix(ZZ, 1,1,[0]).LLL_gram() - Traceback (most recent call last): - ... - ValueError: qflllgram did not return a square matrix, - perhaps the matrix is not positive definite - - sage: Matrix(ZZ, 2, 2, [0,1,1,0]).LLL_gram() - Traceback (most recent call last): - ... - ValueError: qflllgram did not return a square matrix, - perhaps the matrix is not positive definite - - or by running forever:: - - sage: Matrix(ZZ, 2, 2, [-5, -1, -1, -5]).LLL_gram() # not tested - Traceback (most recent call last): - ... - RuntimeError: infinite loop while calling qflllgram - - - - - """ - if self._nrows != self._ncols: - raise ArithmeticError("self must be a square matrix") - - n = self.nrows() - # maybe should be /unimodular/ matrices ? - P = self.__pari__() - try: - U = P.qflllgram(flag) - except (RuntimeError, ArithmeticError) as msg: - raise ValueError("qflllgram failed, " - "perhaps the matrix is not positive definite") - if U.matsize() != [n, n]: - raise ValueError("qflllgram did not return a square matrix, " - "perhaps the matrix is not positive definite"); - MS = matrix_space.MatrixSpace(ZZ,n) - U = MS(U.sage()) - # Fix last column so that det = +1 - if U.det() == -1: - for i in range(n): - U[i,n-1] = - U[i,n-1] - return U def BKZ(self, delta=None, algorithm="fpLLL", fp=None, block_size=10, prune=0, use_givens=False, precision=0, proof=None, **kwds): diff --git a/src/sage/matroids/basis_exchange_matroid.pxd b/src/sage/matroids/basis_exchange_matroid.pxd index df59b3ba4a4..aec3bb54c42 100644 --- a/src/sage/matroids/basis_exchange_matroid.pxd +++ b/src/sage/matroids/basis_exchange_matroid.pxd @@ -1,4 +1,5 @@ from sage.data_structures.bitset cimport * +from sage.data_structures.bitset_base cimport bitset_t, bitset_s from .matroid cimport Matroid from .set_system cimport SetSystem diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index fbe7be949ad..ef90c261ad0 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -38,14 +38,13 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' - from .matroid cimport Matroid from .set_system cimport SetSystem from copy import copy from itertools import combinations, permutations +from sage.data_structures.bitset_base cimport * cdef class BasisExchangeMatroid(Matroid): r""" @@ -241,7 +240,7 @@ cdef class BasisExchangeMatroid(Matroid): """ bitset_clear(I) for f in F: - bitset_add(I, self._idx[f]) + bitset_add(I, self._idx[f]) cdef __unpack(self, bitset_t I): """ @@ -1042,6 +1041,7 @@ cdef class BasisExchangeMatroid(Matroid): sage: setprint(M.components()) [{0, 1, 3, 4}, {2, 5}] """ + cdef long i,j,e if not self._E: return SetSystem(self._E) cdef bitset_t *comp @@ -1056,7 +1056,7 @@ cdef class BasisExchangeMatroid(Matroid): cdef bitset_t active_rows bitset_init(active_rows,self.full_rank()+1) - bitset_set_first_n(active_rows, self.full_rank()) + bitset_set_first_n(active_rows, self.full_rank()) i=0 while i>=0: j = bitset_first(active_rows) @@ -2174,7 +2174,7 @@ cdef class BasisExchangeMatroid(Matroid): bitset_clear(other._input) i = bitset_first(self._input) while i != -1: - bitset_add(other._input, morph[i]) + bitset_add(other._input, morph[i]) i = bitset_next(self._input, i + 1) if self.__is_independent(self._input) != other.__is_independent(other._input): return False diff --git a/src/sage/matroids/basis_matroid.pyx b/src/sage/matroids/basis_matroid.pyx index 99201df57c7..c269d0f76c4 100644 --- a/src/sage/matroids/basis_matroid.pyx +++ b/src/sage/matroids/basis_matroid.pyx @@ -72,8 +72,7 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' - +from sage.data_structures.bitset_base cimport * from sage.structure.richcmp cimport rich_to_bool from .matroid cimport Matroid from .basis_exchange_matroid cimport BasisExchangeMatroid @@ -162,6 +161,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): """ cdef SetSystem NB cdef long i + cdef mp_bitcnt_t bc if isinstance(M, BasisMatroid): BasisExchangeMatroid.__init__(self, groundset=(M)._E, rank=(M)._matroid_rank) @@ -912,7 +912,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): bitset_clear(b2) j = bitset_first(self._b) while j != -1: - bitset_add(b2, morph[j]) + bitset_add(b2, morph[j]) j = bitset_next(self._b, j + 1) if bitset_in((other)._bb, set_to_index(b2)): bitset_free(b2) @@ -975,7 +975,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): Internal version that does no input checking. EXAMPLES:: - + sage: from sage.matroids.advanced import * sage: M = BasisMatroid(matroids.Wheel(3)) sage: N = BasisMatroid(matroids.CompleteGraphic(4)) @@ -996,7 +996,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): if self.full_rank() != other.full_rank(): return None if self.full_rank() == 0: - return {self.groundset_list()[i]: other.groundset_list()[i] for i in xrange(len(self))} + return {self.groundset_list()[i]: other.groundset_list()[i] for i in xrange(len(self))} if self.bases_count() != other.bases_count(): return None @@ -1036,7 +1036,7 @@ cdef class BasisMatroid(BasisExchangeMatroid): return morphism return self.nonbases()._isomorphism(other.nonbases(), PS, PO) - + cpdef _is_isomorphic(self, other, certificate=False): """ Return if this matroid is isomorphic to the given matroid. diff --git a/src/sage/matroids/extension.pyx b/src/sage/matroids/extension.pyx index cb47076bebb..f4f9d44516b 100644 --- a/src/sage/matroids/extension.pyx +++ b/src/sage/matroids/extension.pyx @@ -29,7 +29,7 @@ Methods # https://www.gnu.org/licenses/ # **************************************************************************** -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * from .basis_matroid cimport BasisMatroid from sage.arith.all import binomial diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index cf7d4db0b09..6416af13987 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -30,11 +30,11 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' from libc.string cimport memcpy, memset from cpython.object cimport Py_EQ, Py_NE from cysignals.memory cimport sig_malloc, sig_realloc, sig_free +from sage.data_structures.bitset_base cimport * from sage.matrix.matrix2 cimport Matrix from sage.rings.all import ZZ, FiniteField, GF from sage.rings.integer cimport Integer @@ -524,7 +524,7 @@ cdef class LeanMatrix: `P_rows` and `Q_rows` must be disjoint subsets of row indices. `P_cols` and `Q_cols` must be disjoint subsets of column indices. - Internal version does not verify the above properties hold. + Internal version does not verify the above properties hold. INPUT: @@ -563,14 +563,14 @@ cdef class LeanMatrix: optional column `z2` attached. Let `E_2` be the submatrix using rows `U_2` and columns `V_1` with optional column `z1` attached. - If `E_1` and `E_2` can be extended to a ``m``-separator, then it - returns `True, E`, where `E` is a ``m``-separator. Otherwise it + If `E_1` and `E_2` can be extended to a ``m``-separator, then it + returns `True, E`, where `E` is a ``m``-separator. Otherwise it returns `False, None` `U_1` and `U_2` must be disjoint subsets of row indices. `V_1` and `V_2` must be disjoint subsets of column indices. - Internal version does not verify the above properties hold. + Internal version does not verify the above properties hold. INPUT: @@ -1303,7 +1303,7 @@ cdef class BinaryMatrix(LeanMatrix): cdef BinaryMatrix A = BinaryMatrix(len(rows), len(columns)) for r from 0 <= r < len(rows): for c from 0 <= c < len(columns): - if bitset_in(self._M[rows[r]], columns[c]): + if bitset_in(self._M[rows[r]], columns[c]): bitset_add(A._M[r], c) return A @@ -1389,6 +1389,7 @@ cdef class BinaryMatrix(LeanMatrix): d[c] = [i] Q = BinaryMatrix(len(d), self._nrows) i = 0 + cdef mp_bitcnt_t j for c in sorted(d): for j in d[c]: bitset_add(Q._M[i], j) @@ -2628,7 +2629,7 @@ cdef class QuaternaryMatrix(LeanMatrix): sig_free(cols) bitset_free(mask) return A, order - + def __neg__(self): """ Negate the matrix. diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index ed808974d2d..0fe5b543889 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -111,9 +111,9 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' from cpython.object cimport Py_EQ, Py_NE +from sage.data_structures.bitset_base cimport * from sage.structure.richcmp cimport rich_to_bool from sage.matroids.matroid cimport Matroid from sage.matroids.basis_exchange_matroid cimport BasisExchangeMatroid diff --git a/src/sage/matroids/set_system.pxd b/src/sage/matroids/set_system.pxd index b3af6249e23..c1b0b75f1e5 100644 --- a/src/sage/matroids/set_system.pxd +++ b/src/sage/matroids/set_system.pxd @@ -23,7 +23,7 @@ cdef class SetSystem: cdef SetSystem _groundset_partition(self, SetSystem P, list cnt) cdef long subset_characteristic(self, SetSystem P, long e) cdef subsets_partition(self, SetSystem P=*, E=*) - cdef _distinguish(self, v) + cdef _distinguish(self, Py_ssize_t v) cpdef is_connected(self) cdef initial_partition(self, SetSystem P=*, E=*) diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx index b4f9a7fd368..c03275ff661 100644 --- a/src/sage/matroids/set_system.pyx +++ b/src/sage/matroids/set_system.pyx @@ -27,7 +27,7 @@ Methods #***************************************************************************** from cysignals.memory cimport check_allocarray, check_reallocarray, sig_free -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * # SetSystem @@ -292,7 +292,7 @@ cdef class SetSystem: bitset_init(self._subsets[self._len], self._bitset_size) bitset_clear(self._subsets[self._len]) for x in X: - bitset_add(self._subsets[self._len], self._idx[x]) + bitset_add(self._subsets[self._len], self._idx[x]) self._len += 1 cdef inline _subset(self, long k): @@ -479,7 +479,7 @@ cdef class SetSystem: EP.append(ep) return EP, hash(tuple(eh)) - cdef _distinguish(self, v): + cdef _distinguish(self, Py_ssize_t v): """ Helper method for partition methods below. """ @@ -667,7 +667,7 @@ cdef class SetSystem: sage: S._isomorphism(S) {} """ - cdef long l, p + cdef long l, p, v if SP is None or OP is None: SP, SEP, sh = self._equitable_partition() OP, OEP, oh = other._equitable_partition() @@ -737,6 +737,7 @@ cdef class SetSystem: sage: any(M.is_field_isomorphism(N, p) for p in Permutations(range(6))) False """ + cdef long v if SP is None or OP is None: SP, SEP, sh = self._equitable_partition() OP, OEP, oh = other._equitable_partition() diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index 841e82d1c8f..72aa0034e2f 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -25,7 +25,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/data_structures/bitset.pxi' +from sage.data_structures.bitset_base cimport * import sage.matroids.matroid import sage.matroids.basis_exchange_matroid from .minor_matroid import MinorMatroid diff --git a/src/sage/misc/func_persist.py b/src/sage/misc/func_persist.py index 287743c9d7f..8e03606f08c 100644 --- a/src/sage/misc/func_persist.py +++ b/src/sage/misc/func_persist.py @@ -32,20 +32,20 @@ def bern(n): ``func_persist`` of the current working directory, with one file for each evaluation of the function. """ -from __future__ import absolute_import - ######################################################################## # Copyright (C) 2006 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ######################################################################## -import inspect, os +import inspect +import os from . import persist + class func_persist: r""" Put ``@func_persist`` right before your function diff --git a/src/sage/misc/sage_unittest.py b/src/sage/misc/sage_unittest.py index 8fc8ba71131..f243489e912 100644 --- a/src/sage/misc/sage_unittest.py +++ b/src/sage/misc/sage_unittest.py @@ -50,6 +50,7 @@ class TestSuite(object): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index a7c30d297ff..cde7712117d 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -381,8 +381,10 @@ def __name__(self): if obj is self: return name # then search object that contains self as method - import gc, copy + import gc + import copy gc.collect() + def is_class(gc_ref): if not isinstance(gc_ref, dict): return False diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 082a733f780..ab4bd5ecd1f 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -1167,7 +1167,7 @@ def __mul__(self, other): base_field = self.base_ring() return ModularAbelianVariety(groups, lattice, base_field, check=False) - def quotient(self, other): + def quotient(self, other, **kwds): """ Compute the quotient of self and other, where other is either an abelian subvariety of self or a finite subgroup of self. @@ -1176,6 +1176,7 @@ def quotient(self, other): - ``other`` - a finite subgroup or subvariety + - further named arguments, that are currently ignored. OUTPUT: a pair (A, phi) with phi the quotient map from self to A diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 8f89c06c9b5..5938e777cf0 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -3550,7 +3550,7 @@ def vector_space_span_of_basis(self, basis, check=True): """ return FreeModule_submodule_with_basis_field(self.ambient_vector_space(), basis, check=check) - def quotient(self, sub, check=True): + def quotient(self, sub, check=True, **kwds): """ Return the quotient of ``self`` by the given submodule sub. @@ -3562,6 +3562,9 @@ def quotient(self, sub, check=True): - ``check`` - (default: True) whether or not to check that sub is a submodule. + - further named arguments, that are passed to the constructor + of the quotient space. + EXAMPLES:: @@ -3578,7 +3581,7 @@ def quotient(self, sub, check=True): raise ArithmeticError("sub must be a subspace of self") if self.base_ring() == sage.rings.integer_ring.ZZ: from .fg_pid.fgp_module import FGP_Module - return FGP_Module(self, sub, check=False) + return FGP_Module(self, sub, check=False, **kwds) else: raise NotImplementedError("quotients of modules over rings other than fields or ZZ is not fully implemented") @@ -4577,7 +4580,7 @@ def __quotient_matrices(self, sub): return Q, L - def quotient_abstract(self, sub, check=True): + def quotient_abstract(self, sub, check=True, **kwds): r""" Return an ambient free module isomorphic to the quotient space of ``self`` modulo ``sub``, together with maps from ``self`` to @@ -4595,6 +4598,8 @@ def quotient_abstract(self, sub, check=True): - ``check`` -- (default: ``True``) whether or not to check that sub is a submodule + - further named arguments, that are currently ignored. + OUTPUT: - ``U`` -- the quotient as an abstract *ambient* free module diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index a1bbda47be3..6c6b1667b3d 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -260,9 +260,9 @@ def find_local_minimum(f, a, b, tol=1.48e-08, maxfun=500): actually test):: sage: plot(f, (x,-2.5, -1)).ymin() - -2.1827... + -2.182... sage: plot(f, (x,-2.5, 2)).ymin() - -2.1827... + -2.182... ALGORITHM: @@ -687,9 +687,10 @@ def find_fit(data, model, initial_guess = None, parameters = None, variables = N EXAMPLES: - First we create some data points of a sine function with some random + First we create some data points of a sine function with some "random" perturbations:: + sage: set_random_seed(0) sage: data = [(i, 1.2 * sin(0.5*i-0.2) + 0.1 * normalvariate(0, 1)) for i in xsrange(0, 4*pi, 0.2)] sage: var('a, b, c, x') (a, b, c, x) diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index 2c1689bd256..36efdd8f75d 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -155,7 +155,9 @@ def __call__(self, f, inputs): """ n = self.ncpus v = list(inputs) - import os, sys, signal + import os + import sys + import signal from sage.misc.persist import loads from sage.misc.temporary_file import tmp_dir dir = tmp_dir() @@ -279,7 +281,8 @@ def _subprocess(self, f, dir, args, kwds={}): sage: F._subprocess(operator.add, tmp_dir(), (1, 2)) sage: sys.stdout = saved_stdout """ - import os, sys + import os + import sys try: from importlib import reload except ImportError: diff --git a/src/sage/probability/probability_distribution.pyx b/src/sage/probability/probability_distribution.pyx index 10b5614680b..ef1ef6681ec 100644 --- a/src/sage/probability/probability_distribution.pyx +++ b/src/sage/probability/probability_distribution.pyx @@ -113,6 +113,7 @@ cdef class ProbabilityDistribution: EXAMPLES:: + sage: set_random_seed(0) sage: from sage.probability.probability_distribution import GeneralDiscreteDistribution sage: P = [0.3, 0.4, 0.3] sage: X = GeneralDiscreteDistribution(P) @@ -193,11 +194,17 @@ cdef class SphericalDistribution(ProbabilityDistribution): EXAMPLES:: sage: T = SphericalDistribution() - sage: T.get_random_element() # rel tol 1e-14 - (-0.2922296724828204, -0.9563459345927822, 0.0020668595602153454) + sage: s = T.get_random_element() + sage: s.norm() # rel tol 1e-14 + 1.0 + sage: len(s) + 3 sage: T = SphericalDistribution(dimension=4, rng='luxury') - sage: T.get_random_element() # rel tol 1e-14 - (-0.0363300434761631, 0.6459885817544098, 0.24825817345598158, 0.7209346430129753) + sage: s = T.get_random_element() + sage: s.norm() # rel tol 1e-14 + 1.0 + sage: len(s) + 4 TESTS: @@ -220,8 +227,8 @@ cdef class SphericalDistribution(ProbabilityDistribution): EXAMPLES:: sage: T = SphericalDistribution() - sage: T.get_random_element() # rel tol 1e-14 - (-0.2922296724828204, -0.9563459345927822, 0.0020668595602153454) + sage: T.get_random_element().norm() # rel tol 1e-14 + 1.0 TESTS: @@ -344,8 +351,8 @@ cdef class RealDistribution(ProbabilityDistribution): sage: a = 0 sage: b = 2 sage: T = RealDistribution('uniform', [a, b]) - sage: T.get_random_element() - 0.8175557665526867 + sage: a <= T.get_random_element() <= b + True sage: T.distribution_function(0) 0.5 sage: T.cum_distribution_function(1) @@ -358,8 +365,9 @@ cdef class RealDistribution(ProbabilityDistribution): sage: sigma = 1 sage: T = RealDistribution('gaussian', sigma) - sage: T.get_random_element() - -0.5860943109756299 + sage: s = T.get_random_element() + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 0.3989422804014327 sage: T.cum_distribution_function(1) @@ -371,8 +379,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: sigma = 3 sage: T = RealDistribution('rayleigh', sigma) - sage: T.get_random_element() - 5.748307572643492 + sage: s = T.get_random_element() + sage: s >= 0 + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 0.0 sage: T.cum_distribution_function(1) @@ -386,8 +397,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: zeta = 0 sage: sigma = 1 sage: T = RealDistribution('lognormal', [zeta, sigma]) - sage: T.get_random_element() # abs tol 1e-16 - 0.3876433713532701 + sage: s = T.get_random_element() + sage: s >= 0 + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 0.0 sage: T.cum_distribution_function(1) @@ -400,8 +414,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: a = 1 sage: b = 1 sage: T = RealDistribution('pareto', [a, b]) - sage: T.get_random_element() - 10.418714048916407 + sage: s = T.get_random_element() + sage: s >= b + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 0.0 sage: T.cum_distribution_function(1) @@ -413,8 +430,9 @@ cdef class RealDistribution(ProbabilityDistribution): sage: nu = 1 sage: T = RealDistribution('t', nu) - sage: T.get_random_element() # rel tol 1e-15 - -8.404911172800615 + sage: s = T.get_random_element() + sage: s.parent() + Real Double Field sage: T.distribution_function(0) # rel tol 1e-15 0.3183098861837906 sage: T.cum_distribution_function(1) # rel tol 1e-15 @@ -426,8 +444,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: nu1 = 9; nu2 = 17 sage: F = RealDistribution('F', [nu1,nu2]) - sage: F.get_random_element() # rel tol 1e-14 - 1.239233786115256 + sage: s = F.get_random_element() + sage: s >= 0 + True + sage: s.parent() + Real Double Field sage: F.distribution_function(1) # rel tol 1e-14 0.6695025505192798 sage: F.cum_distribution_function(3.68) # rel tol 1e-14 @@ -439,8 +460,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: nu = 1 sage: T = RealDistribution('chisquared', nu) - sage: T.get_random_element() - 0.4603367753992381 + sage: s = T.get_random_element() + sage: s >= 0 + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) +infinity sage: T.cum_distribution_function(1) # rel tol 1e-14 @@ -454,8 +478,9 @@ cdef class RealDistribution(ProbabilityDistribution): sage: a = 1 sage: b = 2.5 sage: T = RealDistribution('exppow', [a, b]) - sage: T.get_random_element() - 0.16442075306686463 + sage: s = T.get_random_element() + sage: s.parent() + Real Double Field sage: T.distribution_function(0) # rel tol 1e-14 0.5635302489930136 sage: T.cum_distribution_function(1) # rel tol 1e-14 @@ -466,8 +491,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: a = 2 sage: b = 2 sage: T = RealDistribution('beta', [a, b]) - sage: T.get_random_element() # rel tol 1e-14 - 0.7110581877139808 + sage: s = T.get_random_element() + sage: 0 <= s <= 1 + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 0.0 sage: T.cum_distribution_function(1) @@ -478,8 +506,11 @@ cdef class RealDistribution(ProbabilityDistribution): sage: a = 1 sage: b = 1 sage: T = RealDistribution('weibull', [a, b]) - sage: T.get_random_element() - 1.1867854542468694 + sage: s = T.get_random_element() + sage: s >= 0 + True + sage: s.parent() + Real Double Field sage: T.distribution_function(0) 1.0 sage: T.cum_distribution_function(1) @@ -961,8 +992,8 @@ cdef class GeneralDiscreteDistribution(ProbabilityDistribution): sage: P = [0.3, 0.4, 0.3] sage: X = GeneralDiscreteDistribution(P) - sage: X.get_random_element() - 1 + sage: X.get_random_element() in (0, 1, 2) + True Checking the distribution of samples:: @@ -972,8 +1003,8 @@ cdef class GeneralDiscreteDistribution(ProbabilityDistribution): sage: nr_samples = 10000 sage: for _ in range(nr_samples): ....: counts[X.get_random_element()] += 1 - sage: [1.0*x/nr_samples for x in counts] - [0.304200000000000, 0.397300000000000, 0.298500000000000] + sage: [1.0*x/nr_samples for x in counts] # abs tol 3e-2 + [0.3, 0.4, 0.3] The distribution probabilities will automatically be normalised:: @@ -1124,11 +1155,10 @@ cdef class GeneralDiscreteDistribution(ProbabilityDistribution): sage: P = [0.3, 0.4, 0.3] sage: X = GeneralDiscreteDistribution(P) - sage: [X.get_random_element() for _ in range(10)] - [1, 0, 1, 1, 0, 1, 1, 1, 1, 1] + sage: all(X.get_random_element() in (0,1,2) for _ in range(10)) + True sage: isinstance(X.get_random_element(), sage.rings.integer.Integer) True - """ return sage.rings.integer.Integer(gsl_ran_discrete(self.r, self.dist)) diff --git a/src/sage/quivers/algebra_elements.pxi b/src/sage/quivers/algebra_elements.pxi index 91a0ffd351f..61ceb751c36 100644 --- a/src/sage/quivers/algebra_elements.pxi +++ b/src/sage/quivers/algebra_elements.pxi @@ -19,10 +19,10 @@ AUTHORS: from cysignals.memory cimport check_malloc, check_allocarray, sig_free from cysignals.signals cimport sig_check, sig_on, sig_off -include "sage/data_structures/bitset.pxi" - from cpython.ref cimport * from cython.operator cimport predecrement as predec, postincrement as postinc + +from sage.data_structures.bitset_base cimport * from sage.structure.richcmp cimport richcmp_not_equal, rich_to_bool from sage.libs.gmp.mpn cimport mpn_cmp from libc.stdlib cimport free diff --git a/src/sage/quivers/paths.pyx b/src/sage/quivers/paths.pyx index 5d36de7e308..e531e82c76b 100644 --- a/src/sage/quivers/paths.pyx +++ b/src/sage/quivers/paths.pyx @@ -24,8 +24,7 @@ from cysignals.signals cimport sig_check, sig_on, sig_off from sage.data_structures.bounded_integer_sequences cimport * from cpython.slice cimport PySlice_GetIndicesEx from sage.structure.richcmp cimport rich_to_bool - -include "sage/data_structures/bitset.pxi" +from sage.data_structures.bitset_base cimport * cdef class QuiverPath(MonoidElement): r""" diff --git a/src/sage/repl/display/fancy_repr.py b/src/sage/repl/display/fancy_repr.py index 9461a136f2a..8528af195d0 100644 --- a/src/sage/repl/display/fancy_repr.py +++ b/src/sage/repl/display/fancy_repr.py @@ -3,14 +3,14 @@ Representations of objects """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2014 Volker Braun # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** import types from io import StringIO @@ -276,7 +276,8 @@ def __call__(self, obj, p, cycle): try: output = repr(obj) except Exception: - import sys, traceback + import sys + import traceback objrepr = object.__repr__(obj).replace("object at", "at") exc = traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]) exc = (''.join(exc)).strip() diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index e060e4c7fa0..fb21f7a9c97 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -746,7 +746,9 @@ def threejs_scripts(self, online): offline threejs graphics """ if online: - import sage.env, re, os + import sage.env + import re + import os with open(os.path.join(sage.env.THREEJS_DIR, 'build', 'three.min.js')) as f: text = f.read().replace('\n','') version = re.search(r'REVISION="(\d+)"', text).group(1) diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index ec301dd731a..3002d3db66a 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -5239,6 +5239,7 @@ class GrowthGroupFactory(UniqueFactory): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -5267,6 +5268,7 @@ class GrowthGroupFactory(UniqueFactory): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -5295,6 +5297,7 @@ class GrowthGroupFactory(UniqueFactory): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index cc70d95df73..fdcb36e5f1b 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -166,6 +166,13 @@ class CartesianProductFactory(UniqueFactory): Traceback (most recent call last): ... TypeError: Cannot create Cartesian product without factors. + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G1 = GrowthGroup('x^QQ') + sage: G2 = GrowthGroup('log(x)^ZZ') + sage: G = cartesian_product([G1, G2]) + sage: cartesian_product([G1, G2], category=G.category()) is G + True """ def create_key_and_extra_args(self, growth_groups, category, **kwds): r""" @@ -179,8 +186,16 @@ def create_key_and_extra_args(self, growth_groups, category, **kwds): sage: A = GrowthGroup('x^ZZ') sage: CartesianProductFactory('factory').create_key_and_extra_args( ....: [A], category=Sets(), order='blub') - (((Growth Group x^ZZ,), Category of sets), {'order': 'blub'}) + (((Growth Group x^ZZ,), Category of posets), {'order': 'blub'}) """ + + # CartesianProductPosets automatically add Posets() to their categories + from sage.categories.category import Category + from sage.categories.posets import Posets + if not isinstance(category, tuple): + category = (category,) + category = Category.join(category + (Posets(),)) + return (tuple(growth_groups), category), kwds diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index d0d5709c2c4..1f5bb9c1a46 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -4045,6 +4045,7 @@ class TermMonoidFactory(UniqueRepresentation, UniqueFactory): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -4072,6 +4073,7 @@ class TermMonoidFactory(UniqueRepresentation, UniqueFactory): running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_construction() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 1b8ea677ccb..e8773db20cd 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -2,7 +2,7 @@ r""" C-Finite Sequences -C-finite infinite sequences satisfy homogenous linear recurrences with constant coefficients: +C-finite infinite sequences satisfy homogeneous linear recurrences with constant coefficients: .. MATH:: @@ -721,17 +721,17 @@ def recurrence_repr(self): sage: C. = CFiniteSequences(QQ) sage: C((2-x)/(1-x-x^2)).recurrence_repr() - 'Homogenous linear recurrence with constant coefficients of degree 2: a(n+2) = a(n+1) + a(n), starting a(0...) = [2, 1]' + 'homogeneous linear recurrence with constant coefficients of degree 2: a(n+2) = a(n+1) + a(n), starting a(0...) = [2, 1]' sage: C(x/(1-x)^3).recurrence_repr() - 'Homogenous linear recurrence with constant coefficients of degree 3: a(n+3) = 3*a(n+2) - 3*a(n+1) + a(n), starting a(1...) = [1, 3, 6]' + 'homogeneous linear recurrence with constant coefficients of degree 3: a(n+3) = 3*a(n+2) - 3*a(n+1) + a(n), starting a(1...) = [1, 3, 6]' sage: C(1).recurrence_repr() 'Finite sequence [1], offset 0' sage: r = C((-2*x^3 + x^2 - x + 1)/(2*x^2 - 3*x + 1)) sage: r.recurrence_repr() - 'Homogenous linear recurrence with constant coefficients of degree 2: a(n+2) = 3*a(n+1) - 2*a(n), starting a(0...) = [1, 2, 5, 9]' + 'homogeneous linear recurrence with constant coefficients of degree 2: a(n+2) = 3*a(n+1) - 2*a(n), starting a(0...) = [1, 2, 5, 9]' sage: r = CFiniteSequence(x^3/(1-x-x^2)) sage: r.recurrence_repr() - 'Homogenous linear recurrence with constant coefficients of degree 2: a(n+2) = a(n+1) + a(n), starting a(3...) = [1, 1, 2, 3]' + 'homogeneous linear recurrence with constant coefficients of degree 2: a(n+2) = a(n+1) + a(n), starting a(3...) = [1, 1, 2, 3]' """ if self._deg == 0: return 'Finite sequence %s, offset %d' % (str(self._a), self._off) @@ -760,7 +760,7 @@ def recurrence_repr(self): for i in range(maxwexp + self._deg): astr = astr + str(self[self._off + i]) + ', ' astr = astr[:-2] + ']' - return 'Homogenous linear recurrence with constant coefficients of degree ' + str(self._deg) + ': ' + cstr + astr + return 'homogeneous linear recurrence with constant coefficients of degree ' + str(self._deg) + ': ' + cstr + astr def series(self, n): """ @@ -1094,7 +1094,7 @@ def _coerce_map_from_(self, S): def from_recurrence(self, coefficients, values): r""" Create a C-finite sequence given the coefficients $c$ and - starting values $a$ of a homogenous linear recurrence. + starting values $a$ of a homogeneous linear recurrence. .. MATH:: diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index d62645d4b06..a4ded1062e1 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -4574,7 +4574,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(2,3).elliptic_pi(CBF(1,1)) - [0.27029997361983 +/- ...e-15] + [0.715676058329095 +/- ...e-16]*I + [0.2702999736198...] + [0.715676058329...]*I """ cdef ComplexBall result = self._new() @@ -4681,17 +4681,21 @@ cdef class ComplexBall(RingElement): sage: n = CBF(1,1) sage: m = CBF(-2/3, 3/5) - sage: n.elliptic_pi_inc(CBF.pi()/2, m) + sage: n.elliptic_pi_inc(CBF.pi()/2, m) # arb216 [0.8934793755173 +/- ...e-14] + [0.95707868710750 +/- ...e-15]*I + sage: n.elliptic_pi_inc(CBF.pi()/2, m) # arb218 - this is a regression, see :trac:28623 + nan + nan*I sage: n.elliptic_pi(m) - [0.89347937551733 +/- ...e-15] + [0.95707868710750 +/- ...e-15]*I + [0.8934793755173...] + [0.957078687107...]*I sage: n = CBF(2, 3/7) sage: m = CBF(-1/3, 2/9) - sage: n.elliptic_pi_inc(CBF.pi()/2, m) + sage: n.elliptic_pi_inc(CBF.pi()/2, m) # arb216 [0.2969588746419 +/- ...e-14] + [1.3188795332738 +/- ...e-14]*I + sage: n.elliptic_pi_inc(CBF.pi()/2, m) # arb218 - this is a regression, see :trac:28623 + nan + nan*I sage: n.elliptic_pi(m) - [0.29695887464189 +/- ...e-15] + [1.31887953327376 +/- ...e-15]*I + [0.296958874641...] + [1.318879533273...]*I """ cdef ComplexBall result = self._new() cdef ComplexBall my_phi = self._parent.coerce(phi) @@ -4767,7 +4771,7 @@ cdef class ComplexBall(RingElement): EXAMPLES:: sage: CBF(0,1).elliptic_rj(CBF(-1/2,1), CBF(-1,-1), CBF(2)) - [1.004386756285733 +/- ...e-16] + [-0.2451626834391645 +/- ...e-17]*I + [1.00438675628573...] + [-0.24516268343916...]*I """ cdef ComplexBall result = self._new() diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 6c6c792fa8f..9c454868c44 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -1109,55 +1109,10 @@ cdef class ComplexDoubleElement(FieldElement): '2.0' sage: format(CDF(0, 0), '+#.4') '+0.000' - - TESTS:: - - sage: s = format(CDF(1/80, -1/2), '25'); s - ' 0.0125 - 0.5*I' - sage: len(s) == 25 - True - sage: '{:=^ 25}'.format(CDF(1/80, -1/2)) - '===== 0.0125 - 0.5*I=====' - sage: format(float(3), '#.4') == format(CDF(3, 0), '#.4') - True - sage: format(CDF(1, 2), '=+20') - Traceback (most recent call last): - ... - ValueError: '=' alignment not allowed in complex format specifier """ - import re - match = re.match(r'^(.?[><=^])?' # 1: fill and align - r'([ +-]?)' # 2: sign - r'[^\d\.]*?0?(\d*)' # 3: width - r'.*?([eEfFgGn%])?$', # 4: type - format_spec) - if not match: - raise ValueError("invalid format specifier %s" % format_spec) - - # format floats without align and width - float_format = (format_spec[match.start(2):match.start(3)] - + format_spec[match.end(3):]) - - use_str_format = not match.group(4) - if use_str_format and self._complex.imag == 0: - result = format(self._complex.real, float_format) - elif use_str_format and self._complex.real == 0: - result = format(self._complex.imag, float_format) + '*I' - else: - real = format(self._complex.real, float_format) - imag = format(self._complex.imag, - '+' + format_spec[match.end(2):match.start(3)] - + format_spec[match.end(3):]) - result = f"{real} {imag[:1]} {imag[1:]}*I" - - width = match.group(3) - if width: - align = match.group(1) or '>' - if align.endswith('='): - raise ValueError("'=' alignment not allowed in " - "complex format specifier") - result = format(result, align + width) - return result + return complex_number._format_complex_number(self._complex.real, + self._complex.imag, + format_spec) def _latex_(self): """ diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 926db9f2eac..f3242287052 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -535,6 +535,53 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): s = self.real().str(base, **kwds) return s + def __format__(self, format_spec): + """ + Return a formatted string representation of this complex number. + + INPUT: + + - ``format_spec`` -- string; a floating point format specificier as + defined by :python:`the format specification mini-language + ` in Python + + EXAMPLES:: + + sage: format(CC(32/3, 0), ' .4f') + ' 10.6667 + 0.0000*I' + sage: format(CC(-2/3, -2/3), '.4e') + '-6.6667e-1 - 6.6667e-1*I' + + If the representation type character is absent, the output matches the + string representation of the complex number. This has the effect that + real and imaginary part are only shown if they are not zero:: + + sage: format(CC(0, 2/3), '.4') + '0.6667*I' + sage: format(CC(2, 0), '.4') + '2.000' + sage: format(ComplexField(240)(0, 1/7), '.60') + '0.142857142857142857142857142857142857142857142857142857142857*I' + sage: format(ComplexField(240)(0, 1/7), '.60f') + '0.000000000000000000000000000000000000000000000000000000000000 + + 0.142857142857142857142857142857142857142857142857142857142857*I' + + Note that the general format does not exactly match the behaviour of + ``float``:: + + sage: format(CC(3, 0), '.4g') + '3.000 + 0e-15*I' + sage: format(CC(3, 0), '#.4g') + Traceback (most recent call last): + ... + ValueError: invalid format string + sage: format(CC(0, 0), '+#.4') + Traceback (most recent call last): + ... + ValueError: invalid format string + """ + return _format_complex_number(self.real(), self.imag(), format_spec) + def _latex_(self): r""" Method for converting ``self`` to a string with latex formatting. @@ -2740,3 +2787,66 @@ cpdef int cmp_abs(ComplexNumber a, ComplexNumber b): mpfr_clear(tmp) return res + +def _format_complex_number(real, imag, format_spec): + """ + Construct a formatted string from real and imaginary parts. + + TESTS:: + + sage: s = format(CDF(1/80, -1/2), '25'); s + ' 0.0125 - 0.5*I' + sage: len(s) == 25 + True + sage: '{:=^ 25}'.format(CDF(1/80, -1/2)) + '===== 0.0125 - 0.5*I=====' + sage: format(float(3), '#.4') == format(CDF(3, 0), '#.4') + True + sage: format(CDF(1, 2), '=+20') + Traceback (most recent call last): + ... + ValueError: '=' alignment not allowed in complex format specifier + + :: + + sage: format(CC(1/80, -1/2), '55') == format(str(CC(1/80, -1/2)), '>55') + True + sage: '{:=^ 55}'.format(CC(1/80, -1/2)) + '======= 0.0125000000000000 - 0.500000000000000*I=======' + sage: format(CC(1, 2), '=+20') + Traceback (most recent call last): + ... + ValueError: '=' alignment not allowed in complex format specifier + """ + import re + match = re.match(r'^(.?[><=^])?' # 1: fill and align + r'([ +-]?)' # 2: sign + r'[^\d\.]*?0?(\d*)' # 3: width + r'.*?([eEfFgGn%])?$', # 4: type + format_spec) + if not match: + raise ValueError("invalid format specifier %s" % format_spec) + + # format floats without align and width + float_format = (format_spec[match.start(2):match.start(3)] + + format_spec[match.end(3):]) + + use_str_format = not match.group(4) + if use_str_format and imag == 0: + result = format(real, float_format) + elif use_str_format and real == 0: + result = format(imag, float_format) + '*I' + else: + x = format(real, float_format) + y = format(imag, '+' + format_spec[match.end(2):match.start(3)] + + format_spec[match.end(3):]) + result = f"{x} {y[:1]} {y[1:]}*I" + + width = match.group(3) + if width: + align = match.group(1) or '>' + if align.endswith('='): + raise ValueError("'=' alignment not allowed in " + "complex format specifier") + result = format(result, align + width) + return result diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index e3275fe7ce4..a4b396621ff 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -1335,18 +1335,32 @@ cdef class FiniteField(Field): 3 sage: v = GF(2^1000, 'a').construction(); v[0].polys[0] a^1000 + a^5 + a^4 + a^3 + 1 + + The implementation is taken into account, by :trac:`15223`:: + + sage: k = FiniteField(9, 'a', impl='pari_ffelt') + sage: F, R = k.construction() + sage: F(R) is k + True + """ from sage.categories.pushout import AlgebraicExtensionFunctor + try: + kwds = {'impl': self._factory_data[2][3]} + except (AttributeError, IndexError, TypeError): + kwds = {} if self.degree() == 1: # this is not of type FiniteField_prime_modn from sage.rings.integer import Integer - return AlgebraicExtensionFunctor([self.polynomial()], [None], [None]), self.base_ring() + return AlgebraicExtensionFunctor([self.polynomial()], [None], [None], **kwds), self.base_ring() elif hasattr(self, '_prefix'): return (AlgebraicExtensionFunctor([self.degree()], [self.variable_name()], [None], - prefix=self._prefix), + prefix=self._prefix, **kwds), self.base_ring()) else: - return (AlgebraicExtensionFunctor([self.polynomial()], [self.variable_name()], [None]), + return (AlgebraicExtensionFunctor([self.polynomial()], + [self.variable_name()], [None], + **kwds), self.base_ring()) def extension(self, modulus, name=None, names=None, map=False, embedding=None, diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 92ae492580a..d5072839b27 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -89,6 +89,7 @@ class IntegerModFactory(UniqueFactory): - ``is_field`` -- bool (default: ``False``); assert that the order is prime and hence the quotient ring belongs to the category of fields + - ``category`` (optional) - the category that the quotient ring belongs to. .. NOTE:: @@ -187,6 +188,11 @@ class IntegerModFactory(UniqueFactory): sage: R in Fields() True + To avoid side-effects of this test on other tests, we clear the cache of + the ring factory:: + + sage: IntegerModRing._cache.clear() + """ def get_object(self, version, key, extra_args): out = super(IntegerModFactory,self).get_object(version, key, extra_args) @@ -196,7 +202,7 @@ def get_object(self, version, key, extra_args): out._factory_data[3]['category'] = category return out - def create_key_and_extra_args(self, order=0, is_field=False): + def create_key_and_extra_args(self, order=0, is_field=False, category=None): """ An integer mod ring is specified uniquely by its order. @@ -735,6 +741,11 @@ def is_field(self, proof=None): or a probabilistic primality test has failed. In the latter case, please inform the developers. + To avoid side-effects of this test on other tests, we clear the cache + of the ring factory:: + + sage: IntegerModRing._cache.clear() + """ from sage.categories.fields import Fields if not proof: diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index d2216ac02a4..cd4c2212c3b 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -151,6 +151,8 @@ from sage.rings.ring cimport Field from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.categories.homset import Hom +from sage.categories.basic import Fields, Rings +from sage.categories.pushout import AlgebraicExtensionFunctor from sage.rings.all import ZZ, QQ, Integers from sage.rings.finite_rings.finite_field_constructor import zech_log_bound, FiniteField as GF from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro @@ -479,6 +481,42 @@ class ResidueField_generic(Field): self.p = p # Note: we don't call Parent.__init__ since many residue fields use multiple inheritance and will be calling __init__ via their other superclass. + def construction(self): + """ + Construction of this residue field. + + OUTPUT: + + An :class:`~sage.categories.pushout.AlgebraicExtensionFunctor` and the + number field that this residue field has been obtained from. + + The residue field is determined by a prime (fractional) ideal in a + number field. If this ideal can be coerced into a different number + field, then the construction functor applied to this number field will + return the corresponding residue field. See :trac:`15223`. + + EXAMPLES:: + + sage: K. = CyclotomicField(7) + sage: P = K.factor(17)[0][0] + sage: k = K.residue_field(P) + sage: k + Residue field in zbar of Fractional ideal (17) + sage: F, R = k.construction() + sage: F + AlgebraicExtensionFunctor + sage: R + Cyclotomic Field of order 7 and degree 6 + sage: F(R) is k + True + sage: F(ZZ) + Residue field of Integers modulo 17 + sage: F(CyclotomicField(49)) + Residue field in zbar of Fractional ideal (17) + + """ + return AlgebraicExtensionFunctor([self.polynomial()], [self.variable_name()], [None], residue=self.p), self.p.ring() + def ideal(self): r""" Return the maximal ideal that this residue field is the quotient by. diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 203439083d1..1f3957d261e 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -33,7 +33,6 @@ import sage.rings.ring from sage.structure.element import MonoidElement from sage.structure.richcmp import rich_to_bool, richcmp -from sage.interfaces.singular import singular as singular_default import sage.rings.infinity from sage.structure.sequence import Sequence @@ -260,7 +259,8 @@ def __init__(self, ring, gens, coerce=True): gens = [ring(x) for x in gens] gens = tuple(gens) - if len(gens)==0: gens=(ring.zero(),) + if len(gens) == 0: + gens = (ring.zero(),) self.__gens = gens MonoidElement.__init__(self, ring.ideal_monoid()) @@ -1654,7 +1654,7 @@ def __repr__(self): # constructors for standard (benchmark) ideals, written uppercase as # these are constructors -def Cyclic(R, n=None, homog=False, singular=singular_default): +def Cyclic(R, n=None, homog=False, singular=None): """ Ideal of cyclic ``n``-roots from 1-st ``n`` variables of ``R`` if ``R`` is coercible to :class:`Singular `. @@ -1706,6 +1706,10 @@ def Cyclic(R, n=None, homog=False, singular=singular_default): else: n = R.ngens() + if singular is None: + from sage.interfaces.singular import singular as singular_default + singular = singular_default + singular.lib("poly") R2 = R.change_ring(RationalField()) R2._singular_().set_ring() @@ -1716,7 +1720,7 @@ def Cyclic(R, n=None, homog=False, singular=singular_default): I = singular.cyclic(n).homog(R2.gen(n-1)) return R2.ideal(I).change_ring(R) -def Katsura(R, n=None, homog=False, singular=singular_default): +def Katsura(R, n=None, homog=False, singular=None): """ ``n``-th katsura ideal of ``R`` if ``R`` is coercible to :class:`Singular `. @@ -1753,6 +1757,10 @@ def Katsura(R, n=None, homog=False, singular=singular_default): raise ArithmeticError("n must be <= R.ngens().") else: n = R.ngens() + + if singular is None: + from sage.interfaces.singular import singular as singular_default + singular = singular_default singular.lib("poly") R2 = R.change_ring(RationalField()) R2._singular_().set_ring() diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index bc7d5483dc9..d9c83f3e3c1 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -2703,8 +2703,11 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): else: _m=Integer(m) - if mpz_sgn(self.value) <= 0 or mpz_sgn(_m.value) <= 0: - raise ValueError("both self and m must be positive") + self_sgn = mpz_sgn(self.value) + if self_sgn == 0: + return -sage.rings.infinity.infinity + if self_sgn < 0 or mpz_sgn(_m.value) <= 0: + raise ValueError("self must be nonnegative and m must be positive") if mpz_cmp_si(_m.value,2) < 0: raise ValueError("m must be at least 2") @@ -2870,7 +2873,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if type(m) == Integer and type(self) == Integer: elog = self.exact_log(m) - if m**elog == self: + if elog == -sage.rings.infinity.infinity or m**elog == self: return elog if (type(m) == Rational and type(self) == Integer diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index a39a1cc29ff..ae0b0f41327 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -884,7 +884,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): from sage.rings.number_field.order import EquationOrder return EquationOrder(poly, names=names, **kwds) - def quotient(self, I, names=None): + def quotient(self, I, names=None, **kwds): r""" Return the quotient of `\ZZ` by the ideal or integer ``I``. @@ -911,9 +911,9 @@ cdef class IntegerRing_class(PrincipalIdealDomain): raise TypeError("I must be an ideal of ZZ or an integer") if n == 0: return self - return sage.rings.finite_rings.integer_mod_ring.IntegerModRing(n) + return sage.rings.finite_rings.integer_mod_ring.IntegerModRing(n, **kwds) - def residue_field(self, prime, check = True): + def residue_field(self, prime, check=True, names=None): r""" Return the residue field of the integers modulo the given prime, i.e. `\ZZ/p\ZZ`. @@ -923,7 +923,9 @@ cdef class IntegerRing_class(PrincipalIdealDomain): - ``prime`` - a prime number - ``check`` - (boolean, default ``True``) whether or not - to check the primality of prime. + to check the primality of prime + + - ``names`` - ignored (for compatibility with number fields) OUTPUT: The residue field at this prime. diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 6f153cac72e..925b8842cd9 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -281,6 +281,25 @@ class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact): # # sparse setting may not be implemented completely Element = MPowerSeries + @staticmethod + def __classcall__(cls, base_ring, num_gens, name_list, + order='negdeglex', default_prec=10, sparse=False): + """ + Preprocessing of arguments: The term order can be given as string + or as a :class:`~sage.rings.polynomial.term_order.TermOrder` instance. + + TESTS:: + + sage: P1 = PowerSeriesRing(QQ, ['f0','f1','f2','f3'], order = TermOrder('degrevlex')) + sage: P2 = PowerSeriesRing(QQ,4,'f', order='degrevlex') + sage: P1 is P2 # indirect doctest + True + + """ + order = TermOrder(order,num_gens) + return super(MPowerSeriesRing_generic,cls).__classcall__(cls, base_ring, num_gens, name_list, + order, default_prec, sparse) + def __init__(self, base_ring, num_gens, name_list, order='negdeglex', default_prec=10, sparse=False): """ @@ -335,7 +354,6 @@ def __init__(self, base_ring, num_gens, name_list, sage: TestSuite(P).run() """ - order = TermOrder(order,num_gens) self._term_order = order if not base_ring.is_commutative(): raise TypeError("Base ring must be a commutative ring.") @@ -487,9 +505,31 @@ def construction(self): Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field sage: c(R) == M True + + TESTS:: + + sage: M2 = PowerSeriesRing(QQ,4,'f', sparse=True) + sage: M == M2 + False + sage: c,R = M2.construction() + sage: c(R)==M2 + True + sage: M3 = PowerSeriesRing(QQ,4,'f', order='degrevlex') + sage: M3 == M + False + sage: M3 == M2 + False + sage: c,R = M3.construction() + sage: c(R)==M3 + True + """ from sage.categories.pushout import CompletionFunctor - return (CompletionFunctor(self._names, self.default_prec()), + extras = {'order':self.term_order(), 'num_gens':self.ngens()} + if self.is_sparse(): + extras['sparse'] = True + return (CompletionFunctor(self._names, self.default_prec(), + extras=extras), self._poly_ring()) def change_ring(self, R): diff --git a/src/sage/rings/padics/FP_template.pxi b/src/sage/rings/padics/FP_template.pxi index 55b9bf8e0f3..8ef1c76dc3f 100644 --- a/src/sage/rings/padics/FP_template.pxi +++ b/src/sage/rings/padics/FP_template.pxi @@ -1041,12 +1041,12 @@ cdef class FPElement(pAdicTemplateElement): EXAMPLES:: sage: R. = ZZ[] - sage: K. = Qq(25) + sage: K. = QqFP(5^3) sage: W. = K.extension(x^3-5) - sage: (1 + w + O(w^11))._polynomial_list() - [1 + O(5^4), 1 + O(5^4)] - sage: (1 + w + O(w^11))._polynomial_list(pad=True) - [1 + O(5^4), 1 + O(5^4), O(5^3)] + sage: (1 + w)._polynomial_list() + [1, 1] + sage: (1 + w)._polynomial_list(pad=True) + [1, 1, 0] """ R = self.base_ring() if very_pos_val(self.ordp): diff --git a/src/sage/rings/polynomial/binary_form_reduce.py b/src/sage/rings/polynomial/binary_form_reduce.py index 65ed82a1e0f..52b31c9f2be 100644 --- a/src/sage/rings/polynomial/binary_form_reduce.py +++ b/src/sage/rings/polynomial/binary_form_reduce.py @@ -3,7 +3,7 @@ Helper functions for reduction of binary forms. The algorithm for reducing is from Stoll and Cremona's "On the Reduction Theory of -Binary Forms" [CS2003]_. This takes a two variable homogenous polynomial and finds a +Binary Forms" [CS2003]_. This takes a two variable homogeneous polynomial and finds a reduced form. This is an `SL(2,\ZZ)`-equivalent binary form whose covariant in the upper half plane is in the fundamental domain. Further, the algorithm from Hutz and Stoll [HS2018]_ allows the form to be further minimized so that @@ -13,7 +13,7 @@ - Rebecca Lauren Miller -- initial version of reduction as part of GSOC 2016 -- Ben Hutz (2018-7) -- improvements to reduce and implement smallest coefficient model +- Ben Hutz (2018-7) -- improvements to reduce and implement smallest coefficient model """ # **************************************************************************** @@ -107,7 +107,7 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): TESTS:: - sage: R.=QQ[] + sage: R.=QQ[] sage: covariant_z0(x^2 + 24*x*y + y^2) Traceback (most recent call last): ... @@ -139,7 +139,7 @@ def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): f = F.subs({R.gen(1):1}).univariate_polynomial() if f.degree() < d: # we have a root at infinity - if f.constant_coefficient() != 0: + if f.constant_coefficient() != 0: # invert so we find all roots! mat = matrix(ZZ,2,2,[0,-1,1,0]) else: @@ -265,7 +265,7 @@ def epsinv(F, target, prec=53, target_tol=0.001, z=None, emb=None): The true minimum will be within the computed bound. It is computed as the inverse of epsilon_F from [HS2018]_. - + INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots @@ -289,7 +289,7 @@ def epsinv(F, target, prec=53, target_tol=0.001, z=None, emb=None): sage: from sage.rings.polynomial.binary_form_reduce import epsinv sage: R. = QQ[] sage: epsinv(-2*x^3 + 2*x^2*y + 3*x*y^2 + 127*y^3, 31.5022020249597) # tol 1e-12 - 4.02520895942207 + 4.02520895942207 """ def coshdelta(z): #The cosh of the hyperbolic distance from z = t+uj to j @@ -551,7 +551,7 @@ def coshdelta(z): #check if it is smaller. If so, we can improve the bound count += 1 if norm_type == 'norm': - new_size = sum([abs(i)**2 for i in G.coefficients()]) #euclidean norm squared + new_size = sum([abs(i)**2 for i in G.coefficients()]) #euclidean norm squared else: #height new_size = exp(max([c.global_height(prec=prec) for c in G.coefficients()])) if new_size < current_size: diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index c4dc4ff839c..dfec6b2555d 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -2233,7 +2233,7 @@ cdef class MPolynomial(CommutativeRingElement): Return a reduced form of this polynomial. The algorithm is from Stoll and Cremona's "On the Reduction Theory of - Binary Forms" [CS2003]_. This takes a two variable homogenous polynomial and + Binary Forms" [CS2003]_. This takes a two variable homogeneous polynomial and finds a reduced form. This is a `SL(2,\ZZ)`-equivalent binary form whose covariant in the upper half plane is in the fundamental domain. If the polynomial has multiple roots, they are removed and the algorithm @@ -2390,7 +2390,7 @@ cdef class MPolynomial(CommutativeRingElement): Traceback (most recent call last): ... ValueError: (=-8*x^6 - 99*y^6 - 3933*x^3*y - 725085*x^2*y^2 - - 59411592*x*y^3) must be homogenous + 59411592*x*y^3) must be homogeneous :: @@ -2424,7 +2424,7 @@ cdef class MPolynomial(CommutativeRingElement): if self.parent().ngens() != 2: raise ValueError("(=%s) must have two variables"%self) if not self.is_homogeneous(): - raise ValueError("(=%s) must be homogenous"%self) + raise ValueError("(=%s) must be homogeneous"%self) prec = kwds.get('prec', 300) return_conjugation =kwds.get('return_conjugation', True) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index cd0d1a6d98d..879a7bbb707 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -181,7 +181,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): """ return self.ideal(self.gens(), check=False) - def completion(self, names, prec=20): + def completion(self, names, prec=20, extras={}): """ Return the completion of self with respect to the ideal generated by the variable(s) ``names``. @@ -193,7 +193,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): - ``prec`` -- default precision of resulting power series ring - - ``extras`` -- deprecated and ignored + - ``extras`` -- passed as keywords to ``PowerSeriesRing`` EXAMPLES:: @@ -262,7 +262,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): from sage.rings.power_series_ring import PowerSeriesRing new_base = self.remove_var(*vars) - return PowerSeriesRing(new_base, names=vars, default_prec=prec) + return PowerSeriesRing(new_base, names=vars, default_prec=prec, **extras) def remove_var(self, *var, order=None): """ diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index d9da191eb7a..0fc308059be 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -56,6 +56,7 @@ import operator from sage.misc.cachefunc import cached_function +from sage.misc.superseded import deprecated_function_alias def MacMahonOmega(var, expression, denominator=None, op=operator.ge, @@ -737,11 +738,11 @@ def _Omega_numerator_(a, x, y, t): if m == 0: result = 1 - (prod(_Omega_factors_denominator_(x, y)) * - sum(homogenous_symmetric_function(j, xy) + sum(homogeneous_symmetric_function(j, xy) for j in srange(-a)) if a < 0 else 0) elif n == 0: - result = sum(homogenous_symmetric_function(j, xy) + result = sum(homogeneous_symmetric_function(j, xy) for j in srange(a+1)) else: result = _Omega_numerator_P_(a, x_flat[:-1], y_flat, t).subs({t: x_flat[-1]}) @@ -803,7 +804,7 @@ def _Omega_numerator_P_(a, x, y, t): x0 = t result = x0**(-a) + \ (prod(1 - x0*yy for yy in y) * - sum(homogenous_symmetric_function(j, y) * (1-x0**(j-a)) + sum(homogeneous_symmetric_function(j, y) * (1-x0**(j-a)) for j in srange(a)) if a > 0 else 0) else: @@ -951,7 +952,7 @@ def partition(items, predicate=bool): (item for pred, item in b if pred)) -def homogenous_symmetric_function(j, x): +def homogeneous_symmetric_function(j, x): r""" Return a complete homogeneous symmetric polynomial (:wikipedia:`Complete_homogeneous_symmetric_polynomial`). @@ -968,15 +969,15 @@ def homogenous_symmetric_function(j, x): EXAMPLES:: - sage: from sage.rings.polynomial.omega import homogenous_symmetric_function + sage: from sage.rings.polynomial.omega import homogeneous_symmetric_function sage: P = PolynomialRing(ZZ, 'X', 3) - sage: homogenous_symmetric_function(0, P.gens()) + sage: homogeneous_symmetric_function(0, P.gens()) 1 - sage: homogenous_symmetric_function(1, P.gens()) + sage: homogeneous_symmetric_function(1, P.gens()) X0 + X1 + X2 - sage: homogenous_symmetric_function(2, P.gens()) + sage: homogeneous_symmetric_function(2, P.gens()) X0^2 + X0*X1 + X1^2 + X0*X2 + X1*X2 + X2^2 - sage: homogenous_symmetric_function(3, P.gens()) + sage: homogeneous_symmetric_function(3, P.gens()) X0^3 + X0^2*X1 + X0*X1^2 + X1^3 + X0^2*X2 + X0*X1*X2 + X1^2*X2 + X0*X2^2 + X1*X2^2 + X2^3 """ @@ -985,3 +986,5 @@ def homogenous_symmetric_function(j, x): return sum(prod(xx**pp for xx, pp in zip(x, p)) for p in IntegerVectors(j, length=len(x))) + +homogenous_symmetric_function = deprecated_function_alias(30585, homogeneous_symmetric_function) diff --git a/src/sage/rings/polynomial/pbori/blocks.py b/src/sage/rings/polynomial/pbori/blocks.py index 5a9749db5d0..7597ff5943c 100644 --- a/src/sage/rings/polynomial/pbori/blocks.py +++ b/src/sage/rings/polynomial/pbori/blocks.py @@ -7,11 +7,6 @@ from .PyPolyBoRi import Ring, VariableBlock, Polynomial from .PyPolyBoRi import VariableFactory, MonomialFactory from itertools import chain, islice -#class BlockEndException(object): - #pass - #def __init__(self, arg): - # self.arg = arg - # pass class Block(object): @@ -61,7 +56,7 @@ class AlternatingBlock(object): a(0),b(0),a(1),b(1),a(2),b(2) """ def __init__(self, var_names, size_per_variable, start_index=0, - reverse=False): + reverse=False): self.var_names = var_names self.size_per_variable = size_per_variable self.reverse = reverse @@ -120,7 +115,7 @@ def g(j): class AdderBlock(AlternatingBlock): def __init__(self, adder_bits, sums="s", carries="c", input1="a", - input2="b", start_index=0): + input2="b", start_index=0): AlternatingBlock.__init__(self, (sums, carries, input1, input2), adder_bits, start_index=start_index, reverse=True) self.input1 = input1 @@ -140,7 +135,6 @@ def register(self, start, context): b = shift(b, self.start_index) carries = [Polynomial(a(0).ring().zero())] for i in range(self.adder_bits): - #print i, ":" c = 1 + (1 + a(i) * b(i)) * (1 + carries[-1] * a(i)) * (1 + carries[-1] * b(i)) carries.append(c) @@ -159,7 +153,6 @@ def implement(self, equations): for i in range(self.adder_bits): equations.append(self.s(i) + self.add_results[i]) equations.append(self.c(i) + self.carries_polys[i]) - pass class HigherOrderBlock(object): @@ -172,7 +165,7 @@ class HigherOrderBlock(object): """ def __init__(self, var_name, size_tuple, start_index_tuple=None, - reverse=False): + reverse=False): if start_index_tuple is None: start_index_tuple = len(size_tuple) * (0, ) cart = [()] @@ -181,7 +174,6 @@ def __init__(self, var_name, size_tuple, start_index_tuple=None, for i in outer_indices: s_i = start_index_tuple[i] s = size_tuple[i] - #print "cart", cart cart = [(j, ) + c for j in range(s_i, s_i + s) for c in cart] if reverse: cart.reverse() @@ -189,7 +181,6 @@ def __init__(self, var_name, size_tuple, start_index_tuple=None, self.cart2index = dict([(v, k) for (k, v) in enumerate(cart)]) self.var_name = var_name self.names = [var_name + str(c) for c in cart] - pass def __getitem__(self, i): return self.names[i] @@ -209,8 +200,8 @@ def var_func(*indices): class InOutBlock(object): def __init__(self, out_size, in_size, output="out", input="in", - in_start_index=0, out_start_index=0, - out_reverse=False, in_reverse=False): + in_start_index=0, out_start_index=0, + out_reverse=False, in_reverse=False): self.output = Block(var_name=output, start_index=out_start_index, size=out_size, reverse=out_reverse) self.input = Block(var_name=input, start_index=in_start_index, @@ -237,12 +228,11 @@ def register(self, start, context): self.out_vars = shift(context[self.output.var_name], self. out_start_index) self.in_vars = shift(context[self.input.var_name], self.in_start_index) - pass class MultiBlock(object): - def __init__(self, sizes=[], var_names=["v"], start_indices=[], reverses=[ - ]): + def __init__(self, sizes=[], var_names=["v"], + start_indices=[], reverses=[]): self.start_indices = start_indices + [0] * (len(var_names) - len( start_indices)) diff --git a/src/sage/rings/polynomial/pbori/nf.py b/src/sage/rings/polynomial/pbori/nf.py index 2235b340355..86401c73941 100644 --- a/src/sage/rings/polynomial/pbori/nf.py +++ b/src/sage/rings/polynomial/pbori/nf.py @@ -2,13 +2,10 @@ from .PyPolyBoRi import * from .easy_polynomials import (easy_linear_polynomials as - easy_linear_polynomials_func) + easy_linear_polynomials_func) from .statistics import used_vars_set -from random import Random from warnings import warn -import copy import os -import sys class GeneratorLimitExceeded(Exception): @@ -20,7 +17,6 @@ def __init__(self, strat): #super(GeneratorLimitExceeded, self).__init__() self.strat = strat -#used_polynomials=list() def pkey(p): return (p[0], len(p)) @@ -55,20 +51,16 @@ def build_and_print_matrices(v, strat): treated = treated.union(p.set()) m2i = dict([(v, k) for (k, v) in enumerate(list(Polynomial(BooleSet( treated)).terms()))]) - #print polys_in_mat polys_in_mat.sort(key=Polynomial.lead, reverse=True) polys_in_mat = [[m2i[t] for t in p.terms()] for p in polys_in_mat] global mat_counter mat_counter = mat_counter + 1 from PIL import Image - from PIL import ImageDraw rows = len(polys_in_mat) cols = len(m2i) - #print cols,rows im = Image.new("1", (cols, rows), "white") - #im=Image.new("1",(,10000),"white") for i in range(len(polys_in_mat)): p = polys_in_mat[i] for j in p: @@ -88,9 +80,9 @@ def build_and_print_matrices(v, strat): def multiply_polynomials(l, ring): r""" - + TESTS:: - + sage: from sage.rings.polynomial.pbori import * sage: r=Ring(1000) sage: x=r.variable @@ -145,7 +137,6 @@ def build_and_print_matrices_deg_colored(v, strat): global mat_counter mat_counter = mat_counter + 1 from PIL import Image - from PIL import ImageDraw from PIL import ImageColor rows = len(polys_in_mat) @@ -617,7 +608,6 @@ def symmGB_F2_C(G, opt_exchange=True, max_generators=None, red_tail_deg_growth=True, modified_linear_algebra=True, matrix_prefix="", draw_matrices=False): - #print implications if use_noro: raise NotImplementedError("noro not implemented for symmgb") if (isinstance(G, list)): @@ -643,8 +633,6 @@ def symmGB_F2_C(G, opt_exchange=True, strat.redByReduced = False # True - #if PROT: - # print "added first" for g in G: # [1:]: if not g.is_zero(): strat.add_generator_delayed(g) @@ -655,9 +643,9 @@ def symmGB_F2_C(G, opt_exchange=True, def normal_form(poly, ideal, reduced=True): r""" Simple normal form computation of a polynomial against an ideal. - + TESTS:: - + sage: from sage.rings.polynomial.pbori import declare_ring, normal_form sage: r=declare_ring(['x','y'], globals()) sage: normal_form(x+y, [y],reduced=True) diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index ddc3be9cbe7..062065716c9 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -453,6 +453,38 @@ cdef class BooleanPolynomialRing(MPolynomialRing_base): order = self.term_order() return unpickle_BooleanPolynomialRing,(n, names, order) + def construction(self): + """ + A boolean polynomial ring is the quotient of a polynomial ring, + in a special implementation. + + Before :trac:`15223`, the boolean polynomial rings returned the + construction of a polynomial ring, which was of course wrong. + + Now, a :class:`~sage.categories.pushout.QuotientFunctor` is returned + that knows about the `"pbori"` implementation. + + EXAMPLES:: + + sage: P. = BooleanPolynomialRing(4,order='degneglex(2),degneglex(2)') + sage: F,O = P.construction() + sage: O + Multivariate Polynomial Ring in x0, x1, x2, x3 over Finite Field of size 2 + sage: F + QuotientFunctor + sage: F(O) is P + True + + """ + from sage.categories.pushout import QuotientFunctor + I = self.defining_ideal() + R = I.ring() + return QuotientFunctor(I, names=self.variable_names(), + domain=R.category(), codomain=self.category(), + implementation="pbori", + order=self.term_order() + ), R + def ngens(self): """ Return the number of variables in this boolean polynomial ring. diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 808bdb0df9f..9855ad13e95 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -7539,6 +7539,16 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: p.roots(ring=QQbar) [(-5.9223865215328558?e225, 1), (5.9223865215328558?e225, 1)] + Check that :trac:`30522` is fixed:: + + sage: PolynomialRing(SR, names="x")("x^2").roots() + [(0, 2)] + + Check that :trac:`30523` is fixed:: + + sage: PolynomialRing(SR, names="x")("x^2 + q").roots() + [(-sqrt(-q), 1), (sqrt(-q), 1)] + Algorithms used: For brevity, we will use RR to mean any RealField of any precision; @@ -7830,18 +7840,20 @@ cdef class Polynomial(CommutativeAlgebraElement): from sage.libs.pynac.pynac import I coeffs = self.list() D = coeffs[1]*coeffs[1] - 4*coeffs[0]*coeffs[2] + l = None if D > 0: l = [((-coeffs[1]-sqrt(D))/2/coeffs[2], 1), ((-coeffs[1]+sqrt(D))/2/coeffs[2], 1)] elif D < 0: l = [((-coeffs[1]-I*sqrt(-D))/2/coeffs[2], 1), ((-coeffs[1]+I*sqrt(-D))/2/coeffs[2], 1)] - else: - l = [(-coeffs[1]/2/coeffs[2]), 2] - if multiplicities: - return l - else: - return [val for val,m in l] + elif D == 0: + l = [(-coeffs[1]/2/coeffs[2], 2)] + if l: + if multiplicities: + return l + else: + return [val for val,m in l] vname = 'do_not_use_this_name_in_a_polynomial_coefficient' var = SR(vname) expr = self(var) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 05e3ff50a53..230dea7fdf5 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -50,6 +50,8 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_commutative from sage.categories.commutative_algebras import CommutativeAlgebras +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.rings import Rings from sage.structure.category_object import normalize_names from sage.structure.factory import UniqueFactory @@ -760,7 +762,11 @@ def construction(self): -- Simon King (2010-05) """ from sage.categories.pushout import QuotientFunctor - return QuotientFunctor([self.modulus()]*self.base(),self.variable_names()), self.base() + Cover = self.base() + if Cover in CommutativeRings(): + return QuotientFunctor([self.modulus()]*Cover, self.variable_names(), + domain=CommutativeRings(), codomain=CommutativeRings()), Cover + return QuotientFunctor([self.modulus()]*self.base(), self.variable_names()), Cover @cached_method def base_ring(self): @@ -2209,8 +2215,8 @@ class PolynomialQuotientRing_field(PolynomialQuotientRing_domain, Field): sage: loads(xbar.dumps()) == xbar True """ - def __init__(self, ring, polynomial, name=None): - PolynomialQuotientRing_domain.__init__(self, ring, polynomial, name) + def __init__(self, ring, polynomial, name=None, category=None): + PolynomialQuotientRing_domain.__init__(self, ring, polynomial, name, category) def base_field(self): r""" diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index d9b05fdfa22..562cad8dcd1 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -650,7 +650,8 @@ def completion(self, p, prec=20, extras=None): """ if str(p) == self._names[0]: from sage.rings.power_series_ring import PowerSeriesRing - return PowerSeriesRing(self.base_ring(), name=self._names[0], default_prec=prec) + return PowerSeriesRing(self.base_ring(), name=self._names[0], + default_prec=prec, sparse=self.is_sparse()) else: raise TypeError("Cannot complete %s with respect to %s" % (self, p)) @@ -1676,7 +1677,7 @@ def __init__(self, base_ring, name=None, sparse=False, element_class=None, categ PolynomialRing_general.__init__(self, base_ring, name=name, sparse=sparse, element_class=element_class, category=category) - def quotient_by_principal_ideal(self, f, names=None): + def quotient_by_principal_ideal(self, f, names=None, **kwds): """ Return the quotient of this polynomial ring by the principal ideal (generated by) `f`. @@ -1685,6 +1686,7 @@ def quotient_by_principal_ideal(self, f, names=None): - ``f`` - either a polynomial in ``self``, or a principal ideal of ``self``. + - further named arguments that are passed to the quotient constructor. EXAMPLES:: @@ -1716,7 +1718,7 @@ def quotient_by_principal_ideal(self, f, names=None): return self f = I.gen() from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing - return PolynomialQuotientRing(self, f, names) + return PolynomialQuotientRing(self, f, names, **kwds) def weyl_algebra(self): """ diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index d9e7aa124c2..675a52fb2fd 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -824,9 +824,18 @@ def construction(self): Univariate Polynomial Ring in x over Integer Ring sage: R == c(S) True + sage: R = PowerSeriesRing(ZZ, 'x', sparse=True) + sage: c, S = R.construction() + sage: R == c(S) + True + """ from sage.categories.pushout import CompletionFunctor - return CompletionFunctor(self._names[0], self.default_prec()), self._poly_ring() + if self.is_sparse(): + extras = {'sparse': True} + else: + extras = None + return CompletionFunctor(self._names[0], self.default_prec(), extras), self._poly_ring() def _coerce_impl(self, x): """ diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 09c5ba2d02f..c27e6f4df89 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -120,7 +120,7 @@ from sage.categories.rings import Rings from sage.categories.commutative_rings import CommutativeRings -def QuotientRing(R, I, names=None): +def QuotientRing(R, I, names=None, **kwds): r""" Creates a quotient ring of the ring `R` by the twosided ideal `I`. @@ -137,6 +137,9 @@ def QuotientRing(R, I, names=None): - ``names`` -- (optional) a list of strings to be used as names for the variables in the quotient ring `R/I`. + - further named arguments that will be passed to the constructor + of the quotient ring instance. + OUTPUT: `R/I` - the quotient ring `R` mod the ideal `I` ASSUMPTION: @@ -283,6 +286,10 @@ def QuotientRing(R, I, names=None): pass else: names = normalize_names(R.ngens(), names) + if kwds.get('implementation') == 'pbori': + from sage.rings.polynomial.polynomial_ring_constructor import BooleanPolynomialRing_constructor as BooleanPolynomialRing + kwds.pop('implementation') + return BooleanPolynomialRing(R.ngens(), names=names, **kwds) if not isinstance(I, ideal.Ideal_generic) or I.ring() != R: I = R.ideal(I) if I.is_zero(): @@ -305,11 +312,11 @@ def QuotientRing(R, I, names=None): I_lift = S.ideal(G) J = R.defining_ideal() if S == ZZ: - return Integers((I_lift+J).gen()) + return Integers((I_lift+J).gen(), **kwds) return R.__class__(S, I_lift + J, names=names) if isinstance(R, ring.CommutativeRing): - return QuotientRing_generic(R, I, names) - return QuotientRing_nc(R, I, names) + return QuotientRing_generic(R, I, names, **kwds) + return QuotientRing_nc(R, I, names, **kwds) def is_QuotientRing(x): """ @@ -498,7 +505,10 @@ def construction(self): names = self.cover_ring().variable_names() except ValueError: names = None - return QuotientFunctor(self.__I, names=names, as_field=isinstance(self, Field)), self.__R + if self in CommutativeRings(): + return QuotientFunctor(self.__I, names=names, domain=CommutativeRings(), codomain=CommutativeRings(), as_field=isinstance(self, Field)), self.__R + else: + return QuotientFunctor(self.__I, names=names, as_field=isinstance(self, Field)), self.__R def _repr_(self): """ diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 3e2e0df688d..1755c56502f 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -857,6 +857,24 @@ cdef class Rational(sage.structure.element.FieldElement): ....: assert (one1 == one2) is True ....: assert (one1 <= one2) is True ....: assert (one1 >= one2) is True + + Comparisons with gmpy2 values (:trac:`28394`):: + + sage: import gmpy2 + sage: values = [(-2,5),(-1,3),(0,1),(2,9),(1,1),(73,2)] + sage: for num1, den1 in values: + ....: for num2, den2 in values: + ....: a1 = QQ((num1, den1)) + ....: a2 = QQ((num2, den2)) + ....: b1 = gmpy2.mpq(num1, den1) + ....: b2 = gmpy2.mpq(num2, den2) + ....: assert a1 == b1 and b1 == a1 and a2 == b2 and b2 == a2 + ....: assert (a1 == a2) == (b1 == b2) == (a1 == b2) == (b1 == a2) + ....: assert (a1 != a2) == (b1 != b2) == (a1 != b2) == (b1 != a2) + ....: assert (a1 < a2) == (b1 < b2) == (a1 < b2) == (b1 < a2) + ....: assert (a1 <= a2) == (b1 <= b2) == (a1 <= b2) == (b1 <= a2) + ....: assert (a1 > a2) == (b1 > b2) == (a1 > b2) == (b1 > a2) + ....: assert (a1 >= a2) == (b1 >= b2) == (a1 >= b2) == (b1 >= a2) """ cdef int c if op == Py_EQ: diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 82661732fc3..eb5d5434918 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -293,10 +293,12 @@ cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except - EXAMPLES:: - sage: RIF(RBF(2)**(2**100)) # indirect doctest + sage: RIF(RBF(2)**(2**100)) # arb216 # indirect doctest Traceback (most recent call last): ... ArithmeticError: Error converting arb to mpfi. Overflow? + sage: RIF(RBF(2)**(2**100)) # arb218 # indirect doctest + [5.8756537891115869e1388255822130839282 .. +infinity] """ cdef mpfr_t left cdef mpfr_t right @@ -1667,10 +1669,12 @@ cdef class RealBall(RingElement): :: sage: b = RBF(2)^(2^1000) - sage: b.mid() + sage: b.mid() # arb216 Traceback (most recent call last): ... RuntimeError: unable to convert to MPFR (exponent out of range?) + sage: b.mid() # arb218 + +infinity .. SEEALSO:: :meth:`rad`, :meth:`squash` """ diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 0393e48c6dd..431e16de1eb 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -1586,6 +1586,39 @@ cdef class RealNumber(sage.structure.element.RingElement): """ return self.str(truncate=True) + def __format__(self, format_spec): + """ + Return a formatted string representation of this real number. + + EXAMPLES:: + + sage: format(RR(32/3), '.4f') + '10.6667' + sage: '{:.4e}'.format(RR(2/3)) + '6.6667e-1' + sage: format(RealField(240)(1/7), '.60f') + '0.142857142857142857142857142857142857142857142857142857142857' + + TESTS:: + + sage: format(RR(oo), '.4') + 'Infinity' + sage: format(RR(-oo), '.4') + '-Infinity' + sage: format(RR(NaN), '.4') + 'NaN' + sage: '{}'.format(RR(oo)) + '+infinity' + """ + if not format_spec: + # Since there are small formatting differences between RR and + # Decimal, we avoid converting to Decimal when spec is empty, which + # commonly occurs in f-strings. This could be improved by fully + # parsing format_spec in order to avoid using Decimal altogether. + return repr(self) + from decimal import Decimal + return format(Decimal(repr(self)), format_spec) + def _latex_(self): r""" Return a latex representation of ``self``. diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index cfeaf7425a3..0408a5bcabe 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -103,6 +103,7 @@ cdef class Ring(ParentWithGens): running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_divides() . . . pass running ._test_elements() . . . @@ -608,7 +609,7 @@ cdef class Ring(ParentWithGens): return I return self._zero_ideal - def quotient(self, I, names=None): + def quotient(self, I, names=None, **kwds): """ Create the quotient of this ring by a twosided ideal ``I``. @@ -620,6 +621,9 @@ cdef class Ring(ParentWithGens): there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0). + - further named arguments that may be passed to the quotient ring + constructor. + EXAMPLES:: sage: R. = PolynomialRing(ZZ) @@ -638,12 +642,11 @@ cdef class Ring(ParentWithGens): False """ import sage.rings.quotient_ring - return sage.rings.quotient_ring.QuotientRing(self, I, names=names) + return sage.rings.quotient_ring.QuotientRing(self, I, names=names, **kwds) - def quo(self, I, names=None): + def quo(self, I, names=None, **kwds): """ - Create the quotient of `R` by the ideal `I`. This is a synonym for - :meth:`.quotient` + Create the quotient of `R` by the ideal `I`. This is a synonym for :meth:`.quotient` EXAMPLES:: @@ -656,7 +659,7 @@ cdef class Ring(ParentWithGens): sage: a == b False """ - return self.quotient(I, names=names) + return self.quotient(I, names=names, **kwds) def __truediv__(self, I): """ @@ -672,7 +675,7 @@ cdef class Ring(ParentWithGens): """ raise TypeError("Use self.quo(I) or self.quotient(I) to construct the quotient ring.") - def quotient_ring(self, I, names=None): + def quotient_ring(self, I, names=None, **kwds): """ Return the quotient of self by the ideal `I` of ``self``. (Synonym for ``self.quotient(I)``.) @@ -685,6 +688,9 @@ cdef class Ring(ParentWithGens): there are multiple generators, you can specify a single character string and the generators are named in sequence starting with 0.) + - further named arguments that may be passed to the quotient ring + constructor. + OUTPUT: - ``R/I`` -- the quotient ring of `R` by the ideal `I` @@ -706,7 +712,7 @@ cdef class Ring(ParentWithGens): sage: a == b False """ - return self.quotient(I, names) + return self.quotient(I, names, **kwds) def zero(self): """ diff --git a/src/sage/sat/solvers/dimacs.py b/src/sage/sat/solvers/dimacs.py index 88c901d8b6e..0ee5801b363 100644 --- a/src/sage/sat/solvers/dimacs.py +++ b/src/sage/sat/solvers/dimacs.py @@ -23,15 +23,19 @@ # Copyright (C) 2012 Martin Albrecht # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################## -import os, sys, subprocess, shlex +import os +import sys +import subprocess +import shlex from sage.sat.solvers.satsolver import SatSolver from sage.misc.all import tmp_filename from time import sleep + class DIMACS(SatSolver): """ Generic DIMACS Solver. diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 6a315835850..294a4a27bf0 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -348,7 +348,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= self._radius_lst = None self._radius_func = None - if (radius == None and power == None) or radius == 0: + if (radius is None and power is None) or radius == 0: self._type = 1 self._radius = 0 self._power = None @@ -385,7 +385,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise ValueError('radius univariate function but center is constant. ' + \ 'this does not define a type IV point') raise TypeError("symbolic radius must be a real number") - if (not is_RealNumber(radius)) and power == None: + if (not is_RealNumber(radius)) and power is None: if RR.has_coerce_map_from(radius.parent()): self._radius = RR(radius) else: @@ -460,7 +460,7 @@ def center_function(self): """ if self.type_of_point() != 4: raise ValueError('center_function not defined for points which are not type IV') - if self._center_func == None: + if self._center_func is None: raise ValueError('this type IV point does not have a center function') return self._center_func @@ -488,7 +488,7 @@ def radius_function(self): """ if self.type_of_point() != 4: raise ValueError('center_function not defined for points which are not type IV') - if self._radius_func == None: + if self._radius_func is None: raise ValueError('this type IV point does not have a radius function') return self._radius_func @@ -651,7 +651,7 @@ def diameter(self, basepoint=Infinity): """ if basepoint == Infinity: if self._type == 4: - if self._radius_func == None: + if self._radius_func is None: return self._radius_lst[-1] from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing R = PolynomialRing(QQ, names="x") @@ -1308,11 +1308,11 @@ def as_projective_point(self): return new_space(self.center(), power=self.power()) elif self.type_of_point() == 3: return new_space(self.center(), self.radius()) - if self._center_func == None: + if self._center_func is None: center = self.center() else: center = self.center_function() - if self._radius_func == None: + if self._radius_func is None: radius = self.radius() else: radius = self.radius_function() @@ -1951,11 +1951,11 @@ def as_affine_point(self): return new_space(center, power=self.power()) elif self.type_of_point() == 3: return new_space(center, self.radius()) - if self._center_func == None: + if self._center_func is None: center = [i[0] for i in self.center()] else: center = self.center_function() - if self._radius_func == None: + if self._radius_func is None: radius = self.radius() else: radius = self.radius_function() diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 8a26d62244f..80a213e5987 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -424,7 +424,7 @@ def __init__(self, base, ideal=None): if is_AffineSpace(base): base = base.base_ring() if base in NumberFields(): - if ideal == None: + if ideal is None: raise ValueError('passed a number field but not an ideal') if base is not QQ: if not isinstance(ideal, NumberFieldFractionalIdeal): @@ -625,7 +625,7 @@ def __init__(self, base, ideal=None): raise ValueError("base of projective Berkovich space must be " + \ "projective space over Qp or a number field") else: - if ideal == None: + if ideal is None: raise ValueError('passed a number field but not an ideal') if base.base_ring() is not QQ: if not isinstance(ideal, NumberFieldFractionalIdeal): diff --git a/src/sage/schemes/curves/constructor.py b/src/sage/schemes/curves/constructor.py index ca9b570d6ac..9e9a2a28613 100644 --- a/src/sage/schemes/curves/constructor.py +++ b/src/sage/schemes/curves/constructor.py @@ -104,8 +104,8 @@ def Curve(F, A=None): Also not specifying an ambient space will cause the curve to be defined in either affine or projective space based on properties of ``F``. In - particular, if ``F`` contains a nonhomogenous polynomial, the curve is - affine, and if ``F`` consists of homogenous polynomials, then the curve is + particular, if ``F`` contains a nonhomogeneous polynomial, the curve is + affine, and if ``F`` consists of homogeneous polynomials, then the curve is projective. INPUT: diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 3e5e35f2e51..1091c29c20e 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -123,7 +123,7 @@ - Grayson Jorgenson (2016-08) -- Kwankyu Lee (2019-05): added integral projecive curves +- Kwankyu Lee (2019-05): added integral projective curves """ # **************************************************************************** diff --git a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx index 2147a77f3c2..9ef7cbee84a 100644 --- a/src/sage/schemes/elliptic_curves/mod_sym_num.pyx +++ b/src/sage/schemes/elliptic_curves/mod_sym_num.pyx @@ -2350,7 +2350,7 @@ cdef class ModularSymbolNumerical: - ``rr`` - another rational number - - ``eps`` - a postive real number + - ``eps`` - a positive real number OUTPUT: a complex number diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index ee780eed732..352498bad1f 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -626,7 +626,7 @@ def _degree(self, polynomial): OUTPUT: A tuple of integers, one for each projective space component. A - ``ValueError`` is raised if the polynomial is not multihomogenous. + ``ValueError`` is raised if the polynomial is not multihomogeneous. EXAMPLES:: diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 4fed0413bac..7da57b83ebe 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -28,7 +28,7 @@ Projective Space of dimension 5 over Complex Field with 53 bits of precision The third argument specifies the printing names of the generators of the -homogenous coordinate ring. Using the method `.objgens()` you can obtain both +homogeneous coordinate ring. Using the method `.objgens()` you can obtain both the space and the generators as ready to use variables. :: sage: P2, vars = ProjectiveSpace(10, QQ, 't').objgens() diff --git a/src/sage/sets/cartesian_product.py b/src/sage/sets/cartesian_product.py index edd658daf4e..190c8fee518 100644 --- a/src/sage/sets/cartesian_product.py +++ b/src/sage/sets/cartesian_product.py @@ -273,8 +273,8 @@ def construction(self): (The cartesian_product functorial construction, (Integer Ring, Rational Field)) """ - from sage.categories.cartesian_product import cartesian_product - return cartesian_product, self.cartesian_factors() + from sage.categories.cartesian_product import CartesianProductFunctor + return CartesianProductFunctor(self.category()), self.cartesian_factors() def _coerce_map_from_(self, S): r""" diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index 3bb4384b677..f5d21968d4d 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -791,6 +791,7 @@ cdef class CategoryObject(SageObject): running ._test_cardinality() . . . pass running ._test_category() . . . pass running ._test_characteristic() . . . pass + running ._test_construction() . . . pass running ._test_distributivity() . . . pass running ._test_divides() . . . pass running ._test_elements() . . . @@ -864,6 +865,7 @@ cdef class CategoryObject(SageObject): _test_cardinality _test_category _test_characteristic + _test_construction _test_distributivity _test_divides _test_elements diff --git a/src/sage/symbolic/integration/external.py b/src/sage/symbolic/integration/external.py index 0d6a3d39f5e..f32e1bfed60 100644 --- a/src/sage/symbolic/integration/external.py +++ b/src/sage/symbolic/integration/external.py @@ -41,11 +41,12 @@ def maxima_integrator(expression, v, a=None, b=None): if not isinstance(expression, Expression): expression = SR(expression) if a is None: - result = maxima.sr_integral(expression,v) + result = maxima.sr_integral(expression, v) else: result = maxima.sr_integral(expression, v, a, b) return result._sage_() + def sympy_integrator(expression, v, a=None, b=None): """ Integration using SymPy @@ -67,6 +68,7 @@ def sympy_integrator(expression, v, a=None, b=None): result = sympy.integrate(ex, (v, a._sympy_(), b._sympy_())) return result._sage_() + def mma_free_integrator(expression, v, a=None, b=None): """ Integration using Mathematica's online integrator @@ -119,7 +121,7 @@ def mma_free_integrator(expression, v, a=None, b=None): a._mathematica_init_(), b._mathematica_init_()) else: raise ValueError('a(={}) and b(={}) should be both None' - ' or both defined'.format(a,b)) + ' or both defined'.format(a, b)) json_page_data = request_wolfram_alpha(input) all_outputs = parse_moutput_from_json(json_page_data) if not all_outputs: @@ -127,6 +129,7 @@ def mma_free_integrator(expression, v, a=None, b=None): first_output = all_outputs[0] return symbolic_expression_from_mathematica_string(first_output) + def request_wolfram_alpha(input, verbose=False): r""" Request Wolfram Alpha website. @@ -209,8 +212,7 @@ def request_wolfram_alpha(input, verbose=False): 'scantimeout': '0.5', 'sponsorcategories': 'true', 'statemethod': 'deploybutton', - 'storesubpodexprs': 'true' - } + 'storesubpodexprs': 'true'} # # we can also change some parameters # params = { # 'assumptionsversion': '2', @@ -227,11 +229,12 @@ def request_wolfram_alpha(input, verbose=False): params = urlencode(params) url = "https://www.wolframalpha.com/input/json.jsp?%s" % params req = Request(url) - req.add_header('Referer', "https://www.wolframalpha.com/input/") # seems important + req.add_header('Referer', "https://www.wolframalpha.com/input/") # seems important resp = opener.open(req) # the website returns JSON containing the code return json.loads(resp.read().decode("utf-8")) + def parse_moutput_from_json(page_data, verbose=False): r""" Return the list of outputs found in the json (with key ``'moutput'``) @@ -272,7 +275,7 @@ def parse_moutput_from_json(page_data, verbose=False): queryresult = page_data['queryresult'] if not queryresult['success']: raise ValueError('asking wolframalpha.com was not successful') - if not 'pods' in queryresult: + if 'pods' not in queryresult: raise ValueError('json object contains no pods') pods = queryresult['pods'] if verbose: @@ -297,6 +300,7 @@ def parse_moutput_from_json(page_data, verbose=False): L.append(subpod['moutput']) return L + def symbolic_expression_from_mathematica_string(mexpr): r""" Translate a mathematica string into a symbolic expression @@ -323,14 +327,13 @@ def symbolic_expression_from_mathematica_string(mexpr): from sage.calculus.calculus import symbolic_expression_from_string from sage.calculus.calculus import _find_func as find_func - expr = mexpr.replace('\n',' ').replace('\r', '') + expr = mexpr.replace('\n', ' ').replace('\r', '') expr = expr.replace('[', '(').replace(']', ')') expr = expr.replace('{', '[').replace('}', ']') lsymbols = symbol_table['mathematica'].copy() autotrans = [lambda x:x.lower(), # Try it in lower case - un_camel, # Convert `CamelCase` to `camel_case` - lambda x: x # Try the original name - ] + un_camel, # Convert `CamelCase` to `camel_case` + lambda x: x] # Try the original name # Find the MMA funcs/vars/constants - they start with a letter. # Exclude exponents (e.g. 'e8' from 4.e8) p = re.compile(r'(? # Copyright (C) 2010 Burcin Erocal @@ -12,8 +11,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # ****************************************************************************` -from __future__ import print_function - from sage.symbolic.ring import SR, is_SymbolicVariable from sage.symbolic.function import BuiltinFunction @@ -71,6 +68,11 @@ def __init__(self): sage: f = (exp((x-a)/b) + 1)**(-1) sage: (f*f).integrate(x, algorithm="mathematica_free") # optional -- internet -b*log(e^(-(a - x)/b) + 1) + x + b/(e^(-(a - x)/b) + 1) + + Check for :trac:`25119`:: + + sage: integrate(sqrt(x^2)/x,x) + x*sgn(x) """ # The automatic evaluation routine will try these integrators # in the given order. This is an attribute of the class instead of @@ -115,7 +117,8 @@ def _eval_(self, f, x): for integrator in self.integrators: try: A = integrator(f, x) - except (NotImplementedError, TypeError, AttributeError): + except (NotImplementedError, TypeError, + AttributeError, RuntimeError): pass except ValueError: # maxima is telling us something @@ -218,7 +221,8 @@ def _eval_(self, f, x, a, b): for integrator in self.integrators: try: A = integrator(*args) - except (NotImplementedError, TypeError, AttributeError): + except (NotImplementedError, TypeError, + AttributeError, RuntimeError): pass except ValueError: # maxima is telling us something diff --git a/src/sage/tensor/modules/tensor_with_indices.py b/src/sage/tensor/modules/tensor_with_indices.py index 9b5de539a07..23eedee4406 100644 --- a/src/sage/tensor/modules/tensor_with_indices.py +++ b/src/sage/tensor/modules/tensor_with_indices.py @@ -976,8 +976,8 @@ def swap(param,N): ("^" in term)*term.split("^") + ("^" not in term)*(term.split("^")+['1']) for term in decomposition_as_string.replace("x","").split("*") ] - decomposition = [(swap_params[int(x)-1], int(y)) for x,y in decomposition_as_string] - decomposition.reverse() # /!\ The symetric group acts on the right by default /!\. + decomposition = [(swap_params[int(x)-1], int(y)) for x, y in decomposition_as_string] + decomposition.reverse() # /!\ The symmetric group acts on the right by default /!\. else: decomposition = [] # Choice of a basis diff --git a/src/sage/tests/py3_syntax.py b/src/sage/tests/py3_syntax.py deleted file mode 100644 index 6b72323d3de..00000000000 --- a/src/sage/tests/py3_syntax.py +++ /dev/null @@ -1,223 +0,0 @@ -# -*- coding: utf-8 -*- -r""" -Test that the Sage library uses Python 3 syntax - -AUTHORS: - -- Volker Braun (2017-02-11): initial version - -- Frédéric Chapoton (2017-02-28): fixes - -- Julian Rüth (2017-03-14): documentation changes - -EXAMPLES:: - - sage: from sage.tests.py3_syntax import Python3SyntaxTest - sage: py3_syntax = Python3SyntaxTest('sage', 'sage_setup') - sage: py3_syntax.run_tests('.py') # long time -""" -# **************************************************************************** -# Copyright (C) 2017 Volker Braun -# 2017 Frédéric Chapoton -# 2017 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. -# https://www.gnu.org/licenses/ -# **************************************************************************** -from __future__ import print_function - -import os -import itertools -import subprocess - -from sage.env import SAGE_SRC -from sage.cpython.string import bytes_to_str - - -class SortedDirectoryWalkerABC(object): - r""" - Walks the directory tree in a reproducible manner. - - INPUT: - - - ``*directories`` -- roots of the directory trees to walk - - EXAMPLES:: - - sage: from sage.tests.py3_syntax import SortedDirectoryWalkerABC - sage: walker = SortedDirectoryWalkerABC('sage/tests') - - """ - def __init__(self, *directories): - r""" - TESTS:: - - sage: from sage.tests.py3_syntax import Python3SyntaxTest, SortedDirectoryWalkerABC - sage: isinstance(Python3SyntaxTest('sage/tests'), SortedDirectoryWalkerABC) - True - """ - self._directories = tuple( - os.path.join(SAGE_SRC, d) for d in directories - ) - - def __iter__(self): - r""" - Return an iterator over the files in the directories. - - OUTPUT: - - Iterator over triples consisting of path, filename, and file - extension. The iteration order only depends on the filenames - and not on filesystem layout. - - EXAMPLES:: - - sage: from sage.tests.py3_syntax import Python3SyntaxTest - sage: name = 'sage/tests/books/computational-mathematics-with-sagemath' - sage: test = Python3SyntaxTest(name) - sage: next(iter(test)) - ('.../sage/tests/books/computational-mathematics-with-sagemath', 'README', '') - """ - tree_walk = itertools.chain(*map(os.walk, self._directories)) - for path, _, files in tree_walk: - path = os.path.relpath(path) - files.sort() - for filename in files: - if filename.startswith('.'): - continue - _, ext = os.path.splitext(filename) - yield (path, filename, ext) - - def run_tests(self, *extensions): - r""" - Run :meth:`test` on each file with a matching extension. - - INPUT: - - - ``*extensions`` -- the file extensions (including the leading dot) to - check - - EXAMPLES:: - - sage: from sage.tests.py3_syntax import SortedDirectoryWalkerABC - sage: walker = SortedDirectoryWalkerABC('sage/tests/french_book') - sage: walker.run_tests('.py') - Traceback (most recent call last): - ... - NotImplementedError: to be implemented in derived class - """ - buf = [] - for (path, filename, ext) in self: - if ext in extensions: - fqn = os.path.join(path, filename) - buf.append(fqn) - if len(buf) > 20: - self.test(*buf) - buf = [] - self.test(*buf) - - def test(self, *filenames): - r""" - Test the given filenames. - - To be implemented in derived classes. - - INPUT: - - - ``*filenames`` -- the fully qualified filenames to check - - EXAMPLES:: - - sage: from sage.tests.py3_syntax import SortedDirectoryWalkerABC - sage: walker = SortedDirectoryWalkerABC() - sage: from sage.env import SAGE_SRC - sage: filename = os.path.join(SAGE_SRC, 'sage', 'tests', 'py3_syntax.py') - sage: walker.test(filename) - Traceback (most recent call last): - ... - NotImplementedError: to be implemented in derived class - """ - raise NotImplementedError('to be implemented in derived class') - - -PYTHON3_ENV = dict(os.environ) -PYTHON3_ENV.pop('PYTHONPATH', None) - -class Python3SyntaxTest(SortedDirectoryWalkerABC): - r""" - Walks the directory tree and tests that all Python files are valid Python 3 - syntax. - - INPUT: - - - ``*directories`` -- roots of the directory trees to walk - - EXAMPLES:: - - sage: from sage.tests.py3_syntax import Python3SyntaxTest - sage: walker = Python3SyntaxTest('sage/tests') - sage: walker.run_tests('.py') # long time - - """ - def test(self, *filenames): - r""" - Print an error message if the given files are not using valid Python 3 - syntax. - - INPUT: - - - ``*filenames`` -- the fully qualified filenames to check - - EXAMPLES:: - - sage: import os, tempfile - sage: src = tempfile.NamedTemporaryFile(suffix='.py', mode='w+', delete=False) - sage: _ = src.write('print "invalid print statement"') - sage: src.close() - sage: from sage.tests.py3_syntax import Python3SyntaxTest - sage: py3_syntax = Python3SyntaxTest() - sage: py3_syntax.test(src.name) - Invalid Python 3 syntax found: - Missing parentheses in call to 'print'. - Did you mean print("invalid print statement")? (...py, line 1) - sage: os.unlink(src.name) - """ - - # compile all given files in memory, printing all errors - # inspired by the py_compile module (but without writing to file) - script = """ -import sys -import importlib.machinery -rv = 0 -for file in sys.argv[1:]: - loader = importlib.machinery.SourceFileLoader('', file) - source_bytes = loader.get_data(file) - try: - code = loader.source_to_code(source_bytes, file) - except Exception as err: - print(err) - rv = 1 -sys.exit(rv) -""" - cmd = [ - 'python3', - '-c', - script, - ] + list(filenames) - process = subprocess.Popen( - cmd, - env=PYTHON3_ENV, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - stdout, stderr = process.communicate() - if process.returncode == 0: - return - print('Invalid Python 3 syntax found:') - if stdout: - print(bytes_to_str(stdout)) - if stderr: - print(bytes_to_str(stderr)) diff --git a/src/sage/version.py b/src/sage/version.py index 9319df9bb7f..9d20bbfa30b 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.2.beta13' -date = '2020-09-21' -banner = 'SageMath version 9.2.beta13, Release Date: 2020-09-21' +version = '9.2.beta14' +date = '2020-09-30' +banner = 'SageMath version 9.2.beta14, Release Date: 2020-09-30' diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 0841f429e73..910e414f1f5 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -826,7 +826,6 @@ def __init__(self, dir): env_pickle = os.path.join(self._doctrees_dir(), 'environment.pickle') try: with open(env_pickle, 'rb') as f: - import pickle env = pickle.load(f) env.app = FakeApp(self.dir) env.config.values = env.app.config.values diff --git a/src/tox.ini b/src/tox.ini index fd62814b22e..22947a7a7a7 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -21,7 +21,7 @@ ## in a virtual environment. ## [tox] -envlist = doctest, coverage, startuptime, pycodestyle, relint +envlist = doctest, coverage, startuptime, pycodestyle, relint, codespell # When adding environments above, also update the delegations in SAGE_ROOT/tox.ini skipsdist = true @@ -88,9 +88,24 @@ max-line-length = 160 [testenv:relint] description = check whether some forbidden patterns appear - (same as the patchbot pattern-exclusion plugins) + (includes all patchbot pattern-exclusion plugins) # https://github.com/codingjoe/relint # The patterns are in .relint.yml deps = relint whitelist_externals = sh commands = sh -c 'relint -c {toxinidir}/.relint.yml $(for a in {posargs:{toxinidir}/sage/}; do find $a -type f; done)' + +[testenv:codespell] +description = + check for misspelled words in source code +# https://pypi.org/project/codespell/ +deps = codespell +commands = codespell \ + --skip="*.png,*.jpg,*.JPG,*.inv,*.dia,*.pdf,*.ico,*#*,*~*,*.bak,*.orig,*.log,*.sobj,*.tar,*.gz,*.pyc,*.o,*.sws,*.so,*.a,.DS_Store" \ + --skip="doc/ca,doc/de,doc/es,doc/hu,doc/ja,doc/ru,doc/fr,doc/it,doc/pt,doc/tr" \ + --skip="src/doc/ca,src/doc/de,src/doc/es,src/doc/hu,src/doc/ja,src/doc/ru,src/doc/fr,src/doc/it,src/doc/pt,src/doc/tr" \ + --skip=".git,.tox,worktree*,dist,upstream,logs,local,cythonized,scripts-3,autom4te.cache,tmp,lib.*,*.egg-info" \ + --dictionary=- \ + --dictionary={toxinidir}/.codespell-dictionary.txt \ + --ignore-words={toxinidir}/.codespell-ignore.txt \ + {posargs:{toxinidir}/sage/} diff --git a/tox.ini b/tox.ini index a1b45a6fa61..d2e05a7efa2 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ ### envlist = ##### Delegation to src/tox.ini ##### - doctest, coverage, startuptime, pycodestyle, relint, + doctest, coverage, startuptime, pycodestyle, relint, codespell, ##### Sage-the-distribution tests ##### check_configure, ##### Sage-the-distribution portability tests ##### @@ -130,6 +130,8 @@ setenv = TYPE_PATTERN=standard minimal: TYPE_PATTERN=minimal maximal: TYPE_PATTERN=@(standard|optional) + # local envs need HOME set, also Docker 19.03 needs HOME + {local,docker}: HOME={envdir} # # default tag is "latest" # @@ -318,7 +320,6 @@ setenv = ### "local" envs ### homebrew: SYSTEM=homebrew - local: HOME={envdir} local: SHARED_CACHE_DIR={toxworkdir}/Caches local: SETENV=: local-nobootstrap: BOOTSTRAP=: @@ -344,15 +345,15 @@ setenv = # python3: CONFIG_CONFIGURE_ARGS_1= python3_spkg: CONFIG_CONFIGURE_ARGS_1=--without-system-python3 - macos-python3_xcode: CONFIG_CONFIGURE_ARGS_1=--with-python=3 PYTHON3=/usr/bin/python3 + macos-python3_xcode: CONFIG_CONFIGURE_ARGS_1=--with-python=/usr/bin/python3 # Must manually download and install from https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg - macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-python=3 PYTHON3=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3 + macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-python=/Library/Frameworks/Python.framework/Versions/3.7/bin/python3 # https://github.com/pypa/manylinux - manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force PYTHON3=/opt/python/cp38-cp38/bin/python3 - manylinux-python3.6: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force PYTHON3=/opt/python/cp36-cp36m/bin/python3 - manylinux-python3.7: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force PYTHON3=/opt/python/cp37-cp37m/bin/python3 - manylinux-python3.8: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force PYTHON3=/opt/python/cp38-cp38/bin/python3 - manylinux-python3.9: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force PYTHON3=/opt/python/cp39-cp39/bin/python3 + manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp38-cp38/bin/python3 + manylinux-python3.6: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp36-cp36m/bin/python3 + manylinux-python3.7: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp37-cp37m/bin/python3 + manylinux-python3.8: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp38-cp38/bin/python3 + manylinux-python3.9: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp39-cp39/bin/python3 # # - toolchain # @@ -455,7 +456,7 @@ passenv = HOME envdir = {toxworkdir}/src whitelist_externals = tox -commands = tox -c {toxinidir}/src/tox.ini -e {envname} {posargs} +commands = tox -c {toxinidir}/src/tox.ini -e {envname} -- {posargs} [testenv:doctest] description = @@ -496,3 +497,12 @@ passenv = {[sage_src]passenv} envdir = {[sage_src]envdir} commands = {[sage_src]commands} whitelist_externals = {[sage_src]whitelist_externals} + +[testenv:codespell] +description = + check for misspelled words in source code (use -w -i to fix) +passenv = {[sage_src]passenv} +envdir = {[sage_src]envdir} +whitelist_externals = {[sage_src]whitelist_externals} +# Run on the whole project, not just src/ by default, if invoked directly at top level +commands = tox -c {toxinidir}/src/tox.ini -e {envname} -- {posargs:{toxinidir}}