diff --git a/.zenodo.json b/.zenodo.json index fd96736f3c1..e4996fc5334 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -1,10 +1,10 @@ { "description": "Mirror of the Sage https://sagemath.org/ source tree", "license": "other-open", - "title": "sagemath/sage: 9.5.beta7", - "version": "9.5.beta7", + "title": "sagemath/sage: 9.5.beta8", + "version": "9.5.beta8", "upload_type": "software", - "publication_date": "2021-11-18", + "publication_date": "2021-12-12", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.5.beta7", + "identifier": "https://github.com/sagemath/sage/tree/9.5.beta8", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index be62107d97e..2cbf040582f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.5.beta7, Release Date: 2021-11-18 +SageMath version 9.5.beta8, Release Date: 2021-12-12 diff --git a/build/bin/sage-build-env b/build/bin/sage-build-env index 5447398c9e0..ddda5ce212d 100644 --- a/build/bin/sage-build-env +++ b/build/bin/sage-build-env @@ -170,5 +170,8 @@ if [ "x$SAGE_BUILD_ENV_SOURCED" = "x" ]; then fi # Trac #31335: Avoid include paths leaking in from homebrew python3's distutils.cfg -# by using setuptools' own copy of distutils instead of relying on stdlib distutils. -export SETUPTOOLS_USE_DISTUTILS=local +# by using setuptools' own copy of distutils instead of relying on stdlib distutils +# Trac #32944: Only do this on homebrew. +if [ -n "$HOMEBREW" ]; then + export SETUPTOOLS_USE_DISTUTILS=local +fi diff --git a/build/bin/sage-dist-helpers b/build/bin/sage-dist-helpers index c573dee0360..fe364e5263a 100644 --- a/build/bin/sage-dist-helpers +++ b/build/bin/sage-dist-helpers @@ -262,7 +262,7 @@ sdh_pip_install() { esac shift done - python3 -m pip wheel --use-feature=in-tree-build --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option "$@" || \ + python3 -m pip wheel --wheel-dir=dist --verbose --no-deps --no-index --isolated --ignore-requires-python $build_isolation_option "$@" || \ sdh_die "Error building a wheel for $PKG_NAME" sdh_store_and_pip_install_wheel $install_options . diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 4673be50224..12698a7b5c6 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -338,12 +338,11 @@ doc-html: sagemath_doc_html doc-html-no-plot: doc-clean +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) --no-plot" doc-html -# Using mathjax is actually the default, but this target can be used -# to override an environment setting of SAGE_DOC_MATHJAX=no -doc-html-mathjax: - +$(MAKE_REC) SAGE_DOCBUILD_OPTS="$(SAGE_DOCBUILD_OPTS) -j" doc-html +# Using mathjax is actually the only options, but we keep +# this target for backwards compatibility. +doc-html-mathjax: doc-html -# Keep target 'doc-html-jsmath' for backwards compatibility. +# Also Keep target 'doc-html-jsmath' for backwards compatibility. doc-html-jsmath: doc-html-mathjax doc-pdf: sagemath_doc_pdf diff --git a/build/pkgs/beniget/SPKG.rst b/build/pkgs/beniget/SPKG.rst new file mode 100644 index 00000000000..4e2974a6daa --- /dev/null +++ b/build/pkgs/beniget/SPKG.rst @@ -0,0 +1,18 @@ +beniget: Extract semantic information about static Python code +============================================================== + +Description +----------- + +Extract semantic information about static Python code + +License +------- + +BSD 3-Clause + +Upstream Contact +---------------- + +https://pypi.org/project/beniget/ + diff --git a/build/pkgs/beniget/checksums.ini b/build/pkgs/beniget/checksums.ini new file mode 100644 index 00000000000..731aafbe3f0 --- /dev/null +++ b/build/pkgs/beniget/checksums.ini @@ -0,0 +1,5 @@ +tarball=beniget-VERSION.tar.gz +sha1=0167f16d17fbd61b91e620bca07e4ec7054ce51d +md5=a2bbe7f17f10f9c127d8ef00692ddc55 +cksum=2287567629 +upstream_url=https://pypi.io/packages/source/b/beniget/beniget-VERSION.tar.gz diff --git a/build/pkgs/beniget/dependencies b/build/pkgs/beniget/dependencies new file mode 100644 index 00000000000..d792a85db72 --- /dev/null +++ b/build/pkgs/beniget/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) gast | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/beniget/install-requires.txt b/build/pkgs/beniget/install-requires.txt new file mode 100644 index 00000000000..8b5faaea7c2 --- /dev/null +++ b/build/pkgs/beniget/install-requires.txt @@ -0,0 +1 @@ +beniget diff --git a/build/pkgs/beniget/package-version.txt b/build/pkgs/beniget/package-version.txt new file mode 100644 index 00000000000..267577d47e4 --- /dev/null +++ b/build/pkgs/beniget/package-version.txt @@ -0,0 +1 @@ +0.4.1 diff --git a/build/pkgs/beniget/spkg-install.in b/build/pkgs/beniget/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/beniget/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/beniget/type b/build/pkgs/beniget/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/beniget/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/cffi/checksums.ini b/build/pkgs/cffi/checksums.ini index 53187449b65..9d2863a8496 100644 --- a/build/pkgs/cffi/checksums.ini +++ b/build/pkgs/cffi/checksums.ini @@ -1,5 +1,5 @@ tarball=cffi-VERSION.tar.gz -sha1=35bb24955834ecb5b7173e625fa95b02d638fcfd -md5=5c118a18ea897df164dbff67a32876fc -cksum=2930209167 +sha1=9c51c29e35510adf7f94542e1f8e05611930b07b +md5=f3a3f26cd3335fc597479c9475da0a0b +cksum=3482630007 upstream_url=https://pypi.io/packages/source/c/cffi/cffi-VERSION.tar.gz diff --git a/build/pkgs/cffi/package-version.txt b/build/pkgs/cffi/package-version.txt index c6ba3bc1a4e..141f2e805be 100644 --- a/build/pkgs/cffi/package-version.txt +++ b/build/pkgs/cffi/package-version.txt @@ -1 +1 @@ -1.14.6 +1.15.0 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index f97cfada907..446fad340c4 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=d9fff88398cb15f16c7dcb3b6180163bb206a699 -md5=585dece094717b58049a2bfaf9811162 -cksum=3669459096 +sha1=07d98dfc41a546e8fbcedc500e0c29927061d2b2 +md5=ccdc4b7a6c1ec9de8a198a5b69bf7cba +cksum=2208364100 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 0cae6c531de..d1714c48f48 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -cdb3614b34926cee3c4cc918313678d3deea4e8d +36135459d57177c9480eda0f779c0fb8650727be diff --git a/build/pkgs/distlib/checksums.ini b/build/pkgs/distlib/checksums.ini index 11203a5485f..6a01aedfbb5 100644 --- a/build/pkgs/distlib/checksums.ini +++ b/build/pkgs/distlib/checksums.ini @@ -1,5 +1,5 @@ tarball=distlib-VERSION.zip -sha1=b36dcbb9542fa58ada97ab27269176ff7d170724 -md5=50196f22cc2ed82fe2cf6c8fa3220d78 -cksum=801947531 +sha1=47db238631902dbd0fbf730ca303438eaecbb0c8 +md5=442dc114d7e59deec1c727b8a27d7e7c +cksum=4135782264 upstream_url=https://pypi.io/packages/source/d/distlib/distlib-VERSION.zip diff --git a/build/pkgs/distlib/package-version.txt b/build/pkgs/distlib/package-version.txt index d15723fbe8d..1c09c74e221 100644 --- a/build/pkgs/distlib/package-version.txt +++ b/build/pkgs/distlib/package-version.txt @@ -1 +1 @@ -0.3.2 +0.3.3 diff --git a/build/pkgs/gast/SPKG.rst b/build/pkgs/gast/SPKG.rst new file mode 100644 index 00000000000..edd67ff4129 --- /dev/null +++ b/build/pkgs/gast/SPKG.rst @@ -0,0 +1,18 @@ +gast: Python AST that abstracts the underlying Python version +============================================================= + +Description +----------- + +Python AST that abstracts the underlying Python version + +License +------- + +BSD 3-Clause + +Upstream Contact +---------------- + +https://pypi.org/project/gast/ + diff --git a/build/pkgs/gast/checksums.ini b/build/pkgs/gast/checksums.ini new file mode 100644 index 00000000000..e4992447b2b --- /dev/null +++ b/build/pkgs/gast/checksums.ini @@ -0,0 +1,5 @@ +tarball=gast-VERSION.tar.gz +sha1=99491544f561ef76523e6f29a621ce633fc7a1c1 +md5=eb2489df0c85ae198e4740e5711c7299 +cksum=307283150 +upstream_url=https://pypi.io/packages/source/g/gast/gast-VERSION.tar.gz diff --git a/build/pkgs/gast/dependencies b/build/pkgs/gast/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/gast/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/gast/install-requires.txt b/build/pkgs/gast/install-requires.txt new file mode 100644 index 00000000000..beb259c8453 --- /dev/null +++ b/build/pkgs/gast/install-requires.txt @@ -0,0 +1 @@ +gast diff --git a/build/pkgs/gast/package-version.txt b/build/pkgs/gast/package-version.txt new file mode 100644 index 00000000000..cb0c939a936 --- /dev/null +++ b/build/pkgs/gast/package-version.txt @@ -0,0 +1 @@ +0.5.2 diff --git a/build/pkgs/gast/spkg-install.in b/build/pkgs/gast/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/gast/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/gast/type b/build/pkgs/gast/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/gast/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/gengetopt/distros/conda.txt b/build/pkgs/gengetopt/distros/conda.txt new file mode 100644 index 00000000000..2865264cd70 --- /dev/null +++ b/build/pkgs/gengetopt/distros/conda.txt @@ -0,0 +1 @@ +gengetopt diff --git a/build/pkgs/gengetopt/distros/homebrew.txt b/build/pkgs/gengetopt/distros/homebrew.txt new file mode 100644 index 00000000000..2865264cd70 --- /dev/null +++ b/build/pkgs/gengetopt/distros/homebrew.txt @@ -0,0 +1 @@ +gengetopt diff --git a/build/pkgs/gmpy2/checksums.ini b/build/pkgs/gmpy2/checksums.ini index 0db353f62f9..7072505cb52 100644 --- a/build/pkgs/gmpy2/checksums.ini +++ b/build/pkgs/gmpy2/checksums.ini @@ -1,5 +1,5 @@ tarball=gmpy2-VERSION.tar.gz -sha1=ef3cfb93ce0ea8b5ad4cebef5bf3c55d8fb5821a -md5=1504652fcab1cd8ce3e42661d42f9f73 -cksum=3152134051 +sha1=6564cfd78f552a8db82136c10b19aa7465856865 +md5=a1555e0d7ca28b3a49c9a81aa06e2bb6 +cksum=2826987588 upstream_url=https://pypi.io/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz diff --git a/build/pkgs/gmpy2/install-requires.txt b/build/pkgs/gmpy2/install-requires.txt index c91a47224c1..51d24518ef6 100644 --- a/build/pkgs/gmpy2/install-requires.txt +++ b/build/pkgs/gmpy2/install-requires.txt @@ -1,3 +1,3 @@ # We would like to write gmpy2 >=2.1.0b5, but pipenv does not accept prereleases in version ranges # https://github.com/pypa/pipenv/issues/1760 -gmpy2 ==2.1.0b5 +gmpy2 ==2.1.0rc1 diff --git a/build/pkgs/gmpy2/package-version.txt b/build/pkgs/gmpy2/package-version.txt index 326c35e0969..0c271bcf956 100644 --- a/build/pkgs/gmpy2/package-version.txt +++ b/build/pkgs/gmpy2/package-version.txt @@ -1 +1 @@ -2.1.0b5 +2.1.0rc1 diff --git a/build/pkgs/lcalc/checksums.ini b/build/pkgs/lcalc/checksums.ini index 8070b11df88..56275207f10 100644 --- a/build/pkgs/lcalc/checksums.ini +++ b/build/pkgs/lcalc/checksums.ini @@ -1,4 +1,5 @@ -tarball=lcalc-VERSION.tar.bz2 -sha1=b530ae73ff6a668a33660a9b4c404dd23e551abe -md5=de7dc4e06cff7a89c5d922b4acc2be01 -cksum=1982419061 +tarball=lcalc-VERSION.tar.xz +sha1=a5e7aafa6a036815db0c4fd84398baff072fc8b7 +md5=ad68ad9f8b9e120d66ba4cbc6b9a8b18 +cksum=3132152076 +upstream_url=https://gitlab.com/sagemath/lcalc/uploads/4d84022aa5285414eb547121b783601a/lcalc-VERSION.tar.xz diff --git a/build/pkgs/lcalc/dependencies b/build/pkgs/lcalc/dependencies index b955debabd8..d42038dce36 100644 --- a/build/pkgs/lcalc/dependencies +++ b/build/pkgs/lcalc/dependencies @@ -1,4 +1,4 @@ -pari mpfr +pari | gengetopt ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/lcalc/distros/gentoo.txt b/build/pkgs/lcalc/distros/gentoo.txt new file mode 100644 index 00000000000..565a963cad1 --- /dev/null +++ b/build/pkgs/lcalc/distros/gentoo.txt @@ -0,0 +1 @@ +sci-mathematics/lcalc diff --git a/build/pkgs/lcalc/package-version.txt b/build/pkgs/lcalc/package-version.txt index fcbaf9fd8eb..2165f8f9b6a 100644 --- a/build/pkgs/lcalc/package-version.txt +++ b/build/pkgs/lcalc/package-version.txt @@ -1 +1 @@ -1.23.p20 +2.0.4 diff --git a/build/pkgs/lcalc/patches/Lvalue.h.patch b/build/pkgs/lcalc/patches/Lvalue.h.patch deleted file mode 100644 index 245eedaa2c3..00000000000 --- a/build/pkgs/lcalc/patches/Lvalue.h.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/include/Lvalue.h -+++ b/include/Lvalue.h -@@ -486,6 +486,8 @@ - delete [] DELTA; - return L_value*exp(z-.5*log(OMEGA)); - } -+ -+ return L_value*exp(-log(Q)*s)/z; /* as in a later version */ - } - - diff --git a/build/pkgs/lcalc/patches/Makefile.patch b/build/pkgs/lcalc/patches/Makefile.patch deleted file mode 100644 index 1fea170cb6c..00000000000 --- a/build/pkgs/lcalc/patches/Makefile.patch +++ /dev/null @@ -1,240 +0,0 @@ -Notes on changes: -Default to using pari -Sanitize compiler settings and don't set CC as a c++ compiler for everything -Sanitize compiler flags and use standard one -Sanitize default compile rules -Ad hock support for proper extension on windows and OS X -Sanitize calls to make -Remove the use of "PARI_LOCATION*" variables. Everything is in standard location and -patched to be found iin standard location. - -diff --git a/src/Makefile b/src/Makefile -index 84e4e88..5d263a6 100644 ---- a/src/Makefile -+++ b/src/Makefile -@@ -13,7 +13,7 @@ - # elliptic curve routines. Doing so disables the -e option. - # g++ with -DINCLUDE_PARI sends a #define INCLUDE_PARI to the preprocessor. - --#PARI_DEFINE = -DINCLUDE_PARI -+PARI_DEFINE = -DINCLUDE_PARI - #PREPROCESSOR_DEFINE = -DUSE_LONG_DOUBLE - - #OPENMP_FLAG = -fopenmp -@@ -29,11 +29,21 @@ endif - - OS_NAME := $(shell uname) - --CC = g++ -+#CC = g++ - #cc = /home/mrubinst/local/bin/gcc - #CC = /home/mrubinst/local/bin/g++ - #LD = /home/mrubinst/local/bin/g++ - -+ifneq (,$(findstring CYGWIN,$(OS_NAME))) -+ OS_NAME := CYGWIN -+endif -+ -+# Note: I've also changed various rules to use $CXX instead of $CC, -+# since we mostly compile C++, not C, and $CC is by convention -+# used for the *C* compiler. -+CC ?= gcc -+CXX ?= g++ -+ - #CC = /Users/michaelrubinstein/math/L/packages/gcc4.3/usr/local/bin/g++ - #EXTRA= -pg - #EXTRA = -ftree-vectorize -ftree-vectorizer-verbose=5 -funroll-loops -@@ -58,29 +68,12 @@ ifeq ($(G5),TRUE) - #MACHINE_SPECIFIC_FLAGS = -mpowerpc -mpowerpc64 -m64 - endif - --CCFLAGS = -Wa,-W -O3 $(OPENMP_FLAG) -Wno-deprecated $(PREPROCESSOR_DEFINE) $(MACHINE_SPECIFIC_FLAGS) $(EXTRA) --#CCFLAGS = -Wa,-W -O3 $(OPENMP_FLAG) $(PREPROCESSOR_DEFINE) $(MACHINE_SPECIFIC_FLAGS) $(EXTRA) --#CCFLAGS = -Wa,-W -O2 -fno-exceptions -Wno-deprecated $(PREPROCESSOR_DEFINE) $(MACHINE_SPECIFIC_FLAGS) $(EXTRA) -+CXXFLAGS := -O3 $(OPENMP_FLAG) $(PREPROCESSOR_DEFINE) $(MACHINE_SPECIFIC_FLAGS) $(EXTRA) $(CXXFLAGS) - - #warning- O2 doesn't help with -DUSE_LONG_DOUBLE on mac, and actually seems to hurt, making runtime longer - #by a factor of 1.5 - - --ifeq ($(PARI_DEFINE),-DINCLUDE_PARI) -- #location of pari.h. -- LOCATION_PARI_H = /usr/local/include/pari #usual location -- -- #location of libpari.a or of libpari.so -- #depending on whether static or dynamic libraries are being used. -- #On mac os x it's the former, on linux I think usually the latter. -- LOCATION_PARI_LIBRARY = /usr/local/lib #usual location --else -- #supplied as a dummy so as to avoid more ifeq's below -- LOCATION_PARI_H = . -- LOCATION_PARI_LIBRARY = . --endif -- -- - - #INCLUDEFILES= -I../include -I../../packages/gcc4.3/usr/local/include - INCLUDEFILES= -I../include -@@ -88,27 +81,12 @@ INCLUDEFILES= -I../include - #For Mac os x we omit shared library options - - ifeq ($(OS_NAME),Darwin) -- LDFLAGS2 = -- DYN_OPTION=dynamiclib -+ DYN_OPTION=dynamiclib -Wl,-headerpad_max_install_names - else -- LDFLAGS1 = -Xlinker -export-dynamic #not sure why pari calls these when linking but on the web I found -- #'Libtool provides the `-export-dynamic' link flag (see section Link mode), which does this declaration. -- #You need to use this flag if you are linking a shared library that will be dlopened' -- #see notes below -- #ifeq ($(PARI_DEFINE),-DINCLUDE_PARI) -- LDFLAGS2 = $(LDFLAGS1) -Xlinker -rpath -Xlinker $(LOCATION_PARI_LIBRARY) -- #else -- # LDFLAGS2 = $(LDFLAGS1) -- #endif - DYN_OPTION=shared - endif - --ifeq ($(PARI_DEFINE),-DINCLUDE_PARI) -- LDFLAGS = $(LDFLAGS2) -L$(LOCATION_PARI_LIBRARY) -lpari --else -- LDFLAGS = $(LDFLAGS2) --endif -- -+PARI_LIBS = -L$(SAGE_LOCAL)/lib -lpari -lgmp -lm - - - #NOTES: -@@ -129,47 +107,63 @@ endif - #become clear which libraries the computer can find. - - --INSTALL_DIR= /usr/local -+INSTALL_DIR ?= /usr/local -+ -+#binary and library files extensions -+LIBEXT := .so -+EXEEXT := -+ -+ifeq ($(OS_NAME),Darwin) -+ LIBEXT := .dylib -+ EXEEXT := -+endif -+ -+ifeq ($(OS_NAME),CYGWIN) -+ LIBEXT := .dll -+ EXEEXT := .exe -+endif - - #object files for the libLfunction library - OBJ_L = Lglobals.o Lgamma.o Lriemannsiegel.o Lriemannsiegel_blfi.o Ldokchitser.o - - #object files for the command line program --OBJ2=$(OBJ_L) Lcommandline_globals.o Lcommandline_misc.o Lcommandline_numbertheory.o Lcommandline_values_zeros.o --OBJ3=$(OBJ2) Lcommandline_elliptic.o Lcommandline_twist.o Lcommandline.o cmdline.o -+OBJ2 = $(OBJ_L) Lcommandline_globals.o Lcommandline_misc.o Lcommandline_numbertheory.o Lcommandline_values_zeros.o -+OBJ3 = $(OBJ2) Lcommandline_elliptic.o Lcommandline_twist.o Lcommandline.o cmdline.o - OBJECTS = $(OBJ3) - - all: --# make print_vars -- make libLfunction.so -- make lcalc -- make examples --# make find_L --# make test -+# $(MAKE) print_vars -+ $(MAKE) libLfunction$(LIBEXT) -+ $(MAKE) lcalc$(EXEEXT) -+ $(MAKE) examples$(EXEEXT) -+# $(MAKE) find_L -+# $(MAKE) test - - print_vars: - @echo OS_NAME = $(OS_NAME) - --lcalc: $(OBJECTS) -- $(CC) $(CCFLAGS) $(INCLUDEFILES) $(OBJECTS) $(LDFLAGS) -o lcalc $(GMP_FLAGS) -+lcalc$(EXEEXT): $(OBJECTS) -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(LDFLAGS) $(OBJECTS) -o lcalc$(EXEEXT) $(PARI_LIBS) $(GMP_FLAGS) - --examples: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/example.cc libLfunction.so -o example_programs/example $(GMP_FLAGS) -+examples$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/example.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/example$(EXEEXT) $(PARI_LIBS) $(GMP_FLAGS) - - --proc: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/proc.cc libLfunction.so -o example_programs/proc $(GMP_FLAGS) -+proc$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/proc.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/proc$(EXEEXT) $(PARI_LIBS) $(GMP_FLAGS) - --test: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) example_programs/test.cc libLfunction.so -o example_programs/test $(GMP_FLAGS) -+test$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) example_programs/test.cc $(LDFLAGS) libLfunction$(LIBEXT) -o example_programs/test$(EXEEXT) $(PARI_LIBS) $(GMP_FLAGS) - --find_L: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) find_L_functions/find_L_functions.cc libLfunction.so -o find_L_functions/find_L $(GMP_FLAGS) -+find_L$(EXEEXT): -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) find_L_functions/find_L_functions.cc $(LDFLAGS) libLfunction$(LIBEXT) -o find_L_functions/find_L$(EXEEXT) $(PARI_LIBS) $(GMP_FLAGS) - - .cc.o: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) -c $< -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) -c $< -+ -+# Warning: We (Sage) add $CXXFLAGS to CXXFLAGS above. - .c.o: -- $(CC) $(CCFLAGS) $(INCLUDEFILES) -c $< -+ $(CC) $(CFLAGS) $(INCLUDEFILES) -c $< - - - Lglobals.o: ../include/Lglobals.h ../include/Lcommon.h ../include/Lcomplex.h ../include/Lnumeric.h ../include/Lint_complex.h -@@ -227,7 +221,7 @@ Lcommandline_elliptic.o: ../include/Lnumberzeros.h ../include/Lgram.h - Lcommandline_elliptic.o: ../include/Lvalue.h ../include/Lfind_zeros.h - Lcommandline_elliptic.o: ../include/Lcommandline_numbertheory.h - Lcommandline_elliptic.o: ../include/Lcommandline_globals.h -- $(CC) $(CCFLAGS) $(INCLUDEFILES) -I$(LOCATION_PARI_H) $(PARI_DEFINE) -c Lcommandline_elliptic.cc -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(PARI_DEFINE) -c Lcommandline_elliptic.cc - - Lcommandline_twist.o: ../include/Lcommandline_twist.h ../include/L.h - Lcommandline_twist.o: ../include/Lglobals.h ../include/Lcommon.h ../include/Lcomplex.h ../include/Lnumeric.h ../include/Lint_complex.h -@@ -239,7 +233,7 @@ Lcommandline_twist.o: ../include/Lvalue.h ../include/Lfind_zeros.h - Lcommandline_twist.o: ../include/Lcommandline_numbertheory.h - Lcommandline_twist.o: ../include/Lcommandline_globals.h - Lcommandline_twist.o: ../include/Lcommandline_elliptic.h -- $(CC) $(CCFLAGS) $(INCLUDEFILES) -I$(LOCATION_PARI_H) $(PARI_DEFINE) -c Lcommandline_twist.cc -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(PARI_DEFINE) -c Lcommandline_twist.cc - - cmdline.o: ../include/cmdline.h ../include/getopt.h - #$(CC) $(CCFLAGS) $(INCLUDEFILES) -DHAVE_LONG_LONG -c cmdline.c -@@ -258,21 +252,21 @@ Lcommandline.o: ../include/Lcommandline_misc.h - Lcommandline.o: ../include/Lcommandline_elliptic.h - Lcommandline.o: ../include/Lcommandline_twist.h - Lcommandline.o: ../include/Lcommandline_values_zeros.h -- $(CC) $(CCFLAGS) $(INCLUDEFILES) -I$(LOCATION_PARI_H) $(PARI_DEFINE) -c Lcommandline.cc -+ $(CXX) $(CXXFLAGS) $(INCLUDEFILES) $(PARI_DEFINE) -c Lcommandline.cc - - --libLfunction.so: $(OBJ_L) -- g++ -$(DYN_OPTION) -o libLfunction.so $(OBJ_L) -+libLfunction$(LIBEXT): $(OBJ_L) -+ $(CXX) -$(DYN_OPTION) $(CXXFLAGS) -o libLfunction$(LIBEXT) $(LDFLAGS) $(OBJ_L) $(PARI_LIBS) - - clean: -- rm -f *.o lcalc libLfunction.so example_programs/example -+ rm -f *.o lcalc$(EXEEXT) libLfunction$(LIBEXT) example_programs/example$(EXEEXT) - - install: -- cp -f lcalc $(INSTALL_DIR)/bin/. -- cp -f libLfunction.so $(INSTALL_DIR)/lib/. -- cp -rf ../include $(INSTALL_DIR)/include/Lfunction -+ cp -f lcalc$(EXEEXT) $(INSTALL_DIR)/bin/. -+ cp -f libLfunction$(LIBEXT) $(INSTALL_DIR)/lib/. -+ cp -rf ../include $(INSTALL_DIR)/include/Lfunction - - - SRCS = Lcommandline.cc Lcommandline_elliptic.cc Lcommandline_globals.cc Lcommandline_misc.cc Lcommandline_numbertheory.cc Lcommandline_twist.cc Lcommandline_values_zeros.cc Lgamma.cc Lglobals.cc Lmisc.cc Lriemannsiegel.cc Lriemannsiegel_blfi.cc cmdline.c - depend: -- makedepend -f depends -- $(CCFLAGS) -Y../include -- $(SRCS) -+ makedepend -f depends -- $(CXXFLAGS) -Y../include -- $(SRCS) diff --git a/build/pkgs/lcalc/patches/clang.patch b/build/pkgs/lcalc/patches/clang.patch deleted file mode 100644 index 7598f701c3b..00000000000 --- a/build/pkgs/lcalc/patches/clang.patch +++ /dev/null @@ -1,74 +0,0 @@ -diff --git a/include/Lcomplex.h b/include/Lcomplex.h -index aa513a6..a36088e 100644 ---- a/include/Lcomplex.h -+++ b/include/Lcomplex.h -@@ -45,8 +45,6 @@ - - #pragma GCC system_header - --#include -- - //no longer include: - //#include only thing used was is_floating... - //gcc 4.0 cpp_type_traits.h is not compatible with gcc 3.3. -@@ -135,7 +133,7 @@ namespace std - template - complex<_Tp>& operator/=(const complex<_Up>&); - -- friend reset(complex<_Tp>& C) { -+ friend void reset(complex<_Tp>& C) { - reset(C._M_real); - reset(C._M_imag); - } -diff --git a/include/Ldokchitser.h b/include/Ldokchitser.h -index c67f01a..7b3e5c9 100644 ---- a/include/Ldokchitser.h -+++ b/include/Ldokchitser.h -@@ -69,7 +69,7 @@ phi_series(int precision) - - // compute the values m[j] for the respective lambda_k[j] - -- Complex m[N+1]; -+ std::vector m(N+1); - for (j=1;j<=N;j++) - m[j] = -2*lambda_k[j] + 2; - -@@ -78,7 +78,8 @@ phi_series(int precision) - - int n,fact_n; - Complex log_Gamma[N+1][a+1][MYDIGITS+1]; -- Complex sum_log_Gamma[N+1][MYDIGITS+1]; -+ std::vector > sum_log_Gamma(N+1); -+ for (j=1;j<=N;j++) sum_log_Gamma[j].resize(MYDIGITS+1); - - for (j=1;j<=N;j++) - for (n=0;n<=MYDIGITS;n++) -@@ -103,8 +104,13 @@ phi_series(int precision) - - // compute the exponential taylor series for gamma = exp(sum_log_Gamma) - -- Complex exp_sum_log_Gamma[N+1][MYDIGITS+1][MYDIGITS+1]; // symmetric functions -- Complex gamma[N+1][MYDIGITS+1]; // gamma(s+m[j]) for j = 1 to N -+ std::vector > > exp_sum_log_Gamma(N+1); // symmetric functions -+ std::vector > gamma(N+1); // gamma(s+m[j]) for j = 1 to N -+ for (j=1;j<=N;j++){ -+ exp_sum_log_Gamma[j].resize(MYDIGITS+1); -+ gamma[j].resize(MYDIGITS+1); -+ } -+ for (j=1;j<=N;j++) for (n=0;n<=MYDIGITS;n++) exp_sum_log_Gamma[j][n].resize(MYDIGITS+1); - Complex temp_gamma[MYDIGITS+1]; - Complex temp_mult_gamma[MYDIGITS+1]; - Complex temp_exp_sum_log_Gamma[MYDIGITS+1]; -diff --git a/include/Lexplicit_formula.h b/include/Lexplicit_formula.h -index 4b4a4e1..0ede5f6 100644 ---- a/include/Lexplicit_formula.h -+++ b/include/Lexplicit_formula.h -@@ -25,7 +25,7 @@ int L_function :: - dirichlet_coeffs_log_diff(int num_coeffs, Complex *c) - { - -- Complex b[num_coeffs+1]; -+ std::vector b(num_coeffs+1); - int j, n, d1, ind; - Complex total, total2, temp; - diff --git a/build/pkgs/lcalc/patches/lcalc-1.23-gcc11.patch b/build/pkgs/lcalc/patches/lcalc-1.23-gcc11.patch deleted file mode 100644 index bad0f48bb6c..00000000000 --- a/build/pkgs/lcalc/patches/lcalc-1.23-gcc11.patch +++ /dev/null @@ -1,39 +0,0 @@ -diff --git a/include/Lcommon.h b/include/Lcommon.h -index 1b3be43..717fcde 100644 ---- a/include/Lcommon.h -+++ b/include/Lcommon.h -@@ -15,7 +15,7 @@ typedef long long Long; - - - // To aid conversion to int value --inline double lcalc_to_double(const Double& x) { -+inline constexpr double lcalc_to_double(const Double& x) { - #ifdef USE_MPFR - return x.get_d(); - #else -@@ -23,16 +23,16 @@ inline double lcalc_to_double(const Double& x) { - #endif - } - #ifdef USE_MPFR --inline double lcalc_to_double(const double& x) { return x; } -+inline constexpr double lcalc_to_double(const double& x) { return x; } - #endif --//inline double lcalc_to_double(const long double& x) { return x; } --inline double lcalc_to_double(const int& x) { return x; } --inline double lcalc_to_double(const long long& x) { return x; } --inline double lcalc_to_double(const short& x) { return x; } --inline double lcalc_to_double(const char& x) { return x; } --inline double lcalc_to_double(const long int& x) { return x; } --inline double lcalc_to_double(const unsigned int& x) { return x; } --inline double lcalc_to_double(const long unsigned int& x) { return x; } -+inline constexpr double lcalc_to_double(const long double& x) { return x; } -+inline constexpr double lcalc_to_double(const int& x) { return x; } -+inline constexpr double lcalc_to_double(const long long& x) { return x; } -+inline constexpr double lcalc_to_double(const short& x) { return x; } -+inline constexpr double lcalc_to_double(const char& x) { return x; } -+inline constexpr double lcalc_to_double(const long int& x) { return x; } -+inline constexpr double lcalc_to_double(const unsigned int& x) { return x; } -+inline constexpr double lcalc_to_double(const long unsigned int& x) { return x; } - #define Int(x) (int)(lcalc_to_double(x)) - #define Long(x) (Long)(lcalc_to_double(x)) - #define double(x) (double)(lcalc_to_double(x)) diff --git a/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_1.patch b/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_1.patch deleted file mode 100644 index b94fc72d18a..00000000000 --- a/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_1.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Naur lcalc-1.23-vanilla/include/Ldirichlet_series.h lcalc-1.23-fixed-gcc.4.9/include/Ldirichlet_series.h ---- lcalc-1.23-vanilla/include/Ldirichlet_series.h 2012-08-08 23:21:55.000000000 +0200 -+++ lcalc-1.23-fixed-gcc.4.9/include/Ldirichlet_series.h 2014-04-21 14:37:59.027464849 +0200 -@@ -43,7 +43,7 @@ - //XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX - template - Complex L_function :: --dirichlet_series(Complex s, long long N=-1) -+dirichlet_series(Complex s, long long N) - { - Complex z=0.; - long long m,n; -diff -Naur lcalc-1.23-vanilla/include/L.h lcalc-1.23-fixed-gcc.4.9/include/L.h ---- lcalc-1.23-vanilla/include/L.h 2012-08-08 23:21:55.000000000 +0200 -+++ lcalc-1.23-fixed-gcc.4.9/include/L.h 2014-04-21 14:32:04.003467348 +0200 -@@ -491,7 +491,7 @@ - - //#include "Ldirichlet_series.h" //for computing Dirichlet series - Complex partial_dirichlet_series(Complex s, long long N1, long long N2); -- Complex dirichlet_series(Complex s, long long N); -+ Complex dirichlet_series(Complex s, long long N=-1LL); - - //#include "Ltaylor_series.h" //for computing taylor series for Dirichlet series - //void compute_taylor_series(int N, int K, Complex s_0, Complex *series); diff --git a/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_2.patch b/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_2.patch deleted file mode 100644 index f5fd45358d0..00000000000 --- a/build/pkgs/lcalc/patches/lcalc-1.23_default_parameters_2.patch +++ /dev/null @@ -1,56 +0,0 @@ ---- lcalc-1.23/include/Lgamma.h 2012-08-08 23:21:55.000000000 +0200 -+++ lcalc-1.23/include/Lgamma.h 2014-05-18 21:15:27.786889718 +0200 -@@ -77,7 +77,7 @@ - //n=0 should just give log_GAMMA(z)... thus making log_GAMMA - //code obsolete. But leave log_GAMMA intact anyways. - template --precise(ttype) log_GAMMA (ttype z,int n=0) -+precise(ttype) log_GAMMA (ttype z,int n) - { - int M; - precise(ttype) log_G,r,r2,y; -@@ -230,7 +230,7 @@ - //value exp_w which holds exp(-w) - //computes G(z,w), so there's an extra w^(-z) factor. - template --Complex inc_GAMMA (ttype z,ttype w, const char *method="temme", ttype exp_w = 0, bool recycle=false) -+Complex inc_GAMMA (ttype z,ttype w, const char *method, ttype exp_w, bool recycle) - { - - Complex G; -@@ -334,7 +334,7 @@ - - - template --ttype cfrac_GAMMA (ttype z,ttype w, ttype exp_w=0, bool recycle=false) //computes G(z,w) via continued fraction -+ttype cfrac_GAMMA (ttype z,ttype w, ttype exp_w, bool recycle) //computes G(z,w) via continued fraction - { - - ttype G; -@@ -424,7 +424,7 @@ - } - - template --ttype asympt_GAMMA (ttype z,ttype w, ttype exp_w = 0, bool recycle=false) //computes G(z,w) via asymptotic series -+ttype asympt_GAMMA (ttype z,ttype w, ttype exp_w, bool recycle) //computes G(z,w) via asymptotic series - { - - if(my_verbose>3) cout << "called asympt_GAMMA("< --ttype comp_inc_GAMMA (ttype z,ttype w,ttype exp_w = 0, bool recycle=false) //computes g(z,w) -+ttype comp_inc_GAMMA (ttype z,ttype w,ttype exp_w, bool recycle) //computes g(z,w) - { - - ttype g; -@@ -604,7 +604,7 @@ - } - - template --Complex gamma_sum(Complex s, int what_type, ttype *coeff, int N, Double g, Complex l, Double Q, Long Period, Complex delta=1, const char *method="temme") -+Complex gamma_sum(Complex s, int what_type, ttype *coeff, int N, Double g, Complex l, Double Q, Long Period, Complex delta, const char *method) - { - Complex SUM=0; - diff --git a/build/pkgs/lcalc/patches/pari-2.7.patch b/build/pkgs/lcalc/patches/pari-2.7.patch deleted file mode 100644 index d565490d98d..00000000000 --- a/build/pkgs/lcalc/patches/pari-2.7.patch +++ /dev/null @@ -1,67 +0,0 @@ ---- src/src/Lcommandline_elliptic.cc 2010-01-31 16:16:45.000000000 +0100 -+++ src/src/Lcommandline_elliptic.cc 2011-05-10 17:08:10.000000000 +0200 -@@ -121,11 +121,11 @@ - - - F = cgetg(6, t_VEC); -- F[1] = lgeti(BIGDEFAULTPREC); -- F[2] = lgeti(BIGDEFAULTPREC); -- F[3] = lgeti(BIGDEFAULTPREC); -- F[4] = lgeti(BIGDEFAULTPREC); -- F[5] = lgeti(BIGDEFAULTPREC); -+ F[1] = (long)cgeti(BIGDEFAULTPREC); -+ F[2] = (long)cgeti(BIGDEFAULTPREC); -+ F[3] = (long)cgeti(BIGDEFAULTPREC); -+ F[4] = (long)cgeti(BIGDEFAULTPREC); -+ F[5] = (long)cgeti(BIGDEFAULTPREC); - - //gaffsg(a1,(GEN) F[1]); - //gaffsg(a2,(GEN) F[2]); -@@ -133,15 +133,15 @@ - //gaffsg(a4,(GEN) F[4]); - //gaffsg(a6,(GEN) F[5]); - -- gaffect(strtoGEN(a1), (GEN) F[1]); -- gaffect(strtoGEN(a2), (GEN) F[2]); -- gaffect(strtoGEN(a3), (GEN) F[3]); -- gaffect(strtoGEN(a4), (GEN) F[4]); -- gaffect(strtoGEN(a6), (GEN) F[5]); -+ gaffect(gp_read_str(a1), (GEN) F[1]); -+ gaffect(gp_read_str(a2), (GEN) F[2]); -+ gaffect(gp_read_str(a3), (GEN) F[3]); -+ gaffect(gp_read_str(a4), (GEN) F[4]); -+ gaffect(gp_read_str(a6), (GEN) F[5]); - -- E = initell(F,BIGDEFAULTPREC); -+ E = ellinit(F, NULL, BIGDEFAULTPREC); - -- C=globalreduction(E); -+ C=ellglobalred(E); - - x=gtodouble((GEN) C[1]); - -@@ -167,8 +167,8 @@ - - p=n; - gaffsg(p,y); -- coeff[p] = Double(1.*llrint(gtodouble(apell(E,y))))/sqrt(Double(1.*p)); -- //coeff[p] = Double(1.*Long(gtodouble(apell(E,y))+.1))/sqrt(Double(1.*p)); -+ coeff[p] = Double(1.*llrint(gtodouble(ellap(E,y))))/sqrt(Double(1.*p)); -+ //coeff[p] = Double(1.*Long(gtodouble(ellap(E,y))+.1))/sqrt(Double(1.*p)); - - if(gtolong(gmod((GEN) E[12],(GEN) y))==0) // if p|discriminant, i.e. bad reduction - { -diff -ru src/src/Lcommandline.cc b/src/Lcommandline.cc ---- src/src/Lcommandline.cc 2012-08-08 23:21:56.000000000 +0200 -+++ b/src/Lcommandline.cc 2014-01-06 14:02:19.463388366 +0100 -@@ -473,7 +473,9 @@ - - #ifdef INCLUDE_PARI - if(do_elliptic_curve){ -- allocatemoremem((int) N_terms*16+1000000); //XXXXXXXXX this should depend on whether we're double or long double or mpfr double -+ // Reallocate PARI stack -+ paristack_setsize((size_t)N_terms*16 + 1000000, 0); //XXXXXXXXX this should depend on whether we're double or long double or mpfr double -+ - if (my_verbose>0) cout << "Will precompute " << N_terms << " elliptic L-function dirichlet coefficients..." << endl; - initialize_new_L(a1,a2,a3,a4,a6,N_terms); - } diff --git a/build/pkgs/lcalc/patches/pari-mem.patch b/build/pkgs/lcalc/patches/pari-mem.patch deleted file mode 100644 index 1566681a909..00000000000 --- a/build/pkgs/lcalc/patches/pari-mem.patch +++ /dev/null @@ -1,24 +0,0 @@ -Use a much smaller PARI stack size for starting up - -Actually 1MB is sufficient, so there is plenty of margin -with the 16MB in this patch - -See https://trac.sagemath.org/ticket/24516 - -diff -ru lcalc-1.23/src/Lcommandline.cc lcalc-1.23-patched//src/Lcommandline.cc ---- lcalc-1.23/src/Lcommandline.cc 2012-08-08 23:21:56.000000000 +0200 -+++ lcalc-1.23-patched//src/Lcommandline.cc 2018-01-30 11:23:06.975418539 +0100 -@@ -412,12 +412,7 @@ - - t2=.5; //t2=.5 because of the GAMMA(s+1/2) - -- pari_init(1000000000,2); -- //pari_init_opts(400000000,2,INIT_DFTm); // the last option is to prevent -- //pari from giving its interrupt signal when its elliptic curve a_p -- //algorithm is called and interrupted with ctrl-c. Requires a more current -- //version of pari, so use pari_init above until I have a configure set up -- //that detects which pari, if any, is installed. -+ pari_init_opts(16000000, 2, INIT_DFTm); - - coeff = new Double[3]; - //compute the conductor which is copied to coeff[1] diff --git a/build/pkgs/lcalc/patches/pari_include.patch b/build/pkgs/lcalc/patches/pari_include.patch deleted file mode 100644 index 8ad1cc1cc43..00000000000 --- a/build/pkgs/lcalc/patches/pari_include.patch +++ /dev/null @@ -1,30 +0,0 @@ -Notes: -Use a standard include path for pari. It means there is no need -to specify an include path when pari headers are in a standard location. - -diff --git a/include/Lcommandline.h b/include/Lcommandline.h -index 6537e98..5e63e4e 100644 ---- a/include/Lcommandline.h -+++ b/include/Lcommandline.h -@@ -39,7 +39,7 @@ - - #include "Lcommandline_globals.h" //command line global variables - #ifdef INCLUDE_PARI --#include "pari.h" //for pari's elliptic curve functions -+#include "pari/pari.h" //for pari's elliptic curve functions - #undef init //pari has a '#define init pari_init' which - //causes trouble with the stream.h init. - //pari also causes trouble with things like abs. -diff --git a/include/Lcommandline_elliptic.h b/include/Lcommandline_elliptic.h -index 17fa42c..7d80208 100644 ---- a/include/Lcommandline_elliptic.h -+++ b/include/Lcommandline_elliptic.h -@@ -32,7 +32,7 @@ - - - #ifdef INCLUDE_PARI --#include "pari.h" //for pari's elliptic curve functions -+#include "pari/pari.h" //for pari's elliptic curve functions - #undef init //pari has a '#define init pari_init' which - //causes trouble with the stream.h init. - //pari also causes trouble with things like abs. diff --git a/build/pkgs/lcalc/patches/time.h.patch b/build/pkgs/lcalc/patches/time.h.patch deleted file mode 100644 index 5af57b1b914..00000000000 --- a/build/pkgs/lcalc/patches/time.h.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- src/include/Lcommandline_numbertheory.h 2010-01-31 07:16:45.000000000 -0800 -+++ src/include/Lcommandline_numbertheory.h 2010-08-31 18:53:18.000000000 -0700 -@@ -24,6 +24,7 @@ - #ifndef Lcommandline_numbertheory_H - #define Lcommandline_numbertheory_H - -+#include - #include //for things like srand - #include //for input and output - #include //for manipulating output such as setprecision diff --git a/build/pkgs/lcalc/patches/using_namespace_std.patch b/build/pkgs/lcalc/patches/using_namespace_std.patch deleted file mode 100644 index 6d733ffd711..00000000000 --- a/build/pkgs/lcalc/patches/using_namespace_std.patch +++ /dev/null @@ -1,41 +0,0 @@ -diff --git a/include/Lcommon.h b/include/Lcommon.h -index 1b3be43..bf40532 100644 ---- a/include/Lcommon.h -+++ b/include/Lcommon.h -@@ -48,7 +48,7 @@ const bool outputSeries=true; // Whether to output the coefficients or just th - - // Loop i from m to n - // Useful in tidying up most for loops --#define loop(i,m,n) for(typeof(m) i=(m); i!=(n); i++) -+#define loop(i,m,n) for(auto i=(m); i!=(n); i++) - - // A class for calculations involving polynomials of small degree - // Not efficient enough for huge polynomials -diff --git a/include/Lcommon_ld.h b/include/Lcommon_ld.h -index 86ae4df..33c560c 100644 ---- a/include/Lcommon_ld.h -+++ b/include/Lcommon_ld.h -@@ -53,7 +53,7 @@ const bool outputSeries=true; // Whether to output the coefficients or just th - - // Loop i from m to n - // Useful in tidying up most for loops --#define loop(i,m,n) for(typeof(m) i=(m); i!=(n); i++) -+#define loop(i,m,n) for(auto i=(m); i!=(n); i++) - - // A class for calculations involving polynomials of small degree - // Not efficient enough for huge polynomials -diff --git a/include/Lglobals.h b/include/Lglobals.h -index 60002e4..ca2606c 100644 ---- a/include/Lglobals.h -+++ b/include/Lglobals.h -@@ -24,9 +24,9 @@ - #ifndef Lglobals_H - #define Lglobals_H - -+#include - using namespace std; - --#include - #ifdef USE_MPFR - #include "Lgmpfrxx.h" - typedef mpfr_class Double; diff --git a/build/pkgs/lcalc/spkg-build.in b/build/pkgs/lcalc/spkg-build.in index a5c67c2cafa..a5ce6bfaa61 100644 --- a/build/pkgs/lcalc/spkg-build.in +++ b/build/pkgs/lcalc/spkg-build.in @@ -1,14 +1,3 @@ -# Using pari in a C++17 file with "using namespace std doesn't -# work due to a conflict between std::rank and pari's rank -CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") - -# Export everything. Probably not necessary in most cases. -export CXXFLAGS - -export DEFINES="" - -cd src/src # Now we are in src/src. - -# Build everything: -echo "Now building lcalc, example programs and the shared library..." +cd src +sdh_configure --with-pari sdh_make diff --git a/build/pkgs/lcalc/spkg-check.in b/build/pkgs/lcalc/spkg-check.in new file mode 100644 index 00000000000..45b317a382c --- /dev/null +++ b/build/pkgs/lcalc/spkg-check.in @@ -0,0 +1,2 @@ +cd src +sdh_make_check diff --git a/build/pkgs/lcalc/spkg-configure.m4 b/build/pkgs/lcalc/spkg-configure.m4 index 4225eef4e7a..5ef2e5a36a3 100644 --- a/build/pkgs/lcalc/spkg-configure.m4 +++ b/build/pkgs/lcalc/spkg-configure.m4 @@ -1,46 +1,40 @@ SAGE_SPKG_CONFIGURE([lcalc], [ - m4_pushdef([SAGE_LCALC_MINVER],["1.22"]) - SAGE_SPKG_DEPCHECK([pari mpfr], [ - AC_PATH_PROG([LCALC], [lcalc]) - AS_IF([test x$LCALC = x], [ - AC_MSG_NOTICE([lcalc not found. Installing lcalc]) - sage_spkg_install_lcalc=yes], [ - AC_MSG_CHECKING([is lcalc's version good enough? ]) - lcalc_ver=`$LCALC --version 2>>/dev/null | $SED -e 's/lcalc\ //' | $SED -e 's/\ .*//g'` - AX_COMPARE_VERSION([$lcalc_ver], [ge], [$SAGE_LCALC_MINVER], [ - AC_MSG_RESULT([yes.]) - AC_CHECK_HEADER([Lfunction/L.h], [], [sage_spkg_install_lcalc=yes]) - AC_MSG_CHECKING([whether we can link and run a program using libLfunction]) - LCALC_SAVED_LIBS=$LIBS - LIBS="$LIBS -lLfunction" - AC_RUN_IFELSE([ - AC_LANG_PROGRAM([[#include ]], + SAGE_SPKG_DEPCHECK([pari], [ + + dnl Ensure that the "lcalc" program is in the user's executable PATH, + dnl and that our headers are in his compiler's "include" path. + AC_PATH_PROG([LCALC_BIN], [lcalc]) + AS_IF([test -z "${LCALC_BIN}"], [sage_spkg_install_lcalc=yes]) + AC_CHECK_HEADER([lcalc/L.h], [], [sage_spkg_install_lcalc=yes]) + + dnl Check for the lcalc-2.x API that we now use, ensure that + dnl that HAVE_LIBPARI is defined in L.h (via lcalc/config.h), + dnl and check for PRECISION_DOUBLE in the same place. + AC_MSG_CHECKING([for double-precision libLfunction >= 2.0.0 with pari support]) + AC_LANG_PUSH([C++]) + LCALC_SAVED_LIBS="${LIBS}" + LIBS="${LIBS} -lLfunction" + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[#include + #if !HAVE_LIBPARI + #error libLfunction missing PARI support + #endif + #if !PRECISION_DOUBLE + #error libLfunction must use double-precision + #endif]], [[initialize_globals(); - Complex x; - x = Pi*I; - L_function L4; - return 0;]] - )], [AC_MSG_RESULT([yes; use lcalc from the system])], [ - AC_MSG_RESULT([no; install lcalc]) - sage_spkg_install_lcalc=yes - LIBS=$LCALC_SAVED_LIBS - ], [ - AC_MSG_RESULT([cross compiling; check linking only]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[#include ]], - [[initialize_globals(); - Complex x; - x = Pi*I; - L_function L4; - return 0;]] - )], [AC_MSG_RESULT([yes; use lcalc from the system])], [ - AC_MSG_RESULT([no; install lcalc]) - sage_spkg_install_lcalc=yes - LIBS=$LCALC_SAVED_LIBS - ]) - ]) - ]) - ]) + vector zeros; + L_function zeta; + zeta.find_zeros(1, 0, 1025, -1, "", &zeros); + return 0;]]) + ], [ + AC_MSG_RESULT([found; using lcalc from the system]) + ], [ + AC_MSG_RESULT([not found; installing the lcalc SPKG]) + sage_spkg_install_lcalc=yes + LIBS="${LCALC_SAVED_LIBS}" ]) - m4_popdef([SAGE_LCALC_MINVER]) + AC_LANG_POP([C++]) + + ]) ]) diff --git a/build/pkgs/lcalc/spkg-install.in b/build/pkgs/lcalc/spkg-install.in index 9000051aa58..3ea8c053669 100644 --- a/build/pkgs/lcalc/spkg-install.in +++ b/build/pkgs/lcalc/spkg-install.in @@ -1,18 +1,2 @@ -cd src/src - -if [ "$UNAME" = "Darwin" ]; then - export LDFLAGS="$LDFLAGS -Wl,-headerpad_max_install_names" -fi - -# Using pari in a C++17 file with "using namespace std doesn't -# work due to a conflict between std::rank and pari's rank -CXXFLAGS=$(echo "${CXXFLAGS}" | sed "s/-std=c++17//g") - -export CXXFLAGS -mkdir -p "$SAGE_DESTDIR_LOCAL"/{bin,include,lib} -sdh_make_install INSTALL_DIR="$SAGE_DESTDIR_LOCAL" - -if [ "$UNAME" = "Darwin" ]; then - install_name_tool -id "${SAGE_LOCAL}/lib/libLfunction.dylib" \ - "${SAGE_DESTDIR_LOCAL}/lib/libLfunction.dylib" || exit $? -fi +cd src +sdh_make_install diff --git a/build/pkgs/libgraphviz/SPKG.rst b/build/pkgs/libgraphviz/SPKG.rst new file mode 100644 index 00000000000..3c7f04a78f1 --- /dev/null +++ b/build/pkgs/libgraphviz/SPKG.rst @@ -0,0 +1,20 @@ +libgraphviz: Graph visualization software (callable library) +============================================================ + +Description +----------- + +Graphviz is open source graph visualization software. It has several main graph layout programs. +They take descriptions of graphs in a simple text language, and make diagrams in several useful formats. + +This script package represents the callable library. + +License +------- + +Eclipse Public License 1.0 + +Upstream Contact +---------------- + +https://graphviz.org/about/ diff --git a/build/pkgs/libgraphviz/distros/alpine.txt b/build/pkgs/libgraphviz/distros/alpine.txt new file mode 100644 index 00000000000..52aa6f38376 --- /dev/null +++ b/build/pkgs/libgraphviz/distros/alpine.txt @@ -0,0 +1 @@ +graphviz-dev diff --git a/build/pkgs/libgraphviz/distros/arch.txt b/build/pkgs/libgraphviz/distros/arch.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/arch.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/conda.txt b/build/pkgs/libgraphviz/distros/conda.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/conda.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/cygwin.txt b/build/pkgs/libgraphviz/distros/cygwin.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/cygwin.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/debian.txt b/build/pkgs/libgraphviz/distros/debian.txt new file mode 100644 index 00000000000..0552e427799 --- /dev/null +++ b/build/pkgs/libgraphviz/distros/debian.txt @@ -0,0 +1 @@ +libgraphviz-dev diff --git a/build/pkgs/libgraphviz/distros/fedora.txt b/build/pkgs/libgraphviz/distros/fedora.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/fedora.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/freebsd.txt b/build/pkgs/libgraphviz/distros/freebsd.txt new file mode 100644 index 00000000000..f76a7c14a97 --- /dev/null +++ b/build/pkgs/libgraphviz/distros/freebsd.txt @@ -0,0 +1 @@ +graphics/graphviz diff --git a/build/pkgs/libgraphviz/distros/homebrew.txt b/build/pkgs/libgraphviz/distros/homebrew.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/homebrew.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/macports.txt b/build/pkgs/libgraphviz/distros/macports.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/macports.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/nix.txt b/build/pkgs/libgraphviz/distros/nix.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/nix.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/opensuse.txt b/build/pkgs/libgraphviz/distros/opensuse.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/opensuse.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/repology.txt b/build/pkgs/libgraphviz/distros/repology.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/repology.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/distros/void.txt b/build/pkgs/libgraphviz/distros/void.txt new file mode 100644 index 00000000000..4d95609306f --- /dev/null +++ b/build/pkgs/libgraphviz/distros/void.txt @@ -0,0 +1 @@ +graphviz diff --git a/build/pkgs/libgraphviz/spkg-configure.m4 b/build/pkgs/libgraphviz/spkg-configure.m4 new file mode 100644 index 00000000000..124b00c7e34 --- /dev/null +++ b/build/pkgs/libgraphviz/spkg-configure.m4 @@ -0,0 +1,3 @@ +SAGE_SPKG_CONFIGURE([libgraphviz], [ + AC_CHECK_HEADER([graphviz/cgraph.h], [], [sage_spkg_install_libgraphviz=yes]) +]) diff --git a/build/pkgs/libgraphviz/type b/build/pkgs/libgraphviz/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/libgraphviz/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index ec00516a1d6..b736feed312 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,5 +1,5 @@ tarball=pari-VERSION.tar.gz -sha1=40731c850cc50fb4994148fac49fda4f68ce6106 -md5=826064cf75af268be8a482ade6e27501 -cksum=550849474 +sha1=e01647aab7e96a8cb4922cf26a4f224337c6647f +md5=922f740fcdf8630b30d63dc76b58f756 +cksum=297133525 upstream_url=https://pari.math.u-bordeaux.fr/pub/pari/unix/pari-VERSION.tar.gz diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index 94f15e9cc30..a1a4224dd5e 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.13.1 +2.13.3 diff --git a/build/pkgs/pari/patches/pari-rnfdisc.patch b/build/pkgs/pari/patches/pari-rnfdisc.patch deleted file mode 100644 index 39d325911e8..00000000000 --- a/build/pkgs/pari/patches/pari-rnfdisc.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 3edb98db78dd49bb8b4137b46781a7cd570c2556 Mon Sep 17 00:00:00 2001 -From: Bill Allombert -Date: Sun, 28 Mar 2021 13:27:24 +0200 -Subject: [PATCH] rnfdisc_factored: remove spurious Q_primpart [#2284] - -diff --git a/src/basemath/base2.c b/src/basemath/base2.c -index b2b63ada5..531f5c558 100644 ---- a/src/basemath/base2.c -+++ b/src/basemath/base2.c -@@ -3582,7 +3582,7 @@ rnfdisc_factored(GEN nf, GEN pol, GEN *pd) - - nf = checknf(nf); - pol = rnfdisc_get_T(nf, pol, &lim); -- disc = nf_to_scalar_or_basis(nf, nfX_disc(nf, Q_primpart(pol))); -+ disc = nf_to_scalar_or_basis(nf, nfX_disc(nf, pol)); - pol = nfX_to_monic(nf, pol, NULL); - fa = idealfactor_partial(nf, disc, lim); - P = gel(fa,1); l = lg(P); -diff --git a/src/test/32/rnf b/src/test/32/rnf -index 6bd4585..d24e1ce 100644 (file) ---- a/src/test/32/rnf -+++ b/src/test/32/rnf -@@ -832,9 +832,9 @@ error("inconsistent dimensions in idealtwoelt.") - 0 - 0 - 1 --[[7361, 3786, 318, 5823; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1], [-3, 6, -2, 0] --~] --[2, -1] -+[[433, 322, 318, 1318/17; 0, 1, 0, 12/17; 0, 0, 1, 5/17; 0, 0, 0, 1/17], [25 -+/17, -12/17, 12/17, 16/17]~] -+[1, -1] - *** at top-level: rnfdedekind(nf,P,pr2,1) - *** ^----------------------- - *** rnfdedekind: sorry, Dedekind in the difficult case is not yet implemented. diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 8fbd5a77b58..572abe42c1a 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=c6c9348c79a7f8448cd14b9e53baf141ea185dee -md5=efbdb4201a5e6383fb4d12e26f78f355 -cksum=2583473334 +sha1=a243525070cf70f22a185447ebd3e1435dabb218 +md5=0d3f27f4b7fecb33fd573e4f46cc6788 +cksum=3764331442 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/install-requires.txt b/build/pkgs/pip/install-requires.txt index 326e51e0b08..7af2e246633 100644 --- a/build/pkgs/pip/install-requires.txt +++ b/build/pkgs/pip/install-requires.txt @@ -1,2 +1,2 @@ -pip >=21.1.0 -# for use of "pip --use-feature=in-tree-build" by the Sage distribution +pip >=21.3 +# for use of the "in-tree-build" feature, default since 21.3, by the Sage distribution diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 0f058b4809c..0fac3b01f54 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -21.2.4 +21.3.1 diff --git a/build/pkgs/pip/patches/10574.patch b/build/pkgs/pip/patches/10574.patch new file mode 100644 index 00000000000..365b5a9edf0 --- /dev/null +++ b/build/pkgs/pip/patches/10574.patch @@ -0,0 +1,544 @@ +Backported by deleting changes to file tests/unit/resolution_resolvelib/test_resolver.py + +From af383922b226f7caeac79f98d7a2991be86ab904 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Tue, 12 Oct 2021 10:36:35 +0200 +Subject: [PATCH 01/13] Simplify graph in get_topological_weights. + +Fixes https://github.com/pypa/pip/issues/10557 where the resolver spends too much time calculating the weights. + +Also, do not let `get_installation_order` calculate these weights at all when there is nothing left to install. +--- + news/10557.bugfix.rst | 1 + + .../resolution/resolvelib/resolver.py | 41 ++++++++++++++++++- + .../resolution_resolvelib/test_resolver.py | 2 +- + 3 files changed, 42 insertions(+), 2 deletions(-) + create mode 100644 news/10557.bugfix.rst + +diff --git a/news/10557.bugfix.rst b/news/10557.bugfix.rst +new file mode 100644 +index 00000000000..d902cea3c3d +--- /dev/null ++++ b/news/10557.bugfix.rst +@@ -0,0 +1 @@ ++Simplify the graph when calculating weights for the installation order. +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index 12f96702024..8e35c3dc838 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -177,6 +177,10 @@ def get_installation_order( + """ + assert self._result is not None, "must call resolve() first" + ++ if not req_set.requirements: ++ # Nothing is left to install, so we do not need an order. ++ return [] ++ + graph = self._result.graph + weights = get_topological_weights( + graph, +@@ -213,6 +217,35 @@ def get_topological_weights( + path: Set[Optional[str]] = set() + weights: Dict[Optional[str], int] = {} + ++ # Make a copy of the graph and edit the copy, ++ # instead of changing the original. ++ cgraph: "DirectedGraph[Optional[str]]" = graph.copy() ++ ++ def simplify_graph(): ++ # In the first pass, we iterate over the original graph, ++ # looking for any keys that have no dependencies themselves. ++ # Use a large weight for them. ++ # Actually, maybe not. ++ leaves = set() ++ for key in cgraph: ++ if not cgraph._forwards[key] and key is not None: ++ leaves.add(key) ++ if not leaves: ++ # We are done simplifying. ++ return ++ # Calculate the weight for the leaves. ++ weight = len(cgraph) - 1 ++ if weight == 0: ++ # Precaution against dividing by zero. We are done. ++ return ++ for leave in leaves: ++ weights[leave] = weight ++ # Remove the leaves from the copy of the graph, making the copy simpler. ++ for leave in leaves: ++ cgraph.remove(leave) ++ # Now that we have a simpler graph, try to simplify it again. ++ simplify_graph() ++ + def visit(node: Optional[str]) -> None: + if node in path: + # We hit a cycle, so we'll break it here. +@@ -220,13 +253,19 @@ def visit(node: Optional[str]) -> None: + + # Time to visit the children! + path.add(node) +- for child in graph.iter_children(node): ++ for child in cgraph.iter_children(node): + visit(child) + path.remove(node) + + last_known_parent_count = weights.get(node, 0) + weights[node] = max(last_known_parent_count, len(path)) + ++ # Recursively simplify the graph, pruning leaves that have no dependencies. ++ # This is needed for large graphs (say over 200 packages) because the ++ # `visit` function is exponentially slower then, taking minutes. ++ # See https://github.com/pypa/pip/issues/10557 ++ simplify_graph() ++ # Visit the remaining graph. + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + +From 3983756b43dc321d6f6a1c8ab58acd8dcd16db23 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Tue, 12 Oct 2021 13:11:53 +0200 +Subject: [PATCH 02/13] fix mypy + +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index 8e35c3dc838..0ff54e677d7 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -221,7 +221,7 @@ def get_topological_weights( + # instead of changing the original. + cgraph: "DirectedGraph[Optional[str]]" = graph.copy() + +- def simplify_graph(): ++ def simplify_graph() -> None: + # In the first pass, we iterate over the original graph, + # looking for any keys that have no dependencies themselves. + # Use a large weight for them. + +From 9a973209149ea0ce6bf46a911cbc42ea21969e6c Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Tue, 12 Oct 2021 13:13:24 +0200 +Subject: [PATCH 03/13] Removed outdated comment and no longer needed code. + +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index 0ff54e677d7..ebab591c639 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -222,10 +222,6 @@ def get_topological_weights( + cgraph: "DirectedGraph[Optional[str]]" = graph.copy() + + def simplify_graph() -> None: +- # In the first pass, we iterate over the original graph, +- # looking for any keys that have no dependencies themselves. +- # Use a large weight for them. +- # Actually, maybe not. + leaves = set() + for key in cgraph: + if not cgraph._forwards[key] and key is not None: +@@ -235,9 +231,6 @@ def simplify_graph() -> None: + return + # Calculate the weight for the leaves. + weight = len(cgraph) - 1 +- if weight == 0: +- # Precaution against dividing by zero. We are done. +- return + for leave in leaves: + weights[leave] = weight + # Remove the leaves from the copy of the graph, making the copy simpler. + +From 7e53097bb35cd284d52c0f6580e692ee5c91849a Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Tue, 12 Oct 2021 14:18:29 +0200 +Subject: [PATCH 04/13] Fixed another mypy warning, about DiGraph having no + attr _forwards. + +I am not used to mypy and did not have pre-commit installed yet. +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index ebab591c639..c68deb5562c 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -224,7 +224,7 @@ def get_topological_weights( + def simplify_graph() -> None: + leaves = set() + for key in cgraph: +- if not cgraph._forwards[key] and key is not None: ++ if not list(cgraph.iter_children(key)) and key is not None: + leaves.add(key) + if not leaves: + # We are done simplifying. + +From a6b930b2263529940320c1909c60361f62f031ac Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Mon, 25 Oct 2021 09:58:46 +0200 +Subject: [PATCH 05/13] get_topological_weights: minor improvements from + reviews. + +--- + .../resolution/resolvelib/resolver.py | 22 +++++++++---------- + 1 file changed, 10 insertions(+), 12 deletions(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index c68deb5562c..cda2ffc443c 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -217,25 +217,23 @@ def get_topological_weights( + path: Set[Optional[str]] = set() + weights: Dict[Optional[str], int] = {} + +- # Make a copy of the graph and edit the copy, +- # instead of changing the original. +- cgraph: "DirectedGraph[Optional[str]]" = graph.copy() +- + def simplify_graph() -> None: + leaves = set() +- for key in cgraph: +- if not list(cgraph.iter_children(key)) and key is not None: ++ for key in graph: ++ if key is None: ++ continue ++ if next(graph.iter_children(key), None) is not None: + leaves.add(key) + if not leaves: + # We are done simplifying. + return + # Calculate the weight for the leaves. +- weight = len(cgraph) - 1 +- for leave in leaves: +- weights[leave] = weight ++ weight = len(graph) - 1 ++ for leaf in leaves: ++ weights[leaf] = weight + # Remove the leaves from the copy of the graph, making the copy simpler. +- for leave in leaves: +- cgraph.remove(leave) ++ for leaf in leaves: ++ graph.remove(leaf) + # Now that we have a simpler graph, try to simplify it again. + simplify_graph() + +@@ -246,7 +244,7 @@ def visit(node: Optional[str]) -> None: + + # Time to visit the children! + path.add(node) +- for child in cgraph.iter_children(node): ++ for child in graph.iter_children(node): + visit(child) + path.remove(node) + + +From 11ce17e84a59dcf3af32c48ca27b9d151caa0170 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Mon, 25 Oct 2021 10:48:29 +0200 +Subject: [PATCH 06/13] get_topological_weights: fixed condition. + +After minor refactoring, the condition was the wrong way around. +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index cda2ffc443c..2f27badc069 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -222,7 +222,7 @@ def simplify_graph() -> None: + for key in graph: + if key is None: + continue +- if next(graph.iter_children(key), None) is not None: ++ if next(graph.iter_children(key), None) is None: + leaves.add(key) + if not leaves: + # We are done simplifying. + +From 6145342ba71d8e9521984a0fe4c0eed32166f205 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Mon, 25 Oct 2021 11:56:45 +0200 +Subject: [PATCH 07/13] get_topological_weights: iterate instead of calling + next. + +'next' gives mypy errors. +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index 2f27badc069..bc99d5ad733 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -222,7 +222,11 @@ def simplify_graph() -> None: + for key in graph: + if key is None: + continue +- if next(graph.iter_children(key), None) is None: ++ for _child in graph.iter_children(key): ++ # This means we have at least one child ++ break ++ else: ++ # No child. + leaves.add(key) + if not leaves: + # We are done simplifying. + +From b5c0b82c8276c8e8262a87121ff0c6812339afa4 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Tue, 16 Nov 2021 18:33:54 +0100 +Subject: [PATCH 08/13] Replace the recursive simplify_graph function with a + while loop. + +This is easier to wrap your head around. +--- + .../resolution/resolvelib/resolver.py | 44 +++++++++---------- + 1 file changed, 21 insertions(+), 23 deletions(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index bc99d5ad733..a0f9acaeca9 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -217,7 +217,26 @@ def get_topological_weights( + path: Set[Optional[str]] = set() + weights: Dict[Optional[str], int] = {} + +- def simplify_graph() -> None: ++ def visit(node: Optional[str]) -> None: ++ if node in path: ++ # We hit a cycle, so we'll break it here. ++ return ++ ++ # Time to visit the children! ++ path.add(node) ++ for child in graph.iter_children(node): ++ visit(child) ++ path.remove(node) ++ ++ last_known_parent_count = weights.get(node, 0) ++ weights[node] = max(last_known_parent_count, len(path)) ++ ++ # Simplify the graph, pruning leaves that have no dependencies. ++ # This is needed for large graphs (say over 200 packages) because the ++ # `visit` function is exponentially slower then, taking minutes. ++ # See https://github.com/pypa/pip/issues/10557 ++ # We will loop until we explicitly break the loop. ++ while True: + leaves = set() + for key in graph: + if key is None: +@@ -230,7 +249,7 @@ def simplify_graph() -> None: + leaves.add(key) + if not leaves: + # We are done simplifying. +- return ++ break + # Calculate the weight for the leaves. + weight = len(graph) - 1 + for leaf in leaves: +@@ -238,28 +257,7 @@ def simplify_graph() -> None: + # Remove the leaves from the copy of the graph, making the copy simpler. + for leaf in leaves: + graph.remove(leaf) +- # Now that we have a simpler graph, try to simplify it again. +- simplify_graph() + +- def visit(node: Optional[str]) -> None: +- if node in path: +- # We hit a cycle, so we'll break it here. +- return +- +- # Time to visit the children! +- path.add(node) +- for child in graph.iter_children(node): +- visit(child) +- path.remove(node) +- +- last_known_parent_count = weights.get(node, 0) +- weights[node] = max(last_known_parent_count, len(path)) +- +- # Recursively simplify the graph, pruning leaves that have no dependencies. +- # This is needed for large graphs (say over 200 packages) because the +- # `visit` function is exponentially slower then, taking minutes. +- # See https://github.com/pypa/pip/issues/10557 +- simplify_graph() + # Visit the remaining graph. + # `None` is guaranteed to be the root node by resolvelib. + visit(None) + +From 9408cad47390e60a18e56b041748e90defdb3d7e Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Wed, 17 Nov 2021 10:49:34 +0100 +Subject: [PATCH 09/13] Update news/10557.bugfix.rst + +Co-authored-by: Tzu-ping Chung +--- + news/10557.bugfix.rst | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/news/10557.bugfix.rst b/news/10557.bugfix.rst +index d902cea3c3d..2381ea90310 100644 +--- a/news/10557.bugfix.rst ++++ b/news/10557.bugfix.rst +@@ -1 +1 @@ +-Simplify the graph when calculating weights for the installation order. ++Optimize installation order calculation to improve performance when installing requirements that form a complex depedency graph with a large amount of edges. + +From d07cc8cbd762ef1e795bed3948802c6cc5bcd1d0 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Wed, 17 Nov 2021 10:50:19 +0100 +Subject: [PATCH 10/13] Fixed typo in news snippet. + +--- + news/10557.bugfix.rst | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/news/10557.bugfix.rst b/news/10557.bugfix.rst +index 2381ea90310..5a6a4ef26a9 100644 +--- a/news/10557.bugfix.rst ++++ b/news/10557.bugfix.rst +@@ -1 +1 @@ +-Optimize installation order calculation to improve performance when installing requirements that form a complex depedency graph with a large amount of edges. ++Optimize installation order calculation to improve performance when installing requirements that form a complex dependency graph with a large amount of edges. + +From 43d94346b6363421ce8837be74bb81d71e5e2584 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Wed, 17 Nov 2021 20:59:55 +0100 +Subject: [PATCH 11/13] Fixed comment that still talked about a copy of the + graph. + +--- + src/pip/_internal/resolution/resolvelib/resolver.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index a0f9acaeca9..c84c697db4c 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -254,7 +254,7 @@ def visit(node: Optional[str]) -> None: + weight = len(graph) - 1 + for leaf in leaves: + weights[leaf] = weight +- # Remove the leaves from the copy of the graph, making the copy simpler. ++ # Remove the leaves from the graph, making it simpler. + for leaf in leaves: + graph.remove(leaf) + + +From 39ce7b5f241c7c6d0c2cebdb903bd9aacdd5abd4 Mon Sep 17 00:00:00 2001 +From: Maurits van Rees +Date: Wed, 17 Nov 2021 22:36:11 +0100 +Subject: [PATCH 12/13] Updated docstring of get_installation_order and + get_topological_weights. + +--- + .../resolution/resolvelib/resolver.py | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index c84c697db4c..2877d7552e7 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -171,9 +171,11 @@ def get_installation_order( + get installed one-by-one. + + The current implementation creates a topological ordering of the +- dependency graph, while breaking any cycles in the graph at arbitrary +- points. We make no guarantees about where the cycle would be broken, +- other than they would be broken. ++ dependency graph, giving more weight to packages with less ++ or no dependencies, while breaking any cycles in the graph at ++ arbitrary points. ++ We make no guarantees about where the cycle would be broken, ++ other than it *would* be broken. + """ + assert self._result is not None, "must call resolve() first" + +@@ -203,13 +205,22 @@ def get_topological_weights( + This implementation may change at any point in the future without prior + notice. + ++ We first simplify the dependency graph by pruning any leaves ++ and giving them the highest weight: a package without any dependencies ++ should be installed first. ++ We simplify again and again in the same way, giving ever less weight ++ to the newly found leaves. ++ This stops when no leaves are left: all remaining packages have at least ++ one dependency left in the graph. ++ ++ Then we continue with the remaining graph. + We take the length for the longest path to any node from root, ignoring any + paths that contain a single node twice (i.e. cycles). This is done through + a depth-first search through the graph, while keeping track of the path to + the node. + + Cycles in the graph result would result in node being revisited while also +- being it's own path. In this case, take no action. This helps ensure we ++ being on it's own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. + +From 69b71caea18e47db5f3491ae0b86d6cb37d09bdc Mon Sep 17 00:00:00 2001 +From: Tzu-ping Chung +Date: Thu, 18 Nov 2021 15:21:59 +0800 +Subject: [PATCH 13/13] Reflow and fix typo in docstrings + +--- + .../resolution/resolvelib/resolver.py | 28 ++++++++----------- + 1 file changed, 12 insertions(+), 16 deletions(-) + +diff --git a/src/pip/_internal/resolution/resolvelib/resolver.py b/src/pip/_internal/resolution/resolvelib/resolver.py +index 2877d7552e7..8ee36d377d8 100644 +--- a/src/pip/_internal/resolution/resolvelib/resolver.py ++++ b/src/pip/_internal/resolution/resolvelib/resolver.py +@@ -173,9 +173,8 @@ def get_installation_order( + The current implementation creates a topological ordering of the + dependency graph, giving more weight to packages with less + or no dependencies, while breaking any cycles in the graph at +- arbitrary points. +- We make no guarantees about where the cycle would be broken, +- other than it *would* be broken. ++ arbitrary points. We make no guarantees about where the cycle ++ would be broken, other than it *would* be broken. + """ + assert self._result is not None, "must call resolve() first" + +@@ -205,22 +204,19 @@ def get_topological_weights( + This implementation may change at any point in the future without prior + notice. + +- We first simplify the dependency graph by pruning any leaves +- and giving them the highest weight: a package without any dependencies +- should be installed first. +- We simplify again and again in the same way, giving ever less weight +- to the newly found leaves. +- This stops when no leaves are left: all remaining packages have at least +- one dependency left in the graph. ++ We first simplify the dependency graph by pruning any leaves and giving them ++ the highest weight: a package without any dependencies should be installed ++ first. This is done again and again in the same way, giving ever less weight ++ to the newly found leaves. The loop stops when no leaves are left: all ++ remaining packages have at least one dependency left in the graph. + +- Then we continue with the remaining graph. +- We take the length for the longest path to any node from root, ignoring any +- paths that contain a single node twice (i.e. cycles). This is done through +- a depth-first search through the graph, while keeping track of the path to +- the node. ++ Then we continue with the remaining graph, by taking the length for the ++ longest path to any node from root, ignoring any paths that contain a single ++ node twice (i.e. cycles). This is done through a depth-first search through ++ the graph, while keeping track of the path to the node. + + Cycles in the graph result would result in node being revisited while also +- being on it's own path. In this case, take no action. This helps ensure we ++ being on its own path. In this case, take no action. This helps ensure we + don't get stuck in a cycle. + + When assigning weight, the longer path (i.e. larger length) is preferred. diff --git a/build/pkgs/ply/SPKG.rst b/build/pkgs/ply/SPKG.rst new file mode 100644 index 00000000000..7d8da13684b --- /dev/null +++ b/build/pkgs/ply/SPKG.rst @@ -0,0 +1,18 @@ +ply: Python Lex & Yacc +====================== + +Description +----------- + +Python Lex & Yacc + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/ply/ + diff --git a/build/pkgs/ply/checksums.ini b/build/pkgs/ply/checksums.ini new file mode 100644 index 00000000000..bf06a90d888 --- /dev/null +++ b/build/pkgs/ply/checksums.ini @@ -0,0 +1,5 @@ +tarball=ply-VERSION.tar.gz +sha1=10a555a32095991fbc7f7ed10c677a14e21fad1d +md5=6465f602e656455affcd7c5734c638f8 +cksum=375283112 +upstream_url=https://pypi.io/packages/source/p/ply/ply-VERSION.tar.gz diff --git a/build/pkgs/ply/dependencies b/build/pkgs/ply/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/ply/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/ply/install-requires.txt b/build/pkgs/ply/install-requires.txt new file mode 100644 index 00000000000..90412f06833 --- /dev/null +++ b/build/pkgs/ply/install-requires.txt @@ -0,0 +1 @@ +ply diff --git a/build/pkgs/ply/package-version.txt b/build/pkgs/ply/package-version.txt new file mode 100644 index 00000000000..2c0733315e4 --- /dev/null +++ b/build/pkgs/ply/package-version.txt @@ -0,0 +1 @@ +3.11 diff --git a/build/pkgs/ply/spkg-install.in b/build/pkgs/ply/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/ply/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/ply/type b/build/pkgs/ply/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/ply/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/primecount/checksums.ini b/build/pkgs/primecount/checksums.ini index e259fdf1cab..34fcb7a6566 100644 --- a/build/pkgs/primecount/checksums.ini +++ b/build/pkgs/primecount/checksums.ini @@ -1,4 +1,5 @@ tarball=primecount-VERSION.tar.gz -sha1=d7a2c660eb64d14c951feeaee1d94d220c68c23e -md5=ab925094a104499cfc21db2532e65517 -cksum=2340721037 +sha1=845b863f2007dc90d83c199db0af8fc20fd5092e +md5=b855038556de3f0ec8210b54e4057a65 +cksum=2388953073 +upstream_url=https://github.com/kimwalisch/primecount/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primecount/dependencies b/build/pkgs/primecount/dependencies index a3ea3e4380f..79670639026 100644 --- a/build/pkgs/primecount/dependencies +++ b/build/pkgs/primecount/dependencies @@ -1 +1 @@ -cmake +primesieve | cmake diff --git a/build/pkgs/primecount/distros/fedora.txt b/build/pkgs/primecount/distros/fedora.txt new file mode 100644 index 00000000000..c49419b9ec2 --- /dev/null +++ b/build/pkgs/primecount/distros/fedora.txt @@ -0,0 +1,2 @@ +primecount +primecount-devel diff --git a/build/pkgs/primecount/package-version.txt b/build/pkgs/primecount/package-version.txt index a75b92f1ed7..0f0fefae5ac 100644 --- a/build/pkgs/primecount/package-version.txt +++ b/build/pkgs/primecount/package-version.txt @@ -1 +1 @@ -5.1 +7.1 diff --git a/build/pkgs/primecount/patches/sieve_version.patch b/build/pkgs/primecount/patches/sieve_version.patch new file mode 100644 index 00000000000..2b5b550ead1 --- /dev/null +++ b/build/pkgs/primecount/patches/sieve_version.patch @@ -0,0 +1,13 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index d1fb69a..d9d6d43 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -267,7 +267,7 @@ if(BUILD_LIBPRIMESIEVE) + set(BUILD_MANPAGE "${COPY_BUILD_MANPAGE}" CACHE BOOL "Regenerate man page using a2x" FORCE) + set(BUILD_TESTS "${COPY_BUILD_TESTS}" CACHE BOOL "Build test programs" FORCE) + else() +- find_package(primesieve REQUIRED) ++ find_package(primesieve 7.6 REQUIRED) + endif() + + # Testing ############################################################ diff --git a/build/pkgs/primecount/spkg-check.in b/build/pkgs/primecount/spkg-check.in index 76a77d47bbf..11af4c50e28 100644 --- a/build/pkgs/primecount/spkg-check.in +++ b/build/pkgs/primecount/spkg-check.in @@ -1,8 +1,2 @@ -############################################################################### -# -# primecount Sage check script -# -############################################################################### - cd src $MAKE test diff --git a/build/pkgs/primecount/spkg-configure.m4 b/build/pkgs/primecount/spkg-configure.m4 new file mode 100644 index 00000000000..90cdb6026e5 --- /dev/null +++ b/build/pkgs/primecount/spkg-configure.m4 @@ -0,0 +1,32 @@ +SAGE_SPKG_CONFIGURE([primecount], [ + m4_pushdef([SAGE_PRIMECOUNT_MINVER],[7.1]) + m4_pushdef([SAGE_PRIMECOUNT_MAJOR],[7]) + m4_pushdef([SAGE_PRIMECOUNT_MINOR],[1]) + SAGE_SPKG_DEPCHECK([primesieve], [ + dnl Checking for primecount with pkg-config + PKG_CHECK_MODULES([PRIMECOUNT], [primecount >= SAGE_PRIMECOUNT_MINVER], [ ], [ + AC_CHECK_HEADER([primecount.h], [ + AC_SEARCH_LIBS([primecount_pi], [primecount], [ + AC_MSG_CHECKING([checking primecount version directly]) + AC_RUN_IFELSE([AC_LANG_PROGRAM([ + [#include + ]],[[ + if (PRIMECOUNT_VERSION_MAJOR > ]] SAGE_PRIMECOUNT_MAJOR [[ ) return 0; + if (PRIMECOUNT_VERSION_MAJOR == ]] SAGE_PRIMECOUNT_MAJOR [[ && + PRIMECOUNT_VERSION_MINOR >= ]] SAGE_PRIMECOUNT_MINOR [[ ) return 0; + else return 1; + ]])], + [AC_MSG_RESULT([Good.])], + [AC_MSG_RESULT([Too old.]) + sage_spkg_install_primecount=yes], + []) dnl cross-compilation - noop + ], + [sage_spkg_install_primecount=yes]) + ], [sage_spkg_install_primecount=yes]) + ]) + ]) + m4_popdef([SAGE_PRIMECOUNT_MINVER]) + m4_popdef([SAGE_PRIMECOUNT_MAJOR]) + m4_popdef([SAGE_PRIMECOUNT_MINOR]) +]) + diff --git a/build/pkgs/primecount/spkg-install.in b/build/pkgs/primecount/spkg-install.in index dcfb550caa5..197a4e11a9d 100644 --- a/build/pkgs/primecount/spkg-install.in +++ b/build/pkgs/primecount/spkg-install.in @@ -1,22 +1,23 @@ -############################################################################### -# -# primecount Sage install script -# -############################################################################### - cd src if [ "$SAGE_FAT_BINARY" = yes ]; then EXTRA_OPTS="-DWITH_POPCNT=OFF" fi -echo "Configuring primecount." +primc_config(){ +echo "Configuring primecount: building primesieve $1" sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \ -DBUILD_STATIC_LIBS=OFF \ -DBUILD_SHARED_LIBS=ON \ -DBUILD_TESTS=ON \ - ${EXTRA_OPTS} + -DBUILD_LIBPRIMESIEVE=$1 \ + -DCMAKE_FIND_ROOT_PATH=$SAGE_LOCAL/lib/cmake \ + -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH \ + -DCMAKE_INSTALL_PREFIX=$SAGE_LOCAL \ + ${EXTRA_OPTS} && sdh_make_install +} -sdh_make +# we try to use the external primesieve first, and if +# cmake cannot find it, we build a vendored copy -sdh_make_install +(primc_config OFF) || primc_config ON diff --git a/build/pkgs/primecount/type b/build/pkgs/primecount/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/primecount/type +++ b/build/pkgs/primecount/type @@ -1 +1 @@ -optional +standard diff --git a/build/pkgs/primesieve/SPKG.rst b/build/pkgs/primesieve/SPKG.rst new file mode 100644 index 00000000000..097dcfe1fc4 --- /dev/null +++ b/build/pkgs/primesieve/SPKG.rst @@ -0,0 +1,20 @@ +primesieve: CLI program and C/C++ library for generating primes +=============================================================== + +Description +----------- + +A CLI program and C/C++ library for quickly generating prime numbers. +https://github.com/kimwalisch/primesieve + +A dependency of the standard spkg primecount. + +License +------- + +BSD-2-clause + +Upstream Contact +---------------- + +- https://github.com/kimwalisch/primesieve diff --git a/build/pkgs/primesieve/checksums.ini b/build/pkgs/primesieve/checksums.ini new file mode 100644 index 00000000000..72857f87244 --- /dev/null +++ b/build/pkgs/primesieve/checksums.ini @@ -0,0 +1,5 @@ +tarball=primesieve-VERSION.tar.gz +sha1=b78579e0c78fedb75dc9a58b4be3b092eefbcfcb +md5=65eb8972819e47f552f563dc7699be8a +cksum=51195771 +upstream_url=https://github.com/kimwalisch/primesieve/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/primesieve/dependencies b/build/pkgs/primesieve/dependencies new file mode 100644 index 00000000000..ddf67ef8cd6 --- /dev/null +++ b/build/pkgs/primesieve/dependencies @@ -0,0 +1 @@ +| cmake diff --git a/build/pkgs/primesieve/distros/alpine.txt b/build/pkgs/primesieve/distros/alpine.txt new file mode 100644 index 00000000000..5521fa8278d --- /dev/null +++ b/build/pkgs/primesieve/distros/alpine.txt @@ -0,0 +1,2 @@ +primesieve-dev +primesieve diff --git a/build/pkgs/primesieve/distros/arch.txt b/build/pkgs/primesieve/distros/arch.txt new file mode 100644 index 00000000000..2425fd390b0 --- /dev/null +++ b/build/pkgs/primesieve/distros/arch.txt @@ -0,0 +1 @@ +primesieve diff --git a/build/pkgs/primesieve/distros/debian.txt b/build/pkgs/primesieve/distros/debian.txt new file mode 100644 index 00000000000..9368f75a030 --- /dev/null +++ b/build/pkgs/primesieve/distros/debian.txt @@ -0,0 +1 @@ +libprimesieve-dev diff --git a/build/pkgs/primesieve/distros/fedora.txt b/build/pkgs/primesieve/distros/fedora.txt new file mode 100644 index 00000000000..22efb24301d --- /dev/null +++ b/build/pkgs/primesieve/distros/fedora.txt @@ -0,0 +1,2 @@ +primesieve-devel +primesieve diff --git a/build/pkgs/primesieve/distros/homebrew.txt b/build/pkgs/primesieve/distros/homebrew.txt new file mode 100644 index 00000000000..2425fd390b0 --- /dev/null +++ b/build/pkgs/primesieve/distros/homebrew.txt @@ -0,0 +1 @@ +primesieve diff --git a/build/pkgs/primesieve/distros/opensuse.txt b/build/pkgs/primesieve/distros/opensuse.txt new file mode 100644 index 00000000000..2425fd390b0 --- /dev/null +++ b/build/pkgs/primesieve/distros/opensuse.txt @@ -0,0 +1 @@ +primesieve diff --git a/build/pkgs/primesieve/distros/repology.txt b/build/pkgs/primesieve/distros/repology.txt new file mode 100644 index 00000000000..2425fd390b0 --- /dev/null +++ b/build/pkgs/primesieve/distros/repology.txt @@ -0,0 +1 @@ +primesieve diff --git a/build/pkgs/primesieve/package-version.txt b/build/pkgs/primesieve/package-version.txt new file mode 100644 index 00000000000..38abeb202c0 --- /dev/null +++ b/build/pkgs/primesieve/package-version.txt @@ -0,0 +1 @@ +7.6 diff --git a/build/pkgs/primesieve/patches/107.patch b/build/pkgs/primesieve/patches/107.patch new file mode 100644 index 00000000000..d8986d29337 --- /dev/null +++ b/build/pkgs/primesieve/patches/107.patch @@ -0,0 +1,54 @@ +From 1f8d0470b4e9d50393032b86d21a7a2fcc512355 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Sat, 27 Nov 2021 10:50:09 -0800 +Subject: [PATCH] src/MemoryPool.cpp: Work around missing std::align on GCC 4.x + +--- + src/MemoryPool.cpp | 27 ++++++++++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/src/MemoryPool.cpp b/src/MemoryPool.cpp +index 7a4a4006..60f62101 100644 +--- a/src/MemoryPool.cpp ++++ b/src/MemoryPool.cpp +@@ -26,6 +26,31 @@ + + using std::size_t; + ++#if defined(__GNUC__) && __GNUC__ == 4 ++ ++// gcc 4.9 does not implement std::align. ++// Use the implementation from ++// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57350#c11 ++ ++using std::uintptr_t; ++ ++static inline void *align(size_t alignment, size_t size, ++ void *&ptr, size_t &space) ++{ ++ uintptr_t pn = reinterpret_cast(ptr); ++ uintptr_t aligned = (pn + alignment - 1) & -alignment; ++ size_t padding = aligned - pn; ++ if (space < size + padding) return nullptr; ++ space -= padding; ++ return ptr = reinterpret_cast(aligned); ++} ++ ++#else ++ ++using std::align; ++ ++#endif ++ + namespace primesieve { + + void MemoryPool::addBucket(SievingPrime*& sievingPrime) +@@ -70,7 +95,7 @@ void MemoryPool::allocateBuckets() + void* ptr = memory; + + // Align pointer address to sizeof(Bucket) +- if (!std::align(sizeof(Bucket), sizeof(Bucket), ptr, bytes)) ++ if (!align(sizeof(Bucket), sizeof(Bucket), ptr, bytes)) + throw primesieve_error("MemoryPool: failed to align memory!"); + + initBuckets(ptr, bytes); diff --git a/build/pkgs/primesieve/spkg-check.in b/build/pkgs/primesieve/spkg-check.in new file mode 100644 index 00000000000..11af4c50e28 --- /dev/null +++ b/build/pkgs/primesieve/spkg-check.in @@ -0,0 +1,2 @@ +cd src +$MAKE test diff --git a/build/pkgs/primesieve/spkg-configure.m4 b/build/pkgs/primesieve/spkg-configure.m4 new file mode 100644 index 00000000000..afd2f2c8ff4 --- /dev/null +++ b/build/pkgs/primesieve/spkg-configure.m4 @@ -0,0 +1,8 @@ +SAGE_SPKG_CONFIGURE([primesieve], [ + m4_pushdef([SAGE_PRIMESIEVE_MINVER],[7.6]) + dnl Checking for primesieve with pkg-config + PKG_CHECK_MODULES([PRIMESIEVE], [primesieve >= SAGE_PRIMESIEVE_MINVER], [ ], [ + sage_spkg_install_primesieve=yes]) + m4_popdef([SAGE_PRIMESIEVE_MINVER]) +]) + diff --git a/build/pkgs/primesieve/spkg-install.in b/build/pkgs/primesieve/spkg-install.in new file mode 100644 index 00000000000..c7669a664e9 --- /dev/null +++ b/build/pkgs/primesieve/spkg-install.in @@ -0,0 +1,14 @@ +cd src + +if [ "$SAGE_FAT_BINARY" = yes ]; then + EXTRA_OPTS="-DWITH_POPCNT=OFF" +fi + +echo "Configuring primesieve." +sdh_cmake -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DBUILD_STATIC_LIBS=OFF \ + -DBUILD_SHARED_LIBS=ON \ + -DBUILD_TESTS=ON \ + ${EXTRA_OPTS} + +sdh_make_install diff --git a/build/pkgs/primesieve/type b/build/pkgs/primesieve/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/primesieve/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pybind11/checksums.ini b/build/pkgs/pybind11/checksums.ini index d613ae53f27..f49c404fbc4 100644 --- a/build/pkgs/pybind11/checksums.ini +++ b/build/pkgs/pybind11/checksums.ini @@ -1,5 +1,5 @@ tarball=pybind11-VERSION.tar.gz -sha1=16bbf7b2e5ed1e7c0bc9da98457fe25561e7f1a9 -md5=b955fa9250c1ca2f2a50a71ad82f91ea -cksum=3622476020 +sha1=2f15bd2037e91adc14fa1d648e6091acd114ed97 +md5=2fb29e67a2158f80d2038ba619847629 +cksum=2184277382 upstream_url=https://pypi.io/packages/source/p/pybind11/pybind11-VERSION.tar.gz diff --git a/build/pkgs/pybind11/package-version.txt b/build/pkgs/pybind11/package-version.txt index da323e3f838..dbe59006547 100644 --- a/build/pkgs/pybind11/package-version.txt +++ b/build/pkgs/pybind11/package-version.txt @@ -1 +1 @@ -2.7.1.p0 +2.8.1 diff --git a/build/pkgs/pybind11/patches/3270.patch b/build/pkgs/pybind11/patches/3270.patch deleted file mode 100644 index e434eda6834..00000000000 --- a/build/pkgs/pybind11/patches/3270.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 8920453affdcaad3e4a3a02d39181f5b5bf67fb7 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Tue, 14 Sep 2021 21:57:58 -0700 -Subject: [PATCH] include/pybind11/numpy.h: gcc 4.8.4 does not have - is_trivially_copyable - -Manually edited -- the sdist has a layout different from the git repo. - ---- - include/pybind11/numpy.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/include/pybind11/numpy.h b/include/pybind11/numpy.h -index 7313897fe1..34b7536d38 100644 ---- a/pybind11/include/pybind11/numpy.h -+++ b/pybind11/include/pybind11/numpy.h -@@ -319,7 +319,7 @@ template using remove_all_extents_t = typename array_info::type; - - template using is_pod_struct = all_of< - std::is_standard_layout, // since we're accessing directly in memory we need a standard layout type --#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803) -+#if defined(__GLIBCXX__) && (__GLIBCXX__ < 20150422 || __GLIBCXX__ == 20150426 || __GLIBCXX__ == 20150623 || __GLIBCXX__ == 20150626 || __GLIBCXX__ == 20160803) - // libstdc++ < 5 (including versions 4.8.5, 4.9.3 and 4.9.4 which were released after 5) - // don't implement is_trivially_copyable, so approximate it - std::is_trivially_destructible, diff --git a/build/pkgs/pygraphviz/dependencies b/build/pkgs/pygraphviz/dependencies index 4910f651546..232dc205925 100644 --- a/build/pkgs/pygraphviz/dependencies +++ b/build/pkgs/pygraphviz/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) graphviz | $(PYTHON_TOOLCHAIN) +$(PYTHON) libgraphviz | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pythran/SPKG.rst b/build/pkgs/pythran/SPKG.rst new file mode 100644 index 00000000000..489b5f6000a --- /dev/null +++ b/build/pkgs/pythran/SPKG.rst @@ -0,0 +1,18 @@ +pythran: Ahead of Time compiler for numeric kernels +=================================================== + +Description +----------- + +Ahead of Time compiler for numeric kernels + +License +------- + +BSD 3-Clause + +Upstream Contact +---------------- + +https://pypi.org/project/pythran/ + diff --git a/build/pkgs/pythran/checksums.ini b/build/pkgs/pythran/checksums.ini new file mode 100644 index 00000000000..517a1363f8c --- /dev/null +++ b/build/pkgs/pythran/checksums.ini @@ -0,0 +1,5 @@ +tarball=pythran-VERSION.tar.gz +sha1=984636da98ce42914987e33ab42d28bf9d7703a5 +md5=08c93479623fe38ffe4e0e1b180434e3 +cksum=485126628 +upstream_url=https://pypi.io/packages/source/p/pythran/pythran-VERSION.tar.gz diff --git a/build/pkgs/pythran/dependencies b/build/pkgs/pythran/dependencies new file mode 100644 index 00000000000..8a64589c0bc --- /dev/null +++ b/build/pkgs/pythran/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) beniget gast ply numpy | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pythran/install-requires.txt b/build/pkgs/pythran/install-requires.txt new file mode 100644 index 00000000000..86d056b339f --- /dev/null +++ b/build/pkgs/pythran/install-requires.txt @@ -0,0 +1 @@ +pythran diff --git a/build/pkgs/pythran/package-version.txt b/build/pkgs/pythran/package-version.txt new file mode 100644 index 00000000000..78bc1abd14f --- /dev/null +++ b/build/pkgs/pythran/package-version.txt @@ -0,0 +1 @@ +0.10.0 diff --git a/build/pkgs/pythran/spkg-install.in b/build/pkgs/pythran/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/pythran/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/pythran/type b/build/pkgs/pythran/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pythran/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index 3824fd2ce94..20509db618b 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,5 +1,5 @@ tarball=pyzmq-VERSION.tar.gz -sha1=50abf6482a6395f15dce0b2774021e12c9f4faad -md5=f261acf884460e93d30f7113c8e9242e -cksum=1661206741 +sha1=d8bb8cb345c0bfd9e67a95ac796bf4b10354dde6 +md5=ecf13c72fbea05ba5ddc771295409d48 +cksum=1680251717 upstream_url=https://pypi.io/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index 0398faf11c2..8326e27f9b6 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -22.2.1 +22.3.0 diff --git a/build/pkgs/pyzmq/spkg-install.in b/build/pkgs/pyzmq/spkg-install.in index 0ce404ee5a0..069d87767f8 100644 --- a/build/pkgs/pyzmq/spkg-install.in +++ b/build/pkgs/pyzmq/spkg-install.in @@ -1,17 +1,11 @@ -# Since we use environment vars we have to generate setup.cfg - -echo "[build_ext]" > src/setup.cfg - -# (I tried putting quotes around $SAGE_LOCAL to allow for spaces in -# the path---which is never used but is a good habit to support---but -# that simply does not work. Sorry.) - -echo "library_dirs = $SAGE_LOCAL/lib/" >> src/setup.cfg -echo "include_dirs = $SAGE_LOCAL/include/" >> src/setup.cfg - -echo "[configure]" >> src/setup.cfg - cd src +# Generate setup.cfg +cat > setup.cfg <= 4.1.1], [ - - dnl Use pkg-config to get singular's libdir while we're at it. As a - dnl moral compromise for using pkg-config, this ultimately allows us - dnl to pass an absolute path to dlopen(), which is the only approach - dnl that POSIX guarantees will work. - PKG_CHECK_VAR([SINGULAR_LIB_DIR], [Singular], [libdir]) - - dnl libtool.m4 has dedicated cygwin* code to move DLLs from - dnl $libdir to $libdir/../bin. + dnl We have Singular. Now determine the shared library path on + dnl platforms on which sage.libs.singular needs to reload the library with RTLD_GLOBAL. AS_CASE([$host_os], - [cygwin*], [ - SINGULAR_LIB_DIR="${SINGULAR_LIB_DIR}/../bin" - ] - ) + [cygwin*], [dnl Nothing to do + ], + [dnl Use pkg-config to get singular's libdir while we're at it. As a + dnl moral compromise for using pkg-config, this ultimately allows us + dnl to pass an absolute path to dlopen(), which is the only approach + dnl that POSIX guarantees will work. + PKG_CHECK_VAR([SINGULAR_LIB_DIR], [Singular], [libdir]) + dnl The acl_shlibext variable is set in the top-level configure.ac. + LIBSINGULAR_PATH="${SINGULAR_LIB_DIR}/libSingular.${acl_shlibext}" - dnl The acl_shlibext variable is set in the top-level configure.ac. - LIBSINGULAR_PATH="${SINGULAR_LIB_DIR}/libSingular.${acl_shlibext}" + AC_MSG_CHECKING([if we can dlopen($LIBSINGULAR_PATH)]) + ORIG_LIBS="${LIBS}" + LIBS="${LIBS} -ldl" + AC_LANG_PUSH(C) - AC_MSG_CHECKING([if we can dlopen($LIBSINGULAR_PATH)]) - ORIG_LIBS="${LIBS}" - LIBS="${LIBS} -ldl" - AC_LANG_PUSH(C) + dnl if we can dlopen() it, substitute the name for sage_conf; + dnl otherwise, fall back to using the SPKG. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[#include ]], + [[void* h = dlopen("${LIBSINGULAR_PATH}", RTLD_LAZY | RTLD_GLOBAL); + if (h == 0) { return 1; } else { return dlclose(h); }]] + )], [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + sage_spkg_install_singular=yes + ]) - dnl if we can dlopen() it, substitute the name for sage_conf; - dnl otherwise, fall back to using the SPKG. - AC_RUN_IFELSE( - [AC_LANG_PROGRAM( - [[#include ]], - [[void* h = dlopen("${LIBSINGULAR_PATH}", RTLD_LAZY | RTLD_GLOBAL); - if (h == 0) { return 1; } else { return dlclose(h); }]] - )], [ - AC_MSG_RESULT(yes) - ], [ - AC_MSG_RESULT(no) - sage_spkg_install_singular=yes - ]) - - AC_LANG_POP() - LIBS="${ORIG_LIBS}" - ], [ - dnl pkg-config version check failed + AC_LANG_POP() + LIBS="${ORIG_LIBS}" + ] + )], [dnl pkg-config version check failed sage_spkg_install_singular=yes ]) ]) @@ -56,14 +51,12 @@ SAGE_SPKG_CONFIGURE([singular], [ dnl substitution needs to be made even if we skipped the system-Singular dnl checks themselves. AS_IF([test "x${sage_spkg_install_singular}" = "xyes"], [ - LIBSINGULAR_PATH="\$SAGE_LOCAL/lib/libSingular.${acl_shlibext}" - - dnl libtool.m4 has dedicated cygwin* code to move DLLs from - dnl $libdir to $libdir/../bin. AS_CASE([$host_os], - [cygwin*], [ - LIBSINGULAR_PATH="\$SAGE_LOCAL/bin/libSingular.${acl_shlibext}" - ] + [cygwin*], [dnl Nothing to do + ], + [dnl Set shared library path, needed for reloading the library with RTLD_GLOBAL + LIBSINGULAR_PATH="\$SAGE_LOCAL/lib/libSingular.${acl_shlibext}" + ] ) ]) diff --git a/build/pkgs/symengine/spkg-install.in b/build/pkgs/symengine/spkg-install.in index 36856e9d5b3..d617d634e46 100644 --- a/build/pkgs/symengine/spkg-install.in +++ b/build/pkgs/symengine/spkg-install.in @@ -1,8 +1,7 @@ cd src mkdir build cd build -cmake -DCMAKE_INSTALL_PREFIX="$SAGE_LOCAL" \ - -DCMAKE_PREFIX_PATH="$SAGE_LOCAL" \ +cmake -DCMAKE_PREFIX_PATH="$SAGE_LOCAL" \ -DWITH_SYMENGINE_THREAD_SAFE=yes \ -DWITH_ECM=yes \ -DWITH_FLINT=yes \ diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index 1cde11210f8..e0bb3965a82 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,5 +1,5 @@ tarball=sympy-VERSION.tar.gz -sha1=c52dd135f675cee79e46984b8454d9bb6b127edd -md5=37af34367e3f05692e6ddede95eccddb -cksum=4053721036 +sha1=980f08163f02ee04ccbda2e1e9db050eeb37b330 +md5=217f8179c3f1f3c888feb9b0fde0994e +cksum=3068755918 upstream_url=https://github.com/sympy/sympy/releases/download/sympy-VERSION/sympy-VERSION.tar.gz diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index 6259340971b..2e0e38c63a6 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -1.8 +1.9 diff --git a/build/pkgs/zeromq/checksums.ini b/build/pkgs/zeromq/checksums.ini index ea8c36bce53..dc802612ddd 100644 --- a/build/pkgs/zeromq/checksums.ini +++ b/build/pkgs/zeromq/checksums.ini @@ -1,4 +1,5 @@ tarball=zeromq-VERSION.tar.gz -sha1=1132f839d703486c4ee1cf22f056585dfbb329c2 -md5=a1c95b34384257e986842f4d006957b8 -cksum=136261411 +sha1=47277a64749049123d1401600e8cfbab10a3ae28 +md5=c897d4005a3f0b8276b00b7921412379 +cksum=1500782345 +upstream_url=https://github.com/zeromq/libzmq/releases/download/vVERSION/zeromq-VERSION.tar.gz diff --git a/build/pkgs/zeromq/package-version.txt b/build/pkgs/zeromq/package-version.txt index df0228dfaec..eda862a98c1 100644 --- a/build/pkgs/zeromq/package-version.txt +++ b/build/pkgs/zeromq/package-version.txt @@ -1 +1 @@ -4.2.5 +4.3.4 diff --git a/build/pkgs/zeromq/patches/getrandom.patch b/build/pkgs/zeromq/patches/getrandom.patch deleted file mode 100644 index e665114e1cb..00000000000 --- a/build/pkgs/zeromq/patches/getrandom.patch +++ /dev/null @@ -1,39 +0,0 @@ -Patch taken from upstream git repo -and after running aclocal && autoconf -and removing changes to autotools input files - -commit 4ff814f204ee38177a392526cf12c8c0019b480f -Author: Luca Boccassi -Date: Wed May 9 10:41:20 2018 +0100 - - Problem: getrandom test does not check if it's working - - Solution: check return value in autoconf and CMake. On some platforms - the function is available but not implemented (eg: GNU/Hurd). - -diff -ru zeromq-4.2.5//builds/cmake/Modules/ZMQSourceRunChecks.cmake b/builds/cmake/Modules/ZMQSourceRunChecks.cmake ---- zeromq-4.2.5//builds/cmake/Modules/ZMQSourceRunChecks.cmake 2018-03-23 20:33:36.000000000 +0100 -+++ b/builds/cmake/Modules/ZMQSourceRunChecks.cmake 2018-07-24 12:39:23.340035053 +0200 -@@ -287,7 +287,8 @@ - int main (int argc, char *argv []) - { - char buf[4]; -- getrandom(buf, 4, 0); -+ int rc = getrandom(buf, 4, 0); -+ return rc == -1 ? 1 : 0; - } - " - ZMQ_HAVE_GETRANDOM) -diff -ru zeromq-4.2.5//configure b/configure ---- zeromq-4.2.5//configure 2018-03-23 20:34:18.000000000 +0100 -+++ b/configure 2018-07-24 12:39:49.018035120 +0200 -@@ -23315,7 +23315,8 @@ - int main (int argc, char *argv []) - { - char buf[4]; -- getrandom(buf, 4, 0); -+ int rc = getrandom(buf, 4, 0); -+ return rc == -1 ? 1 : 0; - } - - _ACEOF diff --git a/configure.ac b/configure.ac index 699d2aaaaf1..351f48752cd 100644 --- a/configure.ac +++ b/configure.ac @@ -89,7 +89,6 @@ else SAGE_VENV_AUTO=no fi SAGE_SRC="$SAGE_ROOT/src" -SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" AC_ARG_WITH([sage-venv], [AS_HELP_STRING([--with-sage-venv={auto (default),yes,no,SAGE_VENV}], @@ -488,7 +487,7 @@ AC_CONFIG_COMMANDS(mkdirs, "$SAGE_LOCAL/bin" "$SAGE_LOCAL/etc" \ "$SAGE_LOCAL/include" "$SAGE_LOCAL/lib" \ "$SAGE_LOCAL/lib/pkgconfig" \ - "$SAGE_SHARE" "$SAGE_INST"; do + "$SAGE_SHARE" "$SAGE_LOCAL/var/lib/sage/installed"; do AC_MSG_NOTICE([creating directory $d]) mkdir -p "$d" || AC_MSG_ERROR(could not create $d) done @@ -507,7 +506,6 @@ AC_CONFIG_COMMANDS(mkdirs, SAGE_LOGS="$SAGE_ROOT/logs/pkgs" SAGE_LOCAL="$SAGE_LOCAL" SAGE_SHARE="$SAGE_LOCAL/share" - SAGE_INST="$SAGE_SPKG_INST" ]) AC_CONFIG_COMMANDS(links, [ diff --git a/m4/sage_spkg_collect.m4 b/m4/sage_spkg_collect.m4 index 90f10986f78..09ec6240d1b 100644 --- a/m4/sage_spkg_collect.m4 +++ b/m4/sage_spkg_collect.m4 @@ -192,6 +192,39 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do fi SAGE_PACKAGE_TREES="${SAGE_PACKAGE_TREES}$(printf '\ntrees_')${SPKG_NAME} = ${SPKG_TREE_VAR}" + dnl Determine whether it is installed already + AS_VAR_SET([is_installed], [no]) + for treevar in ${SPKG_TREE_VAR} SAGE_LOCAL; do + AS_VAR_COPY([t], [$treevar]) + AS_IF([test -n "$t" -a -d "$t/var/lib/sage/installed/" ], [ + for f in "$t/var/lib/sage/installed/$SPKG_NAME"-*; do + AS_IF([test -r "$f"], [ + AS_VAR_IF([SPKG_SOURCE], [normal], [ + dnl Only run the multiple installation record test for normal packages, + dnl not for script packages. We actually do not clean up after those... + AS_IF([test "$is_installed" = "yes"], [ + AC_MSG_ERROR(m4_normalize([ + multiple installation records for $SPKG_NAME: + m4_newline($(ls -l "$t/var/lib/sage/installed/$SPKG_NAME"-*)) + m4_newline([only one should exist, so please delete some or all + of these files and re-run "$srcdir/configure"]) + ])) + ]) + ]) + AS_VAR_SET([is_installed], [yes]) + ]) + done + dnl Only check the first existing tree, so that we do not issue "multiple installation" warnings + dnl when SAGE_LOCAL = SAGE_VENV + break + ]) + done + + # Determine whether package is enabled + AS_VAR_IF([SAGE_ENABLE_${SPKG_NAME}], [if_installed], + [AS_VAR_SET([SAGE_ENABLE_${SPKG_NAME}], $is_installed)]) + AS_VAR_COPY([want_spkg], [SAGE_ENABLE_${SPKG_NAME}]) + uninstall_message="" SAGE_NEED_SYSTEM_PACKAGES_VAR=SAGE_NEED_SYSTEM_PACKAGES # Check consistency of 'DIR/type' file @@ -229,20 +262,6 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do optional|experimental) in_sdist=no uninstall_message=", use \"$srcdir/configure --disable-$SPKG_NAME\" to uninstall" - stampfile="" - for f in "$SAGE_SPKG_INST/$SPKG_NAME"-*; do - AS_IF([test -r "$f"], [ - AS_IF([test -n "$stampfile"], [ - AC_MSG_ERROR(m4_normalize([ - multiple installation records for $SPKG_NAME: - m4_newline($(ls -l "$SAGE_SPKG_INST/$SPKG_NAME"-*)) - m4_newline([only one should exist, so please delete some or all - of these files and re-run "$srcdir/configure"]) - ])) - ]) - stampfile=yes - ]) - done ;; esac @@ -307,17 +326,6 @@ for DIR in $SAGE_ROOT/build/pkgs/*; do SAGE_SDIST_PACKAGES="${SAGE_SDIST_PACKAGES} \\$(printf '\n ')${SPKG_NAME}" fi - # Determine whether package is enabled - AS_VAR_SET([is_installed], [no]) - for f in "$SAGE_SPKG_INST/${SPKG_NAME}"-*; do - AS_IF([test -r "$f"], - [AS_VAR_SET([is_installed], [yes])]) - done - - AS_VAR_IF([SAGE_ENABLE_${SPKG_NAME}}], [if_installed], - [AS_VAR_SET([SAGE_ENABLE_${SPKG_NAME}], $is_installed)]) - AS_VAR_COPY([want_spkg], [SAGE_ENABLE_${SPKG_NAME}]) - spkg_line=" \\$(printf '\n ')$SPKG_NAME" AS_CASE([$is_installed-$want_spkg], [*-yes], [AS_VAR_APPEND(SAGE_OPTIONAL_INSTALLED_PACKAGES, "$spkg_line")], diff --git a/m4/sage_spkg_configure.m4 b/m4/sage_spkg_configure.m4 index e6aa0f3ca72..a0eeaa53a74 100644 --- a/m4/sage_spkg_configure.m4 +++ b/m4/sage_spkg_configure.m4 @@ -85,7 +85,7 @@ $4 dnl If a version of this package is already installed in local/ we have no dnl choice but to use it and we will actually also update it even if it is not dnl required. -AS_IF([test -n "`ls "${SAGE_SPKG_INST}/${sage_spkg_name}"-* 2>/dev/null`"], [ +AS_IF([test -n "`ls "${SAGE_LOCAL}/var/lib/sage/installed/${sage_spkg_name}"-* 2>/dev/null`"], [ AC_MSG_NOTICE(m4_normalize(SPKG_NAME[ has already been installed by SageMath])) AS_VAR_SET(SPKG_INSTALL, [yes]) AS_VAR_SET(SPKG_USE_SYSTEM, [installed]) diff --git a/src/VERSION.txt b/src/VERSION.txt index 8f24c817c21..c373c991e0d 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.5.beta7 +9.5.beta8 diff --git a/src/bin/sage-list-packages b/src/bin/sage-list-packages index 813b1c0d648..16a6a4ab181 100755 --- a/src/bin/sage-list-packages +++ b/src/bin/sage-list-packages @@ -99,8 +99,8 @@ L.sort(key=lambda pkg: pkg.name) # print (while getting rid of None in versions) for pkg in L: - pkg.installed_version = pkg.installed_version or 'not_installed' - pkg.remote_version = pkg.remote_version or '?' + pkg = pkg._replace(installed_version = pkg.installed_version or 'not_installed', + remote_version = pkg.remote_version or '?') print(format_string.format(**pkg._asdict())) if WARN: print(WARN) diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 288c889bd12..589dc8cd378 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -161,6 +161,10 @@ if __name__ == "__main__": if args.verbose: pytest_options.append("-v") exit_code_pytest = pytest.main(pytest_options + args.filenames) + if exit_code_pytest == 5: + # Exit code 5 means there were no test files, pass in this case + exit_code_pytest = 0 + except ModuleNotFoundError: print("Pytest is not installed, skip checking tests that rely on it.") diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 5d9a4f586f3..028873c9526 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,5 +1,5 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.5.beta7' -SAGE_RELEASE_DATE='2021-11-18' -SAGE_VERSION_BANNER='SageMath version 9.5.beta7, Release Date: 2021-11-18' +SAGE_VERSION='9.5.beta8' +SAGE_RELEASE_DATE='2021-12-12' +SAGE_VERSION_BANNER='SageMath version 9.5.beta8, Release Date: 2021-12-12' diff --git a/src/doc/de/tutorial/afterword.rst b/src/doc/de/tutorial/afterword.rst index 896f09a773f..4e6b09f108d 100644 --- a/src/doc/de/tutorial/afterword.rst +++ b/src/doc/de/tutorial/afterword.rst @@ -121,8 +121,6 @@ sein, also verhält sich Sage an manchen Stellen anders als Python. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # py2 - 0 - **Große ganze Zahlen:** Python besitzt von Hause aus Unterstützung für beliebig große ganze Zahlen zusätzlich zu C-ints. Diese sind diff --git a/src/doc/de/tutorial/programming.rst b/src/doc/de/tutorial/programming.rst index 7d86c01e30b..9da919fedae 100644 --- a/src/doc/de/tutorial/programming.rst +++ b/src/doc/de/tutorial/programming.rst @@ -328,7 +328,7 @@ Sage-Integers): :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Dies ist nützlich wenn man List-Comprehensions verwendet um Listen zu @@ -544,8 +544,7 @@ nichtnegativen ganzen Zahlen bis :math:`10000000`. :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) diff --git a/src/doc/de/tutorial/tour_algebra.rst b/src/doc/de/tutorial/tour_algebra.rst index b04ae3ca54b..baba2553a25 100644 --- a/src/doc/de/tutorial/tour_algebra.rst +++ b/src/doc/de/tutorial/tour_algebra.rst @@ -211,7 +211,7 @@ Lösung: Berechnen Sie die Laplace-Transformierte der ersten Gleichung sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) Das ist schwierig zu lesen, es besagt jedoch, dass @@ -227,7 +227,7 @@ Laplace-Transformierte der zweiten Gleichung: sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s Dies besagt diff --git a/src/doc/de/tutorial/tour_help.rst b/src/doc/de/tutorial/tour_help.rst index f15eb9e8ee3..48074b3882b 100644 --- a/src/doc/de/tutorial/tour_help.rst +++ b/src/doc/de/tutorial/tour_help.rst @@ -257,19 +257,7 @@ Quadrat- und Kubikzahlen. Die elementarste Datenstruktur in Sage ist die Liste. Sie ist -- wie der Name schon sagt -- nichts anderes als eine Liste beliebiger -Objekte. Zum Beispiel erzeugt der ``range`` Befehl, den wir schon -verwendet haben, eine Liste (python 2): - -:: - - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 - [2, 3, 4, 5, 6, 7, 8, 9] - -Hier ist eine etwas kompliziertere Liste: - -:: +Objekte. Hier ist ein Beispiel:: sage: v = [1, "hello", 2/3, sin(x^3)] sage: v diff --git a/src/doc/en/constructions/linear_algebra.rst b/src/doc/en/constructions/linear_algebra.rst index 0dc4dc2fd98..8894de9a5fd 100644 --- a/src/doc/en/constructions/linear_algebra.rst +++ b/src/doc/en/constructions/linear_algebra.rst @@ -278,7 +278,7 @@ Another approach is to use the interface with Maxima: sage: A = maxima("matrix ([1, -4], [1, -1])") sage: eig = A.eigenvectors() sage: eig - [[[-sqrt(3)*%i,sqrt(3)*%i],[1,1]],[[[1,(sqrt(3)*%i+1)/4]],[[1,-(sqrt(3)*%i-1)/4]]]] + [[[-sqrt(3)*%i,sqrt(3)*%i],[1,1]], [[[1,(sqrt(3)*%i+1)/4]],[[1,-(sqrt(3)*%i-1)/4]]]] This tells us that :math:`\vec{v}_1 = [1,(\sqrt{3}i + 1)/4]` is an eigenvector of :math:`\lambda_1 = - \sqrt{3}i` (which occurs diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 0d789336e5e..5b7bb0f8937 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -1182,14 +1182,6 @@ Here are some of the more commonly used variables affecting the build process: Some spkgs might also assume that certain programs are available on the system (for example, ``latex`` or ``pdflatex``). -- :envvar:`SAGE_DOC_MATHJAX` - by default, any LaTeX code in Sage's - documentation is processed by MathJax. - If this variable is set to ``no``, then MathJax is not used -- instead, - math is processed using LaTeX and converted by dvipng to image files, - and then those files are included into the documentation. - Typically, building the documentation using LaTeX and dvipng takes longer - and uses more memory and disk space than using MathJax. - - :envvar:`SAGE_DOCBUILD_OPTS` - the value of this variable is passed as an argument to ``sage --docbuild all html`` or ``sage --docbuild all pdf`` when you run ``make``, ``make doc``, or ``make doc-pdf``. diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index f495c872f48..3ae980667df 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -303,7 +303,7 @@ Below, we show that one can get step sizes other than one as well. :: - sage: range(3, 23, 2) # py2 + sage: list(range(3, 23, 2)) [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] sage: [3,5..21] [3, 5, 7, 9, 11, 13, 15, 17, 19, 21] diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index 846b68f5210..c570a58dd6f 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -533,9 +533,7 @@ methods are place-holders: There is no default implementation, but it is sage: from sage.misc.abstract_method import abstract_methods_of_class sage: abstract_methods_of_class(QuotientFields().element_class)['optional'] ['_add_', '_mul_'] - sage: abstract_methods_of_class(QuotientFields().element_class)['required'] # py2 - ['__nonzero__', 'denominator', 'numerator'] - sage: abstract_methods_of_class(QuotientFields().element_class)['required'] # py3 + sage: abstract_methods_of_class(QuotientFields().element_class)['required'] ['__bool__', 'denominator', 'numerator'] Hence, when implementing elements of a quotient field, it is *required* to @@ -1498,9 +1496,7 @@ The elements have to provide more:: sage: abstract_methods_of_class(QuotientFields().element_class)['optional'] ['_add_', '_mul_'] - sage: abstract_methods_of_class(QuotientFields().element_class)['required'] # py2 - ['__nonzero__', 'denominator', 'numerator'] - sage: abstract_methods_of_class(QuotientFields().element_class)['required'] # py3 + sage: abstract_methods_of_class(QuotientFields().element_class)['required'] ['__bool__', 'denominator', 'numerator'] .. end of output diff --git a/src/doc/en/thematic_tutorials/functional_programming.rst b/src/doc/en/thematic_tutorials/functional_programming.rst index 402e1e5a7b9..d2084384fb7 100644 --- a/src/doc/en/thematic_tutorials/functional_programming.rst +++ b/src/doc/en/thematic_tutorials/functional_programming.rst @@ -245,7 +245,7 @@ uses ``reduce`` and the built-in function ``operator.add`` to add together all integers in a given list. This is followed by using ``sum`` to accomplish the same task:: - sage: from functools import reduce # py3 + sage: from functools import reduce sage: from operator import add sage: L = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] sage: reduce(add, L) @@ -261,7 +261,7 @@ is then implemented using the functions ``operator.add`` and ``reduce`` and ``map``. We then show how ``sum`` and ``map`` could be combined to produce the same result. :: - sage: from functools import reduce # py3 + sage: from functools import reduce sage: from operator import add sage: from operator import mul sage: U = [1, 2, 3] @@ -283,7 +283,7 @@ using ``sum`` as was done previously. The version below uses ``operator.add`` and defines ``mul3`` to multiply three numbers instead of two. :: - sage: from functools import reduce # py3 + sage: from functools import reduce sage: def crt(A, M): ....: from operator import add ....: Mprod = prod(M) diff --git a/src/doc/en/thematic_tutorials/lie/integrable.rst b/src/doc/en/thematic_tutorials/lie/integrable.rst index b317fd4c8b3..3a6702ab294 100644 --- a/src/doc/en/thematic_tutorials/lie/integrable.rst +++ b/src/doc/en/thematic_tutorials/lie/integrable.rst @@ -317,14 +317,6 @@ Catalan numbers (:oeis:`A000108`):: Catalan triangle numbers (:oeis:`A000245`):: - sage: IntegrableRepresentation(Lambda[0]+Lambda[2]).strings(depth=1) # py2 long time - {Lambda[0] + Lambda[2]: [1], - 2*Lambda[1] - delta: [12], - Lambda[3] + Lambda[12] - delta: [3], - Lambda[4] + Lambda[11] - 2*delta: [9], - Lambda[5] + Lambda[10] - 3*delta: [28], - Lambda[6] + Lambda[9] - 4*delta: [90], - Lambda[7] + Lambda[8] - 5*delta: [297]} sage: sorted(IntegrableRepresentation(Lambda[0]+Lambda[2]).strings(depth=1).values()) # long time [[1], [3], [9], [12], [28], [90], [297]] @@ -332,17 +324,5 @@ Central binomial coefficients (:oeis:`A001700`, :oeis:`A128015`):: sage: P = RootSystem(['B',8,1]).weight_lattice(extended=true) sage: Lambda = P.fundamental_weights() - sage: IntegrableRepresentation(Lambda[0]+Lambda[1]).strings(depth=1) # py2 long time - {Lambda[0] + Lambda[1]: [1], - 2*Lambda[0]: [1], - 2*Lambda[1] - delta: [1], - Lambda[2] - delta: [3], - Lambda[3] - delta: [3], - Lambda[4] - 2*delta: [10], - Lambda[5] - 2*delta: [10], - Lambda[6] - 3*delta: [35], - Lambda[7] - 3*delta: [35], - 2*Lambda[8] - 4*delta: [126]} sage: sorted(IntegrableRepresentation(Lambda[0]+Lambda[1]).strings(depth=1).values()) # long time [[1], [1], [1], [3], [3], [10], [10], [35], [35], [126]] - diff --git a/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst b/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst index d7dc8a9b4f0..9d9fed826fb 100644 --- a/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst +++ b/src/doc/en/thematic_tutorials/tutorial-comprehensions.rst @@ -122,9 +122,7 @@ in memory:: sage: sum([binomial(8, i) for i in range(9)]) 256 - sage: sum(binomial(8, i) for i in xrange(9)) # py2 - 256 - sage: sum(binomial(8, i) for i in range(9)) # py3 + sage: sum(binomial(8, i) for i in range(9)) 256 .. TOPIC:: Exercises diff --git a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst index c8cbd71ca83..c319c38e7b9 100644 --- a/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst +++ b/src/doc/en/thematic_tutorials/tutorial-implementing-algebraic-structures.rst @@ -100,10 +100,7 @@ methods ``product_on_basis``, ``one_basis``, ``_repr_`` and ask the category (TODO: find a slicker idiom for this):: sage: from sage.misc.abstract_method import abstract_methods_of_class - sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).element_class) # py2 - {'optional': ['_add_', '_mul_'], - 'required': ['__nonzero__', 'monomial_coefficients']} - sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).element_class) # py3 + sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).element_class) {'optional': ['_add_', '_mul_'], 'required': ['__bool__', 'monomial_coefficients']} sage: abstract_methods_of_class(AlgebrasWithBasis(QQ).parent_class) diff --git a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst index acc641a2427..7fa2d5d054f 100644 --- a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst +++ b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst @@ -293,7 +293,7 @@ Creating Lists III: list comprehensions **Example** We already know how to create the list `[1, 2, \dots, 16]`:: - sage: range(1,17) # py2 + sage: list(range(1,17)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] Using a *list comprehension*, we can now create the list @@ -1032,22 +1032,22 @@ included: :: - sage: range(4) # py2 + sage: list(range(4)) [0, 1, 2, 3] :: - sage: range(1, 5) # py2 + sage: list(range(1, 5)) [1, 2, 3, 4] :: - sage: range(1, 11, 2) # py2 + sage: list(range(1, 11, 2)) [1, 3, 5, 7, 9] :: - sage: range(10, 0, -1) # py2 + sage: list(range(10, 0, -1)) [10, 9, 8, 7, 6, 5, 4, 3, 2, 1] :: diff --git a/src/doc/en/tutorial/programming.rst b/src/doc/en/tutorial/programming.rst index 56ecb369559..f0f43e6f09f 100644 --- a/src/doc/en/tutorial/programming.rst +++ b/src/doc/en/tutorial/programming.rst @@ -311,7 +311,7 @@ Integers): :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] This is useful when using list comprehensions to construct lists: @@ -522,8 +522,7 @@ nonnegative integers up to :math:`10000000`. :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) @@ -671,8 +670,6 @@ the Python int ``1`` is unique, but the Sage Integer ``1`` is not: sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # py2 - True sage: 1 is 1 False sage: 1 == 2/2 diff --git a/src/doc/en/tutorial/tour_algebra.rst b/src/doc/en/tutorial/tour_algebra.rst index f5d56ed1574..2e872cc9059 100644 --- a/src/doc/en/tutorial/tour_algebra.rst +++ b/src/doc/en/tutorial/tour_algebra.rst @@ -218,7 +218,7 @@ the notation :math:`x=x_{1}`, :math:`y=x_{2}`): sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) This is hard to read, but it says that @@ -233,7 +233,7 @@ Laplace transform of the second equation: sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s This says diff --git a/src/doc/en/tutorial/tour_help.rst b/src/doc/en/tutorial/tour_help.rst index 800777882dc..c66c24e470b 100644 --- a/src/doc/en/tutorial/tour_help.rst +++ b/src/doc/en/tutorial/tour_help.rst @@ -255,13 +255,9 @@ and make a table of squares and cubes. The most basic data structure in Sage is the list, which is -- as the name suggests -- just a list of arbitrary objects. For example, -the ``range`` command that we used creates a list (in python 2): +using ``range``, the following command creates a list:: -:: - - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 + sage: list(range(2,10)) [2, 3, 4, 5, 6, 7, 8, 9] Here is a more complicated list: diff --git a/src/doc/es/tutorial/tour_algebra.rst b/src/doc/es/tutorial/tour_algebra.rst index 1f38b2b788f..dc1a7a96719 100644 --- a/src/doc/es/tutorial/tour_algebra.rst +++ b/src/doc/es/tutorial/tour_algebra.rst @@ -198,7 +198,7 @@ la notación :math:`x=x_{1}`, :math:`y=x_{2}`): sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) El resultado puede ser difícil de leer, pero significa que @@ -213,7 +213,7 @@ Toma la transformada de Laplace de la segunda ecuación: sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s Esto dice diff --git a/src/doc/es/tutorial/tour_help.rst b/src/doc/es/tutorial/tour_help.rst index efbb2880940..b2448bf121c 100644 --- a/src/doc/es/tutorial/tour_help.rst +++ b/src/doc/es/tutorial/tour_help.rst @@ -254,13 +254,9 @@ y hacemos una tabla de cuadrados y cubos. La estructura de datos más básica en Sage es la lista, la cual es -- como sugiere su nombre -- solo una lista de objetos arbitrarios. -Por ejemplo, el comando ``range`` que hemos usado crea una lista (python 2): +Por ejemplo, el comando ``range`` que hemos usado crea una lista:: -:: - - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 + sage: list(range(2,10)) [2, 3, 4, 5, 6, 7, 8, 9] He aquí una lista más complicada: diff --git a/src/doc/fr/tutorial/afterword.rst b/src/doc/fr/tutorial/afterword.rst index 18073d84806..857d2de5ec1 100644 --- a/src/doc/fr/tutorial/afterword.rst +++ b/src/doc/fr/tutorial/afterword.rst @@ -132,8 +132,6 @@ Aussi, Sage se comporte différemment de Python à plusieurs égards. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # py2 - 0 - **Entiers longs :** Python possède nativement un support pour les entiers de précision arbitraire, en plus des int du langage C. Les entiers longs diff --git a/src/doc/fr/tutorial/programming.rst b/src/doc/fr/tutorial/programming.rst index 69b4182508e..f109e121414 100644 --- a/src/doc/fr/tutorial/programming.rst +++ b/src/doc/fr/tutorial/programming.rst @@ -325,7 +325,7 @@ Sage) : :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Cela est utile pour construire des listes par compréhension : @@ -540,8 +540,7 @@ d'entiers positifs jusqu'à :math:`10000000`. :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) diff --git a/src/doc/fr/tutorial/tour_algebra.rst b/src/doc/fr/tutorial/tour_algebra.rst index ca63cfec968..658894b2e8b 100644 --- a/src/doc/fr/tutorial/tour_algebra.rst +++ b/src/doc/fr/tutorial/tour_algebra.rst @@ -183,7 +183,7 @@ Solution : Considérons la transformée de Laplace de la première équation sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) La réponse n'est pas très lisible, mais elle signifie que @@ -198,7 +198,7 @@ la seconde équation : sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s Ceci signifie diff --git a/src/doc/fr/tutorial/tour_help.rst b/src/doc/fr/tutorial/tour_help.rst index 3ad0435ec8c..1bfd27c5f87 100644 --- a/src/doc/fr/tutorial/tour_help.rst +++ b/src/doc/fr/tutorial/tour_help.rst @@ -258,20 +258,8 @@ et des cubes en trois colonnes, chacune d'une largeur de six caractères. 4 16 64 La structure de données de base de Sage est la liste, qui est — comme -son nom l'indique — une liste d'objets arbitraires. Par exemple, la -commande ``range`` que nous avons utilisée plus haut crée en fait une -liste (en python 2): - -:: - - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 - [2, 3, 4, 5, 6, 7, 8, 9] - -Voici un exemple plus compliqué de liste : - -:: +son nom l'indique — une liste d'objets arbitraires. Voici un exemple +de liste:: sage: v = [1, "hello", 2/3, sin(x^3)] sage: v diff --git a/src/doc/it/tutorial/tour_algebra.rst b/src/doc/it/tutorial/tour_algebra.rst index 20ee65727fa..5a5311e9b1c 100644 --- a/src/doc/it/tutorial/tour_algebra.rst +++ b/src/doc/it/tutorial/tour_algebra.rst @@ -184,7 +184,7 @@ la notazione :math:`x=x_{1}`, :math:`y=x_{2}`: sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) Questo è di difficile lettura, ma dice che @@ -199,7 +199,7 @@ trasformata di Laplace della seconda equazione: sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s che significa diff --git a/src/doc/ja/tutorial/afterword.rst b/src/doc/ja/tutorial/afterword.rst index 1ca44524078..05a43260d2d 100644 --- a/src/doc/ja/tutorial/afterword.rst +++ b/src/doc/ja/tutorial/afterword.rst @@ -96,8 +96,6 @@ Pythonの数学機能には混乱を招きがちな面があり,SageにはPyth Rational Field sage: 2//3 0 - sage: int(2)/int(3) # py2 - 0 - **長整数:** Python本体は,C言語由来のint型だけではなく任意精度整数をサポートしている. Pythonの任意精度整数はGMP提供のものと比べると著しく速度が劣り,通常のintと区別するために末尾に ``L`` を付けて出力される仕様になっている(この仕様はすぐには変更されそうにない). diff --git a/src/doc/ja/tutorial/programming.rst b/src/doc/ja/tutorial/programming.rst index 9edd5f80a6d..df8d6d4112c 100644 --- a/src/doc/ja/tutorial/programming.rst +++ b/src/doc/ja/tutorial/programming.rst @@ -289,7 +289,7 @@ SageのIntegerクラスが使えるのは言うまでもない(Rationalクラス :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] この ``range`` が便利なのは,リスト内包表記を使ってリストを生成する場合だ: @@ -503,8 +503,7 @@ Pythonには集合(set)型が組込まれている. :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) @@ -648,8 +647,6 @@ Sageにおける異種オブジェクト間の比較演算では,まず対象 sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # py2 - True sage: 1 is 1 False sage: 1 == 2/2 diff --git a/src/doc/ja/tutorial/tour_algebra.rst b/src/doc/ja/tutorial/tour_algebra.rst index 39330d91504..784fd0d5c40 100644 --- a/src/doc/ja/tutorial/tour_algebra.rst +++ b/src/doc/ja/tutorial/tour_algebra.rst @@ -214,7 +214,7 @@ Sageを使って常微分方程式を研究することもできる. :math:`x' sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) この出力は読みにくいけれども,意味しているのは @@ -228,7 +228,7 @@ Sageを使って常微分方程式を研究することもできる. :math:`x' sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s 意味するところは diff --git a/src/doc/ja/tutorial/tour_help.rst b/src/doc/ja/tutorial/tour_help.rst index 7c3994930e5..69d4238012a 100644 --- a/src/doc/ja/tutorial/tour_help.rst +++ b/src/doc/ja/tutorial/tour_help.rst @@ -254,9 +254,7 @@ Sageにおける最も基本的なデータ構造はリストで,名前の示 :: - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 + sage: list(range(2,10)) [2, 3, 4, 5, 6, 7, 8, 9] もう少し複雑なリストの例として: diff --git a/src/doc/pt/tutorial/afterword.rst b/src/doc/pt/tutorial/afterword.rst index c80892aa720..294189edd18 100644 --- a/src/doc/pt/tutorial/afterword.rst +++ b/src/doc/pt/tutorial/afterword.rst @@ -113,8 +113,6 @@ se comporta diferentemente do Python em diversas situações. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # py2 - 0 - **Inteiros longos:** O Python possui suporte nativo para inteiros com precisão arbitrária, além de int's do C. Esses são diff --git a/src/doc/pt/tutorial/programming.rst b/src/doc/pt/tutorial/programming.rst index fd082952410..4f99d595a5f 100644 --- a/src/doc/pt/tutorial/programming.rst +++ b/src/doc/pt/tutorial/programming.rst @@ -341,7 +341,7 @@ Sage): :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Isso é útil quando se usa "list comprehensions" para construir listas: @@ -547,8 +547,7 @@ sobre o quadrados dos números inteiros até :math:`10000000`. :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) @@ -694,8 +693,6 @@ o int ``1`` do Python é único, mas o Inteiro ``1`` do Sage não é. sage: 1 is 2/2 False - sage: int(1) is int(2)/int(2) # py2 - True sage: 1 is 1 False sage: 1 == 2/2 diff --git a/src/doc/pt/tutorial/tour_algebra.rst b/src/doc/pt/tutorial/tour_algebra.rst index fb715fdaf0d..baeb37b1c71 100644 --- a/src/doc/pt/tutorial/tour_algebra.rst +++ b/src/doc/pt/tutorial/tour_algebra.rst @@ -206,7 +206,7 @@ equação (usando a notação :math:`x=x_{1}`, :math:`y=x_{2}`): sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) O resultado é um pouco difícil de ler, mas diz que @@ -221,7 +221,7 @@ calcule a transformada de Laplace da segunda equação: sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s O resultado significa que diff --git a/src/doc/pt/tutorial/tour_help.rst b/src/doc/pt/tutorial/tour_help.rst index 7b4f5243ddb..3ceda948d02 100644 --- a/src/doc/pt/tutorial/tour_help.rst +++ b/src/doc/pt/tutorial/tour_help.rst @@ -260,9 +260,7 @@ exemplo, o comando ``range`` que usamos acima cria uma lista: :: - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 + sage: list(range(2,10)) [2, 3, 4, 5, 6, 7, 8, 9] Abaixo segue uma lista mais complicada: diff --git a/src/doc/ru/tutorial/afterword.rst b/src/doc/ru/tutorial/afterword.rst index 5f952a178e7..fb06acba638 100644 --- a/src/doc/ru/tutorial/afterword.rst +++ b/src/doc/ru/tutorial/afterword.rst @@ -106,8 +106,6 @@ Sage ведет себя немного другим образом. Rational Field sage: 2//3 0 - sage: int(2)/int(3) # py2 - 0 - **Большие целые числа:** Python имеет встроенную поддержку целых чисел произвольной точности в дополнение к C-int’ам. Они намного медленнее, чем то, diff --git a/src/doc/ru/tutorial/programming.rst b/src/doc/ru/tutorial/programming.rst index 4550f32717a..826d3b6df4a 100644 --- a/src/doc/ru/tutorial/programming.rst +++ b/src/doc/ru/tutorial/programming.rst @@ -302,7 +302,7 @@ Python, сработает нормально. :: - sage: range(1, 15) # py2 + sage: list(range(1, 15)) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] Это удобно, когда для создания списков используется вид списка: @@ -508,8 +508,7 @@ http://docs.python.org/lib/typesmapping.html) произвольным объе :: - sage: v = (n^2 for n in xrange(10000000)) # py2 - sage: v = (n^2 for n in range(10000000)) # py3 + sage: v = (n^2 for n in range(10000000)) sage: next(v) 0 sage: next(v) diff --git a/src/doc/ru/tutorial/tour_algebra.rst b/src/doc/ru/tutorial/tour_algebra.rst index 495fb6dcddf..9f08c41d118 100644 --- a/src/doc/ru/tutorial/tour_algebra.rst +++ b/src/doc/ru/tutorial/tour_algebra.rst @@ -200,7 +200,7 @@ Sage может использоваться для решения диффер sage: de1 = maxima("2*diff(x(t),t, 2) + 6*x(t) - 2*y(t)") sage: lde1 = de1.laplace("t","s"); lde1 - 2*((-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s)-2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) + 2*((-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s) -2*'laplace(y(t),t,s)+6*'laplace(x(t),t,s) Данный результат тяжело читаем, однако должен быть понят как @@ -212,7 +212,7 @@ Sage может использоваться для решения диффер sage: de2 = maxima("diff(y(t),t, 2) + 2*y(t) - 2*x(t)") sage: lde2 = de2.laplace("t","s"); lde2 - (-%at('diff(y(t),t,1),t=0))+s^2*'laplace(y(t),t,s)+2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s)-y(0)*s + (-%at('diff(y(t),t,1),t = 0))+s^2*'laplace(y(t),t,s) +2*'laplace(y(t),t,s)-2*'laplace(x(t),t,s) -y(0)*s Результат: diff --git a/src/doc/ru/tutorial/tour_help.rst b/src/doc/ru/tutorial/tour_help.rst index 57d4da60df4..7631f0fba09 100644 --- a/src/doc/ru/tutorial/tour_help.rst +++ b/src/doc/ru/tutorial/tour_help.rst @@ -246,9 +246,7 @@ tanh, taylor``. Данная функция является хорошим сп :: - sage: range(2,10) # py2 - [2, 3, 4, 5, 6, 7, 8, 9] - sage: list(range(2,10)) # py3 + sage: list(range(2,10)) [2, 3, 4, 5, 6, 7, 8, 9] Далее показан пример более сложного списка: diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index fa683e3ee7f..6a13a314175 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -5,16 +5,15 @@ - Travis Scrimshaw (2013-05-03): Initial version """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2017 Travis Scrimshaw # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute @@ -35,7 +34,8 @@ from sage.matrix.matrix_space import MatrixSpace from sage.sets.family import Family, AbstractFamily -class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators): + +class LieAlgebra(Parent, UniqueRepresentation): # IndexedGenerators): r""" A Lie algebra `L` over a base ring `R`. @@ -404,9 +404,10 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, if R is None: raise ValueError("invalid arguments") - check_assoc = lambda A: (isinstance(A, (Ring, MatrixSpace)) - or A in Rings() - or A in Algebras(R).Associative()) + def check_assoc(A): + return (isinstance(A, (Ring, MatrixSpace)) + or A in Rings() + or A in Algebras(R).Associative()) if arg0 in ZZ or check_assoc(arg1): # Check if we need to swap the arguments arg0, arg1 = arg1, arg0 @@ -627,8 +628,8 @@ def _coerce_map_from_(self, R): return False # We check if it is a subalgebra of something that can coerce into ``self`` - #from sage.algebras.lie_algebras.subalgebra import LieSubalgebra - #if isinstance(R, LieSubalgebra) and self.has_coerce_map_from(R._ambient): + # from sage.algebras.lie_algebras.subalgebra import LieSubalgebra + # if isinstance(R, LieSubalgebra) and self.has_coerce_map_from(R._ambient): # return R.ambient_lift # Lie algebras in the same indices over any base that coerces in @@ -663,9 +664,9 @@ def _Hom_(self, Y, category): """ cat = LieAlgebras(self.base_ring()) if category is not None and not category.is_subcategory(cat): - raise TypeError("%s is not a subcategory of Lie algebras"%category) + raise TypeError(f"{category} is not a subcategory of Lie algebras") if Y not in cat: - raise TypeError("%s is not a Lie algebra"%Y) + raise TypeError(f"{Y} is not a Lie algebra") from sage.algebras.lie_algebras.morphism import LieAlgebraHomset return LieAlgebraHomset(self, Y, category=category) @@ -710,7 +711,7 @@ def _from_dict(self, d, coerce=False, remove_zeros=True): assert isinstance(d, dict) if coerce: R = self.base_ring() - d = {key: R(coeff) for key,coeff in d.items()} + d = {key: R(coeff) for key, coeff in d.items()} if remove_zeros: d = {key: coeff for key, coeff in d.items() if coeff} return self.element_class(self, d) @@ -764,7 +765,8 @@ def get_order(self): except AttributeError: raise ValueError("the Lie algebra is not finite dimensional with a basis") - #Element = LieAlgebraElement # Default for all Lie algebras + # Element = LieAlgebraElement # Default for all Lie algebras + class LieAlgebraWithGenerators(LieAlgebra): """ @@ -824,7 +826,7 @@ def gens(self): return tuple(G[i] for i in self.variable_names()) except (KeyError, IndexError): return tuple(G[i] for i in self.indices()) - except (KeyError, ValueError): + except ValueError: return tuple(G) def gen(self, i): @@ -851,6 +853,7 @@ def indices(self): """ return self._indices + class FinitelyGeneratedLieAlgebra(LieAlgebraWithGenerators): r""" A finitely generated Lie algebra. @@ -890,9 +893,9 @@ def _repr_(self): """ if self.__ngens == 1: return "Lie algebra on the generator {0} over {1}".format( - self.gen(0), self.base_ring()) + self.gen(0), self.base_ring()) return "Lie algebra on {0} generators {1} over {2}".format( - self.__ngens, self.gens(), self.base_ring()) + self.__ngens, self.gens(), self.base_ring()) @lazy_attribute def _ordered_indices(self): @@ -919,6 +922,7 @@ def _an_element_(self): """ return self.sum(self.lie_algebra_generators()) + class InfinitelyGeneratedLieAlgebra(LieAlgebraWithGenerators): r""" An infinitely generated Lie algebra. @@ -949,6 +953,7 @@ def _an_element_(self): # """ # return self.lie_algebra_generators() + class LieAlgebraFromAssociative(LieAlgebraWithGenerators): """ A Lie algebra whose elements are from an associative algebra and whose @@ -1097,7 +1102,7 @@ def __classcall_private__(cls, A, gens=None, names=None, index_set=None, index_set = gens.keys() gens = gens.values() ngens = len(gens) - elif gens is not None: # Assume it is list-like + elif gens is not None: # Assume it is list-like gens = tuple(gens) ngens = len(gens) if index_set is None and names is None: @@ -1155,9 +1160,9 @@ def __init__(self, A, gens=None, names=None, index_set=None, category=None): if isinstance(gens, tuple): # This guarantees that the generators have a specified ordering d = {self._indices[i]: self.element_class(self, v) - for i,v in enumerate(gens)} + for i, v in enumerate(gens)} gens = Family(self._indices, lambda i: d[i]) - elif gens is not None: # It is a family + elif gens is not None: # It is a family gens = Family(self._indices, lambda i: self.element_class(self, gens[i]), name="generator map") @@ -1276,7 +1281,7 @@ def monomial(self, i): x """ if i not in self._assoc.basis().keys(): - #return self(self._assoc.monomial(i)) + # return self(self._assoc.monomial(i)) raise ValueError("not an index") return self.element_class(self, self._assoc.monomial(i)) @@ -1292,7 +1297,7 @@ def term(self, i, c=None): 4*x """ if i not in self._assoc.basis().keys(): - #return self(self._assoc.term(i, c)) + # return self(self._assoc.term(i, c)) raise ValueError("not an index") return self.element_class(self, self._assoc.term(i, c)) @@ -1439,6 +1444,7 @@ def monomial_coefficients(self, copy=True): raise NotImplementedError("the basis is not defined") return self.value.monomial_coefficients(copy) + class LiftMorphismToAssociative(LiftMorphism): """ The natural lifting morphism from a Lie algebra constructed from @@ -1494,6 +1500,7 @@ def section(self): return SetMorphism(Hom(self.codomain(), self.domain()), self.preimage) + class MatrixLieAlgebraFromAssociative(LieAlgebraFromAssociative): """ A Lie algebra constructed from a matrix algebra. @@ -1540,4 +1547,3 @@ def matrix(self): return self.value _matrix_ = matrix - diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index d3a035e2a36..af3b7bd509d 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -2198,7 +2198,7 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: sefms('?%at(f(x),x=2)#1') f(2) != 1 sage: a = sage.calculus.calculus.maxima("x#0"); a - x#0 + x # 0 sage: a.sage() x != 0 diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index 755c631f203..f2efb656be4 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -244,30 +244,21 @@ def set_intersphinx_mappings(app, config): html_common_static_path = [os.path.join(SAGE_DOC_SRC, 'common', 'static'), THEBE_DIR, 'static'] -# We use MathJax to build the documentation unless the environment -# variable SAGE_DOC_MATHJAX is set to "no" or "False". (Note that if -# the user does not set this variable, then the script sage-env sets -# it to "True".) +# We use MathJax to build the documentation. +extensions.append('sphinx.ext.mathjax') +mathjax_path = 'MathJax.js?config=TeX-AMS_HTML-full,../mathjax_sage.js' -if (os.environ.get('SAGE_DOC_MATHJAX', 'no') not in ['no', 'False']): - extensions.append('sphinx.ext.mathjax') - mathjax_path = 'MathJax.js?config=TeX-AMS_HTML-full,../mathjax_sage.js' +from sage.misc.latex_macros import sage_mathjax_macros +html_theme_options['mathjax_macros'] = sage_mathjax_macros() - from sage.misc.latex_macros import sage_mathjax_macros - html_theme_options['mathjax_macros'] = sage_mathjax_macros() - - mathjax_relative = os.path.basename(MATHJAX_DIR) - - # It would be really nice if sphinx would copy the entire mathjax - # directory, (so we could have a _static/mathjax directory), rather than - # the contents of the directory - - html_common_static_path.append(MATHJAX_DIR) - exclude_patterns += ['**/'+os.path.join(mathjax_relative, i) - for i in ('docs', 'README*', 'test', 'unpacked', 'LICENSE')] -else: - extensions.append('sphinx.ext.imgmath') +mathjax_relative = os.path.basename(MATHJAX_DIR) +# It would be really nice if sphinx would copy the entire mathjax +# directory, (so we could have a _static/mathjax directory), rather than +# the contents of the directory +html_common_static_path.append(MATHJAX_DIR) +exclude_patterns += ['**/' + os.path.join(mathjax_relative, i) + for i in ('docs', 'README*', 'test', 'unpacked', 'LICENSE')] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. diff --git a/src/sage/env.py b/src/sage/env.py index 40ace135f0d..70ed1e3b6bc 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -345,16 +345,6 @@ def _get_shared_lib_path(*libnames: str) -> Optional[str]: print("permissions to before you start sage.") -CYGWIN_VERSION = None -if UNAME[:6] == 'CYGWIN': - import re - _uname = os.uname() - if len(_uname) >= 2: - m = re.match(r'(\d+\.\d+\.\d+)\(.+\)', _uname[2]) - if m: - CYGWIN_VERSION = tuple(map(int, m.group(1).split('.'))) - - def sage_include_directories(use_sources=False): """ Return the list of include directories for compiling Sage extension modules. diff --git a/src/sage/ext/memory_allocator.pxd b/src/sage/ext/memory_allocator.pxd index 1be5ba69552..d7c287b819a 100644 --- a/src/sage/ext/memory_allocator.pxd +++ b/src/sage/ext/memory_allocator.pxd @@ -87,12 +87,16 @@ cdef class MemoryAllocator: sage: cython(''' ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for i in range(12): - ....: ptr = mem.aligned_calloc(2**i, i, 2**i) - ....: assert ptr == ( ptr) & ~(2**i-1) + ....: def foo(): + ....: cdef MemoryAllocator mem = MemoryAllocator() + ....: cdef void* ptr + ....: for i in range(12): + ....: ptr = mem.aligned_calloc(2**i, i, 2**i) + ....: assert ptr == ( ptr) & ~(2**i-1) ....: ''') + sage: foo() + doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` + See https://trac.sagemath.org/31591 for details. """ # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1 # ⇔ extra * size >= alignment - 1 @@ -118,12 +122,15 @@ cdef class MemoryAllocator: sage: cython(''' ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: cdef MemoryAllocator mem = MemoryAllocator() - ....: cdef void* ptr - ....: for i in range(12): - ....: ptr = mem.aligned_allocarray(2**i, i, 2**i) - ....: assert ptr == ( ptr) & ~(2**i-1) + ....: def foo(): + ....: cdef MemoryAllocator mem = MemoryAllocator() + ....: cdef void* ptr + ....: for i in range(12): + ....: ptr = mem.aligned_allocarray(2**i, i, 2**i) + ....: assert ptr == ( ptr) & ~(2**i-1) ....: ''') + sage: foo() # random # might raise deprecation warning + sage: foo() """ # Find extra such that (nmemb + extra) * size >= nmemb * size + alignment - 1 # ⇔ extra * size >= alignment - 1 diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx index b5ffe70ccc1..819875efd65 100644 --- a/src/sage/ext/memory_allocator.pyx +++ b/src/sage/ext/memory_allocator.pyx @@ -34,11 +34,15 @@ cdef class MemoryAllocator: sage: cython( ....: ''' ....: from sage.ext.memory_allocator cimport MemoryAllocator - ....: cdef MemoryAllocator mem = MemoryAllocator.__new__(MemoryAllocator) - ....: mem.malloc(10000) - ....: print(mem.n) - ....: print(mem.size) + ....: def foo(): + ....: cdef MemoryAllocator mem = MemoryAllocator.__new__(MemoryAllocator) + ....: mem.malloc(10000) + ....: print(mem.n) + ....: print(mem.size) ....: ''') + sage: foo() + doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` + See https://trac.sagemath.org/31591 for details. 1 16 """ @@ -135,9 +139,8 @@ cdef class MemoryAllocator: ....: ptr = mem.malloc(20) ....: mem2.realloc(ptr, 21) ....: ''') + sage: test_realloc_good() # random # might raise deprecation warning sage: test_realloc_good() - doctest:...: DeprecationWarning: this class is deprecated; use the class from the python package `memory_allocator` - See https://trac.sagemath.org/31591 for details. sage: test_realloc_NULL() sage: test_realloc_bad() Traceback (most recent call last): diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index b51b0b93d02..718bd20cd52 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -1118,9 +1118,9 @@ def Bessel(*args, **kwds): sage: x,y = var('x,y') sage: f = maxima(Bessel(typ='K')(x,y)) sage: f.derivative('_SAGE_VAR_x') - (%pi*csc(%pi*_SAGE_VAR_x)*('diff(bessel_i(-_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1)-'diff(bessel_i(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1)))/2-%pi*bessel_k(_SAGE_VAR_x,_SAGE_VAR_y)*cot(%pi*_SAGE_VAR_x) + (%pi*csc(%pi*_SAGE_VAR_x) *('diff(bessel_i(-_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1) -'diff(bessel_i(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1))) /2 -%pi*bessel_k(_SAGE_VAR_x,_SAGE_VAR_y)*cot(%pi*_SAGE_VAR_x) sage: f.derivative('_SAGE_VAR_y') - -(bessel_k(_SAGE_VAR_x+1,_SAGE_VAR_y)+bessel_k(_SAGE_VAR_x-1,_SAGE_VAR_y))/2 + -(bessel_k(_SAGE_VAR_x+1,_SAGE_VAR_y)+bessel_k(_SAGE_VAR_x-1, _SAGE_VAR_y))/2 Compute the particular solution to Bessel's Differential Equation that satisfies `y(1) = 1` and `y'(1) = 1`, then verify the initial conditions diff --git a/src/sage/functions/orthogonal_polys.py b/src/sage/functions/orthogonal_polys.py index de8a18be031..fdeb5144122 100644 --- a/src/sage/functions/orthogonal_polys.py +++ b/src/sage/functions/orthogonal_polys.py @@ -2337,7 +2337,7 @@ def __init__(self): sage: maxima(gen_laguerre(1,2,x, hold=True)) 3*(1-_SAGE_VAR_x/3) sage: maxima(gen_laguerre(n, a, gen_laguerre(n, a, x))) - gen_laguerre(_SAGE_VAR_n,_SAGE_VAR_a,gen_laguerre(_SAGE_VAR_n,_SAGE_VAR_a,_SAGE_VAR_x)) + gen_laguerre(_SAGE_VAR_n,_SAGE_VAR_a, gen_laguerre(_SAGE_VAR_n,_SAGE_VAR_a,_SAGE_VAR_x)) """ OrthogonalFunction.__init__(self, "gen_laguerre", nargs=3, latex_name=r"L", conversions={'maxima':'gen_laguerre', 'mathematica':'LaguerreL', diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index c1d27faebdb..c7ad1ffaea9 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -30,15 +30,10 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import itertools - from sage.structure.element import coerce_binop, is_Vector, is_Matrix -from sage.structure.richcmp import rich_to_bool, op_NE from sage.cpython.string import bytes_to_str from sage.misc.cachefunc import cached_method -from sage.misc.misc_c import prod -from sage.misc.randstate import current_randstate from sage.rings.integer_ring import ZZ from sage.rings.qqbar import AA @@ -47,15 +42,12 @@ from sage.modules.free_module_element import vector from sage.modules.vector_space_morphism import linear_transformation from sage.matrix.constructor import matrix -from sage.arith.misc import integer_floor as floor -from sage.arith.misc import integer_ceil as ceil from sage.misc.lazy_import import lazy_import lazy_import('sage.groups.matrix_gps.finitely_generated', 'MatrixGroup') from sage.geometry.convex_set import AffineHullProjectionData from .constructor import Polyhedron -from .base1 import Polyhedron_base1 -from sage.categories.sets_cat import EmptySetError +from .base2 import Polyhedron_base2 ######################################################################### # Notes if you want to implement your own backend: @@ -100,7 +92,7 @@ def is_Polyhedron(X): ######################################################################### -class Polyhedron_base(Polyhedron_base1): +class Polyhedron_base(Polyhedron_base2): """ Base class for Polyhedron objects @@ -184,7 +176,7 @@ def _init_empty_polyhedron(self): sage: Polyhedron().facet_adjacency_matrix() [0] """ - Polyhedron_base1._init_empty_polyhedron(self) + Polyhedron_base2._init_empty_polyhedron(self) V_matrix = matrix(ZZ, 0, 0, 0) V_matrix.set_immutable() @@ -330,95 +322,6 @@ def _test_basic_properties(self, tester=None, **options): if self.n_inequalities() < 40: tester.assertEqual(self, Polyhedron(ieqs=self.inequalities(), eqns=self.equations(), ambient_dim=self.ambient_dim())) - def _richcmp_(self, other, op): - """ - Compare ``self`` and ``other``. - - INPUT: - - - ``other`` -- a polyhedron - - OUTPUT: - - If ``other`` is a polyhedron, then the comparison - operator "less or equal than" means "is contained in", and - "less than" means "is strictly contained in". - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)]) - sage: Q = Polyhedron(vertices=[(1,0), (0,1)]) - sage: P >= Q - True - sage: Q <= P - True - sage: P == P - True - - The polytope ``Q`` is strictly contained in ``P``:: - - sage: P > Q - True - sage: P < Q - False - sage: P == Q - False - - Test that we have fixed a problem revealed in :trac:`31701`, - where neither of the two polyhedra contains the other:: - - sage: P = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) - sage: Q = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) - sage: Q < P - False - sage: P > Q - False - """ - if self.Vrepresentation() is None or other.Vrepresentation() is None: - raise RuntimeError('some V representation is missing') - # make sure deleted polyhedra are not used in cache - - if self.ambient_dim() != other.ambient_dim(): - return op == op_NE - - c0 = self._is_subpolyhedron(other) - c1 = other._is_subpolyhedron(self) - if c0 and c1: - return rich_to_bool(op, 0) - elif c0: - return rich_to_bool(op, -1) - elif c1: - return rich_to_bool(op, 1) - else: - return op == op_NE - - @coerce_binop - def _is_subpolyhedron(self, other): - """ - Test whether ``self`` is a (not necessarily strict) - sub-polyhedron of ``other``. - - INPUT: - - - ``other`` -- a :class:`Polyhedron` - - OUTPUT: - - Boolean - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)]) - sage: Q = Polyhedron(vertices=[(1,0), (0,1)]) - sage: P._is_subpolyhedron(Q) - False - sage: Q._is_subpolyhedron(P) - True - """ - return all(other_H.contains(self_V) - for other_H in other.Hrepresentation() - for self_V in self.Vrepresentation()) - @cached_method def vertex_facet_graph(self, labels=True): r""" @@ -1729,7 +1632,7 @@ def center(self): .. SEEALSO:: - :meth:`representative_point`. + :meth:`sage.geometry.polyhedron.base1.Polyhedron_base1.representative_point`. OUTPUT: @@ -2106,21 +2009,6 @@ def is_inscribed(self, certificate=False): else: return is_inscribed - def is_compact(self): - """ - Test for boundedness of the polytope. - - EXAMPLES:: - - sage: p = polytopes.icosahedron() # optional - sage.rings.number_field - sage: p.is_compact() # optional - sage.rings.number_field - True - sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0],[0,0,0,1],[1,-1,0,0]]) - sage: p.is_compact() - False - """ - return self.n_rays() == 0 and self.n_lines() == 0 - @cached_method def combinatorial_polyhedron(self): r""" @@ -7661,260 +7549,6 @@ def is_neighborly(self, k=None): """ return self.combinatorial_polyhedron().is_neighborly() - @cached_method - def is_lattice_polytope(self): - r""" - Return whether the polyhedron is a lattice polytope. - - OUTPUT: - - ``True`` if the polyhedron is compact and has only integral - vertices, ``False`` otherwise. - - EXAMPLES:: - - sage: polytopes.cross_polytope(3).is_lattice_polytope() - True - sage: polytopes.regular_polygon(5).is_lattice_polytope() # optional - sage.rings.number_field - False - """ - if not self.is_compact(): - return False - if self.base_ring() is ZZ: - return True - return all(v.is_integral() for v in self.vertex_generator()) - - @cached_method - def lattice_polytope(self, envelope=False): - r""" - Return an encompassing lattice polytope. - - INPUT: - - - ``envelope`` -- boolean (default: ``False``). If the - polyhedron has non-integral vertices, this option decides - whether to return a strictly larger lattice polytope or - raise a ``ValueError``. This option has no effect if the - polyhedron has already integral vertices. - - OUTPUT: - - A :class:`LatticePolytope - `. If the - polyhedron is compact and has integral vertices, the lattice - polytope equals the polyhedron. If the polyhedron is compact - but has at least one non-integral vertex, a strictly larger - lattice polytope is returned. - - If the polyhedron is not compact, a ``NotImplementedError`` is - raised. - - If the polyhedron is not integral and ``envelope=False``, a - ``ValueError`` is raised. - - ALGORITHM: - - For each non-integral vertex, a bounding box of integral - points is added and the convex hull of these integral points - is returned. - - EXAMPLES: - - First, a polyhedron with integral vertices:: - - sage: P = Polyhedron( vertices = [(1, 0), (0, 1), (-1, 0), (0, -1)]) - sage: lp = P.lattice_polytope(); lp - 2-d reflexive polytope #3 in 2-d lattice M - sage: lp.vertices() - M(-1, 0), - M( 0, -1), - M( 0, 1), - M( 1, 0) - in 2-d lattice M - - Here is a polyhedron with non-integral vertices:: - - sage: P = Polyhedron( vertices = [(1/2, 1/2), (0, 1), (-1, 0), (0, -1)]) - sage: lp = P.lattice_polytope() - Traceback (most recent call last): - ... - ValueError: Some vertices are not integral. You probably want - to add the argument "envelope=True" to compute an enveloping - lattice polytope. - sage: lp = P.lattice_polytope(True); lp - 2-d reflexive polytope #5 in 2-d lattice M - sage: lp.vertices() - M(-1, 0), - M( 0, -1), - M( 1, 1), - M( 0, 1), - M( 1, 0) - in 2-d lattice M - """ - if not self.is_compact(): - raise NotImplementedError('only compact lattice polytopes are allowed') - - try: - vertices = self.vertices_matrix(ZZ).columns() - except TypeError: - if not envelope: - raise ValueError('Some vertices are not integral. ' - 'You probably want to add the argument ' - '"envelope=True" to compute an enveloping lattice polytope.') - vertices = [] - for v in self.vertex_generator(): - vbox = [ set([floor(x), ceil(x)]) for x in v ] - vertices.extend( itertools.product(*vbox) ) - - # construct the (enveloping) lattice polytope - from sage.geometry.lattice_polytope import LatticePolytope - return LatticePolytope(vertices) - - def _integral_points_PALP(self): - r""" - Return the integral points in the polyhedron using PALP. - - This method is for testing purposes and will eventually be removed. - - OUTPUT: - - The list of integral points in the polyhedron. If the - polyhedron is not compact, a ``ValueError`` is raised. - - EXAMPLES:: - - sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)])._integral_points_PALP() - [M(-1, -1), M(0, 1), M(1, 0), M(1, 1), M(0, 0)] - sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)]).lattice_polytope(True).points() - M(-1, -1), - M(-1, 0), - M( 0, -1), - M( 1, 1), - M( 0, 1), - M( 1, 0), - M( 0, 0) - in 2-d lattice M - sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)])._integral_points_PALP() - [M(1, 1), M(0, 1), M(1, 0), M(0, 0)] - """ - if not self.is_compact(): - raise ValueError('can only enumerate points in a compact polyhedron') - lp = self.lattice_polytope(True) - # remove cached values to get accurate timings - try: - del lp._points - del lp._npoints - except AttributeError: - pass - if self.is_lattice_polytope(): - return list(lp.points()) - return [p for p in lp.points() if self.contains(p)] - - @cached_method(do_pickle=True) - def h_star_vector(self): - r""" - Return the `h^*`-vector of the lattice polytope. - - The `h^*`-vector records the coefficients of the polynomial in the - numerator of the Ehrhart series of a lattice polytope. - - INPUT: - - - ``self`` -- A lattice polytope. - - OUTPUT: - - A list whose entries give the `h^*`-vector. - - .. NOTE: - - The backend of ``self`` should be ``'normaliz'``. - This function depends on Normaliz (i.e. the ``'pynormaliz'`` optional - package). See the Normaliz documentation for further details. - - EXAMPLES: - - The `h^*`-vector of a unimodular simplex S (a simplex with - volume = `\frac{1}{dim(S)!}`) is always 1. Here we test this on - simplices up to dimension 3:: - - sage: s1 = polytopes.simplex(1,backend='normaliz') # optional - pynormaliz - sage: s2 = polytopes.simplex(2,backend='normaliz') # optional - pynormaliz - sage: s3 = polytopes.simplex(3,backend='normaliz') # optional - pynormaliz - sage: [s1.h_star_vector(),s2.h_star_vector(),s3.h_star_vector()] # optional - pynormaliz - [[1], [1], [1]] - - For a less trivial example, we compute the `h^*`-vector of the - `0/1`-cube, which has the Eulerian numbers `(3,i)` for `i \in [0,2]` - as an `h^*`-vector:: - - sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz') # optional - pynormaliz - sage: cube.h_star_vector() # optional - pynormaliz - [1, 4, 1] - sage: from sage.combinat.combinat import eulerian_number - sage: [eulerian_number(3,i) for i in range(3)] - [1, 4, 1] - - TESTS:: - - sage: s3 = polytopes.simplex(3) - sage: s3.h_star_vector() - Traceback (most recent call last): - ... - TypeError: The backend of self must be normaliz - - sage: t = Polyhedron(vertices=[[0],[1/2]]) - sage: t.h_star_vector() - Traceback (most recent call last): - ... - TypeError: The h_star vector is only defined for lattice polytopes - - sage: t2 = Polyhedron(vertices=[[AA(sqrt(2))],[1/2]]) - sage: t2.h_star_vector() - Traceback (most recent call last): - ... - TypeError: The h_star vector is only defined for lattice polytopes - - Check that the `h^*`-vector is pickled:: - - sage: new_cube = loads(dumps(cube)) # optional - pynormaliz - sage: new_cube.h_star_vector.is_in_cache() # optional - pynormaliz - True - """ - if self.is_empty(): - return 0 - if not self.is_lattice_polytope(): - raise TypeError('The h_star vector is only defined for lattice polytopes') - if not self.backend() == 'normaliz': - raise TypeError('The backend of self must be normaliz') - return self._h_star_vector_normaliz() - - def _h_star_vector_normaliz(self): - r""" - Return the `h^*`-vector of a lattice polytope with backend = 'normaliz'. - - INPUT: - - - ``self`` -- A lattice polytope. - - OUTPUT: - - The `h^*`-vector as a list. - - .. NOTE: - - The backend of ``self`` should be ``'normaliz'``. - - TESTS:: - - sage: s3 = polytopes.simplex(3) - sage: s3._h_star_vector_normaliz() - Traceback (most recent call last): - ... - TypeError: the backend should be normaliz - """ - raise TypeError("the backend should be normaliz") - @cached_method def bounding_box(self, integral=False, integral_hull=False): r""" @@ -7956,6 +7590,8 @@ def bounding_box(self, integral=False, integral_hull=False): ... ValueError: empty polytope is not allowed """ + from sage.arith.misc import integer_ceil as ceil + from sage.arith.misc import integer_floor as floor box_min = [] box_max = [] if not self.is_compact(): @@ -7981,339 +7617,6 @@ def bounding_box(self, integral=False, integral_hull=False): box_min.append(min_coord) return (tuple(box_min), tuple(box_max)) - def integral_points_count(self, **kwds): - r""" - Return the number of integral points in the polyhedron. - - This generic version of this method simply calls ``integral_points``. - - EXAMPLES:: - - sage: P = polytopes.cube() - sage: P.integral_points_count() - 27 - - We shrink the polyhedron a little bit:: - - sage: Q = P*(8/9) - sage: Q.integral_points_count() - 1 - - Same for a polyhedron whose coordinates are not rationals. Note that - the answer is an integer even though there are no guarantees for - exactness:: - - sage: Q = P*RDF(8/9) - sage: Q.integral_points_count() - 1 - - Unbounded polyhedra (with or without lattice points) are not supported:: - - sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) - sage: P.integral_points_count() - Traceback (most recent call last): - ... - NotImplementedError: ... - sage: P = Polyhedron(vertices=[[1, 1]], rays=[[1, 1]]) - sage: P.integral_points_count() - Traceback (most recent call last): - ... - NotImplementedError: ... - - """ - return len(self.integral_points()) - - def integral_points(self, threshold=100000): - r""" - Return the integral points in the polyhedron. - - Uses either the naive algorithm (iterate over a rectangular - bounding box) or triangulation + Smith form. - - INPUT: - - - ``threshold`` -- integer (default: 100000). Use the naive - algorithm as long as the bounding box is smaller than this. - - OUTPUT: - - The list of integral points in the polyhedron. If the - polyhedron is not compact, a ``ValueError`` is raised. - - EXAMPLES:: - - sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]).integral_points() - ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1)) - - sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)]) - sage: simplex.integral_points() - ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7)) - - The polyhedron need not be full-dimensional:: - - sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)]) - sage: simplex.integral_points() - ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5)) - - sage: point = Polyhedron([(2,3,7)]) - sage: point.integral_points() - ((2, 3, 7),) - - sage: empty = Polyhedron() - sage: empty.integral_points() - () - - Here is a simplex where the naive algorithm of running over - all points in a rectangular bounding box no longer works fast - enough:: - - sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)] - sage: simplex = Polyhedron(v); simplex - A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices - sage: len(simplex.integral_points()) - 49 - - A case where rounding in the right direction goes a long way:: - - sage: P = 1/10*polytopes.hypercube(14, backend='field') - sage: P.integral_points() - ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),) - - Finally, the 3-d reflexive polytope number 4078:: - - sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1), - ....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)] - sage: P = Polyhedron(v) - sage: pts1 = P.integral_points() # Sage's own code - sage: all(P.contains(p) for p in pts1) - True - sage: pts2 = LatticePolytope(v).points() # PALP - sage: for p in pts1: p.set_immutable() - sage: set(pts1) == set(pts2) - True - - sage: timeit('Polyhedron(v).integral_points()') # not tested - random - 625 loops, best of 3: 1.41 ms per loop - sage: timeit('LatticePolytope(v).points()') # not tested - random - 25 loops, best of 3: 17.2 ms per loop - - TESTS: - - Test some trivial cases (see :trac:`17937`):: - - sage: P = Polyhedron(ambient_dim=1) # empty polyhedron in 1 dimension - sage: P.integral_points() - () - sage: P = Polyhedron(ambient_dim=0) # empty polyhedron in 0 dimensions - sage: P.integral_points() - () - sage: P = Polyhedron([[3]]) # single point in 1 dimension - sage: P.integral_points() - ((3),) - sage: P = Polyhedron([[1/2]]) # single non-integral point in 1 dimension - sage: P.integral_points() - () - sage: P = Polyhedron([[]]) # single point in 0 dimensions - sage: P.integral_points() - ((),) - - Test unbounded polyhedron:: - - sage: P = Polyhedron(rays=[[1,0,0]]) - sage: P.integral_points() - Traceback (most recent call last): - ... - ValueError: can only enumerate points in a compact polyhedron - """ - if not self.is_compact(): - raise ValueError('can only enumerate points in a compact polyhedron') - # Trivial cases: polyhedron with 0 or 1 vertices - if self.n_vertices() == 0: - return () - if self.n_vertices() == 1: - v = self.vertices_list()[0] - try: - return (vector(ZZ, v),) - except TypeError: # vertex not integral - return () - - # for small bounding boxes, it is faster to naively iterate over the points of the box - box_min, box_max = self.bounding_box(integral_hull=True) - if box_min is None: - return () - box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max)) - if not self.is_lattice_polytope() or \ - (self.is_simplex() and box_points < 1000) or \ - box_points < threshold: - from sage.geometry.integral_points import rectangular_box_points - return rectangular_box_points(list(box_min), list(box_max), self) - - # for more complicate polytopes, triangulate & use smith normal form - from sage.geometry.integral_points import simplex_points - if self.is_simplex(): - return simplex_points(self.Vrepresentation()) - triangulation = self.triangulate() - points = set() - for simplex in triangulation: - triang_vertices = [self.Vrepresentation(i) for i in simplex] - new_points = simplex_points(triang_vertices) - for p in new_points: - p.set_immutable() - points.update(new_points) - # assert all(self.contains(p) for p in points) # slow - return tuple(points) - - def get_integral_point(self, index, **kwds): - r""" - Return the ``index``-th integral point in this polyhedron. - - This is equivalent to ``sorted(self.integral_points())[index]``. - However, so long as self.integral_points_count() does not need to - enumerate all integral points, neither does this method. Hence it can - be significantly faster. If the polyhedron is not compact, a - ``ValueError`` is raised. - - INPUT: - - - ``index`` -- integer. The index of the integral point to be found. If - this is not in [0, ``self.integral_point_count()``), an ``IndexError`` - is raised. - - - ``**kwds`` -- optional keyword parameters that are passed to - :meth:`self.integral_points_count`. - - ALGORITHM: - - The function computes each of the components of the requested point in - turn. To compute x_i, the ith component, it bisects the upper and lower - bounds on x_i given by the bounding box. At each bisection, it uses - :meth:`integral_points_count` to determine on which side of the - bisecting hyperplane the requested point lies. - - .. SEEALSO:: - - :meth:`integral_points_count`. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) - sage: P.get_integral_point(1) - (0, 0) - sage: P.get_integral_point(4) - (1, 1) - sage: sorted(P.integral_points()) - [(-1, -1), (0, 0), (0, 1), (1, 0), (1, 1)] - sage: P.get_integral_point(5) - Traceback (most recent call last): - ... - IndexError: ... - - sage: Q = Polyhedron([(1,3), (2, 7), (9, 77)]) - sage: [Q.get_integral_point(i) for i in range(Q.integral_points_count())] == sorted(Q.integral_points()) - True - sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib') # optional - latte_int - (1, 3) - sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib', foo=True) # optional - latte_int - Traceback (most recent call last): - ... - RuntimeError: ... - - sage: R = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) - sage: R.get_integral_point(0) - Traceback (most recent call last): - ... - ValueError: ... - """ - - if not self.is_compact(): - raise ValueError('can only enumerate points in a compact polyhedron') - - if not 0 <= index < self.integral_points_count(**kwds): - raise IndexError('polytope index out of range') - - D = self.ambient_dim() - lower_bounds, upper_bounds = self.bounding_box() - coordinate = [] - P = self - S = self.parent() - for i in range(D): # Now compute x_i, the ith component of coordinate. - lower, upper = ceil(lower_bounds[i]), floor(upper_bounds[i]) + 1 # So lower <= x_i < upper. - while lower < upper-1: - guess = (lower + upper) // 2 # > lower. - # Build new polyhedron by intersecting P with the halfspace {x_i < guess}. - P_lt_guess = P.intersection(S(None, ([[guess-1] + [0] * i + [-1] + [0] * (D - i - 1)], []))) - # Avoid computing P_geq_guess = P.intersection({x_i >= guess}) right now, it might not be needed. - P_lt_guess_count = P_lt_guess.integral_points_count(**kwds) - if P_lt_guess_count > index: # Move upper down to guess. - upper = guess - index -= 0 - P = P_lt_guess - else: # P_lt_guess_count <= index: # Move lower up to guess. - lower = guess - index -= P_lt_guess_count - P_geq_guess = P.intersection(S(None, ([[-guess] + [0] * i + [1] + [0] * (D - i - 1)], []))) - P = P_geq_guess - coordinate.append(lower) # Record the new component that we have found. - point = vector(ZZ, coordinate) - point.set_immutable() - return point - - def random_integral_point(self, **kwds): - r""" - Return an integral point in this polyhedron chosen uniformly at random. - - INPUT: - - - ``**kwds`` -- optional keyword parameters that are passed to - :meth:`self.get_integral_point`. - - OUTPUT: - - The integral point in the polyhedron chosen uniformly at random. If the - polyhedron is not compact, a ``ValueError`` is raised. If the - polyhedron does not contain any integral points, an ``EmptySetError`` is - raised. - - .. SEEALSO:: - - :meth:`get_integral_point`. - - EXAMPLES:: - - sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) - sage: P.random_integral_point() # random - (0, 0) - sage: P.random_integral_point() in P.integral_points() - True - sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib') # random, optional - latte_int - (1, 1) - sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib', foo=7) # optional - latte_int - Traceback (most recent call last): - ... - RuntimeError: ... - - sage: Q = Polyhedron(vertices=[(2, 1/3)], rays=[(1, 2)]) - sage: Q.random_integral_point() - Traceback (most recent call last): - ... - ValueError: ... - - sage: R = Polyhedron(vertices=[(1/2, 0), (1, 1/2), (0, 1/2)]) - sage: R.random_integral_point() - Traceback (most recent call last): - ... - EmptySetError: ... - """ - - if not self.is_compact(): - raise ValueError('can only sample integral points in a compact polyhedron') - - count = self.integral_points_count() - if count == 0: - raise EmptySetError('polyhedron does not contain any integral points') - - return self.get_integral_point(current_randstate().python_random().randint(0, count-1), **kwds) - @cached_method def combinatorial_automorphism_group(self, vertex_graph_only=False): """ diff --git a/src/sage/geometry/polyhedron/base0.py b/src/sage/geometry/polyhedron/base0.py index 537a4c08bd7..c9bbd20dd81 100644 --- a/src/sage/geometry/polyhedron/base0.py +++ b/src/sage/geometry/polyhedron/base0.py @@ -503,6 +503,21 @@ def n_lines(self): """ return len(self.lines()) + def is_compact(self): + """ + Test for boundedness of the polytope. + + EXAMPLES:: + + sage: p = polytopes.icosahedron() # optional - sage.rings.number_field + sage: p.is_compact() # optional - sage.rings.number_field + True + sage: p = Polyhedron(ieqs = [[0,1,0,0],[0,0,1,0],[0,0,0,1],[1,-1,0,0]]) + sage: p.is_compact() + False + """ + return self.n_rays() == 0 and self.n_lines() == 0 + def Hrepresentation(self, index=None): """ Return the objects of the H-representation. Each entry is diff --git a/src/sage/geometry/polyhedron/base1.py b/src/sage/geometry/polyhedron/base1.py index d580cb8924e..22a9ab53cde 100644 --- a/src/sage/geometry/polyhedron/base1.py +++ b/src/sage/geometry/polyhedron/base1.py @@ -33,6 +33,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.structure.element import coerce_binop +from sage.structure.richcmp import rich_to_bool, op_NE from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.modules.free_module_element import vector @@ -168,6 +170,95 @@ def _repr_(self): return desc + def _richcmp_(self, other, op): + """ + Compare ``self`` and ``other``. + + INPUT: + + - ``other`` -- a polyhedron + + OUTPUT: + + If ``other`` is a polyhedron, then the comparison + operator "less or equal than" means "is contained in", and + "less than" means "is strictly contained in". + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)]) + sage: Q = Polyhedron(vertices=[(1,0), (0,1)]) + sage: P >= Q + True + sage: Q <= P + True + sage: P == P + True + + The polytope ``Q`` is strictly contained in ``P``:: + + sage: P > Q + True + sage: P < Q + False + sage: P == Q + False + + Test that we have fixed a problem revealed in :trac:`31701`, + where neither of the two polyhedra contains the other:: + + sage: P = Polyhedron(vertices=[(1, 1), (0, 0), (1, 2)]) + sage: Q = Polyhedron(vertices=[(1, 2), (0, 0), (0, 2)]) + sage: Q < P + False + sage: P > Q + False + """ + if self.Vrepresentation() is None or other.Vrepresentation() is None: + raise RuntimeError('some V representation is missing') + # make sure deleted polyhedra are not used in cache + + if self.ambient_dim() != other.ambient_dim(): + return op == op_NE + + c0 = self._is_subpolyhedron(other) + c1 = other._is_subpolyhedron(self) + if c0 and c1: + return rich_to_bool(op, 0) + elif c0: + return rich_to_bool(op, -1) + elif c1: + return rich_to_bool(op, 1) + else: + return op == op_NE + + @coerce_binop + def _is_subpolyhedron(self, other): + """ + Test whether ``self`` is a (not necessarily strict) + sub-polyhedron of ``other``. + + INPUT: + + - ``other`` -- a :class:`Polyhedron` + + OUTPUT: + + Boolean + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(1,0), (0,1)], rays=[(1,1)]) + sage: Q = Polyhedron(vertices=[(1,0), (0,1)]) + sage: P._is_subpolyhedron(Q) + False + sage: Q._is_subpolyhedron(P) + True + """ + return all(other_H.contains(self_V) + for other_H in other.Hrepresentation() + for self_V in self.Vrepresentation()) + def is_empty(self): """ Test whether the polyhedron is the empty polyhedron @@ -423,7 +514,7 @@ def representative_point(self): .. SEEALSO:: - :meth:`center`. + :meth:`sage.geometry.polyhedron.base.Polyhedron_base.center`. OUTPUT: diff --git a/src/sage/geometry/polyhedron/base2.py b/src/sage/geometry/polyhedron/base2.py new file mode 100644 index 00000000000..db88f584c66 --- /dev/null +++ b/src/sage/geometry/polyhedron/base2.py @@ -0,0 +1,677 @@ +r""" +Base class for polyhedra, part 2 + +Define methods related to lattice points. +""" + +# **************************************************************************** +# Copyright (C) 2008-2012 Marshall Hampton +# Copyright (C) 2011-2015 Volker Braun +# Copyright (C) 2012-2018 Frederic Chapoton +# Copyright (C) 2013 Andrey Novoseltsev +# Copyright (C) 2014-2017 Moritz Firsching +# Copyright (C) 2014-2019 Thierry Monteil +# Copyright (C) 2015 Nathann Cohen +# Copyright (C) 2015-2017 Jeroen Demeyer +# Copyright (C) 2015-2017 Vincent Delecroix +# Copyright (C) 2015-2018 Dima Pasechnik +# Copyright (C) 2015-2020 Jean-Philippe Labbe +# Copyright (C) 2015-2021 Matthias Koeppe +# Copyright (C) 2016-2019 Daniel Krenn +# Copyright (C) 2017 Marcelo Forets +# Copyright (C) 2017-2018 Mark Bell +# Copyright (C) 2019 Julian Ritter +# Copyright (C) 2019-2020 Laith Rastanawi +# Copyright (C) 2019-2020 Sophia Elia +# Copyright (C) 2019-2021 Jonathan Kliem +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +import itertools + +from sage.misc.cachefunc import cached_method +from sage.rings.integer_ring import ZZ +from sage.modules.free_module_element import vector +from .base1 import Polyhedron_base1 + +class Polyhedron_base2(Polyhedron_base1): + """ + Methods related to lattice points. + + See :class:`sage.geometry.polyhedron.base.Polyhedron_base`. + + TESTS:: + + sage: from sage.geometry.polyhedron.base2 import Polyhedron_base2 + sage: P = polytopes.cube() + sage: Polyhedron_base2.is_lattice_polytope.f(P) + True + sage: Polyhedron_base2.lattice_polytope(P) + 3-d reflexive polytope in 3-d lattice M + sage: Polyhedron_base2.integral_points(P) + ((-1, -1, -1), + (-1, -1, 0), + (-1, -1, 1), + (-1, 0, -1), + (-1, 0, 0), + (-1, 0, 1), + (-1, 1, -1), + (-1, 1, 0), + (-1, 1, 1), + (0, -1, -1), + (0, -1, 0), + (0, -1, 1), + (0, 0, -1), + (0, 0, 0), + (0, 0, 1), + (0, 1, -1), + (0, 1, 0), + (0, 1, 1), + (1, -1, -1), + (1, -1, 0), + (1, -1, 1), + (1, 0, -1), + (1, 0, 0), + (1, 0, 1), + (1, 1, -1), + (1, 1, 0), + (1, 1, 1)) + """ + + @cached_method + def is_lattice_polytope(self): + r""" + Return whether the polyhedron is a lattice polytope. + + OUTPUT: + + ``True`` if the polyhedron is compact and has only integral + vertices, ``False`` otherwise. + + EXAMPLES:: + + sage: polytopes.cross_polytope(3).is_lattice_polytope() + True + sage: polytopes.regular_polygon(5).is_lattice_polytope() # optional - sage.rings.number_field + False + """ + if not self.is_compact(): + return False + if self.base_ring() is ZZ: + return True + return all(v.is_integral() for v in self.vertex_generator()) + + @cached_method + def lattice_polytope(self, envelope=False): + r""" + Return an encompassing lattice polytope. + + INPUT: + + - ``envelope`` -- boolean (default: ``False``). If the + polyhedron has non-integral vertices, this option decides + whether to return a strictly larger lattice polytope or + raise a ``ValueError``. This option has no effect if the + polyhedron has already integral vertices. + + OUTPUT: + + A :class:`LatticePolytope + `. If the + polyhedron is compact and has integral vertices, the lattice + polytope equals the polyhedron. If the polyhedron is compact + but has at least one non-integral vertex, a strictly larger + lattice polytope is returned. + + If the polyhedron is not compact, a ``NotImplementedError`` is + raised. + + If the polyhedron is not integral and ``envelope=False``, a + ``ValueError`` is raised. + + ALGORITHM: + + For each non-integral vertex, a bounding box of integral + points is added and the convex hull of these integral points + is returned. + + EXAMPLES: + + First, a polyhedron with integral vertices:: + + sage: P = Polyhedron( vertices = [(1, 0), (0, 1), (-1, 0), (0, -1)]) + sage: lp = P.lattice_polytope(); lp + 2-d reflexive polytope #3 in 2-d lattice M + sage: lp.vertices() + M(-1, 0), + M( 0, -1), + M( 0, 1), + M( 1, 0) + in 2-d lattice M + + Here is a polyhedron with non-integral vertices:: + + sage: P = Polyhedron( vertices = [(1/2, 1/2), (0, 1), (-1, 0), (0, -1)]) + sage: lp = P.lattice_polytope() + Traceback (most recent call last): + ... + ValueError: Some vertices are not integral. You probably want + to add the argument "envelope=True" to compute an enveloping + lattice polytope. + sage: lp = P.lattice_polytope(True); lp + 2-d reflexive polytope #5 in 2-d lattice M + sage: lp.vertices() + M(-1, 0), + M( 0, -1), + M( 1, 1), + M( 0, 1), + M( 1, 0) + in 2-d lattice M + """ + if not self.is_compact(): + raise NotImplementedError('only compact lattice polytopes are allowed') + + try: + vertices = self.vertices_matrix(ZZ).columns() + except TypeError: + if not envelope: + raise ValueError('Some vertices are not integral. ' + 'You probably want to add the argument ' + '"envelope=True" to compute an enveloping lattice polytope.') + from sage.arith.misc import integer_ceil as ceil + from sage.arith.misc import integer_floor as floor + vertices = [] + for v in self.vertex_generator(): + vbox = [ set([floor(x), ceil(x)]) for x in v ] + vertices.extend( itertools.product(*vbox) ) + + # construct the (enveloping) lattice polytope + from sage.geometry.lattice_polytope import LatticePolytope + return LatticePolytope(vertices) + + def _integral_points_PALP(self): + r""" + Return the integral points in the polyhedron using PALP. + + This method is for testing purposes and will eventually be removed. + + OUTPUT: + + The list of integral points in the polyhedron. If the + polyhedron is not compact, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)])._integral_points_PALP() + [M(-1, -1), M(0, 1), M(1, 0), M(1, 1), M(0, 0)] + sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)]).lattice_polytope(True).points() + M(-1, -1), + M(-1, 0), + M( 0, -1), + M( 1, 1), + M( 0, 1), + M( 1, 0), + M( 0, 0) + in 2-d lattice M + sage: Polyhedron(vertices=[(-1/2,-1/2),(1,0),(1,1),(0,1)])._integral_points_PALP() + [M(1, 1), M(0, 1), M(1, 0), M(0, 0)] + """ + if not self.is_compact(): + raise ValueError('can only enumerate points in a compact polyhedron') + lp = self.lattice_polytope(True) + # remove cached values to get accurate timings + try: + del lp._points + del lp._npoints + except AttributeError: + pass + if self.is_lattice_polytope(): + return list(lp.points()) + return [p for p in lp.points() if self.contains(p)] + + @cached_method(do_pickle=True) + def h_star_vector(self): + r""" + Return the `h^*`-vector of the lattice polytope. + + The `h^*`-vector records the coefficients of the polynomial in the + numerator of the Ehrhart series of a lattice polytope. + + INPUT: + + - ``self`` -- A lattice polytope. + + OUTPUT: + + A list whose entries give the `h^*`-vector. + + .. NOTE: + + The backend of ``self`` should be ``'normaliz'``. + This function depends on Normaliz (i.e. the ``'pynormaliz'`` optional + package). See the Normaliz documentation for further details. + + EXAMPLES: + + The `h^*`-vector of a unimodular simplex S (a simplex with + volume = `\frac{1}{dim(S)!}`) is always 1. Here we test this on + simplices up to dimension 3:: + + sage: s1 = polytopes.simplex(1,backend='normaliz') # optional - pynormaliz + sage: s2 = polytopes.simplex(2,backend='normaliz') # optional - pynormaliz + sage: s3 = polytopes.simplex(3,backend='normaliz') # optional - pynormaliz + sage: [s1.h_star_vector(),s2.h_star_vector(),s3.h_star_vector()] # optional - pynormaliz + [[1], [1], [1]] + + For a less trivial example, we compute the `h^*`-vector of the + `0/1`-cube, which has the Eulerian numbers `(3,i)` for `i \in [0,2]` + as an `h^*`-vector:: + + sage: cube = polytopes.cube(intervals='zero_one', backend='normaliz') # optional - pynormaliz + sage: cube.h_star_vector() # optional - pynormaliz + [1, 4, 1] + sage: from sage.combinat.combinat import eulerian_number + sage: [eulerian_number(3,i) for i in range(3)] + [1, 4, 1] + + TESTS:: + + sage: s3 = polytopes.simplex(3) + sage: s3.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The backend of self must be normaliz + + sage: t = Polyhedron(vertices=[[0],[1/2]]) + sage: t.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The h_star vector is only defined for lattice polytopes + + sage: t2 = Polyhedron(vertices=[[AA(sqrt(2))],[1/2]]) + sage: t2.h_star_vector() + Traceback (most recent call last): + ... + TypeError: The h_star vector is only defined for lattice polytopes + + Check that the `h^*`-vector is pickled:: + + sage: new_cube = loads(dumps(cube)) # optional - pynormaliz + sage: new_cube.h_star_vector.is_in_cache() # optional - pynormaliz + True + """ + if self.is_empty(): + return 0 + if not self.is_lattice_polytope(): + raise TypeError('The h_star vector is only defined for lattice polytopes') + if not self.backend() == 'normaliz': + raise TypeError('The backend of self must be normaliz') + return self._h_star_vector_normaliz() + + def _h_star_vector_normaliz(self): + r""" + Return the `h^*`-vector of a lattice polytope with backend = 'normaliz'. + + INPUT: + + - ``self`` -- A lattice polytope. + + OUTPUT: + + The `h^*`-vector as a list. + + .. NOTE: + + The backend of ``self`` should be ``'normaliz'``. + + TESTS:: + + sage: s3 = polytopes.simplex(3) + sage: s3._h_star_vector_normaliz() + Traceback (most recent call last): + ... + TypeError: the backend should be normaliz + """ + raise TypeError("the backend should be normaliz") + + def integral_points_count(self, **kwds): + r""" + Return the number of integral points in the polyhedron. + + This generic version of this method simply calls ``integral_points``. + + EXAMPLES:: + + sage: P = polytopes.cube() + sage: P.integral_points_count() + 27 + + We shrink the polyhedron a little bit:: + + sage: Q = P*(8/9) + sage: Q.integral_points_count() + 1 + + Same for a polyhedron whose coordinates are not rationals. Note that + the answer is an integer even though there are no guarantees for + exactness:: + + sage: Q = P*RDF(8/9) + sage: Q.integral_points_count() + 1 + + Unbounded polyhedra (with or without lattice points) are not supported:: + + sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) + sage: P.integral_points_count() + Traceback (most recent call last): + ... + NotImplementedError: ... + sage: P = Polyhedron(vertices=[[1, 1]], rays=[[1, 1]]) + sage: P.integral_points_count() + Traceback (most recent call last): + ... + NotImplementedError: ... + + """ + return len(self.integral_points()) + + def integral_points(self, threshold=100000): + r""" + Return the integral points in the polyhedron. + + Uses either the naive algorithm (iterate over a rectangular + bounding box) or triangulation + Smith form. + + INPUT: + + - ``threshold`` -- integer (default: 100000). Use the naive + algorithm as long as the bounding box is smaller than this. + + OUTPUT: + + The list of integral points in the polyhedron. If the + polyhedron is not compact, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]).integral_points() + ((-1, -1), (0, 0), (0, 1), (1, 0), (1, 1)) + + sage: simplex = Polyhedron([(1,2,3), (2,3,7), (-2,-3,-11)]) + sage: simplex.integral_points() + ((-2, -3, -11), (0, 0, -2), (1, 2, 3), (2, 3, 7)) + + The polyhedron need not be full-dimensional:: + + sage: simplex = Polyhedron([(1,2,3,5), (2,3,7,5), (-2,-3,-11,5)]) + sage: simplex.integral_points() + ((-2, -3, -11, 5), (0, 0, -2, 5), (1, 2, 3, 5), (2, 3, 7, 5)) + + sage: point = Polyhedron([(2,3,7)]) + sage: point.integral_points() + ((2, 3, 7),) + + sage: empty = Polyhedron() + sage: empty.integral_points() + () + + Here is a simplex where the naive algorithm of running over + all points in a rectangular bounding box no longer works fast + enough:: + + sage: v = [(1,0,7,-1), (-2,-2,4,-3), (-1,-1,-1,4), (2,9,0,-5), (-2,-1,5,1)] + sage: simplex = Polyhedron(v); simplex + A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 5 vertices + sage: len(simplex.integral_points()) + 49 + + A case where rounding in the right direction goes a long way:: + + sage: P = 1/10*polytopes.hypercube(14, backend='field') + sage: P.integral_points() + ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),) + + Finally, the 3-d reflexive polytope number 4078:: + + sage: v = [(1,0,0), (0,1,0), (0,0,1), (0,0,-1), (0,-2,1), + ....: (-1,2,-1), (-1,2,-2), (-1,1,-2), (-1,-1,2), (-1,-3,2)] + sage: P = Polyhedron(v) + sage: pts1 = P.integral_points() # Sage's own code + sage: all(P.contains(p) for p in pts1) + True + sage: pts2 = LatticePolytope(v).points() # PALP + sage: for p in pts1: p.set_immutable() + sage: set(pts1) == set(pts2) + True + + sage: timeit('Polyhedron(v).integral_points()') # not tested - random + 625 loops, best of 3: 1.41 ms per loop + sage: timeit('LatticePolytope(v).points()') # not tested - random + 25 loops, best of 3: 17.2 ms per loop + + TESTS: + + Test some trivial cases (see :trac:`17937`):: + + sage: P = Polyhedron(ambient_dim=1) # empty polyhedron in 1 dimension + sage: P.integral_points() + () + sage: P = Polyhedron(ambient_dim=0) # empty polyhedron in 0 dimensions + sage: P.integral_points() + () + sage: P = Polyhedron([[3]]) # single point in 1 dimension + sage: P.integral_points() + ((3),) + sage: P = Polyhedron([[1/2]]) # single non-integral point in 1 dimension + sage: P.integral_points() + () + sage: P = Polyhedron([[]]) # single point in 0 dimensions + sage: P.integral_points() + ((),) + + Test unbounded polyhedron:: + + sage: P = Polyhedron(rays=[[1,0,0]]) + sage: P.integral_points() + Traceback (most recent call last): + ... + ValueError: can only enumerate points in a compact polyhedron + """ + from sage.misc.misc_c import prod + if not self.is_compact(): + raise ValueError('can only enumerate points in a compact polyhedron') + # Trivial cases: polyhedron with 0 or 1 vertices + if self.n_vertices() == 0: + return () + if self.n_vertices() == 1: + v = self.vertices_list()[0] + try: + return (vector(ZZ, v),) + except TypeError: # vertex not integral + return () + + # for small bounding boxes, it is faster to naively iterate over the points of the box + box_min, box_max = self.bounding_box(integral_hull=True) + if box_min is None: + return () + box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max)) + if not self.is_lattice_polytope() or \ + (self.is_simplex() and box_points < 1000) or \ + box_points < threshold: + from sage.geometry.integral_points import rectangular_box_points + return rectangular_box_points(list(box_min), list(box_max), self) + + # for more complicate polytopes, triangulate & use smith normal form + from sage.geometry.integral_points import simplex_points + if self.is_simplex(): + return simplex_points(self.Vrepresentation()) + triangulation = self.triangulate() + points = set() + for simplex in triangulation: + triang_vertices = [self.Vrepresentation(i) for i in simplex] + new_points = simplex_points(triang_vertices) + for p in new_points: + p.set_immutable() + points.update(new_points) + # assert all(self.contains(p) for p in points) # slow + return tuple(points) + + def get_integral_point(self, index, **kwds): + r""" + Return the ``index``-th integral point in this polyhedron. + + This is equivalent to ``sorted(self.integral_points())[index]``. + However, so long as self.integral_points_count() does not need to + enumerate all integral points, neither does this method. Hence it can + be significantly faster. If the polyhedron is not compact, a + ``ValueError`` is raised. + + INPUT: + + - ``index`` -- integer. The index of the integral point to be found. If + this is not in [0, ``self.integral_point_count()``), an ``IndexError`` + is raised. + + - ``**kwds`` -- optional keyword parameters that are passed to + :meth:`self.integral_points_count`. + + ALGORITHM: + + The function computes each of the components of the requested point in + turn. To compute x_i, the ith component, it bisects the upper and lower + bounds on x_i given by the bounding box. At each bisection, it uses + :meth:`integral_points_count` to determine on which side of the + bisecting hyperplane the requested point lies. + + .. SEEALSO:: + + :meth:`integral_points_count`. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) + sage: P.get_integral_point(1) + (0, 0) + sage: P.get_integral_point(4) + (1, 1) + sage: sorted(P.integral_points()) + [(-1, -1), (0, 0), (0, 1), (1, 0), (1, 1)] + sage: P.get_integral_point(5) + Traceback (most recent call last): + ... + IndexError: ... + + sage: Q = Polyhedron([(1,3), (2, 7), (9, 77)]) + sage: [Q.get_integral_point(i) for i in range(Q.integral_points_count())] == sorted(Q.integral_points()) + True + sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib') # optional - latte_int + (1, 3) + sage: Q.get_integral_point(0, explicit_enumeration_threshold=0, triangulation='cddlib', foo=True) # optional - latte_int + Traceback (most recent call last): + ... + RuntimeError: ... + + sage: R = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]) + sage: R.get_integral_point(0) + Traceback (most recent call last): + ... + ValueError: ... + """ + from sage.arith.misc import integer_ceil as ceil + from sage.arith.misc import integer_floor as floor + if not self.is_compact(): + raise ValueError('can only enumerate points in a compact polyhedron') + + if not 0 <= index < self.integral_points_count(**kwds): + raise IndexError('polytope index out of range') + + D = self.ambient_dim() + lower_bounds, upper_bounds = self.bounding_box() + coordinate = [] + P = self + S = self.parent() + for i in range(D): # Now compute x_i, the ith component of coordinate. + lower, upper = ceil(lower_bounds[i]), floor(upper_bounds[i]) + 1 # So lower <= x_i < upper. + while lower < upper-1: + guess = (lower + upper) // 2 # > lower. + # Build new polyhedron by intersecting P with the halfspace {x_i < guess}. + P_lt_guess = P.intersection(S(None, ([[guess-1] + [0] * i + [-1] + [0] * (D - i - 1)], []))) + # Avoid computing P_geq_guess = P.intersection({x_i >= guess}) right now, it might not be needed. + P_lt_guess_count = P_lt_guess.integral_points_count(**kwds) + if P_lt_guess_count > index: # Move upper down to guess. + upper = guess + index -= 0 + P = P_lt_guess + else: # P_lt_guess_count <= index: # Move lower up to guess. + lower = guess + index -= P_lt_guess_count + P_geq_guess = P.intersection(S(None, ([[-guess] + [0] * i + [1] + [0] * (D - i - 1)], []))) + P = P_geq_guess + coordinate.append(lower) # Record the new component that we have found. + point = vector(ZZ, coordinate) + point.set_immutable() + return point + + def random_integral_point(self, **kwds): + r""" + Return an integral point in this polyhedron chosen uniformly at random. + + INPUT: + + - ``**kwds`` -- optional keyword parameters that are passed to + :meth:`self.get_integral_point`. + + OUTPUT: + + The integral point in the polyhedron chosen uniformly at random. If the + polyhedron is not compact, a ``ValueError`` is raised. If the + polyhedron does not contain any integral points, an ``EmptySetError`` is + raised. + + .. SEEALSO:: + + :meth:`get_integral_point`. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(-1,-1),(1,0),(1,1),(0,1)]) + sage: P.random_integral_point() # random + (0, 0) + sage: P.random_integral_point() in P.integral_points() + True + sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib') # random, optional - latte_int + (1, 1) + sage: P.random_integral_point(explicit_enumeration_threshold=0, triangulation='cddlib', foo=7) # optional - latte_int + Traceback (most recent call last): + ... + RuntimeError: ... + + sage: Q = Polyhedron(vertices=[(2, 1/3)], rays=[(1, 2)]) + sage: Q.random_integral_point() + Traceback (most recent call last): + ... + ValueError: ... + + sage: R = Polyhedron(vertices=[(1/2, 0), (1, 1/2), (0, 1/2)]) + sage: R.random_integral_point() + Traceback (most recent call last): + ... + EmptySetError: ... + """ + from sage.misc.randstate import current_randstate + + if not self.is_compact(): + raise ValueError('can only sample integral points in a compact polyhedron') + + count = self.integral_points_count() + if count == 0: + from sage.categories.sets_cat import EmptySetError + raise EmptySetError('polyhedron does not contain any integral points') + + return self.get_integral_point(current_randstate().python_random().randint(0, count-1), **kwds) diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index 8901ddbc536..47f3465b3f5 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -1879,7 +1879,7 @@ cdef class CGraphBackend(GenericGraphBackend): def iterator_verts(self, verts=None): """ - Return an iterator over the vertices of ``self`` intersected with + Iterate over the vertices of ``self`` intersected with ``verts``. INPUT: @@ -2179,7 +2179,7 @@ cdef class CGraphBackend(GenericGraphBackend): def iterator_nbrs(self, v): """ - Return an iterator over the neighbors of ``v``. + Iterate over the neighbors of ``v``. INPUT: @@ -2205,16 +2205,33 @@ cdef class CGraphBackend(GenericGraphBackend): sage: P = Graph(graphs.PetersenGraph()) sage: list(P._backend.iterator_nbrs(0)) [1, 4, 5] + sage: Q = DiGraph(P) + sage: list(Q._backend.iterator_nbrs(0)) + [1, 4, 5] """ if not self._directed: - return self.iterator_out_nbrs(v) + yield from self.iterator_out_nbrs(v) + return - return iter(set(self.iterator_in_nbrs(v)) | - set(self.iterator_out_nbrs(v))) + cdef int u_int + cdef int v_int = self.get_vertex(v) + if v_int == -1 or not bitset_in(self.cg().active_vertices, v_int): + raise LookupError("vertex ({0}) is not a vertex of the graph".format(v)) + + cdef set seen = set() + for u_int in self.cg().in_neighbors(v_int): + if u_int not in seen: + yield self.vertex_label(u_int) + seen.add(u_int) + for u_int in self.cg().out_neighbors(v_int): + if u_int not in seen: + yield self.vertex_label(u_int) + seen.add(u_int) + return def iterator_in_nbrs(self, v): """ - Return an iterator over the incoming neighbors of ``v``. + Iterate over the incoming neighbors of ``v``. INPUT: @@ -2257,7 +2274,7 @@ cdef class CGraphBackend(GenericGraphBackend): def iterator_out_nbrs(self, v): """ - Return an iterator over the outgoing neighbors of ``v``. + Iterate over the outgoing neighbors of ``v``. INPUT: @@ -2645,7 +2662,7 @@ cdef class CGraphBackend(GenericGraphBackend): (2, 3, None), (2, 7, None)] """ - return self._iterator_edges(vertices, labels, modus=3) + yield from self._iterator_edges(vertices, labels, modus=3) def iterator_unsorted_edges(self, object vertices, bint labels): """ @@ -2677,7 +2694,7 @@ cdef class CGraphBackend(GenericGraphBackend): sage: list(G._backend.iterator_unsorted_edges([1, 'a'],False)) [(1, 'a')] """ - return self._iterator_edges(vertices, labels, modus=2) + yield from self._iterator_edges(vertices, labels, modus=2) def iterator_out_edges(self, object vertices, bint labels): """ @@ -2700,7 +2717,7 @@ cdef class CGraphBackend(GenericGraphBackend): sage: list(G.iterator_out_edges([1], True)) [(1, 2, 3)] """ - return self._iterator_edges(vertices, labels, modus=0) + yield from self._iterator_edges(vertices, labels, modus=0) def iterator_in_edges(self, object vertices, bint labels): """ @@ -2723,7 +2740,7 @@ cdef class CGraphBackend(GenericGraphBackend): sage: list(G.iterator_in_edges([2], True)) [(1, 2, 3)] """ - return self._iterator_edges(vertices, labels, modus=1) + yield from self._iterator_edges(vertices, labels, modus=1) def _iterator_edges(self, object vertices, const bint labels, const int modus=0): """ @@ -4872,7 +4889,7 @@ cdef class Search_iterator: def __iter__(self): r""" - Return an iterator object over a traversal of a graph. + Iterate over a traversal of a graph. EXAMPLES:: diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index dd1fb496861..ee91fd93fc5 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -863,7 +863,7 @@ cdef class StaticSparseBackend(CGraphBackend): def iterator_verts(self, vertices): r""" - Return an iterator over the vertices + Iterate over the vertices INPUT: @@ -882,9 +882,15 @@ cdef class StaticSparseBackend(CGraphBackend): [1] """ if vertices is None: - return iter(self._vertex_to_labels) - else: - return (x for x in self._vertex_to_labels if x in vertices) + for x in self._vertex_to_labels: + yield x + return + + cdef set V = set(vertices) + for x in self._vertex_to_labels: + if x in V: + yield x + return def num_verts(self): r""" @@ -1003,7 +1009,7 @@ cdef class StaticSparseBackend(CGraphBackend): def iterator_edges(self, vertices, bint labels): r""" - Return an iterator over the graph's edges. + Iterate over the graph's edges. INPUT: @@ -1290,7 +1296,7 @@ cdef class StaticSparseBackend(CGraphBackend): def iterator_nbrs(self, v): r""" - Return an iterator over the neighbors of a vertex + Iterate over the neighbors of a vertex INPUT: @@ -1347,7 +1353,7 @@ cdef class StaticSparseBackend(CGraphBackend): def iterator_out_nbrs(self, v): r""" - Return an iterator over the out-neighbors of a vertex + Iterate over the out-neighbors of a vertex INPUT: @@ -1376,7 +1382,7 @@ cdef class StaticSparseBackend(CGraphBackend): def iterator_in_nbrs(self, v): r""" - Return an iterator over the in-neighbors of a vertex + Iterate over the in-neighbors of a vertex INPUT: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 93264c6a4fd..5f838143a1e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -10651,7 +10651,7 @@ def neighbor_iterator(self, vertex, closed=False): :: sage: D = DiGraph({0: [1, 2], 3: [0]}) - sage: list(D.neighbor_iterator(0)) + sage: sorted(D.neighbor_iterator(0)) [1, 2, 3] :: @@ -16714,6 +16714,10 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, Floyd-Warshall algorithm. Works also with weighted graphs, even with negative weights (but no negative cycle is allowed). + - ``'Floyd-Warshall_SciPy'``: the SciPy implementation of the + Floyd-Warshall algorithm. Works also with weighted graphs, even with + negative weights (but no negative cycle is allowed). + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in NetworkX. It works with weighted graphs, but no negative weight is allowed. @@ -16818,7 +16822,8 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: d5, _ = g.shortest_path_all_pairs(algorithm="Dijkstra_Boost") sage: d6, _ = g.shortest_path_all_pairs(algorithm="Johnson_Boost") sage: d7, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_Boost") - sage: d1 == d2 == d3 == d4 == d5 == d6 == d7 + sage: d8, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_SciPy") + sage: d1 == d2 == d3 == d4 == d5 == d6 == d7 == d8 True Checking that distances are equal regardless of the algorithm used:: @@ -16831,7 +16836,8 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: d5, _ = g.shortest_path_all_pairs(algorithm="Dijkstra_Boost") sage: d6, _ = g.shortest_path_all_pairs(algorithm="Johnson_Boost") sage: d7, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_Boost") - sage: d1 == d2 == d3 == d4 == d5 == d6 == d7 + sage: d8, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_SciPy") + sage: d1 == d2 == d3 == d4 == d5 == d6 == d7 == d8 True Checking that weighted distances are equal regardless of the algorithm @@ -16846,7 +16852,8 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: d3, _ = g.shortest_path_all_pairs(algorithm="Dijkstra_Boost") sage: d4, _ = g.shortest_path_all_pairs(algorithm="Johnson_Boost") sage: d5, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_Boost") - sage: d1 == d2 == d3 == d4 == d5 + sage: d6, _ = g.shortest_path_all_pairs(algorithm="Floyd-Warshall_SciPy") + sage: d1 == d2 == d3 == d4 == d5 == d6 True Checking a random path is valid:: @@ -16885,6 +16892,9 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, sage: g.shortest_path_all_pairs(algorithm='Floyd-Warshall-Cython') ({0: {0: 0, 1: 1, 2: 2}, 1: {1: 0, 2: 1}, 2: {2: 0}}, {0: {0: None, 1: 0, 2: 1}, 1: {1: None, 2: 1}, 2: {2: None}}) + sage: g.shortest_path_all_pairs(algorithm='Floyd-Warshall_SciPy') + ({0: {0: 0.0, 1: 1.0, 2: 2.0}, 1: {1: 0.0, 2: 1.0}, 2: {2: 0.0}}, + {0: {0: None, 1: 0, 2: 1}, 1: {1: None, 2: 1}, 2: {2: None}}) In order to change the default behavior if the graph is disconnected, we can use default values with dictionaries:: @@ -16931,6 +16941,8 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, ... RuntimeError: Dijkstra algorithm does not work with negative weights, use Bellman-Ford instead """ + from sage.rings.infinity import Infinity + if weight_function is not None: by_weight = True @@ -16976,6 +16988,38 @@ def weight_function(e): from sage.graphs.base.boost_graph import floyd_warshall_shortest_paths return floyd_warshall_shortest_paths(self, weight_function, distances=True, predecessors=True) + elif algorithm == "Floyd-Warshall_SciPy": + # Turn the graph to a n x n matrix + n = self.order() + int_to_vertex = list(self) + vertex_to_int = {u: i for i, u in enumerate(int_to_vertex)} + if by_weight: + M = [[float('inf')]*n for _ in range(n)] + for i in range(n): + M[i][i] = 0 + for e in self.edges(sort=False): + u = vertex_to_int[e[0]] + v = vertex_to_int[e[1]] + M[u][v] = min(M[u][v], weight_function(e)) + if not self.is_directed(): + M[v][u] = M[u][v] + else: + M = self.adjacency_matrix(vertices=int_to_vertex) + + # We call the Floyd-Warshall method from SciPy + from numpy import array as np_array + from scipy.sparse.csgraph import floyd_warshall + dd, pp = floyd_warshall(np_array(M), directed=self.is_directed(), + return_predecessors=True, unweighted=not by_weight) + + # and format the result + dist = {int_to_vertex[i]: {int_to_vertex[j]: dd[i, j] for j in range(n) if dd[i, j] != +Infinity} + for i in range(n)} + pred = {int_to_vertex[i]: {int_to_vertex[j]: (int_to_vertex[pp[i, j]] if i != j else None) + for j in range(n) if (i == j or pp[i, j] != -9999)} + for i in range(n)} + return dist, pred + elif algorithm == "Johnson_Boost": from sage.graphs.base.boost_graph import johnson_shortest_paths return johnson_shortest_paths(self, weight_function, distances=True, predecessors=True) @@ -17005,8 +17049,6 @@ def weight_function(e): elif algorithm != "Floyd-Warshall-Python": raise ValueError('unknown algorithm "{}"'.format(algorithm)) - from sage.rings.infinity import Infinity - if self.is_directed(): neighbor = self.neighbor_out_iterator else: @@ -17585,11 +17627,11 @@ def depth_first_search(self, start, ignore_direction=False, You can get edges of the DFS tree instead of the vertices using the ``edges`` parameter:: - sage: D = DiGraph({1: [2, 3], 2: [4], 3: [4], 4: [1, 5], 5: [2, 6]}) - sage: list(D.depth_first_search(1, edges=True)) - [(1, 3), (3, 4), (4, 5), (5, 6), (5, 2)] - sage: list(D.depth_first_search(1, ignore_direction=True, edges=True)) - [(1, 4), (4, 5), (5, 6), (5, 2), (4, 3)] + sage: D = digraphs.Path(5) + sage: list(D.depth_first_search(2, edges=True)) + [(2, 3), (3, 4)] + sage: list(D.depth_first_search(2, edges=True, ignore_direction=True)) + [(2, 3), (3, 4), (2, 1), (1, 0)] TESTS:: diff --git a/src/sage/graphs/path_enumeration.pyx b/src/sage/graphs/path_enumeration.pyx index 2fc2dd6b21f..2fe2403fbe8 100644 --- a/src/sage/graphs/path_enumeration.pyx +++ b/src/sage/graphs/path_enumeration.pyx @@ -282,7 +282,8 @@ def all_paths(G, start, end, use_multiedges=False, report_edges=False, labels=Fa return all_paths def shortest_simple_paths(self, source, target, weight_function=None, - by_weight=False, algorithm=None, report_edges=False, + by_weight=False, check_weight=True, + algorithm=None, report_edges=False, labels=False, report_weight=False): r""" Return an iterator over the simple paths between a pair of vertices. @@ -318,6 +319,9 @@ def shortest_simple_paths(self, source, target, weight_function=None, - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges in the graph are weighted, otherwise all edges have weight 1 + - ``check_weight`` -- boolean (default: ``True``); whether to check that the + ``weight_function`` outputs a number for each edge. + - ``algorithm`` -- string (default: ``None``); the algorithm to use in computing ``k`` shortest paths of ``self``. The following algorithms are supported: @@ -550,20 +554,23 @@ def shortest_simple_paths(self, source, target, weight_function=None, yield from feng_k_shortest_simple_paths(self, source=source, target=target, weight_function=weight_function, - by_weight=by_weight, report_edges=report_edges, + by_weight=by_weight, check_weight=check_weight, + report_edges=report_edges, labels=labels, report_weight=report_weight) elif algorithm == "Yen": yield from yen_k_shortest_simple_paths(self, source=source, target=target, weight_function=weight_function, - by_weight=by_weight, report_edges=report_edges, + by_weight=by_weight, check_weight=check_weight, + report_edges=report_edges, labels=labels, report_weight=report_weight) else: raise ValueError('unknown algorithm "{}"'.format(algorithm)) def yen_k_shortest_simple_paths(self, source, target, weight_function=None, - by_weight=False, report_edges=False, + by_weight=False, check_weight=True, + report_edges=False, labels=False, report_weight=False): r""" Return an iterator over the simple paths between a pair of vertices in @@ -595,6 +602,9 @@ def yen_k_shortest_simple_paths(self, source, target, weight_function=None, - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges in the graph are weighted, otherwise all edges have weight 1 + - ``check_weight`` -- boolean (default: ``True``); whether to check that + the ``weight_function`` outputs a number for each edge + - ``report_edges`` -- boolean (default: ``False``); whether to report paths as list of vertices (default) or list of edges, if ``False`` then ``labels`` parameter is ignored @@ -733,16 +743,12 @@ def yen_k_shortest_simple_paths(self, source, target, weight_function=None, else: G = self - if weight_function is not None: - by_weight = True - - if weight_function is None and by_weight: - def weight_function(e): - return e[2] + by_weight, weight_function = self._get_weight_function(by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight) cdef dict edge_wt if by_weight: - G._check_weight_function(weight_function) # dictionary to get weight of the edges edge_wt = {(e[0], e[1]): weight_function(e) for e in G.edge_iterator()} if not G.is_directed(): @@ -857,7 +863,8 @@ def yen_k_shortest_simple_paths(self, source, target, weight_function=None, exclude_vertices.add(root[-1]) def feng_k_shortest_simple_paths(self, source, target, weight_function=None, - by_weight=False, report_edges=False, + by_weight=False, check_weight=True, + report_edges=False, labels=False, report_weight=False): r""" Return an iterator over the simple paths between a pair of vertices in @@ -891,6 +898,9 @@ def feng_k_shortest_simple_paths(self, source, target, weight_function=None, - ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges in the graph are weighted, otherwise all edges have weight 1 + - ``check_weight`` -- boolean (default: ``True``); whether to check that + the ``weight_function`` outputs a number for each edge + - ``report_edges`` -- boolean (default: ``False``); whether to report paths as list of vertices (default) or list of edges, if ``False`` then ``labels`` parameter is ignored @@ -1087,14 +1097,13 @@ def feng_k_shortest_simple_paths(self, source, target, weight_function=None, def weight_function(e): return e[2] + by_weight, weight_function = self._get_weight_function(by_weight=by_weight, + weight_function=weight_function, + check_weight=check_weight) if by_weight: - G._check_weight_function(weight_function) def reverse_weight_function(e): - e_ = (e[1], e[0], e[2]) - return weight_function(e_) + return weight_function((e[1], e[0], e[2])) else: - def weight_function(e): - return 1 def reverse_weight_function(e): return 1 diff --git a/src/sage/groups/abelian_gps/abelian_group.py b/src/sage/groups/abelian_gps/abelian_group.py index 1c9ce29e858..b6a412bd963 100644 --- a/src/sage/groups/abelian_gps/abelian_group.py +++ b/src/sage/groups/abelian_gps/abelian_group.py @@ -214,6 +214,9 @@ from sage.misc.mrange import mrange, cartesian_product_iterator from sage.groups.group import AbelianGroup as AbelianGroupBase from sage.categories.groups import Groups +from sage.matrix.constructor import matrix +from sage.matrix.special import diagonal_matrix +from sage.modules.free_module_element import vector # TODO: this uses perm groups - the AbelianGroupElement instance method @@ -1724,6 +1727,30 @@ def __contains__(self, x): True sage: a in A True + + TESTS: + + Check that :trac:`32910` is fixed:: + + sage: G. = AbelianGroup(2, [4, 576]) + sage: Hgens = [a^2, a*b^2] + sage: H = G.subgroup(Hgens) + sage: [g in H for g in (a^3, b^2, b^3, a^3*b^2, "junk")] + [False, False, False, True, False] + + Check that :trac:`31507` is fixed:: + + sage: G = AbelianGroup(2, gens_orders=[16, 16]) + sage: f0, f1 = G.gens() + sage: H = G.subgroup([f0*f1^3]) + sage: [g in H for g in (f0, f0*f1^2, f0*f1^3, f0*f1^4)] + [False, False, True, False] + + sage: G. = AbelianGroup(2) + sage: Hgens = [a*b, a*b^-1] + sage: H = G.subgroup(Hgens) + sage: b^2 in H + True """ if not isinstance(x, AbelianGroupElement): return False @@ -1731,27 +1758,14 @@ def __contains__(self, x): return True elif x in self.ambient_group(): amb_inv = self.ambient_group().gens_orders() - for a in range(len(amb_inv)): - if amb_inv[a] == 0 and x.list()[a] != 0: - for g in self._gens: - if g.list()[a] == 0: - continue - if abs(x.list()[a]%g.list()[a]) < abs(x.list()[a]): - if g.list()[a]*x.list()[a] < 0: - x *= g**(x.list()[a]/g.list()[a]) - else: - x *= g**((-1)*(x.list()[a]/g.list()[a])) - if x.list()[a] == 0: - break - elif x.list()[a] != 0: - for g in self._gens: - if g.list()[a] == 0: - continue - if abs(x.list()[a]%g.list()[a])%abs(amb_inv[a]) < x.list()[a]%abs(amb_inv[a]): - x *= g**((-1)*(x.list()[a]/g.list()[a])) - if x.list()[a] == 0: - break - return x == x.parent()(1) + inv_basis = diagonal_matrix(ZZ, amb_inv) + gens_basis = matrix( + ZZ, len(self._gens), len(amb_inv), + [g.list() for g in self._gens] + ) + return (vector(ZZ, x.list()) + in inv_basis.stack(gens_basis).row_module()) + return False def ambient_group(self): """ diff --git a/src/sage/groups/misc_gps/argument_groups.py b/src/sage/groups/misc_gps/argument_groups.py index 199a6a3f678..708d06c6998 100644 --- a/src/sage/groups/misc_gps/argument_groups.py +++ b/src/sage/groups/misc_gps/argument_groups.py @@ -1825,16 +1825,10 @@ def create_key_and_extra_args(self, sage: ArgumentGroup('Arg_CC') is ArgumentGroup(domain=CC) # indirect doctest True """ - from sage.rings.complex_arb import ComplexBallField - from sage.rings.complex_mpfr import ComplexField_class - from sage.rings.complex_interval_field import ComplexIntervalField_class from sage.rings.integer_ring import ZZ from sage.misc.misc import exactly_one_is_true from sage.rings.qqbar import AA from sage.rings.rational_field import QQ - from sage.rings.real_arb import RealBallField - from sage.rings.real_mpfr import RealField_class - from sage.rings.real_mpfi import RealIntervalField_class if not exactly_one_is_true( (data is not None, diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index 8144d525825..4e829805eba 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -148,12 +148,12 @@ sage: f = maxima('(x + 3*y + x^2*y)^3') sage: f.expand() - x^6*y^3+9*x^4*y^3+27*x^2*y^3+27*y^3+3*x^5*y^2+18*x^3*y^2+27*x*y^2+3*x^4*y+9*x^2*y+x^3 + x^6*y^3+9*x^4*y^3+27*x^2*y^3+27*y^3+3*x^5*y^2+18*x^3*y^2+27*x*y^2 +3*x^4*y+9*x^2*y+x^3 sage: f.subst('x=5/z') (5/z+(25*y)/z^2+3*y)^3 sage: g = f.subst('x=5/z') sage: h = g.ratsimp(); h - (27*y^3*z^6+135*y^2*z^5+(675*y^3+225*y)*z^4+(2250*y^2+125)*z^3+(5625*y^3+1875*y)*z^2+9375*y^2*z+15625*y^3)/z^6 + (27*y^3*z^6+135*y^2*z^5+(675*y^3+225*y)*z^4+(2250*y^2+125)*z^3 +(5625*y^3+1875*y)*z^2+9375*y^2*z+15625*y^3) /z^6 sage: h.factor() (3*y*z^2+5*z+25*y)^3/z^6 @@ -161,14 +161,15 @@ sage: eqn = maxima(['a+b*c=1', 'b-a*c=0', 'a+b=5']) sage: s = eqn.solve('[a,b,c]'); s - [[a=-(sqrt(79)*%i-11)/4,b=(sqrt(79)*%i+9)/4,c=(sqrt(79)*%i+1)/10],[a=(sqrt(79)*%i+11)/4,b=-(sqrt(79)*%i-9)/4,c=-(sqrt(79)*%i-1)/10]] + [[a = -(sqrt(79)*%i-11)/4,b = (sqrt(79)*%i+9)/4, c = (sqrt(79)*%i+1)/10], [a = (sqrt(79)*%i+11)/4,b = -(sqrt(79)*%i-9)/4, c = -(sqrt(79)*%i-1)/10]] Here is an example of solving an algebraic equation:: - sage: maxima('x^2+y^2=1').solve('y') - [y=-sqrt(1-x^2),y=sqrt(1-x^2)] + sage: maxima('x^2+y^2=1').solve('y') + [y = -sqrt(1-x^2),y = sqrt(1-x^2)] sage: maxima('x^2 + y^2 = (x^2 - y^2)/sqrt(x^2 + y^2)').solve('y') - [y=-sqrt(((-y^2)-x^2)*sqrt(y^2+x^2)+x^2),y=sqrt(((-y^2)-x^2)*sqrt(y^2+x^2)+x^2)] + [y = -sqrt(((-y^2)-x^2)*sqrt(y^2+x^2)+x^2), y = sqrt(((-y^2)-x^2)*sqrt(y^2+x^2)+x^2)] + You can even nicely typeset the solution in latex:: @@ -197,9 +198,9 @@ sage: f = maxima('x^3 * %e^(k*x) * sin(w*x)'); f x^3*%e^(k*x)*sin(w*x) sage: f.diff('x') - k*x^3*%e^(k*x)*sin(w*x)+3*x^2*%e^(k*x)*sin(w*x)+w*x^3*%e^(k*x)*cos(w*x) + k*x^3*%e^(k*x)*sin(w*x)+3*x^2*%e^(k*x)*sin(w*x)+w*x^3*%e^(k*x) *cos(w*x) sage: f.integrate('x') - (((k*w^6+3*k^3*w^4+3*k^5*w^2+k^7)*x^3+(3*w^6+3*k^2*w^4-3*k^4*w^2-3*k^6)*x^2+((-18*k*w^4)-12*k^3*w^2+6*k^5)*x-6*w^4+36*k^2*w^2-6*k^4)*%e^(k*x)*sin(w*x)+(((-w^7)-3*k^2*w^5-3*k^4*w^3-k^6*w)*x^3+(6*k*w^5+12*k^3*w^3+6*k^5*w)*x^2+(6*w^5-12*k^2*w^3-18*k^4*w)*x-24*k*w^3+24*k^3*w)*%e^(k*x)*cos(w*x))/(w^8+4*k^2*w^6+6*k^4*w^4+4*k^6*w^2+k^8) + (((k*w^6+3*k^3*w^4+3*k^5*w^2+k^7)*x^3 +(3*w^6+3*k^2*w^4-3*k^4*w^2-3*k^6)*x^2+((-18*k*w^4)-12*k^3*w^2+6*k^5)*x-6*w^4 +36*k^2*w^2-6*k^4) *%e^(k*x)*sin(w*x) +(((-w^7)-3*k^2*w^5-3*k^4*w^3-k^6*w)*x^3 +(6*k*w^5+12*k^3*w^3+6*k^5*w)*x^2+(6*w^5-12*k^2*w^3-18*k^4*w)*x-24*k*w^3 +24*k^3*w) *%e^(k*x)*cos(w*x)) /(w^8+4*k^2*w^6+6*k^4*w^4+4*k^6*w^2+k^8) :: @@ -270,7 +271,7 @@ sage: _ = maxima.eval("f(t) := t^5*exp(t)*sin(t)") sage: maxima("laplace(f(t),t,s)") - (360*(2*s-2))/(s^2-2*s+2)^4-(480*(2*s-2)^3)/(s^2-2*s+2)^5+(120*(2*s-2)^5)/(s^2-2*s+2)^6 + (360*(2*s-2))/(s^2-2*s+2)^4-(480*(2*s-2)^3)/(s^2-2*s+2)^5 +(120*(2*s-2)^5)/(s^2-2*s+2)^6 sage: print(maxima("laplace(f(t),t,s)")) 3 5 360 (2 s - 2) 480 (2 s - 2) 120 (2 s - 2) @@ -286,7 +287,7 @@ :: sage: maxima("laplace(diff(x(t),t,2),t,s)") - (-%at('diff(x(t),t,1),t=0))+s^2*'laplace(x(t),t,s)-x(0)*s + (-%at('diff(x(t),t,1),t = 0))+s^2*'laplace(x(t),t,s)-x(0)*s It is difficult to read some of these without the 2d representation:: @@ -460,6 +461,16 @@ sage: test.operator()._latex_() == 'C_+' True +Test that the output is parseable (:trac:`31796`):: + + sage: foo = maxima('a and (b or c)') ; foo + a and (b or c) + sage: bar = maxima(foo) ; bar + a and (b or c) + sage: bar == foo + True + + """ #***************************************************************************** @@ -681,9 +692,9 @@ def _expect_expr(self, expr=None, timeout=None): before integral or limit evaluation, for example): Is a positive or negative? sage: maxima.assume('a>0') - [a>0] + [a > 0] sage: maxima('integrate(1/(x^3*(a+b*x)^(1/3)),x)') - (-(b^2*log((b*x+a)^(2/3)+a^(1/3)*(b*x+a)^(1/3)+a^(2/3)))/(9*a^(7/3)))+(2*b^2*atan((2*(b*x+a)^(1/3)+a^(1/3))/(sqrt(3)*a^(1/3))))/(3^(3/2)*a^(7/3))+(2*b^2*log((b*x+a)^(1/3)-a^(1/3)))/(9*a^(7/3))+(4*b^2*(b*x+a)^(5/3)-7*a*b^2*(b*x+a)^(2/3))/(6*a^2*(b*x+a)^2-12*a^3*(b*x+a)+6*a^4) + (-(b^2*log((b*x+a)^(2/3)+a^(1/3)*(b*x+a)^(1/3)+a^(2/3)))/(9*a^(7/3))) +(2*b^2*atan((2*(b*x+a)^(1/3)+a^(1/3))/(sqrt(3)*a^(1/3))))/(3^(3/2)*a^(7/3)) +(2*b^2*log((b*x+a)^(1/3)-a^(1/3)))/(9*a^(7/3)) +(4*b^2*(b*x+a)^(5/3)-7*a*b^2*(b*x+a)^(2/3)) /(6*a^2*(b*x+a)^2-12*a^3*(b*x+a)+6*a^4) sage: maxima('integrate(x^n,x)') Traceback (most recent call last): ... @@ -692,11 +703,11 @@ def _expect_expr(self, expr=None, timeout=None): integral or limit evaluation, for example): Is n equal to -1? sage: maxima.assume('n+1>0') - [n>-1] + [n > -1] sage: maxima('integrate(x^n,x)') x^(n+1)/(n+1) sage: maxima.forget([fact for fact in maxima.facts()]) - [[a>0,n>-1]] + [[a > 0,n > -1]] sage: maxima.facts() [] sage: var('a') @@ -814,7 +825,7 @@ def _eval_line(self, line, allow_use_file=False, m = r.search(out) if m is not None: out = out[m.end():] - return re.sub(r'\s+', '', out) + return re.sub(r'\s+', ' ', out).rstrip() def _synchronize(self): """ diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index af20ffddd21..a853cfe2096 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -293,7 +293,8 @@ def completions(self, s, verbose=True): # create NAME-IMPL, without the leading $). This causes # name-impl to show up in $APROPOS. We remove it. # https://sourceforge.net/p/maxima/bugs/3643/ - cmd_list = self._eval_line('apropos("%s")'%s, error_check=False).replace('\\ - ','-').replace('\\-','-') + cmd_list = self._eval_line('apropos("%s")'%s, error_check=False) + cmd_list = cmd_list.replace(' ', '').replace('\n', '').replace('\\ - ','-').replace('\\-','-') cmd_list = [x for x in cmd_list[1:-1].split(',') if x[0] != '?' and not x.endswith('-impl')] return [x for x in cmd_list if x.find(s) == 0] @@ -512,7 +513,7 @@ def _equality_symbol(self): sage: var('x y') (x, y) sage: maxima(x == y) - _SAGE_VAR_x=_SAGE_VAR_y + _SAGE_VAR_x = _SAGE_VAR_y """ return '=' @@ -529,7 +530,7 @@ def _inequality_symbol(self): sage: maxima._inequality_symbol() '#' sage: maxima((x != 1)) - _SAGE_VAR_x#1 + _SAGE_VAR_x # 1 """ return '#' @@ -853,13 +854,13 @@ def de_solve(self, de, vars, ics=None): EXAMPLES:: sage: maxima.de_solve('diff(y,x,2) + 3*x = y', ['x','y'], [1,1,1]) - y=3*x-2*%e^(x-1) + y = 3*x-2*%e^(x-1) sage: maxima.de_solve('diff(y,x,2) + 3*x = y', ['x','y']) - y=%k1*%e^x+%k2*%e^-x+3*x + y = %k1*%e^x+%k2*%e^-x+3*x sage: maxima.de_solve('diff(y,x) + 3*x = y', ['x','y']) - y=(%c-3*((-x)-1)*%e^-x)*%e^x + y = (%c-3*((-x)-1)*%e^-x)*%e^x sage: maxima.de_solve('diff(y,x) + 3*x = y', ['x','y'],[1,1]) - y=-%e^-1*(5*%e^x-3*%e*x-3*%e) + y = -%e^-1*(5*%e^x-3*%e*x-3*%e) """ if not isinstance(vars, str): str_vars = '%s, %s'%(vars[1], vars[0]) @@ -897,14 +898,14 @@ def de_solve_laplace(self, de, vars, ics=None): sage: maxima.clear('x'); maxima.clear('f') sage: maxima.de_solve_laplace("diff(f(x),x,2) = 2*diff(f(x),x)-f(x)", ["x","f"], [0,1,2]) - f(x)=x*%e^x+%e^x + f(x) = x*%e^x+%e^x :: sage: maxima.clear('x'); maxima.clear('f') sage: f = maxima.de_solve_laplace("diff(f(x),x,2) = 2*diff(f(x),x)-f(x)", ["x","f"]) sage: f - f(x)=x*%e^x*('at('diff(f(x),x,1),x=0))-f(0)*x*%e^x+f(0)*%e^x + f(x) = x*%e^x*('at('diff(f(x),x,1),x = 0))-f(0)*x*%e^x+f(0)*%e^x sage: print(f) ! x d ! x x @@ -944,7 +945,7 @@ def solve_linear(self, eqns,vars): sage: eqns = ["x + z = y","2*a*x - y = 2*a^2","y - 2*z = 2"] sage: vars = ["x","y","z"] sage: maxima.solve_linear(eqns, vars) - [x=a+1,y=2*a,z=a-1] + [x = a+1,y = 2*a,z = a-1] """ eqs = "[" for i in range(len(eqns)): diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index e0e07c52c78..3e47b3db029 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -68,6 +68,15 @@ sage: maxima_calculus("besselexpand:true") true +The output is parseable (i. e. :trac:`31796` is fixed):: + + sage: foo = maxima_calculus('a and (b or c)') ; foo + a and (b or c) + sage: bar = maxima_calculus(foo) ; bar + a and (b or c) + sage: bar == foo + True + """ # **************************************************************************** @@ -453,7 +462,7 @@ def _eval_line(self, line, locals=None, reformat=True, **kwds): maxima_eval("#$%s$" % statement) if not reformat: return result - return ''.join([x.strip() for x in result.split()]) + return ' '.join(x.strip() for x in result.split()) eval = _eval_line @@ -1388,12 +1397,12 @@ def max_at_to_sage(expr): sage: from sage.interfaces.maxima_lib import maxima_lib, max_at_to_sage sage: a=maxima_lib("'at(f(x,y,z),[x=1,y=2,z=3])") sage: a - 'at(f(x,y,z),[x=1,y=2,z=3]) + 'at(f(x,y,z),[x = 1,y = 2,z = 3]) sage: max_at_to_sage(a.ecl()) f(1, 2, 3) sage: a=maxima_lib("'at(f(x,y,z),x=1)") sage: a - 'at(f(x,y,z),x=1) + 'at(f(x,y,z),x = 1) sage: max_at_to_sage(a.ecl()) f(1, y, z) """ diff --git a/src/sage/interfaces/primecount.pyx b/src/sage/interfaces/primecount.pyx index 01e57c8a590..5fa1441411d 100644 --- a/src/sage/interfaces/primecount.pyx +++ b/src/sage/interfaces/primecount.pyx @@ -1,5 +1,3 @@ -# sage_setup: distribution = sagemath-primecount - r""" Interface to the primecount library """ @@ -17,12 +15,6 @@ from libcpp.string cimport string as cppstring from cpython.int cimport PyInt_FromString from cysignals.signals cimport sig_on, sig_off - - -from sage.misc.superseded import deprecation -deprecation(32412, "the module sage.interfaces.primecount is deprecated") - - cimport sage.libs.primecount as primecount cdef inline int _do_sig(int64_t n): @@ -37,29 +29,15 @@ cpdef int64_t prime_pi(int64_t n, method=None) except -1: - ``n`` - an integer - - ``method`` - deprecated, no longer supported in primecount 5 - EXAMPLES:: - sage: from sage.interfaces.primecount import prime_pi # optional - primecount - doctest:warning - ... - DeprecationWarning: the module sage.interfaces.primecount is deprecated - See https://trac.sagemath.org/32412 for details. - - sage: prime_pi(1000) == 168 # optional - primecount + sage: from sage.interfaces.primecount import prime_pi + sage: prime_pi(1000) == 168 True - sage: prime_pi(1000, method='deleglise_rivat') == 168 # optional - primecount - doctest:warning - ... - DeprecationWarning: primecount 5 no longer supports the 'method' parameter - See https://trac.sagemath.org/28493 for details. + sage: prime_pi(1000, method='deleglise_rivat') == 168 True """ cdef int64_t ans - if method is not None: - from sage.misc.superseded import deprecation_cython as deprecation - deprecation(28493, "primecount 5 no longer supports the 'method' parameter") if _do_sig(n): sig_on() ans = primecount.pi(n) if _do_sig(n): sig_off() @@ -71,9 +49,9 @@ cpdef prime_pi_128(n): EXAMPLES:: - sage: from sage.interfaces.primecount import prime_pi_128 # optional - primecount + sage: from sage.interfaces.primecount import prime_pi_128 - sage: prime_pi_128(1000) # optional - primecount + sage: prime_pi_128(1000) 168 sage: nth_prime_128(2**65) # not tested ? @@ -91,9 +69,9 @@ cpdef int64_t nth_prime(int64_t n) except -1: EXAMPLES:: - sage: from sage.interfaces.primecount import nth_prime # optional - primecount + sage: from sage.interfaces.primecount import nth_prime - sage: nth_prime(168) == 997 # optional - primecount + sage: nth_prime(168) == 997 True """ if n <= 0: @@ -114,11 +92,11 @@ cpdef int64_t phi(int64_t x, int64_t a): EXAMPLES:: - sage: from sage.interfaces.primecount import phi # optional - primecount + sage: from sage.interfaces.primecount import phi - sage: phi(1000, 3) == 266 # optional - primecount + sage: phi(1000, 3) == 266 True - sage: phi(2**30, 100) == 95446716 # optional - primecount + sage: phi(2**30, 100) == 95446716 True """ return primecount.phi(x, a) diff --git a/src/sage/lfunctions/lcalc.py b/src/sage/lfunctions/lcalc.py index aabbd47636f..6efa5fe2f37 100644 --- a/src/sage/lfunctions/lcalc.py +++ b/src/sage/lfunctions/lcalc.py @@ -225,19 +225,54 @@ def values_along_line(self, s0, s1, number_samples, L=''): EXAMPLES:: sage: I = CC.0 - sage: lcalc.values_along_line(0.5, 0.5+20*I, 5) - [(0.500000000, -1.46035451), (0.500000000 + 4.00000000*I, 0.606783764 + 0.0911121400*I), (0.500000000 + 8.00000000*I, 1.24161511 + 0.360047588*I), (0.500000000 + 12.0000000*I, 1.01593665 - 0.745112472*I), (0.500000000 + 16.0000000*I, 0.938545408 + 1.21658782*I)] + sage: values = lcalc.values_along_line(0.5, 0.5+20*I, 5) + sage: values[0][0] # abs tol 1e-8 + 0.5 + sage: values[0][1] # abs tol 1e-8 + -1.46035451 + 0.0*I + sage: values[1][0] # abs tol 1e-8 + 0.5 + 4.0*I + sage: values[1][1] # abs tol 1e-8 + 0.606783764 + 0.0911121400*I + sage: values[2][0] # abs tol 1e-8 + 0.5 + 8.0*I + sage: values[2][1] # abs tol 1e-8 + 1.24161511 + 0.360047588*I + sage: values[3][0] # abs tol 1e-8 + 0.5 + 12.0*I + sage: values[3][1] # abs tol 1e-8 + 1.01593665 - 0.745112472*I + sage: values[4][0] # abs tol 1e-8 + 0.5 + 16.0*I + sage: values[4][1] # abs tol 1e-8 + 0.938545408 + 1.21658782*I Sometimes warnings are printed (by lcalc) when this command is run:: sage: E = EllipticCurve('389a') - sage: E.lseries().values_along_line(0.5, 3, 5) - [(0.000000000, 0.209951303), - (0.500000000, -...e-16), - (1.00000000, 0.133768433), - (1.50000000, 0.360092864), - (2.00000000, 0.552975867)] + sage: values = E.lseries().values_along_line(0.5, 3, 5) + sage: values[0][0] # abs tol 1e-8 + 0.0 + sage: values[0][1] # abs tol 1e-8 + 0.209951303 + 0.0*I + sage: values[1][0] # abs tol 1e-8 + 0.5 + sage: values[1][1] # abs tol 1e-8 + 0.0 + 0.0*I + sage: values[2][0] # abs tol 1e-8 + 1.0 + sage: values[2][1] # abs tol 1e-8 + 0.133768433 - 0.0*I + sage: values[3][0] # abs tol 1e-8 + 1.5 + sage: values[3][1] # abs tol 1e-8 + 0.360092864 - 0.0*I + sage: values[4][0] # abs tol 1e-8 + 2.0 + sage: values[4][1] # abs tol 1e-8 + 0.552975867 + 0.0*I + """ L = self._compute_L(L) CC = sage.rings.all.ComplexField(prec) @@ -281,8 +316,31 @@ def twist_values(self, s, dmin, dmax, L=''): EXAMPLES:: - sage: lcalc.twist_values(0.5, -10, 10) - [(-8, 1.10042141), (-7, 1.14658567), (-4, 0.667691457), (-3, 0.480867558), (5, 0.231750947), (8, 0.373691713)] + sage: values = lcalc.twist_values(0.5, -10, 10) + sage: values[0][0] + -8 + sage: values[0][1] # abs tol 1e-8 + 1.10042141 + 0.0*I + sage: values[1][0] + -7 + sage: values[1][1] # abs tol 1e-8 + 1.14658567 + 0.0*I + sage: values[2][0] + -4 + sage: values[2][1] # abs tol 1e-8 + 0.667691457 + 0.0*I + sage: values[3][0] + -3 + sage: values[3][1] # abs tol 1e-8 + 0.480867558 + 0.0*I + sage: values[4][0] + 5 + sage: values[4][1] # abs tol 1e-8 + 0.231750947 + 0.0*I + sage: values[5][0] + 8 + sage: values[5][1] # abs tol 1e-8 + 0.373691713 + 0.0*I """ L = self._compute_L(L) CC = sage.rings.all.ComplexField(prec) diff --git a/src/sage/lfunctions/pari.py b/src/sage/lfunctions/pari.py index 0aaf91cad3e..33d526697f9 100644 --- a/src/sage/lfunctions/pari.py +++ b/src/sage/lfunctions/pari.py @@ -316,7 +316,7 @@ def lfun_number_field(K): sage: L(3) 1.15202784126080 sage: L(0) - 0.000000000000000 + ...0.000000000000000 """ return pari.lfuncreate(K) diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index 225fe7daf9d..8b0e5661e67 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -829,8 +829,11 @@ cdef class LFunctionZeroSum_abstract(SageObject): EXAMPLES:: sage: E = EllipticCurve("11a") - sage: E.lseries().zeros(2) - [6.36261389, 8.60353962] + sage: zeros = E.lseries().zeros(2) + sage: zeros[0] # abs tol 1e-8 + 6.36261389 + sage: zeros[1] # abs tol 1e-8 + 8.60353962 E is a rank zero curve; the lowest zero has imaginary part ~6.36. The zero sum with tau=0 indicates that there are no zeros at the central diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pxd b/src/sage/libs/lcalc/lcalc_Lfunction.pxd index d1dbb5d95d5..5edf0844f3e 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pxd +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pxd @@ -21,7 +21,7 @@ cdef extern from "lcalc_sage.h": int (* compute_rank) () double (* N) (double T) void (* find_zeros_v)(double T1, double T2, double stepsize, doublevec result ) - void (*find_zeros_via_N_v)(long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec result) + int (*find_zeros)(long count, long start, double max_refine, int rank, const char* message_stamp, doublevec* result) void (*print_data_L)() #Constructor and destructor @@ -38,7 +38,7 @@ cdef extern from "lcalc_sage.h": double (* N) (double T) double *dirichlet_coefficient void (* find_zeros_v)(double T1, double T2, double stepsize, doublevec result ) - void (*find_zeros_via_N_v)(long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec result) + int (*find_zeros)(long count, long start, double max_refine, int rank, const char* message_stamp, doublevec* result) void (*print_data_L)() #Constructor and destructor @@ -54,7 +54,7 @@ cdef extern from "lcalc_sage.h": int (* compute_rank) () double (* N) (double T) void (* find_zeros_v)(double T1, double T2, double stepsize, doublevec result ) - void (*find_zeros_via_N_v)(long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec result) + int (*find_zeros)(long count, long start, double max_refine, int rank, const char* message_stamp, doublevec* result) void (*print_data_L)() #Constructor and destructor @@ -70,7 +70,7 @@ cdef extern from "lcalc_sage.h": int (* compute_rank) () double (* N) (double T) void (* find_zeros_v)(double T1, double T2, double stepsize, doublevec result ) - void (*find_zeros_via_N_v)(long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec result)#puts result in vector result + int (*find_zeros)(long count, long start, double max_refine, int rank, const char* message_stamp, doublevec* result) void (*find_zeros_via_N)(long count,int do_negative,double max_refine, int rank, int test_explicit_formula, char *filename) #puts result in filename #Constructor and destructor @@ -111,7 +111,7 @@ cdef class Lfunction: #strange bug, replacing Double with double gives me a compile error cdef Double __typedN(self, double T) cdef void __find_zeros_v(self, double T1, double T2, double stepsize,doublevec *result) - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula,doublevec *result) + cdef int __find_zeros(self, long count, long start, double max_refine, int rank, const char* message_stamp, doublevec* result) cdef str _repr diff --git a/src/sage/libs/lcalc/lcalc_Lfunction.pyx b/src/sage/libs/lcalc/lcalc_Lfunction.pyx index 58deb827aa9..9d1b85dd7f1 100644 --- a/src/sage/libs/lcalc/lcalc_Lfunction.pyx +++ b/src/sage/libs/lcalc/lcalc_Lfunction.pyx @@ -140,32 +140,43 @@ cdef class Lfunction: EXAMPLES:: - sage: chi = DirichletGroup(5)[2] #This is a quadratic character sage: from sage.libs.lcalc.lcalc_Lfunction import * - sage: L=Lfunction_from_character(chi, type="int") - sage: L.value(.5) # abs tol 3e-15 - 0.231750947504016 + 5.75329642226136e-18*I - sage: L.value(.2+.4*I) - 0.102558603193... + 0.190840777924...*I - - sage: L=Lfunction_from_character(chi, type="double") - sage: L.value(.6) # abs tol 3e-15 - 0.274633355856345 + 6.59869267328199e-18*I - sage: L.value(.6+I) - 0.362258705721... + 0.433888250620...*I + sage: chi = DirichletGroup(5)[2] # This is a quadratic character + sage: L = Lfunction_from_character(chi, type="int") + sage: (L.value(0.5) - 0.231750947504016).abs() < 1e-8 + True + sage: v = L.value(0.2 + 0.4*I) + sage: (v - (0.102558603193 + 0.190840777924*I)).abs() < 1e-8 + True + sage: L = Lfunction_from_character(chi, type="double") + sage: (L.value(0.6) - 0.274633355856345).abs() < 1e-8 + True + sage: v = L.value(0.6 + I) + sage: (v - (0.362258705721 + 0.43388825062*I)).abs() < 1e-8 + True + + :: + sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[1] - sage: L=Lfunction_from_character(chi, type="complex") - sage: L.value(.5) - 0.763747880117... + 0.216964767518...*I - sage: L.value(.6+5*I) - 0.702723260619... - 1.10178575243...*I + sage: L = Lfunction_from_character(chi, type="complex") + sage: v = L.value(0.5) + sage: (v - (0.763747880117 + 0.21696476751*I)).abs() < 1e-8 + True + sage: v = L.value(0.6 + 5*I) + sage: (v - (0.702723260619 - 1.10178575243*I)).abs() < 1e-8 + True + + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * + sage: L = Lfunction_Zeta() + sage: (L.value(0.5) + 1.46035450880).abs() < 1e-8 + True + sage: v = L.value(0.4 + 0.5*I) + sage: (v - (-0.450728958517 - 0.780511403019*I)).abs() < 1e-8 + True - sage: L=Lfunction_Zeta() - sage: L.value(.5) - -1.46035450880... - sage: L.value(.4+.5*I) - -0.450728958517... - 0.780511403019...*I """ cdef ComplexNumber complexified_s = CCC(s) cdef c_Complex z = new_Complex(mpfr_get_d(complexified_s.__re, MPFR_RNDN), mpfr_get_d(complexified_s.__im, MPFR_RNDN)) @@ -175,34 +186,39 @@ cdef class Lfunction: def hardy_z_function(self, s): """ Computes the Hardy Z-function of the L-function at s - + INPUT: - ``s`` - a complex number with imaginary part between -0.5 and 0.5 EXAMPLES:: - sage: chi = DirichletGroup(5)[2] # Quadratic character sage: from sage.libs.lcalc.lcalc_Lfunction import * + sage: chi = DirichletGroup(5)[2] # Quadratic character sage: L = Lfunction_from_character(chi, type="int") - sage: L.hardy_z_function(0) - 0.231750947504... - sage: L.hardy_z_function(.5).imag() # abs tol 1e-15 - 1.17253174178320e-17 - sage: L.hardy_z_function(.4+.3*I) - 0.2166144222685... - 0.00408187127850...*I + sage: (L.hardy_z_function(0) - 0.231750947504).abs() < 1e-8 + True + sage: L.hardy_z_function(0.5).imag().abs() < 1e-8 + True + + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[1] sage: L = Lfunction_from_character(chi, type="complex") - sage: L.hardy_z_function(0) - 0.793967590477... - sage: L.hardy_z_function(.5).imag() # abs tol 1e-15 - 0.000000000000000 + sage: (L.hardy_z_function(0) - 0.793967590477).abs() < 1e-8 + True + sage: L.hardy_z_function(0.5).imag().abs() < 1e-8 + True + + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: E = EllipticCurve([-82,0]) sage: L = Lfunction_from_elliptic_curve(E, number_of_coeffs=40000) - sage: L.hardy_z_function(2.1) - -0.00643179176869... - sage: L.hardy_z_function(2.1).imag() # abs tol 1e-15 - -3.93833660115668e-19 + sage: (L.hardy_z_function(2.1) - (-0.006431791768)).abs() < 1e-8 + True + """ #This takes s -> .5 + I*s cdef ComplexNumber complexified_s = CCC(0.5)+ CCC(0,1)*CCC(s) @@ -218,15 +234,20 @@ cdef class Lfunction: EXAMPLES:: - sage: chi = DirichletGroup(5)[2] #This is a quadratic character sage: from sage.libs.lcalc.lcalc_Lfunction import * - sage: L=Lfunction_from_character(chi, type="int") + sage: chi = DirichletGroup(5)[2] # This is a quadratic character + sage: L = Lfunction_from_character(chi, type="int") sage: L.compute_rank() 0 - sage: E=EllipticCurve([-82,0]) - sage: L=Lfunction_from_elliptic_curve(E, number_of_coeffs=40000) + + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * + sage: E = EllipticCurve([-82,0]) + sage: L = Lfunction_from_elliptic_curve(E, number_of_coeffs=40000) sage: L.compute_rank() 3 + """ return self.__compute_rank() @@ -241,8 +262,8 @@ cdef class Lfunction: sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[2] #This is a quadratic character sage: L=Lfunction_from_character(chi, type="complex") - sage: L.__N(10) - 3.17043978326... + sage: L.__N(10) # abs tol 1e-8 + 4.0 """ cdef RealNumber real_T=RRR(T) cdef double double_T = mpfr_get_d(real_T.value, MPFR_RNDN) @@ -260,7 +281,7 @@ cdef class Lfunction: Use :meth:`find_zeros_via_N` for slower but more rigorous computation. INPUT: - + - ``T1`` -- a real number giving the lower bound - ``T2`` -- a real number giving the upper bound - ``stepsize`` -- step size to be used for the zero search @@ -272,21 +293,26 @@ cdef class Lfunction: EXAMPLES:: sage: from sage.libs.lcalc.lcalc_Lfunction import * - sage: chi = DirichletGroup(5)[2] #This is a quadratic character - sage: L=Lfunction_from_character(chi, type="int") + sage: chi = DirichletGroup(5)[2] # This is a quadratic character + sage: L = Lfunction_from_character(chi, type="int") sage: L.find_zeros(5,15,.1) [6.64845334472..., 9.83144443288..., 11.9588456260...] - - sage: L=Lfunction_from_character(chi, type="double") + sage: L = Lfunction_from_character(chi, type="double") sage: L.find_zeros(1,15,.1) [6.64845334472..., 9.83144443288..., 11.9588456260...] + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[1] - sage: L=Lfunction_from_character(chi, type="complex") + sage: L = Lfunction_from_character(chi, type="complex") sage: L.find_zeros(-8,8,.1) [-4.13290370521..., 6.18357819545...] - sage: L=Lfunction_Zeta() + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * + sage: L = Lfunction_Zeta() sage: L.find_zeros(10,29.1,.1) [14.1347251417..., 21.0220396387..., 25.0108575801...] """ @@ -307,18 +333,21 @@ cdef class Lfunction: return returnvalue #The default values are from L.h. See L.h - def find_zeros_via_N(self, count=0, do_negative=False, max_refine=1025, - rank=-1, test_explicit_formula=0): + def find_zeros_via_N(self, count=0, start=0, max_refine=1025, rank=-1): """ - Finds ``count`` number of zeros with positive imaginary part - starting at real axis. This function also verifies that all - the zeros have been found. + Find ``count`` zeros (in order of increasing magnitude) and output + their imaginary parts. This function verifies that no zeros + are missed, and that all values output are indeed zeros. + + If this L-function is self-dual (if its Dirichlet coefficients + are real, up to a tolerance of 1e-6), then only the zeros with + positive imaginary parts are output. Their conjugates, which + are also zeros, are not output. INPUT: - ``count`` - number of zeros to be found - - ``do_negative`` - (default: False) False to ignore zeros below the - real axis. + - ``start`` - (default: 0) how many initial zeros to skip - ``max_refine`` - when some zeros are found to be missing, the step size used to find zeros is refined. max_refine gives an upper limit on when lcalc should give up. Use default value unless you know @@ -326,44 +355,51 @@ cdef class Lfunction: - ``rank`` - integer (default: -1) analytic rank of the L-function. If -1 is passed, then we attempt to compute it. (Use default if in doubt) - - ``test_explicit_formula`` - integer (default: 0) If nonzero, test - the explicit formula for additional confidence that all the zeros - have been found and are accurate. This is still being tested, so - using the default is recommended. OUTPUT: - + list -- A list of the imaginary parts of the zeros that have been found EXAMPLES:: sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[2] #This is a quadratic character - sage: L=Lfunction_from_character(chi, type="int") + sage: L = Lfunction_from_character(chi, type="int") sage: L.find_zeros_via_N(3) [6.64845334472..., 9.83144443288..., 11.9588456260...] - - sage: L=Lfunction_from_character(chi, type="double") + sage: L = Lfunction_from_character(chi, type="double") sage: L.find_zeros_via_N(3) [6.64845334472..., 9.83144443288..., 11.9588456260...] + :: + + sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[1] - sage: L=Lfunction_from_character(chi, type="complex") - sage: L.find_zeros_via_N(3) - [6.18357819545..., 8.45722917442..., 12.6749464170...] + sage: L = Lfunction_from_character(chi, type="complex") + sage: zeros = L.find_zeros_via_N(3) + sage: (zeros[0] - (-4.13290370521286)).abs() < 1e-8 + True + sage: (zeros[1] - 6.18357819545086).abs() < 1e-8 + True + sage: (zeros[2] - 8.45722917442320).abs() < 1e-8 + True + + :: - sage: L=Lfunction_Zeta() + sage: from sage.libs.lcalc.lcalc_Lfunction import * + sage: L = Lfunction_Zeta() sage: L.find_zeros_via_N(3) [14.1347251417..., 21.0220396387..., 25.0108575801...] + """ - cdef Integer count_I = Integer(count) - cdef Integer do_negative_I = Integer(do_negative) - cdef RealNumber max_refine_R = RRR(max_refine) - cdef Integer rank_I = Integer(rank) - cdef Integer test_explicit_I = Integer(test_explicit_formula) + + # This is the default value for message_stamp, but we have to + # pass it explicitly since we're passing in the next argument, + # our &result pointer. + cdef const char* message_stamp = "" cdef doublevec result sig_on() - self.__find_zeros_via_N_v(mpz_get_si(count_I.value), mpz_get_si(do_negative_I.value), mpfr_get_d(max_refine_R.value, MPFR_RNDN), mpz_get_si(rank_I.value), mpz_get_si(test_explicit_I.value), &result) + self.__find_zeros(count, start, max_refine, rank, message_stamp, &result) sig_off() returnvalue = [] for i in range(result.size()): @@ -380,7 +416,7 @@ cdef class Lfunction: cdef c_Complex __hardy_z_function(self,c_Complex s): raise NotImplementedError - + cdef int __compute_rank(self): raise NotImplementedError @@ -390,7 +426,7 @@ cdef class Lfunction: cdef void __find_zeros_v(self,double T1, double T2, double stepsize, doublevec *result): raise NotImplementedError - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec *result): + cdef int __find_zeros(self, long count, long start, double max_refine, int rank, const char* message_stamp, doublevec *result): raise NotImplementedError ############################################################################## @@ -473,7 +509,7 @@ cdef class Lfunction_I(Lfunction): cdef inline c_Complex __value(self,c_Complex s,int derivative): return ((self.thisptr)).value(s, derivative, "pure") - + cdef inline c_Complex __hardy_z_function(self,c_Complex s): return ((self.thisptr)).value(s, 0, "rotated pure") @@ -486,8 +522,8 @@ cdef class Lfunction_I(Lfunction): cdef double __typedN(self, double T): return (self.thisptr).N(T) - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec *result): - (self.thisptr).find_zeros_via_N_v(count, do_negative, max_refine, rank, test_explicit_formula, result[0]) + cdef int __find_zeros(self, long count, long start, double max_refine, int rank, const char* message_stamp, doublevec *result): + (self.thisptr).find_zeros(count, start, max_refine, rank, message_stamp, result) # debug tools def _print_data_to_standard_output(self): @@ -500,7 +536,7 @@ cdef class Lfunction_I(Lfunction): sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[2] #This is a quadratic character sage: L=Lfunction_from_character(chi, type="int") - sage: L._print_data_to_standard_output() # tol 1e-15 + sage: L._print_data_to_standard_output() # tol 1e-8 ----------------------------------------------- Name of L_function: @@ -624,8 +660,8 @@ cdef class Lfunction_D(Lfunction): cdef double __typedN(self, double T): return (self.thisptr).N(T) - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec *result): - (self.thisptr).find_zeros_via_N_v(count, do_negative, max_refine, rank, test_explicit_formula, result[0]) + cdef int __find_zeros(self, long count, long start,double max_refine, int rank, const char* message_stamp, doublevec *result): + (self.thisptr).find_zeros(count, start, max_refine, rank, message_stamp, result) # debug tools def _print_data_to_standard_output(self): @@ -638,7 +674,7 @@ cdef class Lfunction_D(Lfunction): sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[2] #This is a quadratic character sage: L=Lfunction_from_character(chi, type="double") - sage: L._print_data_to_standard_output() # tol 1e-15 + sage: L._print_data_to_standard_output() # tol 1e-8 ----------------------------------------------- Name of L_function: @@ -769,8 +805,8 @@ cdef class Lfunction_C: cdef double __typedN(self, double T): return (self.thisptr).N(T) - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec *result): - (self.thisptr).find_zeros_via_N_v(count, do_negative, max_refine, rank, test_explicit_formula, result[0]) + cdef int __find_zeros(self, long count, long start, double max_refine, int rank, const char* message_stamp, doublevec *result): + (self.thisptr).find_zeros(count, start, max_refine, rank, message_stamp, result) # debug tools def _print_data_to_standard_output(self): @@ -783,7 +819,7 @@ cdef class Lfunction_C: sage: from sage.libs.lcalc.lcalc_Lfunction import * sage: chi = DirichletGroup(5)[1] sage: L=Lfunction_from_character(chi, type="complex") - sage: L._print_data_to_standard_output() # tol 1e-15 + sage: L._print_data_to_standard_output() # tol 1e-8 ----------------------------------------------- Name of L_function: @@ -854,8 +890,8 @@ cdef class Lfunction_Zeta(Lfunction): cdef double __typedN(self, double T): return (self.thisptr).N(T) - cdef void __find_zeros_via_N_v(self, long count,int do_negative,double max_refine, int rank, int test_explicit_formula, doublevec *result): - (self.thisptr).find_zeros_via_N_v(count, do_negative, max_refine, rank, test_explicit_formula, result[0]) + cdef int __find_zeros(self, long count, long start, double max_refine, int rank, const char* message_stamp, doublevec *result): + (self.thisptr).find_zeros(count, start, max_refine, rank, message_stamp, result) def __dealloc__(self): """ @@ -869,7 +905,7 @@ cdef class Lfunction_Zeta(Lfunction): def Lfunction_from_character(chi, type="complex"): """ - Given a primitive Dirichlet character, this function returns + Given a primitive Dirichlet character, this function returns an lcalc L-function object for the L-function of the character. INPUT: @@ -950,10 +986,11 @@ def Lfunction_from_elliptic_curve(E, number_of_coeffs=10000): sage: L = Lfunction_from_elliptic_curve(EllipticCurve('37')) sage: L L-function with real Dirichlet coefficients - sage: L.value(0.5).abs() < 1e-15 # "noisy" zero on some platforms (see #9615) + sage: L.value(0.5).abs() < 1e-8 + True + sage: (L.value(0.5, derivative=1) - 0.305999773835200).abs() < 1e-6 True - sage: L.value(0.5, derivative=1) - 0.305999... + """ import sage.libs.lcalc.lcalc_Lfunction Q = RRR(E.conductor()).sqrt() / RRR(2 * pi) diff --git a/src/sage/libs/lcalc/lcalc_sage.h b/src/sage/libs/lcalc/lcalc_sage.h index 498528917f9..891a40c403e 100644 --- a/src/sage/libs/lcalc/lcalc_sage.h +++ b/src/sage/libs/lcalc/lcalc_sage.h @@ -1,4 +1,4 @@ -#include "Lfunction/L.h" +#include "lcalc/L.h" int *new_ints(int l) { return new int[l]; @@ -62,4 +62,3 @@ void testL(L_function *L) cout << "Value at 1" << L->value(1.0) <value(.5+I) < = N.chart('Z1:period=1+2*I Z2') sage: XN.periods() - {0: 2*I + 1} + (2*I + 1, None) Coordinates are Sage symbolic variables (see :mod:`sage.symbolic.expression`):: @@ -309,7 +309,10 @@ def __classcall__(cls, domain, coordinates='', for x in names: coordinates += x + ' ' coordinates = coordinates[:-1] - coordinates, coordinate_options = cls._parse_coordinates(domain, coordinates) + coordinates, parsed_options = cls._parse_coordinates(domain, + coordinates) + if not coordinate_options: + coordinate_options = parsed_options coord_string = ' '.join(str(x) for x in coordinates) @@ -317,14 +320,16 @@ def __classcall__(cls, domain, coordinates='', return domain._charts_by_coord[coord_string] except KeyError: # Make coord_restrictions hashable - coord_restrictions = cls._normalize_coord_restrictions(coordinates, coord_restrictions) + coord_restrictions = cls._normalize_coord_restrictions(coordinates, + coord_restrictions) self = super().__classcall__(cls, domain, coordinates, calc_method, coord_restrictions=coord_restrictions, **coordinate_options) domain._charts_by_coord[coord_string] = self return self - def __init__(self, domain, coordinates, calc_method=None, periods=None, coord_restrictions=None): + def __init__(self, domain, coordinates, calc_method=None, periods=None, + coord_restrictions=None): r""" Construct a chart. @@ -364,13 +369,7 @@ def __init__(self, domain, coordinates, calc_method=None, periods=None, coord_re self.simplify = self._calc_method.simplify # Treatment of the coordinates: - if periods is None: - self._periods = {} - else: - # dictionary of periods (if any); key = coord. index - self._periods = {self._sindex + i: period - for i, period in enumerate(periods) - if period is not None} + self._periods = periods if len(coordinates) != self._manifold.dim(): raise ValueError("the list of coordinates must contain " + @@ -431,7 +430,8 @@ def _parse_coordinates(cls, domain, coordinates): - a tuple of variables (as elements of ``SR``) - a dictionary with possible keys: - - `"periods": a tuple of periods + + - ``"periods"``: a tuple of periods TESTS:: @@ -504,7 +504,8 @@ def normalize(r): if coord_restrictions is None: return frozenset() - if callable(coord_restrictions) and not isinstance(coord_restrictions, Expression): + if callable(coord_restrictions) and not isinstance(coord_restrictions, + Expression): # lambda-quoted coord_restrictions = coord_restrictions(*coordinates) @@ -679,13 +680,12 @@ def manifold(self): def periods(self): r""" - Return the coordinate periods as a dictionary, possibly empty if no - coordinate is periodic. + Return the coordinate periods. OUTPUT: - - a dictionary with keys the indices of the periodic coordinates and - with values the periods. + - a tuple containing the period of each coordinate, with the + value ``None`` if the coordinate is not periodic EXAMPLES: @@ -694,31 +694,16 @@ def periods(self): sage: M = Manifold(2, 'M', structure='topological') sage: X. = M.chart() sage: X.periods() - {} + (None, None) Charts with a periodic coordinate:: sage: Y. = M.chart("u v:(0,2*pi):periodic") sage: Y.periods() - {1: 2*pi} + (None, 2*pi) sage: Z. = M.chart(r"a:period=sqrt(2):\alpha b:\beta") sage: Z.periods() - {0: sqrt(2)} - - The key in the output dictionary takes into account the index range - declared on the manifold with ``start_index``:: - - sage: M = Manifold(2, 'M', structure='topological', start_index=1) - sage: Y. = M.chart("u v:(0,2*pi):periodic") - sage: Y[2] - v - sage: Y.periods() - {2: 2*pi} - sage: Z. = M.chart(r"a:period=sqrt(2):\alpha b:\beta") - sage: Z[1] - a - sage: Z.periods() - {1: sqrt(2)} + (sqrt(2), None) Complex manifold with a periodic coordinate:: @@ -726,13 +711,13 @@ def periods(self): ....: start_index=1) sage: X. = M.chart("x y:period=1+I") sage: X.periods() - {2: I + 1} + (None, I + 1) TESTS:: sage: M = Manifold(2, 'M', field=QQ, structure='topological') sage: X. = M.chart(r"xq:period=3/2 yq:\zeta:period=2") - sage: X.periods()[0], X.periods()[1] + sage: X.periods() (3/2, 2) """ @@ -843,9 +828,11 @@ def restrict(self, subset, restrictions=None): for coord in self._xx: coordinates += repr(coord) + ' ' res_coord_restrictions = set(self._restrictions) - res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, restrictions)) + res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, + restrictions)) res = type(self)(subset, coordinates, calc_method=self._calc_method._current, + periods=self._periods, # The coordinate restrictions are added # to the result chart coord_restrictions=res_coord_restrictions) @@ -1781,7 +1768,7 @@ class RealChart(Chart): sage: c_spher1. = \ ....: V.chart(r'r:(0,+oo) th:(0,pi):\theta ph1:(0,2*pi):periodic:\phi_1') sage: c_spher1.periods() - {3: 2*pi} + (None, None, 2*pi) sage: c_spher1.coord_range() r: (0, +oo); th: (0, pi); ph1: [0, 2*pi] (periodic) @@ -1791,7 +1778,7 @@ class RealChart(Chart): sage: c_spher2. = \ ....: V.chart(r'r:(0,+oo) th:(0,pi):\theta ph2:period=2*pi:\phi_2') sage: c_spher2.periods() - {3: 2*pi} + (None, None, 2*pi) sage: c_spher2.coord_range() r: (0, +oo); th: (0, pi); ph2: [0, 2*pi] (periodic) @@ -1869,7 +1856,8 @@ class RealChart(Chart): :meth:`plot`. """ - def __init__(self, domain, coordinates, calc_method=None, bounds=None, periods=None, coord_restrictions=None): + def __init__(self, domain, coordinates, calc_method=None, bounds=None, + periods=None, coord_restrictions=None): r""" Construct a chart on a real topological manifold. @@ -1910,6 +1898,14 @@ def _parse_coordinates(cls, domain, coordinates): character of the coordinate, and the (optional) coordinate LaTeX symbol + OUTPUT: + + - a tuple of variables (as elements of ``SR``) + - a dictionary with possible keys: + + - ``"periods"``: a tuple of periods + - ``"bounds"``: a tuple of coordinate ranges + TESTS:: sage: from sage.manifolds.chart import RealChart @@ -2385,6 +2381,19 @@ def restrict(self, subset, restrictions=None): sage: a in A True + TESTS: + + Check that :trac:`32929` is fixed:: + + sage: M = Manifold(2, 'M') + sage: X. = M.chart(r"x:(0,+oo) y:(0,2):periodic") + sage: U = M.open_subset('U', coord_def={X: x<1}) + sage: XU = X.restrict(U) + sage: XU.coord_range() + x: (0, 1); y: [0, 2] (periodic) + sage: XU.periods() + (None, 2) + """ if subset == self.domain(): return self @@ -2396,10 +2405,11 @@ def restrict(self, subset, restrictions=None): for coord in self._xx: coordinates += repr(coord) + ' ' res_coord_restrictions = set(self._restrictions) - res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, restrictions)) + res_coord_restrictions.update(self._normalize_coord_restrictions(self._xx, + restrictions)) res = type(self)(subset, coordinates, calc_method=self._calc_method._current, - bounds=self._bounds, + bounds=self._bounds, periods=self._periods, # The coordinate restrictions are added # to the result chart and possibly # transformed into coordinate bounds: diff --git a/src/sage/manifolds/differentiable/chart.py b/src/sage/manifolds/differentiable/chart.py index 86a39667cd8..105c1b08849 100644 --- a/src/sage/manifolds/differentiable/chart.py +++ b/src/sage/manifolds/differentiable/chart.py @@ -192,7 +192,7 @@ class DiffChart(Chart): sage: N = Manifold(2, 'N', field='complex') sage: XN. = N.chart('Z1:period=1+2*I Z2') sage: XN.periods() - {0: 2*I + 1} + (2*I + 1, None) Coordinates are Sage symbolic variables (see :mod:`sage.symbolic.expression`):: @@ -877,7 +877,7 @@ class RealDiffChart(DiffChart, RealChart): sage: c_spher1. = \ ....: V.chart(r'r:(0,+oo) th:(0,pi):\theta ph1:(0,2*pi):periodic:\phi_1') sage: c_spher1.periods() - {3: 2*pi} + (None, None, 2*pi) sage: c_spher1.coord_range() r: (0, +oo); th: (0, pi); ph1: [0, 2*pi] (periodic) @@ -887,7 +887,7 @@ class RealDiffChart(DiffChart, RealChart): sage: c_spher2. = \ ....: V.chart(r'r:(0,+oo) th:(0,pi):\theta ph2:period=2*pi:\phi_2') sage: c_spher2.periods() - {3: 2*pi} + (None, None, 2*pi) sage: c_spher2.coord_range() r: (0, +oo); th: (0, pi); ph2: [0, 2*pi] (periodic) diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 70250784db8..230f54f905a 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -669,7 +669,7 @@ def _an_element_(self): sage: p in V True sage: p.coord() - (-pi - 1, 0) + (-pi - 1, 2) """ from sage.rings.infinity import Infinity diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py index 3a841e2d7cb..9c3d112c806 100644 --- a/src/sage/manifolds/point.py +++ b/src/sage/manifolds/point.py @@ -692,28 +692,14 @@ def __eq__(self, other): # raise ValueError("no common chart has been found to compare " + # "{} and {}".format(self, other)) periods = common_chart.periods() - if periods: - # Special case of periodic coordinate(s): - ind = common_chart._sindex - for xs, xo in zip(self._coordinates[common_chart], - other._coordinates[common_chart]): - diff = xs - xo - if ind in periods: - period = periods[ind] - if not (diff/period in ZZ): - return False - else: - if (isinstance(diff, Expression) and - not diff.is_trivial_zero()): - return False - elif not (diff == 0): - return False - ind += 1 - else: - # Generic case: - for xs, xo in zip(self._coordinates[common_chart], - other._coordinates[common_chart]): - diff = xs - xo + for ind, (xs, xo) in enumerate(zip(self._coordinates[common_chart], + other._coordinates[common_chart])): + diff = xs - xo + period = periods[ind] + if period is not None: + if not (diff/period in ZZ): + return False + else: if isinstance(diff, Expression) and not diff.is_trivial_zero(): return False elif not (diff == 0): diff --git a/src/sage/matrix/matrix2.pxd b/src/sage/matrix/matrix2.pxd index 6f99ff58d02..b9724d534b6 100644 --- a/src/sage/matrix/matrix2.pxd +++ b/src/sage/matrix/matrix2.pxd @@ -22,3 +22,5 @@ cdef class Matrix(Matrix1): cpdef _echelon(self, str algorithm) cpdef _echelon_in_place(self, str algorithm) cpdef matrix_window(self, Py_ssize_t row=*, Py_ssize_t col=*, Py_ssize_t nrows=*, Py_ssize_t ncols=*, bint check=*) + cpdef _row_ambient_module(self, base_ring=*) + cpdef _column_ambient_module(self) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ad81b86142c..cf6b29351af 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -5130,14 +5130,47 @@ cdef class Matrix(Matrix1): """ return self.row_module() - def _row_ambient_module(self, base_ring=None): + cpdef _row_ambient_module(self, base_ring=None): + """ + Return the parent of the rows. + + INPUT: + + - ``base_ring`` -- (optional); change the ring of the parent + + EXAMPLES:: + + sage: M = Matrix(ZZ, 3, 4) + sage: M._row_ambient_module() + Ambient free module of rank 4 over the principal ideal domain Integer Ring + sage: M._row_ambient_module(QQ) + Vector space of dimension 4 over Rational Field + sage: M = Matrix(QQ, 4, 5) + sage: M._row_ambient_module() + Vector space of dimension 5 over Rational Field + sage: M._row_ambient_module(ZZ) + Ambient free module of rank 5 over the principal ideal domain Integer Ring + """ + # We optimize for the case ``base_ring == None`` + # to achieve the (almost) same speed as ``_column_ambient_module`` + # in this case. + # See :trac:`32901`. if base_ring is None: - base_ring = self.base_ring() - x = self.fetch('row_ambient_module_%s'%base_ring) - if not x is None: + x = self.fetch('row_ambient_module') + if x is not None: + return x + x = sage.modules.free_module.FreeModule(self._base_ring, self._ncols, + sparse=self.is_sparse_c()) + self.cache('row_ambient_module', x) return x - x = sage.modules.free_module.FreeModule(base_ring, self.ncols(), sparse=self.is_sparse()) - self.cache('row_ambient_module',x) + + cache_name = 'row_ambient_module_' + base_ring.__repr__() + x = self.fetch(cache_name) + if x is not None: + return x + x = sage.modules.free_module.FreeModule(base_ring, self._ncols, + sparse=self.is_sparse_c()) + self.cache(cache_name, x) return x def row_module(self, base_ring=None): @@ -5191,12 +5224,24 @@ cdef class Matrix(Matrix1): """ return self.row_module(base_ring=base_ring) - def _column_ambient_module(self): + cpdef _column_ambient_module(self): + """ + Return the parent of the columns. + + EXAMPLES:: + + sage: M = Matrix(ZZ, 3, 4) + sage: M._column_ambient_module() + Ambient free module of rank 3 over the principal ideal domain Integer Ring + sage: M = Matrix(QQ, 4, 5) + sage: M._column_ambient_module() + Vector space of dimension 4 over Rational Field + """ x = self.fetch('column_ambient_module') if not x is None: return x - x = sage.modules.free_module.FreeModule(self.base_ring(), self.nrows(), - sparse=self.is_sparse()) + x = sage.modules.free_module.FreeModule(self._base_ring, self._nrows, + sparse=self.is_sparse_c()) self.cache('column_ambient_module',x) return x diff --git a/src/sage/matrix/matrix_double_sparse.pxd b/src/sage/matrix/matrix_double_sparse.pxd new file mode 100644 index 00000000000..ba8b303878b --- /dev/null +++ b/src/sage/matrix/matrix_double_sparse.pxd @@ -0,0 +1,4 @@ +from .matrix_generic_sparse cimport Matrix_generic_sparse + +cdef class Matrix_double_sparse(Matrix_generic_sparse): + pass diff --git a/src/sage/matrix/matrix_double_sparse.pyx b/src/sage/matrix/matrix_double_sparse.pyx new file mode 100644 index 00000000000..2e8635a3fcb --- /dev/null +++ b/src/sage/matrix/matrix_double_sparse.pyx @@ -0,0 +1,171 @@ +from .matrix2 cimport Matrix +from .matrix_generic_sparse cimport Matrix_generic_sparse + +cdef class Matrix_double_sparse(Matrix_generic_sparse): + r""" + Class for sparse RDF/CDF matrices. + + EXAMPLES:: + + sage: A = matrix.random(RDF, ZZ.random_element(5), sparse=True) + sage: A.__class__ + + sage: A = matrix.random(CDF, ZZ.random_element(5), sparse=True) + sage: A.__class__ + + + """ + def cholesky(self): + r""" + Returns the Cholesky decomposition of a Hermitian matrix. + + INPUT: + + A positive-definite matrix over ``RDF`` or ``CDF``. + + OUTPUT: + + For a matrix `A` the routine returns a lower triangular + matrix `L` (over the same base ring as `A`) such that, + + .. MATH:: + + A = LL^\ast + + where `L^\ast` is the conjugate-transpose. If the matrix is + not positive-definite (for example, if it is not Hermitian) + then a ``ValueError`` results. + + ALGORITHM: + + If cvxopt is available, we use its sparse `cholmod` routines + to compute the factorization quickly. Otherwise, we fall back + to the naive implementation used for dense matrices. + + EXAMPLES:: + + sage: A = matrix(RDF, [[10, 0, 3, 0], + ....: [ 0, 5, 0,-2], + ....: [ 3, 0, 5, 0], + ....: [ 0,-2, 0, 2]], + ....: sparse=True) + sage: L = A.cholesky() + sage: L.is_triangular() + True + sage: (A - L*L.T).norm(1) < 1e-10 + True + + :: + + sage: A = matrix(CDF, [[ 2, 4 + 2*I, 6 - 4*I], + ....: [ -2*I + 4, 11, 10 - 12*I], + ....: [ 4*I + 6, 10 + 12*I, 37]]) + sage: L = A.cholesky() + sage: L.is_triangular() + True + sage: (A - L*L.H).norm(1) < 1e-10 + True + + TESTS: + + Test the properties of a Cholesky factorization using "random" + symmetric/Hermitian positive-definite matrices. We also + compare with the factorizations obtained over ``RR`` or + ``CC``, which (when cvxopt is available) use a different + implementation. This ensures that both implementations return + comparable answers:: + + sage: n = ZZ.random_element(1,5) + sage: A = matrix.random(RDF, n, sparse=True) + sage: I = matrix.identity(RDF, n, sparse=True) + sage: A = A*A.transpose() + I + sage: L = A.cholesky() + sage: (A - L*L.T).norm(1) < 1e-10 + True + sage: B = A.change_ring(RR) + sage: (B.cholesky() - L).norm(1) < 1e-10 + True + + :: + + sage: n = ZZ.random_element(1,5) + sage: A = matrix.random(CDF, n, sparse=True) + sage: I = matrix.identity(CDF, n, sparse=True) + sage: A = A*A.conjugate_transpose() + I + sage: L = A.cholesky() + sage: (A - L*L.H).norm(1) < 1e-10 + True + sage: B = A.change_ring(CC) + sage: (B.cholesky() - L).norm(1) < 1e-10 + True + """ + cdef Matrix L # output matrix + + L = self.fetch('cholesky') + if L is not None: + return L + + # The superclass method does this, so we should too... + if not self.is_hermitian(): + raise ValueError("matrix is not Hermitian") + + try: + from cvxopt import cholmod, spmatrix, matrix as cvxopt_matrix + except ModuleNotFoundError: + # Sage can be built with --disable-cvxopt, so we have to + # handle the case where cvxopt is not present. The + # superclass method is slow, but no longer raises an + # error, so let's try that. + L = super().cholesky() + + cdef list idx_pairs = self.nonzero_positions(copy=False) + cdef list row_idxs = [r for (r, c) in idx_pairs] + cdef list col_idxs = [c for (r, c) in idx_pairs] + value_type = float + type_code = 'd' + from sage.rings.complex_double import CDF + if self.base_ring() is CDF: + value_type = complex + type_code = 'z' + cdef list values = [value_type(self[idx]) for idx in idx_pairs] + + cvx_self = spmatrix(values, + row_idxs, + col_idxs, + size=self.dimensions(), + tc=type_code) + + # Insist that our ordering (= permutation) is used, and then + # choose the identity permutation. In SageMath, cholesky() + # does not allow for permutations. The 'postorder' option + # I only found documented in the cholmod module's docstring. + cholmod.options['nmethods'] = 1 + cholmod.options['postorder'] = False + id_order = cvxopt_matrix(range(self.nrows())) + + # WARNING: the getfactor() interface used below is undocumented: + # + # https://groups.google.com/g/cvxopt/c/xQ-lR9ESijg/discussion + # + # And while upstream suggests that we should use a simplicial + # factorization, the cvxopt docs themselves say that we should + # use the default (=2) value of 'supernodal' for the PAP^T = + # LL^T factorization that we want. + # + cvx_symbolic = cholmod.symbolic(cvx_self, p=id_order) + + # This overwrites cvx_symbolic with the numeric factorization before + # passing it to getfactor(). + cholmod.numeric(cvx_self, cvx_symbolic) + cvx_L = cholmod.getfactor(cvx_symbolic) + + # The (I[k],J[k]) entry of cvx_L has value V[k]. But beware that V + # contains only the non-zero entries of the matrix; as a result, the + # dict below contains keys only for those nonzero entries. + L = self.matrix_space()({ + (cvx_L.I[k], cvx_L.J[k]): cvx_L.V[k] + for k in range(len(cvx_L.V)) + }) + + self.cache('cholesky', L) + return L diff --git a/src/sage/matrix/matrix_generic_sparse.pyx b/src/sage/matrix/matrix_generic_sparse.pyx index 16a50673f24..35dea4a0020 100644 --- a/src/sage/matrix/matrix_generic_sparse.pyx +++ b/src/sage/matrix/matrix_generic_sparse.pyx @@ -270,6 +270,27 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): sage: a+a [ 2 20 -10/3] [ 1/2 6 8] + + TESTS: + + Adding two subclasses should produce an instance of the subclass; not + a generic matrix:: + + sage: A = matrix(RDF, [[1]], sparse=True) + sage: B = matrix(RDF, [[2]], sparse=True) + sage: C = matrix(ZZ, [[3]], sparse=True) + sage: D = matrix(CDF, [[4]], sparse=True) + sage: (A+B).__class__ == A.__class__ + True + sage: (B+A).__class__ == A.__class__ + True + sage: (A+C).__class__ == A.__class__ + True + sage: (C+A).__class__ == A.__class__ + True + sage: (A+D).__class__ == D.__class__ + True + """ # Compute the sum of two sparse matrices. # This is complicated because of how we represent sparse matrices. @@ -308,12 +329,15 @@ cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse): s[w[j][0]] = w[j][1] j += 1 - cdef Matrix_generic_sparse A - A = Matrix_generic_sparse.__new__(Matrix_generic_sparse, self._parent, 0,0,0) - matrix.Matrix.__init__(A, self._parent) + # Use the parent of the left-hand summand as the parent of the + # result. This allows you to (for example) add two sparse RDF + # matrices together and get another one back, rather than + # getting a generic sparse matrix back. + MS = self._parent + cdef type t = type(self) + A = t.__new__(t, MS) A._entries = s A._zero = self._zero - A._base_ring = self._base_ring return A def __copy__(self): diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 79085b5df8f..68e42719782 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -393,6 +393,10 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): else: return matrix_integer_sparse.Matrix_integer_sparse + if R is sage.rings.real_double.RDF or R is sage.rings.complex_double.CDF: + from . import matrix_double_sparse + return matrix_double_sparse.Matrix_double_sparse + # the fallback return matrix_generic_sparse.Matrix_generic_sparse diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 0d2c2f5818f..d5665f34c3f 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -506,7 +506,7 @@ def standard_packages(): """ from sage.misc.superseded import deprecation deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages' + 'the functions standard_packages, optional_packages, experimental_packages ' 'are deprecated, use sage.features instead') pkgs = list_packages('standard', local=True).values() return (sorted(pkg.name for pkg in pkgs if pkg.is_installed()), @@ -543,7 +543,7 @@ def optional_packages(): """ from sage.misc.superseded import deprecation deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages' + 'the functions standard_packages, optional_packages, experimental_packages ' 'are deprecated, use sage.features instead') pkgs = list_packages('optional', local=True) pkgs = pkgs.values() @@ -576,7 +576,7 @@ def experimental_packages(): """ from sage.misc.superseded import deprecation deprecation(30747, - 'the functions standard_packages, optional_packages, experimental_packages' + 'the functions standard_packages, optional_packages, experimental_packages ' 'are deprecated, use sage.features instead') pkgs = list_packages('experimental', local=True).values() return (sorted(pkg.name for pkg in pkgs if pkg.is_installed()), diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index e6f4ccfb514..eb70516b5fd 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -1532,7 +1532,7 @@ def _open(self, name, testing=False): path = os.path.join(self._base_path, name, "index.html") if not os.path.exists(path): raise OSError("""The document '{0}' does not exist. Please build it -with 'sage -docbuild {0} html --mathjax' and try again.""".format(name)) +with 'sage -docbuild {0} html' and try again.""".format(name)) if testing: return (url, path) diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index efa08a4f2ea..85e71683726 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -750,8 +750,8 @@ def lfunction(self, prec=53, algorithm='pari'): sage: a = a.primitive_character() sage: L = a.lfunction(algorithm='lcalc'); L L-function with complex Dirichlet coefficients - sage: L.value(4) # abs tol 1e-14 - 0.988944551741105 - 5.16608739123418e-18*I + sage: L.value(4) # abs tol 1e-8 + 0.988944551741105 + 0.0*I """ if algorithm is None: algorithm = 'pari' diff --git a/src/sage/modular/pollack_stevens/modsym.py b/src/sage/modular/pollack_stevens/modsym.py index 9766edd13b0..af9fbbb5495 100644 --- a/src/sage/modular/pollack_stevens/modsym.py +++ b/src/sage/modular/pollack_stevens/modsym.py @@ -56,6 +56,7 @@ minusproj = [1, 0, 0, -1] + def _iterate_Up(Phi, p, M, ap, q, aq, check): r""" Return an overconvergent Hecke-eigensymbol lifting self -- self must be a @@ -770,6 +771,16 @@ def evaluate_twisted(self, a, chi): sage: D = L.quadratic_twist() # long time sage: L.symbol().evaluate_twisted(1,D) # long time (4 + 6*7 + 3*7^2 + O(7^4), 6*7 + 6*7^2 + O(7^3), 6 + O(7^2), 1 + O(7)) + + TESTS: + + Check for :trac:`32878`:: + + sage: E = EllipticCurve('11a1') + sage: L = E.padic_lseries(3, implementation="pollackstevens", precision=4) + sage: D = 5 + sage: L.symbol().evaluate_twisted(1, D) + (2 + 3 + 2*3^2 + O(3^4), 2 + 3 + O(3^3), 2 + 3 + O(3^2), 2 + O(3)) """ p = self.parent().prime() S0p = Sigma0(p) @@ -780,8 +791,9 @@ def evaluate_twisted(self, a, chi): m_map = self._map for b in range(1, abs(chi) + 1): if gcd(b, chi) == 1: - M1 = S0p([1, (b / abs(chi)) % p ** M, 0, 1]) - new_dist = m_map(M1 * M2Z([a, 1, p, 0])) * M1 + M1 = S0p([1, (b / abs(chi)) % p**M, 0, 1]) + new_dist = m_map(M2Z([a * abs(chi) + p * b, + 1, p * abs(chi), 0])) * M1 new_dist = new_dist.scale(kronecker(chi, b)).normalize() twisted_dist += new_dist return twisted_dist.normalize() diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index d5677955984..775b68693a8 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -476,7 +476,7 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) Let's find a minimum of `\sin(xy)`:: sage: x,y = var('x y') - sage: f = sin(x*y) + sage: f(x,y) = sin(x*y) sage: minimize_constrained(f, [(None,None),(4,10)],[5,5]) (4.8..., 4.8...) @@ -498,12 +498,23 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) Check if :trac:`6592` is fixed:: sage: x, y = var('x y') - sage: f = (100 - x) + (1000 - y) - sage: c = x + y - 479 # > 0 + sage: f(x,y) = (100 - x) + (1000 - y) + sage: c(x,y) = x + y - 479 # > 0 sage: minimize_constrained(f, [c], [100, 300]) (805.985..., 1005.985...) sage: minimize_constrained(f, c, [100, 300]) (805.985..., 1005.985...) + + If ``func`` is symbolic, its minimizer should be in the same order + as its arguments (:trac:`32511`):: + + sage: x,y = SR.var('x,y') + sage: f(y,x) = x - y + sage: c1(y,x) = x + sage: c2(y,x) = 1-y + sage: minimize_constrained(f, [c1, c2], (0,0)) + (1.0, 0.0) + """ from sage.structure.element import Expression from sage.ext.fast_callable import fast_callable @@ -512,7 +523,7 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) function_type = type(lambda x,y: x+y) if isinstance(func, Expression): - var_list = func.variables() + var_list = func.arguments() fast_f = fast_callable(func, vars=var_list, domain=float) f = lambda p: fast_f(*p) gradient_list = func.gradient() diff --git a/src/sage/plot/arrow.py b/src/sage/plot/arrow.py index 75d917af65a..cfa91aecfa5 100644 --- a/src/sage/plot/arrow.py +++ b/src/sage/plot/arrow.py @@ -405,7 +405,11 @@ def __init__(self, patch, n): self._n = n def get_paths(self, renderer): - paths, fillables = self._patch.get_path_in_displaycoord() + # get_path_in_displaycoord was made private in matplotlib 3.5 + try: + paths, fillables = self._patch._get_path_in_displaycoord() + except AttributeError: + paths, fillables = self._patch.get_path_in_displaycoord() return paths def __call__(self, renderer, gc, tpath, affine, rgbFace): diff --git a/src/sage/plot/contour_plot.py b/src/sage/plot/contour_plot.py index 690311744e7..a7de8a7c912 100644 --- a/src/sage/plot/contour_plot.py +++ b/src/sage/plot/contour_plot.py @@ -331,6 +331,14 @@ def contour_plot(f, xrange, yrange, **options): of two variables. Only segments of the surface where region(x,y) returns a number >0 will be included in the plot. + .. WARNING:: + + Due to an implementation detail in matplotlib, single-contour + plots whose data all lie on one side of the sole contour may + not be plotted correctly. We attempt to detect this situation + and to produce something better than an empty plot when it + happens; a ``UserWarning`` is emitted in that case. + EXAMPLES: Here we plot a simple function of two variables. Note that @@ -814,6 +822,45 @@ def f(x,y): return cos(x) + sin(y) g = contour_plot(f, (-pi,pi), (-pi,pi), fill=False, axes=True) sphinx_plot(g) + If you are plotting a sole countour and if all of your data lie on + one side of it, then (as part of :trac:`21042`) a heuristic may be + used to improve the result; in that case, a warning is emitted:: + + sage: contour_plot(lambda x,y: abs(x^2-y^2), (-1,1), (-1,1), + ....: contours=[0], fill=False, cmap=['blue']) + ... + UserWarning: pathological contour plot of a function whose values + all lie on one side of the sole contour; we are adding more plot + points and perturbing your function values. + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + g = contour_plot(lambda x,y: abs(x**2-y**2), (-1,1), (-1,1), + contours=[0], fill=False, cmap=['blue']) + sphinx_plot(g) + + Constant functions (with a single contour) can be plotted as well; + this was not possible before :trac:`21042`:: + + sage: contour_plot(lambda x,y: 0, (-1,1), (-1,1), + ....: contours=[0], fill=False, cmap=['blue']) + ... + UserWarning: No contour levels were found within the data range. + Graphics object consisting of 1 graphics primitive + + .. PLOT:: + + import warnings + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + g = contour_plot(lambda x,y: 0, (-1,1), (-1,1), + contours=[0], fill=False, cmap=['blue']) + sphinx_plot(g) + TESTS: To check that :trac:`5221` is fixed, note that this has three curves, not @@ -845,13 +892,130 @@ def f(x,y): return cos(x) + sin(y) F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], options['plot_points']) - g = F[0] + h = F[0] xrange, yrange = [r[:2] for r in ranges] - xy_data_array = [[g(x, y) for x in xsrange(*ranges[0], + xy_data_array = [[h(x, y) for x in xsrange(*ranges[0], include_endpoint=True)] for y in xsrange(*ranges[1], include_endpoint=True)] + g = Graphics() + + # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. + # Otherwise matplotlib complains. + scale = options.get('scale', None) + if isinstance(scale, (list, tuple)): + scale = scale[0] + if scale in ('semilogy', 'semilogx'): + options['aspect_ratio'] = 'automatic' + + g._set_extra_kwds(Graphics._extract_kwds_for_show(options, + ignore=['xmin', 'xmax'])) + + + # Was a single contour level explicitly given? If "contours" is + # the integer 1, then there will be a single level, but we can't + # know what it is because it's determined within matplotlib's + # "contour" or "contourf" function. So we punt in that case. If + # there's a single contour and fill=True, we fall through to let + # matplotlib complain that "Filled contours require at least 2 + # levels." + if ( isinstance(options["contours"], (list, tuple)) + and + len(options["contours"]) == 1 + and + options.get("fill") == False): + # When there's only one level (say, zero), matplotlib doesn't + # handle it well. If all of the data lie on one side of that + # level -- for example, if f(x,y) >= 0 for all x,y -- then it + # will fail to plot the points where f(x,y) == 0. This is + # especially catastrophic for implicit_plot(), which tries to + # do just that. Here we handle that special case: if there's + # only one level, and if all of the data lie on one side of + # it, we perturb the data a bit so that they don't. The resulting + # plots don't look great, but they're not empty, which is an + # improvement. + import numpy as np + dx = ranges[0][2] + dy = ranges[1][2] + z0 = options["contours"][0] + + # This works OK for the examples in the doctests, but basing + # it off the plot scale rather than how fast the function + # changes can never be truly satisfactory. + tol = max(dx,dy)/4.0 + xy_data_array = np.ma.asarray(xy_data_array, dtype=float) + + # Special case for constant functions. This is needed because + # otherwise the forthcoming perturbation trick will take values + # like 0,0,0... and perturb them to -tol, -tol, -tol... which + # doesn't work for the same reason 0,0,0... doesn't work. + if np.all(np.abs(xy_data_array - z0) <= tol): + # Up to our tolerance, this is the const_z0 function. + # ...make it actually the const_z0 function. + xy_data_array.fill(z0) + + # We're going to set fill=True in a momemt, so we need to + # prepend an entry to the cmap so that the user's original + # cmap winds up in the right place. + if "cmap" in options: + if isinstance(options["cmap"], (list, tuple)): + oldcmap = options["cmap"][0] + else: + oldcmap = options["cmap"] + else: + # The docs promise this as the default. + oldcmap = "gray" + + # Trick matplotlib into plotting all of the points (minus + # those masked) by using a single, filled contour that + # covers the entire plotting surface. + options["cmap"] = ["white", oldcmap] + options["contours"] = (z0-1, z0) + options["fill"] = True + else: + # The "c" constant is set to plus/minus one to handle both + # of the "all values greater than z0" and "all values less + # than z0" cases at once. + c = 1 + if np.all(xy_data_array <= z0): + xy_data_array *= -1 + c = -1 + # Now we check if (a) all of the data lie on one side of + # z0, and (b) if perturbing the data will actually help by + # moving anything across z0. + if (np.all(xy_data_array >= z0) and + np.any(xy_data_array - z0 < tol)): + + from warnings import warn + warn("pathological contour plot of a function whose " + "values all lie on one side of the sole contour; " + "we are adding more plot points and perturbing " + "your function values.") + + # The choice of "4" here is not based on much of anything. + # It works well enough for the examples in the doctests. + if not isinstance(options["plot_points"], (list, tuple)): + options["plot_points"] = (options["plot_points"], + options["plot_points"]) + options["plot_points"] = (options["plot_points"][0]*4, + options["plot_points"][1]*4) + + # Re-plot with more points... + F, ranges = setup_for_eval_on_grid(ev, [xrange, yrange], + options['plot_points']) + h = F[0] + xrange, yrange = [r[:2] for r in ranges] + + # ...and a function whose values are shifted towards + # z0 by "tol". + xy_data_array = [ [h(x, y) - c*tol + for x in xsrange(*ranges[0], + include_endpoint=True)] + for y in xsrange(*ranges[1], + include_endpoint=True) ] + + if region is not None: import numpy @@ -868,19 +1032,8 @@ def f(x,y): return cos(x) + sin(y) xy_data_array[mask] = numpy.ma.masked - g = Graphics() - - # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'. - # Otherwise matplotlib complains. - scale = options.get('scale', None) - if isinstance(scale, (list, tuple)): - scale = scale[0] - if scale in ('semilogy', 'semilogx'): - options['aspect_ratio'] = 'automatic' - - g._set_extra_kwds(Graphics._extract_kwds_for_show(options, - ignore=['xmin', 'xmax'])) g.add_primitive(ContourPlot(xy_data_array, xrange, yrange, options)) + return g @@ -951,6 +1104,14 @@ def implicit_plot(f, xrange, yrange, **options): to logarithmic scale. The ``"linear"`` scale is the default value when :class:`~sage.plot.graphics.Graphics` is initialized. + .. WARNING:: + + Due to an implementation detail in matplotlib, implicit plots + whose data are all nonpositive or nonnegative may not be + plotted correctly. We attempt to detect this situation and to + produce something better than an empty plot when it happens; a + ``UserWarning`` is emitted in that case. + EXAMPLES: A simple circle with a radius of 2. Note that diff --git a/src/sage/quadratic_forms/binary_qf.py b/src/sage/quadratic_forms/binary_qf.py index 4a143e41487..adac2bc920d 100755 --- a/src/sage/quadratic_forms/binary_qf.py +++ b/src/sage/quadratic_forms/binary_qf.py @@ -416,6 +416,20 @@ def __sub__(self, Q): """ return BinaryQF([self._a - Q._a, self._b - Q._b, self._c - Q._c]) + def __neg__(self): + r""" + Return the negative of this binary quadratic form. + + EXAMPLES:: + + sage: Q = BinaryQF([1,-2,3]) + sage: -Q + -x^2 + 2*x*y - 3*y^2 + sage: -Q == BinaryQF([0,0,0]) - Q + True + """ + return BinaryQF([-self._a, -self._b, -self._c]) + def _repr_(self): """ Display the quadratic form. @@ -1451,36 +1465,46 @@ def solve_integer(self, n): OUTPUT: - A tuple `(x, y)` of integers satisfying `Q(x, y) = n` or ``None`` - if no such `x` and `y` exist. + A tuple `(x, y)` of integers satisfying `Q(x, y) = n`, or ``None`` + if no solution exists. + + ALGORITHM: :pari:`qfbsolve` EXAMPLES:: + sage: Q = BinaryQF([1, 0, 419]) + sage: Q.solve_integer(773187972) + (4919, 1337) + + :: + sage: Qs = BinaryQF_reduced_representatives(-23, primitive_only=True) sage: Qs [x^2 + x*y + 6*y^2, 2*x^2 - x*y + 3*y^2, 2*x^2 + x*y + 3*y^2] sage: [Q.solve_integer(3) for Q in Qs] - [None, (0, 1), (0, 1)] + [None, (0, -1), (0, -1)] sage: [Q.solve_integer(5) for Q in Qs] [None, None, None] sage: [Q.solve_integer(6) for Q in Qs] - [(0, 1), (-1, 1), (1, 1)] - """ - a, b, c = self - d = self.discriminant() - if d >= 0 or a <= 0: - raise NotImplementedError("%s is not positive definite" % self) - ad = -d - an4 = 4*a*n - a2 = 2*a - from sage.arith.srange import xsrange - for y in xsrange(0, 1+an4//ad): - z2 = an4 + d*y**2 - for z in z2.sqrt(extend=False, all=True): - if a2.divides(z-b*y): - x = (z-b*y)//a2 - return (x, y) - return None + [(1, -1), (1, -1), (-1, -1)] + + TESTS: + + The returned solutions are correct (random inputs):: + + sage: Q = BinaryQF([randrange(-10^3, 10^3) for _ in 'abc']) + sage: n = randrange(-10^9, 10^9) + sage: xy = Q.solve_integer(n) + sage: xy is None or Q(*xy) == 0 + True + """ + n = ZZ(n) + if self.is_negative_definite(): # not supported by PARI + return (-self).solve_integer(-n) + + flag = 2 # single solution, possibly imprimitive + sol = self.__pari__().qfbsolve(n, flag) + return tuple(map(ZZ, sol)) if sol else None def BinaryQF_reduced_representatives(D, primitive_only=False, proper=True): diff --git a/src/sage/repl/display/formatter.py b/src/sage/repl/display/formatter.py index 59456cb094a..3025df9021c 100644 --- a/src/sage/repl/display/formatter.py +++ b/src/sage/repl/display/formatter.py @@ -69,6 +69,7 @@ from ipywidgets.widgets.interaction import interactive from sage.repl.display.pretty_print import SagePrettyPrinter +from sage.misc.lazy_import import lazy_import IPYTHON_NATIVE_TYPES = (DisplayObject, interactive) @@ -76,6 +77,7 @@ TEXT_LATEX = 'text/latex' TEXT_HTML = 'text/html' +lazy_import('matplotlib.figure', 'Figure') class SageDisplayFormatter(DisplayFormatter): @@ -178,14 +180,17 @@ def format(self, obj, include=None, exclude=None): """ sage_format, sage_metadata = self.dm.displayhook(obj) assert PLAIN_TEXT in sage_format, 'plain text is always present' + # use Sage rich output for any except those native to IPython, but only # if it is not plain and dull - if (not isinstance(obj, IPYTHON_NATIVE_TYPES) and + if (not isinstance(obj, (IPYTHON_NATIVE_TYPES, Figure)) and not set(sage_format.keys()).issubset([PLAIN_TEXT])): return sage_format, sage_metadata + if self.ipython_display_formatter(obj): # object handled itself, don't proceed return {}, {} + # try IPython display formatter if exclude is not None: exclude = list(exclude) + [PLAIN_TEXT] diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 95022e34c4a..e0357d29a5a 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -575,7 +575,7 @@ def __init__(self, parent, summands, simplify=True, convert=True): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: OT = TermMonoid('O', G, ZZ); ET = TermMonoid('exact', G, ZZ) sage: R = AsymptoticRing(G, ZZ) - sage: lst = [ET(x, 1), ET(x^2, 2), OT(x^3), ET(x^4, 4)] + sage: lst = [ET(x, coefficient=1), ET(x^2, coefficient=2), OT(x^3), ET(x^4, coefficient=4)] sage: expr = R(lst, simplify=False); expr # indirect doctest 4*x^4 + O(x^3) + 2*x^2 + x sage: print(expr.summands.repr_full()) @@ -641,8 +641,10 @@ def __init__(self, parent, summands, simplify=True, convert=True): ValueError: Cannot include 1/2 with parent Exact Term Monoid x^QQ with coefficients in Rational Field in Asymptotic Ring over Integer Ring - > *previous* ValueError: 1/2 is not a coefficient in - Exact Term Monoid x^QQ with coefficients in Integer Ring. + > *previous* ValueError: Cannot create ExactTerm(1) + since given coefficient 1/2 is not valid in + Exact Term Monoid x^QQ with coefficients in Integer Ring. + >> *previous* TypeError: no conversion of this rational to integer Check :trac:`19921`:: @@ -949,7 +951,7 @@ def _simplify_(self): sage: G = GrowthGroup('x^ZZ') sage: OT = TermMonoid('O', G, ZZ); ET = TermMonoid('exact', G, ZZ) sage: R = AsymptoticRing(G, ZZ) - sage: lst = [ET(x, 1), ET(x^2, 2), OT(x^3), ET(x^4, 4)] + sage: lst = [ET(x, coefficient=1), ET(x^2, coefficient=2), OT(x^3), ET(x^4, coefficient=4)] sage: expr = R(lst, simplify=False); expr # indirect doctest 4*x^4 + O(x^3) + 2*x^2 + x sage: expr._simplify_(); expr @@ -1079,7 +1081,9 @@ def monomial_coefficient(self, monomial): ValueError: Cannot include n with parent Exact Term Monoid n^QQ with coefficients in Rational Field in Asymptotic Ring over Rational Field - > *previous* ValueError: n is not in Growth Group m^QQ. + > *previous* ValueError: Growth n is not valid in + Exact Term Monoid m^QQ with coefficients in Rational Field. + >> *previous* ValueError: n is not in Growth Group m^QQ. Only monomials are allowed:: @@ -3068,8 +3072,10 @@ def map_coefficients(self, f, new_coefficient_ring=None): sage: a.map_coefficients(lambda c: 1/c) Traceback (most recent call last): ... - ValueError: ... is not a coefficient in + ValueError: Cannot create ExactTerm(n^3) since + given coefficient 1/2 is not valid in Exact Term Monoid n^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer """ def mapping(term): T = term.parent().change_parameter( @@ -3078,7 +3084,7 @@ def mapping(term): c = f(term.coefficient) if c.is_zero(): return None - return T(term.growth, c) + return T(term.growth, coefficient=c) else: return T(term.growth) @@ -3922,7 +3928,7 @@ def _element_constructor_(self, data, simplify=True, convert=True): ... ValueError: Polynomial y is not in Asymptotic Ring over Integer Ring - > *previous* ValueError: Growth y is not in + > *previous* ValueError: Growth y is not valid in Exact Term Monoid x^ZZ with coefficients in Integer Ring. >> *previous* ValueError: y is not in Growth Group x^ZZ. @@ -3965,7 +3971,7 @@ def _element_constructor_(self, data, simplify=True, convert=True): ... ValueError: Polynomial a + c is not in Asymptotic Ring over Rational Field - > *previous* ValueError: Growth c is not in + > *previous* ValueError: Growth c is not valid in Exact Term Monoid a^ZZ * b^ZZ with coefficients in Rational Field. >> *previous* ValueError: c is not in Growth Group a^ZZ * b^ZZ. >...> *previous* ValueError: c is not in any of the factors @@ -3983,7 +3989,9 @@ def _element_constructor_(self, data, simplify=True, convert=True): ValueError: Cannot include m^3 with parent Exact Term Monoid m^ZZ with coefficients in Integer Ring in Asymptotic Ring over Rational Field - > *previous* ValueError: m^3 is not in Growth Group n^ZZ. + > *previous* ValueError: Growth m^3 is not valid in + Exact Term Monoid n^ZZ with coefficients in Rational Field. + >> *previous* ValueError: m^3 is not in Growth Group n^ZZ. :: @@ -4539,7 +4547,7 @@ def create_summand(self, type, data=None, **kwds): sage: R.create_summand('O', growth=42*x^2, coefficient=1) Traceback (most recent call last): ... - ValueError: Growth 42*x^2 is not in O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + ValueError: Growth 42*x^2 is not valid in O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. > *previous* ValueError: 42*x^2 is not in Growth Group x^ZZ. :: diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 8061c94c1a4..b7288c2e9e6 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -61,7 +61,7 @@ sage: OT = OTermMonoid(TermMonoid, growth_group=G, coefficient_ring=QQ) sage: ET = ExactTermMonoid(TermMonoid, growth_group=G, coefficient_ring=QQ) sage: ot1 = OT(x); ot2 = OT(x^2) - sage: et1 = ET(x^2, 2) + sage: et1 = ET(x^2, coefficient=2) - Because of the definition of `O`-terms (see :wikipedia:`Big_O_notation`), :class:`OTerm` are able to absorb all @@ -99,7 +99,7 @@ :class:`ExactTerm` if the growth coincides with the growth of this element:: - sage: et1.can_absorb(ET(x^2, 5)) + sage: et1.can_absorb(ET(x^2, coefficient=5)) True sage: any(et1.can_absorb(t) for t in [ot1, ot2]) False @@ -107,16 +107,16 @@ As mentioned above, absorption directly corresponds to addition in this case:: - sage: et1.absorb(ET(x^2, 5)) + sage: et1.absorb(ET(x^2, coefficient=5)) 7*x^2 When adding two exact terms, they might cancel out. For technical reasons, ``None`` is returned in this case:: - sage: ET(x^2, 5).can_absorb(ET(x^2, -5)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^2, coefficient=-5)) True - sage: ET(x^2, 5).absorb(ET(x^2, -5)) is None + sage: ET(x^2, coefficient=5).absorb(ET(x^2, coefficient=-5)) is None True - The abstract base terms :class:`GenericTerm` and @@ -391,6 +391,41 @@ def __init__(self, parent, growth): super(GenericTerm, self).__init__(parent=parent) + def construction(self): + r""" + Return a construction of this term. + + INPUT: + + Nothing. + + OUTPUT: + + A pair ``(cls, kwds)`` such that ``cls(**kwds)`` equals this term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + + sage: T = TermMonoid('O', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a + O(x) + sage: cls, kwds = a.construction(); cls, kwds + (, + {'growth': x, + 'parent': O-Term Monoid x^ZZ with implicit coefficients in Rational Field}) + sage: cls(**kwds) == a + True + + .. SEEALSO:: + + :meth:`TermWithCoefficient.construction`, + :meth:`GenericTermMonoid.from_construction` + """ + return (self.__class__, {'parent': self.parent(), + 'growth': self.growth}) + def _mul_(self, other): r""" Multiplication of this term by another. @@ -668,11 +703,11 @@ def absorb(self, other, check=True): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G_QQ = GrowthGroup('x^QQ'); x = G_QQ.gen() - sage: OT = TermMonoid('O', G_QQ, coefficient_ring=ZZ) + sage: OT = TermMonoid('O', G_QQ, coefficient_ring=QQ) sage: ET = TermMonoid('exact', G_QQ, coefficient_ring=QQ) sage: ot1 = OT(x); ot2 = OT(x^2) - sage: et1 = ET(x, 100); et2 = ET(x^2, 2) - sage: et3 = ET(x^2, 1); et4 = ET(x^2, -2) + sage: et1 = ET(x, coefficient=100); et2 = ET(x^2, coefficient=2) + sage: et3 = ET(x^2, coefficient=1); et4 = ET(x^2, coefficient=-2) `O`-Terms are able to absorb other `O`-terms and exact terms with weaker or equal growth. :: @@ -912,7 +947,7 @@ def _lt_(self, other): (Generic Term with growth x, Generic Term with growth x^2) sage: o1 = OT(x^-1); o2 = OT(x^3); o1, o2 (O(x^(-1)), O(x^3)) - sage: t1 = ET_ZZ(x^2, 5); t2 = ET_QQ(x^3, 2/7); t1, t2 + sage: t1 = ET_ZZ(x^2, coefficient=5); t2 = ET_QQ(x^3, coefficient=2/7); t1, t2 (5*x^2, 2/7*x^3) In order for the comparison to work, the terms have to come from @@ -940,9 +975,9 @@ def _lt_(self, other): sage: t1 <= t2 True - sage: ET_ZZ(x, -5) <= ET_ZZ(x, 42) + sage: ET_ZZ(x, coefficient=-5) <= ET_ZZ(x, coefficient=42) False - sage: ET_ZZ(x, 5) <= ET_ZZ(x, 5) + sage: ET_ZZ(x, coefficient=5) <= ET_ZZ(x, coefficient=5) True :: @@ -951,7 +986,7 @@ def _lt_(self, other): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, QQ) - sage: t1 = ET(x, 5); t2 = ET(x^2, 3); t3 = ET(x^2, 42) + sage: t1 = ET(x, coefficient=5); t2 = ET(x^2, coefficient=3); t3 = ET(x^2, coefficient=42) sage: t1 <= t2 # indirect doctest True sage: t2 <= t1 # indirect doctest @@ -965,7 +1000,7 @@ def _lt_(self, other): TESTS:: - sage: ET(x, -2) <= ET(x, 1) + sage: ET(x, coefficient=-2) <= ET(x, coefficient=1) False :: @@ -1014,7 +1049,7 @@ def _eq_(self, other): (Generic Term with growth x, x, O(x)) sage: e == e^2 # indirect doctest False - sage: e == ET(x,1) # indirect doctest + sage: e == ET(x, coefficient=1) # indirect doctest True sage: o == OT(x^2) # indirect doctest False @@ -1696,7 +1731,7 @@ def _coerce_map_from_(self, S): self.coefficient_ring.has_coerce_map_from(S.coefficient_ring): return True - def _element_constructor_(self, data, coefficient=None, **kwds): + def _element_constructor_(self, data, *args, **kwds): r""" Convert the given object to this term monoid. @@ -1708,7 +1743,7 @@ def _element_constructor_(self, data, coefficient=None, **kwds): - ``coefficient`` -- (default: ``None``) an element of the coefficient ring. - - ``**kwds`` -- keywords for creation of some terms like BTerms. + - ``**kwds`` -- keyword arguments passed on to the term. OUTPUT: @@ -1767,13 +1802,14 @@ def _element_constructor_(self, data, coefficient=None, **kwds): sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid sage: G = GrowthGroup('x^ZZ') sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) - sage: t1 = T(x^2, 5); t1 # indirect doctest + sage: t1 = T(x^2, coefficient=5); t1 # indirect doctest Term with coefficient 5 and growth x^2 TESTS:: - sage: O_ZZ = TermMonoid('O', G_ZZ, QQ) - sage: O_ZZ(x^11) + sage: G = GrowthGroup('x^ZZ') + sage: OT = TermMonoid('O', G, ZZ) + sage: OT(x^11) O(x^11) :: @@ -1797,25 +1833,64 @@ def _element_constructor_(self, data, coefficient=None, **kwds): sage: T_log(log(x)) Term with coefficient 1 and growth log(x) + :: + + sage: OT(G.gen(), coefficient=5, growth=G.gen()) + Traceback (most recent call last): + ... + ValueError: Argument 'growth=x' is ambiguous. + sage: OT(SR(3*x), growth=G.gen()) + Traceback (most recent call last): + ... + ValueError: Argument 'growth=x' is ambiguous. + + :: + + sage: OT(G.gen(), 4) + doctest:warning + ... + DeprecationWarning: Passing 'coefficient' as a positional argument is deprecated; + specify it as keyword argument 'coefficient=...'. + See https://trac.sagemath.org/32215 for details. + O(x) + sage: OT(G.gen(), 4, coefficient=5) + Traceback (most recent call last): + ... + ValueError: Argument 'coefficient=5' is ambiguous. """ + if len(args) > 1: + raise TypeError( + f'GenericTermMonoid._element_constructor_ ' + f'takes one positional argument, ' + f'another positional argument is deprecated, ' + f'but {len(args)+1} were given') + elif len(args) == 1: + from sage.misc.superseded import deprecation + deprecation(32215, + "Passing 'coefficient' as a positional argument is deprecated; " + "specify it as keyword argument 'coefficient=...'.") + if 'coefficient' in kwds: + raise ValueError(f"Argument 'coefficient={kwds['coefficient']}' is ambiguous.") + kwds['coefficient'] = args[0] + if isinstance(data, self.element_class) and data.parent() == self: return data - elif isinstance(data, TermWithCoefficient): - return self._create_element_(data.growth, data.coefficient) elif isinstance(data, GenericTerm): - return self._create_element_(data.growth, None) + return self.from_construction(data.construction(), **kwds) elif isinstance(data, int) and data == 0: raise ValueError('No input specified. Cannot continue ' 'creating an element of %s.' % (self,)) from .misc import combine_exceptions + coefficient = kwds.pop('coefficient', None) if coefficient is not None: - try: - data = self.growth_group(data) - except (ValueError, TypeError) as e: - raise combine_exceptions( - ValueError('Growth %s is not in %s.' % (data, self)), e) - return self._create_element_(data, coefficient, **kwds) + growth = data + if 'growth' in kwds: + raise ValueError(f"Argument 'growth={kwds['growth']}' is ambiguous.") + return self.from_construction((None, + {'growth': growth, + 'coefficient': coefficient}), + **kwds) try: growth, coefficient = self._split_growth_and_coefficient_(data) @@ -1823,37 +1898,287 @@ def _element_constructor_(self, data, coefficient=None, **kwds): raise combine_exceptions( ValueError('%s is not in %s.' % (data, self)), e) - return self._create_element_(growth, coefficient) + if 'growth' in kwds: + raise ValueError(f"Argument 'growth={kwds['growth']}' is ambiguous.") - def _create_element_(self, growth, coefficient): + return self.from_construction((None, + {'growth': growth, + 'coefficient': coefficient}), + **kwds) + + def _validate_growth_or_error_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which ensures that the keyword argument ``growth`` + of the term (in the element construction process) is valid. INPUT: - - ``growth`` -- a growth element. + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``growth`` in ``kwds_construction`` might be changed. + If ``growth`` is not valid, an error is raised. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T(G.gen()) # indirect doctest + O(x) + + :: + + sage: T._validate_growth_or_error_({'growth': G.gen()}) + sage: T._validate_growth_or_error_({'growth': None}) + Traceback (most recent call last): + ... + ValueError: Growth None is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* ValueError: None is not in Growth Group x^ZZ. + sage: T._validate_growth_or_error_({'growth': 7}) + Traceback (most recent call last): + ... + ValueError: Growth 7 is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* ValueError: 7 is not in Growth Group x^ZZ. + + :: - - ``coefficient`` -- an element of the coefficient ring. + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': 'x'} + sage: T._validate_growth_or_error_(kwds) + sage: kwds['growth'].parent() + Growth Group x^ZZ + """ + growth = kwds_construction.get('growth') + try: + growth = self.growth_group(growth) + except (ValueError, TypeError) as e: + growth = kwds_construction['growth'] + from .misc import combine_exceptions + raise combine_exceptions( + ValueError(f'Growth {growth} is not valid in {self}.'), e) + kwds_construction['growth'] = growth + + def _validate_coefficient_or_error_(self, kwds_construction): + r""" + Helper method which ensures that the keyword argument ``coefficient`` + of the term (in the element construction process) is valid. + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - A term. + Nothing, but ``coefficient`` in ``kwds_construction`` might be changed. + If the coefficient is not valid, an error is raised. TESTS:: - sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T(G.gen()) # indirect doctest + O(x) + + :: + + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': None}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4/3}) + Traceback (most recent call last): + ... + ValueError: Cannot create OTerm(x) since + given coefficient 4/3 is not valid in + O-Term Monoid x^ZZ with implicit coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer + + :: + + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': G.gen(), 'coefficient': 4} + sage: T._validate_coefficient_or_error_(kwds) + sage: kwds['coefficient'].parent() + Rational Field + """ + coefficient = kwds_construction.get('coefficient', None) + if coefficient is None: + return + try: + coefficient = self.coefficient_ring(coefficient) + except (TypeError, ValueError) as e: + element_name = self.Element.__name__ + growth = kwds_construction['growth'] + from .misc import combine_exceptions + raise combine_exceptions( + ValueError(f'Cannot create {element_name}({growth}) ' + f'since given coefficient {coefficient} ' + f'is not valid in {self}.'), e) + if 'coefficient' in kwds_construction: + kwds_construction['coefficient'] = coefficient + + def _default_kwds_construction_(self): + r""" + Return the default keyword arguments for the construction of a term. + + INPUT: + + Nothing. + + OUTPUT: + + A dictionary. + + TESTS:: + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('O', G, ZZ) + sage: T._default_kwds_construction_() + {} + sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest + O(x) + """ + return {} - sage: G_ZZ = GrowthGroup('x^ZZ') - sage: T_ZZ = GenericTermMonoid(TermMonoid, G_ZZ, QQ) - sage: T_ZZ(G_ZZ.gen()) # indirect doctest - Generic Term with growth x + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = GenericTermMonoid(TermMonoid, G, QQ) + + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + Traceback (most recent call last): + ... + ValueError: Coefficient 3/2 is not 1, + but GenericTerm Monoid x^ZZ with (implicit) coefficients + in Rational Field does not support coefficients. """ + coefficient = kwds_construction.pop('coefficient', None) if coefficient is not None and coefficient != self.coefficient_ring.one(): raise ValueError('Coefficient %s is not 1, but %s does not ' 'support coefficients.' % (coefficient, self)) - return self.element_class(self, growth) + + def from_construction(self, construction, **kwds_overrides): + r""" + Create a term from the construction of another term. + + INPUT: + + - ``construction`` -- a pair ``(cls, kwds_construction)`` + + - ``kwds_overrides`` -- a dictionary + + OUTPUT: + + A term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermMonoid('O', G, QQ) + sage: o = T.an_element() + + We use a construction directly as input:: + + sage: T.from_construction(o.construction()) + O(x) + + We can override the given data:: + + sage: T.from_construction(o.construction(), growth=x^2) + O(x^2) + + A minimalistic example:: + + sage: T.from_construction((None, {'growth': x})) + O(x) + + .. SEEALSO:: + + :meth:`GenericTerm.construction`, + :meth:`TermWithCoefficient.construction` + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: T = GenericTermMonoid(TermMonoid, G, QQ) + sage: T(G.gen()) # indirect doctest + Generic Term with growth x + + sage: T = TermMonoid('O', G, QQ) + sage: T(G.gen()) # indirect doctest + O(x) + sage: T(G.gen(), SR.var('y')) # indirect doctest + Traceback (most recent call last): + ... + ValueError: Cannot create OTerm(x) since given coefficient y + is not valid in O-Term Monoid x^ZZ with implicit coefficients in + Rational Field. + > *previous* TypeError: unable to convert y to a rational + """ + kwds = {} + kwds.update(self._default_kwds_construction_()) + cls, kwds_construction = construction + kwds.update(kwds_construction) + kwds.update(kwds_overrides) + + self._validate_growth_or_error_(kwds) + self._validate_coefficient_or_error_(kwds) + + self._convert_construction_(kwds) + + try: + del kwds['parent'] + except KeyError: + pass + + return self.element_class(self, **kwds) def _create_element_in_extension_(self, growth, coefficient): r""" @@ -1889,7 +2214,7 @@ def _create_element_in_extension_(self, growth, coefficient): if coefficient is not None else self.coefficient_ring, category=self.category()) - return parent(growth, coefficient) + return parent(growth, coefficient=coefficient) def _split_growth_and_coefficient_(self, data): r""" @@ -2279,7 +2604,7 @@ def can_absorb(self, other): sage: t2.can_absorb(t1) True """ - return other <= self + return self.growth >= other.growth def _absorb_(self, other): r""" @@ -2648,47 +2973,44 @@ class OTermMonoid(GenericTermMonoid): # enable the category framework for elements Element = OTerm - def _create_element_(self, growth, coefficient): + def _convert_construction_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). - INPUT: + This is used e.g. for converting one type of term into another - - ``growth`` -- a growth element. + INPUT: - - ``coefficient`` -- an element of the coefficient ring (will be - ignored since we create an O-Term). + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - An O-term. + Nothing, but ``kwds_construction`` might be changed. TESTS:: sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() sage: T = TermMonoid('O', G, QQ) - sage: T(G.gen()) # indirect doctest - O(x) - sage: T(G.gen(), SR.var('y')) # indirect doctest - Traceback (most recent call last): - ... - ValueError: Cannot create O(x) since given coefficient y - is not valid in O-Term Monoid x^ZZ with implicit coefficients in - Rational Field. - > *previous* TypeError: unable to convert y to a rational + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'growth': x} """ - if coefficient is not None: - try: - self.coefficient_ring(coefficient) - except (TypeError, ValueError) as e: - from .misc import combine_exceptions - raise combine_exceptions( - ValueError('Cannot create O(%s) since given coefficient %s ' - 'is not valid in %s.' % - (growth, coefficient, self)), e) - return self.element_class(self, growth) + try: + del kwds_construction['coefficient'] + except KeyError: + pass def _coerce_map_from_(self, S): r""" @@ -2718,8 +3040,8 @@ def _coerce_map_from_(self, S): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid - sage: G_ZZ = GrowthGroup('x^ZZ'); x_ZZ = G_ZZ.gen() - sage: G_QQ = GrowthGroup('x^QQ'); x_QQ = G_QQ.gen() + sage: G_ZZ = GrowthGroup('x^ZZ') + sage: G_QQ = GrowthGroup('x^QQ') sage: OT_ZZ = TermMonoid('O', G_ZZ, QQ) sage: OT_QQ = TermMonoid('O', G_QQ, QQ) sage: ET = TermMonoid('exact', G_ZZ, ZZ) @@ -2736,9 +3058,14 @@ def _coerce_map_from_(self, S): True sage: ET.has_coerce_map_from(OT_ZZ) # indirect doctest False + sage: OT_ZZ.has_coerce_map_from(OT_QQ) # indirect doctest + False + sage: OT_ZZ.has_coerce_map_from(ET) # indirect doctest + True """ if isinstance(S, (ExactTermMonoid,)): - if self.growth_group.has_coerce_map_from(S.growth_group): + if self.growth_group.has_coerce_map_from(S.growth_group) and \ + self.coefficient_ring.has_coerce_map_from(S.coefficient_ring): return True else: return super(OTermMonoid, self)._coerce_map_from_(S) @@ -2790,9 +3117,9 @@ class TermWithCoefficient(GenericTerm): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: CT_ZZ = TermWithCoefficientMonoid(TermMonoid, G, ZZ) sage: CT_QQ = TermWithCoefficientMonoid(TermMonoid, G, QQ) - sage: CT_ZZ(x^2, 5) + sage: CT_ZZ(x^2, coefficient=5) Term with coefficient 5 and growth x^2 - sage: CT_QQ(x^3, 3/8) + sage: CT_QQ(x^3, coefficient=3/8) Term with coefficient 3/8 and growth x^3 """ @@ -2814,17 +3141,19 @@ def __init__(self, parent, growth, coefficient): The coefficients have to be from the given coefficient ring:: - sage: t = CT_ZZ(x, 1/2) + sage: CT_ZZ(x, 1/2) Traceback (most recent call last): ... - ValueError: 1/2 is not a coefficient in + ValueError: Cannot create TermWithCoefficient(x) + since given coefficient 1/2 is not valid in TermWithCoefficient Monoid x^ZZ with coefficients in Integer Ring. - sage: t = CT_QQ(x, 1/2); t + > *previous* TypeError: no conversion of this rational to integer + sage: CT_QQ(x, coefficient=1/2) Term with coefficient 1/2 and growth x For technical reasons, the coefficient 0 is not allowed:: - sage: t = CT_ZZ(x^42, 0) + sage: CT_ZZ(x^42, 0) Traceback (most recent call last): ... ZeroCoefficientError: Zero coefficient 0 is not allowed in @@ -2835,7 +3164,7 @@ def __init__(self, parent, growth, coefficient): sage: x = SR('x'); x.parent() Symbolic Ring - sage: CT_ZZ(x^42, 42) + sage: CT_ZZ(x^42, coefficient=42) Term with coefficient 42 and growth x^42 """ super(TermWithCoefficient, self).__init__(parent=parent, growth=growth) @@ -2851,6 +3180,43 @@ def __init__(self, parent, growth, coefficient): self.coefficient = coefficient + def construction(self): + r""" + Return a construction of this term. + + INPUT: + + Nothing. + + OUTPUT: + + A pair ``(cls, kwds)`` such that ``cls(**kwds)`` equals this term. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + + sage: T = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) + sage: a = T.an_element(); a + 1/2*x + sage: cls, kwds = a.construction(); cls, kwds + (, + {'coefficient': 1/2, + 'growth': x, + 'parent': Exact Term Monoid x^ZZ with coefficients in Rational Field}) + sage: cls(**kwds) == a + True + + .. SEEALSO:: + + :meth:`GenericTerm.construction`, + :meth:`GenericTermMonoid.from_construction` + """ + cls, kwds = super().construction() + kwds.update({'coefficient': self.coefficient}) + return cls, kwds + def _repr_(self): r""" A representation string for this term with coefficient. @@ -2871,7 +3237,7 @@ def _repr_(self): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) - sage: T(x^2, 5)._repr_() + sage: T(x^2, coefficient=5)._repr_() 'Term with coefficient 5 and growth x^2' """ return 'Term with coefficient %s and growth %s' % \ @@ -2899,7 +3265,7 @@ def _repr_product_(self, latex=False): sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermWithCoefficientMonoid(TermMonoid, G, ZZ) - sage: T(x^2, 5)._repr_product_() + sage: T(x^2, coefficient=5)._repr_product_() '5*x^2' """ if latex: @@ -2963,18 +3329,18 @@ def _mul_(self, other): exact terms (i.e. :class:`ExactTerm`). First, an example for abstract terms:: - sage: t1 = CT(x^2, 2); t2 = CT(x^3, 3) + sage: t1 = CT(x^2, coefficient=2); t2 = CT(x^3, coefficient=3) sage: t1 * t2 Term with coefficient 6 and growth x^5 And now, an example for exact terms:: - sage: t1 = ET(x^2, 2); t2 = ET(x^3, 3) + sage: t1 = ET(x^2, coefficient=2); t2 = ET(x^3, coefficient=3) sage: t1 * t2 6*x^5 """ return self.parent()(self.growth * other.growth, - self.coefficient * other.coefficient) + coefficient=self.coefficient * other.coefficient) def _calculate_pow_(self, exponent): r""" @@ -3110,11 +3476,11 @@ def _eq_(self, other): sage: T = TermWithCoefficientMonoid(TermMonoid, GrowthGroup('x^ZZ'), ZZ) sage: t = T.an_element(); t Term with coefficient 1 and growth x - sage: t == T(x, 1) + sage: t == T(x, coefficient=1) True - sage: t == T(x, 2) + sage: t == T(x, coefficient=2) False - sage: t == T(x^2, 1) + sage: t == T(x^2, coefficient=1) False """ return super(TermWithCoefficient, self)._eq_(other) and \ @@ -3189,30 +3555,129 @@ def _repr_(self): return 'TermWithCoefficient Monoid %s with coefficients in %s' % \ (self.growth_group._repr_short_(), self.coefficient_ring) - def _create_element_(self, growth, coefficient): + def _validate_coefficient_or_error_(self, kwds_construction): r""" - Helper method which creates an element by using the ``element_class``. + Helper method which ensures that the keyword argument ``coefficient`` + of the term (in the element construction process) is valid. INPUT: - - ``growth`` -- a growth element. - - - ``coefficient`` -- an element of the coefficient ring. + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) OUTPUT: - A term. + Nothing, but ``coefficient`` in ``kwds_construction`` might be changed + or an error is raised. TESTS:: sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: G_ZZ = GrowthGroup('x^ZZ') - sage: T_ZZ = TermMonoid('exact', G_ZZ, QQ) - sage: T_ZZ(G_ZZ.gen(), 4/3) # indirect doctest + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('exact', G, ZZ) + sage: T(G.gen(), coefficient=4) # indirect doctest + 4*x + + :: + + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4}) + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': None}) + Traceback (most recent call last): + ... + ValueError: Cannot create ExactTerm(x) since no coefficient is given. + sage: T._validate_coefficient_or_error_( + ....: {'growth': G.gen(), 'coefficient': 4/3}) + Traceback (most recent call last): + ... + ValueError: Cannot create ExactTerm(x) since + given coefficient 4/3 is not valid in + Exact Term Monoid x^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer + + :: + + sage: T = TermMonoid('exact', G, QQ) + sage: T(G.gen(), coefficient=4/3) # indirect doctest 4/3*x """ - return self.element_class(self, growth, coefficient) + coefficient = kwds_construction.get('coefficient', None) + if coefficient is None: + element_name = self.Element.__name__ + growth = kwds_construction['growth'] + raise ValueError(f'Cannot create {element_name}({growth}) ' + f'since no coefficient is given.') + super()._validate_coefficient_or_error_(kwds_construction) + + def _default_kwds_construction_(self): + r""" + Return the default keyword arguments for the construction of a term. + + INPUT: + + Nothing. + + OUTPUT: + + A dictionary. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: T = TermMonoid('exact', G, ZZ) + sage: T._default_kwds_construction_() + {'coefficient': 1} + sage: T.from_construction((None, {'growth': G.gen()})) # indirect doctest + x + """ + defaults = {} + defaults.update(super()._default_kwds_construction_()) + defaults.update({'coefficient': self.coefficient_ring.one()}) + return defaults + + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.term_monoid import TermWithCoefficientMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermWithCoefficientMonoid(TermMonoid, G, QQ) + + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'coefficient': 1, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'coefficient': None, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'coefficient': 3/2, 'growth': x} + """ + pass def _an_element_(self): r""" @@ -3240,7 +3705,7 @@ def _an_element_(self): 1/2*x """ return self(self.growth_group.an_element(), - self.coefficient_ring.an_element()) + coefficient=self.coefficient_ring.an_element()) def some_elements(self): r""" @@ -3268,9 +3733,9 @@ def some_elements(self): z^(-2), -z^2, 2*z^(-1/2), -2*z^(1/2)) """ from sage.misc.mrange import cantor_product - return iter(self(g, c) for g, c in cantor_product( + return (self(g, coefficient=c) for g, c in cantor_product( self.growth_group.some_elements(), - iter(c for c in self.coefficient_ring.some_elements() if c != 0))) + (c for c in self.coefficient_ring.some_elements() if c != 0))) class ExactTerm(TermWithCoefficient): @@ -3298,24 +3763,24 @@ class ExactTerm(TermWithCoefficient): Asymptotic exact terms may be multiplied (with the usual rules applying):: - sage: ET(x^2, 3) * ET(x, -1) + sage: ET(x^2, coefficient=3) * ET(x, coefficient=-1) -3*x^3 - sage: ET(x^0, 4) * ET(x^5, 2) + sage: ET(x^0, coefficient=4) * ET(x^5, coefficient=2) 8*x^5 They may also be multiplied with `O`-terms:: sage: OT = TermMonoid('O', G, QQ) - sage: ET(x^2, 42) * OT(x) + sage: ET(x^2, coefficient=42) * OT(x) O(x^3) Absorption for asymptotic exact terms relates to addition:: - sage: ET(x^2, 5).can_absorb(ET(x^5, 12)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^5, coefficient=12)) False - sage: ET(x^2, 5).can_absorb(ET(x^2, 1)) + sage: ET(x^2, coefficient=5).can_absorb(ET(x^2, coefficient=1)) True - sage: ET(x^2, 5).absorb(ET(x^2, 1)) + sage: ET(x^2, coefficient=5).absorb(ET(x^2, coefficient=1)) 6*x^2 Note that, as for technical reasons, `0` is not allowed as a @@ -3323,9 +3788,9 @@ class ExactTerm(TermWithCoefficient): is returned if two asymptotic exact terms cancel out each other during absorption:: - sage: ET(x^2, 42).can_absorb(ET(x^2, -42)) + sage: ET(x^2, coefficient=42).can_absorb(ET(x^2, coefficient=-42)) True - sage: ET(x^2, 42).absorb(ET(x^2, -42)) is None + sage: ET(x^2, coefficient=42).absorb(ET(x^2, coefficient=-42)) is None True Exact terms can also be created by converting monomials with @@ -3357,16 +3822,16 @@ def _repr_(self, latex=False): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, ZZ) - sage: ET(x^2, 2) + sage: ET(x^2, coefficient=2) 2*x^2 TESTS:: - sage: ET(x^2, 1) + sage: ET(x^2, coefficient=1) x^2 - sage: ET(x^2, -1) + sage: ET(x^2, coefficient=-1) -x^2 - sage: ET(x^0, 42) + sage: ET(x^0, coefficient=42) 42 Check that :trac:`19576` is fixed:: @@ -3395,16 +3860,16 @@ def _latex_(self): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: ET = TermMonoid('exact', G, ZZ) - sage: latex(ET(x^2, 2)) + sage: latex(ET(x^2, coefficient=2)) 2 x^{2} :: - sage: latex(ET(x^2, 1)) + sage: latex(ET(x^2, coefficient=1)) x^{2} - sage: latex(ET(x^2, -1)) + sage: latex(ET(x^2, coefficient=-1)) -x^{2} - sage: latex(ET(x^0, 42)) + sage: latex(ET(x^0, coefficient=42)) 42 :: @@ -3439,9 +3904,9 @@ def __invert__(self): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermMonoid('exact', G, ZZ) - sage: ~T(x, 2) # indirect doctest + sage: ~T(x, coefficient=2) # indirect doctest 1/2*x^(-1) - sage: (~T(x, 2)).parent() + sage: (~T(x, coefficient=2)).parent() Exact Term Monoid x^ZZ with coefficients in Rational Field """ try: @@ -3506,7 +3971,7 @@ def can_absorb(self, other): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), ZZ) - sage: t1 = ET(x^21, 1); t2 = ET(x^21, 2); t3 = ET(x^42, 1) + sage: t1 = ET(x^21, coefficient=1); t2 = ET(x^21, coefficient=2); t3 = ET(x^42, coefficient=1) sage: t1.can_absorb(t2) True sage: t2.can_absorb(t1) @@ -3547,7 +4012,7 @@ def _absorb_(self, other): Asymptotic exact terms can absorb other asymptotic exact terms with the same growth:: - sage: et1 = ET(x^2, 2); et2 = ET(x^2, -2) + sage: et1 = ET(x^2, coefficient=2); et2 = ET(x^2, coefficient=-2) sage: et1.absorb(et1) 4*x^2 sage: et1.absorb(et2) is None @@ -3555,7 +4020,7 @@ def _absorb_(self, other): If the growth differs, an ``ArithmeticException`` is raised:: - sage: ET(x^5, 1).absorb(et1) + sage: ET(x^5, coefficient=1).absorb(et1) Traceback (most recent call last): ... ArithmeticError: x^5 cannot absorb 2*x^2 @@ -3564,7 +4029,7 @@ def _absorb_(self, other): if coeff_new.is_zero(): return None else: - return self.parent()(self.growth, coeff_new) + return self.parent()(self.growth, coefficient=coeff_new) def log_term(self, base=None, locals=None): r""" @@ -3949,6 +4414,42 @@ class ExactTermMonoid(TermWithCoefficientMonoid): # enable the category framework for elements Element = ExactTerm + def _convert_construction_(self, kwds_construction): + r""" + Helper method which converts the given keyword arguments + suitable for the term (in the element construction process). + + This is used e.g. for converting one type of term into another + + INPUT: + + - ``kwds_construction`` -- a dictionary representing + the keyword arguments of a term in its construction + (see also :meth:`GenericTerm.construction` and + :meth:`TermWithCoefficient.construction`) + + OUTPUT: + + Nothing, but ``kwds_construction`` might be changed. + + TESTS:: + + sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: G = GrowthGroup('x^ZZ') + sage: x = G.gen() + sage: T = TermMonoid('exact', G, QQ) + sage: kwds = {'growth': x}; T._convert_construction_(kwds); kwds + {'growth': x} + sage: kwds = {'growth': x, 'coefficient': QQ(1)}; T._convert_construction_(kwds); kwds + {'coefficient': 1, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': None}; T._convert_construction_(kwds); kwds + {'coefficient': None, 'growth': x} + sage: kwds = {'growth': x, 'coefficient': 3/2}; T._convert_construction_(kwds); kwds + {'coefficient': 3/2, 'growth': x} + """ + pass + def _repr_(self): r""" A representation string for this exact term monoid. @@ -4027,7 +4528,7 @@ class BTerm(TermWithCoefficient): B(42*x^3*y^2, x >= 10, y >= 20) """ - def __init__(self, parent, growth, coefficient, valid_from): + def __init__(self, parent, growth, valid_from, **kwds): r""" See :class:`BTerm` for more information. @@ -4039,40 +4540,41 @@ def __init__(self, parent, growth, coefficient, valid_from): sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: BT_QQ = TermMonoid('B', G, QQ) - sage: BT_QQ(x^3, 3, valid_from={'m': 20}) + sage: BT_QQ(x^3, coefficient=3, valid_from={'m': 20}) Traceback (most recent call last): ... ValueError: B-Term has valid_from variables defined which do not occur in the term. - sage: BT_QQ(x^3, 0, valid_from={'x': 20}) + sage: BT_QQ(x^3, coefficient=0, valid_from={'x': 20}) Traceback (most recent call last): ... ZeroCoefficientError: Zero coefficient 0 is not allowed in B-Term Monoid x^ZZ with coefficients in Rational Field. sage: BT_ZZ = TermMonoid('B', G, ZZ) - sage: BT_ZZ(x, 1/2, valid_from={'x': 20}) + sage: BT_ZZ(x, coefficient=1/2, valid_from={'x': 20}) Traceback (most recent call last): ... - ValueError: 1/2 is not a coefficient in B-Term Monoid x^ZZ with - coefficients in Integer Ring. + ValueError: Cannot create BTerm(x) since given coefficient 1/2 is not + valid in B-Term Monoid x^ZZ with coefficients in Integer Ring. + > *previous* TypeError: no conversion of this rational to integer sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') sage: BT_ZZ = TermMonoid('B', B, ZZ) - sage: BT_ZZ(x^3, 42, valid_from={'x': 10}) + sage: BT_ZZ(x^3, coefficient=42, valid_from={'x': 10}) B(42*x^3, x >= 10) - sage: BT_ZZ(x^3, 42, valid_from={'x': 10, 'y': 20}) + sage: BT_ZZ(x^3, coefficient=42, valid_from={'x': 10, 'y': 20}) B(42*x^3, x >= 10, y >= 20) - sage: BT_ZZ(x^3*y^2, 42, valid_from={'x': 10}) + sage: BT_ZZ(x^3*y^2, coefficient=42, valid_from={'x': 10}) Traceback (most recent call last): ValueError: B-Term has not defined all variables which occur in the term in valid_from. - sage: BT_ZZ(x^3, 42, valid_from={'x': 10, 'z': 20}) + sage: BT_ZZ(x^3, coefficient=42, valid_from={'x': 10, 'z': 20}) Traceback (most recent call last): ... ValueError: B-Term has valid_from variables defined which do not occur in the term. """ - super().__init__(parent=parent, growth=growth, coefficient=coefficient) - self.coefficient = coefficient + super().__init__(parent=parent, growth=growth, coefficient=kwds['coefficient']) + self.coefficient = kwds['coefficient'] for variable_name in valid_from.keys(): if variable_name not in parent.growth_group.variable_names(): raise ValueError('B-Term has valid_from variables defined which do not occur in the term.') @@ -4101,12 +4603,12 @@ def _repr_(self, latex=False): sage: G = MonomialGrowthGroup(ZZ, 'x'); sage: BT_QQ = TermMonoid('B', G, QQ) - sage: BT_QQ(x^3, 3, valid_from={'x': 20}) + sage: BT_QQ(x^3, coefficient=3, valid_from={'x': 20}) B(3*x^3, x >= 20) sage: B = GrowthGroup('x^ZZ * y^ZZ'); sage: x, y = B('x'), B('y') sage: BT_ZZ = TermMonoid('B', B, ZZ) - sage: BT_ZZ(x^2, 4, valid_from={'x': 10, 'y': 15}) + sage: BT_ZZ(x^2, coefficient=4, valid_from={'x': 10, 'y': 15}) B(4*x^2, x >= 10, y >= 15) """ if latex: @@ -4132,11 +4634,11 @@ def _latex_(self): sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: G = GrowthGroup('x^ZZ'); x = G.gen() sage: T = TermMonoid('B', G, QQ) - sage: latex(T(x, 5, valid_from={'x': 3})) + sage: latex(T(x, coefficient=5, valid_from={'x': 3})) B_{x \ge 3}\left(5 x\right) - sage: latex(T(x^2, 3, valid_from={'x': 5})) + sage: latex(T(x^2, coefficient=3, valid_from={'x': 5})) B_{x \ge 5}\left(3 x^{2}\right) - sage: latex(T(x^3, 6, valid_from={'x': 10})) + sage: latex(T(x^3, coefficient=6, valid_from={'x': 10})) B_{x \ge 10}\left(6 x^{3}\right) """ return self._repr_(latex=True) @@ -4166,9 +4668,9 @@ def can_absorb(self, other): sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: from sage.rings.asymptotic.term_monoid import DefaultTermMonoidFactory as TermMonoid sage: BT = TermMonoid('B', GrowthGroup('x^ZZ'), QQ) - sage: t1 = BT(x^3, 3, valid_from={'x': 20}) - sage: t2 = BT(x^2, 1, valid_from={'x': 10}) - sage: t3 = BT(x^3, 10, valid_from={'x': 10}) + sage: t1 = BT(x^3, coefficient=3, valid_from={'x': 20}) + sage: t2 = BT(x^2, coefficient=1, valid_from={'x': 10}) + sage: t3 = BT(x^3, coefficient=10, valid_from={'x': 10}) sage: t1.can_absorb(t2) True sage: t2.can_absorb(t1) @@ -4178,7 +4680,7 @@ def can_absorb(self, other): sage: t3.can_absorb(t1) True sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) - sage: t4 = ET(x^3, 5) + sage: t4 = ET(x^3, coefficient=5) sage: t1.can_absorb(t4) True @@ -4189,9 +4691,9 @@ def can_absorb(self, other): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = TermMonoid('B', G, QQ) - sage: t1 = BT(x, 3, valid_from={'x': 20}) - sage: t2 = BT(x^3, 5, valid_from={'x': 5}) - sage: t3 = BT(x^3, 10, valid_from={'x': 10}) + sage: t1 = BT(x, coefficient=3, valid_from={'x': 20}) + sage: t2 = BT(x^3, coefficient=5, valid_from={'x': 5}) + sage: t3 = BT(x^3, coefficient=10, valid_from={'x': 10}) sage: t2.absorb(t1) B(2003/400*x^3, x >= 20) sage: t2.absorb(t3) @@ -4219,7 +4721,7 @@ def _absorb_(self, other): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = TermMonoid('B', G, QQ) - sage: t1 = BT(x^3, 4, valid_from={'x': 10}); t2 = BT(x, 5, valid_from={'x': 20}) + sage: t1 = BT(x^3, coefficient=4, valid_from={'x': 10}); t2 = BT(x, coefficient=5, valid_from={'x': 20}) sage: t1 B(4*x^3, x >= 10) sage: t1.can_absorb(t2) @@ -4231,7 +4733,7 @@ def _absorb_(self, other): ... ArithmeticError: B(5*x, x >= 20) cannot absorb B(4*x^3, x >= 10) sage: ET = TermMonoid('exact', GrowthGroup('x^ZZ'), QQ) - sage: t4 = ET(x^3, 5) + sage: t4 = ET(x^3, coefficient=5) sage: t1.absorb(t4) # not tested, see #32229 """ if not (self.growth >= other.growth): @@ -4247,7 +4749,7 @@ def _absorb_(self, other): valid_from_new[variable_name] = (other.valid_from[variable_name]) q = self.growth / other.growth coeff_new = self.coefficient + (other.coefficient / q._find_minimum_(valid_from_new)) - return self.parent()(self.growth, coeff_new, valid_from=valid_from_new) + return self.parent()(self.growth, valid_from=valid_from_new, coefficient=coeff_new) class BTermMonoid(TermWithCoefficientMonoid): @@ -4332,9 +4834,9 @@ def _create_element_(self, growth, coefficient, valid_from): sage: G = MonomialGrowthGroup(ZZ, 'x') sage: BT = TermMonoid('B', G, QQ) - sage: BT(x^3, 4, valid_from={'x': 10}) + sage: BT(x^3, coefficient=4, valid_from={'x': 10}) B(4*x^3, x >= 10) - sage: BT(x^3, 4, valid_from=10) + sage: BT(x^3, coefficient=4, valid_from=10) Traceback (most recent call last): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute 'keys' diff --git a/src/sage/rings/number_field/number_field_base.pyx b/src/sage/rings/number_field/number_field_base.pyx index ce59b8fae3e..26d09cf9ed3 100644 --- a/src/sage/rings/number_field/number_field_base.pyx +++ b/src/sage/rings/number_field/number_field_base.pyx @@ -361,8 +361,16 @@ cdef class NumberField(Field): if self._gen_approx is not None or self._embedding is None: return - from sage.rings.qqbar import AA - from sage.rings.real_lazy import RLF + try: + from sage.rings.qqbar import AA + except ImportError: + AA = None + + try: + from sage.rings.real_lazy import RLF + except ImportError: + RLF = None + codomain = self._embedding.codomain() if codomain is AA or codomain is RLF: self._gen_approx = [] diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 89f50519173..85fcac7b997 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -353,7 +353,11 @@ cdef class RealDoubleField_class(sage.rings.abc.RealDoubleField): return ToRDF(S) from .rational_field import QQ - from .real_lazy import RLF + try: + from .real_lazy import RLF + except ImportError: + RLF = None + if S is ZZ or S is QQ or S is RLF: return ToRDF(S) @@ -369,10 +373,14 @@ cdef class RealDoubleField_class(sage.rings.abc.RealDoubleField): else: return None - from .real_mpfr import RR - connecting = RR._internal_coerce_map_from(S) - if connecting is not None: - return ToRDF(RR) * connecting + try: + from .real_mpfr import RR + except ImportError: + pass + else: + connecting = RR._internal_coerce_map_from(S) + if connecting is not None: + return ToRDF(RR) * connecting def _magma_init_(self, magma): r""" diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index b0b674a0bde..4ac3bd866db 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1516,7 +1516,7 @@ def analytic_rank(self, algorithm="pari", leading_coefficient=False): sage: EllipticCurve([1234567,89101112]).analytic_rank(algorithm='rubinstein') Traceback (most recent call last): ... - RuntimeError: unable to compute analytic rank using rubinstein algorithm (unable to convert ' 6.19283e+19 and is too large' to an integer) + RuntimeError: unable to compute analytic rank using rubinstein algorithm (unable to convert ' 6.19283... and is too large' to an integer) sage: EllipticCurve([1234567,89101112]).analytic_rank(algorithm='sympow') Traceback (most recent call last): ... diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 03af6f1fd30..0a91407c850 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -400,8 +400,22 @@ def twist_values(self, s, dmin, dmax): sage: E = EllipticCurve('37a') sage: vals = E.lseries().twist_values(1, -12, -4) - sage: vals # abs tol 1e-15 - [(-11, 1.47824342), (-8, 8.9590946e-18), (-7, 1.85307619), (-4, 2.45138938)] + sage: vals[0][0] + -11 + sage: vals[0][1] # abs tol 1e-8 + 1.47824342 + 0.0*I + sage: vals[1][0] + -8 + sage: vals[1][1] # abs tol 1e-8 + 0.0 + 0.0*I + sage: vals[2][0] + -7 + sage: vals[2][1] # abs tol 1e-8 + 1.85307619 + 0.0*I + sage: vals[3][0] + -4 + sage: vals[3][1] # abs tol 1e-8 + 2.45138938 + 0.0*I sage: F = E.quadratic_twist(-8) sage: F.rank() 1 diff --git a/src/sage/schemes/plane_conics/con_number_field.py b/src/sage/schemes/plane_conics/con_number_field.py index fdd9e2c02ad..f055ceb8ebd 100644 --- a/src/sage/schemes/plane_conics/con_number_field.py +++ b/src/sage/schemes/plane_conics/con_number_field.py @@ -21,8 +21,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.rings.all import (AA, RLF, PolynomialRing) from sage.rings.rational_field import is_RationalField +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from .con_field import ProjectiveConic_field @@ -343,6 +343,9 @@ def is_locally_solvable(self, p): if self._local_obstruction is None: from sage.categories.map import Map from sage.categories.all import Rings + from sage.rings.qqbar import AA + from sage.rings.real_lazy import RLF + if not (isinstance(p, Map) and p.category_for().is_subcategory(Rings())) or p.codomain() is AA or p.codomain() is RLF: self._local_obstruction = p return False @@ -387,6 +390,7 @@ def local_obstructions(self, finite=True, infinite=True, read_cache=True): if read_cache and self._infinite_obstructions is not None: obs0 = self._infinite_obstructions else: + from sage.rings.qqbar import AA for b in B.embeddings(AA): if not self.is_locally_solvable(b): obs0.append(b) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index e00e455843d..da46ac22970 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -77,8 +77,6 @@ import sage.rings.abc from sage.rings.integer import Integer from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField_generic -from sage.rings.complex_mpfr import ComplexField_class -from sage.rings.complex_interval_field import ComplexIntervalField_class from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.finite_rings.finite_field_constructor import is_PrimeFiniteField from sage.rings.finite_rings.finite_field_constructor import GF @@ -89,8 +87,6 @@ from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics from sage.rings.quotient_ring import QuotientRing_generic from sage.rings.rational_field import QQ -from sage.rings.real_mpfr import RealField_class -from sage.rings.real_mpfi import RealIntervalField_class from sage.schemes.generic.morphism import SchemeMorphism_polynomial diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index 13485e71c4b..679978157fd 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -34,8 +34,11 @@ Here is the list of acceptable features:: - sage: maxima('features') - [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued] + sage: ", ".join(map(str, maxima("features")._sage_())) + 'integer, noninteger, even, odd, rational, irrational, real, imaginary, + complex, analytic, increasing, decreasing, oddfun, evenfun, posfun, + constant, commutative, lassociative, rassociative, symmetric, + antisymmetric, integervalued' Set positive domain using a relation:: @@ -80,6 +83,7 @@ _valid_feature_strings = set() + class GenericDeclaration(UniqueRepresentation): """ This class represents generic assumptions, such as a variable being @@ -110,8 +114,11 @@ class GenericDeclaration(UniqueRepresentation): Here is the list of acceptable features:: - sage: maxima('features') - [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued] + sage: ", ".join(map(str, maxima("features")._sage_())) + 'integer, noninteger, even, odd, rational, irrational, real, imaginary, + complex, analytic, increasing, decreasing, oddfun, evenfun, posfun, + constant, commutative, lassociative, rassociative, symmetric, + antisymmetric, integervalued' Test unique representation behavior:: @@ -148,8 +155,11 @@ def __init__(self, var, assumption): Here is the list of acceptable features:: - sage: maxima('features') - [integer,noninteger,even,odd,rational,irrational,real,imaginary,complex,analytic,increasing,decreasing,oddfun,evenfun,posfun,constant,commutative,lassociative,rassociative,symmetric,antisymmetric,integervalued] + sage: ", ".join(map(str, maxima("features")._sage_())) + 'integer, noninteger, even, odd, rational, irrational, real, + imaginary, complex, analytic, increasing, decreasing, oddfun, + evenfun, posfun, constant, commutative, lassociative, rassociative, + symmetric, antisymmetric, integervalued' """ self._var = var self._assumption = assumption @@ -915,14 +925,13 @@ class assuming: Traceback (most recent call last): ... ValueError: Assumption is redundant - + sage: with assuming(x < -1): "I won't see this" Traceback (most recent call last): ... ValueError: Assumption is inconsistent - """ - def __init__(self,*args, **kwds): + def __init__(self, *args, **kwds): r""" EXAMPLES:: diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 3276af869ed..30cb1422fbe 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -525,7 +525,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=exp(x)); a D[0](f)(e^x) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=%e^_SAGE_VAR_x) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = %e^_SAGE_VAR_x) sage: bool(b.sage() == a) True @@ -534,7 +534,7 @@ def derivative(self, ex, operator): sage: a = df.subs(x=4); a D[0](f)(4) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) + %at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4) sage: bool(b.sage() == a) True @@ -554,7 +554,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4); a D[0](f)(4, y) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR__symbol0,_SAGE_VAR_y),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) + %at('diff('f(_SAGE_VAR__symbol0,_SAGE_VAR_y),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4) sage: bool(b.sage() == a) True @@ -563,7 +563,7 @@ def derivative(self, ex, operator): sage: a = f_x.subs(x=4).subs(y=8); a D[0](f)(4, 8) sage: b = maxima(a); b - %at('diff('f(_SAGE_VAR__symbol0,8),_SAGE_VAR__symbol0,1),_SAGE_VAR__symbol0=4) + %at('diff('f(_SAGE_VAR__symbol0,8),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4) sage: bool(b.sage() == a) True diff --git a/src/sage/version.py b/src/sage/version.py index 60df61416a0..1e5a25ab935 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '9.5.beta7' -date = '2021-11-18' -banner = 'SageMath version 9.5.beta7, Release Date: 2021-11-18' +version = '9.5.beta8' +date = '2021-12-12' +banner = 'SageMath version 9.5.beta8, Release Date: 2021-12-12' diff --git a/src/sage_docbuild/__init__.py b/src/sage_docbuild/__init__.py index f27c6cf086e..8a5c1a19d24 100644 --- a/src/sage_docbuild/__init__.py +++ b/src/sage_docbuild/__init__.py @@ -38,7 +38,7 @@ # **************************************************************************** import logging -import optparse +import argparse import os import pickle import re @@ -49,8 +49,6 @@ import types import warnings -logger = logging.getLogger(__name__) - import sphinx.util.console import sphinx.ext.intersphinx @@ -59,10 +57,15 @@ from sage.misc.misc import sage_makedirs from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC, DOT_SAGE -from .build_options import (LANGUAGES, SPHINXOPTS, PAPER, OMIT, - PAPEROPTS, ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, +from .build_options import (LANGUAGES, SPHINXOPTS, OMIT, + ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, INCREMENTAL_BUILD, ABORT_ON_ERROR) +from .utils import build_many as _build_many + +logger = logging.getLogger(__name__) + + ########################################## # Parallel Building Ref Manual # ########################################## @@ -77,6 +80,7 @@ def build_ref_doc(args): kwds['use_multidoc_inventory'] = False getattr(ReferenceSubBuilder(doc, lang), format)(*args, **kwds) + ########################################## # Builders # ########################################## @@ -91,7 +95,7 @@ def builder_helper(type): Check that :trac:`25161` has been resolved:: sage: from sage_docbuild import DocBuilder, setup_parser - sage: DocBuilder._options = setup_parser().parse_args([])[0] # builder_helper needs _options to be set + sage: DocBuilder._options = setup_parser().parse_args([]) # builder_helper needs _options to be set sage: import sage_docbuild.sphinxbuild sage: def raiseBaseException(): @@ -121,7 +125,7 @@ def f(self, *args, **kwds): else: options += ' -D multidoc_first_pass=1' - build_command = '-b %s -d %s %s %s %s'%(type, self._doctrees_dir(), + build_command = '-b %s -d %s %s %s %s' % (type, self._doctrees_dir(), options, self.dir, output_dir) logger.debug(build_command) @@ -139,7 +143,7 @@ def f(self, *args, **kwds): # regular Exception. Otherwise multiprocessing.Pool.get hangs, see # #25161 if ABORT_ON_ERROR: - raise Exception("Non-exception during docbuild: %s"%(e,), e) + raise Exception("Non-exception during docbuild: %s" % (e,), e) if "/latex" in output_dir: logger.warning("LaTeX file written to {}".format(output_dir)) @@ -218,9 +222,9 @@ def _output_formats(self): ['changes', 'html', 'htmlhelp', 'inventory', 'json', 'latex', 'linkcheck', 'pickle', 'web'] """ - #Go through all the attributes of self and check to - #see which ones have an 'is_output_format' attribute. These - #are the ones created with builder_helper. + # Go through all the attributes of self and check to + # see which ones have an 'is_output_format' attribute. These + # are the ones created with builder_helper. output_formats = [] for attr in dir(self): if hasattr(getattr(self, attr), 'is_output_format'): @@ -262,8 +266,8 @@ def pdf(self): error_message = "failed to run $MAKE %s in %s" command = 'all-pdf' - if subprocess.call(make_target%(tex_dir, command, pdf_dir), shell=True): - raise RuntimeError(error_message%(command, tex_dir)) + if subprocess.call(make_target % (tex_dir, command, pdf_dir), shell=True): + raise RuntimeError(error_message % (command, tex_dir)) logger.warning("Build finished. The built documents can be found in %s", pdf_dir) def clean(self, *args): @@ -284,8 +288,6 @@ def clean(self, *args): inventory = builder_helper('inventory') -from .utils import build_many as _build_many - def build_many(target, args, processes=None): """ Thin wrapper around `sage_docbuild.utils.build_many` which uses the @@ -295,10 +297,11 @@ def build_many(target, args, processes=None): processes = NUM_THREADS try: _build_many(target, args, processes=processes) - except BaseException as exc: + except BaseException: if ABORT_ON_ERROR: raise + ########################################## # Parallel Building Ref Manual # ########################################## @@ -358,7 +361,7 @@ def _wrapper(self, name, *args, **kwds): build_other_doc(target) else: build_many(build_other_doc, L) - logger.warning("Elapsed time: %.1f seconds."%(time.time()-start)) + logger.warning("Elapsed time: %.1f seconds." % (time.time() - start)) logger.warning("Done building the documentation!") def get_all_documents(self): @@ -380,7 +383,7 @@ def get_all_documents(self): for lang in LANGUAGES: for document in os.listdir(os.path.join(SAGE_DOC_SRC, lang)): if (document not in OMIT - and os.path.isdir(os.path.join(SAGE_DOC_SRC, lang, document))): + and os.path.isdir(os.path.join(SAGE_DOC_SRC, lang, document))): documents.append(os.path.join(lang, document)) # Ensure that the reference guide is compiled first so that links from @@ -445,7 +448,7 @@ def create_html_redirects(self): # Walk through all of the files in the sage_directory for dirpath, dirnames, filenames in os.walk(sage_directory): # a string like reference/algebras/sage/algebras - short_path = dirpath[len(path)+1:] + short_path = dirpath[len(path) + 1:] # a string like sage/algebras shorter_path = os.path.join(*short_path.split(os.sep)[2:]) @@ -467,14 +470,13 @@ def create_html_redirects(self): levels_up = len(shorter_path.split(os.sep)) # the relative url that we will redirect to - redirect_url = "/".join(['..']*levels_up + [document_name, shorter_path, filename]) + redirect_url = "/".join(['..'] * levels_up + [document_name, shorter_path, filename]) # write the html file which performs the redirect with open(redirect_filename, 'w') as f: print(redirect_filename) f.write(html_template % redirect_url) - def clean(self): """ When we clean the output for the website index, we need to @@ -575,7 +577,6 @@ def _wrapper(self, format, *args, **kwds): Builds reference manuals: build the top-level document and its components. """ - refdir = self._refdir() logger.info('Building bibliography') self._build_bibliography(format, *args, **kwds) logger.info('Bibliography finished, building dependent manuals') @@ -616,7 +617,7 @@ def get_all_documents(self, refdir): n = len(os.listdir(directory)) documents.append((-n, os.path.join(self.name, doc))) - return [ doc[1] for doc in sorted(documents) ] + return [doc[1] for doc in sorted(documents)] class ReferenceTopBuilder(DocBuilder): @@ -707,7 +708,7 @@ def pdf(self): rst = re.sub(r'`([^`\n]*)`__.*\n\n__ (.*)', r'\1.', rst) rst = re.sub(r'`([^<\n]*)\s+<(.*)>`_', - r'\1', rst) + r'\1', rst) rst = re.sub(r':doc:`([^<]*?)\s+<(.*)/index>`', r'\1 ', rst) # Body: add paragraph

markup. @@ -729,7 +730,7 @@ def pdf(self): # now write the file. with open(os.path.join(output_dir, 'index.html'), 'w') as new_index: new_index.write(html[:html_end_preamble]) - new_index.write('

Sage Reference Manual (PDF version)'+ '

') + new_index.write('

Sage Reference Manual (PDF version)

') new_index.write(rst_body) new_index.write('
    ') new_index.write(rst_toc) @@ -788,7 +789,7 @@ def _wrapper(self, build_type, *args, **kwds): force = False try: if (cache['option_inherited'] != self._options.inherited or - cache['option_underscore'] != self._options.underscore): + cache['option_underscore'] != self._options.underscore): logger.info("Detected change(s) in inherited and/or underscored members option(s).") force = True except KeyError: @@ -863,9 +864,9 @@ def get_sphinx_environment(self): """ Returns the Sphinx environment for this project. """ - from sphinx.environment import BuildEnvironment class FakeConfig(object): values = tuple() + class FakeApp(object): def __init__(self, dir): self.srcdir = dir @@ -1008,13 +1009,14 @@ def get_new_and_updated_modules(self): module_filename = sys.modules[module_name].__file__ if (module_filename.endswith('.pyc') or module_filename.endswith('.pyo')): source_filename = module_filename[:-1] - if (os.path.exists(source_filename)): module_filename = source_filename + if (os.path.exists(source_filename)): + module_filename = source_filename newtime = os.path.getmtime(module_filename) if newtime > mtime: updated_modules.append(module_name) yield module_name - else: # keep good old module + else: # keep good old module old_modules.append(module_name) removed_modules = [] @@ -1024,9 +1026,9 @@ def get_new_and_updated_modules(self): if not (module_name in old_modules or module_name in updated_modules): try: os.remove(os.path.join(self.dir, docname) + '.rst') - except OSError: # already removed + except OSError: # already removed pass - logger.debug("Deleted auto-generated reST file %s".format(docname)) + logger.debug("Deleted auto-generated reST file {}".format(docname)) removed_modules.append(module_name) logger.info("Found %d new modules", len(new_modules)) @@ -1061,7 +1063,7 @@ def get_module_docstring_title(self, module_name): """ Returns the title of the module from its docstring. """ - #Try to import the module + # Try to import the module try: __import__(module_name) except ImportError as err: @@ -1069,15 +1071,15 @@ def get_module_docstring_title(self, module_name): return "UNABLE TO IMPORT MODULE" module = sys.modules[module_name] - #Get the docstring + # Get the docstring doc = module.__doc__ if doc is None: doc = module.doc if hasattr(module, 'doc') else "" - #Extract the title + # Extract the title i = doc.find('\n') if i != -1: - return doc[i+1:].lstrip().splitlines()[0] + return doc[i + 1:].lstrip().splitlines()[0] else: return doc @@ -1091,7 +1093,7 @@ def auto_rest_filename(self, module_name): sage: ReferenceSubBuilder("reference").auto_rest_filename("sage.combinat.partition") '.../doc/en/reference/sage/combinat/partition.rst' """ - return self.dir + os.path.sep + module_name.replace('.',os.path.sep) + '.rst' + return self.dir + os.path.sep + module_name.replace('.', os.path.sep) + '.rst' def write_auto_rest_file(self, module_name): """ @@ -1112,9 +1114,9 @@ def write_auto_rest_file(self, module_name): # Don't doctest the autogenerated file. outfile.write(".. nodoctest\n\n") # Now write the actual content. - outfile.write(".. _%s:\n\n"%(module_name.replace(".__init__",""))) + outfile.write(".. _%s:\n\n" % (module_name.replace(".__init__", ""))) outfile.write(title + '\n') - outfile.write('='*len(title) + "\n\n") + outfile.write('=' * len(title) + "\n\n") outfile.write('.. This file has been autogenerated.\n\n') inherited = ':inherited-members:' if self._options.inherited else '' @@ -1145,7 +1147,7 @@ def get_unincluded_modules(self): Returns an iterator for all the modules in the Sage library which are not included in the reference manual. """ - #Make a dictionary of the included modules + # Make a dictionary of the included modules included_modules = {} for module_name in self.get_all_included_modules(): included_modules[module_name] = True @@ -1159,12 +1161,12 @@ def get_unincluded_modules(self): path = os.path.join(directory, filename) - #Create the module name + # Create the module name module_name = path[len(base_path):].replace(os.path.sep, '.') module_name = 'sage' + module_name module_name = module_name[:-4] if module_name.endswith('pyx') else module_name[:-3] - #Exclude some ones -- we don't want init the manual + # Exclude some ones -- we don't want init the manual if module_name.endswith('__init__') or module_name.endswith('all'): continue @@ -1336,10 +1338,11 @@ def get_builder(name): elif name in get_documents() or name in AllBuilder().get_all_documents(): return DocBuilder(name) else: - print("'%s' is not a recognized document. Type 'sage --docbuild -D' for a list"%name) + print("'%s' is not a recognized document. Type 'sage --docbuild -D' for a list" % name) print("of documents, or 'sage --docbuild --help' for more help.") sys.exit(1) + def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): """ Utility function that formats a list as a simple table and returns @@ -1363,6 +1366,7 @@ def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): s += "\n" return s + def help_usage(s="", compact=False): """ Appends and returns a brief usage message for the Sage @@ -1374,38 +1378,34 @@ def help_usage(s="", compact=False): s += "\n" return s + def help_description(s="", compact=False): """ Appends and returns a brief description of the Sage documentation - builder. If 'compact' is False, the function adds a final newline + builder. If 'compact' is ``False``, the function adds a final newline character. """ - s += "Build or return information about Sage documentation.\n\n" - s += " DOCUMENT name of the document to build\n" - s += " FORMAT document output format\n" - s += " COMMAND document-specific command\n\n" - s += "Note that DOCUMENT may have the form 'file=/path/to/FILE',\n" - s += "which builds the documentation for the specified file.\n\n" - s += "A DOCUMENT and either a FORMAT or a COMMAND are required,\n" - s += "unless a list of one or more of these is requested." + s += "Build or return information about Sage documentation. " + s += "A DOCUMENT and either a FORMAT or a COMMAND are required." if not compact: s += "\n" return s + def help_examples(s=""): """ Appends and returns some usage examples for the Sage documentation builder. """ s += "Examples:\n" - s += " sage --docbuild -FDC all\n" + s += " sage --docbuild -C all\n" s += " sage --docbuild constructions pdf\n" s += " sage --docbuild reference html -jv3\n" - s += " sage --docbuild --mathjax tutorial html\n" s += " sage --docbuild reference print_unincluded_modules\n" - s += " sage --docbuild developer -j html --sphinx-opts -q,-aE --verbose 2" + s += " sage --docbuild developer html --sphinx-opts='-q,-aE' --verbose 2" return s + def get_documents(): """ Returns a list of document names the Sage documentation builder @@ -1416,24 +1416,26 @@ def get_documents(): docs = [(d[3:] if d[0:3] == 'en/' else d) for d in docs] return docs -def help_documents(s=""): + +def help_documents(): """ Appends and returns a tabular list of documents, including a shortcut 'all' for all documents, available to the Sage documentation builder. """ docs = get_documents() - s += "DOCUMENTs:\n" + s = "DOCUMENTs:\n" s += format_columns(docs) s += "\n" if 'reference' in docs: - s+= "Other valid document names take the form 'reference/DIR', where\n" - s+= "DIR is a subdirectory of SAGE_DOC_SRC/en/reference/.\n" - s+= "This builds just the specified part of the reference manual.\n" + s += "Other valid document names take the form 'reference/DIR', where\n" + s += "DIR is a subdirectory of SAGE_DOC_SRC/en/reference/.\n" + s += "This builds just the specified part of the reference manual.\n" s += "DOCUMENT may also have the form 'file=/path/to/FILE', which builds\n" s += "the documentation for the specified file.\n" return s + def get_formats(): """ Returns a list of output formats the Sage documentation builder @@ -1444,16 +1446,16 @@ def get_formats(): formats.remove('html') return ['html', 'pdf'] + formats -def help_formats(s=""): + +def help_formats(): """ Appends and returns a tabular list of output formats available to the Sage documentation builder. """ - s += "FORMATs:\n" - s += format_columns(get_formats()) - return s + return "FORMATs:\n" + format_columns(get_formats()) -def help_commands(name='all', s=""): + +def help_commands(name='all'): """ Appends and returns a tabular list of commands, if any, the Sage documentation builder can run on the indicated document. The @@ -1461,10 +1463,10 @@ def help_commands(name='all', s=""): """ # To do: Generate the lists dynamically, using class attributes, # as with the Builders above. - command_dict = { 'reference' : [ - 'print_included_modules', 'print_modified_modules (*)', - 'print_unincluded_modules', 'print_new_and_updated_modules (*)', - ] } + s = "" + command_dict = {'reference': [ + 'print_included_modules', 'print_modified_modules (*)', + 'print_unincluded_modules', 'print_new_and_updated_modules (*)']} for doc in command_dict: if name == 'all' or doc == name: s += "COMMANDs for the DOCUMENT '" + doc + "':\n" @@ -1472,20 +1474,23 @@ def help_commands(name='all', s=""): s += "(*) Since the last build.\n" return s -def help_message_long(option, opt_str, value, parser): + +class help_message_long(argparse.Action): """ Prints an extended help message for the Sage documentation builder and exits. """ - help_funcs = [ help_usage, help_description, help_documents, - help_formats, help_commands, parser.format_option_help, - help_examples ] - for f in help_funcs: - print(f()) - sys.exit(0) - -def help_message_short(option=None, opt_str=None, value=None, parser=None, - error=False): + def __call__(self, parser, namespace, values, option_string=None): + help_funcs = [help_usage, help_description, help_documents, + help_formats, help_commands] + for f in help_funcs: + print(f()) + parser.print_help() + print(help_examples()) + sys.exit(0) + + +class help_message_short(argparse.Action): """ Prints a help message for the Sage documentation builder. The message includes command-line usage and a list of options. The @@ -1493,172 +1498,136 @@ def help_message_short(option=None, opt_str=None, value=None, parser=None, during this call, the message is printed only if the user hasn't requested a list (e.g., documents, formats, commands). """ - if not hasattr(parser.values, 'printed_help'): - if error is True: - if not hasattr(parser.values, 'printed_list'): - parser.print_help() - else: + def __call__(self, parser, namespace, values, option_string=None): + if not hasattr(namespace, 'printed_help'): parser.print_help() - setattr(parser.values, 'printed_help', 1) + setattr(namespace, 'printed_help', 1) + sys.exit(0) -def help_wrapper(option, opt_str, value, parser): + +class help_wrapper(argparse.Action): """ A helper wrapper for command-line options to the Sage documentation builder that print lists, such as document names, formats, and document-specific commands. """ - if option.dest == 'commands': - print(help_commands(value), end="") - if option.dest == 'documents': - print(help_documents(), end="") - if option.dest == 'formats': - print(help_formats(), end="") - if option.dest == 'all_documents': - if value == 'en/reference' or value == 'reference': - b = ReferenceBuilder('reference') - refdir = os.path.join(os.environ['SAGE_DOC_SRC'], 'en', b.name) - s = b.get_all_documents(refdir) - # Put the bibliography first, because it needs to be built first: - s.remove('reference/references') - s.insert(0, 'reference/references') - elif value == 'all': - s = get_documents() - # Put the reference manual first, because it needs to be built first: - s.remove('reference') - s.insert(0, 'reference') - else: - raise ValueError("argument for --all-documents must be either 'all'" - " or 'reference'") - for d in s: - print(d) - setattr(parser.values, 'printed_list', 1) - - -class IndentedHelpFormatter2(optparse.IndentedHelpFormatter, object): - """ - Custom help formatter class for optparse's OptionParser. - """ - def format_description(self, description): - """ - Returns a formatted description, preserving any original - explicit new line characters. - """ - if description: - lines_in = description.split('\n') - lines_out = [self._format_text(line) for line in lines_in] - return "\n".join(lines_out) + "\n" - else: - return "" - - def format_heading(self, heading): - """ - Returns a formatted heading using the superclass' formatter. - If the heading is 'options', up to case, the function converts - it to ALL CAPS. This allows us to match the heading 'OPTIONS' with - the same token in the builder's usage message. - """ - if heading.lower() == 'options': - heading = "OPTIONS" - return super(IndentedHelpFormatter2, self).format_heading(heading) + def __call__(self, parser, namespace, values, option_string=None): + if option_string in ['-D', '--documents']: + print(help_documents(), end="") + if option_string in ['-F', '--formats']: + print(help_formats(), end="") + if self.dest == 'commands': + print(help_commands(values), end="") + if self.dest == 'all_documents': + if values == 'reference': + b = ReferenceBuilder('reference') + refdir = os.path.join(os.environ['SAGE_DOC_SRC'], 'en', b.name) + s = b.get_all_documents(refdir) + # Put the bibliography first, because it needs to be built first: + s.remove('reference/references') + s.insert(0, 'reference/references') + elif values == 'all': + s = get_documents() + # Put the reference manual first, because it needs to be built first: + s.remove('reference') + s.insert(0, 'reference') + for d in s: + print(d) + setattr(namespace, 'printed_list', 1) + sys.exit(0) def setup_parser(): """ - Sets up and returns a command-line OptionParser instance for the + Sets up and returns a command-line ArgumentParser instance for the Sage documentation builder. """ - # Documentation: http://docs.python.org/library/optparse.html - parser = optparse.OptionParser(add_help_option=False, - usage=help_usage(compact=True), - formatter=IndentedHelpFormatter2(), - description=help_description(compact=True)) - + # Documentation: https://docs.python.org/library/argparse.html + parser = argparse.ArgumentParser(usage=help_usage(compact=True), + description=help_description(compact=True), + add_help=False) # Standard options. Note: We use explicit option.dest names # to avoid ambiguity. - standard = optparse.OptionGroup(parser, "Standard") - standard.add_option("-h", "--help", - action="callback", callback=help_message_short, - help="show a help message and exit") - standard.add_option("-H", "--help-all", - action="callback", callback=help_message_long, - help="show an extended help message and exit") - standard.add_option("-D", "--documents", dest="documents", - action="callback", callback=help_wrapper, - help="list all available DOCUMENTs") - standard.add_option("-F", "--formats", dest="formats", - action="callback", callback=help_wrapper, - help="list all output FORMATs") - standard.add_option("-C", "--commands", dest="commands", - type="string", metavar="DOC", - action="callback", callback=help_wrapper, - help="list all COMMANDs for DOCUMENT DOC; use 'all' to list all") - - standard.add_option("-i", "--inherited", dest="inherited", - default=False, action="store_true", - help="include inherited members in reference manual; may be slow, may fail for PDF output") - standard.add_option("-u", "--underscore", dest="underscore", - default=False, action="store_true", - help="include variables prefixed with '_' in reference manual; may be slow, may fail for PDF output") - - standard.add_option("-j", "--mathjax", "--jsmath", dest="mathjax", - action="store_true", - help="render math using MathJax; FORMATs: html, json, pickle, web") - standard.add_option("--no-plot", dest="no_plot", - action="store_true", - help="do not include graphics auto-generated using the '.. plot' markup") - standard.add_option("--include-tests-blocks", dest="skip_tests", default=True, - action="store_false", - help="include TESTS blocks in the reference manual") - standard.add_option("--no-pdf-links", dest="no_pdf_links", - action="store_true", - help="do not include PDF links in DOCUMENT 'website'; FORMATs: html, json, pickle, web") - standard.add_option("--warn-links", dest="warn_links", - default=False, action="store_true", - help="issue a warning whenever a link is not properly resolved; equivalent to '--sphinx-opts -n' (sphinx option: nitpicky)") - standard.add_option("--check-nested", dest="check_nested", - action="store_true", - help="check picklability of nested classes in DOCUMENT 'reference'") - standard.add_option("--no-prune-empty-dirs", dest="no_prune_empty_dirs", - action="store_true", - help="do not prune empty directories in the documentation sources") - standard.add_option("-N", "--no-colors", dest="color", default=True, - action="store_false", - help="do not color output; does not affect children") - standard.add_option("-q", "--quiet", dest="verbose", - action="store_const", const=0, - help="work quietly; same as --verbose=0") - standard.add_option("-v", "--verbose", dest="verbose", - type="int", default=1, metavar="LEVEL", - action="store", - help="report progress at LEVEL=0 (quiet), 1 (normal), 2 (info), or 3 (debug); does not affect children") - standard.add_option("-o", "--output", dest="output_dir", default=None, - metavar="DIR", action="store", - help="if DOCUMENT is a single file ('file=...'), write output to this directory") - parser.add_option_group(standard) + standard = parser.add_argument_group("Standard") + standard.add_argument("-h", "--help", nargs=0, action=help_message_short, + help="show a help message and exit") + standard.add_argument("-H", "--help-all", nargs=0, action=help_message_long, + help="show an extended help message and exit") + standard.add_argument("-D", "--documents", nargs=0, action=help_wrapper, + help="list all available DOCUMENTs") + standard.add_argument("-F", "--formats", nargs=0, action=help_wrapper, + help="list all output FORMATs") + standard.add_argument("-C", "--commands", dest="commands", + type=str, metavar="DOC", action=help_wrapper, + help="list all COMMANDs for DOCUMENT DOC; use 'all' to list all") + standard.add_argument("-i", "--inherited", dest="inherited", + action="store_true", + help="include inherited members in reference manual; may be slow, may fail for PDF output") + standard.add_argument("-u", "--underscore", dest="underscore", + action="store_true", + help="include variables prefixed with '_' in reference manual; may be slow, may fail for PDF output") + standard.add_argument("-j", "--mathjax", "--jsmath", dest="mathjax", + action="store_true", + help="ignored for backwards compatibility") + standard.add_argument("--no-plot", dest="no_plot", + action="store_true", + help="do not include graphics auto-generated using the '.. plot' markup") + standard.add_argument("--include-tests-blocks", dest="skip_tests", default=True, + action="store_false", + help="include TESTS blocks in the reference manual") + standard.add_argument("--no-pdf-links", dest="no_pdf_links", + action="store_true", + help="do not include PDF links in DOCUMENT 'website'; FORMATs: html, json, pickle, web") + standard.add_argument("--warn-links", dest="warn_links", + action="store_true", + help="issue a warning whenever a link is not properly resolved; equivalent to '--sphinx-opts -n' (sphinx option: nitpicky)") + standard.add_argument("--check-nested", dest="check_nested", + action="store_true", + help="check picklability of nested classes in DOCUMENT 'reference'") + standard.add_argument("--no-prune-empty-dirs", dest="no_prune_empty_dirs", + action="store_true", + help="do not prune empty directories in the documentation sources") + standard.add_argument("-N", "--no-colors", dest="color", + action="store_false", + help="do not color output; does not affect children") + standard.add_argument("-q", "--quiet", dest="verbose", + action="store_const", const=0, + help="work quietly; same as --verbose=0") + standard.add_argument("-v", "--verbose", dest="verbose", + type=int, default=1, metavar="LEVEL", + action="store", + help="report progress at LEVEL=0 (quiet), 1 (normal), 2 (info), or 3 (debug); does not affect children") + standard.add_argument("-o", "--output", dest="output_dir", default=None, + metavar="DIR", action="store", + help="if DOCUMENT is a single file ('file=...'), write output to this directory") # Advanced options. - advanced = optparse.OptionGroup(parser, "Advanced", - "Use these options with care.") - advanced.add_option("-S", "--sphinx-opts", dest="sphinx_opts", - type="string", metavar="OPTS", + advanced = parser.add_argument_group("Advanced", + "Use these options with care.") + advanced.add_argument("-S", "--sphinx-opts", dest="sphinx_opts", + type=str, metavar="OPTS", action="store", - help="pass comma-separated OPTS to sphinx-build") - advanced.add_option("-U", "--update-mtimes", dest="update_mtimes", - default=False, action="store_true", + help="pass comma-separated OPTS to sphinx-build; must precede OPTS with '=', as in '-S=-q,-aE' or '-S=\"-q,-aE\"'") + advanced.add_argument("-U", "--update-mtimes", dest="update_mtimes", + action="store_true", help="before building reference manual, update modification times for auto-generated reST files") - advanced.add_option("-k", "--keep-going", dest="keep_going", - default=False, action="store_true", + advanced.add_argument("-k", "--keep-going", dest="keep_going", + action="store_true", help="Do not abort on errors but continue as much as possible after an error") - advanced.add_option("--all-documents", dest="all_documents", - type="str", metavar="ARG", - action="callback", callback=help_wrapper, - help="if ARG is 'reference', list all subdocuments" - " of en/reference. If ARG is 'all', list all main" - " documents") - parser.add_option_group(advanced) - + advanced.add_argument("--all-documents", dest="all_documents", + type=str, metavar="ARG", + choices=['all', 'reference'], + action=help_wrapper, + help="if ARG is 'reference', list all subdocuments" + " of en/reference. If ARG is 'all', list all main" + " documents") + parser.add_argument("document", nargs='?', type=str, metavar="DOCUMENT", + help="name of the document to build. It can be either one of the documents listed by -D or 'file=/path/to/FILE' to build documentation for this specific file.") + parser.add_argument("format", nargs='?', type=str, + metavar="FORMAT or COMMAND", help='document output format (or command)') return parser + def setup_logger(verbose=1, color=True): r""" Set up a Python Logger instance for the Sage documentation builder. The @@ -1744,19 +1713,18 @@ def fetch_inventory(self, app, uri, inv): def main(): # Parse the command-line. parser = setup_parser() - options, args = parser.parse_args() - DocBuilder._options = options + args = parser.parse_args() + DocBuilder._options = args # Get the name and type (target format) of the document we are # trying to build. - try: - name, type = args - except ValueError: - help_message_short(parser=parser, error=True) + name, typ = args.document, args.format + if not name or not type: + parser.print_help() sys.exit(1) # Set up module-wide logging. - setup_logger(options.verbose, options.color) + setup_logger(args.verbose, args.color) def excepthook(*exc_info): logger.error('Error building the documentation.', exc_info=exc_info) @@ -1769,35 +1737,27 @@ def excepthook(*exc_info): sys.excepthook = excepthook # Process selected options. - # - # MathJax: this check usually has no practical effect, since - # SAGE_DOC_MATHJAX is set to "True" by the script sage-env. - # To disable MathJax, set SAGE_DOC_MATHJAX to "no" or "False". - if options.mathjax or (os.environ.get('SAGE_DOC_MATHJAX', 'no') != 'no' - and os.environ.get('SAGE_DOC_MATHJAX', 'no') != 'False'): - os.environ['SAGE_DOC_MATHJAX'] = 'True' - - if options.check_nested: + if args.check_nested: os.environ['SAGE_CHECK_NESTED'] = 'True' - if options.underscore: + if args.underscore: os.environ['SAGE_DOC_UNDERSCORE'] = "True" global ALLSPHINXOPTS, WEBSITESPHINXOPTS, ABORT_ON_ERROR - if options.sphinx_opts: - ALLSPHINXOPTS += options.sphinx_opts.replace(',', ' ') + " " - if options.no_pdf_links: + if args.sphinx_opts: + ALLSPHINXOPTS += args.sphinx_opts.replace(',', ' ') + " " + if args.no_pdf_links: WEBSITESPHINXOPTS = " -A hide_pdf_links=1 " - if options.warn_links: + if args.warn_links: ALLSPHINXOPTS += "-n " - if options.no_plot: + if args.no_plot: os.environ['SAGE_SKIP_PLOT_DIRECTIVE'] = 'yes' - if options.skip_tests: + if args.skip_tests: os.environ['SAGE_SKIP_TESTS_BLOCKS'] = 'True' - ABORT_ON_ERROR = not options.keep_going + ABORT_ON_ERROR = not args.keep_going - if not options.no_prune_empty_dirs: + if not args.no_prune_empty_dirs: # Delete empty directories. This is needed in particular for empty # directories due to "git checkout" which never deletes empty # directories it leaves behind. See Trac #20010. @@ -1809,7 +1769,7 @@ def excepthook(*exc_info): os.rmdir(dirpath) # Set up Intersphinx cache - C = IntersphinxCache() + _ = IntersphinxCache() - builder = getattr(get_builder(name), type) + builder = getattr(get_builder(name), typ) builder() diff --git a/tox.ini b/tox.ini index 6dd379b1920..154eacd5260 100644 --- a/tox.ini +++ b/tox.ini @@ -241,6 +241,7 @@ setenv = fedora-29: BASE_TAG=29 fedora-29: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-30: BASE_TAG=30 + fedora-30: IGNORE_MISSING_SYSTEM_PACKAGES=yes fedora-31: BASE_TAG=31 fedora-32: BASE_TAG=32 fedora-33: BASE_TAG=33