diff --git a/VERSION.txt b/VERSION.txt index 0091aa67e50..25a6e6cf911 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 7.2.beta4, Release Date: 2016-04-12 +SageMath version 7.2.beta5, Release Date: 2016-04-21 diff --git a/build/pkgs/babel/SPKG.txt b/build/pkgs/babel/SPKG.txt new file mode 100644 index 00000000000..ae0451d58be --- /dev/null +++ b/build/pkgs/babel/SPKG.txt @@ -0,0 +1,7 @@ += babel = + +== Description == + +Internationalization utilities + +A collection of tools for internationalizing Python applications. diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini new file mode 100644 index 00000000000..adfcefcf9db --- /dev/null +++ b/build/pkgs/babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Babel-VERSION.tar.gz +sha1=e02392bc9a16f7672686bce23e4e3cdadcc1b1c8 +md5=1b69e4b2ab3795119266ccaa36b36f15 +cksum=2897183559 diff --git a/build/pkgs/babel/dependencies b/build/pkgs/babel/dependencies new file mode 100644 index 00000000000..247fe4de4d9 --- /dev/null +++ b/build/pkgs/babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip pytz + +---------- +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/babel/package-version.txt b/build/pkgs/babel/package-version.txt new file mode 100644 index 00000000000..ccbccc3dc62 --- /dev/null +++ b/build/pkgs/babel/package-version.txt @@ -0,0 +1 @@ +2.2.0 diff --git a/build/pkgs/babel/spkg-install b/build/pkgs/babel/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/babel/type b/build/pkgs/babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index b9cda22f894..a4c5bec5b26 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=a688b4151ebffbd892199e47edf21f0e295ae06f -md5=1da510585db296e3930eaa33cbb40a87 -cksum=3334341322 +sha1=443f6e49b23bbc1e9004f32c802b83e35abe3a77 +md5=bb7132154839ae45a55ca1c46331289d +cksum=2428928852 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 4c5c8078521..3f7d1915f71 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -158 +159 diff --git a/build/pkgs/flask/SPKG.txt b/build/pkgs/flask/SPKG.txt new file mode 100644 index 00000000000..cf46f1da69e --- /dev/null +++ b/build/pkgs/flask/SPKG.txt @@ -0,0 +1,8 @@ += Flask = + +== Description == + +A microframework based on Werkzeug, Jinja2 and good intentions + +Flask is a microframework for Python based on Werkzeug, Jinja 2 and good +intentions. And before you ask: It’s BSD licensed! diff --git a/build/pkgs/flask/checksums.ini b/build/pkgs/flask/checksums.ini new file mode 100644 index 00000000000..f9167d09e4a --- /dev/null +++ b/build/pkgs/flask/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask-VERSION.tar.gz +sha1=d3d078262b053f4438e2ed3fd6f9b923c2c92172 +md5=378670fe456957eb3c27ddaef60b2b24 +cksum=2901487846 diff --git a/build/pkgs/flask/dependencies b/build/pkgs/flask/dependencies new file mode 100644 index 00000000000..4c9f9037ab5 --- /dev/null +++ b/build/pkgs/flask/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip werkzeug jinja2 itsdangerous + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask/package-version.txt b/build/pkgs/flask/package-version.txt new file mode 100644 index 00000000000..571215736a6 --- /dev/null +++ b/build/pkgs/flask/package-version.txt @@ -0,0 +1 @@ +0.10.1 diff --git a/build/pkgs/flask/spkg-install b/build/pkgs/flask/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/flask/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/flask/type b/build/pkgs/flask/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_autoindex/SPKG.txt b/build/pkgs/flask_autoindex/SPKG.txt new file mode 100644 index 00000000000..6ef8325067a --- /dev/null +++ b/build/pkgs/flask_autoindex/SPKG.txt @@ -0,0 +1,9 @@ += Flask-AutoIndex = + +== Description == + +The mod_autoindex for Flask + +Flask-AutoIndex generates an index page for your Flask application +automatically. The result just like mod_autoindex, but the look is more +awesome! diff --git a/build/pkgs/flask_autoindex/checksums.ini b/build/pkgs/flask_autoindex/checksums.ini new file mode 100644 index 00000000000..032dbb85894 --- /dev/null +++ b/build/pkgs/flask_autoindex/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_AutoIndex-VERSION.tar.gz +sha1=15e08bd3a516aa327ab4af767a29154a4bebdae3 +md5=24984602365704737468bb4d2586a739 +cksum=1140907747 diff --git a/build/pkgs/flask_autoindex/dependencies b/build/pkgs/flask_autoindex/dependencies new file mode 100644 index 00000000000..3904350ca14 --- /dev/null +++ b/build/pkgs/flask_autoindex/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask_silk + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_autoindex/package-version.txt b/build/pkgs/flask_autoindex/package-version.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/build/pkgs/flask_autoindex/package-version.txt @@ -0,0 +1 @@ +0.5 diff --git a/build/pkgs/flask_autoindex/spkg-install b/build/pkgs/flask_autoindex/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_autoindex/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_autoindex/type b/build/pkgs/flask_autoindex/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_autoindex/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_babel/SPKG.txt b/build/pkgs/flask_babel/SPKG.txt new file mode 100644 index 00000000000..f96f6411918 --- /dev/null +++ b/build/pkgs/flask_babel/SPKG.txt @@ -0,0 +1,6 @@ += Flask-Babel = + +== Description == + +Adds i18n/l10n support to Flask applications with the help of the Babel +library. diff --git a/build/pkgs/flask_babel/checksums.ini b/build/pkgs/flask_babel/checksums.ini new file mode 100644 index 00000000000..49652906818 --- /dev/null +++ b/build/pkgs/flask_babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Babel-VERSION.tar.gz +sha1=977d3b152f876e06c215f6bb72616b4ce138fa49 +md5=4762e0392303f464d53cbebedfb87ded +cksum=2286190911 diff --git a/build/pkgs/flask_babel/dependencies b/build/pkgs/flask_babel/dependencies new file mode 100644 index 00000000000..06da0f070bc --- /dev/null +++ b/build/pkgs/flask_babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask speaklater + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_babel/package-version.txt b/build/pkgs/flask_babel/package-version.txt new file mode 100644 index 00000000000..b63ba696b7a --- /dev/null +++ b/build/pkgs/flask_babel/package-version.txt @@ -0,0 +1 @@ +0.9 diff --git a/build/pkgs/flask_babel/spkg-install b/build/pkgs/flask_babel/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_babel/type b/build/pkgs/flask_babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_oldsessions/SPKG.txt b/build/pkgs/flask_oldsessions/SPKG.txt new file mode 100644 index 00000000000..2af9b2ebb23 --- /dev/null +++ b/build/pkgs/flask_oldsessions/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OldSessions = + +== Description == + +Provides a session class that works like the one in Flask before 0.10. diff --git a/build/pkgs/flask_oldsessions/checksums.ini b/build/pkgs/flask_oldsessions/checksums.ini new file mode 100644 index 00000000000..c5fc0993ff0 --- /dev/null +++ b/build/pkgs/flask_oldsessions/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OldSessions-VERSION.tar.gz +sha1=1c0bbcd79b4fc626da2fd34ce9b59b2d43f6d81e +md5=3d731d343d5380bb9f502742ad62df50 +cksum=709943870 diff --git a/build/pkgs/flask_oldsessions/dependencies b/build/pkgs/flask_oldsessions/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/flask_oldsessions/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_oldsessions/package-version.txt b/build/pkgs/flask_oldsessions/package-version.txt new file mode 100644 index 00000000000..68c123cf10e --- /dev/null +++ b/build/pkgs/flask_oldsessions/package-version.txt @@ -0,0 +1 @@ +0.10 diff --git a/build/pkgs/flask_oldsessions/spkg-install b/build/pkgs/flask_oldsessions/spkg-install new file mode 100755 index 00000000000..56301585aa0 --- /dev/null +++ b/build/pkgs/flask_oldsessions/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd *flask-oldsessions* && python setup.py install diff --git a/build/pkgs/flask_oldsessions/type b/build/pkgs/flask_oldsessions/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_oldsessions/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_openid/SPKG.txt b/build/pkgs/flask_openid/SPKG.txt new file mode 100644 index 00000000000..77aa5b1c152 --- /dev/null +++ b/build/pkgs/flask_openid/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OpenID = + +== Description == + +OpenID support for Flask diff --git a/build/pkgs/flask_openid/checksums.ini b/build/pkgs/flask_openid/checksums.ini new file mode 100644 index 00000000000..a17be5fa13a --- /dev/null +++ b/build/pkgs/flask_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OpenID-VERSION.tar.gz +sha1=18d39e03417cd2b577cd5c1f5c2ac117493f3fef +md5=a40c63df701ec634450d03490ddfb6c1 +cksum=995771756 diff --git a/build/pkgs/flask_openid/dependencies b/build/pkgs/flask_openid/dependencies new file mode 100644 index 00000000000..5367d7a02a6 --- /dev/null +++ b/build/pkgs/flask_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask python_openid + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_openid/package-version.txt b/build/pkgs/flask_openid/package-version.txt new file mode 100644 index 00000000000..c813fe116c9 --- /dev/null +++ b/build/pkgs/flask_openid/package-version.txt @@ -0,0 +1 @@ +1.2.5 diff --git a/build/pkgs/flask_openid/spkg-install b/build/pkgs/flask_openid/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_openid/type b/build/pkgs/flask_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_silk/SPKG.txt b/build/pkgs/flask_silk/SPKG.txt new file mode 100644 index 00000000000..92d3009a886 --- /dev/null +++ b/build/pkgs/flask_silk/SPKG.txt @@ -0,0 +1,5 @@ += Flask-Silk = + +== Description == + +Adds silk icons to your Flask application or blueprint, or extension. diff --git a/build/pkgs/flask_silk/checksums.ini b/build/pkgs/flask_silk/checksums.ini new file mode 100644 index 00000000000..a54c85cdfd6 --- /dev/null +++ b/build/pkgs/flask_silk/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Silk-VERSION.tar.gz +sha1=cef42b469c9ebb69a766d0cd33ad27480800d518 +md5=aca545a94063dc4acd21779ea5dde330 +cksum=1557295080 diff --git a/build/pkgs/flask_silk/dependencies b/build/pkgs/flask_silk/dependencies new file mode 100644 index 00000000000..05fe89830e0 --- /dev/null +++ b/build/pkgs/flask_silk/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_silk/package-version.txt b/build/pkgs/flask_silk/package-version.txt new file mode 100644 index 00000000000..3b04cfb60da --- /dev/null +++ b/build/pkgs/flask_silk/package-version.txt @@ -0,0 +1 @@ +0.2 diff --git a/build/pkgs/flask_silk/spkg-install b/build/pkgs/flask_silk/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_silk/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_silk/type b/build/pkgs/flask_silk/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_silk/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/itsdangerous/SPKG.txt b/build/pkgs/itsdangerous/SPKG.txt new file mode 100644 index 00000000000..32264811471 --- /dev/null +++ b/build/pkgs/itsdangerous/SPKG.txt @@ -0,0 +1,6 @@ += itsdangerous = + +== Description == + +Various helpers to pass data to untrusted environments and to get it back +safe and sound. diff --git a/build/pkgs/itsdangerous/checksums.ini b/build/pkgs/itsdangerous/checksums.ini new file mode 100644 index 00000000000..f34dca2926d --- /dev/null +++ b/build/pkgs/itsdangerous/checksums.ini @@ -0,0 +1,4 @@ +tarball=itsdangerous-VERSION.tar.gz +sha1=0a6ae9c20cd72e89d75314ebc7b0f390f93e6a0d +md5=a3d55aa79369aef5345c036a8a26307f +cksum=2767256127 diff --git a/build/pkgs/itsdangerous/dependencies b/build/pkgs/itsdangerous/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/itsdangerous/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/itsdangerous/package-version.txt b/build/pkgs/itsdangerous/package-version.txt new file mode 100644 index 00000000000..fd137eb17eb --- /dev/null +++ b/build/pkgs/itsdangerous/package-version.txt @@ -0,0 +1 @@ +0.24 diff --git a/build/pkgs/itsdangerous/spkg-install b/build/pkgs/itsdangerous/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/itsdangerous/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/itsdangerous/type b/build/pkgs/itsdangerous/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/itsdangerous/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/libfplll/checksums.ini b/build/pkgs/libfplll/checksums.ini index 0796175109d..a3434773354 100644 --- a/build/pkgs/libfplll/checksums.ini +++ b/build/pkgs/libfplll/checksums.ini @@ -1,4 +1,4 @@ -tarball=libfplll-VERSION.tar.bz2 -sha1=0810bb8283671b7bb84392494cc84a20162dd40f -md5=281bbe2b95572401b6cd5264b5694863 -cksum=3698765896 +tarball=libfplll-VERSION.tar.gz +sha1=4f03734645abe800cd4d7838fcec0cbd2c551633 +md5=661440d1249e1f8d6346ab91a8658de6 +cksum=1133822281 diff --git a/build/pkgs/libfplll/package-version.txt b/build/pkgs/libfplll/package-version.txt index d6344119dc3..1528ea5fdf3 100644 --- a/build/pkgs/libfplll/package-version.txt +++ b/build/pkgs/libfplll/package-version.txt @@ -1 +1 @@ -20160107 +20160331 diff --git a/build/pkgs/libfplll/spkg-src b/build/pkgs/libfplll/spkg-src new file mode 100755 index 00000000000..99534d180e2 --- /dev/null +++ b/build/pkgs/libfplll/spkg-src @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +FPLLL_REVISION="b50fd91ba0aeea2067dc9d82e6c352dbe0210eb3" +FPLLL_VERSION="4.0.5" +SPKG_ROOT="$SAGE_ROOT/build/pkgs/libfplll" + +set -e + +cd "$SPKG_ROOT" + +FPLLL_SAGE_VERSION=`cat package-version.txt |sed 's/[.]p.*//'` + +rm -rf fplll-checkout +git clone git://github.com/dstehle/fplll fplll-checkout + +cd fplll-checkout +git checkout "$FPLLL_REVISION" +./autogen.sh +./configure +make dist +tar xvfz libfplll-"$FPLLL_VERSION".tar.gz +rm libfplll-"$FPLLL_VERSION".tar.gz +mv libfplll-"$FPLLL_VERSION" libfplll-"$FPLLL_SAGE_VERSION" +tar cvfz libfplll-"$FPLLL_SAGE_VERSION".tar.gz libfplll-"$FPLLL_SAGE_VERSION" + +mv libfplll-"$FPLLL_SAGE_VERSION".tar.gz "$SAGE_DISTFILES/" +cd .. +rm -rf fplll-checkout diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index 7ea96bd9de2..42b838e8213 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,7 +1,4 @@ -$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | sagenb setuptools - -The sagenb dependency is because of the "pytz" package. If that package -is ever pulled out from sagenb, replace "sagenb" by "pytz" above. +$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | pytz setuptools ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch index 1d8d0c41b0d..74ec30faf63 100644 --- a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch +++ b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch @@ -1,11 +1,25 @@ ---- mpfr-3.1.4/src/mpfr-impl.h 2016-03-06 12:33:04.000000000 +0100 -+++ mpfr-3.1.4/src/mpfr-impl.h 2016-03-06 17:25:16.341090700 +0100 -@@ -257,6 +257,8 @@ typedef struct __gmpfr_cache_s *mpfr_cac - # if defined(_WIN32) - # error "Both __unix__ and _WIN32 are defined" - # endif -+#endif -+#if (defined(__unix__) && !defined(__CYGWIN__)) - # if __GMP_LIBGMP_DLL - # error "__unix__ is defined and __GMP_LIBGMP_DLL is true" - # endif +Patch from upstream needed to fix an error when compiling on Cygwin +See http://trac.sagemath.org/ticket/20423 for discussion and link to +the original upstream patch. +--- trunk/src/mpfr-impl.h 2016/04/08 15:55:03 10257 ++++ trunk/src/mpfr-impl.h 2016/04/08 23:17:07 10260 +@@ -208,19 +208,6 @@ + # define MPFR_WIN_THREAD_SAFE_DLL 1 + #endif + +-/* Detect some possible inconsistencies under Unix. */ +-#if defined(__unix__) +-# if defined(_WIN32) +-# error "Both __unix__ and _WIN32 are defined" +-# endif +-# if __GMP_LIBGMP_DLL +-# error "__unix__ is defined and __GMP_LIBGMP_DLL is true" +-# endif +-# if defined(MPFR_WIN_THREAD_SAFE_DLL) +-# error "Both __unix__ and MPFR_WIN_THREAD_SAFE_DLL are defined" +-# endif +-#endif +- + #if defined(__MPFR_WITHIN_MPFR) || !defined(MPFR_WIN_THREAD_SAFE_DLL) + extern MPFR_THREAD_ATTR mpfr_flags_t __gmpfr_flags; + extern MPFR_THREAD_ATTR mpfr_exp_t __gmpfr_emin; diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index 212e5fa11aa..06666ba7e64 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,4 +1,4 @@ tarball=nbconvert-VERSION.tar.gz -sha1=079fd51c75e5c8b76f9679aa7fc5ad8176004720 -md5=58ee16f5da0dd2976ff992511d6bfb43 -cksum=75346878 +sha1=0f3c792cc624e09b2524cd0a010618036647a8cd +md5=710e356c4effcfddd29a3c10f1d8c3e6 +cksum=137274062 diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index e73b5a93040..8ba4997e1b6 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION.tar.gz -sha1=cf953bcaf73ba43e7ef0ce829126b6605105dd0f -md5=d390cf1a0785a43711a2548c8f3c91b8 -cksum=2636243005 +sha1=f5466b2f04e1b9c913dcf79f7e03812eba718d0d +md5=3a72bb93f6f01caec69e5e65718f5f7f +cksum=3858938777 diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/notebook/patches/help_link_url_fix.patch b/build/pkgs/notebook/patches/help_link_url_fix.patch deleted file mode 100644 index cfa07dee3d3..00000000000 --- a/build/pkgs/notebook/patches/help_link_url_fix.patch +++ /dev/null @@ -1,24 +0,0 @@ -Use require.toUrl for help_links - -Dirty patch for the minified js, real PR is at -https://github.com/jupyter/notebook/pull/958 - - ---- a/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:30.769442884 +0100 -+++ b/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:53.073164049 +0100 -@@ -28358,7 +28358,7 @@ - .append($("") - .attr('target', '_blank') - .attr('title', 'Opens in a new window') -- .attr('href', link.url) -+ .attr('href', require.toUrl(link.url)) - .append($("") - .addClass("fa fa-external-link menu-icon pull-right") - ) -@@ -30547,4 +30547,4 @@ - define("notebook/js/main", function(){}); - - --//# sourceMappingURL=main.min.js.map -\ No newline at end of file -+//# sourceMappingURL=main.min.js.map diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 8afe164d6cd..33d0cc00e8d 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,4 @@ tarball=numpy-VERSION.tar.gz -sha1=46a653d3a3e474bf284cbf213c2ce5fa85c39371 -md5=aed294de0aa1ac7bd3f9745f4f1968ad -cksum=4106390035 +sha1=3e43596cba1d5df4002dd0c87d4041f31ea6e1b5 +md5=bc56fb9fc2895aa4961802ffbdb31d0b +cksum=906704492 diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 45419ec63fd..1cac385c6cb 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.10.4.p1 +1.11.0 diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch index 7a65280efe7..e5f04a4194e 100644 --- a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch +++ b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch @@ -1,3 +1,8 @@ +BUG: Let linspace accept input that has an array_interface but is not otherwise a numpy or python type. + +Upstream PR: https://github.com/numpy/numpy/pull/6659 + + diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core/function_base.py --- numpy-1.10.1.orig/numpy/core/function_base.py 2015-11-11 10:12:45.583322683 +1300 +++ numpy-1.10.1/numpy/core/function_base.py 2015-11-11 10:14:05.813343880 +1300 @@ -5,8 +10,8 @@ diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core __all__ = ['logspace', 'linspace'] from . import numeric as _nx --from .numeric import result_type, NaN -+from .numeric import result_type, NaN, asanyarray +-from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError ++from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError, asanyarray def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): diff --git a/build/pkgs/pathlib2/SPKG.txt b/build/pkgs/pathlib2/SPKG.txt new file mode 100644 index 00000000000..3330269b541 --- /dev/null +++ b/build/pkgs/pathlib2/SPKG.txt @@ -0,0 +1,11 @@ += pathlib = + +== Description == + +Object-oriented filesystem paths + +The old pathlib module on bitbucket is in bugfix-only mode. The goal +of pathlib2 is to provide a backport of standard pathlib module which +tracks the standard library module, so all the newest features of the +standard pathlib can be used also on older Python versions. + diff --git a/build/pkgs/pathlib2/checksums.ini b/build/pkgs/pathlib2/checksums.ini new file mode 100644 index 00000000000..b5c3632d898 --- /dev/null +++ b/build/pkgs/pathlib2/checksums.ini @@ -0,0 +1,4 @@ +tarball=pathlib2-VERSION.tar.gz +sha1=a4329faa7d2f0ba2430eeb57372d3a72ccce6ca2 +md5=38e4f58b4d69dfcb9edb49a54a8b28d2 +cksum=512183712 diff --git a/build/pkgs/pathlib2/dependencies b/build/pkgs/pathlib2/dependencies new file mode 100644 index 00000000000..ae35f4f0477 --- /dev/null +++ b/build/pkgs/pathlib2/dependencies @@ -0,0 +1,5 @@ +setuptools + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pathlib2/package-version.txt b/build/pkgs/pathlib2/package-version.txt new file mode 100644 index 00000000000..7ec1d6db408 --- /dev/null +++ b/build/pkgs/pathlib2/package-version.txt @@ -0,0 +1 @@ +2.1.0 diff --git a/build/pkgs/pathlib2/spkg-install b/build/pkgs/pathlib2/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/pathlib2/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/pathlib2/type b/build/pkgs/pathlib2/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pathlib2/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pickleshare/checksums.ini b/build/pkgs/pickleshare/checksums.ini index a385506705a..69926ba959d 100644 --- a/build/pkgs/pickleshare/checksums.ini +++ b/build/pkgs/pickleshare/checksums.ini @@ -1,4 +1,4 @@ tarball=pickleshare-VERSION.tar.gz -sha1=3f9176870b015f5c84ab4e7afb42470f78a0ed34 -md5=7fadddce8b1b0110c4ef905be795001a -cksum=2251024212 +sha1=94e109ff8b35768388d5a2466b841cd47853f81e +md5=29d74cde0255546b6b2e1b48a0b31a54 +cksum=3883458288 diff --git a/build/pkgs/pickleshare/dependencies b/build/pkgs/pickleshare/dependencies index 945c50635dd..7f16f620fe4 100644 --- a/build/pkgs/pickleshare/dependencies +++ b/build/pkgs/pickleshare/dependencies @@ -1,4 +1,4 @@ -setuptools pathpy +setuptools pathpy pathlib2 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pickleshare/package-version.txt b/build/pkgs/pickleshare/package-version.txt index 5a2a5806df6..7486fdbc50b 100644 --- a/build/pkgs/pickleshare/package-version.txt +++ b/build/pkgs/pickleshare/package-version.txt @@ -1 +1 @@ -0.6 +0.7.2 diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index 6cb77b8fa0f..1d6e55ce60d 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,4 @@ tarball=Pillow-VERSION.tar.gz -sha1=71d8dc1dd38ba2582f7cca8b5ce70af03d19db23 -md5=d382a86c4b9b1c8de684bd00dad43bb8 -cksum=4161496668 +sha1=5381cdd06dc00a86b0221110c768d7b49c27dc56 +md5=7cfd093c11205d9e2ebe3c51dfcad510 +cksum=4253553307 diff --git a/build/pkgs/pillow/package-version.txt b/build/pkgs/pillow/package-version.txt index 94ff29cc4de..944880fa15e 100644 --- a/build/pkgs/pillow/package-version.txt +++ b/build/pkgs/pillow/package-version.txt @@ -1 +1 @@ -3.1.1 +3.2.0 diff --git a/build/pkgs/python_openid/SPKG.txt b/build/pkgs/python_openid/SPKG.txt new file mode 100644 index 00000000000..390a18695b1 --- /dev/null +++ b/build/pkgs/python_openid/SPKG.txt @@ -0,0 +1,11 @@ += python-openid = + +== Description == + +OpenID support for servers and consumers. + +This is a set of Python packages to support use of the OpenID decentralized +identity system in your application. Want to enable single sign-on for your +web site? Use the openid.consumer package. Want to run your own OpenID server? +Check out openid.server. Includes example code and support for a variety of +storage back-ends. diff --git a/build/pkgs/python_openid/checksums.ini b/build/pkgs/python_openid/checksums.ini new file mode 100644 index 00000000000..6e35600da54 --- /dev/null +++ b/build/pkgs/python_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=python_openid-VERSION.tar.gz +sha1=e1a8e9502abf45e22dbc5f5ef76e56e139ea1b3d +md5=393f48b162ec29c3de9e2973548ea50d +cksum=2866118713 diff --git a/build/pkgs/python_openid/dependencies b/build/pkgs/python_openid/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/python_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/python_openid/package-version.txt b/build/pkgs/python_openid/package-version.txt new file mode 100644 index 00000000000..21bb5e156fb --- /dev/null +++ b/build/pkgs/python_openid/package-version.txt @@ -0,0 +1 @@ +2.2.5 diff --git a/build/pkgs/python_openid/spkg-install b/build/pkgs/python_openid/spkg-install new file mode 100755 index 00000000000..9d8788038ec --- /dev/null +++ b/build/pkgs/python_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd python-openid-* && python setup.py install diff --git a/build/pkgs/python_openid/type b/build/pkgs/python_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/python_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pytz/SPKG.txt b/build/pkgs/pytz/SPKG.txt new file mode 100644 index 00000000000..94190513f91 --- /dev/null +++ b/build/pkgs/pytz/SPKG.txt @@ -0,0 +1,12 @@ += pytz = + +== Description == + +World Timezone Definitions for Python + +== Special Update/Build Instructions == + +The upstream tarball was repackaged after sanitizing the file +permissions with + +$ chmod go-w diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini new file mode 100644 index 00000000000..5cdbb099c98 --- /dev/null +++ b/build/pkgs/pytz/checksums.ini @@ -0,0 +1,4 @@ +tarball=pytz-VERSION.tar.bz2 +sha1=5859c52c8a01da679c0047c3e463180d7d0de4bf +md5=bdad4eee126bfa2f1f6ad5aaf4e22ee0 +cksum=2104846726 diff --git a/build/pkgs/pytz/dependencies b/build/pkgs/pytz/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/pytz/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt new file mode 100644 index 00000000000..648f66d05bd --- /dev/null +++ b/build/pkgs/pytz/package-version.txt @@ -0,0 +1 @@ +2016.3 diff --git a/build/pkgs/pytz/spkg-install b/build/pkgs/pytz/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/pytz/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/pytz/type b/build/pkgs/pytz/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pytz/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/sagenb/SPKG.txt b/build/pkgs/sagenb/SPKG.txt index 83cf729472b..92ab236a0e8 100644 --- a/build/pkgs/sagenb/SPKG.txt +++ b/build/pkgs/sagenb/SPKG.txt @@ -12,104 +12,29 @@ GPLv3+ == Upstream Contact == * Keshav Kini - * Homepage: http://nb.sagemath.org/ + * Homepage: https://github.com/sagemath/sagenb == Dependencies == -Included dependencies (for specific version numbers, see the file -util/fetch_deps.py in the sagenb directory): +Build-time dependencies: - * zope.interface + * Python + * setuptools * twisted - * pytz - * Babel - * Werkzeug - * speaklater - * python-openid - * itsdangerous - * Flask - * Flask-Silk - * Flask-AutoIndex - * Flask-Babel - * Flask-OpenID - * pyOpenSSL - * webassets + * flask + - flask-autoindex + - flask-babel + - flask-openid + - flask-oldsessions -Dependencies that are not included in this SPKG but are packaged for -Sage: +Run-time dependencies: - * Python - * setuptools - * pexpect + * Sage * jinja2 - * sphinx + * pexpect * docutils + * sphinx -Dependencies that are not included in this SPKG, and are not packaged -for Sage by default for licensing reasons: +Optional dependency: * OpenSSL (including headers) - -For this last category, please install systemwide using your operating -system's package manager, or manually install the OpenSSL optional SPKG -before attempting to install this sagenb SPKG (see the Installation -section of the Sage documentation for more information). - -== Packaging Instructions == - -To produce a new spkg: - - * Go to your sagenb repository - * Check out the version of sagenb you want to package - * Run dist.sh, optionally with the switch -g to include a copy of the - git repo - * Replace the src/ directory in this SPKG with the newly created dist/ - directory that has now appeared in your sagenb repository - * `cd .. && sage --pkg --no-compress sagenb-x.y.z` - * A winner is you! - -== Changelog == - -=== sagenb-0.10.7.2 (Punarbasu Purkayastha, 2013-08-08) === - - * Upgraded to 0.10.7.2 (minor fix for trac ticket 14469) - -=== sagenb-0.10.7.1 (Keshav Kini, 2013-06-25) === - - * Upgraded to 0.10.7.1 (revert published worksheet sanitization) - -=== sagenb-0.10.7 (Keshav Kini, 2013-06-22) === - - * Upgraded to 0.10.7 (MathJax 2.2, GPLv3+, bugfixes) - -=== sagenb-0.10.6 (Keshav Kini, 2013-04-27) === - - * Upgraded to 0.10.6 (bugfixes) - -=== sagenb-0.10.5 (Keshav Kini, 2013-04-01) === - - * Upgraded to 0.10.5 (bugfixes, LDAP) - -=== sagenb-0.10.4 (Keshav Kini, 2013-01-16) === - - * Upgraded to 0.10.4 (bugfixes, #13504, #13678) - -=== sagenb-0.10.3 (Jason Grout, 2012-11-16) === - - * Upgraded to 0.10.3 (bugfixes) - * Include zope.interface tarball - -=== sagenb-0.10.2 (Keshav Kini, 2012-09-01) === - - * Upgraded to 0.10.2 (bugfixes) - -=== sagenb-0.10.1 (Keshav Kini, 2012-07-08) === - - * Upgraded to 0.10.1 (bugfixes) - -=== sagenb-0.10.0 (Keshav Kini, 2012-06-21) === - - * Upgraded to 0.10.0 - -For an full log of changes, look at the history on github: -http://github.com/sagemath/sagenb diff --git a/build/pkgs/sagenb/checksums.ini b/build/pkgs/sagenb/checksums.ini index 5f53c7fc1dd..9c0d2484fc1 100644 --- a/build/pkgs/sagenb/checksums.ini +++ b/build/pkgs/sagenb/checksums.ini @@ -1,4 +1,4 @@ -tarball=sagenb-VERSION.tar -sha1=8e15129e2014d8ba9a06630ea4269251f2daac61 -md5=c0975aa7af1a0b9d7d36d3ddae95d043 -cksum=616807847 +tarball=sagenb-VERSION.tar.bz2 +sha1=7bde7c50ed46ef75b4f8c107e29aba8aef6711e1 +md5=f421c5c4d347226534ca0ca95ac726e2 +cksum=1196088822 diff --git a/build/pkgs/sagenb/dependencies b/build/pkgs/sagenb/dependencies index 7f09065715b..d5481b6b4e8 100644 --- a/build/pkgs/sagenb/dependencies +++ b/build/pkgs/sagenb/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools pexpect jinja2 sphinx docutils +$(PYTHON) | pip babel flask flask_autoindex flask_babel flask_oldsessions flask_openid mathjax twisted ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagenb/package-version.txt b/build/pkgs/sagenb/package-version.txt index b80f98e66f1..c43e1055fd3 100644 --- a/build/pkgs/sagenb/package-version.txt +++ b/build/pkgs/sagenb/package-version.txt @@ -1 +1 @@ -0.11.7 +0.12 diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 769ce70a2df..8c0eced55f2 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -1,79 +1,29 @@ #!/usr/bin/env bash -# This file is no longer autogenerated. -# don't build on python 3 -if [ $(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1) = "3" ]; then - exit -fi - - -# Begin boilerplate - -CUR=$PWD - -die () { - echo >&2 "$@" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" exit 1 -} - -[ -n "$SAGE_LOCAL" ] || die 'Error: $SAGE_LOCAL not set. Source sage-env or run this script from `sage -sh`.' - -[ -z "$CPATH" ] || CPATH="$CPATH": -[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH="$LIBRARY_PATH": -export CPATH="$CPATH""$SAGE_LOCAL"/include -export LIBRARY_PATH="$LIBRARY_PATH""$SAGE_LOCAL"/lib - -# note: the -D_XOPEN_SOURCE=500 is to fix build errors in Twisted 12.1 on -# Solaris. See http://trac.sagemath.org/sage_trac/ticket/11080#comment:314 and -# the subsequent few comments. -export CPPFLAGS="-I$SAGE_LOCAL/include -D_XOPEN_SOURCE=500 $CPPFLAGS" - -if [ $SAGE64 = "yes" ]; then - echo "Building with extra 64-bit flags for MacOS X and Open Solaris." - if [ -z $CFLAG64 ]; then - CFLAG64=-m64 - fi - export CFLAGS="$CFLAGS $CFLAG64" - export CPPFLAGS="$CPPFLAGS $CFLAG64" - export CXXFLAGS="$CXXFLAGS $CFLAG64" - export LDFLAGS="$LDFLAGS $CFLAG64" fi -# End boilerplate - -# delete old sagenb install, if any -rm -rf "${SAGE_LOCAL}/lib/python/site-packages"/sagenb* - - -# TODO: clean up this crap -# * dependencies should not be part of the sagenb tarball at all -# * get rid of easy_install -# * note that pip currently depends on ssl -# Install dependencies -for PKG in $(cat src/install_order); do - easy_install -Z -H None "src/$PKG" - if [ $? -ne 0 ]; then - echo >&2 "Error: Installing $PKG failed." - exit 1 - fi -done +cd src - -# Install sagenb into site-packages -PKG=$(ls -1 src | GREP_OPTIONS= grep sagenb-) -easy_install -Z -H None "src/$PKG" +# Install a flat package (not an egg), which is the same as how pip +# installs packages. +python setup.py install --single-version-externally-managed --record dummy if [ $? -ne 0 ]; then - echo >&2 "Error: Installing SageNB failed." + echo >&2 "Error installing SageNB" exit 1 fi - -# let sagenb use mathjax spkg -cd "${SAGE_LOCAL}/lib/python/site-packages"/sagenb-*.egg/sagenb/data +# let sagenb use mathjax +cd "${SAGE_LOCAL}/lib/python/site-packages/sagenb/data" if [ $? -ne 0 ]; then echo >&2 "Error: Cannot find SageNB data directory." exit 1 fi -# the following line can be removed once sagenb does not ship mathjax anymore. -rm -rf mathjax -ln -s ../../../../../../share/mathjax/ mathjax +ln -s ../../../../../share/mathjax/ mathjax +if [ ! -d mathjax ]; then + echo >&2 "Error: Cannot symlink mathjax into the SageNB data directory." + exit 1 +fi diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index 79cc040e560..a228ef1930b 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=c953cc4228654d151be4cd84db6391fd050eb3bf -md5=99823e2cd564b996f18820a065f0a974 -cksum=3074680780 +sha1=423fe07da9f27e0a4676f7e98f8e9f4839b6d4a4 +md5=4c5c896ba52e134bbc3507bac6400087 +cksum=1357969954 diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 4dae2985b58..1cac385c6cb 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -1.10.1 +1.11.0 diff --git a/build/pkgs/speaklater/SPKG.txt b/build/pkgs/speaklater/SPKG.txt new file mode 100644 index 00000000000..3143efa59f3 --- /dev/null +++ b/build/pkgs/speaklater/SPKG.txt @@ -0,0 +1,12 @@ += speaklater = + +== Description == + +Implements a lazy string for python useful for use with gettext + +A module that provides lazy strings for translations. Basically you get an +object that appears to be a string but changes the value every time the value +is evaluated based on a callable you provide. + +For example you can have a global lazy_gettext function that returns a lazy +string with the value of the current set language. diff --git a/build/pkgs/speaklater/checksums.ini b/build/pkgs/speaklater/checksums.ini new file mode 100644 index 00000000000..7b88b7ec37e --- /dev/null +++ b/build/pkgs/speaklater/checksums.ini @@ -0,0 +1,4 @@ +tarball=speaklater-VERSION.tar.gz +sha1=65551c6896de20e58dbae795392b7e551ccfe318 +md5=e8d5dbe36e53d5a35cff227e795e8bbf +cksum=75663587 diff --git a/build/pkgs/speaklater/dependencies b/build/pkgs/speaklater/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/speaklater/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/speaklater/package-version.txt b/build/pkgs/speaklater/package-version.txt new file mode 100644 index 00000000000..7e32cd56983 --- /dev/null +++ b/build/pkgs/speaklater/package-version.txt @@ -0,0 +1 @@ +1.3 diff --git a/build/pkgs/speaklater/spkg-install b/build/pkgs/speaklater/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/speaklater/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/speaklater/type b/build/pkgs/speaklater/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/speaklater/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/twisted/SPKG.txt b/build/pkgs/twisted/SPKG.txt new file mode 100644 index 00000000000..d2122e44f7e --- /dev/null +++ b/build/pkgs/twisted/SPKG.txt @@ -0,0 +1,8 @@ += twisted = + +== Description == + +An asynchronous networking framework written in Python + +An extensible framework for Python programming, with special focus on +event-based network programming and multiprotocol integration. diff --git a/build/pkgs/twisted/checksums.ini b/build/pkgs/twisted/checksums.ini new file mode 100644 index 00000000000..58cda2684f5 --- /dev/null +++ b/build/pkgs/twisted/checksums.ini @@ -0,0 +1,4 @@ +tarball=Twisted-VERSION.tar.bz2 +sha1=c7db4b949fc27794ca94677f66082f49be43f283 +md5=0831d7c90d0020062de0f7287530a285 +cksum=3874291662 diff --git a/build/pkgs/twisted/dependencies b/build/pkgs/twisted/dependencies new file mode 100644 index 00000000000..7d60db4e7a0 --- /dev/null +++ b/build/pkgs/twisted/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | setuptools pip zope_interface + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/twisted/package-version.txt b/build/pkgs/twisted/package-version.txt new file mode 100644 index 00000000000..188dd74f5f5 --- /dev/null +++ b/build/pkgs/twisted/package-version.txt @@ -0,0 +1 @@ +15.5.0 diff --git a/build/pkgs/twisted/spkg-install b/build/pkgs/twisted/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/twisted/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/twisted/type b/build/pkgs/twisted/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/twisted/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/vcversioner/checksums.ini b/build/pkgs/vcversioner/checksums.ini index b6fc1fbc8ca..4d294a20994 100644 --- a/build/pkgs/vcversioner/checksums.ini +++ b/build/pkgs/vcversioner/checksums.ini @@ -1,4 +1,4 @@ tarball=vcversioner-VERSION.tar.gz -sha1=723c0915665aa1f01831731605acdd6afe0af9b6 -md5=7848a365ced9941053bc25d9a9f8f4b4 -cksum=343721373 +sha1=ce076b62e8f0772bf79f29762bfc3cf09f6781b5 +md5=aab6ef5e0cf8614a1b1140ed5b7f107d +cksum=1650555311 diff --git a/build/pkgs/vcversioner/package-version.txt b/build/pkgs/vcversioner/package-version.txt index a82ac72a25f..53c62eb78b9 100644 --- a/build/pkgs/vcversioner/package-version.txt +++ b/build/pkgs/vcversioner/package-version.txt @@ -1 +1 @@ -2.14.0.0 +2.16.0.0 diff --git a/build/pkgs/werkzeug/SPKG.txt b/build/pkgs/werkzeug/SPKG.txt new file mode 100644 index 00000000000..fac6f309dee --- /dev/null +++ b/build/pkgs/werkzeug/SPKG.txt @@ -0,0 +1,18 @@ += Werkzeug = + +== Description == + +The Swiss Army knife of Python web development + +Werkzeug started as simple collection of various utilities for WSGI +applications and has become one of the most advanced WSGI utility modules. It +includes a powerful debugger, full featured request and response objects, HTTP +utilities to handle entity tags, cache control headers, HTTP dates, cookie +handling, file uploads, a powerful URL routing system and a bunch of community +contributed addon modules. + +Werkzeug is unicode aware and doesn't enforce a specific template engine, +database adapter or anything else. It doesn’t even enforce a specific way of +handling requests and leaves all that up to the developer. It's most useful +for end user applications which should work on as many server environments as +possible (such as blogs, wikis, bulletin boards, etc.). diff --git a/build/pkgs/werkzeug/checksums.ini b/build/pkgs/werkzeug/checksums.ini new file mode 100644 index 00000000000..55b68e03b80 --- /dev/null +++ b/build/pkgs/werkzeug/checksums.ini @@ -0,0 +1,4 @@ +tarball=Werkzeug-VERSION.tar.gz +sha1=9e1dca470691ae2912d51d382ff086590e3e1453 +md5=daf443a939459e12f14fd2e4658a26df +cksum=606026512 diff --git a/build/pkgs/werkzeug/dependencies b/build/pkgs/werkzeug/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/werkzeug/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/werkzeug/package-version.txt b/build/pkgs/werkzeug/package-version.txt new file mode 100644 index 00000000000..62d5dbdf3c7 --- /dev/null +++ b/build/pkgs/werkzeug/package-version.txt @@ -0,0 +1 @@ +0.11.5 diff --git a/build/pkgs/werkzeug/spkg-install b/build/pkgs/werkzeug/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/werkzeug/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/werkzeug/type b/build/pkgs/werkzeug/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/werkzeug/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/zope_interface/SPKG.txt b/build/pkgs/zope_interface/SPKG.txt new file mode 100644 index 00000000000..ca529666746 --- /dev/null +++ b/build/pkgs/zope_interface/SPKG.txt @@ -0,0 +1,13 @@ += zope.interface = + +== Description == + +This package is intended to be independently reusable in any Python project. +It is maintained by the Zope Toolkit project. + +This package provides an implementation of "object interfaces" for Python. +Interfaces are a mechanism for labeling objects as conforming to a given API +or contract. So, this package can be considered as implementation of the +Design By Contract methodology support in Python. + +For detailed documentation, please see http://docs.zope.org/zope.interface diff --git a/build/pkgs/zope_interface/checksums.ini b/build/pkgs/zope_interface/checksums.ini new file mode 100644 index 00000000000..f552e0133dc --- /dev/null +++ b/build/pkgs/zope_interface/checksums.ini @@ -0,0 +1,4 @@ +tarball=zope.interface-VERSION.tar.gz +sha1=207161e27880d07679aff6d712ed12f55e3d91b6 +md5=9ae3d24c0c7415deb249dd1a132f0f79 +cksum=3834987228 diff --git a/build/pkgs/zope_interface/dependencies b/build/pkgs/zope_interface/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/zope_interface/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/zope_interface/package-version.txt b/build/pkgs/zope_interface/package-version.txt new file mode 100644 index 00000000000..de197cc337f --- /dev/null +++ b/build/pkgs/zope_interface/package-version.txt @@ -0,0 +1 @@ +4.1.3 diff --git a/build/pkgs/zope_interface/spkg-install b/build/pkgs/zope_interface/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/zope_interface/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/zope_interface/type b/build/pkgs/zope_interface/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/zope_interface/type @@ -0,0 +1 @@ +standard diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 071faf7a95d..1778fbea0a0 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 7.2.beta4, Release Date: 2016-04-12 │ +│ SageMath version 7.2.beta5, Release Date: 2016-04-21 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index ec156c83333..dd74a5aff47 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='7.2.beta4' -SAGE_RELEASE_DATE='2016-04-12' +SAGE_VERSION='7.2.beta5' +SAGE_RELEASE_DATE='2016-04-21' diff --git a/src/doc/en/developer/sagenb/set_up_fork.rst b/src/doc/en/developer/sagenb/set_up_fork.rst index 66bd918a4da..b0c82043b49 100644 --- a/src/doc/en/developer/sagenb/set_up_fork.rst +++ b/src/doc/en/developer/sagenb/set_up_fork.rst @@ -15,7 +15,7 @@ Overview cd sagenb git remote add upstream git://github.com/sagemath/sagenb.git -In etail +In Detail ========= Clone Your Fork diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 1ddae8bf0e3..eb5a575d6a8 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -87,8 +87,6 @@ Miscellaneous .. toctree:: :maxdepth: 2 - sage/ext/interactive_constructors_c - sage/repl/readline_extra_commands sage/repl/interpreter diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index 1b822dc51d0..a237896ecc1 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -206,7 +206,7 @@ at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -227,7 +227,16 @@ where `(a_{ij})` is a Cartan matrix. Then Monomial crystals depend on the choice of positive integers `C = (c_{ij})_{i\neq j}` satisfying the condition `c_{ij}+c_{ji}=1`. This choice has been made in Sage such that `c_{ij} = 1` if - `i < j` and `c_{ij} = 0` if `i>j`. + `i < j` and `c_{ij} = 0` if `i>j`, but other choices may be used if + deliberately stated at the intialization of the crystal:: + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['C',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() + [0 0 1] + [1 0 0] + [0 1 0] It is shown in [KKS2007]_ that the connected component of `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we denote by @@ -242,16 +251,18 @@ containing the element `\boldsymbol{1}`, which we denote by sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] -We can also model `B(\infty)` using the monomials `A_{i,k}` instead:: +We can also model `B(\infty)` using the variables `A_{i,k}` instead:: - sage: Ninf = crystals.infinity.NakajimaMonomials(['C',3,1], use_Y = False) - sage: ninf = Ninf.highest_weight_vector() - sage: n = ninf.f_string([0,1,2,3,2,1,0]); n + sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: minf = Minf.highest_weight_vector() + sage: Minf.set_variables('A') + sage: m = minf.f_string([0,1,2,3,2,1,0]); m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 - sage: n.weight() + sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta - sage: n.weight_in_root_lattice() + sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] + sage: Minf.set_variables('Y') Building the crystal graph output for these monomial crystals is the same as the constructions above:: diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index f6b2c1a2c27..6e1bd5599e5 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -25,6 +25,7 @@ NSString *sageBinary; NSString *logPath; + NSString *jupyterURL; NSMutableArray *URLQueue; NSUserDefaults *defaults; @@ -32,6 +33,7 @@ NSTask *theTask; NSTask *launchTask; NSPipe *taskPipe; + NSTask *jupyterTask; int port; BOOL myIsInDock, haveStatusItem, useSystemBrowser, neverOpenedFileBrowser; @@ -39,6 +41,9 @@ } // Server control +-(IBAction)startJupyter:(id)sender; +-(IBAction)stopJupyter:(id)sender; +-(void)receivedData:(NSNotification *)notif; -(IBAction)startServer:(id)sender; -(IBAction)stopServer:(id)sender; -(BOOL)serverIsRunning:(BOOL)wait; @@ -62,6 +67,8 @@ -(IBAction)showPreferences:(id)sender; -(void)ensureReadWrite; +-(void)offerNotebookUpgrade; +-(IBAction)upgradeNotebook:(id)sender; -(void)setupPaths; // Quit diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 1335a160972..204645825e1 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -34,6 +34,7 @@ - (void) awakeFromNib{ // Find sageBinary etc. [self setupPaths]; [self ensureReadWrite]; + [self offerNotebookUpgrade]; // Initialize the StatusItem if desired. // If we are on Tiger, then showing in the dock doesn't work @@ -59,6 +60,7 @@ - (void) awakeFromNib{ } // indicate that we haven't started the server yet + jupyterURL = nil; port = 0; neverOpenedFileBrowser = YES; URLQueue = [[NSMutableArray arrayWithCapacity:3] retain]; @@ -87,15 +89,115 @@ - (void) dealloc { [sageBinary release]; [logPath release]; [theTask release]; + [launchTask release]; + [jupyterTask release]; [taskPipe release]; [URLQueue release]; + [jupyterURL release]; [super dealloc]; } +-(IBAction)startJupyter:(id)sender{ + + if ( jupyterTask != nil ) { + if ( jupyterURL != nil ) { + NSLog(@"Going to browse to %@",jupyterURL); + [self browseRemoteURL:jupyterURL]; + } + return; + } + + NSLog(@"Starting Jupyter server"); + if (haveStatusItem) [statusItem setImage:statusImageGreen]; + + // Add SAGE_BROWSER to environment to point back to this application + if ( !useSystemBrowser ) { + NSString *browserPath = [[NSBundle mainBundle] pathForResource:@"open-location" ofType:@"sh"]; + setenv("SAGE_BROWSER", [browserPath UTF8String], 1); // this overwrites, should it? + } + + // Get any default options they might have for this session + [defaults synchronize]; + NSString *jupyterPath = [defaults objectForKey:@"defaultJupyterPath"]; + NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] + objectForKey:@"jupyter"]; + + // Escape the arguments + NSMutableString *escSageBin = [NSMutableString stringWithString:sageBinary]; + [escSageBin replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escSageBin length])]; + + NSMutableString * escLogPath = [NSMutableString stringWithString:logPath]; + [escLogPath replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escLogPath length])]; + + // Compile the command. + // We have to run it through a shell so that the default arguments are parsed properly + NSString *command = [NSString stringWithFormat: + @"'%@' --notebook=jupyter %@ 2>&1 | tee -a '%@' |" + " grep --line-buffered -i 'ipython notebook is running at' |" + " grep --line-buffered -o http://.*", + escSageBin, + // default args are ready to be + (defArgs == nil) ? @"" : defArgs, + logPath]; + NSLog(@"Command: %@",command); + + // Create a task that starts the server + jupyterTask = [[[NSTask alloc] init] retain]; + [jupyterTask setLaunchPath:@"/bin/bash"]; + [jupyterTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; + [jupyterTask setCurrentDirectoryPath:jupyterPath]; + + // set up std out to + NSPipe *outputPipe = [NSPipe pipe]; + [jupyterTask setStandardOutput:outputPipe]; + + NSFileHandle *fh = [outputPipe fileHandleForReading]; + [fh waitForDataInBackgroundAndNotify]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh]; + + [jupyterTask launch]; + + if (haveStatusItem) [statusItem setImage:statusImageBlue]; +} + + +- (void)receivedData:(NSNotification *)notif { + NSFileHandle *fh = [notif object]; + NSData *data = [fh availableData]; + if (data.length > 0) { + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + jupyterURL = [[str stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]] + retain]; + [str release]; + } +} + + +-(IBAction)stopJupyter:(id)sender{ + + if (jupyterTask == nil ) { + return; + } + if (haveStatusItem) [statusItem setImage:statusImageRed]; + + [jupyterTask terminate]; + [jupyterTask terminate]; // We have to send it twice to actually stop -- actually do I? + [jupyterTask release]; + jupyterTask = nil; + if (haveStatusItem) [statusItem setImage:statusImageGrey]; +} + -(IBAction)startServer:(id)sender{ // TODO: Check to see if it's running before attempting to start - NSLog(@"Starting server"); + NSLog(@"Starting SageNB server"); if (haveStatusItem) [statusItem setImage:statusImageGreen]; NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"start-sage" ofType:@"sh"]; @@ -106,7 +208,7 @@ -(IBAction)startServer:(id)sender{ } // Create a task to start the server - + // Get any default options they might have for this session [defaults synchronize]; NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] @@ -114,6 +216,7 @@ -(IBAction)startServer:(id)sender{ launchTask = [[NSTask launchedTaskWithLaunchPath:scriptPath arguments:[NSArray arrayWithObjects:sageBinary, logPath, + @"sagenb", defArgs, // May be nil, but that's okay nil]] retain]; @@ -155,6 +258,7 @@ -(void)serverStartedWithPort:(int)p{ } - (void)taskTerminated:(NSNotification *)aNotification { + NSLog(@"Task stopped\n"); NSTask *theObject = [aNotification object]; if (theObject == theTask) { @@ -181,13 +285,13 @@ - (void)taskTerminated:(NSNotification *)aNotification { [taskPipe release]; taskPipe = nil; } else if (theObject == launchTask ) { - + const int status = [theObject terminationStatus]; - if (status != 0) { + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; // We failed, so tell the user - if (haveStatusItem) { - [statusItem setImage:statusImageGrey]; - } port = 0; NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Server failed to start" defaultButton:@"View Log" @@ -207,12 +311,40 @@ - (void)taskTerminated:(NSNotification *)aNotification { // Reset for next time. [launchTask release]; launchTask = nil; - + } else if (theObject == jupyterTask ) { + NSLog(@"jupyterTask stopped\n"); + const int status = [theObject terminationStatus]; + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; + // We failed, so tell the user + NSAlert *alert = [NSAlert alertWithMessageText:@"Jupyter Server failed to start" + defaultButton:@"View Log" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"For some reason the Jupyter server failed to start. " + "Please check the log for clues, and have that information handy when asking for help."]; + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { + // View Log + [self viewSageLog:self]; + } else { + // Cancel + } + } + // Reset for next time. + [jupyterTask release]; + jupyterTask = nil; + } else { // NSLog(@"Got called for a different task."); } } +// TODO: Test upgrading... + -(IBAction)stopServer:(id)sender{ if (haveStatusItem) [statusItem setImage:statusImageRed]; @@ -243,6 +375,7 @@ -(IBAction)stopServer:(id)sender{ -(IBAction)stopServerAndQuit:(id)sender{ [self stopServer:self]; + [self stopJupyter:self]; // Tell the application to quit [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; @@ -372,22 +505,70 @@ -(void)ensureReadWrite { } } +-(void)offerNotebookUpgrade { + NSFileManager *filemgr = [NSFileManager defaultManager]; + NSLog(@"Checking if sagenb exists %d.", [defaults boolForKey:@"askToUpgradeNB"]); + if ( ! [filemgr fileExistsAtPath:@"~/.sage/sage_notebook.sagenb/users.pickle"] + && [defaults boolForKey:@"askToUpgradeNB"]) { + + NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Notebook Upgrade" + defaultButton:@"Upgrade" + alternateButton:@"Ask me Later" + otherButton:@"Don't ask again" + informativeTextWithFormat:@"You appear to have data in the old notebook format.\n" + "Sage has changed to use Jupyter notebooks by default.\n" + "Unfortunately, they are not completely compatible.\n" + "We can attempt to upgrade, .\n" + ]; + + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { // Upgrade + [self upgradeNotebook:self]; + } else if ( resp == NSAlertAlternateReturn) { // Ask Me Later + // nothing + NSLog(@"Ask to upgrade later."); + } else { // Don't ask again + NSLog(@"Don't ask to upgrade again."); + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"sagenb" forKey:@"preferredNotebookType"]; + NSLog(@"synchronizing defaults: %@",defaults); + } + } +} + +-(IBAction)upgradeNotebook:(id)sender{ + NSLog(@"Upgrade Notebook."); + [self sageTerminalRun:@"notebook=export" withArguments:nil]; + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"jupyter" forKey:@"preferredNotebookType"]; +} + -(IBAction)revealInFinder:(id)sender{ if ( [[sender title] isEqualToString:@"Reveal in Shell"] ) { [self terminalRun:[NSString stringWithFormat:@"cd '%@' && $SHELL", [sageBinary stringByDeletingLastPathComponent]]]; } else { [[NSWorkspace sharedWorkspace] selectFile:[sageBinary stringByDeletingLastPathComponent] - inFileViewerRootedAtPath:nil]; + inFileViewerRootedAtPath:@""]; } } -(IBAction)openNotebook:(id)sender{ - [self browseLocalSageURL:@""]; + if ( jupyterURL != nil ) { + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@""]; + } } -(IBAction)newWorksheet:(id)sender{ - [self browseLocalSageURL:@"new_worksheet"]; + if ( jupyterURL != nil ) { + // AFAICT you can't create a new worksheet via curl + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@"new_worksheet"]; + } } -(IBAction)showPreferences:(id)sender{ diff --git a/src/mac-app/Defaults.plist b/src/mac-app/Defaults.plist index 15381d6e0f7..033784c7e2d 100644 --- a/src/mac-app/Defaults.plist +++ b/src/mac-app/Defaults.plist @@ -50,7 +50,7 @@ end tell set the_script to "%@; exit" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -70,7 +70,7 @@ end tell set the_script to "%@" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -90,6 +90,38 @@ end tell do shell script "xterm -e '%@' &" xterm - don't exit do shell script "xterm -hold -e '%@' &" + iTerm v3 + set the_script to "%@; exit" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell + iTerm v3 - don't exit + set the_script to "%@" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell alsoShowMenuExtra @@ -124,5 +156,11 @@ end tell autoStartServer + askToUpgradeNB + + preferSageNB + + defaultJupyterPath + ~/Documents/Sage/ diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index 2e2957f963e..ee3be91782f 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -1,8 +1,8 @@ - + - + @@ -181,8 +181,7 @@ - - + @@ -336,18 +335,55 @@ - + + + + NSNegateBoolean + + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -567,7 +603,8 @@ - + + @@ -683,7 +720,7 @@ - + + + + + + + Modern Sage has 3 notebook interfaces. All three offer slightly different user experiences and there are some incompatibilities between them (e.g. interacts). + +1. SageNB. These are the old notebooks of which you may have many. Development of this interface has stalled. + +2. Jupyter. This is the successor of IPython and is intended to be the successor of SageNB on the desktop. + +3. SageMathCloud. This is (currently) a cloud-only offering, and hence not available on the desktop. + + + + + + + + + + + + + + + + + ns placeholder + + + + + + + + + + + + + + + + + + @@ -945,6 +1045,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1081,16 +1182,52 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + - + + + + NSNegateBoolean + + - + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1293,6 +1430,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1369,5 +1507,11 @@ The command will be run as + + + + + + diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index f7b68125f1b..6bc5b3c19b2 100644 Binary files a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib and b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/src/mac-app/MyDocument.m b/src/mac-app/MyDocument.m index ca38b29ab3f..9f2362e5767 100644 --- a/src/mac-app/MyDocument.m +++ b/src/mac-app/MyDocument.m @@ -152,7 +152,7 @@ - (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)fr - (void)webView:(WebView *)wv runOpenPanelForFileButtonWithResultListener:(id )listener { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; [openPanel beginSheetForDirectory:nil - file:nil + file:@"" modalForWindow:[[self webView] window] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index ce1e6e0fba7..6c60965755b 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -1,19 +1,19 @@ #!/bin/bash # start-sage.sh -# Fluidium # # Created by Ivan Andrus on 16/1/10. # Copyright 2010 Ivan Andrus. All rights reserved. # Ensure we have enough arguments -if [ $# -lt 2 ]; then +if [ $# -lt 3 ]; then echo "usage: $0 SAGE_EXECUTABLE LOG [ARGS_FOR_NOTEBOOK]" exit 1; fi SAGE_EXECUTABLE="$1" SAGE_LOG="$2" +NB_TYPE="$3" # Read environment variables cd $(dirname $SAGE_EXECUTABLE) @@ -24,16 +24,23 @@ source local/bin/sage-env # So always run first the respective script handling this # This should also catch Intel vs. PPC or 32Bit vs. 64Bit conflicts echo Checking install location >> "$SAGE_LOG" -# TODO: If relocate-once.py is present run it with some sort of progress +# TODO: If relocate-once.py is present run it with some sort of progress # display, e.g. in a terminal +if [ "$NB_TYPE" = jupyter ]; then + +./sage --notebook=jupyter $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" & + + +else + echo Checking existence of SageNB directory >> "$SAGE_LOG" if [ -e $DOT_SAGE/sage_notebook.sagenb/users.pickle ]; then echo Starting Notebook >> "$SAGE_LOG" - # $3 is not quoted because it comes as one argument from the app, + # $4 is not quoted because it comes as one argument from the app, # so we need the shell to parse it here. - ./sage --notebook $3 >> "$SAGE_LOG" 2>> "$SAGE_LOG" + ./sage --notebook=sagenb $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" else false fi @@ -52,9 +59,12 @@ if [ $? != 0 ]; then sage-native-execute osascript \ -e 'tell app "Terminal"' \ -e ' activate' \ - -e " do script \"'$SAGE_EXECUTABLE' --notebook\"" \ + -e " do script \"'$SAGE_EXECUTABLE' --notebook=sagenb\"" \ -e 'end' - # We don't include $3 here since this should only happen the first time + # We don't include $4 here since this should only happen the first time # they run it, and this way we don't have to worry about quoting it. fi + +fi + exit $? diff --git a/src/module_list.py b/src/module_list.py index ffe3dd6abdc..9a3a6d04a4a 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -871,7 +871,8 @@ def uname_specific(name, value, alternative): Extension("sage.matrix.matrix_complex_ball_dense", ["sage/matrix/matrix_complex_ball_dense.pyx"], libraries=['arb', 'mpfi', 'mpfr'], - include_dirs=[SAGE_INC + '/flint']), + include_dirs=[SAGE_INC + '/flint'], + language = "c++"), Extension('sage.matrix.matrix_complex_double_dense', sources = ['sage/matrix/matrix_complex_double_dense.pyx']), @@ -1260,7 +1261,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.complex_arb", ["sage/rings/complex_arb.pyx"], - libraries=['mpfi', 'mpfr', 'gmp']), + libraries=['mpfi', 'mpfr', 'gmp'], + language = 'c++'), Extension('sage.rings.complex_double', sources = ['sage/rings/complex_double.pyx'], @@ -1331,7 +1333,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.real_arb", ["sage/rings/real_arb.pyx"], - libraries = ['mpfi', 'mpfr']), + libraries = ['mpfi', 'mpfr'], + language = 'c++'), Extension('sage.rings.real_lazy', sources = ['sage/rings/real_lazy.pyx']), diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 750cbe33df1..0f7c76c4663 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -139,10 +139,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): ``grA`` are isomorphic:: sage: grA(A.an_element()) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: elt = A.an_element() + A.algebra_generators()['x'] + 2 sage: grelt = grA(elt); grelt - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) sage: A(grelt) == elt True @@ -241,8 +241,10 @@ def _element_constructor_(self, x): sage: grA = A.graded_algebra() sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) + + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) """ if isinstance(x, CombinatorialFreeModule.Element): if x.parent() is self._A: diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index e9cc4d0f76d..c2fe2128fb7 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -287,7 +287,7 @@ def _an_element_(self): sage: F. = FreeAlgebra(QQ) sage: J = JordanAlgebra(F) sage: J.an_element() - x + 2 + 2*x + 3*y """ return self.element_class(self, self._A.an_element()) diff --git a/src/sage/all.py b/src/sage/all.py index 489fb1ad0fa..669c77be334 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -268,7 +268,9 @@ def quit_sage(verbose=True): from sage.libs.all import symmetrica symmetrica.end() -from sage.ext.interactive_constructors_c import inject_on, inject_off +# A deprecation(20442) warning will be given when this module is +# imported, in particular when these functions are used. +lazy_import("sage.ext.interactive_constructors_c", ["inject_on", "inject_off"]) sage.structure.sage_object.register_unpickle_override('sage.categories.category', 'Sets', Sets) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'HeckeModules', HeckeModules) @@ -278,7 +280,6 @@ def quit_sage(verbose=True): sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'VectorSpaces', VectorSpaces) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'Schemes_over_base', sage.categories.schemes.Schemes_over_base) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'ModularAbelianVarieties', ModularAbelianVarieties) -#sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', '', ) # Cache the contents of star imports. sage.misc.lazy_import.save_cache_file() diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 279ac432e90..3dea3a60a33 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -217,7 +217,7 @@ def __iter__(self): [5, 6, 7] """ - #Check to see if .first() and .next() are overridden in the subclass + # Check if .first() and .next(x) are overridden in the subclass if ( self.first != self._first_from_iterator and self.next != self._next_from_iterator ): return self._iterator_from_next() diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 9e7765aefa8..3de3456a2b6 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -114,8 +114,12 @@ def degree_on_basis(self, m): sage: A.degree_on_basis((x^4).leading_support()) 4 sage: a = A.an_element(); a - U['x']^2*U['y']^2*U['z']^3 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 3*U['y'] + 1 sage: A.degree_on_basis(a.leading_support()) + 1 + sage: s = sorted(a.support(), key=str)[2]; s + U['x']^2*U['y']^2*U['z']^3 + sage: A.degree_on_basis(s) 7 """ return len(m) diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 7b40f116de8..ca176f1e24c 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -235,7 +235,7 @@ def _from_integer_(self, i): def next(self, i): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: @@ -250,7 +250,7 @@ def next(self, i): def some_elements(self): """ - Returns some prime numbers + Return some prime numbers. EXAMPLES:: @@ -268,7 +268,7 @@ def some_elements(self): class Element(Element): def is_prime(self): """ - Returns if a prime number is prime = True ! + Return whether ``self`` is a prime number. EXAMPLES:: @@ -281,13 +281,20 @@ def is_prime(self): def next(self): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: sage: P = Sets().example("inherits") - sage: next(P.an_element()) + sage: p = P.an_element(); p + 47 + sage: p.next() 53 + + .. NOTE:: + + This method is not meant to implement the protocol iterator, + and thus not subject to Python 2 vs Python 3 incompatibilities. """ return self.parent().next(self) @@ -587,15 +594,15 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: z.parent() Integer Ring - The disadvantage of this implementation is that the element doesn't know - that they are primes so that prime testing is slow:: + The disadvantage of this implementation is that the elements do not know + that they are prime, so that prime testing is slow:: sage: pf = Sets().example("facade").an_element() sage: timeit("pf.is_prime()") # random 625 loops, best of 3: 4.1 us per loop compared to the other implementations where prime testing is only done if - needed during the construction of the element. Then the elements themselve + needed during the construction of the element, and later on the elements "know" that they are prime:: sage: pw = Sets().example("wrapper").an_element() @@ -606,18 +613,18 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: timeit("pw.is_prime()") # random 625 loops, best of 3: 854 ns per loop - And moreover, the next methods for the element does not exist:: + Note also that the ``next`` method for the elements does not exist:: sage: pf.next() Traceback (most recent call last): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute 'next' - whereas:: + unlike in the other implementations:: - sage: next(pw) + sage: pw.next() 53 - sage: next(pi) + sage: pi.next() 53 TESTS:: diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 053d119a040..f8cdfbadfe2 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -131,9 +131,10 @@ def to_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p); q - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + + 3*bar(U['y']) + 3*bar(1) sage: q.parent() is A.graded_algebra() True """ @@ -159,7 +160,7 @@ def from_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p) sage: A.from_graded_conversion()(q) == p True @@ -190,7 +191,7 @@ def projection(self, i): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.projection(7)(p); q bar(U['x']^2*U['y']^2*U['z']^3) sage: q.parent() is A.graded_algebra() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 7f2320f7f19..29b372f81be 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -832,12 +832,14 @@ def homogeneous_component(self, n): 0 sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 sage: g.homogeneous_component(-1) 0 sage: g.homogeneous_component(0) - 0 + 1 sage: g.homogeneous_component(2) -2*U['x']*U['y'] sage: g.homogeneous_component(5) @@ -896,22 +898,25 @@ def truncate(self, n): 2*P[] + 2*P[1] + 3*P[2] sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(-1) 0 sage: g.truncate(0) 0 sage: g.truncate(2) - 0 + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(3) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(5) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(7) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(8) U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 TESTS: diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 5d41c17716b..33545bf09a7 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -979,7 +979,7 @@ def _ascii_art_(self): if len(l_repr) == 0: lf_sep += "_"*(t_repr._root+1) else: lf_sep += "_"*(t_repr._l+1) ls_sep += " "*(t_repr._root) + "/" + " "*(t_repr._l-t_repr._root) - mid = whitesep + int((len(lf_sep)-whitesep)/2) + mid = whitesep + (len(lf_sep) - whitesep) // 2 node = node_to_str( self ) t_repr = AsciiArt([lf_sep[:mid-1] + node + lf_sep[mid+len(node)-1:], ls_sep]) * acc t_repr._root = mid diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 8f966281cf9..dd56f978c08 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -13,6 +13,7 @@ AUTHORS: - Florent Hivert (2010-2011): initial implementation. +- Adrien Boussicault (2015): Hook statistics. REFERENCES: @@ -421,7 +422,7 @@ def _ascii_art_( self ): lr_tree = self[0]._ascii_art_() rr_tree = self[1]._ascii_art_() nb_ = lr_tree._l - lr_tree._root + rr_tree._root - 1 - nb_L = int( nb_ / 2 ) + nb_L = nb_ // 2 nb_R = nb_L + ( 1 if nb_ % 2 == 1 else 0 ) f_line = " " ** Integer( lr_tree._root + 1 ) + "_" ** Integer( nb_L ) + node f_line += "_" ** Integer( nb_R ) @@ -2035,6 +2036,226 @@ def single_edge_cut_shapes(self): resu += [(L + 1, L + 2, R)] return resu + def comb(self, side='left'): + r""" + Return the comb of a tree. + + There are two combs in a binary tree: a left comb and a right comb. + + Consider all the vertices of the leftmost (resp. rightmost) branch of + the root. The left (resp. right) comb is the list of right (resp. left) + subtrees of each of these vertices. + + INPUT: + + - ``side`` -- (default: 'left') set to 'left' to obtain a left + comb, and to 'right' to obtain a right comb. + + OUTPUT: + + A list of binary trees. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: [BT.comb('left'), BT.comb('right')] + [[], []] + sage: BT = BinaryTree( '[.,.]' ) + sage: [BT.comb('left'), BT.comb('right')] + [[], []] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT.comb('left') + [., .] + sage: BT.comb('right') + [.] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.comb('left') + [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] + sage: ascii_art(BT.comb('left')) + [ __o___ , , o ] + [ / \ ] + [ o _o_ ] + [ / \ ] + [ o o ] + [ / \ ] + [ o o ] + sage: BT.comb('right') + [., [[., .], [[[., .], [., .]], .]]] + sage: ascii_art(BT.comb('right')) + [ , __o__ ] + [ / \ ] + [ o o ] + [ / ] + [ o ] + [ / \ ] + [ o o ] + """ + + def _comb(side): + if self.is_empty(): + return [] + tree = self[side] + res = [] + while not tree.is_empty(): + res.append(tree[1 - side]) + tree = tree[side] + return res + if side == 'left': + return _comb(0) + elif side == 'right': + return _comb(1) + + def hook_number(self): + r""" + Return the number of hooks. + + Recalling that a branch is a path from a vertex of the tree to a leaf, + the leftmost (resp. rightmost) branch of a vertex `v` is the branch from + `v` made only of left (resp. right) edges. + + The hook of a vertex `v` is a set of vertices formed by the + union of `{v}`, and the vertices of its leftmost and rightmost branches. + + There is a unique way to partition the set of vertices in hooks. + The number of hooks in such a partition is the hook number of the tree. + + We can obtain this partition recursively by extracting the root's hook + and iterating the processus on each tree of the remaining forest. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: BT.hook_number() + 0 + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.hook_number() + 6 + """ + if self.is_empty(): + return 0 + return 1 + sum(t.hook_number() + for t in self.comb('left') + self.comb('right')) + + def twisting_number(self): + r""" + Return a pair (number of maximal left branches, number of maximal right + branches). + + Recalling that a branch of a vertex `v` is a path from a vertex of the + tree to a leaf, a left (resp. right) branch is a branch made only of + left (resp. right) edges. The length of a branch is the number of edges + composing it. A left (resp. right) branch is maximal if it is not + included in a strictly longer left (resp. right) branch. + + + OUTPUT : + + A list of two integers. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o + sage: BT.twisting_number() + [1, 1] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.twisting_number() + [5, 6] + sage: BT = BinaryTree( '[.,[[[.,.],.],.]]' ); ascii_art(BT) + o + \ + o + / + o + / + o + sage: BT.twisting_number() + [1, 1] + """ + tn = [0, 0] + if self.node_number() <= 1: + return tn + + L = self.comb('left') + if len(L): + tn[0] += 1 + for h in L: + tw = BinaryTree([None, h]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] + + R = self.comb('right') + if len(R): + tn[1] += 1 + for l in R: + tw = BinaryTree([l, None]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] + return tn + def q_hook_length_fraction(self, q=None, q_factor=False): r""" Compute the ``q``-hook length fraction of the binary tree ``self``, diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index e32641bbfe6..66bc8604289 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -391,7 +391,7 @@ def use_c_vectors(self, use=True, bot_is_c=False, force=False): self._BC = copy(self._M) if self._bot_is_c != bot_is_c: # If we need to do this. It overrides the previous designations. self._bot_is_c = bot_is_c - if self._bot_is_c == True: + if self._bot_is_c: self._use_c_vec = True if self._m == self._n: # in this case, the second half of a 2n x n matrix is a c-matrix. self._C = copy(self._M[self._n:(self._n+self._m),:self._n]) @@ -623,7 +623,7 @@ def use_fpolys(self, use=True, user_labels=None, user_labels_prefix=None): self._yhat = dict([ (self._U.gen(j),prod([self._R.gen(i)**self._M[i,j] for i in xrange(self._n+self._m)])) for j in xrange(self._n)]) elif self._cluster: raise ValueError("should not be possible to have cluster variables without f-polynomials") # added this as a sanity check. This error should never appear however. - elif self._track_mut == True: # If we can navigate from the root to where we are + elif self._track_mut: # If we can navigate from the root to where we are if not self._use_g_vec: self.use_g_vectors(True) catchup = ClusterSeed(self._b_initial, user_labels=user_labels, user_labels_prefix=user_labels_prefix) @@ -4188,10 +4188,11 @@ def is_LeeLiZel_allowable(T,n,m,b,c): nEA1 += 1 if nAF1 == b*nAF2 or nEA2 == c*nEA1: uv_okay = True - if uv_okay == False: + if not uv_okay: return False return True + def get_green_vertices(C): r""" Get the green vertices from a matrix. Will go through each clumn and return diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index a45a2ab3dda..7cf737d1150 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1139,8 +1139,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): else: long_cycle = [ cycle, ['A',n-1,1] ] # if we haven't found a "long_cycle", we are in finite type A - if long_cycle == False: - long_cycle = [ [], QuiverMutationType(['A',n]) ] + if not long_cycle: + long_cycle = [[], QuiverMutationType(['A', n])] # The 'connected vertices' are now computed. # Attention: 0-1-2 in type A_3 has connecting vertices 0 and 2, while in type D_3 it has connecting vertex 1; diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 403121dbe4e..9a342077220 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -1553,7 +1553,7 @@ def __list_from_iterator(self): def __iterator_from_next(self): """ - An iterator to use when .first() and .next() are provided. + An iterator to use when the .first() and .next(x) methods are provided. EXAMPLES:: @@ -1569,7 +1569,7 @@ def __iterator_from_next(self): while True: try: f = self.next(f) - except (TypeError, ValueError ): + except (TypeError, ValueError): break if f is None or f is False : @@ -1670,18 +1670,18 @@ def __iter__(self): ... NotImplementedError: iterator called but not implemented """ - #Check to see if .first() and .next() are overridden in the subclass + # Check whether .first() and .next(x) are overridden in the subclass if ( self.first != self.__first_from_iterator and self.next != self.__next_from_iterator ): return self.__iterator_from_next() - #Check to see if .last() and .previous() are overridden in the subclass + # Check whether .last() and .previous() are overridden in the subclass elif ( self.last != self.__last_from_iterator and self.previous != self.__previous_from_iterator): return self.__iterator_from_previous() - #Check to see if .unrank() is overridden in the subclass + # Check whether .unrank() is overridden in the subclass elif self.unrank != self.__unrank_from_iterator: return self.__iterator_from_unrank() - #Finally, check to see if .list() is overridden in the subclass + # Check whether .list() is overridden in the subclass elif self.list != self.__list_from_iterator: return self.__iterator_from_list() else: diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 3afde4d9daa..7d6913d5dcf 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -417,7 +417,7 @@ def vertices(self): s = W.simple_reflections() highest_weight_crystal = self._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' @@ -622,7 +622,7 @@ def is_admissible(self): s = W.simple_reflections() highest_weight_crystal = self.parent()._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' @@ -1723,14 +1723,14 @@ def compare_graphs(g1, g2, node1, node2): matched = False for o2 in g2.outgoing_edges( node2 ): if o2[2] == out_edge[2]: - if matched == True: + if matched: print "ERROR: Two edges with the same label for ", out_edge, " exist." return False matched = True result = compare_graphs(g1, g2, out_edge[1], o2[1]) - if result == False: + if not result: return False - if matched == False: + if not matched: print "ERROR: No matching edge for ", out_edge, "." return False return True diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 0cfaf8ba261..cd3c7947a89 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -33,7 +33,7 @@ where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -108,12 +108,17 @@ from sage.combinat.root_system.root_system import RootSystem from sage.rings.integer import Integer from sage.rings.infinity import Infinity +from sage.rings.integer_ring import ZZ +from sage.matrix.matrix import is_Matrix +from sage.matrix.matrix_space import MatrixSpace -class NakajimaYMonomial(Element): +class NakajimaMonomial(Element): r""" - Monomials of the form `Y_{i_1,k_1}^{a_1}\cdots Y_{i_t,k_t}^{y_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `y_1,\dots,y_t` are integers. + An element of the monomial crystal. + + Monomials of the form `Y_{i_1,k_1}^{y_1} \cdots Y_{i_t,k_t}^{y_t}`, + where `i_1, \dots, i_t` are elements of the index set, `k_1, \dots, k_t` + are nonnegative integers, and `y_1, \dots, y_t` are integers. EXAMPLES:: @@ -122,14 +127,26 @@ class NakajimaYMonomial(Element): sage: mg 1 sage: mg.f_string([1,3,2,0,1,2,3,0,0,1]) - Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 + Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + + An example using the `A` variables:: + + sage: M = crystals.infinity.NakajimaMonomials("A3") + sage: M.set_variables('A') + sage: mg = M.module_generators[0] + sage: mg.f_string([1,2,3,2,1]) + A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 + sage: mg.f_string([3,2,1]) + A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 + sage: M.set_variables('Y') """ - def __init__(self,parent,dict): + def __init__(self, parent, Y, A): r""" INPUT: - - ``dict`` -- a dictionary of with pairs of the form ``{(i,k):y}`` + - ``d`` -- a dictionary of with pairs of the form ``{(i,k): y}`` EXAMPLES:: @@ -137,30 +154,74 @@ def __init__(self,parent,dict): sage: mg = M.module_generators[0] sage: TestSuite(mg).run() """ - self._dict = dict + self._Y = Y + self._A = A Element.__init__(self, parent) def _repr_(self): r""" Return a string representation of ``self``. + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) + sage: x = M({(1,0):1, (2,2):-2, (0,5):10}); x + Y(0,5)^10 Y(1,0) Y(2,2)^-2 + sage: M.set_variables('A') + sage: x + A(1,0)^-2 A(1,1)^-2 A(2,0)^-4 A(2,1)^-2 A(3,0)^-2 + sage: M.set_variables('Y') + """ + return getattr(self, '_repr_' + self.parent()._variable)() + + def _repr_Y(self): + r""" + Return a string representation of ``self`` in the `Y` variables. + EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) sage: M({(1,0):1,(2,2):-2,(0,5):10}) Y(0,5)^10 Y(1,0) Y(2,2)^-2 """ - if self._dict == {}: + if not self._Y: return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "Y(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + return ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + + def _repr_A(self): + r""" + Return a string representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['B',4,1]) + sage: m = M.module_generators[0].f_string([4,2,1]) + sage: m._repr_A() + 'A(1,1)^-1 A(2,0)^-1 A(4,0)^-1' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "1" + + L = sorted(Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + ret = ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + if not self._A: + return ret + if Y: + ret += ' ' + L = sorted(self._A.iteritems(), key=lambda x: (x[0][0], x[0][1])) + return ret + ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) def __hash__(self): r""" @@ -172,9 +233,9 @@ def __hash__(self): 4715601665014767730 # 64-bit -512614286 # 32-bit """ - return hash(frozenset(tuple(self._dict.iteritems()))) + return hash(frozenset(tuple(self._Y.iteritems()))) - def __eq__(self,other): + def __eq__(self, other): r""" EXAMPLES:: @@ -186,11 +247,11 @@ def __eq__(self,other): sage: m1.__eq__(m1) True """ - if isinstance(other, NakajimaYMonomial): - return self._dict == other._dict - return self._dict == other + if isinstance(other, NakajimaMonomial): + return self._Y == other._Y + return self._Y == other - def __ne__(self,other): + def __ne__(self, other): r""" EXAMPLES:: @@ -205,7 +266,7 @@ def __ne__(self,other): """ return not self == other - def __lt__(self,other): + def __lt__(self, other): r""" EXAMPLES:: @@ -226,20 +287,71 @@ def _latex_(self): EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) - sage: M.module_generators[0].f_string([1,0,2])._latex_() + sage: x = M.module_generators[0].f_string([1,0,2]) + sage: latex(x) + Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} + sage: M.set_variables('A') + sage: latex(x) + A_{0,0}^{-1} A_{1,0}^{-1} A_{2,0}^{-1} + sage: M.set_variables('Y') + """ + return getattr(self, '_latex_' + self.parent()._variable)() + + def _latex_Y(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `Y` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) + sage: M.module_generators[0].f_string([1,0,2])._latex_Y() 'Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} ' """ - if self._dict == {}: + if not self._Y: return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "Y_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + return return_str + + def _latex_A(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: m._latex_A() + 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "\\boldsymbol{1}" + + L = sorted(Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + L = sorted(self._A.iteritems(), key=lambda x:(x[0][0],x[0][1])) + for x in L: + if x[1] != 1: + return_str += "A_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "A_{%s,%s} "%(x[0][0],x[0][1]) + return return_str def _classical_weight(self): r""" @@ -260,7 +372,7 @@ def _classical_weight(self): """ P = self.parent().weight_lattice_realization() La = P.fundamental_weights() - return P(sum(v*La[k[0]] for k,v in self._dict.iteritems())) + return P(sum(v*La[k[0]] for k,v in self._Y.iteritems())) def weight_in_root_lattice(self): r""" @@ -278,11 +390,15 @@ def weight_in_root_lattice(self): sage: m = mg.f_string([1,3,2,0,1,2,3,0,0,1]) sage: m.weight_in_root_lattice() -3*alpha[0] - 3*alpha[1] - 2*alpha[2] - 2*alpha[3] + + sage: M = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: m = M.module_generators[0].f_string([3,0,1,2,0]) + sage: m.weight_in_root_lattice() + -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] """ Q = RootSystem(self.parent().cartan_type()).root_lattice() - alpha = Q.simple_roots() - path = self.to_highest_weight() - return Q(sum(-alpha[j] for j in path[1])) + al = Q.simple_roots() + return Q.sum(e*al[k[0]] for k,e in self._A.iteritems()) def weight(self): r""" @@ -291,14 +407,19 @@ def weight(self): EXAMPLES:: sage: C = crystals.infinity.NakajimaMonomials(['A',1,1]) - sage: v=C.highest_weight_vector() - sage: v.f(1).weight()+v.f(0).weight() + sage: v = C.highest_weight_vector() + sage: v.f(1).weight() + v.f(0).weight() -delta + + sage: M = crystals.infinity.NakajimaMonomials(['A',4,2]) + sage: m = M.highest_weight_vector().f_string([1,2,0,1]) + sage: m.weight() + 2*Lambda[0] - Lambda[1] - delta """ P = self.parent().weight_lattice_realization() return P(self.weight_in_root_lattice()) - def epsilon(self,i): + def epsilon(self, i): r""" Return the value of `\varepsilon_i` on ``self``. @@ -312,13 +433,18 @@ def epsilon(self,i): sage: m = M.module_generators[0].f(2) sage: [m.epsilon(i) for i in M.index_set()] [0, 0, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.epsilon(i) for i in M.index_set()] + [0, 0, 0, 1, 0] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") h = self.parent().weight_lattice_realization().simple_coroots() return self.phi(i) - self._classical_weight().scalar(h[i]) - def phi(self,i): + def phi(self, i): r""" Return the value of `\varphi_i` on ``self``. @@ -332,28 +458,28 @@ def phi(self,i): sage: m = M.module_generators[0].f(1) sage: [m.phi(i) for i in M.index_set()] [1, -1, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.phi(i) for i in M.index_set()] + [0, 1, -1, 2, -1] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - dict = self._dict - if dict == {}: - return 0 - else: - L = [x[0] for x in dict.keys()] - if i not in L: - return 0 + if not self._Y or all(x[0] != i for x in self._Y): + return ZZ.zero() + + d = copy(self._Y) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) - def _ke(self,i): + def _ke(self, i): r""" Return the value `k_e` with respect to ``i`` and ``self``. @@ -368,31 +494,29 @@ def _ke(self,i): sage: [m._ke(i) for i in M.index_set()] [+Infinity, 0, +Infinity] """ - dict = self._dict - sum = 0 - L = [] + h = self.parent().weight_lattice_realization().simple_coroots() phi = self.phi(i) - if self.epsilon(i) == 0: + if phi == self._classical_weight().scalar(h[i]): # self.epsilon(i) == 0 return Infinity - else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - for var,exp in S: - sum += exp - if sum == phi: - L.append(var[1]) - if L == []: - return 0 + + d = copy(self._Y) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - return max(L) + d[(i,a)] = 0 + total = ZZ.zero() + L = [] + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + for var,exp in S: + total += exp + if total == phi: + L.append(var[1]) + + return max(L) if L else ZZ.zero() - def _kf(self,i): + def _kf(self, i): r""" Return the value `k_f` with respect to ``i`` and ``self``. @@ -407,26 +531,25 @@ def _kf(self,i): sage: [m._kf(i) for i in M.index_set()] [0, 0, 2, 0, 0] """ - d = copy(self._dict) - I = [x[0] for x in d] - if i not in I: - return 0 - else: - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - sum = 0 - phi = self.phi(i) - for var,exp in S: - sum += exp - if sum == phi: - return var[1] - - def e(self,i): + if all(i != x[0] for x in self._Y): + return ZZ.zero() + + d = copy(self._Y) + K = max(key[1] for key in d if key[0] == i) + for a in range(K): + if (i,a) in d: + continue + else: + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + sum = 0 + phi = self.phi(i) + for var,exp in S: + sum += exp + if sum == phi: + return var[1] + + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -442,7 +565,7 @@ def e(self,i): [None, None, None, - Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0) , + Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0), None, None, None, @@ -451,46 +574,58 @@ def e(self,i): sage: M = crystals.infinity.NakajimaMonomials("C5") sage: m = M.module_generators[0].f_string([1,3]) sage: [m.e(i) for i in M.index_set()] - [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0) , + [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0), None, - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) , + Y(1,0)^-1 Y(1,1)^-1 Y(2,0), None, None] + + sage: M = crystals.infinity.NakajimaMonomials(['D',4,1]) + sage: M.set_variables('A') + sage: m = M.module_generators[0].f_string([4,2,3,0]) + sage: [m.e(i) for i in M.index_set()] + [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, + None, + None, + A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, + None] + sage: M.set_variables('Y') """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") if self.epsilon(i) == 0: return None - newdict = copy(self._dict) + newdict = copy(self._Y) ke = self._ke(i) - Aik = {(i, ke):1, (i, ke+1):1} + Aik = {(i, ke): 1, (i, ke+1): 1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if self.parent().cartan_type().is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, ke+c)] = cm[j-shift][i-shift] + c = self.parent()._c[j_index,i-shift] + if cm[j_index,i-shift] != 0: + Aik[(j, ke+c)] = cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) + A = copy(self._A) + A[(i,ke)] = A.get((i,ke),0) + 1 + if not A[(i,ke)]: + del A[(i,ke)] + return self.__class__(self.parent(), newdict, A) - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -503,309 +638,86 @@ def f(self,i): sage: M = crystals.infinity.NakajimaMonomials("B4") sage: m = M.module_generators[0].f_string([1,3,4]) sage: [m.f(i) for i in M.index_set()] - [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2 ] + [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - newdict = copy(self._dict) + newdict = copy(self._Y) kf = self._kf(i) - Aik = {(i, kf):-1, (i, kf+1):-1} + Aik = {(i, kf): -1, (i, kf+1): -1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if ct.is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, kf+c)] = -cm[j-shift][i-shift] + c = self.parent()._c[j_index,i-shift] + if cm[j_index,i-shift] != 0: + Aik[(j, kf+c)] = -cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) - -class NakajimaAMonomial(NakajimaYMonomial): - r""" - Monomials of the form `A_{i_1,k_1}^{a_1}\cdots A_{i_t,k_t}^{a_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `a_1,\dots,a_t` are integers. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("A3",use_Y=False) - sage: mg = M.module_generators[0] - sage: mg.f_string([1,2,3,2,1]) - A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 - sage: mg.f_string([3,2,1]) - A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 - """ - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['B',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,1]) - sage: m - A(1,1)^-1 A(2,0)^-1 A(4,0)^-1 - """ - if self._dict == {}: - return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "A(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "A(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str - - def _latex_(self): - r""" - Return a `\LaTeX` representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: m._latex_() - 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' - """ - if self._dict == {}: - return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] !=1: - return_str += "A_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "A_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str - - def to_Y_monomial(self): - r""" - Represent `\prod_{(i,k)} A_{i,k}^{a_{i}(k)}` in the form - `\prod_{(i,k)} Y_{i,k}^{y_i(k)}` using the formula - - .. MATH:: - - A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{\substack{j \in I \\ j\neq i}} - Y_{i,k+c_{ji}}^{a_{ji}}. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',2,1],use_Y=False) - sage: m = M.module_generators[0].f_string([2,0,1,2,1]) - sage: m - A(0,0)^-1 A(1,0)^-1 A(1,1)^-1 A(2,0)^-1 A(2,1)^-1 - sage: m.to_Y_monomial() - Y(0,1) Y(0,2) Y(1,1)^-1 Y(2,2)^-1 - """ - Y = {} - d = self._dict - ct = self.parent().cartan_type() - cm = ct.cartan_matrix() - for k,v in d.iteritems(): - Y[k] = Y.get(k,0) + v - Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v - shift = 0 - if ct.is_finite(): - shift = 1 - for j in self.parent().index_set(): - if k[0] == j: - continue - c = 0 - if k[0] > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(k[0]-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][k[0]-shift] != 0: - Y[(j, k[1]+c)] = Y.get((j,k[1]+c),0) + v*cm[j-shift][k[0]-shift] - for k in Y.keys(): - if Y[k] == 0: - Y.pop(k) - return NakajimaYMonomial(self.parent(), Y) - - def weight(self): - r""" - Return the weight of ``self`` as an element of - ``self.parent().weight_lattice_realization()``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',4,2],use_Y=False) - sage: m = M.module_generators[0].f_string([1,2,0,1]) - sage: m.weight() - 2*Lambda[0] - Lambda[1] - delta - """ - return self.to_Y_monomial().weight() - - def weight_in_root_lattice(self): - r""" - Return the weight of ``self`` as an element of the root lattice. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',3,1],use_Y=False) - sage: m = M.module_generators[0].f_string([3,0,1,2,0]) - sage: m.weight_in_root_lattice() - -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] - """ - return self.to_Y_monomial().weight_in_root_lattice() - - def epsilon(self,i): - r""" - Return the action of `\varepsilon_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.epsilon(i) for i in M.index_set()] - [0, 0, 0, 1, 0] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().epsilon(i) - - def phi(self,i): - r""" - Return the action of `\varphi_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.phi(i) for i in M.index_set()] - [0, 1, -1, 2, -1] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().phi(i) - - def e(self,i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['D',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,0]) - sage: [m.e(i) for i in M.index_set()] - [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 , - None, - None, - A(0,2)^-1 A(2,1)^-1 A(4,0)^-1 , - None] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - if self.epsilon(i) == 0: - return None - ke = self.to_Y_monomial()._ke(i) - d = copy(self._dict) - d[(i,ke)] = d.get((i,ke),0)+1 - for k in list(d): - if d[k] == 0: - d.pop(k) - return self.__class__(self.parent(), d) - - def f(self,i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("E8",use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,8]) - sage: m - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 - sage: [m.f(i) for i in M.index_set()] - [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2 ] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - kf = self.to_Y_monomial()._kf(i) - d = copy(self._dict) - d[(i,kf)] = d.get((i,kf),0) - 1 - return self.__class__(self.parent(), d) + A = copy(self._A) + A[(i,kf)] = A.get((i,kf),0) - 1 + if not A[(i,kf)]: + del A[(i,kf)] + return self.__class__(self.parent(), newdict, A) class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): r""" + Crystal `B(\infty)` in terms of (modified) Nakajima monomials. + Let `Y_{i,k}`, for `i \in I` and `k \in \ZZ`, be a commuting set of - variables, and let `\boldsymbol{1}` be a new variable which commutes with - each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan datum.) One - may endow the structure of a crystal on the set `\widehat{\mathcal{M}}` of - monomials of the form + variables, and let `\boldsymbol{1}` be a new variable which commutes + with each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan + datum.) One may endow the structure of a crystal on the + set `\widehat{\mathcal{M}}` of monomials of the form .. MATH:: M = \prod_{(i,k) \in I\times \ZZ_{\ge0}} Y_{i,k}^{y_i(k)}\boldsymbol{1}. - Elements of `\widehat{\mathcal{M}}` are called *modified Nakajima monomials*. - We will omit the `\boldsymbol{1}` from the end of a monomial if there exists - at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by + Elements of `\widehat{\mathcal{M}}` are called + *modified Nakajima monomials*. We will omit the `\boldsymbol{1}` + from the end of a monomial if there exists at least one `y_i(k) \neq 0`. + The crystal structure on this set is defined by .. MATH:: \begin{aligned} - \mathrm{wt}(M) &= \sum_{i\in I} \Bigl( \sum_{k\ge 0} y_i(k) \Bigr) \Lambda_i, \\ - \varphi_i(M) &= \max\Bigl\{ \sum_{0\le j \le k} y_i(j) : k\ge 0 \Bigr\}, \\ - \varepsilon_i(M) &= \varphi_i(M) - \langle h_i, \mathrm{wt}(M) \rangle, \\ - k_f = k_f(M) &= \min\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, \\ - k_e = k_e(M) &= \max\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, + \mathrm{wt}(M) & = \sum_{i\in I} \Bigl( \sum_{k \ge 0} + y_i(k) \Bigr) \Lambda_i, \\ + \varphi_i(M) & = \max\Bigl\{ \sum_{0 \le j \le k} y_i(j) : + k \ge 0 \Bigr\}, \\ + \varepsilon_i(M) & = \varphi_i(M) - + \langle h_i, \mathrm{wt}(M) \rangle, \\ + k_f = k_f(M) & = \min\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \\ + k_e = k_e(M) & = \max\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \end{aligned} where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple - coroots and fundamental weights, respectively. With a chosen set of integers - `C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines + coroots and fundamental weights, respectively. With a chosen set of + non-negative integers `C = (c_{ij})_{i\neq j}` such that + `c_{ij} + c_{ji} = 1`, one defines .. MATH:: A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{j\neq i} Y_{j,k+c_{ji}}^{a_{ji}}, - where `(a_{ij})` is a Cartan matrix. Then + where `(a_{ij})_{i,j \in I}` is a Cartan matrix. Then .. MATH:: @@ -816,15 +728,18 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): \end{aligned} It is shown in [KKS07]_ that the connected component of - `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we - denote by `\mathcal{M}(\infty)`, is crystal isomorphic to the crystal - `B(\infty)`. + `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, + which we denote by `\mathcal{M}(\infty)`, is crystal isomorphic + to the crystal `B(\infty)`. INPUT: - ``cartan_type`` -- a Cartan type - - ``use_Y`` -- choice of monomials in terms of `A` or `Y` + - ``c`` -- (optional) the matrix `(c_{ij})_{i,j \in I}` such that + `c_{ii} = 0` for all `i \in I`, `c_{ij} \in \ZZ_{>0}` for all + `i,j \in I`, and `c_{ij} + c_{ji} = 1` for all `i \neq j`; the + default is `c_{ij} = 0` if `i < j` and `0` otherwise EXAMPLES:: @@ -855,9 +770,66 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): sage: BG.is_isomorphic(MG,edge_labels=True) # long time True """ + @staticmethod + def _normalize_c(c, n): + """ + Normalize the input ``c``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.monomial_crystals import InfinityCrystalOfNakajimaMonomials + sage: InfinityCrystalOfNakajimaMonomials._normalize_c(None, 4) + [0 1 1 1] + [0 0 1 1] + [0 0 0 1] + [0 0 0 0] + sage: c = matrix([[0,1,1],[0,0,0],[0,1,0]]); c + [0 1 1] + [0 0 0] + [0 1 0] + sage: c.is_mutable() + True + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 3); C + [0 1 1] + [0 0 0] + [0 1 0] + sage: C.is_mutable() + False + + TESTS:: + + sage: c = matrix([[0,1],[0,1]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have 0's on the diagonal + sage: c = matrix([[0,2],[-1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have non-negative entries + sage: c = matrix([[0,1],[1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: transpose entries do not sum to 1 + """ + if c is None: + # Default is i < j <=> c_{ij} = 1 (0 otherwise) + c = [[1 if i < j else 0 for j in range(n)] for i in range(n)] + MS = MatrixSpace(ZZ, n, n) + c = MS(c) + c.set_immutable() + if any(c[i,i] != 0 for i in range(n)): + raise ValueError("the c matrix must have 0's on the diagonal") + if any(c[i,j] + c[j,i] != 1 for i in range(n) for j in range(i)): + raise ValueError("transpose entries do not sum to 1") + if any(c[i,j] < 0 or c[j,i] < 0 for i in range(n) for j in range(i)): + raise ValueError("the c matrix must have non-negative entries") + return c @staticmethod - def __classcall_private__(cls, ct, category=None, use_Y=True): + def __classcall_private__(cls, ct, c=None, use_Y=None): r""" Normalize input to ensure a unique representation. @@ -873,17 +845,23 @@ def __classcall_private__(cls, ct, category=None, use_Y=True): sage: M is M1 is M2 True """ - if isinstance(use_Y, bool): - if use_Y: - elt_class = NakajimaYMonomial - else: - elt_class = NakajimaAMonomial + if use_Y is not None: + from sage.misc.superseded import deprecation + deprecation(18895, 'use_Y is deprecated; use the set_variables() method instead.') else: - elt_class = use_Y + use_Y = True + cartan_type = CartanType(ct) - return super(InfinityCrystalOfNakajimaMonomials,cls).__classcall__(cls,cartan_type,category,elt_class) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) + if not use_Y: + M.set_variables('A') + else: + M.set_variables('Y') + return M - def __init__(self, ct, category, elt_class): + def __init__(self, ct, c, category=None): r""" EXAMPLES:: @@ -891,20 +869,24 @@ def __init__(self, ct, category, elt_class): sage: TestSuite(Minf).run() # long time """ self._cartan_type = ct + self._c = c + self._variable = 'Y' - self.Element = elt_class if category is None: category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) Parent.__init__(self, category=category) - self.module_generators = (self.element_class(self,{}),) + self.module_generators = (self.element_class(self, {}, {}),) - def _element_constructor_(self,dict): + def _element_constructor_(self, Y=None, A=None): r""" - Construct an element of ``self`` from ``dict``. + Construct an element of ``self`` from ``Y``. INPUT: - - ``dict`` -- a dictionary whose key is a pair and whose value is an integer + - ``Y`` -- a dictionary whose key is a pair and whose value + is an integer + - ``A`` -- a dictionary whose key is a pair and whose value + is an integer EXAMPLES:: @@ -912,8 +894,46 @@ def _element_constructor_(self,dict): sage: m = M({(1,0):-1,(1,1):-1,(2,0):1}) sage: m Y(1,0)^-1 Y(1,1)^-1 Y(2,0) + + sage: M = crystals.infinity.NakajimaMonomials(['A',2,1]) + sage: m = M(A={(0,1): -1, (1,1): -2, (2,0): -1, (2,1): -1}) + sage: m._repr_A() + 'A(0,1)^-1 A(1,1)^-2 A(2,0)^-1 A(2,1)^-1' + sage: m + Y(0,2)^2 Y(1,2)^-1 Y(2,0)^-1 Y(2,1) Y(2,2)^-1 + sage: m == M.highest_weight_vector().f_string([2,0,1,2,1]) + True """ - return self.element_class(self,dict) + if A is None: + if Y is None: + return self.module_generators[0] + # This is a crude way to determine the A, but it works + hw,path = self.element_class(self, Y, {}).to_highest_weight() + hw._A = {} + return hw.f_string(reversed(path)) + elif Y is None or Y == 0: + # The Y == 0 check is because the parent's __call__ has that + # as the first default value + ct = self.cartan_type() + cm = ct.cartan_matrix() + I = self.index_set() + shift = 0 + if ct.is_finite(): + shift = 1 + Y = {} + for k,v in A.iteritems(): + Y[k] = Y.get(k, 0) + v + Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v + for j_index,j in enumerate(I): + if k[0] == j: + continue + c = self._c[j_index,k[0]-shift] + if cm[j_index,k[0]-shift] != 0: + Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] + for k in Y.keys(): + if Y[k] == 0: + del Y[k] + return self.element_class(self, Y, A) def _repr_(self): r""" @@ -926,6 +946,27 @@ def _repr_(self): """ return "Infinity Crystal of modified Nakajima monomials of type {}".format(self._cartan_type) + def c(self): + """ + Return the matrix `c_{ij}` of ``self``. + + EXAMPLES:: + + sage: La = RootSystem(['B',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: M.c() + [0 1 1] + [0 0 1] + [0 0 0] + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() == c + True + """ + return self._c + def cardinality(self): r""" Return the cardinality of ``self``, which is always `\infty`. @@ -962,13 +1003,70 @@ def weight_lattice_realization(self): return F.weight_lattice(extended=True) return F.weight_lattice() -class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): + def set_variables(self, letter): + r""" + Set the type of monomials to use for the element output. + + If the `A` variables are used, the output is written as + `\prod_{i\in I} Y_{i,0}^{\lambda_i} \prod_{i,k} A_{i,k}^{c_{i,k}}`, where + `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding + dominant weight. + + INPUT: + + - ``letter`` -- can be one of the following: + + * ``'Y'`` - use `Y_{i,k}`, corresponds to fundamental weights + * ``'A'`` - use `A_{i,k}`, corresponds to simple roots + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: elt = M.highest_weight_vector().f_string([2,1,3,2,3,2,4,3]) + sage: elt + Y(1,2) Y(2,0)^-1 Y(2,2)^-1 Y(3,0)^-1 Y(3,2)^-1 Y(4,0) + sage: M.set_variables('A') + sage: elt + A(1,1)^-1 A(2,0)^-1 A(2,1)^-2 A(3,0)^-2 A(3,1)^-1 A(4,0)^-1 + sage: M.set_variables('Y') + + :: + + sage: La = RootSystem(['A',2]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: lw = M.lowest_weight_vectors()[0] + sage: lw + Y(1,2)^-1 Y(2,1)^-1 + sage: M.set_variables('A') + sage: lw + Y(1,0) Y(2,0) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 + sage: M.set_variables('Y') + """ + if letter not in ['Y', 'A']: + raise ValueError("invalid monomial type") + self._variable = letter + + def get_variables(self): + """ + Return the type of monomials to use for the element output. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: M.get_variables() + 'Y' + """ + return self._variable + + Element = NakajimaMonomial + +class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): r""" Element class for :class:`~sage.combinat.crystals.monomial_crystals.CrystalOfNakajimaMonomials`. The `f_i` operators need to be modified from the version in - :class:`~sage.combinat.crystals.monomial_crystalsNakajimaYMonomial` + :class:`~sage.combinat.crystals.monomial_crystalsNakajimaMonomial` in order to create irreducible highest weight realizations. This modified `f_i` is defined as @@ -985,7 +1083,7 @@ class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): Y(0,0)^2 Y(0,1)^-1 Y(2,0) sage: TestSuite(m).run() """ - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -999,7 +1097,25 @@ def f(self,i): sage: M = crystals.NakajimaMonomials(['A',5,2],3*La[0]) sage: m = M.module_generators[0] sage: [m.f(i) for i in M.index_set()] - [Y(0,0)^2 Y(0,1)^-1 Y(2,0) , None, None, None] + [Y(0,0)^2 Y(0,1)^-1 Y(2,0), None, None, None] + + :: + + sage: M = crystals.infinity.NakajimaMonomials("E8") + sage: M.set_variables('A') + sage: m = M.module_generators[0].f_string([4,2,3,8]) + sage: m + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 + sage: [m.f(i) for i in M.index_set()] + [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] + sage: M.set_variables('Y') """ if self.phi(i) == 0: return None @@ -1087,9 +1203,23 @@ class CrystalOfNakajimaMonomials(InfinityCrystalOfNakajimaMonomials): sage: GB = B.digraph(subset=SB) sage: GM.is_isomorphic(GB,edge_labels=True) True + + sage: c = matrix([[0,1,0],[0,0,1],[1,0,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: list(M.subcrystal(max_depth=3)) + [Y(1,0)^2, + Y(0,1) Y(1,0) Y(1,1)^-1 Y(2,0), + Y(0,2)^-1 Y(1,0) Y(2,0) Y(2,2), + Y(0,1)^2 Y(1,1)^-2 Y(2,0)^2, + Y(0,0) Y(0,1) Y(1,0) Y(2,1)^-1, + Y(0,0) Y(0,2)^-1 Y(1,0) Y(1,1) Y(2,1)^-1 Y(2,2), + Y(0,1) Y(0,2)^-1 Y(1,1)^-1 Y(2,0)^2 Y(2,2), + Y(0,0) Y(0,1)^2 Y(1,1)^-1 Y(2,0) Y(2,1)^-1, + Y(1,0) Y(1,3) Y(2,0) Y(2,3)^-1] """ @staticmethod - def __classcall_private__(cls, cartan_type, La): + def __classcall_private__(cls, cartan_type, La=None, c=None): r""" Normalize input to ensure a unique representation. @@ -1102,14 +1232,19 @@ def __classcall_private__(cls, cartan_type, La): sage: M is M1 is M2 True """ + if La is None: + La = cartan_type + cartan_type = La.parent().cartan_type() cartan_type = CartanType(cartan_type) if cartan_type.is_affine(): La = RootSystem(cartan_type).weight_lattice(extended=True)(La) else: La = RootSystem(cartan_type).weight_lattice()(La) - return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La, c) - def __init__(self, ct, La): + def __init__(self, ct, La, c): r""" EXAMPLES:: @@ -1125,14 +1260,11 @@ def __init__(self, ct, La): cat = ClassicalCrystals() else: cat = (RegularCrystals(), HighestWeightCrystals(), InfiniteEnumeratedSets()) - InfinityCrystalOfNakajimaMonomials.__init__( self, ct, cat, - CrystalOfNakajimaMonomialsElement ) + InfinityCrystalOfNakajimaMonomials.__init__(self, ct, c, cat) self._cartan_type = ct self.hw = La - gen = {} - for j in range(len(La.support())): - gen[(La.support()[j],0)] = La.coefficients()[j] - self.module_generators = (self.element_class(self,gen),) + gen = {(i,0): c for i,c in La} + self.module_generators = (self.element_class(self, gen, {}),) def _repr_(self): r""" @@ -1167,3 +1299,5 @@ def cardinality(self): return Infinity return super(InfinityCrystalOfNakajimaMonomials, self).cardinality() + Element = CrystalOfNakajimaMonomialsElement + diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index b9cd295cffb..3442ee71c13 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -954,9 +954,9 @@ def __lt__(self, other): if len(self) != len(other): return False for i in range(len(self)): - if (self[i] < other[i]) == True: + if (self[i] < other[i]): return True - if (other[i] < self[i]) == True: + if (other[i] < self[i]): return False return False @@ -1360,9 +1360,9 @@ def positions_of_unmatched_minus(self, i, dual=False, reverse=False): """ unmatched_plus = [] height = 0 - if reverse == True: + if reverse: self = self.reversed() - if dual == False: + if not dual: for j in range(len(self)): minus = self[j].phi(i) plus = self[j].epsilon(i) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 7373d1889f3..a847d7d8714 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -499,7 +499,7 @@ def HughesPlane(q2, check=True): while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() - sage: line = lambda p,q: (b for b in blocks if p in b and q in b).next() + sage: line = lambda p,q: next(b for b in blocks if p in b and q in b) sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 8249b3ebe2f..624ad343f87 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -309,12 +309,11 @@ def is_covering(self): tset[tuple(y)] = True for i in Svt: - if tset[tuple(i)] == False: # uncovered + if not tset[tuple(i)]: # uncovered return False return True # everything was covered - def v(self): """ Return `v`, the number of points in the covering design. diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index e6528330cef..40aafeb96a1 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1612,8 +1612,8 @@ def is_final(self, is_final): sage: A.state((0, 0)).final_word_out = [] sage: A.state((0, 0)).is_final = False - sage: A.state((0, 0)).is_final == False - True + sage: A.state((0, 0)).is_final + False sage: A = FSMState('A', is_final=True, final_word_out=[]) sage: A.is_final = False @@ -3897,7 +3897,7 @@ def __call__(self, *args, **kwargs): kwargs['automatic_output_type'] = not 'format_output' in kwargs input_tape = args[0] if hasattr(input_tape, 'is_finite') and \ - input_tape.is_finite() == False: + not input_tape.is_finite(): if not 'iterator_type' in kwargs: kwargs['iterator_type'] = 'simple' return self.iter_process(*args, **kwargs) @@ -6283,7 +6283,7 @@ class is created and is used during the processing. for out in it_output] # process output: cannot return output to due input parameters - if options['list_of_outputs'] == False: + if options['list_of_outputs'] is False: if not it_output and only_accepted: raise ValueError('No accepting output was found but according ' 'to the given options, an accepting output ' @@ -11984,8 +11984,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (options['list_of_outputs'] is False and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13171,8 +13171,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (options['list_of_outputs'] is False and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13190,7 +13190,6 @@ class is created and is used during the processing. return result[0] return result - def _process_convert_output_(self, output_data, **kwargs): """ Helper function which converts the output of diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index c08f65958e5..4a921e18362 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -51,6 +51,7 @@ from sage.combinat.combinatorial_map import combinatorial_map from sage.misc.all import prod + class GelfandTsetlinPattern(ClonableArray): r""" A Gelfand-Tsetlin (sometimes written as Gelfand-Zetlin or Gelfand-Cetlin) @@ -502,8 +503,8 @@ def Tokuyama_coefficient(self, name='t'): """ R = PolynomialRing(ZZ, name) t = R.gen(0) - if self.is_strict() == False: - return R(0) + if not self.is_strict(): + return R.zero() return (t+1)**(self.number_of_special_entries()) * t**(self.number_of_boxes()) @@ -1017,12 +1018,21 @@ def _cftp(self, start_row): ALGORITHM: - The set of Gelfand-Tsetlin patterns can partially ordered by elementwise - domination. The partial order has unique maximum and minimum elements - that are computed by the methods ``_cftp_upper`` and ``_cftp_lower``. - We then run the Markov chain that randomly toggles each element up or - down from the past until the state reached from the upper and lower start + The set of Gelfand-Tsetlin patterns can partially ordered by + elementwise domination. The partial order has unique maximum + and minimum elements that are computed by the methods + :meth:`_cftp_upper` and :meth:`_cftp_lower`. We then run the Markov + chain that randomly toggles each element up or down from the + past until the state reached from the upper and lower start points coalesce as described in [Propp1997]_. + + EXAMPLES:: + + sage: G = GelfandTsetlinPatterns(3, 5) + sage: G._cftp(0) # random + [[5, 3, 2], [4, 2], [3]] + sage: G._cftp(0) in G + True """ from sage.misc.randstate import current_randstate from sage.misc.randstate import seed diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index fc52464cf88..a3ee179a5ec 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -779,10 +779,21 @@ def __iter__(self): yield [self.n] return - for nbar in range(self.n+1): - n = self.n-nbar - for rest in IntegerVectors_nk(nbar , self.k-1): - yield [n] + rest + rem = -1 # Amount remaining + cur = [self.n+1] + k = int(self.k) + while cur: + cur[-1] -= 1 + rem += 1 + if rem == 0: + yield cur + [Integer(0)] * (k - len(cur)) + elif cur[-1] < 0: + rem += cur.pop() + elif len(cur) == k - 1: + yield cur + [Integer(rem)] + else: + cur.append(rem + 1) + rem = -1 def __repr__(self): """ diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index e9c5b56cf4f..1e180f90139 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -23,6 +23,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from __builtin__ import list as builtinlist +from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -155,9 +156,9 @@ def __contains__(self, x): sage: [3,-1,0] in WeightedIntegerVectors([2,1,1]) False """ - return isinstance(x, (builtinlist, Permutation)) and \ - len(x) == len(self._weights) and \ - all(isinstance(i, (int, Integer)) and i>=0 for i in x) + return (isinstance(x, (builtinlist, Permutation)) + and len(x) == len(self._weights) + and all(isinstance(i, (int, Integer)) and i >= 0 for i in x)) def subset(self, size = None): """ @@ -245,27 +246,6 @@ def __contains__(self, x): return True - def _recfun(self, n, l): - """ - EXAMPLES:: - - sage: w = WeightedIntegerVectors(3, [2,1,1]) - sage: list(w._recfun(3, [1,1,2])) - [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] - """ - w = l[-1] - l = l[:-1] - if l == []: - d = int(n) // int(w) - if n % w == 0: - yield [d] - # Otherwise: bad branch - return - - for d in range(int(n)//int(w), -1, -1): - for x in self._recfun(n-d*w, l): - yield x + [d] - def __iter__(self): """ TESTS:: @@ -288,13 +268,67 @@ def __iter__(self): sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ - if len(self._weights) == 0: + if not self._weights: if self._n == 0: yield [] return perm = Word(self._weights).standard_permutation() - l = [x for x in sorted(self._weights)] - for x in self._recfun(self._n, l): - yield perm.action(x) + perm = [len(self._weights)-i for i in perm] + l = [x for x in sorted(self._weights, reverse=True)] + for x in iterator_fast(self._n, l): + yield [x[i] for i in perm] + #.action(x) #_left_to_right_multiply_on_right(Permutation(x)) + +def iterator_fast(n, l): + """ + Iterate over all ``l`` weighted integer vectors with total weight ``n``. + + INPUT: + + - ``n`` -- an integer + - ``l`` -- the weights in weakly decreasing order + + EXAMPLES:: + + sage: from sage.combinat.integer_vector_weighted import iterator_fast + sage: list(iterator_fast(3, [2,1,1])) + [[1, 1, 0], [1, 0, 1], [0, 3, 0], [0, 2, 1], [0, 1, 2], [0, 0, 3]] + sage: list(iterator_fast(2, [2])) + [[1]] + """ + if n < 0: + return + + zero = ZZ.zero() + one = ZZ.one() + + if not l: + if n == 0: + yield [] + return + if len(l) == 1: + if n % l[0] == 0: + yield [n / l[0]] + return + + k = 0 + cur = [n // l[k] + one] + rem = n - cur[-1] * l[k] # Amount remaining + while cur: + cur[-1] -= one + rem += l[k] + if rem == zero: + yield cur + [zero] * (len(l) - len(cur)) + elif cur[-1] < zero or rem < zero: + rem += cur.pop() * l[k] + k -= 1 + elif len(l) == len(cur) + 1: + if rem % l[-1] == zero: + yield cur + [rem // l[-1]] + else: + k += 1 + cur.append(rem // l[k] + one) + rem -= cur[-1] * l[k] + diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index cefb02569e0..c3b5398162b 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -443,7 +443,7 @@ def to_undirected_graph(self): relabel = True roots = [self] g.add_vertex(name=self.label()) - while len(roots) != 0: + while roots: node = roots.pop() for child in node: g.add_vertex(name=child.label()) @@ -489,7 +489,7 @@ def to_poset(self, root_to_leaf=False): relations = [] elements = [self.label()] roots = [self] - while len(roots) != 0: + while roots: node = roots.pop() for child in node: elements.append(child.label()) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 5cd1640a70d..af2e5206f09 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -76,8 +76,8 @@ sage: Partitions(4).first() [4] -Using the method ``.next()``, we can calculate the 'next' partition. -When we are at the last partition, ``None`` will be returned:: +Using the method ``.next(p)``, we can calculate the 'next' partition +after `p`. When we are at the last partition, ``None`` will be returned:: sage: Partitions(4).next([4]) [3, 1] @@ -6818,7 +6818,7 @@ def __iter__(self): sage: P = Partitions(regular=3) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [2, 1, 1]] """ n = 0 @@ -6896,7 +6896,7 @@ def __iter__(self): sage: P = Partitions(regular=3, max_length=2) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]] """ n = 0 diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index b225d880b5f..938155b3fa6 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -966,7 +966,6 @@ def to_cycles(self, singletons=True): sage: all(from_cycles(size, p.to_cycles()) == p for p in sample) True - Note: there is an alternative implementation called ``_to_cycle_set`` which could be slightly (10%) faster for some input (typically for permutations of size in the range [100, 10000]). You can run the @@ -995,17 +994,17 @@ def to_cycles(self, singletons=True): l = self[:] - #Go through until we've considered every number between 1 and len(l) + # Go through until we've considered every number between 1 and len(l) for i in range(len(l)): - if l[i] == False: + if not l[i]: continue - cycleFirst = i+1 - cycle = [ cycleFirst ] + cycleFirst = i + 1 + cycle = [cycleFirst] l[i], next = False, l[i] while next != cycleFirst: cycle.append( next ) l[next - 1], next = False, l[next - 1] - #Add the cycle to the list of cycles + # Add the cycle to the list of cycles if singletons or len(cycle) > 1: cycles.append(tuple(cycle)) return cycles diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f4db6431e71..ab0d81385b8 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -71,6 +71,7 @@ :meth:`~FinitePoset.is_graded` | Return ``True`` if all maximal chains of the poset has same length. :meth:`~FinitePoset.is_ranked` | Return ``True`` if the poset has a rank function. :meth:`~FinitePoset.is_rank_symmetric` | Return ``True`` if the poset is rank symmetric. + :meth:`~FinitePoset.is_series_parallel` | Return ``True`` if the poset can be built by ordinal sums and disjoint unions. :meth:`~FinitePoset.is_eulerian` | Return ``True`` if the poset is Eulerian. :meth:`~FinitePoset.is_incomparable_chain_free` | Return ``True`` if the poset is (m+n)-free. :meth:`~FinitePoset.is_slender` | Return ``True`` if the poset is slender. @@ -1913,7 +1914,7 @@ def relations_iterator(self, strict=False): sage: it = P.relations_iterator() sage: type(it) - sage: it.next(), it.next() + sage: next(it), next(it) ([1, 1], [1, 2]) sage: P = posets.PentagonPoset() @@ -2549,9 +2550,40 @@ def is_connected(self): """ return self._hasse_diagram.is_connected() + def is_series_parallel(self): + """ + Return ``True`` if the poset is series-parallel, and ``False`` + otherwise. + + A poset is *series-parallel* if it can be built up from one-element + posets using the operations of disjoint union and ordinal + sum. This is also called *N-free* property: every poset that is not + series-parallel contains a subposet isomorphic to the 4-element + N-shaped poset where `a > c, d` and `b > d`. + + See :wikipedia:`Series-parallel partial order`. + + EXAMPLES:: + + sage: VA = Poset({1: [2, 3], 4: [5], 6: [5]}) + sage: VA.is_series_parallel() + True + sage: big_N = Poset({1: [2, 4], 2: [3], 4:[7], 5:[6], 6:[7]}) + sage: big_N.is_series_parallel() + False + + TESTS:: + + sage: Poset().is_series_parallel() + True + """ + # TODO: Add series-parallel decomposition later. + N = Poset({0: [2, 3], 1: [3]}) + return not self.has_isomorphic_subposet(N) + def is_EL_labelling(self, f, return_raising_chains=False): r""" - Returns ``True`` if ``f`` is an EL labelling of ``self``. + Return ``True`` if ``f`` is an EL labelling of ``self``. A labelling `f` of the edges of the Hasse diagram of a poset is called an EL labelling (edge lexicographic labelling) if @@ -3017,7 +3049,7 @@ def lower_covers_iterator(self, x): sage: l0 = P.lower_covers_iterator(3) sage: type(l0) - sage: l0.next() + sage: next(l0) 2 """ for e in self._hasse_diagram.neighbor_in_iterator(self._element_to_vertex(x)): @@ -3493,7 +3525,7 @@ def antichains_iterator(self): sage: it = Posets.PentagonPoset().antichains_iterator(); it - sage: it.next(), it.next() + sage: next(it), next(it) ([], [4]) .. SEEALSO:: :meth:`antichains` diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index f9a8d288ec4..deab2d46083 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -847,7 +847,7 @@ def _children_iter(self, node): L = [range(val + 1) for val in node.up_root.to_vector()] it = itertools.product(*L) - it.next() # First element is the zero element + next(it) # First element is the zero element for root in it: # Convert the list to an honest root in the root space converted_root = RS.sum_of_terms([[I[i], val] for i, val in enumerate(root)]) diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index d728e3a9f8b..eb08ce1fb43 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -678,7 +678,7 @@ def __iter__(self): EXAMPLES:: sage: CM = CoxeterMatrix([[1,8],[8,1]]) - sage: CM.__iter__().next() + sage: next(CM.__iter__()) (1, 8) """ return iter(self._matrix) diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index 58f2271a04c..74c5a262bcb 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -419,8 +419,35 @@ def is_affine(self): """ return False + @cached_method + def coxeter_diagram(self): + """ + Return the Coxeter diagram for ``self``. + + EXAMPLES:: + + sage: cd = CartanType("A2xB2xF4").coxeter_diagram() + sage: cd + Graph on 8 vertices + sage: cd.edges() + [(1, 2, 3), (3, 4, 4), (5, 6, 3), (6, 7, 4), (7, 8, 3)] + sage: CartanType("F4xA2").coxeter_diagram().edges() + [(1, 2, 3), (2, 3, 4), (3, 4, 3), (5, 6, 3)] + sage: cd = CartanType("A1xH3").coxeter_diagram(); cd + Graph on 4 vertices + sage: cd.edges() + [(2, 3, 3), (3, 4, 5)] + """ + from sage.graphs.graph import Graph + relabelling = self._index_relabelling + g = Graph(multiedges=False) + g.add_vertices(self.index_set()) + for i,t in enumerate(self._types): + for [e1, e2, l] in t.coxeter_diagram().edges(): + g.add_edge(relabelling[i,e1], relabelling[i,e2], label=l) + return g class AmbientSpace(ambient_space.AmbientSpace): """ diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index c708fe60921..c89f3540121 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -555,6 +555,16 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): Traceback (most recent call last): ... ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape + + Check that :trac:`20430` is fixed:: + + sage: RSK([1,1,1,1,1,1,1,2,2,2,3], [1,1,1,1,1,1,3,2,2,2,1]) + [[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]], + [[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]] + sage: t = SemistandardTableau([[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]) + sage: RSK_inverse(t, t, 'array') + [[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3], + [1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 1]] """ if insertion == 'hecke': return hecke_insertion_reverse(p, q, output) @@ -577,7 +587,8 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): use_EG = (insertion == 'EG') - for i in reversed(d.values()): # Delete last entry from i-th row of p_copy + for key in sorted(d, reverse=True): # Delete last entry from i-th row of p_copy + i = d[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y_pos = bisect_left(row,x) - 1 @@ -626,8 +637,9 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): #d is now a double family such that for every integers k and j, #the value d[k][j] is the row i such that the (i, j)-th cell of #q is filled with k. - for value, row_dict in reversed(d.items()): - for i in reversed(row_dict.values()): + for value, row_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]): + for key in sorted(row_dict, reverse=True): + i = row_dict[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y = bisect_left(row,x) - 1 diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index b2a16703adc..acd07348528 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -1264,7 +1264,7 @@ def _exp_gen(R = RationalField()): sage: from sage.combinat.species.generating_series import _exp_gen sage: g = _exp_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]] """ return (_exp_term(i, R) for i in _integers_from(0)) @@ -1323,7 +1323,7 @@ def _cl_gen (R = RationalField()): sage: from sage.combinat.species.generating_series import _cl_gen sage: g = _cl_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] """ return (_cl_term(i, R) for i in _integers_from(0)) diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 3cc12c796e8..8f2854e4c00 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -2620,10 +2620,10 @@ def _slide_down(self, c, n): right_neighbor = new_st[spotl][spotc + 1] if go_right is None or upper_neighbor > right_neighbor: go_right = True - if go_right == True: + if go_right is True: new_st[spotl][spotc] = right_neighbor spotc += 1 - elif go_right == False: + elif go_right is False: new_st[spotl][spotc] = upper_neighbor spotl += 1 else: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index e10bc6efa69..e6068e0be35 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -630,7 +630,7 @@ def _process_letter(self, letter): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1),letter) - while end_state == False: + while not end_state: # adjoin a new state rr and create a transition from r to rr rr = len(self._transition_function) self._transition_function[rr] = {} @@ -1096,7 +1096,7 @@ def to_explicit_suffix_tree(self): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1), end_of_string) - while end_state == False: + while not end_state: (s, k) = self._canonize(self._suffix_link[s], (k,i-1)) (end_state, r) = self._test_and_split(s, (k,i-1), end_of_string) # remove the end of string symbol from the word diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 2f0c29fe13e..929d77cd60c 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -527,7 +527,7 @@ def query_by_dict(query, collection=None): # we expect a dictionary from objects or strings to # integers l = query.iteritems() - (key, value) = l.next() + (key, value) = next(l) (collection, to_str) = get_collection(collection, key) @@ -2475,7 +2475,7 @@ def _element_constructor_(self, entry): # check whether entry is iterable (it's not a string!) try: - obj = iter(entry).next() + obj = next(iter(entry)) for (id, c) in self._findstat_collections.iteritems(): if isinstance(obj, c[3]): return self.element_class(self, id, c, entry) diff --git a/src/sage/env.py b/src/sage/env.py index 79443bb7465..f4497d1e76a 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -165,7 +165,7 @@ def sage_include_directories(use_sources=False): sage: sage.env.sage_include_directories() ['.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '.../python.../site-packages', '.../python.../site-packages/sage/ext'] """ diff --git a/src/sage/ext/interactive_constructors_c.pyx b/src/sage/ext/interactive_constructors_c.pyx index c14d66529af..9bc5b4cf745 100644 --- a/src/sage/ext/interactive_constructors_c.pyx +++ b/src/sage/ext/interactive_constructors_c.pyx @@ -3,6 +3,9 @@ Constructors that automatically inject variables into the global module scope """ import sage.rings.all +import sage.misc.superseded +sage.misc.superseded.deprecation(20442, + '''The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead.''') _verbose=True _inject_mode_off = False @@ -26,6 +29,8 @@ def inject_on(verbose=True): EXAMPLES:: sage: inject_on(verbose=True) + doctest:...: DeprecationWarning: The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead. + See http://trac.sagemath.org/20442 for details. Redefining: FiniteField Frac FractionField FreeMonoid GF LaurentSeriesRing NumberField PolynomialRing quo quotient sage: GF(9,'b') Defining b diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 894e542952e..2973d2b417f 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -841,9 +841,9 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): pred = {} if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. @@ -954,9 +954,9 @@ cpdef johnson_shortest_paths(g, weight_function = None): raise ValueError("The graph contains a negative cycle.") if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index fe6f29cec6b..efdf8beac88 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -15671,7 +15671,9 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, def wiener_index(self, by_weight=False, algorithm=None, weight_function=None, check_weight=True): r""" - Returns the Wiener index of the graph. + Return the Wiener index of the graph. + + The graph is expected to have no cycles of negative weight. The Wiener index of a graph `G` is `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` @@ -15688,28 +15690,32 @@ def wiener_index(self, by_weight=False, algorithm=None, - ``by_weight`` (boolean) - if ``True``, the edges in the graph are weighted; if ``False``, all edges have weight 1. - - ``algorithm`` (string) - one of the following algorithms: + - ``algorithm`` (string) - the algorithm to use: - - ``'BFS'`` - the computation is done through a BFS centered on each - vertex successively. Works only if ``by_weight==False``. + - For ``by_weight==False`` only: - - ``'Floyd-Warshall-Cython'`` - the Cython implementation of - the Floyd-Warshall algorithm. Works only if ``by_weight==False``. + - ``'BFS'`` - the computation is done through a BFS centered on + each vertex successively. - - ``'Floyd-Warshall-Python'`` - the Python implementation of - the Floyd-Warshall algorithm. Works also with weighted graphs, even - with negative weights (but no negative cycle is allowed). + - ``'Floyd-Warshall-Cython'`` - the Cython implementation of + the Floyd-Warshall algorithm. Usually slower than ``'BFS'``. - - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in - NetworkX. It works with weighted graphs, but no negative weight is - allowed. + - For graphs without negative weights: - - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost - (works only with positive weights). + - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in + Boost. - - ``'Johnson_Boost'``: the Johnson algorithm, implemented in - Boost (works also with negative weights, if there is no negative - cycle). + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in + NetworkX. Usually slower than ``'Dijkstra_Boost'``. + + - For graphs with negative weights: + + - ``'Johnson_Boost'``: the Johnson algorithm, implemented in + Boost. + + - ``'Floyd-Warshall-Python'`` - the Python implementation of + the Floyd-Warshall algorithm. Usually slower than + ``'Johnson_Boost'``. - ``None`` (default): Sage chooses the best algorithm: ``'BFS'`` for unweighted graphs, ``'Dijkstra_Boost'`` if all weights are diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 7d5b38936d6..fc9eb2ff9e5 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3906,7 +3906,7 @@ def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, return sum(weight(self.edge_label(u, v)) for u, v in d.iteritems()) * 0.5 else: - return Integer(len(d)/2) + return Integer(len(d) // 2) else: return [(u, v, self.edge_label(u, v)) for u, v in d.iteritems() if u < v] @@ -6469,7 +6469,7 @@ def is_prime(self): return D[0] == "Prime" and len(D[1]) == self.order() @rename_keyword(deprecation=19550, method='algorithm') - def _gomory_hu_tree(self, vertices, algorithm="FF"): + def _gomory_hu_tree(self, vertices, algorithm=None): r""" Return a Gomory-Hu tree associated to self. @@ -6486,15 +6486,9 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): fakes one introduced during the computations. This variable is useful for the algorithm and for recursion purposes. - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problem is solved using - Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. EXAMPLE: @@ -6566,7 +6560,7 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): @doc_index("Connectivity, orientations, trees") @rename_keyword(deprecation=19550, method='algorithm') - def gomory_hu_tree(self, algorithm="FF"): + def gomory_hu_tree(self, algorithm=None): r""" Returns a Gomory-Hu tree of self. @@ -6584,15 +6578,9 @@ def gomory_hu_tree(self, algorithm="FF"): INPUT: - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problems are solved - using Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. OUTPUT: diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 8a84ab5aafc..b9074c61238 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -31,10 +31,12 @@ """ #***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) +# Copyright (C) 2013 Volker Braun # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** @@ -84,6 +86,20 @@ class AffineGroupElement(MultiplicativeGroupElement): sage: G(2) [2 0] [0] x |-> [0 2] x + [0] + + Conversion from a matrix and a matrix group element:: + + sage: M = Matrix(4, 4, [0, 0, -1, 1, 0, -1, 0, 1, -1, 0, 0, 1, 0, 0, 0, 1]) + sage: A = AffineGroup(3, ZZ) + sage: A(M) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] + sage: G = MatrixGroup([M]) + sage: A(G.0) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] """ def __init__(self, parent, A, b=0, convert=True, check=True): r""" @@ -95,10 +111,14 @@ def __init__(self, parent, A, b=0, convert=True, check=True): sage: g = G.random_element() sage: TestSuite(g).run() """ + try: + A = A.matrix() + except AttributeError: + pass if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1: g = A - A = g.submatrix(0,0,2,2) d = parent.degree() + A = g.submatrix(0, 0, d, d) b = [ g[i,d] for i in range(d) ] convert = True if convert: diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 846db9becc3..36d380b8368 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -130,35 +130,27 @@ def __init__(self, name, prompt, command=None, server=None, terminal_echo=True): Interface.__init__(self, name) + + # Read environment variables + env_name = 'SAGE_%s_{}'%self.name().upper() + import os + if server is None: + server = os.getenv(env_name.format('SERVER')) + if server_tmpdir is None: + server_tmpdir = os.getenv(env_name.format('TMPDIR')) + if command is None: + command = os.getenv(env_name.format('COMMAND')) + if script_subdirectory is None: + script_subdirectory = os.getenv(env_name.format('SCRIPT_SUBDIRECTORY')) self.__is_remote = False self.__remote_cleaner = remote_cleaner - if command is None: - command = name - if server is not None: - if ulimit: - command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) - else: - command = "sage-native-execute ssh -t %s '%s'"%(server, command) - self.__is_remote = True -# eval_using_file_cutoff = 0 # don't allow this! - if verbose_start: - print "Using remote server" - print command - self._server = server - if server_tmpdir is None: - # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder - print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server - self.__remote_tmpdir = "/tmp/" - else: - self.__remote_tmpdir = server_tmpdir - else: - self._server = None - self.__do_cleaner = do_cleaner + self._expect = None self._eval_using_file_cutoff = eval_using_file_cutoff - self.__command = command + self.__verbose_start = verbose_start + self.set_server_and_command(server, command, server_tmpdir, ulimit) + self.__do_cleaner = do_cleaner self._prompt = prompt self._restart_on_ctrlc = restart_on_ctrlc - self.__verbose_start = verbose_start if path is not None: self.__path = os.path.abspath(path) elif script_subdirectory is None: @@ -169,7 +161,6 @@ def __init__(self, name, prompt, command=None, server=None, raise EnvironmentError("path %r does not exist" % self.__path) self.__initialized = False self.__seq = -1 - self._expect = None self._session_number = 0 self.__init_code = init_code @@ -185,6 +176,70 @@ def __init__(self, name, prompt, command=None, server=None, self._available_vars = [] self._terminal_echo = terminal_echo + def set_server_and_command(self, server=None, command=None, server_tmpdir=None, ulimit=None): + """ + Changes the server and the command to use for this interface. + This raises a Runtime error if the interface is already started. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote', command = 'mymagma') # indirect doctest + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() + 'remote' + sage: magma.command() + "sage-native-execute ssh -t remote 'mymagma'" + """ + if self._expect: + raise RuntimeError("interface has already started") + if command is None: + command = self.name() + self._server = server + if server is not None: + if ulimit: + command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) + else: + command = "sage-native-execute ssh -t %s '%s'"%(server, command) + self.__is_remote = True + self._eval_using_file_cutoff = 0 # don't allow this! + if self.__verbose_start: + print "Using remote server" + print command + if server_tmpdir is None: + # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder + print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server + self.__remote_tmpdir = "/tmp/" + else: + self.__remote_tmpdir = server_tmpdir + else: + self.__is_remote = False + self.__command = command + + def server(self): + """ + Returns the server used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote') + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() # indirect doctest + 'remote' + """ + return self._server + + def command(self): + """ + Returns the command used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(command = 'magma-2.19') + sage: magma.command() # indirect doctest + 'magma-2.19' + """ + return self.__command + def _get(self, wait=0.1, alternate_prompt=None): if self._expect is None: self._start() diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index ebdd76e53cc..da041f83582 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -230,7 +230,7 @@ EXTCODE_DIR = None -def extcode_dir(): +def extcode_dir(iface = None): """ Return directory that contains all the Magma extcode. This is put in a writable directory owned by the user, since when attached, @@ -243,10 +243,25 @@ def extcode_dir(): """ global EXTCODE_DIR if not EXTCODE_DIR: - import shutil - tmp = sage.misc.temporary_file.tmp_dir() - shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') - EXTCODE_DIR = "%s/data/"%tmp + if iface is None or iface._server is None: + import shutil + tmp = sage.misc.temporary_file.tmp_dir() + shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') + EXTCODE_DIR = "%s/data/"%tmp + else: + import os + tmp = iface._remote_tmpdir() + command = 'scp -q -r "%s/magma/" "%s:%s/data" 1>&2 2>/dev/null'%(SAGE_EXTCODE,iface._server,tmp) + try: + ans = os.system(command) + EXTCODE_DIR = "%s/data/"%tmp + if ans != 0: + raise IOError + except (OSError,IOError): + out_str = 'Tried to copy the file structure in "%s/magma/" to "%s:%s/data" and failed (possibly because scp is not installed in the system).\nFor the remote Magma to work you should populate the remote directory by some other method, or install scp in the system and retry.'%(SAGE_EXTCODE, iface._server, tmp) + from warnings import warn, resetwarnings + resetwarnings() + warn(out_str) return EXTCODE_DIR @@ -267,6 +282,9 @@ class Magma(ExtraTabCompletion, Expect): ``magma_free`` command instead, which uses the free demo web interface to Magma. + If you have ssh access to a remote installation of Magma, you can + also set the ``server`` parameter to use it. + EXAMPLES: You must use nvals = 0 to call a function that doesn't return @@ -280,9 +298,9 @@ class Magma(ExtraTabCompletion, Expect): '1.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' sage: magma.SetDefaultRealFieldPrecision(30, nvals=0) # optional - magma """ - def __init__(self, maxread=None, script_subdirectory=None, + def __init__(self, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None, - user_config=False, seed=None): + user_config=False, seed=None, command=None): """ INPUT: @@ -293,24 +311,35 @@ def __init__(self, maxread=None, script_subdirectory=None, - ``server`` - address of remote server + - ``server_tmpdir`` - temporary directory to use in remote server + - ``user_config`` - if True, then local user configuration files will be read by Magma. If False (the default), then Magma is started with the -n option which suppresses user configuration files. + - ``seed`` - Seed to use in the random number generator. + + - ``command`` - (Default: 'magma') The command to execute to start Magma. EXAMPLES:: sage: Magma(logfile=tmp_filename()) Magma """ - # If the -b argument is given to Magma, the opening banner and all other - # introductory messages are suppressed. The final "total time" message is - # also suppressed. - #command = 'sage-native-execute magma -b ' - command = 'sage-native-execute magma' + if command is None: + import os + command = os.getenv('SAGE_MAGMA_COMMAND') or 'magma' + if not user_config: command += ' -n' + + # Obtain the parameters from the environment, to allow the magma = Magma() phrase + # to work with non-default parameters. + if seed is None: + import os + seed = os.getenv('SAGE_MAGMA_SEED') + Expect.__init__(self, name = "magma", prompt = ">>SAGE>>", @@ -567,7 +596,7 @@ def _start(self): self.expect().expect(PROMPT) self.expect().expect(PROMPT) self.expect().expect(PROMPT) - self.attach_spec(extcode_dir() + '/spec') + self.attach_spec(extcode_dir(self) + '/spec') # set random seed self.set_seed(self._seed) @@ -1380,7 +1409,8 @@ def version(self): sage: magma.version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - return magma_version() + t = tuple([int(n) for n in self.eval('GetVersion()').split()]) + return t, 'V%s.%s-%s'%t def help(self, s): """ @@ -2757,8 +2787,9 @@ def magma_version(): sage: magma_version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - t = tuple([int(n) for n in magma.eval('GetVersion()').split()]) - return t, 'V%s.%s-%s'%t + from sage.misc.superseded import deprecation + deprecation(20388, 'This function has been deprecated. Use magma.version() instead.') + return magma.version() class MagmaGBLogPrettyPrinter: """ diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py index 2c952aa3cb8..4b174510856 100644 --- a/src/sage/knots/link.py +++ b/src/sage/knots/link.py @@ -531,7 +531,7 @@ def braid(self): # Get a simple path from a source to a sink in the digraph it = G.all_paths_iterator(starting_vertices=G.sources(), ending_vertices=G.sinks(), simple=True) - ordered_cycles = it.next() + ordered_cycles = next(it) B = BraidGroup(len(ordered_cycles)) available_crossings = copy(pd_code) diff --git a/src/sage/libs/giac.py b/src/sage/libs/giac.py index acf4c8a3189..0baf3d6aafd 100644 --- a/src/sage/libs/giac.py +++ b/src/sage/libs/giac.py @@ -255,7 +255,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** gens = gens.gens() # get the ring from gens - P = iter(gens).next().parent() + P = next(iter(gens)).parent() K = P.base_ring() p = K.characteristic() diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index ed309bec01b..44521b27d73 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -368,6 +368,7 @@ cdef extern from "libsingular.h": cdef ring *currRing cdef ideal *currQuotient + # omalloc bin for numbers cdef omBin *rnumber_bin diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index f42ef9ce53a..74ecee3b53f 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -485,7 +485,6 @@ def all_vectors(s): return False return True - cdef class Converter(SageObject): """ A :class:`Converter` interfaces between Sage objects and Singular @@ -1157,11 +1156,16 @@ cdef class KernelCallHandler(BaseCallHandler): """ return True +# The Sage ring used as a dummy for singular function calls. +cdef object dummy_ring + cdef class SingularFunction(SageObject): """ The base class for Singular functions either from the kernel or from the library. """ + + def __init__(self, name): """ INPUT: @@ -1207,19 +1211,23 @@ cdef class SingularFunction(SageObject): def __call__(self, *args, ring=None, bint interruptible=True, attributes=None): """ Call this function with the provided arguments ``args`` in the - ring ``R``. + ``ring``. INPUT: - - ``args`` - a list of arguments - - ``ring`` - a multivariate polynomial ring - - ``interruptible`` - if ``True`` pressing Ctrl-C during the + - ``args`` -- a list of arguments + - ``ring`` -- a multivariate polynomial ring + - ``interruptible`` -- if ``True`` pressing Ctrl-C during the execution of this function will interrupt the computation (default: ``True``) - - ``attributes`` - a dictionary of optional Singular + - ``attributes`` -- a dictionary of optional Singular attributes assigned to Singular objects (default: ``None``) + If ``ring`` is not specified, it is guessed from the given arguments. + If this is not possible, then a dummy ring, univariate polynomial ring + over ``QQ``, is used. + EXAMPLE:: sage: from sage.libs.singular.function import singular_function @@ -1232,19 +1240,17 @@ cdef class SingularFunction(SageObject): sage: size(2, ring=P) 1 sage: size(2) - Traceback (most recent call last): - ... - ValueError: Could not detect ring. - sage: size(Ideal([a*b + c, a + 1]), ring=P) + 1 + sage: size(Ideal([a*b + c, a + 1])) 2 sage: size(Ideal([a*b + c, a + 1])) 2 - sage: size(1,2, ring=P) + sage: size(1,2) Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'size': Wrong number of arguments - sage: size('foobar', ring=P) + sage: size('foobar') 6 Show the usage of the optional ``attributes`` parameter:: @@ -1300,10 +1306,16 @@ cdef class SingularFunction(SageObject): sage: triangL(G,attributes={G:{'isSB':1}}) [[e + d + c + b + a, ...]] """ + global dummy_ring + if ring is None: ring = self.common_ring(args, ring) - if not (isinstance(ring, MPolynomialRing_libsingular) or \ - isinstance(ring, NCPolynomialRing_plural)): + if ring is None: + if dummy_ring is None: + from sage.all import QQ, PolynomialRing + dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default + ring = dummy_ring + if not (isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural)): raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) return call_function(self, args, ring, interruptible, attributes) @@ -1324,27 +1336,30 @@ function '%s'. This wrapper takes care of converting Sage datatypes to Singular datatypes and vice versa. In addition to whatever parameters the -underlying Singular function accepts when called this function also +underlying Singular function accepts when called, this function also accepts the following keyword parameters: INPUT: -- ``args`` - a list of arguments -- ``ring`` - a multivariate polynomial ring -- ``interruptible`` - if ``True`` pressing Ctrl-C during the - execution of this function will - interrupt the computation (default: ``True``) -- ``attributes`` - a dictionary of optional Singular - attributes assigned to Singular objects (default: ``None``) +- ``args`` -- a list of arguments +- ``ring`` -- a multivariate polynomial ring +- ``interruptible`` -- if ``True`` pressing Ctrl-C during the + execution of this function will interrupt the computation + (default: ``True``) +- ``attributes`` -- a dictionary of optional Singular attributes + assigned to Singular objects (default: ``None``) -EXAMPLE:: +If ``ring`` is not specified, it is guessed from the given arguments. +If this is not possible, then a dummy ring, univariate polynomial ring +over ``QQ``, is used. + +EXAMPLES:: sage: groebner = sage.libs.singular.function_factory.ff.groebner sage: P. = PolynomialRing(QQ) sage: I = P.ideal(x^2-y, y+x) sage: groebner(I) [x + y, y^2 - y] - sage: triangL = sage.libs.singular.function_factory.ff.triang__lib.triangL sage: P. = PolynomialRing(QQ, order='lex') sage: f1 = 1/2*((x1^2 + 2*x1 - 4)*x2^2 + 2*(x1^2 + x1)*x2 + x1^2) @@ -1372,12 +1387,12 @@ The Singular documentation for '%s' is given below. If ``ring`` is not ``None`` this routine checks whether it is the parent/ring of all members of ``args`` instead. - If no common ring was found a ``ValueError`` is raised. + If no common ring was found, None is returned. INPUT: - - ``args`` - a list of Python objects - - ``ring`` - an optional ring to check + - ``args`` -- a list of Python objects + - ``ring`` -- an optional ring to check """ from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense from sage.matrix.matrix_integer_dense import Matrix_integer_dense @@ -1416,8 +1431,6 @@ The Singular documentation for '%s' is given below. ring = ring2 elif ring is not ring2: raise ValueError("Rings do not match up.") - if ring is None: - raise ValueError("Could not detect ring.") return ring def __reduce__(self): @@ -1617,11 +1630,11 @@ def singular_function(name): sage: std(I) [3*y - 8*z - 4, 4*x + 1] sage: size = singular_function("size") - sage: size([2, 3, 3], ring=P) + sage: size([2, 3, 3]) 3 - sage: size("sage", ring=P) + sage: size("sage") 4 - sage: size(["hello", "sage"], ring=P) + sage: size(["hello", "sage"]) 2 sage: factorize = singular_function("factorize") sage: factorize(f) @@ -1631,7 +1644,7 @@ def singular_function(name): We give a wrong number of arguments:: - sage: factorize(ring=P) + sage: factorize() Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'factorize': @@ -1651,13 +1664,13 @@ def singular_function(name): arguments:: sage: singular_list = singular_function("list") - sage: singular_list(2, 3, 6, ring=P) + sage: singular_list(2, 3, 6) [2, 3, 6] - sage: singular_list(ring=P) + sage: singular_list() [] - sage: singular_list(1, ring=P) + sage: singular_list(1) [1] - sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ring=P) + sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] We try to define a non-existing function:: @@ -1672,9 +1685,9 @@ def singular_function(name): sage: from sage.libs.singular.function import lib as singular_lib sage: singular_lib('general.lib') sage: number_e = singular_function('number_e') - sage: number_e(10r,ring=P) + sage: number_e(10r) 67957045707/25000000000 - sage: RR(number_e(10r,ring=P)) + sage: RR(number_e(10r)) 2.71828182828000 :: @@ -1692,7 +1705,7 @@ def singular_function(name): sage: l [0, ['x', 'y', 'z'], [['dp', (1, 1, 1)], ['C', (0,)]], [0]] sage: ring=singular_function("ring") - sage: ring(l, ring=P) + sage: ring(l) sage: matrix = Matrix(P,2,2) sage: matrix.randomize(terms=1) @@ -1703,12 +1716,11 @@ def singular_function(name): sage: coeffs(x*y+y+1,y) [ 1] [x + 1] - sage: F. = GF(3)[] sage: intmat = Matrix(ZZ, 2,2, [100,2,3,4]) - sage: det(intmat, ring=F) + sage: det(intmat) 394 sage: random = singular_function("random") - sage: A = random(10,2,3, ring =F); A.nrows(), max(A.list()) <= 10 + sage: A = random(10,2,3); A.nrows(), max(A.list()) <= 10 (2, True) sage: P. = PolynomialRing(QQ) sage: M=P**3 @@ -1751,7 +1763,7 @@ def singular_function(name): doctest... sage: M [(x + y, x*y)] - sage: syz(M, ring=P) + sage: syz(M) [(0)] sage: mres(I, 0) @@ -1767,16 +1779,14 @@ def singular_function(name): sage: l = ringlist(P) sage: len(l) 6 - sage: ring(l, ring=P) + sage: ring(l) sage: I=twostd(I) sage: l[3]=I - sage: ring(l, ring=P) + sage: ring(l) - """ - cdef SingularFunction fnc try: return SingularKernelFunction(name) except NameError: @@ -1788,7 +1798,7 @@ def lib(name): INPUT: - - ``name`` - a Singular library name + - ``name`` -- a Singular library name EXAMPLE:: diff --git a/src/sage/libs/singular/function_factory.py b/src/sage/libs/singular/function_factory.py index 6d3169a8ff3..ea489b0d1dc 100644 --- a/src/sage/libs/singular/function_factory.py +++ b/src/sage/libs/singular/function_factory.py @@ -1,5 +1,5 @@ """ -libSingular: Function Factory. +libSingular: Function Factory AUTHORS: diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 1428085f9b6..33d7f7ec9c6 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1,5 +1,5 @@ """ -libSingular conversion routines and initialisation. +libSingular: Conversion Routines and Initialisation AUTHOR: diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 218c54fba75..1f12a2f79fa 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -1028,7 +1028,7 @@ def irange(self, start=None): In general, one has always:: - sage: M.irange().next() == M.start_index() + sage: next(M.irange()) == M.start_index() True """ diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 3ccf39386a8..ad12a5f8882 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -452,7 +452,7 @@ class MatrixFactory(object): [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Real Double Field - sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'float64') + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'float64') sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] @@ -464,7 +464,7 @@ class MatrixFactory(object): [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Complex Double Field - sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'complex128') + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'complex128') sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] @@ -481,6 +481,8 @@ class MatrixFactory(object): [3.0 4.0] sage: matrix(numpy.array([[5]])) [5] + sage: matrix(numpy.matrix([[5]])) + [5] A ring and a numpy array:: @@ -720,6 +722,10 @@ class MatrixFactory(object): if is_numpy_type(type(arg)): import numpy if isinstance(arg, numpy.ndarray): + # Convert to a numpy array if it was a matrix. + if type(arg) is not numpy.ndarray: + arg = numpy.array(arg) + str_dtype = str(arg.dtype) if not (arg.flags.c_contiguous is True or arg.flags.f_contiguous is True): diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 2bf6eddcffe..4fc8b805bdb 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -1766,7 +1766,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: K = (A-e).kernel() sage: P = K.basis_matrix() sage: P.str() - '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]' + '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]' Use single-row delimiters where appropriate:: diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index e11cf150da5..63e50d45c35 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -9044,23 +9044,23 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" ... [-1, 1, -6, -6, 5]]) sage: Q, R = A.QR() sage: Q - [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?] - [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?] - [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?] - [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?] + [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?] + [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?] + [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?] + [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?] [ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?] sage: R [ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?] [ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?] - [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?] - [ 0 0 0 1.027626039419836? -3.61930014968662?] - [ 0 0 0 0 0.02455143080702?] + [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?] + [ 0 0 0 1.027626039419836? -3.619300149686620?] + [ 0 0 0 0 0.024551430807012?] sage: Q.conjugate_transpose()*Q - [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12] - [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12] - [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12] - [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12] - [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?] + [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13] + [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13] + [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?] sage: Q*R == A True @@ -9075,24 +9075,24 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: Q, R = A.QR() sage: Q [ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I] - [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I] - [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I] + [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I] + [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I] [ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I] sage: R [ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I] - [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] + [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] [ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I] [ 0 0 0 1.942963944258992? + 0.?e-16*I] sage: Q.conjugate_transpose()*Q [1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] [ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I] + [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I] sage: Q*R - A [ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I] + [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] [0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I] + [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] A rank-deficient rectangular matrix, with both values of the ``full`` keyword. :: @@ -10495,9 +10495,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" True sage: _, T = A.is_similar(B, transformation=True) sage: T - [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I] - [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I] - [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I] + [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I] + [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I] + [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I] sage: T.change_ring(QQ) [ 1 0 0] [-2/3 1/6 -5/6] diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 7a740184cbc..f61735f0e4b 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -173,7 +173,7 @@ def pyx_preparse(s): 'ntl'], ['.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'], @@ -200,7 +200,7 @@ def pyx_preparse(s): ['bar', '.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'] diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index efa7a120239..657b7523554 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -1531,7 +1531,7 @@ def surgroups(self): EXAMPLES:: sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)", S3="(1,2,3)(4,5,6)") - sage: H = G.surgroups().next() + sage: H = next(G.surgroups()) sage: H Arithmetic subgroup with permutations of right cosets S2=(1,2) diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index a1058658395..8468e16deea 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -573,13 +573,13 @@ def period(self, M, prec=53): to it, which can be computed using the method `:meth:~sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.modular_symbol`. These can be used to express the periods of `f` as exact - linear combinations of a basis for the period lattice of `E`:: + linear combinations of the real and the imaginary period of `E`:: sage: s = E.modular_symbol(sign=+1) sage: t = E.modular_symbol(sign=-1) sage: s(3/11), t(3/11) - (1/10, 1) - sage: s(3/11)*omega1 + t(3/11)*omega2.imag()*I + (1/10, 1/2) + sage: s(3/11)*omega1 + t(3/11)*2*omega2.imag()*I 0.634604652139777 + 1.45881661693850*I ALGORITHM: diff --git a/src/sage/modules/vector_double_dense.pyx b/src/sage/modules/vector_double_dense.pyx index 6bf16751023..0809585bf1c 100644 --- a/src/sage/modules/vector_double_dense.pyx +++ b/src/sage/modules/vector_double_dense.pyx @@ -754,9 +754,9 @@ cdef class Vector_double_dense(FreeModuleElement): sage: v = vector(RDF, range(9)) sage: w = vector(CDF, [k+(9-k)*I for k in range(9)]) - sage: v.stats_kurtosis() + sage: v.stats_kurtosis() # rel tol 5e-16 -1.2300000000000002 - sage: w.stats_kurtosis() + sage: w.stats_kurtosis() # rel tol 5e-16 -1.2300000000000002 """ import scipy.stats diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index a6e3d7e84a1..274b373e9f8 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -25,6 +25,7 @@ from sage.categories.monoids import Monoids from sage.categories.poor_man_map import PoorManMap +from sage.categories.sets_cat import Sets from sage.rings.integer import Integer from sage.rings.infinity import infinity from sage.rings.all import ZZ @@ -688,6 +689,8 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): category = category.Finite() else: category = category.Infinite() + if indices in Sets().Finite(): + category = category.FinitelyGeneratedAsMagma() Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index f34fe2ee22f..e4d66562375 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -28,7 +28,7 @@ def is_Monoid(x): return isinstance(x, Monoid_class) class Monoid_class(Parent): - def __init__(self,names): + def __init__(self, names): r""" EXAMPLES:: @@ -42,7 +42,8 @@ def __init__(self,names): sage: TestSuite(F).run() """ from sage.categories.monoids import Monoids - Parent.__init__(self, base=self,names=names,category=Monoids()) + category = Monoids().FinitelyGeneratedAsMagma() + Parent.__init__(self, base=self, names=names, category=category) @cached_method def gens(self): @@ -56,3 +57,17 @@ def gens(self): (a, b, c, d, e) """ return tuple(self.gen(i) for i in range(self.ngens())) + + def monoid_generators(self): + r""" + Returns the generators for ``self``. + + EXAMPLES:: + + sage: F. = FreeMonoid(5) + sage: F.monoid_generators() + Family (a, b, c, d, e) + """ + from sage.sets.family import Family + return Family(self.gens()) + diff --git a/src/sage/numerical/backends/coin_backend.pxd b/src/sage/numerical/backends/coin_backend.pxd index 8d44d1ee11b..c45427cb2e5 100644 --- a/src/sage/numerical/backends/coin_backend.pxd +++ b/src/sage/numerical/backends/coin_backend.pxd @@ -197,7 +197,7 @@ cdef class CoinBackend(GenericBackend): cdef list col_names, row_names cdef str prob_name - cpdef CoinBackend copy(self) + cpdef __copy__(self) cpdef get_basis_status(self) cpdef int set_basis_status(self, list cstat, list rstat) except -1 cpdef get_binva_row(self, int i) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index b994eadc836..7556e0b27df 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -25,6 +25,18 @@ from copy import copy cdef class CoinBackend(GenericBackend): + """ + MIP Backend that uses the COIN solver (CBC). + + TESTS: + + General backend testsuite:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Coin") # optional - cbc + sage: TestSuite(p).run(skip="_test_pickling") # optional - cbc + """ + def __cinit__(self, maximization = True): """ Cython constructor @@ -723,6 +735,9 @@ cdef class CoinBackend(GenericBackend): self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0) + self.col_names.append("") + + cpdef int solve(self) except -1: r""" Solves the problem. @@ -1194,7 +1209,7 @@ cdef class CoinBackend(GenericBackend): else: return "" - cpdef CoinBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. diff --git a/src/sage/numerical/backends/cplex_backend.pxd b/src/sage/numerical/backends/cplex_backend.pxd index 27e720fcdaa..57c81bbc7d1 100644 --- a/src/sage/numerical/backends/cplex_backend.pxd +++ b/src/sage/numerical/backends/cplex_backend.pxd @@ -19,7 +19,7 @@ cdef class CPLEXBackend(GenericBackend): cdef c_cpxlp * lp cdef current_sol cdef str _logfilename - cpdef CPLEXBackend copy(self) + cpdef __copy__(self) cdef extern from "cplex.h": diff --git a/src/sage/numerical/backends/cplex_backend.pyx b/src/sage/numerical/backends/cplex_backend.pyx index 8987f016af6..8415de226f8 100644 --- a/src/sage/numerical/backends/cplex_backend.pyx +++ b/src/sage/numerical/backends/cplex_backend.pyx @@ -20,6 +20,18 @@ from sage.numerical.mip import MIPSolverException cdef class CPLEXBackend(GenericBackend): + """ + MIP Backend that uses the CPLEX solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - CPLEX + + """ + def __cinit__(self, maximization = True): """ Constructor @@ -1437,7 +1449,7 @@ cdef class CPLEXBackend(GenericBackend): status = CPXwriteprob(self.env, self.lp, filename, ext) check(status) - cpdef CPLEXBackend copy(self): + cpdef __copy__(self): r""" Returns a copy of self. diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index c1864e100e6..c736f3cc133 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -19,6 +19,7 @@ AUTHORS: from sage.numerical.mip import MIPSolverException from cvxopt import solvers +from copy import copy cdef class CVXOPTBackend(GenericBackend): """ @@ -36,6 +37,11 @@ cdef class CVXOPTBackend(GenericBackend): sage: p Mixed Integer Program ( maximization, 0 variables, 0 constraints ) + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CVXOPT") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") """ cdef list objective_function #c_matrix @@ -90,6 +96,43 @@ cdef class CVXOPTBackend(GenericBackend): else: self.set_sense(-1) + cpdef __copy__(self): + # Added a second inequality to this doctest, + # because otherwise CVXOPT complains: ValueError: Rank(A) < p or Rank([G; A]) < n + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "CVXOPT") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.add_constraint(b[2] <= 5) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6.0 + """ + cp = CVXOPTBackend() + cp.objective_function = self.objective_function[:] + cp.G_matrix = [row[:] for row in self.G_matrix] + cp.prob_name = self.prob_name + cp.obj_constant_term = self.obj_constant_term + cp.is_maximize = self.is_maximize + + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + + cp.param = copy(self.param) + return cp cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, name=None) except -1: """ diff --git a/src/sage/numerical/backends/generic_backend.pxd b/src/sage/numerical/backends/generic_backend.pxd index 53c3f26579e..f7293ff8165 100644 --- a/src/sage/numerical/backends/generic_backend.pxd +++ b/src/sage/numerical/backends/generic_backend.pxd @@ -5,7 +5,11 @@ # http://www.gnu.org/licenses/ ############################################################################## -cdef class GenericBackend: +from sage.structure.sage_object cimport SageObject + +# We inherit from SageObject to make some testing infrastructure available. + +cdef class GenericBackend (SageObject): cpdef int add_variable(self, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, name=*) except -1 cpdef int add_variables(self, int, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, names=*) except -1 cpdef set_variable_type(self, int variable, int vtype) @@ -44,6 +48,8 @@ cdef class GenericBackend: cpdef solver_parameter(self, name, value=*) cpdef zero(self) cpdef base_ring(self) + cpdef __copy__(self) + cpdef copy(self) cpdef bint is_variable_basic(self, int index) cpdef bint is_variable_nonbasic_at_lower_bound(self, int index) cpdef bint is_slack_variable_basic(self, int index) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 28d443647c9..c45e56f5f6a 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -29,6 +29,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from copy import copy + cdef class GenericBackend: cpdef base_ring(self): @@ -134,6 +136,30 @@ cdef class GenericBackend: """ raise NotImplementedError() + @classmethod + def _test_add_variables(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_variables() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + # Test from CVXOPT interface (part 1): + ncols_added = 5 + ncols_before = p.ncols() + add_variables_result = p.add_variables(ncols_added) + ncols_after = p.ncols() + tester.assertEqual(ncols_after, ncols_before+ncols_added, "Added the wrong number of columns") + cpdef set_variable_type(self, int variable, int vtype): """ Set the type of a variable @@ -417,6 +443,31 @@ cdef class GenericBackend: upper_bound_d = None if upper_bound is None else upper_bound[d] self.add_linear_constraint(coefficients_d, lower_bound_d, upper_bound_d, name=name) + @classmethod + def _test_add_linear_constraint_vector(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_linear_constraint_vector() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + from sage.modules.all import vector + # Ensure there are at least 2 variables + p.add_variables(2) + coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) + upper = vector([5, 5]) + p.add_linear_constraint_vector(2, coeffs, None, upper, 'foo') + # FIXME: Tests here. Careful what we expect regarding ranged constraints with some solvers. + cpdef add_col(self, list indices, list coeffs): """ Add a column. @@ -481,6 +532,36 @@ cdef class GenericBackend: """ raise NotImplementedError() + @classmethod + def _test_add_linear_constraints(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_linear_constraints() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + nrows_before = p.nrows() + nrows_added = 5 + p.add_linear_constraints(nrows_added, None, 2) + nrows_after = p.nrows() + # Test correct number of rows + tester.assertEqual(nrows_after, nrows_before+nrows_added, "Added the wrong number of rows") + # Test contents of the new rows are correct (sparse zero) + for i in range(nrows_before, nrows_after): + tester.assertEqual(p.row(i), ([], [])) + tester.assertEqual(p.row_bounds(i), (None, 2.0)) + # FIXME: Not sure if we should test that no new variables were added. + # Perhaps some backend may need to introduce explicit slack variables? + cpdef int solve(self) except -1: """ Solve the problem. @@ -651,6 +732,11 @@ cdef class GenericBackend: raise NotImplementedError() + def _test_ncols_nonnegative(self, **options): + tester = self._tester(**options) + p = self + tester.assertGreaterEqual(self.ncols(), 0) + cpdef int nrows(self): """ Return the number of rows/constraints. @@ -744,6 +830,61 @@ cdef class GenericBackend: """ raise NotImplementedError() + cpdef copy(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: copy(p).solve() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + + # Override this method in backends. + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: cp = copy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + raise NotImplementedError() + + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: cp = deepcopy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + cpdef row(self, int i): """ Return a row @@ -940,6 +1081,76 @@ cdef class GenericBackend: """ raise NotImplementedError() + def _do_test_problem_data(self, tester, cp): + """ + TESTS: + + Test, with an actual working backend, that comparing a problem with itself works:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: tester = p._tester() + sage: p._do_test_problem_data(tester, p) + """ + tester.assertEqual(type(self), type(cp), + "Classes do not match") + def assert_equal_problem_data(method): + tester.assertEqual(getattr(self, method)(), getattr(cp, method)(), + "{} does not match".format(method)) + for method in ("ncols", "nrows", "objective_constant_term", "problem_name", "is_maximization"): + assert_equal_problem_data(method) + def assert_equal_col_data(method): + for i in range(self.ncols()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("objective_coefficient", "is_variable_binary", "is_variable_binary", "is_variable_integer", + "is_variable_continuous", "col_bounds", "col_name"): + # don't test variable_lower_bound, variable_upper_bound because we already test col_bounds. + # TODO: Add a test elsewhere to ensure that variable_lower_bound, variable_upper_bound + # are consistent with col_bounds. + assert_equal_col_data(method) + def assert_equal_row_data(method): + for i in range(self.nrows()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("row_bounds", "row", "row_name"): + assert_equal_row_data(method) + + def _test_copy(self, **options): + """ + Test whether the backend can be copied + and at least the problem data of the copy is equal to that of the original. + Does not test whether solutions or solver parameters are copied. + """ + tester = self._tester(**options) + cp = copy(self) + self._do_test_problem_data(tester, cp) + + def _test_copy_does_not_share_data(self, **options): + """ + Test whether copy makes an independent copy of the backend. + """ + tester = self._tester(**options) + cp = copy(self) + cpcp = copy(cp) + del cp + self._do_test_problem_data(tester, cpcp) + + # TODO: We should have a more systematic way of generating MIPs for testing. + @classmethod + def _test_copy_some_mips(cls, tester=None, **options): + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + # From doctest of GenericBackend.solve: + p.add_linear_constraints(5, 0, None) + # p.add_col(range(5), range(5)) -- bad test because COIN sparsifies the 0s away on copy + p.add_col(range(5), range(1, 6)) + # From doctest of GenericBackend.problem_name: + p.problem_name("There once was a french fry") + p._test_copy(**options) + p._test_copy_does_not_share_data(**options) + cpdef variable_upper_bound(self, int index, value = False): """ Return or define the upper bound on a variable @@ -1169,7 +1380,7 @@ def default_mip_solver(solver = None): - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical implementation of the revised simplex method in Sage. It works over - any exact ordered field, the default is ``AA``. + any exact ordered field, the default is ``QQ``. ``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``, ``"Gurobi"``, ``"PPL"`, or @@ -1294,13 +1505,16 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical implementation of the revised simplex method in Sage. It works over - any exact ordered field, the default is ``AA``. + any exact ordered field, the default is ``QQ``. ``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``,``"Gurobi"``, ``"PPL"``, ``"InteractiveLP"``, or ``None``. If ``solver=None`` (default), the default solver is used (see ``default_mip_solver`` method). + ``solver`` can also be a callable, in which case it is called, + and its result is returned. + - ``base_ring`` -- If not ``None``, request a solver that works over this (ordered) field. If ``base_ring`` is not a field, its fraction field is used. @@ -1330,24 +1544,44 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba sage: p.base_ring() Real Double Field sage: p = get_solver(base_ring=QQ); p - + <...sage.numerical.backends.ppl_backend.PPLBackend...> sage: p = get_solver(base_ring=ZZ); p - + <...sage.numerical.backends.ppl_backend.PPLBackend...> sage: p.base_ring() Rational Field sage: p = get_solver(base_ring=AA); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Algebraic Real Field sage: d = polytopes.dodecahedron() sage: p = get_solver(base_ring=d.base_ring()); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Number Field in sqrt5 with defining polynomial x^2 - 5 sage: p = get_solver(solver='InteractiveLP', base_ring=QQ); p - + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> sage: p.base_ring() Rational Field + + Passing a callable as the 'solver':: + + sage: from sage.numerical.backends.glpk_backend import GLPKBackend + sage: p = get_solver(GLPKBackend); p + <...sage.numerical.backends.glpk_backend.GLPKBackend...> + + Passing a callable that customizes a backend:: + + sage: def glpk_exact_solver(): + ....: from sage.numerical.backends.generic_backend import get_solver + ....: b = get_solver(solver="GLPK") + ....: b.solver_parameter("simplex_or_intopt", "exact_simplex_only") + ....: return b + sage: delsarte_bound_additive_hamming_space(11,3,4,solver=glpk_exact_solver) # long time + glp_exact... + ... + OPTIMAL SOLUTION FOUND + 8 + """ if solver is None: @@ -1368,6 +1602,12 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba if solver == "Coin" and constraint_generation: solver = "Glpk" + elif callable(solver): + kwds = {} + if base_ring is not None: + kwds['base_ring']=base_ring + return solver(**kwds) + else: solver = solver.capitalize() @@ -1400,4 +1640,4 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None, ba return InteractiveLPBackend(base_ring=base_ring) else: - raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', or None (in which case the default one is used).") + raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', None (in which case the default one is used), or a callable.") diff --git a/src/sage/numerical/backends/glpk_backend.pxd b/src/sage/numerical/backends/glpk_backend.pxd index 2ccbd67dc81..f09f2909741 100644 --- a/src/sage/numerical/backends/glpk_backend.pxd +++ b/src/sage/numerical/backends/glpk_backend.pxd @@ -26,7 +26,7 @@ cdef class GLPKBackend(GenericBackend): cdef glp_smcp * smcp cdef int simplex_or_intopt cdef search_tree_data_t search_tree_data - cpdef GLPKBackend copy(self) + cpdef __copy__(self) cpdef int print_ranges(self, char * filename = *) except -1 cpdef double get_row_dual(self, int variable) cpdef double get_col_dual(self, int variable) diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index d8a833eb2ec..1e629812cfd 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -34,6 +34,17 @@ include "cysignals/signals.pxi" cdef class GLPKBackend(GenericBackend): + """ + MIP Backend that uses the GLPK solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + def __cinit__(self, maximization = True): """ Constructor @@ -1600,7 +1611,7 @@ cdef class GLPKBackend(GenericBackend): """ glp_write_mps(self.lp, modern, NULL, filename) - cpdef GLPKBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. diff --git a/src/sage/numerical/backends/gurobi_backend.pxd b/src/sage/numerical/backends/gurobi_backend.pxd index 0b1025a4be3..560e3a61687 100644 --- a/src/sage/numerical/backends/gurobi_backend.pxd +++ b/src/sage/numerical/backends/gurobi_backend.pxd @@ -96,7 +96,7 @@ cdef class GurobiBackend(GenericBackend): cdef GRBenv * env cdef GRBenv * env_master cdef GRBmodel * model - cpdef GurobiBackend copy(self) + cpdef __copy__(self) cdef int num_vars diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index b025a8273a3..7420289582f 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -31,6 +31,18 @@ include "cysignals/memory.pxi" from sage.numerical.mip import MIPSolverException cdef class GurobiBackend(GenericBackend): + + """ + MIP Backend that uses the Gurobi solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="Gurobi") # optional - Gurobi + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - Gurobi + """ + def __init__(self, maximization = True): """ Constructor @@ -1159,7 +1171,7 @@ cdef class GurobiBackend(GenericBackend): else: raise RuntimeError("This should not happen.") - cpdef GurobiBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. diff --git a/src/sage/numerical/backends/interactivelp_backend.pxd b/src/sage/numerical/backends/interactivelp_backend.pxd index d3a8f2a0e49..7347e2ef7e7 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pxd +++ b/src/sage/numerical/backends/interactivelp_backend.pxd @@ -15,6 +15,7 @@ cdef class InteractiveLPBackend(GenericBackend): cdef object prob_name cdef object lp_std_form + cdef object std_form_transformation cdef object final_dictionary cdef int verbosity diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx index 578c77d328f..69442d8c4f4 100644 --- a/src/sage/numerical/backends/interactivelp_backend.pyx +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -1,5 +1,5 @@ r""" -COIN Backend +InteractiveLP Backend AUTHORS: @@ -22,6 +22,7 @@ AUTHORS: from sage.numerical.mip import MIPSolverException from sage.numerical.interactive_simplex_method import InteractiveLPProblem, default_variable_name from sage.modules.all import vector +from copy import copy cdef class InteractiveLPBackend: """ @@ -38,6 +39,14 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="InteractiveLP") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ def __cinit__(self, maximization = True, base_ring = None): @@ -52,19 +61,22 @@ cdef class InteractiveLPBackend: This backend can work with irrational algebraic numbers:: sage: poly = polytopes.dodecahedron(base_ring=AA) - sage: lp = poly.to_linear_program(solver='InteractiveLP') - sage: b = lp.get_backend() - sage: for k in range(3): b.variable_lower_bound(k, 0) - sage: b.set_objective([1, 1, 1]) + sage: lp, x = poly.to_linear_program(solver='InteractiveLP', return_variable=True) + sage: lp.set_objective(x[0] + x[1] + x[2]) sage: lp.solve() 2.291796067500631? - sage: [b.get_variable_value(k) for k in range(3)] + sage: lp.get_values(x[0], x[1], x[2]) [0.763932022500211?, 0.763932022500211?, 0.763932022500211?] + sage: lp.set_objective(x[0] - x[1] - x[2]) + sage: lp.solve() + 2.291796067500631? + sage: lp.get_values(x[0], x[1], x[2]) + [0.763932022500211?, -0.763932022500211?, -0.763932022500211?] """ if base_ring is None: - from sage.rings.all import AA - base_ring = AA + from sage.rings.all import QQ + base_ring = QQ self.lp = InteractiveLPProblem([], [], [], base_ring=base_ring) self.set_verbosity(0) @@ -74,10 +86,31 @@ cdef class InteractiveLPBackend: else: self.set_sense(-1) - self.obj_constant_term = 0 - self.row_names = [] + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "InteractiveLP") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6 + """ + cp = InteractiveLPBackend(base_ring=self.base_ring()) + cp.lp = self.lp # it's considered immutable; so no need to copy. + cp.row_names = copy(self.row_names) + cp.prob_name = self.prob_name + return cp + cpdef base_ring(self): """ Return the base ring. @@ -91,7 +124,7 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") sage: p.base_ring() - Algebraic Real Field + Rational Field """ return self.lp.base_ring() @@ -196,7 +229,7 @@ cdef class InteractiveLPBackend: sage: p.objective_coefficient(1) 1 """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() cdef int vtype = int(binary) + int(continuous) + int(integer) if vtype == 0: continuous = True @@ -219,7 +252,7 @@ cdef class InteractiveLPBackend: x = tuple(x) + (name,) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) return self.ncols() - 1 cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: @@ -298,7 +331,7 @@ cdef class InteractiveLPBackend: else: raise NotImplementedError() - def _AbcxCVPR(self): + def _AbcxCVPRd(self): """ Retrieve all problem data from the LP. @@ -306,15 +339,16 @@ cdef class InteractiveLPBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "InteractiveLP") - sage: p._AbcxCVPR() - ([], (), (), (), (), (), 'max', Algebraic Real Field) + sage: p._AbcxCVPRd() + ([], (), (), (), (), (), 'max', Rational Field, 0) """ A, b, c, x = self.lp.Abcx() constraint_types = self.lp.constraint_types() variable_types = self.lp.variable_types() problem_type = self.lp.problem_type() base_ring = self.lp.base_ring() - return A, b, c, x, constraint_types, variable_types, problem_type, base_ring + d = self.lp.objective_constant_term() + return A, b, c, x, constraint_types, variable_types, problem_type, base_ring, d cpdef set_sense(self, int sense): """ @@ -337,14 +371,14 @@ cdef class InteractiveLPBackend: sage: p.is_maximization() False """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() if sense == +1: problem_type = "max" else: problem_type = "min" self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef objective_coefficient(self, int variable, coeff=None): """ @@ -372,12 +406,38 @@ cdef class InteractiveLPBackend: if coeff is None: return self.lp.objective_coefficients()[variable] else: - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() c = list(c) c[variable] = coeff self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) + + cpdef objective_constant_term(self, d=None): + """ + Set or get the constant term in the objective function + + INPUT: + + - ``d`` (double) -- its coefficient. If `None` (default), return the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.objective_constant_term() + 0 + sage: p.objective_constant_term(42) + sage: p.objective_constant_term() + 42 + """ + if d is None: + return self.lp.objective_constant_term() + else: + A, b, c, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) cpdef set_objective(self, list coeff, d = 0): """ @@ -423,12 +483,11 @@ cdef class InteractiveLPBackend: -47/5 """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, _, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() c = coeff self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) - self.obj_constant_term = d + problem_type, ring, objective_constant_term=d) cpdef set_verbosity(self, int level): """ @@ -470,13 +529,13 @@ cdef class InteractiveLPBackend: sage: p.get_values([x,y]) [0, 3] """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() A = A.delete_rows((i,)) b = list(b); del b[i] constraint_types=list(constraint_types); del constraint_types[i] self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): """ @@ -511,7 +570,7 @@ cdef class InteractiveLPBackend: sage: p.row_name(1) 'foo' """ - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() if lower_bound is None: if upper_bound is None: raise ValueError("At least one of lower_bound and upper_bound must be provided") @@ -537,7 +596,7 @@ cdef class InteractiveLPBackend: self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef add_col(self, list indices, list coeffs): @@ -633,7 +692,8 @@ cdef class InteractiveLPBackend: """ ## FIXME: standard_form should allow to pass slack names (which we would take from row_names). ## FIXME: Perhaps also pass the problem name as objective name - lp_std_form = self.lp_std_form = self.lp.standard_form() + lp_std_form, transformation = self.lp.standard_form(transformation=True) + self.lp_std_form, self.std_form_transformation = lp_std_form, transformation output = lp_std_form.run_revised_simplex_method() ## FIXME: Display output as a side effect if verbosity is high enough d = self.final_dictionary = lp_std_form.final_revised_dictionary() @@ -674,7 +734,7 @@ cdef class InteractiveLPBackend: v = d.objective_value() if self.lp_std_form.is_negative(): v = - v - return self.obj_constant_term + v + return v cpdef get_variable_value(self, int variable): """ @@ -701,9 +761,8 @@ cdef class InteractiveLPBackend: sage: p.get_variable_value(1) 3/2 """ - if str(self.lp.decision_variables()[variable]) != str(self.lp_std_form.decision_variables()[variable]): - raise NotImplementedError("Undoing the standard-form transformation is not implemented") - return self.final_dictionary.basic_solution()[variable] + solution = self.std_form_transformation(self.final_dictionary.basic_solution()) + return solution[variable] cpdef int ncols(self): """ @@ -1027,12 +1086,12 @@ cdef class InteractiveLPBackend: else: if value != bounds[1]: bounds = (bounds[0], value) - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() variable_types = list(variable_types) variable_types[index] = self._variable_type_from_bounds(*bounds) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef variable_lower_bound(self, int index, value = False): """ @@ -1071,12 +1130,12 @@ cdef class InteractiveLPBackend: else: if value != bounds[0]: bounds = (value, bounds[1]) - A, b, c, x, constraint_types, variable_types, problem_type, ring = self._AbcxCVPR() + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() variable_types = list(variable_types) variable_types[index] = self._variable_type_from_bounds(*bounds) self.lp = InteractiveLPProblem(A, b, c, x, constraint_types, variable_types, - problem_type, ring) + problem_type, ring, objective_constant_term=d) cpdef bint is_variable_basic(self, int index): """ diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index c82691cdc52..73d022d4052 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -23,8 +23,18 @@ from sage.numerical.mip import MIPSolverException from sage.libs.ppl import MIP_Problem, Variable, Variables_Set, Linear_Expression, Constraint, Generator from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational +from copy import copy cdef class PPLBackend(GenericBackend): + + """ + TESTS:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "PPL") + sage: TestSuite(p).run(skip="_test_pickling") + """ + cdef object mip cdef list Matrix cdef list row_lower_bound @@ -89,6 +99,39 @@ cdef class PPLBackend(GenericBackend): cpdef zero(self): return self.base_ring()(0) + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "PPL") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6 + """ + cp = PPLBackend() + cp.Matrix = [row[:] for row in self.Matrix] + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + cp.objective_function = self.objective_function[:] + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + cp.name = self.name + cp.obj_constant_term = self.obj_constant_term + cp.obj_denominator = self.obj_denominator + cp.integer_variables = copy(self.integer_variables) + cp.is_maximize = self.is_maximize + return cp + def init_mip(self): """ Converting the matrix form of the MIP Problem to PPL MIP_Problem. diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 65716aa6bca..0d4324881dd 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -267,6 +267,10 @@ cdef class MixedIntegerLinearProgram(SageObject): - If ``solver=None`` (default), the default solver is used (see :func:`default_mip_solver`) + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. + - ``maximization`` - When set to ``True`` (default), the ``MixedIntegerLinearProgram`` @@ -358,8 +362,12 @@ cdef class MixedIntegerLinearProgram(SageObject): - PPL (``solver="PPL"``). See the `PPL `_ web site. - -If ``solver=None`` (default), the default solver is used (see - ``default_mip_solver`` method. + - If ``solver=None`` (default), the default solver is used, see + :func:`default_mip_solver`. + + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. - ``maximization`` @@ -536,8 +544,11 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: q.number_of_constraints() 1 """ + def copying_solver(**kwdargs): + return ( self._backend).copy() + cdef MixedIntegerLinearProgram p = \ - MixedIntegerLinearProgram(solver="GLPK") + MixedIntegerLinearProgram(solver=copying_solver) try: p._variables = copy(self._variables) except AttributeError: @@ -554,9 +565,37 @@ cdef class MixedIntegerLinearProgram(SageObject): except AttributeError: pass - p._backend = ( self._backend).copy() return p + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram() + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = deepcopy(p) + sage: cp.solve() + 6.0 + + TEST: + + Test that `deepcopy` makes actual copies but preserves identities:: + + sage: mip = MixedIntegerLinearProgram() + sage: ll = [mip, mip] + sage: dcll=deepcopy(ll) + sage: ll[0] is dcll[0] + False + sage: dcll[0] is dcll[1] + True + + """ + return self.__copy__() + def __getitem__(self, v): r""" Returns the symbolic variable corresponding to the key @@ -598,7 +637,8 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='ppl') sage: p.base_ring() Rational Field - sage: p = MixedIntegerLinearProgram(solver='InteractiveLP') + sage: from sage.rings.all import AA + sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', base_ring=AA) sage: p.base_ring() Algebraic Real Field sage: d = polytopes.dodecahedron() diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py index 174703ca40d..ea0d96ccfa6 100644 --- a/src/sage/repl/image.py +++ b/src/sage/repl/image.py @@ -39,7 +39,7 @@ class Image(SageObject): - def __init__(self, mode, size, color=0): + def __init__(self, mode, size, color='white'): """ Creates a new image with the given mode and size. @@ -76,13 +76,12 @@ def __init__(self, mode, size, color=0): - ``size`` -- 2-tuple, containing (width, height) in pixels. - - ``color`` -- string or numeric. What colour to use for the - image. Default is black. If given, this should be a single - integer or floating point value for single-band modes, and a - tuple for multi-band modes (one value per band). When - creating RGB images, you can also use colour strings as - supported by the ImageColor module. If the colour is None, - the image is not initialised. + - ``color`` -- string or tuple of numeric. What colour to use + for the image. Default is black. If given, this should be a + a tuple with one value per band. When creating RGB images, + you can also use colour strings as supported by the + ImageColor module. If the colour is None, the image is not + initialised. OUTPUT: @@ -91,7 +90,7 @@ def __init__(self, mode, size, color=0): EXAMPLES:: sage: from sage.repl.image import Image - sage: Image('P', (16, 16), 13) + sage: Image('P', (16, 16), (13,)) 16x16px 8-bit Color image """ self._pil = PIL.Image.new(mode, size, color) @@ -233,7 +232,7 @@ def save(self, filename): EXAMPLES:: sage: from sage.repl.image import Image - sage: img = Image('P', (12, 34), 13) + sage: img = Image('P', (12, 34), (13,)) sage: filename = tmp_filename(ext='.png') sage: img.save(filename) sage: open(filename).read().startswith('\x89PNG') diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index d3fa97482a2..26372689f13 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -751,13 +751,17 @@ def subfactors(F): 'no common parent was found, and ' 'splitting the factors was unsuccessful.' % (self, other, var)) - + # A wrapper around an iterator that stores additional intermediate data. + # This deviates slightly from the iterator protocol: + # At the end of the iteration the data is reset to None instead + # of raising a StopIteration. class it: def __init__(self, it): self.it = it self.var = None self.factors = None - def next(self): + + def next_custom(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) @@ -772,24 +776,24 @@ def next(self): newS = [] newO = [] - S.next() - O.next() + S.next_custom() + O.next_custom() while S.var is not None or O.var is not None: if S.var is not None and S.var < O.var: newS.extend(S.factors) newO.extend(S.factors) - S.next() + S.next_custom() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) - O.next() + O.next_custom() else: SL, OL = pushout_univariate_factors(self, other, S.var, S.factors, O.factors) newS.extend(SL) newO.extend(OL) - S.next() - O.next() + S.next_custom() + O.next_custom() assert(len(newS) == len(newO)) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 79b78908316..895107646c4 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -137,6 +137,8 @@ import sage.categories.fields cimport sage.rings.integer cimport sage.rings.rational +import sage.rings.number_field.number_field as number_field + from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -148,13 +150,14 @@ from sage.libs.arb.acb_hypgeom cimport * from sage.libs.arb.acb_modular cimport * from sage.libs.arb.arf cimport arf_init, arf_get_mpfr, arf_set_mpfr, arf_clear, arf_set_mag, arf_set from sage.libs.arb.mag cimport mag_init, mag_clear, mag_add, mag_set_d, MAG_BITS, mag_is_inf, mag_is_finite, mag_zero -from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear +from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_abs from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.integer_ring import ZZ -from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi +from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic +from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi, real_part_of_quadratic_element_to_arb from sage.rings.real_arb import RealBallField from sage.rings.real_mpfr cimport RealField_class, RealField, RealNumber from sage.rings.ring import Field @@ -428,6 +431,10 @@ class ComplexBallField(UniqueRepresentation, Field): True sage: CBF.has_coerce_map_from(RealBallField(52)) False + sage: CBF.has_coerce_map_from(QuadraticField(-2)) + True + sage: CBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False Check that there are no coercions from interval or floating-point parents:: @@ -442,6 +449,10 @@ class ComplexBallField(UniqueRepresentation, Field): """ if isinstance(other, (RealBallField, ComplexBallField)): return (other._prec >= self._prec) + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, x=None, y=None): r""" @@ -484,6 +495,9 @@ class ComplexBallField(UniqueRepresentation, Field): [0.3333333333333333 +/- 7.04e-17] + [0.1666666666666667 +/- 7.04e-17]*I sage: ComplexBallField(106)(1/3, 1/6) [0.33333333333333333333333333333333 +/- 6.94e-33] + [0.16666666666666666666666666666666 +/- 7.70e-33]*I + sage: NF. = QuadraticField(-2) + sage: CBF(1/5 + a/2) + [0.2000000000000000 +/- 4.45e-17] + [0.707106781186547 +/- 5.86e-16]*I sage: CBF(infinity, NaN) [+/- inf] + nan*I sage: CBF(x) @@ -500,7 +514,7 @@ class ComplexBallField(UniqueRepresentation, Field): sage: CBF(1+I, 2) Traceback (most recent call last): ... - TypeError: unable to convert I + 1 to a RealBall + ValueError: nonzero imaginary part """ try: return self.element_class(self, x, y) @@ -694,9 +708,19 @@ cdef class ComplexBall(RingElement): Traceback (most recent call last): ... TypeError: unsupported initializer + sage: NF. = QuadraticField(-1, embedding=CC(0, -1)) + sage: CBF(a) + -1.000000000000000*I + sage: NF. = QuadraticField(-1, embedding=None) + sage: CBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding """ cdef fmpz_t tmpz cdef fmpq_t tmpq + cdef NumberFieldElement_quadratic x_as_qe + cdef long myprec RingElement.__init__(self, parent) @@ -726,6 +750,25 @@ cdef class ComplexBall(RingElement): elif isinstance(x, ComplexIntervalFieldElement): ComplexIntervalFieldElement_to_acb(self.value, x) + elif isinstance(x, NumberFieldElement_quadratic): + x_as_qe = x + real_part_of_quadratic_element_to_arb(acb_realref(self.value), + x_as_qe, prec(self)) + myprec = prec(self) + 4 + if mpz_sgn(x_as_qe.D.value) < 0: + if x_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + fmpz_init(tmpz) + fmpz_set_mpz(tmpz, x_as_qe.D.value) + fmpz_abs(tmpz, tmpz) + arb_sqrt_fmpz(acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.b) + arb_mul_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.denom) + arb_div_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, prec(self)) + fmpz_clear(tmpz) + if not x_as_qe.standard_embedding: + acb_conj(self.value, self.value) else: raise TypeError("unsupported initializer") elif isinstance(x, RealBall) and isinstance(y, RealBall): diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index f4c659bbfa9..6aff8028da6 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -55,6 +55,8 @@ cimport real_mpfi cimport real_mpfr from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.mpfr cimport MPFR_RNDU, MPFR_RNDD + cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) @@ -778,38 +780,22 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._div_(CIF(1,-2)) 1.600000000000000? + 0.200000000000000?*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() - cdef mpfi_t a, b, t0, t1, right_nm - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) - mpfi_init2(a, self._prec) - mpfi_init2(b, self._prec) - mpfi_init2(right_nm, self._prec) - - mpfi_sqr(t0, (right).__re) - mpfi_sqr(t1, (right).__im) - mpfi_add(right_nm, t0, t1) - - mpfi_div(a, (right).__re, right_nm) - mpfi_div(b, (right).__im, right_nm) - - ## Do this: x.__re = a * self.__re + b * self.__im - mpfi_mul(t0, a, self.__re) - mpfi_mul(t1, b, self.__im) - mpfi_add(x.__re, t0, t1) - - ## Do this: x.__im = a * self.__im - b * self.__re - mpfi_mul(t0, a, self.__im) - mpfi_mul(t1, b, self.__re) - mpfi_sub(x.__im, t0, t1) - mpfi_clear(t0) - mpfi_clear(t1) - mpfi_clear(a) - mpfi_clear(b) - mpfi_clear(right_nm) - return x + sage: a = CIF((1, 2), (3, 4)) + sage: b = CIF(-1, (2, 3)) + sage: c = a/b + sage: c.endpoints() + (0.500000000000000 - 1.60000000000000*I, + 1.50000000000000 - 0.600000000000000*I, + 0.500000000000000 - 0.600000000000000*I, + 1.50000000000000 - 1.60000000000000*I) + sage: c = b/a + sage: c.endpoints() + (0.246153846153846 + 0.317647058823529*I, + 0.841176470588236 + 0.761538461538462*I, + 0.246153846153846 + 0.761538461538462*I, + 0.841176470588236 + 0.317647058823529*I) + """ + return self * right.__invert__() def __rdiv__(self, left): """ @@ -818,7 +804,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): EXAMPLES:: sage: CIF(2,-3).__rdiv__(CIF(1,-2)) - 0.6153846153846154? - 0.0769230769230769?*I + 0.6153846153846154? - 0.0769230769230770?*I """ return ComplexIntervalFieldElement(self._parent, left)/self @@ -1130,26 +1116,198 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: I = CIF.0 sage: a = ~(5+I) # indirect doctest sage: a * (5+I) - 1.000000000000000? + 0.?e-16*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() + 1.000000000000000? + -1.?e-16*I + sage: a = CIF((1, 2), (3, 4)) + sage: c = a.__invert__() + sage: c.endpoints() + (0.0588235294117647 - 0.300000000000000*I, + 0.153846153846154 - 0.200000000000000*I, + 0.0588235294117647 - 0.200000000000000*I, + 0.153846153846154 - 0.300000000000000*I) - cdef mpfi_t t0, t1 - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) + TESTS: - mpfi_sqr(t0, self.__re) - mpfi_sqr(t1, self.__im) + Check that the code is valid in all kind of complex intervals:: - mpfi_add(t0, t0, t1) # now t0 is the norm - mpfi_div(x.__re, self.__re, t0) # x.__re = self.__re/norm + sage: rpts = [0, -194323/42, -110/439423, -411923/122212, \ + ....: 15423/906, 337/59976, 145151/145112] + sage: rpts = [RIF(a, b) if a <= b else RIF(b,a) \ + ....: for a in rpts for b in rpts] + sage: cpts = [CIF(a, b) for a in rpts for b in rpts if not CIF(a, b).contains_zero()] + sage: for x in cpts: + ....: assert (x * (~x) - 1).contains_zero() - mpfi_neg(t1, self.__im) - mpfi_div(x.__im, t1, t0) # x.__im = -self.__im/norm + REFERENCES: - mpfi_clear(t0) - mpfi_clear(t1) + .. [RL] J. Rokne, P. Lancaster. Complex interval arithmetic. + Communications of the ACM 14. 1971. + """ + + cdef ComplexIntervalFieldElement x + x = self._new() + + cdef mpfr_t a, b, c, d + mpfr_init2(a, self._prec) + mpfr_init2(b, self._prec) + mpfr_init2(c, self._prec) + mpfr_init2(d, self._prec) + + cdef mpfr_t rmin, rmax, imin, imax + mpfr_init2(rmin, self._prec) + mpfr_init2(rmax, self._prec) + mpfr_init2(imin, self._prec) + mpfr_init2(imax, self._prec) + + cdef mpfr_t r + mpfr_init2(r, self._prec) + + mpfi_get_left(a, self.__re) + mpfi_get_right(b, self.__re) + mpfi_get_left(c, self.__im) + mpfi_get_right(d, self.__im) + + cdef mpfr_t a2, b2, d2, c2 + mpfr_init2(a2, self._prec) + mpfr_init2(b2, self._prec) + mpfr_init2(c2, self._prec) + mpfr_init2(d2, self._prec) + + cdef mpfr_t div1, div2, aux, aux2 + mpfr_init2(div1, self._prec) + mpfr_init2(div2, self._prec) + mpfr_init2(aux, self._prec) + mpfr_init2(aux2, self._prec) + + if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0: #input interval lies in first quadrant + # left endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_add(div2, b2, d2, MPFR_RNDU) + mpfr_div(rmin, a, div1, MPFR_RNDD) + mpfr_div(aux, b, div2, MPFR_RNDD) + mpfr_min(rmin, rmin, aux, MPFR_RNDD) + #higher endpoint + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + mpfr_div(aux2, d, div2, MPFR_RNDU) + mpfr_sub(aux2, aux, aux2, MPFR_RNDU) + mpfr_max(imax, aux2, imax, MPFR_RNDU) + # lower endpoint, it is the lowest point of the circle or one of + if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0: + mpfr_add(imin, a, a, MPFR_RNDD) + mpfr_set_si(aux, -1, MPFR_RNDD) + mpfr_div(imin, aux, imin, MPFR_RNDD) + elif mpfr_cmp(c, a) > 0: + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(imin, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + else: + mpfr_mul(d2, d, d, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDD) + mpfr_div(imin, d, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + #right endpoint + if mpfr_cmp(c, a) >=0 and mpfr_cmp(b, c) >= 0: + mpfr_add(rmax, c, c, MPFR_RNDD) + mpfr_set_si(aux, 1, MPFR_RNDU) + mpfr_div(rmax, aux, rmax, MPFR_RNDU) + elif mpfr_cmp(a,c) > 0: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmax, a, div1, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + elif mpfr_sgn(c) > 0 and mpfr_sgn(b) > 0: #between first and second quadrant + # left endpoint + mpfr_abs(aux, a, MPFR_RNDU) + if mpfr_cmp(aux, c) >= 0: + mpfr_set_str(aux, '-0.5', 10, MPFR_RNDD) + mpfr_div(rmin, aux, c, MPFR_RNDD) + else: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmin, a, div1, MPFR_RNDU) + # lower endpoint + mpfr_set_si(aux2, -1, MPFR_RNDD) + mpfr_div(imin, aux2, c, MPFR_RNDD) + #right endpoint + if mpfr_cmp(b, c) >=0: + mpfr_set_str(aux2, '0.5', 10, MPFR_RNDU) + mpfr_div(rmax, aux2, c, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + # upper endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(aux, c, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, b2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_set_zero(aux, -1) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + elif mpfr_sgn(b) <= 0 and mpfr_sgn(d) >=0: #second quadrant or between second and thirthd + I = self.parent().gen(0) + return -I*(-I*self).__invert__() + elif mpfr_sgn(a) <= 0 and mpfr_sgn(d) <= 0: # thirthd quadrant or between thirthd and fourth + return -(-self).__invert__() + elif mpfr_sgn(a) >=0: #fourth or between fourth and first + I = self.parent().gen(0) + return I*(I*self).__invert__() + + + mpfi_set_fr(x.__re, rmin) + mpfi_put_fr(x.__re, rmax) + + mpfi_set_fr(x.__im, imin) + mpfi_put_fr(x.__im, imax) + + mpfr_clear(a) + mpfr_clear(b) + mpfr_clear(c) + mpfr_clear(d) + mpfr_clear(imin) + mpfr_clear(imax) + mpfr_clear(rmin) + mpfr_clear(rmax) + mpfr_clear(r) + mpfr_clear(a2) + mpfr_clear(b2) + mpfr_clear(c2) + mpfr_clear(d2) + mpfr_clear(div1) + mpfr_clear(div2) + mpfr_clear(aux) + mpfr_clear(aux2) return x @@ -1766,7 +1924,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1,1).tan() 0.27175258531952? + 1.08392332733870?*I sage: CIF(2).tan() - -2.18503986326152? + -2.185039863261519? sage: CIF(0,2).tan() 0.964027580075817?*I """ @@ -1855,7 +2013,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2).tanh() 0.964027580075817? sage: CIF(0,2).tanh() - -2.18503986326152?*I + -2.185039863261519?*I """ return self.sinh() / self.cosh() diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index c5065d40638..4fa3e73e8f8 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -4198,7 +4198,16 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: R. = K[] sage: f = (a + 1)*x^145*y^84 + (a + 1)*x^205*y^17 + x^32*y^112 + x^92*y^45 sage: for i in range(100): - ... assert len(f.factor()) == 4 + ....: assert len(f.factor()) == 4 + + Test for :trac:`20435`:: + + sage: x,y = polygen(ZZ,'x,y') + sage: p = x**2-y**2 + sage: z = factor(p); z + (x - y) * (x + y) + sage: z[0][0].parent() + Multivariate Polynomial Ring in x, y over Integer Ring """ cdef ring *_ring = self._parent_ring cdef poly *ptemp @@ -4217,7 +4226,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn try: frac_field = self._parent._base.fraction_field() F = self.change_ring(frac_field).factor() - FF = [(f[0].change_ring(self._parent), f[1]) for f in F] + FF = [(self._parent(f[0]), f[1]) for f in F] U = self._parent._base(F.unit()).factor() return Factorization(list(U) + FF, unit=U.unit()) except Exception: diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 2ca1ff7ea79..d19c435d522 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -5266,6 +5266,90 @@ cdef class Polynomial(CommutativeAlgebraElement): return slopes + def dispersion_set(self, other=None): + r""" + Compute the dispersion set of two polynomials. + + The dispersion set of `f` and `g` is the set of nonnegative integers + `n` such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion set of + ``self``, i.e., its dispersion set with itself. + + ALGORITHM: + + See Section 4 of Man & Wright [ManWright1994]_. + + .. [ManWright1994] Yiu-Kwong Man and Francis J. Wright. + *Fast Polynomial Dispersion Computation and its Application to + Indefinite Summation*. ISSAC 1994. + + .. SEEALSO:: :meth:`dispersion` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion_set(x + 1) + [1] + sage: (x + 1).dispersion_set(x) + [] + + sage: pol = x^3 + x - 7 + sage: (pol*pol(x+3)^2).dispersion_set() + [0, 3] + """ + other = self if other is None else self._parent.coerce(other) + x = self._parent.gen() + dispersions = set() + for p, _ in self.factor(): + # need both due to the semantics of is_primitive() over fields + assert p.is_monic() or p.is_primitive() + for q, _ in other.factor(): + m, n = p.degree(), q.degree() + assert q.is_monic() or q.is_primitive() + if m != n or p[n] != q[n]: + continue + alpha = (q[n-1] - p[n-1])/(n*p[n]) + if alpha.is_integer(): # ZZ() might work for non-integers... + alpha = ZZ(alpha) + else: + continue + if alpha < 0 or alpha in dispersions: + continue + if n >= 1 and p(x + alpha) != q: + continue + dispersions.add(alpha) + return list(dispersions) + + def dispersion(self, other=None): + r""" + Compute the dispersion of a pair of polynomials. + + The dispersion of `f` and `g` is the largest nonnegative integer `n` + such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion of ``self``, + i.e., its dispersion with itself. + + .. SEEALSO:: :meth:`dispersion_set` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion(x + 1) + 1 + sage: (x + 1).dispersion(x) + -Infinity + + sage: Pol. = QQbar[] + sage: pol = Pol([sqrt(5), 1, 3/2]) + sage: pol.dispersion() + 0 + sage: (pol*pol(x+3)).dispersion() + 3 + """ + dispersions = self.dispersion_set(other) + return max(dispersions) if len(dispersions) > 0 else infinity.minus_infinity ##################################################################### # Conversions to other systems @@ -8361,8 +8445,13 @@ cdef class Polynomial_generic_dense(Polynomial): Returns the quotient and remainder of the Euclidean division of ``self`` and ``other``. - Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if ``other`` has - a nonunit leading coefficient. + Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if the division is not exact. + + AUTHORS: + + - Kwankyu Lee (2013-06-02) + + - Bruno Grenet (2014-07-13) EXAMPLES:: @@ -8377,17 +8466,31 @@ cdef class Polynomial_generic_dense(Polynomial): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): ... ZeroDivisionError: Division by zero polynomial + + TESTS: + + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = QQ[] + sage: R. = P[] + sage: f = (2*x^3+1)*y^2 + (x^2-x+3)*y + (3*x+2) + sage: g = (-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1 + sage: h = f * g + sage: h.quo_rem(f) + ((-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1, 0) + sage: h += (2/3*x^2-3*x+1)*y + 7/17*x+6/5 + sage: q,r = h.quo_rem(f) + sage: h == q*f + r and r.degree() < f.degree() + True """ if other.is_zero(): raise ZeroDivisionError("Division by zero polynomial") - if not other.leading_coefficient().is_unit(): - raise ArithmeticError("Nonunit leading coefficient") if self.is_zero(): return self, self @@ -8401,7 +8504,10 @@ cdef class Polynomial_generic_dense(Polynomial): quo = list() for k from m-n >= k >= 0: - q = x[n+k-1]/y[n-1] + try: + q = R(x[n+k-1]/y[n-1]) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") x[n+k-1] = R.zero() for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index f0b3b8c9082..4869659028e 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -773,7 +773,7 @@ def quo_rem(self, other): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): @@ -802,14 +802,18 @@ def quo_rem(self, other): sage: g == f*q + r and r.degree() < f.degree() True + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = PolynomialRing(ZZ, sparse=True) + sage: (4*x).quo_rem(2*x) + (2, 0) + AUTHORS: - Bruno Grenet (2014-07-09) """ if other.is_zero(): raise ZeroDivisionError("Division by zero polynomial") - if not other.leading_coefficient().is_unit(): - raise ArithmeticError("Nonunit leading coefficient") if self.is_zero(): return self, self @@ -821,11 +825,12 @@ def quo_rem(self, other): quo = R.zero() rem = self - inv_lc = R.base_ring().one()/other.leading_coefficient() while rem.degree() >= d: - - c = rem.leading_coefficient()*inv_lc + try: + c = R(rem.leading_coefficient() * ~other.leading_coefficient()) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") e = rem.degree() - d quo += c*R.one().shift(e) # we know that the leading coefficient of rem vanishes diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index ab81f6f7148..2b394fb3487 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -3678,8 +3678,8 @@ def __cmp__(self, other): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-182 - 0.7598602580415435?*I, - 0.?e-249 + 0.7598602580415435?*I, + 0.?e-215 - 0.7598602580415435?*I, + 0.?e-229 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] diff --git a/src/sage/rings/real_arb.pxd b/src/sage/rings/real_arb.pxd index 7f39f9c4efd..34d2407cde9 100644 --- a/src/sage/rings/real_arb.pxd +++ b/src/sage/rings/real_arb.pxd @@ -1,3 +1,5 @@ +cimport sage.rings.number_field.number_field_element_quadratic as nfeq + from sage.libs.arb.arb cimport arb_t from sage.libs.mpfi cimport mpfi_t from sage.rings.real_mpfi cimport RealIntervalField_class, RealIntervalFieldElement @@ -6,6 +8,7 @@ from sage.structure.element cimport RingElement cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision) cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except -1 +cdef int real_part_of_quadratic_element_to_arb(arb_t res, nfeq.NumberFieldElement_quadratic x, const long prec) except -1 cdef class RealBall(RingElement): cdef arb_t value diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index fd9ab476294..f97557eab33 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -189,7 +189,6 @@ Classes and Methods include "cysignals/signals.pxi" - from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -208,7 +207,7 @@ from sage.libs.flint.fmpz cimport ( fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_fdiv_ui ) from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.libs.mpfi cimport mpfi_get_left, mpfi_get_right, mpfi_interv_fr from sage.libs.mpfr cimport mpfr_t, mpfr_init2, mpfr_clear, mpfr_sgn, MPFR_PREC_MIN, mpfr_equal_p from sage.libs.mpfr cimport MPFR_RNDN, MPFR_RNDU, MPFR_RNDD, MPFR_RNDZ @@ -222,6 +221,8 @@ from sage.structure.element cimport Element, ModuleElement, RingElement import operator import sage.categories.fields +import sage.rings.number_field.number_field as number_field + from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_mpfi import RealIntervalField, RealIntervalField_class @@ -304,6 +305,55 @@ cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except - mpfr_clear(left) mpfr_clear(right) +cdef int real_part_of_quadratic_element_to_arb(arb_t res, + nfeq.NumberFieldElement_quadratic x, const long prec) except -1: + r""" + Convert the real part of a quadratic element to an arb object of type + ``arb_t``. + + This function does *not* check that the parent has a real or complex + embedding. + + TESTS:: + + sage: NF. = QuadraticField(2) + sage: a = (sqrt2 - 1)^1000 + sage: RBF(a) + [1.676156872756536e-383 +/- 4.39e-399] + + sage: NF. = QuadraticField(-2) + sage: CBF(1/3 + a).real() + [0.3333333333333333 +/- 7.04e-17] + """ + cdef fmpz_t tmpz + cdef arb_t rootD + cdef long myprec = prec + 6 + fmpz_init(tmpz) + arb_init(rootD) + while True: # a-b√D might cancel + fmpz_set_mpz(tmpz, x.a) + arb_set_fmpz(res, tmpz) + if mpz_sgn(x.D.value) > 0: + if _do_sig(myprec): sig_on() + fmpz_set_mpz(tmpz, x.D.value) + arb_sqrt_fmpz(rootD, tmpz, myprec) + fmpz_set_mpz(tmpz, x.b) + if x.standard_embedding: + arb_addmul_fmpz(res, rootD, tmpz, myprec) + else: + arb_submul_fmpz(res, rootD, tmpz, myprec) + if _do_sig(myprec): sig_off() + if arb_rel_accuracy_bits(res) < prec - 4: + myprec *= 2 + continue + break + if _do_sig(myprec): sig_on() + fmpz_set_mpz(tmpz, x.denom) + arb_div_fmpz(res, res, tmpz, prec) + arb_clear(rootD) + fmpz_clear(tmpz) + if _do_sig(myprec): sig_off() + return 0 class RealBallField(UniqueRepresentation, Field): r""" @@ -436,11 +486,19 @@ class RealBallField(UniqueRepresentation, Field): False sage: RealBallField().has_coerce_map_from(RR) False + sage: RBF.has_coerce_map_from(QuadraticField(2)) + True + sage: RBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False + sage: RBF.has_coerce_map_from(QuadraticField(-2)) + False """ if isinstance(other, RealBallField): return (other._prec >= self._prec) - else: - return False + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, mid=None, rad=None): """ @@ -1073,6 +1131,12 @@ cdef class RealBall(RingElement): sage: RBF(pi, 0.125r) [3e+0 +/- 0.267] + :: + + sage: NF. = QuadraticField(2) + sage: RBF(1/5 + sqrt2/2) + [0.907106781186547 +/- 5.33e-16] + Note that integers and floating-point numbers are ''not'' rounded to the parent's precision:: @@ -1151,12 +1215,39 @@ cdef class RealBall(RingElement): ... ValueError: unsupported string format + sage: NF. = QuadraticField(2) + sage: RBF.coerce(a) + [1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=-1.4) + sage: RBF(a) + [-1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=None) + sage: RBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding + sage: RBF.coerce(a) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... + sage: QQi. = QuadraticField(-1) + sage: RBF(QQi(3)) + 3.000000000000000 + sage: RBF(i) + Traceback (most recent call last): + ... + ValueError: nonzero imaginary part + sage: RBF.coerce(QQi(3)) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... """ import sage.symbolic.constants cdef fmpz_t tmpz cdef fmpq_t tmpq cdef arf_t tmpr cdef mag_t tmpm + cdef nfeq.NumberFieldElement_quadratic mid_as_qe Element.__init__(self, parent) @@ -1188,6 +1279,14 @@ cdef class RealBall(RingElement): arb_set_arf(self.value, tmpr) # no rounding! arf_clear(tmpr) if _do_sig(prec(self)): sig_off() + elif isinstance(mid, nfeq.NumberFieldElement_quadratic): + mid_as_qe = mid + if mpz_sgn(mid_as_qe.b) != 0: + if mpz_sgn(mid_as_qe.D.value) < 0: + raise ValueError("nonzero imaginary part") + elif mid_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + real_part_of_quadratic_element_to_arb(self.value, mid_as_qe, prec(self)) elif isinstance(mid, sage.rings.infinity.AnInfinity): if isinstance(mid, sage.rings.infinity.PlusInfinity): arb_pos_inf(self.value) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index d7928e01f5d..a2c11b3ef66 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -607,7 +607,7 @@ def __init__(self, g, sink=None): # create digraph and initialize some variables DiGraph.__init__(self,g,weighted=True) self._dict = deepcopy(g) - if sink==None: + if sink is None: sink = self.vertices()[0] self._sink = sink # key for sink self._sink_ind = self.vertices().index(sink) @@ -2136,7 +2136,7 @@ def stable_configs(self, smax=None): sage: s = sandpiles.Complete(3) sage: a = s.stable_configs() - sage: a.next() + sage: next(a) {1: 0, 2: 0} sage: [i.values() for i in a] [[0, 1], [1, 0], [1, 1]] @@ -2144,7 +2144,7 @@ def stable_configs(self, smax=None): sage: list(b) [{1: 0, 2: 0}, {1: 1, 2: 0}] """ - if smax==None: + if smax is None: smax = self.max_stable().values() else: c = SandpileConfig(self,smax) @@ -2174,42 +2174,42 @@ def markov_chain(self,state, distrib=None): sage: s = sandpiles.Complete(4) sage: m = s.markov_chain([0,0,0]) - sage: m.next() # random + sage: next(m) # random {1: 0, 2: 0, 3: 0} - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 1, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [2, 2, 1] sage: m = s.markov_chain(s.zero_div(), [0.1,0.1,0.1,0.7]) - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 3] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 4] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 3, 4] .. NOTE:: @@ -2244,7 +2244,7 @@ def markov_chain(self,state, distrib=None): st = SandpileDivisor(self,st) else: raise SyntaxError(state) - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) if isinstance(st,SandpileConfig): @@ -3823,7 +3823,7 @@ def add_random(self, distrib=None): c = deepcopy(self) ind = self._sandpile._sink_ind n = self._sandpile.num_verts() - if distrib==None: # default = uniform distribution on nonsink vertices + if distrib is None: # default = uniform distribution on nonsink vertices distrib = [1/(n-1)]*(n-1) if len(distrib)==n-1: # prob. dist. on nonsink vertices X = GeneralDiscreteDistribution(distrib) @@ -5150,7 +5150,7 @@ def simulate_threshold(self, distrib=None): S = E.sandpile() V = S.vertices() n = S.num_verts() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) while not E.is_alive(): @@ -6034,7 +6034,7 @@ def add_random(self, distrib=None): D = deepcopy(self) S = self.sandpile() V = S.vertices() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution n = S.num_verts() distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a2c85e94832..d1ac3d4ef91 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -254,10 +254,10 @@ def _find_scaling_L_ratio(self): sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) - ... E = EllipticCurve(la) - ... me = E.modular_symbol(use_eclib = True) - ... ms = E.modular_symbol(use_eclib = False) - ... print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) + ....: E = EllipticCurve(la) + ....: me = E.modular_symbol(use_eclib = True) + ....: ms = E.modular_symbol(use_eclib = False) + ....: print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 @@ -268,14 +268,14 @@ def _find_scaling_L_ratio(self): sage: [EllipticCurve(la).modular_symbol(use_eclib=True)(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) - ... E = EllipticCurve(la) - ... m = E.modular_symbol(use_eclib = True) - ... lp = E.padic_lseries(5) - ... for D in [5,17,12,8]: - ... ED = E.quadratic_twist(D) - ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) - ... etaa = lp._quotient_of_periods_to_twist(D) - ... assert ED.lseries().L_ratio()*ED.real_components()*etaa == md + ....: E = EllipticCurve(la) + ....: m = E.modular_symbol(use_eclib = True) + ....: lp = E.padic_lseries(5) + ....: for D in [5,17,12,8]: + ....: ED = E.quadratic_twist(D) + ....: md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) + ....: etaD = lp._quotient_of_periods_to_twist(D) + ....: assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E @@ -361,13 +361,15 @@ def __lalg__(self,D): E = self._E ED = E.quadratic_twist(D) lv = ED.lseries().L_ratio() # this is L(ED,1) divided by the Neron period omD of ED - lv *= ED.real_components() + lv *= ED.real_components() # now it is by the least positive period omD = ED.period_lattice().basis()[0] if D > 0 : om = E.period_lattice().basis()[0] q = sqrt(D)*omD/om * 8 else : om = E.period_lattice().basis()[1].imag() + if E.real_components() == 1: + om *= 2 q = sqrt(-D)*omD/om*8 # see padic_lseries.pAdicLeries._quotient_of_periods_to_twist @@ -409,8 +411,15 @@ def __scale_by_periods_only__(self): print "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2." cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] - q = QQ(int(round(q*200)))/200 + if self._sign == 1: + q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[1].imag() + if E0.real_components() == 1: + q *= 2 + if self._E.real_components() == 1: + q /= 2 + q = ZZ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q @@ -589,7 +598,7 @@ def __init__(self, E, sign, normalize="L_ratio"): 1 sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1) sage: M(1/3) - 1 + 1/2 This is a rank 1 case with vanishing positive twists. The modular symbol is adjusted by -2:: @@ -597,9 +606,9 @@ def __init__(self, E, sign, normalize="L_ratio"): sage: E=EllipticCurve('121b1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1,normalize='L_ratio') sage: M(1/3) - 2 + 1 sage: M._scaling - -2 + -1 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=False) sage: M(0) @@ -679,7 +688,14 @@ def _find_scaling_period(self): else : cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + if self._sign == 1: + q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/E.period_lattice().basis()[1].imag() + if E0.real_components() == 1: + q *= 2 + if E.real_components() == 1: + q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 7cafd5bca35..735fd9cdd3f 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1115,6 +1115,8 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): of ``lseries()``, where the value is also divided by the number of connected components of `E(\RR)`). In particular the modular symbol depends on `E` and not only the isogeny class of `E`. + For the negative part the corresponding period is purely imaginary of + smallest positive imaginary part. INPUT: @@ -1209,6 +1211,9 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): 1 sage: E.modular_symbol(use_eclib=False, normalize='period')(0) 1/25 + sage: E.modular_symbol(sign=-1, use_eclib=False, normalize='L_ratio')(1/3) + 1/2 + """ typ = (sign, normalize, use_eclib) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 1eb267c6f42..501169cf210 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -125,6 +125,7 @@ class pAdicLseries(SageObject): 2 + 4*5 + 4*5^2 + O(5^4) + O(5)*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + O(5)*T^4 + O(T^5) + A prime p such that E[p] is reducible:: sage: L = EllipticCurve('11a').padic_lseries(5) @@ -140,11 +141,11 @@ class pAdicLseries(SageObject): sage: E=EllipticCurve('11a1') sage: lp=E.padic_lseries(7) sage: lp.series(4,eta=1) - 6 + 2*7^3 + 5*7^4 + O(7^6) + (4*7 + 2*7^2 + O(7^3))*T + (2 + 3*7^2 + O(7^3))*T^2 + (1 + 2*7 + 2*7^2 + O(7^3))*T^3 + (1 + 3*7^2 + O(7^3))*T^4 + O(T^5) + 3 + 7^3 + 6*7^4 + 3*7^5 + O(7^6) + (2*7 + 7^2 + O(7^3))*T + (1 + 5*7^2 + O(7^3))*T^2 + (4 + 4*7 + 4*7^2 + O(7^3))*T^3 + (4 + 3*7 + 7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=2) 5 + 6*7 + 4*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + O(7^6) + (6 + 4*7 + 7^2 + O(7^3))*T + (3 + 2*7^2 + O(7^3))*T^2 + (1 + 4*7 + 7^2 + O(7^3))*T^3 + (6 + 6*7 + 6*7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=3) - O(7^6) + (3 + 2*7 + 5*7^2 + O(7^3))*T + (5 + 4*7 + 5*7^2 + O(7^3))*T^2 + (3*7 + 7^2 + O(7^3))*T^3 + (2*7 + 7^2 + O(7^3))*T^4 + O(T^5) + O(7^6) + (5 + 4*7 + 2*7^2 + O(7^3))*T + (6 + 5*7 + 2*7^2 + O(7^3))*T^2 + (5*7 + O(7^3))*T^3 + (7 + 4*7^2 + O(7^3))*T^4 + O(T^5) (Note that the last series vanishes at `T = 0`, which is consistent with :: @@ -208,15 +209,13 @@ def __add_negative_space(self): sage: E = EllipticCurve('11a1') sage: lp = E.padic_lseries(5) sage: lp.modular_symbol(1/7,sign=-1) #indirect doctest - -1 + -1/2 """ if self._use_eclib: verbose('Currently there is no negative modular symbols in eclib, so we have to fall back on the implementation of modular symbols in sage') # once there is a eclib implementation of -1, this should be changed. - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) - else: - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) + self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) def __cmp__(self,other): r""" @@ -297,7 +296,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): Note also that this function does not check if the condition on the quadratic_twist=D is satisfied. So the result will only be correct if for each prime `\ell` dividing `D`, we have - `ord_{\ell(N)}<= ord_{\ell}(D)`, where `N` is the conductor of the curve. + `ord_{\ell}(N)<= ord_{\ell}(D)`, where `N` is the conductor of the curve. INPUT: @@ -314,13 +313,15 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): sage: [lp.modular_symbol(r) for r in [0,1/5,oo,1/11]] [1/5, 6/5, 0, 0] sage: [lp.modular_symbol(r,sign=-1) for r in [0,1/3,oo,1/7]] - [0, 1, 0, -1] + [0, 1/2, 0, -1/2] sage: [lp.modular_symbol(r,quadratic_twist=-20) for r in [0,1/5,oo,1/11]] - [2, 2, 0, 1] + [1, 1, 0, 1/2] - sage: lpt = E.quadratic_twist(-3).padic_lseries(5) - sage: et = E.padic_lseries(5)._quotient_of_periods_to_twist(-3) - sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-3)/et + sage: E = EllipticCurve('20a1') + sage: Et = E.quadratic_twist(-4) + sage: lpt = Et.padic_lseries(5) + sage: eta = lpt._quotient_of_periods_to_twist(-4) + sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-4) / eta True """ @@ -341,7 +342,9 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): raise NotImplementedError("Quadratic twists for negative modular symbols are not yet implemented.") if D > 0: m = self._modular_symbol - s = +1 + return sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,D) ] ) + else: try: m = self._negative_modular_symbol @@ -349,10 +352,8 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): if not hasattr(self, '_modular_symbol_negative'): self.__add_negative_space() m = self._negative_modular_symbol - s = -1 - # without the ZZ here the u is treated as a 'int' and dividing by D gives 0 - # this only happens when it is called from __init__ (?) - return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) + return -sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,-D) ] ) def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): @@ -411,7 +412,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): sage: E = EllipticCurve('11a1') sage: a = E.quadratic_twist(-3).padic_lseries(5).measure(1,2,prec=15) sage: b = E.padic_lseries(5).measure(1,2, quadratic_twist=-3,prec=15) - sage: a == b/E.padic_lseries(5)._quotient_of_periods_to_twist(-3) + sage: a == b * E.padic_lseries(5)._quotient_of_periods_to_twist(-3) True """ @@ -422,7 +423,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): raise NotImplementedError("Quadratic twists not implemented for sign -1") if quadratic_twist < 0: - s = -1 + s = ZZ(-1) try: p, alpha, z, w, f = self.__measure_data[(n,prec,s)] @@ -447,14 +448,17 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): if quadratic_twist == 1: if self._E.conductor() % p == 0: return z * f(a/(p*w)) - return z * f(a/(p*w)) - (z/alpha) * f(a/w) + return z * ( f(a/(p*w)) - f(a/w) / alpha) else: D = quadratic_twist - chip = kronecker_symbol(D,p) + if self.is_ordinary(): + chip = kronecker_symbol(D,p) + else: + chip = 1 # alpha is +- sqrt(-p) anyway if self._E.conductor() % p == 0: - mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,D.abs())]) else: - mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) *( f(a/(p*w)+ZZ(u)/D) - chip /alpha * f(a/w+ZZ(u)/D) ) for u in range(1,D.abs())]) return s*mu def alpha(self, prec=20): @@ -483,9 +487,9 @@ def alpha(self, prec=20): sage: L = E.padic_lseries(3) sage: alpha = L.alpha(10); alpha - (1 + O(3^10))*alpha + alpha + O(alpha^21) sage: alpha^2 - E.ap(3)*alpha + 3 - (O(3^11))*alpha + (O(3^11)) + O(alpha^22) A reducible prime:: @@ -519,8 +523,9 @@ def alpha(self, prec=20): return K(a) raise RunTimeError("bug in p-adic L-function alpha") else: # supersingular case - f = f.change_ring(Qp(p, prec, print_mode='series')) - a = f.root_field('alpha', check_irreducible=False).gen() + f = f.change_ring(K) + A = K.extension(f, names="alpha") + a = A.gen() self._alpha[prec] = a return a @@ -592,13 +597,6 @@ def order_of_vanishing(self): return v n += 1 - -# def _c_bounds(self, n): -# raise NotImplementedError - -# def _prec_bounds(self, n,prec): -# raise NotImplementedError - def teichmuller(self, prec): r""" Return Teichmuller lifts to the given precision. @@ -702,10 +700,12 @@ def _set_series_in_cache(self, n, prec, D, eta, f): def _quotient_of_periods_to_twist(self,D): r""" - For a fundamental discriminant `D` of a quadratic number field this computes the constant `\eta` such that - `\sqrt{D}\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. As in [MTT]_ page 40. - This is either 1 or 2 unless the condition on the twist is not satisfied, e.g. if we are 'twisting back' - to a semi-stable curve. + For a fundamental discriminant `D` of a quadratic number field this + computes the constant `\eta` such that + `\sqrt{\vert D\vert }\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. + As in [MTT]_ page 40. This is either 1 or 2 unless the condition + on the twist is not satisfied, e.g. if we are 'twisting back' to a + semi-stable curve. REFERENCES: @@ -738,7 +738,7 @@ def _quotient_of_periods_to_twist(self,D): sage: Et = E.quadratic_twist(-3) sage: lpt = Et.padic_lseries(5) sage: lpt._quotient_of_periods_to_twist(-3) - 6 + 3 """ from sage.functions.all import sqrt @@ -747,13 +747,14 @@ def _quotient_of_periods_to_twist(self,D): # Note that the number of real components does not change by twisting. if D == 1: return 1 + Et = self._E.quadratic_twist(D) if D > 1: - Et = self._E.quadratic_twist(D) qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] qt *= sqrt(qt.parent()(D)) else: - Et = self._E.quadratic_twist(D) - qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[1].imag() + qt = Et.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[0] + if Et.real_components() == 1: + qt *= 2 qt *= sqrt(qt.parent()(-D)) verbose('the real approximation is %s'%qt) # we know from MTT that the result has a denominator 1 @@ -851,9 +852,9 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): sage: L = EllipticCurve('110a1').padic_lseries(5) sage: for j in [0..3]: print L.series(4, eta=j) O(5^6) + (2 + 2*5 + 2*5^2 + O(5^3))*T + (5 + 5^2 + O(5^3))*T^2 + (4 + 4*5 + 2*5^2 + O(5^3))*T^3 + (1 + 5 + 3*5^2 + O(5^3))*T^4 + O(T^5) - 3 + 2*5 + 2*5^3 + 3*5^4 + O(5^6) + (2 + 5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + 2*5^2 + O(5^3))*T^2 + (1 + 5 + 5^2 + O(5^3))*T^3 + (2 + 4*5 + 4*5^2 + O(5^3))*T^4 + O(T^5) + 4 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + O(5^6) + (1 + 3*5 + 4*5^2 + O(5^3))*T + (3 + 4*5 + 3*5^2 + O(5^3))*T^2 + (3 + 3*5^2 + O(5^3))*T^3 + (1 + 2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) 2 + O(5^6) + (1 + 5 + O(5^3))*T + (2 + 4*5 + 3*5^2 + O(5^3))*T^2 + (4 + 5 + 2*5^2 + O(5^3))*T^3 + (4 + O(5^3))*T^4 + O(T^5) - 1 + 3*5 + 4*5^2 + 2*5^3 + 5^4 + 4*5^5 + O(5^6) + (2 + 4*5 + 3*5^2 + O(5^3))*T + (2 + 3*5 + 5^2 + O(5^3))*T^2 + (1 + O(5^3))*T^3 + (2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) + 3 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + O(5^6) + (1 + 2*5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + O(5^3))*T^2 + (3 + 2*5 + 2*5^2 + O(5^3))*T^3 + (5 + 5^2 + O(5^3))*T^4 + O(T^5) """ n = ZZ(n) if n < 1: @@ -892,7 +893,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): # set prec arbitrary to 20. K = Qp(p, 20, print_mode='series') R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) chip = kronecker_symbol(D,p) if self._E.conductor() % p == 0: L *= 1 - chip/self.alpha() @@ -928,6 +929,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): gamma_power = K(1) teich = self.teichmuller(padic_prec) p_power = p**(n-1) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p_power)) verbose_level = get_verbose() @@ -939,7 +941,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign = 1-2*(eta % 2)).lift() + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si).lift() L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma @@ -952,7 +954,8 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): L = R(L,res_series_prec) aj = L.list() if len(aj) > 0: - aj = [aj[0].add_bigoh(padic_prec-2)] + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] + aj = [aj[0].add_bigoh(padic_prec-2)] + \ + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] L = R(aj,res_series_prec ) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() @@ -1066,7 +1069,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Here the normalization of the `p`-adic L-series is chosen such that `L_p(E,1) = (1-1/\alpha)^2 L(E,1)/\Omega_E` - where `\alpha` is the unit root of the characteristic + where `\alpha` is a root of the characteristic polynomial of Frobenius on `T_pE` and `\Omega_E` is the Neron period of `E`. @@ -1082,6 +1085,12 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Teichmueller character on the group of roots of unity in `\ZZ_p^\times`) + OUTPUT: + + a power series with coefficients in a quadratic ramified extension of + the `p`-adic numbers generated by a root `alpha` of the characteristic + polynomial of Frobenius on `T_pE`. + ALIAS: power_series is identical to series. EXAMPLES: @@ -1093,18 +1102,16 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): sage: L.series(2) O(T^3) sage: L.series(4) # takes a long time (several seconds) - (O(3^2))*alpha + (O(3^3)) + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T^2 + O(T^5) + O(alpha) + (alpha^-2 + O(alpha^0))*T + (alpha^-2 + O(alpha^0))*T^2 + O(T^5) sage: L.alpha(2).parent() - Univariate Quotient Polynomial Ring in alpha over 3-adic Field with capped - relative precision 2 with modulus (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) + Eisenstein Extension of 3-adic Field with capped relative precision 2 in alpha defined by (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) An example where we only compute the leading term (:trac:`15737`):: sage: E = EllipticCurve("17a1") sage: L = E.padic_lseries(3) sage: L.series(4,prec=1) - (2*3^-1 + 1 + 3 + 3^2 + 3^3 + ... + 3^18 + O(3^19))*alpha + (2*3^-1 + 1 + 3 + 3^2 + 3^3 + 3^4 + ... + 3^18 + O(3^19)) + O(T) - + alpha^-2 + alpha^-1 + 2 + 2*alpha + ... + O(alpha^38) + O(T) """ n = ZZ(n) if n < 1: @@ -1131,7 +1138,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): p = self._p eta = ZZ(eta) % (p-1) if p == 2 and self._normalize : - print 'Warning : for p == 2 the normalization might not be correct !' + print 'Warning : for p = 2 the normalization might not be correct !' if prec == 1: if eta == 0: @@ -1141,27 +1148,22 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): alpha = self.alpha(prec=20) K = alpha.parent() R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) - if self._E.has_nonsplit_multiplicative_reduction(p): - L *= 2 - if self._E.has_split_multiplicative_reduction(p): - L *= 0 - else: - chip = kronecker_symbol(D,p) - L *= (1-chip/self.alpha())**2 + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) + L *= (1-1/self.alpha())**2 L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() L = R(L, 1) return L else: # here we need some sums anyway bounds = self._prec_bounds(n,prec) - padic_prec = 20 + alphaadic_prec = 20 else: prec = min(p**(n-1), prec) bounds = self._prec_bounds(n,prec) - padic_prec = max(sum(bounds[1:],[])) + 5 + alphaadic_prec = max(bounds[1:]) + 5 - verbose("using p-adic precision of %s"%padic_prec) + padic_prec = alphaadic_prec//2+1 + verbose("using alpha-adic precision of %s"%padic_prec) ans = self._get_series_from_cache(n, prec, quadratic_twist,eta) if not ans is None: verbose("found series in cache") @@ -1176,6 +1178,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): one_plus_T_factor = R(1) gamma_power = 1 teich = self.teichmuller(padic_prec) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p**(n-1))) verbose_level = get_verbose() @@ -1187,18 +1190,22 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign=1-2*(eta % 2)) + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si) L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma # Now create series but with each coefficient truncated # so it is proven correct: + # the coefficients are now treated as alpha-adic numbers (trac 20254) L = R(L,prec) aj = L.list() if len(aj) > 0: - bj = [aj[0][0].add_bigoh(padic_prec-2) + alpha * aj[0][1].add_bigoh(padic_prec-2)] - bj += [aj[j][0].add_bigoh(bounds[j][0]) + alpha * aj[j][1].add_bigoh(bounds[j][1]) for j in range(1,len(aj))] + bj = [aj[0].add_bigoh(2*(padic_prec-2))] + j = 1 + while j < len(aj): + bj.append( aj[j].add_bigoh(bounds[j]) ) + j += 1 L = R(bj, prec) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() self._set_series_in_cache(n, prec, quadratic_twist, eta, L) @@ -1236,7 +1243,7 @@ def _prec_bounds(self, n,prec): r""" A helper function not designed for direct use. - It returns the `p`-adic precisions of the approximation + It returns the `\alpha`-adic precisions of the approximation to the `p`-adic L-function. EXAMPLES:: @@ -1244,19 +1251,46 @@ def _prec_bounds(self, n,prec): sage: E = EllipticCurve('11a1') sage: Lp = E.padic_lseries(19) sage: Lp._prec_bounds(3,5) - [[+Infinity, +Infinity], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] + [+Infinity, -1, -1, -1, -1] sage: Lp._prec_bounds(2,5) - [[+Infinity, +Infinity], [-1, -2], [-1, -2], [-1, -2], [-1, -2]] + [+Infinity, -2, -2, -2, -2] sage: Lp._prec_bounds(10,5) - [[+Infinity, +Infinity], [3, 2], [3, 2], [3, 2], [3, 2]] + [+Infinity, 6, 6, 6, 6] """ - p = self._p e = self._e_bounds(n-1,prec) - c0 = ZZ(n+2)/2 - c1 = ZZ(n+3)/2 - return [[infinity,infinity]] + [[(e[j] - c0).floor(), (e[j] - c1).floor()] for j in range(1,len(e))] + c0 = ZZ(n+2) + return [infinity] + [ 2* e[j] - c0 for j in range(1,len(e))] + def _poly(self, a): + """ + Given an element a in Qp[alpha] this returns the list + containing the two coordinates in Qp. + + EXAMPLES:: + + sage: E = EllipticCurve("14a1") + sage: lp = E.padic_lseries(5) + sage: K = lp.alpha().parent() + sage: a = K(5) + sage: a + 4*alpha^2 + alpha^4 + O(alpha^42) + sage: lp._poly(a) + [5 + O(5^21), O(5^21)] + """ + # this should be implemented in elements of Eisenstein rings at some point trac 20248 + + if a.is_zero(): + return [0,0] + v, k = a._ntl_rep_abs() + K = a.base_ring() + pi = K.uniformiser() + v0 = K(v[0]._sage_()) * pi**k + v1 = K(v[1]._sage_()) * pi**k + alpha = a.parent().gen() + assert v0 + v1*alpha == a + return [ v0, v1 ] + def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): r""" Returns a vector of two components which are p-adic power series. @@ -1289,7 +1323,7 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): sage: E = EllipticCurve('14a') sage: L = E.padic_lseries(5) sage: L.Dp_valued_series(4) # long time (9s on sage.math, 2011) - (1 + 4*5 + 4*5^3 + O(5^4) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), O(5^4) + O(5)*T + O(5)*T^2 + O(5)*T^3 + (2 + O(5))*T^4 + O(T^5)) + (1 + 4*5 + O(5^2) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), 5^2 + O(5^3) + O(5^2)*T + (4*5 + O(5^2))*T^2 + (2*5 + O(5^2))*T^3 + (2 + 2*5 + O(5^2))*T^4 + O(T^5)) """ E = self._E p = self._p @@ -1298,8 +1332,14 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): # now split up the series in two lps = G + H * alpha R = lps.base_ring().base_ring() # Qp QpT , T = PowerSeriesRing(R,'T',prec).objgen() - G = QpT([lps[n][0] for n in range(0,lps.prec())], prec) - H = QpT([lps[n][1] for n in range(0,lps.prec())], prec) + Gli = [] + Hli = [] + for n in range(0,lps.prec()): + v = self._poly(lps[n]) + Gli.append( v[0] ) + Hli.append( v[1] ) + G = QpT( Gli, prec ) + H = QpT( Hli, prec ) # now compute phi phi = matrix.matrix([[0,-1/p],[1,E.ap(p)/p]]) diff --git a/src/sage/schemes/elliptic_curves/sha_tate.py b/src/sage/schemes/elliptic_curves/sha_tate.py index e06375103c4..d3086ddb0be 100644 --- a/src/sage/schemes/elliptic_curves/sha_tate.py +++ b/src/sage/schemes/elliptic_curves/sha_tate.py @@ -89,6 +89,7 @@ from sage.misc.all import verbose import sage.arith.all as arith from sage.rings.padics.factory import Qp +from sage.modules.free_module_element import vector factor = arith.factor valuation = arith.valuation @@ -542,22 +543,27 @@ def an_padic(self, p, prec=0, use_twists=True): E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 - reg = E.padic_regulator(p) r = E.rank() - + if r > 0 : + reg = E.padic_regulator(p) + else: + if E.is_supersingular(p): + reg = vector([ Qp(p,20)(1), 0 ]) + else: + reg = Qp(p,20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: - D = D/p + D = ZZ(D/p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: - D = D/ell + D = ZZ(D/ell) ve = valuation(D,2) - de = (D/2**ve).abs() + de = ZZ( (D/2**ve).abs() ) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index e22f3a120a3..ca4dea9887f 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -258,6 +258,10 @@ cpdef bint is_numpy_type(t): True sage: is_numpy_type(numpy.float) # Alias for Python float False + sage: is_numpy_type(numpy.ndarray) + True + sage: is_numpy_type(numpy.matrix) + True sage: is_numpy_type(int) False sage: is_numpy_type(Integer) @@ -269,7 +273,13 @@ cpdef bint is_numpy_type(t): """ if not isinstance(t, type): return False - return strncmp((t).tp_name, "numpy.", 6) == 0 + cdef PyTypeObject* T = t + if strncmp(T.tp_name, "numpy.", 6) == 0: + return True + # Check base type. This is needed to detect numpy.matrix. + if strncmp(T.tp_base.tp_name, "numpy.", 6) == 0: + return True + return False cdef object _Integer diff --git a/src/sage/symbolic/comparison.pxd b/src/sage/symbolic/comparison.pxd new file mode 100644 index 00000000000..d8e919f191f --- /dev/null +++ b/src/sage/symbolic/comparison.pxd @@ -0,0 +1,11 @@ +from ginac cimport * +from sage.symbolic.expression cimport Expression + + +cpdef int print_order(lhs, rhs) except -2 +cdef int print_order_c(Expression lhs, Expression rhs) + +cpdef print_sorted(expression_list) + +# cpdef int math_order_c(Expression lhs, Expression rhs) except -2 + diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx new file mode 100644 index 00000000000..de129a18a1b --- /dev/null +++ b/src/sage/symbolic/comparison.pyx @@ -0,0 +1,394 @@ +""" +Comparison of Symbolic Expressions + +There are two useful ways to compare symbolic expressions: + +* :func:`print_order` is how the terms are ordered. This is always + defined. If you need a fast comparison, this is it. + +* :func:`math_order` is the "mathematical" comparison. This may raise + an exception if the answer is unknown (to Sage) or cannot, in + principle, evaluated to a boolean (for example, if it involves + symbolic variables). Can be very slow as it potentially calls + Maxima to prove the inequality. +""" +from cpython cimport * + +from sage.symbolic.ring import SR +from sage.symbolic.expression cimport is_Expression + + +cdef int print_order_c(Expression lhs, Expression rhs): + """ + Print comparison. + + See :meth:`print_order` for details. + """ + return print_order_compare((lhs)._gobj, (rhs)._gobj) + + +cpdef int print_order(lhs, rhs) except -2: + """ + Comparison in the print order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order + sage: print_order(1, oo) + 1 + sage: print_order(e, oo) + -1 + sage: print_order(pi, oo) + 1 + sage: print_order(1, sqrt(2)) + 1 + + Check that :trac:`12967` is fixed:: + + sage: cmp(SR(oo), sqrt(2)) + 1 + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + return print_order_c(lhs, rhs) + + +class _print_key(object): + + def __init__(self, ex): + """ + Sort key to sort in print order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _print_key + sage: _print_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order, _print_key + sage: print_order(1, 2) + -1 + sage: _print_key(1) < _print_key(2) + True + sage: print_order(1, sqrt(2)) + 1 + sage: _print_key(1) < _print_key(sqrt(2)) + False + """ + return print_order_c(self.ex, other.ex) < 0 + + +cpdef print_sorted(expressions): + """ + Sort a list in print order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by :meth:`print_order`. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_sorted + sage: print_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [e, sqrt(2), pi, 1] + """ + return sorted(expressions, key=_print_key) + + +class _math_key(object): + + def __init__(self, ex): + """ + Sort key to sort in "Mathematics" order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) < _math_key(2) + True + sage: _math_key(1) < _math_key(sqrt(2)) + True + + Check that :trac:`12967` is fixed:: + + sage: _math_key(1) < _math_key(oo) + True + """ + less_than = bool(self.ex < other.ex) + greater_than = bool(self.ex > other.ex) + if less_than: + if not greater_than: + return True + else: + assert False # unreachable + else: + if greater_than: + return False + else: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + + +cpdef math_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mathematics" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by ascending (real) value. If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import math_sorted + sage: math_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [1, sqrt(2), e, pi] + """ + return sorted(expressions, key=_math_key) + + +cpdef int mixed_order(lhs, rhs) except -2: + """ + Comparison in the mixed order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_order + sage: mixed_order(1, oo) + -1 + sage: mixed_order(e, oo) + -1 + sage: mixed_order(pi, oo) + -1 + sage: mixed_order(1, sqrt(2)) + -1 + sage: mixed_order(x + x^2, x*(x+1)) + -1 + + Check that :trac:`12967` is fixed:: + + sage: cmp(SR(oo), sqrt(2)) + 1 + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + less_than = _mixed_key(lhs) < _mixed_key(rhs) + if less_than: + return -1 + greater_than = _mixed_key(lhs) > _mixed_key(rhs) + if greater_than: + return 1 + else: + return 0 + + +class _mixed_key(object): + + def __init__(self, ex): + """ + Sort key to sort in mixed order. + + Mixed order is print order if variables are present, + mathematical/numeric if not. This should enable quick + and correct results. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_mixed_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) < _mixed_key(2) + True + sage: _mixed_key(1) < _mixed_key(sqrt(2)) + True + + Check that :trac:`12967` is fixed:: + + sage: _mixed_key(1) < _mixed_key(oo) + True + """ + from sage.rings.real_mpfi import RIF + selfv = len(self.ex.variables()) + otherv = len(other.ex.variables()) + if selfv: + if otherv: + return _print_key(self.ex) < _print_key(other.ex) + else: + return False + else: + if otherv: + return True + + # no variables involved from here on + rel = self.ex < other.ex + if (self.ex.is_infinity() or other.ex.is_infinity()): + pynac_result = decide_relational((rel)._gobj) + if pynac_result == relational_undecidable: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + return pynac_result == relational_true + + det_ex = self.ex - other.ex + if not has_symbol_or_function((rel)._gobj): + while hasattr(det_ex, 'pyobject') and isinstance(det_ex, Expression): + try: + det_ex = det_ex.pyobject() + except TypeError: + break + if not isinstance(det_ex, Expression): + return det_ex < 0 + from sage.rings.qqbar import QQbar + try: + from sage.rings.qqbar import QQbar + num = QQbar(det_ex) + except (TypeError, AttributeError,ValueError,NotImplementedError): + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + else: + return num < 0 + + # here we have expressions containing functions + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + + + +cpdef mixed_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mixed" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + In the list the numeric values are sorted by ascending (real) value, + and the expressions with variables according to print order. + If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_sorted + sage: mixed_sorted([SR(1), SR(e), SR(pi), sqrt(2), x, sqrt(x), sin(1/x)]) + [1, sqrt(2), e, pi, sin(1/x), sqrt(x), x] + """ + return sorted(expressions, key=_mixed_key) + diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 26381a49d71..fc6a7e48647 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -357,6 +357,19 @@ def domain(self): """ return self._domain + def __lt__(self, other): + """ + Perform float comparison with constant. + + EXAMPLES:: + + sage: cmp(pi, 0) + 1 + sage: cmp(pi, SR(0)) + 1 + """ + return self.__float__() < other + def expression(self): """ Returns an expression for this constant. diff --git a/src/sage/symbolic/expression.pxd b/src/sage/symbolic/expression.pxd index c5390ef72bc..6df0fab4f91 100644 --- a/src/sage/symbolic/expression.pxd +++ b/src/sage/symbolic/expression.pxd @@ -1,5 +1,6 @@ from ginac cimport * +cdef class Expression from sage.structure.element cimport CommutativeRingElement cdef class Expression(CommutativeRingElement): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 555b52f023c..31996032c6b 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -142,10 +142,12 @@ import sage.rings.integer import sage.rings.rational from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT from sage.structure.element cimport ModuleElement, RingElement, Element +from sage.symbolic.comparison import mixed_order from sage.symbolic.getitem cimport OperandsWrapper from sage.symbolic.series cimport SymbolicSeries from sage.symbolic.complexity_measures import string_length from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction +cimport sage.symbolic.comparison from sage.rings.rational import Rational # Used for sqrt. from sage.misc.derivative import multi_derivative from sage.misc.superseded import deprecated_function_alias @@ -154,6 +156,7 @@ from sage.misc.decorators import rename_keyword from sage.structure.dynamic_class import dynamic_class from sage.symbolic.operators import FDerivativeOperator, add_vararg, mul_vararg + # a small overestimate of log(10,2) LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 @@ -3458,8 +3461,25 @@ cdef class Expression(CommutativeRingElement): I*x - 1/2 sage: t.subs(x=I*x).subs(x=0).is_positive() False + + Check if :trac:`16397` is fixed: + + sage: cmp(1, sqrt(2)) + -1 + sage: cmp(SR(1), sqrt(2)) + -1 + sage: cmp(log(8), 3*log(2)) + 0 + sage: RLF(1) < RLF(sqrt(2)) + True + sage: RealSet((0, pi),[pi, pi],(pi,4)) + (0, 4) + sage: RealSet((0, pi),[0, pi],(pi,4)) + [0, 4) + sage: RealSet((0, pi),[0, 3.5],(pi,4)) + [0, 4) """ - return print_order_compare(left._gobj, (right)._gobj) + return mixed_order(left, right) cpdef int _cmp_add(Expression left, Expression right) except -2: """ @@ -4542,6 +4562,7 @@ cdef class Expression(CommutativeRingElement): sage: ((x^y)^z).find(w0^w1) [(x^y)^z] """ + from sage.symbolic.comparison import print_sorted cdef Expression p = self.coerce_in(pattern) cdef GExList found self._gobj.find(p._gobj, found) @@ -4550,7 +4571,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(found.end()): res.append(new_Expression_from_GEx(self._parent, itr.obj())) itr.inc() - res.sort(cmp) + res = print_sorted(res) return res def has(self, pattern): @@ -4971,6 +4992,7 @@ cdef class Expression(CommutativeRingElement): """ from sage.symbolic.ring import SR + from sage.symbolic.comparison import print_sorted cdef GExSet sym_set g_list_symbols(self._gobj, sym_set) res = [] @@ -4978,7 +5000,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(sym_set.end()): res.append(new_Expression_from_GEx(SR, itr.obj())) itr.inc() - res.sort(cmp=lambda x,y: -cmp(x,y)) + res = print_sorted(res)[::-1] return tuple(res) def arguments(self): diff --git a/src/sage/symbolic/ginac.pxd b/src/sage/symbolic/ginac.pxd index 7a15668a911..453d5c6e7c3 100644 --- a/src/sage/symbolic/ginac.pxd +++ b/src/sage/symbolic/ginac.pxd @@ -604,7 +604,3 @@ cdef extern from "pynac/order.h": (GEx left, GEx right) except + bint print_order_compare_mul "GiNaC::print_order_mul().compare" \ (GEx left, GEx right) except + - bint print_order "GiNaC::print_order()" \ - (GEx left, GEx right) except + - bint print_order_mul "GiNaC::print_order_mul()" \ - (GEx left, GEx right) except + diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index fa7650d07f3..42726363a88 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -338,8 +338,8 @@ def assert_strict_weak_order(a,b,c, cmp_func): ....: cmp[i,j] = x[i].__cmp__(x[j]) sage: cmp [ 0 -1 -1] - [ 1 0 1] - [ 1 -1 0] + [ 1 0 -1] + [ 1 1 0] """ from sage.matrix.constructor import matrix from sage.combinat.permutation import Permutations diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index e8c4a7ba30c..25fb4bea244 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -92,6 +92,7 @@ # Sage library from ring import SR from expression import Expression +from sage.interfaces.tab_completion import ExtraTabCompletion ############################################################################### # Unit conversions dictionary. @@ -929,11 +930,11 @@ def vars_in_str(s): INPUT: - - `s` -- string + - ``s`` -- a string OUTPUT: - - list of strings (unit names) + - a list of strings (unit names) EXAMPLES:: @@ -950,11 +951,11 @@ def unit_derivations_expr(v): INPUT: - - `v` -- string, name of a unit type such as 'area', 'volume', etc. + - ``v`` -- a string, name of a unit type such as 'area', 'volume', etc. OUTPUT: - - symbolic expression + - a symbolic expression EXAMPLES:: @@ -1016,11 +1017,11 @@ class that derives from symbolic expression, and has a specialized INPUT: - - ``name`` -- string + - ``name`` -- a string OUTPUT: - - UnitExpression + - a :class:`UnitExpression` EXAMPLES:: @@ -1032,9 +1033,9 @@ class that derives from symbolic expression, and has a specialized """ return UnitExpression(SR, SR.var(name)) -class Units: +class Units(ExtraTabCompletion): """ - A collection of units of a some type. + A collection of units of some type. EXAMPLES:: @@ -1101,16 +1102,30 @@ def __cmp__(self, other): return cmp(type(self), type(other)) return cmp((self.__name, self.__data), (other.__name, other.__data)) - def trait_names(self): + def _tab_completion(self): """ - Return completions of this unit objects. This is used by the - Sage command line and notebook to create the list of method - names. + Return tab completions. + + This complements the usual content of :func:`dir`, with the + list of the names of the unit collections (resp. units) for + :obj:`units` (resp. its subcollections), in particular for tab + completion purposes. + + .. SEEALSO:: :class:`ExtraTabCompletion` EXAMPLES:: - sage: units.area.trait_names() + sage: units.area._tab_completion() ['acre', 'are', 'barn', 'hectare', 'rood', 'section', 'square_chain', 'square_meter', 'township'] + sage: units._tab_completion() + ['acceleration', ..., 'volume'] + sage: units.force._tab_completion() + ['dyne', ..., 'ton_force'] + + sage: dir(units) + ['_Units__data', ..., 'acceleration', ..., 'volume'] + sage: dir(units.force) + ['_Units__data', ..., 'dyne', ..., 'ton_force'] """ return sorted([x for x in self.__data.keys() if '/' not in x]) @@ -1168,11 +1183,11 @@ def unitdocs(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - ``string`` + - a string EXAMPLES:: @@ -1199,11 +1214,11 @@ def is_unit(s): INPUT: - - `s` -- an object + - ``s`` -- an object OUTPUT: - - ``bool`` + - a boolean EXAMPLES:: @@ -1231,13 +1246,13 @@ def convert(expr, target): INPUT: - - `expr` -- the symbolic expression converting from + - ``expr`` -- the symbolic expression converting from - - `target` -- (default None) the symbolic expression converting to + - ``target`` -- (default None) the symbolic expression converting to OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1323,11 +1338,11 @@ def base_units(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1373,12 +1388,12 @@ def convert_temperature(expr, target): INPUT: - - `expr` -- a unit of temperature - - `target` -- a units of temperature + - ``expr`` -- a unit of temperature + - ``target`` -- a units of temperature OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: diff --git a/src/sage/version.py b/src/sage/version.py index b8460dee608..af243c34a9a 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '7.2.beta4' -date = '2016-04-12' +version = '7.2.beta5' +date = '2016-04-21'