diff --git a/Makefile b/Makefile index fb8aa9887b5..2b6f00934ab 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ build: all-build # If configure was run before, rerun it with the old arguments. # Otherwise, run configure with argument $PREREQ_OPTIONS. -build/make/Makefile: configure build/pkgs/*/* +build/make/Makefile: configure build/make/deps build/pkgs/*/* rm -f config.log mkdir -p logs/pkgs ln -s logs/pkgs/config.log config.log diff --git a/README.txt b/README.txt index d6ca4807335..fc31e143952 100644 --- a/README.txt +++ b/README.txt @@ -209,7 +209,7 @@ MORE DETAILED INSTRUCTIONS TO BUILD FROM SOURCE 7. The HTML version of the documentation is built during the compilation process of Sage and resides in the directory: - $SAGE_ROOT/src/doc/output/html/ + $SAGE_ROOT/local/share/doc/sage/html/ OPTIONAL: If you want to build the PDF version (requires LaTeX) of the documentation, run: diff --git a/VERSION.txt b/VERSION.txt index c152a49d9d0..373c8b6ca3f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 7.0, released 2016-01-19 +Sage version 7.1.beta3, released 2016-02-11 diff --git a/build/make/deps b/build/make/deps index 7eaf279c855..0c5e027d5f6 100644 --- a/build/make/deps +++ b/build/make/deps @@ -11,7 +11,8 @@ STARTED = $(SAGE_LOCAL)/etc/sage-started.txt .PHONY: all all-sage all-toolchain all-build start \ base toolchain toolchain-deps sagelib \ doc doc-html doc-html-jsmath doc-html-mathjax doc-pdf \ - doc-clean clean sagelib-clean build-clean + doc-clean doc-src-clean doc-output-clean \ + clean sagelib-clean build-clean # Build everything and start Sage. # Note that we put the "doc" target first in the rule below because @@ -211,9 +212,14 @@ doc-html-jsmath: doc-html-mathjax doc-pdf: $(DOC_DEPENDENCIES) cd ../.. && sage-logger './sage --docbuild all pdf $(SAGE_DOCBUILD_OPTS)' logs/docpdf.log -doc-clean: +doc-clean: doc-src-clean doc-output-clean + +doc-src-clean: cd "$(SAGE_SRC)/doc" && $(MAKE) clean +doc-output-clean: + rm -rf "$(SAGE_SHARE)/doc/sage" + ############################################################################### # Cleaning up diff --git a/build/make/install b/build/make/install index 9a8ada98f79..c0ab28194e5 100755 --- a/build/make/install +++ b/build/make/install @@ -5,23 +5,22 @@ ######################################################################## # Assume current directory is SAGE_ROOT/build/make -SAGE_ROOT=`cd ../.. && pwd -P` -SAGE_SRC="$SAGE_ROOT/src" -SAGE_LOCAL="$SAGE_ROOT/local" -SAGE_SHARE="$SAGE_LOCAL/share" -SAGE_EXTCODE="$SAGE_SHARE/sage/ext" -SAGE_LOGS="$SAGE_ROOT/logs/pkgs" -SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" -SAGE_VERSION=`cat $SAGE_ROOT/VERSION.txt | sed 's+.*\ \(.*\),.*+\1+'` +export SAGE_ROOT=`cd ../.. && pwd -P` +export SAGE_SRC="$SAGE_ROOT/src" +export SAGE_LOCAL="$SAGE_ROOT/local" +export SAGE_SHARE="$SAGE_LOCAL/share" +export SAGE_EXTCODE="$SAGE_SHARE/sage/ext" +export SAGE_LOGS="$SAGE_ROOT/logs/pkgs" +export SAGE_SPKG_INST="$SAGE_LOCAL/var/lib/sage/installed" +export SAGE_VERSION=`cat $SAGE_ROOT/VERSION.txt | sed 's+.*\ \(.*\),.*+\1+'` if [ -z "${SAGE_ORIG_PATH_SET}" ]; then SAGE_ORIG_PATH=$PATH && export SAGE_ORIG_PATH SAGE_ORIG_PATH_SET=True && export SAGE_ORIG_PATH_SET fi -PATH="$SAGE_ROOT/build/bin:$SAGE_SRC/bin:$SAGE_LOCAL/bin:$PATH" +export PATH="$SAGE_ROOT/build/bin:$SAGE_SRC/bin:$SAGE_LOCAL/bin:$PATH" -PYTHONPATH="$SAGE_LOCAL" -export SAGE_ROOT SAGE_SRC SAGE_LOCAL SAGE_EXTCODE SAGE_LOGS SAGE_SPKG_INST SAGE_VERSION PATH PYTHONPATH +export PYTHONPATH="$SAGE_LOCAL" # Storing the start time of the build process. The time is stored in # seconds since 1970-01-01 in a hidden file called diff --git a/build/pkgs/backports_abc/SPKG.txt b/build/pkgs/backports_abc/SPKG.txt new file mode 100644 index 00000000000..e6f57200183 --- /dev/null +++ b/build/pkgs/backports_abc/SPKG.txt @@ -0,0 +1,18 @@ += backports_abc = + +== Description == + +A backport of recent additions to the 'collections.abc' module. + +== License == + +Python Software Foundation License + +== Upstream Contact == + +Home page: https://pypi.python.org/pypi/backports_abc + +== Dependencies == + +Python, Setuptools + diff --git a/build/pkgs/backports_abc/checksums.ini b/build/pkgs/backports_abc/checksums.ini new file mode 100644 index 00000000000..cd2685c8116 --- /dev/null +++ b/build/pkgs/backports_abc/checksums.ini @@ -0,0 +1,4 @@ +tarball=backports_abc-VERSION.tar.gz +sha1=76060a68d14f9d1a4198e00c3bb3711467ecb1f4 +md5=0b65a216ce9dc9c1a7e20a729dd7c05b +cksum=1989294907 diff --git a/build/pkgs/backports_abc/dependencies b/build/pkgs/backports_abc/dependencies new file mode 100644 index 00000000000..643eeddd35b --- /dev/null +++ b/build/pkgs/backports_abc/dependencies @@ -0,0 +1,5 @@ +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/backports_abc/package-version.txt b/build/pkgs/backports_abc/package-version.txt new file mode 100644 index 00000000000..bd73f47072b --- /dev/null +++ b/build/pkgs/backports_abc/package-version.txt @@ -0,0 +1 @@ +0.4 diff --git a/build/pkgs/backports_abc/spkg-install b/build/pkgs/backports_abc/spkg-install new file mode 100755 index 00000000000..6255967da12 --- /dev/null +++ b/build/pkgs/backports_abc/spkg-install @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +cd src + +python setup.py install +if [ $? -ne 0 ]; then + echo "Error installing backports_abc ... exiting" + exit 1 +fi diff --git a/build/pkgs/backports_abc/type b/build/pkgs/backports_abc/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/backports_abc/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/backports_ssl_match_hostname/checksums.ini b/build/pkgs/backports_ssl_match_hostname/checksums.ini index 5af6e3be665..25b368b83c4 100644 --- a/build/pkgs/backports_ssl_match_hostname/checksums.ini +++ b/build/pkgs/backports_ssl_match_hostname/checksums.ini @@ -1,4 +1,4 @@ tarball=backports_ssl_match_hostname-VERSION.tar.gz -sha1=194503d8b47066c2ce8d23cab707d19d6ada2eb9 -md5=5def436c23fa2bc09aedf221d61b7017 -cksum=132706006 +sha1=1d7500574eef84c826dfaf507722cd9249bf0672 +md5=c21f63bb4729eeab399932410a012934 +cksum=541176683 diff --git a/build/pkgs/backports_ssl_match_hostname/package-version.txt b/build/pkgs/backports_ssl_match_hostname/package-version.txt index 7c9bc6e1a60..a3a0b2cff0d 100644 --- a/build/pkgs/backports_ssl_match_hostname/package-version.txt +++ b/build/pkgs/backports_ssl_match_hostname/package-version.txt @@ -1 +1 @@ -3.4.0.2 +3.5.0.1 diff --git a/build/pkgs/certifi/checksums.ini b/build/pkgs/certifi/checksums.ini index d72671c5f44..b5ab9b89649 100644 --- a/build/pkgs/certifi/checksums.ini +++ b/build/pkgs/certifi/checksums.ini @@ -1,4 +1,4 @@ tarball=certifi-VERSION.tar.gz -sha1=f53dc8f57aaf6d69c183ebadcec52ece0a55cc3f -md5=315ea4e50673a16ab047099f816fd32a -cksum=3559413705 +sha1=e31ab9b9bee02511d91758e73c8598a82a0b3c35 +md5=e04b512009401603f1485380ac879cf5 +cksum=3504777269 diff --git a/build/pkgs/certifi/package-version.txt b/build/pkgs/certifi/package-version.txt index 37e68fe1100..91b18fbe68c 100644 --- a/build/pkgs/certifi/package-version.txt +++ b/build/pkgs/certifi/package-version.txt @@ -1 +1 @@ -14.05.14 +2015.11.20.1 diff --git a/build/pkgs/cliquer/SPKG.txt b/build/pkgs/cliquer/SPKG.txt index 853c784786e..1dfd7d48e6d 100644 --- a/build/pkgs/cliquer/SPKG.txt +++ b/build/pkgs/cliquer/SPKG.txt @@ -16,7 +16,4 @@ http://users.tkk.fi/pat/cliquer.html * None == Patches == - * Makefile.patch: Patch the Makefile for Sage. Remove hardcoded - compiler and linker flags, allow flags to be set from spkg-install. - More importantly, we're building cliquer as a dynamic shared library, - instead of a stand-alone program. + * autotoolized - see https://github.com/dimpase/autocliquer diff --git a/build/pkgs/cliquer/checksums.ini b/build/pkgs/cliquer/checksums.ini index 7e28001b68f..cec6f64dcc2 100644 --- a/build/pkgs/cliquer/checksums.ini +++ b/build/pkgs/cliquer/checksums.ini @@ -1,4 +1,4 @@ -tarball=cliquer-VERSION.tar.bz2 -sha1=8239530eb14c1273c32ffcf3b671dd3766084374 -md5=32b97b6689318b58d9c099e2c7769521 -cksum=3641271864 +tarball=cliquer-VERSION.tar.gz +sha1=930ec79f64facb8ac4035a3e3702ed2b1dabd092 +md5=1d985e0bed876cc69aed43953a814a6a +cksum=766312376 diff --git a/build/pkgs/cliquer/package-version.txt b/build/pkgs/cliquer/package-version.txt index 52e40c7ff6e..8a68fb3ec24 100644 --- a/build/pkgs/cliquer/package-version.txt +++ b/build/pkgs/cliquer/package-version.txt @@ -1 +1 @@ -1.21.p2 +1.21.p3 diff --git a/build/pkgs/cliquer/patches/Makefile.patch b/build/pkgs/cliquer/patches/Makefile.patch deleted file mode 100644 index f773ecce3eb..00000000000 --- a/build/pkgs/cliquer/patches/Makefile.patch +++ /dev/null @@ -1,46 +0,0 @@ -diff -ru src/Makefile b/Makefile ---- src/Makefile 2010-01-22 08:53:21.000000000 +0100 -+++ b/Makefile 2014-01-16 14:55:51.977047191 +0100 -@@ -1,24 +1,3 @@ -- --##### Configurable options: -- --## Compiler: --CC=gcc --#CC=cc -- --## Compiler flags: -- --# GCC: (also -march=pentium etc, for machine-dependent optimizing) --CFLAGS=-Wall -O3 -fomit-frame-pointer -funroll-loops -- --# GCC w/ debugging: --#CFLAGS=-Wall -g -DINLINE= -- --# Compaq C / Digital C: --#CFLAGS=-arch=host -fast -- --# SunOS: --#CFLAGS=-fast -- - ## Program options: - - # Enable long options for cl (eg. "cl --help"), comment out to disable. -@@ -29,14 +8,14 @@ - ##### End of configurable options - - --all: cl -+all: libcliquer.so - - - testcases: testcases.o cliquer.o graph.o reorder.o - $(CC) $(LDFLAGS) -o $@ testcases.o cliquer.o graph.o reorder.o - --cl: cl.o cliquer.o graph.o reorder.o -- $(CC) $(LDFLAGS) -o $@ cl.o cliquer.o graph.o reorder.o -+libcliquer.so: cl.o cliquer.o graph.o reorder.o -+ $(CC) $(LDFLAGS) $(SAGESOFLAGS) -o $@ cl.o cliquer.o graph.o reorder.o - - - cl.o testcases.o cliquer.o graph.o reorder.o: cliquer.h set.h graph.h misc.h reorder.h Makefile cliquerconf.h diff --git a/build/pkgs/cliquer/spkg-check b/build/pkgs/cliquer/spkg-check index 3c6fd5964f0..cec33f60e3b 100755 --- a/build/pkgs/cliquer/spkg-check +++ b/build/pkgs/cliquer/spkg-check @@ -1,35 +1,18 @@ #!/usr/bin/env bash -if [ "$SAGE_LOCAL" = "" ]; then - echo "SAGE_LOCAL undefined ... exiting"; - echo "Maybe run 'sage -sh'?" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" exit 1 fi -OPTIMIZATION_FLAGS="-O3 -funroll-loops -fomit-frame-pointer" -# Work around a bug in gcc 4.6.0: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48774 -if [ "`testcc.sh $CC`" = GCC ] ; then - if $CC -dumpversion 2>/dev/null |grep >/dev/null '^4\.6\.[01]' ; then - echo "Warning: Working around bug in gcc 4.6.0" - OPTIMIZATION_FLAGS="$OPTIMIZATION_FLAGS -fno-ivopts" - fi -fi - -CFLAGS="$CFLAGS `testcflags.sh -g $OPTIMIZATION_FLAGS -fPIC -KPIC -Wall`" -CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include" -LDFLAGS="$LDFLAGS -L$SAGE_LOCAL/lib" +cd src -# Compile for 64-bit if SAGE64 is set to 'yes'. -# On 64-bit hardware, we don't need to set this variable to true. A -# 64-bit cliquer library would be built on such platform. -if [ "$SAGE64" = yes ]; then - CFLAGS="$CFLAGS -m64 " - LDFLAGS="$LDFLAGS -m64 " +echo "Now building and running cliquer's test suite..." +$MAKE check +if [ $? -ne 0 ]; then + echo >&2 "Error: The cliquer's test suite failed." + exit 1 fi -# Export everything -export CFLAGS -export CPPFLAGS -export LDFLAGS - -cd src && $MAKE test +echo "The cliquer's test suite passed successfully." diff --git a/build/pkgs/cliquer/spkg-install b/build/pkgs/cliquer/spkg-install index efee877c98d..10b9cabca11 100755 --- a/build/pkgs/cliquer/spkg-install +++ b/build/pkgs/cliquer/spkg-install @@ -1,43 +1,14 @@ #!/usr/bin/env bash -OPTIMIZATION_FLAGS="-O3 -funroll-loops -fomit-frame-pointer" -# Work around a bug in gcc 4.6.0: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48774 -if [ "`testcc.sh $CC`" = GCC ] ; then - if $CC -dumpversion 2>/dev/null |grep >/dev/null '^4\.6\.[01]' ; then - echo "Warning: Working around bug in gcc 4.6.0" - OPTIMIZATION_FLAGS="$OPTIMIZATION_FLAGS -fno-ivopts" - fi -fi - -CFLAGS="$CFLAGS `testcflags.sh -g $OPTIMIZATION_FLAGS -fPIC -KPIC -Wall`" -CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include" - -# Compile for 64-bit if SAGE64 is set to 'yes'. -# On 64-bit hardware, we don't need to set this variable to true. A -# 64-bit cliquer library would be built on such platform. -if [ "$SAGE64" = yes ]; then - echo "Building a 64-bit version of cliquer" - CFLAGS="$CFLAGS -m64 " - LDFLAGS="$LDFLAGS -m64 " -fi - -# Flags for building a dynamically linked shared object. -if [ "$UNAME" = "Darwin" ]; then - SAGESOFLAGS="-dynamiclib -single_module -flat_namespace -undefined dynamic_lookup" -elif [ "$UNAME" = "SunOS" ]; then - SAGESOFLAGS="-shared -Wl,-h,libcliquer.so -Wl,-ztext" -else - SAGESOFLAGS="-shared -Wl,-soname,libcliquer.so" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" + exit 1 fi -# Export everything -export CFLAGS -export CPPFLAGS -export LDFLAGS -export SAGESOFLAGS - cd src +echo "Applying patches..." # Apply all patches for patch in ../patches/*.patch; do [ -r "$patch" ] || continue # Skip non-existing or non-readable patches @@ -49,22 +20,16 @@ for patch in ../patches/*.patch; do fi done -$MAKE +echo "Configuring..." +./configure --prefix="$SAGE_LOCAL" --disable-static --libdir="$SAGE_LOCAL/lib" if [ $? -ne 0 ]; then - echo >&2 "Failed to compile cliquer" - exit 1 + echo >&2 "Error configuring cliquer" + exit 1 fi -rm -rf "$SAGE_LOCAL/include/cliquer/" -mkdir -p "$SAGE_LOCAL/include/cliquer/" -cp *.h "$SAGE_LOCAL/include/cliquer/" - -if [ "$UNAME" = "Darwin" ]; then - cp -f libcliquer.so "$SAGE_LOCAL/lib/libcliquer.dylib" - install_name_tool -id "${SAGE_LOCAL}"/lib/libcliquer.dylib "${SAGE_LOCAL}"/lib/libcliquer.dylib -elif [ "$UNAME" = "CYGWIN" ]; then - cp -f libcliquer.so "$SAGE_LOCAL/lib/libcliquer.dll" +echo "Building and installing ..." +$MAKE install +if [ $? -ne 0 ]; then + echo >&2 "Error installing cliquer" + exit 1 fi - -# Copy this in all cases, in any case it doesn't hurt. -cp -f libcliquer.so "$SAGE_LOCAL/lib/libcliquer.so" diff --git a/build/pkgs/cliquer/spkg-src b/build/pkgs/cliquer/spkg-src new file mode 100755 index 00000000000..bcbdabecbe1 --- /dev/null +++ b/build/pkgs/cliquer/spkg-src @@ -0,0 +1,23 @@ +#!/bin/sh +# +# creates the tarball in the current dir, to be moved to ../../../upstream + +#PATCHLEVEL=".p0" +PATCHLEVEL= + +rm -rf autocliquer/ +git clone https://github.com/dimpase/autocliquer.git +cd autocliquer/ + +VERSION=`autoconf --trace='AC_INIT:$2'` +autoreconf -fi +automake --add-missing --copy +./configure + +rm -f cliquer-$VERSION.tar.gz +make dist +mv cliquer-$VERSION.tar.gz ../ +cd .. +rm -rf autocliquer/ + + diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 0482c018bb4..1a4fa50d1ad 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=631a348f0ac864b3ec37dfcb1d7fab79e213a075 -md5=5759d84c96102aee89c7eaea0aadf842 -cksum=2510923339 +sha1=603a0399bf37b613bda1b82cd2bc05058a009104 +md5=15a9898dbcb629850559d7a520ed141f +cksum=3386674131 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index dee261df401..a29644e57ed 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -140 +144 diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index f3ac4410434..d72432ed2d0 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,4 @@ tarball=Cython-VERSION.tar.gz -sha1=d5592dc3d529c55a5ef95346caccf11c556993bd -md5=813df20f7ce5f00e60568e0371fbd07c -cksum=365027876 +sha1=fc574c5050cd5a8e34435432e2a4a693353ed807 +md5=157df1f69bcec6b56fd97e0f2e057f6e +cksum=346066359 diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 9e40e75c5d2..40a6dfede5d 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.23.3 +0.23.4 diff --git a/build/pkgs/functools32/SPKG.txt b/build/pkgs/functools32/SPKG.txt new file mode 100644 index 00000000000..8cab3c8ca44 --- /dev/null +++ b/build/pkgs/functools32/SPKG.txt @@ -0,0 +1,18 @@ += functools32 = + +== Description == + +Backport of the functools module from Python 3.2.3 for use on 2.7 and PyPy. + +== License == + +Python Software Foundation License + +== Upstream Contact == + +Home page: https://pypi.python.org/pypi/functools32 + +== Dependencies == + +Python, Setuptools + diff --git a/build/pkgs/functools32/checksums.ini b/build/pkgs/functools32/checksums.ini new file mode 100644 index 00000000000..7357e795b7d --- /dev/null +++ b/build/pkgs/functools32/checksums.ini @@ -0,0 +1,4 @@ +tarball=functools32-VERSION.tar.gz +sha1=a520082a56af52c7af8d2d1390856bf1a0d755df +md5=09f24ffd9af9f6cd0f63cb9f4e23d4b2 +cksum=3970476845 diff --git a/build/pkgs/functools32/dependencies b/build/pkgs/functools32/dependencies new file mode 100644 index 00000000000..643eeddd35b --- /dev/null +++ b/build/pkgs/functools32/dependencies @@ -0,0 +1,5 @@ +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/functools32/package-version.txt b/build/pkgs/functools32/package-version.txt new file mode 100644 index 00000000000..4fa300a16c7 --- /dev/null +++ b/build/pkgs/functools32/package-version.txt @@ -0,0 +1 @@ +3.2.3-2 diff --git a/build/pkgs/functools32/spkg-install b/build/pkgs/functools32/spkg-install new file mode 100755 index 00000000000..5d13c2818e5 --- /dev/null +++ b/build/pkgs/functools32/spkg-install @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +cd src + +python setup.py install +if [ $? -ne 0 ]; then + echo "Error installing functools32 ... exiting" + exit 1 +fi diff --git a/build/pkgs/functools32/type b/build/pkgs/functools32/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/functools32/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index b7c34dfbe76..1f83910ea17 100755 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -2,7 +2,7 @@ # WARNING -- if you add a package here, also add it to # the gap_reset_workspace() command in -# /devel/sage/sage/interfaces/gap.py +# /src/sage/interfaces/gap.py # if [ -z "$SAGE_LOCAL" ]; then diff --git a/build/pkgs/gp2c/checksums.ini b/build/pkgs/gp2c/checksums.ini index 0f114ad0f51..b057d08ce7b 100644 --- a/build/pkgs/gp2c/checksums.ini +++ b/build/pkgs/gp2c/checksums.ini @@ -1,4 +1,4 @@ tarball=gp2c-VERSION.tar.gz -sha1=5acb1a13e1ed8ee877ffb34baa3b817e720f3e50 -md5=cb263990e399153aca6a2540930b4600 -cksum=1931194041 +sha1=e07cebffcd09c0d644d52335130984f33042b46f +md5=3f6bb47d41ddca7b6a4938d16abbe4e8 +cksum=3338275717 diff --git a/build/pkgs/gp2c/package-version.txt b/build/pkgs/gp2c/package-version.txt index 6eb15535643..e353504d336 100644 --- a/build/pkgs/gp2c/package-version.txt +++ b/build/pkgs/gp2c/package-version.txt @@ -1 +1 @@ -0.0.9pl3 +0.0.9pl5 diff --git a/build/pkgs/gp2c/patches/20150326.patch b/build/pkgs/gp2c/patches/20150326.patch deleted file mode 100644 index 3f7651f8162..00000000000 --- a/build/pkgs/gp2c/patches/20150326.patch +++ /dev/null @@ -1,218 +0,0 @@ -Various fixes between released version 0.0.9pl3 and gp2c git repo - -diff -ru gp2c-0.0.9pl3/gp2c gp2c/gp2c ---- gp2c-0.0.9pl3/gp2c 2011-10-02 22:28:23.000000000 +0200 -+++ gp2c/gp2c 2015-07-09 14:50:05.600098572 +0200 -@@ -14,4 +14,4 @@ - EOF - exit 1; - fi --GP2C_FUNC_DSC=desc/func.dsc src/gp2c $* -+GP2C_FUNC_DSC=desc/func.dsc exec src/gp2c "$@" -diff -ru gp2c-0.0.9pl3/gp2c-dbg gp2c/gp2c-dbg ---- gp2c-0.0.9pl3/gp2c-dbg 2011-10-02 22:28:23.000000000 +0200 -+++ gp2c/gp2c-dbg 2015-07-09 14:50:05.601098584 +0200 -@@ -13,4 +13,4 @@ - EOF - exit 1; - fi --GP2C=./gp2c scripts/gp2c-dbg $* -+GP2C=./gp2c exec scripts/gp2c-dbg "$@" -diff -ru gp2c-0.0.9pl3/gp2c-run gp2c/gp2c-run ---- gp2c-0.0.9pl3/gp2c-run 2011-10-02 22:28:23.000000000 +0200 -+++ gp2c/gp2c-run 2015-07-09 14:50:05.601098584 +0200 -@@ -13,4 +13,4 @@ - EOF - exit 1; - fi --GP2C=./gp2c scripts/gp2c-run $* -+GP2C=./gp2c exec scripts/gp2c-run "$@" -diff -ru gp2c-0.0.9pl3/src/funcdesc.c gp2c/src/funcdesc.c ---- gp2c-0.0.9pl3/src/funcdesc.c 2014-11-17 14:30:55.000000000 +0100 -+++ gp2c/src/funcdesc.c 2015-07-09 14:50:05.603098609 +0200 -@@ -575,7 +575,8 @@ - gpfunc *gp = lfunc + findfunction(entryname(arg[0])); - if (gp->spec==GPuser && gp->user->wrapper>=0) - { -- if (funcmode(*gp)&(1<user->wrapper; -+ if ((funcmode(*gp)&(1<fout, nb-1,arg+1,FC_tovecprec,d->nerr); - else - { -diff -ru gp2c-0.0.9pl3/src/funcspec.c gp2c/src/funcspec.c ---- gp2c-0.0.9pl3/src/funcspec.c 2014-11-22 22:28:59.000000000 +0100 -+++ gp2c/src/funcspec.c 2015-07-09 14:50:05.604098621 +0200 -@@ -432,8 +432,11 @@ - arg[0] = newleaf(pred); - } - genblock(arg[0],n); -- arg[1]=geninsertvar(arg[1],ret); -- makesubblock(arg[1]); -+ if (arg[1]!=GNOARG) -+ { -+ arg[1]=geninsertvar(arg[1],ret); -+ makesubblock(arg[1]); -+ } - if(nb==3) - { - arg[2]=geninsertvar(arg[2],ret==-1?-1:newleaf(ret)); -diff -ru gp2c-0.0.9pl3/src/genfunc.c gp2c/src/genfunc.c ---- gp2c-0.0.9pl3/src/genfunc.c 2014-12-16 10:56:21.000000000 +0100 -+++ gp2c/src/genfunc.c 2015-07-09 14:50:05.605098633 +0200 -@@ -524,13 +524,15 @@ - } - if (name) - { -- if (i>=nb) -- die(err_func,"too few arguments in lambda"); - fputs(" ",fout); - if (c=='p') - fprintf(fout,"prec"); - else -+ { -+ if (i>=nb) -+ die(err_func,"too few arguments in lambda"); - gencode(fout, name[i++]); -+ } - } - } - if (name && i=nb) -- die(err_func,"too few arguments in lambda"); - if (c=='p') -- fprintf(fout,"prec"); -+ { -+ if (mode&(1<=nb) -+ die(err_func,"too few arguments in lambda"); -+ if (!firstarg) fprintf(fout,", "); -+ firstarg=0; -+ ot = tree[name[i]].t; -+ tree[name[i]].t = t; -+ gencast(fout, name[i], ot); -+ tree[name[i]].t = ot; -+ i++; -+ } - } - if (name && iuser->defnode; - int savc=s_ctx.n; -@@ -650,7 +667,6 @@ - int res; - ctxvar *vres; - context *fc=block+gp->user->bl; -- gpfunc *wr=lfunc+wrap; - gpdescarg *rule; - if (!wr->proto.code) - die(wr->node,"Wrapper not defined"); -@@ -688,7 +704,7 @@ - fprintf(fout," = "); - } - fprintf(fout,"%s(",gp->proto.cname); -- firstarg=genwrapargs(fout, wrap, nb, stack); -+ firstarg=genwrapargs(fout, wrap, nb, stack, m); - for (i=0,d=1;is.n;i++) - { - ctxvar *v=fc->c+i; -diff -ru gp2c-0.0.9pl3/src/printnode.c gp2c/src/printnode.c ---- gp2c-0.0.9pl3/src/printnode.c 2013-10-12 20:32:23.000000000 +0200 -+++ gp2c/src/printnode.c 2015-07-09 14:50:05.606098645 +0200 -@@ -231,6 +231,10 @@ - fprintf(fout,"&"); - printnode(fout,x); - break; -+ case Fvararg: -+ printnode(fout,x); -+ fprintf(fout,"[..]"); -+ break; - case Fcall: - printnode(fout,x); - fprintf(fout,"("); -diff -ru gp2c-0.0.9pl3/src/topfunc.c gp2c/src/topfunc.c ---- gp2c-0.0.9pl3/src/topfunc.c 2014-12-16 10:53:09.000000000 +0100 -+++ gp2c/src/topfunc.c 2015-07-09 14:50:05.606098645 +0200 -@@ -109,7 +109,7 @@ - int nn = newanon(); - int nf = newnode(Fdeffunc,newnode(Ffunction,nn,x),y); - int seq = newnode(Fentry,nn,-1); -- topfunc(n,p,fun,pfun,nf,wr); -+ topfunc(nf,p,fun,pfun,nf,wr); - if (fun>=0) tree[n] = tree[seq]; - } - -diff -ru gp2c-0.0.9pl3/test2/gp/apply.gp gp2c/test2/gp/apply.gp ---- gp2c-0.0.9pl3/test2/gp/apply.gp 2013-06-11 12:02:48.000000000 +0200 -+++ gp2c/test2/gp/apply.gp 2015-07-09 14:50:05.609098682 +0200 -@@ -3,6 +3,7 @@ - f3(f,v)=apply(f,v) - g(x)=x^2+1 - f4(v)=apply(g,v) -+f5(z:small)=apply(n:small->(n+z)!,[1,2,3,4,5]) - - g1(v,z)=select(x->x>z,v) - g2(v)=select(isprime,v) -diff -ru gp2c-0.0.9pl3/test2/input/apply gp2c/test2/input/apply ---- gp2c-0.0.9pl3/test2/input/apply 2013-06-11 12:02:48.000000000 +0200 -+++ gp2c/test2/input/apply 2015-07-09 14:50:05.609098682 +0200 -@@ -2,6 +2,7 @@ - f2([1,2,3,4]) - f3(sqr,[1,2,3,4]) - f4([1,2,3,4]) -+f5(-1) - - g1([1,2,3,4],2) - g2([1,2,3,4]) -diff -ru gp2c-0.0.9pl3/test2/res/apply.res gp2c/test2/res/apply.res ---- gp2c-0.0.9pl3/test2/res/apply.res 2013-06-11 12:02:48.000000000 +0200 -+++ gp2c/test2/res/apply.res 2015-07-09 14:50:05.610098694 +0200 -@@ -2,6 +2,7 @@ - [1, 4, 9, 16] - [1, 4, 9, 16] - [2, 5, 10, 17] -+[1, 1, 2, 6, 24] - [3, 4] - [2, 3] - [2, 3] diff --git a/build/pkgs/gp2c/patches/global.patch b/build/pkgs/gp2c/patches/global.patch deleted file mode 100644 index dd0c24d0472..00000000000 --- a/build/pkgs/gp2c/patches/global.patch +++ /dev/null @@ -1,126 +0,0 @@ -Patch taken from upstream to fix global() - -diff --git a/src/genblock.c b/src/genblock.c -index a3582d2..e626e0f 100644 ---- a/src/genblock.c -+++ b/src/genblock.c -@@ -251,7 +251,11 @@ int genblockdeclaration(int args, int n, int flag, int type, int *seq) - /*Make sure GEN objects are gerepilable.*/ - val = newnode(Ftag, newnode(Ftag, newsmall(0), Ggen), tv); - if (decl==global) -- fillvar(var,flag,tv,val); -+ { -+ int f=fillvar(var,flag,tv,-1); -+ if (autogc) -+ affectval(f,val,seq); -+ } - else - pushvar(var,flag,tv,val); - break; -@@ -270,10 +274,12 @@ int genblockdeclaration(int args, int n, int flag, int type, int *seq) - val=newcall("_const_quote",newstringnode(entryname(stack[i]),-1)); - affectval(fillvar(stack[i],flag,mint,-1),val,seq); - } -- else if (autogc) -+ else - { -+ int f = fillvar(stack[i],flag,mint,-1); - /*Make sure (implicitly GEN) objects are gerepilable.*/ -- fillvar(stack[i],flag,mint, newsmall(0)); -+ if (autogc) -+ affectval(f, newsmall(0), seq); - } - break; - case function: -diff --git a/src/topfunc.c b/src/topfunc.c -index 872830e..bff787f 100644 ---- a/src/topfunc.c -+++ b/src/topfunc.c -@@ -154,13 +154,12 @@ static int topfuncproto(int n, int fun, int pfun, int nf) - break; - case 'I': - case 'E': -- if (wr>=-1) -- { -- case 'J': -- seq = newnode(Flambda,var,newleaf(a)); -- tree[a] = tree[seq]; -- topfunclambda(a, n, fun, pfun, wr); -- } -+ if (wr<-1) -+ break; -+ case 'J': /* Fall through */ -+ seq = newnode(Flambda,var,newleaf(a)); -+ tree[a] = tree[seq]; -+ topfunclambda(a, n, fun, pfun, wr); - break; - } - break; -diff --git a/src/toplevel.c b/src/toplevel.c -index f5999c4..23c92a8 100644 ---- a/src/toplevel.c -+++ b/src/toplevel.c -@@ -120,7 +120,7 @@ void gentoplevel(int n) - } - else - gentoplevel(x); -- if (y>=0 && tree[y].f!=Fdeffunc) -+ if (y>=0 && tree[y].f!=Fdeffunc && tree[y].f!=Fseq) - { - int dy = detag(y); - if (isfunc(dy,"global")) -diff --git a/test/gp/initfunc.gp b/test/gp/initfunc.gp -index 03c1dd3..ed0b87d 100644 ---- a/test/gp/initfunc.gp -+++ b/test/gp/initfunc.gp -@@ -1,4 +1,4 @@ --global(y:var,n:small=0,m:small,k=2,L:vec,T2) -+global(y:var='y,n:small=0,m:small,k=2,L:vec,T2) - T=clone([4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0]) - T2=clone([4,3,2,2,1,1,1,1,0,0,0,0,0,0,0,0]) - L=[]~; -@@ -15,3 +15,10 @@ aff()=print(n," ",m); - M=[1,2;3,4] - print(x^4+k); - pow_M(k)=M^k -+global(x1:int,x2:int); -+global(y1,y2):int; -+global(z1,z2); -+f1(x)=my(z=x^2+1);if(z==1,x1=x,x2=x);z -+f2(x)=my(z=x^2+1);if(z==1,y1=x,y2=x);z -+f3(x)=my(z=x^2+1);if(z==1,z1=x,z2=x);z -+global(t1);t1=0 -diff --git a/test/input/initfunc b/test/input/initfunc -index 8506d08..46a126d 100644 ---- a/test/input/initfunc -+++ b/test/input/initfunc -@@ -2,3 +2,9 @@ init_initfunc(); print(pow_M(2)) - vector(100,i,mybfffo(i)) - vector(31,i,mybfffo(1<<(i-1))) - for(i=1,5,count();aff()) -+init_initfunc();f1(0) -+init_initfunc();f1(1) -+init_initfunc();f2(0) -+init_initfunc();f2(1) -+init_initfunc();f3(0) -+init_initfunc();f3(1) -diff --git a/test/res/initfunc.res b/test/res/initfunc.res -index 391d09d..a1e538c 100644 ---- a/test/res/initfunc.res -+++ b/test/res/initfunc.res -@@ -13,3 +13,15 @@ x^4 + 2 - 3 30 - 4 30 - 5 29 -+x^4 + 2 -+1 -+x^4 + 2 -+2 -+x^4 + 2 -+1 -+x^4 + 2 -+2 -+x^4 + 2 -+1 -+x^4 + 2 -+2 diff --git a/build/pkgs/ipykernel/checksums.ini b/build/pkgs/ipykernel/checksums.ini index d0e31288fed..8bb2f620751 100644 --- a/build/pkgs/ipykernel/checksums.ini +++ b/build/pkgs/ipykernel/checksums.ini @@ -1,4 +1,4 @@ tarball=ipykernel-VERSION.tar.gz -sha1=779512583ddd1ad257ce93ea4d326a55b5da91a0 -md5=6093813ebd95b790c7eb4c9841bd07ce -cksum=3904063097 +sha1=968c9f9271543a8036f27d4e69f10d2805105d34 +md5=e920441d231e6b659622033849fb3218 +cksum=4028788610 diff --git a/build/pkgs/ipykernel/package-version.txt b/build/pkgs/ipykernel/package-version.txt index 627a3f43a64..af8c8ec7c13 100644 --- a/build/pkgs/ipykernel/package-version.txt +++ b/build/pkgs/ipykernel/package-version.txt @@ -1 +1 @@ -4.1.1 +4.2.2 diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index 785d210c674..283cee09323 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,4 +1,4 @@ tarball=ipython-VERSION.tar.gz -sha1=26fa227053cf1b0387459a72b53b1818140831f7 -md5=c2fecbcf1c0fbdc82625c77a50733dd6 -cksum=2156781632 +sha1=2491e9f9512ae2b622e600242992a7d54ac4b3d0 +md5=3da622447b3b7ca7d41c868c80bb8b0e +cksum=2683101910 diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index fcdb2e109f6..627a3f43a64 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -4.0.0 +4.1.1 diff --git a/build/pkgs/ipywidgets/checksums.ini b/build/pkgs/ipywidgets/checksums.ini index adc4eb03de7..947c6559dbe 100644 --- a/build/pkgs/ipywidgets/checksums.ini +++ b/build/pkgs/ipywidgets/checksums.ini @@ -1,4 +1,4 @@ tarball=ipywidgets-VERSION.tar.gz -sha1=3e9f86e670fd41feb7a597088ab023471e668962 -md5=8c03ee965b91a8f626d5505b41af8031 -cksum=3467449425 +sha1=600354ce60cdbde316d624f596dd2d516ea712c6 +md5=491affdef8e0586eeeb299226ff634aa +cksum=3253471659 diff --git a/build/pkgs/ipywidgets/package-version.txt b/build/pkgs/ipywidgets/package-version.txt index 4d54daddb61..627a3f43a64 100644 --- a/build/pkgs/ipywidgets/package-version.txt +++ b/build/pkgs/ipywidgets/package-version.txt @@ -1 +1 @@ -4.0.2 +4.1.1 diff --git a/build/pkgs/jinja2/checksums.ini b/build/pkgs/jinja2/checksums.ini index 1f3635ea5e8..45fdd1784b2 100644 --- a/build/pkgs/jinja2/checksums.ini +++ b/build/pkgs/jinja2/checksums.ini @@ -1,4 +1,4 @@ tarball=jinja2-VERSION.tar.gz -sha1=25ab3881f0c1adfcf79053b58de829c5ae65d3ac -md5=b9dffd2f3b43d673802fe857c8445b1a -cksum=3563905877 +sha1=4a33c1a0fd585eba2507e8c274a9cd113b1d13ab +md5=edb51693fe22c53cee5403775c71a99e +cksum=2405780533 diff --git a/build/pkgs/jinja2/package-version.txt b/build/pkgs/jinja2/package-version.txt index 2c9b4ef42ec..a4412fa745d 100644 --- a/build/pkgs/jinja2/package-version.txt +++ b/build/pkgs/jinja2/package-version.txt @@ -1 +1 @@ -2.7.3 +2.8 diff --git a/build/pkgs/jmol/spkg-install b/build/pkgs/jmol/spkg-install index 1cadd145795..f1599945174 100755 --- a/build/pkgs/jmol/spkg-install +++ b/build/pkgs/jmol/spkg-install @@ -14,15 +14,6 @@ if [ ! -d "$SPKGDIR"/patches ]; then exit 1 fi -# Check for sagenb location -if [ ! -d "$SAGE_ROOT/devel/sagenb/sagenb/data/jmol" ]; then - echo "No old Jmol install in notebook. Skipping removal of Jmol from notebook." -else - echo "Removing Jmol files from the notebook data directory..." - rm -r "$SAGE_ROOT/devel/sagenb/sagenb/data/jmol" -fi - - #pure javascript for web and java applet in separate /share/jsmol directory. if [ ! -d "$SAGE_LOCAL"/share/jsmol ]; then echo "Directory "$SAGE_LOCAL"/share/jsmol does not exist. Creating directory..." diff --git a/build/pkgs/jsonschema/checksums.ini b/build/pkgs/jsonschema/checksums.ini index 4ee73728a48..1d8994701e3 100644 --- a/build/pkgs/jsonschema/checksums.ini +++ b/build/pkgs/jsonschema/checksums.ini @@ -1,4 +1,4 @@ tarball=jsonschema-VERSION.tar.gz -sha1=35d4d90ce942a4b030eda75e9a2b32f97e121a98 -md5=661f85c3d23094afbb9ac3c0673840bf -cksum=1595732496 +sha1=c30d415d250699630f302acb8efd660dd006a7ca +md5=374e848fdb69a3ce8b7e778b47c30640 +cksum=3533981975 diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index 643eeddd35b..e7cca5843ce 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(VCVERSIONER) $(INST)/$(FUNCTOOLS32) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/jsonschema/package-version.txt b/build/pkgs/jsonschema/package-version.txt index 197c4d5c2d7..73462a5a134 100644 --- a/build/pkgs/jsonschema/package-version.txt +++ b/build/pkgs/jsonschema/package-version.txt @@ -1 +1 @@ -2.4.0 +2.5.1 diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index a11dab18918..195fea94cce 100644 --- a/build/pkgs/jupyter_client/checksums.ini +++ b/build/pkgs/jupyter_client/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_client-VERSION.tar.gz -sha1=ac279b6aea424b793e2a13c59c37b21dc05b535c -md5=11ec05c5356c86ea3fe6b6008732874c -cksum=145697732 +sha1=6adaeb2bca9b7fcb31efad339cee910d04c0f632 +md5=2a90f6d3ebe6e7f3602aa2a3956f613d +cksum=2559810570 diff --git a/build/pkgs/jupyter_client/package-version.txt b/build/pkgs/jupyter_client/package-version.txt index fcdb2e109f6..627a3f43a64 100644 --- a/build/pkgs/jupyter_client/package-version.txt +++ b/build/pkgs/jupyter_client/package-version.txt @@ -1 +1 @@ -4.0.0 +4.1.1 diff --git a/build/pkgs/jupyter_core/checksums.ini b/build/pkgs/jupyter_core/checksums.ini index 5a8c1115dff..6498e1b8bb7 100644 --- a/build/pkgs/jupyter_core/checksums.ini +++ b/build/pkgs/jupyter_core/checksums.ini @@ -1,4 +1,4 @@ tarball=jupyter_core-VERSION.tar.gz -sha1=cfa33cb28883c3547133949c69af532b02866d28 -md5=1d24d79414b8ec18216bf490e101313a -cksum=1145384470 +sha1=4366f00b33d59fa9ff69fc2baa90ac218590a88b +md5=af6eec7c93b692db007b209a9ce8e52f +cksum=638636473 diff --git a/build/pkgs/jupyter_core/package-version.txt b/build/pkgs/jupyter_core/package-version.txt index c5106e6d139..d13e837c8ec 100644 --- a/build/pkgs/jupyter_core/package-version.txt +++ b/build/pkgs/jupyter_core/package-version.txt @@ -1 +1 @@ -4.0.4 +4.0.6 diff --git a/build/pkgs/meataxe/SPKG.txt b/build/pkgs/meataxe/SPKG.txt new file mode 100644 index 00000000000..9892003f3e2 --- /dev/null +++ b/build/pkgs/meataxe/SPKG.txt @@ -0,0 +1,27 @@ += MeatAxe = + +== Description == + +The MeatAxe is a set of programs for working with matrix representations +over finite fields. Permutation representations are supported to some +extent, too. + +The MeatAxe is developed for the UNIX operating system. Supported platforms +include Linux (x86), SunOS/Solaris (Sparc), HP/UX, DEC OSF/1 (Alpha), and +Windows NT 4.0 (x86, Alpha, PPC). + +== License == + +The C Meat-Axe is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 2 of the License, or (at your option) any later +version. + +== Upstream Contact == + +Michael Ringe (mringe@math.rwth-aachen.de) + +== Special Update/Build Instructions == + +The original upstream tarball was re-packaged, so that it unpacks into a single +folder called meataxe-2.4.24 diff --git a/build/pkgs/meataxe/checksums.ini b/build/pkgs/meataxe/checksums.ini new file mode 100644 index 00000000000..b4d4356ad4e --- /dev/null +++ b/build/pkgs/meataxe/checksums.ini @@ -0,0 +1,4 @@ +tarball=meataxe-VERSION.tar.gz +sha1=0aa4313cc430c78e058068feba805428ef2324aa +md5=e0f384e37a69671c73c2904e4e69dc01 +cksum=3083268116 diff --git a/build/pkgs/meataxe/dependencies b/build/pkgs/meataxe/dependencies new file mode 100644 index 00000000000..2f9f3849682 --- /dev/null +++ b/build/pkgs/meataxe/dependencies @@ -0,0 +1 @@ +# no dependencies diff --git a/build/pkgs/meataxe/package-version.txt b/build/pkgs/meataxe/package-version.txt new file mode 100644 index 00000000000..208b2a0070d --- /dev/null +++ b/build/pkgs/meataxe/package-version.txt @@ -0,0 +1 @@ +2.4.24.p1 diff --git a/build/pkgs/meataxe/patches/IO_fixes.patch b/build/pkgs/meataxe/patches/IO_fixes.patch new file mode 100644 index 00000000000..933908be6be --- /dev/null +++ b/build/pkgs/meataxe/patches/IO_fixes.patch @@ -0,0 +1,78 @@ +Read and create library files in the directory given by MtxLibDir. + +The patch keeps a promise given by upstream. + +AUTHOR: Simon King 2015-09-18, simon.king@uni-jena.de + +diff --git a/src/maketabF.c b/src/maketabF.c +index fa03eda..d7af83e 100644 +--- a/src/maketabF.c ++++ b/src/maketabF.c +@@ -319,7 +319,7 @@ static void writeheader() + int i, j; + + sprintf(filename,"p%3.3ld.zzz",Q); +- fd = SysFopen(filename,FM_CREATE); ++ fd = SysFopen(filename,FM_CREATE|FM_LIB); + if (fd == NULL) + { + perror(filename); +diff --git a/src/os.c b/src/os.c +index a7f4271..b07b971 100644 +--- a/src/os.c ++++ b/src/os.c +@@ -227,25 +227,31 @@ FILE *SysFopen(const char *name, int mode) + MTX_ERROR1("Invalid file mode %d",mode); + return NULL; + } +- f = fopen(name,fmodes[m]); +- if (f != NULL) +- return f; + + /* Search library directory + ------------------------ */ + if ((mode & FM_LIB) != 0) + { +- strcpy(buf,MtxLibDir); +- strcat(buf,"/"); +- strcat(buf,name); +- f = fopen(buf,fmodes[m]); ++ if (*MtxLibDir != 0) ++ { ++ strcpy(buf,MtxLibDir); ++ strcat(buf,"/"); ++ strcat(buf,name); ++ f = fopen(buf,fmodes[m]); ++ } ++ else ++ f = fopen(name,fmodes[m]); + } +- ++ else ++ { ++ f = fopen(name,fmodes[m]); ++ } ++ if (f != NULL) ++ return f; + /* Error handling + -------------- */ + if (f == NULL && (mode & FM_NOERROR) == 0) +- MTX_ERROR1("%s: %S",name); +- ++ MTX_ERROR1("%s: %S",name); + return f; + } + +diff --git a/src/zcv.c b/src/zcv.c +index a9ad7a3..763c9fb 100644 +--- a/src/zcv.c ++++ b/src/zcv.c +@@ -584,7 +584,7 @@ static int Init(int argc, const char **argv) + inpname = App->ArgV[0]; + if (strcmp(inpname,"-")) + { +- src = SysFopen(inpname,FM_READ|FM_TEXT|FM_LIB); ++ src = SysFopen(inpname,FM_READ|FM_TEXT); + if (src == NULL) + { + MTX_ERROR1("Cannot open %s",inpname); diff --git a/build/pkgs/meataxe/patches/StrassenWinogradImplementation.patch b/build/pkgs/meataxe/patches/StrassenWinogradImplementation.patch new file mode 100644 index 00000000000..43664cf6560 --- /dev/null +++ b/build/pkgs/meataxe/patches/StrassenWinogradImplementation.patch @@ -0,0 +1,1246 @@ +Implement Strassen-Winograd multiplication in MeatAxe. + +We use the schedule from Douglas-Heroux-Slishman-Smith; +see also Boyer-Pernet-Zhou, "Memory efficient scheduling of +Strassen-Winograd's matrix multiplication algorithm", +Table 1 (ISSAC 2009). + +AUTHOR: Simon King 2015-09-19, simon.king@uni-jena.de + +diff --git a/Makefile b/Makefile +index b78e244..2ada31e 100644 +--- a/Makefile ++++ b/Makefile +@@ -88,6 +88,7 @@ LIB_OBJS=\ + temap \ + tkinfo vec2mat \ + wgen \ ++ window \ + zcleanrow zcmprow zgap zpermrow \ + zzz2 \ + version +diff --git a/src/kernel-0.c b/src/kernel-0.c +index 4f0a973..178b6cb 100644 +--- a/src/kernel-0.c ++++ b/src/kernel-0.c +@@ -24,8 +24,8 @@ + MTX_DEFINE_FILE_INFO + + typedef unsigned char BYTE; +-static int MPB = 0; /* No. of marks per byte */ +-static int LPR = 0; /* Long ints per row */ ++int MPB = 0; /* No. of marks per byte */ ++int LPR = 0; /* Long ints per row */ + + + +@@ -646,7 +646,7 @@ PTR FfAddRow(PTR dest, PTR src) + + + /** +- ** Add a part two rows. ++ ** Add a part of two rows. + ** This works like FfAddRow(), but the operation is performed only on a given range of + ** columns. Note that the working range is not specified as column indexes but in units of + ** long integers! +@@ -707,7 +707,217 @@ PTR FfAddRowPartial(PTR dest, PTR src, int first, int len) + return dest; + } + ++/** ++ ** Subtract two rows. ++ ** This function subtracts src from dest. Field order and row size must have been set before. ++ ** @param dest The row to subtract from. ++ ** @param src The row to subtract. ++ ** @return Always returns dest. ++ **/ ++ ++PTR FfSubRow(PTR dest, PTR src) ++{ ++ register int i; ++ ++ if (FfChar == 2) /* characteristic 2 is simple... */ ++ { ++#ifdef ASM_MMX ++ /* This assumes Intel with 4 bytes per long, but MMX implies Intel anyway.*/ ++ __asm__( ++ " pushl %ebx\n" ++ " pushl %ecx\n" ++ " pushl %edx\n" + ++ " movl 8(%ebp),%ecx\n" ++ " movl 12(%ebp),%ebx\n" ++ " movl LPR,%edx\n" ++ " sarl $1,%edx\n" ++ " je .SUBROW2\n" ++ " .align 16\n" ++ ".SUBROW1:\n" ++ " movq (%ebx),%mm0\n" ++ " addl $8,%ebx\n" ++ " pxor (%ecx),%mm0\n" ++ " movq %mm0,(%ecx)\n" ++ " addl $8,%ecx\n" ++ " decl %edx\n" ++ " jne .SUBROW1\n" ++ ".SUBROW2:\n" ++ " popl %edx\n" ++ " popl %ecx\n" ++ " popl %ebx\n" ++ ); ++#else ++ register long *l1 = (long *) dest; ++ register long *l2 = (long *) src; ++ for (i = LPR; i != 0; --i) ++ { ++ register long x = *l2++; ++ if (x != 0) *l1 ^= x; ++ l1++; ++ } ++#endif ++ } ++ else /* any other characteristic */ ++ { ++ FEL *table_inv = mtx_tmult[mtx_taddinv[FF_ONE]]; ++#ifdef ASM_MMX ++ register BYTE *p1 = dest; ++ register unsigned long *p2 = (unsigned long *) src; ++ for (i = LPR; i != 0; --i) ++ { ++ register unsigned long a; ++ if ((a = *p2++) != 0) { ++ *p1++ = mtx_tadd[*p1][table_inv[a & 0xffL]]; ++ a >>= 8; ++ *p1++ = mtx_tadd[*p1][table_inv[a & 0xffL]]; ++ a >>= 8; ++ *p1++ = mtx_tadd[*p1][table_inv[a & 0xffL]]; ++ a >>= 8; ++ *p1++ = mtx_tadd[*p1][table_inv[a & 0xffL]]; ++ } else ++ p1 += 4; ++ } ++#else ++ register FEL *p1 = dest; ++ register FEL *p2 = src; ++ for (i = FfTrueRowSize(FfNoc); i != 0; --i) ++ { ++ register int x = *p2++; ++ if (x != 0) *p1 = mtx_tadd[*p1][table_inv[x]]; ++ p1++; ++ } ++#endif ++ } ++ return dest; ++} ++ ++ ++/** ++ ** Subtract a part of two rows. ++ ** This works like FfSubRow(), but the operation is performed only on a given range of ++ ** columns. Note that the working range is not specified as column indexes but in units of ++ ** long integers! ++ ** @param dest The row to subtract from. ++ ** @param src The row to subtract. ++ ** @param first Number of long integers to skip. ++ ** @param len Number of long integers to add. ++ ** @return Always returns dest. ++ **/ ++ ++PTR FfSubRowPartial(PTR dest, PTR src, int first, int len) ++{ ++ register long i; ++ ++ if (FfChar == 2) /* characteristic 2 is simple... */ ++#ifdef ASM_MMX ++ __asm__("\n movl 8(%ebp),%ecx\n" ++ " movl 12(%ebp),%ebx\n" ++ " movl 16(%ebp),%edx\n" ++ " sall $2,%edx\n" ++ " addl %edx,%ecx\n" ++ " addl %edx,%ebx\n" ++ " movl 20(%ebp),%edx\n" ++ " sarl $1,%edx\n" ++ " je .SUBROWPART_1\n" ++ " .align 16\n" ++ ".SUBROWPART_2:\n" ++ " movq (%ebx),%mm0\n" ++ " addl $8,%ebx\n" ++ " pxor (%ecx),%mm0\n" ++ " movq %mm0,(%ecx)\n" ++ " addl $8,%ecx\n" ++ " decl %edx\n" ++ " jne .SUBROWPART_2\n" ++ ".SUBROWPART_1:\n" ++ ); ++#else ++ { register long *l1 = (long *) dest + first; ++ register long *l2 = (long *) src + first; ++ for (i = len; i != 0; --i) ++ { ++ register long x = *l2++; ++ *l1 ^= x; ++ l1++; ++ } ++ } ++#endif ++ else /* any other characteristic */ ++ { FEL *table_inv = mtx_tmult[mtx_taddinv[FF_ONE]]; ++ register BYTE *p1 = dest + first * sizeof(long); ++ register BYTE *p2 = src + first * sizeof(long); ++ for (i = len*sizeof(long); i != 0; --i) ++ { ++ register int x = *p2++; ++ *p1 = mtx_tadd[*p1][table_inv[x]]; ++ p1++; ++ } ++ } ++ return dest; ++} ++ ++ ++/** ++ ** Subtract a part of two rows. ++ ** The difference to FfSubRowPartial is that dest is replaced ++ ** by src-dest, not by dest-src. ++ ** @param dest The row to subtract. ++ ** @param src The row to subtract from. ++ ** @param first Number of long integers to skip. ++ ** @param len Number of long integers to add. ++ ** @return Always returns dest. ++ **/ ++ ++PTR FfSubRowPartialReverse(PTR dest, PTR src, int first, int len) ++{ ++ register long i; ++ ++ if (FfChar == 2) /* characteristic 2 is simple... */ ++#ifdef ASM_MMX ++ __asm__("\n movl 8(%ebp),%ecx\n" ++ " movl 12(%ebp),%ebx\n" ++ " movl 16(%ebp),%edx\n" ++ " sall $2,%edx\n" ++ " addl %edx,%ecx\n" ++ " addl %edx,%ebx\n" ++ " movl 20(%ebp),%edx\n" ++ " sarl $1,%edx\n" ++ " je .SUBROWPART_1\n" ++ " .align 16\n" ++ ".SUBROWPART_2:\n" ++ " movq (%ebx),%mm0\n" ++ " addl $8,%ebx\n" ++ " pxor (%ecx),%mm0\n" ++ " movq %mm0,(%ecx)\n" ++ " addl $8,%ecx\n" ++ " decl %edx\n" ++ " jne .SUBROWPART_2\n" ++ ".SUBROWPART_1:\n" ++ ); ++#else ++ { register long *l1 = (long *) dest + first; ++ register long *l2 = (long *) src + first; ++ for (i = len; i != 0; --i) ++ { ++ register long x = *l2++; ++ *l1 ^= x; ++ l1++; ++ } ++ } ++#endif ++ else /* any other characteristic */ ++ { FEL *table_inv = mtx_tmult[mtx_taddinv[FF_ONE]]; ++ register BYTE *p1 = dest + first * sizeof(long); ++ register BYTE *p2 = src + first * sizeof(long); ++ for (i = len*sizeof(long); i != 0; --i) ++ { ++ register int x = *p2++; ++ *p1 = mtx_tadd[table_inv[*p1]][x]; ++ p1++; ++ } ++ } ++ return dest; ++} + + + /** +diff --git a/src/meataxe.h b/src/meataxe.h +index 819e88e..e2f5a84 100644 +--- a/src/meataxe.h ++++ b/src/meataxe.h +@@ -107,6 +107,9 @@ extern int FfChar; /**< Current characteristic */ + extern FEL FfGen; /**< Generator */ + extern int FfNoc; /**< Number of columns for row ops */ + extern size_t FfCurrentRowSize; ++extern int FfCurrentRowSizeIo; ++extern int MPB; /** No. of marks per byte */ ++extern int LPR; /** Long ints per row */ + + + /* Arithmetic */ +@@ -125,6 +128,9 @@ int FfSetNoc(int noc); + void FfAddMulRow(PTR dest, PTR src, FEL f); + PTR FfAddRow(PTR dest, PTR src); + PTR FfAddRowPartial(PTR dest, PTR src, int first, int len); ++PTR FfSubRow(PTR dest, PTR src); ++PTR FfSubRowPartial(PTR dest, PTR src, int first, int len); ++PTR FfSubRowPartialReverse(PTR dest, PTR src, int first, int len); + PTR FfAlloc(int nor); + int FfCmpRows(PTR p1, PTR p2); + void FfCleanRow(PTR row, PTR matrix, int nor, const int *piv); +@@ -519,6 +525,8 @@ int MatIsValid(const Matrix_t *m); + Matrix_t *MatLoad(const char *fn); + Matrix_t *MatMul(Matrix_t *dest, const Matrix_t *src); + Matrix_t *MatMulScalar(Matrix_t *dest, FEL coeff); ++Matrix_t *MatMulStrassen(Matrix_t *dest, const Matrix_t *A, const Matrix_t *B); ++void StrassenSetCutoff(size_t size); + long MatNullity(const Matrix_t *mat); + long MatNullity__(Matrix_t *mat); + Matrix_t *MatNullSpace(const Matrix_t *mat); +diff --git a/src/window.c b/src/window.c +new file mode 100644 +index 0000000..f374028 +--- /dev/null ++++ b/src/window.c +@@ -0,0 +1,944 @@ ++/* ========================== C MeatAxe ============================= ++ window.c - Matrix window operations and Strassen-Winograd multiplication ++ ++ (C) Copyright 2015 Simon King, Institut fuer Mathematik, ++ FSU Jena, Germany ++ This program is free software; see the file COPYING for details. ++ ================================================================== */ ++ ++#include ++#include ++#include ++#include "meataxe.h" ++ ++/* -------------------------------------------------------------------------- ++ Local data ++ -------------------------------------------------------------------------- */ ++ ++MTX_DEFINE_FILE_INFO ++ ++typedef unsigned char BYTE; ++ ++typedef struct ++{ ++ int Nor; /* #rows of the window */ ++ size_t RowSize; /* size of window rows in long integers */ ++ Matrix_t *Matrix; /* ambient matrix containing the window */ ++ PTR ULCorner; /* Pointer to the upper left window corner */ ++} ++ MatrixWindow_t; ++ ++size_t cutoff = sizeof(long)/2; ++ ++/** The divide-and-conquer approach is only done for ++ * matrices with at least "cutoff*MPB*sizeof(long)" rows which ++ * are formed by at least "cutoff" longs. ++ * ++ * The above rule means that the "critical matrices" are square. ++ **/ ++void StrassenSetCutoff(size_t size) ++{ if (size) ++ cutoff = size; ++ else ++ cutoff = sizeof(long)/2; ++} ++ ++/* ------------------------------------------------------------------ ++ ++ Allocation and deallocation of a matrix window ++ ++ ------------------------------------------------------------------ */ ++/** ++ * Note that the rowsize is given in long, not in byte. The reason is ++ * functions such as FfAddRowPartial or FfAddMapRowWindow internally ++ * operating on longs. By consequence, in the Strassen-Winograd ++ * multiplication algorithm, we have to divide our matrix rows ++ * into longs, not into bytes. ++ **/ ++ ++/* Allocation with initialisation */ ++/* Create an empty matrix that is identical with the window. */ ++/* fl is the field size, nor is the number of rows. rowsize is */ ++/* the size of a row in longs. */ ++MatrixWindow_t *WindowAlloc(int fl, int nor, size_t rowsize) ++{ ++ MatrixWindow_t *out; ++ out = ALLOC(MatrixWindow_t); ++ if (out == NULL) ++ { ++ MTX_ERROR1("%E",MTX_ERR_NOMEM); ++ return NULL; ++ } ++ FfSetField(fl); ++ out->Matrix = MatAlloc(fl, nor, rowsize*sizeof(long)*MPB); ++ if (out->Matrix == NULL) ++ { ++ free(out); ++ MTX_ERROR1("%E",MTX_ERR_NOMEM); ++ return NULL; ++ } ++ out->ULCorner = out->Matrix->Data; ++ out->Nor = nor; ++ out->RowSize = rowsize; ++ return out; ++} ++ ++/** WARNING: Only to be used if the surrounding matrix can be destroyed ++ Otherwise, just do free(m)! **/ ++void WindowFree(MatrixWindow_t *m) ++{ ++ if (m->Matrix != NULL) ++ { ++ MatFree(m->Matrix); ++ } ++ free(m); ++} ++ ++/* ------------------------------------------------------------------ ++ * Auxiliary / Debugging ++ ----------------------------------------------------------------- */ ++ ++void WindowShow(MatrixWindow_t *A) ++{ ++long i,j; ++PTR p = A->ULCorner; ++FfSetNoc(A->Matrix->Noc); ++for (i=A->Nor; i>0; i--, FfStepPtr(&p)) ++ { ++ for (j=0; j< (A->RowSize)*sizeof(long); j++) ++ printf("%3.3d ", (unsigned char)p[j]); ++ printf("\n"); ++ } ++} ++ ++/** ++ ** Overwrite the window by zeroes, but let the ++ ** rest of the ambient matrix untouched ++ **/ ++ ++void WindowClear(MatrixWindow_t *A) ++{ ++register long i; ++register size_t rowsize = A->RowSize*sizeof(long); ++PTR p = A->ULCorner; ++FfSetNoc(A->Matrix->Noc); ++for (i=A->Nor; i>0; i--, FfStepPtr(&p)) ++{ memset(p, FF_ZERO, rowsize); } ++} ++ ++/** ++ ** Multiply a vector by a matrix window. ++ ** This function multiplies the vector @em row from the right by the matrix window ++ ** @em mat and adds the result into @em result. ++ ** The number of columns in both @em mat and @em result is determined by @em rowsize. ++ ** @attention @em result and @em row must not overlap. Otherwise the result is ++ ** undefined. ++ ** @param row The source vector (nor columns). ++ ** @param matrix A matrix window (nor by (rowsize*sizeof(long)*MPB)) of a matrix whose rowsize is FfCurrRowSize. ++ ** @param nor number of rows in the matrix window. ++ ** @param[out] result The resulting vector ((rowsize*sizeof(long)*MPB) columns). ++ ** @param rowsize number of longs forming a row of @em mat. ++ **/ ++ ++void FfAddMapRowWindow(PTR row, PTR matrix, int nor, PTR result, size_t rowsize) ++ ++{ ++ register int i; ++ register FEL f; ++ BYTE *m = (BYTE *) matrix; ++ ++#ifdef DEBUG ++ if (result >= row && result < row + FfRowSize(nor)) ++ MTX_ERROR("row and result overlap: undefined result!"); ++ if (row >= result && row < result + (rowsize*sizeof(long))) ++ MTX_ERROR("row and result overlap: undefined result!"); ++#endif ++ ++ if (FfOrder == 2) /* GF(2) is a special case */ ++ { ++ register long *x1 = (long *) matrix; ++ register BYTE *r = (BYTE *) row; ++ ++ for (i = nor; i > 0; ++r) ++ { ++ register BYTE mask; ++ if (*r == 0) ++ { ++ i -= 8; ++ x1 += 8 * LPR; /* Skip 8 rows of the matrix window in the ambient matrix*/ ++ continue; ++ } ++ for (mask = 0x80; mask != 0 && i > 0; mask >>= 1, --i) ++ { ++ if ((mask & *r) == 0) ++ { ++ x1 += LPR; /* Skip a single row */ ++ continue; ++ } ++ ++#ifdef ASM_MMX ++__asm__(" pushl %ebx\n"); ++__asm__(" movl %0,%%ebx" : : "g" (x1) ); ++__asm__(" pushl %ecx\n" ++ " pushl %edx\n" ++ " movl 20(%ebp),%ecx\n" /* result */ ++ ); ++__asm__ ( ++ " movl 24(%ebp),%edx\n" /* this time, it is rowsize, not LPR */ ++ " sarl $1,%edx\n" ++ " je .FASTXOR_1\n" ++ " .align 16\n" ++ ".FASTXOR_2:\n" ++ " movq (%ebx),%mm0\n" ++ " addl $8,%ebx\n" ++ " pxor (%ecx),%mm0\n" ++ " movq %mm0,(%ecx)\n" ++ " addl $8,%ecx\n" ++ " decl %edx\n" ++ " jne .FASTXOR_2\n" ++ ".FASTXOR_1:\n" ++ " popl %edx\n" ++ " popl %ecx\n"); ++__asm__(" movl %%ebx,%0" : : "g" (x1) ); ++__asm__(" popl %ebx\n" ++ ); ++#else ++ { ++ register long *x2 = (long *)result; ++ register int k; ++ for (k = rowsize; k; --k) ++ *x2++ ^= *x1++; ++ /* Now, x1 points to the first item ++ * after the current line of the window. ++ * We need to move it to the first position ++ * of the next line of the window. ++ */ ++ x1 += (LPR-rowsize); ++ } ++#endif ++ } ++ } ++ } ++ else /* Any other field */ ++ { ++ register BYTE *brow = (BYTE *) row; ++ register int pos = 0; ++ size_t l_rowsize = rowsize*sizeof(long); ++ for (i = nor; i > 0; --i) ++ { ++ f = mtx_textract[pos][*brow]; ++ if (++pos == (int) MPB) ++ { ++ pos = 0; ++ ++brow; ++ } ++ if (f != FF_ZERO) ++ { ++ register BYTE *v = m; ++ register BYTE *r = result; ++ if (f == FF_ONE) ++ { ++ register size_t k = l_rowsize; ++ for (; k != 0; --k) ++ { ++ *r = mtx_tadd[*r][*v++]; ++ ++r; ++ } ++ } ++ else ++ { ++ register BYTE *multab = mtx_tmult[f]; ++ register size_t k = l_rowsize; ++ for (; k != 0; --k) ++ { ++ if (*v != 0) ++ *r = mtx_tadd[multab[*v]][*r]; ++ ++v; ++ ++r; ++ } ++ } ++ } ++ m += FfCurrentRowSize; /* next row of window in the ambient matrix */ ++ } ++ } ++} ++ ++/** dest := left+right ++ left and right must be distinct, but one of them may coincide with dest -- under the assumption ++ that, in that case, the ambient matrices coincide as well. **/ ++MatrixWindow_t *WindowSum(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) ++{ ++ PTR x, result, tmp; ++ int i; ++ ++ int lnoc, rnoc, dnoc; ++ ++ FfSetField(left->Matrix->Field); ++ if (left->Matrix->Field != right->Matrix->Field || (left->Nor != right->Nor) || (left->RowSize != right->RowSize)) ++ { ++ MTX_ERROR1("Windows cannot be added: %E", MTX_ERR_INCOMPAT); ++ return NULL; ++ } ++ size_t rowsize = left->RowSize; ++ ++ lnoc = left->Matrix->Noc; ++ rnoc = right->Matrix->Noc; ++ dnoc = dest->Matrix->Noc; ++ /* We have to distinguish cases as to whether dest ++ is equal to either left or right */ ++ result = dest->ULCorner; ++ if (left->ULCorner == dest->ULCorner) ++ { /* we write into left */ ++ x = right->ULCorner; ++ for (i = left->Nor; i != 0; --i) ++ { ++ FfAddRowPartial(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(rnoc); ++ FfStepPtr(&x); ++ } ++ } ++ else if (right->ULCorner == dest->ULCorner) ++ { /* we write into right */ ++ x = left->ULCorner; ++ for (i = left->Nor; i != 0; --i) ++ { ++ FfAddRowPartial(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(lnoc); ++ FfStepPtr(&x); ++ } ++ } ++ else ++ { /* we need to copy left into dest first */ ++ x = right->ULCorner; ++ tmp = left->ULCorner; ++ size_t l_rowsize = rowsize * sizeof(long); ++ for (i = left->Nor; i != 0; --i) ++ { ++ memcpy(result, tmp, l_rowsize); ++ FfSetNoc(lnoc); ++ FfStepPtr(&tmp); ++ FfAddRowPartial(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(rnoc); ++ FfStepPtr(&x); ++ } ++ } ++ return dest; ++} ++ ++/** dest := left-right ++ left and right must be distinct, but one of them may coincide with dest -- under the assumption ++ that, in that case, the ambient matrices coincide as well. ++**/ ++MatrixWindow_t *WindowDif(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) ++{ ++ PTR x, result, tmp; ++ int i; ++ int lnoc, rnoc, dnoc; ++ ++ FfSetField(left->Matrix->Field); ++ if (left->Matrix->Field != right->Matrix->Field || (left->Nor != right->Nor) || (left->RowSize != right->RowSize)) ++ { ++ MTX_ERROR1("Windows cannot be subtracted: %E", MTX_ERR_INCOMPAT); ++ return NULL; ++ } ++ size_t rowsize = left->RowSize; ++ ++ lnoc = left->Matrix->Noc; ++ rnoc = right->Matrix->Noc; ++ dnoc = dest->Matrix->Noc; ++ /* We have to distinguish cases as to whether dest ++ is equal to either left or right */ ++ result = dest->ULCorner; ++ if (left->ULCorner == dest->ULCorner) ++ { /* we write into left */ ++ x = right->ULCorner; ++ for (i = left->Nor; i != 0; --i) ++ { ++ FfSubRowPartial(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(rnoc); ++ FfStepPtr(&x); ++ } ++ } ++ else if (right->ULCorner == dest->ULCorner) ++ { /* we write into right */ ++ x = left->ULCorner; ++ for (i = left->Nor; i != 0; --i) ++ { ++ FfSubRowPartialReverse(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(lnoc); ++ FfStepPtr(&x); ++ } ++ } ++ else ++ { /* we need to copy left into dest first */ ++ x = right->ULCorner; ++ tmp = left->ULCorner; ++ size_t l_rowsize = rowsize * sizeof(long); ++ for (i = left->Nor; i != 0; --i) ++ { ++ memcpy(result, tmp, l_rowsize); ++ FfSetNoc(lnoc); ++ FfStepPtr(&tmp); ++ FfSubRowPartial(result, x, 0, rowsize); ++ FfSetNoc(dnoc); ++ FfStepPtr(&result); ++ FfSetNoc(rnoc); ++ FfStepPtr(&x); ++ } ++ } ++ return dest; ++} ++ ++/** ++ Add left*right to dest. ++ ++ It is assumed that "dest->Matrix" is allocated (with the correct field and dimensions as well), so that we ++ can write the result into it. Moreover, the chunk of memory pointed at by dest MUST be disjoint ++ from the chunks for left and right! ++ ++ Dimensions are not tested! ++**/ ++MatrixWindow_t *WindowAddMul(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) ++{ ++ PTR x, y, result; ++ long i; ++ ++ FfSetField(left->Matrix->Field); ++ x = left->ULCorner; ++ y = right->ULCorner; ++ result = dest->ULCorner; ++ ++ for (i = dest->Nor; i != 0; --i) ++ { ++ /* Set the noc of the surrounding matrix of the right factor, ++ which is assumed by zmaprow_window */ ++ FfSetNoc(right->Matrix->Noc); ++ FfAddMapRowWindow(x, y, right->Nor, result, right->RowSize); ++ /* We want to step to the next line of the left factor */ ++ FfSetNoc(left->Matrix->Noc); ++ FfStepPtr(&x); ++ /* We want to step to the next line of the result */ ++ FfSetNoc(dest->Matrix->Noc); ++ FfStepPtr(&result); ++ } ++ /* ++ dest->RowSize = right->RowSize; ++ dest->Nor = left->Nor; ++ */ ++ return dest; ++} ++ ++inline void MatrixToWindow (MatrixWindow_t *out, const Matrix_t *M, long nor, long rowsize, PTR p) ++/* presumably M will be freed separately. Hence, use free(...) to free ++ the result of this function ++*/ ++{ ++ out->Matrix = M; ++ out->Nor = nor; ++ out->RowSize = rowsize; ++ out->ULCorner = p; ++} ++ ++/** ++ ** Multiply matrix windows ++ ** This function multiplies @em A_win from the right by @em B_win and writes ++ ** the result into @em dest_win. ++ ** The matrix windows must be compatible for multiplication, i.e. they must be over ++ ** the same field, and the number of columns of @em A_win must be equal to the ++ ** number of rows of @em B_win. ++ ** Moreover, it is assumed that @em dest_win is allocated in the right dimensions. ++ ** Since parts of @em dest_win are used to store temporary results, it is essential ++ ** that @em dest_win initially is zero! ++ ** @param[out] dest_win Result. ++ ** @param A_win Left factor. ++ ** @param B_win Right factor ++ ** @return The function returns 0 on success and a nonzero value on error. ++ **/ ++ ++int StrassenStep(MatrixWindow_t *dest_win, MatrixWindow_t *A_win, MatrixWindow_t *B_win) ++{ ++ FfSetField(A_win->Matrix->Field); ++ int MPL = MPB*sizeof(long); ++ int full_nrow_cutoff = cutoff*MPL; ++ /* Determine the size of submatrices in divide-and-conquer */ ++ /** ++ * Note that the rowsize is given in the unit "long". ++ * Generally we have trailing padding empty bytes. We have to cut ++ * so that two full blocks fit into the non-padded area. This is what we do: ++ * - We halve the number of rows of A (rounded down). ++ * - We halve the rowsize of B (rounded down) , since padding doesn't matter here. ++ * - We determine how many FULL longs fit into a *row* (of A) of B->Nor items. ++ * Half of it (rounded down) gives the rowsize of A's submatrices. ++ * - From that rowsize, we obtain the corresponding number of rows of ++ * B's submatrices. ++ **/ ++ /* ++ printf("we start with A_win\n"); ++ WindowShow(A_win); ++ */ ++ int A_sub_nrows = A_win->Nor/2; ++ size_t B_sub_rowsize = B_win->RowSize/2; ++ size_t A_sub_rowsize = (B_win->Nor/MPL)/2; ++ int B_sub_nrows = A_sub_rowsize*MPL; ++ /*printf("A_sub_nrows %d\nA_subrowsize %d\nB_sub_nrows %d\nB_sub_rowsize %d\n", A_sub_nrows,A_sub_rowsize,B_sub_nrows,B_sub_rowsize);*/ ++ ++ /* If the submatrices were too small, we use school book multiplication */ ++ if ((A_sub_nrows < full_nrow_cutoff) || ++ (B_sub_nrows < full_nrow_cutoff) || ++ (A_sub_rowsize < cutoff) || ++ (B_sub_rowsize < cutoff)) ++ { ++ /* The ambient matrix of dest_win is supposed to be empty. Thus, we add rather than overwrite */ ++ /* printf("Classical for %d x %d and %d x %d\n", A_win->Nor, A_win->RowSize*MPB*sizeof(long), B_win->Nor, B_win->RowSize*MPB*sizeof(long));*/ ++ WindowAddMul(dest_win, A_win, B_win); ++ return 0; ++ } ++ /* printf("Strassen step for %d x %d and %d x %d\n", A_win->Nor, A_win->RowSize*MPB*sizeof(long), B_win->Nor, B_win->RowSize*MPB*sizeof(long));*/ ++ size_t B_sub_rowsize2 = B_sub_rowsize + B_sub_rowsize; ++ size_t A_sub_rowsize2 = A_sub_rowsize + A_sub_rowsize; ++ size_t B_sub_rowsize2b = B_sub_rowsize2*sizeof(long); /* size in byte */ ++ size_t A_sub_rowsize2b = A_sub_rowsize2*sizeof(long); ++ int B_sub_nrows2 = B_sub_nrows + B_sub_nrows; ++ int A_sub_nrows2 = A_sub_nrows + A_sub_nrows; ++ ++ Matrix_t *A, *B, *dest; ++ A = A_win->Matrix; ++ B = B_win->Matrix; ++ dest = dest_win->Matrix; ++ ++ /* Because of rounding, there are stripes on the right ++ * and the lower boundary that are not part of the ++ * clean divide-and-conquer algorithm. ++ * */ ++ int A_nrows_rem = A_win->Nor - A_sub_nrows2; ++ size_t A_rowsize_rem = A_win->RowSize - A_sub_rowsize2; ++ ++ int B_nrows_rem = B_win->Nor - B_sub_nrows2; ++ size_t B_rowsize_rem = B_win->RowSize - B_sub_rowsize2; ++ ++ /* ---------------------------------------------------- ++ * Allocate temporary space. ++ * We use a schedule introduced by Douglas-Heroux-Slishman-Smith ++ * (see also Boyer-Pernet-Zhou, "Memory efficient scheduling of ++ * Strassen-Winograd's matrix multiplication algorithm", Table 1). ++ ---------------------------------------------------- */ ++ ++ MatrixWindow_t *X, *Y; ++ if (A_sub_rowsize>B_sub_rowsize) ++ { ++ X = WindowAlloc(A->Field, A_sub_nrows, A_sub_rowsize); } ++ else ++ { ++ X = WindowAlloc(A->Field, A_sub_nrows, B_sub_rowsize); } ++ if (X == NULL) ++ { MTX_ERROR1("Error allocating a temporary window: %E",MTX_ERR_NOMEM); ++ return 1; ++ } ++ Y = WindowAlloc(A->Field, B_sub_nrows, B_sub_rowsize); ++ if (Y == NULL) ++ { ++ WindowFree(X); ++ MTX_ERROR1("Error allocating a temporary window: %E",MTX_ERR_NOMEM); ++ return 1; ++ } ++ ++ /* Define the sub-windows of A, B and dest */ ++ /* ++ printf("original windows\n"); ++ printf("A\n"); ++ WindowShow(A_win); ++ printf("B\n"); ++ WindowShow(B_win); ++ printf("dest\n"); ++ WindowShow(dest_win); ++ printf("scratch X\n"); ++ WindowShow(X); ++ printf("scratch Y\n"); ++ WindowShow(Y); ++ */ ++ FfSetNoc(A->Noc); ++ MatrixWindow_t A00[1], A01[1], A10[1], A11[1], B00[1], B01[1], B10[1], B11[1]; ++ MatrixWindow_t A_last_col[1], A_last_row[1]; ++ MatrixWindow_t B_last_col[1], B_last_row[1], B_bulk[1]; ++ MatrixWindow_t dest_last_col[1], dest_last_row[1], dest_bulk[1]; ++ MatrixToWindow(A00, A, A_sub_nrows, A_sub_rowsize, A_win->ULCorner); ++ MatrixToWindow(A01, A, A_sub_nrows, A_sub_rowsize, (PTR)((char*)(A_win->ULCorner)+A_sub_rowsize*sizeof(long))); ++ MatrixToWindow(A10, A, A_sub_nrows, A_sub_rowsize, FfGetPtr(A_win->ULCorner, A_sub_nrows)); ++ MatrixToWindow(A11, A, A_sub_nrows, A_sub_rowsize, ++ (PTR)((char*)(A_win->ULCorner)+(A_sub_nrows*FfCurrentRowSize+A_sub_rowsize*sizeof(long)))); ++ /* ++ printf("A00\n"); ++ WindowShow(A00); ++ printf("A01\n"); ++ WindowShow(A01); ++ printf("A10\n"); ++ WindowShow(A10); ++ printf("A11\n"); ++ WindowShow(A11); ++ */ ++ FfSetNoc(B->Noc); ++ MatrixToWindow(B00, B, B_sub_nrows, B_sub_rowsize, B_win->ULCorner); ++ MatrixToWindow(B01, B, B_sub_nrows, B_sub_rowsize, (PTR)((char*)(B_win->ULCorner)+B_sub_rowsize*sizeof(long))); ++ MatrixToWindow(B10, B, B_sub_nrows, B_sub_rowsize, FfGetPtr(B_win->ULCorner, B_sub_nrows)); ++ MatrixToWindow(B11, B, B_sub_nrows, B_sub_rowsize, ++ (PTR)((char*)(B_win->ULCorner)+(B_sub_nrows*FfCurrentRowSize+B_sub_rowsize*sizeof(long)))); ++ /* ++ printf("B00\n"); ++ WindowShow(B00); ++ printf("B01\n"); ++ WindowShow(B01); ++ printf("B10\n"); ++ WindowShow(B10); ++ printf("B11\n"); ++ WindowShow(B11); ++ */ ++ FfSetNoc(dest->Noc); // since we may multiply into X, the size is not necessarily the same as for B. ++ PTR dest00 = dest_win->ULCorner; ++ PTR dest01 = (PTR)((char*)(dest_win->ULCorner)+B_sub_rowsize*sizeof(long)); ++ PTR dest10 = FfGetPtr(dest_win->ULCorner,A_sub_nrows); ++ PTR dest11 = (PTR)((char*)(dest_win->ULCorner)+(A_sub_nrows*FfCurrentRowSize)+B_sub_rowsize*sizeof(long)); ++ ++ /* Matrix windows containing temporary results */ ++ MatrixWindow_t S0[1], S1[1], S2[1], S3[1], T0[1], T1[1], T2[1], T3[1], P0[1], P1[1], P2[1], P3[1], P4[1], P5[1], P6[1], U0[1], U1[1], U2[1], U3[1], U4[1], U5[1], U6[1]; ++ ++ /* 1. S2 = A00-A10 in X */ ++ S2->Nor = A_sub_nrows; ++ S2->RowSize = A_sub_rowsize; ++ S2->Matrix = X->Matrix; ++ S2->ULCorner = X->ULCorner; ++ WindowDif(S2, A00, A10); ++ /* ++ printf("1. S2 = A00-A10 in X\n"); ++ WindowShow(X); ++ printf("resp.\n"); ++ WindowShow(S2); ++ */ ++ ++ /* 2. T2 = B11-B01 in Y */ ++ T2->Nor = B_sub_nrows; ++ T2->RowSize = B_sub_rowsize; ++ T2->Matrix = Y->Matrix; ++ T2->ULCorner = Y->ULCorner; ++ WindowDif(T2, B11, B01); ++ /* ++ printf("2. T2 = B11-B01 in Y\n"); ++ WindowShow(Y); ++ */ ++ ++ /* 3. P6 = S2*T2 in dest10 */ ++ P6->Nor = A_sub_nrows; ++ P6->RowSize = B_sub_rowsize; ++ P6->Matrix = dest; ++ P6->ULCorner = dest10; ++ /* dest is supposed to be empty */ ++ if (StrassenStep(P6, S2, T2)) return 1; ++ /* ++ printf("3. P6 = S2*T2 in dest10\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /* 4. S0 = A10+A11 in X */ ++ S0->Nor = A_sub_nrows; ++ S0->RowSize = A_sub_rowsize; ++ S0->Matrix = X->Matrix; ++ S0->ULCorner = X->ULCorner; ++ WindowSum(S0, A10, A11); ++ /* ++ printf("4. S0 = A10+A11 in X\n"); ++ WindowShow(X); ++ */ ++ ++ /* 5. T0 = B01-B00 in Y */ ++ T0->Nor = B_sub_nrows; ++ T0->RowSize = B_sub_rowsize; ++ T0->Matrix = Y->Matrix; ++ T0->ULCorner = Y->ULCorner; ++ WindowDif(T0, B01, B00); ++ /* ++ printf("5. T0 = B01-B00 in Y\n"); ++ WindowShow(Y); ++ */ ++ ++ /* 6. P4 = S0*T0 in dest11 */ ++ P4->Nor = A_sub_nrows; ++ P4->RowSize = B_sub_rowsize; ++ P4->Matrix = dest; ++ P4->ULCorner = dest11; ++ /* dest is supposed to be empty */ ++ if (StrassenStep(P4, S0, T0)) return 1; ++ /* ++ printf("6. P4 = S0*T0 in dest11\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /* 7. S1 = S0-A00 in X */ ++ S1->Nor = A_sub_nrows; ++ S1->RowSize = A_sub_rowsize; ++ S1->Matrix = X->Matrix; ++ S1->ULCorner = X->ULCorner; ++ WindowDif(S1, S0, A00); ++ /* ++ printf("7. S1 = S0-A00 in X\n"); ++ WindowShow(X); ++ */ ++ ++ /* 8. T1 = B11-T0 in Y */ ++ T1->Nor = B_sub_nrows; ++ T1->RowSize = B_sub_rowsize; ++ T1->Matrix = Y->Matrix; ++ T1->ULCorner = Y->ULCorner; ++ WindowDif(T1, B11, T0); ++ /* ++ printf("8. T1 = B11-T0 in Y\n"); ++ WindowShow(Y); ++ */ ++ ++ /* 9. P5 = S1*T1 in dest01 */ ++ P5->Nor = A_sub_nrows; ++ P5->RowSize = B_sub_rowsize; ++ P5->Matrix = dest; ++ P5->ULCorner = dest01; ++ /* dest is supposed to be empty */ ++ if (StrassenStep(P5, S1, T1)) return 1; ++ /* ++ printf("9. P5 = S1*T1 in dest01\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*10. S3 = A01-S1 in X */ ++ S3->Nor = A_sub_nrows; ++ S3->RowSize = A_sub_rowsize; ++ S3->Matrix = X->Matrix; ++ S3->ULCorner = X->ULCorner; ++ WindowDif(S3, A01, S1); ++ /* ++ printf("10. S3 = A01-S1 in X\n"); ++ WindowShow(X); ++ */ ++ ++ /*11. P2 = S3*B11 in dest00 */ ++ P2->Nor = A_sub_nrows; ++ P2->RowSize = B_sub_rowsize; ++ P2->Matrix = dest; ++ P2->ULCorner = dest00; ++ /* That part of dest is still supposed to be empty */ ++ if (StrassenStep(P2, S3, B11)) return 1; ++ /* ++ printf("11. P2 = S3*B11 in dest00\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*12. P0 = A00*B00 in X */ ++ P0->Nor = A_sub_nrows; ++ P0->RowSize = B_sub_rowsize; ++ P0->Matrix = X->Matrix; ++ P0->ULCorner = X->ULCorner; ++ /* ++ This time, the matrix we write our product to may be non-empty. ++ Hence, we clear the destination first. ++ */ ++ WindowClear(P0); ++ if (StrassenStep(P0, A00, B00)) return 1; ++ /* ++ printf("12. P0 = A00*B00 in X\n"); ++ WindowShow(X); ++ */ ++ ++ /*13. U1 = P0+P5 in dest01 */ ++ U1->Nor = A_sub_nrows; ++ U1->RowSize = B_sub_rowsize; ++ U1->Matrix = dest; ++ U1->ULCorner = dest01; ++ WindowSum(U1, P0, P5); ++ /* ++ printf("13. U1 = P0+P5 in dest01\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*14. U2 = U1+P6 in dest10 */ ++ U2->Nor = A_sub_nrows; ++ U2->RowSize = B_sub_rowsize; ++ U2->Matrix = dest; ++ U2->ULCorner = dest10; ++ WindowSum(U2, U1, P6); ++ /* ++ printf("14. U2 = U1+P6 in dest10\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*15. U3 = U1+P4 in dest01 */ ++ U3->Nor = A_sub_nrows; ++ U3->RowSize = B_sub_rowsize; ++ U3->Matrix = dest; ++ U3->ULCorner = dest01; ++ WindowSum(U3, U1, P4); ++ /* ++ printf("15. U3 = U1+P4 in dest01\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*16. U6 = U2+P4 in dest11 (final) */ ++ U6->Nor = A_sub_nrows; ++ U6->RowSize = B_sub_rowsize; ++ U6->Matrix = dest; ++ U6->ULCorner = dest11; ++ WindowSum(U6, U2, P4); ++ /* ++ printf("16. U6 = U2+P4 in dest11 (final)\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*17. U4 = U3+P2 in dest01 (final) */ ++ U4->Nor = A_sub_nrows; ++ U4->RowSize = B_sub_rowsize; ++ U4->Matrix = dest; ++ U4->ULCorner = dest01; ++ WindowSum(U4, U3, P2); ++ /* ++ printf("17. U4 = U3+P2 in dest01 (final)\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*18. T3 = T1-B10 in Y */ ++ T3->Nor = B_sub_nrows; ++ T3->RowSize = B_sub_rowsize; ++ T3->Matrix = Y->Matrix; ++ T3->ULCorner = Y->ULCorner; ++ WindowDif(T3, T1, B10); ++ /* ++ printf("18. T3 = T1-B10 in Y\n"); ++ WindowShow(Y); ++ */ ++ ++ /*19. P3 = A11*T3 in dest00 */ ++ P3->Nor = A_sub_nrows; ++ P3->RowSize = B_sub_rowsize; ++ P3->Matrix = dest; ++ P3->ULCorner = dest00; ++ /* Meanwhile dest00 is non-empty. Hence, overwrite */ ++ WindowClear(P3); ++ if (StrassenStep(P3, A11, T3)) return 1; ++ /* ++ printf("19. P3 = A11*T3 in dest00\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*20. U5 = U2-P3 in dest10 (final) */ ++ U5->Nor = A_sub_nrows; ++ U5->RowSize = B_sub_rowsize; ++ U5->Matrix = dest; ++ U5->ULCorner = dest10; ++ WindowDif(U5, U2, P3); ++ /* ++ printf("20. U5 = U2-P3 in dest10 (final)\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*21. P1 = A01*B10 in dest00 */ ++ P1->Nor = A_sub_nrows; ++ P1->RowSize = B_sub_rowsize; ++ P1->Matrix = dest; ++ P1->ULCorner = dest00; ++ /* Again, we need to overwrite */ ++ WindowClear(P1); ++ if (StrassenStep(P1, A01, B10)) return 1; ++ /* ++ printf("21. P1 = A01*B10 in dest00\n"); ++ WindowShow(dest_win); ++ */ ++ ++ /*22. U0 = P0+P1 in dest00 (final) */ ++ U0->Nor = A_sub_nrows; ++ U0->RowSize = B_sub_rowsize; ++ U0->Matrix = dest; ++ U0->ULCorner = dest00; ++ WindowSum(U0, P0, P1); ++ /* ++ printf("22. U0 = P0+P1 in dest00 (final)\n"); ++ WindowShow(dest_win); ++ */ ++ WindowFree(X); ++ WindowFree(Y); ++ ++ /* --------------------------------------------------------- ++ Deal with the leftovers on the bottom and the right wing ++ --------------------------------------------------------- */ ++ ++ if (B_rowsize_rem) ++ { ++ MatrixToWindow(B_last_col, B, B_win->Nor, B_rowsize_rem, (PTR)((char*)(B_win->ULCorner) + B_sub_rowsize2b)); ++ MatrixToWindow(dest_last_col, dest, A_win->Nor, B_rowsize_rem, (PTR)((char*)(dest_win->ULCorner) + B_sub_rowsize2b)); ++ /* that part of dest is still supposed to be empty, so we can add the product */ ++ WindowAddMul(dest_last_col, A_win, B_last_col); ++ } ++ if (A_nrows_rem) ++ { ++ FfSetNoc(A->Noc); ++ MatrixToWindow(A_last_row, A, A_nrows_rem, A_win->RowSize, (PTR)((char*)(A_win->ULCorner) + (A_sub_nrows2*FfCurrentRowSize))); ++ if (B_rowsize_rem) /* We have already considered the lower right corner in the previous if-clause */ ++ { ++ MatrixToWindow(B_bulk, B, B_win->Nor, B_sub_rowsize2, B_win->ULCorner); ++ FfSetNoc(dest->Noc); ++ MatrixToWindow(dest_last_row, dest, A_nrows_rem, B_sub_rowsize2, (PTR)((char*)(dest_win->ULCorner) + (A_sub_nrows2*FfCurrentRowSize))); ++ /* that part of dest is still supposed to be empty, so we can add the product */ ++ WindowAddMul(dest_last_row, A_last_row, B_bulk); ++ } ++ else ++ { ++ FfSetNoc(dest->Noc); ++ MatrixToWindow(dest_last_row, dest, A_nrows_rem, B_win->RowSize, (PTR)((char*)(dest_win->ULCorner) + (A_sub_nrows2*FfCurrentRowSize))); ++ /* that part of dest is still supposed to be empty, so we can add the product */ ++ WindowAddMul(dest_last_row, A_last_row, B_win); ++ } ++ } ++ if (A_rowsize_rem) ++ { /* By the above operations, we don't need to consider the lower right corner of either A or B. */ ++ MatrixToWindow(A_last_col, A, A_sub_nrows2, A_rowsize_rem, (PTR)((char*)(A_win->ULCorner) + A_sub_rowsize2b)); ++ FfSetNoc(B->Noc); ++ MatrixToWindow(B_last_row, B, B_nrows_rem, B_sub_rowsize2, (PTR)((char*)(B_win->ULCorner) + (B_sub_nrows2*FfCurrentRowSize))); ++ FfSetNoc(dest->Noc); ++ MatrixToWindow(dest_bulk, dest, A_sub_nrows2, B_sub_rowsize2, dest_win->ULCorner); ++ /* now we are supposed to add the product to the result obtained so far */ ++ WindowAddMul(dest_bulk, A_last_col, B_last_row); ++ } ++ return 0; ++} ++ ++/** ++ ** Multiply matrices ++ ** This function multiplies @em A from the right by @em B and writes ++ ** the result into @em dest. ++ ** The matrices must be compatible for multiplication, i.e. they must be over ++ ** the same field, and the number of columns of @em A must be equal to the ++ ** number of rows of @em B. ++ ** Moreover, it is assumed that @em dest is allocated in the right dimensions. ++ ** Since parts of @em dest are used to store temporary results, it is essential ++ ** that @em dest initially is zero! ++ ** @param[out] dest Result. ++ ** @param A Left factor. ++ ** @param B Right factor ++ ** @return The function returns @em dest, or NULL on error. ++ **/ ++Matrix_t *MatMulStrassen(Matrix_t *dest, const Matrix_t *A, const Matrix_t *B) ++{ ++ FfSetField(A->Field); ++ MatrixWindow_t A_win[1], B_win[1], dest_win[1]; ++ FfSetNoc(A->Noc); ++ MatrixToWindow(A_win, A, A->Nor, LPR, A->Data); ++ FfSetNoc(B->Noc); ++ MatrixToWindow(B_win, B, B->Nor, LPR, B->Data); ++ FfSetNoc(dest->Noc); ++ MatrixToWindow(dest_win, dest, A->Nor, LPR, dest->Data); ++ if (StrassenStep(dest_win, A_win, B_win)) return NULL; ++ return dest; ++} diff --git a/build/pkgs/meataxe/patches/StrassenWinogradUsage.patch b/build/pkgs/meataxe/patches/StrassenWinogradUsage.patch new file mode 100644 index 00000000000..1959336c3fe --- /dev/null +++ b/build/pkgs/meataxe/patches/StrassenWinogradUsage.patch @@ -0,0 +1,359 @@ +Use Strassen-Winograd multiplication in some MeatAxe functions. + +AUTHOR: Simon King 2015-09-18, simon.king@uni-jena.de + +diff --git a/src/chbasis.c b/src/chbasis.c +index 1ea9c9f..34cf886 100644 +--- a/src/chbasis.c ++++ b/src/chbasis.c +@@ -9,6 +9,8 @@ + + + #include "meataxe.h" ++#include ++#include + + MTX_DEFINE_FILE_INFO + +@@ -36,9 +38,6 @@ MTX_DEFINE_FILE_INFO + int MrChangeBasis(MatRep_t *rep, const Matrix_t *trans) + + { +- Matrix_t *bi; +- int i; +- + /* Check arguments + --------------- */ + if (!MrIsValid(rep)) +@@ -46,11 +45,6 @@ int MrChangeBasis(MatRep_t *rep, const Matrix_t *trans) + MTX_ERROR1("rep: %E",MTX_ERR_BADARG); + return -1; + } +- if (!MatIsValid(trans)) +- { +- MTX_ERROR1("trans: %E",MTX_ERR_BADARG); +- return -1; +- } + if (rep->NGen <= 0) + return 0; + if (trans->Field != rep->Gen[0]->Field || +@@ -60,54 +54,50 @@ int MrChangeBasis(MatRep_t *rep, const Matrix_t *trans) + MTX_ERROR1("%E",MTX_ERR_INCOMPAT); + return -1; + } +- +- +- /* Basis transformation +- -------------------- */ +- if ((bi = MatInverse(trans)) == NULL) +- { +- MTX_ERROR("Basis transformation is singular"); +- return -1; +- } +- for (i = 0; i < rep->NGen; ++i) +- { +- Matrix_t *tmp = MatDup(trans); +- MatMul(tmp,rep->Gen[i]); +- MatMul(tmp,bi); +- MatFree(rep->Gen[i]); +- rep->Gen[i] = tmp; +- } +- MatFree(bi); +- return 0; ++ return ChangeBasis(trans, rep->NGen, (const Matrix_t **)(rep->Gen), rep->Gen); + } + + +- +-int ChangeBasisOLD(const Matrix_t *M, int ngen, const Matrix_t *gen[], ++/** Conjugate a list @em gen of @em ngen square matrices over the same ++ * field and of the same dimensions by a mattrix @em trans ++ * and write the result into @em newgen. If @em gen == @em newgen, then ++ * the previous content of @em newgen will be overridden. **/ ++int ChangeBasis(const Matrix_t *trans, int ngen, const Matrix_t *gen[], + Matrix_t *newgen[]) + + { +- Matrix_t *bi, *tmp; ++ Matrix_t *bi; + int i; + + MTX_VERIFY(ngen >= 0); +- if (!MatIsValid(M)) ++ if (!MatIsValid(trans)) ++ { ++ MTX_ERROR1("trans: %E",MTX_ERR_BADARG); + return -1; +- if ((bi = MatInverse(M)) == NULL) ++ } ++ ++ if ((bi = MatInverse(trans)) == NULL) + { +- MTX_ERROR("Matrix is singular"); ++ MTX_ERROR("Basis transformation is singular"); + return -1; + } ++ ++ Matrix_t *tmp = MatAlloc(trans->Field, trans->Nor, trans->Noc); ++ size_t tmpsize = FfCurrentRowSize*trans->Nor; + for (i = 0; i < ngen; ++i) + { +- tmp = MatDup(M); +- MatMul(tmp,gen[i]); +- MatMul(tmp,bi); +- if ((const Matrix_t **)newgen == gen) +- MatFree(newgen[i]); +- newgen[i] = tmp; ++ MTX_VERIFY(gen[i]->Nor==trans->Nor); ++ MTX_VERIFY(gen[i]->Noc==trans->Noc); ++ memset(tmp->Data, FF_ZERO, tmpsize); ++ MatMulStrassen(tmp, trans, gen[i]); ++ if ((const Matrix_t **)newgen == gen) ++ memset(newgen[i]->Data, FF_ZERO, tmpsize); ++ else ++ newgen[i] = MatAlloc(trans->Field, trans->Nor, trans->Noc); ++ MatMulStrassen(newgen[i], tmp, bi); + } + MatFree(bi); ++ MatFree(tmp); + return 0; + } + +diff --git a/src/chop.c b/src/chop.c +index 65a2a98..0f3f38f 100644 +--- a/src/chop.c ++++ b/src/chop.c +@@ -538,7 +538,7 @@ static int checkspl(const MatRep_t *rep, Matrix_t *nsp) + ------------------------------------------------------------ */ + sb1 = SpinUp(nsp,rep,SF_FIRST|SF_CYCLIC|SF_STD,NULL,NULL); + MTX_VERIFY(sb1 != NULL && sb1->Nor == sb1->Noc); +- ChangeBasisOLD(sb1,LI.NGen,(const Matrix_t **)rep->Gen,g1); ++ ChangeBasis(sb1,LI.NGen,(const Matrix_t **)rep->Gen,g1); + endo = MrAlloc(0,NULL,0); + + sb2 = NULL; /* Mark as unused */ +@@ -576,7 +576,7 @@ static int checkspl(const MatRep_t *rep, Matrix_t *nsp) + sb2 = SpinUp(v2,rep,SF_FIRST|SF_CYCLIC|SF_STD,NULL,NULL); + MTX_VERIFY(sb2 != NULL && sb2->Nor == sb2->Noc); + MatFree(v2); +- ChangeBasisOLD(sb2,rep->NGen,(const Matrix_t **)rep->Gen,g2); ++ ChangeBasis(sb2,rep->NGen,(const Matrix_t **)rep->Gen,g2); + + /* Compare the two representations. If they are different, + we know that the splitting field degree must be smaller +@@ -762,7 +762,7 @@ static void newirred(node_t *n) + LI.Cf[i].spl = n->spl = n->nsp->Nor; + b = SpinUp(n->nsp,n->Rep,SF_FIRST|SF_CYCLIC|SF_STD,NULL,NULL); + MTX_VERIFY(b != NULL && b->Nor == b->Noc); +- ChangeBasisOLD(b,LI.NGen,(const Matrix_t **)n->Rep->Gen,n->Rep->Gen); ++ ChangeBasis(b,LI.NGen,(const Matrix_t **)n->Rep->Gen,n->Rep->Gen); + MatFree(b); + + /* Write out the generators +diff --git a/src/homcomp.c b/src/homcomp.c +index a808089..351af2b 100644 +--- a/src/homcomp.c ++++ b/src/homcomp.c +@@ -112,10 +112,10 @@ Matrix_t *HomogeneousPart(MatRep_t *m, MatRep_t *s, Matrix_t *npw, + { + PTR matptr = MatGetPtr(A,j); + int u; +- a = MatDup(V[j]); +- b = MatDup(s->Gen[i]); +- MatMul(a,m->Gen[i]); /* the equations that describe */ +- MatMul(b,V[j]); /* that a vector in the null- */ ++ a = MatAlloc(V[j]->Field, V[j]->Nor, m->Gen[i]->Noc); ++ b = MatAlloc(s->Gen[i]->Field, s->Gen[i]->Nor, V[j]->Noc); ++ MatMulStrassen(a, V[j], m->Gen[i]); /* the equations that describe */ ++ MatMulStrassen(b,s->Gen[i], V[j]); /* that a vector in the null- */ + MatMulScalar(b,FfNeg(FF_ONE)); /* space is the first element */ + MatAdd(a, b); /* of a standard basis of a */ + /* module isomorphic to S */ +diff --git a/src/isisom.c b/src/isisom.c +index 790d2b0..e2b7f07 100644 +--- a/src/isisom.c ++++ b/src/isisom.c +@@ -9,7 +9,7 @@ + + + #include "meataxe.h" +- ++#include + + MTX_DEFINE_FILE_INFO + +@@ -114,7 +114,7 @@ int IsIsomorphic(const MatRep_t *rep1, const CfInfo *info1, + { + int j; + WgData_t *wg; +- Matrix_t *word, *m, *seed, *b, *bi; ++ Matrix_t *word, *m, *seed, *b, *g1, *g2; + int result; + + if (CheckArgs(rep1->NGen,rep1->Gen,info1,rep2->Gen,use_pw) != 0) +@@ -148,27 +148,35 @@ int IsIsomorphic(const MatRep_t *rep1, const CfInfo *info1, + MatFree(b); + return 0; + } +- bi = MatInverse(b); + + /* Compare generators + ------------------ */ ++ /** ++ * We test whether b*rep2_j*b^-1 == rep1_j ++ * by testing whether b*rep2_j == rep1_j*b ++ * */ ++ g1 = MatAlloc(b->Field, b->Nor, b->Noc); ++ g2 = MatAlloc(b->Field, b->Nor, b->Noc); ++ size_t memsize = FfCurrentRowSize*b->Nor; + for (j = 0, result = 0; result == 0 && j < rep2->NGen; ++j) + { +- Matrix_t *g = MatDup(b); +- MatMul(g,rep2->Gen[j]); +- MatMul(g,bi); +- if (MatCompare(g,rep1->Gen[j]) != 0) +- result = 1; +- MatFree(g); ++ MatMulStrassen(g2, b, rep2->Gen[j]); ++ MatMulStrassen(g1, rep1->Gen[j], b); ++ if (MatCompare(g1, g2) != 0) ++ { result = 1; ++ break; ++ } ++ memset(g1->Data, FF_ZERO, memsize); ++ memset(g2->Data, FF_ZERO, memsize); + } + + /* Clean up + -------- */ + if (trans != NULL && result == 0) +- *trans = b; ++ *trans = b; + else +- MatFree(b); +- MatFree(bi); +- ++ MatFree(b); ++ MatFree(g1); ++ MatFree(g2); + return (result == 0); + } +diff --git a/src/meataxe.h b/src/meataxe.h +index e2f5a84..5123f1c 100644 +--- a/src/meataxe.h ++++ b/src/meataxe.h +@@ -1096,11 +1096,7 @@ int LdFree(LdLattice_t *l); + int LdAddIncidence(LdLattice_t *lat, int sub, int sup); + int LdSetPositions(LdLattice_t *l); + +- +- +- +-/* OLD STUFF */ +-int ChangeBasisOLD(const Matrix_t *M, int ngen, const Matrix_t *gen[], ++int ChangeBasis(const Matrix_t *M, int ngen, const Matrix_t *gen[], + Matrix_t *newgen[]); + + +diff --git a/src/mktree.c b/src/mktree.c +index ede7881..3e99489 100644 +--- a/src/mktree.c ++++ b/src/mktree.c +@@ -213,8 +213,8 @@ static int MakeTree() + { + /* Calculate next element + ---------------------- */ +- Matrix_t *newelem = MatDup(Elms[src].Matrix); +- MatMul(newelem,Rep->Gen[g]); ++ Matrix_t *newelem = MatAlloc(Elms[src].Matrix->Field, Elms[src].Matrix->Nor, Rep->Gen[g]->Noc); ++ MatMulStrassen(newelem, Elms[src].Matrix, Rep->Gen[g]); + + /* If it is new, add to tree, else discard + --------------------------------------- */ +diff --git a/src/precond.c b/src/precond.c +index f144716..efc2f3d 100644 +--- a/src/precond.c ++++ b/src/precond.c +@@ -391,8 +391,8 @@ static void MakePQ(int n, int mj, int nj) + for (k = 0; k < spl; ++k) + { + FEL f; +- Matrix_t *x = MatDup(endo[i]); +- MatMul(x,endo[k]); ++ Matrix_t *x = MatAlloc(endo[i]->Field, endo[i]->Nor, endo[k]->Noc); ++ MatMulStrassen(x,endo[i],endo[k]); + f = MatTrace(x); + FfInsert(pptr,k,f); + MatFree(x); +diff --git a/src/pseudochop.c b/src/pseudochop.c +index 68cadae..3f1fa97 100644 +--- a/src/pseudochop.c ++++ b/src/pseudochop.c +@@ -105,8 +105,8 @@ int main(int argc, const char *argv[]) + { + Matrix_t *newmat; + oldnul = newnul; +- newmat = MatDup(old); +- MatMul(newmat,old); ++ newmat = MatAlloc(old->Field, old->Nor, old->Noc); ++ MatMulStrassen(newmat, old, old); + MatFree(old); + MatFree(nulsp); + old = MatDup(newmat); +diff --git a/src/pwkond.c b/src/pwkond.c +index c14c20e..5eaa5de 100644 +--- a/src/pwkond.c ++++ b/src/pwkond.c +@@ -309,8 +309,8 @@ static void gkond(const Lat_Info *li, int i, Matrix_t *b, Matrix_t *k, + char fn[LAT_MAXBASENAME+10]; + Matrix_t *x1, *x2; + +- x1 = MatDup(k); +- MatMul(x1,w); ++ x1 = MatAlloc(k->Field, k->Nor, w->Noc); ++ MatMulStrassen(x1, k, w); + x2 = QProjection(b,x1); + sprintf(fn,"%s%s.%s",li->BaseName,Lat_CfName(li,i),name); + MatSave(x2,fn); +@@ -340,7 +340,7 @@ static void Standardize(int cf) + MESSAGE(0,(" Transforming to standard basis\n")); + sb = SpinUp(CfList[cf].PWNullSpace,CfList[cf].Gen, + SF_FIRST|SF_CYCLIC|SF_STD,&script,NULL); +- ChangeBasisOLD(sb,CfList[cf].Gen->NGen, ++ ChangeBasis(sb,CfList[cf].Gen->NGen, + (const Matrix_t **)CfList[cf].Gen->Gen,std); + MatFree(sb); + +@@ -782,7 +782,7 @@ static int try2(long w, FEL f) + MESSAGE(3,("failed\n")); + return -1; /* Nullity should be 0 */ + } +- nul = MatNullity__(MatMul(MatDup(word),word)); ++ nul = MatNullity__(MatMulStrassen(MatAlloc(word->Field, word->Nor, word->Noc), word, word)); + if (nul != CfList[i].Info->spl) + { + MatFree(word); +@@ -915,7 +915,7 @@ static int try_p(long w) + /* Check if the nullity is stable + ------------------------------ */ + wp = MatInsert(word,mp->Factor[k]); +- wp2 = MatMul(MatDup(wp),wp); ++ wp2 = MatMulStrassen(MatAlloc(wp->Field, wp->Nor, wp->Noc), wp, wp); + MatFree(wp); + nul = MatNullity__(wp2); + if (nul != CfList[i].Info->spl) +diff --git a/src/soc.c b/src/soc.c +index 789a02b..199a2e0 100644 +--- a/src/soc.c ++++ b/src/soc.c +@@ -294,8 +294,8 @@ static int NextLayer() + Matrix_t *mat, *stgen; + + mat = MatCutRows(basis,basis->Nor - Dimension,Dimension); +- stgen = MatDup(bas); +- MatMul(stgen, mat); ++ stgen = MatAlloc(bas->Field, bas->Nor, mat->Noc); ++ MatMulStrassen(stgen, bas, mat); + MatCopyRegion(basis,basis->Nor - Dimension,0,stgen,0,0,Dimension,-1); + MatFree(mat); + MatFree(stgen); diff --git a/build/pkgs/meataxe/patches/TweakEchelon.patch b/build/pkgs/meataxe/patches/TweakEchelon.patch new file mode 100644 index 00000000000..eeee5e4d8f6 --- /dev/null +++ b/build/pkgs/meataxe/patches/TweakEchelon.patch @@ -0,0 +1,229 @@ +Improve echelon computation by restricting FfAddMulRow to the +nonzero part of the to-be-added row. + +Also remove some compiler warnings. + +AUTHOR: + +- Simon King, 2015-09-22 +diff --git a/src/c-kernel.c b/src/c-kernel.c +index f74e97e..d4355bc 100644 +--- a/src/c-kernel.c ++++ b/src/c-kernel.c +@@ -311,13 +311,10 @@ void TestFelToInt(unsigned flags) + static void TestSubfield1(int fld, int sub) + + { +- FEL tabfld[256], tabsub[256]; ++ FEL tabsub[256]; + FEL tabemb[256]; + int i; + +- FfSetField(fld); +- for (i = 0; i < fld; ++i) +- tabfld[i] = FfFromInt(i); + FfSetField(sub); + for (i = 0; i < sub; ++i) + tabsub[i] = FfFromInt(i); +diff --git a/src/cfcomp.c b/src/cfcomp.c +index 7434549..fa739d6 100644 +--- a/src/cfcomp.c ++++ b/src/cfcomp.c +@@ -131,7 +131,7 @@ static void Compare(const char *name) + { + ReadGens(name); + FindEquiv(name); +- FreeGens(name); ++ FreeGens(); + } + + +diff --git a/src/kernel-0.c b/src/kernel-0.c +index 178b6cb..6ef2f72 100644 +--- a/src/kernel-0.c ++++ b/src/kernel-0.c +@@ -919,6 +919,54 @@ PTR FfSubRowPartialReverse(PTR dest, PTR src, int first, int len) + return dest; + } + ++/** ++ ** Add a multiple of a part of a row. ++ ** This function adds a multiple of @em src to @em dest. ++ ** This works like FfAddRow(), but the operation is performed only on a given range of ++ ** columns. ++ ** @param dest The row to add to. ++ ** @param src The row to add. ++ ** @param first Number of bytes to skip. ++ ** @param len Number of bytes to add. ++**/ ++/* Warning!! Let L be the long integer to which the first byte of the a row ++ * belongs. It is assumed that all previous bytes in L are zero! ++ * Moreover, it is assumed that either the part of the rows ends at the ++ * end of the row, or that it ends with a full long. ++ */ ++void FfAddMulRowPartial(PTR dest, PTR src, FEL f, int first, int len) ++{ ++ register int i; ++ register BYTE *p1, *p2, *multab; ++ ++ CHECKFEL(f); ++ if (f == FF_ZERO) ++ return; ++ int lfirst; ++ if (f == FF_ONE) ++ { ++ lfirst = first/sizeof(long); ++ if (first+len>=FfCurrentRowSizeIo) ++ { ++ FfAddRowPartial(dest,src,lfirst,FfCurrentRowSize/sizeof(long)-lfirst); ++ return; ++ } ++ FfAddRowPartial(dest,src,lfirst,(first+len)/sizeof(long)-lfirst); ++ return; ++ } ++ multab = mtx_tmult[f]; ++ p1 = dest + first; ++ p2 = src + first; ++ int rem = FfCurrentRowSizeIo - first; ++ if (rem > len) rem = len; ++ for (i = rem; i != 0; --i) ++ { ++ register BYTE x = *p2++; ++ if (x!=0) ++ *p1 = mtx_tadd[*p1][multab[x]]; ++ ++p1; ++ } ++} + + /** + ** Multiply a row by a coefficient. +@@ -977,10 +1025,12 @@ void FfAddMulRow(PTR dest, PTR src, FEL f) + multab = mtx_tmult[f]; + p1 = dest; + p2 = src; +- for (i = FfTrueRowSize(FfNoc); i != 0; --i) ++ for (i = FfCurrentRowSizeIo; i != 0; --i) + { +- *p1 = mtx_tadd[*p1][multab[*p2++]]; +- ++p1; ++ register BYTE x = *p2++; ++ if (x!=0) ++ *p1 = mtx_tadd[*p1][multab[x]]; ++ ++p1; + } + } + +@@ -1131,7 +1181,9 @@ __asm__(" popl %ebx\n" + { + for (; k != 0; --k) + { +- *r = mtx_tadd[*r][*v++]; ++ register BYTE x = *v++; ++ if (x!=0) ++ *r = mtx_tadd[*r][x]; + ++r; + } + } +@@ -1140,9 +1192,9 @@ __asm__(" popl %ebx\n" + register BYTE *multab = mtx_tmult[f]; + for (; k != 0; --k) + { +- if (*v != 0) +- *r = mtx_tadd[multab[*v]][*r]; +- ++v; ++ if (*v != 0) ++ *r = mtx_tadd[multab[*v]][*r]; ++ ++v; + ++r; + } + } +diff --git a/src/matcopy.c b/src/matcopy.c +index 75b29c0..457dfeb 100644 +--- a/src/matcopy.c ++++ b/src/matcopy.c +@@ -57,7 +57,10 @@ int MatCopyRegion(Matrix_t *dest, int destrow, int destcol, + if (!MatIsValid(src) || !MatIsValid(dest)) + return -1; + if (src->Field != dest->Field) +- return MTX_ERROR1("%E",MTX_ERR_INCOMPAT), -1; ++ { ++ MTX_ERROR1("%E",MTX_ERR_INCOMPAT); ++ return -1; ++ } + if (nrows == -1) + nrows = src->Nor - row1; + if (ncols == -1) +diff --git a/src/meataxe.h b/src/meataxe.h +index 5123f1c..368b37b 100644 +--- a/src/meataxe.h ++++ b/src/meataxe.h +@@ -126,6 +126,7 @@ int FfSetNoc(int noc); + + + void FfAddMulRow(PTR dest, PTR src, FEL f); ++void FfAddMulRowPartial(PTR dest, PTR src, FEL f, int first, int len); + PTR FfAddRow(PTR dest, PTR src); + PTR FfAddRowPartial(PTR dest, PTR src, int first, int len); + PTR FfSubRow(PTR dest, PTR src); +diff --git a/src/window.c b/src/window.c +index f374028..9c87694 100644 +--- a/src/window.c ++++ b/src/window.c +@@ -236,12 +236,14 @@ __asm__(" popl %ebx\n" + { + register BYTE *v = m; + register BYTE *r = result; ++ register BYTE x; + if (f == FF_ONE) + { + register size_t k = l_rowsize; + for (; k != 0; --k) + { +- *r = mtx_tadd[*r][*v++]; ++ x=*v++; ++ if (x) *r = mtx_tadd[*r][x]; + ++r; + } + } +@@ -251,9 +253,8 @@ __asm__(" popl %ebx\n" + register size_t k = l_rowsize; + for (; k != 0; --k) + { +- if (*v != 0) +- *r = mtx_tadd[multab[*v]][*r]; +- ++v; ++ x=*v++; ++ if (x) *r = mtx_tadd[multab[x]][*r]; + ++r; + } + } +diff --git a/src/zcleanrow.c b/src/zcleanrow.c +index 649e551..b4dcb30 100644 +--- a/src/zcleanrow.c ++++ b/src/zcleanrow.c +@@ -35,18 +35,21 @@ MTX_DEFINE_FILE_INFO + + void FfCleanRow(PTR row, PTR matrix, int nor, const int *piv) + { +- int i; ++ register int i, pivi, first; + PTR x; + + for (i=0, x=matrix; i < nor; ++i, FfStepPtr(&x)) + { +- FEL f = FfExtract(row,piv[i]); ++ pivi = piv[i]; ++ FEL f = FfExtract(row,pivi); + if (f != FF_ZERO) +- FfAddMulRow(row,x,FfNeg(FfDiv(f,FfExtract(x,piv[i])))); ++ { ++ first = pivi/MPB; ++ FfAddMulRowPartial(row,x,FfNeg(FfDiv(f,FfExtract(x,pivi))),first,FfCurrentRowSizeIo-first); ++ } + } + } + +- + /** + ** Clean Row and Record Operations. + ** This function works like FfCleanRow(), but it stores a record of the operations performed diff --git a/build/pkgs/meataxe/patches/UseErrorPropagation.patch b/build/pkgs/meataxe/patches/UseErrorPropagation.patch new file mode 100644 index 00000000000..00745e8b967 --- /dev/null +++ b/build/pkgs/meataxe/patches/UseErrorPropagation.patch @@ -0,0 +1,1147 @@ +In functions that appear in matrix arithmetic, use specific return values +on error, and propagate errors. This is *not* done in other parts of +MeatAxe (e.g., not for greased matrices or polynomials) and not for +standalone programs. + +AUTHOR: + +- Simon King, 2015-09-26 + +diff --git a/src/cfinfo.c b/src/cfinfo.c +index 293526b..9c1a004 100644 +--- a/src/cfinfo.c ++++ b/src/cfinfo.c +@@ -215,7 +215,7 @@ int Lat_ReadInfo(Lat_Info *li, const char *basename) + } + for (i = 0; i < li->NCf; ++i) + { +- ReadWord(f,&(li->Cf[i].idword),&(li->Cf[i].idpol),fn); ++ if (!ReadWord(f,&(li->Cf[i].idword),&(li->Cf[i].idpol),fn)) return -1; + if (StfMatch(f,i < li->NCf - 1 ? "," : "];") != 0) + { + MTX_ERROR2("%s: %E",fn,MTX_ERR_FILEFMT); +@@ -232,7 +232,7 @@ int Lat_ReadInfo(Lat_Info *li, const char *basename) + } + for (i = 0; i < li->NCf; ++i) + { +- ReadWord(f,&(li->Cf[i].peakword),&(li->Cf[i].peakpol),fn); ++ if (!ReadWord(f,&(li->Cf[i].peakword),&(li->Cf[i].peakpol),fn)) return -1; + if (StfMatch(f,i < li->NCf - 1 ? "," : "];") != 0) + { + MTX_ERROR2("%s: %E",fn,MTX_ERR_FILEFMT); +diff --git a/src/chbasis.c b/src/chbasis.c +index 34cf886..f1ee2e8 100644 +--- a/src/chbasis.c ++++ b/src/chbasis.c +@@ -61,7 +61,8 @@ int MrChangeBasis(MatRep_t *rep, const Matrix_t *trans) + /** Conjugate a list @em gen of @em ngen square matrices over the same + * field and of the same dimensions by a mattrix @em trans + * and write the result into @em newgen. If @em gen == @em newgen, then +- * the previous content of @em newgen will be overridden. **/ ++ * the previous content of @em newgen will be overridden. ++ * Return -1 on error and 0 on success. **/ + int ChangeBasis(const Matrix_t *trans, int ngen, const Matrix_t *gen[], + Matrix_t *newgen[]) + +@@ -83,18 +84,36 @@ int ChangeBasis(const Matrix_t *trans, int ngen, const Matrix_t *gen[], + } + + Matrix_t *tmp = MatAlloc(trans->Field, trans->Nor, trans->Noc); ++ if (!tmp) return -1; + size_t tmpsize = FfCurrentRowSize*trans->Nor; + for (i = 0; i < ngen; ++i) + { + MTX_VERIFY(gen[i]->Nor==trans->Nor); + MTX_VERIFY(gen[i]->Noc==trans->Noc); + memset(tmp->Data, FF_ZERO, tmpsize); +- MatMulStrassen(tmp, trans, gen[i]); ++ if (!MatMulStrassen(tmp, trans, gen[i])) ++ { ++ MatFree(tmp); ++ return -1; ++ } + if ((const Matrix_t **)newgen == gen) + memset(newgen[i]->Data, FF_ZERO, tmpsize); + else ++ { + newgen[i] = MatAlloc(trans->Field, trans->Nor, trans->Noc); +- MatMulStrassen(newgen[i], tmp, bi); ++ if (!newgen[i]) ++ { ++ MatFree(tmp); ++ MatFree(bi); ++ return -1; ++ } ++ } ++ if (!MatMulStrassen(newgen[i], tmp, bi)) ++ { ++ MatFree(tmp); ++ MatFree(bi); ++ return -1; ++ } + } + MatFree(bi); + MatFree(tmp); +diff --git a/src/ffio.c b/src/ffio.c +index 92f9360..d2e3f1c 100644 +--- a/src/ffio.c ++++ b/src/ffio.c +@@ -71,8 +71,11 @@ int FfReadRows(FILE *f, PTR buf, int n) + if (fread(b,FfTrueRowSize(FfNoc),1,f) != 1) break; + b += FfCurrentRowSize; + } +- if (ferror(f)) +- MTX_ERROR("Read failed: %S"); ++ if (ferror(f)) ++ { ++ MTX_ERROR("Read failed: %S"); ++ return -1; ++ } + return i; + } + +@@ -106,8 +109,11 @@ int FfWriteRows(FILE *f, PTR buf, int n) + if (fwrite(b,FfTrueRowSize(FfNoc),1,f) != 1) break; + b += FfCurrentRowSize; + } +- if (ferror(f)) +- MTX_ERROR("Write failed: %S"); ++ if (ferror(f)) ++ { ++ MTX_ERROR("Write failed: %S"); ++ return -1; ++ } + return i; + } + +diff --git a/src/kernel-0.c b/src/kernel-0.c +index 6ef2f72..431f01a 100644 +--- a/src/kernel-0.c ++++ b/src/kernel-0.c +@@ -304,7 +304,10 @@ static FILE *OpenTableFile(int fl) + /* Create the table file. + ---------------------- */ + if (FfMakeTables(fl) != 0) +- MTX_ERROR("Unable to build arithmetic tables"); ++ { ++ MTX_ERROR("Unable to build arithmetic tables"); ++ return NULL; ++ } + fd = SysFopen(fn,FM_READ|FM_LIB); + return fd; + } +@@ -363,8 +366,7 @@ static int ReadTableFile(FILE *fd, int field) + return -1; + } + FfOrder = field; +- FfSetNoc(FfOrder); +- return 0; ++ return FfSetNoc(FfOrder); + } + + +@@ -471,7 +473,7 @@ size_t FfTrueRowSize(int noc) + ** Embed a subfield. + ** @param a Element of the subfield field. + ** @param subfield Subfield order. Must be a divisor of the current field order. +- ** @return @em a, embedded into the current field. ++ ** @return @em a, embedded into the current field, or 255 on error. + **/ + + FEL FfEmbed(FEL a, int subfield) +@@ -482,7 +484,9 @@ FEL FfEmbed(FEL a, int subfield) + return a; + for (i = 0; mtx_embedord[i] != subfield && i < 4; ++i); + if (i >= 4) +- MTX_ERROR2("Cannot embed GF(%d) into GF(%d)",(int)subfield,(int)FfOrder); ++ { MTX_ERROR2("Cannot embed GF(%d) into GF(%d)",(int)subfield,(int)FfOrder); ++ return (FEL)255; ++ } + return mtx_embed[i][a]; + } + +@@ -498,6 +502,7 @@ FEL FfEmbed(FEL a, int subfield) + ** FfSetField(subfield). + ** @param a Element of the current field. + ** @param subfield Subfield order. Must be a divisor of the current field order. ++ ** Return 255 on error. + **/ + + FEL FfRestrict(FEL a, int subfield) +@@ -511,6 +516,7 @@ FEL FfRestrict(FEL a, int subfield) + { + MTX_ERROR2("Cannot restrict GF(%d) to GF(%d)",(int)FfOrder, + (int)subfield); ++ return (FEL)255; + } + return mtx_restrict[i][a]; + } +diff --git a/src/maddmul.c b/src/maddmul.c +index f5c171d..24ad3a5 100644 +--- a/src/maddmul.c ++++ b/src/maddmul.c +@@ -59,7 +59,7 @@ Matrix_t *MatAddMul(Matrix_t *dest, const Matrix_t *src, FEL coeff) + ------------ */ + PTR dp = dest->Data, sp = src->Data; + int n; +- FfSetField(src->Field); ++ FfSetField(src->Field); /* No error checking */ + FfSetNoc(src->Noc); + for (n = src->Nor; n > 0; --n) + { +diff --git a/src/maketabF.c b/src/maketabF.c +index d7af83e..0fa26fb 100644 +--- a/src/maketabF.c ++++ b/src/maketabF.c +@@ -175,7 +175,7 @@ static void polymod(POLY a, POLY b) + testprim() - Test for primitivity. + ----------------------------------------------------------------- */ + +-static void testprim() ++static int testprim() + { + int i, a[256]; + +@@ -187,7 +187,9 @@ static void testprim() + { + fprintf(stderr,"*** a[%d]=%d.",i,a[i]); + MTX_ERROR("Polynome is not primitive."); ++ return 1; + } ++ return 0; + } + + +@@ -195,7 +197,7 @@ static void testprim() + initarith() - Initialize index and zech logarithm tables. + ----------------------------------------------------------------- */ + +-static void initarith() ++static int initarith() + { int i,elem; + POLY a; + +@@ -214,7 +216,7 @@ static void initarith() + polmultx(a); + polymod(a,irred); + } +- testprim(); ++ if (testprim()) return 1; + + /* Calculate zech logarithms + ------------------------- */ +@@ -222,6 +224,7 @@ static void initarith() + { elem = (int)((i%P)==P-1 ? i+1-P : i+1); /* add 1 */ + zech[indx[i]]=indx[elem]; /* Zech-table=result */ + } ++ return 0; + } + + +@@ -314,7 +317,7 @@ static BYTE pack(BYTE a[8]) + and initialize tables. + ----------------------------------------------------------------- */ + +-static void writeheader() ++static int writeheader() + { + int i, j; + +@@ -324,6 +327,7 @@ static void writeheader() + { + perror(filename); + MTX_ERROR("Cannot open table file"); ++ return 1; + } + for (CPM=1,maxmem=Q; (long)maxmem * Q <= 256L; ++CPM, maxmem *= Q); + for (i = 0; irrednrs[i] != (int) Q && irrednrs[i] != 0; ++i); +@@ -333,7 +337,7 @@ static void writeheader() + for (j = 0; j <= MAXGRAD; j++) + irred[j] = irreducibles[i][MAXGRAD-j]; + G = P; /* Generator is X */ +- initarith(); /* Init index- and Zech-tables */ ++ if (initarith()) return 1; /* Init index- and Zech-tables */ + } + else + { +@@ -357,6 +361,7 @@ static void writeheader() + } + MESSAGE(1,("Generator : %ld\n",info[1])); + MESSAGE(1,("Packing : %ld/byte\n",info[3])); ++ return 0; + } + + +@@ -364,14 +369,14 @@ static void writeheader() + checkq() - Set Q and N. Verify that Q is a prime power. + ----------------------------------------------------------------- */ + +-static void checkq(long l) ++static int checkq(long l) + { + long q, d; + + if (l < 2 || l > 256) + { +- fprintf(stderr,"Field order out of range (2-256)\n"); +- exit(EXIT_ERR); ++ MTX_ERROR1("Field order out of range (2-256): %E", MTX_ERR_RANGE); ++ return 1; + } + + Q = l; +@@ -381,9 +386,10 @@ static void checkq(long l) + q /= d; + if (q != 1) + { +- fprintf(stderr,"Illegal Field order\n"); +- exit(EXIT_ERR); ++ MTX_ERROR("Illegal Field order\n"); ++ return 1; + } ++ return 0; + } + + +@@ -407,7 +413,7 @@ static void inittables() + mkembed() - Calculate embeddings of all subfields. + ----------------------------------------------------------------- */ + +-static void mkembed() ++static int mkembed() + { + int n; /* Degree of subfield over Z_p */ + long q; /* subfield order */ +@@ -456,6 +462,7 @@ static void mkembed() + { + fprintf(stderr,"*** q=%ld, Q=%ld.",q,Q); + MTX_ERROR("Internal error."); ++ return 1; + } + + /* Calculate a generator for the subfield +@@ -502,13 +509,13 @@ static void mkembed() + fflush(stdout); + } + } ++ return 0; + } + + + static int Init(int field) + { +- checkq(field); +- return 0; ++ return checkq(field); + } + + /* ----------------------------------------------------------------- +@@ -526,7 +533,7 @@ int FfMakeTables(int field) + ---------- */ + if (Init(field) != 0) + return 1; +- writeheader(); /* Open file and write header */ ++ if (writeheader()) return 1; /* Open file and write header */ + inittables(); + + /* Make insert table +@@ -618,7 +625,7 @@ int FfMakeTables(int field) + } + } + +- mkembed(); ++ if (mkembed()) return 1; + + MESSAGE(1,("Writing tables to %s\n",filename)); + if ( +@@ -639,6 +646,7 @@ int FfMakeTables(int field) + { + perror(filename); + MTX_ERROR("Error writing table file"); ++ return 1; + } + fclose(fd); + return(0); +diff --git a/src/matadd.c b/src/matadd.c +index 54dbcb1..2d86d86 100644 +--- a/src/matadd.c ++++ b/src/matadd.c +@@ -48,7 +48,7 @@ Matrix_t *MatAdd(Matrix_t *dest, const Matrix_t *src) + ------------------- */ + dp = dest->Data; + sp = src->Data; +- FfSetField(src->Field); ++ FfSetField(src->Field); /* No error checking */ + FfSetNoc(src->Noc); + for (n = src->Nor; n > 0; --n) + { +diff --git a/src/matclean.c b/src/matclean.c +index e7307bf..16f02d6 100644 +--- a/src/matclean.c ++++ b/src/matclean.c +@@ -53,7 +53,7 @@ int MatClean(Matrix_t *mat, const Matrix_t *sub) + + /* Clean + ----- */ +- FfSetNoc(mat->Noc); ++ FfSetNoc(mat->Noc); /* No error checking */ + for (i = 0; i < mat->Nor; ++i) + { + PTR m = MatGetPtr(mat,i); +diff --git a/src/matcmp.c b/src/matcmp.c +index b778ec4..d503285 100644 +--- a/src/matcmp.c ++++ b/src/matcmp.c +@@ -38,7 +38,7 @@ MTX_DEFINE_FILE_INFO + ** not necessarily mean that an error has occured. + ** @param a First matrix. + ** @param b Second matrix. +- ** @return 0 if the matrices are equal, nonzero otherwise (see description). ++ ** @return 0 if the matrices are equal, nonzero otherwise (see description), -2 on error. + **/ + + int MatCompare(const Matrix_t *a, const Matrix_t *b) +@@ -50,7 +50,7 @@ int MatCompare(const Matrix_t *a, const Matrix_t *b) + if (!MatIsValid(a) || !MatIsValid(b)) + { + MTX_ERROR1("%E",MTX_ERR_BADARG); +- return -1; ++ return -2; + } + + /* Compare fields and dimensions +@@ -65,7 +65,7 @@ int MatCompare(const Matrix_t *a, const Matrix_t *b) + /* Compare the entries row by row. We do not use memcmp on the + whole matrix because we must ignore padding bytes. + ----------------------------------------------------------- */ +- FfSetField(a->Field); ++ FfSetField(a->Field); /* No error checking */ + FfSetNoc(a->Noc); + for (i = 0; i < a->Nor; ++i) + { +diff --git a/src/matcopy.c b/src/matcopy.c +index 75852dd..c3e7850 100644 +--- a/src/matcopy.c ++++ b/src/matcopy.c +@@ -105,7 +105,7 @@ int MatCopyRegion(Matrix_t *dest, int destrow, int destcol, + { + #ifdef PARANOID + FEL f; +- FfSetNoc(src->Noc); ++ FfSetNoc(src->Noc); /* No error checking */ + f = FfExtract(s,k); + FfSetNoc(dest->Noc); + FfInsert(d,destcol+k-col1,f); +diff --git a/src/matcore.c b/src/matcore.c +index 1f27dfd..0dc9d92 100644 +--- a/src/matcore.c ++++ b/src/matcore.c +@@ -131,7 +131,7 @@ Matrix_t *MatAlloc(int field, int nor, int noc) + SysFree(m); + return NULL; + } +- FfSetNoc(noc); ++ if (FfSetNoc(noc)) return NULL; + m->Magic = MAT_MAGIC; + m->Field = field; + m->Nor = nor; +diff --git a/src/matcut.c b/src/matcut.c +index fde0662..f274311 100644 +--- a/src/matcut.c ++++ b/src/matcut.c +@@ -79,11 +79,12 @@ Matrix_t *MatCut(const Matrix_t *src, int row1, int col1, int nrows, int ncols) + /* Initialize pointers to the source and destination matrix + -------------------------------------------------------- */ + s = MatGetPtr(src,row1); ++ if (!s) return NULL; + d = result->Data; + + /* Copy the requested data + ----------------------- */ +- FfSetNoc(ncols); ++ if (FfSetNoc(ncols)) return NULL; + for (n = nrows; n > 0; --n) + { + if (col1 == 0) +@@ -95,9 +96,9 @@ Matrix_t *MatCut(const Matrix_t *src, int row1, int col1, int nrows, int ncols) + { + #ifdef PARANOID + FEL f; +- FfSetNoc(src->Noc); ++ FfSetNoc(src->Noc); /* No error checking */ + f = FfExtract(s,col1+k); +- FfSetNoc(ncols); ++ FfSetNoc(ncols); /* error was checked above */ + FfInsert(d,k,f); + #else + FfInsert(d,k,FfExtract(s,col1+k)); +diff --git a/src/matech.c b/src/matech.c +index ed31cf4..ee52ebe 100644 +--- a/src/matech.c ++++ b/src/matech.c +@@ -124,7 +124,7 @@ int MatEchelonize(Matrix_t *mat) + + /* Build the pivot table + --------------------- */ +- FfSetField(mat->Field); ++ FfSetField(mat->Field); /* No error checking */ + FfSetNoc(mat->Noc); + rank = zmkechelon(mat->Data,mat->Nor,mat->Noc,mat->PivotTable,is_pivot); + +@@ -163,13 +163,14 @@ long MatNullity(const Matrix_t *mat) + ** This function calculates the dimension of the null-space of a matrix + ** and deletes the matrix. + ** @param mat Pointer to the matrix. +- ** @return Nullity of @em mat, or -$ on error. ++ ** @return Nullity of @em mat, or $-1$ on error. + **/ + + long MatNullity__(Matrix_t *mat) + { + long nul; +- MatEchelonize(mat); ++ if (!mat) return -1; ++ if (MatEchelonize(mat)==-1) return -1; + nul = mat->Noc - mat->Nor; + MatFree(mat); + return nul; +diff --git a/src/matins.c b/src/matins.c +index 45c31e4..50fe9c1 100644 +--- a/src/matins.c ++++ b/src/matins.c +@@ -54,7 +54,7 @@ Matrix_t *MatInsert_(Matrix_t *mat, const Poly_t *pol) + return NULL; + } + +- FfSetField(mat->Field); ++ FfSetField(mat->Field); /* No error checking */ + FfSetNoc(nor); + + /* Special case: p(x) = 0 +@@ -81,7 +81,10 @@ Matrix_t *MatInsert_(Matrix_t *mat, const Poly_t *pol) + /* Evaluate p(A) + ------------- */ + if (pol->Degree > 1) +- x = MatDup(mat); ++ { ++ x = MatDup(mat); ++ if (!x) return NULL; ++ } + if ((f = pol->Data[pol->Degree]) != FF_ONE) + { + for (l = nor, v = mat->Data; l > 0; --l, FfStepPtr(&v)) +@@ -147,6 +150,7 @@ Matrix_t *MatInsert(const Matrix_t *mat, const Poly_t *pol) + if (pol->Degree == 0) + { + x = MatAlloc(mat->Field,nor,nor); ++ if (!x) return NULL; + for (l = 0, v = x->Data; l < nor; ++l, FfStepPtr(&v)) + FfInsert(v,l,pol->Data[0]); + return x; +@@ -155,6 +159,7 @@ Matrix_t *MatInsert(const Matrix_t *mat, const Poly_t *pol) + /* Evaluate p(A) + ------------- */ + x = MatDup(mat); ++ if (!x) return NULL; + if ((f = pol->Data[pol->Degree]) != FF_ONE) + { + for (l = nor, v = x->Data; l > 0; --l, FfStepPtr(&v)) +diff --git a/src/matinv.c b/src/matinv.c +index 217eb0e..990cbe7 100644 +--- a/src/matinv.c ++++ b/src/matinv.c +@@ -114,6 +114,7 @@ Matrix_t *MatInverse(const Matrix_t *mat) + /* Copy matrix into workspace + -------------------------- */ + tmp = FfAlloc(mat->Nor); ++ if (!tmp) return NULL; + memcpy(tmp,mat->Data,FfCurrentRowSize * mat->Nor); + + /* Inversion +diff --git a/src/matmul.c b/src/matmul.c +index 20f5e88..bed30fc 100644 +--- a/src/matmul.c ++++ b/src/matmul.c +@@ -63,7 +63,7 @@ Matrix_t *MatMul(Matrix_t *dest, const Matrix_t *src) + + /* Matrix multiplication + --------------------- */ +- FfSetField(src->Field); ++ FfSetField(src->Field); /* no error checking, since the matrix *exists* */ + FfSetNoc(src->Noc); + result = tmp = FfAlloc(dest->Nor); + if (result == NULL) +diff --git a/src/matnull.c b/src/matnull.c +index 4f28566..2550b96 100644 +--- a/src/matnull.c ++++ b/src/matnull.c +@@ -27,6 +27,8 @@ MTX_DEFINE_FILE_INFO + - |piv| contains a pivot table for the null space. + If |flags| is nonzero, the null-space is not reduced to echelon form, + and the contents of |piv| are undefined. ++ ++ Return -1 on error, the dimension of the null-space on success. + ** @see + **/ + +@@ -40,7 +42,7 @@ static long znullsp(PTR matrix, long nor, int *piv, PTR nsp, int flags) + + /* Make the identity matrix in . + ---------------------------------- */ +- FfSetNoc(nor); ++ if (FfSetNoc(nor)) return -1; + x = nsp; + for (i = 0; i < nor; ++i) + { +@@ -61,13 +63,12 @@ static long znullsp(PTR matrix, long nor, int *piv, PTR nsp, int flags) + + for (k = 0; k < i; ++k) + { +- FfSetNoc(noc); ++ FfSetNoc(noc); /* No error checking, since noc used to be the previously assigned number of columns */ + if ((p = piv[k]) >= 0 && (f = FfExtract(x,p)) != FF_ZERO) + { + f = FfNeg(FfDiv(f,FfExtract(xx,p))); +- FfSetNoc(noc); + FfAddMulRow(x,xx,f); +- FfSetNoc(nor); ++ FfSetNoc(nor); /* we have asserted above that it doesn't fail */ + FfAddMulRow(y,yy,f); + } + FfSetNoc(noc); +@@ -151,11 +152,21 @@ Matrix_t *MatNullSpace_(Matrix_t *mat, int flags) + if (nsp == NULL) + return NULL; + nsp->PivotTable = NREALLOC(nsp->PivotTable,int,mat->Nor); ++ if (!nsp->PivotTable) ++ { ++ MatFree(nsp); ++ return NULL; ++ } + + /* Calculate the null-space + ------------------------ */ +- FfSetNoc(mat->Noc); ++ FfSetNoc(mat->Noc); /* No error checking */ + dim = znullsp(mat->Data,mat->Nor,nsp->PivotTable,nsp->Data,flags); ++ if (dim==-1) ++ { ++ MatFree(nsp); ++ return NULL; ++ } + if (flags) + { + SysFree(nsp->PivotTable); +diff --git a/src/matorder.c b/src/matorder.c +index 16aec74..24b31a3 100644 +--- a/src/matorder.c ++++ b/src/matorder.c +@@ -32,7 +32,7 @@ MTX_DEFINE_FILE_INFO + ** the order is greater than 1000000, or if the order on any cyclic + ** subspace is greater than 1000. + ** @param mat Pointer to the matrix. +- ** @return The order of @em mat, or 1 on error. ++ ** @return The order of @em mat, or -1 on error. + **/ + + int MatOrder(const Matrix_t *mat) +@@ -59,15 +59,29 @@ int MatOrder(const Matrix_t *mat) + FfSetNoc(mat->Noc); + nor = mat->Nor; + m1 = FfAlloc(nor); ++ if (!m1) return -1; + memcpy(m1,mat->Data,FfCurrentRowSize * nor); + bend = basis = FfAlloc(nor+1); ++ if (!bend) ++ { ++ SysFree(m1); ++ return -1; ++ } + + piv = NALLOC(int,nor+1); + done = NALLOC(char,nor); ++ if (!piv || !done) ++ { SysFree(m1); ++ return -1; ++ } + memset(done,0,(size_t)nor); + v1 = FfAlloc(1); + v2 = FfAlloc(1); + v3 = FfAlloc(1); ++ if (!v1 || !v2 || !v3) ++ { SysFree(m1); ++ return -1; ++ } + tord = ord = 1; + dim = 0; + j1 = 1; +diff --git a/src/matpivot.c b/src/matpivot.c +index abe342a..c843282 100644 +--- a/src/matpivot.c ++++ b/src/matpivot.c +@@ -71,7 +71,6 @@ static int zmkpivot(PTR matrix, int nor, int noc, int *piv, int *ispiv) + + int MatPivotize(Matrix_t *mat) + { +- int rc; + int *newtab; + static int *is_pivot = NULL; + static int maxnoc = -1; +@@ -106,9 +105,7 @@ int MatPivotize(Matrix_t *mat) + --------------------- */ + FfSetField(mat->Field); + FfSetNoc(mat->Noc); +- rc = zmkpivot(mat->Data,mat->Nor,mat->Noc,mat->PivotTable,is_pivot); +- +- return rc; ++ return zmkpivot(mat->Data,mat->Nor,mat->Noc,mat->PivotTable,is_pivot); + } + + /** +diff --git a/src/matpwr.c b/src/matpwr.c +index cd66b5e..b06e5b2 100644 +--- a/src/matpwr.c ++++ b/src/matpwr.c +@@ -119,8 +119,14 @@ Matrix_t *MatPower(const Matrix_t *mat, long n) + FfSetField(mat->Field); + FfSetNoc(mat->Noc); + tmp = FfAlloc(FfNoc); ++ if (!tmp) return NULL; + memcpy(tmp,mat->Data,FfCurrentRowSize * FfNoc); + tmp2 = FfAlloc(FfNoc); ++ if (!tmp2) ++ { ++ SysFree(tmp); ++ return NULL; ++ } + result = MatAlloc(mat->Field,mat->Nor,mat->Noc); + if (result != NULL) + matpwr_(n,tmp,result->Data,tmp2); +diff --git a/src/matread.c b/src/matread.c +index 031d100..06e6e6b 100644 +--- a/src/matread.c ++++ b/src/matread.c +@@ -46,8 +46,9 @@ Matrix_t *MatRead(FILE *f) + return NULL; + if (FfReadRows(f,m->Data,m->Nor) != m->Nor) + { +- MatFree(m); +- return NULL; ++ MTX_ERROR("Number of given rows does not coincide with given row number"); ++ MatFree(m); ++ return NULL; + } + return m; + } +diff --git a/src/mattrace.c b/src/mattrace.c +index f500248..772a6e4 100644 +--- a/src/mattrace.c ++++ b/src/mattrace.c +@@ -21,7 +21,7 @@ + ** This function calculates the sum of all diagonal elements of a matrix. + ** Note that the matrix need not be square. + ** @param mat Pointer to the matrix. +- ** @return Trace of @a mat, @c FF_ZERO on error. ++ ** @return Trace of @a mat, @c 255 on error. + **/ + + FEL MatTrace(const Matrix_t *mat) +@@ -35,7 +35,7 @@ FEL MatTrace(const Matrix_t *mat) + ------------------ */ + #ifdef DEBUG + if (!MatIsValid(mat)) +- return FF_ZERO; ++ return (FEL)255; + #endif + + maxi = mat->Nor > mat->Noc ? mat->Noc : mat->Nor; +diff --git a/src/matwrite.c b/src/matwrite.c +index 1fb6af3..b364e80 100644 +--- a/src/matwrite.c ++++ b/src/matwrite.c +@@ -44,7 +44,10 @@ int MatWrite(const Matrix_t *mat, FILE *f) + FfSetField(mat->Field); + FfSetNoc(mat->Noc); + if (FfWriteRows(f,mat->Data,mat->Nor) != mat->Nor) +- return -1; ++ { ++ MTX_ERROR("Cannot write rows"); ++ return -1; ++ } + return 0; + } + +@@ -75,7 +78,10 @@ int MatSave(const Matrix_t *mat, const char *fn) + i = MatWrite(mat,f); + fclose(f); + if (i != 0) +- MTX_ERROR1("Cannot write matrix to %s",fn); ++ { ++ MTX_ERROR1("Cannot write matrix to %s",fn); ++ return -1; ++ } + return i; + } + +diff --git a/src/meataxe.h b/src/meataxe.h +index 368b37b..0efa7dd 100644 +--- a/src/meataxe.h ++++ b/src/meataxe.h +@@ -135,8 +135,8 @@ PTR FfSubRowPartialReverse(PTR dest, PTR src, int first, int len); + PTR FfAlloc(int nor); + int FfCmpRows(PTR p1, PTR p2); + void FfCleanRow(PTR row, PTR matrix, int nor, const int *piv); +-void FfCleanRow2(PTR row, PTR matrix, int nor, const int *piv, PTR row2); +-void FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, ++int FfCleanRow2(PTR row, PTR matrix, int nor, const int *piv, PTR row2); ++int FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, + PTR row2, PTR mat2); + void FfCopyRow(PTR dest, PTR src); + FEL FfEmbed(FEL a, int subfield); +diff --git a/src/mmulscal.c b/src/mmulscal.c +index 281be16..9bff3ff 100644 +--- a/src/mmulscal.c ++++ b/src/mmulscal.c +@@ -21,7 +21,7 @@ + ** Multiply a Matrix by a Constant. + ** @param dest Pointer to the matrix. + ** @param coeff Value to multiply with. +- ** @return The function returns @a dest. ++ ** @return The function returns @a dest, or NULL on error in debug mode only. + **/ + + Matrix_t *MatMulScalar(Matrix_t *dest, FEL coeff) +diff --git a/src/mtensor.c b/src/mtensor.c +index b2b9ac7..2fb90ca 100644 +--- a/src/mtensor.c ++++ b/src/mtensor.c +@@ -85,6 +85,11 @@ Matrix_t *MatTensor(const Matrix_t *m1, const Matrix_t *m2) + ---------------------------------------- */ + x1 = m1->Data; + x3 = MatGetPtr(temat,i2); ++ if (!x3) ++ { ++ MatFree(temat); ++ return NULL; ++ } + FfSetNoc(temat->Noc); + + /* Loop through all rows of +diff --git a/src/quotient.c b/src/quotient.c +index f44b556..eea0754 100644 +--- a/src/quotient.c ++++ b/src/quotient.c +@@ -82,16 +82,23 @@ Matrix_t *QProjection(const Matrix_t *subspace, const Matrix_t *vectors) + sdim = subspace->Nor; + qdim = subspace->Noc - sdim; + result = MatAlloc(subspace->Field,vectors->Nor,qdim); ++ if (!result) return NULL; + + /* Calculate the projection + ------------------------ */ + FfSetNoc(subspace->Noc); + tmp = FfAlloc(1); ++ if (!tmp) return NULL; + non_piv = subspace->PivotTable + subspace->Nor; + for (i = 0; i < vectors->Nor; ++i) + { + int k; + PTR q = MatGetPtr(result,i); ++ if (!q) ++ { ++ SysFree(tmp); ++ return NULL; ++ } + FfCopyRow(tmp,MatGetPtr(vectors,i)); + FfCleanRow(tmp,subspace->Data,sdim,subspace->PivotTable); + for (k = 0; k < qdim; ++k) +@@ -158,14 +165,20 @@ Matrix_t *QAction(const Matrix_t *subspace, const Matrix_t *gen) + + /* Calculate the action on the quotient + ------------------------------------ */ +- FfSetNoc(dim); ++ FfSetNoc(dim); /* No error checking, since dim is the ->Noc of an existing matrix */ + tmp = FfAlloc(1); ++ if (!tmp) return NULL; + piv = subspace->PivotTable; + non_piv = piv + subspace->Nor; + for (k = 0; k < qdim; ++k) + { + int l; + PTR qx = MatGetPtr(action,k); ++ if (!qx) ++ { ++ SysFree(tmp); ++ return NULL; ++ } + FfCopyRow(tmp,MatGetPtr(gen,non_piv[k])); + FfCleanRow(tmp,subspace->Data,sdim,piv); + for (l = 0; l < qdim; ++l) +diff --git a/src/saction.c b/src/saction.c +index adae3cf..0aba44d 100644 +--- a/src/saction.c ++++ b/src/saction.c +@@ -68,8 +68,14 @@ Matrix_t *SAction(const Matrix_t *subspace, const Matrix_t *gen) + sdim = subspace->Nor; + FfSetField(subspace->Field); + action = MatAlloc(FfOrder,sdim,sdim); +- FfSetNoc(dim); ++ if (!action) return NULL; ++ FfSetNoc(dim); /* No error checking, since dim is the ->Noc of an existing matrix */ + tmp = FfAlloc(1); ++ if (!tmp) ++ { ++ MatFree(action); ++ return NULL; ++ } + + /* Calaculate the action. + ---------------------- */ +@@ -77,6 +83,12 @@ Matrix_t *SAction(const Matrix_t *subspace, const Matrix_t *gen) + { + PTR xi = MatGetPtr(subspace,i); + PTR yi = MatGetPtr(action,i); ++ if (!xi || !yi) ++ { ++ MatFree(action); ++ SysFree(tmp); ++ return NULL; ++ } + FEL f; + + /* Calculate the image of the -th row of . +@@ -85,10 +97,20 @@ Matrix_t *SAction(const Matrix_t *subspace, const Matrix_t *gen) + + /* Clean the image with the subspace and store coefficients. + --------------------------------------------------------- */ +- FfCleanRow2(tmp,subspace->Data,sdim,subspace->PivotTable,yi); ++ if (FfCleanRow2(tmp,subspace->Data,sdim,subspace->PivotTable,yi)) ++ { ++ MatFree(action); ++ SysFree(tmp); ++ return NULL; ++ } + if (FfFindPivot(tmp,&f) >= 0) +- MTX_ERROR("Split(): Subspace not invariant"); ++ { ++ MatFree(action); ++ SysFree(tmp); ++ MTX_ERROR("Split(): Subspace not invariant"); ++ return NULL; + } ++ } + + /* Clean up and return the result. + ------------------------------- */ +diff --git a/src/stabpwr.c b/src/stabpwr.c +index ff33bc6..01282ab 100644 +--- a/src/stabpwr.c ++++ b/src/stabpwr.c +@@ -68,15 +68,18 @@ int StablePower_(Matrix_t *mat, int *pwr, Matrix_t **ker) + --------------------------- */ + p = 1; + k1 = MatNullSpace(mat); +- MatMul(mat,mat); ++ if (!k1) return -1; ++ if (!MatMul(mat,mat)) return -1; + k2 = MatNullSpace(mat); ++ if (!k2) return -1; + while (k2->Nor > k1->Nor) + { + p *= 2; + MatFree(k1); + k1 = k2; +- MatMul(mat,mat); ++ if (!MatMul(mat,mat)) return -1; + k2 = MatNullSpace(mat); ++ if (!k2) return -1; + } + MatFree(k2); + +diff --git a/src/sumint.c b/src/sumint.c +index 278acd8..905fa79 100644 +--- a/src/sumint.c ++++ b/src/sumint.c +@@ -77,7 +77,7 @@ int FfSumAndIntersection(PTR wrk1, int *nor1, int *nor2, PTR wrk2, int *piv) + { + FEL f; + int p; +- FfCleanRowAndRepeat(x1,wrk1,k,piv,x2,wrk2); ++ if (FfCleanRowAndRepeat(x1,wrk1,k,piv,x2,wrk2)) return -1; + if ((p = FfFindPivot(x1,&f)) < 0) + continue; /* Null row - ignore */ + if (k < i) +diff --git a/src/temap.c b/src/temap.c +index 7ba445a..4c2d493 100644 +--- a/src/temap.c ++++ b/src/temap.c +@@ -74,17 +74,21 @@ Matrix_t *TensorMap(Matrix_t *vec, const Matrix_t *a, const Matrix_t *b) + for (i = 0; i < vec->Nor; ++i) + { + Matrix_t *tmp = MatTransposed(a); ++ if (!tmp) return NULL; + Matrix_t *v = VectorToMatrix(vec,i,b->Nor); + if (v == NULL) + { + MTX_ERROR("Conversion failed"); +- break; ++ return NULL; + } +- MatMul(tmp,v); ++ if (!MatMul(tmp,v)) return NULL; + MatFree(v); +- MatMul(tmp,b); ++ if (!MatMul(tmp,b)) return NULL; + if (MatrixToVector(tmp,result,i)) +- MTX_ERROR("Conversion failed"); ++ { ++ MTX_ERROR("Conversion failed"); ++ return NULL; ++ } + MatFree(tmp); + } + return result; +diff --git a/src/vec2mat.c b/src/vec2mat.c +index 1047805..e76ad88 100644 +--- a/src/vec2mat.c ++++ b/src/vec2mat.c +@@ -63,8 +63,11 @@ Matrix_t *VectorToMatrix(Matrix_t *vecs, int n, int noc) + return NULL; + for (i = 0; i < result->Nor; ++i) + { +- if (MatCopyRegion(result,i,0, vecs,n,i*noc,1,noc) != 0) +- MTX_ERROR("Copy failed"); ++ if (MatCopyRegion(result,i,0, vecs,n,i*noc,1,noc) != 0) ++ { ++ MTX_ERROR("Copy failed"); ++ return NULL; ++ } + } + return result; + } +diff --git a/src/window.c b/src/window.c +index 9c87694..fbeb943 100644 +--- a/src/window.c ++++ b/src/window.c +@@ -69,7 +69,11 @@ MatrixWindow_t *WindowAlloc(int fl, int nor, size_t rowsize) + MTX_ERROR1("%E",MTX_ERR_NOMEM); + return NULL; + } +- FfSetField(fl); ++ if (FfSetField(fl)) ++ { ++ free(out); ++ return NULL; ++ } + out->Matrix = MatAlloc(fl, nor, rowsize*sizeof(long)*MPB); + if (out->Matrix == NULL) + { +@@ -266,7 +270,8 @@ __asm__(" popl %ebx\n" + + /** dest := left+right + left and right must be distinct, but one of them may coincide with dest -- under the assumption +- that, in that case, the ambient matrices coincide as well. **/ ++ that, in that case, the ambient matrices coincide as well. ++ Return dest, or NULL on error (the only error may occur in a compatibility check). **/ + MatrixWindow_t *WindowSum(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) + { + PTR x, result, tmp; +@@ -335,6 +340,7 @@ MatrixWindow_t *WindowSum(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWind + /** dest := left-right + left and right must be distinct, but one of them may coincide with dest -- under the assumption + that, in that case, the ambient matrices coincide as well. ++ Return dest, or NULL on error (the only error may occur in a compatibility check). + **/ + MatrixWindow_t *WindowDif(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) + { +@@ -407,7 +413,7 @@ MatrixWindow_t *WindowDif(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWind + can write the result into it. Moreover, the chunk of memory pointed at by dest MUST be disjoint + from the chunks for left and right! + +- Dimensions are not tested! ++ Dimensions are not tested, always dest will be returned (no error value). + **/ + MatrixWindow_t *WindowAddMul(MatrixWindow_t *dest, MatrixWindow_t *left, MatrixWindow_t *right) + { +@@ -617,7 +623,7 @@ int StrassenStep(MatrixWindow_t *dest_win, MatrixWindow_t *A_win, MatrixWindow_t + S2->RowSize = A_sub_rowsize; + S2->Matrix = X->Matrix; + S2->ULCorner = X->ULCorner; +- WindowDif(S2, A00, A10); ++ WindowDif(S2, A00, A10); /* No error checking, as we know that the windows are compatible */ + /* + printf("1. S2 = A00-A10 in X\n"); + WindowShow(X); +@@ -653,7 +659,7 @@ int StrassenStep(MatrixWindow_t *dest_win, MatrixWindow_t *A_win, MatrixWindow_t + S0->RowSize = A_sub_rowsize; + S0->Matrix = X->Matrix; + S0->ULCorner = X->ULCorner; +- WindowSum(S0, A10, A11); ++ WindowSum(S0, A10, A11); /* no error checking here and below, as we know the dimensions of the windows */ + /* + printf("4. S0 = A10+A11 in X\n"); + WindowShow(X); +diff --git a/src/zcleanrow.c b/src/zcleanrow.c +index b4dcb30..d36a165 100644 +--- a/src/zcleanrow.c ++++ b/src/zcleanrow.c +@@ -63,10 +63,10 @@ void FfCleanRow(PTR row, PTR matrix, int nor, const int *piv) + ** @param nor Number of rows. + ** @param piv Pivot table for @em matrix. + ** @param row2 Pointer to row where the operations are recorded. +- ** @return Always 0. ++ ** @return 0, or 1 on error. + **/ + +-void FfCleanRow2(PTR row, PTR mat, int nor, const int *piv, PTR row2) ++int FfCleanRow2(PTR row, PTR mat, int nor, const int *piv, PTR row2) + { + int i; + PTR x; +@@ -74,7 +74,7 @@ void FfCleanRow2(PTR row, PTR mat, int nor, const int *piv, PTR row2) + if (row2 == NULL || piv == NULL) + { + MTX_ERROR1("%E",MTX_ERR_BADARG); +- return; ++ return 1; + } + for (i = 0, x = mat; i < nor; ++i, FfStepPtr(&x)) + { +@@ -86,6 +86,7 @@ void FfCleanRow2(PTR row, PTR mat, int nor, const int *piv, PTR row2) + FfInsert(row2,i,f); + } + } ++ return 0; + } + + +@@ -100,10 +101,10 @@ void FfCleanRow2(PTR row, PTR mat, int nor, const int *piv, PTR row2) + ** @param piv Pivot table for @em mat. + ** @param row2 Pointer to the second row to be cleaned. + ** @param mat2 Matrix to the second matrix. +- ** @return Always 0. ++ ** @return 0, or 1 on error. + **/ + +-void FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, PTR row2, PTR mat2) ++int FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, PTR row2, PTR mat2) + { + int i; + PTR x, x2; +@@ -112,7 +113,7 @@ void FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, PTR row2, PT + if (row2 == NULL || piv == NULL || row2 == NULL || mat2 == NULL) + { + MTX_ERROR1("%E",MTX_ERR_BADARG); +- return; ++ return 1; + } + #endif + for (i = 0, x = mat, x2 = mat2; i < nor; ++i, FfStepPtr(&x), FfStepPtr(&x2)) +@@ -125,6 +126,7 @@ void FfCleanRowAndRepeat(PTR row, PTR mat, int nor, const int *piv, PTR row2, PT + FfAddMulRow(row2,x2,f); + } + } ++ return 0; + } + + diff --git a/build/pkgs/meataxe/spkg-install b/build/pkgs/meataxe/spkg-install new file mode 100755 index 00000000000..7733e9e44cb --- /dev/null +++ b/build/pkgs/meataxe/spkg-install @@ -0,0 +1,95 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +cd src + +for patch in ../patches/*.patch; do + [ -r "$patch" ] || continue # Skip non-existing or non-readable patches + echo "Applying $patch" + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + return 1 + fi +done + +## The following *could* be put into Makefile.conf + +# This is the place where arithmetic tables and some other input files are +# searched by default. +export MTXLIB="$DOT_SAGE/meataxe" +# Directory where executables are installed. +export MTXBIN="$SAGE_LOCAL/bin" +# Default compiler flags +export CFLAGS1="-std=gnu99 -O -Wall -fPIC" +# Field size up to GF(256) +export ZZZ=0 + +# In principle, one should uncomment for field sizes up to GF(2^16). +# But upstream doesn't provide the required sources. +#export ZZZ=1 + +# The following is just to make MeatAxe's Makefile happy +touch Makefile.conf + +# We create a directory for the multiplication tables +mkdir -p $MTXLIB + +if [ $? -ne 0 ]; then + echo >&2 "Error creating directory for multiplication tables." + exit 1 +fi + +## Install! Aparently MeatAxe would rebuild everything when +## testing, and "make check" also installs. So, if a test +## is requested then we do it in one go. + +if [ "x$SAGE_CHECK" = xyes ]; then + $MAKE check +else + $MAKE +fi + +if [ $? -ne 0 ]; then + echo >&2 "Error installing MeatAxe." + exit 1 +fi + +## Surprisingly, MeatAxe's Makefile does NOT install the binaries +## in MTXBIN. Hence, we do it manually. + +mv bin/* "$MTXBIN" +if [ $? -ne 0 ]; then + echo >&2 "Error copying MeatAxe executables." + exit 1 +fi + +# We move the meataxe library to a permanent location +mv tmp/libmtx.a "$SAGE_LOCAL/lib" +if [ $? -ne 0 ]; then + echo >&2 "Error copying MeatAxe library." + exit 1 +fi + +cp src/meataxe.h "$SAGE_LOCAL/include/" +if [ $? -ne 0 ]; then + echo >&2 "Error copying MeatAxe header." + exit 1 +fi + +# Are we supposed to install the documentation? +if [ "x$SAGE_SPKG_INSTALL_DOCS" = xyes ] ; then + mkdir -p $SAGE_ROOT/local/share/doc/meataxe/ + cp -r doc/* $SAGE_ROOT/local/share/doc/meataxe/ + if [ $? -ne 0 ]; then + echo "Error copying documentation." + exit 1 + else + echo "The documentation can be found in $SAGE_ROOT/local/share/doc/meataxe/" + fi +fi diff --git a/build/pkgs/meataxe/type b/build/pkgs/meataxe/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/meataxe/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/mistune/checksums.ini b/build/pkgs/mistune/checksums.ini index 8162151f3af..6fe269db4c0 100644 --- a/build/pkgs/mistune/checksums.ini +++ b/build/pkgs/mistune/checksums.ini @@ -1,4 +1,4 @@ tarball=mistune-VERSION.tar.gz -sha1=21f0c7bf9bad7fac2e34355ed2c50e48557e2c8d -md5=10a42265bfc7e9ad817fe777a4857821 -cksum=492791270 +sha1=82b4fdb21f968266154530db51da7c3c88138df7 +md5=057bc28bf629d6a1283d680a34ed9d0f +cksum=1962061358 diff --git a/build/pkgs/mistune/package-version.txt b/build/pkgs/mistune/package-version.txt index 4b9fcbec101..39e898a4f95 100644 --- a/build/pkgs/mistune/package-version.txt +++ b/build/pkgs/mistune/package-version.txt @@ -1 +1 @@ -0.5.1 +0.7.1 diff --git a/build/pkgs/mpmath/checksums.ini b/build/pkgs/mpmath/checksums.ini index db86a30f6e4..64388d39b1d 100644 --- a/build/pkgs/mpmath/checksums.ini +++ b/build/pkgs/mpmath/checksums.ini @@ -1,4 +1,4 @@ tarball=mpmath-VERSION.tar.gz -sha1=b103be6ab76aa2747f5b5d46b9a097b97a191dee -md5=e2e7a7932969f52f25b7e19d8f0438bd -cksum=1143910860 +sha1=65da0ed4ed01d067f0f5b890ca28ad2f2fb56343 +md5=af5cc956b2673b33a25c3e57299bae7b +cksum=1713388601 diff --git a/build/pkgs/mpmath/package-version.txt b/build/pkgs/mpmath/package-version.txt index a4d2aceeb8d..caa4836d8e0 100644 --- a/build/pkgs/mpmath/package-version.txt +++ b/build/pkgs/mpmath/package-version.txt @@ -1 +1 @@ -0.18 +0.19 diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index 7e388037867..212e5fa11aa 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,4 +1,4 @@ tarball=nbconvert-VERSION.tar.gz -sha1=7b149cb25a1122763c47c5ae8327ea2ce3cea5bc -md5=223067790b8f193f587f5b59ee76f568 -cksum=418065607 +sha1=079fd51c75e5c8b76f9679aa7fc5ad8176004720 +md5=58ee16f5da0dd2976ff992511d6bfb43 +cksum=75346878 diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index fcdb2e109f6..ee74734aa22 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -4.0.0 +4.1.0 diff --git a/build/pkgs/nbformat/checksums.ini b/build/pkgs/nbformat/checksums.ini index 1c6f0fe4005..fec20b2e779 100644 --- a/build/pkgs/nbformat/checksums.ini +++ b/build/pkgs/nbformat/checksums.ini @@ -1,4 +1,4 @@ tarball=nbformat-VERSION.tar.gz -sha1=f8a2533ccee18596eb53c6404af6062ed2432585 -md5=ce31bb7f34c6a3554dc4a7625fc3033f -cksum=227017057 +sha1=c823d95938cb12b8e8af918bd2e547d3ca966e4f +md5=684c4dc3c6fd8036fd1ae1c908003e23 +cksum=3054783982 diff --git a/build/pkgs/nbformat/package-version.txt b/build/pkgs/nbformat/package-version.txt index fcdb2e109f6..1454f6ed4b7 100644 --- a/build/pkgs/nbformat/package-version.txt +++ b/build/pkgs/nbformat/package-version.txt @@ -1 +1 @@ -4.0.0 +4.0.1 diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index 9aaf119a3b8..3ca7296c40c 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,4 +1,4 @@ tarball=networkx-VERSION.tar.gz -sha1=99292e464c25be5e96de295752880bf5e5f1848a -md5=eb7a065e37250a4cc009919dacfe7a9d -cksum=2520536431 +sha1=ac24380b13dfe92633370ad2091c0c04b6d098a2 +md5=6ef584a879e9163013e9a762e1cf7cd1 +cksum=1278894037 diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index c044b1a3269..09601587019 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -1.10 +1.11 diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index e10dd9db4ab..e73b5a93040 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION.tar.gz -sha1=b6546f0b68e60ea2efa354be24fa4ded19708aaa -md5=91d6d780832f1bec7545c66e367d34f3 -cksum=2631689251 +sha1=cf953bcaf73ba43e7ef0ce829126b6605105dd0f +md5=d390cf1a0785a43711a2548c8f3c91b8 +cksum=2636243005 diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index f0cd2d909bb..ee74734aa22 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -4.0.4.p2 +4.1.0 diff --git a/build/pkgs/notebook/patches/help_link_url_fix.patch b/build/pkgs/notebook/patches/help_link_url_fix.patch new file mode 100644 index 00000000000..cfa07dee3d3 --- /dev/null +++ b/build/pkgs/notebook/patches/help_link_url_fix.patch @@ -0,0 +1,24 @@ +Use require.toUrl for help_links + +Dirty patch for the minified js, real PR is at +https://github.com/jupyter/notebook/pull/958 + + +--- a/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:30.769442884 +0100 ++++ b/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:53.073164049 +0100 +@@ -28358,7 +28358,7 @@ + .append($("") + .attr('target', '_blank') + .attr('title', 'Opens in a new window') +- .attr('href', link.url) ++ .attr('href', require.toUrl(link.url)) + .append($("") + .addClass("fa fa-external-link menu-icon pull-right") + ) +@@ -30547,4 +30547,4 @@ + define("notebook/js/main", function(){}); + + +-//# sourceMappingURL=main.min.js.map +\ No newline at end of file ++//# sourceMappingURL=main.min.js.map diff --git a/build/pkgs/notebook/patches/mathjax.patch b/build/pkgs/notebook/patches/mathjax.patch deleted file mode 100644 index 1237afef049..00000000000 --- a/build/pkgs/notebook/patches/mathjax.patch +++ /dev/null @@ -1,73 +0,0 @@ -commit 17b364389182e667d64db93db2dcecc84dada8f9 -Author: Jeroen Demeyer -Date: Mon Oct 26 18:29:42 2015 +0100 - - Interpret mathjax_url relative to base_url - -diff --git a/notebook/base/handlers.py b/notebook/base/handlers.py -index 30b1223..127a6ba 100644 ---- a/notebook/base/handlers.py -+++ b/notebook/base/handlers.py -@@ -32,7 +32,7 @@ from ipython_genutils.path import filefind - from ipython_genutils.py3compat import string_types - - import notebook --from notebook.utils import is_hidden, url_path_join, url_escape -+from notebook.utils import is_hidden, url_path_join, url_is_absolute, url_escape - from notebook.services.security import csp_report_uri - - #----------------------------------------------------------------------------- -@@ -155,7 +155,10 @@ class IPythonHandler(AuthenticatedHandler): - - @property - def mathjax_url(self): -- return self.settings.get('mathjax_url', '') -+ url = self.settings.get('mathjax_url', '') -+ if not url or url_is_absolute(url): -+ return url -+ return url_path_join(self.base_url, url) - - @property - def base_url(self): -diff --git a/notebook/notebookapp.py b/notebook/notebookapp.py -index bfbe467..c74e4eb 100644 ---- a/notebook/notebookapp.py -+++ b/notebook/notebookapp.py -@@ -658,9 +658,7 @@ class NotebookApp(JupyterApp): - def _mathjax_url_default(self): - if not self.enable_mathjax: - return u'' -- static_url_prefix = self.tornado_settings.get("static_url_prefix", -- url_path_join(self.base_url, "static") -- ) -+ static_url_prefix = self.tornado_settings.get("static_url_prefix", "static") - return url_path_join(static_url_prefix, 'components', 'MathJax', 'MathJax.js') - - def _mathjax_url_changed(self, name, old, new): -diff --git a/notebook/utils.py b/notebook/utils.py -index 9ba424c..ee9c6ac 100644 ---- a/notebook/utils.py -+++ b/notebook/utils.py -@@ -13,9 +13,10 @@ import sys - from distutils.version import LooseVersion - - try: -- from urllib.parse import quote, unquote -+ from urllib.parse import quote, unquote, urlparse - except ImportError: - from urllib import quote, unquote -+ from urlparse import urlparse - - from ipython_genutils import py3compat - -@@ -39,6 +40,10 @@ def url_path_join(*pieces): - if result == '//': result = '/' - return result - -+def url_is_absolute(url): -+ """Determine whether a given URL is absolute""" -+ return urlparse(url).path.startswith("/") -+ - def path2url(path): - """Convert a local file path to a URL""" - pieces = [ quote(p) for p in path.split(os.sep) ] diff --git a/build/pkgs/ntl/package-version.txt b/build/pkgs/ntl/package-version.txt index 1d9dfd00d48..9641c01e7a1 100644 --- a/build/pkgs/ntl/package-version.txt +++ b/build/pkgs/ntl/package-version.txt @@ -1 +1 @@ -9.6.2.p0 +9.6.2.p1 diff --git a/build/pkgs/ntl/spkg-install b/build/pkgs/ntl/spkg-install index a9dee4679a8..cbb6ba126a4 100755 --- a/build/pkgs/ntl/spkg-install +++ b/build/pkgs/ntl/spkg-install @@ -86,8 +86,7 @@ ntl_configure() CXX="$CXX" CXXFLAGS="$CXXFLAGS $SHAREDFLAGS" \ LDFLAGS="$LDFLAGS" LIBTOOL_LINK_FLAGS="$LIBTOOL_LINK_FLAGS" \ NTL_GMP_LIP=on NTL_GF2X_LIB=on \ - LIBTOOL="$CUR/src/libtool/libtool"\ - NTL_LEGACY_SP_MULMOD=on + LIBTOOL="$CUR/src/libtool/libtool" if [ $? -ne 0 ]; then echo >&2 "Error configuring NTL." diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 4f7ed88365b..8afe164d6cd 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,4 @@ tarball=numpy-VERSION.tar.gz -sha1=589dac943b51edaffc7d09c80eab92d5f52ab3cd -md5=3fed2b50906bc19018cec5fa26168aa5 -cksum=3353851441 +sha1=46a653d3a3e474bf284cbf213c2ce5fa85c39371 +md5=aed294de0aa1ac7bd3f9745f4f1968ad +cksum=4106390035 diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 4dae2985b58..18b31142065 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.10.1 +1.10.4 diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-backport-1.patch b/build/pkgs/numpy/patches/numpy-1.10.1-backport-1.patch deleted file mode 100644 index 77a3c010371..00000000000 --- a/build/pkgs/numpy/patches/numpy-1.10.1-backport-1.patch +++ /dev/null @@ -1,127 +0,0 @@ -From 3a816a4db9b498eb64eb837fdcca0fa8ddbe063e Mon Sep 17 00:00:00 2001 -From: Allan Haldane -Date: Sat, 17 Oct 2015 14:00:36 -0400 -Subject: [PATCH] BUG: recarrays viewed as subarrays don't convert to np.record - type - -Record array views were updated in #5943 to return np.record dtype -where possible, but forgot about the case of sub-arrays. - -That's fixed here, so accessing subarray fields by attribute or index -works sensibly, as well as viewing a record array as a subarray dtype, -and printing subarrays. - -This also happens to fix #6459, since it affects the same lines. - -Fixes #6497 #6459 ---- - numpy/core/records.py | 30 +++++++++++++++++++----------- - numpy/core/tests/test_records.py | 23 +++++++++++++++++++++++ - 2 files changed, 42 insertions(+), 11 deletions(-) - -diff --git a/numpy/core/records.py b/numpy/core/records.py -index 4a99553..4ce3fe9 100644 ---- a/numpy/core/records.py -+++ b/numpy/core/records.py -@@ -448,12 +448,14 @@ def __getattribute__(self, attr): - - # At this point obj will always be a recarray, since (see - # PyArray_GetField) the type of obj is inherited. Next, if obj.dtype is -- # non-structured, convert it to an ndarray. If obj is structured leave -- # it as a recarray, but make sure to convert to the same dtype.type (eg -- # to preserve numpy.record type if present), since nested structured -- # fields do not inherit type. -+ # non-structured, convert it to an ndarray. Then if obj is structured -+ # with void type convert it to the same dtype.type (eg to preserve -+ # numpy.record type if present), since nested structured fields do not -+ # inherit type. Don't do this for non-void structures though. - if obj.dtype.fields: -- return obj.view(dtype=(self.dtype.type, obj.dtype.fields)) -+ if issubclass(obj.dtype.type, nt.void): -+ return obj.view(dtype=(self.dtype.type, obj.dtype)) -+ return obj - else: - return obj.view(ndarray) - -@@ -463,8 +465,9 @@ def __getattribute__(self, attr): - # Thus, you can't create attributes on-the-fly that are field names. - def __setattr__(self, attr, val): - -- # Automatically convert (void) dtypes to records. -- if attr == 'dtype' and issubclass(val.type, nt.void): -+ # Automatically convert (void) structured types to records -+ # (but not non-void structures, subarrays, or non-structured voids) -+ if attr == 'dtype' and issubclass(val.type, nt.void) and val.fields: - val = sb.dtype((record, val)) - - newattr = attr not in self.__dict__ -@@ -499,7 +502,9 @@ def __getitem__(self, indx): - # we might also be returning a single element - if isinstance(obj, ndarray): - if obj.dtype.fields: -- return obj.view(dtype=(self.dtype.type, obj.dtype.fields)) -+ if issubclass(obj.dtype.type, nt.void): -+ return obj.view(dtype=(self.dtype.type, obj.dtype)) -+ return obj - else: - return obj.view(type=ndarray) - else: -@@ -519,11 +524,14 @@ def __repr__(self): - # If this is a full record array (has numpy.record dtype), - # or if it has a scalar (non-void) dtype with no records, - # represent it using the rec.array function. Since rec.array -- # converts dtype to a numpy.record for us, use only dtype.descr, -- # not repr(dtype). -+ # converts dtype to a numpy.record for us, convert back -+ # to non-record before printing -+ plain_dtype = self.dtype -+ if plain_dtype.type is record: -+ plain_dtype = sb.dtype((nt.void, plain_dtype)) - lf = '\n'+' '*len("rec.array(") - return ('rec.array(%s, %sdtype=%s)' % -- (lst, lf, repr(self.dtype.descr))) -+ (lst, lf, plain_dtype)) - else: - # otherwise represent it using np.array plus a view - # This should only happen if the user is playing -diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py -index 7a18f29..290bc4f 100644 ---- a/numpy/core/tests/test_records.py -+++ b/numpy/core/tests/test_records.py -@@ -121,6 +121,23 @@ def test_recarray_views(self): - assert_equal(type(rv), np.recarray) - assert_equal(rv.dtype.type, np.record) - -+ # check that accessing nested structures keep record type, but -+ # not for subarrays, non-void structures, non-structured voids -+ test_dtype = [('a', 'f4,f4'), ('b', 'V8'), ('c', ('f4',2)), -+ ('d', ('i8', 'i4,i4'))] -+ r = np.rec.array([((1,1), b'11111111', [1,1], 1), -+ ((1,1), b'11111111', [1,1], 1)], dtype=test_dtype) -+ assert_equal(r.a.dtype.type, np.record) -+ assert_equal(r.b.dtype.type, np.void) -+ assert_equal(r.c.dtype.type, np.float32) -+ assert_equal(r.d.dtype.type, np.int64) -+ # check the same, but for views -+ r = np.rec.array(np.ones(4, dtype='i4,i4')) -+ assert_equal(r.view('f4,f4').dtype.type, np.record) -+ assert_equal(r.view(('i4',2)).dtype.type, np.int32) -+ assert_equal(r.view('V8').dtype.type, np.void) -+ assert_equal(r.view(('i8', 'i4,i4')).dtype.type, np.int64) -+ - #check that we can undo the view - arrs = [np.ones(4, dtype='f4,i4'), np.ones(4, dtype='f8')] - for arr in arrs: -@@ -135,6 +152,12 @@ def test_recarray_repr(self): - a = np.array(np.ones(4, dtype='f8')) - assert_(repr(np.rec.array(a)).startswith('rec.array')) - -+ # check that the 'np.record' part of the dtype isn't shown -+ a = np.rec.array(np.ones(3, dtype='i4,i4')) -+ assert_equal(repr(a).find('numpy.record'), -1) -+ a = np.rec.array(np.ones(3, dtype='i4')) -+ assert_(repr(a).find('dtype=int32') != -1) -+ - def test_recarray_from_names(self): - ra = np.rec.array([ - (1, 'abc', 3.7000002861022949, 0), diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-backport-2.patch b/build/pkgs/numpy/patches/numpy-1.10.1-backport-2.patch deleted file mode 100644 index 9c33704f8e2..00000000000 --- a/build/pkgs/numpy/patches/numpy-1.10.1-backport-2.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 0d25dc4175e00cdaf9545e8b1b1a5b879cf67248 Mon Sep 17 00:00:00 2001 -From: Ethan Kruse -Date: Mon, 19 Oct 2015 13:29:01 -0700 -Subject: [PATCH 1/2] Potential fix for #6462 - ---- - numpy/lib/function_base.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py -index 555d083..fef69df 100644 ---- a/numpy/lib/function_base.py -+++ b/numpy/lib/function_base.py -@@ -3339,7 +3339,7 @@ def _median(a, axis=None, out=None, overwrite_input=False): - indexer[axis] = slice(index-1, index+1) - - # Check if the array contains any nan's -- if np.issubdtype(a.dtype, np.inexact): -+ if np.issubdtype(a.dtype, np.inexact) and sz > 0: - # warn and return nans like mean would - rout = mean(part[indexer], axis=axis, out=out) - part = np.rollaxis(part, axis, part.ndim) - -From 59d859fb2160950ac93267d7461ad952145c8724 Mon Sep 17 00:00:00 2001 -From: Ethan Kruse -Date: Tue, 20 Oct 2015 11:40:49 -0700 -Subject: [PATCH 2/2] Added tests for median of empty arrays - ---- - numpy/lib/tests/test_function_base.py | 30 ++++++++++++++++++++++++++++++ - 1 file changed, 30 insertions(+) - -diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py -index 4516c92..aa41c1f 100644 ---- a/numpy/lib/tests/test_function_base.py -+++ b/numpy/lib/tests/test_function_base.py -@@ -2597,6 +2597,36 @@ def test_nan_behavior(self): - assert_equal(np.median(a, (0, 2)), b) - assert_equal(len(w), 1) - -+ def test_empty(self): -+ # empty arrays -+ a = np.array([], dtype=float) -+ with warnings.catch_warnings(record=True) as w: -+ warnings.filterwarnings('always', '', RuntimeWarning) -+ assert_equal(np.median(a), np.nan) -+ assert_(w[0].category is RuntimeWarning) -+ -+ # multiple dimensions -+ a = np.array([], dtype=float, ndmin=3) -+ # no axis -+ with warnings.catch_warnings(record=True) as w: -+ warnings.filterwarnings('always', '', RuntimeWarning) -+ assert_equal(np.median(a), np.nan) -+ assert_(w[0].category is RuntimeWarning) -+ -+ # axis 0 and 1 -+ b = np.array([], dtype=float, ndmin=2) -+ with warnings.catch_warnings(record=True) as w: -+ warnings.filterwarnings('always', '', RuntimeWarning) -+ assert_equal(np.median(a, axis=0), b) -+ assert_equal(np.median(a, axis=1), b) -+ -+ # axis 2 -+ b = np.array(np.nan, dtype=float, ndmin=2) -+ with warnings.catch_warnings(record=True) as w: -+ warnings.filterwarnings('always', '', RuntimeWarning) -+ assert_equal(np.median(a, axis=2), b) -+ assert_(w[0].category is RuntimeWarning) -+ - def test_object(self): - o = np.arange(7.) - assert_(type(np.median(o.astype(object))), float) diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-revert-mingwinpy.patch b/build/pkgs/numpy/patches/numpy-1.10.1-revert-mingwinpy.patch deleted file mode 100644 index 9d3256e7a99..00000000000 --- a/build/pkgs/numpy/patches/numpy-1.10.1-revert-mingwinpy.patch +++ /dev/null @@ -1,305 +0,0 @@ -Francois Bissey: this causes build failures on arm - -From a08437e12d6b6796f4b0beda49bce9adc37db63d Mon Sep 17 00:00:00 2001 -From: Charles Harris -Date: Tue, 20 Oct 2015 08:59:26 -0600 -Subject: [PATCH] Revert "Merge pull request #5614 from - charris/cleanup-gh-5587" - -Revert mingwpy modifications to distutils. They are causing problems -for non-windows builds and it is better to wait until mingypy is -further along. - -This reverts commit 96abd32de241864ee97f30357234cbc9a96c43ae, reversing -changes made to 06af9918f6bf03b8d818ec834f9fb48db57d1489. ---- - INSTALL.txt | 32 ------------- - numpy/core/src/multiarray/multiarraymodule.c | 9 ++++ - numpy/distutils/fcompiler/gnu.py | 32 +++++-------- - numpy/distutils/mingw32ccompiler.py | 67 +++++++++------------------- - numpy/distutils/system_info.py | 18 -------- - numpy/lib/tests/test_function_base.py | 2 +- - setup.py | 1 - - 7 files changed, 42 insertions(+), 119 deletions(-) - -diff --git a/INSTALL.txt b/INSTALL.txt -index 12fb47d..6339cbb 100644 ---- a/INSTALL.txt -+++ b/INSTALL.txt -@@ -152,38 +152,6 @@ is broken). gcc 4.4 will hopefully be able to run natively. - This is the only tested way to get a numpy with a FULL blas/lapack (scipy - does not work because of C++). - --Carl Kleffner's mingw-w64 toolchain --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- --Carl Kleffner has been working on mingw-w64 / OpenBLAS support and has put --together toolchains for that option. The toolchains are available at --https://bitbucket.org/carlkl/mingw-w64-for-python/downloads. The site.cfg --should be configured like so: -- -- [openblas] -- libraries = openblaspy -- library_dirs = /lib -- include_dirs = /include -- --The libopenblaspy.dll from /bin must be copied to numpy/core --before the build. For this mingw-w64 toolchain manual creation of the python --import libs is necessary, i.e.: -- -- gendef python2.7.dll -- dlltool -D python27.dll -d python27.def -l libpython27.dll.a -- move libpython27.dll.a libs\libpython27.dll.a -- --For python-2.6 up to python 3.2 use --https://bitbucket.org/carlkl/mingw-w64-for-python/downloads/mingwpy_win32_vc90.tar.xz --or --https://bitbucket.org/carlkl/mingw-w64-for-python/downloads/mingwpy_amd64_vc90.tar.xz -- --For python-3.3 and python-3.4 use --https://bitbucket.org/carlkl/mingw-w64-for-python/downloads/mingwpy_win32_vc100.tar.xz --or --https://bitbucket.org/carlkl/mingw-w64-for-python/downloads/mingwpy_amd64_vc100.tar.xz -- -- - MS compilers - ------------ - -diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c -index 2c694f9..10c22ae 100644 ---- a/numpy/core/src/multiarray/multiarraymodule.c -+++ b/numpy/core/src/multiarray/multiarraymodule.c -@@ -4524,6 +4524,15 @@ PyMODINIT_FUNC initmultiarray(void) { - goto err; - } - -+#if defined(MS_WIN64) && defined(__GNUC__) -+ PyErr_WarnEx(PyExc_Warning, -+ "Numpy built with MINGW-W64 on Windows 64 bits is experimental, " \ -+ "and only available for \n" \ -+ "testing. You are advised not to use it for production. \n\n" \ -+ "CRASHES ARE TO BE EXPECTED - PLEASE REPORT THEM TO NUMPY DEVELOPERS", -+ 1); -+#endif -+ - /* Initialize access to the PyDateTime API */ - numpy_pydatetime_import(); - -diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py -index a7fd3a7..37be080 100644 ---- a/numpy/distutils/fcompiler/gnu.py -+++ b/numpy/distutils/fcompiler/gnu.py -@@ -20,8 +20,6 @@ - # XXX: handle cross compilation - def is_win64(): - return sys.platform == "win32" and platform.architecture()[0] == "64bit" --def is_win32(): -- return sys.platform == "win32" and platform.architecture()[0] == "32bit" - - if is_win64(): - #_EXTRAFLAGS = ["-fno-leading-underscore"] -@@ -138,7 +136,7 @@ def get_flags_linker_so(self): - - opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) - else: -- opt.append("-shared -Wl,-gc-sections -Wl,-s") -+ opt.append("-shared") - if sys.platform.startswith('sunos'): - # SunOS often has dynamically loaded symbols defined in the - # static library libg2c.a The linker doesn't like this. To -@@ -210,18 +208,9 @@ def get_flags_opt(self): - # With this compiler version building Fortran BLAS/LAPACK - # with -O3 caused failures in lib.lapack heevr,syevr tests. - opt = ['-O2'] -- elif v and v >= '4.6.0': -- if is_win32(): -- # use -mincoming-stack-boundary=2 -- # due to the change to 16 byte stack alignment since GCC 4.6 -- # but 32 bit Windows ABI defines 4 bytes stack alignment -- opt = ['-O2 -march=core2 -mtune=generic -mfpmath=sse -msse2 ' -- '-mincoming-stack-boundary=2'] -- else: -- opt = ['-O2 -march=x86-64 -DMS_WIN64 -mtune=generic -msse2'] - else: -- opt = ['-O2'] -- -+ opt = ['-O3'] -+ opt.append('-funroll-loops') - return opt - - def _c_arch_flags(self): -@@ -361,7 +350,10 @@ def get_target(self): - return "" - - def get_flags_opt(self): -- return GnuFCompiler.get_flags_opt(self) -+ if is_win64(): -+ return ['-O0'] -+ else: -+ return GnuFCompiler.get_flags_opt(self) - - def _can_target(cmd, arch): - """Return true if the architecture supports the -arch flag""" -@@ -386,13 +378,9 @@ def _can_target(cmd, arch): - from distutils import log - log.set_verbosity(2) - -- try: -- compiler = GnuFCompiler() -- compiler.customize() -- print(compiler.get_version()) -- except Exception: -- msg = get_exception() -- print(msg) -+ compiler = GnuFCompiler() -+ compiler.customize() -+ print(compiler.get_version()) - - try: - compiler = Gnu95FCompiler() -diff --git a/numpy/distutils/mingw32ccompiler.py b/numpy/distutils/mingw32ccompiler.py -index f72c3bb..d22a281 100644 ---- a/numpy/distutils/mingw32ccompiler.py -+++ b/numpy/distutils/mingw32ccompiler.py -@@ -87,30 +87,17 @@ def __init__ (self, - elif self.linker_dll == 'gcc': - self.linker = 'g++' - -- p = subprocess.Popen(['gcc', '--version'], shell=True, -- stdout=subprocess.PIPE) -- out_string = p.stdout.read() -- p.stdout.close() -- -- # Before build with MinGW-W64 generate the python import library -- # with gendef and dlltool according to the MingW-W64 FAQ. -- # Use the MinGW-W64 provided msvc runtime import libraries. -- # Don't call build_import_library() and build_msvcr_library. -- -- if 'MinGW-W64' not in str(out_string): -- -- # **changes: eric jones 4/11/01 -- # 1. Check for import library on Windows. Build if it doesn't -- # exist. -- build_import_library() -- -- # Check for custom msvc runtime library on Windows. Build if it -- # doesn't exist. -- msvcr_success = build_msvcr_library() -- msvcr_dbg_success = build_msvcr_library(debug=True) -- if msvcr_success or msvcr_dbg_success: -- # add preprocessor statement for using customized msvcr lib -- self.define_macro('NPY_MINGW_USE_CUSTOM_MSVCR') -+ # **changes: eric jones 4/11/01 -+ # 1. Check for import library on Windows. Build if it doesn't exist. -+ -+ build_import_library() -+ -+ # Check for custom msvc runtime library on Windows. Build if it doesn't exist. -+ msvcr_success = build_msvcr_library() -+ msvcr_dbg_success = build_msvcr_library(debug=True) -+ if msvcr_success or msvcr_dbg_success: -+ # add preprocessor statement for using customized msvcr lib -+ self.define_macro('NPY_MINGW_USE_CUSTOM_MSVCR') - - # Define the MSVC version as hint for MinGW - msvcr_version = '0x%03i0' % int(msvc_runtime_library().lstrip('msvcr')) -@@ -131,12 +118,10 @@ def __init__ (self, - else: - # gcc-4 series releases do not support -mno-cygwin option - self.set_executables( -- compiler='gcc -march=x86-64 -mtune=generic -DMS_WIN64' -- ' -O2 -msse2 -Wall', -- compiler_so='gcc -march=x86-64 -mtune=generic -DMS_WIN64' -- ' -O2 -msse2 -Wall -Wstrict-prototypes', -- linker_exe='gcc', -- linker_so='gcc -shared -Wl,-gc-sections -Wl,-s') -+ compiler='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall', -+ compiler_so='gcc -g -DDEBUG -DMS_WIN64 -O0 -Wall -Wstrict-prototypes', -+ linker_exe='gcc -g', -+ linker_so='gcc -g -shared') - else: - if self.gcc_version <= "3.0.0": - self.set_executables( -@@ -154,21 +139,13 @@ def __init__ (self, - linker_exe='g++ -mno-cygwin', - linker_so='g++ -mno-cygwin -shared') - else: -- # gcc-4 series releases do not support -mno-cygwin option i686 -- # build needs '-mincoming-stack-boundary=2' due to ABI -- # incompatibility to Win32 ABI -- self.set_executables( -- compiler='gcc -O2 -march=core2 -mtune=generic' -- ' -mfpmath=sse -msse2' -- ' -mincoming-stack-boundary=2 -Wall', -- compiler_so='gcc -O2 -march=core2 -mtune=generic' -- ' -mfpmath=sse -msse2' -- ' -mincoming-stack-boundary=2 -Wall' -- ' -Wstrict-prototypes', -- linker_exe='g++ ', -- linker_so='g++ -shared -Wl,-gc-sections -Wl,-s') -- # added for python2.3 support we can't pass it through set_executables -- # because pre 2.2 would fail -+ # gcc-4 series releases do not support -mno-cygwin option -+ self.set_executables(compiler='gcc -O2 -Wall', -+ compiler_so='gcc -O2 -Wall -Wstrict-prototypes', -+ linker_exe='g++ ', -+ linker_so='g++ -shared') -+ # added for python2.3 support -+ # we can't pass it through set_executables because pre 2.2 would fail - self.compiler_cxx = ['g++'] - - # Maybe we should also append -mthreads, but then the finished dlls -diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py -index 9dd48e2..0da13a7 100644 ---- a/numpy/distutils/system_info.py -+++ b/numpy/distutils/system_info.py -@@ -1751,24 +1751,6 @@ def check_embedded_lapack(self, info): - res = False - finally: - shutil.rmtree(tmpdir) -- if sys.platform == 'win32' and not res: -- c = distutils.ccompiler.new_compiler(compiler='mingw32') -- tmpdir = tempfile.mkdtemp() -- src = os.path.join(tmpdir, 'source.c') -- out = os.path.join(tmpdir, 'a.out') -- try: -- with open(src, 'wt') as f: -- f.write(s) -- obj = c.compile([src], output_dir=tmpdir) -- try: -- c.link_executable(obj, out, libraries=info['libraries'], -- library_dirs=info['library_dirs'], -- extra_postargs=extra_args) -- res = True -- except distutils.ccompiler.LinkError: -- res = False -- finally: -- shutil.rmtree(tmpdir) - return res - - -diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py -index 4516c92..3a4ffd7 100644 ---- a/numpy/lib/tests/test_function_base.py -+++ b/numpy/lib/tests/test_function_base.py -@@ -732,7 +732,7 @@ def test_ufunc(self): - args = np.array([0, 0.5 * np.pi, np.pi, 1.5 * np.pi, 2 * np.pi]) - r1 = f(args) - r2 = np.cos(args) -- assert_array_almost_equal(r1, r2) -+ assert_array_equal(r1, r2) - - def test_keywords(self): - -diff --git a/setup.py b/setup.py -index 8e5c3d0..8f4a989 100755 ---- a/setup.py -+++ b/setup.py -@@ -221,7 +221,6 @@ def setup_package(): - platforms = ["Windows", "Linux", "Solaris", "Mac OS-X", "Unix"], - test_suite='nose.collector', - cmdclass={"sdist": sdist_checked}, -- package_data={'numpy.core': ['libopenblaspy.dll']}, - ) - - # Run build diff --git a/build/pkgs/openssl/checksums.ini b/build/pkgs/openssl/checksums.ini index e362cededa2..3b551ef00fb 100644 --- a/build/pkgs/openssl/checksums.ini +++ b/build/pkgs/openssl/checksums.ini @@ -1,4 +1,4 @@ tarball=openssl-VERSION.tar.gz -sha1=2c5691496761cb18f98476eefa4d35c835448fb6 -md5=5262bfa25b60ed9de9f28d5d52d77fc5 -cksum=1572674046 +sha1=2047c592a6e5a42bd37970bdb4a931428110a927 +md5=b3bf73f507172be9292ea2a8c28b659d +cksum=3086440701 diff --git a/build/pkgs/openssl/package-version.txt b/build/pkgs/openssl/package-version.txt index f95b908b4dd..fa7f8563aa2 100644 --- a/build/pkgs/openssl/package-version.txt +++ b/build/pkgs/openssl/package-version.txt @@ -1 +1 @@ -1.0.2e +1.0.2f diff --git a/build/pkgs/pari/checksums.ini b/build/pkgs/pari/checksums.ini index 285ed40fe54..39db207d6d2 100644 --- a/build/pkgs/pari/checksums.ini +++ b/build/pkgs/pari/checksums.ini @@ -1,4 +1,4 @@ tarball=pari-VERSION.tar.gz -sha1=57e936ce58dcd6959fd4f10530f5ebcb096f6f8b -md5=ca27af679818f4cfef7e8eef9edc83cb -cksum=738345973 +sha1=0f8221f5052610c1e6826852ab29ecb1e1cddeec +md5=03b83e4af898f456cae16c9ade1e1cb5 +cksum=3725243671 diff --git a/build/pkgs/pari/package-version.txt b/build/pkgs/pari/package-version.txt index 04d5fd34a94..bd07947c430 100644 --- a/build/pkgs/pari/package-version.txt +++ b/build/pkgs/pari/package-version.txt @@ -1 +1 @@ -2.8-2044-g89b0f1e.p0 +2.8-2341-g61b65cc.p0 diff --git a/build/pkgs/pari/patches/README.txt b/build/pkgs/pari/patches/README.txt index a7444b27d1b..01f52e99355 100644 --- a/build/pkgs/pari/patches/README.txt +++ b/build/pkgs/pari/patches/README.txt @@ -12,5 +12,5 @@ Patches to configuration files: the flag unconditionally. C files: -* public_memory_functions.patch (Jeroen Demeyer, #16997): Make some of - PARI's private memory functions public to improve interface with Sage. +* stackwarn.patch (Jeroen Demeyer, #19883): do not display warnings + regarding the stack size (unless DEBUGMEM is set). diff --git a/build/pkgs/pari/patches/public_memory_functions.patch b/build/pkgs/pari/patches/public_memory_functions.patch deleted file mode 100644 index b3726d7dfdf..00000000000 --- a/build/pkgs/pari/patches/public_memory_functions.patch +++ /dev/null @@ -1,61 +0,0 @@ -diff --git a/src/headers/paridecl.h b/src/headers/paridecl.h -index 7067183..4ede6ed 100644 ---- a/src/headers/paridecl.h -+++ b/src/headers/paridecl.h -@@ -2819,6 +2819,9 @@ GEN pari_thread_start(struct pari_thread *t); - void pari_thread_valloc(struct pari_thread *t, size_t s, size_t v, GEN arg); - GEN pari_version(void); - void pari_warn(int numerr, ...); -+void * pari_mainstack_malloc(size_t size); -+void pari_mainstack_mfree(void *s, size_t size); -+void pari_mainstack_free(struct pari_mainstack *st); - void paristack_alloc(size_t rsize, size_t vsize); - void paristack_newrsize(ulong newsize); - void paristack_resize(ulong newsize); -diff --git a/src/language/init.c b/src/language/init.c -index 7b5922d..2a578d7 100644 ---- a/src/language/init.c -+++ b/src/language/init.c -@@ -612,7 +612,7 @@ pari_add_oldmodule(entree *ep) - #ifndef MAP_NORESERVE - #define MAP_NORESERVE 0 - #endif --static void * -+void * - pari_mainstack_malloc(size_t size) - { - void *b = mmap(NULL, size, PROT_READ|PROT_WRITE, -@@ -620,7 +620,7 @@ pari_mainstack_malloc(size_t size) - return (b == MAP_FAILED) ? NULL: b; - } - --static void -+void - pari_mainstack_mfree(void *s, size_t size) - { - munmap(s, size); -@@ -634,13 +634,13 @@ pari_mainstack_mreset(void *s, size_t size) - - #else - #define PARI_STACK_ALIGN (0x40UL) --static void * -+void * - pari_mainstack_malloc(size_t s) - { - return malloc(s); /* NOT pari_malloc, e_MEM would be deadly */ - } - --static void -+void - pari_mainstack_mfree(void *s, size_t size) { (void) size; free(s); } - - static void -@@ -681,7 +681,7 @@ pari_mainstack_alloc(struct pari_mainstack *st, size_t rsize, size_t vsize) - st->memused = 0; - } - --static void -+void - pari_mainstack_free(struct pari_mainstack *st) - { - pari_mainstack_mfree((void*)st->vbot, st->vsize ? st->vsize : fix_size(st->rsize)); diff --git a/build/pkgs/pari/patches/stackwarn.patch b/build/pkgs/pari/patches/stackwarn.patch new file mode 100644 index 00000000000..c0a18548746 --- /dev/null +++ b/build/pkgs/pari/patches/stackwarn.patch @@ -0,0 +1,53 @@ +commit 7cf2260b69d3711f9292a3abab4d3a35d0c74059 +Author: Jeroen Demeyer +Date: Thu Jan 14 10:13:39 2016 +0100 + + Use DEBUGMEM for stack size warnings + +diff --git a/src/language/init.c b/src/language/init.c +index fa5c167..5cd1a13 100644 +--- a/src/language/init.c ++++ b/src/language/init.c +@@ -722,7 +722,8 @@ parivstack_resize(ulong newsize) + evalstate_reset(); + paristack_setsize(pari_mainstack->rsize, newsize); + s = pari_mainstack->vsize ? pari_mainstack->vsize : pari_mainstack->rsize; +- pari_warn(warner,"new maximum stack size = %lu (%.3f Mbytes)", s, s/1048576.); ++ if (DEBUGMEM > 0) ++ pari_warn(warner,"new maximum stack size = %lu (%.3f Mbytes)", s, s/1048576.); + pari_init_errcatch(); + cb_pari_err_recover(-1); + } +@@ -736,7 +737,8 @@ paristack_newrsize(ulong newsize) + pari_mainstack_resize(pari_mainstack, newsize, vsize); + evalstate_reset(); + s = pari_mainstack->rsize; +- pari_warn(warner,"new stack size = %lu (%.3f Mbytes)", s, s/1048576.); ++ if (DEBUGMEM > 0) ++ pari_warn(warner,"new stack size = %lu (%.3f Mbytes)", s, s/1048576.); + pari_init_errcatch(); + cb_pari_err_recover(-1); + } +@@ -750,7 +752,8 @@ paristack_resize(ulong newsize) + newsize = maxuu(minuu(newsize, vsize), pari_mainstack->size); + pari_mainstack->size = newsize; + pari_mainstack->bot = pari_mainstack->top - pari_mainstack->size; +- pari_warn(warner,"increasing stack size to %lu",newsize); ++ if (DEBUGMEM > 0) ++ pari_warn(warner,"increasing stack size to %lu",newsize); + } + + void +diff --git a/src/test/dotest b/src/test/dotest +index 3cb790d..6e4cc70 100755 +--- a/src/test/dotest ++++ b/src/test/dotest +@@ -86,7 +86,7 @@ for testdata in $testlist; do + for suf in $SUF; do + file_diff=$testname-$suf.dif + gp=$execdir/gp-$suf +- (echo 'gettime();0;'; cat $file_in; \ ++ (echo 'gettime();default(debugmem,1);'; cat $file_in; \ + echo 'print("Total time spent: ",gettime);') \ + | $RUNTEST $gp -q --test > $file_test 2>&1 + if test -n "$crlf"; then diff --git a/build/pkgs/pari_jupyter/checksums.ini b/build/pkgs/pari_jupyter/checksums.ini index d3b36f6aebb..62c78967973 100644 --- a/build/pkgs/pari_jupyter/checksums.ini +++ b/build/pkgs/pari_jupyter/checksums.ini @@ -1,4 +1,4 @@ tarball=pari_jupyter-VERSION.tar.gz -sha1=404df06171e68056d9efe8a29804204138488885 -md5=902b290a997128e6be949c0bec44ca6e -cksum=3922118226 +sha1=234ddaa82b9229a83083f0ba4171acb801cc3a8d +md5=80ec021034affd77cebba79eb4bc4f83 +cksum=99491927 diff --git a/build/pkgs/pari_jupyter/package-version.txt b/build/pkgs/pari_jupyter/package-version.txt index 3eefcb9dd5b..9084fa2f716 100644 --- a/build/pkgs/pari_jupyter/package-version.txt +++ b/build/pkgs/pari_jupyter/package-version.txt @@ -1 +1 @@ -1.0.0 +1.1.0 diff --git a/build/pkgs/pickleshare/checksums.ini b/build/pkgs/pickleshare/checksums.ini index 90c7efe70ec..a385506705a 100644 --- a/build/pkgs/pickleshare/checksums.ini +++ b/build/pkgs/pickleshare/checksums.ini @@ -1,4 +1,4 @@ tarball=pickleshare-VERSION.tar.gz -sha1=fba6e858ca5c83caca25509cb5b8bd41c49d233a -md5=25337740507cb855ad58bfcf60f7710e -cksum=1340562053 +sha1=3f9176870b015f5c84ab4e7afb42470f78a0ed34 +md5=7fadddce8b1b0110c4ef905be795001a +cksum=2251024212 diff --git a/build/pkgs/pickleshare/package-version.txt b/build/pkgs/pickleshare/package-version.txt index 2eb3c4fe4ee..5a2a5806df6 100644 --- a/build/pkgs/pickleshare/package-version.txt +++ b/build/pkgs/pickleshare/package-version.txt @@ -1 +1 @@ -0.5 +0.6 diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index d68ab300d10..6cb77b8fa0f 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,4 @@ tarball=Pillow-VERSION.tar.gz -sha1=053337a612dd16ec6f1f6fc544374ca5fe65ae2c -md5=4c04262dc88ee38e429ed8fdcd98c8d5 -cksum=3491044754 +sha1=71d8dc1dd38ba2582f7cca8b5ce70af03d19db23 +md5=d382a86c4b9b1c8de684bd00dad43bb8 +cksum=4161496668 diff --git a/build/pkgs/pillow/package-version.txt b/build/pkgs/pillow/package-version.txt index b1b25a5ffae..94ff29cc4de 100644 --- a/build/pkgs/pillow/package-version.txt +++ b/build/pkgs/pillow/package-version.txt @@ -1 +1 @@ -2.2.2 +3.1.1 diff --git a/build/pkgs/pillow/patches/fix_include_path_ordering.patch b/build/pkgs/pillow/patches/fix_include_path_ordering.patch deleted file mode 100644 index ecb2380f70e..00000000000 --- a/build/pkgs/pillow/patches/fix_include_path_ordering.patch +++ /dev/null @@ -1,85 +0,0 @@ -diff --git a/setup.py b/setup.py -index 5885703..2724c73 100644 ---- a/setup.py -+++ b/setup.py -@@ -37,7 +37,10 @@ _LIB_IMAGING = ( - - - def _add_directory(path, dir, where=None): -- if dir and os.path.isdir(dir) and dir not in path: -+ if dir is None: -+ return -+ dir = os.path.realpath(dir) -+ if os.path.isdir(dir) and dir not in path: - if where is None: - path.append(dir) - else: -@@ -156,6 +159,30 @@ class pil_build_ext(build_ext): - _add_directory(library_dirs, lib_root) - _add_directory(include_dirs, include_root) - -+ # respect CFLAGS/LDFLAGS -+ for k in ('CFLAGS', 'LDFLAGS'): -+ if k in os.environ: -+ for match in re.finditer(r'-I([^\s]+)', os.environ[k]): -+ _add_directory(include_dirs, match.group(1)) -+ for match in re.finditer(r'-L([^\s]+)', os.environ[k]): -+ _add_directory(library_dirs, match.group(1)) -+ -+ # include, rpath, if set as environment variables: -+ for k in ('C_INCLUDE_PATH', 'CPATH', 'INCLUDE'): -+ if k in os.environ: -+ for d in os.environ[k].split(os.path.pathsep): -+ _add_directory(include_dirs, d) -+ -+ for k in ('LD_RUN_PATH', 'LIBRARY_PATH', 'LIB'): -+ if k in os.environ: -+ for d in os.environ[k].split(os.path.pathsep): -+ _add_directory(library_dirs, d) -+ -+ prefix = sysconfig.get_config_var("prefix") -+ if prefix: -+ _add_directory(library_dirs, os.path.join(prefix, "lib")) -+ _add_directory(include_dirs, os.path.join(prefix, "include")) -+ - # - # add platform directories - -@@ -187,7 +214,7 @@ class pil_build_ext(build_ext): - _add_directory(include_dirs, os.path.join(prefix, 'include')) - except: - pass # homebrew not installed -- -+ - elif sys.platform.startswith("linux"): - for platform_ in (plat.processor(), plat.architecture()[0]): - -@@ -211,24 +238,11 @@ class pil_build_ext(build_ext): - # work ;-) - self.add_multiarch_paths() - -- _add_directory(library_dirs, "/usr/local/lib") -- # FIXME: check /opt/stuff directories here? -- -- # include, rpath, if set as environment variables: -- for k in 'C_INCLUDE_PATH INCLUDE'.split(): -- if k in os.environ: -- for d in os.environ[k].split(os.path.pathsep): -- _add_directory(include_dirs, d) -- -- for k in 'LD_RUN_PATH LIBRARY_PATH LIB'.split(): -- if k in os.environ: -- for d in os.environ[k].split(os.path.pathsep): -- _add_directory(library_dirs, d) -+ elif sys.platform.startswith("netbsd"): -+ _add_directory(library_dirs, "/usr/pkg/lib") -+ _add_directory(include_dirs, "/usr/pkg/include") - -- prefix = sysconfig.get_config_var("prefix") -- if prefix: -- _add_directory(library_dirs, os.path.join(prefix, "lib")) -- _add_directory(include_dirs, os.path.join(prefix, "include")) -+ # FIXME: check /opt/stuff directories here? - - # - # locate tkinter libraries diff --git a/build/pkgs/pillow/spkg-install b/build/pkgs/pillow/spkg-install index 8f3fcd0ef8d..1d935898d9b 100755 --- a/build/pkgs/pillow/spkg-install +++ b/build/pkgs/pillow/spkg-install @@ -10,6 +10,7 @@ cd src # PATCH for patch in ../patches/*.patch; do + [ -f "$patch" ] || continue patch -p1 <"$patch" if [ $? -ne 0 ]; then echo >&2 "Error: Patch \"$patch\" failed to apply." @@ -22,7 +23,10 @@ site_packages="$SAGE_LOCAL/lib/python/site-packages" rm -rf "$site_packages"/PIL "$site_packages"/PIL-*.egg* "$site_packages"/Pillow-*.egg* # Note: Avoid shared libraries inside egg files, Trac #19467 -python setup.py install \ +python setup.py \ + build_ext \ + --disable-jpeg \ + install \ --single-version-externally-managed \ --record /dev/null if [ $? -ne 0 ]; then diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index 68b7d29c90f..076d0a444f7 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,4 +1,4 @@ tarball=pip-VERSION.tar.gz -sha1=f649528990ba4c6b5a3f4811c63ced45405ec4d4 -md5=6b19e0a934d982a5a4b798e957cb6d45 -cksum=1921827206 +sha1=974a8c345d272b9d9072287f399aab8410067f7e +md5=3a73c4188f8dbad6a1e6f6d44d117eeb +cksum=1325085465 diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index f3b5af39e43..8b22a322d0f 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -6.1.1 +8.0.2 diff --git a/build/pkgs/ptyprocess/checksums.ini b/build/pkgs/ptyprocess/checksums.ini index 47df115e7f0..fad05afe5e3 100644 --- a/build/pkgs/ptyprocess/checksums.ini +++ b/build/pkgs/ptyprocess/checksums.ini @@ -1,4 +1,4 @@ tarball=ptyprocess-VERSION.tar.gz -sha1=bf0c6664249a738c2b2a7251f23a7b14c5088150 -md5=0d1ecfba622cb4e35ee157c38de18eae -cksum=1479514456 +sha1=3290062d67ef8a2f136bff9c2cd106673ff21316 +md5=94e537122914cc9ec9c1eadcd36e73a1 +cksum=1748633415 diff --git a/build/pkgs/ptyprocess/package-version.txt b/build/pkgs/ptyprocess/package-version.txt index 2eb3c4fe4ee..4b9fcbec101 100644 --- a/build/pkgs/ptyprocess/package-version.txt +++ b/build/pkgs/ptyprocess/package-version.txt @@ -1 +1 @@ -0.5 +0.5.1 diff --git a/build/pkgs/pygments/checksums.ini b/build/pkgs/pygments/checksums.ini index a39165a205a..1fa47d18407 100644 --- a/build/pkgs/pygments/checksums.ini +++ b/build/pkgs/pygments/checksums.ini @@ -1,4 +1,4 @@ tarball=Pygments-VERSION.tar.gz -sha1=fe2c8178a039b6820a7a86b2132a2626df99c7f8 -md5=238587a1370d62405edabd0794b3ec4a -cksum=3310001812 +sha1=c2c056473623b513609a3bb6f13983524c54674b +md5=84533d22f72de894f6d3907c3ca9eddf +cksum=2408114741 diff --git a/build/pkgs/pygments/package-version.txt b/build/pkgs/pygments/package-version.txt index 07ea703c9ad..da9492f78e0 100644 --- a/build/pkgs/pygments/package-version.txt +++ b/build/pkgs/pygments/package-version.txt @@ -1 +1 @@ -2.0.2.p0 +2.1.p0 diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index 274a2a0a0a0..084dc92442b 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,4 @@ tarball=pynac-VERSION.tar.bz2 -sha1=1c955b11dd52b6eefb43d5ebf00c3a735140faec -md5=4cdac770128d41e230b06e3c82d928e0 -cksum=2890824965 +sha1=8de4355307261c4012922560b4257e07606aabea +md5=551c7a8b07d8b5dbc7bf9adb8f710362 +cksum=226559555 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index a918a2aa18d..ee6cdce3c29 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.6.0 +0.6.1 diff --git a/build/pkgs/pyparsing/checksums.ini b/build/pkgs/pyparsing/checksums.ini index b2e17a2bc00..5735a9b63a8 100644 --- a/build/pkgs/pyparsing/checksums.ini +++ b/build/pkgs/pyparsing/checksums.ini @@ -1,4 +1,4 @@ tarball=pyparsing-VERSION.tar.gz -sha1=7fa1b0a3e54e7794667210934b49f04256b6ca19 -md5=bc985f06d0bb4b2ecf80d4ad784f0542 -cksum=752880293 +sha1=2352101b6f5c3fbf96ae3fe649f7075a89ddcef6 +md5=1c8bed7530642ca19197f3caa05fd28b +cksum=2559022969 diff --git a/build/pkgs/pyparsing/dependencies b/build/pkgs/pyparsing/dependencies index edf27112135..643eeddd35b 100644 --- a/build/pkgs/pyparsing/dependencies +++ b/build/pkgs/pyparsing/dependencies @@ -1,4 +1,4 @@ -$(INST)/$(PYTHON) +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pyparsing/package-version.txt b/build/pkgs/pyparsing/package-version.txt index 38f77a65b30..f1547e6d134 100644 --- a/build/pkgs/pyparsing/package-version.txt +++ b/build/pkgs/pyparsing/package-version.txt @@ -1 +1 @@ -2.0.1 +2.0.7 diff --git a/build/pkgs/pyparsing/patches/deprecation_fix.patch b/build/pkgs/pyparsing/patches/deprecation_fix.patch deleted file mode 100644 index 934a186f75e..00000000000 --- a/build/pkgs/pyparsing/patches/deprecation_fix.patch +++ /dev/null @@ -1,86 +0,0 @@ ---- a/pyparsing.py 2013-07-17 10:08:10.000000000 +0100 -+++ b/pyparsing.py 2014-03-30 12:56:57.746032995 +0100 -@@ -2823,12 +2823,12 @@ - - Note: take care when assigning to C{Forward} not to overlook precedence of operators. - Specifically, '|' has a lower precedence than '<<', so that:: -- fwdExpr << a | b | c -+ fwdExpr <<= a | b | c - will actually be evaluated as:: -- (fwdExpr << a) | b | c -+ (fwdExpr <<= a) | b | c - thereby leaving b and c out as parseable alternatives. It is recommended that you - explicitly group the values inserted into the C{Forward}:: -- fwdExpr << (a | b | c) -+ fwdExpr <<= (a | b | c) - Converting to use the '<<=' operator instead will avoid this problem. - """ - def __init__( self, other=None ): -@@ -2892,7 +2895,7 @@ - return super(Forward,self).copy() - else: - ret = Forward() -- ret << self -+ ret <<= self - return ret - - class _ForwardNoRecurse(Forward): -@@ -3063,7 +3066,7 @@ - arrayExpr = Forward() - def countFieldParseAction(s,l,t): - n = t[0] -- arrayExpr << (n and Group(And([expr]*n)) or Group(empty)) -+ arrayExpr <<= (n and Group(And([expr]*n)) or Group(empty)) - return [] - if intExpr is None: - intExpr = Word(nums).setParseAction(lambda t:int(t[0])) -@@ -3098,13 +3101,13 @@ - def copyTokenToRepeater(s,l,t): - if t: - if len(t) == 1: -- rep << t[0] -+ rep <<= t[0] - else: - # flatten t tokens - tflat = _flatten(t.asList()) -- rep << And( [ Literal(tt) for tt in tflat ] ) -+ rep <<= And( [ Literal(tt) for tt in tflat ] ) - else: -- rep << Empty() -+ rep <<= Empty() - expr.addParseAction(copyTokenToRepeater, callDuringTry=True) - return rep - -@@ -3123,7 +3126,7 @@ - """ - rep = Forward() - e2 = expr.copy() -- rep << e2 -+ rep <<= e2 - def copyTokenToRepeater(s,l,t): - matchTokens = _flatten(t.asList()) - def mustMatchTheseTokens(s,l,t): -@@ -3485,9 +3488,9 @@ - raise ValueError("operator must indicate right or left associativity") - if pa: - matchExpr.setParseAction( pa ) -- thisExpr << ( matchExpr | lastExpr ) -+ thisExpr <<= ( matchExpr | lastExpr ) - lastExpr = thisExpr -- ret << lastExpr -+ ret <<= lastExpr - return ret - operatorPrecedence = infixNotation - -@@ -3543,9 +3546,9 @@ - raise ValueError("opening and closing arguments must be strings if no content expression is given") - ret = Forward() - if ignoreExpr is not None: -- ret << Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) -+ ret <<= Group( Suppress(opener) + ZeroOrMore( ignoreExpr | ret | content ) + Suppress(closer) ) - else: -- ret << Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) -+ ret <<= Group( Suppress(opener) + ZeroOrMore( ret | content ) + Suppress(closer) ) - return ret - - def indentedBlock(blockStatementExpr, indentStack, indent=True): diff --git a/build/pkgs/python2/spkg-install b/build/pkgs/python2/spkg-install index 875764dfb01..db35674f8fe 100755 --- a/build/pkgs/python2/spkg-install +++ b/build/pkgs/python2/spkg-install @@ -42,13 +42,26 @@ if [ "$SAGE_VALGRIND" = "yes" ]; then PYTHON_CONFIGURE="$PYTHON_CONFIGURE --without-pymalloc" fi -if [ `uname` = "Darwin" ]; then +# Use EXTRA_CFLAGS for user-defined CFLAGS since Python puts its own +# default flags like -O3 after CFLAGS but before EXTRA_CFLAGS. +# We also disable warnings about unused variables/functions which are +# common in Cython-generated code. +export EXTRA_CFLAGS="`testcflags.sh -Wno-unused` $CFLAGS" +unset CFLAGS + +if [ "$UNAME" = Darwin ]; then PYTHON_CONFIGURE="--disable-toolbox-glue $PYTHON_CONFIGURE" # Workaround for El Capitan, Trac #19626 xcode=$(xcode-select --print-path) mkdir "$CUR"/include cp -rp "${xcode}"/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-migrator/sdk/MacOSX.sdk/usr/include/openssl "$CUR"/include - export CFLAGS="$CFLAGS -I$CUR/include" + export CFLAGS="-I$CUR/include" +elif [ "$UNAME" = SunOS ]; then + # Enable some C99 features on Solaris. This in particular enables + # the isinf() and isfinite() functions. It works both for C and + # C++ code (which is not true for -std=c99). + # See http://trac.sagemath.org/sage_trac/ticket/14265 + export CFLAGS="-D__C99FEATURES__" fi if [ "$SAGE64" = yes ]; then @@ -56,14 +69,6 @@ if [ "$SAGE64" = yes ]; then export CC="$CC -m64" fi -# Enable some C99 features on Solaris. This in particular enables the -# isinf() and isfinite() functions. It works both for C and C++ code -# (which is not true for -std=c99). -# See http://trac.sagemath.org/sage_trac/ticket/14265 -if [ "$UNAME" = SunOS ]; then - export CFLAGS="-D__C99FEATURES__ $CFLAGS" -fi - build() { @@ -108,13 +113,6 @@ build cd "$SAGE_LOCAL/lib" -# If we are upgrading from Python-2.6, remove compiled Python files. -# We do not care about still older Python versions, since upgrades -# from such old Sage versions are not supported anyway. -if [ -d python2.6 ]; then - rm -rf "$SAGE_ROOT"/devel/sage-*/build -fi - # Make symbolic link (after removing old link first) rm -f python ln -s python2.7 python @@ -123,9 +121,6 @@ if [ $? -ne 0 ]; then exit 1 fi -# Remove previous Python installs -rm -rf python2.6 - # On OS X with XCode 4, the presence of # $SAGE_LOCAL/lib/python/config/libpython2.7.a causes problems with # GiNaC -- see #11967. It is easiest to test the version of OS X; we diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index 225ba064dc8..3d6d3b2e019 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,4 +1,4 @@ tarball=pyzmq-VERSION.tar.gz -sha1=85ea779755374e3f0188b5cabfc8b7143562f912 -md5=92e49cc9be92f0c95a6f790a85309df5 -cksum=1092518080 +sha1=73b307397ac9befc21b8d85dbe629b95b25e87e7 +md5=9722046c27475441d47ac17a98c665bb +cksum=2237817485 diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index 32f02f10ebe..6c43fc8aefc 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -14.3.0 +15.2.0 diff --git a/build/pkgs/rpy2/checksums.ini b/build/pkgs/rpy2/checksums.ini index ae62064b00a..a130197968a 100644 --- a/build/pkgs/rpy2/checksums.ini +++ b/build/pkgs/rpy2/checksums.ini @@ -1,4 +1,4 @@ tarball=rpy2-VERSION.tar.gz -sha1=5a814340b62ba7a9a3741cb4b31ead36f669d31e -md5=459118a2eebdaa362157ae09637b4f77 -cksum=630858783 +sha1=875989cfbf0d088e3f58e9bc780886ad38dd7697 +md5=56ca162bca76bb9c3f935f099c916196 +cksum=3965017162 diff --git a/build/pkgs/rpy2/package-version.txt b/build/pkgs/rpy2/package-version.txt index a603bb50a29..6a81b4c8379 100644 --- a/build/pkgs/rpy2/package-version.txt +++ b/build/pkgs/rpy2/package-version.txt @@ -1 +1 @@ -2.7.5 +2.7.8 diff --git a/build/pkgs/rpy2/patches/cygwin.patch b/build/pkgs/rpy2/patches/cygwin.patch index 90641d43455..d2088e419e1 100644 --- a/build/pkgs/rpy2/patches/cygwin.patch +++ b/build/pkgs/rpy2/patches/cygwin.patch @@ -58,15 +58,6 @@ diff -ru rpy2-2.7.4.orig/rpy/rinterface/_rinterface.c rpy2-2.7.4/rpy/rinterface/ void win32CallBack() { /* called during i/o, eval, graphics in ProcessEvents */ -@@ -1489,7 +1489,7 @@ - } else { - rtruefalse = FALSE; - } --#if defined(Win32) || defined(Win64) -+#if defined(Win32) || defined(Win64) || defined(__CYGWIN__) - Rp->R_Interactive = rtruefalse; - #else - R_Interactive = rtruefalse; @@ -3749,7 +3749,7 @@ } @@ -103,3 +94,15 @@ diff -ru rpy2-2.7.4.orig/rpy/rinterface/_rinterface.c rpy2-2.7.4/rpy/rinterface/ NAComplex_Type.tp_base=&PyComplex_Type; #endif if (PyType_Ready(&NAComplex_Type) < 0) { +diff -ru rpy2-2.7.4.orig/setup rpy2-2.7.4/setup.py +--- rpy2-2.7.4.orig/setup.py 2016-01-15 01:45:48.447894400 -0800 ++++ rpy2-2.7.4/setup.py 2016-01-15 01:48:55.222874000 -0800 +@@ -160,7 +160,7 @@ + extra_link_args = [] + extra_compile_args = [] + include_dirs = [] +- libraries = [] ++ libraries = ['readline'] + library_dirs = [] + + #FIXME: crude way (will break in many cases) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index cd0c4354db5..e68055e02e4 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,4 +1,4 @@ tarball=scipy-VERSION.tar.gz -sha1=562aa8c6658b5face5e7832deff3c6f2bee55426 -md5=967cdb8588a4249f820344d8264a2143 -cksum=2565278984 +sha1=d785d7e0becde9047dcf384e3119cff3a2c34667 +md5=5ff2971e1ce90e762c59d2cd84837224 +cksum=148722989 diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 2a0970ca757..c5523bd09b1 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -0.16.1 +0.17.0 diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 00d733500d0..0155fc9d7a1 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools-VERSION.tar.gz -sha1=066319dafd105cc7eb0d1c63aa2efa49d500a414 -md5=f72e87f34fbf07f299f6cb46256a0b06 -cksum=2781334732 +sha1=e5c0df99825119207d2a5a3467344d0d38f3af6b +md5=fb22b2474ca037e0b08f3c3b293e02e6 +cksum=697636194 diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 33718932a44..9a7c1e503f7 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -18.1 +20.0 diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index bee26a695cc..79cc040e560 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=c6829e48b2371ca48f7f61c10c6a82fbfd750755 -md5=d0423feeabda9c6a869d963cdc397d64 -cksum=2293631060 +sha1=c953cc4228654d151be4cd84db6391fd050eb3bf +md5=99823e2cd564b996f18820a065f0a974 +cksum=3074680780 diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index bd8bf882d06..4dae2985b58 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -1.7.0 +1.10.1 diff --git a/build/pkgs/sphinx/SPKG.txt b/build/pkgs/sphinx/SPKG.txt index d56a0fffed1..0fa724f05b9 100644 --- a/build/pkgs/sphinx/SPKG.txt +++ b/build/pkgs/sphinx/SPKG.txt @@ -69,16 +69,3 @@ Home Page: http://sphinx.pocoo.org, Grammar2.7.pickle in site-packages/Sphinx-.../sphinx/pycode/. This helps to avoid race conditions when building the documentation in parallel. - - * To do: autogenerate the file sage_autodoc.py from - src/sphinx/ext/autodoc.py. sage_autodoc.py is currently in - SAGE_ROOT/devel/sage/doc/common and is tracked in the Sage library - repository, and as a result, it is now somewhat out-of-synch with - autodoc.py. Instead we could do the following: in spkg-install, - - cp sphinx/ext/autodoc.py sphinx/ext/sage_autodoc.py - - Then patch sage_autodoc.py in place. This would require modifying - devel/sage/doc/common/conf.py: 'extensions' would use - 'sphinx.ext.sage_autodoc' instead of 'sage_autodoc'. - diff --git a/build/pkgs/tornado/checksums.ini b/build/pkgs/tornado/checksums.ini index fa670cb8723..1a5d79f2db8 100644 --- a/build/pkgs/tornado/checksums.ini +++ b/build/pkgs/tornado/checksums.ini @@ -1,4 +1,4 @@ tarball=tornado-VERSION.tar.gz -sha1=c6bd9ccd6d5449c36206ee83a02e8fc854158bf8 -md5=a06ea343375f2247344257ef691641f9 -cksum=4109011802 +sha1=42e5184bddf53c32ee7bb406e3d630e403a508c4 +md5=d13a99dc0b60ba69f5f8ec1235e5b232 +cksum=1526132571 diff --git a/build/pkgs/tornado/dependencies b/build/pkgs/tornado/dependencies index 3e87ffe857c..37cb25f7537 100644 --- a/build/pkgs/tornado/dependencies +++ b/build/pkgs/tornado/dependencies @@ -1,4 +1,4 @@ -$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(BACKPORTS_SSL_MATCH_HOSTNAME) $(INST)/$(CERTIFI) +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) $(INST)/$(BACKPORTS_ABC) $(INST)/$(BACKPORTS_SSL_MATCH_HOSTNAME) $(INST)/$(CERTIFI) $(INST)/$(SINGLEDISPATCH) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/tornado/package-version.txt b/build/pkgs/tornado/package-version.txt index 7d5c902e777..69df05f33b7 100644 --- a/build/pkgs/tornado/package-version.txt +++ b/build/pkgs/tornado/package-version.txt @@ -1 +1 @@ -4.1 +4.3 diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index 7c6c5f431ee..7746f78a1e7 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,4 +1,4 @@ tarball=traitlets-VERSION.tar.gz -sha1=f57049393ee8c514299e3873fc7ec9228778e929 -md5=27df56a921848686cf52766177a434f2 -cksum=3551254293 +sha1=06118412dd55f4996d2504e449430d6730a06abc +md5=2ebf5e11a19f82f25395b4a793097080 +cksum=362227070 diff --git a/build/pkgs/traitlets/package-version.txt b/build/pkgs/traitlets/package-version.txt index fcdb2e109f6..ee74734aa22 100644 --- a/build/pkgs/traitlets/package-version.txt +++ b/build/pkgs/traitlets/package-version.txt @@ -1 +1 @@ -4.0.0 +4.1.0 diff --git a/build/pkgs/vcversioner/SPKG.txt b/build/pkgs/vcversioner/SPKG.txt new file mode 100644 index 00000000000..eae91aa062e --- /dev/null +++ b/build/pkgs/vcversioner/SPKG.txt @@ -0,0 +1,20 @@ += vcversioner = + +== Description == + +Write a setup.py with no version information specified, and +vcversioner will find a recent, properly-formatted VCS tag and extract +a version from it. + +== License == + +Python Software Foundation License + +== Upstream Contact == + +Home page: https://pypi.python.org/pypi/vcversioner/ + +== Dependencies == + +Python, Setuptools + diff --git a/build/pkgs/vcversioner/checksums.ini b/build/pkgs/vcversioner/checksums.ini new file mode 100644 index 00000000000..b6fc1fbc8ca --- /dev/null +++ b/build/pkgs/vcversioner/checksums.ini @@ -0,0 +1,4 @@ +tarball=vcversioner-VERSION.tar.gz +sha1=723c0915665aa1f01831731605acdd6afe0af9b6 +md5=7848a365ced9941053bc25d9a9f8f4b4 +cksum=343721373 diff --git a/build/pkgs/vcversioner/dependencies b/build/pkgs/vcversioner/dependencies new file mode 100644 index 00000000000..643eeddd35b --- /dev/null +++ b/build/pkgs/vcversioner/dependencies @@ -0,0 +1,5 @@ +$(INST)/$(PYTHON) $(INST)/$(SETUPTOOLS) + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/vcversioner/package-version.txt b/build/pkgs/vcversioner/package-version.txt new file mode 100644 index 00000000000..a82ac72a25f --- /dev/null +++ b/build/pkgs/vcversioner/package-version.txt @@ -0,0 +1 @@ +2.14.0.0 diff --git a/build/pkgs/vcversioner/spkg-install b/build/pkgs/vcversioner/spkg-install new file mode 100755 index 00000000000..b2ad7e3c051 --- /dev/null +++ b/build/pkgs/vcversioner/spkg-install @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined ... exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +cd src + +python setup.py install +if [ $? -ne 0 ]; then + echo "Error installing vcversioner ... exiting" + exit 1 +fi diff --git a/build/pkgs/vcversioner/type b/build/pkgs/vcversioner/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/vcversioner/type @@ -0,0 +1 @@ +standard diff --git a/src/bin/sage b/src/bin/sage index 57f4825efe9..dc2cd8f36bc 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -927,7 +927,7 @@ fi if [ "$1" = "-docbuild" -o "$1" = "--docbuild" ]; then shift - exec python "$SAGE_DOC/common/builder.py" "$@" + exec python -m "sage_setup.docbuild" "$@" fi if [ "$1" = '-gdb' -o "$1" = "--gdb" ]; then diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 1d71aa46db5..31e25f9495d 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,8 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 7.0, Release Date: 2016-01-19 │ +│ SageMath Version 7.1.beta3, Release Date: 2016-02-11 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ +┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ +┃ Warning: this is a prerelease version, and it may be unstable. ┃ +┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 0853b4fc51d..205c8ed5a47 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -1,4 +1,4 @@ # Sage version information for shell scripts # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='7.0' -SAGE_RELEASE_DATE='2016-01-19' +SAGE_VERSION='7.1.beta3' +SAGE_RELEASE_DATE='2016-02-11' diff --git a/src/doc/Makefile b/src/doc/Makefile index e3947aff837..5160e35cc71 100644 --- a/src/doc/Makefile +++ b/src/doc/Makefile @@ -2,10 +2,8 @@ all: @echo "Please build the doc using either 'make doc' from SAGE_ROOT, or" @echo "'sage -docbuild all html'. See 'sage -docbuild help' for more informations." clean: - @echo "Deleting generated docs..." rm -rf en/reference/*/sage rm -rf en/reference/*/sagenb rm -rf en/reference/sage rm -rf en/reference/sagenb - rm -rf output - + rm -f common/*.pyc diff --git a/src/doc/common/__init__.py b/src/doc/common/__init__.py index 8b137891791..e69de29bb2d 100644 --- a/src/doc/common/__init__.py +++ b/src/doc/common/__init__.py @@ -1 +0,0 @@ - diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index 1b32a9a603c..1f5736e8ee7 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -1,17 +1,10 @@ import sys, os, sphinx -from sage.env import SAGE_DOC +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT, SAGE_SRC from datetime import date -def get_doc_abspath(path): - """ - return the absolute path from a SAGE_DOC relative path - """ - return os.path.abspath(os.path.join(SAGE_DOC, path)) -# If your extensions are in another directory, add it here. If the directory -# is relative to the documentation root, use get_doc_abspath to make it -# absolute, like shown here. -sys.path.append(get_doc_abspath('common')) +# If your extensions are in another directory, add it here. +sys.path.append(os.path.join(SAGE_SRC, "sage_setup", "docbuild", "ext")) # General configuration # --------------------- @@ -52,7 +45,7 @@ def sphinx_plot(plot): # Add any paths that contain templates here, relative to this directory. -templates_path = [os.path.join(SAGE_DOC, 'common/templates'), 'templates'] +templates_path = [os.path.join(SAGE_DOC, 'common', 'templates'), 'templates'] # The suffix of source filenames. source_suffix = '.rst' @@ -72,10 +65,6 @@ def sphinx_plot(plot): from sage.version import version release = version -#version = '3.1.2' -# The full version, including alpha/beta/rc tags. -#release = '3.1.2' - # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None @@ -126,14 +115,15 @@ def sphinx_plot(plot): # Cross-links to other project's online documentation. intersphinx_mapping = { - 'python' : ('https://docs.python.org/', get_doc_abspath('common/python.inv'))} + 'python': ('https://docs.python.org/', + os.path.join(SAGE_DOC, "common", "python.inv"))} def set_intersphinx_mappings(app): """ Add precompiled inventory (the objects.inv) """ - refpath = get_doc_abspath('output/html/en/reference') - invpath = get_doc_abspath('output/inventory/en/reference') + refpath = os.path.join(SAGE_DOC_OUTPUT, "html", "en", "reference") + invpath = os.path.join(SAGE_DOC_OUTPUT, "inventory", "en", "reference") if app.config.multidoc_first_pass == 1 or \ not (os.path.exists(refpath) and os.path.exists(invpath)): app.config.intersphinx_mapping = {} @@ -212,7 +202,7 @@ def set_intersphinx_mappings(app): # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = [os.path.join(SAGE_DOC, 'common/static'), 'static'] +html_static_path = [os.path.join(SAGE_DOC, 'common', 'static'), 'static'] # We use MathJax to build the documentation unless the environment # variable SAGE_DOC_MATHJAX is set to "no" or "False". (Note that if @@ -745,6 +735,9 @@ def skip_TESTS_block(app, what, name, obj, options, docstringlines): See sage.misc.sagedoc.skip_TESTS_block for more information. """ from sage.misc.sagedoc import skip_TESTS_block as sagedoc_skip_TESTS + if len(docstringlines) == 0: + # No docstring, so don't do anything. See :trac:`19932`. + return s = sagedoc_skip_TESTS("\n".join(docstringlines)) lines = s.split("\n") for i in range(len(lines)): diff --git a/src/doc/common/custom-sphinx-build.py b/src/doc/common/custom-sphinx-build.py deleted file mode 100644 index 50952e50305..00000000000 --- a/src/doc/common/custom-sphinx-build.py +++ /dev/null @@ -1,219 +0,0 @@ -""" -This is Sage's version of the sphinx-build script - -Enhancements are: - -* import the Sage library to access the docstrings, otherwise doc - building doesn't work. - -* redirect stdout to our own logger, and remove some unwanted chatter. -""" - -# override the fancy multi-line formatting -def term_width_line(text): - return text + '\n' - -sphinx.util.console.term_width_line = term_width_line - - -# useless_chatter: regular expressions to be filtered from Sphinx -# output. - -useless_chatter = ( - re.compile('^$'), - re.compile('^Running Sphinx v'), - re.compile('^loading intersphinx inventory from '), - re.compile('^Compiling a sub-document'), - re.compile('^updating environment: 0 added, 0 changed, 0 removed'), - re.compile('^looking for now-outdated files... none found'), - re.compile('^building \[.*\]: targets for 0 source files that are out of date'), - re.compile('^loading pickled environment... done'), - re.compile('^loading cross citations... done \([0-9]* citations\).'), - re.compile('WARNING: favicon file \'favicon.ico\' does not exist') - ) - -# replacements: pairs of regular expressions and their replacements, -# to be applied to Sphinx output. - -replacements = () - -if 'inventory' in sys.argv: - # When building the inventory, ignore warnings about missing - # citations and the search index. - useless_chatter += ( - re.compile('^None:[0-9]*: WARNING: citation not found: '), - re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') - ) - replacements += ([re.compile('build succeeded, [0-9]+ warning[s]?.'), - 'build succeeded.'], ) - -# warnings: regular expressions (or strings) indicating a problem with -# docbuilding. Raise an exception if any of these occur. -warnings = (re.compile('Segmentation fault'), - re.compile('SEVERE'), - re.compile('ERROR'), - re.compile('^make.*Error'), - re.compile('Exception occurred'), - re.compile('Sphinx error')) - -# We want all warnings to actually be errors. -# Exceptions: -# - warnings upon building the LaTeX documentation -# - undefined labels upon the first pass of the compilation: some -# cross links may legitimately not yet be resolvable at this point. -if 'latex' not in sys.argv: - if 'multidoc_first_pass=1' in sys.argv: - # Catch all warnings except 'WARNING: undefined label' - warnings += (re.compile('WARNING: (?!undefined label)'),) - else: - warnings += (re.compile('WARNING:'),) - - -# Do not error out at the first warning, sometimes there is more -# information. So we run until the end of the file and only then raise -# the error. -ERROR_MESSAGE = None - - -class SageSphinxLogger(object): - """ - This implements the file object interface to serve as sys.stdout - replacement. - """ - ansi_color = re.compile(r'\x1b\[[0-9;]*m') - ansi_reset = re.compile(r'\x1b\[39;49;00m') - prefix_len = 9 - - def __init__(self, stream, prefix): - self._stream = stream - self._color = stream.isatty() - prefix = prefix[0:self.prefix_len] - prefix = ('[{0:'+str(self.prefix_len)+'}]').format(prefix) - self._is_stdout = (stream.fileno() == 1) - self._is_stderr = (stream.fileno() == 2) - if self._is_stdout: - color = 'darkgreen' - elif self._is_stderr: - color = 'red' - else: - color = 'lightgray' - self._prefix = sphinx.util.console.colorize(color, prefix) - - def _filter_out(self, line): - if ERROR_MESSAGE and self._is_stdout: - # swallow non-errors after an error occurred - return True - line = re.sub(self.ansi_color, '', line) - global useless_chatter - for regex in useless_chatter: - if regex.match(line) is not None: - return True - return False - - def _check_warnings(self, line): - global ERROR_MESSAGE - if ERROR_MESSAGE: - return # we already have found an error - global warnings - for regex in warnings: - if regex.search(line) is not None: - ERROR_MESSAGE = line - return - - def _log_line(self, line): - if self._filter_out(line): - return - global replacements - for (old, new) in replacements: - line = old.sub(new, line) - line = self._prefix + ' ' + line.strip() + '\n' - if not self._color: - line = self.ansi_color.sub('', line) - self._stream.write(line) - self._stream.flush() - self._check_warnings(line) - - _line_buffer = '' - - def _break_long_lines(self): - """ - Break text that has been formated with string.ljust() back - into individual lines. Return partial output. Do nothing if - the filter rule matches, otherwise subsequent lines would be - not filtered out. - """ - if self._filter_out(self._line_buffer): - return - cols = sphinx.util.console._tw - lines = [] - buf = self._line_buffer - while len(buf) > cols: - lines.append(buf[0:cols]) - buf = buf[cols:] - lines.append(buf) - self._line_buffer = '\n'.join(lines) - - def _write(self, string): - self._line_buffer += string - #self._break_long_lines() - lines = self._line_buffer.splitlines() - for i, line in enumerate(lines): - last = (i == len(lines)-1) - if last and not self._line_buffer.endswith('\n'): - self._line_buffer = line - return - self._log_line(line) - self._line_buffer = '' - - - # file object interface follows - - closed = False - encoding = None - mode = 'w' - name = '' - newlines = None - softspace = 0 - - def isatty(self): - return True - - def close(self): - if self._line_buffer != '': - self._log_line(self._line_buffer) - self._line_buffer = '' - - def flush(self): - self._stream.flush() - - def write(self, str): - try: - self._write(str) - except OSError: - raise - except Exception: - import traceback - traceback.print_exc(file=self._stream) - - def writelines(self, sequence): - for line in sequence: - self.write(line) - - -output_dir = sys.argv[-1] - -saved_stdout = sys.stdout -saved_stderr = sys.stderr - -try: - sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) - sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) - sphinx.cmdline.main(sys.argv) -finally: - sys.stdout = saved_stdout - sys.stderr = saved_stderr - -if ERROR_MESSAGE and ABORT_ON_ERROR: - sys.stdout.flush() - sys.stderr.flush() - raise OSError(ERROR_MESSAGE) diff --git a/src/doc/en/constructions/linear_codes.rst b/src/doc/en/constructions/linear_codes.rst index 570e734f0a3..57f488a9104 100644 --- a/src/doc/en/constructions/linear_codes.rst +++ b/src/doc/en/constructions/linear_codes.rst @@ -105,15 +105,11 @@ implemented. sage: C = codes.HammingCode(3,GF(2)) sage: MS = MatrixSpace(GF(2),1,7) sage: F = GF(2); a = F.gen() - sage: v1 = [a,a,F(0),a,a,F(0),a] - sage: C.decode_to_code(v1, "Syndrome") - (1, 1, 0, 1, 0, 0, 1) - sage: v2 = matrix([[a,a,F(0),a,a,F(0),a]]) - sage: C.decode_to_code(v2, "Syndrome") - (1, 1, 0, 1, 0, 0, 1) - sage: v3 = vector([a,a,F(0),a,a,F(0),a]) - sage: c = C.decode_to_code(v3, "Syndrome"); c - (1, 1, 0, 1, 0, 0, 1) + sage: v = vector([a,a,F(0),a,a,F(0),a]) + sage: c = C.decode_to_code(v, "Syndrome"); c + (1, 1, 0, 1, 0, 0, 1) + sage: c in C + True To plot the (histogram of) the weight distribution of a code, one can use the matplotlib package included with Sage: @@ -307,3 +303,4 @@ The idea is that a cryptosystem is a map plaintext (or message) space, and ciphertext space, respectively. :math:`E` is presumed to be injective, so ``e.key()`` returns the pre-image key. + diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index a2e8af7f11e..34827f65824 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -52,7 +52,7 @@ order to build a **html** version of this document, type:: sage --docbuild tutorial html -You can now open ``SAGE_ROOT/src/doc/output/html/en/tutorial/index.html`` in +You can now open ``SAGE_ROOT/local/share/doc/sage/html/en/tutorial/index.html`` in your web browser. - Do you want to **add a new file** to the documentation? :ref:`Click here @@ -165,7 +165,7 @@ Building the Manuals All of the Sage manuals are built using the ``sage --docbuild`` script. The content of the ``sage --docbuild`` script is defined in -``SAGE_ROOT/src/doc/common/builder.py``. It is a thin wrapper around +``SAGE_ROOT/src/sage_setup/docbuild/__init__.py``. It is a thin wrapper around the ``sphinx-build`` script which does all of the real work. It is designed to be a replacement for the default Makefiles generated by the ``sphinx-quickstart`` script. The general form of the command diff --git a/src/doc/en/developer/sagenb/index.rst b/src/doc/en/developer/sagenb/index.rst index 3cfd29176b6..5e2a7f9db0c 100644 --- a/src/doc/en/developer/sagenb/index.rst +++ b/src/doc/en/developer/sagenb/index.rst @@ -23,7 +23,7 @@ changes (it need not have the name or location given below). mkdir ~/hackdir cd ~/hackdir git clone git://github.com/sagemath/sagenb.git sagenb-git - cd SAGE_ROOT/devel + cd SAGE_ROOT/src rm sagenb ln -s ~/hackdir/sagenb sagenb cd sagenb diff --git a/src/doc/en/faq/faq-general.rst b/src/doc/en/faq/faq-general.rst index edb77996be5..cac7e59e515 100644 --- a/src/doc/en/faq/faq-general.rst +++ b/src/doc/en/faq/faq-general.rst @@ -312,22 +312,21 @@ page on the Sage website. Each release of Sage comes with the full documentation that makes up the Sage standard documentation. If you have downloaded a binary Sage release, the HTML version of the corresponding documentation comes pre-built and can be found under the -directory -``SAGE_ROOT/src/doc/output/html/``. During the compilation -of Sage from source, the HTML version of the documentation is also -built in the process. To build the HTML version of the documentation, -issue the following command from ``SAGE_ROOT``:: +directory ``SAGE_ROOT/local/share/doc/sage/html/``. +During the compilation of Sage from source, the HTML version of the +documentation is also built in the process. To build the HTML version +of the documentation, issue the following command from ``SAGE_ROOT``:: - $ ./sage -docbuild --no-pdf-links all html + $ ./sage --docbuild --no-pdf-links all html Building the PDF version requires that your system has a working LaTeX installation. To build the PDF version of the documentation, issue the following command from ``SAGE_ROOT``:: - $ ./sage -docbuild all pdf + $ ./sage --docbuild all pdf For more command line options, refer to the output of any of the following commands:: - $ ./sage -help - $ ./sage -advanced + $ ./sage --help + $ ./sage --advanced diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index fe0d8928895..0d0d81d48b8 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -794,8 +794,8 @@ how it is built: primarily intended for use when producing certain binary distributions of Sage, to lower the size of the distribution. As of this writing (December 2014, Sage 6.5), there are only a few such - plots, adding about 4M to the :file:`src/doc/output/` directory. In - the future, this may grow, of course. Note: after using this, if you + plots, adding about 4M to the :file:`local/share/doc/sage/` directory. + In the future, this may grow, of course. Note: after using this, if you want to build the documentation and include the pictures, you should run ``make doc-clean``, because the presence, or lack, of pictures is cached in the documentation output. diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index f07d5a3f78e..f9b04d812db 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -38,6 +38,8 @@ Algebras sage/algebras/hall_algebra sage/algebras/jordan_algebra + sage/algebras/orlik_solomon + sage/algebras/quatalg/quaternion_algebra sage/algebras/schur_algebra diff --git a/src/doc/en/reference/calculus/index.rst b/src/doc/en/reference/calculus/index.rst index 08b764e1770..25887543141 100644 --- a/src/doc/en/reference/calculus/index.rst +++ b/src/doc/en/reference/calculus/index.rst @@ -15,6 +15,7 @@ Symbolic Calculus sage/symbolic/function sage/symbolic/function_factory sage/calculus/functional + sage/symbolic/series sage/symbolic/integration/integral sage/symbolic/integration/external sage/calculus/test_sympy diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index dc56b715243..94155007dbe 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -3,29 +3,66 @@ Coding Theory ============= +Abstract classes, catalogs and databases +---------------------------------------- + .. toctree:: - :maxdepth: 1 + :maxdepth: 2 sage/coding/decoder - sage/coding/decoders_catalog sage/coding/encoder - sage/coding/encoders_catalog - sage/coding/channel_constructions + sage/coding/bounds_catalog sage/coding/channels_catalog sage/coding/codes_catalog + sage/coding/decoders_catalog + sage/coding/encoders_catalog + sage/coding/two_weight_db + +Linear codes and related constructions +--------------------------------------- + +.. toctree:: + :maxdepth: 1 + + sage/coding/binary_code sage/coding/grs sage/coding/linear_code sage/coding/code_constructions - sage/coding/guava sage/coding/sd_codes - sage/coding/bounds_catalog + sage/coding/guava + +Bounds on codes +--------------- + +.. toctree:: + :maxdepth: 1 + sage/coding/code_bounds - sage/coding/codecan/codecan - sage/coding/codecan/autgroup_can_label sage/coding/delsarte_bounds + +Channels and related constructions +---------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/coding/channel_constructions + +Source coding +------------- + +.. toctree:: + :maxdepth: 1 + sage/coding/source_coding/huffman - sage/coding/binary_code - sage/coding/decoder - sage/coding/two_weight_db + +Canonical forms +--------------- + +.. toctree:: + :maxdepth: 1 + + sage/coding/codecan/codecan + sage/coding/codecan/autgroup_can_label .. include:: ../footer.txt diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 88f0d74a3c0..11602a135e3 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -34,7 +34,7 @@ Comprehensive Module list sage/combinat/cluster_algebra_quiver/mutation_type sage/combinat/cluster_algebra_quiver/quiver sage/combinat/cluster_algebra_quiver/quiver_mutation_type - sage/combinat/colored_permutations + sage/combinat/colored_permutations sage/combinat/combinat sage/combinat/combinat_cython sage/combinat/combination @@ -70,6 +70,7 @@ Comprehensive Module list sage/combinat/crystals/littelmann_path sage/combinat/crystals/monomial_crystals sage/combinat/crystals/spins + sage/combinat/crystals/star_crystal sage/combinat/crystals/tensor_product sage/combinat/cyclic_sieving_phenomenon sage/combinat/debruijn_sequence diff --git a/src/doc/en/reference/conf.py b/src/doc/en/reference/conf.py index 26e14114b26..6fff8266082 100644 --- a/src/doc/en/reference/conf.py +++ b/src/doc/en/reference/conf.py @@ -12,12 +12,12 @@ # serve to show the default. import sys, os -from sage.env import SAGE_DOC +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT sys.path.append(SAGE_DOC) from common.conf import * ref_src = os.path.join(SAGE_DOC, 'en', 'reference') -ref_out = os.path.join(SAGE_DOC, 'output', 'html', 'en', 'reference') +ref_out = os.path.join(SAGE_DOC_OUTPUT, 'html', 'en', 'reference') # General information about the project. project = u"Sage Reference Manual" diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py index 54aefe527d0..f66a6c70bf6 100644 --- a/src/doc/en/reference/conf_sub.py +++ b/src/doc/en/reference/conf_sub.py @@ -12,12 +12,12 @@ # serve to show the default. import sys, os -from sage.env import SAGE_DOC +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT sys.path.append(SAGE_DOC) from common.conf import * ref_src = os.path.join(SAGE_DOC, 'en', 'reference') -ref_out = os.path.join(SAGE_DOC, 'output', 'html', 'en', 'reference') +ref_out = os.path.join(SAGE_DOC_OUTPUT, 'html', 'en', 'reference') # We use the main document's title, if we can find it. rst_file = open('index.rst', 'r') diff --git a/src/doc/en/reference/finite_rings/index.rst b/src/doc/en/reference/finite_rings/index.rst index 15a1066f957..a3965ae540a 100644 --- a/src/doc/en/reference/finite_rings/index.rst +++ b/src/doc/en/reference/finite_rings/index.rst @@ -16,7 +16,7 @@ Finite Fields .. toctree:: :maxdepth: 2 - sage/rings/finite_rings/constructor + sage/rings/finite_rings/finite_field_constructor sage/rings/finite_rings/finite_field_base sage/rings/finite_rings/element_base sage/rings/finite_rings/homset diff --git a/src/doc/en/reference/geometry/index.rst b/src/doc/en/reference/geometry/index.rst index da7fa8dac02..e2fa633b836 100644 --- a/src/doc/en/reference/geometry/index.rst +++ b/src/doc/en/reference/geometry/index.rst @@ -90,5 +90,6 @@ Internals sage/geometry/fan_isomorphism sage/geometry/hasse_diagram sage/geometry/integral_points + sage/geometry/hyperplane_arrangement/check_freeness .. include:: ../footer.txt diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index ed28a7ac84a..104cf4e2684 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -86,6 +86,7 @@ Geometry and Topology * :doc:`Combinatorial Geometry ` * :doc:`Cell Complexes and their Homology ` * :doc:`Differential Forms ` +* :doc:`Manifolds ` * :doc:`Parametrized Surfaces ` Number Theory, Algebraic Geometry diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 7f60aac9e62..d701e91c76b 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -50,7 +50,6 @@ to be aware of the modules described in this chapter. sage/libs/libecm sage/libs/lrcalc/lrcalc sage/libs/pari/handle_error - sage/libs/pari/gen_py sage/libs/pari/gen sage/libs/pari/pari_instance sage/libs/pari/closure diff --git a/src/doc/en/reference/manifolds/conf.py b/src/doc/en/reference/manifolds/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/manifolds/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/manifolds/index.rst b/src/doc/en/reference/manifolds/index.rst new file mode 100644 index 00000000000..f0d5932b1f3 --- /dev/null +++ b/src/doc/en/reference/manifolds/index.rst @@ -0,0 +1,18 @@ +Manifolds +========= + +This is the Sage implementation of manifolds resulting from the +`SageManifolds project `_. +This section describes only the "manifold" part of SageManifolds; +the pure algebraic part is described in the section +:ref:`tensors-on-free-modules`. + +More documentation (in particular example worksheets) can be found +`here `_. + +.. toctree:: + :maxdepth: 2 + + manifold + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst new file mode 100644 index 00000000000..d9364a345c3 --- /dev/null +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -0,0 +1,15 @@ +Topological Manifolds +===================== + +.. toctree:: + :maxdepth: 2 + + sage/manifolds/manifold + + sage/manifolds/subset + + sage/manifolds/structure + + sage/manifolds/point + + sage/manifolds/chart diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index ab999774fd0..044e4374163 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -114,8 +114,8 @@ Lazyness sage/misc/lazy_attribute sage/misc/lazy_format sage/misc/lazy_import - sage/misc/lazy_list sage/misc/lazy_import_cache + sage/misc/lazy_list sage/misc/lazy_string Caching diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 94226e21741..c2d2a85ad95 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -46,4 +46,8 @@ Modules sage/modules/vector_real_double_dense sage/modules/vector_symbolic_dense + sage/modules/filtered_vector_space + sage/modules/multi_filtered_vector_space + sage/modules/tensor_operations + .. include:: ../footer.txt diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 0b7b2d11074..1ddae8bf0e3 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -93,6 +93,7 @@ Miscellaneous sage/repl/interpreter sage/repl/ipython_extension + sage/repl/interface_magic sage/repl/ipython_kernel/install sage/repl/ipython_kernel/kernel sage/repl/ipython_tests diff --git a/src/doc/en/reference/schemes/index.rst b/src/doc/en/reference/schemes/index.rst index 7124500cb7b..6480e8ad6fb 100644 --- a/src/doc/en/reference/schemes/index.rst +++ b/src/doc/en/reference/schemes/index.rst @@ -41,7 +41,6 @@ Projective Schemes sage/schemes/projective/projective_space sage/schemes/projective/projective_point sage/schemes/projective/projective_morphism - sage/schemes/projective/projective_morphism_helper sage/schemes/projective/projective_rational_point sage/schemes/projective/projective_homset sage/schemes/projective/endPN_automorphism_group @@ -79,5 +78,7 @@ Toric Varieties sage/schemes/toric/homset sage/schemes/toric/points + sage/schemes/toric/sheaf/constructor + sage/schemes/toric/sheaf/klyachko .. include:: ../footer.txt diff --git a/src/doc/en/thematic_tutorials/coding_theory.rst b/src/doc/en/thematic_tutorials/coding_theory.rst index 93fadbc90a1..96fc46beef5 100644 --- a/src/doc/en/thematic_tutorials/coding_theory.rst +++ b/src/doc/en/thematic_tutorials/coding_theory.rst @@ -1,1067 +1,560 @@ .. -*- coding: utf-8 -*- +.. linkall .. _coding_theory: ====================== -Sage and Coding Theory +Coding Theory in Sage ====================== .. MODULEAUTHOR:: David Joyner and Robert Miller (2008), edited by Ralf Stephan + for the initial version. David Lucas (2016) for this version. -This brief paper surveys recent work in Sage on implementing algorithms -to compute with linear block codes. -Included in Sage is the group theory package GAP [GAP]_ and GUAVA [GUAVA]_, GAP's coding -theory package. All of GUAVA's functions can be accessed within Sage. +This tutorial, designed for beginners who want to discover how to use Sage +for their work (research, experimentation, teaching) on coding theory, +will present several key features of Sage's coding theory library +and explain how to find classes and methods you look for. -General coding theory functions -=============================== +During this tutorial, we will cover the following parts: -Many of the following coding theory functions are pure Python and do not -call GUAVA: +- what can you do with **generic linear codes and associated methods**, +- what can you do with **structured code families**, +- what can you do to **encode and recover messages, correct errors** and +- what can you do to **easily add errors to codewords**. -#. ``LinearCode`` class definition; ``LinearCodeFromVectorspace`` conversion - function. +The goal of this tutorial is to give a quick overview of what can be done +with the library and how to use the main functionalities. +It is neither a comprehensive description of all methods nor of specific +classes. If one needs some specific information on the behaviour of a +class/method related to coding theory, one should check the documentation +for this class/method. - EXAMPLES: +.. contents:: Table of contents + :depth: 3 - :: +I. Generic Linear codes and associated methods +============================================== - sage: MS = MatrixSpace(GF(2),4,7) - sage: G = MS([[1,1,1,0,0,0,0], [1,0,0,1,1,0,0], - ....: [0,1,0,1,0,1,0], [1,1,0,1,0,0,1]]) - sage: C = LinearCode(G) - sage: C - Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C.base_ring() - Finite Field of size 2 - sage: C.dimension() - 4 - sage: C.length() - 7 - sage: C.minimum_distance() - 3 - sage: C.spectrum() - [1, 0, 0, 7, 7, 0, 0, 1] - sage: C.weight_distribution() - [1, 0, 0, 7, 7, 0, 0, 1] +Let us start with the most generic code one can build: a generic linear code +without any specific structure. - This function also enables you to create your own code functions. The - following function implements the hexacode. +To build such a code, one just need to provide +its generator matrix, as follows:: - :: - - - def Hexacode(): - """ - This function returns the [6,3,4] hexacode over GF(4). - It is an extremal (Hermitian) self-dual Type IV code. - - EXAMPLES: - - sage: C = Hexacode() # known bug - sage: C.minimum_distance() # known bug - 4 - - """ - F = GF(4,"z") - z = F.gen() - MS = MatrixSpace(F, 3, 6) - G = MS([[1, 0, 0, 1, z, z ],\ - [0, 1, 0, z, 1, z ],\ - [0, 0, 1, z, z, 1 ]]) - return LinearCode(G) - -#. The ``spectrum`` (weight distribution), ``minimum_distance`` programs - (calling Steve Linton's C programs in GAP), - ``characteristic_function`` (as in [vanLint]_), and several implementations of - the Duursma zeta function (``zeta_polynomial``, ``zeta_function``, - ``chinen_polynomial``, for example), - - :: - - - sage: C = codes.HammingCode(3,GF(2)) - sage: C.zeta_polynomial() - 2/5*T^2 + 2/5*T + 1/5 - sage: C = best_known_linear_code(6,3,GF(2)) # known bug - sage: C.minimum_distance() # known bug - 3 - sage: C.zeta_polynomial() # known bug - 2/5*T^2 + 2/5*T + 1/5 - -#. ``gen_mat``, ``check_mat``, ``decode``, ``dual_code``, - ``extended_code``, ``binomial_moment`` for LinearCode. - - The i-th binomial moment of the :math:`[n,k,d]_q`-code :math:`C` is - - .. math:: B_i(C) = \sum_{S, |S|=i} \frac{q^{k_S}-1}{q-1} - - where :math:`k_S` is the dimension of the shortened code - :math:`C_{J-S}`, :math:`J=[1,2,...,n]`. - - EXAMPLES: - - :: - - - sage: C = codes.HammingCode(3,GF(2)) - sage: C.binomial_moment(2) - 0 - sage: C.binomial_moment(3) # known bug - 0 - sage: C.binomial_moment(4) # long time - 35 - sage: C = codes.HammingCode(3,GF(2)) - sage: MS = MatrixSpace(GF(2),1,7) - sage: F = GF(2); a = F.gen() - sage: v1 = [a,a,F(0),a,a,F(0),a] - sage: C.decode(v1) # known bug - (1, 0, 0, 1, 1, 0, 1) - - Decoding, at the moment, merely uses syndrome decoding via GUAVA. - -#. Boolean-valued functions such as "==", ``is_self_dual``, - ``is_self_orthogonal``, ``is_permutation_automorphism``, - -#. permutation methods: ``automorphism_group_binary_code``, - ``is_permutation_automorphism``, ``standard_form``, - ``module_composition_factors``. - - This latter function simply calls up the MeatAxe record from GAP. - - EXAMPLES: - - :: - - - sage: C = codes.HammingCode(3,GF(2)) - sage: G = C.automorphism_group_binary_code(); G # known bug - Permutation Group with generators [(2,3)(5,7), (2,5)(3,7), - (2,3,7,5)(4,6), (2,4)(6,7), (1,2)(3,4)] - sage: G.order() # known bug - 168 - sage: C = codes.HammingCode(3,GF(2)) - sage: C.generator_matrix() # known bug - [1 0 0 1 0 1 0] - [0 1 0 1 0 1 1] - [0 0 1 1 0 0 1] - [0 0 0 0 1 1 1] - sage: C.redundancy_matrix() # known bug - [1 1 0] - [1 1 1] - [1 0 1] - [0 1 1] - sage: C.standard_form()[0].generator_matrix() # known bug - [1 0 0 0 1 1 0] - [0 1 0 0 1 1 1] - [0 0 1 0 1 0 1] - [0 0 0 1 0 1 1] - sage: MS = MatrixSpace(GF(2),4,8) - sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0], # known bug - ....: [0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]]) # known bug - sage: C = codes.LinearCode(G) # known bug - sage: gp = C.automorphism_group_binary_code() # known bug - sage: C.module_composition_factors(gp) # known bug - [ rec( - field := GF(2), - isMTXModule := true, - dimension := 1, - generators := [ [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], - [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ] ], - smashMeataxe := rec( - algebraElement := - [ [ [ 5, 3 ], [ 5, 3 ] ], [ Z(2)^0, Z(2)^0, 0*Z(2), Z(2)^0, - 0*Z(2), Z(2)^0, Z(2)^0, Z(2)^0 ] ], - algebraElementMatrix := [ [ 0*Z(2) ] ], - characteristicPolynomial := x_1, - charpolFactors := x_1, - nullspaceVector := [ Z(2)^0 ], - ndimFlag := 1 ), - IsIrreducible := true ), rec( - field := GF(2), - isMTXModule := true, - dimension := 1, - generators := [ [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], - [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ] ], - smashMeataxe := rec( - algebraElement := - [ [ [ 5, 2 ], [ 1, 2 ] ], [ 0*Z(2), 0*Z(2), 0*Z(2), 0*Z(2), - Z(2)^0, 0*Z(2), Z(2)^0, 0*Z(2) ] ], - algebraElementMatrix := [ [ 0*Z(2) ] ], - characteristicPolynomial := x_1, - charpolFactors := x_1, - nullspaceVector := [ Z(2)^0 ], - ndimFlag := 1 ), - IsIrreducible := true ), rec( - field := GF(2), - isMTXModule := true, - dimension := 1, - generators := [ [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], - [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ] ], - smashMeataxe := rec( - algebraElement := - [ [ [ 4, 2 ], [ 7, 4 ] ], [ 0*Z(2), Z(2)^0, Z(2)^0, 0*Z(2), - Z(2)^0, Z(2)^0, Z(2)^0, Z(2)^0 ] ], - algebraElementMatrix := [ [ 0*Z(2) ] ], - characteristicPolynomial := x_1, - charpolFactors := x_1, - nullspaceVector := [ Z(2)^0 ], - ndimFlag := 1 ), - IsIrreducible := true ), rec( - field := GF(2), - isMTXModule := true, - dimension := 1, - generators := [ [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], - [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ], [ [ Z(2)^0 ] ] ], - smashMeataxe := rec( - algebraElement := - [ [ [ 4, 6 ], [ 1, 6 ] ], [ 0*Z(2), Z(2)^0, Z(2)^0, 0*Z(2), - Z(2)^0, 0*Z(2), Z(2)^0, Z(2)^0 ] ], - algebraElementMatrix := [ [ Z(2)^0 ] ], - characteristicPolynomial := x_1+Z(2)^0, - charpolFactors := x_1+Z(2)^0, - nullspaceVector := [ Z(2)^0 ], - ndimFlag := 1 ), - IsIrreducible := true ) ] - -#. design-theoretic methods: ``assmus_mattson_designs`` (implementing - the Assmus-Mattson Theorem). - - **Theorem 1.** (Assmus and Mattson Theorem. par. 8.4, page 303 of [HP]_) Let - :math:`A_0, A_1, ..., A_n` be the weight distribution of the - codewords in a binary linear :math:`[n , k, d]` code :math:`C`, and - let [1]_ :math:`A_0^\perp, A_1^\perp, ..., A_n^\perp` be the weight - distribution of the codewords in its dual :math:`[n,n-k, d^\perp]` - code :math:`C^\perp`. Fix a :math:`t`, :math:`00` elements called **points**, - and :math:`B` is a non-empty finite multiset of size :math:`b` whose - elements are called **blocks**, such that each block is a non-empty - finite multiset of :math:`k` points. :math:`A` design without - repeated blocks is called a **simple** block design. If every subset - of points of size :math:`t` is contained in exactly :math:`\lambda` - blocks the the block design is called a - :math:`\mathbf{t-(v,k,\lambda)}` **design** (or simply a - :math:`t`-design when the parameters are not specfied). When - :math:`\lambda=1` then the block design is called a - :math:`\mathbf{S(t,k,v)}` **Steiner system**. - - In the Assmus and Mattson Theorem, :math:`X` is the set - :math:`\{1,2,...,n\}` of coordinate locations and - :math:`B = \{supp(c)\ |\ c \in C_i\}` is the set of supports of the - codewords of :math:`C` of weight :math:`i`. Therefore, the parameters - of the :math:`t`-design for :math:`C_i` are - - .. math:: - - \begin{aligned} - t &= {\rm given},\\ - v &= n,\\ - k &= i,\ \ \text{(this $k$ is not to be confused with $\dim(C)$!)},\\ - b &= A_i,\\ - \lambda &= b*\frac{\binom{k}{t}}{\binom{v}{t}} \end{aligned} - - (by Theorem 8.1.6, p. 294, in [HP]_). - - Setting the ``mode="verbose"`` option prints out the values of the - parameters. - - The first example below means that the binary :math:`[24,12,8]`-code - :math:`C` has the property that the (support of the) codewords of - weight 8 (resp, 12, 16) form a 5-design. Similarly for its dual code - :math:`C^\perp` (of course :math:`C=C^\perp` in this case, so this - info is extraneous). The test fails to produce 6-designs (ie, the - hypotheses of the theorem fail to hold, not that the 6-designs - definitely don't exist). The command - ``assmus_mattson_designs(C,5,mode="verbose")`` returns the same value - but prints out more detailed information. - - The second example below illustrates the blocks of the - :math:`5`-:math:`(24, 8, 1)` design (i.e., the :math:`S(5,8,24)` - Steiner system). - - EXAMPLES: - - :: - - - sage: C = codes.ExtendedBinaryGolayCode() # example 1 - sage: C.assmus_mattson_designs(5) - ['weights from C: ', - [8, 12, 16, 24], - 'designs from C: ', - [[5, (24, 8, 1)], [5, (24, 12, 48)], [5, (24, 16, 78)], [5, (24, 24, 1)]], - 'weights from C*: ', - [8, 12, 16], - 'designs from C*: ', - [[5, (24, 8, 1)], [5, (24, 12, 48)], [5, (24, 16, 78)]]] - sage: C.assmus_mattson_designs(6) - 0 - sage: X = range(24)# example 2 - sage: blocks = [c.support() for c in C if hamming_weight(c)==8] # known bug - sage: len(blocks) # known bug - 759 - - -The method ``automorphism_group_binary_code`` is actually an interface -to an extremely fast implementation written by the second author. It -uses an open-source implementation of permutation backtracking, written -by Robert Miller and developed into a Sage module called NICE. This -package is described more fully in [Miller1]_. - -A permutation :math:`g \in S_n` of the fixed basis gives rise to a -permutation of the vectors, or words, in :math:`GF(2)^n`, sending -:math:`(w_i)` to :math:`(w_{g(i)})`. The **(permutation) automorphism -group** of the code :math:`C` is the set of permutations of the indices -that bijectively map :math:`C` to itself. Sage uses a partition -refinement algorithm to compute the automorphism group of any binary -code. In future work, this will be extended to other base rings. - -Native constructions -==================== - -Sage contains GUAVA but most of GUAVA's functions have not been -implemented in Python, so they must be called via the GAP interface. -(See the _`GUAVA manual`: https://code.google.com/p/guava-libraries/ -for details on the syntax of GUAVA.) - -In addition, here are some of the special codes implemented natively in -Python: - -- ``BCHCode`` - A 'Bose-Chaudhuri-Hockenghem code' (or BCH code, for short) - is the largest possible cyclic code of length :math:`n` over field - :math:`F=GF(q)`, whose generator polynomial has zeros (contained in) - :math:`\{\alpha^{b},\alpha^{b+1},\ldots \alpha^{b+\delta-2}\}`, where :math:`\alpha` is a primitive - :math:`n^{th}` root of unity in the splitting field :math:`GF(q^m)`, - :math:`b` is an integer :math:`0\leq b\leq n-\delta+1` and :math:`m` - is the multiplicative order of :math:`q` modulo :math:`n`. - - SEEALSO: :wikipedia:`BCH_code` - - EXAMPLES: - - :: - - - sage: FF. = GF(3^2,"a") - sage: x = PolynomialRing(FF,"x").gen() - sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L) - sage: f = x^(8)-1 - sage: g.divides(f) - True - sage: C = codes.CyclicCode(8,g); C - Linear code of length 8, dimension 4 over Finite Field of size 3 - sage: C.minimum_distance() - 4 - sage: C = codes.BCHCode(8,3,GF(3),1); C - Linear code of length 8, dimension 4 over Finite Field of size 3 - sage: C.minimum_distance() - 4 - sage: C = codes.BCHCode(8,5,GF(3)); C - Linear code of length 8, dimension 3 over Finite Field of size 3 - sage: C.minimum_distance() - 5 - -- ``BinaryGolayCode``, ``ExtendedBinaryGolayCode``, ``TernaryGolayCode``, - the - well-known "extremal" Golay codes: :wikipedia:`Golay_code` - - EXAMPLES: - - :: - - - sage: C = codes.ExtendedBinaryGolayCode() - sage: C - Linear code of length 24, dimension 12 over Finite Field of size 2 - sage: C.minimum_distance() - 8 - sage: C.is_self_dual() - True - sage: C = codes.TernaryGolayCode() - sage: C - Linear code of length 11, dimension 6 over Finite Field of size 3 - sage: C.minimum_distance() - 5 - -- Cyclic codes - ``CyclicCodeFromGeneratingPolynomial`` (= ``CyclicCode``), - ``CyclicCodeFromCheckPolynomial``: :wikipedia:`Cyclic_code` - - EXAMPLES: - - :: - - - sage: P. = PolynomialRing(GF(3),"x") - sage: g = x-1 - sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C - Linear code of length 4, dimension 3 over Finite Field of size 3 - sage: P. = PolynomialRing(GF(4,"a"),"x") - sage: g = x^3+1 - sage: C = codes.CyclicCodeFromGeneratingPolynomial(9,g); C - Linear code of length 9, dimension 6 over Finite Field in a of size 2^2 - sage: P. = PolynomialRing(GF(2),"x") - sage: g = x^3+x+1 - sage: C = codes.CyclicCodeFromGeneratingPolynomial(7,g); C - Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C.generator_matrix() - [1 1 0 1 0 0 0] - [0 1 1 0 1 0 0] - [0 0 1 1 0 1 0] - [0 0 0 1 1 0 1] - sage: g = x+1 - sage: C = codes.CyclicCodeFromGeneratingPolynomial(4,g); C - Linear code of length 4, dimension 3 over Finite Field of size 2 - sage: C.generator_matrix() - [1 1 0 0] - [0 1 1 0] - [0 0 1 1] - sage: P. = PolynomialRing(GF(3),"x") - sage: C = codes.CyclicCodeFromCheckPolynomial(4,x + 1); C - Linear code of length 4, dimension 1 over Finite Field of size 3 - sage: C = codes.CyclicCodeFromCheckPolynomial(4,x^3 + x^2 + x + 1); C - Linear code of length 4, dimension 3 over Finite Field of size 3 - sage: C.generator_matrix() - [2 1 0 0] - [0 2 1 0] - [0 0 2 1] - -- ``DuadicCodeEvenPair``, ``DuadicCodeOddPair`` - Constructs the "even" (resp. - "odd") pair of duadic codes associated to a "splitting" :math:`S_1`, - :math:`S_2` of :math:`n`. This is a special type of cyclic code whose - generator is determined by :math:`S_1`, :math:`S_2`. See chapter 6 in [HP]_. - - EXAMPLES: - - :: - - - sage: from sage.coding.code_constructions import is_a_splitting - sage: n = 11; q = 3 - sage: C = Zmod(n).cyclotomic_cosets(q); C - [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] - sage: S1 = C[1] - sage: S2 = C[2] - sage: is_a_splitting(S1,S2,11) - True - sage: codes.DuadicCodeOddPair(GF(q),S1,S2) - (Linear code of length 11, dimension 6 over Finite Field of size 3, - Linear code of length 11, dimension 6 over Finite Field of size 3) - - This is consistent with Theorem 6.1.3 in [HP]_. - -- ``HammingCode`` - the well-known Hamming code. - - The :math:`r^{th}` Hamming code over :math:`F=GF(q)` is an - :math:`[n,k,d]` code with length :math:`n=(q^r-1)/(q-1)`, dimension - :math:`k=(q^r-1)/(q-1) - r` and minimum distance :math:`d=3`. The - parity check matrix of a Hamming code has rows consisting of all - nonzero vectors of length r in its columns, modulo a scalar factor so - no parallel columns arise. A Hamming code is a single - error-correcting code. - - SEEALSO: :wikipedia:`Hamming_code` - - EXAMPLES: - - :: - - - sage: codes.HammingCode(3,GF(2)) - Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C = codes.HammingCode(3,GF(3)); C - Linear code of length 13, dimension 10 over Finite Field of size 3 - sage: C.minimum_distance() - 3 - sage: C = codes.HammingCode(3,GF(4,'a')); C - Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 - -- ``LinearCodeFromCheckMatrix`` - for specifing the code using the check - matrix instead of the generator matrix. - - A linear :math:`[n,k]`-code :math:`C` is uniquely determined by its - generator matrix :math:`G` and check matrix :math:`H`. These objects - and morphisms fit into the following short exact sequence, - - .. math:: - - 0 \rightarrow - {\mathbf{F}}^k \stackrel{G}{\rightarrow} - {\mathbf{F}}^n \stackrel{H}{\rightarrow} - {\mathbf{F}}^{n-k} \rightarrow - 0. - - Here, "short exact" means (a) the arrow :math:`G` is injective, i.e., - :math:`G` is a full-rank :math:`k\times n` matrix, (b) the arrow - :math:`H` is surjective, and (c) - :math:`{\rm image}(G)={\rm kernel}(H)`. - - EXAMPLES: - - :: - - - sage: C = codes.HammingCode(3,GF(2)) - sage: H = C.parity_check_matrix(); H # known bug - [1 0 0 1 1 0 1] - [0 1 0 1 0 1 1] - [0 0 1 1 1 1 0] - sage: codes.LinearCodeFromCheckMatrix(H) == C # known bug - True - sage: C = codes.HammingCode(2,GF(3)) - sage: H = C.parity_check_matrix(); H # known bug - [1 0 2 2] - [0 1 2 1] - sage: codes.LinearCodeFromCheckMatrix(H) == C # known bug - True - sage: C = codes.RandomLinearCode(10,5,GF(4,"a")) - sage: H = C.parity_check_matrix() - sage: codes.LinearCodeFromCheckMatrix(H) == C # known bug - True - -- ``QuadraticResidueCodeEvenPair``, ``QuadraticResidueCodeOddPair``: Quadratic - residue codes of a given odd prime length and base ring either don't - exist at all or occur as 4-tuples - a pair of "odd-like" codes and a - pair of "even-like" codes. If :math:`n > 2` is prime then (Theorem - 6.6.2 in [HP]_) a QR code exists over :math:`GF(q)` if and only if - :math:`q` is a quadratic residue :math:`\pmod n`. Here they are - constructed as "even-like" (resp., "odd-like") duadic codes - associated the splitting :math:`(Q,N) \pmod n`, where :math:`Q` is - the set of non-zero quadratic residues and :math:`N` is the - non-residues. - - ``QuadraticResidueCode`` (a special case) and - ``ExtendedQuadraticResidueCode`` are included as well. - - EXAMPLES: - - :: - - - sage: codes.QuadraticResidueCodeEvenPair(17,GF(13)) - (Linear code of length 17, dimension 8 over Finite Field of size 13, - Linear code of length 17, dimension 8 over Finite Field of size 13) - sage: codes.QuadraticResidueCodeEvenPair(17,GF(2)) - (Linear code of length 17, dimension 8 over Finite Field of size 2, - Linear code of length 17, dimension 8 over Finite Field of size 2) - sage: codes.QuadraticResidueCodeEvenPair(13,GF(9,"z")) - (Linear code of length 13, dimension 6 over Finite Field in z of size 3^2, - Linear code of length 13, dimension 6 over Finite Field in z of size 3^2) - sage: C1 = codes.QuadraticResidueCodeEvenPair(7,GF(2))[0] - sage: C1.is_self_orthogonal() - True - sage: C2 = codes.QuadraticResidueCodeEvenPair(7,GF(2))[1] - sage: C2.is_self_orthogonal() - True - sage: C3 = codes.QuadraticResidueCodeOddPair(17,GF(2))[0] - sage: C4 = codes.QuadraticResidueCodeEvenPair(17,GF(2))[1] - sage: C3 == C4.dual_code() - True - - This is consistent with Theorem 6.6.9 and Exercise 365 in [HP]_. - -- ``RandomLinearCode`` - Repeatedly applies Sage's ``random_element`` - applied to the ambient ``MatrixSpace`` of the generator matrix until a - full rank matrix is found. - -- ``GeneralizedReedSolomonCode`` - Given a finite field :math:`\mathbb{F}` - of order :math:`q`, let :math:`n` and - :math:`k` be chosen such that :math:`1 \leq k \leq n \leq q`. Pick - :math:`n` distinct elements of :math:`\mathbb{F}`, denoted - :math:`\{ x_1, x_2, ... , x_n \}`. Then, the codewords are obtained - by evaluating every polynomial in :math:`\mathbb{F}[x]` of degree less - than :math:`k` at each :math:`x_i`: + sage: G = matrix(GF(3), [[1, 0, 0, 0, 1, 2, 1], + ....: [0, 1, 0, 0, 2, 1, 0], + ....: [0, 0, 1, 2, 2, 2, 2]]) + sage: C = LinearCode(G) - .. math:: +With these lines, you just created a linear code, congratulations! +Note that if you pass a matrix which is not full rank, Sage will turn +it into a full-rank matrix before building the code, +as illustrated in the following example:: - C = \left\{ \left( f(x_1), f(x_2), ..., f(x_n) \right)\ |\ f \in \mathbb{F}[x], - {\rm deg}(f) = GF(3^2,"a") - sage: pts = [0,1,a,a^2,2*a,2*a+1] - sage: len(Set(pts)) == 6 # to make sure there are no duplicates - True - sage: C = codes.GeneralizedReedSolomonCode(pts, 4); C - [6, 4, 3] Generalized Reed-Solomon Code over Finite Field in a of size 3^2 - sage: C.minimum_distance() - 3 - -- ``ToricCode`` - Let :math:`P` denote a list of lattice points in - :math:`\mathbb{Z}^d` and let :math:`T` denote a listing of all points in - :math:`(\mathbb{F}^x )^d`. Put :math:`n=|T|` and let :math:`k` denote the - dimension of the vector space of functions - :math:`V = Span \{x^e \ |\ e \in P\}`. The associated toric code - :math:`C` is the evaluation code which is the image of the evaluation - map :math:`eval_T : V \rightarrow \mathbb{F}^n`, where :math:`x^e` is the - multi-index notation. - - EXAMPLES: - - :: - - - sage: C = codes.ToricCode([[0,0],[1,0],[2,0],[0,1],[1,1]],GF(7)) - sage: C - Linear code of length 36, dimension 5 over Finite Field of size 7 - sage: C.minimum_distance() - 24 - sage: P = [ [0,0],[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[3,1],[3,2],[4,1]] - sage: C = codes.ToricCode(P, GF(8,"a")) - sage: C - Linear code of length 49, dimension 11 over Finite Field in a of size 2^3 +Of course, as ``C`` is a generic linear code, an exhaustive search algorithm +is run to find the minimum distance, which will be slower and slower as the +code grows. - This is in fact a :math:`[49,11,28]` code over :math:`GF(8)`. If you - type next ``C.minimum_distance()`` and wait overnight (!), you will - get 28. +By just typing the name of our code, we get a sentence which briefly +describes it and gives its parameters:: -- ``WalshCode`` - a binary linear :math:`[2^m,m,2^{m-1}]` code related to - Hadamard matrices. :wikipedia:`Walsh_code` - - EXAMPLES: + sage: C + Linear code of length 7, dimension 3 over Finite Field of size 3 - :: +As the aim of this tutorial is not to give a comprehensive view of the methods, +we won't describe any other methods. +If one wants to get all methods that can be run on a linear code, one can: - sage: C = codes.WalshCode(4); C - Linear code of length 16, dimension 4 over Finite Field of size 2 - sage: C.minimum_distance() - 8 +- either check the manual page of the file :ref:`sage.coding.linear_code` +- or type:: -Bounds -====== + C. -Regarding bounds on coding theory parameters, this module implements: + in Sage to get a list of all available methods for ``C``. + Afterwards, typing:: -- ``best_known_linear_code_www`` (interface with codetables.de since A. - Brouwer's online tables have been disabled). Explains the - construction of the best known linear code over :math:`GF(q)` with - length :math:`n` and dimension :math:`k`, courtesy of the www page - http://www.codetables.de/. + C.method? - INPUT: + will show the manual page for ``method``. - - ``n`` - integer, the length of the code +.. NOTE:: - - ``k`` - integer, the dimension of the code + Some generic methods require the installation of the optional + package Guava for Gap. While some work is done to always propose + a default implementation which *does not* require an optional package, + there exist some methods which are not up to date - yet. + If you're receiving an error message related to Gap, please check the + documentation of the method to verify if Guava has to be installed. - - ``F`` - finite field, whose field order must be in [2, 3, 4, 5, 7, - 8, 9] +II. Structured code families and an overview of the encoding and decoding system +================================================================================ - - ``verbose`` - bool (default=False), print verbose mesSage +II.1 Create specific codes in Sage +---------------------------------- - EXAMPLES: +Now that we know how to create generic linear codes, we want to go deeper +and created specific code families. In Sage, all codes families can be +accessed by typing:: - :: + codes. +Doing so, you will get the comprehensive list of all code families +Sage can build. - sage: L = codes.best_known_linear_code_www(72, 36, GF(2)) # known bug - sage: print L # known bug - Construction of a linear code [72,36,15] over GF(2): - [1]: [73, 36, 16] Cyclic Linear Code over GF(2) - CyclicCode of length 73 with generating polynomial x^37 + x^36 - + x^34 + x^33 + x^32 + x^27 + x^25 + x^24 + x^22 + x^21 + x^19 - + x^18 + x^15 + x^11 + x^10 + x^8 + x^7 + x^5 + x^3 + 1 - [2]: [72, 36, 15] Linear Code over GF(2) - Puncturing of [1] at 1 - last modified: 2002-03-20 +For the rest of this section, we will illustrate specific functionalities +of these code families by manipulating +:class:`sage.coding.grs.GeneralizedReedSolomonCode`. -- ``bounds_minimum_distance`` which call tables in GUAVA (updated May - 2006) created by Cen Tjhai instead of the online internet tables. It - simply returns the GAP record for that code: +So, for starters, we want to create a Generalized Reed-Solomon (GRS) code. - :: +By clicking on the link provided above, or typing:: + codes.GeneralizedReedSolomonCode? - sage: print bounds_minimum_distance(10,5,GF(2)) # known bug - rec( - n := 10, - k := 5, - q := 2, - references := rec( - ), - construction := - [ , [ [ , [ [ - , - [ [ , [ 8, 2 ] ] ] ], - [ , - [ [ , - [ [ , [ 4, 2 ] ] ] ], - [ , [ 4, 2 ] ] ] ] ] ], - [ 1, 2, 3, 4, 5, 6 ] ] ], - lowerBound := 4, - lowerBoundExplanation := - [ "Lb(10,5)=4, by shortening of:", - "Lb(16,11)=4, by the u|u+v construction applied to C1 [8,7,2] and C2 [8,4,4]: ", - "Lb(8,7)=2, dual of the repetition code", - "Lb(8,4)=4, by the u|u+v construction applied to C1 [4,3,2] and C2 [4,1,4]: ", - "Lb(4,3)=2, dual of the repetition code", "Lb(4,1)=4, repetition code" - ], - upperBound := 4, - upperBoundExplanation := [ "Ub(10,5)=4, by the Griesmer bound" ] ) +one can access the documentation page for GRS codes, find a definition +of these and learn what is needed to build one in Sage. -- ``codesize_upper_bound(n,d,q)``, for the best known (as of May, 2006) - upper bound :math:`A(n,d)` for the size of a code of length - :math:`n`, minimum distance :math:`d` over a field of size :math:`q`. +Here we choose to build the [12, 6] GRS code over :math:`\GF{13}`. +To do this, we need up to three elements: - EXAMPLES: +- The **list of evaluation points**, +- the **dimension of the code**, and +- optionally, the **list of column multipliers**. - :: +We build our code as follows:: + sage: F = GF(7) + sage: length, dimension = 6, 3 + sage: evaluation_pts = F.list()[:length] + sage: column_mults = F.list()[1:length+1] + sage: C = codes.GeneralizedReedSolomonCode(evaluation_pts, dimension, column_mults) - sage: codesize_upper_bound(10, 3, 2) # known bug - 85 +Our GRS code is now created. We can ask for its parameters, as we did in the +previous section:: - This means that there is a :math:`(10,85,3)` binary (non-linear) - code. Since :math:`85>2^6`, this is a better code that a - :math:`[10,6,3]` binary (linear) code, assuming one exists. Let's use - ``best_known_linear_code_www`` to find out: + sage: C.length() + 6 + sage: C.dimension() + 3 + sage: C.base_ring() + Finite Field of size 7 - :: +It is also possible to ask for the evaluation points and +the column multipliers by calling +:meth:`sage.coding.grs.GeneralizedReedSolomonCode.evaluation_points` and +:meth:`sage.coding.grs.GeneralizedReedSolomonCode.column_multipliers`. +Now, if you know some theory for GRS codes, you know that it's especially easy +to compute their minimum distance, which is: +:math:`d = n - k + 1`, where :math:`n` is the length of the code and +:math:`k` is the dimension of the code. - sage: L = best_known_linear_code_www(10, 6, GF(2)) # known bug - sage: print L # known bug - Construction of a linear code - [10,6,3] over GF(2): - [1]: [4, 1, 4] Cyclic Linear Code over GF(2) - RepetitionCode of length 4 - [2]: [4, 3, 2] Cyclic Linear Code over GF(2) - Dual of the RepetitionCode of length 4 - [3]: [8, 4, 4] Quasicyclic of degree 2 Linear Code over GF(2) - PlotkinSum of [2] and [1] - [4]: [8, 7, 2] Cyclic Linear Code over GF(2) - Dual of the RepetitionCode of length 8 - [5]: [16, 11, 4] Linear Code over GF(2) - PlotkinSum of [4] and [3] - [6]: [15, 11, 3] Linear Code over GF(2) - Puncturing of [5] at 1 - [7]: [10, 6, 3] Linear Code over GF(2) - Shortening of [6] at { 11 .. 15 } +Because Sage knows ``C`` is a GRS code, it will not run the exhaustive +search algorithm presented in section I to find ``C``'s minimum distance +but use the operation introduced above. And you instantly get:: - last modified: 2001-01-30 + sage: C.minimum_distance() + 4 - Not only does a :math:`[10,6,3]` binary linear code exist, the value - :math:`d=3` is the minimum distance is best known for :math:`n=10`, - :math:`k=6`. +All these parameters are summarized inside the string representation +of our code:: -- ``dimension_upper_bound(n,d,q)``, an upper bound - :math:`B(n,d)=B_q(n,d)` for the dimension of a linear code of length - :math:`n`, minimum distance :math:`d` over a field of size :math:`q`. + sage: C + [6, 3, 4] Generalized Reed-Solomon Code over Finite Field of size 7 - EXAMPLES: +.. NOTE:: - :: + Writing proper classes for code families is a work in progress. + Some constructions under ``codes.`` might thus be functions which + build a generic linear code, and in that case are only able to use + generic algorithms. + Please refer to the documentation of a construction to check if it + is a function or a class. - sage: codes.bounds.dimension_upper_bound(10, 3, 2) - 6 +II.2 Encode and decode in Sage +------------------------------ - This was established in the example above. +In the previous part, we learnt how to find specific code families +in Sage and create instances of these families. -- ``gilbert_lower_bound(n,q,d)``, a lower bound for number of elements - in the largest code of minimum distance :math:`d` in - :math:`\mathbb{F}_q^n`. +In this part, we will learn how to encode and decode. -- ``gv_info_rate(n,delta,q)``, namely :math:`log_q(GLB)/n`, where GLB - is the Gilbert lower bound above and ``delta`` :math:`= d/n`. +First of all, we want to generate a codeword to play with. +There is two different ways to do that: - Let +- It is possible to just generate a random element of our code, as follows:: - .. math:: R = R(C) = \frac{k}{n}, + sage: c = C.random_element() + sage: c in C + True - which measures the information rate of the code, and +- Alternatively, we can create a message and then encode it into a codeword:: - .. math:: \delta = \delta(C) = \frac{d}{n}, + sage: msg = random_vector(C.base_field(), C.dimension()) + sage: c = C.encode(msg) + sage: c in C + True - which measures the error correcting ability of the code. Let - :math:`\Sigma_q` denote the set of all - :math:`(\delta,R)\in [0,1]^2` such that there exists a sequence - :math:`C_i`, :math:`i=1,2,...`, of - :math:`[n_i,k_i,d_i]`-codes for which - :math:`\lim_{i\rightarrow \infty} d_i/n_1=\delta` and - :math:`\lim_{i\rightarrow \infty} k_i/n_i=R`. - - The following theorem describes information-theoretical limits on how - "good" a linear code can be. - - **Theorem 2** (Manin [SS]_, chapter 1). There exists a continuous decreasing function - - .. math:: \alpha_q:[0,1]\rightarrow [0,1], - - such that - - - :math:`\alpha_q` is strictly decreasing on - :math:`[0,{\frac{q-1}{q}}]`, - - - :math:`\alpha_q(0)=1`, - - - if :math:`{\frac{q-1}{q}}\leq x\leq 1` then :math:`\alpha_q(x)=0`, - - - :math:`\Sigma_q=\{(\delta,R)\in [0,1]^2\ |\ 0\leq R\leq \alpha_q(\delta)\}`. - - Not a single value of :math:`\alpha_q(x)` is known for - :math:`00`, there exists an - :math:`(n,k,d)`-code :math:`C` (which may depend on :math:`\epsilon`) - with - - .. math:: - - \begin{aligned} - R(C)+\delta(C) \geq - &1- \delta(C)\log_q({\frac{q-1}{q}})-\delta(C)\log_q(\delta(C))\\ - &-(1-\delta(C))\log_q(1-\delta(C))-\epsilon.\\ - \end{aligned} - - The curve - :math:`(\delta, 1- \delta\log_q({\frac{q-1}{q}})-\delta\log_q(\delta)- - (1-\delta)\log_q(1-\delta)))` is called the **Gilbert-Varshamov - curve**. - -- ``gv_bound_asymp(delta,q)``, asymptotic analog of the Gilbert lower - bound. - - :: - - Sage : f = lambda x: gv_bound_asymp(x,2) - Sage : plot(f,0,1/2) - -.. figure:: media/gv-bound-asymp.png - :align: center - :scale: 50 % - - Figure 1: Plot of the Gilbert-Varshamov curve using Sage (i.e., ``y = gv_bound_asymp(x, 2)``). - -- ``plotkin_upper_bound(n,q,d)`` - -- ``plotkin_bound_asymp(delta,q)``, asymptotic analog of the Plotkin - upper bound. - -.. figure:: media/plotkin-bound-asymp.png - :align: center - :scale: 50 % - - Figure 2: Plot using Sage of ``y = plotkin_bound_asymp(x, 2)``. - -- ``griesmer_upper_bound(n,q,d)``, the Griesmer upper bound. - -- ``elias_upper_bound(n,q,d)``, the Elias upper bound. - -- ``elias_bound_asymp(delta,q)``, asymptotic analog of the Elias upper - bound. - -.. figure:: media/elias-bound-asymp.png - :align: center - :scale: 50 % - - Figure 3: Plot using Sage of ``y = elias_bound_asymp(x, 2)``. - -- ``hamming_upper_bound(n,q,d)``, the Hamming upper bound. - -- ``hamming_bound_asymp(delta,q)``, asymptotic analog of the Hamming - upper bound. - -.. figure:: media/hamming-bound-asymp.png - :align: center - :scale: 50 % - - Figure 4: Plot using Sage of ``y = hamming_bound_asymp(x, 2)``. - -- ``singleton_upper_bound(n,q,d)``, the Singleton upper bound. - -- ``singleton_bound_asymp(delta,q)``, asymptotic analog of the - Singleton upper bound. - -.. figure:: media/singleton-bound-asymp.png - :align: center - :scale: 50 % - - Figure 5: Plot using Sage of ``y = singleton_bound_asymp(x, 2)``. - -- ``mrrw1_bound_asymp(delta,q)``, "first" asymptotic - McEliese-Rumsey-Rodemich-Welsh upper bound for the information rate . - -.. figure:: media/mrrw1-bound-asymp.png - :align: center - :scale: 50 % - - Figure 6: Plot using Sage of ``y = mrrw1_bound_asymp(x, 2)``. - -Here are all the bounds together: - -:: - - - sage: f1 = lambda x: codes.bounds.gv_bound_asymp(x,2) - sage: P1 = plot(f1,0,1/2,linestyle=":") - sage: f2 = lambda x: codes.bounds.plotkin_bound_asymp(x,2) - sage: P2 = plot(f2,0,1/2,linestyle="--") - sage: f3 = lambda x: codes.bounds.elias_bound_asymp(x,2) - sage: P3 = plot(f3,0,1/2,rgbcolor=(1,0,0)) - sage: f4 = lambda x: codes.bounds.singleton_bound_asymp(x,2) - sage: P4 = plot(f4,0,1/2,linestyle="-.") - sage: f5 = lambda x: codes.bounds.mrrw1_bound_asymp(x,2) - sage: P5 = plot(f5,0,1/2,linestyle="steps") - sage: f6 = lambda x: codes.bounds.hamming_bound_asymp(x,2) - sage: P6 = plot(f6,0,1/2,rgbcolor=(0,1,0)) - sage: show(P1+P2+P3+P4+P5+P6) - -.. figure:: media/all-bounds-asymp.png - :align: center - :scale: 100 % - - Figure 7: Plot of the Gilbert-Varshamov (dotted), Elias (red), Plotkin (dashed), - Singleton (dash-dotted), Hamming (green), and MRRW (stepped) curves using - Sage. - - -Self-dual codes -=============== - -Sage also includes a database of all self-dual binary codes of length -:math:`\leq 20` (and some of length :math:`22`). The main function is -``self_dual_codes_binary``, which is a case-by-case list of entries, -each represented by a Python dictionary. - -Format of each entry: a Python dictionary with keys ``order autgp``, -``spectrum``, ``code``, ``Comment``, ``Type``, where - -- ``code`` - a self-dual code :math:`C` of length :math:`n`, dimension - :math:`n/2`, over :math:`GF(2)`, - -- ``order autgp`` - order of the permutation automorphism group of - :math:`C`, - -- ``Type`` - the type of :math:`C` (which can be "I" or "II", in the - binary case), - -- ``spectrum`` - the spectrum :math:`[A_0,A_1,...,A_n]`, - -- ``Comment`` - possibly an empty string. - -In fact, in Table 9.10 of , the number :math:`B_n` of inequivalent -self-dual binary codes of length :math:`n` is given: - -+---------------+-----+-----+-----+-----+------+------+------+------+------+------+------+------+-------+-------+-------+ -| :math:`n` | 2 | 4 | 6 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | -+===============+=====+=====+=====+=====+======+======+======+======+======+======+======+======+=======+=======+=======+ -| :math:`B_n` | 1 | 1 | 1 | 2 | 2 | 3 | 4 | 7 | 9 | 16 | 25 | 55 | 103 | 261 | 731 | -+---------------+-----+-----+-----+-----+------+------+------+------+------+------+------+------+-------+-------+-------+ - -According to an entry in Sloane's Online Encyclopedia of Integer -Sequences, http://oeis.org/A003179, the next -2 entries are: 3295, 24147. - -EXAMPLES: - -:: - - - sage: C = self_dual_codes_binary(10)["10"] - sage: C["0"]["code"] == C["0"]["code"].dual_code() - True - sage: C["1"]["code"] == C["1"]["code"].dual_code() - True - sage: len(C.keys()) # number of inequiv sd codes of length 10 - 2 - sage: C = self_dual_codes_binary(12)["12"] - sage: C["0"]["code"] == C["0"]["code"].dual_code() - True - sage: C["1"]["code"] == C["1"]["code"].dual_code() - True - sage: C["2"]["code"] == C["2"]["code"].dual_code() - True - -These Sage commands simply show that the two inequivalent self-dual -binary codes of length 10, and the two inequivalent self-dual binary -codes of length 12, are indeed self dual. - -A lot of work on the classification of doubly even self-orthogonal codes -using Sage can be found at http://www.rlmiller.org/de_codes/. - -The number of permutation equivalence classes of all doubly even -:math:`[n,k]`-codes is shown in the table at -http://www.rlmiller.org/de_codes/, and the list of codes so far -discovered is linked from the list entries. Each link on that webpage -points to a Sage object file, which when loaded (e.g., -``Sage : L = load('24_12_de_codes.sobj')``) is a list of matrices in -standard form. The algorithm is described in . - -REFERENCES: - -.. [GAP] The GAP Group, GAP - Groups, Algorithms, and Programming, Version - 4.4.10; 2007. http://www.gap-system.org. - -.. [GUAVA] GUAVA, a coding theory package for GAP, - http://sage.math.washington.edu/home/wdj/guava/. - -.. [HP] W. C. Huffman and V. Pless, **Fundamentals of error-correcting codes**, - Cambridge Univ. Press, 2003. - -.. [vanLint] J. van Lint, **Introduction to coding theory, 3rd ed.**, Springer-Verlag - GTM, 86, 1999. - -.. [Miller1] Robert Miller, *Graph automorphism computation*, March 2007. - -.. [Miller2] ---, *Doubly even codes*, http://www.rlmiller.org/talks/June_Meeting.pdf, - June 2007. - -.. [Sage] The Sage Group, Sage : *Mathematical software*, version 3.0. - http://www.sagemath.org/. - -.. [SS] S. Shokranian and M.A. Shokrollahi, **Coding theory and bilinear - complexity**, Scientific Series of the International Bureau, KFA Juelich, - Vol. 21, 1994. - -.. [1] - For typographical reasons, the output of the program - ``assmus_mattson_designs`` uses C\* instead of :math:`C^\perp`. - -.. [2] - A code :math:`C` whose parameters satisfy :math:`k+d=n+1` is called - **maximum distance separable** or **MDS**. +Either way, we obtained a codeword. +So, we might want to put some errors in it, and try to correct these +errors afterwards. We can obviously do it by changing the values at +some random positions of our codeword, but we propose here something +more general: communication channels. +:class:`sage.coding.channel_constructions.Channel` objects are meant +as abstractions for communication channels and for manipulation of +data representation. In this case, we want to emulate a communication channel +which adds some, but not too many, errors to a transmitted word:: + + sage: err = 2 + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), err) + sage: Chan + Static error rate channel creating 2 errors, of input and output space Vector space of dimension 6 over Finite Field of size 7 + sage: r = Chan.transmit(c) + sage: len((c-r).nonzero_positions()) + 2 + +If you want to learn more on Channels, please refer to section IV +of this tutorial. + +Thanks to our channel, we got a "received word`, ``r``, as a codeword +with errors on it. We can try to correct the errors and recover the +original codeword:: + + sage: c_dec = C.decode_to_code(r) + sage: c_dec == c + True + +Perhaps we want the original *message* back rather than the codeword. +All we have to do then is to *unencode* it back to the message space:: + + sage: m_unenc = C.unencode(c_dec) + sage: m_unenc == msg + True + +It is also possible to perform the two previous operations +(correct the errors and recover the original message) in one line, +as illustrated below:: + + sage: m_unenc2 = C.decode_to_message(r) + sage: m_unenc2 == msg + True + + +III. A deeper view of the Encoder and Decoder structure +======================================================= + +In the previous section, we saw that encoding, decoding and unencoding +a vector can be easily done using methods directly on the code object. +These methods are actually shortcuts, added for usability, for when one +does not care more specifically about how encoding and decoding takes place. +At some point, however, one might need more control. + +This section will thus go into details on the mechanism +of Encoders and Decoders. + +At the core, the three mentioned operations are handled by +:class:`sage.coding.encoder.Encoder` and +:class:`sage.coding.decoder.Decoder`. +These objects possess their own methods to +operate on words. When one calls (as seen above):: + + C.encode(msg) + +one actually calls the method :meth:`sage.coding.encoder.Encoder.encode` +on the default encoder of ``C``. +Every code object possess a list of encoders and decoders it can use. +Let us see how one can explore this:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12, GF(59).list()[1:41]) + sage: C.encoders_available() + ['EvaluationPolynomial', 'EvaluationVector'] + sage: C.decoders_available() + ['Syndrome', 'NearestNeighbor'] + +We got a list of the available encoders and decoders for our GRS code. +Rather than using the default ones as we did before, +we can now ask for specific encoder and decoder:: + + sage: Evect = C.encoder("EvaluationVector") + sage: Evect + Evaluation vector-style encoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + sage: type(Evect) + + sage: msg = random_vector(GF(59), C.dimension()) #random + sage: c = Evect.encode(msg) + sage: NN = C.decoder("NearestNeighbor") + sage: NN + Nearest neighbor decoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + +Calling:: + + C.encoder(encoder_name) + +is actually a short-hand for constructing the encoder manually, +by calling the constructor for +:class:`sage.coding.grs.EncoderGRSEvaluationVector` yourself. +If you don't supply ``encoder_name`` to +:meth:`sage.coding.linear_code.AbstractLinearCode.encoder` +you get the default encoder for the code. +:meth:`sage.coding.linear_code.AbstractLinearCode.encoder` +also has an important side-effect: **it caches the constructed encoder** +before returning it. This means that each time one will access the same +EvaluationVector encoder for C, which saves construction time. + +All the above things are similar for Decoders. +This reinforces that Encoders and Decoders are rarely constructed but used +many times, which allows them to perform expensive precomputation +at construction or first use, for the benefit of future use. + +This gives a good idea of how the elements work internally. +Let us now go a bit more into details on specific points. + +III.1 Message spaces +--------------------- + +The point of an Encoder is to encode messages into the code. +These messages are often just vectors over the base field of the code +and whose length match code's dimension. +But it could be anything: vectors over other fields, polynomials, or even +something quite different. +Therefore, each Encoder has a :meth:`sage.coding.encoder.Encoder.message_space`. +For instance, we saw earlier that our GRS code has two possible encoders; +let us investigate the one we left behind in the part just before:: + + sage: Epoly = C.encoder("EvaluationPolynomial") + sage: Epoly + Evaluation polynomial-style encoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + sage: Epoly.message_space() + Univariate Polynomial Ring in x over Finite Field of size 59 + sage: msg_p = Epoly.message_space().random_element(degree=C.dimension()-1); msg_p #random + 31*x^11 + 49*x^10 + 56*x^9 + 31*x^8 + 36*x^6 + 58*x^5 + 9*x^4 + 17*x^3 + 29*x^2 + 50*x + 46 + +``Epoly`` reflects that GRS codes are often constructed +as evaluations of polynomials, and that a natural way to consider +their messages is as polynomials of degree at most :math:`k-1`, +where :math:`k` is the dimension of the code. +Notice that the message space of ``Epoly`` is all univariate polynomials: +``message_space`` is the ambient space of the messages, and sometimes an Encoder +demands that the messages are actually picked from a subspace hereof. + +The default encoder for a code is always one with vector behaviour, +so when we call +:meth:`sage.coding.linear_code.AbstractLinearCode.decode_to_message` or +:meth:`sage.coding.linear_code.AbstractLinearCode.unencode` on the code itself, +as illustrated on the first example, this will always return +vectors whose length are the dimension of the code. + + +III.2 Generator matrices +------------------------ + +Whenever the message space of an Encoder is a vector space +and it encodes using a linear map, then the Encoder will +possess a generator matrix (note that this notion does not +make sense for other types of encoders), which specifies that linear map. + +Generator matrices have been placed on Encoder objects since a code +has many generator matrices, and each of these will encode messages differently. +One will also find +:meth:`sage.coding.linear_code.AbstractLinearCode.generator_matrix` +on code objects, but this is again simply a convenience method which forwards +the query to the default encoder. + +Let us see this in Sage, using the first encoder we constructed:: + + sage: Evect.message_space() + Vector space of dimension 12 over Finite Field of size 59 + sage: G = Evect.generator_matrix() + sage: G == C.generator_matrix() + True + +III.3 Decoders and introspection +-------------------------------- + +Each Decoder uses his own decoding algorithm, amd these decoding algorithms +can have quite different behaviours: +some might return a list of codewords, while some just return one codeword, +some cannot decode more than half the minimum distance, while some can etc... + +When it comes to benchmarking, it can be useful to sort the decoders in order +to compare decoders which share properties. And in any case, the user might like +to know the properties of a given decoder. + +We call these properties *types*. One can access these for any decoders as +follows:: + + sage: C = codes.RandomLinearCode(7, 4, GF(2)) + sage: D = C.decoder('NearestNeighbor') + sage: D.decoder_type() + {'always-succeed', 'complete', 'hard-decision', 'unique'} + +IV. A deeper look at channels +============================= + +In Section II, we briefly introduced the Channel objects as +a way to put errors in a word. +In this section, we will look deeper at their functionality and +introduce a second Channel. + + .. NOTE:: + + Once again, we chose a specific class as a running example through + all this section, as we do not want to make an exhaustive catalog + of all channels. + If one wants to get this list, one can access it by typing:: + + channels. + + in Sage. + +Consider again the +:meth:`sage.coding.channel_constructions.ChannelStaticErrorRate` from before. +This is a channel that places errors in the transmitted vector +but within controlled boundaries. +We can describe these boundaries in two ways: + +- The first one was illustrated in Section II and consists in passing + an integer, as shown below:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12) + sage: t = 14 + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), t) + sage: Chan + Static error rate channel creating 14 errors, of input and output space Vector space of dimension 40 over Finite Field of size 59 + +- We can also pass a tuple of two integers, the first smaller than the second. + Then each time a word is transmitted, a random number of errors + between these two integers will be added:: + + sage: t = (1, 14) + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), t) + sage: Chan + Static error rate channel creating between 1 and 14 errors, of input and output space Vector space of dimension 40 over Finite Field of size 59 + +We already know that a channel has a +:meth:`sage.coding.channel_constuctions.Channel.transmit` method which will +perform transmission over the channel; in this case it will return +the transmitted word with some errors in it. +This method will always check if the provided word belongs to +the input space of the channel. +In a case one is absolutely certain that one's word is in the input space, +one might want to avoid this check, which is time consuming - especially +if one is simulating millions of transmissions. +For this usage there is +:meth:`sage.coding.channel_constructions.Channel.transmit_unsafe` which does +the same as +:meth:`sage.coding.channel_constuctions.Channel.transmit` +but without checking the input, as illustrated thereafter:: + + sage: c = C.random_element() + sage: c in C + True + sage: c_trans = Chan.transmit_unsafe(c) + sage: c_trans in C + False + +Note there exists a useful shortcut for +:meth:`sage.coding.channel_constuctions.Channel.transmit` :: + + sage: r = Chan(c) + sage: r in C + False + +A channel for errors and erasures +--------------------------------- + +Let us introduce a new Channel object which adds errors and erasures. +When it transmits a word, it both adds some errors +as well as it erases some positions:: + + sage: Chan = channels.ErrorErasureChannel(C.ambient_space(), 3, 4) + sage: Chan + Error-and-erasure channel creating 3 errors and 4 erasures of input space Vector space of dimension 40 over Finite Field of size 59 and output space The Cartesian product of (Vector space of dimension 40 over Finite Field of size 59, Vector space of dimension 40 over Finite Field of size 2) + + +The first parameter is the input space of the channel. +The next two are (respectively) the number of errors +and the number or erasures. +Each of these can be tuples too, just as it was with +:class:`sage.coding.channel_constructions.StaticErrorRateChannel`. +As opposed to this channel though, the output of +:class:`sage.coding.channel_constructions.ErrorErasureChannel` +is not the same as its input space, i.e. the ambient space of C. +Rather, it will return two vectors: the first is the transmitted word +with the errors added and erased positions set to 0. +The second one is the erasure vector over which has 1 on the erased positions +and 0 elsewhere. +This is reflected in :meth:`sage.coding.channel_constructions.output_space`:: + + sage: C = codes.RandomLinearCode(10, 5, GF(7)) + sage: Chan.output_space() + The Cartesian product of (Vector space of dimension 40 over Finite Field of size 59, Vector space of dimension 40 over Finite Field of size 2) + sage: Chan(c) # random + ((0, 3, 6, 4, 4, 0, 1, 0, 0, 1), + (1, 0, 0, 0, 0, 1, 0, 0, 1, 0)) + +Note it is guaranteed by construction that errors and erasures +will never overlap, so when you ask for ``e`` errors and ``t`` erasures, +you will always receive a vector with ``e`` errors and ``t`` erased positions. + +V. Conclusion - Afterword +========================= + +This last section concludes our tutorial on coding theory. + +After reading this, you should know enough to create +and manipulate codes in Sage! + +We did not illustrate all the content of the library in this tutorial. +For instance, we did not mention how Sage manages bounds on codes. + +All objects, constructions and methods related to coding theory are hidden +under the prefix ``codes`` in Sage. + +For instance, it is possible to find all encoders you can build by typing:: + + codes.encoders. + +So, if you are looking for a specific object related to code, you should always +type:: + + codes. + +and check if there's a subcategory which matches your needs. + +Despite all the hard work we put on it, there's always much to do! + +Maybe at some point you might want to create you own codes for Sage. +If it's the case and if you don't know how to do that, don't panic! +We also wrote a tutorial for this specific case, which you can find here: +:ref:`structures_in_coding_theory`. diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index f90f045d9db..939a5247398 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -468,7 +468,7 @@ And indeed, ``MS2`` has *more* methods than ``MS1``:: sage: len([s for s in dir(MS1) if inspect.ismethod(getattr(MS1,s,None))]) 59 sage: len([s for s in dir(MS2) if inspect.ismethod(getattr(MS2,s,None))]) - 87 + 89 This is because the class of ``MS2`` also inherits from the parent class for algebras:: diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/modular_forms_and_hecke_operators.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/modular_forms_and_hecke_operators.rst index f908b03c883..681f38cb3d9 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/modular_forms_and_hecke_operators.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/modular_forms_and_hecke_operators.rst @@ -167,7 +167,7 @@ dimension formulas. sage: a = [dimension_cusp_forms(Gamma0(N),2) for N in [1..25]]; a [0, 0, ..., 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 2, 2, 1, 0] sage: oeis(a) # optional - internet - 0: A001617: Genus of modular group GAMMA_0 (n). Or, genus of modular curve X_0(n). + 0: A001617: Genus of modular group Gamma_0(n). Or, genus of modular curve X_0(n). Sage doesn't have simple formulas for dimensions of spaces of modular forms of weight :math:`1`, since such formulas perhaps do diff --git a/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst b/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst index af5ed9821dd..847d838240e 100644 --- a/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst +++ b/src/doc/en/thematic_tutorials/lie/weyl_character_ring.rst @@ -366,7 +366,7 @@ SL versus GL ------------ Sage takes the weight space for type ``['A',r]`` to be `r+1` -dimensional. As a biproduct, if you create the Weyl character ring +dimensional. As a by-product, if you create the Weyl character ring with the command:: sage: A2 = WeylCharacterRing("A2") diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 17ad5aee2af..1335a160972 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -526,7 +526,7 @@ -(IBAction)terminalSessionPromptForFile:(id)sender{ // process the files. NSString * base_dir = nil; if (neverOpenedFileBrowser) { - base_dir = [NSString stringWithFormat:@"%@/../devel/sage/sage",sageBinary]; + base_dir = [NSString stringWithFormat:@"%@/../src/sage",sageBinary]; neverOpenedFileBrowser=NO; } // If they supply files, then run the command diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index 220a3ee5e1d..ce1e6e0fba7 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -12,44 +12,23 @@ if [ $# -lt 2 ]; then exit 1; fi -# Ensure that we have the original sage and therefore the directory it's in should be SAGE_ROOT (right?) -SAGE_EXECUTABLE=`readlink -n "$1" 2> /dev/null` || \ SAGE_EXECUTABLE="$1" - -# Strip the last section off -- this should be SAGE_ROOT. We could -# just call `sage --root`, but I'm afraid this may not work (if there -# are spaces). But if sageBinary is not set, the "sage" gets passed -# in, and we have no choice, but to call `sage --root`, and it should -# work in that case (assuming of course that sage is in PATH) -if [ "x$SAGE_EXECUTABLE" = "xsage" ]; then - SAGE_ROOT=`sage --root` -else - SAGE_ROOT="${SAGE_EXECUTABLE%/*}/" -fi SAGE_LOG="$2" -# Work around spaces in the path (and perhaps cut down on location changes). -# We delete and recreate the symlink every time to ensure we are in the right place -rm -f /tmp/sage-mac-app -ln -s "$SAGE_ROOT" /tmp/sage-mac-app +# Read environment variables +cd $(dirname $SAGE_EXECUTABLE) +source local/bin/sage-env -# Move to a fake SAGE_ROOT -- without spaces -cd /tmp/sage-mac-app || exit 1 - -# Set SAGE_ROOT and all the other environment variables by sourcing -# sage-env. In order to support older versions 4.x of Sage, we try both -# spkg/bin/sage-env and local/bin/sage-env. -echo Setting environment variables >> "$SAGE_LOG" -{ . spkg/bin/sage-env || . local/bin/sage-env; } >> "$SAGE_LOG" 2>> "$SAGE_LOG" || exit 1 -export SAGE_ROOT # Mac OS X app bundles are *intended* to be moved around, and/or given away # So always run first the respective script handling this -# (This should also catch Intel vs. PPC or 32Bit vs. 64Bit conflicts - untested) +# This should also catch Intel vs. PPC or 32Bit vs. 64Bit conflicts echo Checking install location >> "$SAGE_LOG" -./local/bin/sage-location >> "$SAGE_LOG" 2>> "$SAGE_LOG" || exit 1 +# TODO: If relocate-once.py is present run it with some sort of progress +# display, e.g. in a terminal + -echo Checking existence of notebook directory >> "$SAGE_LOG" +echo Checking existence of SageNB directory >> "$SAGE_LOG" if [ -e $DOT_SAGE/sage_notebook.sagenb/users.pickle ]; then echo Starting Notebook >> "$SAGE_LOG" # $3 is not quoted because it comes as one argument from the app, @@ -73,7 +52,7 @@ if [ $? != 0 ]; then sage-native-execute osascript \ -e 'tell app "Terminal"' \ -e ' activate' \ - -e " do script \"'$SAGE_ROOT'/sage --notebook\"" \ + -e " do script \"'$SAGE_EXECUTABLE' --notebook\"" \ -e 'end' # We don't include $3 here since this should only happen the first time # they run it, and this way we don't have to worry about quoting it. diff --git a/src/module_list.py b/src/module_list.py index 66e432267ee..d49463140cb 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -656,6 +656,14 @@ def uname_specific(name, value, alternative): Extension('sage.libs.mpmath.ext_libmp', sources = ["sage/libs/mpmath/ext_libmp.pyx"]), + ################################### + ## + ## sage.libs.arb + ## + ################################### + + Extension('*', ["sage/libs/arb/*.pyx"]), + ################################### ## ## sage.libs.eclib @@ -903,6 +911,11 @@ def uname_specific(name, value, alternative): Extension('sage.matrix.matrix_window', sources = ['sage/matrix/matrix_window.pyx']), + OptionalExtension("sage.matrix.matrix_gfpn_dense", + sources = ['sage/matrix/matrix_gfpn_dense.pyx'], + libraries = ['mtx'], + package = 'meataxe'), + Extension('sage.matrix.misc', sources = ['sage/matrix/misc.pyx'], libraries=['mpfr']), @@ -1429,6 +1442,11 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.cyclotomic', sources = ['sage/rings/polynomial/cyclotomic.pyx']), + Extension('sage.rings.polynomial.evaluation', + libraries = ["flint", "gmp", "ntl", "mpfr", "mpfi"], + sources = ['sage/rings/polynomial/evaluation.pyx'], + language = 'c++'), + Extension('sage.rings.polynomial.laurent_polynomial', sources = ['sage/rings/polynomial/laurent_polynomial.pyx']), @@ -1531,6 +1549,7 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.symmetric_reduction', sources = ['sage/rings/polynomial/symmetric_reduction.pyx']), + ################################ ## ## sage.rings.semirings @@ -1678,7 +1697,3 @@ def uname_specific(name, value, alternative): Extension('sage.tests.cython', sources = ['sage/tests/cython.pyx']), ] - -# Add auto-generated modules -import sage_setup.autogen.interpreters -ext_modules += sage_setup.autogen.interpreters.modules diff --git a/src/sage/algebras/affine_nil_temperley_lieb.py b/src/sage/algebras/affine_nil_temperley_lieb.py index 52e43ec58b8..4bc0cde250b 100644 --- a/src/sage/algebras/affine_nil_temperley_lieb.py +++ b/src/sage/algebras/affine_nil_temperley_lieb.py @@ -41,7 +41,7 @@ class AffineNilTemperleyLiebTypeA(CombinatorialFreeModule): sage: a[0]*a[3]*a[0] 0 sage: A.an_element() - 2*a0 + 3*a0*a1 + 1 + a0*a1*a2*a3 + 2*a0 + 1 + 3*a1 + a0*a1*a2*a3 """ def __init__(self, n, R = ZZ, prefix = 'a'): diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 06910534633..2eee01891de 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -25,11 +25,13 @@ - :class:`algebras.Incidence ` - :class:`algebras.IwahoriHecke ` -- :class:`algebras.Mobius ` +- :class:`algebras.Moebius ` - :class:`algebras.Jordan ` - :class:`algebras.NilCoxeter ` +- :class:`algebras.OrlikSolomon + ` - :func:`algebras.Quaternion ` - :class:`algebras.Schur ` @@ -53,11 +55,12 @@ lazy_import('sage.algebras.free_zinbiel_algebra', 'FreeZinbielAlgebra', 'FreeZinbiel') lazy_import('sage.algebras.hall_algebra', 'HallAlgebra', 'Hall') lazy_import('sage.algebras.jordan_algebra', 'JordanAlgebra', 'Jordan') +lazy_import('sage.algebras.orlik_solomon', 'OrlikSolomonAlgebra', 'OrlikSolomon') lazy_import('sage.algebras.shuffle_algebra', 'ShuffleAlgebra', 'Shuffle') lazy_import('sage.algebras.schur_algebra', 'SchurAlgebra', 'Schur') lazy_import('sage.algebras.commutative_dga', 'GradedCommutativeAlgebra', 'GradedCommutative') lazy_import('sage.combinat.posets.incidence_algebras', 'IncidenceAlgebra', 'Incidence') -lazy_import('sage.combinat.posets.mobius_algebra', 'MobiusAlgebra', 'Mobius') +lazy_import('sage.combinat.posets.moebius_algebra', 'MoebiusAlgebra', 'Moebius') lazy_import('sage.combinat.free_prelie_algebra', 'FreePreLieAlgebra', 'FreePreLie') del lazy_import # We remove the object from here so it doesn't appear under tab completion diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index e4edc04a738..f95d76779a0 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -49,7 +49,6 @@ from sage.algebras.algebra import Algebra from sage.algebras.free_algebra import is_FreeAlgebra from sage.algebras.free_algebra_quotient_element import FreeAlgebraQuotientElement -from sage.structure.parent_gens import ParentWithGens from sage.structure.unique_representation import UniqueRepresentation class FreeAlgebraQuotient(UniqueRepresentation, Algebra, object): diff --git a/src/sage/algebras/nil_coxeter_algebra.py b/src/sage/algebras/nil_coxeter_algebra.py index 2411c5a48e3..b84bae29dc1 100644 --- a/src/sage/algebras/nil_coxeter_algebra.py +++ b/src/sage/algebras/nil_coxeter_algebra.py @@ -39,7 +39,7 @@ class NilCoxeterAlgebra(IwahoriHeckeAlgebra.T): sage: u2*u1*u2 == u1*u2*u1 True sage: U.an_element() - u[0,1,2,3] + 3*u[0,1] + 2*u[0] + 1 + u[0,1,2,3] + 2*u[0] + 3*u[1] + 1 """ def __init__(self, W, base_ring = QQ, prefix='u'): diff --git a/src/sage/algebras/orlik_solomon.py b/src/sage/algebras/orlik_solomon.py new file mode 100644 index 00000000000..e3d720a9b9d --- /dev/null +++ b/src/sage/algebras/orlik_solomon.py @@ -0,0 +1,452 @@ +r""" +Orlik-Solomon Algebras +""" + +#***************************************************************************** +# Copyright (C) 2015 William Slofstra +# Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.combinat.free_module import CombinatorialFreeModule +from sage.categories.algebras import Algebras +from sage.sets.family import Family + +class OrlikSolomonAlgebra(CombinatorialFreeModule): + r""" + An Orlik-Solomon algebra. + + Let `R` be a commutative ring. Let `M` be a matroid with ground set + `X`. Let `C(M)` denote the set of circuits of `M`. Let `E` denote + the exterior algera over `R` generated by `\{ e_x \mid x \in X \}`. + The *Orlik-Solomon ideal* `J(M)` is the ideal of `E` generated by + + .. MATH:: + + \partial e_S := \sum_{i=1}^t (-1)^{i-1} e_{j_1} \wedge e_{j_2} + \wedge \cdots \wedge \widehat{e}_{j_i} \wedge \cdots \wedge e_{j_t} + + for all `S = \left\{ j_1 < j_2 < \cdots < j_t \right\} \in C(M)`, + where `\widehat{e}_{j_i}` means that the term `e_{j_i}` is being + omitted. The notation `\partial e_S` is not a coincidence, as + `\partial e_S` is actually the image of + `e_S := e_{j_1} \wedge e_{j_2} \wedge \cdots \wedge e_{j_t}` under the + unique derivation `\partial` of `E` which sends all `e_x` to `1`. + + It is easy to see that `\partial e_S \in J(M)` not only for circuits + `S`, but also for any dependent set `S` of `M`. Moreover, every + dependent set `S` of `M` satisfies `e_S \in J(M)`. + + The *Orlik-Solomon algebra* `A(M)` is the quotient `E / J(M)`. This is + a graded finite-dimensional skew-commutative `R`-algebra. Fix + some ordering on `X`; then, the NBC sets of `M` (that is, the subsets + of `X` containing no broken circuit of `M`) form a basis of `A(M)`. + (Here, a *broken circuit* of `M` is defined to be the result of + removing the smallest element from a circuit of `M`.) + + In the current implementation, the basis of `A(M)` is indexed by the + NBC sets, which are implemented as frozensets. + + INPUT: + + - ``R`` -- the base ring + - ``M`` -- the defining matroid + - ``ordering`` -- (optional) an ordering of the ground set + + EXAMPLES: + + We create the Orlik-Solomon algebra of the uniform matroid `U(3, 4)` + and do some basic computations:: + + sage: M = matroids.Uniform(3, 4) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.dimension() + 14 + sage: G = OS.algebra_generators() + sage: M.broken_circuits() + frozenset({frozenset({1, 2, 3})}) + sage: G[1] * G[2] * G[3] + OS{0, 1, 2} - OS{0, 1, 3} + OS{0, 2, 3} + + REFERENCES: + + .. [CE01] Raul Cordovil and Gwihen Etienne. + *A note on the Orlik-Solomon algebra*. + Europ. J. Combinatorics. **22** (2001). pp. 165-170. + http://www.math.ist.utl.pt/~rcordov/Ce.pdf + + - :wikipedia:`Arrangement_of_hyperplanes#The_Orlik-Solomon_algebra` + """ + @staticmethod + def __classcall_private__(cls, R, M, ordering=None): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: from sage.algebras.orlik_solomon import OrlikSolomonAlgebra + sage: OS1 = OrlikSolomonAlgebra(QQ, M) + sage: OS2 = OrlikSolomonAlgebra(QQ, M, ordering=(0,1,2,3,4,5)) + sage: OS3 = OrlikSolomonAlgebra(QQ, M, ordering=[0,1,2,3,4,5]) + sage: OS1 is OS2 and OS2 is OS3 + True + """ + if ordering is None: + ordering = sorted(M.groundset()) + return super(OrlikSolomonAlgebra, cls).__classcall__(cls, R, M, tuple(ordering)) + + def __init__(self, R, M, ordering=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: TestSuite(OS).run() + + We check on the matroid associated to the graph with 3 vertices and + 2 edges between each vertex:: + + sage: G = Graph([[1,2],[1,2],[2,3],[2,3],[1,3],[1,3]], multiedges=True) + sage: M = Matroid(G) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: elts = OS.some_elements() + list(OS.algebra_generators()) + sage: TestSuite(OS).run(elements=elts) + """ + self._M = M + self._sorting = {x:i for i,x in enumerate(ordering)} + + # set up the dictionary of broken circuits + self._broken_circuits = dict() + for c in self._M.circuits(): + L = sorted(c, key=lambda x: self._sorting[x]) + self._broken_circuits[frozenset(L[1:])] = L[0] + + cat = Algebras(R).FiniteDimensional().WithBasis().Graded() + CombinatorialFreeModule.__init__(self, R, M.no_broken_circuits_sets(ordering), + prefix='OS', bracket='{', + generator_cmp=self._cmp_term, + category=cat) + + def _cmp_term(self, x, y): + """ + Compare the terms indexed by ``x`` and ``y``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS._cmp_term(frozenset({1, 2}), frozenset({1, 4})) + -1 + sage: OS._cmp_term(frozenset({0, 1, 2}), frozenset({1, 4})) + -1 + sage: OS._cmp_term(frozenset({}), frozenset({1, 4})) + 1 + """ + c = cmp(len(x), len(y)) + if c != 0: + return -c + return cmp(sorted(x), sorted(y)) + + def _repr_term(self, m): + """ + Return a string representation of the basis element indexed by `m`. + + EXAMPLES:: + + sage: M = matroids.Uniform(3, 4) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS._repr_term(frozenset([0])) + 'OS{0}' + """ + return "OS{{{}}}".format(str(list(m))[1:-1]) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: M.orlik_solomon_algebra(QQ) + Orlik-Solomon algebra of Wheel(3): Regular matroid of rank 3 + on 6 elements with 16 bases + """ + return "Orlik-Solomon algebra of {}".format(self._M) + + @cached_method + def one_basis(self): + """ + Return the index of the basis element corresponding to `1` + in ``self``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.one_basis() == frozenset([]) + True + """ + return frozenset({}) + + @cached_method + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + These form a family indexed by the ground set `X` of `M`. For + each `x \in X`, the `x`-th element is `e_x`. + + EXAMPLES:: + + sage: M = matroids.Uniform(2, 2) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.algebra_generators() + Finite family {0: OS{0}, 1: OS{1}} + + sage: M = matroids.Uniform(1, 2) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.algebra_generators() + Finite family {0: OS{0}, 1: OS{0}} + + sage: M = matroids.Uniform(1, 3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.algebra_generators() + Finite family {0: OS{0}, 1: OS{0}, 2: OS{0}} + """ + return Family(sorted(self._M.groundset()), + lambda i: self.subset_image(frozenset([i]))) + + @cached_method + def product_on_basis(self, a, b): + """ + Return the product in ``self`` of the basis elements + indexed by ``a`` and ``b``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.product_on_basis(frozenset([2]), frozenset([3,4])) + OS{0, 1, 2} - OS{0, 1, 4} + OS{0, 2, 3} + OS{0, 3, 4} + + :: + + sage: G = OS.algebra_generators() + sage: prod(G) + 0 + sage: G[2] * G[4] + -OS{1, 2} + OS{1, 4} + sage: G[3] * G[4] * G[2] + OS{0, 1, 2} - OS{0, 1, 4} + OS{0, 2, 3} + OS{0, 3, 4} + sage: G[2] * G[3] * G[4] + OS{0, 1, 2} - OS{0, 1, 4} + OS{0, 2, 3} + OS{0, 3, 4} + sage: G[3] * G[2] * G[4] + -OS{0, 1, 2} + OS{0, 1, 4} - OS{0, 2, 3} - OS{0, 3, 4} + + TESTS: + + Let us check that `e_{s_1} e_{s_2} \cdots e_{s_k} = e_S` for any + subset `S = \{ s_1 < s_2 < \cdots < s_k \}` of the ground set:: + + sage: G = Graph([[1,2],[1,2],[2,3],[3,4],[4,2]], multiedges=True) + sage: M = Matroid(G) + sage: E = M.groundset_list() + sage: OS = M.orlik_solomon_algebra(ZZ) + sage: G = OS.algebra_generators() + sage: import itertools + sage: def test_prod(F): + ....: LHS = OS.subset_image(frozenset(F)) + ....: RHS = OS.prod([G[i] for i in sorted(F)]) + ....: return LHS == RHS + sage: all( test_prod(F) for k in range(len(E)+1) + ....: for F in itertools.combinations(E, k) ) + True + """ + if not a: + return self.basis()[b] + if not b: + return self.basis()[a] + + if not a.isdisjoint(b): + return self.zero() + + R = self.base_ring() + # since a is disjoint from b, we can just multiply the generator + if len(a) == 1: + i = list(a)[0] + # insert i into nbc, keeping track of sign in coeff + ns = b.union({i}) + ns_sorted = sorted(ns, key=lambda x: self._sorting[x]) + coeff = (-1)**ns_sorted.index(i) + + return R(coeff) * self.subset_image(ns) + + # r is the accumalator + # we reverse a in the product, so add a sign + # note that l>=2 here + if len(a) % 4 < 2: + sign = R.one() + else: + sign = - R.one() + r = self._from_dict({b: sign}, remove_zeros=False) + + # now do the multiplication generator by generator + G = self.algebra_generators() + for i in sorted(a, key=lambda x: self._sorting[x]): + r = G[i] * r + + return r + + @cached_method + def subset_image(self, S): + """ + Return the element `e_S` of `A(M)` (``== self``) corresponding to + a subset `S` of the ground set of `M`. + + INPUT: + + - ``S`` -- a frozenset which is a subset of the ground set of `M` + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: BC = sorted(M.broken_circuits(), key=sorted) + sage: for bc in BC: (sorted(bc), OS.subset_image(bc)) + ([1, 3], -OS{0, 1} + OS{0, 3}) + ([1, 4, 5], OS{0, 1, 4} - OS{0, 1, 5} - OS{0, 3, 4} + OS{0, 3, 5}) + ([2, 3, 4], OS{0, 1, 2} - OS{0, 1, 4} + OS{0, 2, 3} + OS{0, 3, 4}) + ([2, 3, 5], OS{0, 2, 3} + OS{0, 3, 5}) + ([2, 4], -OS{1, 2} + OS{1, 4}) + ([2, 5], -OS{0, 2} + OS{0, 5}) + ([4, 5], -OS{3, 4} + OS{3, 5}) + + sage: M4 = matroids.CompleteGraphic(4) + sage: OS = M4.orlik_solomon_algebra(QQ) + sage: OS.subset_image(frozenset({2,3,4})) + OS{0, 2, 3} + OS{0, 3, 4} + + An example of a custom ordering:: + + sage: G = Graph([[3, 4], [4, 1], [1, 2], [2, 3], [3, 5], [5, 6], [6, 3]]) + sage: M = Matroid(G) + sage: s = [(5, 6), (1, 2), (3, 5), (2, 3), (1, 4), (3, 6), (3, 4)] + sage: sorted([sorted(c) for c in M.circuits()]) + [[(1, 2), (1, 4), (2, 3), (3, 4)], + [(3, 5), (3, 6), (5, 6)]] + sage: OS = M.orlik_solomon_algebra(QQ, ordering=s) + sage: OS.subset_image(frozenset([])) + OS{} + sage: OS.subset_image(frozenset([(1,2),(3,4),(1,4),(2,3)])) + 0 + sage: OS.subset_image(frozenset([(2,3),(1,2),(3,4)])) + OS{(1, 2), (3, 4), (2, 3)} + sage: OS.subset_image(frozenset([(1,4),(3,4),(2,3),(3,6),(5,6)])) + -OS{(1, 2), (5, 6), (2, 3), (1, 4), (3, 6)} + + OS{(1, 2), (5, 6), (3, 4), (1, 4), (3, 6)} + - OS{(1, 2), (5, 6), (3, 4), (2, 3), (3, 6)} + sage: OS.subset_image(frozenset([(1,4),(3,4),(2,3),(3,6),(3,5)])) + OS{(1, 2), (5, 6), (2, 3), (1, 4), (3, 5)} + - OS{(1, 2), (5, 6), (2, 3), (1, 4), (3, 6)} + + OS{(1, 2), (5, 6), (3, 4), (1, 4), (3, 5)} + + OS{(1, 2), (5, 6), (3, 4), (1, 4), (3, 6)} + - OS{(1, 2), (5, 6), (3, 4), (2, 3), (3, 5)} + - OS{(1, 2), (5, 6), (3, 4), (2, 3), (3, 6)} + + TESTS:: + + sage: G = Graph([[1,2],[1,2],[2,3],[2,3],[1,3],[1,3]], multiedges=True) + sage: M = Matroid(G) + sage: sorted([sorted(c) for c in M.circuits()]) + [[0, 1], [0, 2, 4], [0, 2, 5], [0, 3, 4], + [0, 3, 5], [1, 2, 4], [1, 2, 5], [1, 3, 4], + [1, 3, 5], [2, 3], [4, 5]] + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.subset_image(frozenset([])) + OS{} + sage: OS.subset_image(frozenset([1, 2, 3])) + 0 + sage: OS.subset_image(frozenset([1, 3, 5])) + 0 + sage: OS.subset_image(frozenset([1, 2])) + OS{0, 2} + sage: OS.subset_image(frozenset([3, 4])) + -OS{0, 2} + OS{0, 4} + sage: OS.subset_image(frozenset([1, 5])) + OS{0, 4} + + sage: G = Graph([[1,2],[1,2],[2,3],[3,4],[4,2]], multiedges=True) + sage: M = Matroid(G) + sage: sorted([sorted(c) for c in M.circuits()]) + [[0, 1], [2, 3, 4]] + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.subset_image(frozenset([])) + OS{} + sage: OS.subset_image(frozenset([1, 3, 4])) + -OS{0, 2, 3} + OS{0, 2, 4} + + We check on a non-standard ordering:: + + sage: M = matroids.Wheel(3) + sage: o = [5,4,3,2,1,0] + sage: OS = M.orlik_solomon_algebra(QQ, ordering=o) + sage: BC = sorted(M.broken_circuits(ordering=o), key=sorted) + sage: for bc in BC: (sorted(bc), OS.subset_image(bc)) + ([0, 1], OS{0, 3} - OS{1, 3}) + ([0, 1, 4], OS{0, 3, 5} - OS{0, 4, 5} - OS{1, 3, 5} + OS{1, 4, 5}) + ([0, 2], OS{0, 5} - OS{2, 5}) + ([0, 2, 3], -OS{0, 3, 5} + OS{2, 3, 5}) + ([1, 2], OS{1, 4} - OS{2, 4}) + ([1, 2, 3], -OS{1, 3, 5} + OS{1, 4, 5} + OS{2, 3, 5} - OS{2, 4, 5}) + ([3, 4], OS{3, 5} - OS{4, 5}) + """ + if not isinstance(S, frozenset): + raise ValueError("S needs to be a frozenset") + for bc in self._broken_circuits: + if bc.issubset(S): + i = self._broken_circuits[bc] + if i in S: + # ``S`` contains not just a broken circuit, but an + # actual circuit; then `e_S = 0`. + return self.zero() + coeff = self.base_ring().one() + # Now, reduce ``S``, and build the result ``r``: + r = self.zero() + switch = False + Si = S.union({i}) + Ss = sorted(Si, key=lambda x: self._sorting[x]) + for j in Ss: + if j in bc: + r += coeff * self.subset_image(Si.difference({j})) + if switch: + coeff *= -1 + if j == i: + switch = True + return r + else: # So ``S`` is an NBC set. + return self.monomial(S) + + def degree_on_basis(self, m): + """ + Return the degree of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: M = matroids.Wheel(3) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS.degree_on_basis(frozenset([1])) + 1 + sage: OS.degree_on_basis(frozenset([0, 2, 3])) + 3 + """ + return len(m) + diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 331b02ab6b8..4729da631eb 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -38,7 +38,7 @@ from sage.rings.all import RR, Integer from sage.rings.integer_ring import ZZ from sage.rings.rational import Rational -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.ring import Algebra from sage.rings.ideal import Ideal_fractional diff --git a/src/sage/all.py b/src/sage/all.py index 658a3451a36..297d7d03131 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -174,6 +174,8 @@ from sage.game_theory.all import * +from sage.manifolds.all import * + # Lazily import notebook functions and interacts (#15335) lazy_import('sagenb.notebook.notebook_object', 'notebook') lazy_import('sagenb.notebook.notebook_object', 'inotebook') diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 6690669d8ac..7ed60d5e9fb 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Miscellaneous arithmetic functions """ @@ -259,6 +260,7 @@ def bernoulli(n, algorithm='default', num_threads=1): - ``'default'`` -- use 'flint' for n <= 300000, and 'bernmm' otherwise (this is just a heuristic, and not guaranteed to be optimal on all hardware) + - ``'arb'`` -- use the arb library - ``'flint'`` -- use the FLINT library - ``'pari'`` -- use the PARI C library - ``'gap'`` -- use GAP @@ -278,6 +280,8 @@ def bernoulli(n, algorithm='default', num_threads=1): We demonstrate each of the alternative algorithms:: + sage: bernoulli(12, algorithm='arb') + -691/2730 sage: bernoulli(12, algorithm='flint') -691/2730 sage: bernoulli(12, algorithm='gap') @@ -295,7 +299,7 @@ def bernoulli(n, algorithm='default', num_threads=1): TESTS:: - sage: algs = ['gap','gp','pari','bernmm','flint'] + sage: algs = ['arb','gap','gp','pari','bernmm','flint'] sage: test_list = [ZZ.random_element(2, 2255) for _ in range(500)] sage: vals = [[bernoulli(i,algorithm = j) for j in algs] for i in test_list] # long time (up to 21s on sage.math, 2011) sage: union([len(union(x))==1 for x in vals]) # long time (depends on previous line) @@ -315,7 +319,10 @@ def bernoulli(n, algorithm='default', num_threads=1): if algorithm == 'default': algorithm = 'flint' if n <= 300000 else 'bernmm' - if algorithm == 'flint': + if algorithm == 'arb': + import sage.libs.arb.arith as arb_arith + return arb_arith.bernoulli(n) + elif algorithm == 'flint': return flint_arith.bernoulli_number(n) elif algorithm == 'pari': x = pari(n).bernfrac() # Use the PARI C library @@ -3819,7 +3826,7 @@ def quadratic_residues(n): class Moebius: r""" - Returns the value of the Moebius function of abs(n), where n is an + Returns the value of the Möbius function of abs(n), where n is an integer. DEFINITION: `\mu(n)` is 0 if `n` is not square @@ -3907,7 +3914,7 @@ def __repr__(self): def plot(self, xmin=0, xmax=50, pointsize=30, rgbcolor=(0,0,1), join=True, **kwds): """ - Plot the Moebius function. + Plot the Möbius function. INPUT: @@ -3941,9 +3948,9 @@ def plot(self, xmin=0, xmax=50, pointsize=30, rgbcolor=(0,0,1), join=True, def range(self, start, stop=None, step=None): """ - Return the Moebius function evaluated at the given range of values, + Return the Möbius function evaluated at the given range of values, i.e., the image of the list range(start, stop, step) under the - Mobius function. + Möbius function. This is much faster than directly computing all these values with a list comprehension. diff --git a/src/sage/categories/commutative_algebra_ideals.py b/src/sage/categories/commutative_algebra_ideals.py index 9a3ca918ced..cb21e14c22b 100644 --- a/src/sage/categories/commutative_algebra_ideals.py +++ b/src/sage/categories/commutative_algebra_ideals.py @@ -52,8 +52,8 @@ def __init__(self, A): # TODO: replace by ``A in CommutativeAlgebras(*)`` once a # suitable mantra has been implemented for this. from sage.algebras.algebra import is_Algebra - from sage.rings.commutative_ring import is_CommutativeRing - if not (is_Algebra(A) and is_CommutativeRing(A)): + from sage.rings.ring import CommutativeRing + if not (is_Algebra(A) and isinstance(A, CommutativeRing)): raise TypeError("A (=%s) must be a commutative algebra"%A) Category_in_ambient.__init__(self, A) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index f98faf1f33e..a6e3d32d9f8 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Coxeter Groups """ @@ -210,7 +211,7 @@ def __iter__(self): [ 0 1 0] [ 0 0 1] sage: next(g) - [ 0 -1 2] + [ 1 0 0] [ 1 -1 1] [ 0 0 1] """ @@ -235,7 +236,7 @@ def weak_order_ideal(self, predicate, side ="right", category = None): sage: I.cardinality() 7 sage: list(I) - [(), (1,), (1, 2), (1, 2, 1), (2,), (2, 1), (2, 1, 2)] + [(), (1,), (2,), (1, 2), (2, 1), (1, 2, 1), (2, 1, 2)] We now consider an infinite Coxeter group:: @@ -243,7 +244,7 @@ def weak_order_ideal(self, predicate, side ="right", category = None): sage: I = W.weak_order_ideal(predicate = lambda w: w.length() <= 2) sage: list(iter(I)) [ - [1 0] [-1 2] [ 3 -2] [ 1 0] [-1 2] + [1 0] [-1 2] [ 1 0] [ 3 -2] [-1 2] [0 1], [ 0 1], [ 2 -1], [ 2 -1], [-2 3] ] @@ -262,7 +263,7 @@ def weak_order_ideal(self, predicate, side ="right", category = None): 5 sage: list(I) [ - [1 0] [-1 2] [ 3 -2] [ 1 0] [-1 2] + [1 0] [-1 2] [ 1 0] [ 3 -2] [-1 2] [0 1], [ 0 1], [ 2 -1], [ 2 -1], [-2 3] ] @@ -277,6 +278,15 @@ def weak_order_ideal(self, predicate, side ="right", category = None): roughly Constant Amortized Time and constant memory (taking the operations and size of the generated objects as constants). + + TESTS: + + We iterate over each level (i.e., breadth-first-search in the + search forest), see :trac:`19926`:: + + sage: W = CoxeterGroup(['A',2]) + sage: [x.length() for x in W] + [0, 1, 1, 2, 2, 3] """ from sage.combinat.backtrack import SearchForest def succ(u): @@ -287,15 +297,17 @@ def succ(u): return from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups default_category = FiniteEnumeratedSets() if self in FiniteCoxeterGroups() else EnumeratedSets() - return SearchForest((self.one(),), succ, category = default_category.or_subcategory(category)) + return SearchForest((self.one(),), succ, algorithm='breadth', + category = default_category.or_subcategory(category)) - def grassmannian_elements(self, side = "right"): + def grassmannian_elements(self, side="right"): """ - INPUT: + Return the left or right grassmanian elements of ``self`` + as an enumerated set. - - ``side``: "left" or "right" (default: "right") + INPUT: - Returns the left or right grassmanian elements of self, as an enumerated set + - ``side`` -- (default: ``"right"``) ``"left"`` or ``"right"`` EXAMPLES:: @@ -304,7 +316,9 @@ def grassmannian_elements(self, side = "right"): sage: G.cardinality() 12 sage: G.list() - [(0, 1, 2, 3), (1, 0, 2, 3), (2, 0, 1, 3), (3, 0, 1, 2), (0, 2, 1, 3), (1, 2, 0, 3), (0, 3, 1, 2), (1, 3, 0, 2), (2, 3, 0, 1), (0, 1, 3, 2), (0, 2, 3, 1), (1, 2, 3, 0)] + [(0, 1, 2, 3), (1, 0, 2, 3), (0, 2, 1, 3), (0, 1, 3, 2), + (2, 0, 1, 3), (1, 2, 0, 3), (0, 3, 1, 2), (0, 2, 3, 1), + (3, 0, 1, 2), (1, 3, 0, 2), (1, 2, 3, 0), (2, 3, 0, 1)] sage: sorted(tuple(w.descents()) for w in G) [(), (0,), (0,), (0,), (1,), (1,), (1,), (1,), (1,), (2,), (2,), (2,)] sage: G = S.grassmannian_elements(side = "left") @@ -314,7 +328,8 @@ def grassmannian_elements(self, side = "right"): [(), (0,), (0,), (0,), (1,), (1,), (1,), (1,), (1,), (2,), (2,), (2,)] """ order_side = "left" if side == "right" else "right" - return self.weak_order_ideal(attrcall("is_grassmannian", side = side), side = order_side) + return self.weak_order_ideal(attrcall("is_grassmannian", side=side), + side=order_side) def from_reduced_word(self, word): r""" @@ -1842,7 +1857,7 @@ def bruhat_le(self, other): The implementation uses the equivalent condition that any reduced word for ``other`` contains a reduced word for ``self`` as subword. See Stembridge, A short derivation of - the Mobius function for the Bruhat order. J. Algebraic + the Möbius function for the Bruhat order. J. Algebraic Combin. 25 (2007), no. 2, 141--148, Proposition 1.1. Complexity: `O(l * c)`, where `l` is the minimum of the diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index 5b011e8444e..2242c5f487d 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -48,15 +48,15 @@ class DihedralGroup(UniqueRepresentation, Parent): sage: list(G) [(), - (1,), - (1, 2), - (1, 2, 1), - (1, 2, 1, 2), - (1, 2, 1, 2, 1), - (2,), - (2, 1), - (2, 1, 2), - (2, 1, 2, 1)] + (1,), + (2,), + (1, 2), + (2, 1), + (1, 2, 1), + (2, 1, 2), + (1, 2, 1, 2), + (2, 1, 2, 1), + (1, 2, 1, 2, 1)] This reduced word is unique, except for the longest element where the choosen reduced word is `(1,2,1,2\dots)`:: @@ -229,10 +229,10 @@ def apply_simple_reflection_right(self, i): EXAMPLES:: sage: D5 = FiniteCoxeterGroups().example(5) - sage: [i^2 for i in D5] - [(), (), (1, 2, 1, 2), (), (2, 1), (), (), (2, 1, 2, 1), (), (1, 2)] - sage: [i^5 for i in D5] - [(), (1,), (), (1, 2, 1), (), (1, 2, 1, 2, 1), (2,), (), (2, 1, 2), ()] + sage: [i^2 for i in D5] # indirect doctest + [(), (), (), (1, 2, 1, 2), (2, 1, 2, 1), (), (), (2, 1), (1, 2), ()] + sage: [i^5 for i in D5] # indirect doctest + [(), (1,), (2,), (), (), (1, 2, 1), (2, 1, 2), (), (), (1, 2, 1, 2, 1)] """ from copy import copy reduced_word = copy(self.value) diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 8c332ea803b..30f0d33ed8e 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -68,7 +68,7 @@ class ParentMethods: sage: W.some_elements() [(1,), (2,), (), (1, 2)] sage: list(W) - [(), (1,), (1, 2), (1, 2, 1), (2,), (2, 1)] + [(), (1,), (2,), (1, 2), (2, 1), (1, 2, 1)] """ some_elements = CoxeterGroups.ParentMethods.__dict__["some_elements"] __iter__ = CoxeterGroups.ParentMethods.__dict__["__iter__"] diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 24e868de6c8..c71f8a0470a 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -500,6 +500,107 @@ def is_empty(self): """ return False + class ElementMethods: + + def __truediv__(left, right): + """ + Return the result of the division of ``left`` by ``right``, if possible. + + This top-level implementation delegates the work to + the ``_div_`` method if ``left`` and ``right`` have + the same parent and to coercion otherwise. See the + extensive documentation at the top of + :ref:`sage.structure.element`. + + .. SEEALSO:: :meth:`_div_` + + EXAMPLES:: + + sage: G = FreeGroup(2) + sage: x0, x1 = G.group_generators() + sage: c1 = cartesian_product([x0, x1]) + sage: c2 = cartesian_product([x1, x0]) + sage: c1.__div__(c2) + (x0*x1^-1, x1*x0^-1) + sage: c1 / c2 + (x0*x1^-1, x1*x0^-1) + + Division supports coercion:: + + sage: C = cartesian_product([G, G]) + sage: H = Hom(G, C) + sage: phi = H(lambda g: cartesian_product([g, g])) + sage: phi.register_as_coercion() + sage: x1 / c1 + (x1*x0^-1, 1) + sage: c1 / x1 + (x0*x1^-1, 1) + + Depending on how the division itself is implemented in + :meth:`_div_`, division may fail even when ``right`` + actually divides ``left``:: + + sage: x = cartesian_product([2, 1]) + sage: y = cartesian_product([1, 1]) + sage: x / y + (2, 1) + sage: x / x + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + + TESTS:: + + sage: c1.__div__.__module__ + 'sage.categories.magmas' + """ + from sage.structure.element import have_same_parent + if have_same_parent(left, right): + return left._div_(right) + from sage.structure.element import get_coercion_model + import operator + return get_coercion_model().bin_op(left, right, operator.div) + __div__ = __truediv__ # For Python2/3 compatibility; see e.g. #18578 + + def _div_(left, right): + r""" + Default implementation of division, multiplying (on the right) by the inverse. + + INPUT: + + - ``left``, ``right`` -- two elements of the same unital magma + + .. SEEALSO:: :meth:`__div__` + + EXAMPLES:: + + sage: G = FreeGroup(2) + sage: x0, x1 = G.group_generators() + sage: c1 = cartesian_product([x0, x1]) + sage: c2 = cartesian_product([x1, x0]) + sage: c1._div_(c2) + (x0*x1^-1, x1*x0^-1) + + With this implementation, division will fail as soon + as ``right`` is not invertible, even if ``right`` + actually divides ``left``:: + + sage: x = cartesian_product([2, 1]) + sage: y = cartesian_product([1, 1]) + sage: x / y + (2, 1) + sage: x / x + Traceback (most recent call last): + ... + TypeError: no conversion of this rational to integer + + TESTS:: + + sage: c1._div_.__module__ + 'sage.categories.magmas' + """ + return left._mul_(~right) + class SubcategoryMethods: @cached_method diff --git a/src/sage/categories/posets.py b/src/sage/categories/posets.py index 52baeec2e41..4226e3569ea 100644 --- a/src/sage/categories/posets.py +++ b/src/sage/categories/posets.py @@ -285,8 +285,6 @@ def order_ideal(self, elements): [0, 1, 2, 3, 4, 5, 6, 7, 8, 10] """ - lower_set = order_ideal - @abstract_method(optional = True) def order_filter(self, elements): r""" @@ -304,8 +302,6 @@ def order_filter(self, elements): [3, 7, 8, 9, 10, 11, 12, 13, 14, 15] """ - upper_set = order_filter - def directed_subset(self, elements, direction): r""" Return the order filter or the order ideal generated by a diff --git a/src/sage/categories/schemes.py b/src/sage/categories/schemes.py index 5229fa4ae13..a7884ce1774 100644 --- a/src/sage/categories/schemes.py +++ b/src/sage/categories/schemes.py @@ -139,9 +139,9 @@ def _call_(self, x): if is_SchemeMorphism(x): return x from sage.rings.morphism import is_RingHomomorphism - from sage.rings.commutative_ring import is_CommutativeRing + from sage.rings.ring import CommutativeRing from sage.schemes.generic.spec import Spec - if is_CommutativeRing(x): + if isinstance(x, CommutativeRing): return Spec(x) elif is_RingHomomorphism(x): A = Spec(x.codomain()) diff --git a/src/sage/categories/semigroups.py b/src/sage/categories/semigroups.py index a69def11e11..553af2b39e5 100644 --- a/src/sage/categories/semigroups.py +++ b/src/sage/categories/semigroups.py @@ -433,6 +433,48 @@ def subsemigroup(self, generators, one=None, category=None): return AutomaticSemigroup(generators, ambient=self, one=one, category=category) + def trivial_representation(self, base_ring=None, side="twosided"): + """ + Return the trivial representation of ``self`` over ``base_ring``. + + INPUT: + + - ``base_ring`` -- (optional) the base ring; the default is `\ZZ` + - ``side`` -- ignored + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.trivial_representation() + Trivial representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + if base_ring is None: + from sage.rings.all import ZZ + base_ring = ZZ + from sage.modules.with_basis.representation import TrivialRepresentation + return TrivialRepresentation(self, base_ring) + + def regular_representation(self, base_ring=None, side="left"): + """ + Return the regular representation of ``self`` over ``base_ring``. + + - ``side`` -- (default: ``"left"``) whether this is the + ``"left"`` or ``"right"`` regular representation + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.regular_representation() + Left Regular Representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + if base_ring is None: + from sage.rings.all import ZZ + base_ring = ZZ + from sage.modules.with_basis.representation import RegularRepresentation + return RegularRepresentation(self, base_ring, side) + class ElementMethods: def _pow_(self, n): @@ -623,3 +665,42 @@ def product_on_basis(self, g1, g2): """ return self.monomial(g1 * g2) + def trivial_representation(self, side="twosided"): + """ + Return the trivial representation of ``self``. + + INPUT: + + - ``side`` -- ignored + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: A = G.algebra(QQ) + sage: V = A.trivial_representation() + sage: V == G.trivial_representation(QQ) + True + """ + S = self.basis().keys() + return S.trivial_representation(self.base_ring()) + + def regular_representation(self, side="left"): + """ + Return the regular representation of ``self``. + + INPUT: + + - ``side`` -- (default: ``"left"``) whether this is the + ``"left"`` or ``"right"`` regular representation + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: A = G.algebra(QQ) + sage: V = A.regular_representation() + sage: V == G.regular_representation(QQ) + True + """ + S = self.basis().keys() + return S.regular_representation(self.base_ring(), side) + diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 7cb979bd6b3..9673810afed 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -1,5 +1,5 @@ r""" -Fast binary code routines. +Fast binary code routines Some computations with linear binary codes. Fix a basis for $GF(2)^n$. A linear binary code is a linear subspace of $GF(2)^n$, together with diff --git a/src/sage/coding/channel_constructions.py b/src/sage/coding/channel_constructions.py index 44c3cdab68e..05f07447f67 100644 --- a/src/sage/coding/channel_constructions.py +++ b/src/sage/coding/channel_constructions.py @@ -5,6 +5,29 @@ the input space (the message) and transforms it into an element of the output space (the transmitted message). +In Sage, Channels simulate error-prone transmission over communication +channels, and we borrow the nomenclature from communication theory, such as +"transmission" and "positions" as the elements of transmitted vectors. +Transmission can be achieved with two methods: + +- :meth:`Channel.transmit`. Considering a channel ``Chan`` and a message + ``msg``, transmitting ``msg`` with ``Chan`` can be done this way:: + + Chan.transmit(msg) + + It can also be written in a more convenient way:: + + Chan(msg) + +- :meth:`transmit_unsafe`. This does the exact same thing as + :meth:`transmit` except that it does not check if ``msg`` belongs to the + input space of ``Chan``:: + + Chan.transmit_unsafe(msg) + +This is useful in e.g. an inner-loop of a long simulation as a +lighter-weight alternative to :meth:`Channel.transmit`. + This file contains the following elements: - :class:`Channel`, the abstract class for Channels @@ -26,12 +49,13 @@ from sage.structure.sage_object import SageObject from sage.rings.integer import Integer -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.misc.prandom import randint, random, sample from sage.modules.free_module_element import vector from sage.misc.abstract_method import abstract_method from sage.categories.cartesian_product import cartesian_product from sage.modules.free_module import VectorSpace +from sage.functions.other import binomial from copy import copy def random_error_vector(n, F, error_positions): @@ -272,24 +296,6 @@ class StaticErrorRateChannel(Channel): The input space and the output space of this channel are the same. - The main purpose of communication channels is to transmit messages, which can be achieved with - two methods: - - - with the method :meth:`Channel.transmit`. Considering a channel ``Chan`` - and a message ``msg``, transmitting - ``msg`` with ``Chan`` can be done this way:: - - Chan.transmit(msg) - - It can also be written in a more convenient way:: - - Chan(msg) - - - with the method :meth:`transmit_unsafe`. It does the exact same thing as :meth:`transmit` except - that it does not check if ``msg`` belongs to the input space of ``Chan``:: - - Chan.transmit_unsafe(msg) - INPUT: - ``space`` -- the space of both input and output @@ -448,24 +454,6 @@ class ErrorErasureChannel(Channel): The output space of this channel is a Cartesian product between its input space and a VectorSpace of the same dimension over GF(2) - The main purpose of communication channels is to transmit messages, which can be achieved with - two methods: - - - with the method :meth:`Channel.transmit`. Considering a channel ``Chan`` - and a message ``msg``, transmitting - ``msg`` with ``Chan`` can be done this way:: - - Chan.transmit(msg) - - It can also be written in a more convenient way:: - - Chan(msg) - - - with the method :meth:`transmit_unsafe`. It does the exact same thing as :meth:`transmit` except - that it does not check if ``msg`` belongs to the input space of ``Chan``:: - - Chan.transmit_unsafe(msg) - INPUT: - ``space`` -- the input and output space @@ -658,3 +646,197 @@ def number_erasures(self): (3, 3) """ return self._number_erasures + + + + + + + + + + +class QarySymmetricChannel(Channel): + r""" + The q-ary symmetric, memoryless communication channel. + + Given an alphabet `\Sigma` with `|\Sigma| = q` and an error probability + `\epsilon`, a q-ary symmetric channel sends an element of `\Sigma` into the + same element with probability `1 - \epsilon`, and any one of the other `q - + 1` elements with probability `\frac{\epsilon}{q - 1}`. This implementation + operates over vectors in `\Sigma^n`, and "transmits" each element of the + vector independently in the above manner. + + Though `\Sigma` is usually taken to be a finite field, this implementation + allows any structure for which Sage can represent `\Sigma^n` and for which + `\Sigma` has a `random_element()` method. However, beware that if `\Sigma` + is infinite, errors will not be uniformly distributed (since + `random_element()` does not draw uniformly at random). + + The input space and the output space of this channel are the same: + `\Sigma^n`. + + INPUT: + + - ``space`` -- the input and output space of the channel. It has to be + `GF(q)^n` for some finite field `GF(q)`. + + - ``epsilon`` -- the transmission error probability of the individual elements. + + EXAMPLES: + + We construct a QarySymmetricChannel which corrupts 30% of all transmitted + symbols:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: Chan + q-ary symmetric channel with error probability 0.300000000000000, + of input and output space Vector space of dimension 50 over Finite Field of size 59 + """ + + def __init__(self, space, epsilon): + r""" + TESTS: + + If ``space`` is not a vector space, an error is raised:: + + sage: epsilon = 0.42 + sage: Chan = channels.QarySymmetricChannel(GF(59), epsilon) + Traceback (most recent call last): + ... + ValueError: space has to be of the form Sigma^n, where Sigma has a random_element() method + + If ``epsilon`` is not between 0 and 1, an error is raised:: + + sage: epsilon = 42 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + Traceback (most recent call last): + ... + ValueError: Error probability must be between 0 and 1 + """ + if epsilon >= 1 or epsilon <= 0: + raise ValueError("Error probability must be between 0 and 1") + + super(QarySymmetricChannel, self).__init__(space, space) + self._epsilon = epsilon + try: + self.transmit_unsafe(space.random_element()) + except: + raise ValueError("space has to be of the form Sigma^n, where Sigma has a random_element() method") + + def __repr__(self): + r""" + Returns a string representation of ``self``. + + EXAMPLES:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: Chan + q-ary symmetric channel with error probability 0.300000000000000, + of input and output space Vector space of dimension 50 over Finite Field of size 59 + """ + return "q-ary symmetric channel with error probability %s, of input and output space %s"\ + % (self.error_probability(), self.input_space()) + + def _latex_(self): + r""" + Returns a latex representation of ``self``. + + EXAMPLES:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: latex(Chan) + \textnormal{q-ary symmetric channel with error probability 0.300000000000000, + of input and output space Vector space of dimension 50 over Finite Field of size 59} + """ + return "\\textnormal{q-ary symmetric channel with error probability %s, of input and output space %s}"\ + % (self.error_probability(), self.input_space()) + + def transmit_unsafe(self, message): + r""" + Returns ``message`` where each of the symbols has been changed to another from the alphabet with + probability :meth:`error_probability`. + + This method does not check if ``message`` belongs to the input space of``self``. + + INPUT: + + - ``message`` -- a vector + + EXAMPLES:: + + sage: F = GF(59)^11 + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(F, epsilon) + sage: msg = F((3, 14, 15, 9, 26, 53, 58, 9, 7, 9, 3)) + sage: set_random_seed(10) + sage: Chan.transmit_unsafe(msg) + (3, 14, 15, 53, 12, 53, 58, 9, 55, 9, 3) + """ + epsilon = self.error_probability() + V = self.input_space() + F = V.base_ring() + msg = copy(message.list()) + for i in range(len(msg)): + if random() <= epsilon: + a = F.random_element() + while a == msg[i]: + a = F.random_element() + msg[i] = a + return V(msg) + + def error_probability(self): + r""" + Returns the error probability of a single symbol transmission of + ``self``. + + EXAMPLES:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: Chan.error_probability() + 0.300000000000000 + """ + return self._epsilon + + def probability_of_exactly_t_errors(self, t): + r""" + Returns the probability ``self`` has to return + exactly ``t`` errors. + + INPUT: + + - ``t`` -- an integer + + EXAMPLES:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: Chan.probability_of_exactly_t_errors(15) + 0.122346861835401 + """ + n = self.input_space().dimension() + epsilon = self.error_probability() + return binomial(n, t) * epsilon**t * (1-epsilon)**(n-t) + + def probability_of_at_most_t_errors(self, t): + r""" + Returns the probability ``self`` has to return + at most ``t`` errors. + + INPUT: + + - ``t`` -- an integer + + EXAMPLES:: + + sage: epsilon = 0.3 + sage: Chan = channels.QarySymmetricChannel(GF(59)^50, epsilon) + sage: Chan.probability_of_at_most_t_errors(20) + 0.952236164579467 + """ + return sum(self.probability_of_exactly_t_errors(i) + for i in range(t+1)) diff --git a/src/sage/coding/channels_catalog.py b/src/sage/coding/channels_catalog.py index 625826d4bd7..be93f670cb6 100644 --- a/src/sage/coding/channels_catalog.py +++ b/src/sage/coding/channels_catalog.py @@ -1,11 +1,13 @@ r""" -Index of Channels +Index of channels The ``channels`` object may be used to access the codes that Sage can build. - :func:`channel_constructions.ErrorErasureChannel ` - :func:`channel_constructions.StaticErrorRateChannel ` +- :func:`channel_constructions.QarySymmetricChannel ` + .. NOTE:: To import these names into the global namespace, use: @@ -14,4 +16,4 @@ """ -from channel_constructions import (ErrorErasureChannel, StaticErrorRateChannel) +from channel_constructions import (ErrorErasureChannel, StaticErrorRateChannel, QarySymmetricChannel) diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 7d44067ee47..1dfe0a579ff 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -152,7 +152,7 @@ from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import matrix -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.misc.all import prod from linear_code import LinearCodeFromVectorSpace, LinearCode diff --git a/src/sage/coding/codecan/autgroup_can_label.pyx b/src/sage/coding/codecan/autgroup_can_label.pyx index 6f171adf165..8ba1397c562 100644 --- a/src/sage/coding/codecan/autgroup_can_label.pyx +++ b/src/sage/coding/codecan/autgroup_can_label.pyx @@ -1,5 +1,5 @@ r""" -Canonical forms and automorphisms for linear codes over finite fields. +Canonical forms and automorphisms for linear codes over finite fields We implemented the algorithm described in [Feu2009]_ which computes, a unique code (canonical form) in the equivalence class of a given diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index 7b0bec30e4a..b8ca45c6cde 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -1,5 +1,5 @@ r""" -Canonical forms and automorphism group computation for linear codes over finite fields. +Canonical forms and automorphism group computation for linear codes over finite fields We implemented the algorithm described in [Feu2009]_ which computes the unique semilinearly isometric code (canonical form) in the equivalence class of a given diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index dea34888d76..dae184932d7 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -1,5 +1,5 @@ r""" -Index of Codes +Index of codes The ``codes`` object may be used to access the codes that Sage can build. diff --git a/src/sage/coding/decoder.py b/src/sage/coding/decoder.py index 91338cd1a2e..a0c41dd2c97 100644 --- a/src/sage/coding/decoder.py +++ b/src/sage/coding/decoder.py @@ -146,7 +146,7 @@ def decoder_type(self): sage: C = LinearCode(G) sage: D = C.decoder() sage: D.decoder_type() - {'always-succeed', 'complete', 'hard-decision', 'unique'} + {'complete', 'hard-decision', 'might-error', 'unique'} """ return self._decoder_type diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index bfc864a6ab1..08014e2ebb6 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -1,7 +1,7 @@ r""" -Delsarte, a.k.a. Linear Programming (LP), upper bounds. +Delsarte, a.k.a. Linear Programming (LP), upper bounds -This module provides LP upper bounds for the parameters of codes. +This module provides LP upper bounds for the parameters of codes. Exact LP solver, PPL, is used by defaut, ensuring that no rounding/overflow problems occur. diff --git a/src/sage/coding/grs.py b/src/sage/coding/grs.py index d72514bddce..8d7434e29a9 100644 --- a/src/sage/coding/grs.py +++ b/src/sage/coding/grs.py @@ -1,5 +1,5 @@ r""" -Generalized Reed-Solomon Code +Generalized Reed-Solomon code Given `n` different evaluation points `\alpha_1, \dots, \alpha_n` from some finite field `F`, and `n` column multipliers `\beta_1, \dots, \beta_n`, the diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index 43842884356..29fcf0cac79 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -1,5 +1,5 @@ r""" -Guava error-correcting code constructions. +Guava error-correcting code constructions This module only contains Guava wrappers (Guava is an optional GAP package). @@ -34,7 +34,7 @@ from sage.interfaces.all import gap from sage.misc.randstate import current_randstate from sage.matrix.matrix_space import MatrixSpace -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.interfaces.gap import gfq_gap_to_sage from sage.groups.perm_gps.permgroup import * from linear_code import * diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index d3dcc3e7033..d71c7e40934 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -Linear Codes +Linear code VERSION: 1.2 @@ -217,7 +217,7 @@ from sage.categories.fields import Fields from copy import copy from sage.interfaces.all import gap -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.groups.perm_gps.permgroup import PermutationGroup from sage.matrix.matrix_space import MatrixSpace from sage.matrix.constructor import Matrix @@ -240,6 +240,8 @@ from sage.misc.superseded import deprecated_function_alias from encoder import Encoder from decoder import Decoder, DecodingError +from sage.combinat.subset import Subsets +from sage.categories.cartesian_product import cartesian_product # import compatible with py2 and py3 from six.moves.urllib.request import urlopen @@ -1558,6 +1560,7 @@ def parity_check_matrix(self): check_mat = deprecated_function_alias(17973, parity_check_matrix) + @cached_method def covering_radius(self): r""" Wraps Guava's ``CoveringRadius`` command. @@ -1716,7 +1719,8 @@ def decoder(self, decoder_name=None, **kwargs): sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) sage: C.decoder() - Syndrome decoder for Linear code of length 7, dimension 4 over Finite Field of size 2 + Syndrome decoder for Linear code of length 7, dimension 4 over Finite Field of size 2 handling errors of weight up to 1 + If the name of a decoder which is not known by ``self`` is passed, an exception will be raised:: @@ -4015,30 +4019,158 @@ def generator_matrix(self): ####################### decoders ############################### class LinearCodeSyndromeDecoder(Decoder): r""" - Construct a decoder for Linear Codes. + Constructs a decoder for Linear Codes based on syndrome lookup table. - .. WARNING:: + The decoding algorithm works as follows: + + - First, a lookup table is built by computing the syndrome of every error + pattern of weight up to ``maximum_error_weight``. + - Then, whenever one tries to decode a word ``r``, the syndrome of ``r`` is + computed. The corresponding error pattern is recovered from the + pre-computed lookup table. + - Finally, the recovered error pattern is subtracted from ``r`` to recover + the original word. + + ``maximum_error_weight`` need never exceed the covering radius of the code, + since there are then always lower-weight errors with the same syndrome. If + one sets ``maximum_error_weight`` to a value greater than the covering + radius, then the covering radius will be determined while building the + lookup-table. This lower value is then returned if you query + ``decoding_radius`` after construction. + + If ``maximum_error_weight`` is left unspecified or set to a number at least + the covering radius of the code, this decoder is complete, i.e. it decodes + every vector in the ambient space. - As explained in trac #19623, despite its name this decoder actually uses a - nearest neighbor decoding algorithm. + NOTE: + + Constructing the lookup table takes time exponential in the length of the + code and the size of the code's base field. Afterwards, the individual + decodings are fast. INPUT: - ``code`` -- A code associated to this decoder + + - ``maximum_error_weight`` -- (default: ``None``) the maximum number of + errors to look for when building the table. An error is raised if it is + set greater than `n-k`, since this is an upper bound on the covering + radius on any linear code. If ``maximum_error_weight`` is kept + unspecified, it will be set to `n - k`, where `n` is the length of + ``code`` and `k` its dimension. + + EXAMPLES:: + + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) + sage: C = LinearCode(G) + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) + sage: D + Syndrome decoder for Linear code of length 9, dimension 3 over Finite Field of size 3 handling errors of weight up to 4 + + If one wants to correct up to a lower number of errors, one can do as follows:: + + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, maximum_error_weight=2) + sage: D + Syndrome decoder for Linear code of length 9, dimension 3 over Finite Field of size 3 handling errors of weight up to 2 + + If one checks the list of types of this decoder before constructing it, + one will notice it contains the keyword ``dynamic``. + Indeed, the behaviour of the syndrome decoder depends on the maximum + error weight one wants to handle, and how it compares to the minimum + distance and the covering radius of ``code``. + In the following examples, we illustrate this property by computing + different instances of syndrome decoder for the same code. + + We choose the following linear code, whose covering radius equals to 4 + and minimum distance to 5 (half the minimum distance is 2):: + + sage: G = matrix(GF(5), [[1, 0, 0, 0, 0, 4, 3, 0, 3, 1, 0], + ....: [0, 1, 0, 0, 0, 3, 2, 2, 3, 2, 1], + ....: [0, 0, 1, 0, 0, 1, 3, 0, 1, 4, 1], + ....: [0, 0, 0, 1, 0, 3, 4, 2, 2, 3, 3], + ....: [0, 0, 0, 0, 1, 4, 2, 3, 2, 2, 1]]) + sage: C = LinearCode(G) + + In the following examples, we illustrate how the choice of + ``maximum_error_weight`` influences the types of the instance of + syndrome decoder, alongside with its decoding radius. + + We build a first syndrome decoder, and pick a ``maximum_error_weight`` + smaller than both the covering radius and half the minimum distance:: + + sage: D = C.decoder("Syndrome", maximum_error_weight = 1) + sage: D.decoder_type() + {'always-succeed', 'bounded_distance', 'hard-decision', 'unique'} + sage: D.decoding_radius() + 1 + + In that case, we are sure the decoder will always succeed. It is also + a bounded distance decoder. + + We now build another syndrome decoder, and this time, + ``maximum_error_weight`` is chosen to be bigger than half the minimum distance, + but lower than the covering radius:: + + sage: D = C.decoder("Syndrome", maximum_error_weight = 3) + sage: D.decoder_type() + {'bounded_distance', 'hard-decision', 'might-error', 'unique'} + sage: D.decoding_radius() + 3 + + Here, we still get a bounded distance decoder. + But because we have a maximum error weight bigger than half the + minimum distance, we know it might return a codeword which was not + the original codeword. + + And now, we build a third syndrome decoder, whose ``maximum_error_weight`` + is bigger than both the covering radius and half the minimum distance:: + + sage: D = C.decoder("Syndrome", maximum_error_weight = 5) + sage: D.decoder_type() + {'complete', 'hard-decision', 'might-error', 'unique'} + sage: D.decoding_radius() + 4 + + In that case, the decoder might still return an unexpected codeword, but + it is now complete. Note the decoding radius is equal to 4: it was + determined while building the syndrome lookup table that any error with + weight more than 4 will be decoded incorrectly. That is because the covering + radius for the code is 4. + + The minimum distance and the covering radius are both determined while + computing the syndrome lookup table. They user did not explicitly ask to + compute these on the code ``C``. The dynamic typing of the syndrome decoder + might therefore seem slightly surprising, but in the end is quite + informative. """ - def __init__(self, code): + def __init__(self, code, maximum_error_weight=None): r""" - EXAMPLES:: + TESTS: + + If ``maximum_error_weight`` is greater or equal than `n-k`, where `n` + is ``code``'s length, and `k` is ``code``'s dimension, + an error is raised:: sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) sage: C = LinearCode(G) - sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) - sage: D - Syndrome decoder for Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, 42) + Traceback (most recent call last): + ... + ValueError: maximum_error_weight has to be less than code's length minus its dimension """ + n_minus_k = code.length() - code.dimension() + if maximum_error_weight == None: + self._maximum_error_weight = n_minus_k + elif not isinstance(maximum_error_weight, (Integer, int)): + raise ValueError("maximum_error_weight has to be a Sage integer or a Python int") + elif maximum_error_weight > n_minus_k: + raise ValueError("maximum_error_weight has to be less than code's length minus its dimension") + else: + self._maximum_error_weight = maximum_error_weight super(LinearCodeSyndromeDecoder, self).__init__(code, code.ambient_space(),\ code._default_encoder_name) + self._lookup_table = self._build_lookup_table() def __eq__(self, other): r""" @@ -4046,14 +4178,15 @@ def __eq__(self, other): EXAMPLES:: - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) sage: D1 = codes.decoders.LinearCodeSyndromeDecoder(LinearCode(G)) sage: D2 = codes.decoders.LinearCodeSyndromeDecoder(LinearCode(G)) sage: D1 == D2 True """ return isinstance(other, LinearCodeSyndromeDecoder)\ - and self.code() == other.code() + and self.code() == other.code()\ + and self.maximum_error_weight() == other.maximum_error_weight() def _repr_(self): r""" @@ -4061,13 +4194,13 @@ def _repr_(self): EXAMPLES:: - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) sage: C = LinearCode(G) sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) sage: D - Syndrome decoder for Linear code of length 7, dimension 4 over Finite Field of size 2 + Syndrome decoder for Linear code of length 9, dimension 3 over Finite Field of size 3 handling errors of weight up to 4 """ - return "Syndrome decoder for %s" % self.code() + return "Syndrome decoder for %s handling errors of weight up to %s" % (self.code(), self.maximum_error_weight()) def _latex_(self): r""" @@ -4075,91 +4208,207 @@ def _latex_(self): EXAMPLES:: - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) sage: C = LinearCode(G) sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) sage: latex(D) - \textnormal{Syndrome decoder for }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} + \textnormal{Syndrome decoder for [9, 3]\textnormal{ Linear code over }\Bold{F}_{3} handling errors of weight up to 4} """ - return "\\textnormal{Syndrome decoder for }%s" % self.code()._latex_() + return "\\textnormal{Syndrome decoder for %s handling errors of weight up to %s}" % (self.code()._latex_(), self.maximum_error_weight()) - def syndrome(self, r): + @cached_method + def _build_lookup_table(self): r""" - The vector r is a received word, so should be in the same ambient - space V as ``self.code()``. - Returns all elements in V having the same syndrome (ie, the coset r+C, sorted by weight). - - INPUT: - - - ``r`` -- a vector of the ambient space of ``self.code()`` - - OUTPUT: - - - a list of vectors + Builds lookup table for all possible error patterns of weight up to :meth:`maximum_error_weight`. EXAMPLES:: - sage: C = codes.HammingCode(2,GF(3)) - sage: V = VectorSpace(GF(3), 4) - sage: r = V([0, 2, 0, 1]) - sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) - sage: D.syndrome(r) - [(0, 0, 1, 0), (0, 2, 0, 1), (2, 0, 0, 2), (1, 1, 0, 0), (2, 2, 2, 0), (1, 0, 2, 1), (0, 1, 2, 2), (1, 2, 1, 2), (2, 1, 1, 1)] - + sage: G = Matrix(GF(3),[ + ....: [1, 0, 0, 0, 2, 2, 1, 1], + ....: [0, 1, 0, 0, 0, 0, 1, 1], + ....: [0, 0, 1, 0, 2, 0, 0, 2], + ....: [0, 0, 0, 1, 0, 2, 0, 1]]) + sage: C = LinearCode(G) + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, maximum_error_weight = 1) + sage: D._build_lookup_table() + {(0, 0, 0, 0): (0, 0, 0, 0, 0, 0, 0, 0), + (0, 0, 0, 1): (0, 0, 0, 0, 1, 0, 0, 0), + (0, 0, 0, 2): (0, 0, 0, 0, 2, 0, 0, 0), + (0, 0, 1, 0): (0, 0, 1, 0, 0, 0, 0, 0), + (0, 0, 1, 2): (0, 0, 0, 0, 0, 0, 0, 1), + (0, 0, 2, 0): (0, 0, 2, 0, 0, 0, 0, 0), + (0, 0, 2, 1): (0, 0, 0, 0, 0, 0, 0, 2), + (0, 1, 0, 0): (0, 1, 0, 0, 0, 0, 0, 0), + (0, 1, 1, 2): (0, 0, 0, 0, 0, 0, 2, 0), + (0, 2, 0, 0): (0, 2, 0, 0, 0, 0, 0, 0), + (0, 2, 2, 1): (0, 0, 0, 0, 0, 0, 1, 0), + (1, 0, 0, 0): (1, 0, 0, 0, 0, 0, 0, 0), + (1, 2, 0, 2): (0, 0, 0, 0, 0, 1, 0, 0), + (1, 2, 2, 0): (0, 0, 0, 1, 0, 0, 0, 0), + (2, 0, 0, 0): (2, 0, 0, 0, 0, 0, 0, 0), + (2, 1, 0, 1): (0, 0, 0, 0, 0, 2, 0, 0), + (2, 1, 1, 0): (0, 0, 0, 2, 0, 0, 0, 0)} """ + t = self._maximum_error_weight + self._code_covering_radius = None + self._code_minimum_distance = None + self._decoder_type = copy(self._decoder_type) + self._decoder_type.remove("dynamic") C = self.code() - V = C.ambient_space() - if not isinstance(r, list): - r = r.list() - r = V(r) - coset = [[c + r, (c + r).hamming_weight()] for c in C] - return [x[0] for x in sorted(coset, key=lambda x: x[1])] + n = C.length() + k = C.dimension() + H = C.parity_check_matrix() + F = C.base_ring() + l = F.list() + zero = F.zero() + #Builds a list of generators of all error positions for all + #possible error weights + if zero in l: + l.remove(zero) + # Remember to include the no-error-vector to handle codes of minimum + # distance 1 gracefully + zero_syndrome = vector(F,[F.zero()]*(n-k)) + zero_syndrome.set_immutable() + lookup = { zero_syndrome : vector(F,[F.zero()]*n) } + error_position_tables = [cartesian_product([l]*i) for i in range(1, t+1)] + first_collision = True + #Filling the lookup table + for i in range(1, t+1): + stop = True + patterns = Subsets(range(n), i) + basic = vector(F, n) + for p in patterns: + for error in error_position_tables[i-1]: + ind = 0 + e = copy(basic) + for pos in p: + e[pos] = error[ind] + ind += 1 + s = H * e + s.set_immutable() + try: + e_cur = lookup[s] + #if this is the first time we see a collision + #we learn the minimum distance of the code + if first_collision: + self._code_minimum_distance = e.hamming_weight() + e_cur.hamming_weight() + first_collision = False + except KeyError: + stop = False + lookup[s] = copy(e) + #if we reached the early termination condition + #we learn the covering radius of the code + if stop: + self._code_covering_radius = i - 1 + self._maximum_error_weight = self._code_covering_radius + break + # Update decoder types depending on whether we are decoding up to covering radius + if self._code_covering_radius: + self._decoder_type.add("complete") + else: + self._decoder_type.add("bounded_distance") + # Update decoder types depending on whether we are decoding beyond d/2 + if self._code_minimum_distance: + if t == (self._code_minimum_distance-1)//2: + self._decoder_type.add("minimum-distance", "always-succeed") + else: + # then t > (d-1)/2 + self._decoder_type.add("might-error") + else: + self._decoder_type.add("always-succeed") + return lookup + def decode_to_code(self, r): r""" - Decode the received word ``r`` to an element in associated code of ``self``. + Corrects the errors in ``word`` and returns a codeword. INPUT: - - ``r`` -- a vector of same length as the length of the associated - code of ``self`` and over the base field of the associated code of ``self`` + - ``r`` -- a codeword of ``self`` OUTPUT: - - a codeword of the associated code of ``self`` + - a vector of ``self``'s message space EXAMPLES:: - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: G = Matrix(GF(3),[ + ....: [1, 0, 0, 0, 2, 2, 1, 1], + ....: [0, 1, 0, 0, 0, 0, 1, 1], + ....: [0, 0, 1, 0, 2, 0, 0, 2], + ....: [0, 0, 0, 1, 0, 2, 0, 1]]) + sage: C = LinearCode(G) + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C, maximum_error_weight = 2) + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), 2) + sage: c = C.random_element() + sage: r = Chan(c) + sage: c == D.decode_to_code(r) + True + """ + lookup_table = self.syndrome_table() + s = self.code().parity_check_matrix() * r + s.set_immutable() + if s.is_zero(): + return r + err = lookup_table[s] + r_corr = copy(r) + for i in range(self.code().length()): + r_corr[i] = r[i] - err[i] + return r_corr + + def maximum_error_weight(self): + r""" + Returns the maximal number of errors a received word can have + and for which ``self`` is guaranteed to return a most likely codeword. + + Same as ``self.decoding_radius``. + + EXAMPLES:: + + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) sage: C = LinearCode(G) sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) - sage: word = vector(GF(2), (1, 1, 0, 0, 1, 1, 0)) - sage: w_err = word + vector(GF(2), (1, 0, 0, 0, 0, 0, 0)) - sage: D.decode_to_code(word) - (1, 1, 0, 0, 1, 1, 0) + sage: D.maximum_error_weight() + 4 """ - V = self.input_space() - if not isinstance(r, list): - r = r.list() - r = V(r) - c = -V(self.syndrome(r)[0]) + r - c.set_immutable() - return c + return self._maximum_error_weight def decoding_radius(self): r""" - Return maximal number of errors ``self`` can decode. + Returns the maximal number of errors a received word can have + and for which ``self`` is guaranteed to return a most likely codeword. EXAMPLES:: - sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: G = Matrix(GF(3), [[1,0,0,1,0,1,0,1,2],[0,1,0,2,2,0,1,1,0],[0,0,1,0,2,2,2,1,2]]) sage: C = LinearCode(G) sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) sage: D.decoding_radius() - 1 + 4 """ + return self._maximum_error_weight - return (self.code().minimum_distance()-1) // 2 + def syndrome_table(self): + r""" + Returns the syndrome lookup table of ``self``. + + EXAMPLES:: + + sage: G = Matrix(GF(2), [[1,1,1,0,0,0,0],[1,0,0,1,1,0,0],[0,1,0,1,0,1,0],[1,1,0,1,0,0,1]]) + sage: C = LinearCode(G) + sage: D = codes.decoders.LinearCodeSyndromeDecoder(C) + sage: D.syndrome_table() + {(0, 0, 0): (0, 0, 0, 0, 0, 0, 0), + (1, 0, 0): (1, 0, 0, 0, 0, 0, 0), + (0, 1, 0): (0, 1, 0, 0, 0, 0, 0), + (1, 1, 0): (0, 0, 1, 0, 0, 0, 0), + (0, 0, 1): (0, 0, 0, 1, 0, 0, 0), + (1, 0, 1): (0, 0, 0, 0, 1, 0, 0), + (0, 1, 1): (0, 0, 0, 0, 0, 1, 0), + (1, 1, 1): (0, 0, 0, 0, 0, 0, 1)} + """ + return self._lookup_table @@ -4288,6 +4537,6 @@ def decoding_radius(self): LinearCode._registered_encoders["GeneratorMatrix"] = LinearCodeGeneratorMatrixEncoder LinearCode._registered_decoders["Syndrome"] = LinearCodeSyndromeDecoder -LinearCodeSyndromeDecoder._decoder_type = {"hard-decision", "unique", "always-succeed", "complete"} +LinearCodeSyndromeDecoder._decoder_type = {"hard-decision", "unique", "dynamic"} LinearCode._registered_decoders["NearestNeighbor"] = LinearCodeNearestNeighborDecoder LinearCodeNearestNeighborDecoder._decoder_type = {"hard-decision", "unique", "always-succeed", "complete"} diff --git a/src/sage/coding/sd_codes.py b/src/sage/coding/sd_codes.py index 1c36ed125ee..d04a9b7140b 100644 --- a/src/sage/coding/sd_codes.py +++ b/src/sage/coding/sd_codes.py @@ -88,7 +88,7 @@ Math 3 (1972) 209-246. """ from sage.misc.lazy_import import lazy_import -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.matrix.matrix_space import MatrixSpace lazy_import("sage.coding.linear_code", "LinearCode") #from linear_code import LinearCode diff --git a/src/sage/coding/two_weight_db.py b/src/sage/coding/two_weight_db.py index 6752eb498e1..07f3f5516f5 100644 --- a/src/sage/coding/two_weight_db.py +++ b/src/sage/coding/two_weight_db.py @@ -39,7 +39,7 @@ ....: assert (code['w1'], code['w2']) == (w1, w2) """ -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.matrix.constructor import Matrix from sage.coding.linear_code import LinearCode diff --git a/src/sage/combinat/binary_recurrence_sequences.py b/src/sage/combinat/binary_recurrence_sequences.py index b82c52c9e8d..2aac724cc84 100644 --- a/src/sage/combinat/binary_recurrence_sequences.py +++ b/src/sage/combinat/binary_recurrence_sequences.py @@ -67,7 +67,7 @@ from sage.matrix.constructor import matrix, vector from sage.rings.number_field.number_field import QuadraticField from sage.rings.finite_rings.integer_mod_ring import Integers -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.integer import Integer from sage.arith.all import gcd, lcm, next_prime, is_prime, next_prime_power, legendre_symbol from sage.functions.log import log diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index a49a587798d..15805d6573f 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1960,13 +1960,40 @@ def __iter__(self): sage: Compositions(0).list() [[]] """ - if self.n == 0: - yield self.element_class(self, []) - return + for c in composition_iterator_fast(self.n): + yield self.element_class(self, c) + +def composition_iterator_fast(n): + """ + Iterator over compositions of ``n`` yielded as lists. + + TESTS:: - for i in range(1,self.n+1): - for c in Compositions_n(self.n-i): - yield self.element_class(self, [i]+list(c)) + sage: from sage.combinat.composition import composition_iterator_fast + sage: L = list(composition_iterator_fast(4)); L + [[1, 1, 1, 1], [1, 1, 2], [1, 2, 1], [1, 3], [2, 1, 1], [2, 2], [3, 1], [4]] + sage: type(L[0]) + + """ + # Special cases + if n < 0: + return + if n == 0: + yield [] + return + + s = Integer(0) # Current sum + cur = [Integer(0)] + while cur: + cur[-1] += 1 + s += 1 + # Note that because we are adding 1 every time, + # we will never have s > n + if s == n: + yield list(cur) + s -= cur.pop() + else: + cur.append(Integer(0)) from sage.structure.sage_object import register_unpickle_override register_unpickle_override('sage.combinat.composition', 'Composition_class', Composition) diff --git a/src/sage/combinat/crystals/catalog_infinity_crystals.py b/src/sage/combinat/crystals/catalog_infinity_crystals.py index 36e785f78e6..680921a4677 100644 --- a/src/sage/combinat/crystals/catalog_infinity_crystals.py +++ b/src/sage/combinat/crystals/catalog_infinity_crystals.py @@ -5,10 +5,12 @@ * :class:`GeneralizedYoungWalls ` +* :class:`LSPaths ` * :class:`NakajimaMonomials ` * :class:`PolyhedralRealization ` * :class:`RiggedConfigurations ` +* :class:`Star ` * :class:`Tableaux ` """ from generalized_young_walls import InfinityCrystalOfGeneralizedYoungWalls as GeneralizedYoungWalls @@ -16,4 +18,6 @@ from sage.combinat.rigged_configurations.rc_infinity import InfinityCrystalOfRiggedConfigurations as RiggedConfigurations from infinity_crystals import InfinityCrystalOfTableaux as Tableaux from sage.combinat.crystals.polyhedral_realization import InfinityCrystalAsPolyhedralRealization as PolyhedralRealization +from sage.combinat.crystals.star_crystal import StarCrystal as Star +from sage.combinat.crystals.littelmann_path import InfinityCrystalOfLSPaths as LSPaths diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 6e5d34913ca..6936fa304f9 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -2915,13 +2915,28 @@ def epsilon0(self): sage: b = K.module_generators[0] sage: b.epsilon(0) # indirect doctest 1 + + TESTS: + + Check that :trac:`19982` is fixed:: + + sage: K = crystals.KirillovReshetikhin(['D',3,2], 2,3) + sage: def eps0_defn(elt): + ....: x = elt.e(0) + ....: eps = 0 + ....: while x is not None: + ....: x = x.e(0) + ....: eps = eps + 1 + ....: return eps + sage: all(eps0_defn(x) == x.epsilon0() for x in K) + True """ n = self.parent().cartan_type().rank()-1 [b,l] = self.lift().to_highest_weight(index_set=range(2,n+1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l1 = pm.pm_diagram[n-1][0] l4 = pm.pm_diagram[n][0] - return l1+l4 + return l1+l4/2 def phi0(self): r""" @@ -2933,13 +2948,28 @@ def phi0(self): sage: b = K.module_generators[0] sage: b.phi(0) # indirect doctest 0 + + TESTS: + + Check that :trac:`19982` is fixed:: + + sage: K = crystals.KirillovReshetikhin(['D',3,2], 2,3) + sage: def phi0_defn(elt): + ....: x = elt.f(0) + ....: phi = 0 + ....: while x is not None: + ....: x = x.f(0) + ....: phi = phi + 1 + ....: return phi + sage: all(phi0_defn(x) == x.phi0() for x in K) + True """ n = self.parent().cartan_type().rank()-1 [b,l] = self.lift().to_highest_weight(index_set=range(2,n+1)) pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) l2 = pm.pm_diagram[n-1][1] l4 = pm.pm_diagram[n][0] - return l2+l4 + return l2+l4/2 KR_type_Dn_twisted.Element = KR_type_Dn_twistedElement diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index a2f68709eb2..0797003bb5f 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -5,7 +5,9 @@ - Mark Shimozono, Anne Schilling (2012): Initial version - Anne Schilling (2013): Implemented - :class:`~sage.combinat.crystals.littlemann_path.CrystalOfProjectedLevelZeroLSPaths` + :class:`~sage.combinat.crystals.littelmann_path.CrystalOfProjectedLevelZeroLSPaths` +- Travis Scrimshaw (2016): Implemented + :class:`~sage.combinat.crystals.littelmann_path.InfinityCrystalOfLSPaths` """ #**************************************************************************** # Copyright (C) 2012 Mark Shimozono @@ -23,7 +25,7 @@ # http://www.gnu.org/licenses/ #**************************************************************************** -from sage.misc.cachefunc import cached_in_parent_method +from sage.misc.cachefunc import cached_in_parent_method, cached_method from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element_wrapper import ElementWrapper from sage.structure.parent import Parent @@ -124,7 +126,7 @@ def __classcall_private__(cls, starting_weight, cartan_type = None, starting_wei Classcall to mend the input. Internally, the - :class:`~sage.combinat.crystals.littlemann_path.CrystalOfLSPaths` code + :class:`~sage.combinat.crystals.littelmann_path.CrystalOfLSPaths` code works with a ``starting_weight`` that is in the weight space associated to the crystal. The user can, however, also input a ``cartan_type`` and the coefficients of the fundamental weights as @@ -178,7 +180,7 @@ def __init__(self, starting_weight, starting_weight_parent): sage: C.weight.parent() Extended weight space over the Rational Field of the Root system of type ['A', 2, 1] sage: C.module_generators - [(-Lambda[0] + Lambda[2],)] + ((-Lambda[0] + Lambda[2],),) TESTS:: @@ -219,10 +221,10 @@ def __init__(self, starting_weight, starting_weight_parent): Parent.__init__(self, category = ClassicalCrystals()) if starting_weight == starting_weight.parent().zero(): - initial_element = self(tuple([])) + initial_element = self(()) else: - initial_element = self(tuple([starting_weight])) - self.module_generators = [initial_element] + initial_element = self((starting_weight,)) + self.module_generators = (initial_element,) def _repr_(self): """ @@ -297,18 +299,7 @@ def compress(self): sage: c.compress() (Lambda[1] + Lambda[2],) """ - def positively_parallel_weights(v, w): - """ - Checks whether the vectors ``v`` and ``w`` are positive scalar multiples of each other. - """ - supp = v.support() - if len(supp) > 0: - i = supp[0] - if v[i]*w[i] > 0 and v[i]*w == w[i]*v: - return True - return False - - if len(self.value) == 0: + if not self.value: return self q = [] curr = self.value[0] @@ -626,6 +617,10 @@ def _latex_(self): return [latex(p) for p in self.value] +##################################################################### +## Projected level-zero + + class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths): r""" Crystal of projected level zero LS paths. @@ -636,7 +631,7 @@ class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths): When ``weight`` is just a single fundamental weight `\Lambda_r`, this crystal is isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also - :meth:`sage.combinat.crystals.kirillov_reshetikhin.crystals.KirillovReshetikhinFromLSPaths`. + :meth:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhinFromLSPaths`. For general weights, it is isomorphic to a tensor product of single-column KR crystals. EXAMPLES:: @@ -677,9 +672,9 @@ def __classcall_private__(cls, weight): Classcall to mend the input. Internally, the - :class:`~sage.combinat.crystals.littlemann_path.CrystalOfProjectedLevelZeroLSPaths` + :class:`~sage.combinat.crystals.littelmann_path.CrystalOfProjectedLevelZeroLSPaths` uses a level zero weight, which is passed on to - :class:`~sage.combinat.crystals.littlemann_path.CrystalOfLSPaths`. + :class:`~sage.combinat.crystals.littelmann_path.CrystalOfLSPaths`. ``weight`` is first coerced to a level zero weight. TESTS:: @@ -1116,3 +1111,320 @@ def stretch_short_root(a): return s/2 else: return s + + +##################################################################### +## B(\infty) + + +class InfinityCrystalOfLSPaths(UniqueRepresentation, Parent): + r""" + LS path model for `\mathcal{B}(\infty)`. + + Elements of `\mathcal{B}(\infty)` are equivalence classes of paths `[\pi]` + in `\mathcal{B}(k\rho)` for `k\gg 0`, where `\rho` is the Weyl vector. A + canonical representative for an element of `\mathcal{B}(\infty)` is chosen + by taking `k` to be minimal such that the endpoint of `\pi` is strictly + dominant but its representative in `\mathcal{B}((k-1)\rho)` is on the wall + of the dominant chamber. + + REFERENCES: + + .. [LZ11] Bin Li and Hechun Zhang. + *Path realization of crystal* `B(\infty)`. + Front. Math. China, **6** (4), (2011) pp. 689--706. + :doi:`10.1007/s11464-010-0073-x` + """ + @staticmethod + def __classcall_private__(cls, cartan_type): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: B1 = crystals.infinity.LSPaths(['A',4]) + sage: B2 = crystals.infinity.LSPaths('A4') + sage: B3 = crystals.infinity.LSPaths(CartanType(['A',4])) + sage: B1 is B2 and B2 is B3 + True + """ + cartan_type = CartanType(cartan_type) + return super(InfinityCrystalOfLSPaths, cls).__classcall__(cls, cartan_type) + + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['D',4,3]) + sage: TestSuite(B).run(max_runs=500) + sage: B = crystals.infinity.LSPaths(['B',3]) + sage: TestSuite(B).run() # long time + """ + Parent.__init__(self, category=(HighestWeightCrystals(), + InfiniteEnumeratedSets())) + self._cartan_type = cartan_type + self.module_generators = (self.module_generator(),) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: crystals.infinity.LSPaths(['A',4]) + The infinity crystal of LS paths of type ['A', 4] + """ + return "The infinity crystal of LS paths of type %s" % self._cartan_type + + @cached_method + def module_generator(self): + r""" + Return the module generator (or highest weight element) of ``self``. + + The module generator is the unique path + `\pi_\infty\colon t \mapsto t\rho`, for `t \in [0,\infty)`. + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['A',6,2]) + sage: mg = B.module_generator(); mg + (Lambda[0] + Lambda[1] + Lambda[2] + Lambda[3],) + sage: mg.weight() + 0 + """ + rho = self.weight_lattice_realization().rho() + return self((rho,)) + + def weight_lattice_realization(self): + """ + Return the weight lattice realization of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['C',4]) + sage: B.weight_lattice_realization() + Weight space over the Rational Field of the Root system of type ['C', 4] + """ + if self._cartan_type.is_affine(): + return self._cartan_type.root_system().weight_space(extended=True) + return self._cartan_type.root_system().weight_space() + + class Element(CrystalOfLSPaths.Element): + + def e(self, i, power=1, length_only=False): + r""" + Return the `i`-th crystal raising operator on ``self``. + + INPUT: + + - ``i`` -- element of the index set + - ``power`` -- (default: 1) positive integer; specifies the + power of the lowering operator to be applied + - ``length_only`` -- (default: ``False``) boolean; if ``True``, + then return the distance to the anti-dominant end of the + `i`-string of ``self`` + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['B',3,1]) + sage: mg = B.module_generator() + sage: mg.e(0) + sage: mg.e(1) + sage: mg.e(2) + sage: x = mg.f_string([1,0,2,1,0,2,1,1,0]) + sage: all(x.f(i).e(i) == x for i in B.index_set()) + True + sage: all(x.e(i).f(i) == x for i in B.index_set() if x.epsilon(i) > 0) + True + + TESTS: + + Check that this works in affine types:: + + sage: B = crystals.infinity.LSPaths(['A',3,1]) + sage: mg = B.highest_weight_vector() + sage: x = mg.f_string([0,1,2,3]) + sage: x.e_string([3,2,1,0]) == mg + True + + We check that :meth:`epsilon` works:: + + sage: B = crystals.infinity.LSPaths(['D',4]) + sage: mg = B.highest_weight_vector() + sage: x = mg.f_string([1,3,4,2,4,3,2,1,4]) + sage: [x.epsilon(i) for i in B.index_set()] + [1, 1, 0, 1] + """ + ret = super(InfinityCrystalOfLSPaths.Element, self).e(i, power=power, + length_only=length_only) + if ret is None: + return None + if length_only: + return ret + WLR = self.parent().weight_lattice_realization() + value = list(ret.value) + endpoint = sum(p for p in value) + rho = WLR.rho() + h = WLR.simple_coroots() + I = self.parent().index_set() + + if not positively_parallel_weights(value[-1], rho): + value.append(rho) + endpoint += rho + + while any(endpoint.scalar(alc) < 1 for alc in h): + value[-1] += rho + endpoint += rho + while all(endpoint.scalar(alc) > 1 for alc in h): + value[-1] -= rho + endpoint -= rho + while value[-1] == WLR.zero(): + value.pop() + ret.value = tuple(value) + return ret + + def f(self, i, power=1, length_only=False): + r""" + Return the `i`-th crystal lowering operator on ``self``. + + INPUT: + + - ``i`` -- element of the index set + - ``power`` -- (default: 1) positive integer; specifies the + power of the lowering operator to be applied + - ``length_only`` -- (default: ``False``) boolean; if ``True``, + then return the distance to the anti-dominant end of the + `i`-string of ``self`` + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['D',3,2]) + sage: mg = B.highest_weight_vector() + sage: mg.f(1) + (3*Lambda[0] - Lambda[1] + 3*Lambda[2], + 2*Lambda[0] + 2*Lambda[1] + 2*Lambda[2]) + sage: mg.f(2) + (Lambda[0] + 2*Lambda[1] - Lambda[2], + 2*Lambda[0] + 2*Lambda[1] + 2*Lambda[2]) + sage: mg.f(0) + (-Lambda[0] + 2*Lambda[1] + Lambda[2] - delta, + 2*Lambda[0] + 2*Lambda[1] + 2*Lambda[2]) + """ + dual_path = self.dualize() + dual_path = super(InfinityCrystalOfLSPaths.Element, dual_path).e(i, power, length_only=length_only) + if length_only: + return dual_path + if dual_path is None: + return None + ret = dual_path.dualize() + WLR = self.parent().weight_lattice_realization() + value = list(ret.value) + endpoint = sum(p for p in value) + rho = WLR.rho() + h = WLR.simple_coroots() + + if not positively_parallel_weights(value[-1], rho): + value.append(rho) + endpoint += rho + + while any(endpoint.scalar(alc) < 1 for alc in h): + value[-1] += rho + endpoint += rho + while all(endpoint.scalar(alc) > 1 for alc in h): + value[-1] -= rho + endpoint -= rho + while value[-1] == WLR.zero(): + value.pop() + ret.value = tuple(value) + return ret + + @cached_method + def weight(self): + """ + Return the weight of ``self``. + + .. TODO:: + + This is a generic algorithm. We should find a better + description and implement it. + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['E',6]) + sage: mg = B.highest_weight_vector() + sage: f_seq = [1,4,2,6,4,2,3,1,5,5] + sage: x = mg.f_string(f_seq) + sage: x.weight() + -3*Lambda[1] - 2*Lambda[2] + 2*Lambda[3] + Lambda[4] - Lambda[5] + + sage: al = B.cartan_type().root_system().weight_space().simple_roots() + sage: x.weight() == -sum(al[i] for i in f_seq) + True + """ + WLR = self.parent().weight_lattice_realization() + alpha = WLR.simple_roots() + return -WLR.sum(alpha[i] for i in self.to_highest_weight()[1]) + + def phi(self,i): + r""" + Return `\varphi_i` of ``self``. + + Let `\pi \in \mathcal{B}(\infty)`. Define + + .. MATH:: + + \varphi_i(\pi) := \varepsilon_i(\pi) + \langle h_i, + \mathrm{wt}(\pi) \rangle, + + where `h_i` is the `i`-th simple coroot and `\mathrm{wt}(\pi)` + is the :meth:`weight` of `\pi`. + + INPUT: + + - ``i`` -- element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.LSPaths(['D',4]) + sage: mg = B.highest_weight_vector() + sage: x = mg.f_string([1,3,4,2,4,3,2,1,4]) + sage: [x.phi(i) for i in B.index_set()] + [-1, 4, -2, -3] + """ + WLR = self.parent().weight_lattice_realization() + h = WLR.simple_coroots() + return self.epsilon(i) + WLR(self.weight()).scalar(h[i]) + + +##################################################################### +## Helper functions + + +def positively_parallel_weights(v, w): + """ + Check whether the vectors ``v`` and ``w`` are positive scalar + multiples of each other. + + EXAMPLES:: + + sage: from sage.combinat.crystals.littelmann_path import positively_parallel_weights + sage: La = RootSystem(['A',5,2]).weight_space(extended=True).fundamental_weights() + sage: rho = sum(La) + sage: positively_parallel_weights(rho, 4*rho) + True + sage: positively_parallel_weights(4*rho, rho) + True + sage: positively_parallel_weights(rho, -rho) + False + sage: positively_parallel_weights(rho, La[1] + La[2]) + False + """ + supp = v.support() + if len(supp) > 0: + i = supp[0] + if v[i]*w[i] > 0 and v[i]*w == w[i]*v: + return True + return False + diff --git a/src/sage/combinat/crystals/star_crystal.py b/src/sage/combinat/crystals/star_crystal.py new file mode 100644 index 00000000000..b29bd3531f4 --- /dev/null +++ b/src/sage/combinat/crystals/star_crystal.py @@ -0,0 +1,297 @@ +r""" +Star-Crystal Structure On `B(\infty)` + +AUTHORS: + +- Ben Salisbury: Initial version + +- Travis Scrimshaw: Initial version +""" + +#***************************************************************************** +# Copyright (C) 2016 Ben Salisbury +# Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.element_wrapper import ElementWrapper +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.crystals.elementary_crystals import ElementaryCrystal +from sage.combinat.crystals.tensor_product import TensorProductOfCrystals + + +class StarCrystal(UniqueRepresentation, Parent): + r""" + The star-crystal or `*`-crystal version of a highest weight crystal. + + The `*`-crystal structure on `B(\infty)` is the structure induced by + the algebra antiautomorphism `* \colon U_q(\mathfrak{g}) \longrightarrow + U_q(\mathfrak{g})` that stabilizes the negative half `U_q^-(\mathfrak{g})`. + It is defined by + + .. MATH:: + + E_i^* = E_i , \ \ \ + F_i^* = F_i , \ \ \ + q^* = q, \ \ \ + (q^h)^* = q^{-h}, + + where `E_i` and `F_i` are the Chevalley generators of `U_q(\mathfrak{g})` + and `h` is an element of the Cartan subalgebra. + + The induced operation on the crystal `B(\infty)` is called the + *Kashiwara involution*. Its implementation here is based on the + recursive algorithm from Theorem 2.2.1 of [Kash95]_, which states + that for any `i \in I` there is a unique strict crystal embedding + + .. MATH:: + + \Psi_i\colon B(\infty) \longrightarrow B_i \otimes B(\infty) + + such that + + - `u_{\infty} \mapsto b_i(0) \otimes u_{\infty}`, where `u_{\infty}` + is the highest weight vector in `B(\infty)`; + + - if `\Psi_i(b) = f_i^mb_i(0) \otimes b_0`, then + `\Psi_i(f_i^*b) =f_i^{m+1}b_i(0) \otimes b_0` + and `\varepsilon_i(b^*) = m`; + + - the image of `\Psi_i` is `\{f_i^mb_i(0)\otimes b : + \varepsilon_i(b^*) = 0, \ m\ge 0\}`. + + Here, `B_i` is the `i`-th elementary crystal. See + :class:`~sage.combinat.crystals.elementary_crystals.ElementaryCrystal` + for more information. + + INPUT: + + - ``Binf`` -- a crystal from + :class:`~sage.combinat.crystals.catalog_infinity_crystals` + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['A',2]) + sage: Bstar = crystals.infinity.Star(B) + sage: mg = Bstar.highest_weight_vector() + sage: mg + [[1, 1], [2]] + sage: mg.f_string([1,2,1,2,2]) + [[1, 1, 1, 1, 1, 2, 2], [2, 3, 3, 3]] + + REFERENCES: + + .. [Kash95] M. Kashiwara. + The crystal base and Littelmann's refined Demazure character formula. + Duke Math. J. **71** (1993), no. 3, 839-858. + """ + def __init__(self, Binf): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['A',2]) + sage: Bstar = crystals.infinity.Star(B) + sage: TestSuite(Bstar).run(max_runs=40) + sage: TestSuite(Bstar).run(max_runs=1000) # long time + """ + self._Binf = Binf + self._cartan_type = Binf.cartan_type() + Parent.__init__(self, category=HighestWeightCrystals().Infinite()) + self.module_generators = (self(self._Binf.module_generators[0]),) + t0 = Binf.highest_weight_vector() + B = {i: ElementaryCrystal(Binf.cartan_type(),i) for i in self.index_set()} + self._tens = {i: B[i].tensor(Binf) for i in self.index_set()} + gens = {i: self._tens[i](B[i](0), t0) for i in self.index_set()} + self._embedding = {i: Binf.crystal_morphism({t0: gens[i]}) for i in self.index_set()} + self._pullback = {i: self._tens[i].crystal_morphism({gens[i]: t0}) for i in self.index_set()} + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Ystar = crystals.infinity.Star(Y) + sage: Ystar + Star-crystal version of Crystal of generalized Young walls of type ['A', 3, 1] + """ + return "Star-crystal version of %s" % self._Binf + + class Element(ElementWrapper): + + def e(self,i): + r""" + Return the action of `e_i^*` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: ascii_art(nuJ.e(1)) + -1[ ]-1 (/) 0[ ]1 (/) -1[ ]-1 (/) -2[ ]-1 + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: m.e(1) + Y(0,0)^-1 Y(0,2)^-1 Y(1,1) Y(1,2)^-1 Y(2,1)^2 + """ + P = self.parent() + image = P._embedding[i](self.value) + if image[0].e(i)._m > 0: + return None + return P(P._pullback[i]( P._tens[i](image[0].e(i),image[1]) )) + + def f(self,i): + r""" + Return the action of `f_i^*` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: T = crystals.infinity.Tableaux("G2") + sage: Tstar = crystals.infinity.Star(T) + sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) + sage: t + [[1, 1, 1, 2, 0], [2, 3]] + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: m + Y(0,0)^-1 Y(0,2)^-1 Y(1,0)^-1 Y(1,2)^-1 Y(2,0)^2 Y(2,1)^2 + """ + P = self.parent() + image = P._embedding[i](self.value) + return P(P._pullback[i]( P._tens[i](image[0].f(i),image[1]) )) + + def weight(self): + r""" + Return the weight of ``self``. + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: nuJ.weight() + -Lambda[0] - 2*Lambda[1] + 2*Lambda[3] - Lambda[4] + + 2*Lambda[5] - 2*Lambda[6] - delta + """ + return self.value.weight() + + def epsilon(self, i): + r""" + Return `\varepsilon_i^*` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Ystar = crystals.infinity.Star(Y) + sage: y = Ystar.module_generators[0].f_string([0,1,3,2,1,0]) + sage: [y.epsilon(i) for i in y.index_set()] + [1, 0, 1, 0] + + sage: RC = crystals.infinity.RiggedConfigurations(['E',6,1]) + sage: RCstar = crystals.infinity.Star(RC) + sage: nuJ = RCstar.module_generators[0].f_string([0,4,6,1,2]) + sage: [nuJ.epsilon(i) for i in nuJ.index_set()] + [0, 1, 1, 0, 0, 0, 1] + """ + ep = -1 + while self is not None: + ep += 1 + self = self.e(i) + return ep + + def phi(self, i): + r""" + Return `\varphi_i^*` of ``self``. + + For `b \in B(\infty)`, + + .. MATH:: + + \varphi_i^*(b) = \varepsilon_i^*(b) + \langle h_i, + \mathrm{wt}(b) \rangle, + + where `h_i` is a simple coroot. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: T = crystals.infinity.Tableaux("A2") + sage: Tstar = crystals.infinity.Star(T) + sage: t = Tstar.module_generators[0].f_string([1,2,1,1,2]) + sage: [t.phi(i) for i in t.index_set()] + [-3, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['B',2,1]) + sage: Mstar = crystals.infinity.Star(M) + sage: m = Mstar.module_generators[0].f_string([0,1,2,2,1,0]) + sage: [m.phi(i) for i in m.index_set()] + [-1, -1, 4] + """ + P = self.parent().weight_lattice_realization() + ac = P.simple_coroot(i) + return P(self.weight()).scalar(ac) + self.epsilon(i) + + def jump(self, i): + r""" + Return the `i`-jump of ``self``. + + For `b \in B(\infty)`, + + .. MATH:: + + \operatorname{jump}_i(b) = \varepsilon_i(b) + \varepsilon_i^*(b) + + \langle h_i, \mathrm{wt}(b) \rangle, + + where `h_i` is a simple coroot. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: RC = crystals.infinity.RiggedConfigurations("D4") + sage: RCstar = crystals.infinity.Star(RC) + sage: nu0star = RCstar.module_generators[0] + sage: nustar = nu0star.f_string([2,1,3,4,2]) + sage: [nustar.jump(i) for i in RC.index_set()] + [0, 1, 0, 0] + sage: nustar = nu0star.f_string([2,1,3,4,2,2,1,3,2]) # long time + sage: [nustar.jump(i) for i in RC.index_set()] # long time + [1, 0, 1, 2] + """ + P = self.parent().weight_lattice_realization() + ac = P.simple_coroot(i) + return P(self.value.weight()).scalar(ac) + self.epsilon(i) + self.value.epsilon(i) + diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 99e14ad8651..8e20cf2837a 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -635,7 +635,7 @@ def v_4_1_BIBD(v, check=True): return projective_plane(3)._blocks if v == 16: from block_design import AffineGeometryDesign - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField return AffineGeometryDesign(2,1,FiniteField(4,'x'))._blocks if v == 25 or v == 37: from difference_family import difference_family @@ -1084,7 +1084,7 @@ def BIBD_5q_5_for_q_prime_power(q): sage: for q in [25, 45, 65, 85, 125, 145, 185, 205, 305, 405, 605]: # long time ....: _ = BIBD_5q_5_for_q_prime_power(q/5) # long time """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField if q%4 != 1 or not is_prime_power(q): raise ValueError("q is not a prime power or q%4!=1.") @@ -1187,7 +1187,7 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): # From now on, the code assumes the notations of [Denniston69] for n,q, so # that the BIBD returned by the method will have the requested parameters. - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.libs.gap.libgap import libgap from sage.matrix.constructor import Matrix diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 51d0dc3171d..7d14d629678 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -60,7 +60,7 @@ from sage.arith.all import binomial, integer_floor, is_prime_power from incidence_structures import IncidenceStructure from sage.misc.decorators import rename_keyword -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.sets_cat import EmptySetError from sage.misc.unknown import Unknown from sage.matrix.matrix_space import MatrixSpace diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 32f0c7d8b8b..89f8192a745 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -378,7 +378,7 @@ def OA_9_40(): sage: designs.orthogonal_arrays.is_available(9,40) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None),(0,None),(0,None),(0,None),(0,None),(0,None),(0,None),(0,None),(0,None),(0,None)], @@ -678,7 +678,7 @@ def OA_11_80(): sage: designs.orthogonal_arrays.is_available(11,80) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None)], @@ -720,7 +720,7 @@ def OA_15_112(): sage: designs.orthogonal_arrays.is_available(15,112) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (1,None), (4,None), (2,None), (2,None), (4,None), (1,None)], @@ -899,7 +899,7 @@ def OA_11_160(): sage: designs.orthogonal_arrays.is_available(11,160) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (1,None), (4,None), (4,None), (1,None)], @@ -942,7 +942,7 @@ def OA_16_176(): sage: designs.orthogonal_arrays.is_available(16,176) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(0 ,None),(1 ,None),(4 ,None),(9 ,None)], @@ -1141,7 +1141,7 @@ def OA_16_208(): sage: designs.orthogonal_arrays.is_available(16,208) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (0 ,None), (1 ,None)], @@ -1200,7 +1200,7 @@ def OA_15_224(): sage: designs.orthogonal_arrays.is_available(15,224) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (1,None), (4,None), (2,None), (2,None), (4,None), (1,None)], @@ -1286,7 +1286,7 @@ def OA_20_352(): sage: designs.orthogonal_arrays.is_available(20,352) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField # Column 8, line 6 : 4,25 became 4,27 # line 17: 3,0 became 3,None @@ -1345,7 +1345,7 @@ def OA_20_416(): sage: designs.orthogonal_arrays.is_available(20,416) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField Z = None A=[ @@ -1405,7 +1405,7 @@ def OA_20_544(): sage: designs.orthogonal_arrays.is_available(20,544) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField Z = None @@ -1469,7 +1469,7 @@ def OA_17_560(): sage: designs.orthogonal_arrays.is_available(17,560) True """ - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF alpha = 5 beta = 4 p = 2 @@ -1533,7 +1533,7 @@ def OA_11_640(): sage: designs.orthogonal_arrays.is_available(11,640) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (1,None), (4,None), (4,None), (1,None)], @@ -1860,7 +1860,7 @@ def OA_15_896(): sage: designs.orthogonal_arrays.is_available(15,896) True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField A = [ [(0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (0,None), (1,None), (4,None), (2,None), (2,None), (4,None), (1,None)], @@ -3539,7 +3539,7 @@ def DM_45_7_1(): sage: _ = designs.difference_matrix(45,7) """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.cartesian_product import cartesian_product G533 = cartesian_product((FiniteField(5),FiniteField(3),FiniteField(3))) @@ -3592,7 +3592,7 @@ def DM_48_9_1(): sage: _ = designs.difference_matrix(48,9) """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField F16 = FiniteField(16,'x') F3 = FiniteField(3) F3F16 = F3.cartesian_product(F16) @@ -3684,7 +3684,7 @@ def DM_52_6_1(): sage: _ = designs.difference_matrix(52,6) """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField F4 = FiniteField(4,'z') G13 = FiniteField(13) G = F4.cartesian_product(G13) @@ -3796,7 +3796,7 @@ def DM_56_8_1(): sage: _ = designs.difference_matrix(56,8) """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField F8 = FiniteField(8,'z') F7 = FiniteField(7) G = F8.cartesian_product(F7) @@ -3923,7 +3923,7 @@ def DM_75_8_1(): sage: _ = designs.difference_matrix(75,8) """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.cartesian_product import cartesian_product F3 = FiniteField(3) @@ -4160,7 +4160,7 @@ def BIBD_45_9_8(from_code=False): """ if from_code: from sage.coding.code_constructions import ExtendedQuadraticResidueCode - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField C = ExtendedQuadraticResidueCode(47,FiniteField(2)) min_weight = [map(int,x)[3:] for x in C if x.hamming_weight() == 12 and diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 52fd4ffdd64..23cabe7d4da 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -46,6 +46,7 @@ import sage.arith.all as arith from sage.misc.unknown import Unknown from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ def group_law(G): r""" @@ -368,7 +369,7 @@ def singer_difference_set(q,d): assert q.is_prime_power() assert d >= 2 - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.finite_rings.conway_polynomials import conway_polynomial from sage.rings.finite_rings.integer_mod_ring import Zmod @@ -947,7 +948,7 @@ def twin_prime_powers_difference_set(p, check=True): sage: D [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]] """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.cartesian_product import cartesian_product from itertools import product Fp = FiniteField(p,'x') @@ -974,6 +975,142 @@ def twin_prime_powers_difference_set(p, check=True): return G, [d] +def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): + r""" + Test whether ``(v,k,lmbda)`` is a triple that can be obtained from the + construction from [McF1973]_. + + See :func:`mcfarland_1973_construction`. + + INPUT: + + - ``v``, ``k``, ``lmbda`` - (integers) parameters of the difference family + + - ``return_parameters`` -- (boolean, default ``False``) if ``True`` return a + pair ``(True, (q, s))`` so that ``(q,s)`` can be used in the function + :func:`mcfarland_1973_construction` to actually build a + ``(v,k,lmbda)``-difference family. Or ``(False, None)`` if the + construction is not possible. + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters + sage: are_mcfarland_1973_parameters(64, 28, 12) + True + sage: are_mcfarland_1973_parameters(64, 28, 12, return_parameters=True) + (True, (2, 2)) + sage: are_mcfarland_1973_parameters(60, 13, 5) + False + sage: are_mcfarland_1973_parameters(98125, 19500, 3875) + True + sage: are_mcfarland_1973_parameters(98125, 19500, 3875, True) + (True, (5, 3)) + + sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters + sage: for v in range(1, 100): + ....: for k in range(1,30): + ....: for l in range(1,15): + ....: if are_mcfarland_1973_parameters(v,k,l): + ....: answer, (q,s) = are_mcfarland_1973_parameters(v,k,l,return_parameters=True) + ....: print v,k,l,q,s + ....: assert answer is True + ....: assert designs.difference_family(v,k,l,existence=True) is True + ....: G,D = designs.difference_family(v,k,l) + 16 6 2 2 1 + 45 12 3 3 1 + 64 28 12 2 2 + 96 20 4 4 1 + """ + if v <= k or k <= lmbda: + return (False,None) if return_parameters else False + k = ZZ(k) + lmbda = ZZ(lmbda) + qs,r = (k - lmbda).sqrtrem() # sqrt(k-l) should be q^s + if r or (qs*(qs-1))%lmbda: + return (False,None) if return_parameters else False + + q = qs*(qs-1) // lmbda + 1 + if (q <= 1 or + v * (q-1) != qs*q * (qs*q+q-2) or + k * (q-1)!= qs * (qs*q-1)): + return (False,None) if return_parameters else False + + # NOTE: below we compute the value of s so that qs = q^s. If the method + # is_power_of of integers would be able to return the exponent, we could use + # that... but currently this is not the case + # see trac ticket #19792 + p1,a1 = qs.is_prime_power(get_data=True) + p2,a2 = q.is_prime_power(get_data=True) + + if a1 == 0 or a2 == 0 or p1 != p2 or a1%a2: + return (False,None) if return_parameters else False + + return (True, (q, a1//a2)) if return_parameters else True + +def mcfarland_1973_construction(q, s): + r""" + Return a difference set. + + The difference set returned has the following parameters + + .. MATH:: + + v = \frac{q^{s+1}(q^{s+1}+q-2)}{q-1}, + k = \frac{q^s (q^{s+1}-1)}{q-1}, + \lambda = \frac{q^s(q^s-1)}{q-1} + + This construction is due to [McF1973]_. + + INPUT: + + - ``q``, ``s`` - (integers) parameters for the difference set (see the above + formulas for the expression of ``v``, ``k``, ``l`` in terms of ``q`` and + ``s``) + + .. SEEALSO:: + + The function :func:`are_mcfarland_1973_parameters` makes the translation + between the parameters `(q,s)` corresponding to a given triple + `(v,k,\lambda)`. + + REFERENCES: + + .. [McF1973] Robert L. McFarland + "A family of difference sets in non-cyclic groups" + Journal of Combinatorial Theory (A) vol 15 (1973). + http://dx.doi.org/10.1016/0097-3165(73)90031-9 + + EXAMPLES:: + + sage: from sage.combinat.designs.difference_family import ( + ....: mcfarland_1973_construction, is_difference_family) + + sage: G,D = mcfarland_1973_construction(3, 1) + sage: assert is_difference_family(G, D, 45, 12, 3) + + sage: G,D = mcfarland_1973_construction(2, 2) + sage: assert is_difference_family(G, D, 64, 28, 12) + """ + from sage.rings.finite_rings.finite_field_constructor import GF + from sage.modules.free_module import VectorSpace + from sage.rings.finite_rings.integer_mod_ring import Zmod + from sage.categories.cartesian_product import cartesian_product + from itertools import izip + + r = (q**(s+1)-1) // (q-1) + F = GF(q,'a') + V = VectorSpace(F, s+1) + K = Zmod(r+1) + + G = cartesian_product([F]*(s+1) + [K]) + + D = [] + for k,H in izip(K, V.subspaces(s)): + for v in H: + D.append(G((tuple(v) + (k,)))) + + return G,[D] + def difference_family(v, k, l=1, existence=False, explain_construction=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. @@ -1033,14 +1170,16 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: G,D = designs.difference_family(15,7,3) sage: G - The Cartesian product of (Finite Field of size 3, Finite Field of size 5) + Ring of integers modulo 15 sage: D - [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]] + [[0, 1, 2, 4, 5, 8, 10]] sage: print designs.difference_family(15,7,3,explain_construction=True) - Twin prime powers difference family + Singer difference set sage: print designs.difference_family(91,10,1,explain_construction=True) Singer difference set + sage: print designs.difference_family(64,28,12, explain_construction=True) + McFarland 1973 construction For `k=6,7` we look at the set of small prime powers for which a construction is available:: @@ -1233,7 +1372,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch elif explain_construction: return "The database contains a ({},{})-evenly distributed set".format(v,k) - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF poly,B = EDS[k][v] if poly is None: # q is prime K = G = GF(v) @@ -1264,36 +1403,54 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch return G, [range(1,v)] factorization = arith.factor(v) - D = None + if len(factorization) == 1: + from sage.rings.finite_rings.finite_field_constructor import GF + K = GF(v,'z') - if len(factorization) == 1: # i.e. is v a prime power - from sage.rings.finite_rings.constructor import GF - G = K = GF(v,'z') + if are_mcfarland_1973_parameters(v,k,l): + if existence: + return True + elif explain_construction: + return "McFarland 1973 construction" + else: + _, (q,s) = are_mcfarland_1973_parameters(v,k,l,True) + G,D = mcfarland_1973_construction(q,s) - if radical_difference_family(K, k, l, existence=True): - if existence: - return True - elif explain_construction: - return "Radical difference family on a finite field" - else: - D = radical_difference_family(K,k,l) + elif are_hyperplanes_in_projective_geometry_parameters(v,k,l): + if existence: + return True + elif explain_construction: + return "Singer difference set" + else: + _, (q,d) = are_hyperplanes_in_projective_geometry_parameters(v,k,l,True) + G,D = singer_difference_set(q,d) - elif l == 1 and k == 6 and df_q_6_1(K,existence=True): - if existence: - return True - elif explain_construction: - return "Wilson 1972 difference family made from the union of two cyclotomic cosets" - else: - D = df_q_6_1(K) + elif len(factorization) == 1 and radical_difference_family(K, k, l, existence=True): + if existence: + return True + elif explain_construction: + return "Radical difference family on a finite field" + else: + D = radical_difference_family(K,k,l) + G = K + + elif len(factorization) == 1 and l == 1 and k == 6 and df_q_6_1(K, existence=True): + if existence: + return True + elif explain_construction: + return "Wilson 1972 difference family made from the union of two cyclotomic cosets" + else: + D = df_q_6_1(K) + G = K - # Twin prime powers construction - # i.e. v = p(p+2) where p and p+2 are prime powers - # k = (v-1)/2 - # lambda = (k-1)/2 (ie 2l+1 = k) elif (k == (v-1)//2 and l == (k-1)//2 and len(factorization) == 2 and abs(pow(*factorization[0]) - pow(*factorization[1])) == 2): + # Twin prime powers construction + # i.e. v = p(p+2) where p and p+2 are prime powers + # k = (v-1)/2 + # lambda = (k-1)/2 (ie 2l+1 = k) if existence: return True elif explain_construction: @@ -1305,16 +1462,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch p,q = q,p G,D = twin_prime_powers_difference_set(p,check=False) - if D is None and are_hyperplanes_in_projective_geometry_parameters(v,k,l): - _, (q,d) = are_hyperplanes_in_projective_geometry_parameters(v,k,l,True) - if existence: - return True - elif explain_construction: - return "Singer difference set" - else: - G,D = singer_difference_set(q,d) - - if D is None: + else: if existence: return Unknown raise NotImplementedError("No constructions for these parameters") diff --git a/src/sage/combinat/designs/difference_matrices.py b/src/sage/combinat/designs/difference_matrices.py index 6a25fb5f11c..5c53532284d 100644 --- a/src/sage/combinat/designs/difference_matrices.py +++ b/src/sage/combinat/designs/difference_matrices.py @@ -12,7 +12,7 @@ from sage.misc.unknown import Unknown from sage.misc.cachefunc import cached_function from sage.categories.sets_cat import EmptySetError -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.arith.all import is_prime_power, divisors from designs_pyx import is_difference_matrix from database import DM as DM_constructions diff --git a/src/sage/combinat/designs/group_divisible_designs.py b/src/sage/combinat/designs/group_divisible_designs.py index 1a9859adf71..b576a2fa01e 100644 --- a/src/sage/combinat/designs/group_divisible_designs.py +++ b/src/sage/combinat/designs/group_divisible_designs.py @@ -178,7 +178,7 @@ def GDD_4_2(q,existence=False,check=True): if existence: return True - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF G = GF(q,'x') w = G.primitive_element() e = w**((q-1)/3) diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 58721e4d90f..ee7d9c36ba2 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1655,7 +1655,7 @@ def OA_n_times_2_pow_c_from_matrix(k,c,G,A,Y,check=True): Some new MOLS of order 2np for p a prime power, The Australasian Journal of Combinatorics, vol 10 (1994) """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.rings.integer import Integer from itertools import izip,combinations from designs_pyx import is_difference_matrix @@ -1855,7 +1855,7 @@ def OA_from_Vmt(m,t,V): sage: _ = designs.orthogonal_arrays.build(6,46) # indirect doctest """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField q = m*t+1 Fq, M = QDM_from_Vmt(m,t,V) return OA_from_quasi_difference_matrix(M,Fq,add_col = False) @@ -1905,7 +1905,7 @@ def QDM_from_Vmt(m,t,V): sage: _ = designs.orthogonal_arrays.build(6,46) # indirect doctest """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField q = m*t+1 Fq = FiniteField(q, 'x') w = Fq.multiplicative_generator() diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index f95bb553b62..75fec6e9526 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -671,7 +671,7 @@ def thwart_lemma_3_5(k,n,m,a,b,c,d=0,complement=False,explain_construction=False Designs, Codes and Cryptography 5, no. 3 (1995): 189-197. """ from sage.arith.all import is_prime_power - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF if complement: a,b,c = n-a,n-b,n-c @@ -784,7 +784,7 @@ def thwart_lemma_4_1(k,n,m,explain_construction=False): Canad. Math. Bull vol7 num.4 (1964) """ from sage.combinat.designs.designs_pyx import is_orthogonal_array - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.arith.all import is_prime_power from block_design import DesarguesianProjectivePlaneDesign from itertools import chain diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 0ab02ce8cd6..e378052e865 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -202,7 +202,7 @@ def kirkman_triple_system(v,existence=False): # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v-1)//2)%6 == 1 and is_prime_power((v-1)//2): - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//2 K = GF(q,'x') a = K.primitive_element() @@ -243,7 +243,7 @@ def kirkman_triple_system(v,existence=False): # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v//3)%6 == 1 and is_prime_power(v//3): - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = v//3 K = GF(q,'x') a = K.primitive_element() @@ -391,7 +391,7 @@ def v_4_1_rbibd(v,existence=False): if existence: return Unknown raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,4)) - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//3 nn = (q-1)//4 G = GF(q,'x') diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index eafac50a1d9..d123a49e3b5 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -266,6 +266,12 @@ class DyckWord(CombinatorialElement): the `NE = (1,1)` and the `SE = (1,-1)` direction such that it does not pass below the horizontal axis. + .. PLOT:: + :width: 400 px + + d = DyckWord([1,0,1,1,1,1,0,1,0,0,1,0,1,1,0,1,0,1,1,0,0,0,0,0]) + sphinx_plot(d.plot(aspect_ratio=1)) + A path representing a Dyck word (either using `N` and `E` steps, or using `NE` and `SE` steps) is called a Dyck path. @@ -915,6 +921,25 @@ def _latex_(self): res += "\\end{tikzpicture}$}}" return res + def plot(self, **kwds): + """ + Plot a Dyck word as a continuous path. + + EXAMPLES:: + + sage: w = DyckWords(100).random_element() + sage: w.plot() + Graphics object consisting of 1 graphics primitive + """ + from sage.plot.plot import list_plot + step = [-1, 1] + sigma = 0 + list_sigma = [0] + for l in self: + sigma += step[l] + list_sigma.append(sigma) + return list_plot(list_sigma, plotjoined=True, **kwds) + def length(self): r""" Return the length of ``self``. diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index d67af0aca55..7d6cb643f91 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -416,10 +416,99 @@ def latex_options(self): d["vspace"] = self.parent().global_options["latex_vspace"] return d + def _find_node_positions(self, hspace=1, vspace=1): + """ + Compute a nice embedding. + + If `x` precedes `y`, then `y` will always be placed on top of `x` + and/or to the right of `x`. + Decreasing relations tend to be drawn vertically and increasing + relations horizontally. + The algorithm tries to avoid superposition but on big + interval-posets, it might happen. + + OUTPUT: + + a dictionary {vertex: (x,y)} + + EXAMPLES:: + + sage: ti = TamariIntervalPosets(4)[2] + sage: ti._find_node_positions().values() + [[0, 0], [0, -1], [0, -2], [1, -2]] + """ + node_positions = {} + + to_draw = [(1, 0)] + current_parent = [self.increasing_parent(1)] + parenty = [0] + x = 0 + y = 0 + for i in xrange(2, self.size() + 1): + decreasing_parent = self.decreasing_parent(i) + increasing_parent = self.increasing_parent(i) + while to_draw and (decreasing_parent is None or + decreasing_parent < to_draw[-1][0]): + n = to_draw.pop() + node_positions[n[0]] = [x, n[1]] + if i != current_parent[-1]: + if (not self.le(i, i - 1) and decreasing_parent is not None): + x += hspace + if current_parent[-1] is not None: + y -= vspace + else: + y -= vspace + if increasing_parent != current_parent[-1]: + current_parent.append(increasing_parent) + parenty.append(y) + nodey = y + else: + current_parent.pop() + x += hspace + nodey = parenty.pop() + if not current_parent or increasing_parent != current_parent[-1]: + current_parent.append(increasing_parent) + parenty.append(nodey) + to_draw.append((i, nodey)) + + for n in to_draw: + node_positions[n[0]] = [x, n[1]] + return node_positions + + def plot(self, **kwds): + """ + Return a picture. + + The picture represents the Hasse diagram, where the covers are + colored in blue if they are increasing and in red if they are + decreasing. + + This uses the same coordinates as the latex view. + + EXAMPLES:: + + sage: ti = TamariIntervalPosets(4)[2] + sage: ti.plot() + Graphics object consisting of 6 graphics primitives + """ + c0 = 'blue' # self.latex_options()["color_increasing"] + c1 = 'red' # self.latex_options()["color_decreasing"] + G = self.poset().hasse_diagram() + G.set_pos(self._find_node_positions()) + for a, b, c in G.edges(): + if a < b: + G.set_edge_label(a, b, 0) + else: + G.set_edge_label(a, b, 1) + return G.plot(color_by_label={0: c0, 1: c1}, **kwds) + def _latex_(self): r""" A latex representation of ``self`` using the tikzpicture package. + This picture shows the union of the Hasse diagrams of the + initial and final forests. + If `x` precedes `y`, then `y` will always be placed on top of `x` and/or to the right of `x`. Decreasing relations tend to be drawn vertically and increasing @@ -435,20 +524,22 @@ def _latex_(self): sage: ip = TamariIntervalPoset(4,[(2,4),(3,4),(2,1),(3,1)]) sage: print ip._latex_() \begin{tikzpicture}[scale=1] + \node(T1) at (1,0) {1}; \node(T2) at (0,-1) {2}; \node(T3) at (1,-2) {3}; - \node(T1) at (1,0) {1}; \node(T4) at (2,-1) {4}; - \draw[line width = 0.5, color=blue] (T2) -- (T4); + \draw[line width = 0.5, color=red] (T3) -- (T1); \draw[line width = 0.5, color=red] (T2) -- (T1); + \draw[line width = 0.5, color=blue] (T2) -- (T4); \draw[line width = 0.5, color=blue] (T3) -- (T4); - \draw[line width = 0.5, color=red] (T3) -- (T1); \end{tikzpicture} """ latex.add_package_to_preamble_if_available("tikz") latex_options = self.latex_options() start = "\\begin{tikzpicture}[scale=" + str(latex_options['tikz_scale']) + "]\n" end = "\\end{tikzpicture}" + vspace = latex_options["vspace"] + hspace = latex_options["hspace"] def draw_node(j, x, y): r""" @@ -467,65 +558,21 @@ def draw_decreasing(i, j): Internal method to draw decreasing relations """ return "\\draw[line width = " + str(latex_options["line_width"]) + ", color=" + latex_options["color_decreasing"] + "] (T" + str(i) + ") -- (T" + str(j) + ");\n" + if self.size() == 0: nodes = "\\node(T0) at (0,0){$\emptyset$};" relations = "" else: - nodes = "" # latex for node decraltions + positions = self._find_node_positions(hspace, vspace) + nodes = "" # latex for node declarations relations = "" # latex for drawing relations - to_draw = [] - to_draw.append((1, 0)) # a pilo of nodes to be drawn with their y position - - current_parent = [self.increasing_parent(1)] # a pilo for the current increasing parents - parenty = [0] # a pilo for the current parent y positions - if current_parent[-1] is not None: - relations += draw_increasing(1, current_parent[-1]) - vspace = latex_options["vspace"] - hspace = latex_options["hspace"] - x = 0 - y = 0 - - # the idea is that we draw the nodes from left to right and save their y position - for i in xrange(2, self.size() + 1): - # at each, we draw all possible nodes and add the current node to the to_draw pilo - decreasing_parent = self.decreasing_parent(i) - increasing_parent = self.increasing_parent(i) - while len(to_draw) > 0 and (decreasing_parent is None or decreasing_parent < to_draw[-1][0]): - # we draw all the nodes which can be placed at x - # we know these nodes won't have any more decreasing children (so their horizontal position is fixed) - n = to_draw.pop() - nodes += draw_node(n[0], x, n[1]) - if i != current_parent[-1]: - #i is not the current increasing parent - if (not self.le(i, i - 1) and decreasing_parent is not None): - # there is no decreasing relation between i and i-1 - #they share a decreasing parent and are placed alongside horizontally - x += hspace - if current_parent[-1] is not None: - y -= vspace - else: - #otherwise, they are placed alongside vertically - y -= vspace - if increasing_parent != current_parent[-1]: - current_parent.append(increasing_parent) - parenty.append(y) - nodey = y - else: - # i is the current increasing parent so it takes the current vertical position - current_parent.pop() - x += hspace - nodey = parenty.pop() - if len(current_parent) == 0 or increasing_parent != current_parent[-1]: - current_parent.append(increasing_parent) - parenty.append(nodey) - to_draw.append((i, nodey)) - if increasing_parent is not None: - relations += draw_increasing(i, increasing_parent) - if decreasing_parent is not None: - relations += draw_decreasing(i, decreasing_parent) - for n in to_draw: - # we draw all remaining nodes - nodes += draw_node(n[0], x, n[1]) + for i in range(1, self.size() + 1): + nodes += draw_node(i, *positions[i]) + for i, j in self.decreasing_cover_relations(): + relations += draw_decreasing(i, j) + for i, j in self.increasing_cover_relations(): + relations += draw_increasing(i, j) + return start + nodes + relations + end def poset(self): @@ -556,10 +603,11 @@ def __hash__(self): EXAMPLES:: - sage: hash(TamariIntervalPosets(4)[0]) - 3527539 + sage: len(set([hash(u) for u in TamariIntervalPosets(4)])) + 68 """ - return hash(self._cover_relations) + pair = (self.size(), tuple(tuple(e) for e in self._cover_relations)) + return hash(pair) @cached_method def increasing_cover_relations(self): diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index f966e4e166a..65c4ad5f74c 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -135,7 +135,7 @@ def hadamard_matrix_paleyI(n, normalize=True): if not(is_prime_power(p) and (p % 4 == 3)): raise ValueError("The order %s is not covered by the Paley type I construction." % n) - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(p,'x') K_list = list(K) K_list.insert(0,K.zero()) @@ -198,7 +198,7 @@ def hadamard_matrix_paleyII(n): if not(n%2==0 and is_prime_power(q) and (q % 4 == 1)): raise ValueError("The order %s is not covered by the Paley type II construction." % n) - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(q,'x') K_list = list(K) K_list.insert(0,K.zero()) @@ -514,11 +514,11 @@ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False A Hadamard matrix is said to be *regular* if its rows all sum to the same value. - When `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if + For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if `M` is a regular symmetric Hadamard matrix with constant diagonal - `\delta\in\{-1,+1\}` and row values all equal to `\delta \epsilon + `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in - [BH12]_. + [BH12]_. For the case `n=324`, see :func:`RSHCD_324` and [CP16]_. INPUT: @@ -550,6 +550,11 @@ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False 100 x 100 dense matrix over Integer Ring 196 x 196 dense matrix over Integer Ring + sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324 + ....: print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e) # not tested - long time + 324 x 324 dense matrix over Integer Ring + 324 x 324 dense matrix over Integer Ring + From two close prime powers:: sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1) @@ -619,6 +624,10 @@ def true(): return true() M = strongly_regular_graph(196,91,42,42).adjacency_matrix() M = J(196) - 2*M + elif n == 324: + if existence: + return true() + M = RSHCD_324(e) elif ( e == 1 and n%16 == 0 and is_square(n) and @@ -655,6 +664,63 @@ def true(): return M +def RSHCD_324(e): + r""" + Return a size 324x324 Regular Symmetric Hadamard Matrix with Constant Diagonal. + + We build the matrix `M` for the case `n=324`, `\epsilon=1` directly from + :meth:`JankoKharaghaniTonchevGraph + ` + and for the case `\epsilon=-1` from the "twist" `M'` of `M`, using Lemma 11 + in [HX10]_. Namely, it turns out that the matrix + + .. math:: + + M'=\begin{pmatrix} M_{12} & M_{11}\\ M_{11}^\top & M_{21} \end{pmatrix}, + \quad\text{where}\quad + M=\begin{pmatrix} M_{11} & M_{12}\\ M_{21} & M_{22} \end{pmatrix}, + + and the `M_{ij}` are 162x162-blocks, also RSHCD, its diagonal blocks having zero row + sums, as needed by [loc.cit.]. Interestingly, the corresponding + `(324,152,70,72)`-strongly regular graph + has a vertex-transitive automorphism group of order 2592, twice the order of the + (intransitive) automorphism group of the graph corresponding to `M`. Cf. [CP16]_. + + INPUT: + + - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon` + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import RSHCD_324, is_hadamard_matrix + sage: for e in [1,-1]: # long time + ....: M = RSHCD_324(e) # long time + ....: print M==M.T,is_hadamard_matrix(M),all([M[i,i]==1 for i in xrange(324)]) # long time + ....: print set(map(sum,M)) # long time + True True True + set([18]) + True True True + set([-18]) + + REFERENCE: + + .. [CP16] N. Cohen, D. Pasechnik, + Implementing Brouwer's database of strongly regular graphs, + http://arxiv.org/abs/1601.00181 + """ + + from sage.graphs.generators.smallgraphs import JankoKharaghaniTonchevGraph as JKTG + M = JKTG().adjacency_matrix() + M = J(324) - 2*M + if e==-1: + M1=M[:162].T + M2=M[162:].T + M11=M1[:162] + M12=M1[162:].T + M21=M2[:162].T + M=block_matrix([[M12,-M11],[-M11.T,M21]]) + return M + def _helper_payley_matrix(n, zero_position=True): r""" Return the marix constructed in Lemma 1.19 page 291 of [SWW72]_. @@ -716,7 +782,7 @@ def _helper_payley_matrix(n, zero_position=True): [ 1 1 -1 -1 1 -1 -1 1 1 0 -1] [ 1 -1 1 -1 -1 -1 1 1 -1 1 0] """ - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF K = GF(n,conway=True,prefix='x') # Order the elements of K in K_list @@ -839,10 +905,10 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): .. [GS70s] J.M. Goethals and J. J. Seidel, A skew Hadamard matrix of order 36, - J. Aust. Math. Soc. 11(1970), 343-344 + J. Aust. Math. Soc. 11(1970), 343-344 .. [Wall71] J. Wallis, A skew-Hadamard matrix of order 92, - Bull. Aust. Math. Soc. 5(1971), 203-204 + Bull. Aust. Math. Soc. 5(1971), 203-204 .. [KoSt08] C. Koukouvinos, S. Stylianou On skew-Hadamard matrices, Discrete Math. 308(2008) 2723-2731 @@ -857,7 +923,7 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): assert A+A.T==2*I(n) M = block_matrix([[ A, B*R, C*R, D*R], - [-B*R, A, -D.T*R, C.T*R], + [-B*R, A, -D.T*R, C.T*R], [-C*R, D.T*R, A, -B.T*R], [-D*R, -C.T*R, B.T*R, A]]) if check: @@ -900,7 +966,7 @@ def pmtoZ(s): if existence: return n in [36, 52, 92] - + if n==36: a=[ 1, 1, 1, -1, 1, -1, 1, -1, -1] b=[ 1, -1, 1, 1, -1, -1, 1, 1, -1] @@ -918,8 +984,8 @@ def pmtoZ(s): c = [1, 1,-1,-1,-1, 1,-1, 1,-1, 1,-1, 1, 1,-1, 1,-1, 1,-1, 1,-1,-1,-1, 1] d = [1,-1,-1,-1,-1, 1,-1,-1, 1,-1,-1, 1, 1,-1,-1, 1,-1,-1, 1,-1,-1,-1,-1] return WGS(a,b,c,d, check=check) - return None - + return None + _skew_had_cache={} def skew_hadamard_matrix(n,existence=False, skew_normalize=True, check=True): diff --git a/src/sage/combinat/matrices/latin.py b/src/sage/combinat/matrices/latin.py index 8bcab6bf107..6bf0fc14675 100644 --- a/src/sage/combinat/matrices/latin.py +++ b/src/sage/combinat/matrices/latin.py @@ -140,7 +140,7 @@ from sage.interfaces.gap import gap from sage.groups.perm_gps.permgroup import PermutationGroup from sage.arith.all import is_prime -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.misc.misc import uniq from sage.misc.flatten import flatten diff --git a/src/sage/combinat/ncsf_qsym/ncsf.py b/src/sage/combinat/ncsf_qsym/ncsf.py index 87bb58e914a..c8cf7a4f5b0 100644 --- a/src/sage/combinat/ncsf_qsym/ncsf.py +++ b/src/sage/combinat/ncsf_qsym/ncsf.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Non-Commutative Symmetric Functions """ @@ -253,7 +254,7 @@ class NonCommutativeSymmetricFunctions(UniqueRepresentation, Parent): R[1, 3, 2] + R[1, 5] + R[4, 2] + R[6] This is the sum of all fatter compositions. Using the usual - Moebius function for the boolean lattice, the inverse change of + Möbius function for the boolean lattice, the inverse change of basis is given by the alternating sum of all fatter compositions:: sage: complete(ribbon[1,3,2]) @@ -268,7 +269,7 @@ class NonCommutativeSymmetricFunctions(UniqueRepresentation, Parent): sage: ribbon(elementary([1,3,2])) R[1, 1, 1, 1, 1, 1] + R[1, 1, 1, 2, 1] + R[2, 1, 1, 1, 1] + R[2, 1, 2, 1] - By Moebius inversion on the composition poset, the ribbon + By Möbius inversion on the composition poset, the ribbon basis element corresponding to a composition `I` is then the alternating sum over all compositions fatter than the complement composition of `I` in the elementary basis:: diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 6cdd45766b1..7923ecf42c4 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -391,7 +391,7 @@ def lt(s, t): p = self.realization_of().p() P = Poset((A.coarsenings(), lt)) R = self.base_ring() - return p._from_dict({B: R(P.mobius_function(A, B)) for B in P}) + return p._from_dict({B: R(P.moebius_function(A, B)) for B in P}) @cached_method def _m_to_cp_on_basis(self, A): @@ -1315,7 +1315,7 @@ def _p_to_e_on_basis(self, A): P_refine = Poset((A.refinements(), A.parent().lt)) c = prod((-1)**(i-1) * factorial(i-1) for i in A.shape()) R = self.base_ring() - return e._from_dict({B: R(P_refine.mobius_function(B, A) / ZZ(c)) + return e._from_dict({B: R(P_refine.moebius_function(B, A) / ZZ(c)) for B in P_refine}, remove_zeros=False) @cached_method @@ -1343,7 +1343,7 @@ def _p_to_h_on_basis(self, A): P_refine = Poset((A.refinements(), A.parent().lt)) c = abs(prod((-1)**(i-1) * factorial(i-1) for i in A.shape())) R = self.base_ring() - return h._from_dict({B: R(P_refine.mobius_function(B, A) / ZZ(c)) + return h._from_dict({B: R(P_refine.moebius_function(B, A) / ZZ(c)) for B in P_refine}, remove_zeros=False) @cached_method @@ -1786,7 +1786,7 @@ def lt(s, t): p = self.realization_of().p() P_refine = Poset((A.refinements(), lt)) R = self.base_ring() - return p._from_dict({B: R(P_refine.mobius_function(B, A)) + return p._from_dict({B: R(P_refine.moebius_function(B, A)) for B in P_refine}) x = x_basis diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index 5935bb2af45..cefb02569e0 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -1136,7 +1136,7 @@ class LabelledOrderedTree(AbstractLabelledClonableTree, OrderedTree): INPUT: - ``children`` -- a list or tuple or more generally any iterable - of trees or object convertible to trees + of trees or object convertible to trees - ``label`` -- any Sage object (default: ``None``) EXAMPLES:: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 21e62bf3809..a92c09d4d22 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -6698,7 +6698,7 @@ class RegularPartitions(Partitions): - ``is_infinite`` -- boolean; if the subset of `\ell`-regular partitions is infinite """ - def __init__(self, ell, is_infinte=False): + def __init__(self, ell, is_infinite=False): """ Initialize ``self``. @@ -6708,7 +6708,7 @@ def __init__(self, ell, is_infinte=False): sage: TestSuite(P).run() """ self._ell = ell - Partitions.__init__(self, is_infinte) + Partitions.__init__(self, is_infinite) def ell(self): r""" diff --git a/src/sage/combinat/posets/cartesian_product.py b/src/sage/combinat/posets/cartesian_product.py index a630867f3d2..6121f95b5b9 100644 --- a/src/sage/combinat/posets/cartesian_product.py +++ b/src/sage/combinat/posets/cartesian_product.py @@ -193,6 +193,18 @@ def le_lex(self, left, right): (1, 0) <= (1, 1) = True (1, 0) <= (0, 1) = False (1, 0) <= (1, 0) = True + + TESTS: + + Check that :trac:`19999` is resolved:: + + sage: P = Poset((srange(2), lambda left, right: left <= right)) + sage: Q = cartesian_product((P, P), order='product') + sage: R = cartesian_product((Q, P), order='lex') + sage: R(((1, 0), 0)) <= R(((0, 1), 0)) + False + sage: R(((0, 1), 0)) <= R(((1, 0), 0)) + False """ for l, r, S in \ zip(left.value, right.value, self.cartesian_factors()): @@ -202,6 +214,7 @@ def le_lex(self, left, right): return True if S.le(r, l): return False + return False # incomparable components return True # equal diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 9e43e76dfdf..10d68ebf8d3 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -707,7 +707,8 @@ def cardinality(self): """ return self.order() - def mobius_function(self,i,j): # dumb algorithm + from sage.misc.superseded import deprecated_function_alias + def moebius_function(self,i,j): # dumb algorithm r""" Returns the value of the Möbius function of the poset on the elements ``i`` and ``j``. @@ -716,49 +717,51 @@ def mobius_function(self,i,j): # dumb algorithm sage: P = Poset([[1,2,3],[4],[4],[4],[]]) sage: H = P._hasse_diagram - sage: H.mobius_function(0,4) + sage: H.moebius_function(0,4) 2 sage: for u,v in P.cover_relations_iterator(): - ... if P.mobius_function(u,v) != -1: - ... print "Bug in mobius_function!" + ....: if P.moebius_function(u,v) != -1: + ....: print "Bug in moebius_function!" """ try: - return self._mobius_function_values[(i,j)] + return self._moebius_function_values[(i,j)] except AttributeError: - self._mobius_function_values = {} - return self.mobius_function(i,j) + self._moebius_function_values = {} + return self.moebius_function(i,j) except KeyError: if i == j: - self._mobius_function_values[(i,j)] = 1 + self._moebius_function_values[(i,j)] = 1 elif i > j: - self._mobius_function_values[(i,j)] = 0 + self._moebius_function_values[(i,j)] = 0 else: ci = self.closed_interval(i,j) if len(ci) == 0: - self._mobius_function_values[(i,j)] = 0 + self._moebius_function_values[(i,j)] = 0 else: - self._mobius_function_values[(i,j)] = \ - -sum([self.mobius_function(i,k) for k in ci[:-1]]) - return self._mobius_function_values[(i,j)] + self._moebius_function_values[(i,j)] = \ + -sum([self.moebius_function(i,k) for k in ci[:-1]]) + return self._moebius_function_values[(i,j)] + mobius_function = deprecated_function_alias(19855, moebius_function) - def mobius_function_matrix(self): + from sage.misc.superseded import deprecated_function_alias + def moebius_function_matrix(self): r""" Returns the matrix of the Möbius function of this poset This returns the sparse matrix over `\ZZ` whose ``(x, y)`` entry is the value of the Möbius function of ``self`` evaluated on - ``x`` and ``y``, and redefines :meth:`mobius_function` to use + ``x`` and ``y``, and redefines :meth:`moebius_function` to use it. .. NOTE:: - The result is cached in :meth:`_mobius_function_matrix`. + The result is cached in :meth:`_moebius_function_matrix`. EXAMPLES:: sage: from sage.combinat.posets.hasse_diagram import HasseDiagram sage: H = HasseDiagram({0:[1,3,2],1:[4],2:[4,5,6],3:[6],4:[7],5:[7],6:[7],7:[]}) - sage: H.mobius_function_matrix() + sage: H.moebius_function_matrix() [ 1 -1 -1 -1 1 0 1 0] [ 0 1 0 0 -1 0 0 0] [ 0 0 1 0 -1 -1 -1 2] @@ -770,22 +773,24 @@ def mobius_function_matrix(self): TESTS:: - sage: H.mobius_function_matrix().is_immutable() + sage: H.moebius_function_matrix().is_immutable() True - sage: hasattr(H,'_mobius_function_matrix') + sage: hasattr(H,'_moebius_function_matrix') True - sage: H.mobius_function == H._mobius_function_from_matrix + sage: H.moebius_function == H._moebius_function_from_matrix True """ - if not hasattr(self,'_mobius_function_matrix'): - self._mobius_function_matrix = self.lequal_matrix().inverse().change_ring(ZZ) - self._mobius_function_matrix.set_immutable() - self.mobius_function = self._mobius_function_from_matrix - return self._mobius_function_matrix + if not hasattr(self,'_moebius_function_matrix'): + self._moebius_function_matrix = self.lequal_matrix().inverse().change_ring(ZZ) + self._moebius_function_matrix.set_immutable() + self.moebius_function = self._moebius_function_from_matrix + return self._moebius_function_matrix + mobius_function_matrix = deprecated_function_alias(19855, moebius_function_matrix) - # Redefine self.mobius_function - def _mobius_function_from_matrix(self, i,j): + # Redefine self.moebius_function + from sage.misc.superseded import deprecated_function_alias + def _moebius_function_from_matrix(self, i,j): r""" Returns the value of the Möbius function of the poset on the elements ``i`` and ``j``. @@ -794,16 +799,17 @@ def _mobius_function_from_matrix(self, i,j): sage: P = Poset([[1,2,3],[4],[4],[4],[]]) sage: H = P._hasse_diagram - sage: H.mobius_function(0,4) # indirect doctest + sage: H.moebius_function(0,4) # indirect doctest 2 sage: for u,v in P.cover_relations_iterator(): - ... if P.mobius_function(u,v) != -1: - ... print "Bug in mobius_function!" + ....: if P.moebius_function(u,v) != -1: + ....: print "Bug in moebius_function!" - This uses ``self._mobius_function_matrix``, as computed by - :meth:`mobius_function_matrix`. + This uses ``self._moebius_function_matrix``, as computed by + :meth:`moebius_function_matrix`. """ - return self._mobius_function_matrix[i,j] + return self._moebius_function_matrix[i,j] + _mobius_function_from_matrix = deprecated_function_alias(19855, _moebius_function_from_matrix) @cached_method def coxeter_transformation(self): @@ -827,7 +833,7 @@ def coxeter_transformation(self): sage: M**8 == 1 True """ - return - self.lequal_matrix()*self.mobius_function_matrix().transpose() + return - self.lequal_matrix()*self.moebius_function_matrix().transpose() def order_filter(self, elements): """ diff --git a/src/sage/combinat/posets/incidence_algebras.py b/src/sage/combinat/posets/incidence_algebras.py index c67a36e18bd..d1f4e0a4b5b 100644 --- a/src/sage/combinat/posets/incidence_algebras.py +++ b/src/sage/combinat/posets/incidence_algebras.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Incidence Algebras """ @@ -42,7 +43,7 @@ class IncidenceAlgebra(CombinatorialFreeModule): This has a natural basis given by indicator functions for the interval `[a, b]`, i.e. `X_{a,b}(x,y) = \delta_{ax} \delta_{by}`. The incidence algebra is a unital algebra with the identity given - by the Kronecker delta `\delta(x, y) = \delta_{xy}`. The Mobius + by the Kronecker delta `\delta(x, y) = \delta_{xy}`. The Möbius function of `P` is another element of `I_p` whose inverse is the `\zeta` function of the poset (so `\zeta(x, y) = 1` for every interval `[x, y]`). @@ -164,7 +165,7 @@ def some_elements(self): I[0, 0] - I[0, 1] + I[1, 1], I[0, 0] + I[0, 1] + I[1, 1]] """ - return [self.an_element(), self.mobius(), self.zeta()] + return [self.an_element(), self.moebius(), self.zeta()] def product_on_basis(self, A, B): r""" @@ -220,27 +221,29 @@ def zeta(self): sage: P = posets.BooleanLattice(4) sage: I = P.incidence_algebra(QQ) - sage: I.zeta() * I.mobius() == I.one() + sage: I.zeta() * I.moebius() == I.one() True """ return self.sum(self.basis()) + from sage.misc.superseded import deprecated_function_alias @cached_method - def mobius(self): + def moebius(self): """ - Return the Mobius function of ``self``. + Return the Möbius function of ``self``. EXAMPLES:: sage: P = posets.BooleanLattice(2) sage: I = P.incidence_algebra(QQ) - sage: I.mobius() + sage: I.moebius() I[0, 0] - I[0, 1] - I[0, 2] + I[0, 3] + I[1, 1] - I[1, 3] + I[2, 2] - I[2, 3] + I[3, 3] """ - mu = self._poset.mobius_function + mu = self._poset.moebius_function R = self.base_ring() return self.sum_of_terms((A, R(mu(*A))) for A in self.basis().keys()) + mobius = deprecated_function_alias(19855, moebius) def __getitem__(self, A): """ @@ -305,7 +308,7 @@ def __call__(self, x, y): sage: P = posets.BooleanLattice(4) sage: I = P.incidence_algebra(QQ) - sage: mu = I.mobius() + sage: mu = I.moebius() sage: mu(0, 12) 1 sage: mu(0, 7) @@ -333,7 +336,7 @@ def to_matrix(self): sage: P = posets.BooleanLattice(2) sage: I = P.incidence_algebra(QQ) - sage: I.mobius().to_matrix() + sage: I.moebius().to_matrix() [ 1 -1 -1 1] [ 0 1 0 -1] [ 0 0 1 -1] @@ -350,7 +353,7 @@ def to_matrix(self): sage: P = posets.BooleanLattice(4) sage: I = P.incidence_algebra(QQ) - sage: mu = I.mobius() + sage: mu = I.moebius() sage: (mu*mu).to_matrix() == mu.to_matrix() * mu.to_matrix() True """ @@ -371,7 +374,7 @@ def is_unit(self): sage: P = posets.BooleanLattice(2) sage: I = P.incidence_algebra(QQ) - sage: mu = I.mobius() + sage: mu = I.moebius() sage: mu.is_unit() True sage: zeta = I.zeta() @@ -380,14 +383,14 @@ def is_unit(self): sage: x = mu - I.zeta() + I[2,2] sage: x.is_unit() False - sage: y = I.mobius() + I.zeta() + sage: y = I.moebius() + I.zeta() sage: y.is_unit() True This depends on the base ring:: sage: I = P.incidence_algebra(ZZ) - sage: y = I.mobius() + I.zeta() + sage: y = I.moebius() + I.zeta() sage: y.is_unit() False """ @@ -401,7 +404,7 @@ def __invert__(self): sage: P = posets.BooleanLattice(2) sage: I = P.incidence_algebra(QQ) - sage: mu = I.mobius() + sage: mu = I.moebius() sage: ~mu I[0, 0] + I[0, 1] + I[0, 2] + I[0, 3] + I[1, 1] + I[1, 3] + I[2, 2] + I[2, 3] + I[3, 3] @@ -415,7 +418,7 @@ def __invert__(self): sage: P = posets.BooleanLattice(4) sage: I = P.incidence_algebra(QQ) - sage: mu = I.mobius() + sage: mu = I.moebius() sage: ~mu == I.zeta() True sage: ~I.one() == I.one() @@ -436,7 +439,7 @@ class ReducedIncidenceAlgebra(CombinatorialFreeModule): The reduced incidence algebra `R_P` is a subalgebra of the incidence algebra `I_P` where `\alpha(x, y) = \alpha(x', y')` when - `[x, y]` is isomorphic to `[x', y']` as posets. Thus the delta, Mobius, + `[x, y]` is isomorphic to `[x', y']` as posets. Thus the delta, Möbius, and zeta functions are all elements of `R_P`. """ def __init__(self, I, prefix='R'): @@ -513,7 +516,7 @@ def some_elements(self): R[(0, 0)] - R[(0, 1)] + R[(0, 3)] - R[(0, 7)] + R[(0, 15)], R[(0, 0)] + R[(0, 1)] + R[(0, 3)] + R[(0, 7)] + R[(0, 15)]] """ - return [self.an_element(), self.mobius(), self.zeta()] + return [self.an_element(), self.moebius(), self.zeta()] @cached_method def one_basis(self): @@ -567,21 +570,23 @@ def zeta(self): """ return self.sum(self.basis()) + from sage.misc.superseded import deprecated_function_alias @cached_method - def mobius(self): + def moebius(self): """ - Return the Mobius function of ``self``. + Return the Möbius function of ``self``. EXAMPLES:: sage: P = posets.BooleanLattice(4) sage: R = P.incidence_algebra(QQ).reduced_subalgebra() - sage: R.mobius() + sage: R.moebius() R[(0, 0)] - R[(0, 1)] + R[(0, 3)] - R[(0, 7)] + R[(0, 15)] """ - mu = self._ambient._poset.mobius_function + mu = self._ambient._poset.moebius_function R = self.base_ring() return self.sum_of_terms((A, R(mu(*A))) for A in self.basis().keys()) + mobius = deprecated_function_alias(19855, moebius) @cached_method def _lift_basis(self, x): @@ -672,7 +677,7 @@ def _retract(self, x): True sage: R._retract(I.delta()) == R.delta() True - sage: R._retract(I.mobius()) == R.mobius() + sage: R._retract(I.moebius()) == R.moebius() True """ return self.sum_of_terms((k, x[k]) for k in self.basis().keys()) @@ -710,9 +715,9 @@ def _mul_(self, other): sage: x = R.an_element() sage: x * R.zeta() 2*R[(0, 0)] + 4*R[(0, 1)] + 9*R[(0, 3)] + 17*R[(0, 7)] + 28*R[(0, 15)] - sage: x * R.mobius() + sage: x * R.moebius() 2*R[(0, 0)] + R[(0, 3)] - 5*R[(0, 7)] + 12*R[(0, 15)] - sage: x * R.mobius() * R.zeta() == x + sage: x * R.moebius() * R.zeta() == x True """ P = self.parent() @@ -727,7 +732,7 @@ def to_matrix(self): sage: P = posets.BooleanLattice(2) sage: R = P.incidence_algebra(QQ).reduced_subalgebra() - sage: mu = R.mobius() + sage: mu = R.moebius() sage: mu.to_matrix() [ 1 -1 -1 1] [ 0 1 0 -1] diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index af47772aa01..284a5e0c065 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -734,7 +734,7 @@ def complements(self, element=None): INPUT: - - ``element`` - an element of the poset whose complement is + - ``element`` - an element of the lattice whose complement is returned. If ``None`` (default) then dictionary of complements for all elements having at least one complement is returned. @@ -1084,7 +1084,7 @@ def is_upper_semimodular(self): Return ``True`` if the lattice is upper semimodular and ``False`` otherwise. - A lattice is upper semimodular if for any `x` in the poset that is + A lattice is upper semimodular if for any `x` in the lattice that is covered by `y` and `z`, both `y` and `z` are covered by their join. See also :meth:`is_modular` and :meth:`is_lower_semimodular`. @@ -1126,7 +1126,7 @@ def is_lower_semimodular(self): Return ``True`` if the lattice is lower semimodular and ``False`` otherwise. - A lattice is lower semimodular if for any `x` in the poset that covers + A lattice is lower semimodular if for any `x` in the lattice that covers `y` and `z`, both `y` and `z` cover their meet. See also :meth:`is_modular` and :meth:`is_upper_semimodular`. @@ -1320,7 +1320,7 @@ def frattini_sublattice(self): def moebius_algebra(self, R): """ - Return the Mobius algebra of ``self`` over ``R``. + Return the Möbius algebra of ``self`` over ``R``. EXAMPLES:: @@ -1333,7 +1333,7 @@ def moebius_algebra(self, R): def quantum_moebius_algebra(self, q=None): """ - Return the quantum Mobius algebra of ``self`` with parameter ``q``. + Return the quantum Möbius algebra of ``self`` with parameter ``q``. INPUT: diff --git a/src/sage/combinat/posets/moebius_algebra.py b/src/sage/combinat/posets/moebius_algebra.py index ca6de4dc2de..f37a2765b07 100644 --- a/src/sage/combinat/posets/moebius_algebra.py +++ b/src/sage/combinat/posets/moebius_algebra.py @@ -269,8 +269,8 @@ def _to_natural_basis(self, x): """ M = self.realization_of() N = M.natural() - mobius = M._lattice.mobius_function - return N.sum_of_terms((y, mobius(y,x)) for y in M._lattice.order_ideal([x])) + moebius = M._lattice.moebius_function + return N.sum_of_terms((y, moebius(y,x)) for y in M._lattice.order_ideal([x])) def product_on_basis(self, x, y): """ @@ -456,11 +456,11 @@ def product_on_basis(self, x, y): """ L = self.realization_of()._lattice q = self.realization_of()._q - mobius = L.mobius_function + moebius = L.moebius_function rank = L.rank_function() R = L.rank() j = L.join(x,y) - return self.sum_of_terms(( z, mobius(a,z) * q**(R - rank(a)) ) + return self.sum_of_terms(( z, moebius(a,z) * q**(R - rank(a)) ) for z in L.order_filter([j]) for a in L.closed_interval(j, z)) @@ -478,10 +478,10 @@ def one(self): """ L = self.realization_of()._lattice q = self.realization_of()._q - mobius = L.mobius_function + moebius = L.moebius_function rank = L.rank_function() R = L.rank() - return self.sum_of_terms((x, mobius(y,x) * q**(rank(y) - R)) + return self.sum_of_terms((x, moebius(y,x) * q**(rank(y) - R)) for x in L for y in L.order_ideal([x])) natural = E diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 98cdbd6b2e9..0e16a16e90c 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -29,6 +29,7 @@ :meth:`~Posets.RestrictedIntegerPartitions` | Return the poset of integer partitions of `n`, ordered by restricted refinement. :meth:`~Posets.ShardPoset` | Return the shard intersection order. :meth:`~Posets.SSTPoset` | Return the poset on semistandard tableaux of shape `s` and largest entry `f` that is ordered by componentwise comparison. + :meth:`~Posets.StandardExample` | Return the standard example of a poset with dimension `n`. :meth:`~Posets.SymmetricGroupBruhatIntervalPoset` | The poset of permutations with respect to Bruhat order. :meth:`~Posets.SymmetricGroupBruhatOrderPoset` | The poset of permutations with respect to Bruhat order. :meth:`~Posets.SymmetricGroupWeakOrderPoset` | The poset of permutations of `\{ 1, 2, \ldots, n \}` with respect to the weak order. @@ -513,6 +514,65 @@ def tableaux_is_less_than(a,b): E = SemistandardTableaux(s, max_entry=f) return Poset((E, tableaux_is_less_than)) + @staticmethod + def StandardExample(n, facade=None): + r""" + Return the partially ordered set on ``2n`` elements with + dimension ``n``. + + Let `P` be the poset on `\{0, 1, 2, \ldots, 2n-1\}` whose defining + relations are that `i < j` for every `0 \leq i < n \leq j < 2n` + except when `i + n = j`. The poset `P` is the so-called + *standard example* of a poset with dimension `n`. + + INPUT: + + - ``n`` -- an integer `\ge 2`, dimension of the constructed poset + - ``facade`` (boolean) -- whether to make the returned poset a + facade poset (see :mod:`sage.categories.facade_sets`); the + default behaviour is the same as the default behaviour of + the :func:`~sage.combinat.posets.posets.Poset` constructor + + OUTPUT: + + The standard example of a poset of dimension `n`. + + EXAMPLES:: + + sage: A = Posets.StandardExample(3); A + Finite poset containing 6 elements + sage: A.dimension() + 3 + + REFERENCES: + + .. [Rosen] K. Rosen *Handbook of Discrete and Combinatorial + Mathematics* (1999), Chapman and Hall. + + .. [Garg] V. Garg *Introduction to Lattice Theory with Computer + Science Applications* (2015), Wiley. + + TESTS:: + + sage: A = Posets.StandardExample(10); A + Finite poset containing 20 elements + sage: len(A.cover_relations()) + 90 + + sage: P = Posets.StandardExample(5, facade=False) + sage: P(4) < P(3), P(4) > P(3) + (False, False) + """ + try: + n = Integer(n) + except TypeError: + raise TypeError("dimension must be an integer, not {0}".format(n)) + if n < 2: + raise ValueError("dimension must be at least 2, not {0}".format(n)) + return Poset((range(2*n), [[i, j+n] for i in range(n) + for j in range(n) if i != j]), + facade=facade) + @staticmethod def SymmetricGroupBruhatOrderPoset(n): """ diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index ef34f1983e7..9c71c4087e0 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -104,6 +104,7 @@ :meth:`~FinitePoset.dual` | Return the dual poset of this poset. :meth:`~FinitePoset.completion_by_cuts` | Return the Dedekind-MacNeille completion of the poset. :meth:`~FinitePoset.connected_components` | Return the connected components of the poset as subposets. + :meth:`~FinitePoset.ordinal_summands` | Return the ordinal summands of the poset. :meth:`~FinitePoset.subposet` | Return the subposet containing elements with partial order induced by this poset. :meth:`~FinitePoset.random_subposet` | Return a random subposet that contains each element with given probability. :meth:`~FinitePoset.canonical_label` | Return copy of the poset canonically (re)labelled with elements `\{0, \ldots, n-1\}`. @@ -171,7 +172,7 @@ :meth:`~FinitePoset.chain_polytope` | Return the chain polytope of the poset. :meth:`~FinitePoset.order_polytope` | Return the order polytope of the poset. -**Other & not yet classified** +**Graphs** .. csv-table:: :class: contentstable @@ -182,11 +183,20 @@ :meth:`~FinitePoset.cover_relations_graph` | Return the (undirected) graph of cover relations. :meth:`~FinitePoset.comparability_graph` | Return the comparability graph of the poset. :meth:`~FinitePoset.incomparability_graph` | Return the incomparability graph of the poset. + :meth:`~FinitePoset.linear_extensions_graph` | Return the linear extensions graph of the poset. + +**Other & not yet classified** + +.. csv-table:: + :class: contentstable + :widths: 30, 70 + :delim: | + :meth:`~FinitePoset.isomorphic_subposets` | Return all subposets isomorphic to another poset. :meth:`~FinitePoset.isomorphic_subposets_iterator` | Return an iterator over the subposets isomorphic to another poset. :meth:`~FinitePoset.has_isomorphic_subposet` | Return ``True`` if the poset contains a subposet isomorphic to another poset. - :meth:`~FinitePoset.mobius_function` | Return the value of Möbius function of given elements in the poset. - :meth:`~FinitePoset.mobius_function_matrix` | Return a matrix whose ``(i,j)`` entry is the value of the Möbius function evaluated at ``self.linear_extension()[i]`` and ``self.linear_extension()[j]``. + :meth:`~FinitePoset.moebius_function` | Return the value of Möbius function of given elements in the poset. + :meth:`~FinitePoset.moebius_function_matrix` | Return a matrix whose ``(i,j)`` entry is the value of the Möbius function evaluated at ``self.linear_extension()[i]`` and ``self.linear_extension()[j]``. :meth:`~FinitePoset.is_linear_extension` | Return whether ``l`` is a linear extension of ``self``. :meth:`~FinitePoset.linear_extension` | Return a linear extension of this poset. :meth:`~FinitePoset.linear_extensions` | Return the enumerated set of all the linear extensions of this poset. @@ -3046,45 +3056,48 @@ def cardinality(self): """ return Integer(self._hasse_diagram.order()) - def mobius_function(self,x,y): + from sage.misc.superseded import deprecated_function_alias + def moebius_function(self,x,y): r""" - Returns the value of the Mobius function of the poset on the + Returns the value of the Möbius function of the poset on the elements x and y. EXAMPLES:: sage: P = Poset([[1,2,3],[4],[4],[4],[]]) - sage: P.mobius_function(P(0),P(4)) + sage: P.moebius_function(P(0),P(4)) 2 - sage: sum([P.mobius_function(P(0),v) for v in P]) + sage: sum([P.moebius_function(P(0),v) for v in P]) 0 - sage: sum([abs(P.mobius_function(P(0),v)) \ + sage: sum([abs(P.moebius_function(P(0),v)) \ ....: for v in P]) 6 sage: for u,v in P.cover_relations_iterator(): - ....: if P.mobius_function(u,v) != -1: - ....: print "Bug in mobius_function!" + ....: if P.moebius_function(u,v) != -1: + ....: print "Bug in moebius_function!" :: sage: Q = Poset([[1,3,2],[4],[4,5,6],[6],[7],[7],[7],[]]) - sage: Q.mobius_function(Q(0),Q(7)) + sage: Q.moebius_function(Q(0),Q(7)) 0 - sage: Q.mobius_function(Q(0),Q(5)) + sage: Q.moebius_function(Q(0),Q(5)) 0 - sage: Q.mobius_function(Q(2),Q(7)) + sage: Q.moebius_function(Q(2),Q(7)) 2 - sage: Q.mobius_function(Q(3),Q(3)) + sage: Q.moebius_function(Q(3),Q(3)) 1 - sage: sum([Q.mobius_function(Q(0),v) for v in Q]) + sage: sum([Q.moebius_function(Q(0),v) for v in Q]) 0 """ i,j = map(self._element_to_vertex,(x,y)) - return self._hasse_diagram.mobius_function(i,j) + return self._hasse_diagram.moebius_function(i,j) + mobius_function = deprecated_function_alias(19855, moebius_function) - def mobius_function_matrix(self, ring = ZZ, sparse = False): + from sage.misc.superseded import deprecated_function_alias + def moebius_function_matrix(self, ring = ZZ, sparse = False): r""" - Returns a matrix whose ``(i,j)`` entry is the value of the Mobius + Returns a matrix whose ``(i,j)`` entry is the value of the Möbius function evaluated at ``self.linear_extension()[i]`` and ``self.linear_extension()[j]``. @@ -3098,9 +3111,9 @@ def mobius_function_matrix(self, ring = ZZ, sparse = False): sage: P = Poset([[4,2,3],[],[1],[1],[1]]) sage: x,y = (P.linear_extension()[0],P.linear_extension()[1]) - sage: P.mobius_function(x,y) + sage: P.moebius_function(x,y) -1 - sage: M = P.mobius_function_matrix(); M + sage: M = P.moebius_function_matrix(); M [ 1 -1 -1 -1 2] [ 0 1 0 0 -1] [ 0 0 1 0 -1] @@ -3113,15 +3126,16 @@ def mobius_function_matrix(self, ring = ZZ, sparse = False): We now demonstrate the usage of the optional parameters:: - sage: P.mobius_function_matrix(ring=QQ, sparse=False).parent() + sage: P.moebius_function_matrix(ring=QQ, sparse=False).parent() Full MatrixSpace of 5 by 5 dense matrices over Rational Field """ - M = self._hasse_diagram.mobius_function_matrix() + M = self._hasse_diagram.moebius_function_matrix() if ring is not ZZ: M = M.change_ring(ring) if not sparse: M = M.dense_matrix() return M + mobius_function_matrix = deprecated_function_alias(19855, moebius_function_matrix) def lequal_matrix(self, ring = ZZ, sparse = False): """ @@ -3635,6 +3649,96 @@ def connected_components(self): return [self.subposet(self._vertex_to_element(x) for x in cc) for cc in comps] + def ordinal_summands(self): + r""" + Return the ordinal summands of the poset as subposets. + + The ordinal summands of a poset `P` is the longest list of + non-empty subposets `P_1, \ldots, P_n` whose ordinal sum is `P`. This + decomposition is unique. + + .. SEEALSO:: + + :meth:`ordinal_sum` + + EXAMPLES:: + + sage: P = Poset({'a': ['c', 'd'], 'b': ['d'], 'c': ['x', 'y'], + ....: 'd': ['x', 'y']}) + sage: parts = P.ordinal_summands(); parts + [Finite poset containing 4 elements, Finite poset containing 2 elements] + sage: sorted(parts[0]) + ['a', 'b', 'c', 'd'] + sage: Q = parts[0].ordinal_sum(parts[1]) + sage: Q.is_isomorphic(P) + True + + ALGORITHM: + + Suppose that a poset `P` is the ordinal sum of posets `L` and `U`. Then + `P` contains maximal antichains `l` and `u` such that every element of + `u` covers every element of `l`; they correspond to maximal elements of + `L` and minimal elements of `U`. + + We consider a linear extension `x_1,\ldots,x_n` of the poset's + elements. + + We keep track of the maximal elements of subposet induced by elements + `0,\ldots,x_i` and minimal elements of subposet induced by elements + `x_{i+1},\ldots,x_n`, incrementing `i` one by one. We then check if + `l` and `u` fit the previous description. + + TESTS:: + + sage: Poset().ordinal_summands() + [Finite poset containing 0 elements] + sage: Poset({1: []}).ordinal_summands() + [Finite poset containing 1 elements] + """ + n = self.cardinality() + if n <= 0: + return [self] + + H = self._hasse_diagram + cut_points = [-1] + in_degrees = H.in_degree() + lower = set([]) + upper = set(H.sources()) + + for e in range(n): + + # update 'lower' by adding 'e' to it + lower.add(e) + lower.difference_update(H.neighbors_in(e)) + + # update 'upper' too + upper.discard(e) + up_covers = H.neighbors_out(e) + for uc in up_covers: + in_degrees[uc] -= 1 + if in_degrees[uc] == 0: + upper.add(uc) + + if e+1 in up_covers: + uc_len = len(upper) + for l in lower: + if H.out_degree(l) != uc_len: + break + else: + for l in lower: + if set(H.neighbors_out(l)) != upper: + break + else: + cut_points.append(e) + + cut_points.append(n-1) + + parts = [] + for i,j in zip(cut_points,cut_points[1:]): + parts.append(self.subposet([self._vertex_to_element(e) + for e in range(i+1,j+1)])) + return parts + def product(self, other): """ Return the Cartesian product of the poset with ``other``. @@ -3873,7 +3977,7 @@ def ordinal_sum(self, other, labels='pairs'): .. SEEALSO:: - :meth:`disjoint_union`, :meth:`ordinal_product` + :meth:`ordinal_summands`, :meth:`disjoint_union`, :meth:`ordinal_product` """ from sage.combinat.posets.lattices import LatticePoset, \ JoinSemilattice, MeetSemilattice, FiniteLatticePoset, \ @@ -5218,7 +5322,7 @@ def characteristic_polynomial(self): \sum_{x \in P} \mu(\hat{0}, x) q^{n-\rho(x)} \in \ZZ[q], - where `\rho` is the rank function, and `\mu` is the Moebius + where `\rho` is the rank function, and `\mu` is the Möbius function of `P`. See section 3.10 of [EnumComb1]_. @@ -5244,7 +5348,7 @@ def characteristic_polynomial(self): n = rk(hasse.maximal_elements()[0]) x0 = hasse.minimal_elements()[0] q = polygen(ZZ, 'q') - return sum(hasse.mobius_function(x0, x) * q**(n - rk(x)) for x in hasse) + return sum(hasse.moebius_function(x0, x) * q**(n - rk(x)) for x in hasse) def chain_polynomial(self): """ @@ -5628,7 +5732,7 @@ def is_eulerian(self): return False H = self._hasse_diagram - M = H.mobius_function_matrix() + M = H.moebius_function_matrix() for i in range(n): for j in range(i): if H.is_lequal(j, i): diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index e4c21a06386..69d66ab3f2f 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" q-Analogues """ @@ -313,13 +314,13 @@ def q_binomial(n, k, q=None, algorithm='auto'): else: num = prod(one - q**i for i in range(n-k+1, n+1)) try: - return num//denom - except TypeError: try: - return num/denom - except (TypeError,ZeroDivisionError): - #try a substitution - return q_binomial(n,k)(q) + return num // denom + except TypeError: + return num / denom + except (TypeError, ZeroDivisionError): + # use substitution instead + return q_binomial(n,k)(q) elif algorithm == 'cyclo_generic': from sage.rings.polynomial.cyclotomic import cyclotomic_value return prod(cyclotomic_value(d,q) @@ -657,7 +658,7 @@ def q_subgroups_of_abelian_group(la, mu, q=None, algorithm='birkhoff'): Mathematical Society 101, no. 4 (1987): 771-775. :doi:`10.1090/S0002-9939-1987-0911049-8` - .. [Delsarte48] S. Delsarte, *Fonctions de Mobius Sur Les Groupes Abeliens + .. [Delsarte48] S. Delsarte, *Fonctions de Möbius Sur Les Groupes Abeliens Finis*, Annals of Mathematics, second series, Vol. 45, No. 3, (Jul 1948), pp. 600-609. http://www.jstor.org/stable/1969047 diff --git a/src/sage/combinat/quickref.py b/src/sage/combinat/quickref.py index 64d9cd70fac..efaeb99224c 100644 --- a/src/sage/combinat/quickref.py +++ b/src/sage/combinat/quickref.py @@ -7,7 +7,7 @@ sage: s = oeis([1,3,19,211]); s # optional - internet 0: A000275: Coefficients of a Bessel function (reciprocal of J_0(z)); also pairs of permutations with rise/rise forbidden. sage: s[0].programs() # optional - internet - 0: (PARI) a(n)=if(n<0,0,n!^2*4^n*polcoeff(1/besselj(0,x+x*O(x^(2*n))),2*n)) /* _Michael Somos_, May 17 2004 */ + 0: (PARI) {a(n) = if( n<0, 0, n!^2 * 4^n * polcoeff( 1 / besselj(0, x + x * O(x^(2*n))), 2*n))}; /* _Michael Somos_, May 17 2004 */ Combinatorial objects:: diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index 7c9f8105ba6..983baa70fef 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Functions that compute some of the sequences in Sloane's tables @@ -6451,7 +6452,7 @@ def _eval(self, n): class A008683(SloaneSequence): def __init__(self): r""" - Moebius function `\mu(n)`. + Möbius function `\mu(n)`. INPUT: diff --git a/src/sage/combinat/species/composition_species.py b/src/sage/combinat/species/composition_species.py index 88e5544305f..0d6dbb1b67e 100644 --- a/src/sage/combinat/species/composition_species.py +++ b/src/sage/combinat/species/composition_species.py @@ -63,11 +63,22 @@ def transport(self, perm): f, gs = self._list pi = self._partition.transport(perm) f = f.change_labels(pi._list) - g = [g.change_labels(part) for g,part in zip(gs, pi.labels())] + g = [g.change_labels(part) for g,part in zip(gs, pi)] return self.__class__(self, self._labels, pi, f, gs) def change_labels(self, labels): """ + Return a relabelled structure. + + INPUT: + + - ``labels``, a list of labels. + + OUTPUT: + + A structure with the i-th label of self replaced with the i-th + label of the list. + EXAMPLES:: sage: p = PermutationGroupElement((2,3)) @@ -81,8 +92,8 @@ def change_labels(self, labels): """ f, gs = self._list pi = self._partition.change_labels(labels) - f = f.change_labels(pi._list) - g = [g.change_labels(part) for g,part in zip(gs, pi.labels())] + f = f.change_labels(list(pi)) + g = [g.change_labels(part) for g,part in zip(gs, pi)] return self.__class__(self, labels, pi, f, g) @@ -155,11 +166,11 @@ def _structures(self, structure_class, labels): from itertools import product P = PartitionSpecies() for pi in P.structures(labels): - #The labels of the G-structures will be just be the things - #in labels - gs = product(*[self._G.structures(part.labels()) for part in pi]) + # The labels of the G-structures will be just be the things + # in labels + gs = product(*[self._G.structures(part.label_subset()) for part in pi]) - #The labels of the F-structure will be set objects + # The labels of the F-structure will be set objects fs = self._F.structures(list(pi)) for f, gg in product(fs, gs): yield structure_class(self, labels, pi, f, gg) diff --git a/src/sage/combinat/species/library.py b/src/sage/combinat/species/library.py index 1aac7491563..ae6adf3eb31 100644 --- a/src/sage/combinat/species/library.py +++ b/src/sage/combinat/species/library.py @@ -53,14 +53,14 @@ def SimpleGraphSpecies(): TESTS:: sage: seq = S.isotype_generating_series().counts(6)[1:] - sage: oeis(seq)[0] # optional - internet + sage: oeis(seq)[0] # optional -- internet A000088: Number of graphs on n unlabeled nodes. :: sage: seq = S.generating_series().counts(10)[1:] - sage: oeis(seq)[0] # optional - internet - A006125: 2^(n(n-1)/2). + sage: oeis(seq)[0] # optional -- internet + A006125: a(n) = 2^(n(n-1)/2). """ E = SetSpecies() E2 = SetSpecies(size=2) @@ -97,7 +97,7 @@ def BinaryTreeSpecies(): TESTS:: sage: seq = B.isotype_generating_series().counts(10)[1:] - sage: oeis(seq)[0] # optional - internet + sage: oeis(seq)[0] # optional -- internet A000108: Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!). Also called Segner numbers. """ B = CombinatorialSpecies() @@ -130,7 +130,7 @@ def BinaryForestSpecies(): TESTS:: sage: seq = F.isotype_generating_series().counts(10)[1:] - sage: oeis(seq)[0] # optional - internet + sage: oeis(seq)[0] # optional -- internet A052854: Number of forests of ordered trees on n total nodes. """ B = BinaryTreeSpecies() diff --git a/src/sage/combinat/species/partition_species.py b/src/sage/combinat/species/partition_species.py index c9f9db9bbac..6781fd00310 100644 --- a/src/sage/combinat/species/partition_species.py +++ b/src/sage/combinat/species/partition_species.py @@ -111,6 +111,17 @@ def automorphism_group(self): def change_labels(self, labels): """ + Return a relabelled structure. + + INPUT: + + - ``labels``, a list of labels. + + OUTPUT: + + A structure with the i-th label of self replaced with the i-th + label of the list. + EXAMPLES:: sage: p = PermutationGroupElement((2,3)) diff --git a/src/sage/combinat/species/product_species.py b/src/sage/combinat/species/product_species.py index 9e1f98c9a52..2f3e7ff01d4 100644 --- a/src/sage/combinat/species/product_species.py +++ b/src/sage/combinat/species/product_species.py @@ -21,7 +21,20 @@ from sage.misc.cachefunc import cached_function from sage.structure.unique_representation import UniqueRepresentation -class ProductSpeciesStructure(GenericSpeciesStructure): +class ProductSpeciesStructure(GenericSpeciesStructure): + def __init__(self, parent, labels, subset, left, right): + """ + TESTS:: + + sage: S = species.SetSpecies() + sage: F = S * S + sage: a = F.structures(['a','b','c']).random_element() + sage: a == loads(dumps(a)) + True + """ + self._subset = subset + GenericSpeciesStructure.__init__(self, parent, labels, [left, right]) + def __repr__(self): """ Returns the string representation of this object. @@ -41,19 +54,6 @@ def __repr__(self): right = "(%s)"%right return "%s*%s"%(left, right) - def __init__(self, parent, labels, subset, left, right): - """ - TESTS:: - - sage: S = species.SetSpecies() - sage: F = S * S - sage: a = F.structures(['a','b','c']).random_element() - sage: a == loads(dumps(a)) - True - """ - self._subset = subset - GenericSpeciesStructure.__init__(self, parent, labels, [left, right]) - def transport(self, perm): """ EXAMPLES:: @@ -68,8 +68,8 @@ def transport(self, perm): """ left, right = self._list new_subset = self._subset.transport(perm) - left_labels = new_subset.labels() - right_labels = new_subset.complement().labels() + left_labels = new_subset.label_subset() + right_labels = new_subset.complement().label_subset() return self.__class__(self.parent(), self._labels, new_subset, @@ -108,8 +108,8 @@ def canonical_label(self): """ left, right = self._list new_subset = self._subset.canonical_label() - left_labels = new_subset.labels() - right_labels = new_subset.complement().labels() + left_labels = new_subset.label_subset() + right_labels = new_subset.complement().label_subset() return self.__class__(self.parent(), self._labels, new_subset, @@ -118,6 +118,17 @@ def canonical_label(self): def change_labels(self, labels): """ + Return a relabelled structure. + + INPUT: + + - ``labels``, a list of labels. + + OUTPUT: + + A structure with the i-th label of self replaced with the i-th + label of the list. + EXAMPLES:: sage: S = species.SetSpecies() @@ -129,8 +140,8 @@ def change_labels(self, labels): """ left, right = self._list new_subset = self._subset.change_labels(labels) - left_labels = new_subset.labels() - right_labels = new_subset.complement().labels() + left_labels = new_subset.label_subset() + right_labels = new_subset.complement().label_subset() return self.__class__(self.parent(), labels, new_subset, left.change_labels(left_labels), @@ -221,11 +232,40 @@ def __init__(self, F, G, min=None, max=None, weight=None): _default_structure_class = ProductSpeciesStructure + def left_factor(self): + """ + Returns the left factor of this product. + + EXAMPLES:: + + sage: P = species.PermutationSpecies() + sage: X = species.SingletonSpecies() + sage: F = P*X + sage: F.left_factor() + Permutation species + """ + return self._F + + def right_factor(self): + """ + Returns the right factor of this product. + + EXAMPLES:: + + sage: P = species.PermutationSpecies() + sage: X = species.SingletonSpecies() + sage: F = P*X + sage: F.right_factor() + Singleton species + """ + return self._G + def _name(self): """ Note that we use a function to return the name of this species because we can't do it in the __init__ method due to it - requiring that self._F and self._G already be unpickled. + requiring that self.left_factor() and self.right_factor() + already be unpickled. EXAMPLES:: @@ -234,7 +274,7 @@ def _name(self): sage: F._name() 'Product of (Permutation species) and (Permutation species)' """ - return "Product of (%s) and (%s)"%(self._F, self._G) + return "Product of (%s) and (%s)"%(self.left_factor(), self.right_factor()) def _structures(self, structure_class, labels): """ @@ -271,12 +311,12 @@ def _times_gen(self, structure_class, attr, labels): S = SubsetSpecies() for u in getattr(S, attr)(labels): - vl = u.complement().labels() - ul = u.labels() - if c(self._F, len(ul)) == 0 or c(self._G, len(vl)) == 0: + vl = u.complement().label_subset() + ul = u.label_subset() + if c(self.left_factor(), len(ul)) == 0 or c(self.right_factor(), len(vl)) == 0: continue - for x in getattr(self._F, attr)(ul): - for y in getattr(self._G, attr)(vl): + for x in getattr(self.left_factor(), attr)(ul): + for y in getattr(self.right_factor(), attr)(vl): yield structure_class(self, labels, u, x, y) def _gs(self, series_ring, base_ring): @@ -288,7 +328,8 @@ def _gs(self, series_ring, base_ring): sage: F.generating_series().coefficients(5) [1, 2, 3, 4, 5] """ - res = self._F.generating_series(base_ring) * self._G.generating_series(base_ring) + res = (self.left_factor().generating_series(base_ring) * + self.right_factor().generating_series(base_ring)) if self.is_weighted(): res = self._weight * res return res @@ -303,8 +344,8 @@ def _itgs(self, series_ring, base_ring): sage: F.isotype_generating_series().coefficients(5) [1, 2, 5, 10, 20] """ - res = (self._F.isotype_generating_series(base_ring) * - self._G.isotype_generating_series(base_ring)) + res = (self.left_factor().isotype_generating_series(base_ring) * + self.right_factor().isotype_generating_series(base_ring)) if self.is_weighted(): res = self._weight * res return res @@ -322,8 +363,8 @@ def _cis(self, series_ring, base_ring): 4*p[1, 1, 1] + 4*p[2, 1] + 2*p[3], 5*p[1, 1, 1, 1] + 6*p[2, 1, 1] + 3*p[2, 2] + 4*p[3, 1] + 2*p[4]] """ - res = (self._F.cycle_index_series(base_ring) * - self._G.cycle_index_series(base_ring)) + res = (self.left_factor().cycle_index_series(base_ring) * + self.right_factor().cycle_index_series(base_ring)) if self.is_weighted(): res = self._weight * res return res @@ -355,7 +396,9 @@ def weight_ring(self): sage: C.weight_ring() Univariate Polynomial Ring in t over Rational Field """ - return self._common_parent([self._F.weight_ring(), self._G.weight_ring(), self._weight.parent()]) + return self._common_parent([self.left_factor().weight_ring(), + self.right_factor().weight_ring(), + self._weight.parent()]) def _equation(self, var_mapping): """ diff --git a/src/sage/combinat/species/structure.py b/src/sage/combinat/species/structure.py index 748de2f2de1..9be327ddc3d 100644 --- a/src/sage/combinat/species/structure.py +++ b/src/sage/combinat/species/structure.py @@ -37,32 +37,17 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from sage.combinat.combinat import CombinatorialClass, CombinatorialObject -from sage.structure.sage_object import SageObject from sage.rings.integer import Integer from copy import copy -class SpeciesStructure(SageObject): - def parent(self): - """ - Returns the species that this structure is associated with. - - EXAMPLES:: - sage: L = species.LinearOrderSpecies() - sage: a,b = L.structures([1,2]) - sage: a.parent() - Linear order species - """ - try: - return self._parent - except AttributeError: - raise NotImplementedError - -class GenericSpeciesStructure(CombinatorialObject, SpeciesStructure): +class GenericSpeciesStructure(CombinatorialObject): def __init__(self, parent, labels, list): """ - EXAMPLES:: + This is a base class from which the classes for the structures inherit. + EXAMPLES:: + sage: from sage.combinat.species.structure import GenericSpeciesStructure sage: a = GenericSpeciesStructure(None, [2,3,4], [1,2,3]) sage: a @@ -76,6 +61,22 @@ def __init__(self, parent, labels, list): self._labels = labels CombinatorialObject.__init__(self, list) + def parent(self): + """ + Returns the species that this structure is associated with. + + EXAMPLES:: + + sage: L = species.LinearOrderSpecies() + sage: a,b = L.structures([1,2]) + sage: a.parent() + Linear order species + """ + try: + return self._parent + except AttributeError: + raise NotImplementedError + def __repr__(self): """ EXAMPLES:: @@ -85,7 +86,7 @@ def __repr__(self): sage: a [2, 3, 4] """ - return repr(self.labels()) + return repr([self._relabel(i) for i in self._list]) def __eq__(self, other): """ @@ -105,17 +106,35 @@ def __eq__(self, other): def labels(self): """ + Returns the labels used for this structure. + + .. note:: + + This includes labels which may not "appear" in this + particular structure. + EXAMPLES:: sage: P = species.SubsetSpecies() - sage: S = P.structures(["a", "b", "c"]) - sage: [s.labels() for s in S] - [[], ['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']] + sage: s = P.structures(["a", "b", "c"]).random_element() + sage: s.labels() + ['a', 'b', 'c'] """ - return [self._relabel(i) for i in self._list] + return copy(self._labels) def change_labels(self, labels): """ + Return a relabelled structure. + + INPUT: + + - ``labels``, a list of labels. + + OUTPUT: + + A structure with the i-th label of self replaced with the i-th + label of the list. + EXAMPLES:: sage: P = species.SubsetSpecies() @@ -166,9 +185,32 @@ def is_isomorphic(self, x): else: return False +#For backward compatibility. This should be removed in the near +#future since I doubt that there is any code that depends directly on +#SpeciesStructure. +SpeciesStructure = GenericSpeciesStructure + class SpeciesStructureWrapper(GenericSpeciesStructure): def __init__(self, parent, s, **options): """ + This is a class for the structures of species such as the sum + species that do not provide "additional" structure. For example, + if you have the sum `C` of species `A` and `B`, + then a structure of `C` will either be either something from `A` or `B`. + Instead of just returning one of these directly, a "wrapper" is + put around them so that they have their parent is `C` rather than `A` or + `B`:: + + sage: X = species.SingletonSpecies() + sage: X2 = X+X + sage: s = X2.structures([1]).random_element(); s + 1 + sage: s.parent() + Sum of (Singleton species) and (Singleton species) + sage: from sage.combinat.species.structure import SpeciesStructureWrapper + sage: issubclass(type(s), SpeciesStructureWrapper) + True + EXAMPLES:: sage: E = species.SetSpecies(); B = E+E @@ -233,19 +275,31 @@ def canonical_label(self): """ return self.__class__(self._parent, self._s.canonical_label(), **self._options) - def labels(self): + def change_labels(self, labels): """ + Return a relabelled structure. + + INPUT: + + - ``labels``, a list of labels. + + OUTPUT: + + A structure with the i-th label of self replaced with the i-th + label of the list. + EXAMPLES:: - sage: P = species.PartitionSpecies() - sage: s = (P+P).structures([1,2,3]).random_element(); s - {{1, 3}, {2}} - sage: s.labels() - [{1, 3}, {2}] - sage: type(_) - + sage: X = species.SingletonSpecies() + sage: X2 = X+X + sage: s = X2.structures([1]).random_element(); s + 1 + sage: s.change_labels(['a']) + 'a' """ - return self._s.labels() + c = GenericSpeciesStructure.change_labels(self, labels) + c._s = c._s.change_labels(labels) + return c ############################################################## @@ -254,6 +308,15 @@ def labels(self): class SpeciesWrapper(CombinatorialClass): def __init__(self, species, labels, iterator, generating_series, name, structure_class): """ + This is a abstract base class for the set of structures of a + species as well as the set of isotypes of the species. + + .. note:: + + One typically does not use :class:`SpeciesWrapper` + directly, but instead instantiates one of its subclasses: + :class:`StructuresWrapper` or :class:`IsotypesWrapper`. + EXAMPLES:: sage: from sage.combinat.species.structure import SpeciesWrapper @@ -273,6 +336,20 @@ def __init__(self, species, labels, iterator, generating_series, name, structure self._name = "%s for %s with labels %s"%(name, species, labels) self._structure_class = structure_class if structure_class is not None else species._default_structure_class + def labels(self): + """ + Returns the labels used on these structures. If `X` is the + species, then :meth:`labels` returns the preimage of these + structures under the functor `X`. + + EXAMPLES:: + + sage: F = species.SetSpecies() + sage: F.structures([1,2,3]).labels() + [1, 2, 3] + """ + return copy(self._labels) + def __iter__(self): """ EXAMPLES:: @@ -302,6 +379,8 @@ def __iter__(self): def cardinality(self): """ + Returns the number of structures in this set. + EXAMPLES:: sage: F = species.SetSpecies() @@ -313,6 +392,10 @@ def cardinality(self): class StructuresWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ + A base class for the set of structures of a species with given + set of labels. An object of this type is returned when you + call the :meth:`structures` method of a species. + EXAMPLES:: sage: F = species.SetSpecies() @@ -329,6 +412,10 @@ def __init__(self, species, labels, structure_class): class IsotypesWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ + A base class for the set of isotypes of a species with given + set of labels. An object of this type is returned when you + call the :meth:`isotypes` method of a species. + EXAMPLES:: sage: F = species.SetSpecies() @@ -346,6 +433,10 @@ def __init__(self, species, labels, structure_class): class SimpleStructuresWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ + .. warning:: + + This is deprecated and currently not used for anything. + EXAMPLES:: sage: F = species.SetSpecies() @@ -359,9 +450,14 @@ def __init__(self, species, labels, structure_class): "Simple structures", structure_class) + class SimpleIsotypesWrapper(SpeciesWrapper): def __init__(self, species, labels, structure_class): """ + .. warning:: + + This is deprecated and currently not used for anything. + EXAMPLES:: sage: F = species.SetSpecies() diff --git a/src/sage/combinat/species/subset_species.py b/src/sage/combinat/species/subset_species.py index 2e6fbfbdfdb..3064a7509d1 100644 --- a/src/sage/combinat/species/subset_species.py +++ b/src/sage/combinat/species/subset_species.py @@ -49,13 +49,17 @@ def canonical_label(self): rng = range(1, len(self._list)+1) return self.__class__(self.parent(), self._labels, rng) - def labels(self): + + def label_subset(self): """ + Returns a subset of the labels that "appear" in this + structure. + EXAMPLES:: sage: P = species.SubsetSpecies() sage: S = P.structures(["a", "b", "c"]) - sage: [s.labels() for s in S] + sage: [s.label_subset() for s in S] [[], ['a'], ['b'], ['c'], ['a', 'b'], ['a', 'c'], ['b', 'c'], ['a', 'b', 'c']] """ return [self._relabel(i) for i in self._list] diff --git a/src/sage/combinat/species/sum_species.py b/src/sage/combinat/species/sum_species.py index 62db8ff11a5..62f54960900 100644 --- a/src/sage/combinat/species/sum_species.py +++ b/src/sage/combinat/species/sum_species.py @@ -61,11 +61,38 @@ def __init__(self, F, G, min=None, max=None, weight=None): _default_structure_class = SumSpeciesStructure + def left_summand(self): + """ + Returns the left summand of this species. + + EXAMPLES:: + + sage: P = species.PermutationSpecies() + sage: F = P + P*P + sage: F.left_summand() + Permutation species + """ + return self._F + + def right_summand(self): + """ + Returns the right summand of this species. + + EXAMPLES:: + + sage: P = species.PermutationSpecies() + sage: F = P + P*P + sage: F.right_summand() + Product of (Permutation species) and (Permutation species) + """ + return self._G + def _name(self): """ Note that we use a function to return the name of this species because we can't do it in the __init__ method due to it - requiring that self._F and self._G already be unpickled. + requiring that self.left_summand() and self.right_summand() + already be unpickled. EXAMPLES:: @@ -74,7 +101,7 @@ def _name(self): sage: F._name() 'Sum of (Permutation species) and (Permutation species)' """ - return "Sum of (%s) and (%s)"%(self._F, self._G) + return "Sum of (%s) and (%s)"%(self.left_summand(), self.right_summand()) def _structures(self, structure_class, labels): """ @@ -85,10 +112,10 @@ def _structures(self, structure_class, labels): sage: F.structures([1,2]).list() [[1, 2], [2, 1], [1, 2], [2, 1]] """ - for res in self._F.structures(labels): + for res in self.left_summand().structures(labels): yield structure_class(self, res, tag="left") - for res in self._G.structures(labels): + for res in self.right_summand().structures(labels): yield structure_class(self, res, tag="right") def _isotypes(self, structure_class, labels): @@ -117,7 +144,8 @@ def _gs(self, series_ring, base_ring): sage: F.generating_series().coefficients(5) [2, 2, 2, 2, 2] """ - return self._F.generating_series(base_ring) + self._G.generating_series(base_ring) + return (self.left_summand().generating_series(base_ring) + + self.right_summand().generating_series(base_ring)) def _itgs(self, series_ring, base_ring): @@ -131,8 +159,8 @@ def _itgs(self, series_ring, base_ring): sage: F.isotype_generating_series().coefficients(5) [2, 2, 4, 6, 10] """ - return (self._F.isotype_generating_series(base_ring) + - self._G.isotype_generating_series(base_ring)) + return (self.left_summand().isotype_generating_series(base_ring) + + self.right_summand().isotype_generating_series(base_ring)) def _cis(self, series_ring, base_ring): """ @@ -149,7 +177,8 @@ def _cis(self, series_ring, base_ring): 2*p[1, 1, 1] + 2*p[2, 1] + 2*p[3], 2*p[1, 1, 1, 1] + 2*p[2, 1, 1] + 2*p[2, 2] + 2*p[3, 1] + 2*p[4]] """ - return self._F.cycle_index_series(base_ring) + self._G.cycle_index_series(base_ring) + return (self.left_summand().cycle_index_series(base_ring) + + self.right_summand().cycle_index_series(base_ring)) def weight_ring(self): """ @@ -171,7 +200,8 @@ def weight_ring(self): sage: C.weight_ring() Univariate Polynomial Ring in t over Rational Field """ - return self._common_parent([self._F.weight_ring(), self._G.weight_ring()]) + return self._common_parent([self.left_summand().weight_ring(), + self.right_summand().weight_ring()]) def _equation(self, var_mapping): """ diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index 9989765ece4..5c8b2021de0 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -496,7 +496,7 @@ def kappa_preimages(self): sage: kappa = SC.kappa_preimages() sage: for F in SC: print F, [w.reduced_word() for w in kappa[F]] (0, 1) [[]] - (0, 4) [[2, 1], [2]] + (0, 4) [[2], [2, 1]] (1, 2) [[1]] (2, 3) [[1, 2]] (3, 4) [[1, 2, 1]] @@ -948,8 +948,8 @@ def kappa_preimage(self): (0, 4) sage: F.kappa_preimage() [ - [-1 1] [ 1 0] - [-1 0], [ 1 -1] + [ 1 0] [-1 1] + [ 1 -1], [-1 0] ] """ W = self.parent().group() diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 36f06856d7a..cd79c7622ba 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -223,7 +223,7 @@ sage: oeis([1,1,2,5,14]) # optional -- internet 0: A000108: Catalan numbers: C(n) = binomial(2n,n)/(n+1) = (2n)!/(n!(n+1)!). Also called Segner numbers. - 1: A120588: G.f. satisfies: 3*A(x) = 2 + x + A(x)^2, starting with [1,1,1]. + 1: A120588: G.f. satisfies: 3*A(x) = 2 + x + A(x)^2, with a(0) = 1. 2: A080937: Number of Catalan paths (nonnegative, starting and ending at 0, step +/-1) of 2*n steps with all values <= 5. The result suggests that the trees are counted by one of the most famous @@ -1730,10 +1730,9 @@ sage: oeis(L) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - 1: A185357: Expansion of 1/(1 - x - x^2 + x^18 - x^20). + 1: A212804: Expansion of (1-x)/(1-x-x^2). 2: A132636: Fib(n) mod n^3. - This is an immediate consequence of the recurrence relation. One can also generate immediately all the Fibonacci words of a given length, with the same limitations resulting from the generic display. diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index 8efcd29bb6a..d5172fafd44 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -5732,7 +5732,7 @@ def is_tangent(self): The finite word ``self`` must be defined on a two-letter alphabet. A binary word is said to be *tangent* if it can appear in - infintely many cutting sequences of a smooth curve, where each + infinitely many cutting sequences of a smooth curve, where each cutting sequence is observed on a progressively smaller grid. This class of words strictly contains the class of 1-balanced diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 07fd1e0c490..3840f257d97 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # coding=utf-8 r""" Word morphisms/substitutions diff --git a/src/sage/crypto/block_cipher/miniaes.py b/src/sage/crypto/block_cipher/miniaes.py index ebb682cdfe4..a40545d2e6e 100644 --- a/src/sage/crypto/block_cipher/miniaes.py +++ b/src/sage/crypto/block_cipher/miniaes.py @@ -30,7 +30,7 @@ from sage.matrix.matrix_space import MatrixSpace from sage.monoids.string_monoid import BinaryStrings from sage.monoids.string_monoid_element import StringMonoidElement -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.rings.integer import Integer from sage.structure.sage_object import SageObject diff --git a/src/sage/crypto/block_cipher/sdes.py b/src/sage/crypto/block_cipher/sdes.py index 904728c6cd3..c45a21784ea 100644 --- a/src/sage/crypto/block_cipher/sdes.py +++ b/src/sage/crypto/block_cipher/sdes.py @@ -1255,7 +1255,7 @@ def permute_substitute(self, B, key): if len(key) != 8: raise ValueError("input key must be an 8-bit subkey") - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField GF = FiniteField(2, "x") bin = BinaryStrings() bin_to_GF2 = {bin("0"): GF(0), bin("1"): GF(1)} diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 4ecc77ccac9..4509fbae15e 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Boolean functions @@ -31,9 +32,9 @@ from libc.string cimport memcpy from sage.structure.sage_object cimport SageObject from sage.rings.integer_ring import ZZ from sage.rings.integer cimport Integer -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.polynomial.pbori import BooleanPolynomial -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.polynomial.polynomial_element import is_Polynomial @@ -108,7 +109,7 @@ cdef long yellow_code(unsigned long a): cdef reed_muller(mp_limb_t* f, int ldn): r""" - The Reed Muller transform (also known as binary Moebius transform) + The Reed Muller transform (also known as binary Möbius transform) is an orthogonal transform. For a function `f` defined by .. math:: f(x) = \bigoplus_{I\subset\{1,\ldots,n\}} \left(a_I \prod_{i\in I} x_i\right) diff --git a/src/sage/crypto/lfsr.py b/src/sage/crypto/lfsr.py index aa154d500d5..20285e0904c 100644 --- a/src/sage/crypto/lfsr.py +++ b/src/sage/crypto/lfsr.py @@ -148,7 +148,7 @@ from sage.structure.all import Sequence from sage.rings.all import Integer, PolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField def lfsr_sequence(key, fill, n): diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index 3af4eb37aa3..a9c0e0f0fe8 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -431,7 +431,7 @@ from sage.matrix.constructor import matrix from sage.matrix.constructor import column_matrix from sage.structure.element import Matrix -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.rings.integer import Integer from sage.structure.sage_object import SageObject from sage.matrix.matrix_space import MatrixSpace diff --git a/src/sage/crypto/mq/sbox.py b/src/sage/crypto/mq/sbox.py index 97b30adbefd..d95d0bcd2be 100644 --- a/src/sage/crypto/mq/sbox.py +++ b/src/sage/crypto/mq/sbox.py @@ -7,7 +7,7 @@ from sage.misc.misc_c import prod as mul from sage.modules.free_module_element import vector from sage.rings.finite_rings.element_base import is_FiniteFieldElement -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.ideal import FieldIdeal, Ideal from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index f37d3d3ef27..ccea00666fb 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -310,7 +310,7 @@ Within the AES*\; in Advances in Cryptology \- CRYPTO 2002\; LNCS 2442\; Springer Verlag 2002 """ -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing, BooleanPolynomialRing_constructor as BooleanPolynomialRing diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index 2964eb578d3..42be38832f2 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -18,7 +18,7 @@ from sage.crypto.util import random_blum_prime from sage.monoids.string_monoid import BinaryStrings from sage.arith.all import gcd, power_mod -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.rings.finite_rings.integer_mod_ring import IntegerModFactory from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/data_structures/mutable_poset.py b/src/sage/data_structures/mutable_poset.py index 3848d59ddf7..14f3212dd31 100644 --- a/src/sage/data_structures/mutable_poset.py +++ b/src/sage/data_structures/mutable_poset.py @@ -1089,7 +1089,7 @@ def _iter_topological_visit_(self, marked, return marked.add(self) S = self.predecessors(reverse) - if key is not None: + if key is not None and len(S) > 1: S = sorted(S, key=key) for shell in S: for e in shell._iter_topological_visit_(marked, reverse, @@ -3454,7 +3454,7 @@ def map(self, function, topological=False, reverse=False): .. NOTE:: If ``function`` returns ``None``, then the element is - removed after the mapping. + removed. EXAMPLES:: diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index c625d52f9f7..ff882867bc8 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" The On-Line Encyclopedia of Integer Sequences (OEIS) @@ -25,10 +26,12 @@ sage: search = oeis([3, 7, 15, 1], max_results=4) ; search # optional -- internet 0: A001203: Continued fraction expansion of Pi. - 1: A165416: Irregular array read by rows: The n-th row contains those distinct positive integers that each, when written in binary, occurs as a substring in binary n. - 2: A193583: Number of fixed points under iteration of sum of squares of digits in base b. - 3: A082495: (2^n-1) mod n. + 1: A082495: a(n) = (2^n - 1) mod n. + 2: A165416: Irregular array read by rows: The n-th row contains those distinct positive integers that each, when written in binary, occurs as a substring in binary n. + 3: A246674: Run Length Transform of A000225. + sage: [u.id() for u in search] # optional -- internet + ['A001203', 'A082495', 'A165416', 'A246674'] sage: c = search[0] ; c # optional -- internet A001203: Continued fraction expansion of Pi. @@ -40,7 +43,7 @@ sage: c.examples() # optional -- internet 0: Pi = 3.1415926535897932384... 1: = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + ...)))) - 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 292, ...] + 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...] sage: c.comments() # optional -- internet 0: The first 5,821,569,425 terms were computed by _Eric W. Weisstein_ on Sep 18 2011. @@ -49,8 +52,8 @@ :: - sage: x = c.natural_object() ; x.parent() # optional -- internet - Field of all continued fractions + sage: x = c.natural_object() ; type(x) # optional -- internet + sage: x.convergents()[:7] # optional -- internet [3, 22/7, 333/106, 355/113, 103993/33102, 104348/33215, 208341/66317] @@ -86,7 +89,7 @@ 0: A000798: Number of different quasi-orders (or topologies, or transitive digraphs) with n labeled elements. 1: A001035: Number of partially ordered sets ("posets") with n labeled elements (or labeled acyclic transitive digraphs). 2: A001930: Number of topologies, or transitive digraphs with n unlabeled nodes. - 3: A006057: Number of labeled topologies with n points. + 3: A006057: Number of topologies on n labeled points satisfying axioms T_0-T_4. 4: A079263: Number of constrained mixed models with n factors. 5: A079265: Number of antisymmetric transitive binary relations on n unlabeled points. @@ -103,15 +106,15 @@ 5832742205057, 51724158235372] sage: oeis(L) # optional -- internet - 0: A000110: Bell or exponential numbers: ways of placing n labeled balls into n indistinguishable boxes. + 0: A000110: Bell or exponential numbers: number of ways to partition a set of n labeled elements. sage: b = _[0] # optional -- internet sage: b.formulas()[0] # optional -- internet - 'E.g.f.: exp( exp(x) - 1).' + 'E.g.f.: exp(exp(x) - 1).' - sage: b.comments()[89] # optional -- internet - 'Number n is prime if mod(a(n)-2,n) = 0. [From _Dmitry Kruchinin_, Feb 14 2012]' + sage: [i for i in b.comments() if 'prime' in i][-1] # optional -- internet + 'Number n is prime if mod(a(n)-2,n) = 0. -_Dmitry Kruchinin_, Feb 14 2012' sage: [n for n in range(2, 20) if (b(n)-2) % n == 0] # optional -- internet [2, 3, 5, 7, 11, 13, 17, 19] @@ -144,13 +147,7 @@ #***************************************************************************** from sage.structure.sage_object import SageObject -from sage.structure.sequence import Sequence -from sage.misc.lazy_import import lazy_import -lazy_import('sage.rings.semirings.non_negative_integer_semiring', 'NN') -from sage.rings.integer_ring import IntegerRing from sage.rings.integer import Integer -from sage.rings.contfrac import ContinuedFractionField -from sage.rings.real_lazy import RealLazyField from sage.misc.misc import verbose from sage.misc.cachefunc import cached_method from sage.misc.flatten import flatten @@ -163,6 +160,7 @@ oeis_url = 'http://oeis.org/' + def _fetch(url): r""" Fetch the given ``url``. @@ -182,7 +180,7 @@ def _fetch(url): '' """ try: - _ = verbose("Fetching URL %s ..." %url, caller_name='OEIS') + verbose("Fetching URL %s ..." % url, caller_name='OEIS') f = urlopen(url) result = f.read() f.close() @@ -190,6 +188,7 @@ def _fetch(url): except IOError as msg: raise IOError("%s\nError fetching %s." % (msg, url)) + def _urls(html_string): r""" Return the list of URLs contained in ``html_string``. @@ -215,6 +214,7 @@ def _urls(html_string): """ urls = [] from HTMLParser import HTMLParser + class MyHTMLParser(HTMLParser): def handle_starttag(self, tag, attrs): if tag == 'a': @@ -224,8 +224,10 @@ def handle_starttag(self, tag, attrs): MyHTMLParser().feed(html_string) return urls + to_tuple = lambda string: tuple(Integer(x) for x in string.split(",") if x) + class OEIS: r""" The On-Line Encyclopedia of Integer Sequences. @@ -280,7 +282,7 @@ class OEIS: sage: search = oeis([1,2,3,5,8,13]) ; search # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - 1: A027926: Triangular array T read by rows: T(n,0)=T(n,2n)=1 for n >= 0; ... + 1: A027926: Triangular array T read by rows: T(n,0) = T(n,2n) = 1 for n >= 0; T(n,1) = 1 for n >= 1; T(n,k) = T(n-1,k-2) + T(n-1,k-1) for k = 2..2n-1, n >= 2. 2: A001129: Iccanobif numbers: reverse digits of two previous terms and add. sage: fibo = search[0] # optional -- internet @@ -310,7 +312,7 @@ class OEIS: sage: sfibo.first_terms(absolute_value=True)[2:20] == fibo.first_terms()[:18] # optional -- internet True - sage: fibo.formulas()[3] # optional -- internet + sage: fibo.formulas()[4] # optional -- internet 'F(n) = F(n-1) + F(n-2) = -(-1)^n F(-n).' sage: fibo.comments()[1] # optional -- internet @@ -335,7 +337,7 @@ class OEIS: sage: oeis([1,2,3,5,8,13]) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - 1: A027926: Triangular array T read by rows: T(n,0)=T(n,2n)=1 for n >= 0; ... + 1: A027926: Triangular array T read by rows: T(n,0) = T(n,2n) = 1 for n >= 0; T(n,1) = 1 for n >= 1; T(n,k) = T(n-1,k-2) + T(n-1,k-1) for k = 2..2n-1, n >= 2. 2: A001129: Iccanobif numbers: reverse digits of two previous terms and add. sage: fibo = oeis('A000045') # optional -- internet @@ -346,7 +348,7 @@ class OEIS: sage: oeis([1,2,3,5,8,13]) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - 1: A027926: Triangular array T read by rows: T(n,0)=T(n,2n)=1 for n >= 0; ... + 1: A027926: Triangular array T read by rows: T(n,0) = T(n,2n) = 1 for n >= 0; T(n,1) = 1 for n >= 1; T(n,k) = T(n-1,k-2) + T(n-1,k-1) for k = 2..2n-1, n >= 2. 2: A001129: Iccanobif numbers: reverse digits of two previous terms and add. sage: fibo = _[0] # optional -- internet @@ -364,7 +366,7 @@ def __call__(self, query, max_results=3, first_result=0): TypeError: __call__() takes at least 2 arguments (1 given) """ if isinstance(query, str): - if re.match('^A[0-9]{6}$',query): + if re.match('^A[0-9]{6}$', query): return self.find_by_id(query) else: return self.find_by_description(query, max_results, first_result) @@ -408,7 +410,7 @@ def find_by_id(self, ident): if not isinstance(ident, str): ident = str(ident) ident = 'A000000'[:-len(ident)] + ident - options = {'q':ident, 'n':'1', 'fmt':'text'} + options = {'q': ident, 'n': '1', 'fmt': 'text'} url = oeis_url + "search?" + urlencode(options) sequence = _fetch(url).split('\n\n')[2] return OEISSequence(sequence) @@ -459,10 +461,10 @@ def find_by_description(self, description, max_results=3, first_result=0): 2: A131957: Busy Beaver sigma variation: maximum number of 1's ... 3: A052200: Number of n-state, 2-symbol, d+ in {LEFT, RIGHT}, ... """ - options = {'q':description, - 'n':str(max_results), - 'fmt':'text', - 'start':str(first_result)} + options = {'q': description, + 'n': str(max_results), + 'fmt': 'text', + 'start': str(first_result)} url = oeis_url + "search?" + urlencode(options) sequence_list = _fetch(url).split('\n\n')[2:-1] return FancyTuple([OEISSequence(_) for _ in sequence_list]) @@ -493,7 +495,7 @@ def find_by_subsequence(self, subsequence, max_results=3, first_result=0): sage: oeis.find_by_subsequence([2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]) # optional -- internet 0: A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. 1: A177194: Fibonacci numbers whose decimal expression does not contain any digit 0. - 2: A020695: Pisot sequence E(2,3). + 2: A212804: Expansion of (1-x)/(1-x-x^2). sage: fibo = _[0] ; fibo # optional -- internet A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. @@ -599,6 +601,7 @@ def _imaginary_sequence(self, keywords='sign,easy'): """ return OEISSequence(self._imaginary_entry(keywords)) + class OEISSequence(SageObject): r""" The class of OEIS sequences. @@ -814,7 +817,7 @@ def author(self): A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. sage: f.author() # optional -- internet - '_N. J. A. Sloane_.' + '_N. J. A. Sloane_, Apr 30 1991' TESTS:: @@ -838,7 +841,7 @@ def keywords(self): A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. sage: f.keywords() # optional -- internet - ('core', 'nonn', 'easy', 'nice', 'changed') + ('core', 'nonn', 'nice', 'easy', 'hear') TESTS:: @@ -863,8 +866,7 @@ def natural_object(self): RealLazyField()). - If the sequence ``self`` corresponds to the convergents of a - continued fraction, returns the associated continued - fraction (as an element of ContinuedFractionField()). + continued fraction, returns the associated continued fraction. .. WARNING:: @@ -882,10 +884,14 @@ def natural_object(self): sage: g = oeis("A002852") ; g # optional -- internet A002852: Continued fraction for Euler's constant (or Euler-Mascheroni constant) gamma. - sage: x = g.natural_object() ; x.parent() # optional -- internet - Field of all continued fractions + sage: x = g.natural_object() ; type(x) # optional -- internet + - sage: x[:20] == continued_fraction(euler_gamma, nterms=20) # optional -- internet + sage: RDF(x) == RDF(euler_gamma) # optional -- internet + True + + sage: cfg = continued_fraction(euler_gamma) + sage: x[:90] == cfg[:90] # optional -- internet True :: @@ -921,7 +927,7 @@ def natural_object(self): :: sage: sfib = oeis('A039834') ; sfib # optional -- internet - A039834: a(n+2)=-a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices. + A039834: a(n+2) = -a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices. sage: x = sfib.natural_object() ; x.universe() # optional -- internet Integer Ring @@ -929,8 +935,8 @@ def natural_object(self): TESTS:: sage: s = oeis._imaginary_sequence('nonn,cofr') - sage: s.natural_object().parent() - QQ as continued fractions + sage: type(s.natural_object()) + sage: s = oeis._imaginary_sequence('nonn') sage: s.natural_object().universe() @@ -941,15 +947,21 @@ def natural_object(self): Integer Ring """ if 'cofr' in self.keywords() and not 'frac' in self.keywords(): - return ContinuedFractionField()(self.first_terms()) - if 'cons' in self.keywords(): + from sage.rings.continued_fraction import continued_fraction + return continued_fraction(self.first_terms()) + elif 'cons' in self.keywords(): offset = self.offsets()[0] terms = self.first_terms() + tuple([0] * abs(offset)) + from sage.rings.real_lazy import RealLazyField return RealLazyField()('0' + ''.join(map(str, terms[:offset])) + '.' + ''.join(map(str, terms[offset:]))) elif 'nonn' in self.keywords(): + from sage.structure.sequence import Sequence + from sage.rings.semirings.non_negative_integer_semiring import NN return Sequence(self.first_terms(), NN) else: - return Sequence(self.first_terms(),IntegerRing()) + from sage.structure.sequence import Sequence + from sage.rings.integer_ring import ZZ + return Sequence(self.first_terms(), ZZ) def is_finite(self): r""" @@ -971,7 +983,7 @@ def is_finite(self): EXAMPLES:: sage: s = oeis('A114288') ; s # optional -- internet - A114288: Lexicographically minimal solution of any 9 X 9 sudoku, read by rows. + A114288: Lexicographically earliest solution of any 9 X 9 sudoku, read by rows. sage: s.is_finite() # optional -- internet True @@ -1017,7 +1029,7 @@ def is_full(self): EXAMPLES:: sage: s = oeis('A114288') ; s # optional -- internet - A114288: Lexicographically minimal solution of any 9 X 9 sudoku, read by rows. + A114288: Lexicographically earliest solution of any 9 X 9 sudoku, read by rows. sage: s.is_full() # optional -- internet True @@ -1096,9 +1108,9 @@ def first_terms(self, number=None, absolute_value=False): 1 """ if absolute_value or ('nonn' in self.keywords()): - fields = ['S','T','U'] + fields = ['S', 'T', 'U'] elif ('sign' in self.keywords()): - fields = ['V','W','X'] + fields = ['V', 'W', 'X'] else: raise TypeError("You found a sign inconsistency, please contact OEIS") return to_tuple(" ".join(flatten([self._fields[a] for a in fields])))[:number] @@ -1301,9 +1313,8 @@ def __iter__(self): if not self.is_full(): raise LookupError("Future values not provided by OEIS.") - def __eq__(self,other): + def __eq__(self, other): r""" - Returns ``True`` if ``self`` is equal to ``other`` and ``False`` otherwise. Two integer sequences are considered equal if they have the same OEIS ID. @@ -1332,7 +1343,6 @@ def __eq__(self,other): def __ne__(self, other): r""" - Returns ``True`` if ``self`` has a different OEIS ID than ``other`` and ``False`` otherwise. @@ -1373,7 +1383,8 @@ def references(self): sage: w.references() # optional -- internet 0: A. H. Beiler, Recreations in the Theory of Numbers, Dover, NY, 1964, p. 52. 1: C. Clawson, Mathematical Mysteries, Plenum Press, 1996, p. 180. - 2: Edgar Costa, Robert Gerbicz, and David Harvey, A search for Wilson primes, 2012 + 2: R. Crandall and C. Pomerance, Prime Numbers: A Computational Perspective, Springer, NY, 2001; see p. 29. + 3: G. H. Hardy and E. M. Wright, An Introduction to the Theory of Numbers, 5th ed., Oxford Univ. Press, 1979, th. 80. ... sage: _[0] # optional -- internet @@ -1461,7 +1472,7 @@ def links(self, browse=None, format='guess'): webbrowser.open(url_list[url_number]) elif browse == 'all': for url in url_list: - webbrowser.open(url) + webbrowser.open(url) def formulas(self): r""" @@ -1476,7 +1487,7 @@ def formulas(self): sage: f = oeis(45) ; f # optional -- internet A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. - sage: f.formulas()[1] # optional -- internet + sage: f.formulas()[2] # optional -- internet 'F(n) = ((1+sqrt(5))^n-(1-sqrt(5))^n)/(2^n*sqrt(5)).' TESTS:: @@ -1542,10 +1553,10 @@ def extensions_or_errors(self): EXAMPLES:: sage: sfibo = oeis('A039834') ; sfibo # optional -- internet - A039834: a(n+2)=-a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices. + A039834: a(n+2) = -a(n+1)+a(n) (signed Fibonacci numbers); or Fibonacci numbers (A000045) extended to negative indices. sage: sfibo.extensions_or_errors()[0] # optional -- internet - 'Signs corrected by Len Smiley (smiley(AT)math.uaa.alaska.edu) and _N. J. A. Sloane_.' + 'Signs corrected by _Len Smiley_ and _N. J. A. Sloane_.' TESTS:: @@ -1572,7 +1583,7 @@ def examples(self): sage: c.examples() # optional -- internet 0: Pi = 3.1415926535897932384... 1: = 3 + 1/(7 + 1/(15 + 1/(1 + 1/(292 + ...)))) - 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 292, ...] + 2: = [a_0; a_1, a_2, a_3, ...] = [3; 7, 15, 1, 292, ...] TESTS:: @@ -1596,9 +1607,9 @@ def comments(self): A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. sage: f.comments()[:3] # optional -- internet - ("Also called Lam{\\'e}'s sequence.", - "F(n+2) = number of binary sequences of length n that have no consecutive 0's.", - 'F(n+2) = number of subsets of {1,2,...,n} that contain no consecutive integers.') + 0: Also sometimes called Lamé's sequence. + 1: F(n+2) = number of binary sequences of length n that have no consecutive 0's. + 2: F(n+2) = number of subsets of {1,2,...,n} that contain no consecutive integers. TESTS:: @@ -1639,8 +1650,9 @@ def browse(self): EXAMPLES:: - sage: f = oeis(45) ; f # optional -- internet + sage: f = oeis(45) ; f # optional -- internet webbrowser A000045: Fibonacci numbers: F(n) = F(n-1) + F(n-2) with F(0) = 0 and F(1) = 1. + sage: f.browse() # optional -- internet webbrowser TESTS:: @@ -1663,16 +1675,16 @@ def show(self): A012345 NAME - sinh(arcsin(x)*arcsin(x))=2/2!*x^2+8/4!*x^4+248/6!*x^6+11328/8!*x^8... + Coefficients in the expansion sinh(arcsin(x)*arcsin(x)) = 2*x^2/2!+8*x^4/4!+248*x^6/6!+11328*x^8/8!+... FIRST TERMS - (2, 8, 248, 11328, 849312, 94857600, 14819214720, 3091936512000, ... - - KEYWORDS - ('nonn',) + (2, 8, 248, 11328, 849312, 94857600, 14819214720, 3091936512000, 831657655349760, 280473756197529600, 115967597965430077440, 57712257892456911912960, 34039765801079493369569280) + FORMULAS + ... OFFSETS (0, 1) + URL http://oeis.org/A012345 @@ -1702,14 +1714,14 @@ def show(self): 'programs', 'keywords', 'offsets', 'url', 'old_IDs', 'author', 'extensions_or_errors']: if embedded() and s == 'links': - print re.sub('_',' ',s).upper() + print re.sub('_', ' ', s).upper() getattr(self, s)() print '\n' else: result = getattr(self, s)() if result != '' and result != ('',) and result != (): - print re.sub('_',' ',s).upper() - print str(result) + '\n' + print re.sub('_', ' ', s).upper() + print str(result) + '\n' def programs(self, language='other'): r""" @@ -1732,7 +1744,7 @@ def programs(self, language='other'): A001113: Decimal expansion of e. sage: ee.programs()[0] # optional -- internet - '(PARI) { default(realprecision, 50080); x=exp(1); for (n=1, 50000, d=floor(x); x=(x-d)*10; write("b001113.txt", n, " ", d)); } [From Harry J. Smith, Apr 15 2009]' + '(PARI) { default(realprecision, 50080); x=exp(1); for (n=1, 50000, d=floor(x); x=(x-d)*10; write("b001113.txt", n, " ", d)); } \\\\ _Harry J. Smith_, Apr 15 2009' TESTS:: @@ -1761,6 +1773,7 @@ def programs(self, language='other'): else: return FancyTuple(self._fields['o']) + class FancyTuple(tuple): r""" This class inherits from ``tuple``, it allows to nicely print tuples whose @@ -1794,8 +1807,30 @@ def __repr__(self): 3: three 4: 4 """ - length = len(str(len(self)-1)) + length = len(str(len(self) - 1)) return '\n'.join((('{0:>%d}' % length).format(str(i)) + ': ' + str(self[i]) for i in range(len(self)))) -oeis = OEIS() + def __getslice__(self, i, j): + r""" + The slice of a FancyTuple remains a FancyTuple. + + EXAMPLES:: + sage: from sage.databases.oeis import FancyTuple + sage: t = FancyTuple(['zero', 'one', 'two', 'three', 4]) + sage: t[-2:] + 0: three + 1: 4 + + TESTS:: + + sage: t = ('é', 'è', 'à', 'ç') + sage: t + ('\xc3\xa9', '\xc3\xa8', '\xc3\xa0', '\xc3\xa7') + sage: FancyTuple(t)[2:4] + 0: à + 1: ç + """ + return FancyTuple(tuple(self).__getslice__(i, j)) + +oeis = OEIS() diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 4edad5c4da8..953613b7b6b 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -546,7 +546,6 @@ def all_files(): from glob import glob self.files.append(opj(SAGE_SRC, 'sage')) self.files.append(opj(SAGE_SRC, 'sage_setup')) - self.files.append(opj(SAGE_SRC, 'doc', 'common')) self.files.extend(glob(opj(SAGE_SRC, 'doc', '[a-z][a-z]'))) self.options.sagenb = True DOT_GIT= opj(SAGE_ROOT, '.git') diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 9d0efe01f4d..afd256279e9 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -596,6 +596,7 @@ def in_lib(self): return (self.options.force_lib or self.basename.startswith('sage.') or self.basename.startswith('doc.') or + self.basename.startswith('sage_setup.docbuild') or self.basename.startswith('sagenb.')) def create_doctests(self, namespace): diff --git a/src/sage/env.py b/src/sage/env.py index 4d8ee8af192..cb03f6b73dc 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -103,6 +103,7 @@ def _add_variable_or_fallback(key, fallback, force=False): _add_variable_or_fallback('SAGE_LOGS', opj('$SAGE_ROOT', 'logs', 'pkgs')) _add_variable_or_fallback('SAGE_SPKG_INST', opj('$SAGE_LOCAL', 'var', 'lib', 'sage', 'installed')) _add_variable_or_fallback('SAGE_DOC', opj('$SAGE_SRC', 'doc')) +_add_variable_or_fallback('SAGE_DOC_OUTPUT', opj('$SAGE_SHARE', 'doc', 'sage')) _add_variable_or_fallback('DOT_SAGE', opj(os.environ.get('HOME','$SAGE_ROOT'), '.sage')) _add_variable_or_fallback('SAGE_DOT_GIT', opj('$SAGE_ROOT', '.git')) _add_variable_or_fallback('SAGE_DISTFILES', opj('$SAGE_ROOT', 'upstream')) diff --git a/src/sage/ext/interrupt/pxi.h b/src/sage/ext/interrupt/pxi.h index b4bfe496097..4cafb5a740f 100644 --- a/src/sage/ext/interrupt/pxi.h +++ b/src/sage/ext/interrupt/pxi.h @@ -6,3 +6,7 @@ #include "interrupt/struct_signals.h" #include "interrupt/interrupt_api.h" #include "interrupt/macros.h" + +/* Undefine this macro from interrupt_api.h to avoid compiler warnings: + * Cython redefines it when cimporting interrupt.pxd */ +#undef _signals diff --git a/src/sage/ext/interrupt/tests_helper.c b/src/sage/ext/interrupt/tests_helper.c index df9e835d3d4..eb204787551 100644 --- a/src/sage/ext/interrupt/tests_helper.c +++ b/src/sage/ext/interrupt/tests_helper.c @@ -11,7 +11,8 @@ #include #include #include -#include "interrupt/pxi.h" +#include "interrupt/struct_signals.h" +#include "interrupt/interrupt_api.h" /* Wait ``ms`` milliseconds */ diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index 4f8cb11aa5d..fb0bdc97d60 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -25,7 +25,7 @@ from transcendental import (zeta, zetaderiv, zeta_symmetric, hurwitz_zeta, - dickman_rho) + dickman_rho, stieltjes) from sage.functions.bessel import (bessel_I, bessel_J, bessel_K, bessel_Y, Bessel) diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index a25b85f5976..f3146eb35d2 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -39,6 +39,8 @@ def __init__(self): 12.182493960703473 sage: exp(RDF('2.5')) 12.182493960703473 + sage: exp(I*pi/12) + 1/12*sqrt(6)*(sqrt(3) + 3) - 1/12*I*sqrt(6)*(sqrt(3) - 3) To prevent automatic evaluation, use the ``hold`` parameter:: @@ -117,6 +119,13 @@ def __init__(self): sage: 2*sqrt(e) 2*sqrt(e) + + Check that :trac:`19918` is fixed:: + + sage: exp(-x^2).subs(x=oo) + 0 + sage: exp(-x).subs(x=-oo) + +Infinity """ GinacFunction.__init__(self, "exp", latex_name=r"\exp", conversions=dict(maxima='exp')) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 417f8ea7cbe..c6e3a4734cc 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -932,7 +932,7 @@ def __init__(self): EXAMPLES:: sage: gamma_inc(CDF(0,1), 3) - 0.003208574993369116 + 0.012406185811871568*I + 0.0032085749933691158 + 0.012406185811871568*I sage: gamma_inc(RDF(1), 3) 0.049787068367863944 sage: gamma_inc(3,2) diff --git a/src/sage/functions/transcendental.py b/src/sage/functions/transcendental.py index 6f1327c6e1f..57d4ba66726 100644 --- a/src/sage/functions/transcendental.py +++ b/src/sage/functions/transcendental.py @@ -63,6 +63,10 @@ def __init__(self): zeta(I) sage: zeta(I).n() 0.00330022368532410 - 0.418155449141322*I + sage: zeta(sqrt(2)) + zeta(sqrt(2)) + sage: zeta(sqrt(2)).n() # rel tol 1e-10 + 3.02073767948603 It is possible to use the ``hold`` argument to prevent automatic evaluation:: @@ -76,12 +80,20 @@ def __init__(self): sage: a = zeta(2,hold=True); a.simplify() 1/6*pi^2 - Check that :trac:`15846` is resolved:: + The Laurent expansion of `\zeta(s)` at `s=1` is + implemented by means of the + :wikipedia:`Stieltjes constants `:: + + sage: s = SR('s') + sage: zeta(s).series(s==1, 2) + 1*(s - 1)^(-1) + (euler_gamma) + (-1/2*stieltjes(1))*(s - 1) + Order((s - 1)^2) + + Generally, the Stieltjes constants occur in the Laurent + expansion of `\zeta`-type singularities:: + + sage: zeta(2*s/(s+1)).series(s==1, 2) + 2*(s - 1)^(-1) + (euler_gamma + 1) + (-1/4*stieltjes(1))*(s - 1) + Order((s - 1)^2) - sage: zeta(x).series(x==1, 1) - 1*(x - 1)^(-1) + (euler_gamma + log(2) + log(pi) + 2*zetaderiv(1, 0)) + Order(x - 1) - sage: zeta(x).residue(x==1) - 1 TESTS:: @@ -95,12 +107,72 @@ def __init__(self): Infinity sage: zeta(x).subs(x=1) Infinity + + Check that :trac:`19799` is resolved:: + + sage: zeta(pi) + zeta(pi) + sage: zeta(pi).n() # rel tol 1e-10 + 1.17624173838258 """ GinacFunction.__init__(self, "zeta") zeta = Function_zeta() +class Function_stieltjes(GinacFunction): + def __init__(self): + r""" + Stieltjes constant of index ``n``. + + ``stieltjes(0)`` is identical to the Euler-Mascheroni constant + (:class:`sage.symbolic.constants.EulerGamma`). The Stieltjes + constants are used in the series expansions of `\zeta(s)`. + + INPUT: + + - ``n`` - non-negative integer + + EXAMPLES:: + + sage: _ = var('n') + sage: stieltjes(n) + stieltjes(n) + sage: stieltjes(0) + euler_gamma + sage: stieltjes(2) + stieltjes(2) + sage: stieltjes(int(2)) + stieltjes(2) + sage: stieltjes(2).n(100) + -0.0096903631928723184845303860352 + sage: RR = RealField(200) + sage: stieltjes(RR(2)) + -0.0096903631928723184845303860352125293590658061013407498807014 + + It is possible to use the ``hold`` argument to prevent + automatic evaluation:: + + sage: stieltjes(0,hold=True) + stieltjes(0) + + sage: latex(stieltjes(n)) + \gamma_{n} + sage: a = loads(dumps(stieltjes(n))) + sage: a.operator() == stieltjes + True + + sage: stieltjes(x).subs(x==0) + euler_gamma + """ + GinacFunction.__init__(self, "stieltjes", nargs=1, + conversions=dict(mathematica='StieltjesGamma', + sympy='stieltjes'), + latex_name='\gamma') + +stieltjes = Function_stieltjes() + + class Function_HurwitzZeta(BuiltinFunction): def __init__(self): r""" diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index b362662a84f..7998f44ba33 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -160,6 +160,10 @@ def __init__(self): sage: tan(complex(1,1)) # rel tol 1e-15 (0.2717525853195118+1.0839233273386946j) + Check that :trac:`19791` is fixed:: + + sage: tan(2+I).imag().n() + 1.16673625724092 """ GinacFunction.__init__(self, "tan", latex_name=r"\tan") @@ -600,6 +604,13 @@ def __init__(self): sage: arctan(x).operator() arctan + + Check that :trac:`19918` is fixed:: + + sage: arctan(-x).subs(x=oo) + -1/2*pi + sage: arctan(-x).subs(x=-oo) + 1/2*pi """ GinacFunction.__init__(self, "arctan", latex_name=r'\arctan', conversions=dict(maxima='atan', sympy='atan')) diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index 30911094693..4abe37b662c 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -79,7 +79,7 @@ from sage.rings.rational_field import RationalField QQ = RationalField() infinity = Infinity -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField GF = FiniteField from sage.calculus.calculus import SR #SR = SymbolicRing() diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 44527666dde..6122592820c 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -3326,7 +3326,7 @@ def complex(self, base_ring=ZZ, extended=False): 0 where the leftmost non-zero entry is in degree `0` and the - rightmost entry in degree `d`. See [Klyachko], eq. (3.2). This + rightmost entry in degree `d`. See [Klyachko]_, eq. (3.2). This complex computes the homology of `|\Sigma|\subset N_\RR` with arbitrary support, @@ -3429,13 +3429,6 @@ def complex(self, base_ring=ZZ, extended=False): sage: fan = Fan([Cone([(-1,0,0),(0,-1,0),(0,0,-1)])]) sage: fan.complex().homology() {0: 0, 1: 0, 2: 0, 3: 0} - - REFERENCES: - - .. [Klyachko] - A. A. Klyachko, - Equivariant Bundles on Toral Varieties. - Mathematics of the USSR - Izvestiya 35 (1990), 337-375. """ dim = self.dim() delta = dict() diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py index f7bb0626e28..27b7c6e48d5 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_geodesic.py @@ -63,7 +63,7 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.geometry.hyperbolic_space.hyperbolic_isometry', - 'mobius_transform') + 'moebius_transform') class HyperbolicGeodesic(SageObject): @@ -549,9 +549,9 @@ def reflection_involution(self): The above tests go through the Upper Half Plane. It remains to test that the matrices in the models do what we intend. :: - sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import mobius_transform + sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform sage: R = H.PD().get_geodesic(-1,1).reflection_involution() - sage: bool(mobius_transform(R.matrix(), 0) == 0) + sage: bool(moebius_transform(R.matrix(), 0) == 0) True """ return self._cached_geodesic.reflection_involution().to_model(self._model) @@ -1048,7 +1048,7 @@ def midpoint(self): # UHP end_p = start else: end_p = end - return self._model.get_point(mobius_transform(M, end_p)) + return self._model.get_point(moebius_transform(M, end_p)) def angle(self, other): # UHP r""" @@ -1117,7 +1117,7 @@ def angle(self, other): # UHP # with endpoints [0,oo] T = HyperbolicGeodesicUHP._crossratio_matrix(p1, p1 + 1, p2) # b1 and b2 are the endpoints of the image of other - b1, b2 = [mobius_transform(T, k) for k in [q1, q2]] + b1, b2 = [moebius_transform(T, k) for k in [q1, q2]] # If other is now a straight line... if (b1 == infinity or b2 == infinity): # then since they intersect, they are equal @@ -1184,15 +1184,15 @@ def _crossratio_matrix(p0, p1, p2): # UHP EXAMPLES:: sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import HyperbolicGeodesicUHP - sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import mobius_transform + sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform sage: UHP = HyperbolicPlane().UHP() sage: (p1, p2, p3) = [UHP.random_point().coordinates() for k in range(3)] sage: A = HyperbolicGeodesicUHP._crossratio_matrix(p1, p2, p3) - sage: bool(abs(mobius_transform(A, p1)) < 10**-9) + sage: bool(abs(moebius_transform(A, p1)) < 10**-9) True - sage: bool(abs(mobius_transform(A, p2) - 1) < 10**-9) + sage: bool(abs(moebius_transform(A, p2) - 1) < 10**-9) True - sage: bool(mobius_transform(A, p3) == infinity) + sage: bool(moebius_transform(A, p3) == infinity) True sage: (x,y,z) = var('x,y,z'); HyperbolicGeodesicUHP._crossratio_matrix(x,y,z) [ y - z -x*(y - z)] diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py index a448e4e5b75..ff10dae5cb7 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_isometry.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Hyperbolic Isometries @@ -653,7 +654,7 @@ def _call_(self, p): #UHP sage: bool(UHP.dist(I2(p), p) < 10**-9) True """ - return self.codomain().get_point(mobius_transform(self._matrix, p.coordinates())) + return self.codomain().get_point(moebius_transform(self._matrix, p.coordinates())) def preserves_orientation(self): #UHP r""" @@ -910,7 +911,7 @@ def _call_(self, p): #PD sage: bool(PD.dist(I2(q), q) < 10**-9) True """ - _image = mobius_transform(self._matrix, p.coordinates()) + _image = moebius_transform(self._matrix, p.coordinates()) return self.codomain().get_point(_image) def __mul__(self, other): #PD @@ -1018,10 +1019,11 @@ def _call_(self, p): #KM ##################################################################### ## Helper functions -def mobius_transform(A, z): +from sage.misc.superseded import deprecated_function_alias +def moebius_transform(A, z): r""" Given a matrix ``A`` in `GL(2, \CC)` and a point ``z`` in the complex - plane return the mobius transformation action of ``A`` on ``z``. + plane return the Möbius transformation action of ``A`` on ``z``. INPUT: @@ -1034,21 +1036,21 @@ def mobius_transform(A, z): EXAMPLES:: - sage: from sage.geometry.hyperbolic_space.hyperbolic_model import mobius_transform - sage: mobius_transform(matrix(2,[1,2,3,4]),2 + I) + sage: from sage.geometry.hyperbolic_space.hyperbolic_model import moebius_transform + sage: moebius_transform(matrix(2,[1,2,3,4]),2 + I) 2/109*I + 43/109 sage: y = var('y') - sage: mobius_transform(matrix(2,[1,0,0,1]),x + I*y) + sage: moebius_transform(matrix(2,[1,0,0,1]),x + I*y) x + I*y The matrix must be square and `2 \times 2`:: - sage: mobius_transform(matrix([[3,1,2],[1,2,5]]),I) + sage: moebius_transform(matrix([[3,1,2],[1,2,5]]),I) Traceback (most recent call last): ... TypeError: A must be an invertible 2x2 matrix over the complex numbers or a symbolic ring - sage: mobius_transform(identity_matrix(3),I) + sage: moebius_transform(identity_matrix(3),I) Traceback (most recent call last): ... TypeError: A must be an invertible 2x2 matrix over the complex numbers or a symbolic ring @@ -1057,11 +1059,11 @@ def mobius_transform(A, z): or complex numbers, but must be provably invertible:: sage: a,b,c,d = var('a,b,c,d'); - sage: mobius_transform(matrix(2,[a,b,c,d]),I) + sage: moebius_transform(matrix(2,[a,b,c,d]),I) (I*a + b)/(I*c + d) - sage: mobius_transform(matrix(2,[1,b,c,b*c+1]),I) + sage: moebius_transform(matrix(2,[1,b,c,b*c+1]),I) (b + I)/(b*c + I*c + 1) - sage: mobius_transform(matrix(2,[0,0,0,0]),I) + sage: moebius_transform(matrix(2,[0,0,0,0]),I) Traceback (most recent call last): ... TypeError: A must be an invertible 2x2 matrix over the complex numbers or a symbolic ring @@ -1081,3 +1083,4 @@ def mobius_transform(A, z): return (a*w + b) / (c*w + d) raise TypeError("A must be an invertible 2x2 matrix over the" " complex numbers or a symbolic ring") +mobius_transform = deprecated_function_alias(19855, moebius_transform) diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py index 6717d0206fe..917921d6c05 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py @@ -94,7 +94,7 @@ HyperbolicPoint, HyperbolicPointUHP) from sage.geometry.hyperbolic_space.hyperbolic_isometry import ( HyperbolicIsometry, HyperbolicIsometryUHP, - HyperbolicIsometryPD, HyperbolicIsometryKM, mobius_transform) + HyperbolicIsometryPD, HyperbolicIsometryKM, moebius_transform) from sage.geometry.hyperbolic_space.hyperbolic_geodesic import ( HyperbolicGeodesic, HyperbolicGeodesicUHP, HyperbolicGeodesicPD, HyperbolicGeodesicKM, HyperbolicGeodesicHM) @@ -992,7 +992,7 @@ def _dist_geod_point(self, start, end, p): # Is a straight line: # Map the endpoints to 0 and infinity and another endpoint to 1. T = HyperbolicGeodesicUHP._crossratio_matrix(start, start + 1, end) - x = mobius_transform(T, p) + x = moebius_transform(T, p) return self._dist_points(x, abs(x)*I) ################# @@ -1064,13 +1064,13 @@ def isometry_from_fixed_points(self, repel, attract): repel = real(repel) attract = real(attract) if repel == infinity: - A = self._mobius_sending([infinity, attract, attract + 1], + A = self._moebius_sending([infinity, attract, attract + 1], [infinity, attract, attract + 2]) elif attract == infinity: - A = self._mobius_sending([repel, infinity, repel + 1], + A = self._moebius_sending([repel, infinity, repel + 1], [repel, infinity, repel + 2]) else: - A = self._mobius_sending([repel, attract, infinity], + A = self._moebius_sending([repel, attract, infinity], [repel, attract, max(repel, attract) + 1]) return self.get_isometry(A) @@ -1111,7 +1111,7 @@ def random_isometry(self, preserve_orientation=True, **kwargs): ################### @staticmethod - def _mobius_sending(z, w): #UHP + def _moebius_sending(z, w): #UHP r""" Given two lists ``z`` and ``w`` of three points each in `\mathbb{CP}^1`, return the linear fractional transformation @@ -1120,16 +1120,16 @@ def _mobius_sending(z, w): #UHP EXAMPLES:: sage: from sage.geometry.hyperbolic_space.hyperbolic_model import HyperbolicModelUHP - sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import mobius_transform - sage: bool(abs(mobius_transform(HyperbolicModelUHP._mobius_sending([1,2,infinity],[3 - I, 5*I,-12]),1) - 3 + I) < 10^-4) + sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform + sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),1) - 3 + I) < 10^-4) True - sage: bool(abs(mobius_transform(HyperbolicModelUHP._mobius_sending([1,2,infinity],[3 - I, 5*I,-12]),2) - 5*I) < 10^-4) + sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),2) - 5*I) < 10^-4) True - sage: bool(abs(mobius_transform(HyperbolicModelUHP._mobius_sending([1,2,infinity],[3 - I, 5*I,-12]),infinity) + 12) < 10^-4) + sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),infinity) + 12) < 10^-4) True """ if len(z) != 3 or len(w) != 3: - raise TypeError("mobius_sending requires each list to be three points long") + raise TypeError("moebius_sending requires each list to be three points long") A = HyperbolicGeodesicUHP._crossratio_matrix(z[0],z[1],z[2]) B = HyperbolicGeodesicUHP._crossratio_matrix(w[0],w[1],w[2]) return B.inverse() * A diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index bb58ffd52a0..fec7d7878a4 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Hyperplane Arrangements @@ -41,9 +42,9 @@ sage: H. = HyperplaneArrangements(QQ) sage: box = x | y | x-1 | y-1; box Arrangement - sage: box == H(x, y, x-1, y-1) # alternative syntax + sage: box == H(x, y, x-1, y-1) # alternative syntax True - + Notation (ii): by passing anything that defines a hyperplane, for example a coefficient vector and constant term:: @@ -67,7 +68,7 @@ sage: H. = HyperplaneArrangements(GF(5)) sage: k = [x+i for i in range(4)]; k - [Hyperplane x + 0*y + 0*z + 0, Hyperplane x + 0*y + 0*z + 1, + [Hyperplane x + 0*y + 0*z + 0, Hyperplane x + 0*y + 0*z + 1, Hyperplane x + 0*y + 0*z + 2, Hyperplane x + 0*y + 0*z + 3] sage: H(k) Arrangement @@ -93,7 +94,7 @@ New arrangements from old:: sage: a = hyperplane_arrangements.braid(3) - sage: b = a.add_hyperplane([4, 1, 2, 3]) + sage: b = a.add_hyperplane([4, 1, 2, 3]) sage: b Arrangement sage: c = b.deletion([4, 1, 2, 3]) @@ -139,33 +140,33 @@ sage: b.n_regions() 19 sage: b.regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 6 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) + (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 6 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) sage: b.bounded_regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 6 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices) + (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 6 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices) sage: b.n_bounded_regions() 7 sage: a.unbounded_regions() @@ -186,7 +187,7 @@ separating them. For example:: sage: r1 = b.regions()[0] - sage: r2 = b.regions()[1] + sage: r2 = b.regions()[1] sage: b.distance_between_regions(r1, r2) 1 sage: [hyp for hyp in b if b.is_separating_hyperplane(r1, r2, hyp)] @@ -221,7 +222,7 @@ where the sum is `P` is the :meth:`~HyperplaneArrangementElement.intersection_poset` of the -arrangement and `\mu` is the Moebius function of `P`:: +arrangement and `\mu` is the Möbius function of `P`:: sage: a = hyperplane_arrangements.semiorder(5) sage: a.characteristic_polynomial() # long time (about a second on Core i7) @@ -277,8 +278,8 @@ TESTS:: sage: H. = HyperplaneArrangements(QQ) - sage: h = H([(1, 106), 106266], [(83, 101), 157866], [(111, 110), 186150], [(453, 221), 532686], - ....: [(407, 237), 516882], [(55, 32), 75620], [(221, 114), 289346], [(452, 115), 474217], + sage: h = H([(1, 106), 106266], [(83, 101), 157866], [(111, 110), 186150], [(453, 221), 532686], + ....: [(407, 237), 516882], [(55, 32), 75620], [(221, 114), 289346], [(452, 115), 474217], ....: [(406, 131), 453521], [(28, 9), 32446], [(287, 19), 271774], [(241, 35), 244022], ....: [(231, 1), 210984], [(185, 17), 181508], [(23, -8), 16609]) sage: h.n_regions() @@ -289,7 +290,7 @@ sage: Zero = HyperplaneArrangements(QQ) sage: Zero - Hyperplane arrangements in 0-dimensional linear space over Rational Field with coordinate + Hyperplane arrangements in 0-dimensional linear space over Rational Field with coordinate sage: Zero() Empty hyperplane arrangement of dimension 0 sage: Zero.an_element() @@ -312,7 +313,7 @@ REFERENCES: -.. [RS] +.. [RS] Stanley, Richard: *Hyperplane Arrangements*, Geometric Combinatorics (E. Miller, V. Reiner, and B. Sturmfels, eds.), IAS/Park City Mathematics Series, vol. 13, American Mathematical Society, @@ -323,15 +324,15 @@ # Copyright (C) 2013 David Perkinson # Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. +# 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/ #***************************************************************************** # Possible extensions for hyperplane_arrangement.py: # - the big face lattice -# - Orlik-Solomon algebras # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields @@ -343,6 +344,7 @@ from sage.misc.misc import uniq from sage.matrix.constructor import matrix, vector from sage.modules.free_module import VectorSpace +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane @@ -351,7 +353,7 @@ class HyperplaneArrangementElement(Element): """ - An element in a hyperplane arrangement. + A hyperplane arrangement. .. WARNING:: @@ -366,7 +368,7 @@ def __init__(self, parent, hyperplanes, check=True): INPUT: - ``parent`` -- the parent :class:`HyperplaneArrangements` - + - ``hyperplanes`` -- a tuple of hyperplanes - ``check`` -- boolean (optional; default ``True``); whether @@ -410,7 +412,7 @@ def __getitem__(self, i): Return the `i`-th hyperplane. INPUT: - + - ``i`` -- integer OUTPUT: @@ -424,11 +426,11 @@ def __getitem__(self, i): Arrangement sage: h[0] Hyperplane 0*x + y + 0 - sage: h[1] + sage: h[1] Hyperplane x + 0*y + 0 """ return self._hyperplanes[i] - + def n_hyperplanes(self): r""" Return the number of hyperplanes in the arrangement. @@ -505,7 +507,7 @@ def dimension(self): OUTPUT: An integer. - + EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) @@ -536,8 +538,8 @@ def rank(self): sage: B = hyperplane_arrangements.braid(3) sage: B.hyperplanes() - (Hyperplane 0*t0 + t1 - t2 + 0, - Hyperplane t0 - t1 + 0*t2 + 0, + (Hyperplane 0*t0 + t1 - t2 + 0, + Hyperplane t0 - t1 + 0*t2 + 0, Hyperplane t0 + 0*t1 - t2 + 0) sage: B.dimension() 3 @@ -632,7 +634,7 @@ def plot(self, **kwds): def cone(self, variable='t'): r""" - Return the cone over the hyperplane arrangement. + Return the cone over the hyperplane arrangement. INPUT: @@ -640,10 +642,19 @@ def cone(self, variable='t'): OUTPUT: - A new yperplane arrangement. Its equations consist of + A new hyperplane arrangement. Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the original arrangement and the equation `[0, 1, 0, \ldots, 0]`. + .. WARNING:: + + While there is an almost-one-to-one correspondence between the + hyperplanes of ``self`` and those of ``self.cone()``, there is + no guarantee that the order in which they appear in + ``self.hyperplanes()`` will match the order in which their + counterparts in ``self.cone()`` will appear in + ``self.cone().hyperplanes()``! + EXAMPLES:: sage: a. = hyperplane_arrangements.semiorder(3) @@ -653,19 +664,19 @@ def cone(self, variable='t'): sage: b.characteristic_polynomial().factor() (x - 1) * x * (x^2 - 6*x + 12) sage: a.hyperplanes() - (Hyperplane 0*x + y - z - 1, - Hyperplane 0*x + y - z + 1, - Hyperplane x - y + 0*z - 1, - Hyperplane x - y + 0*z + 1, - Hyperplane x + 0*y - z - 1, + (Hyperplane 0*x + y - z - 1, + Hyperplane 0*x + y - z + 1, + Hyperplane x - y + 0*z - 1, + Hyperplane x - y + 0*z + 1, + Hyperplane x + 0*y - z - 1, Hyperplane x + 0*y - z + 1) sage: b.hyperplanes() - (Hyperplane -t + 0*x + y - z + 0, - Hyperplane -t + x - y + 0*z + 0, - Hyperplane -t + x + 0*y - z + 0, - Hyperplane t + 0*x + 0*y + 0*z + 0, - Hyperplane t + 0*x + y - z + 0, - Hyperplane t + x - y + 0*z + 0, + (Hyperplane -t + 0*x + y - z + 0, + Hyperplane -t + x - y + 0*z + 0, + Hyperplane -t + x + 0*y - z + 0, + Hyperplane t + 0*x + 0*y + 0*z + 0, + Hyperplane t + 0*x + y - z + 0, + Hyperplane t + x - y + 0*z + 0, Hyperplane t + x + 0*y - z + 0) """ hyperplanes = [] @@ -677,7 +688,7 @@ def cone(self, variable='t'): names = (variable,) + P._names H = HyperplaneArrangements(self.parent().base_ring(), names=names) return H(*hyperplanes) - + @cached_method def intersection_poset(self): r""" @@ -741,8 +752,8 @@ def _slow_characteristic_polynomial(self): x = polygen(QQ, 'x') P = self.intersection_poset() n = self.dimension() - return sum([P.mobius_function(0, p) * x**(n - P.rank(p)) for p in P]) - + return sum([P.moebius_function(0, p) * x**(n - P.rank(p)) for p in P]) + @cached_method def characteristic_polynomial(self): r""" @@ -757,7 +768,7 @@ def characteristic_polynomial(self): sage: a = hyperplane_arrangements.coordinate(2) sage: a.characteristic_polynomial() x^2 - 2*x + 1 - + TESTS:: sage: H. = HyperplaneArrangements(QQ) @@ -914,7 +925,7 @@ def restriction(self, hyperplane): def change_ring(self, base_ring): """ Return hyperplane arrangement over the new base ring. - + INPUT: - ``base_ring`` -- the new base ring; must be a field for @@ -925,6 +936,16 @@ def change_ring(self, base_ring): The hyperplane arrangement obtained by changing the base field, as a new hyperplane arrangement. + .. WARNING:: + + While there is often a one-to-one correspondence between the + hyperplanes of ``self`` and those of + ``self.change_ring(base_ring)``, there is + no guarantee that the order in which they appear in + ``self.hyperplanes()`` will match the order in which their + counterparts in ``self.cone()`` will appear in + ``self.change_ring(base_ring).hyperplanes()``! + EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) @@ -1053,7 +1074,7 @@ def is_linear(self): sage: b = hyperplane_arrangements.braid(3) sage: b.is_linear() True - + sage: H. = HyperplaneArrangements(QQ) sage: c = H(x+1, y+1) sage: c.is_linear() @@ -1142,9 +1163,9 @@ def essentialization(self): sage: B.essentialization() Arrangement <-x + 1 | x + 1> sage: B.essentialization().parent() - Hyperplane arrangements in 1-dimensional linear space over + Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - + sage: H. = HyperplaneArrangements(GF(2)) sage: C = H([(1,1),1], [(1,1),0]) sage: C.essentialization() @@ -1251,7 +1272,7 @@ def sign_vector(self, p): signs = vector(ZZ, [sign(_) for _ in values]) signs.set_immutable() return signs - + def face_vector(self): r""" Return the face vector. @@ -1334,9 +1355,9 @@ def vertices(self, exclude_sandwiched=False): The vertices are the zero-dimensional faces, see :meth:`face_vector`. - + INPUT: - + - ``exclude_sandwiched`` -- boolean (default: ``False``). Whether to exclude hyperplanes that are sandwiched between parallel hyperplanes. Useful if you only @@ -1346,7 +1367,7 @@ def vertices(self, exclude_sandwiched=False): The vertices in a sorted tuple. Each vertex is returned as a vector in the ambient vector space. - + EXAMPLES:: sage: A = hyperplane_arrangements.Shi(3).essentialization() @@ -1358,7 +1379,7 @@ def vertices(self, exclude_sandwiched=False): ((-2/3, 1/3), (-1/3, -1/3), (0, -1), (0, 0), (1/3, -2/3), (2/3, -1/3)) sage: point2d(A.vertices(), size=20) + A.plot() Graphics object consisting of 7 graphics primitives - + sage: H. = HyperplaneArrangements(QQ) sage: chessboard = [] sage: N = 8 @@ -1414,7 +1435,7 @@ def _make_region(self, hyperplanes): The polyhedron constructed from taking the linear expressions as inequalities. - + EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) @@ -1424,7 +1445,7 @@ def _make_region(self, hyperplanes): """ ieqs = [h.dense_coefficient_list() for h in hyperplanes] from sage.geometry.polyhedron.constructor import Polyhedron - return Polyhedron(ieqs=ieqs, ambient_dim=self.dimension(), + return Polyhedron(ieqs=ieqs, ambient_dim=self.dimension(), base_ring=self.parent().base_ring()) @cached_method @@ -1445,7 +1466,7 @@ def regions(self): sage: a = hyperplane_arrangements.braid(2) sage: a.regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) sage: H. = HyperplaneArrangements(QQ) @@ -1455,7 +1476,7 @@ def regions(self): A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) - + sage: chessboard = [] sage: N = 8 sage: for x0 in range(N+1): @@ -1485,6 +1506,506 @@ def regions(self): regions = subdivided return tuple(regions) + @cached_method + def closed_faces(self, labelled=True): + r""" + Return the closed faces of the hyperplane arrangement ``self`` + (provided that ``self`` is defined over a totally ordered field). + + Let `\mathcal{A}` be a hyperplane arrangement in the vector + space `K^n`, whose hyperplanes are the zero sets of the + affine-linear functions `u_1, u_2, \ldots, u_N`. (We consider + these functions `u_1, u_2, \ldots, u_N`, and not just the + hyperplanes, as given. We also assume the field `K` to be + totally ordered.) For any point `x \in K^n`, we define the + *sign vector* of `x` to be the vector + `(v_1, v_2, \ldots, v_N) \in \{-1, 0, 1\}^N` such that (for each + `i`) the number `v_i` is the sign of `u_i(x)`. For any + `v \in \{-1, 0, 1\}^N`, we let `F_v` be the set of all `x \in K^n` + which have sign vector `v`. The nonempty ones among all these + subsets `F_v` are called the *open faces* of `\mathcal{A}`. They + form a partition of the set `K^n`. + + Furthermore, for any + `v = (v_1, v_2, \ldots, v_N) \in \{-1, 0, 1\}^N`, we let `G_v` be + the set of all `x \in K^n` such that, for every `i`, the sign of + `u_i(x)` is either `0` or `v_i`. + Then, `G_v` is a polyhedron. The nonempty ones among all these + polyhedra `G_v` are called the *closed faces* of `\mathcal{A}`. + While several sign vectors `v` can lead to one and the same + closed face `G_v`, we can assign to every closed face a canonical + choice of a sign vector: Namely, if `G` is a closed face of + `\mathcal{A}`, then the *sign vector* of `G` is defined to be the + vector `(v_1, v_2, \ldots, v_N) \in \{-1, 0, 1\}^N` where `x` is + any point in the relative interior of `G` and where, for each `i`, + the number `v_i` is the sign of `u_i(x)`. (This does not depend on + the choice of `x`.) + + There is a one-to-one correspondence between the closed faces and + the open faces of `\mathcal{A}`. It sends a closed face `G` to + the open face `F_v`, where `v` is the sign vector of `G`; this + `F_v` is also the relative interior of `G_v`. The inverse map + sends any open face `O` to the closure of `O`. + + INPUT: + + - ``labelled`` -- boolean (default: ``True``); if ``True``, then + this method returns not the faces itself but rather pairs + `(v, F)` where `F` is a closed face and `v` is its sign vector + (here, the order and the orientation of the + `u_1, u_2, \ldots, u_N` is as given by ``self.hyperplanes()``). + + OUTPUT: + + A tuple containing the closed faces as polyhedra, or (if + ``labelled`` is set to ``True``) the pairs of sign vectors and + corresponding closed faces. + + .. TODO:: + + Should the output rather be a dictionary where the keys are + the sign vectors and the values are the faces? + + EXAMPLES:: + + sage: a = hyperplane_arrangements.braid(2) + sage: a.hyperplanes() + (Hyperplane t0 - t1 + 0,) + sage: a.closed_faces() + (((0,), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line), + ((1,), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line), + ((-1,), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line)) + sage: a.closed_faces(labelled=False) + (A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] + [((0,), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line, + (0, 0)), + ((1,), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (0, -1)), + ((-1,), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (-1, 0))] + + sage: H. = HyperplaneArrangements(QQ) + sage: a = H(x, y+1) + sage: a.hyperplanes() + (Hyperplane 0*x + y + 1, Hyperplane x + 0*y + 0) + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] + [((0, 0), + A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, + (0, -1)), + ((0, 1), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, + (1, -1)), + ((0, -1), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, + (-1, -1)), + ((1, 0), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, + (0, 0)), + ((1, 1), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + (1, 0)), + ((1, -1), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + (-1, 0)), + ((-1, 0), + A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, + (0, -2)), + ((-1, 1), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + (1, -2)), + ((-1, -1), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + (-1, -2))] + + sage: a = hyperplane_arrangements.braid(3) + sage: a.hyperplanes() + (Hyperplane 0*t0 + t1 - t2 + 0, + Hyperplane t0 - t1 + 0*t2 + 0, + Hyperplane t0 + 0*t1 - t2 + 0) + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] + [((0, 0, 0), + A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line, + (0, 0, 0)), + ((0, 1, 1), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (0, -1, -1)), + ((0, -1, -1), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (-1, 0, 0)), + ((1, 0, 1), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (1, 1, 0)), + ((1, 1, 1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (0, -1, -2)), + ((1, -1, 0), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (-1, 0, -1)), + ((1, -1, 1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (1, 2, 0)), + ((1, -1, -1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (-2, 0, -1)), + ((-1, 0, -1), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (0, 0, 1)), + ((-1, 1, 0), + A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, + (1, 0, 1)), + ((-1, 1, 1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (0, -2, -1)), + ((-1, 1, -1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (1, 0, 2)), + ((-1, -1, -1), + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, + (-1, 0, 1))] + + Let us check that the number of closed faces with a given + dimension computed using ``self.closed_faces()`` equals the one + computed using :meth:`face_vector`:: + + sage: def test_number(a): + ....: Qx = PolynomialRing(QQ, 'x'); x = Qx.gen() + ....: RHS = Qx.sum(vi * x ** i for i, vi in enumerate(a.face_vector())) + ....: LHS = Qx.sum(x ** F[1].dim() for F in a.closed_faces()) + ....: return LHS == RHS + sage: a = hyperplane_arrangements.Catalan(2) + sage: test_number(a) + True + sage: a = hyperplane_arrangements.Shi(3) + sage: test_number(a) # long time + True + + TESTS: + + An empty border case:: + + sage: H. = HyperplaneArrangements(QQ) + sage: a = H() + sage: a.closed_faces() + (((), + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 lines),) + """ + R = self.base_ring() + if R.characteristic() != 0: + raise ValueError('base field must have characteristic zero') + from sage.geometry.polyhedron.constructor import Polyhedron + dim = self.dimension() + hypes = self.hyperplanes() + universe = Polyhedron(eqns=[[0] + [0] * dim], base_ring=R) + faces = [((), universe)] + for k, hyperplane in enumerate(hypes): + # Loop invariant: + # ``faces == Hk.closed_faces()``, where ``Hk`` is the + # hyperplane arrangement given by the first ``k`` hyperplanes + # in the list ``hypes`` (that is, by ``hypes[:k]``). + ieq = vector(R, hyperplane.dense_coefficient_list()) + zero_half = Polyhedron(eqns=[ieq], base_ring=R) + # ``zero_half`` is the hyperplane ``hyperplane`` itself + # (viewed as a polyhedron). + pos_half = Polyhedron(ieqs=[ ieq], base_ring=R) + neg_half = Polyhedron(ieqs=[-ieq], base_ring=R) + subdivided = [] + for signs, face in faces: + # So ``face`` is a face of the hyperplane arrangement + # given by the first ``k`` hyperplanes in the list + # ``hypes``, and ``signs`` is the corresponding + # (length-``k``) sign vector. + face_dim = face.dim() + # Adding the intersection of ``face`` with ``hyperplane``: + zero_part = face.intersection(zero_half) + zero_part_dim = zero_part.dim() + if zero_part_dim == face_dim: + # If the intersection of ``face`` with ``hyperplane`` + # has the same dimension as ``face``, then this + # intersection *is* ``face``, so we can continue + # (without adding the other two intersections, since + # those are empty): + subdivided.append((signs + (0,), face)) + continue + # If we are here, then ``face`` is not contained in + # ``hyperplane``. + if zero_part_dim >= 0: + # Do not append ``zero_part`` yet! It might be + # redundant (in the sense that some of its defining + # inequalities are always equalities on it). Check for + # this: + zero_part_point = zero_part.representative_point() + for l, testhype in enumerate(hypes[:k]): + if signs[l] != 0: + h = testhype.dense_coefficient_list() + testval = R.sum(h[i+1] * gi for i, gi in enumerate(zero_part_point)) + h[0] + if testval == 0: + break + else: + # Now we know ``zero_part`` is not redundant. + subdivided.append((signs + (0,), zero_part)) + # Adding the intersection of ``face`` with the positive + # halfspace: + pos_part = face.intersection(pos_half) + pos_part_dim = pos_part.dim() + if pos_part_dim == face_dim: + # If this condition is not satisfied, then + # ``pos_part`` is either ``zero_part`` or the empty + # set; in either case we need not add it. Conversely, + # if it is satisfied, then ``pos_part`` is not yet in + # ``subdivided``, nor is it redundant. + subdivided.append((signs + (1,), pos_part)) + neg_part = face.intersection(neg_half) + neg_part_dim = neg_part.dim() + if neg_part_dim == face_dim: + # If this condition is not satisfied, then + # ``neg_part`` is either ``zero_part`` or the empty + # set; in either case we need not add it. Conversely, + # if it is satisfied, then ``neg_part`` is not yet in + # ``subdivided``, nor is it redundant. + subdivided.append((signs + (-1,), neg_part)) + faces = subdivided + if labelled: + return tuple(faces) + # Or, if we want a dictionary: + # return {F[0]: F[1] for F in faces} + return tuple(x[1] for x in faces) + + def face_product(self, F, G, normalize=True): + r""" + Return the product `FG` in the face semigroup of ``self``, where + `F` and `G` are two closed faces of ``self``. + + The face semigroup of a hyperplane arrangement `\mathcal{A}` is + defined as follows: As a set, it is the set of all open faces + of ``self`` (see :meth:`closed_faces`). Its product is defined by + the following rule: If `F` and `G` are two open faces of + `\mathcal{A}`, then `FG` is an open face of `\mathcal{A}`, and + for every hyperplane `H \in \mathcal{A}`, the open face `FG` lies + on the same side of `H` as `F` unless `F \subseteq H`, in which + case `FG` lies on the same side of `H` as `G`. Alternatively, + `FG` can be defined as follows: If `f` and `g` are two points in + `F` and `G`, respectively, then `FG` is the face that contains + the point `(f + \varepsilon g) / (1 + \varepsilon)` for any + sufficiently small positive `\varepsilon`. + + In our implementation, the face semigroup consists of closed faces + rather than open faces (thanks to the 1-to-1 correspondence + between open faces and closed faces, this is not really a + different semigroup); these closed faces are given as polyhedra. + + The face semigroup of a hyperplane arrangement is always a + left-regular band (i.e., a semigroup satisfying the identities + `x^2 = x` and `xyx = xy`). When the arrangement is central, then + this semigroup is a monoid. See [Brown2000]_ (Appendix A in + particular) for further properties. + + INPUT: + + - ``F``, ``G`` -- two faces of ``self`` (as polyhedra) + + - ``normalize`` -- Boolean (default: ``True``); if ``True``, then + this method returns the precise instance of `FG` in the list + returned by ``self.closed_faces()``, rather than creating a new + instance + + EXAMPLES:: + + sage: a = hyperplane_arrangements.braid(3) + sage: a.hyperplanes() + (Hyperplane 0*t0 + t1 - t2 + 0, + Hyperplane t0 - t1 + 0*t2 + 0, + Hyperplane t0 + 0*t1 - t2 + 0) + sage: faces = {F0: F1 for F0, F1 in a.closed_faces()} + sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z + sage: xGyEz.representative_point() + (0, -1, -1) + sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z + sage: xGyEz.representative_point() + (0, -1, -1) + sage: yGxGz = faces[(1, -1, 1)] # closed face y >= x >= z + sage: xGyGz = faces[(1, 1, 1)] # closed face x >= y >= z + sage: a.face_product(xGyEz, yGxGz) == xGyGz + True + sage: a.face_product(yGxGz, xGyEz) == yGxGz + True + sage: xEzGy = faces[(-1, 1, 0)] # closed face x = z >= y + sage: xGzGy = faces[(-1, 1, 1)] # closed face x >= z >= y + sage: a.face_product(xEzGy, yGxGz) == xGzGy + True + + REFERENCES: + + .. [Brown2000] Kenneth S. Brown, *Semigroups, rings, and Markov + chains*, :arxiv:`math/0006145v1`. + """ + f = F.representative_point() + g = G.representative_point() + n = len(f) + R = self.base_ring() + from sage.geometry.polyhedron.constructor import Polyhedron + eqns = [[0] + [0] * n] + ieqs = [] + signs = [] + for hyperplane in self.hyperplanes(): + # Decide which side of ``hyperplane`` our face ``FG`` will be + # on. + H = hyperplane.dense_coefficient_list() + ieq = vector(R, H) + x = R.sum(H[i+1] * fi for i, fi in enumerate(f)) + H[0] + if x < 0: + side = -1 + elif x > 0: + side = 1 + else: + x = R.sum(H[i+1] * gi for i, gi in enumerate(g)) + H[0] + if x < 0: + side = -1 + elif x > 0: + side = 1 + else: + side = 0 + signs.append(side) + if side == 0: + eqns.append(ieq) + elif side == -1: + ieqs.append(-ieq) + else: + ieqs.append(ieq) + face = Polyhedron(eqns=eqns, ieqs=ieqs, base_ring=R) + if not normalize: + return face + # Look for ``I`` in ``self.closed_faces()``: + for I in self.closed_faces(): + if I[0] == tuple(signs): + return I[1] + + def face_semigroup_algebra(self, field=None, names='e'): + r""" + Return the face semigroup algebra of ``self``. + + This is the semigroup algebra of the face semigroup of ``self`` + (see :meth:`face_product` for the definition of the semigroup). + + Due to limitations of the current Sage codebase (e.g., semigroup + algebras do not profit from the functionality of the + :class:`FiniteDimensionalAlgebra` class), this is implemented not + as a semigroup algebra, but as a + :class:`FiniteDimensionalAlgebra`. The closed faces of ``self`` + (in the order in which the :meth:`closed_faces` method outputs + them) are identified with the vectors `(0, 0, \ldots, 0, 1, 0, 0, + \ldots, 0)` (with the `1` moving from left to right). + + INPUT: + + - ``field`` -- a field (default: `\mathbb{Q}`), to be used as the + base ring for the algebra (can also be a commutative ring, but + then certain representation-theoretical methods might misbehave) + + - ``names`` -- (default: ``'e'``) string; names for the basis + elements of the algebra + + .. TODO:: + + Also implement it as an actual semigroup algebra? + + EXAMPLES:: + + sage: a = hyperplane_arrangements.braid(3) + sage: [(i, F[0]) for i, F in enumerate(a.closed_faces())] + [(0, (0, 0, 0)), + (1, (0, 1, 1)), + (2, (0, -1, -1)), + (3, (1, 0, 1)), + (4, (1, 1, 1)), + (5, (1, -1, 0)), + (6, (1, -1, 1)), + (7, (1, -1, -1)), + (8, (-1, 0, -1)), + (9, (-1, 1, 0)), + (10, (-1, 1, 1)), + (11, (-1, 1, -1)), + (12, (-1, -1, -1))] + sage: U = a.face_semigroup_algebra(); U + Finite-dimensional algebra of degree 13 over Rational Field + sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() + sage: e0 * e1 + e1 + sage: e0 * e5 + e5 + sage: e5 * e0 + e5 + sage: e3 * e2 + e6 + sage: e7 * e12 + e7 + sage: e3 * e12 + e6 + sage: e4 * e8 + e4 + sage: e8 * e4 + e11 + sage: e8 * e1 + e11 + sage: e5 * e12 + e7 + sage: (e3 + 2*e4) * (e1 - e7) + e4 - e6 + + sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 + Finite-dimensional algebra of degree 13 over Finite Field of size 3 + + TESTS: + + The ``names`` keyword works:: + + sage: a = hyperplane_arrangements.braid(3) + sage: U = a.face_semigroup_algebra(names='x'); U + Finite-dimensional algebra of degree 13 over Rational Field + sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() + sage: e0 * e1 + x1 + """ + if field is None: + from sage.rings.rational_field import QQ + field = QQ + zero = field.zero() + one = field.one() + from sage.matrix.matrix_space import MatrixSpace + Fs = [F0 for F0, F1 in self.closed_faces()] + # ``Fs`` is the list of the sign vectors of all closed faces of + # ``self``. + Fdict = {v: i for i, v in enumerate(Fs)} + # ``Fdict`` is a dictionary whose keys are the sign vectors of the + # closed faces of ``self``, and whose values are their positions + # in the list ``Fs``. + N = len(Fs) + # Some hackery to generate a matrix quickly and without + # unnecessary sanitization/ducktyping: + MS = MatrixSpace(field, N, N) + MC = MS._get_matrix_class() + table = [] + for j, sj in enumerate(Fs): + matrix_j = [] + for i, si in enumerate(Fs): + row_i = [zero] * N + sk = [sil if sil != 0 else sj[l] + for l, sil in enumerate(si)] + k = Fdict[tuple(sk)] + row_i[k] = one + matrix_j.extend(row_i) + table.append(MC(MS, matrix_j, copy=False, coerce=False)) + from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra as FDA + return FDA(field, table, names=names, assume_associative=True) + def region_containing_point(self, p): r""" The region in the hyperplane arrangement containing a given point. @@ -1554,7 +2075,7 @@ def _bounded_region_indices(self): (2, 7, 8, 9, 10, 11, 16) """ from sage.geometry.polyhedron.constructor import Polyhedron - normal = Polyhedron(vertices=[[0]*self.dimension()], + normal = Polyhedron(vertices=[[0]*self.dimension()], lines=[hyperplane.normal() for hyperplane in self]) if normal.dim() == 0: transverse = lambda poly: poly @@ -1621,17 +2142,17 @@ def unbounded_regions(self): sage: B.n_regions() - B.n_bounded_regions() 12 sage: B.unbounded_regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) """ s = set(range(self.n_regions())).difference(set(self._bounded_region_indices())) @@ -1671,7 +2192,7 @@ def whitney_data(self): m1 = zero_matrix(ZZ, top+1, top+1) m2 = zero_matrix(ZZ, top+1, top+1) for i, j in p.relations_iterator(): - m1[r(i), r(j)] += p.mobius_function(i, j) + m1[r(i), r(j)] += p.moebius_function(i, j) m2[r(i), r(j)] += 1 m1.set_immutable() m2.set_immutable() @@ -1681,7 +2202,7 @@ def doubly_indexed_whitney_number(self, i, j, kind=1): r""" Return the `i,j`-th doubly-indexed Whitney number. - If ``kind=1``, this number is obtained by adding the Moebius function + If ``kind=1``, this number is obtained by adding the Möbius function values `mu(x,y)` over all `x, y` in the intersection poset with `\mathrm{rank}(x) = i` and `\mathrm{rank}(y) = j`. @@ -1734,13 +2255,13 @@ def whitney_number(self, k, kind=1): r""" Return the ``k``-th Whitney number. - If ``kind=1``, this number is obtained by summing the Moebius function + If ``kind=1``, this number is obtained by summing the Möbius function values `mu(0, x)` over all `x` in the intersection poset with `\mathrm{rank}(x) = k`. If ``kind=2``, this number is the number of elements `x, y` in the intersection poset such that `x \leq y` with ranks `i` and `j`, - respectively. + respectively. See [GZ]_ for more details. @@ -1854,7 +2375,7 @@ def distance_between_regions(self, region1, region2): sage: c.distance_between_regions(s, s) 0 """ - count = sum(1 for hyperplane in self + count = sum(1 for hyperplane in self if self.is_separating_hyperplane(region1, region2, hyperplane)) return ZZ(count) @@ -1966,6 +2487,31 @@ def matroid(self): from sage.matroids.constructor import Matroid return Matroid(matrix=matrix(norms).transpose()) + def orlik_solomon_algebra(self, base_ring=None, ordering=None): + """ + Return the Orlik-Solomon algebra of ``self``. + + INPUT: + + - ``base_ring`` -- (default: the base field of ``self``) the ring + over which the Orlik-Solomon algebra will be defined + - ``ordering`` -- (optional) an ordering of the ground set + + EXAMPLES:: + + sage: P. = HyperplaneArrangements(QQ) + sage: A = P(x, y, z, x+y+z, 2*x+y+z, 2*x+3*y+z, 2*x+3*y+4*z) + sage: A.orlik_solomon_algebra() + Orlik-Solomon algebra of Linear matroid of rank 3 on 7 elements + represented over the Rational Field + sage: A.orlik_solomon_algebra(base_ring=ZZ) + Orlik-Solomon algebra of Linear matroid of rank 3 on 7 elements + represented over the Rational Field + """ + if base_ring is None: + base_ring = self.base_ring() + return self.matroid().orlik_solomon_algebra(base_ring, ordering) + @cached_method def minimal_generated_number(self): r""" @@ -2040,6 +2586,250 @@ def is_formal(self): """ return self.minimal_generated_number() <= 3 + def defining_polynomial(self): + r""" + Return the defining polynomial of ``A``. + + Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` + corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then + the *defining polynomial* of `A` is given by + + .. MATH:: + + Q(A) = \prod_i \alpha_{H_i} \in S(V^*). + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: A = H([2*x + y - z, -x - 2*y + z]) + sage: p = A.defining_polynomial(); p + -2*x^2 - 5*x*y - 2*y^2 + 3*x*z + 3*y*z - z^2 + sage: p.factor() + (-1) * (x + 2*y - z) * (2*x + y - z) + """ + S = self.parent().ambient_space().symmetric_space() + return S.prod(H.to_symmetric_space() for H in self) + + @cached_method + def derivation_module_free_chain(self): + r""" + Return a free chain for the derivation module if one + exists, otherwise return ``None``. + + .. SEEALSO:: + + :meth:`is_free` + + EXAMPLES:: + + sage: W = WeylGroup(['A',3], prefix='s') + sage: A = W.long_element().inversion_arrangement() + sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) + [ 1 0 0] + [ 0 1 0] + [ 0 0 a3] + + [ 1 0 0] + [ 0 0 1] + [ 0 a2 0] + + [ 1 0 0] + [ 0 -1 -1] + [ 0 a2 -a3] + + [ 0 1 0] + [ 0 0 1] + [a1 0 0] + + [ 1 0 -1] + [a3 -1 0] + [a1 0 a2] + + [ 1 0 0] + [ a3 -1 -1] + [ 0 a1 -a2 - a3] + + """ + if not self.is_central(): + raise NotImplementedError("only implemented for central arrangements") + from sage.geometry.hyperplane_arrangement.check_freeness import construct_free_chain + return construct_free_chain(self) + + @cached_method(key=lambda self,a: None) + def is_free(self, algorithm="singular"): + """ + Return if ``self`` is free. + + A hyperplane arrangement `A` is free if the module + of derivations `\operatorname{Der}(A)` is a free `S`-module, + where `S` is the corresponding symmetric space. + + INPUT: + + - ``algorithm`` -- (default: ``"singular"``) can be one of + the following: + + * ``"singular"`` -- use Singular's minimal free resolution + * ``"BC"`` -- use the algorithm given by Barakat and Cuntz + in [BC12]_ (much slower than using Singular) + + ALGORITHM: + + .. RUBRIC:: singular + + Check that the minimal free resolution has length at most 2 + by using Singular. + + .. RUBRIC:: BC + + This implementation follows [BC12]_ by constructing a chain + of free modules + + .. MATH:: + + D(A) = D(A_n) < D(A_{n-1}) < \cdots < D(A_1) < D(A_0) + + corresponding to some ordering of the arrangements `A_0 \subset + A_1 \subset \cdots \subset A_{n-1} \subset A_n = A`. Such a + chain is found by using a backtracking algorithm. + + EXAMPLES: + + For type `A` arrangements, chordality is equivalent to freeness. + We verify that in type `A_3`:: + + sage: W = WeylGroup(['A',3], prefix='s') + sage: for x in W: + ....: A = x.inversion_arrangement() + ....: assert A.matroid().is_chordal() == A.is_free() + + TESTS: + + We check that the algorithms agree:: + + sage: W = WeylGroup(['B',3], prefix='s') + sage: for x in W: # long time + ....: A = x.inversion_arrangement() + ....: assert (A.is_free(algorithm="BC") + ....: == A.is_free(algorithm="singular")) + + REFERENCES: + + .. [BC12] Mohamed Barakat and Michael Cuntz. + *Coxeter and crystallographic arrangements are inductively free*. + Adv. in Math. **229** Issue 1 (2012). pp. 691-709. + :doi:`10.1016/j.aim.2011.09.011`, :arxiv:`1011.4228`. + """ + if not self.is_central(): + raise NotImplementedError("only implemented for central arrangements") + if algorithm == "singular": + # TODO: Implement this using libSingular + mres = self.defining_polynomial().jacobian_ideal()._singular_().mres(0) + return len(mres) <= 2 + elif algorithm == "BC": + return self.derivation_module_free_chain() is not None + else: + raise ValueError("invalid algorithm") + + def derivation_module_basis(self, algorithm="singular"): + """ + Return a basis for the derivation module of ``self`` if + one exists, otherwise return ``None``. + + .. SEEALSO:: + + :meth:`derivation_module_free_chain`, :meth:`is_free` + + INPUT: + + - ``algorithm`` -- (default: ``"singular"``) can be one of + the following: + + * ``"singular"`` -- use Singular's minimal free resolution + * ``"BC"`` -- use the algorithm given by Barakat and Cuntz + in [BC12]_ (much slower than using Singular) + + OUTPUT: + + A basis for the derivation module (over `S`, the + :meth:`symmetric space + `) + as vectors of a free module over `S`. + + ALGORITHM: + + .. RUBRIC:: Singular + + This gets the reduced syzygy module of the Jacobian ideal of + the defining polynomial `f` of ``self``. It then checks Saito's + criterion that the determinat of the basis matrix is a scalar + multiple of `f`. If the basis matrix is not square or it fails + Saito's criterion, then we check if the arrangement is free. + If it is free, then we fall back to the Barakat-Cuntz algorithm. + + .. RUBRIC:: BC + + Return the product of the derivation module free chain matrices. + See Section 6 of [BC12]_. + + EXAMPLES:: + + sage: W = WeylGroup(['A',2], prefix='s') + sage: A = W.long_element().inversion_arrangement() + sage: A.derivation_module_basis() + [(a1, a2), (0, a1*a2 + a2^2)] + + TESTS: + + We check the algorithms produce a basis with the same exponents:: + + sage: W = WeylGroup(['A',2], prefix='s') + sage: exponents = lambda B: sorted([max(x.degree() for x in b) + ....: for b in B]) + sage: for x in W: # long time + ....: A = x.inversion_arrangement() + ....: B = A.derivation_module_basis(algorithm="singular") + ....: Bp = A.derivation_module_basis(algorithm="BC") + ....: if B is None: + ....: assert Bp is None + ....: else: + ....: assert exponents(B) == exponents(Bp) + """ + alg = algorithm # prevent possible changes to a global variable + if alg == "singular": + #import sage.libs.singular.function_factory + #syz = sage.libs.singular.function_factory.ff.syz + f = self.defining_polynomial() + I = f + f.jacobian_ideal() + IS = I._singular_() + ISS = IS.syz() + MSTD = ISS.mstd() + basis = MSTD[2]._sage_().transpose().submatrix(0,1) + try: + det = basis.det() + # Check using Saito's criterion + if det / f in f.parent().base_ring() and not det.is_zero(): + return basis.rows() + except ValueError: # Non-square matrix or det = 0 + pass + # Check if it is free + if not self.is_free(algorithm=alg): + return None + # The syzygy module did not give a basis, but since it is free, + # fallback to the Barakat-Cuntz method + alg = "BC" + if alg == "BC": + C = self.derivation_module_free_chain() + if C is not None: + if not C: # C is an empty list + S = self.parent().ambient_space().symmetric_space() + return matrix.identity(S, self.dimension()).rows() + from sage.misc.misc_c import prod + return prod(reversed(C)).rows() + return None + else: + raise ValueError("invalid algorithm") + class HyperplaneArrangements(Parent, UniqueRepresentation): """ Hyperplane arrangements. @@ -2088,7 +2878,7 @@ def __init__(self, base_ring, names=tuple()): sage: TestSuite(K).run() """ from sage.categories.all import Fields, Sets - if not base_ring in Fields: + if not base_ring in Fields: raise ValueError('base ring must be a field') super(HyperplaneArrangements, self).__init__(category=Sets()) self._base_ring = base_ring @@ -2119,7 +2909,7 @@ def change_ring(self, base_ring): - ``base_ring`` -- a ring; the new base ring. OUTPUT: - + A new :class:`HyperplaneArrangements` instance over the new base ring. @@ -2129,7 +2919,7 @@ def change_ring(self, base_ring): sage: L.gen(0) Hyperplane x + 0*y + 0 sage: L.change_ring(RR).gen(0) - Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 + Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 TESTS:: @@ -2296,7 +3086,7 @@ def ngens(self): def gens(self): """ Return the coordinate hyperplanes. - + OUTPUT: A tuple of linear expressions, one for each linear variable. @@ -2305,8 +3095,8 @@ def gens(self): sage: L = HyperplaneArrangements(QQ, ('x', 'y', 'z')) sage: L.gens() - (Hyperplane x + 0*y + 0*z + 0, - Hyperplane 0*x + y + 0*z + 0, + (Hyperplane x + 0*y + 0*z + 0, + Hyperplane 0*x + y + 0*z + 0, Hyperplane 0*x + 0*y + z + 0) """ return self.ambient_space().gens() @@ -2335,7 +3125,7 @@ def gen(self, i): def _coerce_map_from_(self, P): """ Return whether there is a coercion. - + TESTS:: sage: L. = HyperplaneArrangements(QQ); L diff --git a/src/sage/geometry/hyperplane_arrangement/check_freeness.py b/src/sage/geometry/hyperplane_arrangement/check_freeness.py new file mode 100644 index 00000000000..1a86e90a675 --- /dev/null +++ b/src/sage/geometry/hyperplane_arrangement/check_freeness.py @@ -0,0 +1,143 @@ +r""" +Helper Functions For Freeness Of Hyperplane Arrangements + +This contains the algorithms to check for freeness of a hyperplane +arrangement. See +:meth:`sage.geometry.hyperplane_arrangement.HyperplaneArrangementElement.is_free` +for details. + +.. NOTE:: + + This could be extended to a freeness check for more general modules + over a polynomial ring. +""" + +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.matrix.constructor import matrix +import sage.libs.singular.function_factory as fun_fact + +def less_generators(X): + """ + Reduce the generator matrix of the module defined by ``X``. + + This is Algorithm 6.4 in [BC12]_ and relies on the row syzygies of + the matrix ``X``. + + EXAMPLES:: + + sage: from sage.geometry.hyperplane_arrangement.check_freeness import less_generators + sage: R. = QQ[] + sage: m = matrix([[1, 0, 0], [0, z, -1], [0, 0, 0], [0, y, 1]]) + sage: less_generators(m) + [ 1 0 0] + [ 0 z -1] + [ 0 y 1] + """ + R = X.base_ring() + syz = fun_fact.ff.syz + zero = R.zero() + while True: + Z = matrix(R, syz(X.transpose())) + # get_column_independent_unit_positions + J = range(Z.ncols()) + K = [] + for r in Z.rows(): + for j in J: + elt = r[j] + if elt.is_unit(): + K.append(j) + J = [l for l in J if r[l] == zero] + break + if not K: # K is empty + return X + Kd = set(range(X.nrows())).difference(K) + X = X.matrix_from_rows(sorted(Kd)) + +def construct_free_chain(A): + """ + Construct the free chain for the hyperplanes ``A``. + + ALGORITHM: + + We follow Algorithm 6.5 in [BC12]_. + + INPUT: + + - ``A`` -- a hyperplane arrangement + + EXAMPLES:: + + sage: from sage.geometry.hyperplane_arrangement.check_freeness import construct_free_chain + sage: H. = HyperplaneArrangements(QQ) + sage: A = H(z, y+z, x+y+z) + sage: construct_free_chain(A) + [ + [1 0 0] [ 1 0 0] [ 0 1 0] + [0 1 0] [ 0 z -1] [y + z 0 -1] + [0 0 z], [ 0 y 1], [ x 0 1] + ] + """ + AL = list(A) + if not AL: # Empty arrangement + return [] + + S = A.parent().ambient_space().symmetric_space() + # Compute the morphisms \phi_{H_j} : S^{1xR} \to S / + B = [H.to_symmetric_space() for H in AL] + phi = [matrix(S, [[beta.derivative(x)] for x in S.gens()]) for beta in B] + + # Setup variables + syz = fun_fact.ff.syz + G = S.gens() + r = len(G) + indices = list(range(len(B))) + X = [] + + # Helper function + def next_step(indices, prev, T): + for pos,i in enumerate(indices): + U = prev * T + mu = U * phi[i] + mu = mu.stack(matrix.diagonal([B[i]]).dense_matrix()) + row_syzygy = matrix(S, syz(mu.transpose())).matrix_from_columns(range(r)) + Y = less_generators(row_syzygy) + if not Y.is_square(): + continue + + if len(indices) == 1: + return [prev, Y] + + I = list(indices) + I.pop(pos) + ret = next_step(I, Y, U) + if ret is not None: + return [prev] + ret + return None + + T = matrix.identity(S, r) + for i in indices: + mu = phi[i].stack(matrix.diagonal([B[i]]).dense_matrix()) + row_syzygy = matrix(S, syz(mu.transpose())).matrix_from_columns(range(r)) + Y = less_generators(row_syzygy) + if not Y.is_square(): + continue + + if len(indices) == 1: + return [Y] + + I = list(indices) + I.pop(i) + ret = next_step(I, Y, T) + if ret is not None: + return ret + return None + diff --git a/src/sage/geometry/hyperplane_arrangement/hyperplane.py b/src/sage/geometry/hyperplane_arrangement/hyperplane.py index 15e4faa852c..fd19ce6cf0e 100644 --- a/src/sage/geometry/hyperplane_arrangement/hyperplane.py +++ b/src/sage/geometry/hyperplane_arrangement/hyperplane.py @@ -137,7 +137,7 @@ class Hyperplane(LinearExpression): Hyperplane x + y - 1 For technical reasons, we must allow the degenerate cases of - an empty empty and full space:: + an empty space and of a full space:: sage: 0*x Hyperplane 0*x + 0*y + 0 @@ -629,7 +629,31 @@ def __or__(self, other): arrangement = HyperplaneArrangements(parent.base_ring(), names=parent._names) return arrangement(self, other) - + def to_symmetric_space(self): + """ + Return ``self`` considered as an element in the corresponding + symmetric space. + + EXAMPLES:: + + sage: L. = HyperplaneArrangements(QQ) + sage: h = -1/3*x + 1/2*y + sage: h.to_symmetric_space() + -1/3*x + 1/2*y + + sage: hp = -1/3*x + 1/2*y - 1 + sage: hp.to_symmetric_space() + Traceback (most recent call last): + ... + ValueError: the hyperplane must pass through the origin + """ + coeff = self.coefficients() + if coeff[0] != 0: + raise ValueError("the hyperplane must pass through the origin") + S = self.parent().symmetric_space() + G = S.gens() + # We skip the first coefficient since it corresponds to the constant term + return S.sum(G[i]*c for i,c in enumerate(coeff[1:])) class AmbientVectorSpace(LinearExpressionModule): """ @@ -713,3 +737,23 @@ def change_ring(self, base_ring): """ return AmbientVectorSpace(base_ring, self._names) + def symmetric_space(self): + """ + Construct the symmetric space of ``self``. + + Consider a hyperplane arrangement `A` in the vector space + `V = k^n`, for some field `k`. The symmetric space is the + symmetric algebra `S(V^*)` as the polynomial ring + `k[x_1, x_2, \ldots, x_n]` where `(x_1, x_2, \ldots, x_n)` is + a basis for `V`. + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: A = H.ambient_space() + sage: A.symmetric_space() + Multivariate Polynomial Ring in x, y, z over Rational Field + """ + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + return PolynomialRing(self.base_ring(), self.variable_names()) + diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index 192f3042560..e1a34b7b5ef 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -406,7 +406,7 @@ def Polyhedron(vertices=None, rays=None, lines=None, if all(is_Integer(x) for x in values): if got_Vrep: base_ring = ZZ - else: # integral inequalities usually do not determine a latice polytope! + else: # integral inequalities usually do not determine a lattice polytope! base_ring = QQ convert = False elif all(is_Rational(x) for x in values): diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 711c0f1a202..a7879ec5551 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -14,8 +14,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.modules.free_module import is_FreeModule from sage.misc.cachefunc import cached_method -from sage.rings.commutative_ring import is_CommutativeRing -from sage.rings.all import ZZ, QQ, RDF +from sage.rings.all import ZZ, QQ, RDF, CommutativeRing from sage.categories.fields import Fields from sage.geometry.polyhedron.base import Polyhedron_base, is_Polyhedron @@ -637,7 +636,7 @@ def _get_action_(self, other, op, self_is_left): extended_self._internal_coerce_map_from(self).__copy__()) return action - if op is operator.mul and is_CommutativeRing(other): + if op is operator.mul and isinstance(other, CommutativeRing): ring = self._coerce_base_ring(other) if ring is self.base_ring(): return ActedUponAction(other, self, not self_is_left) diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index f9966376f99..9da5939de33 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -293,7 +293,7 @@ def ButterflyGraph(self, n, vertices='strings'): butterfly[(padded_bv,i)]=[(padded_bv,i+1), (padded_bw,i+1)] elif vertices=='vectors': from sage.modules.free_module import VectorSpace - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from copy import copy butterfly = {} for v in VectorSpace(FiniteField(2),n): diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index d67dd828664..53c0b81ea3c 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -22,7 +22,7 @@ from sage.graphs.graph import Graph from sage.graphs import graph from sage.arith.all import is_prime_power -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField def SymplecticPolarGraph(d, q, algorithm=None): r""" @@ -1181,7 +1181,7 @@ def HaemersGraph(q, hyperoval=None, hyperoval_matching=None, field=None, check_h """ from sage.modules.free_module_element import free_module_element as vector - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF from itertools import combinations p, k = is_prime_power(q,get_data=True) diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 74810ca98fa..b4984d335aa 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -47,7 +47,7 @@ def JohnsonGraph(n, k): True The complement of the Johnson graph `J(n,2)` is isomorphic to the Kneser - Graph `K(n,2)`. In paritcular the complement of `J(5,2)` is isomorphic to + Graph `K(n,2)`. In particular the complement of `J(5,2)` is isomorphic to the Petersen graph. :: sage: g = graphs.JohnsonGraph(5,2) @@ -1050,7 +1050,7 @@ def GeneralizedPetersenGraph(n,k): For `k=1` the result is a graph isomorphic to the circular ladder graph with the same `n`. The regular Petersen Graph has `n=5` and `k=2`. Other named graphs that can be described using this notation include - the Desargues graph and the Moebius-Kantor graph. + the Desargues graph and the Möbius-Kantor graph. INPUT: @@ -1587,7 +1587,7 @@ def PaleyGraph(q): True """ from sage.rings.finite_rings.integer_mod import mod - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.arith.all import is_prime_power assert is_prime_power(q), "Parameter q must be a prime power" assert mod(q,4)==1, "Parameter q must be congruent to 1 mod 4" @@ -2558,7 +2558,7 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None): Colloq. Math. Soc. János Bolyai, 25, North-Holland, Amsterdam-New York, 1981. """ - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.integer_ring import ZZ from sage.matrix.constructor import matrix, block_matrix, \ ones_matrix, identity_matrix diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index e5835185cf0..456191c2f4b 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -18,8 +18,6 @@ from sage.graphs.graph import Graph from sage.misc.randstate import current_randstate from sage.misc.prandom import randint -from sage.rings.rational_field import QQ - from sage.misc.decorators import rename_keyword @rename_keyword(deprecation=19559 , method='algorithm') @@ -915,15 +913,23 @@ def _contour_and_graph_from_word(w): sage: seq, G = _contour_and_graph_from_word(_auxiliary_random_word(20)) sage: G.is_tree() True + + sage: longw = [1,1,0,1,0,0,0,1,0,1,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0] + sage: seq, G = _contour_and_graph_from_word(longw) + sage: G.get_embedding() + {0: [1], 1: [0, 2], 2: [1, 3, 4], 3: [2], 4: [2, 5, 6], 5: [4], 6: [4]} """ index = 0 # numbering of inner vertices - word = [('in', 0)] # initial vertex is inner + word = [('in', 0)] # initial vertex is inner leaf_stack = [0, 0] # stack of leaves still to be created inner_stack = [0] # stack of active inner nodes edges = [] + embedding = {0: []} # records the planar embedding of the tree for x in w: if x == 1: # going up to a new inner vertex index += 1 + embedding[index] = inner_stack[-1:] + embedding[inner_stack[-1]].append(index) leaf_stack.extend([index, index]) inner_stack.append(index) edges.append(inner_stack[-2:]) @@ -935,7 +941,9 @@ def _contour_and_graph_from_word(w): else: # going down to a known inner vertex inner_stack.pop() word.append(('in', inner_stack[-1])) - return word[:-1], Graph(edges, format='list_of_edges') + G = Graph(edges, format='list_of_edges') + G.set_embedding(embedding) + return word[:-1], G def RandomTriangulation(n, set_position=False): @@ -950,12 +958,15 @@ def RandomTriangulation(n, set_position=False): - `n` -- an integer - ``set_position`` -- boolean (default ``False``) if set to ``True``, this - will compute a planar embedding of the graph. + will compute coordinates for a planar drawing of the graph. OUTPUT: A random triangulation chosen uniformly among the *rooted* triangulations on - `n` vertices. Because some triangulations have nontrivial automorphism + `n` vertices. This is a planar graph and comes with a combinatorial + embedding. + + Because some triangulations have nontrivial automorphism groups, this may not be equal to the uniform distribution among unrooted triangulations. @@ -978,9 +989,13 @@ def RandomTriangulation(n, set_position=False): At every step of the algorithm, newly created edges are recorded in a graph, which will be returned at the end. + The combinatorial embedding is also computed and recorded in the + output graph. + .. SEEALSO:: - :meth:`~sage.graphs.graph_generators.GraphGenerators.triangulations`. + :meth:`~sage.graphs.graph_generators.GraphGenerators.triangulations`, + :meth:`~sage.homology.examples.RandomTwoSphere`. EXAMPLES:: @@ -995,9 +1010,14 @@ def RandomTriangulation(n, set_position=False): TESTS:: + sage: G.get_embedding() is not None + True sage: for i in range(10): ....: g = graphs.RandomTriangulation(30) ....: assert g.is_planar() + sage: for i in range(10): + ....: g = graphs.RandomTriangulation(10) + ....: assert g.is_planar(on_embedding=g.get_embedding()) REFERENCES: @@ -1013,14 +1033,20 @@ def RandomTriangulation(n, set_position=False): word, graph = _contour_and_graph_from_word(w) edges = [] + embedding = graph.get_embedding() + # 'partial closures' described in 2.1 of [PS2006]_. + pattern = ['in', 'in', 'in', 'lf', 'in'] def rotate_word_to_next_occurrence(word): - # Rotates 'word' so that 'in1,in2,in3,lf,in3' occurs at word[:5]. - pattern = ['in', 'in', 'in', 'lf', 'in'] - n = len(word) - for i in range(n): - if all(word[(i + j) % n][0] == pattern[j] for j in range(5)): + """ + Rotate ``word`` so that the given pattern occurs at the beginning. + + If the given pattern is not found, return the empty list. + """ + N = len(word) + for i in range(N): + if all(word[(i + j) % N][0] == pattern[j] for j in range(5)): return word[i:] + word[:i] return [] @@ -1029,7 +1055,12 @@ def rotate_word_to_next_occurrence(word): word2 = rotate_word_to_next_occurrence(word) if len(word2) >= 5: word = [word2[0]] + word2[4:] - edges.append([u[1] for u in word[:2]]) # edge 'in1,in3' + in1, in2, in3 = [u[1] for u in word2[:3]] + edges.append([in1, in3]) # edge 'in1,in3' + idx = embedding[in1].index(in2) + embedding[in1].insert(idx, in3) + idx = embedding[in3].index(in2) + embedding[in3].insert(idx + 1, in1) else: break @@ -1042,19 +1073,42 @@ def rotate_word_to_next_occurrence(word): # Every remaining 'lf' vertex is linked either to 'a' or to 'b'. # Switching a/b happens when one meets the sequence 'lf','in','lf'. a_or_b = 'a' + embedding['a'] = [] + embedding['b'] = [] last_lf_occurrence = -42 + change = {} for x in word: last_lf_occurrence -= 1 if x[0] == 'lf': if last_lf_occurrence == -2: + change[a_or_b] = x[1] a_or_b = 'b' if a_or_b == 'a' else 'a' graph.add_edge((a_or_b, x[1])) + embedding[a_or_b].insert(0, x[1]) last_lf_occurrence = 0 + # conjugates the embeddings of a and b + # in a way that helps to complete the embedding + for a_or_b in ['a', 'b']: + emba = embedding[a_or_b] + idx = emba.index(change[a_or_b]) + embedding[a_or_b] = emba[idx:] + emba[:idx] + embedding['a'].append('b') + embedding['b'].append('a') + + # completes the embedding by inserting missing half-edges + for a_or_b in ['a', 'b']: + emb = embedding[a_or_b] + for i, v in enumerate(emb[:-1]): + if i == 0: + embedding[v].insert(embedding[v].index(emb[1]) + 1, a_or_b) + else: + embedding[v].insert(embedding[v].index(emb[i - 1]), a_or_b) + assert graph.num_edges() == 3 * (n - 2) assert graph.num_verts() == n - graph.relabel() + graph.set_embedding(embedding) if set_position: graph.layout(layout="planar", save_pos=True) diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index 99f8039a9ea..d38bdec0537 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -1413,7 +1413,7 @@ def BrouwerHaemersGraph(): sage: set(g.spectrum()) == {20,2,-7} True """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.modules.free_module import VectorSpace from sage.matrix.constructor import Matrix from sage.matrix.constructor import identity_matrix @@ -1949,7 +1949,7 @@ def DejterGraph(): """ from sage.graphs.generators.families import CubeGraph from sage.coding.code_constructions import HammingCode - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from string import join g = CubeGraph(7) @@ -2897,7 +2897,7 @@ def HeawoodGraph(): Returns a Heawood graph. The Heawood graph is a cage graph that has 14 nodes. It is a cubic - symmetric graph. (See also the Moebius-Kantor graph). It is + symmetric graph. (See also the Möbius-Kantor graph). It is nonplanar and Hamiltonian. It has diameter = 3, radius = 3, girth = 6, chromatic number = 2. It is 4-transitive but not 5-transitive. @@ -3805,9 +3805,9 @@ def McLaughlinGraph(): def MoebiusKantorGraph(): """ - Returns a Moebius-Kantor Graph. + Returns a Möbius-Kantor Graph. - A Moebius-Kantor graph is a cubic symmetric graph. (See also the + A Möbius-Kantor graph is a cubic symmetric graph. (See also the Heawood graph). It has 16 nodes and 24 edges. It is nonplanar and Hamiltonian. It has diameter = 4, girth = 6, and chromatic number = 2. It is identical to the Generalized Petersen graph, P[8,3]. @@ -3816,7 +3816,7 @@ def MoebiusKantorGraph(): REFERENCES: - - [1] Weisstein, E. (1999). "Moebius-Kantor Graph - from + - [1] Weisstein, E. (1999). "Möbius-Kantor Graph - from Wolfram MathWorld". [Online] Available: http://mathworld.wolfram.com/Moebius-KantorGraph.html [2007, February 17] @@ -4859,7 +4859,7 @@ def JankoKharaghaniGraph(v): http://journals.cambridge.org/article_S1446788700033929 """ - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.matrix.constructor import matrix # The notations of [JK02] are rather tricky, and so this code attempts to @@ -4930,3 +4930,72 @@ def JankoKharaghaniGraph(v): return Graph([e for e,v in D.dict().iteritems() if v == 1], multiedges=False, name="Janko-Kharaghani") + +def JankoKharaghaniTonchevGraph(): + r""" + Returns a (324,153,72,72)-strongly regular graph from [JKT01]_ + + Build the graph using the description given in [JKT01]_, taking + sets B1 and B163 in the text as adjacencies of vertices 1 and 163, + respectively, and taking the edge orbits of the group `G` provided. + + EXAMPLES:: + + sage: Gamma=graphs.JankoKharaghaniTonchevGraph() # long time + sage: Gamma.is_strongly_regular(parameters=True) # long time + (324, 153, 72, 72) + + REFERENCES: + + .. [JKT01] Z.Janko, H.Kharaghani, V.D.Tonchev + The existence of a Bush-type Hadamard matrix of order 324 + and two new infinite classes of symmetric designs. + Des. Codes Cryptogr. 24(2001), 225-232 + + """ + from itertools import product + from sage.misc.misc_c import prod + from sage.combinat.permutation import Permutation as P + from sage.libs.gap.libgap import libgap + + m1=prod([P((9*x+k,9*x+k+3,9*x+k+6)) for k,x in product(xrange(1,4),xrange(36))]) + m2=prod([P((3*x+1,3*x+2,3*x+3)) for x in xrange(108)]) + t=prod(prod(map(P,[(9*x+2,9*x+3),(9*x+4,9*x+7),(9*x+5,9*x+9),(9*x+6,9*x+8)])) for + x in xrange(36)) + n1=prod(prod(map(P,[(1+x,19+x,37+x),(55+x,73+x,91+x),(109+x,127+x,145+x), + (163+x,181+x,199+x),(217+x,235+x,253+x),(271+x,289+x,307+x)])) + for x in xrange(18)) + n2=prod(prod(map(P,[(1+x,55+x,109+x),(19+x,73+x,127+x),(37+x,91+x,145+x), + (163+x,217+x,271+x),(181+x,235+x,289+x),(199+x,253+x,307+x)])) + for x in xrange(18)) + s=prod(prod(map(P,[(19+x,37+x),(55+x,109+x),(73+x,145+x),(91+x,127+x), + (181+x,199+x),(217+x,271+x),(235+x,307+x),(253+x,289+x)])) + for x in xrange(18)) + k=prod(prod(map(P,[(18*x+1,18*x+10),(18*x+2,18*x+11),(18*x+3,18*x+12), + (18*x+4,18*x+13),(18*x+5,18*x+14),(18*x+6,18*x+15),(18*x+7,18*x+16), + (18*x+8,18*x+17),(18*x+9,18*x+18)])) + for x in xrange(18)) + G=libgap.Group(map(lambda p: libgap.PermList(p), [m1,m2,t,n1,n2,s,k])) + st=libgap.Group(map(lambda p: libgap.PermList(p), [t,s])) + B1=(19,22,25,29,30,31,33,34,35,37,40,43,47,48,49,51,52,53,55,56,57,65, + 66,67,68,70,72,76,77,78,79,80,81,82,86,90,92,93,95,96,98,99,100,105,107, + 109,110,111,119,120,121,122,124,126,128,129,131,132,134,135,136,141,143, + 148,149,150,151,152,153,154,158,162,167,168,170,171,172,176,177,179,180, + 184,186,187,188,190,191,192,193,196,202,204,205,206,208,209,210,211,214, + 218,219,221,225,226,227,228,229,232,236,237,238,241,244,245,246,249,251, + 254,255,256,259,262,265,266,268,270,272,273,275,279,280,281,282,283,286, + 290,291,292,295,298,301,302,304,306,308,309,310,313,316,317,318,321,323) + B163=(5,6,8,9,10,14,15,17,18,22,24,25,26,28,29,30,31,34,40,42,43,44,46, + 47,48,49,52,56,57,59,63,64,65,66,67,70,74,75,76,79,82,83,84,87,89,92,93, + 94,97,100,103,104,106,108,110,111,113,117,118,119,120,121,124,128,129, + 130,133,136,139,140,142,144,146,147,148,151,154,155,156,159,161,181,185, + 189,191,192,194,195,197,198,199,203,207,209,210,212,213,215,216,217,222, + 224,229,230,231,232,233,234,236,237,238,240,241,242,244,245,246,254,255, + 256,257,259,261,262,265,268,271,276,278,283,284,285,286,287,288,290,291, + 292,293,295,297,298,301,304,308,309,310,312,313,314,316,317,318) + Gamma=Graph(multiedges=False,name='Janko-Kharaghani-Tonchev') + for i,b in ((1,B1),(163,B163)): + for j in map(lambda x: x[0], st.OrbitsDomain(b)): + Gamma.add_edges(map(tuple,G.Orbit(libgap.Set([i,j]), libgap.OnSets))) + Gamma.relabel() + return Gamma diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 718e20f28fd..2c1318db8a4 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Generic graphs (common to directed/undirected) @@ -155,6 +156,7 @@ :meth:`~GenericGraph.is_vertex_transitive` | Return whether the automorphism group of self is transitive within the partition provided :meth:`~GenericGraph.is_isomorphic` | Test for isomorphism between self and other. :meth:`~GenericGraph.canonical_label` | Return the unique graph on `\{0,1,...,n-1\}` ( ``n = self.order()`` ) which 1) is isomorphic to self 2) is invariant in the isomorphism class. + :meth:`~GenericGraph.is_cayley` | Check whether the graph is a Cayley graph. **Graph properties:** @@ -1167,7 +1169,7 @@ def export_to_file(self, filename, format=None, **kwds): import networkx formats = {"adjlist" : networkx.write_adjlist, - "dot" : networkx.write_dot, + "dot" : networkx.drawing.nx_pydot.write_dot, "edgelist" : networkx.write_edgelist, "gexf" : networkx.write_gexf, "gml" : networkx.write_gml, @@ -7026,17 +7028,21 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con else: edges = [(u,v,self.edge_label(u,v)), (v,u,self.edge_label(v,u))] - answer = self.subgraph(edges = edges) + answer = self.subgraph(edges = edges, immutable = False) answer.set_pos(self.get_pos()) answer.name("TSP from "+self.name()) + if self.is_immutable(): + answer = answer.copy(immutable = True) return answer else: if self.has_multiple_edges() and len(self.edge_label(u,v)) > 1: edges = self.edges() edges.sort(key=weight) - answer = self.subgraph(edges = edges[:2]) + answer = self.subgraph(edges = edges[:2], immutable = False) answer.set_pos(self.get_pos()) answer.name("TSP from "+self.name()) + if self.is_immutable(): + answer = answer.copy(immutable = True) return answer raise EmptySetError("The given graph is not Hamiltonian") @@ -7215,9 +7221,11 @@ def traveling_salesman_problem(self, use_edge_labels = False, solver = None, con raise EmptySetError("The given graph is not Hamiltonian") # We can now return the TSP ! - answer = self.subgraph(edges = h.edges()) + answer = self.subgraph(edges = h.edges(), immutable = False) answer.set_pos(self.get_pos()) answer.name("TSP from "+g.name()) + if self.is_immutable(): + answer = answer.copy(immutable = True) return answer ################################################# @@ -12548,7 +12556,7 @@ def is_clique(self, vertices=None, directed_clique=False): False """ if directed_clique and self._directed: - subgraph=self.subgraph(vertices) + subgraph=self.subgraph(vertices, immutable = False) subgraph.allow_loops(False) subgraph.allow_multiple_edges(False) n=subgraph.order() @@ -20789,6 +20797,158 @@ def canonical_label(self, partition=None, certify=False, verbosity=0, else: return H + def is_cayley(self, return_group = False, mapping = False, + generators = False, allow_disconnected = False): + r""" + Check whether the graph is a Cayley graph. + + If none of the parameters are ``True``, return a boolean indicating + whether the graph is a Cayley graph. Otherwise, return a tuple + containing said boolean and the requested data. If the graph is not + a Cayley graph, each of the data will be ``None``. + + .. NOTE:: + + For this routine to work on all graphs, the optional packages + ``gap_packages`` and ``database_gap`` need to be installed: to do + so, it is enough to run ``sage -i gap_packages database_gap``. + + INPUT: + + - ``return_group`` (boolean; ``False``) -- If True, return a group for + which the graph is a Cayley graph. + + - ``mapping`` (boolean; ``False``) -- If True, return a mapping from + vertices to group elements. + + - ``generators`` (boolean; ``False``) -- If True, return the generating + set of the Cayley graph. + + - ``allow_disconnected`` (boolean; ``False``) -- If True, disconnected + graphs are considered Cayley if they can be obtained from the Cayley + construction with a generating set that does not generate the group. + + ALGORITHM: + + For connected graphs, find a regular subgroup of the automorphism + group. For disconnected graphs, check that the graph is + vertex-transitive and perform the check on one of its connected + components. If a simple graph has density over 1/2, perform the check + on its complement as its disconnectedness may increase performance. + + EXAMPLES: + + A Petersen Graph is not a Cayley graph:: + + sage: g = graphs.PetersenGraph() + sage: g.is_cayley() + False + + A Cayley digraph is a Cayley graph:: + + sage: C7 = groups.permutation.Cyclic(7) + sage: S = [(1,2,3,4,5,6,7), (1,3,5,7,2,4,6), (1,5,2,6,3,7,4)] + sage: d = C7.cayley_graph(generators=S) + sage: d.is_cayley() + True + + Graphs with loops and multiedges will have identity and repeated + elements, respectively, among the generators:: + + sage: g = Graph(graphs.PaleyGraph(9), loops=True, multiedges=True) + sage: g.add_edges([(u, u) for u in g]) + sage: g.add_edges([(u, u+1) for u in g]) + sage: _, S = g.is_cayley(generators=True) + sage: S # random + [(), + (0,2,1)(a,a + 2,a + 1)(2*a,2*a + 2,2*a + 1), + (0,2,1)(a,a + 2,a + 1)(2*a,2*a + 2,2*a + 1), + (0,1,2)(a,a + 1,a + 2)(2*a,2*a + 1,2*a + 2), + (0,1,2)(a,a + 1,a + 2)(2*a,2*a + 1,2*a + 2), + (0,2*a + 2,a + 1)(1,2*a,a + 2)(2,2*a + 1,a), + (0,a + 1,2*a + 2)(1,a + 2,2*a)(2,a,2*a + 1)] + + TESTS: + + Cayley graphs can be reconstructed from the group and generating set:: + + sage: g = graphs.PaleyGraph(9) + sage: _, G, S = g.is_cayley(return_group=True, generators=True) + sage: Graph(G.cayley_graph(generators=S)).is_isomorphic(g) + True + + A disconnected graphs may also be a Cayley graph:: + + sage: g = graphs.PaleyGraph(9) + sage: h = g.disjoint_union(g) + sage: h = h.disjoint_union(h) + sage: h = h.disjoint_union(g) + sage: _, G, d, S = h.is_cayley(return_group=True, mapping=True, generators=True, allow_disconnected=True) + sage: all(set(d[u] for u in h.neighbors(v)) == set(d[v]*x for x in S) for v in h) + True + + The method also works efficiently with dense simple graphs:: + + sage: graphs.CompleteBipartiteGraph(50, 50).is_cayley() + True + + """ + compute_map = mapping or generators + certificate = return_group or compute_map + c, G, map, genset = False, None, None, None + if not self.is_connected(): + if allow_disconnected and self.is_vertex_transitive(): + C = self.connected_components_subgraphs() + if certificate: + c, CG = C[0].is_cayley(return_group = True) + if c: + from sage.groups.perm_gps.permgroup import PermutationGroup + I = [C[0].is_isomorphic(g, certify=True)[1] for g in C] + # gens generate the direct product of CG and a cyclic group + gens = [sum([[tuple([M[x] for x in p]) + for p in h.cycle_tuples()] for M in I], []) + for h in CG.gens()] + \ + [[tuple([M[v] for M in I]) + for v in C[0].vertices()]] + G = PermutationGroup(gens, domain = self.vertices()) + else: + c = C[0].is_cayley(return_group = False) + elif not self.allows_loops() and not self.allows_multiple_edges() and \ + self.density() > Rational(1)/Rational(2): + if certificate: + c, G = self.complement().is_cayley(return_group = True, + allow_disconnected = True) + else: + c = self.complement().is_cayley(return_group = False, + allow_disconnected = True) + else: + A = self.automorphism_group() + if certificate: + G = A.has_regular_subgroup(return_group = True) + c = G is not None + else: + c = A.has_regular_subgroup(return_group = False) + if c and compute_map: + v = next(self.vertex_iterator()) + map = {(f**-1)(v): f for f in G} + if generators: + # self.(out_)neighbors ignores multiedges, + # so we use edge_iterator instead + adj = [y if v == x else x + for x, y, z in self.edge_iterator(v)] + genset = [map[u] for u in adj] + if certificate: + out = [c] + if return_group: + out.append(G) + if mapping: + out.append(map) + if generators: + out.append(genset) + return tuple(out) + else: + return c + import types import sage.graphs.distances_all_pairs diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 78d46ebc797..7dc9166da3b 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ GenericGraph Cython functions @@ -1088,7 +1089,7 @@ cpdef tuple find_hamiltonian( G, long max_iter=100000, long reset_bound=30000, l sage: fh(G,find_path=True) (True, [8, 9, 10, 11, 18, 17, 4, 3, 19, 0, 1, 2, 6, 7, 14, 13, 12, 16, 15, 5]) - Another test, now in the Moebius-Kantor graph which is also + Another test, now in the Möbius-Kantor graph which is also Hamiltonian, as in our previous example, we are able to find a Hamiltonian cycle and path :: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index bbe712c65c0..cc842af9096 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3648,7 +3648,7 @@ def chromatic_symmetric_function(self, R=None): sage: s(XG) 30*s[1, 1, 1, 1, 1] + 10*s[2, 1, 1, 1] + 10*s[2, 2, 1] - Not all graphs have a postive Schur expansion:: + Not all graphs have a positive Schur expansion:: sage: G = graphs.ClawGraph() sage: XG = G.chromatic_symmetric_function(); XG diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 3319716c742..63672b7b81f 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -128,6 +128,7 @@ def __append_to_doc(methods): "HoltGraph", "HortonGraph", "JankoKharaghaniGraph", + "JankoKharaghaniTonchevGraph", "KittellGraph", "KrackhardtKiteGraph", "Klein3RegularGraph", @@ -1924,6 +1925,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None HoltGraph = staticmethod(sage.graphs.generators.smallgraphs.HoltGraph) HortonGraph = staticmethod(sage.graphs.generators.smallgraphs.HortonGraph) JankoKharaghaniGraph = staticmethod(sage.graphs.generators.smallgraphs.JankoKharaghaniGraph) + JankoKharaghaniTonchevGraph = staticmethod(sage.graphs.generators.smallgraphs.JankoKharaghaniTonchevGraph) KittellGraph = staticmethod(sage.graphs.generators.smallgraphs.KittellGraph) KrackhardtKiteGraph = staticmethod(sage.graphs.generators.smallgraphs.KrackhardtKiteGraph) Klein3RegularGraph = staticmethod(sage.graphs.generators.smallgraphs.Klein3RegularGraph) diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 3a68ca639d2..8648d072360 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -52,6 +52,7 @@ first two calls to :meth:`~sage.graphs.generic_graph.GenericGraph.show` use this option, while the third does not (a value for ``figsize`` is explicitly given):: + sage: import sage.graphs.graph_plot sage: sage.graphs.graph_plot.DEFAULT_SHOW_OPTIONS['figsize'] = [6,6] sage: graphs.PetersenGraph().show() # long time sage: graphs.ChvatalGraph().show() # long time diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 8bd35726be9..df8198b0b69 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -11,7 +11,7 @@ Hyperbolicity .. MATH:: - S_1 = dist(a, b) + dist(b, c)\\ + S_1 = dist(a, b) + dist(d, c)\\ S_2 = dist(a, c) + dist(b, d)\\ S_3 = dist(a, d) + dist(b, c)\\ @@ -153,6 +153,7 @@ AUTHORS: distribution, sampling - David Coudert (2014): improved exact algorithm using far-apart pairs - Michele Borassi (2015): cleaned the code and implemented the new algorithm +- Karan Desai (2016): fixed minor typo in documentation Methods diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index c7c9dc9c616..2c1a24436cd 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -38,7 +38,7 @@ from sage.combinat.designs.bibd import balanced_incomplete_block_design from sage.graphs.graph import Graph from libc.math cimport sqrt, floor from sage.matrix.constructor import Matrix -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.coding.linear_code import LinearCode from sage.rings.sum_of_squares cimport two_squares_c from libc.stdint cimport uint_fast32_t @@ -1135,7 +1135,9 @@ def SRG_from_RSHCD(v,k,l,mu, existence=False,check=True): guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - EXAMPLES:: + EXAMPLES: + + some graphs :: sage: from sage.graphs.strongly_regular_db import SRG_from_RSHCD sage: SRG_from_RSHCD(784, 0, 14, 38, existence=True) @@ -1145,6 +1147,16 @@ def SRG_from_RSHCD(v,k,l,mu, existence=False,check=True): sage: SRG_from_RSHCD(144, 65, 28, 30) Graph on 144 vertices + an example with vertex-transitive automorphism group, found during the + implementation of the case `v=324` :: + + sage: G=SRG_from_RSHCD(324,152,70,72) # long time + sage: a=G.automorphism_group() # long time + sage: a.order() # long time + 2592 + sage: len(a.orbits()) # long time + 1 + TESTS:: sage: SRG_from_RSHCD(784, 0, 14, 38) diff --git a/src/sage/groups/abelian_gps/element_base.py b/src/sage/groups/abelian_gps/element_base.py index 657ee1d2c2a..409164fbca9 100644 --- a/src/sage/groups/abelian_gps/element_base.py +++ b/src/sage/groups/abelian_gps/element_base.py @@ -33,7 +33,7 @@ class AbelianGroupElementBase(MultiplicativeGroupElement): The group element is defined by a tuple whose ``i``-th entry is an integer in the range from 0 (inclusively) to ``G.gen(i).order()`` (exclusively) if the `i`-th generator is of finite order, and an - arbitrary integer if the `i`-th generator is of infinte order. + arbitrary integer if the `i`-th generator is of infinite order. INPUT: diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index 61bf372f931..419c73fbf8e 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -173,7 +173,7 @@ def __classcall__(cls, *args, **kwds): degree, ring = args from sage.rings.integer import is_Integer if is_Integer(ring): - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) return super(AffineGroup, cls).__classcall__(cls, degree, ring) diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index 36fb41a18a0..305b57052bc 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -63,7 +63,7 @@ from sage.rings.all import ZZ from sage.rings.integer import is_Integer from sage.rings.ring import is_Ring -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.interfaces.gap import gap from sage.matrix.matrix import is_Matrix from sage.matrix.matrix_space import MatrixSpace, is_MatrixSpace @@ -194,7 +194,7 @@ def QuaternionMatrixGroupGF3(): sage: QP.is_isomorphic(H) False """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace MS = MatrixSpace(FiniteField(3), 2) aye = MS([1,1,1,2]) diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 4195fa41e8a..698a58790d6 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -51,7 +51,7 @@ from sage.rings.all import ZZ from sage.rings.integer import is_Integer from sage.rings.ring import is_Ring -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.interfaces.gap import gap from sage.matrix.matrix import is_Matrix from sage.matrix.matrix_space import MatrixSpace, is_MatrixSpace diff --git a/src/sage/groups/matrix_gps/morphism.py b/src/sage/groups/matrix_gps/morphism.py index 2d4a0e67d47..2e2c47a805e 100644 --- a/src/sage/groups/matrix_gps/morphism.py +++ b/src/sage/groups/matrix_gps/morphism.py @@ -224,17 +224,17 @@ def pushforward(self, J, *args,**kwds): INPUT: - ``J`` -- a subgroup or an element of the domain of ``self``. + ``J`` -- a subgroup or an element of the domain of ``self`` OUTPUT: - The image of ``J`` under ``self`` + The image of ``J`` under ``self``. - NOTE: + .. NOTE:: - ``pushforward`` is the method that is used when a map is called on - anything that is not an element of its domain. For historical reasons, - we keep the alias ``image()`` for this method. + ``pushforward`` is the method that is used when a map is called + on anything that is not an element of its domain. For historical + reasons, we keep the alias ``image()`` for this method. EXAMPLES:: @@ -254,9 +254,9 @@ def pushforward(self, J, *args,**kwds): [0 1] ) - The following tests against trac ticket #10659:: + The following tests against :trac:`10659`:: - sage: phi(H) # indirect doctestest + sage: phi(H) # indirect doctest Matrix group over Finite Field of size 7 with 1 generators ( [4 0] [0 1] @@ -269,16 +269,17 @@ def pushforward(self, J, *args,**kwds): from sage.groups.matrix_gps.all import MatrixGroup img_gens = [x.matrix(F) for x in phi.Image(gapJ).GeneratorsOfGroup()] return MatrixGroup(img_gens) - return phi.Image(gapJ).matrix(F) + C = self.codomain() + return C(phi.Image(gapJ).matrix(F)) image = pushforward - def _call_( self, g ): + def _call_(self, g): """ Call syntax for morphisms. - Some python code for wrapping GAP's Images function for a - matrix group G. Returns an error if g is not in G. + Some python code for wrapping GAP's ``Images`` function for a + matrix group ``G``. Returns an error if ``g`` is not in ``G``. EXAMPLES:: @@ -304,10 +305,10 @@ def _call_( self, g ): [1 1] [0 1] - TEST: + TESTS: The following tests that the call method was successfully - improved in trac ticket #10659:: + improved in :trac:`10659`:: sage: O = WeylGroup(['D',6]) sage: r = prod(O.gens()) @@ -347,9 +348,19 @@ def _call_( self, g ): [0 0 0 0 1 0] [ 0 0 0 0 1 0] [1 0 0 0 0 0], [-1 0 0 0 0 0] ) + + We check that :trac:`19780` is fixed:: + + sage: G = groups.matrix.SO(3, 3) + sage: H = groups.matrix.GL(3, 3) + sage: phi = G.hom([H(x) for x in G.gens()]) + sage: phi(G.one()).parent() + General Linear Group of degree 3 over Finite Field of size 3 """ phi = self.gap() G = self.domain() - F = G.base_ring() + C = self.codomain() + F = C.base_ring() h = g.gap() - return phi.Image(h).matrix(F) + return C(phi.Image(h).matrix(F)) + diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index c2b1b6189b4..d27d97636ed 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -110,7 +110,7 @@ def normalize_args_vectorspace(*args, **kwds): from sage.rings.integer import is_Integer try: ring = ZZ(ring) - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) except (ValueError, TypeError): diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx index 726c7909c14..cd2a4724dd0 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx @@ -1052,7 +1052,7 @@ def random_tests(num=50, n_max=50, k_max=6, nwords_max=200, perms_per_code=10, d from sage.misc.prandom import random, randint from sage.combinat.permutation import Permutations from sage.matrix.constructor import random_matrix, matrix - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF cdef int h, i, j, n, k, num_tests = 0, num_codes = 0 cdef LinearBinaryCodeStruct B, C cdef NonlinearBinaryCodeStruct B_n, C_n diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx index 1776a179b0d..296150b403e 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx @@ -317,7 +317,7 @@ def random_tests(n=10, nrows_max=50, ncols_max=50, nsymbols_max=10, perms_per_ma from sage.misc.prandom import random, randint from sage.combinat.permutation import Permutations from sage.matrix.constructor import random_matrix, matrix - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.arith.all import next_prime cdef int h, i, j, nrows, k, num_tests = 0, num_matrices = 0 cdef MatrixStruct M, N diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 336f0d400a0..bd860c917de 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -27,6 +27,13 @@ Mathematical Folk Humor." Notices Amer. Math. Soc. 52, 24-34, 2005. +Index of methods +---------------- + +Here are the method of a :func:`PermutationGroup` + +{METHODS_OF_PermutationGroup_generic} + AUTHORS: - David Joyner (2005-10-14): first version @@ -930,17 +937,19 @@ def identity(self): return self._element_class()([], self, check=True) def exponent(self): - """ - Computes the exponent of the group. The exponent `e` of a - group `G` is the LCM of the orders of its elements, that - is, `e` is the smallest integer such that `g^e=1` - for all `g \in G`. + r""" + Computes the exponent of the group. + + The exponent `e` of a group `G` is the LCM of the orders of its + elements, that is, `e` is the smallest integer such that `g^e=1` for all + `g \in G`. EXAMPLES:: sage: G = AlternatingGroup(4) sage: G.exponent() 6 + """ return Integer(self._gap_().Exponent()) @@ -1744,9 +1753,10 @@ def socle(self): def frattini_subgroup(self): r""" - Returns the Frattini subgroup of ``self``. The Frattini - subgroup of a group $G$ is the intersection of all - maximal subgroups of $G$. + Returns the Frattini subgroup of ``self``. + + The Frattini subgroup of a group $G$ is the intersection of all maximal + subgroups of `G`. EXAMPLES:: @@ -1762,9 +1772,10 @@ def frattini_subgroup(self): def fitting_subgroup(self): r""" - Returns the Fitting subgroup of ``self``. The Fitting - subgroup of a group $G$ is the largest nilpotent normal - subgroup of $G$. + Returns the Fitting subgroup of ``self``. + + The Fitting subgroup of a group $G$ is the largest nilpotent normal + subgroup of `G`. EXAMPLES:: @@ -2554,6 +2565,7 @@ def cohomology(self, n, p = 0): r""" Computes the group cohomology `H^n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if `p > 0` is a prime. + Wraps HAP's ``GroupHomology`` function, written by Graham Ellis. REQUIRES: GAP package HAP (in gap_packages-\*.spkg). @@ -2602,7 +2614,9 @@ def cohomology_part(self, n, p = 0): """ Computes the p-part of the group cohomology `H^n(G, F)`, where `F = \ZZ` if `p=0` and `F = \ZZ / p \ZZ` if - `p > 0` is a prime. Wraps HAP's Homology function, written + `p > 0` is a prime. + + Wraps HAP's Homology function, written by Graham Ellis, applied to the `p`-Sylow subgroup of `G`. @@ -2713,7 +2727,9 @@ def character_table(self): r""" Returns the matrix of values of the irreducible characters of a permutation group `G` at the conjugacy classes of - `G`. The columns represent the conjugacy classes of + `G`. + + The columns represent the conjugacy classes of `G` and the rows represent the different irreducible characters in the ordering given by GAP. @@ -2846,7 +2862,9 @@ def character(self, values): def conjugacy_classes_representatives(self): """ Returns a complete list of representatives of conjugacy classes in - a permutation group `G`. The ordering is that given by GAP. + a permutation group `G`. + + The ordering is that given by GAP. EXAMPLES:: @@ -2878,8 +2896,9 @@ def conjugacy_classes_representatives(self): def conjugacy_classes_subgroups(self): """ Returns a complete list of representatives of conjugacy classes of - subgroups in a permutation group `G`. The ordering is that given by - GAP. + subgroups in a permutation group `G`. + + The ordering is that given by GAP. EXAMPLES:: @@ -2959,6 +2978,82 @@ def subgroups(self): all_sg.append(self.subgroup(gap_group=h)) return all_sg + @cached_method + def _regular_subgroup_gap(self): + r""" + Return a conjugacy class of regular subgroups, if there is one, as a + GAP element. + + This allows finding such a group without constructing it in Sage. + The result is cached, so constructing the obtained subgroup later is + possible without recomputing it. + + EXAMPLES: + + The symmetric group on 4 elements has a regular subgroup:: + + sage: S4 = groups.permutation.Symmetric(4) + sage: S4._regular_subgroup_gap() # random + ConjugacyClassSubgroups(SymmetricGroup( [ 1 .. 4 ] ),Group( + [ (1,4)(2,3), (1,3)(2,4) ] )) + + """ + gap = self._gap_().parent() + C = gap.new(""" + First(ConjugacyClassesSubgroups(%s), + x -> IsRegular(Representative(x), [1..%d])) + """ % (self._gap_().name(), self.degree())) + # prevent caching GAP fails + if gap.eval('%s = fail' % C.name()) == 'true': + return None + return C + + @cached_method + def has_regular_subgroup(self, return_group = False): + r""" + Return whether the group contains a regular subgroup. + + INPUT: + + - ``return_group`` (boolean) -- If ``return_group = True``, a regular + subgroup is returned if there is one, and ``None`` if there isn't. + When ``return_group = False`` (default), only a boolean indicating + whether such a group exists is returned instead. + + EXAMPLES: + + The symmetric group on 4 elements has a regular subgroup:: + + sage: S4 = groups.permutation.Symmetric(4) + sage: S4.has_regular_subgroup() + True + sage: S4.has_regular_subgroup(return_group = True) # random + Subgroup of (Symmetric group of order 4! as a permutation group) generated by [(1,3)(2,4), (1,4)(2,3)] + + But the automorphism group of Petersen's graph does not:: + + sage: G = graphs.PetersenGraph().automorphism_group() + sage: G.has_regular_subgroup() + False + + """ + b = False + G = None + if self.order() % self.degree() == 0: + if self.order() == len(self.domain()): + b = self.is_transitive() + if b: + G = self + else: + C = self._regular_subgroup_gap() + b = (C is not None) + if b and return_group: + G = self.subgroup(gap_group=C.Representative()) + if return_group: + return G + else: + return b + def blocks_all(self, representatives = True): r""" Returns the list of block systems of imprimitivity. @@ -3632,10 +3727,10 @@ def fixed_points(self): return [i for i in self.domain() if i not in non_fixed_points] def is_transitive(self, domain=None): - """ + r""" Returns ``True`` if ``self`` acts transitively on ``domain``. - A group $G$ acts transitively on set $S$ if for all $x,y\in S$ - there is some $g\in G$ such that $x^g=y$. + A group $G$ acts transitively on set $S$ if for all `x,y\in S` + there is some `g\in G` such that `x^g=y`. EXAMPLES:: @@ -3939,7 +4034,7 @@ def molien_series(self): pi = self._gap_().NaturalCharacter() # because NaturalCharacter forgets about fixed points : pi += self._gap_().TrivialCharacter() * len(self.fixed_points()) - + M = pi.MolienSeries() R = QQ['x'] @@ -3974,14 +4069,14 @@ def normal_subgroups(self): return [self.subgroup(gap_group=group) for group in NS] def poincare_series(self, p=2, n=10): - """ - Returns the Poincare series of `G \mod p` (`p \geq 2` must be a - prime), for `n` large. In other words, if you input a finite - group `G`, a prime `p`, and a positive integer `n`, it returns a - quotient of polynomials `f(x) = P(x) / Q(x)` whose coefficient of - `x^k` equals the rank of the vector space - `H_k(G, \ZZ / p \ZZ)`, for all `k` in the - range `1 \leq k \leq n`. + r""" + Returns the Poincare series of `G \mod p` (`p \geq 2` must be a prime), + for `n` large. + + In other words, if you input a finite group `G`, a prime `p`, and a + positive integer `n`, it returns a quotient of polynomials `f(x) = P(x) + / Q(x)` whose coefficient of `x^k` equals the rank of the vector space + `H_k(G, \ZZ / p \ZZ)`, for all `k` in the range `1 \leq k \leq n`. REQUIRES: GAP package HAP (in gap_packages-\*.spkg). @@ -3997,6 +4092,7 @@ def poincare_series(self, p=2, n=10): AUTHORS: - David Joyner and Graham Ellis + """ if not is_package_installed('gap_packages'): raise RuntimeError("You must install the optional gap_packages package.") @@ -4017,7 +4113,9 @@ def sylow_subgroup(self, p): """ Returns a Sylow `p`-subgroup of the finite group `G`, where `p` is a prime. This is a `p`-subgroup of `G` whose index in `G` is coprime to - `p`. Wraps the GAP function ``SylowSubgroup``. + `p`. + + Wraps the GAP function ``SylowSubgroup``. EXAMPLES:: @@ -4300,3 +4398,5 @@ def is_normal(self, other=None): other = self.ambient_group() return PermutationGroup_generic.is_normal(self, other) +from sage.misc.rest_index_of_methods import gen_rest_table_index +__doc__ = __doc__.format(METHODS_OF_PermutationGroup_generic=gen_rest_table_index(PermutationGroup_generic)) diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 5b5eb7f93d0..dc06826b270 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -85,7 +85,7 @@ from sage.rings.all import Integer from sage.interfaces.all import gap -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.arith.all import factor, valuation from sage.groups.abelian_gps.abelian_group import AbelianGroup from sage.misc.functional import is_even diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py index 8f53b82725c..e86220a1003 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/homology/examples.py @@ -33,6 +33,7 @@ - :func:`PoincareHomologyThreeSphere` - :func:`PseudoQuaternionicProjectivePlane` - :func:`RandomComplex` +- :func:`RandomTwoSphere` - :func:`RealProjectivePlane` - :func:`RealProjectiveSpace` - :func:`Simplex` @@ -1447,7 +1448,72 @@ def SumComplex(n, A): return UniqueSimplicialComplex(facets, name='Sum complex on vertices Z/{}Z associated to {}'.format(n, Set(A))) +def RandomTwoSphere(n): + r""" + Return a random triangulation of the 2-dimensional sphere with `n` + vertices. + + INPUT: + + `n` -- an integer + + OUTPUT: + + A random triangulation of the sphere chosen uniformly among + the *rooted* triangulations on `n` vertices. Because some + triangulations have nontrivial automorphism groups, this may + not be equal to the uniform distribution among unrooted + triangulations. + + ALGORITHM: + + The algorithm is taken from [PS2006]_, section 2.1. + + Starting from a planar tree (represented by its contour as a + sequence of vertices), one first performs local closures, until no + one is possible. A local closure amounts to replace in the cyclic + contour word a sequence ``in1,in2,in3,lf,in3`` by + ``in1,in3``. After all local closures are done, one has reached + the partial closure, as in [PS2006]_, figure 5 (a). + + Then one has to perform complete closure by adding two more + vertices, in order to reach the situation of [PS2006]_, figure 5 + (b). For this, it is necessary to find inside the final contour + one of the two subsequences ``lf,in,lf``. + + At every step of the algorithm, newly created triangles are added + in a simplicial complex. + + This algorithm is implemented in + :meth:`~sage.graphs.generators.random.RandomTriangulation`, which + creates an embedded graph. The triangles of the simplicial + complex are recovered from this embedded graph. + + EXAMPLES:: + + sage: G = simplicial_complexes.RandomTwoSphere(6); G + Simplicial complex with vertex set (0, 1, 2, 3, 'a', 'b') + and 8 facets + sage: G.homology() + {0: 0, 1: 0, 2: Z} + sage: G.is_pure() + True + sage: fg = G.flip_graph(); fg + Graph on 8 vertices + sage: fg.is_planar() and fg.is_regular(3) + True + """ + from sage.graphs.generators.random import RandomTriangulation + + graph = RandomTriangulation(n) + + graph = graph.relabel(inplace=False) + triangles = [(u, v, w) for u, L in graph._embedding.iteritems() + for v, w in zip(L, L[1:] + [L[0]]) if u < v and u < w] + + return SimplicialComplex(triangles, maximality_check=False) + + # For taking care of old pickles from sage.structure.sage_object import register_unpickle_override register_unpickle_override('sage.homology.examples', 'SimplicialSurface', SimplicialComplex) - diff --git a/src/sage/homology/simplicial_complexes_catalog.py b/src/sage/homology/simplicial_complexes_catalog.py index 828d50e9bf4..39b2c63d032 100644 --- a/src/sage/homology/simplicial_complexes_catalog.py +++ b/src/sage/homology/simplicial_complexes_catalog.py @@ -33,6 +33,7 @@ - :meth:`~sage.homology.examples.PoincareHomologyThreeSphere` - :meth:`~sage.homology.examples.PseudoQuaternionicProjectivePlane` - :meth:`~sage.homology.examples.RandomComplex` +- :meth:`~sage.homology.examples.RandomTwoSphere` - :meth:`~sage.homology.examples.RealProjectivePlane` - :meth:`~sage.homology.examples.RealProjectiveSpace` - :meth:`~sage.homology.examples.Simplex` @@ -63,4 +64,5 @@ ComplexProjectivePlane, PseudoQuaternionicProjectivePlane, PoincareHomologyThreeSphere, RealProjectiveSpace, K3Surface, BarnetteSphere, BrucknerGrunbaumSphere, NotIConnectedGraphs, - MatchingComplex, ChessboardComplex, RandomComplex, SumComplex) + MatchingComplex, ChessboardComplex, RandomComplex, SumComplex, + RandomTwoSphere) diff --git a/src/sage/interfaces/all.py b/src/sage/interfaces/all.py index 90edec342c9..e751feff750 100644 --- a/src/sage/interfaces/all.py +++ b/src/sage/interfaces/all.py @@ -3,45 +3,71 @@ from frobby import frobby from four_ti_2 import four_ti_2 -from axiom import Axiom, axiom, axiom_console -from fricas import FriCAS, fricas, fricas_console +from axiom import Axiom, axiom +from fricas import FriCAS, fricas -from gap import gap, gap_reset_workspace, gap_console, set_gap_memory_pool_size, Gap -from gap3 import gap3, gap3_console, gap3_version, Gap3 +from gap import gap, gap_reset_workspace, set_gap_memory_pool_size, Gap +from gap3 import gap3, gap3_version, Gap3 lazy_import('sage.interfaces.genus2reduction', ['genus2reduction', 'Genus2reduction']) from gfan import gfan, Gfan -from giac import giac, giac_console, Giac -from gp import gp, gp_console, gp_version, Gp -from gnuplot import gnuplot, gnuplot_console -from kash import kash, kash_console, kash_version, Kash -from lisp import lisp, lisp_console, Lisp -from magma import magma, magma_console, magma_version, Magma +from giac import giac, Giac +from gp import gp, gp_version, Gp +from gnuplot import gnuplot +from kash import kash, kash_version, Kash +from lisp import lisp, Lisp +from magma import magma, magma_version, Magma from magma_free import magma_free -from macaulay2 import macaulay2, macaulay2_console, Macaulay2 -from maple import maple, maple_console, Maple -from maxima_abstract import maxima_console +from macaulay2 import macaulay2, Macaulay2 +from maple import maple, Maple from maxima import maxima, Maxima # import problems #from maxima_lib import maxima_lib -from mathematica import mathematica, mathematica_console, Mathematica -from matlab import matlab, matlab_console, matlab_version, Matlab -from mupad import mupad, mupad_console, Mupad # NOT functional yet -from mwrank import mwrank, Mwrank, mwrank_console -from octave import octave, octave_console, octave_version, Octave -from qepcad import qepcad, qepcad_console, qepcad_version, qepcad_formula +from mathematica import mathematica, Mathematica +from matlab import matlab, matlab_version, Matlab +from mupad import mupad, Mupad # NOT functional yet +from mwrank import mwrank, Mwrank +from octave import octave, octave_version, Octave +from qepcad import qepcad, qepcad_version, qepcad_formula from qsieve import qsieve -from singular import singular, singular_console, singular_version, Singular -from sage0 import sage0 as sage0, sage0_console, sage0_version, Sage +from singular import singular, singular_version, Singular +from sage0 import sage0 as sage0, sage0_version, Sage from scilab import scilab from tachyon import tachyon_rt from psage import PSage from ecm import ECM, ecm from povray import povray -from lie import lie, lie_console, LiE -from r import r, r_console, R, r_version +from lie import lie, LiE +from r import r, R, r_version from read_data import read_data interfaces = ['gap', 'gap3', 'giac', 'gp', 'mathematica', 'gnuplot', \ 'kash', 'magma', 'macaulay2', 'maple', 'maxima', \ 'mathematica', 'mwrank', 'octave', 'r', \ 'singular', 'sage0', 'sage'] + + +from sage.repl.rich_output.display_manager import get_display_manager +if get_display_manager().is_in_terminal(): + from axiom import axiom_console + from fricas import fricas_console + from gap import gap_console + from gap3 import gap3_console + from giac import giac_console + from gp import gp_console + from gnuplot import gnuplot_console + from kash import kash_console + from lisp import lisp_console + from magma import magma_console + from macaulay2 import macaulay2_console + from maple import maple_console + from maxima_abstract import maxima_console + from mathematica import mathematica_console + from matlab import matlab_console + from mupad import mupad_console + from mwrank import mwrank_console + from octave import octave_console + from qepcad import qepcad_console + from singular import singular_console + from sage0 import sage0_console + from lie import lie_console + from r import r_console diff --git a/src/sage/interfaces/axiom.py b/src/sage/interfaces/axiom.py index 1ac2013d081..f7efc22bc02 100644 --- a/src/sage/interfaces/axiom.py +++ b/src/sage/interfaces/axiom.py @@ -1020,5 +1020,8 @@ def axiom_console(): ----------------------------------------------------------------------------- """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%axiom magics instead.') os.system('axiom -nox') diff --git a/src/sage/interfaces/fricas.py b/src/sage/interfaces/fricas.py index 5b0520874ae..56e4fc4a427 100644 --- a/src/sage/interfaces/fricas.py +++ b/src/sage/interfaces/fricas.py @@ -336,6 +336,9 @@ def fricas_console(): Issue )quit to leave AXIOM and return to shell. ----------------------------------------------------------------------------- """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%fricas magics instead.') os.system('fricas -nox') def __doctest_cleanup(): diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 0f1f942c3fe..e21931fea79 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1884,7 +1884,10 @@ def gap_console(): True sage: 'sorry' not in gap_startup True - """ + """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%gap magics instead.') cmd, _ = gap_command(use_workspace_cache=False) cmd += ' ' + os.path.join(SAGE_EXTCODE,'gap','console.g') os.system(cmd) diff --git a/src/sage/interfaces/gap3.py b/src/sage/interfaces/gap3.py index 309b01fd44a..092420a6add 100644 --- a/src/sage/interfaces/gap3.py +++ b/src/sage/interfaces/gap3.py @@ -879,6 +879,9 @@ def gap3_console(): For help enter: ? gap> """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%gap3 magics instead.') os.system(gap3_cmd) def gap3_version(): diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index e403c1c622e..e1a46115865 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -1134,7 +1134,10 @@ def giac_console(): ------------------------------------------------- Press CTRL and D simultaneously to finish session Type ?commandname for help - """ + """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%giac magics instead.') os.system('giac') diff --git a/src/sage/interfaces/gnuplot.py b/src/sage/interfaces/gnuplot.py index 981b7f98571..bf669fd380d 100644 --- a/src/sage/interfaces/gnuplot.py +++ b/src/sage/interfaces/gnuplot.py @@ -189,6 +189,9 @@ def console(self): def gnuplot_console(): + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%gnuplot magics instead.') os.system('sage-native-execute gnuplot') diff --git a/src/sage/interfaces/gp.py b/src/sage/interfaces/gp.py index b7bec6615ed..dc89173ab35 100644 --- a/src/sage/interfaces/gp.py +++ b/src/sage/interfaces/gp.py @@ -890,7 +890,7 @@ def _sage_(self): sage: gp(I).sage() i sage: gp(I).sage().parent() - Maximal Order in Number Field in i with defining polynomial x^2 + 1 + Number Field in i with defining polynomial x^2 + 1 :: @@ -1096,6 +1096,9 @@ def gp_console(): compiled: Jul 21 2010, gcc-4.6.0 20100705 (experimental) (GCC) (readline v6.0 enabled, extended help enabled) """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%gp magics instead.') os.system('gp') diff --git a/src/sage/interfaces/kash.py b/src/sage/interfaces/kash.py index 14e64ceb321..cb4335a835b 100644 --- a/src/sage/interfaces/kash.py +++ b/src/sage/interfaces/kash.py @@ -697,6 +697,9 @@ def reduce_load_Kash(): def kash_console(): + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%kash magics instead.') os.system("kash3 ") diff --git a/src/sage/interfaces/lie.py b/src/sage/interfaces/lie.py index e9834c86733..314d9070aef 100644 --- a/src/sage/interfaces/lie.py +++ b/src/sage/interfaces/lie.py @@ -931,6 +931,9 @@ def lie_console(): ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%lie magics instead.') os.system('bash `which lie`') diff --git a/src/sage/interfaces/lisp.py b/src/sage/interfaces/lisp.py index 5c0a6f70bbc..102b768db03 100644 --- a/src/sage/interfaces/lisp.py +++ b/src/sage/interfaces/lisp.py @@ -561,4 +561,7 @@ def lisp_console(): Type :h for Help. Top level. ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%lisp magics instead.') os.system('ecl') diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 4844e6243ae..8d086852087 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -1220,6 +1220,9 @@ def macaulay2_console(): ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%macaulay2 magics instead.') os.system('M2') diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index 0832e97d14d..7b3e6a6caaf 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2733,6 +2733,9 @@ def magma_console(): > Total time: 2.820 seconds, Total memory usage: 3.95MB """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%magma magics instead.') console('sage-native-execute magma') def magma_version(): diff --git a/src/sage/interfaces/maple.py b/src/sage/interfaces/maple.py index c1b22dbffb8..43aea2118d9 100644 --- a/src/sage/interfaces/maple.py +++ b/src/sage/interfaces/maple.py @@ -1169,6 +1169,9 @@ def maple_console(): | Type ? for help. > """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%maple magics instead.') os.system('maple') diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index b8d2a236801..ddb49cf5432 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -973,6 +973,9 @@ def reduce_load(X): def mathematica_console(readline=True): + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%mathematica magics instead.') if not readline: os.system('math') return diff --git a/src/sage/interfaces/matlab.py b/src/sage/interfaces/matlab.py index 45db1d78e60..5a97d3e62c0 100644 --- a/src/sage/interfaces/matlab.py +++ b/src/sage/interfaces/matlab.py @@ -387,6 +387,9 @@ def matlab_console(): matlab, like Sage, remembers its history from one session to another. """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%matlab magics instead.') os.system('matlab -nodisplay') diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 14ac6b522bd..42fad31e361 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Abstract interface to Maxima @@ -799,7 +800,7 @@ def plot3d_parametric(self, r, vars, urange, vrange, options=None): sage: _ = maxima.eval("expr_1: cos(y)*(10.0+6*cos(x)); expr_2: sin(y)*(10.0+6*cos(x)); expr_3: -6*sin(x);") sage: maxima.plot3d_parametric(["expr_1","expr_2","expr_3"], ["x","y"],[0,6],[0,6]) # not tested - Here is a Mobius strip:: + Here is a Möbius strip:: sage: x = "cos(u)*(3 + v*cos(u/2))" sage: y = "sin(u)*(3 + v*cos(u/2))" @@ -2366,4 +2367,7 @@ def maxima_console(): Maxima 5.34.1 http://maxima.sourceforge.net ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%maxima magics instead.') os.system('maxima') diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 2793e21ee43..57ba0bfd34f 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -1565,7 +1565,7 @@ def sr_to_max(expr): max_op_dict[op_max]=op return EclObject(([sage_op_dict[op]], [sr_to_max(o) for o in expr.operands()])) - elif expr.is_symbol() or expr.is_constant(): + elif expr.is_symbol() or expr._is_registered_constant_(): if not expr in sage_sym_dict: sym_max=maxima(expr).ecl() sage_sym_dict[expr]=sym_max diff --git a/src/sage/interfaces/mupad.py b/src/sage/interfaces/mupad.py index 6ec1cb065c6..4f4cfcc8fad 100644 --- a/src/sage/interfaces/mupad.py +++ b/src/sage/interfaces/mupad.py @@ -680,6 +680,9 @@ def mupad_console(): *----* Licensed to: ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%mupad magics instead.') os.system('mupkern') diff --git a/src/sage/interfaces/mwrank.py b/src/sage/interfaces/mwrank.py index 2585e02a8cc..2bb30463164 100644 --- a/src/sage/interfaces/mwrank.py +++ b/src/sage/interfaces/mwrank.py @@ -358,5 +358,8 @@ def mwrank_console(): sage: mwrank_console() # not tested: expects console input Program mwrank: ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%mwrank magics instead.') os.system('mwrank') diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index 862b3fc786f..176cb7e1eac 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -773,6 +773,9 @@ def octave_console(): octave, like Sage, remembers its history from one session to another. """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%octave magics instead.') os.system('octave') diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index bde7c745139..183626bf483 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -1667,6 +1667,9 @@ def qepcad_console(memcells=None): ... Enter an informal description between '[' and ']': """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%qepcat magics instead.') # This will only spawn local processes os.system(_qepcad_cmd(memcells)) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 38d006b033f..01c1f48354d 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -2097,6 +2097,9 @@ def r_console(): ISBN 3-900051-07-0 ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%r magics instead.') # This will only spawn local processes os.system('R --vanilla') diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index 1462030975e..166d58be19a 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -529,6 +529,9 @@ def sage0_console(): ---------------------------------------------------------------------- ... """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%sage0 magics instead.') os.system('sage') def sage0_version(): diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index 44a231ea2b9..e1893da0ef2 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -2326,6 +2326,9 @@ def singular_console(): by: G.-M. Greuel, G. Pfister, H. Schoenemann \ Nov 2007 FB Mathematik der Universitaet, D-67653 Kaiserslautern \ """ + from sage.repl.rich_output.display_manager import get_display_manager + if not get_display_manager().is_in_terminal(): + raise RuntimeError('Can use the console only in the terminal. Try %%singular magics instead.') os.system('Singular') diff --git a/src/sage/libs/arb/acb.pxd b/src/sage/libs/arb/acb.pxd index c04b39d6d4d..180ed97f2c8 100644 --- a/src/sage/libs/arb/acb.pxd +++ b/src/sage/libs/arb/acb.pxd @@ -158,3 +158,6 @@ cdef extern from "acb.h": void acb_agm1(acb_t m, const acb_t z, long prec) void acb_agm1_cpx(acb_ptr m, const acb_t z, long len, long prec) + + acb_ptr _acb_vec_init(long n) + void _acb_vec_clear(acb_ptr v, long n) diff --git a/src/sage/libs/arb/arith.pyx b/src/sage/libs/arb/arith.pyx new file mode 100644 index 00000000000..8cd25979899 --- /dev/null +++ b/src/sage/libs/arb/arith.pyx @@ -0,0 +1,54 @@ +""" +Arithmetic functions using the arb library +""" + +#***************************************************************************** +# Copyright (C) 2016 Jeroen Demeyer +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from ..flint.types cimport ulong +from ..flint.fmpq cimport fmpq_t, fmpq_init, fmpq_clear, fmpq_get_mpq +from .bernoulli cimport bernoulli_fmpq_ui +from sage.rings.rational cimport Rational + +def bernoulli(n): + """ + Return the ``n``-th Bernoulli number using ``arb``. + + INPUT: + + - ``n`` -- unsigned integer + + OUTPUT: a rational number + + EXAMPLES:: + + sage: from sage.libs.arb.arith import bernoulli + sage: bernoulli(24) + -236364091/2730 + sage: bernoulli(0) + 1 + sage: bernoulli(1) + -1/2 + + TESTS:: + + sage: bernoulli(-1) + Traceback (most recent call last): + ... + OverflowError: can't convert negative value to mp_limb_t + """ + cdef ulong i = n + cdef Rational q = Rational.__new__(Rational) + cdef fmpq_t x + fmpq_init(x) + bernoulli_fmpq_ui(x, i) + fmpq_get_mpq(q.value, x) + fmpq_clear(x) + return q diff --git a/src/sage/libs/arb/bernoulli.pxd b/src/sage/libs/arb/bernoulli.pxd new file mode 100644 index 00000000000..f1d1974a57d --- /dev/null +++ b/src/sage/libs/arb/bernoulli.pxd @@ -0,0 +1,6 @@ +# distutils: libraries = arb + +from ..flint.types cimport fmpq_t, ulong + +cdef extern from "bernoulli.h": + void bernoulli_fmpq_ui(fmpq_t b, ulong n) diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx index 842ae99d680..4dc0e1231c5 100644 --- a/src/sage/libs/fes.pyx +++ b/src/sage/libs/fes.pyx @@ -79,7 +79,7 @@ include 'sage/ext/stdsage.pxi' #sage_calloc(), sage_free() from sage.rings.integer import Integer from sage.rings.infinity import Infinity -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.structure.parent cimport Parent from sage.structure.sequence import Sequence diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index a9a39ad224e..2415513a270 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1434,7 +1434,7 @@ cdef class GapElement_FiniteField(GapElement): deg = self.DegreeFFE().sage() char = self.Characteristic().sage() if ring is None: - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF ring = GF(char**deg, name=var) if self.IsOne(): @@ -1678,7 +1678,7 @@ cdef class GapElement_Ring(GapElement): Finite Field in A of size 3^2 """ size = self.Size().sage() - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF return GF(size, name=var) @@ -2602,7 +2602,7 @@ cdef class GapElement_RecordIterator(object): def __next__(self): r""" - The next elemnt in the record. + Return the next element in the record. OUTPUT: diff --git a/src/sage/libs/meataxe.pxd b/src/sage/libs/meataxe.pxd new file mode 100644 index 00000000000..68878f3fa19 --- /dev/null +++ b/src/sage/libs/meataxe.pxd @@ -0,0 +1,148 @@ +#***************************************************************************** +# Copyright (C) 2015 Simon King +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# +# Import SOME features from meataxe.h +# (most types are not needed, but listed here +# in the comments, for completeness) +# +cdef extern from "meataxe.h": + # general ctype emulations + # ctypedef int size_t # size_t should be a standard type! + # ctypedef unsigned long Ulong + # ctypedef unsigned short Ushort + # ctypedef unsigned char Uchar + ctypedef unsigned char FEL + ctypedef FEL *PTR + + # global constants + cdef extern int FfOrder # Current field order + cdef extern int FfChar # Current characteristic + cdef extern FEL FfGen # Generator + cdef extern int FfNoc # Number of columns for row ops + cdef extern size_t FfCurrentRowSize # The byte size of a single row in memory, + # always a multiple of sizeof(long) + cdef extern size_t FfCurrentRowSizeIo # The number of bytes actually used in a row. + cdef extern char MtxLibDir[250] # Where to search/create multiplication tables + + # we only wrap MeatAxe for small fields (size < 255) + cdef extern FEL mtx_tmult[256][256] + cdef extern FEL mtx_tadd[256][256] + cdef extern FEL mtx_taddinv[256] + cdef extern FEL mtx_tmultinv[256] + cdef extern FEL mtx_tinsert[8][256] + cdef extern FEL mtx_textract[8][256] + cdef extern FEL FF_ONE, FF_ZERO + +######################################### +# function prototypes + ## global parameters + size_t FfRowSize(int noc) + size_t FfTrueRowSize(int noc) # Difference to FfRowSize: Doesn't count padding bytes + int FfSetField(int field) except -1 + int FfSetNoc(int ncols) except -1 + + ## Finite Fields + # FEL FfAdd(FEL a,FEL b) + # FEL FfSub(FEL a, FEL b) + # FEL FfNeg(FEL a) + # FEL FfMul(FEL a, FEL b) + # FEL FfDiv(FEL a, FEL b) + # FEL FfInv(FEL a) + # FEL FfEmbed(FEL a, int subfield) except 255 + # FEL FfRestrict(FEL a, int subfield) except 255 + FEL FfFromInt(int l) + int FfToInt(FEL f) + + ## Rows + void FfMulRow(PTR row, FEL mark) + void FfAddMulRow(PTR dest, PTR src, FEL f) + PTR FfAddRow(PTR dest, PTR src) + PTR FfSubRow(PTR dest, PTR src) + FEL FfExtract(PTR row, int col) + void FfInsert(PTR row, int col, FEL mark) + int FfFindPivot(PTR row, FEL *mark) + FEL FfScalarProduct(PTR a, PTR b) + void FfSwapRows(PTR dest, PTR src) + void FfPermRow(PTR row, long *perm, PTR result) + int FfCmpRows(PTR p1, PTR p2) + + ## multiple rows + PTR FfAlloc(int nor) except NULL + void FfExtractColumn(PTR mat,int nor,int col,PTR result) + int FfStepPtr(PTR *x) # Advance to next row + PTR FfGetPtr(PTR base, int row) # Advance to "row" rows after base + void FfInsert(PTR row, int col, FEL mark) + void FfMapRow(PTR row, PTR matrix, int nor, PTR result) + + ############ + ## Skip: Application, error handling, i/o + + ############ + ## Matrices + ############ + ctypedef struct Matrix_t: + unsigned long Magic #/* Used internally */ + int Field, Nor, Noc #/* Field, #rows, #columns */ + PTR Data #/* Pointer to data area */ + int RowSize # Size (in bytes) of one row + int *PivotTable # Pivot table (if matrix is in echelon form + ## Basic memory operations + Matrix_t *MatAlloc(int field, int nor, int noc) except NULL + int MatFree(Matrix_t *mat) + PTR MatGetPtr(Matrix_t *mat, int row) except NULL + int MatCompare(Matrix_t *a, Matrix_t *b) except -2 + int MatCopyRegion(Matrix_t *dest, int destrow, int destcol, Matrix_t *src, int row1, int col1, int nrows, int ncols) except -1 + Matrix_t *MatCut(Matrix_t *src, int row1, int col1, int nrows, int ncols) except NULL + Matrix_t *MatCutRows(Matrix_t *src, int row1, int nrows) except NULL + Matrix_t *MatDup(Matrix_t *src) except NULL + Matrix_t *MatId(int fl, int nor) except NULL + Matrix_t *MatLoad(char *fn) except NULL + int MatSave(Matrix_t *mat, char *fn) except -1 + + + ## Basic Arithmetic ## general rule: dest is changed, src/mat are unchanged! + Matrix_t *MatTransposed(Matrix_t *src) except NULL + Matrix_t *MatAdd(Matrix_t *dest, Matrix_t *src) except NULL + Matrix_t *MatAddMul(Matrix_t *dest, Matrix_t *src, FEL coeff) except NULL + Matrix_t *MatMul(Matrix_t *dest, Matrix_t *src) except NULL + Matrix_t *MatMulScalar(Matrix_t *dest, FEL coeff) except NULL + Matrix_t *MatPower(Matrix_t *mat, long n) except NULL + int StablePower(Matrix_t *mat, int *pwr, Matrix_t **ker) except -1 + FEL MatTrace(Matrix_t *mat) except 255 + Matrix_t *MatMulStrassen(Matrix_t *dest, Matrix_t *A, Matrix_t *B) except NULL + void StrassenSetCutoff(size_t size) + + ## "Higher" Arithmetic + Matrix_t *MatTensor(Matrix_t *m1, Matrix_t *m2) except NULL + Matrix_t *TensorMap(Matrix_t *vec, Matrix_t *a, Matrix_t *b) except NULL + + int MatClean(Matrix_t *mat, Matrix_t *sub) except -1 + int MatEchelonize(Matrix_t *mat) except -1 + int MatOrder(Matrix_t *mat) except? -1 + long MatNullity(Matrix_t *mat) + Matrix_t *MatInverse(Matrix_t *src) except NULL + Matrix_t *MatNullSpace(Matrix_t *mat) except NULL + + ## Error handling + cdef extern int MTX_ERR_NOMEM, MTX_ERR_GAME_OVER, MTX_ERR_DIV0, MTX_ERR_FILEFMT, MTX_ERR_BADARG + cdef extern int MTX_ERR_RANGE, MTX_ERR_NOTECH, MTX_ERR_NOTSQUARE, MTX_ERR_INCOMPAT + cdef extern int MTX_ERR_BADUSAGE, MTX_ERR_OPTION, MTX_ERR_NARGS, MTX_ERR_NOTMATRIX, MTX_ERR_NOTPERM + ctypedef struct MtxFileInfo_t: + char *Name + char *BaseName + + ctypedef struct MtxErrorRecord_t: + MtxFileInfo_t *FileInfo + int LineNo + char *Text + + ctypedef void MtxErrorHandler_t(MtxErrorRecord_t*) + MtxErrorHandler_t *MtxSetErrorHandler(MtxErrorHandler_t *h) diff --git a/src/sage/libs/ntl/ntl_GF2E.pyx b/src/sage/libs/ntl/ntl_GF2E.pyx index 06cec5eeba3..13877a7e57b 100644 --- a/src/sage/libs/ntl/ntl_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_GF2E.pyx @@ -446,7 +446,7 @@ cdef class ntl_GF2E(object): e = GF2E_degree() if k is None: - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField f = self.c.m._sage_() k = FiniteField(2**e, name='a', modulus=f) diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index 5d8251fb974..419004239a6 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -529,7 +529,7 @@ cdef class ntl_GF2X(object): """ if R is None: from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField R = PolynomialRing(FiniteField(2), 'x') return R(map(int,self.list())) diff --git a/src/sage/libs/ntl/ntl_mat_GF2.pyx b/src/sage/libs/ntl/ntl_mat_GF2.pyx index 0ef43e339b9..a97505e7243 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2.pyx @@ -538,7 +538,7 @@ cdef class ntl_mat_GF2(object): [0 0 0 1 1 1] [0 0 1 1 1 1] """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.matrix.constructor import Matrix m = Matrix(FiniteField(2),self.x.NumRows(),self.x.NumCols()) diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index f165dd18f1b..96cc56d6532 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -521,7 +521,7 @@ cdef class ntl_mat_GF2E(object): [a^2 + 1 a^2 + a] """ if k is None: - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField f = self.c.m._sage_() e = GF2E_degree() k = FiniteField(2**e, name='a', modulus=f) diff --git a/src/sage/libs/pari/declinl.pxi b/src/sage/libs/pari/declinl.pxi index 7d2e017114b..61dd0155da2 100644 --- a/src/sage/libs/pari/declinl.pxi +++ b/src/sage/libs/pari/declinl.pxi @@ -14,390 +14,561 @@ AUTHORS: cdef extern from "sage/libs/pari/parisage.h": - ################################################################### - # # - # CONSTRUCTORS # - # # - ################################################################### - - GEN mkintmod(GEN x, GEN y) - GEN mkintmodu(ulong x, ulong y) - GEN mkpolmod(GEN x, GEN y) - GEN mkfrac(GEN x, GEN y) - GEN mkfraccopy(GEN x, GEN y) - GEN mkrfrac(GEN x, GEN y) - GEN mkcomplex(GEN x, GEN y) - GEN gen_I() - GEN cgetc(long l) - GEN mkquad(GEN n, GEN x, GEN y) - GEN mkvecsmall(long x) - GEN mkvecsmall2(long x,long y) - GEN mkvecsmall3(long x,long y,long z) - GEN mkvecsmall4(long x,long y,long z,long t) - GEN mkvec(GEN x) - GEN mkvec2(GEN x, GEN y) - GEN mkvec3(GEN x, GEN y, GEN z) - GEN mkvec4(GEN x, GEN y, GEN z, GEN t) - GEN mkvec5(GEN x, GEN y, GEN z, GEN t, GEN u) - GEN mkvecs(long x) - GEN mkvec2s(long x, long y) - GEN mkvec3s(long x, long y, long z) - GEN mkveccopy(GEN x) - GEN mkvec2copy(GEN x, GEN y) - GEN mkcol(GEN x) - GEN mkcol2(GEN x, GEN y) - GEN mkcolcopy(GEN x) - GEN mkmat(GEN x) - GEN mkmat2(GEN x, GEN y) - GEN mkmatcopy(GEN x) - GEN pol_x(long v) - GEN pol_1(long v) - GEN const_vec(long n, GEN x) - GEN const_col(long n, GEN x) - GEN const_vecsmall(long n, long c) - - ### Zero ### - GEN zeropadic(GEN p, long e) - GEN zeroser(long v, long e) - GEN zeropol(long v) - GEN zerocol(long n) - GEN zerovec(long n) - GEN zeromat(long m, long n) - GEN zero_Flx(long sv) - GEN zero_Flv(long n) - GEN zero_Flm(long m, long n) - GEN zero_F2v(long m) - GEN zero_F2m(long m, long n) - GEN zero_F2m_copy(long m, long n) - GEN zeromatcopy(long m, long n) - - GEN col_ei(long n, long i) - GEN vec_ei(long n, long i) - GEN vecsmall_ei(long n, long i) - GEN Rg_col_ei(GEN x, long n, long i) - GEN shallowcopy(GEN x) - GEN vecsmall_copy(GEN x) - GEN vectrunc_init(long l) - void vectrunc_append(GEN x, GEN t) - GEN vecsmalltrunc_init(long l) - void vecsmalltrunc_append(GEN x, long t) - - ################################################################### - # # - # VEC / COL / VECSMALL # - # # - ################################################################### - - GEN vec_shorten(GEN v, long n) - GEN vec_lengthen(GEN v, long n) - GEN vec_setconst(GEN v, GEN x) - GEN vecsmall_shorten(GEN v, long n) - GEN vecsmall_lengthen(GEN v, long n) - GEN vec_to_vecsmall(GEN z) - GEN vecsmall_to_vec(GEN z) - GEN vecsmall_to_col(GEN z) - int vecsmall_lexcmp(GEN x, GEN y) - int vecsmall_prefixcmp(GEN x, GEN y) - GEN vecsmall_prepend(GEN V, long s) - GEN vecsmall_append(GEN V, long s) - GEN vecsmall_concat(GEN u, GEN v) - long vecsmall_coincidence(GEN u, GEN v) - long vecsmall_isin(GEN v, long x) - long vecsmall_pack(GEN V, long base, long mod) - long vecsmall_max(GEN x) - long vecsmall_min(GEN x) - int ZV_isscalar(GEN x) - int QV_isscalar(GEN x) - int RgV_isscalar(GEN x) - int RgX_isscalar(GEN x) - int RgX_is_rational(GEN x) - int RgX_is_ZX(GEN x) - int RgX_is_monomial(GEN x) - int RgM_is_ZM(GEN x) - - ################################################################### - # # - # Dynamic arrays implementation # - # # - ################################################################### - - # Omitted - - ################################################################### - # # - # EXTRACT # - # # - ################################################################### - - GEN vecslice(GEN A, long y1, long y2) - GEN vecslicepermute(GEN A, GEN p, long y1, long y2) - GEN rowslicepermute(GEN A, GEN p, long x1, long x2) - GEN rowslice(GEN A, long x1, long x2) - GEN row(GEN A, long x0) - GEN row_Flm(GEN A, long x0) - GEN rowcopy(GEN A, long x0) - GEN row_i(GEN A, long x0, long x1, long x2) - GEN vecreverse(GEN A) - GEN vecpermute(GEN A, GEN p) - GEN rowpermute(GEN A, GEN p) - void vecselect_p(GEN A, GEN B, GEN p, long init, long lB) - void rowselect_p(GEN A, GEN B, GEN p, long init) - - ################################################################### - # # - # PERMUTATIONS # - # # - ################################################################### - - GEN identity_perm(long n) - GEN cyclic_perm(long n, long d) - GEN perm_mul(GEN s, GEN t) - GEN perm_inv(GEN x) - GEN perm_conj(GEN s, GEN t) - - ################################################################### - # # - # MALLOC/FREE WRAPPERS # - # # - ################################################################### - - void pari_free(void *pointer) - void* pari_malloc(size_t size) - void* pari_realloc(void *pointer, size_t size) - void* pari_calloc(size_t size) - GEN cgetalloc(long t, size_t l) - - ################################################################### - # # - # GARBAGE COLLECTION # - # # - ################################################################### - - GEN icopy_avma(GEN x, pari_sp av) - GEN gerepileuptoleaf(pari_sp av, GEN x) - GEN gerepileuptoint(pari_sp av, GEN x) - GEN gerepileupto(pari_sp av, GEN x) - GEN gerepilecopy(pari_sp av, GEN x) - void gerepilemany(pari_sp av, GEN* gptr[], int n) - void gerepileall(pari_sp av, int n, ...) - void gerepilecoeffs(pari_sp av, GEN x, int n) - void gerepilecoeffs2(pari_sp av, GEN x, int n, GEN y, int o) - void cgiv(GEN x) - void killblock(GEN x) - int is_universal_constant(GEN x) - - ################################################################### - # # - # CONVERSION / ASSIGNMENT # - # # - ################################################################### - - GEN cxcompotor(GEN z, long prec) - GEN cxtofp(GEN x, long prec) - double gtodouble(GEN x) - long gtos(GEN x) - GEN absfrac(GEN x) - GEN Q_abs(GEN x) - GEN gtofp(GEN z, long prec) - GEN RgX_gtofp(GEN x, long prec) - GEN RgC_gtofp(GEN x, long prec) - GEN RgM_gtofp(GEN x, long prec) - GEN RgX_fpnorml2(GEN x, long prec) - GEN RgC_fpnorml2(GEN x, long prec) - GEN RgM_fpnorml2(GEN x, long prec) - void affgr(GEN x, GEN y) - GEN affc_fixlg(GEN x, GEN res) - GEN trunc_safe(GEN x) - - ################################################################### - # # - # LENGTH CONVERSIONS # - # # - ################################################################### - - # Omitted - - ################################################################### - # # - # OPERATIONS MODULO m # - # # - ################################################################### - - GEN Fp_red(GEN a, GEN m) - GEN Fp_add(GEN a, GEN b, GEN m) - GEN Fp_sub(GEN a, GEN b, GEN m) - GEN Fp_neg(GEN b, GEN m) - GEN Fp_center(GEN u, GEN p, GEN ps2) - GEN Fp_mul(GEN a, GEN b, GEN m) - GEN Fp_sqr(GEN a, GEN m) - GEN Fp_mulu(GEN a, ulong b, GEN m) - GEN Fp_inv(GEN a, GEN m) - GEN Fp_invsafe(GEN a, GEN m) - GEN Fp_div(GEN a, GEN b, GEN m) - - ################################################################### - # # - # GEN SUBTYPES # - # # - ################################################################### - - int is_const_t(long t) - int is_extscalar_t(long t) - int is_intreal_t(long t) - int is_matvec_t(long t) - int is_noncalc_t(long tx) - int is_rational_t(long t) - int is_recursive_t(long t) - int is_scalar_t(long t) - int is_vec_t(long t) - - ################################################################### - # # - # TRANSCENDENTAL # - # # - ################################################################### - - GEN sqrtr(GEN x) - GEN sqrtnr(GEN x, long n) - - ################################################################### - # # - # MISCELLANEOUS # - # # - ################################################################### - - int isintzero(GEN x) - int isint1(GEN x) - int isintm1(GEN x) - int equali1(GEN n) - int equalim1(GEN n) - int is_pm1(GEN n) - int is_bigint(GEN n) - - # Many functions omitted - - ### POLYNOMIALS - GEN constant_term(GEN x) - GEN leading_term(GEN x) - long degpol(GEN x) - long lgpol(GEN x) - GEN truecoeff(GEN x, long n) - - ################################################################### - # # - # ASSIGNMENTS # - # # - ################################################################### - - # Omitted - - ################################################################### - # # - # ELLIPTIC CURVES # - # # - ################################################################### - - GEN ell_get_a1(GEN e) - GEN ell_get_a2(GEN e) - GEN ell_get_a3(GEN e) - GEN ell_get_a4(GEN e) - GEN ell_get_a6(GEN e) - GEN ell_get_b2(GEN e) - GEN ell_get_b4(GEN e) - GEN ell_get_b6(GEN e) - GEN ell_get_b8(GEN e) - GEN ell_get_c4(GEN e) - GEN ell_get_c6(GEN e) - GEN ell_get_disc(GEN e) - GEN ell_get_j(GEN e) - GEN ell_get_roots(GEN e) - - int ell_is_inf(GEN z) - int ell_is_padic(GEN x) - int ell_is_real(GEN x) - - ################################################################### - # # - # ALGEBRAIC NUMBER THEORY # - # # - ################################################################### - - GEN pr_get_p(GEN pr) - GEN pr_get_gen(GEN pr) - long pr_get_e(GEN pr) - long pr_get_f(GEN pr) - GEN pr_get_tau(GEN pr) - int pr_is_inert(GEN P) - GEN pr_norm(GEN pr) - - long nf_get_varn(GEN nf) - GEN nf_get_pol(GEN nf) - long nf_get_degree(GEN nf) - long nf_get_r1(GEN nf) - long nf_get_r2(GEN nf) - GEN nf_get_disc(GEN nf) - GEN nf_get_index(GEN nf) - GEN nf_get_M(GEN nf) - GEN nf_get_G(GEN nf) - GEN nf_get_roundG(GEN nf) - GEN nf_get_Tr(GEN nf) - GEN nf_get_TrInv(GEN nf) - GEN nf_get_roots(GEN nf) - GEN nf_get_zk(GEN nf) - GEN nf_get_invzk(GEN nf) - void nf_get_sign(GEN nf, long *r1, long *r2) - - GEN bnf_get_nf(GEN bnf) - GEN bnf_get_clgp(GEN bnf) - GEN bnf_get_no(GEN bnf) - GEN bnf_get_cyc(GEN bnf) - GEN bnf_get_gen(GEN bnf) - GEN bnf_get_reg(GEN bnf) - GEN bnf_get_logfu(GEN bnf) - GEN bnf_get_tuU(GEN bnf) - long bnf_get_tuN(GEN bnf) - GEN bnf_get_fu(GEN bnf) - GEN bnf_get_fu_nocheck(GEN bnf) - - GEN bnr_get_bnf(GEN bnr) - GEN bnr_get_bid(GEN bnr) - GEN bnr_get_mod(GEN bnr) - GEN bnr_get_nf(GEN bnr) - GEN bnr_get_no(GEN bnr) - GEN bnr_get_cyc(GEN bnr) - GEN bnr_get_gen_nocheck(GEN bnr) - GEN bnr_get_gen(GEN bnr) - - GEN bid_get_mod(GEN bid) - GEN bid_get_ideal(GEN bid) - GEN bid_get_arch(GEN bid) - GEN bid_get_cyc(GEN bid) - GEN bid_get_gen_nocheck(GEN bid) - GEN bid_get_gen(GEN bid) - - GEN gal_get_pol(GEN gal) - GEN gal_get_p(GEN gal) - GEN gal_get_e(GEN gal) - GEN gal_get_mod(GEN gal) - GEN gal_get_roots(GEN gal) - GEN gal_get_invvdm(GEN gal) - GEN gal_get_den(GEN gal) - GEN gal_get_group(GEN gal) - GEN gal_get_gen(GEN gal) - GEN gal_get_orders(GEN gal) - - long rnf_get_degree(GEN rnf) - - GEN idealpseudomin(GEN I, GEN G) - GEN idealpseudomin_nonscalar(GEN I, GEN G) - GEN idealred_elt(GEN nf, GEN I) - GEN idealred(GEN nf, GEN I) - - ################################################################### - # # - # CLOSURES # - # # - ################################################################### - - long closure_arity(GEN C) - long closure_is_variadic(GEN C) + # pariinl.h + GEN abgrp_get_cyc(GEN x) + GEN abgrp_get_gen(GEN x) + GEN abgrp_get_no(GEN x) + GEN bid_get_arch(GEN bid) + GEN bid_get_cyc(GEN bid) + GEN bid_get_gen(GEN bid) + GEN bid_get_gen_nocheck(GEN bid) + GEN bid_get_grp(GEN bid) + GEN bid_get_ideal(GEN bid) + GEN bid_get_mod(GEN bid) + GEN bid_get_no(GEN bid) + GEN bid_get_U(GEN bid) + GEN bnf_get_clgp(GEN bnf) + GEN bnf_get_cyc(GEN bnf) + GEN bnf_get_fu(GEN bnf) + GEN bnf_get_fu_nocheck(GEN bnf) + GEN bnf_get_gen(GEN bnf) + GEN bnf_get_logfu(GEN bnf) + GEN bnf_get_nf(GEN bnf) + GEN bnf_get_no(GEN bnf) + GEN bnf_get_reg(GEN bnf) + GEN bnf_get_tuU(GEN bnf) + long bnf_get_tuN(GEN bnf) + GEN bnr_get_bnf(GEN bnr) + GEN bnr_get_clgp(GEN bnr) + GEN bnr_get_cyc(GEN bnr) + GEN bnr_get_gen(GEN bnr) + GEN bnr_get_gen_nocheck(GEN bnr) + GEN bnr_get_no(GEN bnr) + GEN bnr_get_bid(GEN bnr) + GEN bnr_get_mod(GEN bnr) + GEN bnr_get_nf(GEN bnr) + GEN ell_get_a1(GEN e) + GEN ell_get_a2(GEN e) + GEN ell_get_a3(GEN e) + GEN ell_get_a4(GEN e) + GEN ell_get_a6(GEN e) + GEN ell_get_b2(GEN e) + GEN ell_get_b4(GEN e) + GEN ell_get_b6(GEN e) + GEN ell_get_b8(GEN e) + GEN ell_get_c4(GEN e) + GEN ell_get_c6(GEN e) + GEN ell_get_disc(GEN e) + GEN ell_get_j(GEN e) + long ell_get_type(GEN e) + int ell_is_inf(GEN z) + GEN ellinf() + GEN ellff_get_field(GEN x) + GEN ellff_get_a4a6(GEN x) + GEN ellnf_get_nf(GEN x) + GEN ellQp_get_p(GEN E) + long ellQp_get_prec(GEN E) + GEN ellQp_get_zero(GEN x) + long ellR_get_prec(GEN x) + long ellR_get_sign(GEN x) + + GEN gal_get_pol(GEN gal) + GEN gal_get_p(GEN gal) + GEN gal_get_e(GEN gal) + GEN gal_get_mod(GEN gal) + GEN gal_get_roots(GEN gal) + GEN gal_get_invvdm(GEN gal) + GEN gal_get_den(GEN gal) + GEN gal_get_group(GEN gal) + GEN gal_get_gen(GEN gal) + GEN gal_get_orders(GEN gal) + GEN idealchineseinit(GEN nf, GEN x) + GEN idealpseudomin(GEN I, GEN G) + GEN idealpseudomin_nonscalar(GEN I, GEN G) + GEN idealpseudored(GEN I, GEN G) + GEN idealred_elt(GEN nf, GEN I) + GEN idealred(GEN nf, GEN I) + GEN modpr_get_pr(GEN x) + GEN modpr_get_p(GEN x) + GEN modpr_get_T(GEN x) + GEN nf_get_M(GEN nf) + GEN nf_get_G(GEN nf) + GEN nf_get_Tr(GEN nf) + GEN nf_get_diff(GEN nf) + long nf_get_degree(GEN nf) + GEN nf_get_disc(GEN nf) + GEN nf_get_index(GEN nf) + GEN nf_get_invzk(GEN nf) + GEN nf_get_pol(GEN nf) + long nf_get_r1(GEN nf) + long nf_get_r2(GEN nf) + GEN nf_get_ramified_primes(GEN nf) + GEN nf_get_roots(GEN nf) + GEN nf_get_roundG(GEN nf) + void nf_get_sign(GEN nf, long *r1, long *r2) + long nf_get_varn(GEN nf) + GEN nf_get_zk(GEN nf) + long pr_get_e(GEN pr) + long pr_get_f(GEN pr) + GEN pr_get_gen(GEN pr) + GEN pr_get_p(GEN pr) + GEN pr_get_tau(GEN pr) + int pr_is_inert(GEN P) + GEN pr_norm(GEN pr) + GEN rnf_get_alpha(GEN rnf) + long rnf_get_absdegree(GEN rnf) + long rnf_get_degree(GEN rnf) + GEN rnf_get_idealdisc(GEN rnf) + GEN rnf_get_invzk(GEN rnf) + GEN rnf_get_k(GEN rnf) + GEN rnf_get_map(GEN rnf) + GEN rnf_get_nf(GEN rnf) + long rnf_get_nfdegree(GEN rnf) + GEN rnf_get_nfpol(GEN rnf) + long rnf_get_nfvarn(GEN rnf) + GEN rnf_get_pol(GEN rnf) + GEN rnf_get_polabs(GEN rnf) + GEN rnf_get_zk(GEN nf) + void rnf_get_nfzk(GEN rnf, GEN *b, GEN *cb) + long rnf_get_varn(GEN rnf) + + long closure_arity(GEN C) + char * closure_codestr(GEN C) + GEN closure_get_code(GEN C) + GEN closure_get_oper(GEN C) + GEN closure_get_data(GEN C) + GEN closure_get_dbg(GEN C) + GEN closure_get_text(GEN C) + GEN closure_get_frame(GEN C) + long closure_is_variadic(GEN C) + + GEN addmuliu(GEN x, GEN y, ulong u) + GEN addmuliu_inplace(GEN x, GEN y, ulong u) + GEN lincombii(GEN u, GEN v, GEN x, GEN y) + GEN mulsubii(GEN y, GEN z, GEN x) + GEN submulii(GEN x, GEN y, GEN z) + GEN submuliu(GEN x, GEN y, ulong u) + GEN submuliu_inplace(GEN x, GEN y, ulong u) + + GEN FpXQ_add(GEN x, GEN y, GEN T, GEN p) + GEN FpXQ_sub(GEN x, GEN y, GEN T, GEN p) + GEN Flxq_add(GEN x, GEN y, GEN T, ulong p) + GEN Flxq_sub(GEN x, GEN y, GEN T, ulong p) + + GEN FpXQX_div(GEN x, GEN y, GEN T, GEN p) + GEN FlxqX_div(GEN x, GEN y, GEN T, ulong p) + + GEN Fq_red(GEN x, GEN T, GEN p) + GEN Fq_to_FpXQ(GEN x, GEN T, GEN p) + GEN gener_Fq_local(GEN T, GEN p, GEN L) + GEN FqX_Fp_mul(GEN P, GEN U, GEN T, GEN p) + GEN FqX_Fq_mul(GEN P, GEN U, GEN T, GEN p) + GEN FqX_add(GEN x, GEN y, GEN T, GEN p) + GEN FqX_div(GEN x, GEN y, GEN T, GEN p) + GEN FqX_div_by_X_x(GEN x, GEN y, GEN T, GEN p, GEN *z) + GEN FqX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *z) + GEN FqX_extgcd(GEN P, GEN Q, GEN T, GEN p, GEN *U, GEN *V) + GEN FqX_gcd(GEN P, GEN Q, GEN T, GEN p) + GEN FqX_get_red(GEN S, GEN T, GEN p) + GEN FqX_mul(GEN x, GEN y, GEN T, GEN p) + GEN FqX_mulu(GEN x, ulong y, GEN T, GEN p) + GEN FqX_neg(GEN x, GEN T, GEN p) + GEN FqX_powu(GEN x, ulong n, GEN T, GEN p) + GEN FqX_red(GEN z, GEN T, GEN p) + GEN FqX_rem(GEN x, GEN y, GEN T, GEN p) + GEN FqX_sqr(GEN x, GEN T, GEN p) + GEN FqX_sub(GEN x, GEN y, GEN T, GEN p) + + GEN FqXQ_add(GEN x, GEN y, GEN S, GEN T, GEN p) + GEN FqXQ_div(GEN x, GEN y, GEN S, GEN T, GEN p) + GEN FqXQ_inv(GEN x, GEN S, GEN T, GEN p) + GEN FqXQ_invsafe(GEN x, GEN S, GEN T, GEN p) + GEN FqXQ_mul(GEN x, GEN y, GEN S, GEN T, GEN p) + GEN FqXQ_pow(GEN x, GEN n, GEN S, GEN T, GEN p) + GEN FqXQ_sqr(GEN x, GEN S, GEN T, GEN p) + GEN FqXQ_sub(GEN x, GEN y, GEN S, GEN T, GEN p) + + ulong F2m_coeff(GEN x, long a, long b) + void F2m_clear(GEN x, long a, long b) + void F2m_flip(GEN x, long a, long b) + void F2m_set(GEN x, long a, long b) + void F2v_clear(GEN x, long v) + ulong F2v_coeff(GEN x, long v) + void F2v_flip(GEN x, long v) + GEN F2v_to_F2x(GEN x, long sv) + void F2v_set(GEN x, long v) + void F2x_clear(GEN x, long v) + ulong F2x_coeff(GEN x, long v) + void F2x_flip(GEN x, long v) + void F2x_set(GEN x, long v) + int F2x_equal1(GEN x) + int F2x_equal(GEN V, GEN W) + GEN F2x_div(GEN x, GEN y) + GEN F2x_renormalize(GEN x, long lx) + GEN F2m_copy(GEN x) + GEN F2v_copy(GEN x) + GEN F2v_ei(long n, long i) + GEN Flm_copy(GEN x) + GEN Flv_copy(GEN x) + int Flx_equal1(GEN x) + GEN Flx_copy(GEN x) + GEN Flx_div(GEN x, GEN y, ulong p) + ulong Flx_lead(GEN x) + GEN Flx_mulu(GEN x, ulong a, ulong p) + GEN FpV_FpC_mul(GEN x, GEN y, GEN p) + GEN FpXQX_renormalize(GEN x, long lx) + GEN FpXX_renormalize(GEN x, long lx) + GEN FpX_div(GEN x, GEN y, GEN p) + GEN FpX_renormalize(GEN x, long lx) + GEN Fp_add(GEN a, GEN b, GEN m) + GEN Fp_addmul(GEN x, GEN y, GEN z, GEN p) + GEN Fp_center(GEN u, GEN p, GEN ps2) + GEN Fp_div(GEN a, GEN b, GEN m) + GEN Fp_halve(GEN y, GEN p) + GEN Fp_inv(GEN a, GEN m) + GEN Fp_invsafe(GEN a, GEN m) + GEN Fp_mul(GEN a, GEN b, GEN m) + GEN Fp_muls(GEN a, long b, GEN m) + GEN Fp_mulu(GEN a, ulong b, GEN m) + GEN Fp_neg(GEN b, GEN m) + GEN Fp_red(GEN x, GEN p) + GEN Fp_sqr(GEN a, GEN m) + GEN Fp_sub(GEN a, GEN b, GEN m) + GEN GENbinbase(GENbin *p) + GEN Q_abs(GEN x) + GEN Q_abs_shallow(GEN x) + int QV_isscalar(GEN x) + GEN RgC_fpnorml2(GEN x, long prec) + GEN RgC_gtofp(GEN x, long prec) + GEN RgC_gtomp(GEN x, long prec) + void RgM_dimensions(GEN x, long *m, long *n) + GEN RgM_fpnorml2(GEN x, long prec) + GEN RgM_gtofp(GEN x, long prec) + GEN RgM_gtomp(GEN x, long prec) + GEN RgM_inv(GEN a) + GEN RgM_minor(GEN a, long i, long j) + GEN RgM_shallowcopy(GEN x) + int RgV_isscalar(GEN x) + int RgV_is_ZV(GEN x) + int RgV_is_QV(GEN x) + long RgX_equal_var(GEN x, GEN y) + int RgX_is_monomial(GEN x) + int RgX_is_rational(GEN x) + int RgX_is_QX(GEN x) + int RgX_is_ZX(GEN x) + int RgX_isscalar(GEN x) + GEN RgX_shift_inplace(GEN x, long v) + void RgX_shift_inplace_init(long v) + GEN RgXQ_mul(GEN x, GEN y, GEN T) + GEN RgXQ_sqr(GEN x, GEN T) + GEN RgXQX_div(GEN x, GEN y, GEN T) + GEN RgXQX_rem(GEN x, GEN y, GEN T) + GEN RgX_coeff(GEN x, long n) + GEN RgX_copy(GEN x) + GEN RgX_div(GEN x, GEN y) + GEN RgX_fpnorml2(GEN x, long prec) + GEN RgX_gtofp(GEN x, long prec) + GEN RgX_rem(GEN x, GEN y) + GEN RgX_renormalize(GEN x) + GEN Rg_col_ei(GEN x, long n, long i) + GEN ZC_hnfrem(GEN x, GEN y) + GEN ZM_hnfrem(GEN x, GEN y) + GEN ZM_lll(GEN x, double D, long f) + int ZV_dvd(GEN x, GEN y) + int ZV_isscalar(GEN x) + GEN ZV_to_zv(GEN x) + GEN ZX_ZXY_resultant(GEN a, GEN b) + int ZX_equal1(GEN x) + GEN ZX_renormalize(GEN x, long lx) + GEN ZXQ_mul(GEN x, GEN y, GEN T) + GEN ZXQ_sqr(GEN x, GEN T) + long Z_ispower(GEN x, ulong k) + long Z_issquare(GEN x) + GEN absfrac(GEN x) + GEN absfrac_shallow(GEN x) + GEN affc_fixlg(GEN x, GEN res) + GEN bin_copy(GENbin *p) + long bit_accuracy(long x) + double bit_accuracy_mul(long x, double y) + long bit_prec(GEN x) + int both_odd(long x, long y) + GEN cbrtr(GEN x) + GEN cgetc(long x) + GEN cgetalloc(long t, size_t l) + GEN cxcompotor(GEN z, long prec) + void cgiv(GEN x) + GEN col_ei(long n, long i) + GEN const_col(long n, GEN x) + GEN const_vec(long n, GEN x) + GEN const_vecsmall(long n, long c) + GEN constant_term(GEN x) + GEN cxnorm(GEN x) + GEN cyclic_perm(long l, long d) + double dbllog2r(GEN x) + long degpol(GEN x) + long divsBIL(long n) + void gabsz(GEN x, long prec, GEN z) + GEN gaddgs(GEN y, long s) + void gaddz(GEN x, GEN y, GEN z) + int gcmpgs(GEN y, long s) + void gdiventz(GEN x, GEN y, GEN z) + GEN gdivsg(long s, GEN y) + void gdivz(GEN x, GEN y, GEN z) + GEN gen_I() + void gerepileall(pari_sp av, int n, ...) + void gerepilecoeffs(pari_sp av, GEN x, int n) + GEN gerepilecopy(pari_sp av, GEN x) + void gerepilemany(pari_sp av, GEN* g[], int n) + int gequalgs(GEN y, long s) + GEN gerepileupto(pari_sp av, GEN q) + GEN gerepileuptoint(pari_sp av, GEN q) + GEN gerepileuptoleaf(pari_sp av, GEN q) + GEN gmaxsg(long s, GEN y) + GEN gminsg(long s, GEN y) + void gmodz(GEN x, GEN y, GEN z) + void gmul2nz(GEN x, long s, GEN z) + GEN gmulgs(GEN y, long s) + void gmulz(GEN x, GEN y, GEN z) + void gnegz(GEN x, GEN z) + void gshiftz(GEN x, long s, GEN z) + GEN gsubgs(GEN y, long s) + void gsubz(GEN x, GEN y, GEN z) + double gtodouble(GEN x) + GEN gtofp(GEN z, long prec) + GEN gtomp(GEN z, long prec) + long gtos(GEN x) + long gval(GEN x, long v) + GEN identity_perm(long l) + int equali1(GEN n) + int equalim1(GEN n) + long inf_get_sign(GEN x) + int is_bigint(GEN n) + int is_const_t(long t) + int is_extscalar_t(long t) + int is_intreal_t(long t) + int is_matvec_t(long t) + int is_noncalc_t(long tx) + int is_pm1(GEN n) + int is_rational_t(long t) + int is_recursive_t(long t) + int is_scalar_t(long t) + int is_universal_constant(GEN x) + int is_vec_t(long t) + int isint1(GEN x) + int isintm1(GEN x) + int isintzero(GEN x) + int ismpzero(GEN x) + int isonstack(GEN x) + void killblock(GEN x) + GEN leading_term(GEN x) + long lgcols(GEN x) + long lgpol(GEN x) + GEN matpascal(long n) + GEN matslice(GEN A, long x1, long x2, long y1, long y2) + GEN mkcol(GEN x) + GEN mkcol2(GEN x, GEN y) + GEN mkcol2s(long x, long y) + GEN mkcol3(GEN x, GEN y, GEN z) + GEN mkcol3s(long x, long y, long z) + GEN mkcol4(GEN x, GEN y, GEN z, GEN t) + GEN mkcol4s(long x, long y, long z, long t) + GEN mkcol5(GEN x, GEN y, GEN z, GEN t, GEN u) + GEN mkcol6(GEN x, GEN y, GEN z, GEN t, GEN u, GEN v) + GEN mkcolcopy(GEN x) + GEN mkcols(long x) + GEN mkcomplex(GEN x, GEN y) + GEN mkerr(long n) + GEN mkmoo() + GEN mkoo() + GEN mkfrac(GEN x, GEN y) + GEN mkfraccopy(GEN x, GEN y) + GEN mkintmod(GEN x, GEN y) + GEN mkintmodu(ulong x, ulong y) + GEN mkmat(GEN x) + GEN mkmat2(GEN x, GEN y) + GEN mkmat3(GEN x, GEN y, GEN z) + GEN mkmat4(GEN x, GEN y, GEN z, GEN t) + GEN mkmat5(GEN x, GEN y, GEN z, GEN t, GEN u) + GEN mkmatcopy(GEN x) + GEN mkpolmod(GEN x, GEN y) + GEN mkqfi(GEN x, GEN y, GEN z) + GEN mkquad(GEN n, GEN x, GEN y) + GEN mkrfrac(GEN x, GEN y) + GEN mkrfraccopy(GEN x, GEN y) + GEN mkvec(GEN x) + GEN mkvec2(GEN x, GEN y) + GEN mkvec2copy(GEN x, GEN y) + GEN mkvec2s(long x, long y) + GEN mkvec3(GEN x, GEN y, GEN z) + GEN mkvec3s(long x, long y, long z) + GEN mkvec4(GEN x, GEN y, GEN z, GEN t) + GEN mkvec4s(long x, long y, long z, long t) + GEN mkvec5(GEN x, GEN y, GEN z, GEN t, GEN u) + GEN mkveccopy(GEN x) + GEN mkvecs(long x) + GEN mkvecsmall(long x) + GEN mkvecsmall2(long x, long y) + GEN mkvecsmall3(long x, long y, long z) + GEN mkvecsmall4(long x, long y, long z, long t) + void mpcosz(GEN x, GEN z) + void mpexpz(GEN x, GEN z) + void mplogz(GEN x, GEN z) + void mpsinz(GEN x, GEN z) + GEN mul_content(GEN cx, GEN cy) + GEN mul_denom(GEN cx, GEN cy) + long nbits2nlong(long x) + long nbits2extraprec(long x) + long nbits2ndec(long x) + long nbits2prec(long x) + long nbits2lg(long x) + long nbrows(GEN x) + long nchar2nlong(long x) + long ndec2nbits(long x) + long ndec2nlong(long x) + long ndec2prec(long x) + void normalize_frac(GEN z) + int odd(long x) + void pari_free(void *pointer) + void* pari_calloc(size_t size) + void* pari_malloc(size_t bytes) + void* pari_realloc(void *pointer, size_t size) + GEN perm_conj(GEN s, GEN t) + GEN perm_inv(GEN x) + GEN perm_mul(GEN s, GEN t) + GEN pol_0(long v) + GEN pol_1(long v) + GEN pol_x(long v) + GEN pol0_F2x(long sv) + GEN pol1_F2x(long sv) + GEN polx_F2x(long sv) + GEN pol0_Flx(long sv) + GEN pol1_Flx(long sv) + GEN polx_Flx(long sv) + GEN polx_zx(long sv) + GEN powii(GEN x, GEN n) + GEN powIs(long n) + long prec2nbits(long x) + double prec2nbits_mul(long x, double y) + long prec2ndec(long x) + long precdbl(long x) + GEN quad_disc(GEN x) + GEN qfb_disc(GEN x) + GEN qfb_disc3(GEN x, GEN y, GEN z) + GEN quadnorm(GEN q) + long remsBIL(long n) + GEN resultant(GEN x, GEN y) + GEN row(GEN A, long x1) + GEN Flm_row(GEN A, long x0) + GEN row_i(GEN A, long x0, long x1, long x2) + GEN zm_row(GEN x, long i) + GEN rowcopy(GEN A, long x0) + GEN rowpermute(GEN A, GEN p) + GEN rowslice(GEN A, long x1, long x2) + GEN rowslicepermute(GEN A, GEN p, long x1, long x2) + int ser_isexactzero(GEN x) + GEN shallowcopy(GEN x) + GEN sqrfrac(GEN x) + GEN sqrti(GEN x) + GEN sqrtnr(GEN x, long n) + GEN sqrtr(GEN x) + void pari_stack_alloc(pari_stack *s, long nb) + void** pari_stack_base(pari_stack *s) + void pari_stack_delete(pari_stack *s) + void pari_stack_init(pari_stack *s, size_t size, void **data) + long pari_stack_new(pari_stack *s) + void pari_stack_pushp(pari_stack *s, void *u) + long sturm(GEN x) + GEN truecoeff(GEN x, long n) + GEN trunc_safe(GEN x) + GEN vec_ei(long n, long i) + GEN vec_append(GEN v, GEN s) + GEN vec_lengthen(GEN v, long n) + GEN vec_setconst(GEN v, GEN x) + GEN vec_shorten(GEN v, long n) + GEN vec_to_vecsmall(GEN z) + GEN vecpermute(GEN A, GEN p) + GEN vecreverse(GEN A) + void vecreverse_inplace(GEN y) + GEN vecsmallpermute(GEN A, GEN p) + GEN vecslice(GEN A, long y1, long y2) + GEN vecslicepermute(GEN A, GEN p, long y1, long y2) + GEN vecsplice(GEN a, long j) + GEN vecsmall_append(GEN V, long s) + long vecsmall_coincidence(GEN u, GEN v) + GEN vecsmall_concat(GEN u, GEN v) + GEN vecsmall_copy(GEN x) + GEN vecsmall_ei(long n, long i) + long vecsmall_indexmax(GEN x) + long vecsmall_indexmin(GEN x) + long vecsmall_isin(GEN v, long x) + GEN vecsmall_lengthen(GEN v, long n) + int vecsmall_lexcmp(GEN x, GEN y) + long vecsmall_max(GEN v) + long vecsmall_min(GEN v) + long vecsmall_pack(GEN V, long base, long mod) + int vecsmall_prefixcmp(GEN x, GEN y) + GEN vecsmall_prepend(GEN V, long s) + GEN vecsmall_shorten(GEN v, long n) + GEN vecsmall_to_col(GEN z) + GEN vecsmall_to_vec(GEN z) + void vecsmalltrunc_append(GEN x, long t) + GEN vecsmalltrunc_init(long l) + void vectrunc_append(GEN x, GEN t) + void vectrunc_append_batch(GEN x, GEN y) + GEN vectrunc_init(long l) + GEN zc_to_ZC(GEN x) + GEN zero_F2m(long n, long m) + GEN zero_F2m_copy(long n, long m) + GEN zero_F2v(long m) + GEN zero_F2x(long sv) + GEN zero_Flm(long m, long n) + GEN zero_Flm_copy(long m, long n) + GEN zero_Flv(long n) + GEN zero_Flx(long sv) + GEN zero_zm(long x, long y) + GEN zero_zv(long x) + GEN zero_zx(long sv) + GEN zerocol(long n) + GEN zeromat(long m, long n) + GEN zeromatcopy(long m, long n) + GEN zeropadic(GEN p, long e) + GEN zeropadic_shallow(GEN p, long e) + GEN zeropol(long v) + GEN zeroser(long v, long e) + GEN zerovec(long n) + GEN zm_copy(GEN x) + GEN zm_to_zxV(GEN x, long sv) + GEN zm_transpose(GEN x) + GEN zv_copy(GEN x) + GEN zv_to_ZV(GEN x) + GEN zv_to_zx(GEN x, long sv) + GEN zx_renormalize(GEN x, long l) + GEN zx_shift(GEN x, long n) + GEN zx_to_zv(GEN x, long N) + + GEN err_get_compo(GEN e, long i) + long err_get_num(GEN e) + void pari_err_BUG(char *f) + void pari_err_COMPONENT(char *f, char *op, GEN l, GEN x) + void pari_err_CONSTPOL(char *f) + void pari_err_COPRIME(char *f, GEN x, GEN y) + void pari_err_DIM(char *f) + void pari_err_DOMAIN(char *f, char *v, char *op, GEN l, GEN x) + void pari_err_FILE(char *f, char *g) + void pari_err_FLAG(char *f) + void pari_err_IMPL(char *f) + void pari_err_INV(char *f, GEN x) + void pari_err_IRREDPOL(char *f, GEN x) + void pari_err_MAXPRIME(ulong c) + void pari_err_MODULUS(char *f, GEN x, GEN y) + void pari_err_OP(char *f, GEN x, GEN y) + void pari_err_OVERFLOW(char *f) + void pari_err_PACKAGE(char *f) + void pari_err_PREC(char *f) + void pari_err_PRIME(char *f, GEN x) + void pari_err_PRIORITY(char *f, GEN x, char *op, long v) + void pari_err_SQRTN(char *f, GEN x) + void pari_err_TYPE(char *f, GEN x) + void pari_err_TYPE2(char *f, GEN x, GEN y) + void pari_err_VAR(char *f, GEN x, GEN y) + void pari_err_ROOTS0(char *f) diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 75a3286802a..66f2a5b19ff 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -73,10 +73,12 @@ from sage.libs.gmp.mpz cimport * from sage.libs.gmp.pylong cimport mpz_set_pylong from sage.libs.pari.closure cimport objtoclosure -from pari_instance cimport PariInstance, prec_bits_to_words, pari_instance +from pari_instance cimport (PariInstance, pari_instance, + prec_bits_to_words, prec_words_to_bits, default_bitprec) cdef PariInstance P = pari_instance from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational include 'auto_gen.pxi' @@ -1408,12 +1410,22 @@ cdef class gen(gen_auto): def python(self, locals=None): """ - Return Python eval of self. + Return the closest Python/Sage equivalent of the given PARI object. - Note: is self is a real (type t_REAL) the result will be a - RealField element of the equivalent precision; if self is a complex - (type t_COMPLEX) the result will be a ComplexField element of - precision the minimum precision of the real and imaginary parts. + INPUT: + + - `z` -- PARI ``gen`` + + - `locals` -- optional dictionary used in fallback cases that + involve :func:`sage_eval` + + .. NOTE:: + + If ``self`` is a real (type ``t_REAL``), then the result + will be a RealField element of the equivalent precision; + if ``self`` is a complex (type ``t_COMPLEX``), then the + result will be a ComplexField element of precision the + maximal precision of the real and imaginary parts. EXAMPLES:: @@ -1426,17 +1438,115 @@ cdef class gen(gen_auto): sage: f.python({'x':x, 'y':y}) 2/3*x^3 + x + y - 5/7 - You can also use .sage, which is a psynonym:: + You can also use :meth:`.sage`, which is an alias:: sage: f.sage({'x':x, 'y':y}) 2/3*x^3 + x + y - 5/7 - """ - import sage.libs.pari.gen_py - return sage.libs.pari.gen_py.python(self, locals=locals) - sage = python + Converting a real number:: + + sage: pari.set_real_precision(70) + 15 + sage: a = pari('1.234').python(); a + 1.234000000000000000000000000000000000000000000000000000000000000000000000000 + sage: a.parent() + Real Field with 256 bits of precision + sage: pari.set_real_precision(15) + 70 + sage: a = pari('1.234').python(); a + 1.23400000000000000 + sage: a.parent() + Real Field with 64 bits of precision + + For complex numbers, the parent depends on the PARI type:: + + sage: a = pari('(3+I)').python(); a + i + 3 + sage: a.parent() + Number Field in i with defining polynomial x^2 + 1 + + sage: a = pari('2^31-1').python(); a + 2147483647 + sage: a.parent() + Integer Ring + + sage: a = pari('12/34').python(); a + 6/17 + sage: a.parent() + Rational Field + + sage: a = pari('(3+I)/2').python(); a + 1/2*i + 3/2 + sage: a.parent() + Number Field in i with defining polynomial x^2 + 1 + + sage: z = pari(CC(1.0+2.0*I)); z + 1.00000000000000 + 2.00000000000000*I + sage: a = z.python(); a + 1.00000000000000000 + 2.00000000000000000*I + sage: a.parent() + Complex Field with 64 bits of precision + + sage: I = sqrt(-1) + sage: a = pari(1.0 + 2.0*I).python(); a + 1.00000000000000000 + 2.00000000000000000*I + sage: a.parent() + Complex Field with 64 bits of precision + + Vectors and matrices:: + + sage: a = pari('[1,2,3,4]') + sage: a + [1, 2, 3, 4] + sage: a.type() + 't_VEC' + sage: b = a.python(); b + [1, 2, 3, 4] + sage: type(b) + + + sage: a = pari('[1,2;3,4]') + sage: a.type() + 't_MAT' + sage: b = a.python(); b + [1 2] + [3 4] + sage: b.parent() + Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + + sage: a = pari('Vecsmall([1,2,3,4])') + sage: a.type() + 't_VECSMALL' + sage: a.python() + [1, 2, 3, 4] + + We use the locals dictionary:: + + sage: f = pari('(2/3)*x^3 + x - 5/7 + y') + sage: x,y=var('x,y') + sage: from sage.libs.pari.gen import gentoobj + sage: gentoobj(f, {'x':x, 'y':y}) + 2/3*x^3 + x + y - 5/7 + sage: gentoobj(f) + Traceback (most recent call last): + ... + NameError: name 'x' is not defined + + Conversion of p-adics:: + + sage: K = Qp(11,5) + sage: x = K(11^-10 + 5*11^-7 + 11^-6); x + 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) + sage: y = pari(x); y + 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) + sage: y.sage() + 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) + sage: pari(K(11^-5)).sage() + 11^-5 + O(11^0) + """ + return gentoobj(self, locals) - _sage_ = _eval_ = python + sage = _sage_ = _eval_ = python def __long__(gen self): """ @@ -6215,9 +6325,9 @@ cdef class gen(gen_auto): sage: e.elllseries(2.1) 0.402838047956645 sage: e.elllseries(1, precision=128) - 2.98766720445395 E-38 + 6.21952537507477 E-39 sage: e.elllseries(1, precision=256) - 5.48956813891054 E-77 + 2.95993347819786 E-77 sage: e.elllseries(-2) 0 sage: e.elllseries(2.1, A=1.1) @@ -6933,11 +7043,11 @@ cdef class gen(gen_auto): sage: G = pari(x^4 + 1).galoisinit() sage: G.galoisfixedfield(G[5][1], flag=2) - [x^2 + 4, Mod(2*x^2, x^4 + 1), [x^2 - 1/2*y, x^2 + 1/2*y]] + [x^2 - 2, Mod(-x^3 + x, x^4 + 1), [x^2 - y*x + 1, x^2 + y*x + 1]] sage: G.galoisfixedfield(G[5][5:7]) [x^4 + 1, Mod(x, x^4 + 1)] sage: L = G.galoissubgroups() - sage: G.galoisfixedfield(L[2], flag=2, v='z') + sage: G.galoisfixedfield(L[3], flag=2, v='z') [x^2 + 2, Mod(x^3 + x, x^4 + 1), [x^2 - z*x - 1, x^2 + z*x - 1]] .. _galoisfixedfield: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoisfixedfield @@ -6975,7 +7085,7 @@ cdef class gen(gen_auto): sage: G.galoissubfields(flag=1) [x, x^2 + 972, x^3 + 54, x^3 + 864, x^3 - 54, x^6 + 108] sage: G = pari(x^4 + 1).galoisinit() - sage: G.galoissubfields(flag=2, v='z')[2] + sage: G.galoissubfields(flag=2, v='z')[3] [x^2 + 2, Mod(x^3 + x, x^4 + 1), [x^2 - z*x - 1, x^2 + z*x - 1]] .. _galoissubfields: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoissubfields @@ -8197,7 +8307,7 @@ cdef class gen(gen_auto): Now it takes much less than a second:: sage: pari.allocatemem(200000) - PARI stack size set to 200000 bytes + PARI stack size set to 200000 bytes, maximum size set to ... sage: x = polygen(ZpFM(3,10)) sage: pol = ((x-1)^50 + x) sage: pari(pol).poldisc() @@ -9057,7 +9167,7 @@ cdef class gen(gen_auto): sage: pari(0).znstar() [2, [2], [-1]] sage: pari(96).znstar() - [32, [8, 2, 2], [Mod(37, 96), Mod(79, 96), Mod(65, 96)]] + [32, [8, 2, 2], [Mod(37, 96), Mod(31, 96), Mod(65, 96)]] sage: pari(-5).znstar() [4, [4], [Mod(2, 5)]] """ @@ -9573,6 +9683,100 @@ cdef class gen(gen_auto): cpdef gen objtogen(s): """ Convert any Sage/Python object to a PARI gen. + + For Sage types, this uses the `_pari_()` method on the object. + Basic Python types like ``int`` are converted directly. For other + types, the string representation is used. + + EXAMPLES:: + + sage: pari([2,3,5]) + [2, 3, 5] + sage: pari(Matrix(2,2,range(4))) + [0, 1; 2, 3] + sage: pari(x^2-3) + x^2 - 3 + + :: + + sage: a = pari(1); a, a.type() + (1, 't_INT') + sage: a = pari(1/2); a, a.type() + (1/2, 't_FRAC') + sage: a = pari(1/2); a, a.type() + (1/2, 't_FRAC') + + Conversion from reals uses the real's own precision:: + + sage: a = pari(1.2); a, a.type(), a.precision() + (1.20000000000000, 't_REAL', 4) # 32-bit + (1.20000000000000, 't_REAL', 3) # 64-bit + + Conversion from strings uses the current PARI real precision. + By default, this is 64 bits:: + + sage: a = pari('1.2'); a, a.type(), a.precision() + (1.20000000000000, 't_REAL', 4) # 32-bit + (1.20000000000000, 't_REAL', 3) # 64-bit + + But we can change this precision:: + + sage: pari.set_real_precision(35) # precision in decimal digits + 15 + sage: a = pari('1.2'); a, a.type(), a.precision() + (1.2000000000000000000000000000000000, 't_REAL', 6) # 32-bit + (1.2000000000000000000000000000000000, 't_REAL', 4) # 64-bit + + Set the precision to 15 digits for the remaining tests:: + + sage: pari.set_real_precision(15) + 35 + + Conversion from matrices and vectors is supported:: + + sage: a = pari(matrix(2,3,[1,2,3,4,5,6])); a, a.type() + ([1, 2, 3; 4, 5, 6], 't_MAT') + sage: v = vector([1.2, 3.4, 5.6]) + sage: pari(v) + [1.20000000000000, 3.40000000000000, 5.60000000000000] + + Some more exotic examples:: + + sage: K. = NumberField(x^3 - 2) + sage: pari(K) + [y^3 - 2, [1, 1], -108, 1, [[1, 1.25992104989487, 1.58740105196820; 1, -0.629960524947437 + 1.09112363597172*I, -0.793700525984100 - 1.37472963699860*I], [1, 1.25992104989487, 1.58740105196820; 1, 0.461163111024285, -2.16843016298270; 1, -1.72108416091916, 0.581029111014503], [1, 1, 2; 1, 0, -2; 1, -2, 1], [3, 0, 0; 0, 0, 6; 0, 6, 0], [6, 0, 0; 0, 6, 0; 0, 0, 3], [2, 0, 0; 0, 0, 1; 0, 1, 0], [2, [0, 0, 2; 1, 0, 0; 0, 1, 0]], []], [1.25992104989487, -0.629960524947437 + 1.09112363597172*I], [1, y, y^2], [1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0, 0, 0, 2, 0, 2, 0; 0, 1, 0, 1, 0, 0, 0, 0, 2; 0, 0, 1, 0, 1, 0, 1, 0, 0]] + + sage: E = EllipticCurve('37a1') + sage: pari(E) + [0, 0, 1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vecsmall([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]] + + Conversion from basic Python types:: + + sage: pari(int(-5)) + -5 + sage: pari(long(2**150)) + 1427247692705959881058285969449495136382746624 + sage: pari(float(pi)) + 3.14159265358979 + sage: pari(complex(exp(pi*I/4))) + 0.707106781186548 + 0.707106781186548*I + sage: pari(False) + 0 + sage: pari(True) + 1 + + Some commands are just executed without returning a value:: + + sage: pari("dummy = 0; kill(dummy)") + sage: type(pari("dummy = 0; kill(dummy)")) + + + TESTS:: + + sage: pari(None) + Traceback (most recent call last): + ... + ValueError: Cannot convert None to pari """ cdef GEN g cdef Py_ssize_t length, i @@ -9635,6 +9839,73 @@ cpdef gen objtogen(s): return objtogen(str(s)) +cpdef gentoobj(gen z, locals={}): + """ + Convert a PARI gen to a Sage/Python object. + + See the ``python`` method of :class:`gen` for documentation and + examples. + """ + cdef GEN g = z.g + cdef long t = typ(g) + cdef long tx, ty + cdef gen real, imag + cdef Py_ssize_t i, j, nr, nc + + if t == t_INT: + return Integer(z) + elif t == t_FRAC: + return Rational(z) + elif t == t_REAL: + from sage.rings.all import RealField + prec = prec_words_to_bits(z.precision()) + return RealField(prec)(z) + elif t == t_COMPLEX: + real = z.real() + imag = z.imag() + tx = typ(real.g) + ty = typ(imag.g) + if tx in [t_INTMOD, t_PADIC] or ty in [t_INTMOD, t_PADIC]: + raise NotImplementedError("No conversion to python available for t_COMPLEX with t_INTMOD or t_PADIC components") + if tx == t_REAL or ty == t_REAL: + xprec = real.precision() # will be 0 if exact + yprec = imag.precision() # will be 0 if exact + if xprec == 0: + prec = prec_words_to_bits(yprec) + elif yprec == 0: + prec = prec_words_to_bits(xprec) + else: + prec = max(prec_words_to_bits(xprec), prec_words_to_bits(yprec)) + + from sage.rings.all import RealField, ComplexField + R = RealField(prec) + C = ComplexField(prec) + return C(R(real), R(imag)) + else: + from sage.rings.all import QuadraticField + K = QuadraticField(-1, 'i') + return K([gentoobj(real), gentoobj(imag)]) + elif t == t_VEC or t == t_COL: + return [gentoobj(x, locals) for x in z.python_list()] + elif t == t_VECSMALL: + return z.python_list_small() + elif t == t_MAT: + nc = lg(g)-1 + nr = 0 if nc == 0 else lg(gel(g,1))-1 + L = [gentoobj(z[i,j], locals) for i in range(nr) for j in range(nc)] + from sage.matrix.constructor import matrix + return matrix(nr, nc, L) + elif t == t_PADIC: + from sage.rings.padics.factory import Qp + p = z.padicprime() + K = Qp(Integer(p), precp(g)) + return K(z.lift()) + + # Fallback (e.g. polynomials): use string representation + from sage.misc.sage_eval import sage_eval + return sage_eval(str(z), locals=locals) + + cdef GEN _Vec_append(GEN v, GEN a, long n): """ This implements appending zeros (or another constant GEN ``a``) to diff --git a/src/sage/libs/pari/gen_py.py b/src/sage/libs/pari/gen_py.py index ef81e47b7fe..c507a80a0b7 100644 --- a/src/sage/libs/pari/gen_py.py +++ b/src/sage/libs/pari/gen_py.py @@ -1,104 +1,8 @@ -"Pari objects" - -from sage.rings.all import * - def pari(x): """ Return the PARI object constructed from a Sage/Python object. - For Sage types, this uses the `_pari_()` method on the object if - possible and otherwise it uses the string representation. - - EXAMPLES:: - - sage: pari([2,3,5]) - [2, 3, 5] - sage: pari(Matrix(2,2,range(4))) - [0, 1; 2, 3] - sage: pari(x^2-3) - x^2 - 3 - - :: - - sage: a = pari(1); a, a.type() - (1, 't_INT') - sage: a = pari(1/2); a, a.type() - (1/2, 't_FRAC') - sage: a = pari(1/2); a, a.type() - (1/2, 't_FRAC') - - Conversion from reals uses the real's own precision:: - - sage: a = pari(1.2); a, a.type(), a.precision() - (1.20000000000000, 't_REAL', 4) # 32-bit - (1.20000000000000, 't_REAL', 3) # 64-bit - - Conversion from strings uses the current PARI real precision. - By default, this is 96 bits on 32-bit systems and 128 bits on - 64-bit systems:: - - sage: a = pari('1.2'); a, a.type(), a.precision() - (1.20000000000000, 't_REAL', 5) # 32-bit - (1.20000000000000, 't_REAL', 4) # 64-bit - - But we can change this precision:: - - sage: pari.set_real_precision(35) # precision in decimal digits - 15 - sage: a = pari('1.2'); a, a.type(), a.precision() - (1.2000000000000000000000000000000000, 't_REAL', 6) # 32-bit - (1.2000000000000000000000000000000000, 't_REAL', 4) # 64-bit - - Set the precision to 15 digits for the remaining tests:: - - sage: pari.set_real_precision(15) - 35 - - Conversion from matrices and vectors is supported:: - - sage: a = pari(matrix(2,3,[1,2,3,4,5,6])); a, a.type() - ([1, 2, 3; 4, 5, 6], 't_MAT') - sage: v = vector([1.2, 3.4, 5.6]) - sage: pari(v) - [1.20000000000000, 3.40000000000000, 5.60000000000000] - - Some more exotic examples:: - - sage: K. = NumberField(x^3 - 2) - sage: pari(K) - [y^3 - 2, [1, 1], -108, 1, [[1, 1.25992104989487, 1.58740105196820; 1, -0.629960524947437 + 1.09112363597172*I, -0.793700525984100 - 1.37472963699860*I], [1, 1.25992104989487, 1.58740105196820; 1, 0.461163111024285, -2.16843016298270; 1, -1.72108416091916, 0.581029111014503], [1, 1, 2; 1, 0, -2; 1, -2, 1], [3, 0, 0; 0, 0, 6; 0, 6, 0], [6, 0, 0; 0, 6, 0; 0, 0, 3], [2, 0, 0; 0, 0, 1; 0, 1, 0], [2, [0, 0, 2; 1, 0, 0; 0, 1, 0]], []], [1.25992104989487, -0.629960524947437 + 1.09112363597172*I], [1, y, y^2], [1, 0, 0; 0, 1, 0; 0, 0, 1], [1, 0, 0, 0, 0, 2, 0, 2, 0; 0, 1, 0, 1, 0, 0, 0, 0, 2; 0, 0, 1, 0, 1, 0, 1, 0, 0]] - - sage: E = EllipticCurve('37a1') - sage: pari(E) - [0, 0, 1, -1, 0, 0, -2, 1, -1, 48, -216, 37, 110592/37, Vecsmall([1]), [Vecsmall([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]] - - Conversion from basic Python types:: - - sage: pari(int(-5)) - -5 - sage: pari(long(2**150)) - 1427247692705959881058285969449495136382746624 - sage: pari(float(pi)) - 3.14159265358979 - sage: pari(complex(exp(pi*I/4))) - 0.707106781186548 + 0.707106781186548*I - sage: pari(False) - 0 - sage: pari(True) - 1 - - Some commands are just executed without returning a value:: - - sage: pari("dummy = 0; kill(dummy)") - sage: type(pari("dummy = 0; kill(dummy)")) - - - TESTS:: - - sage: pari(None) - Traceback (most recent call last): - ... - ValueError: Cannot convert None to pari + This is deprecated, import ``pari`` from ``sage.libs.pari.all``. """ from sage.misc.superseded import deprecation deprecation(17451, 'gen_py.pari is deprecated, use sage.libs.pari.all.pari instead') @@ -110,170 +14,18 @@ def python(z, locals=None): """ Return the closest Python/Sage equivalent of the given PARI object. - INPUT: - - - `z` -- PARI ``gen`` - - - `locals` -- optional dictionary used in fallback cases that - involve sage_eval - - The component parts of a t_COMPLEX may be t_INT, t_REAL, t_INTMOD, - t_FRAC, t_PADIC. The components need not have the same type - (e.g. if z=2+1.2*I then z.real() is t_INT while z.imag() is - t_REAL(). They are converted as follows: - - t_INT: ZZ[i] - t_FRAC: QQ(i) - t_REAL: ComplexField(prec) for equivalent precision - t_INTMOD, t_PADIC: raise NotImplementedError - - EXAMPLES:: - - sage: a = pari('(3+I)').python(); a - i + 3 - sage: a.parent() - Maximal Order in Number Field in i with defining polynomial x^2 + 1 - - sage: a = pari('2^31-1').python(); a - 2147483647 - sage: a.parent() - Integer Ring - - sage: a = pari('12/34').python(); a - 6/17 - sage: a.parent() - Rational Field - - sage: a = pari('1.234').python(); a - 1.23400000000000000 - sage: a.parent() - Real Field with 64 bits of precision - - sage: a = pari('(3+I)/2').python(); a - 1/2*i + 3/2 - sage: a.parent() - Number Field in i with defining polynomial x^2 + 1 - - Conversion of complex numbers: the next example is converting from - an element of the Symbolic Ring, which goes via the string - representation:: + This is deprecated, use the ``python`` method of :class:`gen` + instead. - sage: I = SR(I) - sage: a = pari(1.0+2.0*I).python(); a - 1.00000000000000000 + 2.00000000000000000*I - sage: type(a) - - sage: a.parent() - Complex Field with 64 bits of precision - - For architecture-independent complex numbers, start from a - suitable ComplexField:: - - sage: z = pari(CC(1.0+2.0*I)); z - 1.00000000000000 + 2.00000000000000*I - sage: a=z.python(); a - 1.00000000000000000 + 2.00000000000000000*I - sage: a.parent() - Complex Field with 64 bits of precision - - Vectors and matrices:: - - sage: a = pari('[1,2,3,4]') - sage: a - [1, 2, 3, 4] - sage: a.type() - 't_VEC' - sage: b = a.python(); b - [1, 2, 3, 4] - sage: type(b) - - - sage: a = pari('[1,2;3,4]') - sage: a.type() - 't_MAT' - sage: b = a.python(); b - [1 2] - [3 4] - sage: b.parent() - Full MatrixSpace of 2 by 2 dense matrices over Integer Ring - - sage: a = pari('Vecsmall([1,2,3,4])') - sage: a.type() - 't_VECSMALL' - sage: a.python() - [1, 2, 3, 4] - - We use the locals dictionary:: - - sage: f = pari('(2/3)*x^3 + x - 5/7 + y') - sage: x,y=var('x,y') - sage: import sage.libs.pari.gen_py - sage: sage.libs.pari.gen_py.python(f, {'x':x, 'y':y}) - 2/3*x^3 + x + y - 5/7 - sage: sage.libs.pari.gen_py.python(f) - Traceback (most recent call last): - ... - NameError: name 'x' is not defined - - Conversion of p-adics:: + TESTS:: - sage: K = Qp(11,5) - sage: x = K(11^-10 + 5*11^-7 + 11^-6); x - 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) - sage: y = pari(x); y - 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) - sage: y.sage() - 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) - sage: pari(K(11^-5)).sage() - 11^-5 + O(11^0) + sage: from sage.libs.pari.gen_py import python + sage: python(pari(3)) + doctest:...: DeprecationWarning: gen_py.python is deprecated, use sage.libs.pari.gen.gentoobj or the .python() method instead + See http://trac.sagemath.org/19888 for details. + 3 """ - from sage.libs.pari.pari_instance import prec_words_to_bits - - t = z.type() - if t == "t_REAL": - return RealField(prec_words_to_bits(z.precision()))(z) - elif t == "t_FRAC": - Q = RationalField() - return Q(z) - elif t == "t_INT": - Z = IntegerRing() - return Z(z) - elif t == "t_COMPLEX": - tx = z.real().type() - ty = z.imag().type() - if tx in ["t_INTMOD", "t_PADIC"] or ty in ["t_INTMOD", "t_PADIC"]: - raise NotImplementedError("No conversion to python available for t_COMPLEX with t_INTMOD or t_PADIC components") - if tx == "t_REAL" or ty == "t_REAL": - xprec = z.real().precision() # will be 0 if exact - yprec = z.imag().precision() # will be 0 if exact - if xprec == 0: - prec = prec_words_to_bits(yprec) - elif yprec == 0: - prec = prec_words_to_bits(xprec) - else: - prec = max(prec_words_to_bits(xprec), prec_words_to_bits(yprec)) - R = RealField(prec) - C = ComplexField(prec) - return C(R(z.real()), R(z.imag())) - if tx == "t_FRAC" or ty == "t_FRAC": - return QuadraticField(-1,'i')([python(c) for c in list(z)]) - if tx == "t_INT" or ty == "t_INT": - return QuadraticField(-1,'i').ring_of_integers()([python(c) for c in list(z)]) - raise NotImplementedError("No conversion to python available for t_COMPLEX with components %s"%(tx,ty)) - elif t == "t_VEC": - return [python(x) for x in z.python_list()] - elif t == "t_VECSMALL": - return z.python_list_small() - elif t == "t_MAT": - from sage.matrix.constructor import matrix - return matrix(z.nrows(),z.ncols(),[python(z[i,j]) for i in range(z.nrows()) for j in range(z.ncols())]) - elif t == "t_PADIC": - from sage.rings.padics.factory import Qp - Z = IntegerRing() - p = z.padicprime() - rprec = Z(z.padicprec(p)) - Z(z._valp()) - K = Qp(Z(p), rprec) - return K(z.lift()) - else: - from sage.misc.sage_eval import sage_eval - return sage_eval(str(z), locals=locals) + from sage.misc.superseded import deprecation + deprecation(19888, 'gen_py.python is deprecated, use sage.libs.pari.gen.gentoobj or the .python() method instead') + from sage.libs.pari.gen import gentoobj + return gentoobj(z, locals) diff --git a/src/sage/libs/pari/handle_error.pyx b/src/sage/libs/pari/handle_error.pyx index 9dfaeffb619..9183ff0defc 100644 --- a/src/sage/libs/pari/handle_error.pyx +++ b/src/sage/libs/pari/handle_error.pyx @@ -159,18 +159,20 @@ cdef int _pari_err_handle(GEN E) except 0: """ cdef long errnum = E[1] - if errnum == e_STACK: - # PARI is out of memory. We double the size of the PARI stack - # and retry the computation. - pari_instance.allocatemem(silent=True) - return 0 sig_block() cdef char* errstr cdef char* s try: - errstr = pari_err2str(E) - pari_error_string = errstr.decode('ascii') + if errnum == e_STACK: + # Custom error message for PARI stack overflow + pari_error_string = "the PARI stack overflows (current size: {}; maximum size: {})\n" + pari_error_string += "You can use pari.allocatemem() to change the stack size and try again" + pari_error_string = pari_error_string.format(pari_mainstack.size, pari_mainstack.vsize) + else: + errstr = pari_err2str(E) + pari_error_string = errstr.decode('ascii') + pari_free(errstr) s = closure_func_err() if s is not NULL: @@ -178,7 +180,6 @@ cdef int _pari_err_handle(GEN E) except 0: raise PariError(errnum, pari_error_string, pari_instance.new_gen_noclear(E)) finally: - pari_free(errstr) sig_unblock() @@ -193,19 +194,13 @@ cdef void _pari_err_recover(long errnum): Perform a computation that requires doubling the default stack several times:: - sage: pari.allocatemem(2^12) - PARI stack size set to 4096 bytes + sage: pari.allocatemem(2^12, 2^26) + PARI stack size set to 4096 bytes, maximum size set to 67108864 sage: x = pari('2^(2^26)') sage: x == 2^(2^26) True """ - if not PyErr_Occurred(): - # No exception was raised => retry the computation starting - # from sig_on(). This can happen if we successfully enlarged the - # PARI stack in _pari_handle_exception(). - sig_retry() - else: - # An exception was raised. Jump to the signal-handling code - # which will cause sig_on() to see the exception. - sig_error() + # An exception was raised. Jump to the signal-handling code + # which will cause sig_on() to see the exception. + sig_error() diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd index 0550ded0ee9..35f5d73eef5 100644 --- a/src/sage/libs/pari/pari_instance.pxd +++ b/src/sage/libs/pari/pari_instance.pxd @@ -7,6 +7,8 @@ cimport cython from sage.libs.pari.gen cimport gen cpdef long prec_bits_to_words(unsigned long prec_in_bits) +cpdef long prec_words_to_bits(long prec_in_words) +cpdef long default_bitprec() cdef class PariInstance_auto(ParentWithBase): pass diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index b27e83ca8c2..20a8d932473 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -299,7 +299,7 @@ cpdef long prec_bits_to_words(unsigned long prec_in_bits): # This equals ceil(prec_in_bits/wordsize) + 2 return (prec_in_bits - 1)//wordsize + 3 -def prec_words_to_bits(long prec_in_words): +cpdef long prec_words_to_bits(long prec_in_words): r""" Convert from pari real precision expressed in words to precision expressed in bits. Note: this adjusts for the two codewords of a @@ -318,6 +318,18 @@ def prec_words_to_bits(long prec_in_words): # see user's guide to the pari library, page 10 return (prec_in_words - 2) * BITS_IN_LONG +cpdef long default_bitprec(): + r""" + Return the default precision in bits. + + EXAMPLES:: + + sage: from sage.libs.pari.pari_instance import default_bitprec + sage: default_bitprec() + 64 + """ + return (prec - 2) * BITS_IN_LONG + def prec_dec_to_words(long prec_in_dec): r""" Convert from precision expressed in decimal to precision expressed @@ -437,10 +449,25 @@ cdef class PariInstance(PariInstance_auto): if avma: return # pari already initialized. - # The size here doesn't really matter, because we will allocate - # our own stack anyway. We ask PARI not to set up signal and - # error handlers. - pari_init_opts(10000, maxprime, INIT_DFTm) + # PARI has a "real" stack size (parisize) and a "virtual" stack + # size (parisizemax). The idea is that the real stack will be + # used if possible, but the stack might be increased up to + # the complete virtual stack. Therefore, it is not a problem to + # set the virtual stack size to a large value. There are two + # constraints for the virtual stack size: + # 1) on 32-bit systems, even virtual memory can be a scarce + # resource since it is limited by 4GB (of which the kernel + # needs a significant part) + # 2) the system should actually be able to handle a stack size + # as large as the complete virtual stack. + # As a simple heuristic, we set the virtual stack to 1/4 of the + # virtual memory. + + from sage.misc.memory_info import MemoryInfo + mem = MemoryInfo() + + pari_init_opts(size, maxprime, INIT_DFTm) + paristack_setsize(size, mem.virtual_memory_limit() // 4) # Disable PARI's stack overflow checking which is incompatible # with multi-threading. @@ -452,11 +479,6 @@ cdef class PariInstance(PariInstance_auto): # so we need to reset them. init_memory_functions() - # Free the PARI stack and allocate our own (using Cython) - pari_mainstack_free(pari_mainstack) - pari_mainstack.vbot = 0 - init_stack(size) - # Set printing functions global pariOut, pariErr @@ -517,13 +539,8 @@ cdef class PariInstance(PariInstance_auto): indirect doctest. See the discussion at :trac:`13741`. """ - sage_free(pari_mainstack.vbot) - pari_mainstack.rsize = 0 - pari_mainstack.vsize = 0 - pari_mainstack.bot = 0 - pari_mainstack.vbot = 0 - pari_mainstack.top = 0 - pari_close() + if avma: + pari_close() def __repr__(self): return "Interface to the PARI C library" @@ -1108,71 +1125,56 @@ cdef class PariInstance(PariInstance_auto): def stacksize(self): r""" - Returns the current size of the PARI stack, which is `10^6` + Return the current size of the PARI stack, which is `10^6` by default. However, the stack size is automatically doubled - when needed. It can also be set directly using - ``pari.allocatemem()``. + when needed up to some maximum. + + .. SEEALSO:: + + - :meth:`stacksizemax` to get the maximum stack size + - :meth:`allocatemem` to change the current or maximum + stack size EXAMPLES:: sage: pari.stacksize() 1000000 - + sage: pari.allocatemem(2^18, silent=True) + sage: pari.stacksize() + 262144 """ - return pari_mainstack.rsize + return pari_mainstack.size - def _allocate_huge_mem(self): + def stacksizemax(self): r""" - This function tries to allocate an impossibly large amount of - PARI stack in order to test ``init_stack()`` failing. + Return the maximum size of the PARI stack, which is determined + at startup in terms of available memory. Usually, the PARI + stack size is (much) smaller than this maximum but the stack + will be increased up to this maximum if needed. - TESTS:: + .. SEEALSO:: - sage: pari.allocatemem(10^6, silent=True) - sage: pari._allocate_huge_mem() - Traceback (most recent call last): - ... - MemoryError: Unable to allocate ... (instead, allocated 1000000 bytes) - - Test that the PARI stack is sane after this failure:: + - :meth:`stacksize` to get the current stack size + - :meth:`allocatemem` to change the current or maximum + stack size - sage: a = pari('2^10000000') - sage: pari.allocatemem(10^6, silent=True) - """ - # Since size_t is unsigned, this should wrap over to a very - # large positive number. - init_stack((-4096)) - - def _setup_failed_retry(self): - r""" - This function pretends that the PARI stack is larger than it - actually is such that allocatemem(0) will allocate much more - than double the current stack. That allocation will then fail. - This function is meant to be used only in this doctest. - - TESTS:: + EXAMPLES:: - sage: pari.allocatemem(4096, silent=True) - sage: pari._setup_failed_retry() - sage: a = pari('2^1000000') - Traceback (most recent call last): - ... - MemoryError: Unable to enlarge PARI stack (instead, kept the stack at ... bytes) - sage: pari.allocatemem(10^6, silent=True) + sage: pari.allocatemem(2^18, 2^26, silent=True) + sage: pari.stacksizemax() + 67108864 """ - # Pretend that the stack is much larger than it actually is, - # JUST FOR TESTING! - pari_mainstack.rsize = (-16) + return pari_mainstack.vsize - def allocatemem(self, long s=0, silent=False): + def allocatemem(self, size_t s=0, size_t sizemax=0, *, silent=False): r""" - Change the PARI stack space to the given size (or double the - current size if ``s`` is `0`). + Change the PARI stack space to the given size ``s`` (or double + the current size if ``s`` is `0`) and change the maximum stack + size to ``sizemax``. - If `s = 0` and insufficient memory is avaible to double, the - PARI stack will be enlarged by a smaller amount. In any case, - a ``MemoryError`` will be raised if the requested memory cannot - be allocated. + PARI tries to use only its current stack (the size which is set + by ``s``), but it will increase its stack if needed up to the + maximum size which is set by ``sizemax``. The PARI stack is never automatically shrunk. You can use the command ``pari.allocatemem(10^6)`` to reset the size to `10^6`, @@ -1183,26 +1185,28 @@ cdef class PariInstance(PariInstance_auto): It does no real harm to set this to a small value as the PARI stack will be automatically doubled when we run out of memory. - However, it could make some calculations slower (since they have - to be redone from the start after doubling the stack until the - stack is big enough). INPUT: - - ``s`` - an integer (default: 0). A non-zero argument should - be the size in bytes of the new PARI stack. If `s` is zero, - try to double the current stack size. + - ``s`` - an integer (default: 0). A non-zero argument is the + size in bytes of the new PARI stack. If `s` is zero, double + the current stack size. + + - ``sizemax`` - an integer (default: 0). A non-zero argument + is the maximum size in bytes of the PARI stack. If + ``sizemax`` is 0, the maximum of the current maximum and + ``s`` is taken. EXAMPLES:: sage: pari.allocatemem(10^7) - PARI stack size set to 10000000 bytes + PARI stack size set to 10000000 bytes, maximum size set to 67108864 sage: pari.allocatemem() # Double the current size - PARI stack size set to 20000000 bytes + PARI stack size set to 20000000 bytes, maximum size set to 67108864 sage: pari.stacksize() 20000000 sage: pari.allocatemem(10^6) - PARI stack size set to 1000000 bytes + PARI stack size set to 1000000 bytes, maximum size set to 67108864 The following computation will automatically increase the PARI stack size:: @@ -1215,27 +1219,56 @@ cdef class PariInstance(PariInstance_auto): sage: pari.stacksize() 16000000 - sage: pari.allocatemem(10^6) - PARI stack size set to 1000000 bytes - sage: pari.stacksize() - 1000000 + + Setting a small maximum size makes this fail:: + + sage: pari.allocatemem(10^6, 2^22) + PARI stack size set to 1000000 bytes, maximum size set to 4194304 + sage: a = pari('2^100000000') + Traceback (most recent call last): + ... + PariError: _^s: the PARI stack overflows (current size: 1000000; maximum size: 4194304) + You can use pari.allocatemem() to change the stack size and try again TESTS: Do the same without using the string interface and starting from a very small stack size:: - sage: pari.allocatemem(1) - PARI stack size set to 1024 bytes + sage: pari.allocatemem(1, 2^26) + PARI stack size set to 1024 bytes, maximum size set to 67108864 sage: a = pari(2)^100000000 sage: pari.stacksize() 16777216 - """ - if s < 0: - raise ValueError("Stack size must be nonnegative") - init_stack(s) + + We do not allow ``sizemax`` less than ``s``:: + + sage: pari.allocatemem(10^7, 10^6) + Traceback (most recent call last): + ... + ValueError: the maximum size (10000000) should be at least the stack size (1000000) + """ + if s == 0: + s = pari_mainstack.size * 2 + if s < pari_mainstack.size: + raise OverflowError("cannot double stack size") + elif s < 1024: + s = 1024 # arbitrary minimum size + if sizemax == 0: + # For the default sizemax, use the maximum of current + # sizemax and the given size s. + if pari_mainstack.vsize > s: + sizemax = pari_mainstack.vsize + else: + sizemax = s + elif sizemax < s: + raise ValueError("the maximum size ({}) should be at least the stack size ({})".format(s, sizemax)) + pari_catch_sig_on() + paristack_setsize(s, sizemax) + pari_catch_sig_off() if not silent: - print "PARI stack size set to", self.stacksize(), "bytes" + print("PARI stack size set to {} bytes, maximum size set to {}". + format(self.stacksize(), self.stacksizemax())) def pari_version(self): return str(PARIVERSION) @@ -1520,7 +1553,7 @@ cdef class PariInstance(PariInstance_auto): sage: pari.polsubcyclo(8, 4) [x^4 + 1] sage: pari.polsubcyclo(8, 2, 'z') - [z^2 - 2, z^2 + 1, z^2 + 2] + [z^2 + 2, z^2 - 2, z^2 + 1] sage: pari.polsubcyclo(8, 1) [x - 1] sage: pari.polsubcyclo(8, 3) @@ -1673,97 +1706,6 @@ cdef class PariInstance(PariInstance_auto): return self.new_gen(genus2red(t0.g, NULL)) -cdef int init_stack(size_t requested_size) except -1: - r""" - Low-level Cython function to allocate the PARI stack. This - function should not be called directly, use ``pari.allocatemem()`` - instead. - """ - global avma - - cdef size_t old_size = pari_mainstack.rsize - - cdef size_t new_size - cdef size_t max_size = (-1) - if (requested_size == 0): - if old_size < max_size/2: - # Double the stack - new_size = 2*old_size - elif old_size < 4*(max_size/5): - # We cannot possibly double since we already use over half - # the addressable memory: take the average of current and - # maximum size - new_size = max_size/2 + old_size/2 - else: - # We already use 80% of the addressable memory => give up - raise MemoryError("Unable to enlarge PARI stack (instead, kept the stack at %s bytes)"%(old_size)) - else: - new_size = requested_size - - # Align size to 16 bytes and take 1024 bytes as a minimum - new_size = (new_size/16)*16 - if (new_size < 1024): - new_size = 1024 - - # Disable interrupts - sig_on() - sig_block() - - # If this is non-zero, the size we failed to allocate - cdef size_t failed_size = 0 - - cdef pari_sp bot # New stack - try: - # Free the current stack - libc.stdlib.free(pari_mainstack.vbot) - pari_mainstack.rsize = 0 - pari_mainstack.vsize = 0 - pari_mainstack.bot = 0 - pari_mainstack.vbot = 0 - pari_mainstack.top = 0 - - # Allocate memory for new stack. - bot = libc.stdlib.malloc(new_size) - - # If doubling failed, instead add 25% to the current stack size. - # We already checked that we use less than 80% of the maximum value - # for s, so this will not overflow. - if (bot == 0) and (requested_size == 0): - new_size = (old_size/64)*80 - bot = libc.stdlib.malloc(new_size) - - if not bot: - failed_size = new_size - # We lost our PARI stack and are not able to allocate the - # requested size. If we just raise an exception now, we end up - # *without* a PARI stack which is not good. We will raise an - # exception later, after allocating *some* PARI stack. - new_size = old_size - while new_size >= 1024: # hope this never fails! - bot = libc.stdlib.malloc(new_size) - if bot: break - new_size = (new_size/32)*16 - - if not bot: - avma = 0 - raise SystemError("Unable to allocate PARI stack, all subsequent PARI computations will crash") - - pari_mainstack.rsize = new_size - pari_mainstack.vsize = 0 - pari_mainstack.bot = bot - pari_mainstack.vbot = bot - pari_mainstack.top = bot + new_size - avma = pari_mainstack.top - - if failed_size: - raise MemoryError("Unable to allocate %s bytes for the PARI stack (instead, allocated %s bytes)"%(failed_size, new_size)) - - return 0 - finally: - sig_unblock() - sig_off() - - cdef inline void INT_to_mpz(mpz_ptr value, GEN g): """ Store a PARI ``t_INT`` as an ``mpz_t``. diff --git a/src/sage/libs/pari/paridecl.pxd b/src/sage/libs/pari/paridecl.pxd index d60565b02f5..f6461d8be76 100644 --- a/src/sage/libs/pari/paridecl.pxd +++ b/src/sage/libs/pari/paridecl.pxd @@ -137,6 +137,7 @@ cdef extern from "sage/libs/pari/parisage.h": long F2x_degree(GEN x) GEN F2x_deriv(GEN x) GEN F2x_divrem(GEN x, GEN y, GEN *pr) + ulong F2x_eval(GEN P, ulong x) void F2x_even_odd(GEN p, GEN *pe, GEN *po) GEN F2x_extgcd(GEN a, GEN b, GEN *ptu, GEN *ptv) GEN F2x_gcd(GEN a, GEN b) @@ -183,7 +184,6 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ZM_to_F2m(GEN x) GEN ZV_to_F2v(GEN x) GEN ZX_to_F2x(GEN x) - GEN ZXT_to_FlxT(GEN z, ulong p) GEN ZXX_to_F2xX(GEN B, long v) GEN gener_F2xq(GEN T, GEN *po) bb_field *get_F2xq_field(void **E, GEN T) @@ -194,6 +194,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN F2xq_ellcard(GEN a2, GEN a6, GEN T) GEN F2xq_ellgens(GEN a2, GEN a6, GEN ch, GEN D, GEN m, GEN T) GEN F2xq_ellgroup(GEN a2, GEN a6, GEN N, GEN T, GEN *pt_m) + void F2xq_elltwist(GEN a, GEN a6, GEN T, GEN *pt_a, GEN *pt_a6) GEN F2xqE_add(GEN P, GEN Q, GEN a2, GEN T) GEN F2xqE_changepoint(GEN x, GEN ch, GEN T) GEN F2xqE_changepointinv(GEN x, GEN ch, GEN T) @@ -215,7 +216,8 @@ cdef extern from "sage/libs/pari/parisage.h": ulong Fl_elldisc_pre(ulong a4, ulong a6, ulong p, ulong pi) ulong Fl_ellj(ulong a4, ulong a6, ulong p) void Fl_ellj_to_a4a6(ulong j, ulong p, ulong *pt_a4, ulong *pt_a6) - void Fl_elltwist(ulong a4, ulong a6, ulong D, ulong p, ulong *pt_a4, ulong *pt_a6) + void Fl_elltwist(ulong a4, ulong a6, ulong p, ulong *pt_a4, ulong *pt_a6) + void Fl_elltwist_disc(ulong a4, ulong a6, ulong D, ulong p, ulong *pt_a4, ulong *pt_a6) GEN Fle_add(GEN P, GEN Q, ulong a4, ulong p) GEN Fle_dbl(GEN P, ulong a4, ulong p) GEN Fle_changepoint(GEN x, GEN ch, ulong p) @@ -237,7 +239,6 @@ cdef extern from "sage/libs/pari/parisage.h": # Flx.c - bb_group * get_Flxq_star(void **E, GEN T, ulong p) GEN Fl_to_Flx(ulong x, long sv) int Fl2_equal1(GEN x) GEN Fl2_inv_pre(GEN x, ulong D, ulong p, ulong pi) @@ -256,6 +257,8 @@ cdef extern from "sage/libs/pari/parisage.h": void Flv_inv_pre_inplace(GEN x, ulong p, ulong pi) GEN Flv_inv_pre(GEN x, ulong p, ulong pi) GEN Flv_polint(GEN xa, GEN ya, ulong p, long vs) + ulong Flv_prod(GEN v, ulong p) + ulong Flv_prod_pre(GEN x, ulong p, ulong pi) GEN Flv_roots_to_pol(GEN a, ulong p, long vs) GEN Flv_to_Flx(GEN x, long vs) GEN Fly_to_FlxY(GEN B, long v) @@ -336,15 +339,17 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FlxX_to_ZXX(GEN B) GEN FlxX_triple(GEN x, ulong p) GEN FlxXV_to_FlxM(GEN v, long n, long sv) + GEN FlxY_Flx_div(GEN x, GEN y, ulong p) + GEN FlxY_Flx_translate(GEN P, GEN c, ulong p) GEN FlxY_Flxq_evalx(GEN P, GEN x, GEN T, ulong p) GEN FlxY_FlxqV_evalx(GEN P, GEN x, GEN T, ulong p) - GEN FlxY_Flx_div(GEN x, GEN y, ulong p) ulong FlxY_eval_powers_pre(GEN pol, GEN ypowers, GEN xpowers, ulong p, ulong pi) GEN FlxY_evalx(GEN Q, ulong x, ulong p) GEN FlxY_evalx_powers_pre(GEN pol, GEN ypowers, ulong p, ulong pi) GEN FlxYqq_pow(GEN x, GEN n, GEN S, GEN T, ulong p) GEN Flxq_autpow(GEN x, ulong n, GEN T, ulong p) GEN Flxq_autsum(GEN x, ulong n, GEN T, ulong p) + GEN Flxq_auttrace(GEN x, ulong n, GEN T, ulong p) GEN Flxq_charpoly(GEN x, GEN T, ulong p) GEN Flxq_conjvec(GEN x, GEN T, ulong p) GEN Flxq_div(GEN x, GEN y, GEN T, ulong p) @@ -376,12 +381,13 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FlxqX_divrem(GEN x, GEN y, GEN T, ulong p, GEN *pr) GEN FlxqX_extgcd(GEN a, GEN b, GEN T, ulong p, GEN *ptu, GEN *ptv) GEN FlxqX_gcd(GEN P, GEN Q, GEN T, ulong p) + GEN FlxqX_get_red(GEN S, GEN T, ulong p) GEN FlxqX_invBarrett(GEN T, GEN Q, ulong p) GEN FlxqX_mul(GEN x, GEN y, GEN T, ulong p) GEN FlxqX_normalize(GEN z, GEN T, ulong p) GEN FlxqX_pow(GEN V, long n, GEN T, ulong p) GEN FlxqX_red(GEN z, GEN T, ulong p) - GEN FlxqX_rem_Barrett(GEN x, GEN mg, GEN T, GEN Q, ulong p) + GEN FlxqX_rem(GEN x, GEN y, GEN T, ulong p) GEN FlxqX_safegcd(GEN P, GEN Q, GEN T, ulong p) GEN FlxqX_sqr(GEN x, GEN T, ulong p) GEN FlxqXQ_div(GEN x, GEN y, GEN S, GEN T, ulong p) @@ -402,16 +408,24 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Z_to_Flx(GEN x, ulong p, long v) GEN ZX_to_Flx(GEN x, ulong p) GEN ZXV_to_FlxV(GEN v, ulong p) + GEN ZXT_to_FlxT(GEN z, ulong p) GEN ZXX_to_FlxX(GEN B, ulong p, long v) + GEN ZXXT_to_FlxXT(GEN z, ulong p, long v) GEN ZXXV_to_FlxXV(GEN V, ulong p, long v) GEN gener_Flxq(GEN T, ulong p, GEN *o) long get_Flx_degree(GEN T) GEN get_Flx_mod(GEN T) long get_Flx_var(GEN T) + long get_FlxqX_degree(GEN T) + GEN get_FlxqX_mod(GEN T) + long get_FlxqX_var(GEN T) bb_field *get_Flxq_field(void **E, GEN T, ulong p) + bb_group *get_Flxq_star(void **E, GEN T, ulong p) + GEN monomial_Flx(ulong a, long d, long vs) GEN pol1_FlxX(long v, long sv) GEN polx_FlxX(long v, long sv) GEN random_Flx(long d1, long v, ulong p) + GEN random_FlxqX(long d1, long v, GEN T, ulong p) GEN zx_to_Flx(GEN x, ulong p) GEN zxX_to_FlxX(GEN B, ulong p) GEN zxX_to_Kronecker(GEN P, GEN Q) @@ -421,6 +435,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Flxq_ellcard(GEN a4, GEN a6, GEN T, ulong p) GEN Flxq_ellgens(GEN a4, GEN a6, GEN ch, GEN D, GEN m, GEN T, ulong p) GEN Flxq_ellgroup(GEN a4, GEN a6, GEN N, GEN T, ulong p, GEN *pt_m) + void Flxq_elltwist(GEN a, GEN a6, GEN T, ulong p, GEN *pt_a, GEN *pt_a6) GEN Flxq_ellj(GEN a4, GEN a6, GEN T, ulong p) void Flxq_ellj_to_a4a6(GEN j, GEN T, ulong p, GEN *pt_a4, GEN *pt_a6) GEN FlxqE_add(GEN P, GEN Q, GEN a4, GEN T, ulong p) @@ -448,6 +463,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Fp_ellgroup(GEN a4, GEN a6, GEN N, GEN p, GEN *pt_m) GEN Fp_ellj(GEN a4, GEN a6, GEN p) int Fp_elljissupersingular(GEN j, GEN p) + void Fp_elltwist(GEN a4, GEN a6, GEN p, GEN *pt_a4, GEN *pt_a6) GEN Fp_ffellcard(GEN a4, GEN a6, GEN q, long n, GEN p) GEN FpE_add(GEN P, GEN Q, GEN a4, GEN p) GEN FpE_changepoint(GEN x, GEN ch, GEN p) @@ -467,6 +483,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpXQ_ellgroup(GEN a4, GEN a6, GEN N, GEN T, GEN p, GEN *pt_m) GEN FpXQ_ellj(GEN a4, GEN a6, GEN T, GEN p) int FpXQ_elljissupersingular(GEN j, GEN T, GEN p) + void FpXQ_elltwist(GEN a4, GEN a6, GEN T, GEN p, GEN *pt_a4, GEN *pt_a6) GEN FpXQE_add(GEN P, GEN Q, GEN a4, GEN T, GEN p) GEN FpXQE_changepoint(GEN x, GEN ch, GEN T, GEN p) GEN FpXQE_changepointinv(GEN x, GEN ch, GEN T, GEN p) @@ -512,6 +529,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpX_disc(GEN x, GEN p) GEN FpX_div_by_X_x(GEN a, GEN x, GEN p, GEN *r) GEN FpX_divrem(GEN x, GEN y, GEN p, GEN *pr) + GEN FpX_dotproduct(GEN x, GEN y, GEN p) GEN FpX_eval(GEN x, GEN y, GEN p) GEN FpX_extgcd(GEN x, GEN y, GEN p, GEN *ptu, GEN *ptv) GEN FpX_fromdigits(GEN x, GEN T, GEN p) @@ -540,6 +558,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpXQ_autpow(GEN x, ulong n, GEN T, GEN p) GEN FpXQ_autpowers(GEN aut, long f, GEN T, GEN p) GEN FpXQ_autsum(GEN x, ulong n, GEN T, GEN p) + GEN FpXQ_auttrace(GEN x, ulong n, GEN T, GEN p) GEN FpXQ_charpoly(GEN x, GEN T, GEN p) GEN FpXQ_conjvec(GEN x, GEN T, GEN p) GEN FpXQ_div(GEN x, GEN y, GEN T, GEN p) @@ -587,6 +606,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Flx_degfact(GEN f, ulong p) GEN Flx_factor(GEN f, ulong p) long Flx_nbfact(GEN z, ulong p) + long Flx_nbfact_Frobenius(GEN T, GEN XP, ulong p) GEN Flx_nbfact_by_degree(GEN z, long *nb, ulong p) long Flx_nbroots(GEN f, ulong p) ulong Flx_oneroot(GEN f, ulong p) @@ -602,6 +622,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpX_factor(GEN f, GEN p) GEN FpX_factorff(GEN P, GEN T, GEN p) long FpX_nbfact(GEN f, GEN p) + long FpX_nbfact_Frobenius(GEN T, GEN XP, GEN p) long FpX_nbroots(GEN f, GEN p) GEN FpX_oneroot(GEN f, GEN p) GEN FpX_roots(GEN f, GEN p) @@ -630,17 +651,16 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpXQX_FpXQXQ_eval(GEN P, GEN x, GEN S, GEN T, GEN p) GEN FpXQX_div_by_X_x(GEN a, GEN x, GEN T, GEN p, GEN *pr) GEN FpXQX_divrem(GEN x, GEN y, GEN T, GEN p, GEN *pr) - GEN FpXQX_divrem_Barrett(GEN x, GEN B, GEN S, GEN T, GEN p, GEN *pr) GEN FpXQX_digits(GEN x, GEN B, GEN T, GEN p) GEN FpXQX_extgcd(GEN x, GEN y, GEN T, GEN p, GEN *ptu, GEN *ptv) GEN FpXQX_fromdigits(GEN x, GEN B, GEN T, GEN p) GEN FpXQX_gcd(GEN P, GEN Q, GEN T, GEN p) + GEN FpXQX_get_red(GEN S, GEN T, GEN p) GEN FpXQX_invBarrett(GEN S, GEN T, GEN p) GEN FpXQX_mul(GEN x, GEN y, GEN T, GEN p) GEN FpXQX_powu(GEN x, ulong n, GEN T, GEN p) GEN FpXQX_red(GEN z, GEN T, GEN p) GEN FpXQX_rem(GEN x, GEN S, GEN T, GEN p) - GEN FpXQX_rem_Barrett(GEN x, GEN mg, GEN S, GEN T, GEN p) GEN FpXQX_sqr(GEN x, GEN T, GEN p) GEN FpXQXQ_div(GEN x, GEN y, GEN S, GEN T, GEN p) GEN FpXQXQ_inv(GEN x, GEN S, GEN T, GEN p) @@ -670,6 +690,10 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Kronecker_to_ZXX(GEN z, long N, long v) GEN ZXX_mul_Kronecker(GEN x, GEN y, long n) GEN ZXX_sqr_Kronecker(GEN x, long n) + long get_FpXQX_degree(GEN T) + GEN get_FpXQX_mod(GEN T) + long get_FpXQX_var(GEN T) + GEN random_FpXQX(long d1, long v, GEN T, GEN p) # FpV.c @@ -692,6 +716,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Flm_mul(GEN x, GEN y, ulong p) GEN Flm_neg(GEN y, ulong p) GEN Flm_powu(GEN x, ulong n, ulong p) + GEN Flm_sub(GEN x, GEN y, ulong p) GEN Flm_to_mod(GEN z, ulong pp) GEN Flm_transpose(GEN x) GEN Flv_add(GEN x, GEN y, ulong p) @@ -699,9 +724,12 @@ cdef extern from "sage/libs/pari/parisage.h": ulong Flv_dotproduct(GEN x, GEN y, ulong p) ulong Flv_dotproduct_pre(GEN x, GEN y, ulong p, ulong pi) GEN Flv_center(GEN z, ulong p, ulong ps2) + GEN Flv_neg(GEN v, ulong p) + void Flv_neg_inplace(GEN v, ulong p) GEN Flv_sub(GEN x, GEN y, ulong p) void Flv_sub_inplace(GEN x, GEN y, ulong p) ulong Flv_sum(GEN x, ulong p) + ulong Flx_dotproduct(GEN x, GEN y, ulong p) GEN Fp_to_mod(GEN z, GEN p) GEN FpC_FpV_mul(GEN x, GEN y, GEN p) GEN FpC_Fp_mul(GEN x, GEN y, GEN p) @@ -718,6 +746,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FpM_mul(GEN x, GEN y, GEN p) GEN FpM_powu(GEN x, ulong n, GEN p) GEN FpM_red(GEN z, GEN p) + GEN FpM_sub(GEN x, GEN y, GEN p) GEN FpM_to_mod(GEN z, GEN p) GEN FpMs_FpC_mul(GEN M, GEN B, GEN p) GEN FpMs_FpCs_solve(GEN M, GEN B, long nbrow, GEN p) @@ -835,6 +864,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN RgV_dotsquare(GEN x) int RgV_is_ZMV(GEN V) long RgV_isin(GEN v, GEN x) + GEN RgV_kill0(GEN v) GEN RgV_neg(GEN x) GEN RgV_prod(GEN v) GEN RgV_sub(GEN x, GEN y) @@ -857,6 +887,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN Kronecker_to_mod(GEN z, GEN pol) GEN QX_ZXQV_eval(GEN P, GEN V, GEN dV) + GEN QXQ_charpoly(GEN A, GEN T, long v) GEN QXQ_powers(GEN a, long n, GEN T) GEN QXQX_to_mod_shallow(GEN z, GEN T) GEN QXQV_to_mod(GEN V, GEN T) @@ -933,6 +964,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN RgXQX_red(GEN P, GEN T) GEN RgXQX_sqr(GEN x, GEN T) GEN RgXQX_translate(GEN P, GEN c, GEN T) + GEN RgXV_RgV_eval(GEN Q, GEN x) GEN RgXV_to_RgM(GEN v, long n) GEN RgXV_unscale(GEN v, GEN h) GEN RgXX_to_RgM(GEN v, long n) @@ -958,6 +990,7 @@ cdef extern from "sage/libs/pari/parisage.h": bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x)) GEN gen_bkeval_powers(GEN P, long d, GEN V, void *E, bb_algebra *ff, GEN cmul(void *E, GEN P, long a, GEN x)) + bb_algebra * get_Rg_algebra() # ZG.c GEN G_ZGC_mul(GEN x, GEN v) @@ -1001,12 +1034,15 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ZM_add(GEN x, GEN y) GEN ZM_copy(GEN x) GEN ZM_det_triangular(GEN mat) + GEN ZM_diag_mul(GEN m, GEN d) int ZM_equal(GEN A, GEN B) GEN ZM_hnfdivrem(GEN x, GEN y, GEN *Q) int ZM_ishnf(GEN x) int ZM_isidentity(GEN x) + int ZM_isscalar(GEN x, GEN s) long ZM_max_lg(GEN x) GEN ZM_mul(GEN x, GEN y) + GEN ZM_mul_diag(GEN m, GEN d) GEN ZM_multosym(GEN x, GEN y) GEN ZM_neg(GEN x) GEN ZM_nm_mul(GEN x, GEN y) @@ -1059,6 +1095,7 @@ cdef extern from "sage/libs/pari/parisage.h": long zv_sum(GEN v) GEN zv_to_Flv(GEN z, ulong p) GEN zv_z_mul(GEN v, long n) + GEN zv_ZM_mul(GEN x, GEN y) int zvV_equal(GEN V, GEN W) # ZX.c @@ -1115,9 +1152,9 @@ cdef extern from "sage/libs/pari/parisage.h": GEN alg_centralproj(GEN al, GEN z, int maps) GEN alg_change_overorder_shallow(GEN al, GEN ord) - GEN alg_complete(GEN rnf, GEN aut, GEN hi, GEN hf, int maxord) - GEN alg_csa_table(GEN nf, GEN mt, long v, int maxord) - GEN alg_cyclic(GEN rnf, GEN aut, GEN b, int maxord) + GEN alg_complete(GEN rnf, GEN aut, GEN hi, GEN hf, long maxord) + GEN alg_csa_table(GEN nf, GEN mt, long v, long maxord) + GEN alg_cyclic(GEN rnf, GEN aut, GEN b, long maxord) GEN alg_decomposition(GEN al) long alg_get_absdim(GEN al) long algabsdim(GEN al) @@ -1127,8 +1164,8 @@ cdef extern from "sage/libs/pari/parisage.h": GEN alg_get_auts(GEN al) GEN alg_get_b(GEN al) GEN algb(GEN al) + GEN algcenter(GEN al) GEN alg_get_center(GEN al) - GEN alggetcenter(GEN al) GEN alg_get_char(GEN al) GEN algchar(GEN al) long alg_get_degree(GEN al) @@ -1139,16 +1176,15 @@ cdef extern from "sage/libs/pari/parisage.h": GEN alghassef(GEN al) GEN alg_get_hasse_i(GEN al) GEN alghassei(GEN al) - GEN alg_get_invord(GEN al) + GEN alg_get_invbasis(GEN al) GEN alginvbasis(GEN al) GEN alg_get_multable(GEN al) - GEN alggetmultable(GEN al) - GEN alg_get_ord(GEN al) + GEN alg_get_basis(GEN al) GEN algbasis(GEN al) GEN alg_get_relmultable(GEN al) GEN algrelmultable(GEN al) GEN alg_get_splitpol(GEN al) - GEN alg_get_splitting(GEN al) + GEN alg_get_splittingfield(GEN al) GEN algsplittingfield(GEN al) GEN alg_get_splittingbasis(GEN al) GEN alg_get_splittingbasisinv(GEN al) @@ -1172,7 +1208,6 @@ cdef extern from "sage/libs/pari/parisage.h": GEN algbasismultable(GEN al, GEN x) GEN algbasismultable_Flm(GEN mt, GEN x, ulong m) GEN algbasistoalg(GEN al, GEN x) - GEN algcenter(GEN al) GEN algcharpoly(GEN al, GEN x, long v) GEN algdisc(GEN al) GEN algdivl(GEN al, GEN x, GEN y) @@ -1193,6 +1228,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN algleftordermodp(GEN al, GEN Ip, GEN p) GEN algmul(GEN al, GEN x, GEN y) GEN algmultable(GEN al) + GEN alglathnf(GEN al, GEN m) GEN algleftmultable(GEN al, GEN x) GEN algneg(GEN al, GEN x) GEN algnorm(GEN al, GEN x) @@ -1205,26 +1241,20 @@ cdef extern from "sage/libs/pari/parisage.h": GEN algsqr(GEN al, GEN x) GEN algsub(GEN al, GEN x, GEN y) GEN algtableinit(GEN mt, GEN p) - GEN algtensor(GEN al1, GEN al2, int maxord) + GEN algtensor(GEN al1, GEN al2, long maxord) GEN algtrace(GEN al, GEN x) long algtype(GEN al) GEN bnfgwgeneric(GEN bnf, GEN Lpr, GEN Ld, GEN pl, long var) - GEN bnrconj(GEN bnr, long i) GEN bnrgwsearch(GEN bnr, GEN Lpr, GEN Ld, GEN pl) void checkalg(GEN x) void checkhasse(GEN nf, GEN hi, GEN hf, long n) long cyclicrelfrob(GEN rnf, GEN nf2, GEN auts, GEN pr) - GEN gp_algcenter(GEN al) - GEN gp_algmultable(GEN al, GEN x) GEN hassecoprime(GEN hi, GEN hf, long n) GEN hassedown(GEN nf, long n, GEN hi, GEN hf) GEN hassewedderburn(GEN hi, GEN hf, long n) long localhasse(GEN rnf, GEN nf2, GEN cnd, GEN pl, GEN auts, GEN b, long k) GEN nfgrunwaldwang(GEN nf0, GEN Lpr, GEN Ld, GEN pl, long var) GEN nfgwkummer(GEN nf, GEN Lpr, GEN Ld, GEN pl, long var) - GEN extchinese(GEN nf, GEN x, GEN y, GEN pl, GEN* red) - GEN factoredextchinese(GEN nf, GEN x, GEN y, GEN pl, GEN* fa) - GEN factoredextchinesetest(GEN nf, GEN x, GEN y, GEN pl, GEN* fa, GEN data, int (*test)(GEN, GEN, GEN)) # alglin1.c @@ -1306,7 +1336,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN RgM_invimage(GEN A, GEN B) GEN RgM_solve(GEN a, GEN b) GEN RgM_solve_realimag(GEN x, GEN y) - void RgMs_structelim(GEN M, long N, GEN A, GEN *p_col, GEN *p_lin) + void RgMs_structelim(GEN M, long nbrow, GEN A, GEN *p_col, GEN *p_lin) GEN ZM_det(GEN a) GEN ZM_detmult(GEN A) GEN ZM_gauss(GEN a, GEN b) @@ -1314,6 +1344,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ZM_indeximage(GEN x) GEN ZM_indexrank(GEN x) GEN ZM_inv(GEN M, GEN dM) + GEN ZM_inv_ratlift(GEN M, GEN *pden) long ZM_rank(GEN x) GEN ZlM_gauss(GEN a, GEN b, ulong p, long e, GEN C) GEN closemodinvertible(GEN x, GEN y) @@ -1409,6 +1440,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN parapply(GEN V, GEN C) void parfor(GEN a, GEN b, GEN code, void *E, long call(void*, GEN, GEN)) void parforprime(GEN a, GEN b, GEN code, void *E, long call(void*, GEN, GEN)) + void parforvec(GEN x, GEN code, long flag, void *E, long call(void*, GEN, GEN)) GEN parselect(GEN C, GEN D, long flag) GEN select0(GEN A, GEN f, long flag) GEN shallowextract(GEN x, GEN L) @@ -1437,7 +1469,6 @@ cdef extern from "sage/libs/pari/parisage.h": long fetch_var_higher() GEN fetch_var_value(long vx, GEN t) GEN gp_read_str(char *t) - GEN gp_read_str_multiline(char *t) entree* install(void *f, char *name, char *code) entree* is_entry(char *s) void kill0(char *e) @@ -1477,6 +1508,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN nupow(GEN x, GEN n, GEN L) GEN primeform(GEN x, GEN p, long prec) GEN primeform_u(GEN x, ulong p) + int qfb_equal1(GEN f) GEN qfbcompraw(GEN x, GEN y) GEN qfbpowraw(GEN x, long n) GEN qfbred0(GEN x, long flag, GEN D, GEN isqrtD, GEN sqrtD) @@ -1484,6 +1516,9 @@ cdef extern from "sage/libs/pari/parisage.h": GEN qfbsolve(GEN Q, GEN n) GEN qfi(GEN x, GEN y, GEN z) GEN qfi_1(GEN x) + GEN qfi_Shanks(GEN a, GEN g, long n) + GEN qfi_log(GEN a, GEN g, GEN o) + GEN qfi_order(GEN q, GEN o) GEN qficomp(GEN x, GEN y) GEN qficompraw(GEN x, GEN y) GEN qfipowraw(GEN x, long n) @@ -1560,6 +1595,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ZV_chinese_tree(GEN A, GEN P, GEN tree, GEN *pt_mod) GEN ZV_producttree(GEN xa) GEN ZX_nv_mod_tree(GEN P, GEN xa, GEN T) + GEN Zideallog(GEN bid, GEN x) long Zp_issquare(GEN a, GEN p) GEN bestappr(GEN x, GEN k) GEN bestapprPade(GEN x, long B) @@ -1570,6 +1606,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN classno(GEN x) GEN classno2(GEN x) long clcm(long a, long b) + GEN conrey_normalize(GEN m, GEN cyc) GEN contfrac0(GEN x, GEN b, long flag) GEN contfracpnqn(GEN x, long n) GEN fibo(long n) @@ -1607,8 +1644,10 @@ cdef extern from "sage/libs/pari/parisage.h": long logint(GEN B, GEN y, GEN *ptq) long logint0(GEN B, GEN y, GEN *ptq) GEN mpfact(long n) + GEN muls_interval(long a, long b) GEN mulu_interval(ulong a, ulong b) - GEN nmV_chinese(GEN A, GEN P, GEN *pt_mod) + GEN ncV_chinese_center(GEN A, GEN P, GEN *pt_mod) + GEN nmV_chinese_center(GEN A, GEN P, GEN *pt_mod) GEN odd_prime_divisors(GEN q) GEN order(GEN x) GEN pnqn(GEN x) @@ -1620,16 +1659,23 @@ cdef extern from "sage/libs/pari/parisage.h": GEN rootsof1_Fp(GEN n, GEN p) GEN rootsof1u_Fp(ulong n, GEN p) GEN sqrtint(GEN a) + GEN ramanujantau(GEN n) ulong ugcd(ulong a, ulong b) long uisprimepower(ulong n, ulong *p) long uissquare(ulong A) long uissquareall(ulong A, ulong *sqrtA) long unegisfundamental(ulong x) long uposisfundamental(ulong x) + GEN znconreychar(GEN bid, GEN m) + GEN znconreyconductor(GEN bid, GEN co, GEN *pm) + GEN znconreyexp(GEN bid, GEN x) + GEN znconreyfromchar(GEN bid, GEN chi) + GEN znconreylog(GEN bid, GEN x) GEN znlog(GEN x, GEN g, GEN o) GEN znorder(GEN x, GEN o) GEN znprimroot(GEN m) GEN znstar(GEN x) + GEN znstar0(GEN N, long flag) # arith2.c @@ -1673,14 +1719,6 @@ cdef extern from "sage/libs/pari/parisage.h": GEN sumdigits0(GEN n, GEN B) ulong sumdigitsu(ulong n) - # DedekZeta.c - - GEN glambdak(GEN nfz, GEN s, long prec) - GEN gzetak(GEN nfz, GEN s, long prec) - GEN gzetakall(GEN nfz, GEN s, long flag, long prec) - GEN initzeta(GEN pol, long prec) - GEN dirzetak(GEN nf, GEN b) - # base1.c GEN FpX_FpC_nfpoleval(GEN nf, GEN pol, GEN a, GEN p) @@ -1707,11 +1745,14 @@ cdef extern from "sage/libs/pari/parisage.h": GEN get_nfpol(GEN x, GEN *nf) GEN get_prid(GEN x) GEN idealfrobenius(GEN nf, GEN gal, GEN pr) + GEN idealfrobenius_aut(GEN nf, GEN gal, GEN pr, GEN aut) + GEN idealramfrobenius(GEN nf, GEN gal, GEN pr, GEN ram) GEN idealramgroups(GEN nf, GEN gal, GEN pr) GEN nf_get_allroots(GEN nf) long nf_get_prec(GEN x) GEN nfcertify(GEN x) GEN nfgaloismatrix(GEN nf, GEN s) + GEN nfgaloispermtobasis(GEN nf, GEN gal) GEN nfinit(GEN x, long prec) GEN nfinit0(GEN x, long flag, long prec) GEN nfinitall(GEN x, long flag, long prec) @@ -1829,6 +1870,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN nfembed(GEN nf, GEN x, long k) GEN nfinv(GEN nf, GEN x) GEN nfinvmodideal(GEN nf, GEN x, GEN ideal) + int nfchecksigns(GEN nf, GEN x, GEN pl) GEN nfmod(GEN nf, GEN a, GEN b) GEN nfmul(GEN nf, GEN x, GEN y) GEN nfmuli(GEN nf, GEN x, GEN y) @@ -2009,13 +2051,15 @@ cdef extern from "sage/libs/pari/parisage.h": int QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec) GEN R_from_QR(GEN x, long prec) + GEN RgM_Babai(GEN B, GEN t) int RgM_QR_init(GEN x, GEN *pB, GEN *pQ, GEN *pL, long prec) + GEN RgM_gram_schmidt(GEN e, GEN *ptB) GEN Xadic_lindep(GEN x) GEN algdep(GEN x, long n) GEN algdep0(GEN x, long n, long bit) + void forqfvec(void *E, long (*fun)(void *, GEN, GEN, double), GEN a, GEN BORNE) void forqfvec0(GEN a, GEN BORNE, GEN code) GEN gaussred_from_QR(GEN x, long prec) - GEN gram_schmidt(GEN e, GEN *ptB) GEN lindep0(GEN x, long flag) GEN lindep(GEN x) GEN lindep2(GEN x, long bit) @@ -2114,10 +2158,11 @@ cdef extern from "sage/libs/pari/parisage.h": GEN bits_to_int(GEN x, long l) ulong bits_to_u(GEN v, long l) GEN binaire(GEN x) - GEN binary_zv(GEN x) GEN binary_2k(GEN x, long k) - GEN binary_2k_zv(GEN x, long k) + GEN binary_2k_nv(GEN x, long k) + GEN binary_zv(GEN x) long bittest(GEN x, long n) + GEN fromdigits_2k(GEN x, long k) GEN gbitand(GEN x, GEN y) GEN gbitneg(GEN x, long n) GEN gbitnegimply(GEN x, GEN y) @@ -2129,6 +2174,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ibitnegimply(GEN x, GEN y) GEN ibitor(GEN x, GEN y) GEN ibitxor(GEN x, GEN y) + GEN nv_fromdigits_2k(GEN x, long k) # buch1.c @@ -2160,11 +2206,14 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ABC_to_bnr(GEN A, GEN B, GEN C, GEN *H, int gen) GEN Buchray(GEN bnf, GEN module, long flag) GEN bnrautmatrix(GEN bnr, GEN aut) + GEN bnrchar(GEN bnr, GEN g, GEN v) + GEN bnrchar_primitive(GEN bnr, GEN chi, GEN bnrc) GEN bnrclassno(GEN bignf, GEN ideal) GEN bnrclassno0(GEN A, GEN B, GEN C) GEN bnrclassnolist(GEN bnf, GEN listes) GEN bnrconductor0(GEN A, GEN B, GEN C, long flag) GEN bnrconductor(GEN bnr, GEN H0, long flag) + GEN bnrconductor_i(GEN bnr, GEN H0, long flag) GEN bnrconductorofchar(GEN bnr, GEN chi) GEN bnrdisc0(GEN A, GEN B, GEN C, long flag) GEN bnrdisc(GEN bnr, GEN H, long flag) @@ -2180,6 +2229,18 @@ cdef extern from "sage/libs/pari/parisage.h": GEN buchnarrow(GEN bignf) long bnfcertify(GEN bnf) long bnfcertify0(GEN bnf, long flag) + int char_check(GEN cyc, GEN chi) + GEN charker(GEN cyc, GEN chi) + GEN charker0(GEN cyc, GEN chi) + GEN charconj(GEN cyc, GEN chi) + GEN charconj0(GEN cyc, GEN chi) + GEN charorder(GEN cyc, GEN x) + GEN charorder0(GEN x, GEN chi) + GEN char_denormalize(GEN cyc, GEN D, GEN chic) + GEN char_normalize(GEN chi, GEN ncyc) + GEN char_rootof1(GEN d, long prec) + GEN char_rootof1_u(ulong d, long prec) + GEN cyc_normalize(GEN c) GEN decodemodule(GEN nf, GEN fa) GEN discrayabslist(GEN bnf, GEN listes) GEN discrayabslistarch(GEN bnf, GEN arch, ulong bound) @@ -2204,10 +2265,6 @@ cdef extern from "sage/libs/pari/parisage.h": long hyperell_locally_soluble(GEN pol, GEN p) long nf_hyperell_locally_soluble(GEN nf, GEN pol, GEN p) - # classpoly.c - - GEN polclass(GEN D, long inv, long xvar) - # compile.c GEN closure_deriv(GEN G) @@ -2254,6 +2311,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN sd_path(char *v, long flag) GEN sd_prettyprinter(char *v, long flag) GEN sd_primelimit(char *v, long flag) + GEN sd_realbitprecision(char *v, long flag) GEN sd_realprecision(char *v, long flag) GEN sd_secure(char *v, long flag) GEN sd_seriesprecision(char *v, long flag) @@ -2298,16 +2356,24 @@ cdef extern from "sage/libs/pari/parisage.h": int gp_handle_exception(long numerr) void gp_alarm_handler(int sig) void gp_sigint_fun() + extern int h_REGULAR, h_LONG, h_APROPOS, h_RL void gp_help(char *s, long flag) void gp_echo_and_log(char *prompt, char *s) void print_fun_list(char **list, long nbli) + # dirichlet.c + + GEN direxpand(GEN a, long L) + GEN direuler(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, GEN c) + # ellanal.c GEN ellanalyticrank(GEN e, GEN eps, long prec) + GEN ellanalyticrank_bitprec(GEN e, GEN eps, long bitprec) GEN ellanal_globalred_all(GEN e, GEN *N, GEN *cb, GEN *tam) GEN ellheegner(GEN e) GEN ellL1(GEN e, long r, long prec) + GEN ellL1_bitprec(GEN E, long r, long bitprec) # elldata.c @@ -2316,13 +2382,14 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ellidentify(GEN E) GEN ellsearch(GEN A) GEN ellsearchcurve(GEN name) - void forell(void *E, long call(void*, GEN), long a, long b) + void forell(void *E, long call(void*, GEN), long a, long b, long flag) # ellfromeqn.c GEN ellfromeqn(GEN s) # elliptic.c + extern int t_ELL_Rg, t_ELL_Q, t_ELL_Qp, t_ELL_Fp, t_ELL_Fq, t_ELL_NF long ellQ_get_CM(GEN e) int ell_is_integral(GEN E) GEN ellbasechar(GEN E) @@ -2397,6 +2464,9 @@ cdef extern from "sage/libs/pari/parisage.h": GEN elllocalred(GEN e, GEN p1) GEN elllog(GEN e, GEN a, GEN g, GEN o) GEN ellminimalmodel(GEN E, GEN *ptv) + GEN ellminimaltwist(GEN e) + GEN ellminimaltwist0(GEN e, long fl) + GEN ellminimaltwistcond(GEN e) GEN ellmul(GEN e, GEN z, GEN n) GEN ellnonsingularmultiple(GEN e, GEN P) GEN ellneg(GEN e, GEN z) @@ -2410,9 +2480,11 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ellpadiclog(GEN E, GEN p, long n, GEN P) GEN ellpadics2(GEN E, GEN p, long n) GEN ellperiods(GEN w, long flag, long prec) + GEN elltwist(GEN E, GEN D) GEN ellrandom(GEN e) long ellrootno(GEN e, GEN p) long ellrootno_global(GEN e) + GEN ellsea(GEN E, GEN p, long early_abort) GEN ellsigma(GEN om, GEN z, long flag, long prec) GEN ellsub(GEN e, GEN z1, GEN z2) GEN elltaniyama(GEN e, long prec) @@ -2438,18 +2510,19 @@ cdef extern from "sage/libs/pari/parisage.h": # ellpadicL.c GEN ellpadicL(GEN E, GEN p, long n, long r, GEN D, GEN C) + GEN ellpadicmoments(GEN E, GEN pp, long n, long r, GEN DD) # ellisogeny.c GEN ellisogenyapply(GEN f, GEN P) GEN ellisogeny(GEN e, GEN G, long only_image, long vx, long vy) + GEN ellisomat(GEN E, long flag) # ellsea.c GEN Fp_ellcard_SEA(GEN a4, GEN a6, GEN p, long early_abort) GEN Fq_ellcard_SEA(GEN a4, GEN a6, GEN q, GEN T, GEN p, long early_abort) GEN ellmodulareqn(long l, long vx, long vy) - GEN ellsea(GEN E, GEN p, long early_abort) # es.c @@ -2476,6 +2549,7 @@ cdef extern from "sage/libs/pari/parisage.h": void err_printf(char* pat, ...) GEN gp_getenv(char *s) GEN gp_read_file(char *s) + GEN gp_read_str_multiline(char *s, char *last) GEN gp_read_stream(FILE *f) GEN gp_readvec_file(char *s) GEN gp_readvec_stream(FILE *f) @@ -2529,11 +2603,12 @@ cdef extern from "sage/libs/pari/parisage.h": void out_term_color(PariOUT *out, long c) void out_vprintf(PariOUT *out, char *fmt, va_list ap) char* pari_sprint0(char *msg, GEN g, long flag) + extern int f_RAW, f_PRETTYMAT, f_PRETTY, f_TEX void print0(GEN g, long flag) void print1(GEN g) void printf0(char *fmt, GEN args) - void printsep(char *s, GEN g, long flag) - void printsep1(char *s, GEN g, long flag) + void printsep(char *s, GEN g) + void printsep1(char *s, GEN g) void printtex(GEN g) char* stack_sprintf(char *fmt, ...) char* stack_strcat(char *s, char *t) @@ -2554,8 +2629,10 @@ cdef extern from "sage/libs/pari/parisage.h": # eval.c + extern int br_NONE, br_BREAK, br_NEXT, br_MULTINEXT, br_RETURN void bincopy_relink(GEN C, GEN vi) GEN break0(long n) + GEN call0(GEN fun, GEN args) GEN closure_callgen1(GEN C, GEN x) GEN closure_callgen1prec(GEN C, GEN x, long prec) GEN closure_callgen2(GEN C, GEN x, GEN y) @@ -2575,6 +2652,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN copybin_unlink(GEN C) GEN get_lex(long vn) long get_localprec() + long get_localbitprec() GEN gp_call(void *E, GEN x) GEN gp_callprec(void *E, GEN x, long prec) GEN gp_call2(void *E, GEN x, GEN y) @@ -2586,6 +2664,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN gp_evalupto(void *E, GEN x) long gp_evalvoid(void *E, GEN x) void localprec(long p) + void localbitprec(long p) long loop_break() GEN next0(long n) GEN pareval(GEN C) @@ -2595,6 +2674,7 @@ cdef extern from "sage/libs/pari/parisage.h": void pop_lex(long n) void pop_localprec() void push_lex(GEN a, GEN C) + void push_localbitprec(long p) void push_localprec(long p) GEN return0(GEN x) void set_lex(long vn, GEN x) @@ -2611,11 +2691,13 @@ cdef extern from "sage/libs/pari/parisage.h": GEN FF_conjvec(GEN x) GEN FF_div(GEN a, GEN b) GEN FF_ellcard(GEN E) + GEN FF_ellcard_SEA(GEN E, long smallfact) GEN FF_ellgens(GEN E) GEN FF_ellgroup(GEN E) GEN FF_elllog(GEN E, GEN P, GEN Q, GEN o) GEN FF_ellmul(GEN E, GEN P, GEN n) GEN FF_ellorder(GEN E, GEN P, GEN o) + GEN FF_elltwist(GEN E) GEN FF_ellrandom(GEN E) GEN FF_elltatepairing(GEN E, GEN P, GEN Q, GEN m) GEN FF_ellweilpairing(GEN E, GEN P, GEN Q, GEN m) @@ -2823,8 +2905,11 @@ cdef extern from "sage/libs/pari/parisage.h": GEN QpV_to_QV(GEN v) GEN RgM_mulreal(GEN x, GEN y) GEN RgX_RgM_eval_col(GEN x, GEN M, long c) + GEN RgX_cxeval(GEN T, GEN u, GEN ui) GEN RgX_deflate_max(GEN x0, long *m) + long RgX_degree(GEN x, long v) GEN RgX_integ(GEN x) + GEN bitprecision0(GEN x, long n) GEN ceil_safe(GEN x) GEN ceilr(GEN x) GEN centerlift(GEN x) @@ -2928,9 +3013,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN polcoeff0(GEN x, long n, long v) GEN polcoeff_i(GEN x, long n, long v) long poldegree(GEN x, long v) - long RgX_degree(GEN x, long v) GEN poleval(GEN x, GEN y) - GEN RgX_cxeval(GEN T, GEN u, GEN ui) GEN pollead(GEN x, long v) long precision(GEN x) GEN precision0(GEN x, long n) @@ -3001,6 +3084,8 @@ cdef extern from "sage/libs/pari/parisage.h": GEN ZM_hnfcenter(GEN M) GEN ZM_hnflll(GEN A, GEN *ptB, int remove) GEN ZV_extgcd(GEN A) + GEN ZV_snfall(GEN D, GEN *pU, GEN *pV) + GEN ZV_snf_group(GEN d, GEN *newU, GEN *newUi) GEN ZM_hnfmod(GEN x, GEN d) GEN ZM_hnfmodall(GEN x, GEN dm, long flag) GEN ZM_hnfmodid(GEN x, GEN d) @@ -3043,6 +3128,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN absi_factor(GEN n) GEN absi_factor_limit(GEN n, ulong all) long bigomega(GEN n) + long bigomegau(ulong n) GEN core(GEN n) ulong coreu(ulong n) GEN eulerphi(GEN n) @@ -3065,6 +3151,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN nextprime(GEN n) GEN numdiv(GEN n) long omega(GEN n) + long omegau(ulong n) GEN precprime(GEN n) GEN sumdiv(GEN n) GEN sumdivk(GEN n, long k) @@ -3134,6 +3221,7 @@ cdef extern from "sage/libs/pari/parisage.h": void pari_init_opts(size_t parisize, ulong maxprime, ulong init_opts) void pari_init(size_t parisize, ulong maxprime) void pari_stackcheck_init(void *pari_stack_base) + void pari_sighandler(int sig) void pari_sig_init(void (*f)(int)) void pari_thread_alloc(pari_thread *t, size_t s, GEN arg) void pari_thread_close() @@ -3143,10 +3231,6 @@ cdef extern from "sage/libs/pari/parisage.h": void pari_thread_valloc(pari_thread *t, size_t s, size_t v, GEN arg) GEN pari_version() void pari_warn(int numerr, ...) - void * pari_mainstack_malloc(size_t size) - void pari_mainstack_mfree(void *s, size_t size) - void pari_mainstack_free(_pari_mainstack *st) - void paristack_alloc(size_t rsize, size_t vsize) void paristack_newrsize(ulong newsize) void paristack_resize(ulong newsize) void paristack_setsize(size_t rsize, size_t vsize) @@ -3170,10 +3254,11 @@ cdef extern from "sage/libs/pari/parisage.h": GEN intnumgaussinit(long n, long prec) GEN intnuminit(GEN a, GEN b, long m, long prec) GEN intnumromb(void *E, GEN (*eval) (void *, GEN), GEN a, GEN b, long flag, long prec) + GEN intnumromb_bitprec(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long flag, long bit) GEN sumnum(void *E, GEN (*eval)(void*, GEN), GEN a, GEN tab, long prec) GEN sumnuminit(GEN fast, long prec) - GEN sumnummon(void *E, GEN (*eval)(void*, GEN), GEN a, GEN tab, long prec) - GEN sumnummoninit(GEN asymp, GEN w, GEN n0, long prec) + GEN sumnummonien(void *E, GEN (*eval)(void*, GEN), GEN a, GEN tab, long prec) + GEN sumnummonieninit(GEN asymp, GEN w, GEN n0, long prec) # krasner.c @@ -3188,16 +3273,16 @@ cdef extern from "sage/libs/pari/parisage.h": long is_linit(GEN data) GEN ldata_get_an(GEN ldata) - long ldata_get_selfdual(GEN ldata) - long ldata_isreal(GEN ldata) + GEN ldata_get_dual(GEN ldata) GEN ldata_get_gammavec(GEN ldata) long ldata_get_degree(GEN ldata) long ldata_get_k(GEN ldata) GEN ldata_get_conductor(GEN ldata) GEN ldata_get_rootno(GEN ldata) GEN ldata_get_residue(GEN ldata) - GEN ldata_vecan(GEN ldata, long L, long prec) long ldata_get_type(GEN ldata) + long ldata_isreal(GEN ldata) + GEN ldata_vecan(GEN ldata, long L, long prec) long linit_get_type(GEN linit) GEN linit_get_ldata(GEN linit) GEN linit_get_tech(GEN linit) @@ -3212,7 +3297,6 @@ cdef extern from "sage/libs/pari/parisage.h": GEN lfun_get_w2(GEN tech) GEN lfun_get_expot(GEN tech) long lfun_get_der(GEN tech) - long lfun_get_bitprec(GEN tech) GEN lfun(GEN ldata, GEN s, long prec) GEN lfun_bitprec(GEN ldata, GEN s, long bitprec) GEN lfun0_bitprec(GEN ldata, GEN s, long der, long bitprec) @@ -3220,12 +3304,17 @@ cdef extern from "sage/libs/pari/parisage.h": long lfuncheckfeq(GEN data, GEN t0, long prec) long lfuncheckfeq_bitprec(GEN data, GEN t0, long bitprec) GEN lfunconductor(GEN data, GEN maxcond, long flag, long prec) + GEN lfunconductor_bitprec(GEN data, GEN maxcond, long flag, long bitprec) + GEN lfuncost(GEN lmisc, GEN dom, long der, long bitprec) + GEN lfuncost0(GEN L, GEN dom, long der, long bitprec) GEN lfuncreate(GEN obj) GEN lfunan(GEN ldata, long L, long prec) GEN lfunhardy(GEN ldata, GEN t, long prec) GEN lfunhardy_bitprec(GEN ldata, GEN t, long bitprec) GEN lfuninit(GEN ldata, GEN dom, long der, long prec) GEN lfuninit_bitprec(GEN ldata, GEN dom, long der, long bitprec) + GEN lfuninit0(GEN ldata, GEN dom, long der, long prec) + GEN lfuninit0_bitprec(GEN ldata, GEN dom, long der, long bitprec) GEN lfuninit_make(long t, GEN ldata, GEN molin, GEN domain) long lfunisvgaell(GEN Vga, long flag) GEN lfunlambda(GEN ldata, GEN s, long prec) @@ -3244,12 +3333,14 @@ cdef extern from "sage/libs/pari/parisage.h": GEN lfunrtopoles(GEN r) GEN lfuntheta(GEN data, GEN t, long m, long prec) GEN lfuntheta_bitprec(GEN data, GEN t, long m, long bitprec) + long lfunthetacost0(GEN L, GEN tdom, long m, long bitprec) + long lfunthetacost(GEN ldata, GEN tdom, long m, long bitprec) GEN lfunthetainit(GEN ldata, GEN tinf, long m, long prec) GEN lfunthetainit_bitprec(GEN ldata, GEN tdom, long m, long bitprec) GEN lfunthetacheckinit(GEN data, GEN tinf, long m, long *ptbitprec, long fl) GEN lfunzeros(GEN ldata, GEN lim, long divz, long prec) GEN lfunzeros_bitprec(GEN ldata, GEN lim, long divz, long bitprec) - int sdomain_isincl(GEN dom, GEN dom0) + int sdomain_isincl(long k, GEN dom, GEN dom0) GEN theta_get_an(GEN tdata) GEN theta_get_K(GEN tdata) GEN theta_get_R(GEN tdata) @@ -3262,20 +3353,24 @@ cdef extern from "sage/libs/pari/parisage.h": GEN dirzetak(GEN nf, GEN b) GEN ellmoddegree(GEN e, long prec) + GEN ellmoddegree_bitprec(GEN e, long bitprec) GEN lfunabelianrelinit(GEN bnfabs, GEN bnf, GEN polrel, GEN dom, long der, long prec) GEN lfunabelianrelinit_bitprec(GEN bnfabs, GEN bnf, GEN polrel, GEN dom, long der, long bitprec) - GEN lfunconvol(GEN a1, GEN a2) + GEN lfunartin(GEN N, GEN G, GEN M, long o) GEN lfundiv(GEN ldata1, GEN ldata2, long prec) - GEN lfunzetakinit(GEN pol, GEN dom, long der, long flag, long prec) - GEN lfunzetakinit_bitprec(GEN pol, GEN dom, long der, long flag, long bitprec) + GEN lfunellmfpeters_bitprec(GEN E, long bitprec) GEN lfunetaquo(GEN ldata) GEN lfunmfspec(GEN ldata, long prec) + GEN lfunmfspec_bitprec(GEN lmisc, long bitprec) GEN lfunmfpeters(GEN ldata, long prec) GEN lfunmfpeters_bitprec(GEN ldata, long bitprec) GEN lfunmul(GEN ldata1, GEN ldata2, long prec) GEN lfunqf(GEN ldata) GEN lfunsymsq(GEN ldata, GEN known, long prec) GEN lfunsymsqspec(GEN ldata, long prec) + GEN lfunsymsqspec_bitprec(GEN lmisc, long bitprec) + GEN lfunzetakinit(GEN pol, GEN dom, long der, long flag, long prec) + GEN lfunzetakinit_bitprec(GEN pol, GEN dom, long der, long flag, long bitprec) # lll.c @@ -3312,6 +3407,10 @@ cdef extern from "sage/libs/pari/parisage.h": # mellininv.c + double dbllambertW0(double a) + double dbllambertW_1(double a) + double dbllemma526(double a, double b, double c, long B) + double dblcoro526(double a, double c, long B) GEN gammamellininv(GEN Vga, GEN s, long m, long prec) GEN gammamellininv_bitprec(GEN Vga, GEN s, long m, long bitprec) GEN gammamellininvasymp(GEN Vga, long nlimmax, long m) @@ -3370,14 +3469,6 @@ cdef extern from "sage/libs/pari/parisage.h": GEN member_zk(GEN x) GEN member_zkst(GEN bid) - # polmodular.c - - GEN Flm_Fl_polmodular_evalx(GEN phi, long L, ulong j, ulong p, ulong pi) - GEN Fp_polmodular_evalx(long L, long inv, GEN J, GEN P, long v, int compute_derivs) - GEN polmodular(long L, long inv, GEN x, long yvar, long compute_derivs) - GEN polmodular_ZM(long L, long inv) - GEN polmodular_ZXX(long L, long inv, long xvar, long yvar) - # mp.c GEN addmulii(GEN x, GEN y, GEN z) @@ -3572,6 +3663,7 @@ cdef extern from "sage/libs/pari/parisage.h": # polarit2.c + GEN FpV_factorback(GEN L, GEN e, GEN p) GEN Q_content(GEN x) GEN Q_denom(GEN x) GEN Q_div_to_int(GEN x, GEN c) @@ -3736,6 +3828,18 @@ cdef extern from "sage/libs/pari/parisage.h": GEN pol_x_powers(long N, long v) GEN residual_characteristic(GEN x) + # polclass.c + + GEN polclass(GEN D, long inv, long xvar) + + # polmodular.c + + GEN Flm_Fl_polmodular_evalx(GEN phi, long L, ulong j, ulong p, ulong pi) + GEN Fp_polmodular_evalx(long L, long inv, GEN J, GEN P, long v, int compute_derivs) + GEN polmodular(long L, long inv, GEN x, long yvar, long compute_derivs) + GEN polmodular_ZM(long L, long inv) + GEN polmodular_ZXX(long L, long inv, long xvar, long yvar) + # prime.c long BPSW_isprime(GEN x) @@ -3774,10 +3878,12 @@ cdef extern from "sage/libs/pari/parisage.h": GEN qfisom0(GEN g, GEN h, GEN flags) GEN qfisominit(GEN g, GEN flags) GEN qfisominit0(GEN g, GEN flags) + GEN qforbits(GEN G, GEN V) # qfparam.c - GEN qfsolve(GEN G) - GEN qfparam(GEN G, GEN sol, long fl) + + GEN qfsolve(GEN G) + GEN qfparam(GEN G, GEN sol, long fl) # random.c @@ -3804,7 +3910,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN realroots(GEN P, GEN ab, long prec) long ZX_sturm(GEN P) long ZX_sturmpart(GEN P, GEN ab) - GEN ZX_uspensky(GEN P, GEN ab, long flag, long prec) + GEN ZX_Uspensky(GEN P, GEN ab, long flag, long prec) # subcyclo.c @@ -3827,13 +3933,13 @@ cdef extern from "sage/libs/pari/parisage.h": GEN bnrL1(GEN bnr, GEN sbgrp, long flag, long prec) GEN bnrrootnumber(GEN bnr, GEN chi, long flag, long prec) GEN bnrstark(GEN bnr, GEN subgroup, long prec) + GEN cyc2elts(GEN cyc) # sumiter.c GEN asympnum(void *E, GEN (*f)(void *, GEN, long), long muli, GEN alpha, long prec) GEN derivnum(void *E, GEN (*eval)(void *, GEN, long prec), GEN x, long prec) GEN derivfun(void *E, GEN (*eval)(void *, GEN, long prec), GEN x, long prec) - GEN direuler(void *E, GEN (*eval)(void *, GEN), GEN ga, GEN gb, GEN c) int forcomposite_init(forcomposite_t *C, GEN a, GEN b) GEN forcomposite_next(forcomposite_t *C) GEN forprime_next(forprime_t *T) @@ -3855,6 +3961,7 @@ cdef extern from "sage/libs/pari/parisage.h": void u_forprime_restrict(forprime_t *T, ulong c) int u_forprime_arith_init(forprime_t *T, ulong a, ulong b, ulong c, ulong q) GEN zbrent(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, long prec) + GEN solvestep(void *E, GEN (*eval)(void *, GEN), GEN a, GEN b, GEN step, long flag, long prec) # thue.c @@ -3886,15 +3993,19 @@ cdef extern from "sage/libs/pari/parisage.h": GEN exp1r_abs(GEN x) GEN gcos(GEN x, long prec) GEN gcotan(GEN x, long prec) + GEN gcotanh(GEN x, long prec) GEN gexp(GEN x, long prec) GEN gexpm1(GEN x, long prec) GEN glog(GEN x, long prec) GEN gpow(GEN x, GEN n, long prec) GEN gpowers(GEN x, long n) + GEN gpowers0(GEN x, long n, GEN x0) GEN gpowgs(GEN x, long n) + GEN grootsof1(long N, long prec) GEN gsin(GEN x, long prec) GEN gsinc(GEN x, long prec) void gsincos(GEN x, GEN *s, GEN *c, long prec) + GEN gsqrpowers(GEN q, long n) GEN gsqrt(GEN x, long prec) GEN gsqrtn(GEN x, GEN n, GEN *zetan, long prec) GEN gtan(GEN x, long prec) @@ -3989,11 +4100,11 @@ cdef extern from "sage/libs/pari/parisage.h": GEN veceint1(GEN nmax, GEN C, long prec) GEN vecthetanullk(GEN q, long k, long prec) GEN vecthetanullk_tau(GEN tau, long k, long prec) + GEN veczeta(GEN a, GEN b, long N, long prec) GEN weber0(GEN x, long flag, long prec) GEN weberf(GEN x, long prec) GEN weberf1(GEN x, long prec) GEN weberf2(GEN x, long prec) - GEN zetaBorweinRecycled(long s, long h, long N, long prec) # modsym.c GEN Eisenstein_symbol(GEN W, GEN c) @@ -4004,6 +4115,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN RgX_act_Gl2Q(GEN g, long k) GEN RgX_act_ZGl2Q(GEN z, long k) void checkms(GEN W) + GEN msfromcusp(GEN W, GEN c) GEN msfromell(GEN E, long signe) GEN msatkinlehner(GEN W, long Q, GEN) GEN mscuspidal(GEN W, long flag) @@ -4012,12 +4124,15 @@ cdef extern from "sage/libs/pari/parisage.h": GEN mshecke(GEN W, long p, GEN H) GEN msinit(GEN N, GEN k, long sign) long msissymbol(GEN W, GEN s) + GEN mspadicmoments(GEN W, GEN phi, long p, long n) GEN mspathgens(GEN W) GEN mspathlog(GEN W, GEN path) GEN msnew(GEN W) GEN msstar(GEN W, GEN) GEN msqexpansion(GEN W, GEN proV, ulong B) GEN mssplit(GEN W, GEN H) + GEN mstooms(GEN W, GEN phi, long p, long n) + GEN omseval(GEN O, GEN path) # zetamult.c GEN zetamult(GEN avec, long prec) @@ -4025,6 +4140,8 @@ cdef extern from "sage/libs/pari/parisage.h": # level1.h ulong Fl_add(ulong a, ulong b, ulong p) + ulong Fl_addmul_pre(ulong x0, ulong x1, ulong y0, ulong p, ulong pi) + ulong Fl_addmulmul_pre(ulong x0, ulong y0, ulong x1, ulong y1, ulong p, ulong pi) long Fl_center(ulong u, ulong p, ulong ps2) ulong Fl_div(ulong a, ulong b, ulong p) ulong Fl_double(ulong a, ulong p) @@ -4203,6 +4320,7 @@ cdef extern from "sage/libs/pari/parisage.h": GEN real_0(long prec) GEN real_0_bit(long bitprec) GEN real_1(long prec) + GEN real_1_bit(long bit) GEN real_m1(long prec) GEN remii(GEN a, GEN b) void remiiz(GEN x, GEN y, GEN z) diff --git a/src/sage/libs/pari/types.pxd b/src/sage/libs/pari/types.pxd index b85aefb5853..55d5c10606e 100644 --- a/src/sage/libs/pari/types.pxd +++ b/src/sage/libs/pari/types.pxd @@ -76,6 +76,7 @@ cdef extern from "sage/libs/pari/parisage.h": struct gp_context struct pariFILE struct pari_mt + struct pari_stack struct pari_thread struct pari_timer struct GENbin diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 8401b9d3c4e..a0dd0729f35 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -47,7 +47,6 @@ from sage.rings.finite_rings.finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.libs.pari.all import pari from sage.libs.gmp.all cimport * -from sage.structure.parent_base cimport ParentWithBase from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular _saved_options = (int(0),0,0) diff --git a/src/sage/manifolds/__init__.py b/src/sage/manifolds/__init__.py new file mode 100644 index 00000000000..932b79829cf --- /dev/null +++ b/src/sage/manifolds/__init__.py @@ -0,0 +1 @@ +# Empty file diff --git a/src/sage/manifolds/all.py b/src/sage/manifolds/all.py new file mode 100644 index 00000000000..990657e143b --- /dev/null +++ b/src/sage/manifolds/all.py @@ -0,0 +1,3 @@ +from sage.misc.lazy_import import lazy_import +lazy_import('sage.manifolds.manifold', 'Manifold') + diff --git a/src/sage/manifolds/chart.py b/src/sage/manifolds/chart.py new file mode 100644 index 00000000000..6a6194bbc57 --- /dev/null +++ b/src/sage/manifolds/chart.py @@ -0,0 +1,2103 @@ +r""" +Coordinate Charts + +The class :class:`Chart` implements coordinate charts on a topological +manifold over a topological field `K`. The subclass :class:`RealChart` +is devoted to the case `K=\RR`, for which the concept of coordinate +range is meaningful. + +Transition maps between charts are implemented via the class +:class:`CoordChange`. + +AUTHORS: + +- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version +- Travis Scrimshaw (2015): review tweaks + +REFERENCES: + +- Chap. 2 of [Lee11]_ J.M. Lee: *Introduction to Topological Manifolds*, + 2nd ed., Springer (New York) (2011) + +- Chap. 1 of [Lee13]_ J.M. Lee : *Introduction to Smooth Manifolds*, + 2nd ed., Springer (New York) (2013) +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.structure.unique_representation import UniqueRepresentation +from sage.symbolic.ring import SR +from sage.rings.infinity import Infinity +from sage.misc.latex import latex + +class Chart(UniqueRepresentation, SageObject): + r""" + Chart on a topological manifold. + + Given a topological manifold `M` of dimension `n` over a topological + field `K`, a *chart* on `M` is a pair `(U, \varphi)`, where `U` is an + open subset of `M` and `\varphi : U \rightarrow V \subset K^n` is a + homeomorphism from `U` to an open subset `V` of `K^n`. + + The components `(x^1, \ldots, x^n)` of `\varphi`, defined by + `\varphi(p) = (x^1(p), \ldots, x^n(p)) \in K^n` for any point + `p \in U`, are called the *coordinates* of the chart `(U, \varphi)`. + + INPUT: + + - ``domain`` -- open subset `U` on which the chart is defined (must be + an instance of :class:`~sage.manifolds.manifold.TopologicalManifold`) + - ``coordinates`` -- (default: ``''`` (empty string)) the string + defining the coordinate symbols, see below + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used) + + The string ``coordinates`` has the space ``' '`` as a separator and each + item has at most two fields, separated by a colon (``:``): + + 1. the coordinate symbol (a letter or a few letters); + 2. (optional) the LaTeX spelling of the coordinate, if not provided the + coordinate symbol given in the first field will be used. + + If it contains any LaTeX expression, the string ``coordinates`` must be + declared with the prefix 'r' (for "raw") to allow for a proper treatment + of LaTeX's backslash character (see examples below). + If no LaTeX spelling is to be set for any coordinate, the argument + ``coordinates`` can be omitted when the shortcut operator ``<,>`` is + used via Sage preparser (see examples below). + + EXAMPLES: + + A chart on a complex 2-dimensional topological manifold:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X = M.chart('x y'); X + Chart (M, (x, y)) + sage: latex(X) + \left(M,(x, y)\right) + sage: type(X) + + + To manipulate the coordinates `(x,y)` as global variables, + one has to set:: + + sage: x,y = X[:] + + However, a shortcut is to use the declarator ```` in the left-hand + side of the chart declaration (there is then no need to pass the string + ``'x y'`` to ``chart()``):: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart(); X + Chart (M, (x, y)) + + The coordinates are then immediately accessible:: + + sage: y + y + sage: x is X[0] and y is X[1] + True + + Note that ``x`` and ``y`` declared in ```` are mere Python variable + names and do not have to coincide with the coordinate symbols; + for instance, one may write:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart('x y'); X + Chart (M, (x, y)) + + Then ``y`` is not known as a global Python variable and the + coordinate `y` is accessible only through the global variable ``y1``:: + + sage: y1 + y + sage: latex(y1) + y + sage: y1 is X[1] + True + + However, having the name of the Python variable coincide with the + coordinate symbol is quite convenient; so it is recommended to declare:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + + In the above example, the chart X covers entirely the manifold ``M``:: + + sage: X.domain() + Complex 2-dimensional topological manifold M + + Of course, one may declare a chart only on an open subset of ``M``:: + + sage: U = M.open_subset('U') + sage: Y. = U.chart(r'z1:\zeta_1 z2:\zeta_2'); Y + Chart (U, (z1, z2)) + sage: Y.domain() + Open subset U of the Complex 2-dimensional topological manifold M + + In the above declaration, we have also specified some LaTeX writing + of the coordinates different from the text one:: + + sage: latex(z1) + {\zeta_1} + + Note the prefix ``r`` in front of the string ``r'z1:\zeta_1 z2:\zeta_2'``; + it makes sure that the backslash character is treated as an ordinary + character, to be passed to the LaTeX interpreter. + + Coordinates are Sage symbolic variables (see + :mod:`sage.symbolic.expression`):: + + sage: type(z1) + + + In addition to the Python variable name provided in the operator ``<.,.>``, + the coordinates are accessible by their indices:: + + sage: Y[0], Y[1] + (z1, z2) + + The index range is that declared during the creation of the manifold. By + default, it starts at 0, but this can be changed via the parameter + ``start_index``:: + + sage: M1 = Manifold(2, 'M_1', field='complex', structure='topological', + ....: start_index=1) + sage: Z. = M1.chart() + sage: Z[1], Z[2] + (u, v) + + The full set of coordinates is obtained by means of the slice + operator ``[:]``:: + + sage: Y[:] + (z1, z2) + + Some partial sets of coordinates:: + + sage: Y[:1] + (z1,) + sage: Y[1:] + (z2,) + + Each constructed chart is automatically added to the manifold's user + atlas:: + + sage: M.atlas() + [Chart (M, (x, y)), Chart (U, (z1, z2))] + + and to the atlas of the chart's domain:: + + sage: U.atlas() + [Chart (U, (z1, z2))] + + Manifold subsets have a *default chart*, which, unless changed via the + method + :meth:`~sage.manifolds.manifold.TopologicalManifold.set_default_chart`, + is the first defined chart on the subset (or on a open subset of it):: + + sage: M.default_chart() + Chart (M, (x, y)) + sage: U.default_chart() + Chart (U, (z1, z2)) + + The default charts are not privileged charts on the manifold, but rather + charts whose name can be skipped in the argument list of functions having + an optional ``chart=`` argument. + + The chart map `\varphi` acting on a point is obtained by passing + it as an input to the map:: + + sage: p = M.point((1+i, 2), chart=X); p + Point on the Complex 2-dimensional topological manifold M + sage: X(p) + (I + 1, 2) + sage: X(p) == p.coord(X) + True + + .. SEEALSO:: + + :class:`sage.manifolds.chart.RealChart` for charts on topological + manifolds over `\RR`. + + """ + def __init__(self, domain, coordinates='', names=None): + r""" + Construct a chart. + + TESTS:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X + Chart (M, (x, y)) + sage: type(X) + + sage: assumptions() # no assumptions on x,y set by X._init_coordinates + [] + sage: TestSuite(X).run() + + """ + from sage.manifolds.manifold import TopologicalManifold + if not isinstance(domain, TopologicalManifold): + raise TypeError("the first argument must be an open subset of " + + "a topological manifold") + if coordinates == '': + for x in names: + coordinates += x + ' ' + coordinates = coordinates[:-1] + self._manifold = domain.manifold() + self._domain = domain + # Treatment of the coordinates: + if ' ' in coordinates: + coord_list = coordinates.split() + else: + coord_list = [coordinates] + if len(coord_list) != self._manifold.dim(): + raise ValueError("the list of coordinates must contain " + + "{} elements".format(self._manifold.dim())) + # The treatment of coordinates is performed by a seperate method, + # _init_coordinates, which sets self._xx and + # which may be redefined for subclasses (for instance RealChart). + self._init_coordinates(coord_list) + coord_string = ' '.join(str(x) for x in self._xx) + if coord_string in self._domain._charts_by_coord: + raise ValueError("the chart with coordinates " + coord_string + + " has already been declared on " + + "the {}".format(self._domain)) + self._domain._charts_by_coord[coord_string] = self + # + # Additional restrictions on the coordinates + self._restrictions = [] # to be set with method add_restrictions() + # + # The chart is added to the domain's atlas, as well as to all the + # atlases of the domain's supersets; moreover the fist defined chart + # is considered as the default chart + for sd in self._domain._supersets: + # the chart is added in the top charts only if its coordinates have + # not been used: + for chart in sd._atlas: + if self._xx == chart._xx: + break + else: + sd._top_charts.append(self) + sd._atlas.append(self) + if sd._def_chart is None: + sd._def_chart = self + # The chart is added to the list of the domain's covering charts: + self._domain._covering_charts.append(self) + # Initialization of the set of charts that are restrictions of the + # current chart to subsets of the chart domain: + self._subcharts = set([self]) + # Initialization of the set of charts which the current chart is a + # restriction of: + self._supercharts = set([self]) + # + self._dom_restrict = {} # dict. of the restrictions of self to + # subsets of self._domain, with the + # subsets as keys + + def _init_coordinates(self, coord_list): + r""" + Initialization of the coordinates as symbolic variables. + + This method must be redefined by derived classes in order to take + into account specificities (e.g. enforcing real coordinates). + + INPUT: + + - ``coord_list`` -- list of coordinate fields, which items in each + field separated by ":"; there are at most 2 items per field: + the coordinate name and the coordinate LaTeX symbol + + TESTS:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X._init_coordinates(['z1', 'z2']) + sage: X + Chart (M, (z1, z2)) + sage: X._init_coordinates([r'z1:\zeta_1', r'z2:\zeta_2']) + sage: X + Chart (M, (z1, z2)) + sage: latex(X) + \left(M,({\zeta_1}, {\zeta_2})\right) + + """ + xx_list = [] # will contain the coordinates as Sage symbolic variables + for coord_field in coord_list: + coord_properties = coord_field.split(':') + coord_symb = coord_properties[0].strip() # the coordinate symbol + # LaTeX symbol: + coord_latex = None + for prop in coord_properties[1:]: + coord_latex = prop.strip() + # Construction of the coordinate as some Sage's symbolic variable: + coord_var = SR.var(coord_symb, latex_name=coord_latex) + xx_list.append(coord_var) + self._xx = tuple(xx_list) + + def _repr_(self): + r""" + String representation of the object. + + TESTS:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X + Chart (M, (x, y)) + + """ + return 'Chart ({}, {})'.format(self._domain._name, self._xx) + + def _latex_(self): + r""" + LaTeX representation of the object. + + TESTS:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X._latex_() + '\\left(M,(x, y)\\right)' + sage: Y. = M.chart(r'z1:\zeta_1 z2:\zeta2') + sage: Y._latex_() + '\\left(M,({\\zeta_1}, {\\zeta2})\\right)' + sage: latex(Y) + \left(M,({\zeta_1}, {\zeta2})\right) + + """ + description = r'\left(' + latex(self._domain).strip() + ',(' + n = len(self._xx) + for i in range(n-1): + description += latex(self._xx[i]).strip() + ', ' + description += latex(self._xx[n-1]).strip() + r')\right)' + return description + + def _first_ngens(self, n): + r""" + Return the list of coordinates. + + This is useful only for the use of Sage preparser:: + + sage: preparse("c_cart. = M.chart()") + "c_cart = M.chart(names=('x', 'y', 'z',)); (x, y, z,) = c_cart._first_ngens(3)" + + """ + return self[:] + + def __getitem__(self, i): + r""" + Access to the coordinates. + + INPUT: + + - ``i`` -- index of the coordinate; if the slice ``[:]``, then all + the coordinates are returned + + OUTPUT: + + - the coordinate of index ``i`` or all the coordinates (as a tuple) + if ``i`` is the slice ``[:]`` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X[0] + x + sage: X[1] + y + sage: X[:] + (x, y) + + The index range is controlled by the parameter ``start_index``:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological', + ....: start_index=1) + sage: X. = M.chart() + sage: X[1] + x + sage: X[2] + y + sage: X[:] + (x, y) + + We check that slices are properly shifted as well:: + + sage: X[2:] + (y,) + sage: X[:2] + (x,) + """ + if isinstance(i, slice): + start,stop = i.start,i.stop + if start is not None: + start -= self._manifold._sindex + if stop is not None: + stop -= self._manifold._sindex + return self._xx[start:stop:i.step] + else: + return self._xx[i-self._manifold._sindex] + + def __call__(self, point): + r""" + Return the coordinates of a given point. + + INPUT: + + - ``point`` -- point in the domain of the chart + + OUTPUT: + + - tuple of the coordinates of the point + + EXAMPLES:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: p = M.point((1+i, 2-i), chart=X) + sage: X(p) + (I + 1, -I + 2) + sage: X(M.an_element()) + (0, 0) + + """ + return point.coord(self) + + def domain(self): + r""" + Return the open subset on which the chart is defined. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: X.domain() + 2-dimensional topological manifold M + sage: U = M.open_subset('U') + sage: Y. = U.chart() + sage: Y.domain() + Open subset U of the 2-dimensional topological manifold M + + """ + return self._domain + + def manifold(self): + r""" + Return the manifold on which the chart is defined. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: X. = U.chart() + sage: X.manifold() + 2-dimensional topological manifold M + sage: X.domain() + Open subset U of the 2-dimensional topological manifold M + + """ + return self._manifold + + def add_restrictions(self, restrictions): + r""" + Add some restrictions on the coordinates. + + INPUT: + + - ``restrictions`` -- list of restrictions on the + coordinates, in addition to the ranges declared by the intervals + specified in the chart constructor + + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list ``restrictions`` are combined with the ``and`` operator; + if some restrictions are to be combined with the ``or`` operator + instead, they have to be passed as a tuple in some single item + of the list ``restrictions``. For example:: + + restrictions = [x > y, (x != 0, y != 0), z^2 < x] + + means (``x > y``) and ((``x != 0``) or (``y != 0``)) and + (``z^2 < x``). If the list ``restrictions`` contains only one + item, this item can be passed as such, i.e. writing ``x > y`` + instead of the single element list ``[x > y]``. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X.add_restrictions(abs(x) > 1) + sage: X.valid_coordinates(2+i, 1) + True + sage: X.valid_coordinates(i, 1) + False + + """ + if not isinstance(restrictions, list): + # case of a single condition or conditions to be combined by "or" + restrictions = [restrictions] + self._restrictions.extend(restrictions) + + def restrict(self, subset, restrictions=None): + r""" + Return the restriction of the chart to some open subset of its domain. + + If the current chart is `(U,\varphi)`, a *restriction* (or *subchart*) + is a chart `(V,\psi)` such that `V\subset U` and `\psi = \varphi |_V`. + + If such subchart has not been defined yet, it is constructed here. + + The coordinates of the subchart bare the same names as the coordinates + of the current chart. + + INPUT: + + - ``subset`` -- open subset `V` of the chart domain `U` (must be an + instance of :class:`~sage.manifolds.manifold.TopologicalManifold`) + - ``restrictions`` -- (default: ``None``) list of coordinate + restrictions defining the subset `V` + + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list ``restrictions`` are combined with the ``and`` operator; + if some restrictions are to be combined with the ``or`` operator + instead, they have to be passed as a tuple in some single item + of the list ``restrictions``. For example:: + + restrictions = [x > y, (x != 0, y != 0), z^2 < x] + + means (``x > y``) and ((``x != 0``) or (``y != 0``)) and + (``z^2 < x``). If the list ``restrictions`` contains only one + item, this item can be passed as such, i.e. writing ``x > y`` + instead of the single element list ``[x > y]``. + + OUTPUT: + + - chart `(V,\psi)`, as an instance of :class:`Chart`. + + EXAMPLES: + + Coordinates on the unit open ball of `\CC^2` as a subchart + of the global coordinates of `\CC^2`:: + + sage: M = Manifold(2, 'C^2', field='complex', structure='topological') + sage: X. = M.chart() + sage: B = M.open_subset('B') + sage: X_B = X.restrict(B, abs(z1)^2 + abs(z2)^2 < 1); X_B + Chart (B, (z1, z2)) + + """ + if subset == self._domain: + return self + if subset not in self._dom_restrict: + if not subset.is_subset(self._domain): + raise ValueError("the specified subset is not a subset " + + "of the domain of definition of the chart") + coordinates = "" + for coord in self._xx: + coordinates += repr(coord) + ' ' + res = type(self)(subset, coordinates) + res._restrictions.extend(self._restrictions) + # The coordinate restrictions are added to the result chart and + # possibly transformed into coordinate bounds: + if restrictions is not None: + res.add_restrictions(restrictions) + # Update of supercharts and subcharts: + res._supercharts.update(self._supercharts) + for schart in self._supercharts: + schart._subcharts.add(res) + schart._dom_restrict[subset] = res + # Update of domain restrictions: + self._dom_restrict[subset] = res + return self._dom_restrict[subset] + + def valid_coordinates(self, *coordinates, **kwds): + r""" + Check whether a tuple of coordinates can be the coordinates of a + point in the chart domain. + + INPUT: + + - ``*coordinates`` -- coordinate values + - ``**kwds`` -- options: + + - ``parameters=None``, dictionary to set numerical values to + some parameters (see example below) + + OUTPUT: + + - ``True`` if the coordinate values are admissible in the chart + image, ``False`` otherwise + + EXAMPLES:: + + sage: M = Manifold(2, 'M', field='complex', structure='topological') + sage: X. = M.chart() + sage: X.add_restrictions([abs(x)<1, y!=0]) + sage: X.valid_coordinates(0, i) + True + sage: X.valid_coordinates(i, 1) + False + sage: X.valid_coordinates(i/2, 1) + True + sage: X.valid_coordinates(i/2, 0) + False + sage: X.valid_coordinates(2, 0) + False + + Example of use with the keyword ``parameters`` to set a specific value + to a parameter appearing in the coordinate restrictions:: + + sage: var('a') # the parameter is a symbolic variable + a + sage: Y. = M.chart() + sage: Y.add_restrictions(abs(v) y`` or ``x^2 + y^2 != 0``. The items + of the list ``restrictions`` are combined with the ``and`` operator; + if some restrictions are to be combined with the ``or`` operator + instead, they have to be passed as a tuple in some single item + of the list ``restrictions``. For example:: + + restrictions = [x > y, (x != 0, y != 0), z^2 < x] + + means (``x > y``) and ((``x != 0``) or (``y != 0``)) and + (``z^2 < x``). If the list ``restrictions`` contains only one + item, this item can be passed as such, i.e. writing ``x > y`` + instead of the single element list ``[x > y]``. + + OUTPUT: + + - the transition map `\psi \circ \varphi^{-1}` defined on + `U \cap V`, as an instance of :class:`CoordChange` + + EXAMPLES: + + Transition map between two stereographic charts on the circle `S^1`:: + + sage: M = Manifold(1, 'S^1', structure='topological') + sage: U = M.open_subset('U') # Complement of the North pole + sage: cU. = U.chart() # Stereographic chart from the North pole + sage: V = M.open_subset('V') # Complement of the South pole + sage: cV. = V.chart() # Stereographic chart from the South pole + sage: M.declare_union(U,V) # S^1 is the union of U and V + sage: trans = cU.transition_map(cV, 1/x, intersection_name='W', + ....: restrictions1= x!=0, restrictions2 = y!=0) + sage: trans + Change of coordinates from Chart (W, (x,)) to Chart (W, (y,)) + sage: trans.display() + y = 1/x + + The subset `W`, intersection of `U` and `V`, has been created by + ``transition_map()``:: + + sage: M.list_of_subsets() + [1-dimensional topological manifold S^1, + Open subset U of the 1-dimensional topological manifold S^1, + Open subset V of the 1-dimensional topological manifold S^1, + Open subset W of the 1-dimensional topological manifold S^1] + sage: W = M.list_of_subsets()[3] + sage: W is U.intersection(V) + True + sage: M.atlas() + [Chart (U, (x,)), Chart (V, (y,)), Chart (W, (x,)), Chart (W, (y,))] + + Transition map between the spherical chart and the Cartesian + one on `\RR^2`:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() + sage: U = M.open_subset('U') # the complement of the half line {y=0, x >= 0} + sage: c_spher. = U.chart(r'r:(0,+oo) phi:(0,2*pi):\phi') + sage: trans = c_spher.transition_map(c_cart, (r*cos(phi), r*sin(phi)), + ....: restrictions2=(y!=0, x<0)) + sage: trans + Change of coordinates from Chart (U, (r, phi)) to Chart (U, (x, y)) + sage: trans.display() + x = r*cos(phi) + y = r*sin(phi) + + In this case, no new subset has been created since `U \cap M = U`:: + + sage: M.list_of_subsets() + [2-dimensional topological manifold R^2, + Open subset U of the 2-dimensional topological manifold R^2] + + but a new chart has been created: `(U, (x, y))`:: + + sage: M.atlas() + [Chart (R^2, (x, y)), Chart (U, (r, phi)), Chart (U, (x, y))] + + """ + dom1 = self._domain + dom2 = other._domain + dom = dom1.intersection(dom2, name=intersection_name) + if dom is dom1: + chart1 = self + else: + chart1 = self.restrict(dom, restrictions1) + if dom is dom2: + chart2 = other + else: + chart2 = other.restrict(dom, restrictions2) + if not isinstance(transformations, (tuple, list)): + transformations = [transformations] + return CoordChange(chart1, chart2, *transformations) + +#***************************************************************************** + +class RealChart(Chart): + r""" + Chart on a topological manifold over `\RR`. + + Given a topological manifold `M` of dimension `n` over `\RR`, a *chart* + on `M` is a pair `(U,\varphi)`, where `U` is an open subset of `M` and + `\varphi : U \to V \subset \RR^n` is a homeomorphism from `U` to + an open subset `V` of `\RR^n`. + + The components `(x^1, \ldots, x^n)` of `\varphi`, defined by + `\varphi(p) = (x^1(p), \ldots, x^n(p))\in \RR^n` for any point + `p \in U`, are called the *coordinates* of the chart `(U, \varphi)`. + + INPUT: + + - ``domain`` -- open subset `U` on which the chart is defined + - ``coordinates`` -- (default: ``''`` (empty string)) string defining + the coordinate symbols and ranges, see below + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used) + + The string ``coordinates`` has the space ``' '`` as a separator and each + item has at most three fields, separated by a colon (``:``): + + 1. The coordinate symbol (a letter or a few letters). + 2. (optional) The interval `I` defining the coordinate range: if not + provided, the coordinate is assumed to span all `\RR`; otherwise + `I` must be provided in the form ``(a,b)`` (or equivalently + ``]a,b[``). The bounds ``a`` and ``b`` can be ``+/-Infinity``, + ``Inf``, ``infinity``, ``inf`` or ``oo``. + For *singular* coordinates, non-open intervals such as ``[a,b]`` and + ``(a,b]`` (or equivalently ``]a,b]``) are allowed. + Note that the interval declaration must not contain any whitespace. + 3. (optional) The LaTeX spelling of the coordinate; if not provided the + coordinate symbol given in the first field will be used. + + The order of the fields 2 and 3 does not matter and each of them can be + omitted. If it contains any LaTeX expression, the string ``coordinates`` + must be declared with the prefix 'r' (for "raw") to allow for a proper + treatment of LaTeX backslash characters (see examples below). If no + interval range and no LaTeX spelling is to be set for any coordinate, + the argument ``coordinates`` can be omitted when the shortcut + operator ``<,>`` is used via Sage preparser (see examples below). + + EXAMPLES: + + Cartesian coordinates on `\RR^3`:: + + sage: M = Manifold(3, 'R^3', r'\RR^3', structure='topological', + ....: start_index=1) + sage: c_cart = M.chart('x y z'); c_cart + Chart (R^3, (x, y, z)) + sage: type(c_cart) + + + To have the coordinates accessible as global variables, one has to set:: + + sage: (x,y,z) = c_cart[:] + + However, a shortcut is to use the declarator ```` in the left-hand + side of the chart declaration (there is then no need to pass the string + ``'x y z'`` to ``chart()``):: + + sage: M = Manifold(3, 'R^3', r'\RR^3', structure='topological', + ....: start_index=1) + sage: c_cart. = M.chart(); c_cart + Chart (R^3, (x, y, z)) + + The coordinates are then immediately accessible:: + + sage: y + y + sage: y is c_cart[2] + True + + Note that ``x, y, z`` declared in ```` are mere Python variable + names and do not have to coincide with the coordinate symbols; for + instance, one may write:: + + sage: M = Manifold(3, 'R^3', r'\RR^3', structure='topological', start_index=1) + sage: c_cart. = M.chart('x y z'); c_cart + Chart (R^3, (x, y, z)) + + Then ``y`` is not known as a global variable and the coordinate `y` + is accessible only through the global variable ``y1``:: + + sage: y1 + y + sage: y1 is c_cart[2] + True + + However, having the name of the Python variable coincide with the + coordinate symbol is quite convenient; so it is recommended to declare:: + + sage: forget() # for doctests only + sage: M = Manifold(3, 'R^3', r'\RR^3', structure='topological', start_index=1) + sage: c_cart. = M.chart() + + Spherical coordinates on the subset `U` of `\RR^3` that is the + complement of the half-plane `\{y=0, x \geq 0\}`:: + + sage: U = M.open_subset('U') + sage: c_spher. = U.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') + sage: c_spher + Chart (U, (r, th, ph)) + + Note the prefix 'r' for the string defining the coordinates in the + arguments of ``chart``. + + Coordinates are Sage symbolic variables (see + :mod:`sage.symbolic.expression`):: + + sage: type(th) + + sage: latex(th) + {\theta} + sage: assumptions(th) + [th is real, th > 0, th < pi] + + Coordinate are also accessible by their indices:: + + sage: x1 = c_spher[1]; x2 = c_spher[2]; x3 = c_spher[3] + sage: print x1, x2, x3 + r th ph + sage: (x1, x2, x3) == (r, th, ph) + True + + The full set of coordinates is obtained by means of the slice ``[:]``:: + + sage: c_cart[:] + (x, y, z) + sage: c_spher[:] + (r, th, ph) + + Let us check that the declared coordinate ranges have been taken into + account:: + + sage: c_cart.coord_range() + x: (-oo, +oo); y: (-oo, +oo); z: (-oo, +oo) + sage: c_spher.coord_range() + r: (0, +oo); th: (0, pi); ph: (0, 2*pi) + sage: bool(th>0 and th 0, th is real, + th > 0, th < pi, ph is real, ph > 0, ph < 2*pi] + + The coordinate ranges are used for simplifications:: + + sage: simplify(abs(r)) # r has been declared to lie in the interval (0,+oo) + r + sage: simplify(abs(x)) # no positive range has been declared for x + abs(x) + + Each constructed chart is automatically added to the manifold's + user atlas:: + + sage: M.atlas() + [Chart (R^3, (x, y, z)), Chart (U, (r, th, ph))] + + and to the atlas of its domain:: + + sage: U.atlas() + [Chart (U, (r, th, ph))] + + Manifold subsets have a *default chart*, which, unless changed + via the method + :meth:`~sage.manifolds.manifold.TopologicalManifold.set_default_chart`, + is the first defined chart on the subset (or on a open subset of it):: + + sage: M.default_chart() + Chart (R^3, (x, y, z)) + sage: U.default_chart() + Chart (U, (r, th, ph)) + + The default charts are not privileged charts on the manifold, but rather + charts whose name can be skipped in the argument list of functions having + an optional ``chart=`` argument. + + The chart map `\varphi` acting on a point is obtained by means of the + call operator, i.e. the operator ``()``:: + + sage: p = M.point((1,0,-2)); p + Point on the 3-dimensional topological manifold R^3 + sage: c_cart(p) + (1, 0, -2) + sage: c_cart(p) == p.coord(c_cart) + True + sage: q = M.point((2,pi/2,pi/3), chart=c_spher) # point defined by its spherical coordinates + sage: c_spher(q) + (2, 1/2*pi, 1/3*pi) + sage: c_spher(q) == q.coord(c_spher) + True + sage: a = U.point((1,pi/2,pi)) # the default coordinates on U are the spherical ones + sage: c_spher(a) + (1, 1/2*pi, pi) + sage: c_spher(a) == a.coord(c_spher) + True + + Cartesian coordinates on `U` as an example of chart construction with + coordinate restrictions: since `U` is the complement of the half-plane + `\{y = 0, x \geq 0\}`, we must have `y \neq 0` or `x < 0` on U. + Accordingly, we set:: + + sage: c_cartU. = U.chart() + sage: c_cartU.add_restrictions((y!=0, x<0)) + sage: U.atlas() + [Chart (U, (r, th, ph)), Chart (U, (x, y, z))] + sage: M.atlas() + [Chart (R^3, (x, y, z)), Chart (U, (r, th, ph)), Chart (U, (x, y, z))] + sage: c_cartU.valid_coordinates(-1,0,2) + True + sage: c_cartU.valid_coordinates(1,0,2) + False + sage: c_cart.valid_coordinates(1,0,2) + True + + Note that, as an example, the following would have meant `y \neq 0` + *and* `x < 0`:: + + c_cartU.add_restrictions([y!=0, x<0]) + """ + def __init__(self, domain, coordinates='', names=None): + r""" + Construct a chart on a real topological manifold. + + TESTS:: + + sage: forget() # for doctests only + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: X + Chart (M, (x, y)) + sage: type(X) + + sage: assumptions() # assumptions set in X._init_coordinates + [x is real, y is real] + sage: TestSuite(X).run() + + """ + Chart.__init__(self, domain, coordinates=coordinates, names=names) + + def _init_coordinates(self, coord_list): + r""" + Initialization of the coordinates as symbolic variables. + + This method must be redefined by derived classes in order to take + into account specificities (e.g. enforcing real coordinates). + + INPUT: + + - ``coord_list`` -- list of coordinate fields, which items in each + field separated by ":"; there are at most 3 items per field: + the coordinate name, the coordinate LaTeX symbol and the + coordinate range + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: X._init_coordinates(['x', 'y']) + sage: X + Chart (M, (x, y)) + sage: latex(X) + \left(M,(x, y)\right) + sage: X.coord_range() + x: (-oo, +oo); y: (-oo, +oo) + sage: X._init_coordinates([r'x1:\xi:(0,1)', r'y1:\eta']) + sage: X + Chart (M, (x1, y1)) + sage: latex(X) + \left(M,({\xi}, {\eta})\right) + sage: X.coord_range() + x1: (0, 1); y1: (-oo, +oo) + + """ + from sage.symbolic.assumptions import assume + xx_list = [] # will contain the coordinates as Sage symbolic variables + bounds_list = [] # will contain the coordinate bounds + for coord_field in coord_list: + coord_properties = coord_field.split(':') + coord_symb = coord_properties[0].strip() # the coordinate symbol + # default values, possibly redefined below: + coord_latex = None + xmin = -Infinity; xmin_included = False + xmax = +Infinity; xmax_included = False + # scan of the properties other than the symbol: + for prop in coord_properties[1:]: + prop1 = prop.strip() + delim_min = prop1[0] + if delim_min in ['[', ']', '(']: + # prop1 is the coordinate's range + xmin_str, xmax_str = prop1[1:len(prop1)-1].split(',') + if xmin_str not in ['-inf', '-Inf', '-infinity', + '-Infinity', '-oo']: + xmin = SR(xmin_str) + xmin_included = ( delim_min == '[' ) + if xmax_str not in ['inf', '+inf', 'Inf', '+Inf', + 'infinity', '+infinity', 'Infinity', + '+Infinity', 'oo', '+oo']: + xmax = SR(xmax_str) + xmax_included = ( prop1[-1] == ']' ) + else: + # prop1 is the coordinate's LaTeX symbol + coord_latex = prop1 + # Construction of the coordinate as some Sage's symbolic variable: + coord_var = SR.var(coord_symb, domain='real', + latex_name=coord_latex) + assume(coord_var, 'real') + if xmin != -Infinity: + if xmin_included: + assume(coord_var >= xmin) + else: + assume(coord_var > xmin) + if xmax != Infinity: + if xmax_included: + assume(coord_var <= xmax) + else: + assume(coord_var < xmax) + xx_list.append(coord_var) + bounds_list.append(((xmin, xmin_included), (xmax, xmax_included))) + self._xx = tuple(xx_list) + self._bounds = tuple(bounds_list) + + def coord_bounds(self, i=None): + r""" + Return the lower and upper bounds of the range of a coordinate. + + For a nicely formatted output, use :meth:`coord_range` instead. + + INPUT: + + - ``i`` -- (default: ``None``) index of the coordinate; if ``None``, + the bounds of all the coordinates are returned + + OUTPUT: + + - the coordinate bounds as the tuple + ``((xmin, min_included), (xmax, max_included))`` where + + - ``xmin`` is the coordinate lower bound + - ``min_included`` is a boolean, indicating whether the coordinate + can take the value ``xmin``, i.e. ``xmin`` is a strict lower + bound iff ``min_included`` is ``False`` + - ``xmin`` is the coordinate upper bound + - ``max_included`` is a boolean, indicating whether the coordinate + can take the value ``xmax``, i.e. ``xmax`` is a strict upper + bound iff ``max_included`` is ``False`` + + EXAMPLES: + + Some coordinate bounds on a 2-dimensional manifold:: + + sage: forget() # for doctests only + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart('x y:[0,1)') + sage: c_xy.coord_bounds(0) # x in (-oo,+oo) (the default) + ((-Infinity, False), (+Infinity, False)) + sage: c_xy.coord_bounds(1) # y in [0,1) + ((0, True), (1, False)) + sage: c_xy.coord_bounds() + (((-Infinity, False), (+Infinity, False)), ((0, True), (1, False))) + sage: c_xy.coord_bounds() == (c_xy.coord_bounds(0), c_xy.coord_bounds(1)) + True + + The coordinate bounds can also be recovered via the method + :meth:`coord_range`:: + + sage: c_xy.coord_range() + x: (-oo, +oo); y: [0, 1) + sage: c_xy.coord_range(y) + y: [0, 1) + + or via Sage's function + :func:`sage.symbolic.assumptions.assumptions`:: + + sage: assumptions(x) + [x is real] + sage: assumptions(y) + [y is real, y >= 0, y < 1] + + """ + if i is None: + return self._bounds + else: + return self._bounds[i-self._manifold._sindex] + + def coord_range(self, xx=None): + r""" + Display the range of a coordinate (or all coordinates), as an + interval. + + INPUT: + + - ``xx`` -- (default: ``None``) symbolic expression corresponding + to a coordinate of the current chart; if ``None``, the ranges of + all coordinates are displayed + + EXAMPLES: + + Ranges of coordinates on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: X.coord_range() + x: (-oo, +oo); y: (-oo, +oo) + sage: X.coord_range(x) + x: (-oo, +oo) + sage: U = M.open_subset('U', coord_def={X: [x>1, y y`` or ``x^2 + y^2 != 0``. The items + of the list ``restrictions`` are combined with the ``and`` operator; + if some restrictions are to be combined with the ``or`` operator + instead, they have to be passed as a tuple in some single item + of the list ``restrictions``. For example:: + + restrictions = [x > y, (x != 0, y != 0), z^2 < x] + + means (``x > y``) and ((``x != 0``) or (``y != 0``)) and + (``z^2 < x``). If the list ``restrictions`` contains only one + item, this item can be passed as such, i.e. writing ``x > y`` + instead of the single element list ``[x > y]``. + + EXAMPLES: + + Cartesian coordinates on the open unit disc in `\RR^2`:: + + sage: M = Manifold(2, 'M', structure='topological') # the open unit disc + sage: X. = M.chart() + sage: X.add_restrictions(x^2+y^2<1) + sage: X.valid_coordinates(0,2) + False + sage: X.valid_coordinates(0,1/3) + True + + The restrictions are transmitted to subcharts:: + + sage: A = M.open_subset('A') # annulus 1/2 < r < 1 + sage: X_A = X.restrict(A, x^2+y^2 > 1/4) + sage: X_A._restrictions + [x^2 + y^2 < 1, x^2 + y^2 > (1/4)] + sage: X_A.valid_coordinates(0,1/3) + False + sage: X_A.valid_coordinates(2/3,1/3) + True + + If appropriate, the restrictions are transformed into bounds on + the coordinate ranges:: + + sage: U = M.open_subset('U') + sage: X_U = X.restrict(U) + sage: X_U.coord_range() + x: (-oo, +oo); y: (-oo, +oo) + sage: X_U.add_restrictions([x<0, y>1/2]) + sage: X_U.coord_range() + x: (-oo, 0); y: (1/2, +oo) + + """ + import operator + if not isinstance(restrictions, list): + # case of a single condition or conditions to be combined by "or" + restrictions = [restrictions] + self._restrictions.extend(restrictions) + # Update of the coordinate bounds from the restrictions: + bounds = list(self._bounds) # convert to a list for modifications + new_restrictions = [] + for restrict in self._restrictions: + restrict_used = False # determines whether restrict is used + # to set some coordinate bound + if not isinstance(restrict, tuple): # case of 'or' conditions + # excluded + operands = restrict.operands() + left = operands[0] + right = operands[1] + right_var = right.variables() + if left in self._xx: + # the l.h.s. of the restriction is a single + # coordinate + right_coord = [coord for coord in self._xx + if coord in right_var] + if not right_coord: + # there is no other coordinate in the r.h.s. + ind = self._xx.index(left) + left_bounds = list(bounds[ind]) + oper = restrict.operator() + oinf = left_bounds[0][0] # old coord inf + osup = left_bounds[1][0] # old coord sup + if oper == operator.lt: + if osup == Infinity or right <= osup: + left_bounds[1] = (right, False) + restrict_used = True + elif oper == operator.le: + if osup == Infinity or right < osup: + left_bounds[1] = (right, True) + restrict_used = True + elif oper == operator.gt: + if oinf == -Infinity or right >= oinf: + left_bounds[0] = (right, False) + restrict_used = True + elif oper == operator.ge: + if oinf == -Infinity or right > oinf: + left_bounds[0] = (right, True) + restrict_used = True + bounds[ind] = tuple(left_bounds) + if not restrict_used: + # if restrict has not been used to set a coordinate bound + # it is maintained in the list of restrictions: + new_restrictions.append(restrict) + self._bounds = tuple(bounds) + self._restrictions = new_restrictions + + def restrict(self, subset, restrictions=None): + r""" + Return the restriction of the chart to some open subset of its domain. + + If the current chart is `(U,\varphi)`, a *restriction* (or *subchart*) + is a chart `(V,\psi)` such that `V\subset U` and `\psi = \varphi |_V`. + + If such subchart has not been defined yet, it is constructed here. + + The coordinates of the subchart bare the same names as the coordinates + of the current chart. + + INPUT: + + - ``subset`` -- open subset `V` of the chart domain `U` (must be an + instance of :class:`~sage.manifolds.manifold.TopologicalManifold`) + - ``restrictions`` -- (default: ``None``) list of coordinate + restrictions defining the subset `V` + + A restriction can be any symbolic equality or inequality involving + the coordinates, such as ``x > y`` or ``x^2 + y^2 != 0``. The items + of the list ``restrictions`` are combined with the ``and`` operator; + if some restrictions are to be combined with the ``or`` operator + instead, they have to be passed as a tuple in some single item + of the list ``restrictions``. For example:: + + restrictions = [x > y, (x != 0, y != 0), z^2 < x] + + means (``x > y``) and ((``x != 0``) or (``y != 0``)) and + (``z^2 < x``). If the list ``restrictions`` contains only one + item, this item can be passed as such, i.e. writing ``x > y`` + instead of the single element list ``[x > y]``. + + OUTPUT: + + - chart `(V,\psi)`, as an instance of :class:`RealChart`. + + EXAMPLES: + + Cartesian coordinates on the unit open disc in `\RR^2` as a subchart + of the global Cartesian coordinates:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: D = M.open_subset('D') # the unit open disc + sage: c_cart_D = c_cart.restrict(D, x^2+y^2<1) + sage: p = M.point((1/2, 0)) + sage: p in D + True + sage: q = M.point((1, 2)) + sage: q in D + False + + Cartesian coordinates on the annulus `1 < \sqrt{x^2+y^2} < 2`:: + + sage: A = M.open_subset('A') + sage: c_cart_A = c_cart.restrict(A, [x^2+y^2>1, x^2+y^2<4]) + sage: p in A, q in A + (False, False) + sage: a = M.point((3/2,0)) + sage: a in A + True + + """ + if subset == self._domain: + return self + if subset not in self._dom_restrict: + if not subset.is_subset(self._domain): + raise ValueError("the specified subset is not a subset " + + "of the domain of definition of the chart") + coordinates = "" + for coord in self._xx: + coordinates += repr(coord) + ' ' + res = type(self)(subset, coordinates) + res._bounds = self._bounds + res._restrictions.extend(self._restrictions) + # The coordinate restrictions are added to the result chart and + # possibly transformed into coordinate bounds: + if restrictions is not None: + res.add_restrictions(restrictions) + # Update of supercharts and subcharts: + res._supercharts.update(self._supercharts) + for schart in self._supercharts: + schart._subcharts.add(res) + schart._dom_restrict[subset] = res + # Update of domain restrictions: + self._dom_restrict[subset] = res + return self._dom_restrict[subset] + + def valid_coordinates(self, *coordinates, **kwds): + r""" + Check whether a tuple of coordinates can be the coordinates of a + point in the chart domain. + + INPUT: + + - ``*coordinates`` -- coordinate values + - ``**kwds`` -- options: + + - ``tolerance=0``, to set the absolute tolerance in the test of + coordinate ranges + - ``parameters=None``, to set some numerical values to parameters + + + OUTPUT: + + - ``True`` if the coordinate values are admissible in the chart range + and ``False`` otherwise + + EXAMPLES: + + Cartesian coordinates on a square interior:: + + sage: forget() # for doctest only + sage: M = Manifold(2, 'M', structure='topological') # the square interior + sage: X. = M.chart('x:(-2,2) y:(-2,2)') + sage: X.valid_coordinates(0,1) + True + sage: X.valid_coordinates(-3/2,5/4) + True + sage: X.valid_coordinates(0,3) + False + + The unit open disk inside the square:: + + sage: D = M.open_subset('D', coord_def={X: x^2+y^2<1}) + sage: XD = X.restrict(D) + sage: XD.valid_coordinates(0,1) + False + sage: XD.valid_coordinates(-3/2,5/4) + False + sage: XD.valid_coordinates(-1/2,1/2) + True + sage: XD.valid_coordinates(0,0) + True + + """ + n = len(coordinates) + if n != self._manifold._dim: + return False + if 'tolerance' in kwds: + tolerance = kwds['tolerance'] + else: + tolerance = 0 + if 'parameters' in kwds: + parameters = kwds['parameters'] + else: + parameters = None + # Check of the coordinate ranges: + for x, bounds in zip(coordinates, self._bounds): + xmin = bounds[0][0] - tolerance + min_included = bounds[0][1] + xmax = bounds[1][0] + tolerance + max_included = bounds[1][1] + if parameters: + xmin = xmin.subs(parameters) + xmax = xmax.subs(parameters) + if min_included: + if x < xmin: + return False + else: + if x <= xmin: + return False + if max_included: + if x > xmax: + return False + else: + if x >= xmax: + return False + # Check of additional restrictions: + if self._restrictions != []: + substitutions = dict(zip(self._xx, coordinates)) + if parameters: + substitutions.update(parameters) + for restrict in self._restrictions: + if isinstance(restrict, tuple): # case of or conditions + combine = False + for expr in restrict: + combine = combine or bool(expr.subs(substitutions)) + if not combine: + return False + else: + if not bool(restrict.subs(substitutions)): + return False + # All tests have been passed: + return True + + +#***************************************************************************** + +class CoordChange(SageObject): + r""" + Transition map between two charts of a topological manifold. + + Giving two coordinate charts `(U, \varphi)` and `(V, \psi)` on a + topological manifold `M` of dimension `n` over a topological field `K`, + the *transition map from* `(U, \varphi)` *to* `(V, \psi)` is the map + + .. MATH:: + + \psi\circ\varphi^{-1}: \varphi(U\cap V) \subset K^n + \rightarrow \psi(U\cap V) \subset K^n. + + In other words, the transition map `\psi \circ \varphi^{-1}` expresses + the coordinates `(y^1, \ldots, y^n)` of `(V, \psi)` in terms of the + coordinates `(x^1, \ldots, x^n)` of `(U, \varphi)` on the open subset + where the two charts intersect, i.e. on `U \cap V`. + + INPUT: + + - ``chart1`` -- chart `(U, \varphi)` + - ``chart2`` -- chart `(V, \psi)` + - ``transformations`` -- tuple (or list) `(Y_1, \ldots, Y_2)`, where + `Y_i` is the symbolic expression of the coordinate `y^i` in terms + of the coordinates `(x^1, \ldots, x^n)` + + EXAMPLES: + + Transition map on a 2-dimensional topological manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + sage: type(X_to_Y) + + sage: X_to_Y.display() + u = x + y + v = x - y + + """ + def __init__(self, chart1, chart2, *transformations): + r""" + Construct a transition map. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + sage: type(X_to_Y) + + sage: TestSuite(X_to_Y).run() + + """ + self._n1 = len(chart1._xx) + self._n2 = len(chart2._xx) + if len(transformations) != self._n2: + raise ValueError("{} coordinate transformations ".format(self._n2) + + "must be provided") + self._chart1 = chart1 + self._chart2 = chart2 + #*# when MultiCoordFunction will be implemented (trac #18640): + # self._transf = chart1.multifunction(*transformations) + #*# for now: + self._transf = transformations + self._inverse = None + # If the two charts are on the same open subset, the coordinate change + # is added to the subset (and supersets) dictionary: + if chart1._domain == chart2._domain: + domain = chart1._domain + for sdom in domain._supersets: + sdom._coord_changes[(chart1, chart2)] = self + + def _repr_(self): + r""" + String representation of the transition map. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y._repr_() + 'Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))' + sage: repr(X_to_Y) # indirect doctest + 'Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))' + sage: X_to_Y # indirect doctest + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + + """ + return "Change of coordinates from {} to {}".format(self._chart1, + self._chart2) + + def _latex_(self): + r""" + LaTeX representation of the transition map. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y._latex_() + \left(M,(x, y)\right) \rightarrow \left(M,(u, v)\right) + sage: latex(X_to_Y) # indirect doctest + \left(M,(x, y)\right) \rightarrow \left(M,(u, v)\right) + + """ + return latex(self._chart1) + r' \rightarrow ' + latex(self._chart2) + + def __eq__(self, other): + r""" + Equality operator. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y == X_to_Y + True + sage: X_to_Y1 = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y == X_to_Y1 + True + sage: X_to_Y2 = X.transition_map(Y, [2*y, -x]) + sage: X_to_Y == X_to_Y2 + False + sage: Z. = M.chart() + sage: X_to_Z = X.transition_map(Z, [x+y, x-y]) + sage: X_to_Y == X_to_Z + False + + """ + if other is self: + return True + if not isinstance(other, CoordChange): + return False + return ((self._chart1 == other._chart1) + and (self._chart2 == other._chart2) + and (self._transf == other._transf)) + + def __ne__(self, other): + r""" + Unequality operator. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y2 = X.transition_map(Y, [2*y, -x]) + sage: X_to_Y != X_to_Y2 + True + + """ + return not (self == other) + + def __call__(self, *coords): + r""" + Compute the new coordinates from old ones. + + INPUT: + + - ``coords`` -- values of coordinates of ``chart1`` + + OUTPUT: + + - tuple of values of coordinates of ``chart2`` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: X_to_Y(1,2) + (3, -1) + + """ + #*# When MultiCoordFunction is implemented (trac #18640): + # return self._transf(*coords) + #*# for now: + substitutions = {self._chart1._xx[j]: coords[j] for j in range(self._n1)} + return tuple([self._transf[i].subs(substitutions).simplify_full() + for i in range(self._n2)]) + + def inverse(self): + r""" + Compute the inverse coordinate transformation. + + OUTPUT: + + - an instance of :class:`CoordChange` representing the inverse of + the current coordinate transformation + + EXAMPLES: + + Inverse of a coordinate transformation corresponding to a + `\pi/3`-rotation in the plane:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: c_uv. = M.chart() + sage: xy_to_uv = c_xy.transition_map(c_uv, ((x - sqrt(3)*y)/2, (sqrt(3)*x + y)/2)) + sage: M.coord_changes() + {(Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + sage: uv_to_xy = xy_to_uv.inverse(); uv_to_xy + Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y)) + sage: uv_to_xy.display() + x = 1/2*sqrt(3)*v + 1/2*u + y = -1/2*sqrt(3)*u + 1/2*v + sage: M.coord_changes() # random (dictionary output) + {(Chart (M, (u, v)), + Chart (M, (x, y))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y)), + (Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + + """ + from sage.symbolic.relation import solve + if self._inverse is not None: + return self._inverse + # The computation is necessary: + x1 = self._chart1._xx # list of coordinates in chart1 + x2 = self._chart2._xx # list of coordinates in chart2 + n1 = self._n1 + n2 = self._n2 + if n1 != n2: + raise ValueError("the change of coordinates is not invertible " + + "(different number of coordinates in the two " + + "charts)") + # New symbolic variables (different from x2 to allow for a + # correct solution even when chart2 = chart1): + base_field = self._chart1.domain().base_field_type() + if base_field == 'real': + coord_domain = ['real' for i in range(n2)] + elif base_field == 'complex': + coord_domain = ['complex' for i in range(n2)] + else: + coord_domain = [None for i in range(n2)] + for i in range(n2): + if x2[i].is_positive(): + coord_domain[i] = 'positive' + xp2 = [ SR.var('xxxx' + str(i), domain=coord_domain[i]) + for i in range(n2) ] + #*# when MultiCoordFunction will be implemented (trac #18640): + # xx2 = self._transf.expr() + #*# for now: + xx2 = self._transf + equations = [xp2[i] == xx2[i] for i in range(n2)] + try: + solutions = solve(equations, *x1, solution_dict=True) + except RuntimeError: + raise RuntimeError("the system could not be solved; use " + + "set_inverse() to set the inverse manually") + substitutions = dict(zip(xp2, x2)) + if len(solutions) == 1: + x2_to_x1 = [solutions[0][x1[i]].subs(substitutions) + for i in range(n1)] + else: + list_x2_to_x1 = [] + for sol in solutions: + if x2[0] in sol: + raise ValueError("the system could not be solved; use " + + "set_inverse() to set the inverse " + + "manually") + x2_to_x1 = [sol[x1[i]].subs(substitutions) for i in range(n1)] + if self._chart1.valid_coordinates(*x2_to_x1): + list_x2_to_x1.append(x2_to_x1) + if len(list_x2_to_x1) == 0: + raise ValueError("no solution found; use set_inverse() to " + + "set the inverse manually") + if len(list_x2_to_x1) > 1: + print "Multiple solutions found: " + print list_x2_to_x1 + raise ValueError( + "non-unique solution to the inverse coordinate " + + "transformation; use set_inverse() to set the inverse " + + "manually") + x2_to_x1 = list_x2_to_x1[0] + self._inverse = type(self)(self._chart2, self._chart1, *x2_to_x1) + return self._inverse + + def set_inverse(self, *transformations, **kwds): + r""" + Sets the inverse of the coordinate transformation. + + This is useful when the automatic computation via :meth:`inverse()` + fails. + + INPUT: + + - ``transformations`` -- the inverse transformations expressed as a + list of the expressions of the "old" coordinates in terms of the + "new" ones + - ``kwds`` -- keyword arguments: only ``verbose=True`` or + ``verbose=False`` (default) are meaningful; it determines whether + the provided transformations are checked to be indeed the inverse + coordinate transformations + + EXAMPLES: + + From spherical coordinates to Cartesian ones in the plane:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: U = M.open_subset('U') # the complement of the half line {y=0, x>= 0} + sage: c_cart. = U.chart() + sage: c_spher. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') + sage: spher_to_cart = c_spher.transition_map(c_cart, [r*cos(ph), r*sin(ph)]) + sage: spher_to_cart.set_inverse(sqrt(x^2+y^2), atan2(y,x)) + sage: spher_to_cart.inverse() + Change of coordinates from Chart (U, (x, y)) to Chart (U, (r, ph)) + sage: spher_to_cart.inverse().display() + r = sqrt(x^2 + y^2) + ph = arctan2(y, x) + sage: M.coord_changes() # random (dictionary output) + {(Chart (U, (r, ph)), + Chart (U, (x, y))): Change of coordinates from Chart (U, (r, ph)) to Chart (U, (x, y)), + (Chart (U, (x, y)), + Chart (U, (r, ph))): Change of coordinates from Chart (U, (x, y)) to Chart (U, (r, ph))} + + Introducing a wrong inverse transformation (note the ``x^3`` typo) is + revealed by setting ``verbose`` to ``True``:: + + sage: spher_to_cart.set_inverse(sqrt(x^3+y^2), atan2(y,x), verbose=True) + Check of the inverse coordinate transformation: + r == sqrt(r^3*cos(ph)^3 + r^2*sin(ph)^2) + ph == arctan2(r*sin(ph), r*cos(ph)) + x == sqrt(x^3 + y^2)*x/sqrt(x^2 + y^2) + y == sqrt(x^3 + y^2)*y/sqrt(x^2 + y^2) + + """ + verbose = kwds.get('verbose', False) + self._inverse = type(self)(self._chart2, self._chart1, + *transformations) + if verbose: + print("Check of the inverse coordinate transformation:") + x1 = self._chart1._xx + x2 = self._chart2._xx + n1 = len(x1) + for i in range(n1): + print(" {} == {}".format(x1[i], self._inverse(*(self(*x1)))[i])) + for i in range(n1): + print(" {} == {}".format(x2[i], self(*(self._inverse(*x2)))[i])) + + def __mul__(self, other): + r""" + Composition with another change of coordinates. + + INPUT: + + - ``other`` -- another change of coordinate, the final chart of + it is the initial chart of ``self`` + + OUTPUT: + + - the change of coordinates `X_1 \to X_3`, where `X_1` is the initial + chart of ``other`` and `X_3` is the final chart of ``self`` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: U. = M.chart() + sage: X_to_U = X.transition_map(U, (x+y, x-y)) + sage: W. = M.chart() + sage: U_to_W = U.transition_map(W, (u+cos(u)/2, v-sin(v)/2)) + sage: X_to_W = U_to_W * X_to_U; X_to_W + Change of coordinates from Chart (M, (x, y)) to Chart (M, (w, z)) + sage: X_to_W.display() + w = 1/2*cos(x)*cos(y) - 1/2*sin(x)*sin(y) + x + y + z = -1/2*cos(y)*sin(x) + 1/2*cos(x)*sin(y) + x - y + + """ + if not isinstance(other, CoordChange): + raise TypeError("{} is not a change of coordinate".format(other)) + if other._chart2 != self._chart1: + raise ValueError("composition not possible: " + + "{} is different from {}".format(other._chart2, + other._chart1)) + #*# when MultiCoordFunction will be implemented (trac #18640): + # transf = self._transf(*(other._transf.expr())) + #*# for now: + transf = self(*(other._transf)) + return type(self)(other._chart1, self._chart2, *transf) + + def restrict(self, dom1, dom2=None): + r""" + Restriction to subsets. + + INPUT: + + - ``dom1`` -- open subset of the domain of ``chart1`` + - ``dom2`` -- (default: ``None``) open subset of the domain of + ``chart2``; if ``None``, ``dom1`` is assumed + + OUTPUT: + + - the transition map between the charts restricted to the + specified subsets + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + sage: U = M.open_subset('U', coord_def={X: x>0, Y: u+v>0}) + sage: X_to_Y_U = X_to_Y.restrict(U); X_to_Y_U + Change of coordinates from Chart (U, (x, y)) to Chart (U, (u, v)) + sage: X_to_Y_U.display() + u = x + y + v = x - y + + The result is cached:: + + sage: X_to_Y.restrict(U) is X_to_Y_U + True + + """ + if dom2 is None: + dom2 = dom1 + ch1 = self._chart1.restrict(dom1) + ch2 = self._chart2.restrict(dom2) + if (ch1, ch2) in dom1.coord_changes(): + return dom1.coord_changes()[(ch1,ch2)] + #*# when MultiCoordFunction will be implemented (trac #18640): + # return type(self)(self._chart1.restrict(dom1), + # self._chart2.restrict(dom2), *(self._transf.expr())) + #*# for now: + return type(self)(self._chart1.restrict(dom1), + self._chart2.restrict(dom2), *(self._transf)) + + def display(self): + r""" + Display of the coordinate transformation. + + The output is either text-formatted (console mode) or LaTeX-formatted + (notebook mode). + + EXAMPLES: + + From spherical coordinates to Cartesian ones in the plane:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: U = M.open_subset('U') # the complement of the half line {y=0, x>= 0} + sage: c_cart. = U.chart() + sage: c_spher. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') + sage: spher_to_cart = c_spher.transition_map(c_cart, [r*cos(ph), r*sin(ph)]) + sage: spher_to_cart.display() + x = r*cos(ph) + y = r*sin(ph) + sage: latex(spher_to_cart.display()) + \left\{\begin{array}{lcl} x & = & r \cos\left({\phi}\right) \\ + y & = & r \sin\left({\phi}\right) \end{array}\right. + + A shortcut is ``disp()``:: + + sage: spher_to_cart.disp() + x = r*cos(ph) + y = r*sin(ph) + + """ + from sage.misc.latex import latex + from sage.tensor.modules.format_utilities import FormattedExpansion + coords2 = self._chart2[:] + n2 = len(coords2) + #*# when MultiCoordFunction will be implemented (trac #18640): + # expr = self._transf.expr() + #*# for now: + expr = self._transf + rtxt = "" + if n2 == 1: + rlatex = r"\begin{array}{lcl}" + else: + rlatex = r"\left\{\begin{array}{lcl}" + for i in range(n2): + x2 = coords2[i] + x2f = expr[i] + rtxt += repr(x2) + " = " + repr(x2f) + "\n" + rlatex += latex(x2) + r" & = & " + latex(x2f) + r"\\" + rtxt = rtxt[:-1] # remove the last new line + rlatex = rlatex[:-2] + r"\end{array}" + if n2 > 1: + rlatex += r"\right." + return FormattedExpansion(rtxt, rlatex) + + disp = display + diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py new file mode 100644 index 00000000000..218c54fba75 --- /dev/null +++ b/src/sage/manifolds/manifold.py @@ -0,0 +1,1659 @@ +r""" +Topological Manifolds + +Given a topological field `K` (in most applications, `K = \RR` or +`K = \CC`) and a non-negative integer `n`, a *topological manifold of +dimension* `n` *over K* is a topological space `M` such that + +- `M` is a Hausdorff space, +- `M` is second countable, +- every point in `M` has a neighborhood homeomorphic to `K^n`. + +Topological manifolds are implemented via the class +:class:`TopologicalManifold`. Open subsets of topological manifolds +are also implemented via :class:`TopologicalManifold`, since they are +topological manifolds by themselves. + +In the current setting, topological manifolds are mostly described by +means of charts (see :class:`~sage.manifolds.chart.Chart`). + +:class:`TopologicalManifold` serves as a base class for more specific +manifold classes. + +The user interface is provided by the generic function +:func:`~sage.manifolds.manifold.Manifold`, with +with the argument ``structure`` set to ``'topological'``. + +.. RUBRIC:: Example 1: the 2-sphere as a topological manifold of dimension + 2 over `\RR` + +One starts by declaring `S^2` as a 2-dimensional topological manifold:: + + sage: M = Manifold(2, 'S^2', structure='topological') + sage: M + 2-dimensional topological manifold S^2 + +Since the base topological field has not been specified in the argument list +of ``Manifold``, `\RR` is assumed:: + + sage: M.base_field() + Real Field with 53 bits of precision + sage: dim(M) + 2 + +Let us consider the complement of a point, the "North pole" say; this is an +open subset of `S^2`, which we call `U`:: + + sage: U = M.open_subset('U'); U + Open subset U of the 2-dimensional topological manifold S^2 + +A standard chart on `U` is provided by the stereographic projection from the +North pole to the equatorial plane:: + + sage: stereoN. = U.chart(); stereoN + Chart (U, (x, y)) + +Thanks to the operator ```` on the left-hand side, the coordinates +declared in a chart (here `x` and `y`), are accessible by their names; +they are Sage's symbolic variables:: + + sage: y + y + sage: type(y) + + +The South pole is the point of coordinates `(x, y) = (0, 0)` in the above +chart:: + + sage: S = U.point((0,0), chart=stereoN, name='S'); S + Point S on the 2-dimensional topological manifold S^2 + +Let us call `V` the open subset that is the complement of the South pole and +let us introduce on it the chart induced by the stereographic projection from +the South pole to the equatorial plane:: + + sage: V = M.open_subset('V'); V + Open subset V of the 2-dimensional topological manifold S^2 + sage: stereoS. = V.chart(); stereoS + Chart (V, (u, v)) + +The North pole is the point of coordinates `(u, v) = (0, 0)` in this chart:: + + sage: N = V.point((0,0), chart=stereoS, name='N'); N + Point N on the 2-dimensional topological manifold S^2 + +To fully construct the manifold, we declare that it is the union of `U` +and `V`:: + + sage: M.declare_union(U,V) + +and we provide the transition map between the charts ``stereoN`` = +`(U, (x, y))` and ``stereoS`` = `(V, (u, v))`, denoting by `W` the +intersection of `U` and `V` (`W` is the subset of `U` defined by +`x^2 + y^2 \neq 0`, as well as the subset of `V` defined by +`u^2 + v^2 \neq 0`):: + + sage: stereoN_to_S = stereoN.transition_map(stereoS, [x/(x^2+y^2), y/(x^2+y^2)], + ....: intersection_name='W', restrictions1= x^2+y^2!=0, + ....: restrictions2= u^2+v^2!=0) + sage: stereoN_to_S + Change of coordinates from Chart (W, (x, y)) to Chart (W, (u, v)) + sage: stereoN_to_S.display() + u = x/(x^2 + y^2) + v = y/(x^2 + y^2) + +We give the name ``W`` to the Python variable representing `W = U \cap V`:: + + sage: W = U.intersection(V) + +The inverse of the transition map is computed by the method +:meth:`sage.manifolds.chart.CoordChange.inverse`:: + + sage: stereoN_to_S.inverse() + Change of coordinates from Chart (W, (u, v)) to Chart (W, (x, y)) + sage: stereoN_to_S.inverse().display() + x = u/(u^2 + v^2) + y = v/(u^2 + v^2) + +At this stage, we have four open subsets on `S^2`:: + + sage: M.list_of_subsets() + [2-dimensional topological manifold S^2, + Open subset U of the 2-dimensional topological manifold S^2, + Open subset V of the 2-dimensional topological manifold S^2, + Open subset W of the 2-dimensional topological manifold S^2] + +`W` is the open subset that is the complement of the two poles:: + + sage: N in W or S in W + False + +The North pole lies in `V` and the South pole in `U`:: + + sage: N in V, N in U + (True, False) + sage: S in U, S in V + (True, False) + +The manifold's (user) atlas contains four charts, two of them +being restrictions of charts to a smaller domain:: + + sage: M.atlas() + [Chart (U, (x, y)), Chart (V, (u, v)), + Chart (W, (x, y)), Chart (W, (u, v))] + +Let us consider the point of coordinates `(1, 2)` in the chart ``stereoN``:: + + sage: p = M.point((1,2), chart=stereoN, name='p'); p + Point p on the 2-dimensional topological manifold S^2 + sage: p.parent() + 2-dimensional topological manifold S^2 + sage: p in W + True + +The coordinates of `p` in the chart ``stereoS`` are computed by letting +the chart act on the point:: + + sage: stereoS(p) + (1/5, 2/5) + +Given the definition of `p`, we have of course:: + + sage: stereoN(p) + (1, 2) + +Similarly:: + + sage: stereoS(N) + (0, 0) + sage: stereoN(S) + (0, 0) + + +.. RUBRIC:: Example 2: the Riemann sphere as a topological manifold of + dimension 1 over `\CC` + +We declare the Riemann sphere `\CC^*` as a 1-dimensional topological manifold +over `\CC`:: + + sage: M = Manifold(1, 'C*', structure='topological', field='complex'); M + Complex 1-dimensional topological manifold C* + +We introduce a first open subset, which is actually +`\CC = \CC^*\setminus\{\infty\}` if we interpret `\CC^*` as the +Alexandroff one-point compactification of `\CC`:: + + sage: U = M.open_subset('U') + +A natural chart on `U` is then nothing but the identity map of `\CC`, hence +we denote the associated coordinate by `z`:: + + sage: Z. = U.chart() + +The origin of the complex plane is the point of coordinate `z = 0`:: + + sage: O = U.point((0,), chart=Z, name='O'); O + Point O on the Complex 1-dimensional topological manifold C* + +Another open subset of `\CC^*` is `V = \CC^*\setminus\{O\}`:: + + sage: V = M.open_subset('V') + +We define a chart on `V` such that the point at infinity is the point of +coordinate `0` in this chart:: + + sage: W. = V.chart(); W + Chart (V, (w,)) + sage: inf = M.point((0,), chart=W, name='inf', latex_name=r'\infty') + sage: inf + Point inf on the Complex 1-dimensional topological manifold C* + +To fully construct the Riemann sphere, we declare that it is the union +of `U` and `V`:: + + sage: M.declare_union(U,V) + +and we provide the transition map between the two charts as `w = 1 / z` +on `A = U \cap V`:: + + sage: Z_to_W = Z.transition_map(W, 1/z, intersection_name='A', + ....: restrictions1= z!=0, restrictions2= w!=0) + sage: Z_to_W + Change of coordinates from Chart (A, (z,)) to Chart (A, (w,)) + sage: Z_to_W.display() + w = 1/z + sage: Z_to_W.inverse() + Change of coordinates from Chart (A, (w,)) to Chart (A, (z,)) + sage: Z_to_W.inverse().display() + z = 1/w + +Let consider the complex number `i` as a point of the Riemann sphere:: + + sage: i = M((I,), chart=Z, name='i'); i + Point i on the Complex 1-dimensional topological manifold C* + +Its coordinates w.r.t. the charts ``Z`` and ``W`` are:: + + sage: Z(i) + (I,) + sage: W(i) + (-I,) + +and we have:: + + sage: i in U + True + sage: i in V + True + +The following subsets and charts have been defined:: + + sage: M.list_of_subsets() + [Open subset A of the Complex 1-dimensional topological manifold C*, + Complex 1-dimensional topological manifold C*, + Open subset U of the Complex 1-dimensional topological manifold C*, + Open subset V of the Complex 1-dimensional topological manifold C*] + sage: M.atlas() + [Chart (U, (z,)), Chart (V, (w,)), Chart (A, (z,)), Chart (A, (w,))] + + +AUTHORS: + +- Eric Gourgoulhon (2015): initial version +- Travis Scrimshaw (2015): structure described via + :class:`~sage.manifolds.structure.TopologicalStructure` or + :class:`~sage.manifolds.structure.RealTopologicalStructure` + + +REFERENCES: + +.. [Lee11] J.M. Lee : *Introduction to Topological Manifolds*, + 2nd ed., Springer (New York) (2011). +.. [Lee13] J.M. Lee : *Introduction to Smooth Manifolds*, + 2nd ed., Springer (New York) (2013) +.. [KN63] S. Kobayashi & K. Nomizu : *Foundations of Differential Geometry*, + vol. 1, Interscience Publishers (New York) (1963). +.. [Huybrechts05] D. Huybrechts : *Complex Geometry*, + Springer (Berlin) (2005). +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.fields import Fields +from sage.categories.manifolds import Manifolds +from sage.rings.all import CC +from sage.rings.real_mpfr import RR, RealField_class +from sage.rings.complex_field import ComplexField_class +from sage.misc.prandom import getrandbits +from sage.rings.integer import Integer +from sage.manifolds.subset import ManifoldSubset +from sage.manifolds.structure import TopologicalStructure, \ + RealTopologicalStructure + +############################################################################# +## Class + +class TopologicalManifold(ManifoldSubset): + r""" + Topological manifold over a topological field `K`. + + Given a topological field `K` (in most applications, `K = \RR` or + `K = \CC`) and a non-negative integer `n`, a *topological manifold of + dimension* `n` *over K* is a topological space `M` such that + + - `M` is a Hausdorff space, + - `M` is second countable, and + - every point in `M` has a neighborhood homeomorphic to `K^n`. + + This is a Sage *parent* class, the corresponding *element* + class being :class:`~sage.manifolds.point.ManifoldPoint`. + + INPUT: + + - ``n`` -- positive integer; dimension of the manifold + - ``name`` -- string; name (symbol) given to the manifold + - ``field`` -- field `K` on which the manifold is + defined; allowed values are + + - ``'real'`` or an object of type ``RealField`` (e.g., ``RR``) for + a manifold over `\RR` + - ``'complex'`` or an object of type ``ComplexField`` (e.g., ``CC``) + for a manifold over `\CC` + - an object in the category of topological fields (see + :class:`~sage.categories.fields.Fields` and + :class:`~sage.categories.topological_spaces.TopologicalSpaces`) + for other types of manifolds + + - ``structure`` -- manifold structure (see + :class:`~sage.manifolds.structure.TopologicalStructure` or + :class:`~sage.manifolds.structure.RealTopologicalStructure`) + - ``ambient`` -- (default: ``None``) if not ``None``, must be a + topological manifold; the created object is then an open subset of + ``ambient`` + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the manifold; if none are provided, it is set to ``name`` + - ``start_index`` -- (default: 0) integer; lower value of the range of + indices used for "indexed objects" on the manifold, e.g., coordinates + in a chart + - ``category`` -- (default: ``None``) to specify the category; if + ``None``, ``Manifolds(field)`` is assumed (see the category + :class:`~sage.categories.manifolds.Manifolds`) + - ``unique_tag`` -- (default: ``None``) tag used to force the construction + of a new object when all the other arguments have been used previously + (without ``unique_tag``, the + :class:`~sage.structure.unique_representation.UniqueRepresentation` + behavior inherited from + :class:`~sage.manifolds.subset.ManifoldSubset` + would return the previously constructed object corresponding to these + arguments) + + EXAMPLES: + + A 4-dimensional topological manifold (over `\RR`):: + + sage: M = Manifold(4, 'M', latex_name=r'\mathcal{M}', structure='topological') + sage: M + 4-dimensional topological manifold M + sage: latex(M) + \mathcal{M} + sage: type(M) + + sage: M.base_field() + Real Field with 53 bits of precision + sage: dim(M) + 4 + + The input parameter ``start_index`` defines the range of indices + on the manifold:: + + sage: M = Manifold(4, 'M', structure='topological') + sage: list(M.irange()) + [0, 1, 2, 3] + sage: M = Manifold(4, 'M', structure='topological', start_index=1) + sage: list(M.irange()) + [1, 2, 3, 4] + sage: list(Manifold(4, 'M', structure='topological', start_index=-2).irange()) + [-2, -1, 0, 1] + + A complex manifold:: + + sage: N = Manifold(3, 'N', structure='topological', field='complex'); N + Complex 3-dimensional topological manifold N + + A manifold over `\QQ`:: + + sage: N = Manifold(6, 'N', structure='topological', field=QQ); N + 6-dimensional topological manifold N over the Rational Field + + A manifold over `\QQ_5`, the field of 5-adic numbers:: + + sage: N = Manifold(2, 'N', structure='topological', field=Qp(5)); N + 2-dimensional topological manifold N over the 5-adic Field with capped + relative precision 20 + + A manifold is a Sage *parent* object, in the category of topological + manifolds over a given topological field (see + :class:`~sage.categories.manifolds.Manifolds`):: + + sage: isinstance(M, Parent) + True + sage: M.category() + Category of manifolds over Real Field with 53 bits of precision + sage: from sage.categories.manifolds import Manifolds + sage: M.category() is Manifolds(RR) + True + sage: M.category() is Manifolds(M.base_field()) + True + sage: M in Manifolds(RR) + True + sage: N in Manifolds(Qp(5)) + True + + The corresponding Sage *elements* are points:: + + sage: X. = M.chart() + sage: p = M.an_element(); p + Point on the 4-dimensional topological manifold M + sage: p.parent() + 4-dimensional topological manifold M + sage: M.is_parent_of(p) + True + sage: p in M + True + + The manifold's points are instances of class + :class:`~sage.manifolds.point.ManifoldPoint`:: + + sage: isinstance(p, sage.manifolds.point.ManifoldPoint) + True + + Since an open subset of a topological manifold `M` is itself a + topological manifold, open subsets of `M` are instances of the class + :class:`TopologicalManifold`:: + + sage: U = M.open_subset('U'); U + Open subset U of the 4-dimensional topological manifold M + sage: isinstance(U, sage.manifolds.manifold.TopologicalManifold) + True + sage: U.base_field() == M.base_field() + True + sage: dim(U) == dim(M) + True + sage: U.category() + Join of Category of subobjects of sets and Category of manifolds over + Real Field with 53 bits of precision + + The manifold passes all the tests of the test suite relative to its + category:: + + sage: TestSuite(M).run() + + .. SEEALSO:: + + :mod:`sage.manifolds.manifold` + """ + def __init__(self, n, name, field, structure, ambient=None, + latex_name=None, start_index=0, category=None, + unique_tag=None): + r""" + Construct a topological manifold. + + TESTS:: + + sage: M = Manifold(3, 'M', latex_name=r'\mathbb{M}', + ....: structure='topological', start_index=1) + sage: M + 3-dimensional topological manifold M + sage: latex(M) + \mathbb{M} + sage: dim(M) + 3 + sage: X. = M.chart() + sage: TestSuite(M).run() + + Tests for open subsets:: + + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: TestSuite(U).run() + sage: U.category() is M.category().Subobjects() + True + + """ + # Initialization of the attributes _dim, _field, _field_type: + self._dim = n + if field == 'real': + self._field = RR + self._field_type = 'real' + elif field == 'complex': + self._field = CC + self._field_type = 'complex' + else: + if field not in Fields(): + raise TypeError("the argument 'field' must be a field") + self._field = field + if isinstance(field, RealField_class): + self._field_type = 'real' + elif isinstance(field, ComplexField_class): + self._field_type = 'complex' + else: + self._field_type = 'neither_real_nor_complex' + # Structure and category: + self._structure = structure + if ambient is None: + ambient = self + category = Manifolds(self._field).or_subcategory(category) + category = self._structure.subcategory(category) + else: + category = ambient.category().Subobjects() + # Initialization as a manifold set: + ManifoldSubset.__init__(self, ambient, name, latex_name=latex_name, + category=category) + self._is_open = True + self._open_covers.append([self]) # list of open covers of self + # + if not isinstance(start_index, (int, Integer)): + raise TypeError("the starting index must be an integer") + self._sindex = start_index + # + self._atlas = [] # list of charts defined on subsets of self + self._top_charts = [] # list of charts defined on subsets of self + # that are not subcharts of charts on larger subsets + self._def_chart = None # default chart + self._charts_by_coord = {} # dictionary of charts whose domain is self + # (key: string formed by the coordinate + # symbols separated by a white space) + self._coord_changes = {} # dictionary of transition maps (key: pair of + # of charts) + # List of charts that individually cover self, i.e. whose + # domains are self (if non-empty, self is a coordinate domain): + self._covering_charts = [] + + def _repr_(self): + r""" + Return a string representation of the manifold. + + TESTS:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: M._repr_() + '3-dimensional topological manifold M' + sage: repr(M) # indirect doctest + '3-dimensional topological manifold M' + sage: M # indirect doctest + 3-dimensional topological manifold M + sage: M = Manifold(3, 'M', structure='topological', field='complex') + sage: M._repr_() + 'Complex 3-dimensional topological manifold M' + sage: M = Manifold(3, 'M', structure='topological', field=QQ) + sage: M._repr_() + '3-dimensional topological manifold M over the Rational Field' + + If the manifold is actually an open subset of a larger manifold, the + string representation is different:: + + sage: U = M.open_subset('U') + sage: U._repr_() + 'Open subset U of the 3-dimensional topological manifold M + over the Rational Field' + """ + if self is self._manifold: + if self._field_type == 'real': + return "{}-dimensional {} manifold {}".format(self._dim, + self._structure.name, + self._name) + elif self._field_type == 'complex': + return "Complex {}-dimensional {} manifold {}".format(self._dim, + self._structure.name, + self._name) + return "{}-dimensional {} manifold {} over the {}".format(self._dim, + self._structure.name, + self._name, + self._field) + else: + return "Open subset {} of the {}".format(self._name, self._manifold) + + def _an_element_(self): + r""" + Construct some point on the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M._an_element_(); p + Point on the 2-dimensional topological manifold M + sage: p.coord() + (0, 0) + sage: U = M.open_subset('U', coord_def={X: y>1}); U + Open subset U of the 2-dimensional topological manifold M + sage: p = U._an_element_(); p + Point on the 2-dimensional topological manifold M + sage: p in U + True + sage: p.coord() + (0, 2) + sage: V = U.open_subset('V', coord_def={X.restrict(U): x<-pi}) + sage: p = V._an_element_(); p + Point on the 2-dimensional topological manifold M + sage: p in V + True + sage: p.coord() + (-pi - 1, 2) + + """ + from sage.rings.infinity import Infinity + if self._def_chart is None: + return self.element_class(self) + # Attempt to construct a point in the domain of the default chart + chart = self._def_chart + if self._field_type == 'real': + coords = [] + for coord_range in chart._bounds: + xmin = coord_range[0][0] + xmax = coord_range[1][0] + if xmin == -Infinity: + if xmax == Infinity: + x = 0 + else: + x = xmax - 1 + else: + if xmax == Infinity: + x = xmin + 1 + else: + x = (xmin + xmax)/2 + coords.append(x) + else: + coords = self._dim*[0] + if not chart.valid_coordinates(*coords): + # Attempt to construct a point in the domain of other charts + if self._field_type == 'real': + for ch in self._atlas: + if ch is self._def_chart: + continue # since this case has already been attempted + coords = [] + for coord_range in ch._bounds: + xmin = coord_range[0][0] + xmax = coord_range[1][0] + if xmin == -Infinity: + if xmax == Infinity: + x = 0 + else: + x = xmax - 1 + else: + if xmax == Infinity: + x = xmin + 1 + else: + x = (xmin + xmax)/2 + coords.append(x) + if ch.valid_coordinates(*coords): + chart = ch + break + else: + # A generic element with specific coordinates could not be + # automatically generated, due to too complex cooordinate + # conditions. An element without any coordinate set is + # returned instead: + return self.element_class(self) + else: + # Case of manifolds over a field different from R + for ch in self._atlas: + if ch is self._def_chart: + continue # since this case has already been attempted + if ch.valid_coordinates(*coords): + chart = ch + break + else: + return self.element_class(self) + # The point is constructed with check_coords=False since the check + # has just been performed above: + return self.element_class(self, coords=coords, chart=chart, + check_coords=False) + + def __contains__(self, point): + r""" + Check whether a point is contained in the manifold. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M.point((1,2), chart=X) + sage: M.__contains__(p) + True + sage: p in M # indirect doctest + True + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: U.__contains__(p) + True + sage: p in U # indirect doctest + True + sage: V = U.open_subset('V', coord_def={X.restrict(U): y<0}) + sage: V.__contains__(p) + False + sage: p in V # indirect doctest + False + + """ + # for efficiency, a quick test first: + if point.parent() is self: + return True + if point.parent().is_subset(self): + return True + for chart in self._atlas: + if chart in point._coordinates: + if chart.valid_coordinates( *(point._coordinates[chart]) ): + return True + for chart in point._coordinates: + for schart in chart._subcharts: + if schart in self._atlas and schart.valid_coordinates( + *(point._coordinates[chart]) ): + return True + return False + + def open_subset(self, name, latex_name=None, coord_def={}): + r""" + Create an open subset of the manifold. + + An open subset is a set that is (i) included in the manifold and (ii) + open with respect to the manifold's topology. It is a topological + manifold by itself. Hence the returned object is an instance of + :class:`TopologicalManifold`. + + INPUT: + + - ``name`` -- name given to the open subset + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote + the subset; if none are provided, it is set to ``name`` + - ``coord_def`` -- (default: {}) definition of the subset in + terms of coordinates; ``coord_def`` must a be dictionary with keys + charts on the manifold and values the symbolic expressions formed + by the coordinates to define the subset + + OUTPUT: + + - the open subset, as an instance of :class:`TopologicalManifold` + + EXAMPLES: + + Creating an open subset of a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.open_subset('A'); A + Open subset A of the 2-dimensional topological manifold M + + As an open subset of a topological manifold, ``A`` is itself a + topological manifold, on the same topological field and of the same + dimension as ``M``:: + + sage: isinstance(A, sage.manifolds.manifold.TopologicalManifold) + True + sage: A.base_field() == M.base_field() + True + sage: dim(A) == dim(M) + True + sage: A.category() is M.category().Subobjects() + True + + Creating an open subset of ``A``:: + + sage: B = A.open_subset('B'); B + Open subset B of the 2-dimensional topological manifold M + + We have then:: + + sage: A.subsets() # random (set output) + {Open subset B of the 2-dimensional topological manifold M, + Open subset A of the 2-dimensional topological manifold M} + sage: B.is_subset(A) + True + sage: B.is_subset(M) + True + + Defining an open subset by some coordinate restrictions: the open + unit disk in `\RR^2`:: + + sage: M = Manifold(2, 'R^2', structure='topological') + sage: c_cart. = M.chart() # Cartesian coordinates on R^2 + sage: U = M.open_subset('U', coord_def={c_cart: x^2+y^2<1}); U + Open subset U of the 2-dimensional topological manifold R^2 + + Since the argument ``coord_def`` has been set, ``U`` is automatically + provided with a chart, which is the restriction of the Cartesian one + to ``U``:: + + sage: U.atlas() + [Chart (U, (x, y))] + + Therefore, one can immediately check whether a point belongs + to ``U``:: + + sage: M.point((0,0)) in U + True + sage: M.point((1/2,1/3)) in U + True + sage: M.point((1,2)) in U + False + + """ + resu = TopologicalManifold(self._dim, name, self._field, + self._structure, ambient=self._manifold, + latex_name=latex_name, + start_index=self._sindex) + resu._supersets.update(self._supersets) + for sd in self._supersets: + sd._subsets.add(resu) + self._top_subsets.add(resu) + # Charts on the result from the coordinate definition: + for chart, restrictions in coord_def.iteritems(): + if chart not in self._atlas: + raise ValueError("the {} does not belong to ".format(chart) + + "the atlas of {}".format(self)) + chart.restrict(resu, restrictions) + # Transition maps on the result inferred from those of self: + for chart1 in coord_def: + for chart2 in coord_def: + if chart2 != chart1 and (chart1, chart2) in self._coord_changes: + self._coord_changes[(chart1, chart2)].restrict(resu) + return resu + + def get_chart(self, coordinates, domain=None): + r""" + Get a chart from its coordinates. + + The chart must have been previously created by the method + :meth:`chart`. + + INPUT: + + - ``coordinates`` -- single string composed of the coordinate symbols + separated by a space + - ``domain`` -- (default: ``None``) string containing the name of the + chart's domain, which must be a subset of the current manifold; if + ``None``, the current manifold is assumed + + OUTPUT: + + - instance of + :class:`~sage.manifolds.chart.Chart` (or of the subclass + :class:`~sage.manifolds.chart.RealChart`) representing the chart + corresponding to the above specifications + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: M.get_chart('x y') + Chart (M, (x, y)) + sage: M.get_chart('x y') is X + True + sage: U = M.open_subset('U', coord_def={X: (y!=0,x<0)}) + sage: Y. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') + sage: M.atlas() + [Chart (M, (x, y)), Chart (U, (x, y)), Chart (U, (r, ph))] + sage: M.get_chart('x y', domain='U') + Chart (U, (x, y)) + sage: M.get_chart('x y', domain='U') is X.restrict(U) + True + sage: U.get_chart('r ph') + Chart (U, (r, ph)) + sage: M.get_chart('r ph', domain='U') + Chart (U, (r, ph)) + sage: M.get_chart('r ph', domain='U') is Y + True + + """ + if domain is None: + dom = self + else: + dom = self.get_subset(domain) + try: + return dom._charts_by_coord[coordinates] + except KeyError: + raise KeyError("the coordinates '{}' ".format(coordinates) + + "do not correspond to any chart with " + + "the {} as domain".format(dom)) + + def dimension(self): + r""" + Return the dimension of the manifold over its base field. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.dimension() + 2 + + A shortcut is ``dim()``:: + + sage: M.dim() + 2 + + The Sage global function ``dim`` can also be used:: + + sage: dim(M) + 2 + + """ + return self._dim + + dim = dimension + + def base_field(self): + r""" + Return the field on which the manifold is defined. + + OUTPUT: + + - a topological field + + EXAMPLES:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: M.base_field() + Real Field with 53 bits of precision + sage: M = Manifold(3, 'M', structure='topological', field='complex') + sage: M.base_field() + Complex Field with 53 bits of precision + sage: M = Manifold(3, 'M', structure='topological', field=QQ) + sage: M.base_field() + Rational Field + + """ + return self._field + + def base_field_type(self): + r""" + Return the type of topological field on which the manifold is defined. + + OUTPUT: + + - a string describing the field, with three possible values: + + - ``'real'`` for the real field `\RR` + - ``'complex'`` for the complex field `\CC` + - ``'neither_real_nor_complex'`` for a field different from `\RR` + and `\CC` + + EXAMPLES:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: M.base_field_type() + 'real' + sage: M = Manifold(3, 'M', structure='topological', field='complex') + sage: M.base_field_type() + 'complex' + sage: M = Manifold(3, 'M', structure='topological', field=QQ) + sage: M.base_field_type() + 'neither_real_nor_complex' + + """ + return self._field_type + + def start_index(self): + r""" + Return the first value of the index range used on the manifold. + + This is the parameter ``start_index`` passed at the construction of + the manifold. + + OUTPUT: + + - the integer `i_0` such that all indices of indexed objects on the + manifold range from `i_0` to `i_0 + n - 1`, where `n` is the + manifold's dimension + + EXAMPLES:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: M.start_index() + 0 + sage: M = Manifold(3, 'M', structure='topological', start_index=1) + sage: M.start_index() + 1 + + """ + return self._sindex + + def irange(self, start=None): + r""" + Single index generator. + + INPUT: + + - ``start`` -- (default: ``None``) initial value `i_0` of the index; + if none are provided, the value returned by :meth:`start_index()` + is assumed + + OUTPUT: + + - an iterable index, starting from `i_0` and ending at + `i_0 + n - 1`, where `n` is the manifold's dimension + + EXAMPLES: + + Index range on a 4-dimensional manifold:: + + sage: M = Manifold(4, 'M', structure='topological') + sage: for i in M.irange(): + ....: print i, + ....: + 0 1 2 3 + sage: for i in M.irange(2): + ....: print i, + ....: + 2 3 + sage: list(M.irange()) + [0, 1, 2, 3] + + Index range on a 4-dimensional manifold with starting index=1:: + + sage: M = Manifold(4, 'M', structure='topological', start_index=1) + sage: for i in M.irange(): + ....: print i, + ....: + 1 2 3 4 + sage: for i in M.irange(2): + ....: print i, + ....: + 2 3 4 + + In general, one has always:: + + sage: M.irange().next() == M.start_index() + True + + """ + si = self._sindex + imax = self._dim + si + if start is None: + i = si + else: + i = start + while i < imax: + yield i + i += 1 + + def index_generator(self, nb_indices): + r""" + Generator of index series. + + INPUT: + + - ``nb_indices`` -- number of indices in a series + + OUTPUT: + + - an iterable index series for a generic component with the specified + number of indices + + EXAMPLES: + + Indices on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological', start_index=1) + sage: for ind in M.index_generator(2): + ....: print ind + ....: + (1, 1) + (1, 2) + (2, 1) + (2, 2) + + Loops can be nested:: + + sage: for ind1 in M.index_generator(2): + ....: print ind1, " : ", + ....: for ind2 in M.index_generator(2): + ....: print ind2, + ....: print "" + ....: + (1, 1) : (1, 1) (1, 2) (2, 1) (2, 2) + (1, 2) : (1, 1) (1, 2) (2, 1) (2, 2) + (2, 1) : (1, 1) (1, 2) (2, 1) (2, 2) + (2, 2) : (1, 1) (1, 2) (2, 1) (2, 2) + + """ + si = self._sindex + imax = self._dim - 1 + si + ind = [si for k in range(nb_indices)] + ind_end = [si for k in range(nb_indices)] + ind_end[0] = imax+1 + while ind != ind_end: + yield tuple(ind) + ret = 1 + for pos in range(nb_indices-1,-1,-1): + if ind[pos] != imax: + ind[pos] += ret + ret = 0 + elif ret == 1: + if pos == 0: + ind[pos] = imax + 1 # end point reached + else: + ind[pos] = si + ret = 1 + + def atlas(self): + r""" + Return the list of charts that have been defined on the manifold. + + EXAMPLES: + + Let us consider `\RR^2` as a 2-dimensional manifold:: + + sage: M = Manifold(2, 'R^2', structure='topological') + + Immediately after the manifold creation, the atlas is empty, since no + chart has been defined yet:: + + sage: M.atlas() + [] + + Let us introduce the chart of Cartesian coordinates:: + + sage: c_cart. = M.chart() + sage: M.atlas() + [Chart (R^2, (x, y))] + + The complement of the half line `\{y = 0, x \geq 0\}`:: + + sage: U = M.open_subset('U', coord_def={c_cart: (y!=0,x<0)}) + sage: U.atlas() + [Chart (U, (x, y))] + sage: M.atlas() + [Chart (R^2, (x, y)), Chart (U, (x, y))] + + Spherical (polar) coordinates on ``U``:: + + sage: c_spher. = U.chart(r'r:(0,+oo) ph:(0,2*pi):\phi') + sage: U.atlas() + [Chart (U, (x, y)), Chart (U, (r, ph))] + sage: M.atlas() + [Chart (R^2, (x, y)), Chart (U, (x, y)), Chart (U, (r, ph))] + + .. SEEALSO:: + + :meth:`top_charts` + + """ + return list(self._atlas) # Make a (shallow) copy + + def top_charts(self): + r""" + Return the list of charts defined on subsets of the current manifold + that are not subcharts of charts on larger subsets. + + OUTPUT: + + - list of charts defined on open subsets of the manifold but not on + larger subsets + + EXAMPLES: + + Charts on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: Y. = U.chart() + sage: M.top_charts() + [Chart (M, (x, y)), Chart (U, (u, v))] + + Note that the (user) atlas contains one more chart: ``(U, (x,y))``, + which is not a "top" chart:: + + sage: M.atlas() + [Chart (M, (x, y)), Chart (U, (x, y)), Chart (U, (u, v))] + + .. SEEALSO:: + + :meth:`atlas` for the complete list of charts defined on the + manifold. + + """ + return list(self._top_charts) # Make a (shallow) copy + + def default_chart(self): + r""" + Return the default chart defined on the manifold. + + Unless changed via :meth:`set_default_chart`, the *default chart* + is the first one defined on a subset of the manifold (possibly itself). + + OUTPUT: + + - instance of :class:`~sage.manifolds.chart.Chart` + representing the default chart + + EXAMPLES: + + Default chart on a 2-dimensional manifold and on some subsets:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.chart('x y') + Chart (M, (x, y)) + sage: M.chart('u v') + Chart (M, (u, v)) + sage: M.default_chart() + Chart (M, (x, y)) + sage: A = M.open_subset('A') + sage: A.chart('t z') + Chart (A, (t, z)) + sage: A.default_chart() + Chart (A, (t, z)) + + """ + return self._def_chart + + def set_default_chart(self, chart): + r""" + Changing the default chart on ``self``. + + INPUT: + + - ``chart`` -- a chart (must be defined on some subset ``self``) + + EXAMPLES: + + Charts on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: c_uv. = M.chart() + sage: M.default_chart() + Chart (M, (x, y)) + sage: M.set_default_chart(c_uv) + sage: M.default_chart() + Chart (M, (u, v)) + + """ + from chart import Chart + if not isinstance(chart, Chart): + raise TypeError("{} is not a chart".format(chart)) + if chart._domain is not self: + if self.is_manifestly_coordinate_domain(): + raise TypeError("the chart domain must coincide with " + + "the {}".format(self)) + if chart not in self._atlas: + raise ValueError("the chart must be defined on the " + + "{}".format(self)) + self._def_chart = chart + + def coord_change(self, chart1, chart2): + r""" + Return the change of coordinates (transition map) between two charts + defined on the manifold. + + The change of coordinates must have been defined previously, for + instance by the method + :meth:`~sage.manifolds.chart.Chart.transition_map`. + + INPUT: + + - ``chart1`` -- chart 1 + - ``chart2`` -- chart 2 + + OUTPUT: + + - instance of :class:`~sage.manifolds.chart.CoordChange` + representing the transition map from chart 1 to chart 2 + + EXAMPLES: + + Change of coordinates on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: c_uv. = M.chart() + sage: c_xy.transition_map(c_uv, (x+y, x-y)) # defines the coord. change + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + sage: M.coord_change(c_xy, c_uv) # returns the coord. change defined above + Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)) + + """ + if (chart1, chart2) not in self._coord_changes: + raise TypeError("the change of coordinates from " + + "{} to {}".format(chart1, chart2) + " has not " + + "been defined on the {}".format(self)) + return self._coord_changes[(chart1, chart2)] + + def coord_changes(self): + r""" + Return the changes of coordinates (transition maps) defined on + subsets of the manifold. + + OUTPUT: + + - dictionary of changes of coordinates, with pairs of charts as keys + + EXAMPLES: + + Various changes of coordinates on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: c_uv. = M.chart() + sage: xy_to_uv = c_xy.transition_map(c_uv, [x+y, x-y]) + sage: M.coord_changes() + {(Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + sage: uv_to_xy = xy_to_uv.inverse() + sage: M.coord_changes() # random (dictionary output) + {(Chart (M, (u, v)), + Chart (M, (x, y))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y)), + (Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + sage: c_rs. = M.chart() + sage: uv_to_rs = c_uv.transition_map(c_rs, [-u+2*v, 3*u-v]) + sage: M.coord_changes() # random (dictionary output) + {(Chart (M, (u, v)), + Chart (M, (r, s))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (r, s)), + (Chart (M, (u, v)), + Chart (M, (x, y))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y)), + (Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v))} + sage: xy_to_rs = uv_to_rs * xy_to_uv + sage: M.coord_changes() # random (dictionary output) + {(Chart (M, (u, v)), + Chart (M, (r, s))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (r, s)), + (Chart (M, (u, v)), + Chart (M, (x, y))): Change of coordinates from Chart (M, (u, v)) to Chart (M, (x, y)), + (Chart (M, (x, y)), + Chart (M, (u, v))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (u, v)), + (Chart (M, (x, y)), + Chart (M, (r, s))): Change of coordinates from Chart (M, (x, y)) to Chart (M, (r, s))} + + """ + return self._coord_changes + + def is_manifestly_coordinate_domain(self): + r""" + Return ``True`` if the manifold is known to be the domain of some + coordinate chart and ``False`` otherwise. + + If ``False`` is returned, either the manifold cannot be the domain of + some coordinate chart or no such chart has been declared yet. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: X. = U.chart() + sage: U.is_manifestly_coordinate_domain() + True + sage: M.is_manifestly_coordinate_domain() + False + sage: Y. = M.chart() + sage: M.is_manifestly_coordinate_domain() + True + + """ + return bool(self._covering_charts) + + def chart(self, coordinates='', names=None): + r""" + Define a chart, the domain of which is the manifold. + + A *chart* is a pair `(U, \varphi)`, where `U` is the current + manifold and `\varphi: U \rightarrow V \subset K^n` + is a homeomorphism from `U` to an open subset `V` of `K^n`, `K` + being the field on which the manifold is defined. + + The components `(x^1, \ldots, x^n)` of `\varphi`, defined by + `\varphi(p) = (x^1(p), \ldots, x^n(p)) \in K^n` for any point + `p \in U`, are called the *coordinates* of the chart `(U, \varphi)`. + + See :class:`~sage.manifolds.chart.Chart` for a complete + documentation. + + INPUT: + + - ``coordinates`` -- (default: ``''`` (empty string)) string + defining the coordinate symbols and ranges, see below + - ``names`` -- (default: ``None``) unused argument, except if + ``coordinates`` is not provided; it must then be a tuple containing + the coordinate symbols (this is guaranteed if the shortcut operator + ``<,>`` is used) + + The coordinates declared in the string ``coordinates`` are + separated by ``' '`` (whitespace) and each coordinate has at most three + fields, separated by a colon (``':'``): + + 1. The coordinate symbol (a letter or a few letters). + 2. (optional, only for manifolds over `\RR`) The interval `I` + defining the coordinate range: if not provided, the coordinate + is assumed to span all `\RR`; otherwise `I` must be provided + in the form ``(a,b)`` (or equivalently ``]a,b[``) + The bounds ``a`` and ``b`` can be ``+/-Infinity``, ``Inf``, + ``infinity``, ``inf`` or ``oo``. For *singular* coordinates, + non-open intervals such as ``[a,b]`` and + ``(a,b]`` (or equivalently ``]a,b]``) are allowed. Note that + the interval declaration must not contain any space character. + 3. (optional) The LaTeX spelling of the coordinate; if not provided + the coordinate symbol given in the first field will be used. + + The order of the fields 2 and 3 does not matter and each of them can + be omitted. If it contains any LaTeX expression, the string + ``coordinates`` must be declared with the prefix 'r' (for "raw") to + allow for a proper treatment of the backslash character (see + examples below). If no interval range and no LaTeX spelling is to + be provided for any coordinate, the argument ``coordinates`` can be + omitted when the shortcut operator ``<,>`` is used via Sage + preparser (see examples below). + + OUTPUT: + + - the created chart, as an instance of + :class:`~sage.manifolds.chart.Chart` or of the subclass + :class:`~sage.manifolds.chart.RealChart` for manifolds over `\RR`. + + EXAMPLES: + + Chart on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: X = U.chart('x y'); X + Chart (U, (x, y)) + sage: X[0] + x + sage: X[1] + y + sage: X[:] + (x, y) + + The declared coordinates are not known at the global level:: + + sage: y + Traceback (most recent call last): + ... + NameError: name 'y' is not defined + + They can be recovered by the operator ``[:]`` applied to the chart:: + + sage: (x, y) = X[:] + sage: y + y + sage: type(y) + + + But a shorter way to proceed is to use the operator ``<,>`` in the + left-hand side of the chart declaration (there is then no need to + pass the string 'x y' to chart()):: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: X. = U.chart(); X + Chart (U, (x, y)) + + Indeed, the declared coordinates are then known at the global level:: + + sage: y + y + sage: (x,y) == X[:] + True + + Actually the instruction ``X. = U.chart()`` is + equivalent to the combination of the two instructions + ``X = U.chart('x y')`` and ``(x,y) = X[:]``. + + See the documentation of class + :class:`~sage.manifolds.chart.Chart` for more examples, + especially regarding the coordinates ranges and restrictions. + + """ + return self._structure.chart(self, coordinates=coordinates, names=names) + + def is_open(self): + """ + Return if ``self`` is an open set. + + In the present case (manifold or open subset of it), always + return ``True``. + + TEST:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.is_open() + True + + """ + return True + +############################################################################## +## Constructor function + +def Manifold(dim, name, latex_name=None, field='real', structure='smooth', + start_index=0, **extra_kwds): + r""" + Construct a manifold of a given type over a topological field `K`. + + Given a topological field `K` (in most applications, `K = \RR` or + `K = \CC`) and a non-negative integer `n`, a *topological manifold of + dimension* `n` *over K* is a topological space `M` such that + + - `M` is a Hausdorff space, + - `M` is second countable, and + - every point in `M` has a neighborhood homeomorphic to `K^n`. + + A *real manifold* is a manifold over `\RR`. A *differentiable* (resp. + *smooth*, resp. *analytic*) is a real manifold such that all transition + maps are *differentiable* (resp. *smooth*, resp. *analytic*). + + INPUT: + + - ``dim`` -- positive integer; dimension of the manifold + - ``name`` -- string; name (symbol) given to the manifold + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the manifold; if none are provided, it is set to ``name`` + - ``field`` -- (default: ``'real'``) field `K` on which the + manifold is defined; allowed values are + + - ``'real'`` or an object of type ``RealField`` (e.g. ``RR``) for a + manifold over `\RR` + - ``'complex'`` or an object of type ``ComplexField`` (e.g. ``CC``) + for a manifold over `\CC` + - an object in the category of topological fields (see + :class:`~sage.categories.fields.Fields` and + :class:`~sage.categories.topological_spaces.TopologicalSpaces`) + for other types of manifolds + + - ``structure`` -- (default: ``'smooth'``) to specify the structure or + type of manifold; allowed values are + + - ``'topological'`` or ``'top'`` for a topological manifold + - ``'differentiable'`` or ``'diff'`` for a differentiable manifold + - ``'smooth'`` for a smooth manifold + - ``'analytic'`` for an analytic manifold + + - ``start_index`` -- (default: 0) integer; lower value of the range of + indices used for "indexed objects" on the manifold, e.g. coordinates + in a chart + - ``extra_kwds`` -- keywords meaningful only for some specific types + of manifolds + + OUTPUT: + + - a manifold of the specified type + + EXAMPLES: + + A 3-dimensional real topological manifold:: + + sage: M = Manifold(3, 'M', structure='topological'); M + 3-dimensional topological manifold M + + Given the default value of the parameter ``field``, the above is + equivalent to:: + + sage: M = Manifold(3, 'M', structure='topological', field='real'); M + 3-dimensional topological manifold M + + A complex topological manifold:: + + sage: M = Manifold(3, 'M', structure='topological', field='complex'); M + Complex 3-dimensional topological manifold M + + A topological manifold over `\QQ`:: + + sage: M = Manifold(3, 'M', structure='topological', field=QQ); M + 3-dimensional topological manifold M over the Rational Field + + See the documentation of class + :class:`~sage.manifolds.manifold.TopologicalManifold` for more + detailed examples. + + .. RUBRIC:: Uniqueness of manifold objects + + Suppose we construct a manifold named `M`:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + + At some point, we change our mind and would like to restart with a new + manifold, using the same name `M` and keeping the previous manifold for + reference:: + + sage: M_old = M # for reference + sage: M = Manifold(2, 'M', structure='topological') + + This results in a brand new object:: + + sage: M.atlas() + [] + + The object ``M_old`` is intact:: + + sage: M_old.atlas() + [Chart (M, (x, y))] + + Both objects have the same display:: + + sage: M + 2-dimensional topological manifold M + sage: M_old + 2-dimensional topological manifold M + + but they are different:: + + sage: M != M_old + True + + Let us introduce a chart on ``M``, using the same coordinate symbols + as for ``M_old``:: + + sage: X. = M.chart() + + The charts are displayed in the same way:: + + sage: M.atlas() + [Chart (M, (x, y))] + sage: M_old.atlas() + [Chart (M, (x, y))] + + but they are actually different:: + + sage: M.atlas()[0] != M_old.atlas()[0] + True + + Moreover, the two manifolds ``M`` and ``M_old`` are still considered + distinct:: + + sage: M != M_old + True + + This reflects the fact that the equality of manifold objects holds only + for identical objects, i.e. one has ``M1 == M2`` if, and only if, + ``M1 is M2``. Actually, the manifold classes inherit from + :class:`~sage.misc.fast_methods.WithEqualityById`:: + + sage: isinstance(M, sage.misc.fast_methods.WithEqualityById) + True + """ + from time import time + # Some sanity checks + if not isinstance(dim, (int, Integer)): + raise TypeError("the manifold dimension must be an integer") + if dim < 1: + raise ValueError("the manifold dimension must be strictly positive") + + if structure in ['topological', 'top']: + if field == 'real' or isinstance(field, RealField_class): + structure = RealTopologicalStructure() + else: + structure = TopologicalStructure() + else: + raise NotImplementedError("manifolds of type {} are not ".format(structure) + + "implemented") + return TopologicalManifold(dim, name, field, structure, + latex_name=latex_name, start_index=start_index, + unique_tag=getrandbits(128)*time()) + diff --git a/src/sage/manifolds/point.py b/src/sage/manifolds/point.py new file mode 100644 index 00000000000..8fdb6d4d576 --- /dev/null +++ b/src/sage/manifolds/point.py @@ -0,0 +1,684 @@ +r""" +Points of Topological Manifolds + +The class :class:`ManifoldPoint` implements points of a +topological manifold. + +A :class:`ManifoldPoint` object can have coordinates in +various charts defined on the manifold. Two points are declared +equal if they have the same coordinates in the same chart. + +AUTHORS: + +- Eric Gourgoulhon, Michal Bejger (2013-2015) : initial version + +REFERENCES: + +- [Lee11]_ J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed., + Springer (New York) (2011) +- [Lee13]_ J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed., + Springer (New York, 2013) + +EXAMPLES: + +Defining a point in `\RR^3` by its spherical coordinates:: + + sage: M = Manifold(3, 'R^3', structure='topological') + sage: U = M.open_subset('U') # the complement of the half-plane (y=0, x>=0) + sage: c_spher. = U.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') + +We construct the point in the coordinates in the default chart of ``U`` +(``c_spher``):: + + sage: p = U((1, pi/2, pi), name='P') + sage: p + Point P on the 3-dimensional topological manifold R^3 + sage: latex(p) + P + sage: p in U + True + sage: p.parent() + Open subset U of the 3-dimensional topological manifold R^3 + sage: c_spher(p) + (1, 1/2*pi, pi) + sage: p.coordinates(c_spher) # equivalent to above + (1, 1/2*pi, pi) + +Computing the coordinates of ``p`` in a new chart:: + + sage: c_cart. = U.chart() # Cartesian coordinates on U + sage: spher_to_cart = c_spher.transition_map(c_cart, + ....: [r*sin(th)*cos(ph), r*sin(th)*sin(ph), r*cos(th)]) + sage: c_cart(p) # evaluate P's Cartesian coordinates + (-1, 0, 0) + +Points can be compared:: + + sage: p1 = U((1, pi/2, pi)) + sage: p == p1 + True + sage: q = U((1,2,3), chart=c_cart, name='Q') # point defined by its Cartesian coordinates + sage: p == q + False + +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.element import Element + +class ManifoldPoint(Element): + r""" + Point of a topological manifold. + + This is a Sage *element* class, the corresponding *parent* class + being :class:`~sage.manifolds.manifold.TopologicalManifold` + or :class:`~sage.manifolds.subset.ManifoldSubset`. + + INPUT: + + - ``parent`` -- the manifold subset to which the point belongs + - ``coords`` -- (default: ``None``) the point coordinates (as a tuple + or a list) in the chart ``chart`` + - ``chart`` -- (default: ``None``) chart in which the coordinates are + given; if ``None``, the coordinates are assumed to refer to the + default chart of ``parent`` + - ``name`` -- (default: ``None``) name given to the point + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the point; + if ``None``, the LaTeX symbol is set to ``name`` + - ``check_coords`` -- (default: ``True``) determines whether ``coords`` + are valid coordinates for the chart ``chart``; for symbolic + coordinates, it is recommended to set ``check_coords`` to ``False`` + + EXAMPLES: + + A point on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: (a, b) = var('a b') # generic coordinates for the point + sage: p = M.point((a, b), name='P'); p + Point P on the 2-dimensional topological manifold M + sage: p.coordinates() # coordinates of P in the subset's default chart + (a, b) + + Since points are Sage *elements*, the *parent* of which being the + subset on which they are defined, it is equivalent to write:: + + sage: p = M((a, b), name='P'); p + Point P on the 2-dimensional topological manifold M + + A point is an element of the manifold subset in which it has + been defined:: + + sage: p in M + True + sage: p.parent() + 2-dimensional topological manifold M + sage: U = M.open_subset('U', coord_def={c_xy: x>0}) + sage: q = U.point((2,1), name='q') + sage: q.parent() + Open subset U of the 2-dimensional topological manifold M + sage: q in U + True + sage: q in M + True + + By default, the LaTeX symbol of the point is deduced from its name:: + + sage: latex(p) + P + + But it can be set to any value:: + + sage: p = M.point((a, b), name='P', latex_name=r'\mathcal{P}') + sage: latex(p) + \mathcal{P} + + Points can be drawn in 2D or 3D graphics thanks to the + method :meth:`plot`. + """ + def __init__(self, parent, coords=None, chart=None, name=None, + latex_name=None, check_coords=True): + r""" + Construct a manifold point. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,3), name='p'); p + Point p on the 2-dimensional topological manifold M + sage: TestSuite(p).run() + sage: U = M.open_subset('U', coord_def={X: x<0}) + sage: q = U((-1,2), name='q'); q + Point q on the 2-dimensional topological manifold M + sage: TestSuite(q).run() + + """ + Element.__init__(self, parent) + self._manifold = parent.manifold() # a useful shortcut + self._coordinates = {} # dictionary of the point coordinates in various + # charts, with the charts as keys + if coords is not None: + if len(coords) != parent.manifold().dimension(): + raise ValueError("the number of coordinates must be equal " + + "to the manifold's dimension") + from sage.manifolds.manifold import TopologicalManifold + if chart is None: + chart = parent._def_chart + elif isinstance(parent, TopologicalManifold): + if chart not in parent._atlas: + raise ValueError("the {} has not been".format(chart) + + "defined on the {}".format(parent)) + if check_coords: + if not chart.valid_coordinates(*coords): + raise ValueError("the coordinates {}".format(coords) + + " are not valid on the {}".format(chart)) + for schart in chart._supercharts: + self._coordinates[schart] = tuple(coords) + for schart in chart._subcharts: + if schart != chart: + if schart.valid_coordinates(*coords): + self._coordinates[schart] = tuple(coords) + self._name = name + if latex_name is None: + self._latex_name = self._name + else: + self._latex_name = latex_name + + def _repr_(self): + r""" + Return a string representation of the point. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,-3)) + sage: p._repr_() + 'Point on the 2-dimensional topological manifold M' + sage: p = M((2,-3), name='p') + sage: p._repr_() + 'Point p on the 2-dimensional topological manifold M' + sage: repr(p) # indirect doctest + 'Point p on the 2-dimensional topological manifold M' + + """ + description = "Point" + if self._name is not None: + description += " " + self._name + description += " on the {}".format(self._manifold) + return description + + def _latex_(self): + r""" + Return a LaTeX representation of the point. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,-3)) + sage: p._latex_() + '\\mbox{Point on the 2-dimensional topological manifold M}' + sage: p = M((2,-3), name='p') + sage: p._latex_() + 'p' + sage: p = M((2,-3), name='p', latex_name=r'\mathcal{P}') + sage: p._latex_() + '\\mathcal{P}' + sage: latex(p) # indirect doctest + \mathcal{P} + + """ + if self._latex_name is None: + return r'\mbox{' + str(self) + r'}' + return self._latex_name + + def coordinates(self, chart=None, old_chart=None): + r""" + Return the point coordinates in the specified chart. + + If these coordinates are not already known, they are computed from + known ones by means of change-of-chart formulas. + + An equivalent way to get the coordinates of a point is to let the + chart acting on the point, i.e. if ``X`` is a chart and ``p`` a + point, one has ``p.coordinates(chart=X) == X(p)``. + + INPUT: + + - ``chart`` -- (default: ``None``) chart in which the coordinates + are given; if none are provided, the coordinates are assumed to + refer to the subset's default chart + - ``old_chart`` -- (default: ``None``) chart from which the + coordinates in ``chart`` are to be computed; if ``None``, a chart + in which the point's coordinates are already known will be picked, + privileging the subset's default chart + + EXAMPLES: + + Spherical coordinates of a point on `\RR^3`:: + + sage: M = Manifold(3, 'M', structure='topological') + sage: c_spher. = M.chart(r'r:(0,+oo) th:(0,pi):\theta ph:(0,2*pi):\phi') # spherical coordinates + sage: p = M.point((1, pi/2, pi)) + sage: p.coordinates() # coordinates in the manifold's default chart + (1, 1/2*pi, pi) + + Since the default chart of ``M`` is ``c_spher``, it is equivalent to + write:: + + sage: p.coordinates(c_spher) + (1, 1/2*pi, pi) + + An alternative way to get the coordinates is to let the chart act + on the point (from the very definition of a chart):: + + sage: c_spher(p) + (1, 1/2*pi, pi) + + A shortcut for ``coordinates`` is ``coord``:: + + sage: p.coord() + (1, 1/2*pi, pi) + + Computing the Cartesian coordinates from the spherical ones:: + + sage: c_cart. = M.chart() # Cartesian coordinates + sage: c_spher.transition_map(c_cart, [r*sin(th)*cos(ph), + ....: r*sin(th)*sin(ph), r*cos(th)]) + Change of coordinates from Chart (M, (r, th, ph)) to Chart (M, (x, y, z)) + + The computation is performed by means of the above change + of coordinates:: + + sage: p.coord(c_cart) + (-1, 0, 0) + sage: p.coord(c_cart) == c_cart(p) + True + + Coordinates of a point on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: (a, b) = var('a b') # generic coordinates for the point + sage: P = M.point((a, b), name='P') + + Coordinates of ``P`` in the manifold's default chart:: + + sage: P.coord() + (a, b) + + Coordinates of ``P`` in a new chart:: + + sage: c_uv. = M.chart() + sage: ch_xy_uv = c_xy.transition_map(c_uv, [x-y, x+y]) + sage: P.coord(c_uv) + (a - b, a + b) + + Coordinates of ``P`` in a third chart:: + + sage: c_wz. = M.chart() + sage: ch_uv_wz = c_uv.transition_map(c_wz, [u^3, v^3]) + sage: P.coord(c_wz, old_chart=c_uv) + (a^3 - 3*a^2*b + 3*a*b^2 - b^3, a^3 + 3*a^2*b + 3*a*b^2 + b^3) + + Actually, in the present case, it is not necessary to specify + ``old_chart='uv'``. Note that the first command erases all + the coordinates except those in the chart ``c_uv``:: + + sage: P.set_coord((a-b, a+b), c_uv) + sage: P._coordinates + {Chart (M, (u, v)): (a - b, a + b)} + sage: P.coord(c_wz) + (a^3 - 3*a^2*b + 3*a*b^2 - b^3, a^3 + 3*a^2*b + 3*a*b^2 + b^3) + sage: P._coordinates # random (dictionary output) + {Chart (M, (u, v)): (a - b, a + b), + Chart (M, (w, z)): (a^3 - 3*a^2*b + 3*a*b^2 - b^3, + a^3 + 3*a^2*b + 3*a*b^2 + b^3)} + + """ + if chart is None: + dom = self.parent() + chart = dom._def_chart + def_chart = chart + else: + dom = chart._domain + def_chart = dom._def_chart + if self not in dom: + raise ValueError("the point does not belong to the domain " + + "of {}".format(chart)) + if chart not in self._coordinates: + # Check whether chart corresponds to a superchart of a chart + # in which the coordinates are known: + for ochart in self._coordinates: + if chart in ochart._supercharts or chart in ochart._subcharts: + self._coordinates[chart] = self._coordinates[ochart] + return self._coordinates[chart] + # If this point is reached, some change of coordinates must be + # performed + if old_chart is not None: + s_old_chart = old_chart + s_chart = chart + else: + # A chart must be found as a starting point of the computation + # The domain's default chart is privileged: + if (def_chart in self._coordinates + and (def_chart, chart) in dom._coord_changes): + old_chart = def_chart + s_old_chart = def_chart + s_chart = chart + else: + for ochart in self._coordinates: + for subchart in ochart._subcharts: + if (subchart, chart) in dom._coord_changes: + old_chart = ochart + s_old_chart = subchart + s_chart = chart + break + if old_chart is not None: + break + if old_chart is None: + # Some search involving the subcharts of chart is + # performed: + for schart in chart._subcharts: + for ochart in self._coordinates: + for subchart in ochart._subcharts: + if (subchart, schart) in dom._coord_changes: + old_chart = ochart + s_old_chart = subchart + s_chart = schart + break + if old_chart is not None: + break + if old_chart is not None: + break + if old_chart is None: + raise ValueError("the coordinates of {}".format(self) + + " in the {}".format(chart) + " cannot be computed " + + "by means of known changes of charts.") + else: + chcoord = dom._coord_changes[(s_old_chart, s_chart)] + self._coordinates[chart] = chcoord(*self._coordinates[old_chart]) + return self._coordinates[chart] + + coord = coordinates + + def set_coordinates(self, coords, chart=None): + r""" + Sets the point coordinates in the specified chart. + + Coordinates with respect to other charts are deleted, in order to + avoid any inconsistency. To keep them, use the method :meth:`add_coord` + instead. + + INPUT: + + - ``coords`` -- the point coordinates (as a tuple or a list) + - ``chart`` -- (default: ``None``) chart in which the coordinates + are given; if none are provided, the coordinates are assumed to + refer to the subset's default chart + + EXAMPLES: + + Setting coordinates to a point on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M.point() + + We set the coordinates in the manifold's default chart:: + + sage: p.set_coordinates((2,-3)) + sage: p.coordinates() + (2, -3) + sage: X(p) + (2, -3) + + A shortcut for ``set_coordinates`` is ``set_coord``:: + + sage: p.set_coord((2,-3)) + sage: p.coord() + (2, -3) + + Let us introduce a second chart on the manifold:: + + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + + If we set the coordinates of ``p`` in chart ``Y``, those in chart ``X`` + are lost:: + + sage: Y(p) + (-1, 5) + sage: p.set_coord(Y(p), chart=Y) + sage: p._coordinates + {Chart (M, (u, v)): (-1, 5)} + + """ + self._coordinates.clear() + self.add_coord(coords, chart) + + set_coord = set_coordinates + + def add_coordinates(self, coords, chart=None): + r""" + Adds some coordinates in the specified chart. + + The previous coordinates with respect to other charts are kept. To + clear them, use :meth:`set_coord` instead. + + INPUT: + + - ``coords`` -- the point coordinates (as a tuple or a list) + - ``chart`` -- (default: ``None``) chart in which the coordinates + are given; if none are provided, the coordinates are assumed to + refer to the subset's default chart + + .. WARNING:: + + If the point has already coordinates in other charts, it + is the user's responsibility to make sure that the coordinates + to be added are consistent with them. + + EXAMPLES: + + Setting coordinates to a point on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M.point() + + We give the point some coordinates in the manifold's default chart:: + + sage: p.add_coordinates((2,-3)) + sage: p.coordinates() + (2, -3) + sage: X(p) + (2, -3) + + A shortcut for ``add_coordinates`` is ``add_coord``:: + + sage: p.add_coord((2,-3)) + sage: p.coord() + (2, -3) + + Let us introduce a second chart on the manifold:: + + sage: Y. = M.chart() + sage: X_to_Y = X.transition_map(Y, [x+y, x-y]) + + If we add coordinates for ``p`` in chart ``Y``, those in chart ``X`` + are kept:: + + sage: p.add_coordinates((-1,5), chart=Y) + sage: p._coordinates # random (dictionary output) + {Chart (M, (u, v)): (-1, 5), Chart (M, (x, y)): (2, -3)} + + On the contrary, with the method :meth:`set_coordinates`, the + coordinates in charts different from ``Y`` would be lost:: + + sage: p.set_coordinates((-1,5), chart=Y) + sage: p._coordinates + {Chart (M, (u, v)): (-1, 5)} + + """ + if len(coords) != self.parent().manifold()._dim: + raise ValueError("the number of coordinates must be equal to " + + "the manifold's dimension.") + if chart is None: + chart = self.parent()._def_chart + else: + if chart not in self.parent()._atlas: + raise ValueError("the {}".format(chart) + " has not been " + + "defined on the {}".format(self.parent())) + self._coordinates[chart] = coords + + add_coord = add_coordinates + + def __eq__(self, other): + r""" + Compares the current point with another one. + + EXAMPLES: + + Comparison with coordinates in the same chart:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,-3), chart=X) + sage: q = M((2,-3), chart=X) + sage: p == q + True + sage: q = M((-2,-3), chart=X) + sage: p == q + False + + Comparison with coordinates of other in a subchart:: + + sage: U = M.open_subset('U', coord_def={X: x>0}) + sage: XU = X.restrict(U) + sage: q = U((2,-3), chart=XU) + sage: p == q and q == p + True + sage: q = U((1,-3), chart=XU) + sage: p == q or q == p + False + + Comparison requiring a change of chart:: + + sage: Y. = U.chart() + sage: XU_to_Y = XU.transition_map(Y, (ln(x), x+y)) + sage: XU_to_Y.inverse()(u,v) + (e^u, v - e^u) + sage: q = U((ln(2),-1), chart=Y) + sage: p == q and q == p + True + sage: q = U((ln(3),1), chart=Y) + sage: p == q or q == p + False + + """ + if other is self: + return True + if not isinstance(other, ManifoldPoint): + return False + if other.parent().manifold() != self.parent().manifold(): + return False + # Search for a common chart to compare the coordinates + common_chart = None + # the subset's default chart is privileged: + # FIXME: Make this a better test + if hasattr(self.parent(), '_def_chart'): # self.parent() is open + def_chart = self.parent()._def_chart + else: + def_chart = self.parent().manifold()._def_chart + if def_chart in self._coordinates and def_chart in other._coordinates: + common_chart = def_chart + else: + for chart in self._coordinates: + if chart in other._coordinates: + common_chart = chart + break + if common_chart is None: + # At this stage, a commont chart is searched via a coordinate + # transformation: + for chart in self._coordinates: + try: + other.coordinates(chart) + common_chart = chart + break + except ValueError: + pass + else: + # Attempt a coordinate transformation in the reverse way: + for chart in other._coordinates: + try: + self.coordinates(chart) + common_chart = chart + break + except ValueError: + pass + if common_chart is None: + return False + #!# Another option would be: + # raise ValueError("no common chart has been found to compare " + + # "{} and {}".format(self, other)) + return self._coordinates[common_chart] == other._coordinates[common_chart] + + def __ne__(self, other): + r""" + Non-equality operator. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,-3), chart=X) + sage: q = M((0,1), chart=X) + sage: p != q + True + sage: p != M((2,-3), chart=X) + False + + """ + return not (self == other) + + def __hash__(self): + r""" + Return the hash of ``self``. + + This hash function is set to constant on a given manifold, to fulfill + Python's credo:: + + p == q ==> hash(p) == hash(q) + + This is necessary since ``p`` and ``q`` may be created in + different coordinate systems and nevertheless be equal. + + .. TODO:: + + Find a better hash function. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((2,-3), chart=X) + sage: hash(p) == hash(M) + True + + """ + return hash(self.parent().manifold()) + diff --git a/src/sage/manifolds/structure.py b/src/sage/manifolds/structure.py new file mode 100644 index 00000000000..170bf46c330 --- /dev/null +++ b/src/sage/manifolds/structure.py @@ -0,0 +1,71 @@ +r""" +Manifold Structures + +These classes encode the structure of a manifold. + +AUTHORS: + +- Travis Scrimshaw (2015-11-25): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.fast_methods import Singleton +from sage.manifolds.chart import Chart, RealChart + +# This is a slight abuse by making this a Singleton, but there is no +# need to have different copies of this object. +class TopologicalStructure(Singleton): + """ + The structure of a topological manifold over a general topological field. + """ + chart = Chart + name = "topological" + + def subcategory(self, cat): + """ + Return the subcategory of ``cat`` corresponding to the structure + of ``self``. + + EXAMPLES:: + + sage: from sage.manifolds.structure import TopologicalStructure + sage: from sage.categories.manifolds import Manifolds + sage: TopologicalStructure().subcategory(Manifolds(RR)) + Category of manifolds over Real Field with 53 bits of precision + + """ + return cat + +class RealTopologicalStructure(Singleton): + """ + The structure of a topological manifold over `\RR`. + """ + chart = RealChart + name = "topological" + + def subcategory(self, cat): + """ + Return the subcategory of ``cat`` corresponding to the structure + of ``self``. + + EXAMPLES:: + + sage: from sage.manifolds.structure import RealTopologicalStructure + sage: from sage.categories.manifolds import Manifolds + sage: RealTopologicalStructure().subcategory(Manifolds(RR)) + Category of manifolds over Real Field with 53 bits of precision + + """ + return cat + diff --git a/src/sage/manifolds/subset.py b/src/sage/manifolds/subset.py new file mode 100644 index 00000000000..9ada6377340 --- /dev/null +++ b/src/sage/manifolds/subset.py @@ -0,0 +1,1149 @@ +r""" +Subsets of Topological Manifolds + +The class :class:`ManifoldSubset` implements generic subsets of a +topological manifold. Open subsets are implemented by the class +:class:`~sage.manifolds.manifold.TopologicalManifold` (since an +open subset of a manifold is a manifold by itself), which inherits +from :class:`ManifoldSubset`. + +AUTHORS: + +- Eric Gourgoulhon, Michal Bejger (2013-2015): initial version +- Travis Scrimshaw (2015): review tweaks; removal of facade parents + +REFERENCES: + +- [Lee11]_ J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed., + Springer (New York) (2011) + +EXAMPLES: + +Two subsets on a manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A'); a + Subset A of the 2-dimensional topological manifold M + sage: b = M.subset('B'); b + Subset B of the 2-dimensional topological manifold M + sage: M.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M] + +The intersection of the two subsets:: + + sage: c = a.intersection(b); c + Subset A_inter_B of the 2-dimensional topological manifold M + +Their union:: + + sage: d = a.union(b); d + Subset A_union_B of the 2-dimensional topological manifold M + +Lists of subsets after the above operations:: + + sage: M.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset A_inter_B of the 2-dimensional topological manifold M, + Subset A_union_B of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M] + sage: a.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset A_inter_B of the 2-dimensional topological manifold M] + sage: c.list_of_subsets() + [Subset A_inter_B of the 2-dimensional topological manifold M] + sage: d.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset A_inter_B of the 2-dimensional topological manifold M, + Subset A_union_B of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M] + +""" +#***************************************************************************** +# Copyright (C) 2015 Eric Gourgoulhon +# Copyright (C) 2015 Michal Bejger +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.sets_cat import Sets +from sage.manifolds.point import ManifoldPoint + +class ManifoldSubset(UniqueRepresentation, Parent): + r""" + Subset of a topological manifold. + + The class :class:`ManifoldSubset` inherits from the generic + class :class:`~sage.structure.parent.Parent`. + The corresponding element class is + :class:`~sage.manifolds.point.ManifoldPoint`. + + Note that open subsets are not implemented directly by this class, but + by the derived class :class:`~sage.manifolds.manifold.TopologicalManifold` + (an open subset of a topological manifold being itself a topological + manifold). + + INPUT: + + - ``manifold`` -- topological manifold on which the subset is defined + - ``name`` -- string; name (symbol) given to the subset + - ``latex_name`` -- (default: ``None``) string; LaTeX symbol to + denote the subset; if none are provided, it is set to ``name`` + - ``category`` -- (default: ``None``) to specify the categeory; + if ``None``, the category for generic subsets is used + + EXAMPLES: + + A subset of a manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: from sage.manifolds.subset import ManifoldSubset + sage: A = ManifoldSubset(M, 'A', latex_name=r'\mathcal{A}') + sage: A + Subset A of the 2-dimensional topological manifold M + sage: latex(A) + \mathcal{A} + sage: A.is_subset(M) + True + + Instead of importing :class:`ManifoldSubset` in the global + namespace, it is recommended to use the method + :meth:`~sage.manifolds.subset.ManifoldSubset.subset` to create a new + subset:: + + sage: B = M.subset('B', latex_name=r'\mathcal{B}'); B + Subset B of the 2-dimensional topological manifold M + sage: M.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M] + + The manifold is itself a subset:: + + sage: isinstance(M, ManifoldSubset) + True + sage: M in M.subsets() + True + + Instances of :class:`ManifoldSubset` are parents:: + + sage: isinstance(A, Parent) + True + sage: A.category() + Category of subobjects of sets + sage: p = A.an_element(); p + Point on the 2-dimensional topological manifold M + sage: p.parent() + Subset A of the 2-dimensional topological manifold M + sage: p in A + True + sage: p in M + True + + """ + + Element = ManifoldPoint + + def __init__(self, manifold, name, latex_name=None, category=None): + r""" + Construct a manifold subset. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A'); A + Subset A of the 2-dimensional topological manifold M + sage: type(A) + + sage: A.category() + Category of subobjects of sets + sage: TestSuite(A).run(skip='_test_elements') + + .. NOTE:: + + ``_test_elements`` cannot be passed without a proper + coordinate definition of the subset. + + """ + if not isinstance(name, str): + raise TypeError("{} is not a string".format(name)) + self._name = name + if latex_name is None: + self._latex_name = self._name + else: + if not isinstance(latex_name, str): + raise TypeError("{} is not a string".format(latex_name)) + self._latex_name = latex_name + if category is None: + category = Sets().Subobjects() + base = None + else: + base = manifold._field + Parent.__init__(self, base=base, category=category) + if self is not manifold: + for dom in manifold._subsets: + if name == dom._name: + raise ValueError("the name '" + name + + "' is already used for another " + + "subset of the {}".format(manifold)) + manifold._subsets.add(self) + self._supersets = set([manifold, self]) # subsets containing self + self._subsets = set([self]) # subsets of self + self._top_subsets = set([self]) # subsets contained in self but not + # in another strict subset of self + self._intersections = {} # dict. of intersections with other subsets + # (key: subset name) + self._unions = {} # dict. of unions with other subsets (key: subset + # name) + self._open_covers = [] # list of open covers of self + self._is_open = False # a priori (may be redifined by subclasses) + self._manifold = manifold # the ambient manifold + + def _repr_(self): + r""" + String representation of the object. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: A._repr_() + 'Subset A of the 2-dimensional topological manifold M' + sage: repr(A) # indirect doctest + 'Subset A of the 2-dimensional topological manifold M' + + """ + return "Subset {} of the {}".format(self._name, self._manifold) + + def _latex_(self): + r""" + LaTeX representation of ``self``. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: A._latex_() + 'A' + sage: B = A.subset('B', latex_name=r'\mathcal{B}') + sage: B._latex_() + '\\mathcal{B}' + sage: latex(B) # indirect doctest + \mathcal{B} + + sage: M = Manifold(3, 'M', structure='topological') + sage: M._latex_() + 'M' + sage: latex(M) + M + sage: M = Manifold(3, 'M', latex_name=r'\mathcal{M}', + ....: structure='topological') + sage: M._latex_() + '\\mathcal{M}' + sage: latex(M) # indirect doctest + \mathcal{M} + """ + return self._latex_name + + #### Methods required for any Parent in the category of sets: + + def _element_constructor_(self, coords=None, chart=None, name=None, + latex_name=None, check_coords=True): + r""" + Construct a point in the subset from its coordinates in some chart. + + INPUT: + + - ``coords`` -- (default: ``None``) either (i) the point coordinates + (as a tuple or a list) in the chart ``chart`` or (ii) another point + in the subset + - ``chart`` -- (default: ``None``) chart in which the coordinates + are given; if none are provided, the coordinates are assumed to + refer to the subset's default chart + - ``name`` -- (default: ``None``) name given to the point + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + point; if none are provided, the LaTeX symbol is set to ``name`` + - ``check_coords`` -- (default: ``True``) determines whether + ``coords`` are valid coordinates for the chart ``chart``; + for symbolic coordinates, it is recommended to set ``check_coords`` + to ``False`` + + OUTPUT: + + - an instance of :class:`~sage.manifolds.point.ManifoldPoint` + representing a point in the current subset. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: p = M((-2,3)); p # coord in the default chart + Point on the 2-dimensional topological manifold M + sage: X(p) + (-2, 3) + + A generic subset has no default chart, so the chart must + be explicitly given:: + + sage: A = M.subset('A') + sage: p = A((-2,3), chart=X); p + Point on the 2-dimensional topological manifold M + sage: X(p) + (-2, 3) + sage: p.parent() + Subset A of the 2-dimensional topological manifold M + sage: p in A + True + + Coordinates in a chart with some coordinate restrictions:: + + sage: Y. = M.chart('u:(-1,1) v:(-1,1)') + sage: p = A((0,1/2), chart=Y); p + Point on the 2-dimensional topological manifold M + sage: Y(p) + (0, 1/2) + sage: p = A((0,1/2), chart=Y, check_coords=False); p + Point on the 2-dimensional topological manifold M + sage: Y(p) + (0, 1/2) + sage: p = A((3,1/2), chart=Y) + Traceback (most recent call last): + ... + ValueError: the coordinates (3, 1/2) are not valid on the Chart (M, (u, v)) + + Specifying the name of the point:: + + sage: p = A((-2,3), chart=X, name='p'); p + Point p on the 2-dimensional topological manifold M + + A point as entry:: + + sage: q = A(p); q + Point p on the 2-dimensional topological manifold M + sage: X(q) + (-2, 3) + + """ + if isinstance(coords, ManifoldPoint): + point = coords # for readability + # This should actually never happen by the coercion framework... + if point.parent() is self: + return point + if point in self: + resu = self.element_class(self, name=point._name, + latex_name=point._latex_name) + for chart, coords in point._coordinates.iteritems(): + resu._coordinates[chart] = coords + return resu + else: + raise ValueError("the {}".format(point) + + " is not in {}".format(self)) + return self.element_class(self, coords=coords, chart=chart, + name=name, latex_name=latex_name, + check_coords=check_coords) + + def _an_element_(self): + r""" + Construct some point in the subset. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: A = M.subset('A') + sage: p = A._an_element_(); p + Point on the 2-dimensional topological manifold M + sage: p in A + True + + """ + #!# should be improved... + return self.element_class(self) + + #### End of methods required for any Parent in the category of sets + + def __contains__(self, point): + r""" + Check whether ``point`` is contained in ``self``. + + TESTS:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: A = M.subset('A') + sage: p = A((-2,3), chart=X); p + Point on the 2-dimensional topological manifold M + sage: A.__contains__(p) + True + sage: p in A # indirect doctest + True + sage: A.__contains__(A.an_element()) + True + sage: q = M((0,0), chart=X); q + Point on the 2-dimensional topological manifold M + sage: A.__contains__(q) + False + """ + # for efficiency, a quick test first: + if point.parent() is self: + return True + if point.parent().is_subset(self): + return True + #!# should be improved once coordinate definition have been introduced + # in ManifoldSubset + return False + + def lift(self, p): + r""" + Return the lift of ``p`` to the ambient manifold of ``self``. + + INPUT: + + - ``p`` -- point of the subset + + OUTPUT: + + - the same point, considered as a point of the ambient manifold + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: A = M.open_subset('A', coord_def={X: x>0}) + sage: p = A((1, -2)); p + Point on the 2-dimensional topological manifold M + sage: p.parent() + Open subset A of the 2-dimensional topological manifold M + sage: q = A.lift(p); q + Point on the 2-dimensional topological manifold M + sage: q.parent() + 2-dimensional topological manifold M + sage: q.coord() + (1, -2) + sage: (p == q) and (q == p) + True + + """ + return self._manifold(p) + + def retract(self, p): + r""" + Return the retract of ``p`` to ``self``. + + INPUT: + + - ``p`` -- point of the ambient manifold + + OUTPUT: + + - the same point, considered as a point of the subset + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: X. = M.chart() + sage: A = M.open_subset('A', coord_def={X: x>0}) + sage: p = M((1, -2)); p + Point on the 2-dimensional topological manifold M + sage: p.parent() + 2-dimensional topological manifold M + sage: q = A.retract(p); q + Point on the 2-dimensional topological manifold M + sage: q.parent() + Open subset A of the 2-dimensional topological manifold M + sage: q.coord() + (1, -2) + sage: (q == p) and (p == q) + True + + Of course, if the point does not belong to ``A``, the ``retract`` + method fails:: + + sage: p = M((-1, 3)) # x < 0, so that p is not in A + sage: q = A.retract(p) + Traceback (most recent call last): + ... + ValueError: the Point on the 2-dimensional topological manifold M + is not in Open subset A of the 2-dimensional topological manifold M + + """ + return self(p) + + #### Accessors + + def manifold(self): + r""" + Return the ambient manifold of ``self``. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: A.manifold() + 2-dimensional topological manifold M + sage: A.manifold() is M + True + sage: B = A.subset('B') + sage: B.manifold() is M + True + + An alias is ``ambient``:: + + sage: A.ambient() is A.manifold() + True + + """ + return self._manifold + + ambient = manifold + + def is_open(self): + """ + Return if ``self`` is an open set. + + This method always returns ``False``, since open subsets must be + constructed as instances of the subclass + :class:`~sage.manifolds.manifold.TopologicalManifold` + (which redefines ``is_open``) + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: A.is_open() + False + + """ + return False + + def open_covers(self): + r""" + Return the list of open covers of the current subset. + + If the current subset, `A` say, is a subset of the manifold `M`, an + *open cover* of `A` is list (indexed set) `(U_i)_{i\in I}` of + open subsets of `M` such that + + .. MATH:: + + A \subset \bigcup_{i \in I} U_i. + + If `A` is open, we ask that the above inclusion is actually an + identity: + + .. MATH:: + + A = \bigcup_{i \in I} U_i. + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: M.open_covers() + [[2-dimensional topological manifold M]] + sage: U = M.open_subset('U') + sage: U.open_covers() + [[Open subset U of the 2-dimensional topological manifold M]] + sage: A = U.open_subset('A') + sage: B = U.open_subset('B') + sage: U.declare_union(A,B) + sage: U.open_covers() + [[Open subset U of the 2-dimensional topological manifold M], + [Open subset A of the 2-dimensional topological manifold M, + Open subset B of the 2-dimensional topological manifold M]] + sage: V = M.open_subset('V') + sage: M.declare_union(U,V) + sage: M.open_covers() + [[2-dimensional topological manifold M], + [Open subset U of the 2-dimensional topological manifold M, + Open subset V of the 2-dimensional topological manifold M], + [Open subset A of the 2-dimensional topological manifold M, + Open subset B of the 2-dimensional topological manifold M, + Open subset V of the 2-dimensional topological manifold M]] + + """ + return self._open_covers + + def subsets(self): + r""" + Return the set of subsets that have been defined on the + current subset. + + OUTPUT: + + - a Python set containing all the subsets that have been defined on + the current subset + + .. NOTE:: + + To get the subsets as a list, used the method + :meth:`list_of_subsets` instead. + + EXAMPLES: + + Subsets of a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.subset('V') + sage: M.subsets() # random (set output) + {Subset V of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M, + Open subset U of the 2-dimensional topological manifold M} + sage: type(M.subsets()) + + sage: U in M.subsets() + True + + The method :meth:`list_of_subsets` returns a list (sorted + alphabetically by the subset names) instead of a set:: + + sage: M.list_of_subsets() + [2-dimensional topological manifold M, + Open subset U of the 2-dimensional topological manifold M, + Subset V of the 2-dimensional topological manifold M] + + """ + return frozenset(self._subsets) + + def list_of_subsets(self): + r""" + Return the list of subsets that have been defined on the current + subset. + + The list is sorted by the alphabetical names of the subsets. + + OUTPUT: + + - a list containing all the subsets that have been defined on + the current subset + + .. NOTE:: + + To get the subsets as a Python set, used the method + :meth:`subsets` instead. + + EXAMPLES: + + Subsets of a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: U = M.open_subset('U') + sage: V = M.subset('V') + sage: M.list_of_subsets() + [2-dimensional topological manifold M, + Open subset U of the 2-dimensional topological manifold M, + Subset V of the 2-dimensional topological manifold M] + + The method :meth:`subsets` returns a set instead of a list:: + + sage: M.subsets() # random (set output) + {Subset V of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M, + Open subset U of the 2-dimensional topological manifold M} + + """ + return sorted(self._subsets, key=lambda x: x._name) + + def get_subset(self, name): + r""" + Get a subset by its name. + + The subset must have been previously created by the method + :meth:`subset` (or + :meth:`~sage.manifolds.manifold.TopologicalManifold.open_subset`) + + INPUT: + + - ``name`` -- (string) name of the subset + + OUTPUT: + + - instance of :class:`~sage.manifolds.subset.ManifoldSubset` (or + of the derived class + :class:`~sage.manifolds.manifold.TopologicalManifold` for an open + subset) representing the subset whose name is ``name`` + + EXAMPLES:: + + sage: M = Manifold(4, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = A.subset('B') + sage: U = M.open_subset('U') + sage: M.list_of_subsets() + [Subset A of the 4-dimensional topological manifold M, + Subset B of the 4-dimensional topological manifold M, + 4-dimensional topological manifold M, + Open subset U of the 4-dimensional topological manifold M] + sage: M.get_subset('A') + Subset A of the 4-dimensional topological manifold M + sage: M.get_subset('A') is A + True + sage: M.get_subset('B') is B + True + sage: A.get_subset('B') is B + True + sage: M.get_subset('U') + Open subset U of the 4-dimensional topological manifold M + sage: M.get_subset('U') is U + True + + """ + for ss in self._subsets: + if ss._name == name: + return ss + raise ValueError("no subset of name '{}' found".format(name)) + + #### End of accessors + + def is_subset(self, other): + r""" + Return ``True`` if and only if ``self`` is included in ``other``. + + EXAMPLES: + + Subsets on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A') + sage: b = a.subset('B') + sage: c = M.subset('C') + sage: a.is_subset(M) + True + sage: b.is_subset(a) + True + sage: b.is_subset(M) + True + sage: a.is_subset(b) + False + sage: c.is_subset(a) + False + """ + return self in other._subsets + + def declare_union(self, dom1, dom2): + r""" + Declare that the current subset is the union of two subsets. + + Suppose `U` is the current subset, then this method declares + that `U` + + .. MATH:: + + U = U_1 \cup U_2, + + where `U_1 \subset U` and `U_2 \subset U`. + + INPUT: + + - ``dom1`` -- the subset `U_1` + - ``dom2`` -- the subset `U_2` + + EXAMPLES:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: A = M.subset('A') + sage: B = M.subset('B') + sage: M.declare_union(A, B) + sage: A.union(B) + 2-dimensional topological manifold M + + """ + if dom1 == dom2: + if dom1 != self: + raise ValueError("the union of two identical sets must be " + + "this set") + return + if not dom1.is_subset(self): + raise TypeError("the {} is not a subset of ".format(dom1) + + "the {}".format(self)) + if not dom2.is_subset(self): + raise TypeError("the {} is not a subset of ".format(dom2) + + "the {}".format(self)) + dom1._unions[dom2._name] = self + dom2._unions[dom1._name] = self + for oc1 in dom1._open_covers: + for oc2 in dom2._open_covers: + oc = oc1[:] + for s in oc2: + if s not in oc: + oc.append(s) + self._open_covers.append(oc) + + def point(self, coords=None, chart=None, name=None, latex_name=None): + r""" + Define a point in ``self``. + + See :class:`~sage.manifolds.point.ManifoldPoint` for a + complete documentation. + + INPUT: + + - ``coords`` -- the point coordinates (as a tuple or a list) in the + chart specified by ``chart`` + - ``chart`` -- (default: ``None``) chart in which the point + coordinates are given; if ``None``, the coordinates are assumed + to refer to the default chart of the current subset + - ``name`` -- (default: ``None``) name given to the point + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + point; if ``None``, the LaTeX symbol is set to ``name`` + + OUTPUT: + + - the declared point, as an instance of + :class:`~sage.manifolds.point.ManifoldPoint` + + EXAMPLES: + + Points on a 2-dimensional manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: c_xy. = M.chart() + sage: p = M.point((1,2), name='p'); p + Point p on the 2-dimensional topological manifold M + sage: p in M + True + sage: a = M.open_subset('A') + sage: c_uv. = a.chart() + sage: q = a.point((-1,0), name='q'); q + Point q on the 2-dimensional topological manifold M + sage: q in a + True + sage: p._coordinates + {Chart (M, (x, y)): (1, 2)} + sage: q._coordinates + {Chart (A, (u, v)): (-1, 0)} + """ + return self.element_class(self, coords=coords, chart=chart, + name=name, latex_name=latex_name) + + #### Construction of new sets from self: + + def subset(self, name, latex_name=None, is_open=False): + r""" + Create a subset of the current subset. + + INPUT: + + - ``name`` -- name given to the subset + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote + the subset; if none are provided, it is set to ``name`` + - ``is_open`` -- (default: ``False``) if ``True``, the created subset + is assumed to be open with respect to the manifold's topology + + OUTPUT: + + - the subset, as an instance of :class:`ManifoldSubset`, or + of the derived class + :class:`~sage.manifolds.manifold.TopologicalManifold` + if ``is_open`` is ``True`` + + EXAMPLES: + + Creating a subset of a manifold:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A'); a + Subset A of the 2-dimensional topological manifold M + + Creating a subset of ``A``:: + + sage: b = a.subset('B', latex_name=r'\mathcal{B}'); b + Subset B of the 2-dimensional topological manifold M + sage: latex(b) + \mathcal{B} + + We have then:: + + sage: b.is_subset(a) + True + sage: b in a.subsets() + True + """ + if is_open: + return self.open_subset(name, latex_name=latex_name) + res = ManifoldSubset(self._manifold, name, latex_name=latex_name) + res._supersets.update(self._supersets) + for sd in self._supersets: + sd._subsets.add(res) + self._top_subsets.add(res) + return res + + def superset(self, name, latex_name=None, is_open=False): + r""" + Create a superset of the current subset. + + A *superset* is a manifold subset in which the current subset is + included. + + INPUT: + + - ``name`` -- name given to the superset + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote + the superset; if none are provided, it is set to ``name`` + - ``is_open`` -- (default: ``False``) if ``True``, the created subset + is assumed to be open with respect to the manifold's topology + + OUTPUT: + + - the superset, as an instance of :class:`ManifoldSubset` or + of the derived class + :class:`~sage.manifolds.manifold.TopologicalManifold` + if ``is_open`` is ``True`` + + EXAMPLES: + + Creating some superset of a given subset:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A') + sage: b = a.superset('B'); b + Subset B of the 2-dimensional topological manifold M + sage: b.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M] + sage: a._supersets # random (set output) + {Subset B of the 2-dimensional topological manifold M, + Subset A of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M} + + The superset of the whole manifold is itself:: + + sage: M.superset('SM') is M + True + + Two supersets of a given subset are a priori different:: + + sage: c = a.superset('C') + sage: c == b + False + + """ + if self is self._manifold: + return self + if is_open: + res = self._manifold.open_subset(name, latex_name=latex_name) + else: + res = ManifoldSubset(self._manifold, name, latex_name=latex_name) + res._subsets.update(self._subsets) + for sd in self._subsets: + sd._supersets.add(res) + if is_open and self._is_open: + res._atlas = list(self._atlas) + res._top_charts = list(self._top_charts) + res._coord_changes = dict(self._coord_changes) + res._def_chart = self._def_chart + return res + + def intersection(self, other, name=None, latex_name=None): + r""" + Return the intersection of the current subset with another subset. + + INPUT: + + - ``other`` -- another subset of the same manifold + - ``name`` -- (default: ``None``) name given to the intersection + in the case the latter has to be created; the default is + ``self._name`` inter ``other._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + intersection in the case the latter has to be created; the default + is built upon the symbol `\cap` + + OUTPUT: + + - instance of :class:`ManifoldSubset` representing the + subset that is the intersection of the current subset with ``other`` + + EXAMPLES: + + Intersection of two subsets:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A') + sage: b = M.subset('B') + sage: c = a.intersection(b); c + Subset A_inter_B of the 2-dimensional topological manifold M + sage: a.list_of_subsets() + [Subset A of the 2-dimensional topological manifold M, + Subset A_inter_B of the 2-dimensional topological manifold M] + sage: b.list_of_subsets() + [Subset A_inter_B of the 2-dimensional topological manifold M, + Subset B of the 2-dimensional topological manifold M] + sage: c._supersets # random (set output) + {Subset B of the 2-dimensional topological manifold M, + Subset A_inter_B of the 2-dimensional topological manifold M, + Subset A of the 2-dimensional topological manifold M, + 2-dimensional topological manifold M} + + Some checks:: + + sage: (a.intersection(b)).is_subset(a) + True + sage: (a.intersection(b)).is_subset(a) + True + sage: a.intersection(b) is b.intersection(a) + True + sage: a.intersection(a.intersection(b)) is a.intersection(b) + True + sage: (a.intersection(b)).intersection(a) is a.intersection(b) + True + sage: M.intersection(a) is a + True + sage: a.intersection(M) is a + True + + """ + if other._manifold != self._manifold: + raise ValueError("the two subsets do not belong to the same manifold") + # Particular cases: + if self is self._manifold: + return other + if other is self._manifold: + return self + if self in other._subsets: + return self + if other in self._subsets: + return other + # Generic case: + if other._name in self._intersections: + # the intersection has already been created: + return self._intersections[other._name] + else: + # the intersection must be created: + if latex_name is None: + if name is None: + latex_name = self._latex_name + r'\cap ' + other._latex_name + else: + latex_name = name + if name is None: + name = self._name + "_inter_" + other._name + if self._is_open and other._is_open: + res = self.open_subset(name, latex_name=latex_name) + else: + res = self.subset(name, latex_name=latex_name) + res._supersets.update(other._supersets) + for sd in other._supersets: + sd._subsets.add(res) + other._top_subsets.add(res) + self._intersections[other._name] = res + other._intersections[self._name] = res + return res + + def union(self, other, name=None, latex_name=None): + r""" + Return the union of the current subset with another subset. + + INPUT: + + - ``other`` -- another subset of the same manifold + - ``name`` -- (default: ``None``) name given to the union in the + case the latter has to be created; the default is + ``self._name`` union ``other._name`` + - ``latex_name`` -- (default: ``None``) LaTeX symbol to denote the + union in the case the latter has to be created; the default + is built upon the symbol `\cup` + + OUTPUT: + + - instance of :class:`ManifoldSubset` representing the + subset that is the union of the current subset with ``other`` + + EXAMPLES: + + Union of two subsets:: + + sage: M = Manifold(2, 'M', structure='topological') + sage: a = M.subset('A') + sage: b = M.subset('B') + sage: c = a.union(b); c + Subset A_union_B of the 2-dimensional topological manifold M + sage: a._supersets # random (set output) + set([subset 'A_union_B' of the 2-dimensional manifold 'M', + 2-dimensional manifold 'M', + subset 'A' of the 2-dimensional manifold 'M']) + sage: b._supersets # random (set output) + set([subset 'B' of the 2-dimensional manifold 'M', + 2-dimensional manifold 'M', + subset 'A_union_B' of the 2-dimensional manifold 'M']) + sage: c._subsets # random (set output) + set([subset 'A_union_B' of the 2-dimensional manifold 'M', + subset 'A' of the 2-dimensional manifold 'M', + subset 'B' of the 2-dimensional manifold 'M']) + + Some checks:: + + sage: a.is_subset(a.union(b)) + True + sage: b.is_subset(a.union(b)) + True + sage: a.union(b) is b.union(a) + True + sage: a.union(a.union(b)) is a.union(b) + True + sage: (a.union(b)).union(a) is a.union(b) + True + sage: a.union(M) is M + True + sage: M.union(a) is M + True + + """ + if other._manifold != self._manifold: + raise ValueError("the two subsets do not belong to the same manifold") + # Particular cases: + if (self is self._manifold) or (other is self._manifold): + return self._manifold + if self in other._subsets: + return other + if other in self._subsets: + return self + # Generic case: + if other._name in self._unions: + # the union has already been created: + return self._unions[other._name] + else: + # the union must be created: + if latex_name is None: + if name is None: + latex_name = self._latex_name + r'\cup ' + other._latex_name + else: + latex_name = name + if name is None: + name = self._name + "_union_" + other._name + res_open = self._is_open and other._is_open + res = self.superset(name, latex_name, is_open=res_open) + res._subsets.update(other._subsets) + res._top_subsets.add(self) + res._top_subsets.add(other) + for sd in other._subsets: + sd._supersets.add(res) + if res._is_open: + for chart in other._atlas: + if chart not in res._atlas: + res._atlas.append(chart) + for chart in other._top_charts: + if chart not in res._top_charts: + res._top_charts.append(chart) + res._coord_changes.update(other._coord_changes) + self._unions[other._name] = res + other._unions[self._name] = res + # Open covers of the union: + for oc1 in self._open_covers: + for oc2 in other._open_covers: + oc = oc1[:] + for s in oc2: + if s not in oc: + oc.append(s) + res._open_covers.append(oc) + return res + + #### End of construction of new sets from self + diff --git a/src/sage/matrix/constructor.py b/src/sage/matrix/constructor.py index b6a93e5abd6..84c22c038ab 100644 --- a/src/sage/matrix/constructor.py +++ b/src/sage/matrix/constructor.py @@ -21,6 +21,7 @@ from sage.rings.ring import is_Ring import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import vector +from sage.structure.coerce import py_scalar_parent from sage.structure.element import is_Vector from sage.structure.sequence import Sequence from sage.rings.real_double import RDF @@ -792,18 +793,22 @@ def prepare(w): Traceback (most recent call last): ... TypeError: unable to find a common ring for all elements + + TESTS: + + Check that :trac:`19920` is fixed:: + + sage: import numpy + sage: matrix([[numpy.int8(1)]]) + [1] """ - if 0 == len(w): + if not w: return Sequence([], rings.ZZ), rings.ZZ entries = Sequence(w) ring = entries.universe() - if ring is int or ring is long: - ring = rings.ZZ - elif ring is float: - ring = rings.RDF - elif ring is complex: - ring = rings.CDF - elif not is_Ring(ring): + if isinstance(ring,type): + ring = py_scalar_parent(ring) + if not is_Ring(ring): raise TypeError("unable to find a common ring for all elements") return entries, ring @@ -1018,7 +1023,7 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', *args, **kwds) eigenvectors, if computed by hand, will have only integer entries. - - ``*args, **kwds`` - arguments and keywords to describe additional + - ``*args, **kwds`` - arguments and keywords to describe additional properties. See more detailed documentation below. .. warning:: @@ -1175,9 +1180,14 @@ def random_matrix(ring, nrows, ncols=None, algorithm='randomize', *args, **kwds) The default implementation of :meth:`~sage.matrix.matrix2.randomize` relies on the ``random_element()`` method for the base ring. The ``density`` and - ``sparse`` keywords behave as described above. :: + ``sparse`` keywords behave as described above. Since we have a different + randomisation when using the optional meataxe package, we have to make sure + that we use the default implementation in this test:: sage: K.=FiniteField(3^2) + sage: from sage.matrix.matrix_generic_dense import Matrix_generic_dense + sage: MS = MatrixSpace(K, 2, 5) + sage: MS._MatrixSpace__matrix_class = Matrix_generic_dense sage: random_matrix(K, 2, 5) [ 1 a 1 2*a + 1 2] [ 2*a a + 2 0 2 1] diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f418222ebe3..0c04197c300 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -31,9 +31,11 @@ include "sage/ext/python.pxi" include "sage/ext/interrupt.pxi" from sage.misc.randstate cimport randstate, current_randstate +from sage.structure.coerce cimport py_scalar_parent from sage.structure.sequence import Sequence from sage.structure.element import is_Vector from sage.misc.misc import verbose, get_verbose +from sage.rings.ring import is_Ring from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.integer_ring import ZZ, is_IntegerRing from sage.rings.integer import Integer @@ -779,7 +781,7 @@ cdef class Matrix(matrix1.Matrix): 36.0000000000000 The permanent above is directed to the Sloane's sequence :oeis:`A079908` - ("The Dancing School Problems") for which the third term is 36: + ("The Dancing School Problems") for which the third term is 36: :: @@ -1876,6 +1878,12 @@ cdef class Matrix(matrix1.Matrix): sage: m.apply_map(lambda x: x*x, sparse=True).parent() Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring + + Check that :trac:`19920` is fixed:: + + sage: matrix.ones(2).apply_map(lambda x: int(-3)) + [-3 -3] + [-3 -3] """ if self._nrows == 0 or self._ncols == 0: if sparse is None or self.is_sparse() is sparse: @@ -1894,6 +1902,11 @@ cdef class Matrix(matrix1.Matrix): if R is None: R = sage.structure.sequence.Sequence(values).universe() + if isinstance(R, type): + R = py_scalar_parent(R) + if not is_Ring(R): + raise TypeError("unable to find a common ring for all elements") + if sparse is None or sparse is self.is_sparse(): M = self.parent().change_ring(R) else: @@ -3352,8 +3365,6 @@ cdef class Matrix(matrix1.Matrix): verbose ... verbose 1 () computing right kernel matrix over an arbitrary field for 3x4 matrix ... - verbose 1 () done computing right kernel matrix over an arbitrary field for 3x4 matrix - ... Vector space of degree 4 and dimension 2 over Finite Field in a of size 5^2 Basis matrix: [ 1 0 3*a + 4 2*a + 2] @@ -3800,13 +3811,25 @@ cdef class Matrix(matrix1.Matrix): [ 0 1 2*a 3*a + 3] sage: A*K.basis_matrix().transpose() == zero_matrix(F, 3, 2) True - sage: B = copy(A) + + In the following test, we have to force usage of + :class:`~sage.matrix.matrix_generic_dense.Matrix_generic_dense`, + since the option ``basis = 'pivot'`` would simply yield the same + result as the previous test, if the optional meataxe package is + installed. :: + + sage: from sage.matrix.matrix_generic_dense import Matrix_generic_dense + sage: B = Matrix_generic_dense(A.parent(), A.list(), False, False) sage: P = B.right_kernel(basis = 'pivot'); P Vector space of degree 4 and dimension 2 over Finite Field in a of size 5^2 User basis matrix: [ 4 4 1 0] [ a + 2 3*a + 3 0 1] - sage: B*P.basis_matrix().transpose() == zero_matrix(F, 3, 2) + + If the optional meataxe package is installed, we again have to make sure + to work with a copy of B that has the same type as ``P.basis_matrix()``:: + + sage: B.parent()(B.list())*P.basis_matrix().transpose() == zero_matrix(F, 3, 2) True sage: K == P True @@ -8479,7 +8502,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: filename = tmp_filename(ext='.png') sage: img.save(filename) - sage: open(filename).read().startswith('\x89PNG') + sage: open(filename).read().startswith('\x89PNG') True """ cdef int x, y, _x, _y, v, bi, bisq @@ -12200,7 +12223,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" r""" Determines if a real or symmetric matrix is positive definite. - A square matrix `A` is postive definite if it is + A square matrix `A` is positive definite if it is symmetric with real entries or Hermitan with complex entries, and for every non-zero vector `\vec{x}` @@ -12239,7 +12262,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" A real symmetric matrix that is positive definite, as evidenced by the positive entries for the diagonal - matrix of the indefinite factorization and the postive + matrix of the indefinite factorization and the positive determinants of the leading principal submatrices. :: sage: A = matrix(QQ, [[ 4, -2, 4, 2], diff --git a/src/sage/matrix/matrix_gfpn_dense.pxd b/src/sage/matrix/matrix_gfpn_dense.pxd new file mode 100644 index 00000000000..34487536b14 --- /dev/null +++ b/src/sage/matrix/matrix_gfpn_dense.pxd @@ -0,0 +1,29 @@ +#***************************************************************************** +# Copyright (C) 2015 Simon King +# +# 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/ +#***************************************************************************** + +cdef class FieldConverter_class: + cdef object field # that's a function converting an int to a field element + cpdef object int_to_field(self, int x) + cpdef int field_to_int(self, x) + +from sage.matrix.matrix_dense cimport Matrix_dense +from sage.structure.element cimport Matrix +from sage.libs.meataxe cimport * + +cdef class Matrix_gfpn_dense(Matrix_dense): + cdef Matrix_t *Data + cdef FieldConverter_class _converter + cdef Matrix_gfpn_dense _new(self, Py_ssize_t nrows, Py_ssize_t ncols) + cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value) + cdef inline int get_unsafe_int(self, Py_ssize_t i, Py_ssize_t j) + cdef list _rowlist_(self, i, j=*) + cdef Matrix _matrix_times_matrix_(self, Matrix right) + cpdef Matrix_gfpn_dense _multiply_classical(Matrix_gfpn_dense self, Matrix_gfpn_dense right) + cpdef Matrix_gfpn_dense _multiply_strassen(Matrix_gfpn_dense self, Matrix_gfpn_dense right, cutoff=*) diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx new file mode 100644 index 00000000000..7f5c1103316 --- /dev/null +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -0,0 +1,1630 @@ +r""" +Dense Matrices over `\mathbb F_q`, with `q<255` + +This module is a wrapper for version 2.4.24 of the Aachen +`C-MeatAxe `_, +improved by an implementation of the Winograd-Strassen multiplication +algorithm. It provides matrices over the finite field `\mathbb F_q`, +where `q\le 255`. + +By default, it is only used when `q` is odd and not prime, because other +matrix implementations in SageMath perform better for prime fields or in +characteristic two. + +AUTHORS: + +- Simon King (2015-09): initial version + +""" + +#***************************************************************************** +# Copyright (C) 2015 Simon King +# +# 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/ +#***************************************************************************** + + +## Define an environment variable that enables MeatAxe to find +## its multiplication tables. + +from sage.env import DOT_SAGE +import os +cdef extern from "Python.h": + object PyString_FromStringAndSize(char *s, Py_ssize_t len) + char* PyString_AsString(object string) +MtxLibDir = PyString_AsString(os.path.join(DOT_SAGE,'meataxe')) + +#################### +# +# import sage types +# +#################### + +from sage.rings.integer import Integer +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.rings.finite_rings.integer_mod import IntegerMod_int +from sage.matrix.constructor import random_matrix +from sage.rings.arith import is_prime_power, factor +from sage.matrix.matrix_space import MatrixSpace +from sage.misc.randstate import current_randstate +from sage.misc.cachefunc import cached_method, cached_function +from sage.structure.element cimport Element, ModuleElement, RingElement, Matrix + +from libc.stdlib cimport free +from sage.ext.memory cimport check_realloc +from libc.string cimport memset, memcpy + +cimport sage.matrix.matrix0 + +include 'sage/ext/stdsage.pxi' + +#################### +# +# auxiliary functions +# +#################### +import sys +from libc.string cimport memcpy + +# Fast conversion from field to int and int to field +cdef class FieldConverter_class: + """ + An auxiliary class, used to convert between and finite field element + + This class is for non-prime fields only. The method + :meth:`int_to_field` exists for speed. The method + :meth:`field_to_int` exists in order to have a common interface + for elements of prime and non-prime fields; see + :class:`PrimeFieldConverter_class`. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class # optional: meataxe + sage: F. = GF(125) + sage: C = FieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(15) # optional: meataxe + 3*y + sage: F.fetch_int(15) # optional: meataxe + 3*y + sage: %timeit C.int_to_field(15) # not tested + 625 loops, best of 3: 1.04 µs per loop + sage: %timeit F.fetch_int(15) # not tested + 625 loops, best of 3: 3.97 µs per loop + sage: C.field_to_int(y) # optional: meataxe + 5 + sage: y.integer_representation() + 5 + + """ + def __init__(self, field): + """ + INPUT: + + A finite *non-prime* field. This assumption is not tested. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class # optional: meataxe + sage: F. = GF(125) + sage: C = FieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(15) # optional: meataxe + 3*y + sage: F.fetch_int(15) + 3*y + sage: C.field_to_int(y) # optional: meataxe + 5 + sage: y.integer_representation() + 5 + + """ + self.field = field._cache.fetch_int + cpdef object int_to_field(self, int x): + """ + Fetch a python int into the field. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class # optional: meataxe + sage: F. = GF(125) + sage: C = FieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(15) # optional: meataxe + 3*y + sage: F.fetch_int(15) + 3*y + + """ + return self.field(x) + cpdef int field_to_int(self, x): + """ + Represent a field element by a python int. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import FieldConverter_class # optional: meataxe + sage: F. = GF(125) + sage: C = FieldConverter_class(F) # optional: meataxe + sage: C.field_to_int(y) # optional: meataxe + 5 + sage: y.integer_representation() + 5 + + """ + return x.integer_representation() + +cdef class PrimeFieldConverter_class(FieldConverter_class): + """ + An auxiliary class, used to convert between and finite field element + + This class is for prime fields only. The methods + :meth:`int_to_field` and :meth:`field_to_int` exist in order to + have a common interface for elements of prime and non-prime fields; + see :class:`FieldConverter_class`. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class # optional: meataxe + sage: F = GF(5) + sage: C = PrimeFieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(int(2)) # optional: meataxe + 2 + sage: F(2) + 2 + sage: C.field_to_int(F(2)) # optional: meataxe + 2 + sage: int(F(2)) + 2 + + """ + def __init__(self, field): + """ + INPUT: + + A finite *prime* field. This assumption is not tested. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class # optional: meataxe + sage: F = GF(5) + sage: C = PrimeFieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(int(2)) # optional: meataxe + 2 + sage: F(2) + 2 + sage: C.field_to_int(F(2)) # optional: meataxe + 2 + sage: int(F(2)) + 2 + + """ + self.field = field + cpdef object int_to_field(self, int x): + """ + Fetch a python int into the field. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class # optional: meataxe + sage: F = GF(5) + sage: C = PrimeFieldConverter_class(F) # optional: meataxe + sage: C.int_to_field(int(2)) # optional: meataxe + 2 + sage: F(2) + 2 + + """ + return IntegerMod_int(self.field, x) + cpdef int field_to_int(self, x): + """ + Represent a field element by a python int. + + EXAMPLE:: + + sage: from sage.matrix.matrix_gfpn_dense import PrimeFieldConverter_class # optional: meataxe + sage: F = GF(5) + sage: C = PrimeFieldConverter_class(F) # optional: meataxe + sage: C.field_to_int(F(2)) # optional: meataxe + 2 + sage: int(F(2)) + 2 + + """ + return int(x) + +cdef dict _converter_cache = {} +cdef FieldConverter_class FieldConverter(field): + """ + Return a :class:`FieldConverter_class` or :class:`PrimeFieldConverter_class` instance, + depending whether the field is prime or not. + + EXAMPLE:: + + sage: MS = MatrixSpace(GF(5^3,'y'),2) + sage: A = MS.random_element() + sage: A*2 == A+A # indirect doctest + True + sage: A = MS.random_element() + sage: A*2 == A+A + True + + """ + try: + return _converter_cache[field] + except KeyError: + if field.is_prime_field(): + return _converter_cache.setdefault(field, PrimeFieldConverter_class(field)) + return _converter_cache.setdefault(field, FieldConverter_class(field)) + +###################################### +## Error handling for MeatAxe, to prevent immediate exit of the program + +cdef dict ErrMsg = { + "Not enough memory": MemoryError, + "Time limit exceeded": RuntimeError, + "Division by zero": ZeroDivisionError, + "Bad file format": IOError, + "Bad argument": ValueError, + "Argument out of range": IndexError, + + "Matrix not in echelon form": ValueError, + "Matrix not square": ArithmeticError, + "Incompatible objects": TypeError, + + "Bad syntax, try `-help'": SyntaxError, + "Bad usage of option, try `-help'": ValueError, + "Bad number of arguments, try `-help'": ValueError, + + "Not a matrix": TypeError, + "Not a permutation": TypeError +} + +from cpython.exc cimport PyErr_SetObject + +cdef void ErrorHandler(MtxErrorRecord_t *err): + PyErr_SetObject(ErrMsg.get(err.Text, SystemError), "{} in file {} (line {})".format(err.Text, err.FileInfo.BaseName, err.LineNo)) + +MtxSetErrorHandler(ErrorHandler) + +###################################### +## +## Wrapper for MeatAxe matrices +## +###################################### + +cdef class Matrix_gfpn_dense(Matrix_dense): + r""" + Dense matrices over `\mathbb F_q`, `q<255` odd and not prime. + + NOTE: + + This class uses a major modification of the Aachen C-MeatAxe + as backend. In principle, it would also work for prime fields + and in characteristic two. However, other matrices in Sage, + relying on linbox, m4ri or m4rie, are more efficient in these + cases. + + EXAMPLES:: + + sage: M = MatrixSpace(GF(25,'z'),2,3)([1,2,3,4,5,6]) + sage: print M + [1 2 3] + [4 0 1] + sage: type(M) # optional: meataxe + + + The documentation of the ``__init__`` methods shows further + ways of creating a :class:`Matrix_gfpn_dense` instance. + However, these should only be of internal use. + + """ +################## +## Init, Dealloc, Copy + def __cinit__(self, parent=None, entries=None, *args, **kwds): + """ + TESTS:: + + sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense # optional: meataxe + sage: Matrix_gfpn_dense.__new__(Matrix_gfpn_dense) # optional: meataxe + [] + sage: Matrix_gfpn_dense(MatrixSpace(GF(64,'z'),4), None) # optional: meataxe + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] + + """ + if parent is None: # this makes Matrix_gfpn_dense.__new__(Matrix_gfpn_dense) work, + # returning a non-initialised matrix + return + if isinstance(parent, basestring): # this allows to provide a file when initialising a matrix + return + cdef int f = parent.base_ring().order() + cdef int nrows = parent.nrows() + cdef int ncols = parent.ncols() + self.Data = MatAlloc(f, nrows, ncols) + + def __dealloc__(self): + """ + TESTS:: + + sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense # optional: meataxe + sage: Matrix_gfpn_dense.__new__(Matrix_gfpn_dense) # optional: meataxe + [] + sage: M = None + sage: M = Matrix_gfpn_dense(MatrixSpace(GF(64,'z'),4), None) # optional: meataxe + sage: del M # indirect doctest + """ + if self.Data != NULL: + MatFree(self.Data) + self.Data = NULL + + def __init__(self, parent, data=None, mutable=True, copy=False, coerce=False): + """ + Matrix extension class using libmeataxe as backend + + INPUT: + + Instances of this class can be created by providing one of + the following input data, where ``q<255`` is a prime power, + ``m,n`` are non-negative integers, and `a_{11},...,a_{mn}` + can be coerced into ``GF(q)``. Note that a user should + create these instances via the matrix constructors; what + we explain here is for internal use only! + + - None => empty matrix over an unspecified field (used for unpickling) + - a string ``f`` ==> load matrix from the file named ``f`` + - A matrix space of `m\\times n` matrices over GF(q) and either + + - a list `[a_{11},a_{12},...,a_{1n},a_{21},...,a_{m1},...,a_{mn}]`, + which results in a matrix with the given marks + - ``None``, which is the fastest way to creata a zero matrix. + - an element of GF(q), which results in a diagonal matrix with the + given element on the diagonal. + + If the optional parameter ``mutable`` is ``False`` (by default, + it is ``True``), the resulting matrix can not be changed, and + it can be used as key in a Python dictionary. + + The arguments ``copy`` and ``coerce`` are ignored, they are only + here for a common interface with :class:`~sage.matrix.matrix.Matrix`. + + EXAMPLES:: + + sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense # optional: meataxe + + 1. Creating an empty matrix:: + + sage: Matrix_gfpn_dense(None) # optional: meataxe + [] + + 2. Creating a zero (3x2)-matrix:: + + sage: Matrix_gfpn_dense(MatrixSpace(GF(4,'z'),3,2)) # optional: meataxe + [0 0] + [0 0] + [0 0] + + 3. Creating a matrix from a list or list of lists:: + + sage: Matrix_gfpn_dense(MatrixSpace(GF(5),2,3),[1,2,3,4,5,6]) # optional: meataxe + [1 2 3] + [4 0 1] + sage: Matrix_gfpn_dense(MatrixSpace(GF(5),2,3),[[1,2,3],[4,5,6]]) # optional: meataxe + [1 2 3] + [4 0 1] + + 4. Creating a diagonal matrix:: + + sage: M = Matrix_gfpn_dense(MatrixSpace(GF(7),5),2); M # optional: meataxe + [2 0 0 0 0] + [0 2 0 0 0] + [0 0 2 0 0] + [0 0 0 2 0] + [0 0 0 0 2] + + 5. Creating a matrix from a file in MeatAxe format. + + This is not tested. + + TESTS:: + + sage: MS = MatrixSpace(GF(125,'y'),2) # indirect doctest + sage: A = MS(0) + sage: A.left_kernel() + Vector space of degree 2 and dimension 2 over Finite Field in y of size 5^3 + Basis matrix: + [1 0] + [0 1] + sage: A.right_kernel() + Vector space of degree 2 and dimension 2 over Finite Field in y of size 5^3 + Basis matrix: + [1 0] + [0 1] + + """ + if parent is None: + self._is_immutable = False + self._ncols = 0 + self._nrows = 0 + self._cache = {} + return + if isinstance(parent, basestring): # load from file + FILE = os.path.realpath(parent) + try: + fsock = open(FILE,"rb",0) + fsock.close() + except (OSError,IOError): + return + self.Data = MatLoad(FILE) + FfSetField(self.Data.Field) + B = GF(self.Data.Field, 'z') + parent = MatrixSpace(B, self.Data.Nor, self.Data.Noc) + self._is_immutable = False + self._parent = parent + self._base_ring = B + self._converter = FieldConverter(B) + self._ncols = self.Data.Noc + self._nrows = self.Data.Nor + self._cache = {} + return + + if not self.Data: # should have been initialised by __cinit__ + raise MemoryError, "Error allocating memory for MeatAxe matrix" + Matrix_dense.__init__(self, parent) + self._is_immutable = not mutable + B = self._base_ring + self._converter = FieldConverter(B) + if data is None: + return + + cdef int i,j + cdef FEL f + cdef PTR x + if not isinstance(data,list): + if not data: + return + if self._nrows != self._ncols: + raise ValueError("Cannot initialise non-square matrix from {}".format(data)) + f = FfFromInt(self._converter.field_to_int(self._coerce_element(data))) + x = self.Data.Data + for j from 0 <= j < self.Data.Noc: + FfInsert(x,j,f) + FfStepPtr(&x) + return + + x = self.Data.Data + cdef int nr = self.Data.Nor + cdef int nc = self.Data.Noc + assert self._ncols == nc + assert self._nrows == nr + if nr==0 or nc==0: + return + if len(data)self.Data.Data,self.Data.RowSize * self.Data.Nor), + not self._is_immutable) # for backward compatibility with the group cohomology package + else: + return mtx_unpickle, (0, 0, 0, '', not self._is_immutable) + + cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Get an element without checking. + + TEST:: + + sage: F. = GF(9) + sage: M = MatrixSpace(F,3)(sorted(list(F))) + sage: type(M) # optional: meataxe + + sage: M # indirect doctest + [ 0 1 2] + [ z z + 1 z + 2] + [ 2*z 2*z + 1 2*z + 2] + + """ + if self.Data == NULL: + raise IndexError, "Matrix is empty" + FfSetField(self.Data.Field) + return self._converter.int_to_field(FfToInt(FfExtract(MatGetPtr(self.Data,i), j))) + + cdef inline int get_unsafe_int(self, Py_ssize_t i, Py_ssize_t j): + # NOTE: + # It is essential that you call FfSetField and FfSetNoc YOURSELF + # and that you assert that the matrix is not empty! + # This method is here for speed! + return FfToInt(FfExtract(MatGetPtr(self.Data,i), j)) + + cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): + """ + Set values without bound checking. + + TESTS: + + The following test would have failed in a preliminary version + of this MeatAxe wrapper:: + + sage: K. = GF(125) + sage: M = MatrixSpace(K,9,9)() + sage: N = MatrixSpace(GF(9,'x'),20).random_element() + sage: M[2,2] = x + sage: M + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 x 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + [0 0 0 0 0 0 0 0 0] + + """ + # ASSUMPTION: value's parent is the base ring + if self.Data == NULL: + raise IndexError, "Matrix is empty" + FfSetField(self.Data.Field) + FfInsert(MatGetPtr(self.Data,i), j, FfFromInt(self._converter.field_to_int(value))) + + cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value): + # NOTE: + # It is essential that you call FfSetField and FfSetNoc YOURSELF + # and that you assert that the matrix is not empty! + # This method is here for speed! + FfInsert(FfGetPtr(self.Data.Data,i), j, FfFromInt(value)) + + def randomize(self, density=None, nonzero=False, *args, **kwds): + """ + Fill the matrix with random values. + + INPUT: + + - ``density`` (optional real number between zero and one) -- + the expected density of the resulting matrix + - ``nonzero`` (optional bool, default ``False``) -- + If true, all inserted marks are non-zero. + + EXAMPLE:: + + sage: MS = MatrixSpace(GF(27,'z'),6,6) + sage: M = MS.random_element() # indirect doctest + sage: M # optional: meataxe + [ 1 z + 1 z^2 + z + 1 z^2 2*z^2 + z z + 1] + [2*z^2 + 2*z + 2 2*z^2 + z + 2 z^2 + 1 2*z^2 + 2*z + 2 z^2 + z 2*z^2 + z + 1] + [ 2*z + 2 z^2 + z + 2 z + 2 2*z^2 + 2*z + 2 2*z^2 2*z^2] + [ 2*z^2 + z + 2 z^2 z + 2 z^2 + z 2*z^2 + 2 z^2 + 2] + [ 2*z^2 + z 2*z 2*z^2 + 2*z + 1 2*z^2 + 1 2*z^2 + 2*z + 1 2*z^2 + z] + [ 2*z + 1 z^2 + z z^2 z^2 2*z^2 + 2*z z + 1] + sage: type(M) # optional: meataxe + + sage: MS.random_element(nonzero=True) # optional: meataxe + [ 2*z 1 z^2 + 2*z + 1 2*z^2 + z + 1 z^2 z^2 + z + 1] + [ 2*z^2 + 2*z 2*z^2 + z + 2 2*z + 1 z^2 + 2*z 2*z^2 + 2*z z^2] + [ z^2 + z z^2 + z + 2 2*z^2 + 2*z + 1 z^2 + 2 1 2*z^2] + [ z 2*z^2 + 2*z 2*z^2 2*z + 1 z + 2 z + 2] + [ z^2 + z z^2 z + 2 2*z^2 + 2*z 2*z + 1 z^2 + z] + [ z^2 + z + 2 2*z^2 + z z^2 z + 1 2*z^2 + 2*z z^2 + 2*z + 1] + sage: MS.random_element(density=0.5) # optional: meataxe + [ z^2 + 2 0 z^2 + 2*z + 2 2*z^2 + z 0 z^2 + z + 2] + [ 0 1 0 0 0 0] + [ 2*z^2 + z + 1 2*z^2 + z + 2 0 z^2 + z + 2 0 z^2 + z + 1] + [ 0 0 0 0 0 0] + [2*z^2 + 2*z + 2 0 0 2*z^2 + z + 2 0 2*z + 1] + [ 0 2*z^2 + z 0 1 0 2*z^2 + z + 1] + + """ + self.check_mutability() + cdef int fl = self.Data.Field + density = float(density) + if density <= 0: + return + if density > 1: + density = float(1) + + self.clear_cache() + + cdef PTR x + cdef unsigned char *y + x = self.Data.Data + cdef int nr = self.Data.Nor + cdef int nc = self.Data.Noc + cdef int i, j, k + + FfSetField(fl) + FfSetNoc(nc) + cdef int O, MPB, tmp + randint = current_randstate().c_random + randdouble = current_randstate().c_rand_double + + if not nonzero: + if density == 1: + MPB = 0 + tmp = fl + while tmp <= 256: + MPB += 1 + tmp *= fl + O = (fl**MPB) + sig_on() + if nc%MPB: + for i from 0 <= i < nr: + y = x + for j from 0 <= j < FfCurrentRowSizeIo-1: + y[j] = randint()%O + y[FfCurrentRowSizeIo-1] = randint()%(fl**(nc%MPB)) + FfStepPtr(&(x)) + else: + for i from 0 <= i < nr: + y = x + for j from 0 <= j < FfCurrentRowSizeIo: + y[j] = randint()%O + FfStepPtr(&(x)) + sig_off() + else: + sig_on() + for i from 0 <= i < nr: + for j from 0 <= j < nc: + if randdouble() < density: + FfInsert(x, j, FfFromInt( (randint()%fl) )) + FfStepPtr(&(x)) + sig_off() + else: + if density == 1: + fl -= 1 + sig_on() + for i from 0 <= i < nr: + for j from 0 <= j < nc: + FfInsert(x, j, FfFromInt( (randint()%fl)+1 )) + FfStepPtr(&(x)) + sig_off() + else: + fl -= 1 + sig_on() + for i from 0 <= i < nr: + for j from 0 <= j < nc: + if randdouble() < density: + FfInsert(x, j, FfFromInt( (randint()%fl)+1 )) + FfStepPtr(&(x)) + sig_off() + +## Debugging +# def show_contents(self, r=None): +# FfSetField(self.Data.Field) +# FfSetNoc(self.Data.Noc) +# cdef PTR p +# cdef size_t i, j +# if r is not None: +# r_min = r +# r_max = r+1 +# else: +# r_min = 0 +# r_max = self.Data.Nor +# for i in range(r_min, r_max): +# p = FfGetPtr(self.Data.Data, i) +# for j from 0<=j' doesn't make much sense for matrices. + + EXAMPLES:: + + sage: M = MatrixSpace(GF(125,'x'),3,20)([20*[0],20*[0],[1]+19*[0]]) + sage: N = copy(M) + sage: M == N + True + sage: M != N + False + sage: M < N + False + sage: N[2,19] = 1 + sage: M == N + False + sage: M != N + True + """ + cdef Matrix_gfpn_dense self = left + cdef Matrix_gfpn_dense N = right + if self is None or N is None: + return -1 + cdef char* d1 + cdef char* d2 + if self.Data == NULL: + if N.Data == NULL: + return 0 + else: + return 1 + elif N.Data == NULL: + return -1 + if self.Data.Field != N.Data.Field: + if self.Data.Field > N.Data.Field: + return 1 + return -1 + if self.Data.Noc != N.Data.Noc: + if self.Data.Noc > N.Data.Noc: + return 1 + return -1 + if self.Data.Nor != N.Data.Nor: + if self.Data.Nor > N.Data.Nor: + return 1 + return -1 + d1 = (self.Data.Data) + d2 = (N.Data.Data) + cdef str s1 = PyString_FromStringAndSize(d1,self.Data.RowSize * self.Data.Nor) + cdef str s2 = PyString_FromStringAndSize(d2,N.Data.RowSize * N.Data.Nor) + if s1 != s2: + if s1 > s2: + return 1 + return -1 + return 0 + + cdef list _rowlist_(self, i, j=-1): + "M._rowlist_(i): Return row as a list of python ints" + cdef int k + if self.Data: + FfSetField(self.Data.Field) + FfSetNoc(self.Data.Noc) + else: + raise ValueError("Matrix is empty") + if (i<0) or (i>=self.Data.Nor): + raise IndexError("Index {} out of range 0..{}",format(i,self.Data.Nor-1)) + cdef PTR p + p = MatGetPtr(self.Data,i) + L = [FfToInt(FfExtract(p,k)) for k in range(self.Data.Noc)] + if j!=-1: + if not(isinstance(j,int) or isinstance(j,Integer)): + raise TypeError, "Second index must be an integer" + if j >= self.Data.Nor: + raise IndexError, "Index out of range" + for k from i < k <= j: + FfStepPtr(&(p)) # This is only called after MatGetPtr, hence, after FfSetNoc. + L.extend([FfToInt(FfExtract(p,l)) for l in range(self.Data.Noc)]) + return L + + def _list(self): + """ + Return a flat list of all entries of this matrix. + + The result is cached. + + EXAMPLES:: + + sage: MatrixSpace(GF(9,'x'),3)(sorted(list(GF(9,'x')))).list() # indirect doctest + [0, 1, 2, x, x + 1, x + 2, 2*x, 2*x + 1, 2*x + 2] + + """ + cdef list x = self.fetch('list') + if not x is None: + return x + x = [] + cdef int i + if self.Data: + FfSetField(self.Data.Field) + FfSetNoc(self.Data.Noc) + else: + raise IndexError, "Matrix is empty" + cdef PTR p + p = self.Data.Data + sig_on() + for i from 1<=i = GF(25) + sage: M = MatrixSpace(K,5,5)(sorted(list(K))) + sage: M + [ 0 1 2 3 4] + [ x x + 1 x + 2 x + 3 x + 4] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + sage: M.rescale_row(1, 3) # indirect doctest + sage: M + [ 0 1 2 3 4] + [ 3*x 3*x + 3 3*x + 1 3*x + 4 3*x + 2] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + sage: M.rescale_row(4, x) + sage: M + [ 0 1 2 3 4] + [ 3*x 3*x + 3 3*x + 1 3*x + 4 3*x + 2] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [4*x + 2 2 x + 2 2*x + 2 3*x + 2] + + """ + if start_col != 0 or self.Data == NULL: + raise ValueError("We can only rescale a full row of a non-empty matrix") + FfMulRow(MatGetPtr(self.Data, i), FfFromInt(self._converter.field_to_int(self._base_ring(s)))) + + cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col): + """ + Add the ``multiple``-fold of row ``row_from`` in-place to row ``row_to``. + + EXAMPLES:: + + sage: K. = GF(25) + sage: M = MatrixSpace(K,5,5)(sorted(list(K))) + sage: M + [ 0 1 2 3 4] + [ x x + 1 x + 2 x + 3 x + 4] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + sage: M.add_multiple_of_row(2, 4, x) # indirect doctest + sage: M + [ 0 1 2 3 4] + [ x x + 1 x + 2 x + 3 x + 4] + [ x + 2 2*x + 3 3*x + 4 4*x 1] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + + """ + if start_col != 0 or self.Data == NULL: + raise ValueError("We can only rescale a full row of a non-empty matrix") + FfAddMulRow(MatGetPtr(self.Data, row_to), MatGetPtr(self.Data, row_from), FfFromInt(self._converter.field_to_int(self._base_ring(multiple)))) + + cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2): + """ + Swap the rows ``row1`` and ``row2`` in-place. + + EXAMPLES:: + + sage: K. = GF(25) + sage: M = MatrixSpace(K,5,5)(sorted(list(K))) + sage: M + [ 0 1 2 3 4] + [ x x + 1 x + 2 x + 3 x + 4] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + sage: M.swap_rows(1, 3) # indirect doctest + sage: M + [ 0 1 2 3 4] + [ 3*x 3*x + 1 3*x + 2 3*x + 3 3*x + 4] + [ 2*x 2*x + 1 2*x + 2 2*x + 3 2*x + 4] + [ x x + 1 x + 2 x + 3 x + 4] + [ 4*x 4*x + 1 4*x + 2 4*x + 3 4*x + 4] + + """ + FfSwapRows(MatGetPtr(self.Data, row1), MatGetPtr(self.Data, row2)) + + def trace(self): + """ + Trace of this matrix, i.e., the sum of diagonal elements. + + EXAMPLES:: + + sage: K. = GF(125) + sage: MatrixSpace(K,7,7)(x).trace() + 2*x + + """ + if self._nrows != self._ncols: + raise ValueError, "self must be a square matrix" + return self._converter.int_to_field(FfToInt(MatTrace(self.Data))) + + def stack(self, Matrix_gfpn_dense other): + """ + Stack two matrices of the same number of columns. + + EXAMPLES:: + + sage: M = MatrixSpace(GF(9,'x'),1,9)(sorted(list(GF(9,'x')))) + sage: M + [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2] + sage: M.stack(M) + [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2] + [ 0 1 2 x x + 1 x + 2 2*x 2*x + 1 2*x + 2] + + """ + if self._ncols != other._ncols: + raise TypeError("Both numbers of columns must match.") + if self._nrows == 0 or self.Data == NULL: + return other.__copy__() + if other._nrows == 0 or other.Data == NULL: + return self.__copy__() + cdef Matrix_gfpn_dense OUT = self._new(self._nrows+other._nrows, self._ncols) + OUT.Data = MatAlloc(self.Data.Field, self.Data.Nor+other.Data.Nor, self.Data.Noc) + memcpy(OUT.Data.Data, self.Data.Data, FfCurrentRowSize*self.Data.Nor) + memcpy(MatGetPtr(OUT.Data, self.Data.Nor), other.Data.Data, FfCurrentRowSize*other.Data.Nor) + return OUT + + cpdef ModuleElement _add_(self, ModuleElement right): + """ + TESTS:: + + sage: K. = GF(9) + sage: M = MatrixSpace(K,3,3)(sorted(list(K))) + sage: N = MatrixSpace(K,3,3)(2*x) + sage: M+N # indirect doctest + [ 2*x 1 2] + [ x 1 x + 2] + [ 2*x 2*x + 1 x + 2] + + """ + cdef Matrix_gfpn_dense Self = self + cdef Matrix_gfpn_dense Right = right + assert Self is not None + assert Right is not None + if Self.Data == NULL or Right.Data == NULL: + raise NotImplementedError, "The matrices must not be empty" + cdef Matrix_gfpn_dense Left = Self.__copy__() + Left._cache = {} + MatAdd(Left.Data, Right.Data) + return Left + + cpdef ModuleElement _sub_(self, ModuleElement right): + """ + TESTS:: + + sage: K. = GF(9) + sage: M = MatrixSpace(K,3,3)(sorted(list(K))) + sage: N = MatrixSpace(K,3,3)(2*x) + sage: M-N # indirect doctest + [ x 1 2] + [ x 2*x + 1 x + 2] + [ 2*x 2*x + 1 2] + + """ + cdef Matrix_gfpn_dense Self = self + cdef Matrix_gfpn_dense Right = right + assert Self is not None + assert Right is not None + if Self.Data == NULL or Right.Data == NULL: + raise NotImplementedError, "The matrices must not be empty" + cdef Matrix_gfpn_dense Left = Self.__copy__() + Left._is_immutable = False + Left._cache = {} + MatAddMul(Left.Data, Right.Data, mtx_taddinv[1]) + return Left + + def __neg__(self): + """ + TESTS:: + + sage: M = MatrixSpace(GF(9,'x'),3,3)(sorted(list(GF(9,'x')))) + sage: -M + [ 0 2 1] + [ 2*x 2*x + 2 2*x + 1] + [ x x + 2 x + 1] + + :: + + sage: M = MatrixSpace(GF(125,'x'),10,30).random_element() + sage: N = MatrixSpace(GF(125,'x'),10,30).random_element() + sage: M + (-N) == M - N == -(N - M) + True + + """ + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + return self._rmul_(self._base_ring(-1)) + + cpdef ModuleElement _rmul_(self, RingElement left): + """ + EXAMPLES:: + + sage: M = MatrixSpace(GF(9,'x'),3,3)(sorted(list(GF(9,'x')))) + sage: K. = GF(9) + sage: M = MatrixSpace(K,3,3)(list(K)) + sage: x*M # indirect doctest + [ 0 x + 1 2*x + 1] + [ 2 2*x 2*x + 2] + [ x + 2 1 x] + sage: -M == (-1)*M + True + + """ + if self.Data == NULL: + return self.__copy__() + FfSetField(self.Data.Field) + cdef Matrix_gfpn_dense OUT = self.__copy__() + OUT._cache = {} + MatMulScalar(OUT.Data, FfFromInt(self._converter.field_to_int(left))) + return OUT + + cpdef ModuleElement _lmul_(self, RingElement right): + """ + EXAMPLES:: + + sage: M = MatrixSpace(GF(9,'x'),3,3)(sorted(list(GF(9,'x')))) + sage: K. = GF(9) + sage: M = MatrixSpace(K,3,3)(sorted(list(K))) + sage: x*M # indirect doctest + [ 0 x 2*x] + [ x + 1 2*x + 1 1] + [2*x + 2 2 x + 2] + sage: -M == (-1)*M + True + + """ + if self.Data == NULL: + return self.__copy__() + FfSetField(self.Data.Field) + cdef Matrix_gfpn_dense OUT = self.__copy__() + OUT._cache = {} + MatMulScalar(OUT.Data, FfFromInt(self._converter.field_to_int(right))) + return OUT + + cdef int _strassen_default_cutoff(self, sage.matrix.matrix0.Matrix right) except -2: + # Surprisingly, Winograd-Strassen can compete with school book + # multiplication for smallish matrices, and of course it is + # asymptotically faster. So, we used it by default. + return 0 + + cpdef Matrix_gfpn_dense _multiply_classical(Matrix_gfpn_dense self, Matrix_gfpn_dense right): + """ + Multiplication using the cubic school book multiplication algorithm. + + EXAMPLES: + + Since by default the asymptotically faster Strassen-Winograd + multiplication algorithm is used, the following is a valid + consistency check:: + + sage: M = MatrixSpace(GF(9,'x'),1000,500).random_element() + sage: N = MatrixSpace(GF(9,'x'),500,2000).random_element() + sage: M*N == M._multiply_classical(N) # optional: meataxe + True + + """ + "multiply two meataxe matrices by the school book algorithm" + if self.Data == NULL or right.Data == NULL: + raise ValueError("The matrices must not be empty") + if self._ncols != right._nrows: + raise ArithmeticError("left ncols must match right nrows") + cdef Matrix_gfpn_dense OUT = self._new(self._nrows, right._ncols) + sig_on() + OUT.Data = MatDup(self.Data) + MatMul(OUT.Data,right.Data) + sig_off() + OUT._is_immutable = False + OUT._cache = {} + return OUT + + cpdef Matrix_gfpn_dense _multiply_strassen(Matrix_gfpn_dense self, Matrix_gfpn_dense right, cutoff=0): + """ + Matrix multiplication using the asymptotically fast Strassen-Winograd algorithm. + + INPUT: + + - ``right`` -- a matrix of dimensions suitable to do multiplication + - ``cutoff`` (optional integer) -- indicates the minimal size of submatrices + that will be considered in the divide-and-conquer algorithm. The size is + *not* expressed by the number of rows/columns, but the rowsize expressed + in bytes. Depending on the base field, one byte may represent up to eight + entries in a matrix row. The default is ``sizeof(long)^2/2`` byte. + + EXAMPLES: + + We test that different cutoffs yield the same result:: + + sage: M = MatrixSpace(GF(9,'x'),1500,600).random_element() + sage: N = MatrixSpace(GF(9,'x'),600,1500).random_element() + sage: M._multiply_strassen(N) == M._multiply_strassen(N,80) == M._multiply_strassen(N,2) # optional: meataxe + True + + """ + if self.Data == NULL or right.Data == NULL: + raise ValueError("The matrices must not be empty") + if self._ncols != right._nrows: + raise ArithmeticError("left ncols must match right nrows") + MS = self.matrix_space(self._nrows, right._ncols, False) + cdef Matrix_gfpn_dense OUT = Matrix_gfpn_dense(MS, None) + # Now, OUT.Data is initialised, which is needed for MatMulStrassen to work. + cutoff = cutoff//sizeof(long) + StrassenSetCutoff(cutoff) + sig_on() + MatMulStrassen(OUT.Data, self.Data, right.Data) + sig_off() + return OUT + + cdef ModuleElement _mul_long(self, long n): + "multiply an MTX matrix with a field element represented by an integer" + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + cdef Matrix_gfpn_dense left + cdef FEL r + if n < 0: + r = mtx_taddinv[FfFromInt(-n)] + else: + r = FfFromInt(n) + left = self.__copy__() + left._cache = {} + MatMulScalar(left.Data, r) + return left + + def __div__(Matrix_gfpn_dense self, p): + """ + Divide a matrix by a scalar. + + EXAMPLES:: + + sage: K. = GF(9) + sage: M = MatrixSpace(K,3,3)(sorted(list(K))) + sage: M + [ 0 1 2] + [ x x + 1 x + 2] + [ 2*x 2*x + 1 2*x + 2] + sage: M/2 # indirect doctest + [ 0 2 1] + [ 2*x 2*x + 2 2*x + 1] + [ x x + 2 x + 1] + sage: M/x + [ 0 x + 2 2*x + 1] + [ 1 x 2*x + 2] + [ 2 x + 1 2*x] + + """ + if self.Data == NULL: + return self.__copy__() + if not p: + raise ZeroDivisionError + if p not in self._base_ring: + raise ValueError("{} is not a scalar".format(p)) + p = self._base_ring(p) + FfSetField(self.Data.Field) + cdef Matrix_gfpn_dense OUT = self.__copy__() + OUT._cache = {} + cdef FEL r = mtx_tmultinv[FfFromInt(self._converter.field_to_int(p))] + MatMulScalar(OUT.Data, r) + return OUT + + def __invert__(Matrix_gfpn_dense self): + """ + Multiplicative inverse of this matrix (if available) + + TESTS:: + + sage: MS = MatrixSpace(GF(9,'x'),500) + sage: while 1: + ....: M = MS.random_element() + ....: if M.rank() == 500: + ....: break + sage: Minv = ~M # indirect doctest + sage: Minv*M == M*Minv == 1 + True + + We use the occasion to demonstrate that errors in MeatAxe are + correctly handled in Sage:: + + sage: MS = MatrixSpace(GF(25,'x'),5) + sage: while 1: + ....: M = MS.random_element(density=0.4) + ....: if M.rank() < 5: + ....: break + sage: ~M # optional: meataxe + Traceback (most recent call last): + ... + ZeroDivisionError: Division by zero in file matinv.c (line 50) + + """ + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + if not self.is_square(): + raise ArithmeticError("self must be a square matrix") + cdef Matrix_gfpn_dense OUT = self._new(self._nrows, self._ncols) + OUT._is_immutable = False + OUT._cache = {} + sig_on() + try: + OUT.Data = MatInverse(self.Data) + except ZeroDivisionError: + # Attempting to invert singular matrices happens + # in the tests, and we make the special case here + # so that the sig_on/off count is fine. + sig_off() + raise + sig_off() + if OUT.Data != NULL: + return OUT + raise ArithmeticError("This matrix is not invertible") + + def transpose(Matrix_gfpn_dense self): + """ + Return the transposed matrix. + + EXAMPLES:: + + sage: K. = GF(9) + sage: M = MatrixSpace(K, 2,4)(sorted(list(K)[1:])) + sage: M + [ 1 2 x x + 1] + [ x + 2 2*x 2*x + 1 2*x + 2] + sage: M.transpose() + [ 1 x + 2] + [ 2 2*x] + [ x 2*x + 1] + [ x + 1 2*x + 2] + + """ + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + cdef Matrix_gfpn_dense OUT = self._new(self._ncols, self._nrows) + OUT._is_immutable = False + OUT._cache = {} + OUT.Data = MatTransposed(self.Data) + return OUT + + def order(self): + """ + Return the multiplicative order of this matrix. + + EXAMPLES:: + + sage: K. = GF(27) + sage: M = MatrixSpace(K, 4)([2*x^2 + 2*x, 2*x^2 + x, 2*x^2 + x + 1, + ....: x^2 + x + 2, x + 2, x^2, 2*x + 2, 2*x^2 + 2*x, 2*x^2 + 1, + ....: 1, 2, x^2 + 2*x + 1, x^2 + x + 2, x + 1, 2*x^2 + 2*x, x^2 + x]) + sage: M.order() # optional: meataxe + 104 + sage: M^104 == 1 + True + sage: M^103 == 1 + False + + """ + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + if (self.Data.Nor <> self.Data.Noc): + raise ValueError("only defined for square matrices") + o = MatOrder(self.Data) + if o==-1: + raise ArithmeticError("order too large") + else: + return o + +################### +## Gauss algorithm + + def left_kernel_matrix(self): + """ + Return the null space of this matrix, represented as a matrix. + + NOTE: + + - For a matrix `M`, ``M.left_kernel_matrix()*M`` is a null matrix. + - The command `M.left_kernel()` uses a generic implementation in Sage, + that relies on computing the echelon form of the transposed + matrix. This method however uses a MeatAxe function to compute + the left kernel matrix. + + EXAMPLES:: + + sage: K. = GF(25) + sage: M = MatrixSpace(K, 10)() + sage: entries = [((0, 2), x), ((0, 4), 3*x + 2), + ....: ((0, 8), 2*x), ((1, 1), x + 3), ((1, 5), 3*x), + ....: ((1, 6), x + 4), ((2, 3), 2*x), ((2, 5), 4*x + 1), + ....: ((2, 6), 4), ((3, 4), x + 4), ((3, 5), x + 1), + ....: ((5, 5), 3*x), ((5, 7), x + 3), ((6, 1), x), + ....: ((6, 2), x + 1), ((6, 5), x + 1), ((8, 2), 4), + ....: ((8, 8), 4), ((8, 9), x + 3), ((9, 8), 4*x + 2)] + sage: for (i,j),v in entries: M[i,j] = v + sage: M.left_kernel() + Vector space of degree 10 and dimension 2 over Finite Field in x of size 5^2 + Basis matrix: + [0 0 0 0 1 0 0 0 0 0] + [0 0 0 0 0 0 0 1 0 0] + sage: M.left_kernel_matrix() # optional: meataxe + [0 0 0 0 1 0 0 0 0 0] + [0 0 0 0 0 0 0 1 0 0] + + """ + cdef Matrix_gfpn_dense OUT = self.fetch("left_kernel_matrix") + if OUT is not None: + return OUT + if self.Data == NULL: + raise ValueError("The matrix must not be empty") + OUT = type(self).__new__(type(self)) + OUT.Data = MatNullSpace(self.Data) + OUT._nrows = OUT.Data.Nor + OUT._ncols = OUT.Data.Noc + OUT._is_immutable = False + OUT._parent = self.matrix_space(OUT._nrows, OUT._ncols, False) + OUT._base_ring = self._base_ring + OUT._converter = self._converter + OUT._cache = {} + self.cache("left_kernel_matrix", OUT) + return OUT + + def _echelon_in_place_classical(self, reduced=True, **kwds): + """ + Change this matrix into echelon form, using classical Gaussian elimination. + + INPUT: + + - ``reduced`` (optional, default ``True``) -- will result + in the row-reduced echelon form (otherwise, only a + semi-echelon form results). + + EXAMPLES:: + + sage: K. = GF(25) + sage: M = MatrixSpace(K, 10)() + sage: entries = [((0, 2), x), ((0, 4), 3*x + 2), + ....: ((0, 8), 2*x), ((1, 1), x + 3), ((1, 5), 3*x), + ....: ((1, 6), x + 4), ((2, 3), 2*x), ((2, 5), 4*x + 1), + ....: ((2, 6), 4), ((3, 4), x + 4), ((3, 5), x + 1), + ....: ((5, 5), 3*x), ((5, 7), x + 3), ((6, 1), x), + ....: ((6, 2), x + 1), ((6, 5), x + 1), ((8, 2), 4), + ....: ((8, 8), 4), ((8, 9), x + 3), ((9, 8), 4*x + 2)] + sage: for (i,j),v in entries: M[i,j] = v + sage: M + [ 0 0 x 0 3*x + 2 0 0 0 2*x 0] + [ 0 x + 3 0 0 0 3*x x + 4 0 0 0] + [ 0 0 0 2*x 0 4*x + 1 4 0 0 0] + [ 0 0 0 0 x + 4 x + 1 0 0 0 0] + [ 0 0 0 0 0 0 0 0 0 0] + [ 0 0 0 0 0 3*x 0 x + 3 0 0] + [ 0 x x + 1 0 0 x + 1 0 0 0 0] + [ 0 0 0 0 0 0 0 0 0 0] + [ 0 0 4 0 0 0 0 0 4 x + 3] + [ 0 0 0 0 0 0 0 0 4*x + 2 0] + sage: M.echelon_form() # indirect doctest + [ 0 1 0 0 0 0 0 0 0 4*x + 4] + [ 0 0 1 0 0 0 0 0 0 4*x + 2] + [ 0 0 0 1 0 0 0 0 0 3*x + 4] + [ 0 0 0 0 1 0 0 0 0 3*x + 3] + [ 0 0 0 0 0 1 0 0 0 2*x + 3] + [ 0 0 0 0 0 0 1 0 0 x] + [ 0 0 0 0 0 0 0 1 0 2*x + 2] + [ 0 0 0 0 0 0 0 0 1 0] + [ 0 0 0 0 0 0 0 0 0 0] + [ 0 0 0 0 0 0 0 0 0 0] + + A semi-echelon form can be produced by invoking the single-underscore + method directly:: + + sage: N = copy(M) + sage: N._echelon_in_place_classical(reduced=False) # optional: meataxe + sage: N # optional: meataxe + [ 0 0 x 0 3*x + 2 0 0 0 2*x 0] + [ 0 x + 3 0 0 0 3*x x + 4 0 0 0] + [ 0 0 0 2*x 0 4*x + 1 4 0 0 0] + [ 0 0 0 0 x + 4 x + 1 0 0 0 0] + [ 0 0 0 0 0 3*x 0 x + 3 0 0] + [ 0 0 0 0 0 0 2*x + 2 4*x 3*x + 3 0] + [ 0 0 0 0 0 0 0 x + 1 1 x + 3] + [ 0 0 0 0 0 0 0 0 4*x + 2 0] + [ 0 0 0 0 0 0 0 0 0 0] + [ 0 0 0 0 0 0 0 0 0 0] + + TESTS: + + We verify that the above echelon form is consistent with Sage's generic + implementation of dense matrices:: + + sage: type(M) # optional: meataxe + + sage: MS = M.parent() + sage: from sage.matrix.matrix_generic_dense import Matrix_generic_dense + sage: MS._MatrixSpace__matrix_class = Matrix_generic_dense + sage: X = MS(M._list()) + sage: type(X) + + sage: X.echelon_form() + [ 0 1 0 0 0 0 0 0 0 4*x + 4] + [ 0 0 1 0 0 0 0 0 0 4*x + 2] + [ 0 0 0 1 0 0 0 0 0 3*x + 4] + [ 0 0 0 0 1 0 0 0 0 3*x + 3] + [ 0 0 0 0 0 1 0 0 0 2*x + 3] + [ 0 0 0 0 0 0 1 0 0 x] + [ 0 0 0 0 0 0 0 1 0 2*x + 2] + [ 0 0 0 0 0 0 0 0 1 0] + [ 0 0 0 0 0 0 0 0 0 0] + [ 0 0 0 0 0 0 0 0 0 0] + + The following was a problem in a preliminary version of the code:: + + sage: K. = GF(25) + sage: M = MatrixSpace(K, 2, 4)([4, 4, 1, 0, 0, 2*a+1, a+2, 1]) + sage: M + [ 4 4 1 0] + [ 0 2*a + 1 a + 2 1] + sage: M.echelonize() + sage: M + [ 1 0 3*a + 4 2*a + 2] + [ 0 1 2*a 3*a + 3] + + """ + if self._nrows == 0 or self._ncols == 0: + self.cache('in_echelon_form',True) + self.cache('rank', 0) + self.cache('pivots', ()) + return self + MatEchelonize(self.Data) + self._cache = {} + # Now, self.Data is in semi-echelon form. + r = self.Data.Nor + cdef size_t i, j, pos + cdef PTR old, dest, src + cdef FEL piv + self.cache('rank', r) + # Next, we do permutations to achieve the reduced echelon form, + # if requested. + sig_on() + if reduced: + pivs = [(self.Data.PivotTable[i],i) for i in range(r)] + pivs.sort() + if pivs != [(self.Data.PivotTable[i],i) for i in range(r)] or self.Data.Nor < self._nrows: + # We copy the row one by one, sorting their pivot positions + old = self.Data.Data + self.Data.Data = FfAlloc(self._nrows) + for i, (pos,j) in enumerate(pivs): + # We have to move row j to row i + dest = self.Data.Data+FfCurrentRowSize*i + memcpy(dest, old+FfCurrentRowSize*j, FfCurrentRowSize) + self.Data.PivotTable[i] = pos + free(old) + self.Data.Nor = self._nrows + # Now, the pivot columns are strictly increasing. + # We now normalize each row, and annulate everything + # above the pivot (currently, we only know that the matrix + # is zero below the pivots). + for i from 0 <= i < r: + src = MatGetPtr(self.Data, i) + piv = FfExtract(src, self.Data.PivotTable[i]) + assert piv!=FF_ZERO + if piv != FF_ONE: + FfMulRow(src, mtx_tmultinv[piv]) + for j from 0 <= j < i: + dest = MatGetPtr(self.Data, j) + piv = FfExtract(dest, self.Data.PivotTable[i]) + if piv != FF_ONE: + FfAddMulRow(dest, src, mtx_taddinv[piv]) + else: + FfSubRow(dest, src) + elif self.Data.Nor < self._nrows: + # Some rows may have vanished. In SageMath, we + # want that the number of rows does not change, + # thus, we have to append zero rows. + self.Data.Data = check_realloc(self.Data.Data, FfCurrentRowSize*self._nrows) + memset(self.Data.Data + FfCurrentRowSize*self.Data.Nor, FF_ZERO, FfCurrentRowSize*(self._nrows-self.Data.Nor)) + self.Data.Nor = self._nrows + sig_off() + self.cache('pivots', tuple(self.Data.PivotTable[i] for i in range(r))) + self.cache('in_echelon_form',True) + +def mtx_unpickle(f, int nr, int nc, str Data, bint m): + """ + Helper function for unpickling. + + TESTS:: + + sage: M = MatrixSpace(GF(9,'x'),10,10).random_element() + sage: M == loads(dumps(M)) # indirect doctest + True + sage: M is loads(dumps(M)) + False + """ + cdef Matrix_gfpn_dense OUT + OUT = Matrix_gfpn_dense.__new__(Matrix_gfpn_dense) + if isinstance(f, (int, long)): + # This is for old pickles created with the group cohomology spkg + Matrix_dense.__init__(OUT, MatrixSpace(GF(f, 'z'), nr, nc)) + else: + Matrix_dense.__init__(OUT, f) + f = OUT._base_ring.order() + OUT.Data = MatAlloc(f, nr, nc) + OUT._is_immutable = not m + OUT._converter = FieldConverter(OUT._base_ring) + cdef char *x + if Data: + x = PyString_AsString(Data) + memcpy(OUT.Data.Data, x, OUT.Data.RowSize*OUT.Data.Nor) + return OUT diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index f582217e075..76ec45a2a9a 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Dense matrices over the integer ring diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 6e1b3014f6f..db953abb5eb 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -2059,7 +2059,7 @@ def unpickle_matrix_mod2_dense_v1(r, c, data, size): True """ from sage.matrix.constructor import Matrix - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF cdef int i, j cdef Matrix_mod2_dense A @@ -2109,7 +2109,7 @@ def from_png(filename): True """ from sage.matrix.constructor import Matrix - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF cdef int i,j,r,c cdef Matrix_mod2_dense A diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 717fd3ac525..3696d74bfbd 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -72,7 +72,7 @@ from sage.structure.element cimport ModuleElement, RingElement, Element, Vector from sage.rings.integer cimport Integer from sage.rings.ring import is_Ring from sage.rings.integer_ring import ZZ, is_IntegerRing -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing from sage.rings.rational_field import QQ from sage.arith.all import gcd diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 6336b8e1c25..f7603557180 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -56,7 +56,6 @@ import matrix_mpolynomial_dense - # Sage imports from sage.misc.superseded import deprecation import sage.structure.coerce @@ -65,7 +64,7 @@ import sage.rings.integer as integer import sage.rings.number_field.all import sage.rings.finite_rings.integer_mod_ring -import sage.rings.finite_rings.constructor +import sage.rings.finite_rings.finite_field_constructor import sage.rings.polynomial.multi_polynomial_ring_generic import sage.misc.latex as latex import sage.modules.free_module @@ -986,6 +985,12 @@ def _get_matrix_class(self): sage: type(matrix(GF(16007), 2, range(4))) + sage: type(matrix(GF(2), 2, range(4))) + + sage: type(matrix(GF(64,'z'), 2, range(4))) + + sage: type(matrix(GF(125,'z'), 2, range(4))) # optional: meataxe + """ R = self.base_ring() if self.is_dense(): @@ -1013,19 +1018,26 @@ def _get_matrix_class(self): elif R.order() < matrix_modn_dense_double.MAX_MODULUS: return matrix_modn_dense_double.Matrix_modn_dense_double return matrix_generic_dense.Matrix_generic_dense - elif sage.rings.finite_rings.constructor.is_FiniteField(R) and R.characteristic() == 2 and R.order() <= 65536: - return matrix_gf2e_dense.Matrix_gf2e_dense + elif sage.rings.finite_rings.finite_field_constructor.is_FiniteField(R): + if R.characteristic() == 2: + if R.order() <= 65536: + return matrix_gf2e_dense.Matrix_gf2e_dense + elif R.order() <= 255: + try: + import matrix_gfpn_dense + return matrix_gfpn_dense.Matrix_gfpn_dense + except ImportError: + pass elif sage.rings.polynomial.multi_polynomial_ring_generic.is_MPolynomialRing(R) and R.base_ring() in _Fields: return matrix_mpolynomial_dense.Matrix_mpolynomial_dense #elif isinstance(R, sage.rings.padics.padic_ring_capped_relative.pAdicRingCappedRelative): # return padics.matrix_padic_capped_relative_dense # the default - else: - from sage.symbolic.ring import SR # causes circular imports - if R is SR: - import matrix_symbolic_dense - return matrix_symbolic_dense.Matrix_symbolic_dense - return matrix_generic_dense.Matrix_generic_dense + from sage.symbolic.ring import SR # causes circular imports + if R is SR: + import matrix_symbolic_dense + return matrix_symbolic_dense.Matrix_symbolic_dense + return matrix_generic_dense.Matrix_generic_dense else: if sage.rings.finite_rings.integer_mod_ring.is_IntegerModRing(R) and R.order() < matrix_modn_sparse.MAX_MODULUS: diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 66a8087aabf..e78d65fe4d5 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -136,6 +136,7 @@ additional functionality (e.g. linear extensions). - :meth:`chow_ring() ` - :meth:`matroid_polytope() ` - :meth:`independence_matroid_polytope() ` + - :meth:`orlik_solomon_algebra() ` In addition to these, all methods provided by :class:`SageObject ` are available, @@ -2813,9 +2814,10 @@ cdef class Matroid(SageObject): r""" Return the list of broken circuits of ``self``. - A *broken circuit* `B` for is a subset of the ground set under - some total ordering `<` such that `B \cup \{ u \}` is a circuit - and `u < b` for all `b \in B`. + Let `M` be a matroid with ground set `E`, and let `<` be a total + ordering on `E`. A *broken circuit* for `M` means a subset `B` of + `E` such that there exists a `u \in E` for which `B \cup \{ u \}` + is a circuit of `M` and `u < b` for all `b \in B`. INPUT: @@ -2891,6 +2893,31 @@ cdef class Matroid(SageObject): ret.append(I) return ret + def orlik_solomon_algebra(self, R, ordering=None): + """ + Return the Orlik-Solomon algebra of ``self``. + + INPUT: + + - ``R`` -- the base ring + - ``ordering`` -- (optional) an ordering of the ground set + + .. SEEALSO:: + + :class:`~sage.algebras.orlik_solomon.OrlikSolomonAlgebra` + + EXAMPLES:: + + sage: M = matroids.Uniform(3, 4) + sage: OS = M.orlik_solomon_algebra(QQ) + sage: OS + Orlik-Solomon algebra of U(3, 4): Matroid of rank 3 on 4 elements + with circuit-closures + {3: {{0, 1, 2, 3}}} + """ + from sage.algebras.orlik_solomon import OrlikSolomonAlgebra + return OrlikSolomonAlgebra(R, self, ordering) + # polytopes def matroid_polytope(self): diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index cf0c1ba5906..c32d67b55c8 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -974,6 +974,33 @@ cdef class CachedFunction(object): self.cache[k] = w return w + def cached(self, *args, **kwds): + """ + Return the result from the cache if available. If the value is + not cached, raise ``KeyError``. + + EXAMPLES:: + + sage: @cached_function + ....: def f(x): + ....: return x + sage: f.cached(5) + Traceback (most recent call last): + ... + KeyError: ((5,), ()) + sage: f(5) + 5 + sage: f.cached(5) + 5 + """ + k = self.get_key_args_kwds(args, kwds) + + try: + return self.cache[k] + except TypeError: # k is not hashable + k = dict_key(k) + return self.cache[k] + def get_cache(self): """ Returns the cache dictionary. @@ -1883,6 +1910,47 @@ cdef class CachedMethodCaller(CachedFunction): cache[k] = w return w + def cached(self, *args, **kwds): + """ + Return the result from the cache if available. If the value is + not cached, raise ``KeyError``. + + EXAMPLES:: + + sage: class CachedMethodTest(object): + ....: @cached_method + ....: def f(self, x): + ....: return x + sage: o = CachedMethodTest() + sage: CachedMethodTest.f.cached(o, 5) + Traceback (most recent call last): + ... + KeyError: ((5,), ()) + sage: o.f.cached(5) + Traceback (most recent call last): + ... + KeyError: ((5,), ()) + sage: o.f(5) + 5 + sage: CachedMethodTest.f.cached(o, 5) + 5 + sage: o.f.cached(5) + 5 + """ + if self._instance is None: + # cached method bound to a class + instance = args[0] + args = args[1:] + return self._cachedmethod.__get__(instance).cached(*args, **kwds) + + k = self.get_key_args_kwds(args, kwds) + + try: + return self.cache[k] + except TypeError: # k is not hashable + k = dict_key(k) + return self.cache[k] + def __get__(self, inst, cls): r""" Get a :class:`CachedMethodCaller` bound to a specific diff --git a/src/sage/misc/latex_macros.py b/src/sage/misc/latex_macros.py index 4feaadc205a..072906d3129 100644 --- a/src/sage/misc/latex_macros.py +++ b/src/sage/misc/latex_macros.py @@ -40,9 +40,9 @@ define the field with four elements in Sage, you also need to specify the name of a generator.) -To see evidence of the results of the code here, run ``sage -docbuild +To see evidence of the results of the code here, run ``sage --docbuild tutorial latex`` (for example), and look at the resulting LaTeX file in -``SAGE_DOC/output/latex/en/tutorial/``. The preamble should +``SAGE_DOC_OUTPUT/latex/en/tutorial/``. The preamble should contain '\newcommand' lines for each of the entries in ``macros``. """ @@ -73,7 +73,7 @@ def produce_latex_macro(name, *sample_args): If the Sage object is not in the global name space, describe it like so:: - sage: produce_latex_macro('sage.rings.finite_rings.constructor.FiniteField', 3) + sage: produce_latex_macro('sage.rings.finite_rings.finite_field_constructor.FiniteField', 3) '\\newcommand{\\FiniteField}[1]{\\Bold{F}_{#1}}' """ from sage.misc.latex import LatexCall @@ -146,10 +146,10 @@ def convert_latex_macro_to_mathjax(macro): # form [name, arguments], which will be passed to the function # produce_latex_macro: see that for more documentation. # -# To see the results of this, run 'sage -docbuild tutorial latex' (for +# To see the results of this, run 'sage --docbuild tutorial latex' (for # example -- you could replace 'tutorial' with your favorite piece of # documentation), and look at the resulting tex file in -# SAGE_DOC/output/latex/en/tutorial. The preamble should contain +# SAGE_DOC_OUTPUT/latex/en/tutorial. The preamble should contain # \newcommand's for each of the entries here. macros = [["ZZ"], ["NN"], diff --git a/src/sage/misc/lazy_list.pxd b/src/sage/misc/lazy_list.pxd index c81aa4263fe..b12b41df4f9 100644 --- a/src/sage/misc/lazy_list.pxd +++ b/src/sage/misc/lazy_list.pxd @@ -1,18 +1,17 @@ -from cpython.object cimport * - -cdef class lazy_list(object): - cdef object iterator - cdef list cache +cdef class lazy_list_generic(object): + cdef list cache # the cache + cdef lazy_list_generic master # a reference if self is a slice cdef Py_ssize_t start, stop, step + cpdef get(self, Py_ssize_t i) + cpdef int _fit(self, Py_ssize_t n) except -1 cdef int update_cache_up_to(self, Py_ssize_t i) except -1 -cdef class lazy_list_iterator(object): - cdef lazy_list l - cdef Py_ssize_t pos, step - -cdef class stopped_lazy_list_iterator(object): - cdef lazy_list l - cdef Py_ssize_t pos, step, stop +cdef class lazy_list_from_iterator(lazy_list_generic): + cdef object iterator +cdef class lazy_list_from_function(lazy_list_generic): + cdef object callable +cdef class lazy_list_from_update_function(lazy_list_generic): + cdef object update_function diff --git a/src/sage/misc/lazy_list.pyx b/src/sage/misc/lazy_list.pyx index 77537dfd520..450434802b5 100644 --- a/src/sage/misc/lazy_list.pyx +++ b/src/sage/misc/lazy_list.pyx @@ -43,317 +43,272 @@ But, naturally, not the other way around:: Traceback (most recent call last): ... TypeError: can only add list to lazy_list -""" -# in types.pxd -# bint PyType_Check(object o) -# bint PyType_CheckExact(object o) -# include "sage/ext/python_iterator.pxi" +You can easily create your own class inheriting from :class:`lazy_list_generic`. You +should call the :class:`lazy_list_generic` constructor (optionally with some +precomputed values for the cache) and implement the method ``_new_slice`` that +returns a new chunk of data at each call. Here is an example of implementation +of the Thue--Morse word that is obtained as the fixed point of the substitution +`0 \to 01` and `1 \to 10`:: + + sage: from sage.misc.lazy_list import lazy_list_generic + sage: class MyThueMorseWord(lazy_list_generic): + ....: def __init__(self): + ....: self.i = 1 + ....: lazy_list_generic.__init__(self, cache=[0,1]) + ....: def _new_slice(self): + ....: letter = self.get(self.i) + ....: self.i += 1 + ....: return [0,1] if letter == 0 else [1,0] + sage: w = MyThueMorseWord() + sage: w + lazy list [0, 1, 1, ...] + sage: all(w[i] == ZZ(i).popcount()%2 for i in range(100)) + True + sage: w[:500].list() == w[:1000:2].list() + True + +Alternatively, you can create the lazy list from an update function:: + + sage: def thue_morse_update(values): + ....: n = len(values) + ....: if n == 0: + ....: letter = 0 + ....: else: + ....: assert n%2 == 0 + ....: letter = values[n//2] + ....: values.append(letter) + ....: values.append(1-letter) + sage: w2 = lazy_list(update_function=thue_morse_update) + sage: w2 + lazy list [0, 1, 1, ...] + sage: w2[:500].list() == w[:500].list() + True + +You can also create extension type inheriting from :class:`lazy_list_generic` +(with Cython). In that case you would better implement directly the method +`update_cache_up_to`. See the examples in this file with the classes +:class:`lazy_list_from_iterator` and :class:`lazy_list_from_function`. +""" +#***************************************************************************** +# Copyright (C) 2015 Vincent Delecroix <20100.delecroix@gmail.com> +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** cdef extern from "Python.h": Py_ssize_t PY_SSIZE_T_MAX -from cpython.list cimport * +# make a unique instance of empty lazy lists +cdef lazy_list_generic empty_lazy_list +empty_lazy_list = lazy_list_generic.__new__(lazy_list_generic) +empty_lazy_list.start = 0 +empty_lazy_list.stop = 0 +empty_lazy_list.step = 1 +empty_lazy_list.cache = [] -from libc cimport limits -cdef class lazy_list_iterator(object): - """ - Iterator for a lazy list. +def lazy_list(data=None, initial_values=None, start=None, stop=None, step=None, + update_function=None): + r""" + Return a lazy list. INPUT: - - ``l`` -- a lazy list + - ``data`` -- data to create a lazy list from. This can be - - ``pos`` -- (Default: ``None``) ``None`` or a non-negative integer - specifying the starting position - """ - def __init__(self, l, pos=None): - r""" - Initialize ``self``. + #. a (possibly infinite) iterable, + #. a function (that takes as input an integer ``n`` and return + the ``n``-th term of the list), + #. or a standard Python container ``list`` or ``tuple``. - EXAMPLES:: + - ``initial_values`` -- the beginning of the sequence that will not be computed from + the ``data`` provided. - sage: from sage.misc.lazy_list import lazy_list - sage: l = lazy_list(2*i for i in xrange(2**10)) - sage: e1 = iter(l) - sage: e2 = iter(l) - sage: next(e1), next(e1) - (0, 2) - sage: next(e2), next(e2), next(e2) - (0, 2, 4) - sage: next(e1), next(e1) - (4, 6) - sage: next(e2) - 6 - """ - if not isinstance(l, lazy_list): - raise TypeError("argument must be lazy_list not %s"%type(l).__name__) - self.l = l - self.step = self.l.step - if pos is not None: - self.pos = pos - else: - self.pos = self.l.start - self.step + - ``update_function`` -- you can also construct a lazy list from a function + that takes as input a list of precomputed values and updates it with some + more values. - def __reduce__(self): - r""" - Pickling support. + - ``start``, ``stop``, ``step`` -- deprecated arguments - TESTS:: + .. NOTE:: - sage: from sage.misc.lazy_list import lazy_list - sage: from itertools import count - sage: l = lazy_list(count()) - sage: iterator = iter(l) - sage: next(iterator) - 0 - sage: iterator2 = loads(dumps(iterator)) - sage: next(iterator2) - 1 - """ - return lazy_list_iterator, (self.l, self.pos) + If you want finer tuning of the constructor you can directly instantiate + the classes associated to lazy lists that are + :class:`lazy_list_generic`, :class:`lazy_list_from_iterator`, + :class:`lazy_list_from_function`. - def __repr__(self): - r""" - Return a string representation. + EXAMPLES: - TESTS:: + The basic construction of lazy lists. + :: - sage: from sage.misc.lazy_list import lazy_list - sage: l = lazy_list(p**2-18*2+4 for p in Primes()) - sage: iter(l) #indirect doctest - iterator of lazy list [-28, -23, -7, ...] - """ - return "iterator of %s"%self.l + sage: from sage.misc.lazy_list import lazy_list - def __iter__(self): - r""" - Return ``self`` since this is an iterator. + #. Iterators:: - EXAMPLES:: + sage: from itertools import count + sage: lazy_list(count()) + lazy list [0, 1, 2, ...] - sage: from sage.misc.lazy_list import lazy_list - sage: l = lazy_list(iter(Primes())) - sage: i = iter(l) - sage: i.__iter__() is i - True - """ - return self + #. Functions:: - def __next__(self): - r""" - Return the next element. + sage: lazy_list(lambda n: (n**2)%17) + lazy list [0, 1, 4, ...] - EXAMPLES:: + #. Plain lists:: - sage: from sage.misc.lazy_list import lazy_list - sage: l = lazy_list(p**2-18*2+4 for p in Primes()) - sage: i = iter(l) - sage: next(i) # indirect doctest - -28 - sage: next(i) # indirect doctest - -23 - """ - self.pos += self.step - self.l.update_cache_up_to(self.pos) # possibly raise a StopIteration - return PyList_GET_ITEM(self.l.cache, self.pos) + sage: lazy_list([1,5,7,2]) + lazy list [1, 5, 7, ...] -cdef class stopped_lazy_list_iterator(object): - """ - A lazy list iterator which eventually stops. + If a function is only defined for large values, you can provide the beginning + of the sequence manually:: - INPUT: + sage: l = lazy_list(divisors, [None]) + sage: l + lazy list [None, [1], [1, 2], ...] - - ``l`` -- a lazy list + Lazy lists behave like lists except that they are immutable:: - - ``pos`` -- (Default: ``None``) ``None`` or a non-negative integer - specifying the starting position - """ - def __init__(self, l, pos=None): - r""" - Initialize ``self``. + sage: l[3::5] + lazy list [[1, 3], [1, 2, 4, 8], [1, 13], ...] - EXAMPLES:: + If your lazy list is finite, you can obtain the underlying list with the + method `.list()`:: - sage: from sage.misc.lazy_list import lazy_list - sage: from itertools import count - sage: l = lazy_list((i**2-i-1 for i in count()), start=5, stop=15555) - sage: i1 = iter(l); i2 = iter(l) - sage: print next(i1), next(i1) - 19 29 - sage: print next(i2), next(i2) - 19 29 - sage: print next(i1), next(i2) - 41 41 - """ - if not isinstance(l, lazy_list): - raise TypeError("argument must be a lazy list") - - self.l = l - self.step = self.l.step - self.stop = self.l.stop - if pos is not None: - self.pos = pos - else: - self.pos = self.l.start - self.l.step - self.stop = self.l.stop + sage: l[30:50:5].list() + [[1, 2, 3, 5, 6, 10, 15, 30], + [1, 5, 7, 35], + [1, 2, 4, 5, 8, 10, 20, 40], + [1, 3, 5, 9, 15, 45]] - def __reduce__(self): - r""" - Pickling support. + TESTS:: - EXAMPLES:: - - sage: from sage.misc.lazy_list import lazy_list - sage: from itertools import count - sage: l = lazy_list(count(), start=3, stop=255, step=18) - sage: l - lazy list [3, 21, 39, ...] - sage: i = iter(l) - sage: loads(dumps(i)) - iterator of lazy list [3, 21, 39, ...] - """ - return lazy_list_iterator, (self.l,self.pos) + sage: lazy_list(count(), start=5) + doctest:...: DeprecationWarning: The arguments start, stop, step are deprecated. Use + direct slicing as in my_data[start:stop:step] + See http://trac.sagemath.org/16137 for details. + lazy list [5, 6, 7, ...] - def __iter__(self): - r""" - Return ``self`` since this is an iterator. - - TESTS:: - - sage: from sage.misc.lazy_list import lazy_list - sage: L = lazy_list(iter(Primes()))[:100] - sage: i = iter(L) - sage: type(i) - - sage: i is iter(i) #indirect doctest - True - """ - return self + sage: lazy_list() + lazy list [] + sage: lazy_list(data='hey', update_function='hello') + Traceback (most recent call last): + ... + ValueError: only one of the arguments 'data' or 'update_function' + can be used + """ + cdef lazy_list_generic l + + if data is None and update_function is None: + return empty_lazy_list + elif data is not None and update_function is not None: + raise ValueError("only one of the arguments 'data' or 'update_function' can be used") + + if initial_values is None: + cache = [] + else: + cache = list(initial_values) + + if update_function is not None: + assert callable(update_function) + return lazy_list_from_update_function(update_function, cache) + + if isinstance(data, (tuple,list)): + data = cache + list(data) + l = lazy_list_generic(data, 0, len(data), 1) + else: + # the code below is not very clean + # we just want to differentiate on the one hand iterable (= object with a + # proper __iter__ method)/iterator (= object with a next method) and on the + # other hand callable (= object with __call__) + try: + data = iter(data) + except TypeError: + pass - def __next__(self): - r""" - Return the current object and go to the next state. + from sage.misc.misc import is_iterator + if is_iterator(data): + l = lazy_list_from_iterator(iter(data), cache) + elif callable(data): + l = lazy_list_from_function(data, cache) + else: + raise ValueError("not able to build a lazy list from {}".format(type(data))) - TESTS:: + if start is not None or stop is not None or step is not None: + from sage.misc.superseded import deprecation + deprecation(16137, "The arguments start, stop, step are deprecated. " + "Use direct slicing as in my_data[start:stop:step]") + return l[start:stop:step] + else: + return l - sage: from sage.misc.lazy_list import lazy_list - sage: L = lazy_list(iter(Primes()))[:100] - sage: i = iter(L) - sage: type(i) - - sage: next(i) #indirect doctest - 2 - sage: next(i) #indirect doctest - 3 - """ - self.pos += self.step - if self.pos == self.stop: # in the definition of a lazy list - # the stop index is congruent to start - # modulo step - raise StopIteration - self.l.update_cache_up_to(self.pos) # possibly raise a StopIteration - return PyList_GET_ITEM(self.l.cache, self.pos) - -cdef class lazy_list(object): +def slice_unpickle(master, start, stop, step): r""" - Lazy list. + Unpickle helper - INPUT: - - - ``iterator`` -- an iterable or an iterator + TESTS:: - - ``cache`` -- ``None`` (default) or a list - used to initialize the cache. + sage: from sage.misc.lazy_list import slice_unpickle + sage: slice_unpickle(range(35), 1, 3, 7) == range(35)[1:3:7] + True + """ + return master[start:stop:step] - - ``start``, ``stop``, ``step`` -- ``None`` (default) or a non-negative - integer - parameters for slices +cdef class lazy_list_generic(object): + r""" + A lazy list EXAMPLES:: sage: from sage.misc.lazy_list import lazy_list - sage: from itertools import count - sage: m = lazy_list(count()); m - lazy list [0, 1, 2, ...] + sage: l = lazy_list(Primes()) + sage: l + lazy list [2, 3, 5, ...] + sage: l[200] + 1229 + """ + def __init__(self, cache=None, start=None, stop=None, step=None): + r""" + No check is performed on input and bad input can result in a Sage crash. + You are advised to use the function :func:`lazy_list` instead. The only + case where you might want to use directly this constructor is if you + have a list that you want to wrap (without copy) into a lazy list. + See in the example below. - sage: m2 = lazy_list(count(), start=8, stop=20551, step=2) - sage: m2 - lazy list [8, 10, 12, ...] + INPUT:: - sage: x = iter(m) - sage: print next(x), next(x), next(x) - 0 1 2 - sage: y = iter(m) - sage: print next(y), next(y), next(y) - 0 1 2 - sage: print next(x), next(y) - 3 3 - sage: loads(dumps(m)) - lazy list [0, 1, 2, ...] + - ``cache`` -- an optional list to be used as the cache. Be careful that + there is no copy. - .. NOTE:: + - ``start``, ``stop``, ``step`` -- for slices - - :class:`lazy_list` interprets the constant ``(size_t)-1`` as infinity - - all entry indices are stictly less than ``stop`` so that - :class:`lazy_list` agrees with ``range(start, stop)`` - """ - def __init__(self, iterator, cache=None, start=None, stop=None, step=None): - r""" - Initialize ``self``. - - TESTS:: + .. NOTE:: - sage: from sage.misc.lazy_list import lazy_list - sage: from itertools import count - sage: f = lazy_list(count()) - sage: loads(dumps(f)) - lazy list [0, 1, 2, ...] - """ - if start is None: - start = 0 - if stop is None: - stop = PY_SSIZE_T_MAX - if step is None: - step = 1 + Everywhere the constant ``PY_SSIZE_T_MAX`` plays the role of infinity - from sage.misc.misc import is_iterator - if not is_iterator(iterator): - if isinstance(iterator, (list, tuple)): - stop = min(stop,len(iterator)) - iterator = iter(iterator) - self.iterator = iterator + EXAMPLES:: - if cache is None or stop <= start: - cache = [] - elif not isinstance(cache, list): - raise TypeError("cache must be a list, not %s"%type(cache).__name__) - self.cache = cache + sage: from sage.misc.lazy_list import lazy_list_generic + sage: l = [0,1,2] + sage: ll = lazy_list_generic(l, 0, 2, None) + sage: ll + lazy list [0, 1] - if not isinstance(start, (int,long)): - try: - start = start.__index__() - except Exception: - raise TypeError("start must be None or integer") - if start < 0: - raise ValueError("start must be non negative") - if not isinstance(stop, (int,long)): - try: - stop = stop.__index__() - except Exception: - raise TypeError("stop must be None or integer") - if stop < 0: - raise ValueError("stop must be larger than -2") - if not isinstance(step, (int,long)): - try: - step = step.__index__() - except Exception: - raise TypeError("step must be None or integer") - if step <= 0: - raise ValueError("step must be positive") + The above code may be dangerous since the lazy list holds a reference + to the initial list:: - self.start = start - self.stop = stop - self.step = step + sage: l[0] = 'haha' + sage: ll + lazy list ['haha', 1] + """ + self.cache = [] if cache is None else cache + self.start = 0 if start is None else start + self.stop = PY_SSIZE_T_MAX if stop is None else stop + self.step = 1 if step is None else step def start_stop_step(self): r""" @@ -363,18 +318,14 @@ cdef class lazy_list(object): EXAMPLES:: sage: from sage.misc.lazy_list import lazy_list - sage: p = lazy_list(iter(Primes()))[:2147483647] + sage: p = lazy_list(Primes())[100:1042240:12] sage: p.start_stop_step() - (0, 2147483647, 1) - sage: q = p[100:1042233:12] - sage: q.start_stop_step() + doctest:...: DeprecationWarning: The method start_stop_step is deprecated. Consider using _info() instead. + See http://trac.sagemath.org/16137 for details. (100, 1042240, 12) - sage: r = q[233::3] - sage: r.start_stop_step() - (2896, 1042252, 36) - sage: 1042241%3 == 233%3 - True """ + from sage.misc.superseded import deprecation + deprecation(16137, "The method start_stop_step is deprecated. Consider using _info() instead.") return (self.start, self.stop, self.step) def list(self): @@ -384,13 +335,13 @@ cdef class lazy_list(object): .. NOTE:: If the iterator is sufficiently large, this will build a list - of length ``(size_t)-1`` which should be beyond the capacity of + of length ``PY_SSIZE_T_MAX`` which should be beyond the capacity of your RAM! EXAMPLES:: sage: from sage.misc.lazy_list import lazy_list - sage: P = lazy_list(iter(Primes())) + sage: P = lazy_list(Primes()) sage: P[2:143:5].list() [5, 19, 41, 61, 83, 107, 137, 163, 191, 223, 241, 271, 307, 337, 367, 397, 431, 457, 487, 521, 563, 593, 617, 647, 677, 719, 751, 787, 823] sage: P = lazy_list(iter([1,2,3])) @@ -413,13 +364,30 @@ cdef class lazy_list(object): sage: lazy.list() [2, 3, 5, 7, 11] """ - try: - self.update_cache_up_to(self.stop-1) - except StopIteration: - pass + self._fit(self.stop - self.step) return self.cache[self.start:self.stop:self.step] def info(self): + r""" + Deprecated method + + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list + sage: lazy_list([0]).info() + doctest:...: DeprecationWarning: info is deprecated in favor of a private method. + Use _info() instead + See http://trac.sagemath.org/19428 for details. + cache length 1 + start 0 + stop 1 + step 1 + """ + from sage.misc.superseded import deprecation + deprecation(19428, "info is deprecated in favor of a private method. Use _info() instead") + return self._info() + + def _info(self): r""" Print information about ``self`` on standard output. @@ -427,14 +395,14 @@ cdef class lazy_list(object): sage: from sage.misc.lazy_list import lazy_list sage: P = lazy_list(iter(Primes()))[10:21474838:4] - sage: P.info() + sage: P._info() cache length 0 start 10 stop 21474838 step 4 sage: P[0] 31 - sage: P.info() + sage: P._info() cache length 11 start 10 stop 21474838 @@ -463,10 +431,19 @@ cdef class lazy_list(object): True sage: p ['huit', 'douze'] + sage: ([0,2] + lazy_list([0,1])).list() + [0, 2, 0, 1] """ - if isinstance(self, list): - return lazy_list(iter(other), cache=self[:]) - raise TypeError("can only add list to lazy_list") + if not isinstance(self, list): + raise TypeError("can only add list to lazy_list") + + cdef lazy_list_from_iterator l = lazy_list_from_iterator.__new__(lazy_list_from_iterator) + l.cache = self[:] + l.start = 0 + l.stop = PY_SSIZE_T_MAX + l.step = 1 + l.iterator = iter(other) + return l def __repr__(self): r""" @@ -494,7 +471,7 @@ cdef class lazy_list(object): lazy list [0, 1, 2, ...] """ cdef Py_ssize_t num_elts = 1 + (self.stop-self.start-1) / self.step - cdef Py_ssize_t length = PyList_GET_SIZE(self.cache) + cdef Py_ssize_t length = len(self.cache) if (length <= self.start + 3*self.step and num_elts != length / self.step): @@ -505,23 +482,23 @@ cdef class lazy_list(object): return "lazy list []" if num_elts == 1: - return "lazy list [%s]"%(repr(self.get(0))) + return "lazy list [{!r}]".format(self.get(0)) if num_elts == 2: - return "lazy list [%s, %s]"%( - repr(self.get(0)), - repr(self.get(1))) + return "lazy list [{!r}, {!r}]".format( + self.get(0), + self.get(1)) if num_elts == 3: - return "lazy list [%s, %s, %s]"%( - repr(self.get(0)), - repr(self.get(1)), - repr(self.get(2))) + return "lazy list [{!r}, {!r}, {!r}]".format( + self.get(0), + self.get(1), + self.get(2)) - return "lazy list [%s, %s, %s, ...]"%( - repr(self.get(0)), - repr(self.get(1)), - repr(self.get(2))) + return "lazy list [{!r}, {!r}, {!r}, ...]".format( + self.get(0), + self.get(1), + self.get(2)) def __reduce__(self): r""" @@ -536,71 +513,71 @@ cdef class lazy_list(object): sage: y = iter(x) sage: print next(y), next(y), next(y) 0 1 2 + sage: m2 = m[3::2] + sage: loads(dumps(m2)) + lazy list [3, 5, 7, ...] """ - return lazy_list, (self.iterator, self.cache, self.start, self.stop, self.step) + if self.master is None: + raise NotImplementedError + return slice_unpickle, (self.master, self.start, self.stop, self.step) - cdef int update_cache_up_to(self, Py_ssize_t i) except -1: + cpdef int _fit(self, Py_ssize_t n) except -1: r""" - Update the cache up to ``i``. + Fill the cache making the term at index ``n`` available. - If the iterator stops, the function silently return 0 (no error are - raised). Otherwise it returns 1. + You can access the term at position ``n`` from the cache when it returns + ``0``. - .. TODO:: + OUTPUT: - This method should be implemented in such way that it does not raise - StopIteration (using PyIter_Next from sage/ext/python_iterator.pxi). - The fact that the iterator stops before than expected may be encoded - in the returned value of the function: + - ``1`` -- the lazy list is actually finite and shorter than ``n`` - * 0 : function succeded - * 1 : iterator stopped before stop was reached - * -1 : an error occurred - """ - while PyList_GET_SIZE(self.cache) <= i: - PyList_Append(self.cache, next(self.iterator)) - return 0 + - ``0`` -- you can safely access the term at position ``n`` after this call - def _fit(self, n): - r""" - Re-adjust ``self.stop`` if the iterator stops before ``n``. - - After a call to ``self._fit(n)`` and if *after the call* ``n`` is less - than ``self.stop`` then you may safely call ``self.cache[n]``. In other - words, ``self._fit(n)` ensure that either the lazy list is completely - expanded in memory or that you may have access to the ``n``-th item. + - ``-1`` -- to handle Python errors (you can ignore it in Python code) EXAMPLES:: sage: from sage.misc.lazy_list import lazy_list - sage: l = lazy_list([0,1,2,-34,3,2,-5,12,1,4,-18,5,-12])[2::3] - sage: l.info() + sage: l = lazy_list(iter([0,1,2,-34,3,2,-5,12,1,4,-18,5,-12]))[2::3] + sage: l._info() cache length 0 start 2 - stop 14 + stop 9223372036854775807 # 64-bit + stop 2147483647 # 32-bit step 3 - sage: l - lazy list [2, 2, 1, ...] sage: l._fit(13) - sage: l.info() + 1 + sage: l._info() cache length 13 start 2 stop 14 step 3 + + sage: l = lazy_list([0]*12)[1::2] + sage: l._fit(100) + 1 + sage: l._info() + cache length 12 + start 1 + stop 13 + step 2 + sage: l._fit(100) + 1 """ - if n >= self.stop: - n = self.stop - try: - self.update_cache_up_to(n) - except StopIteration: - self.stop = PyList_GET_SIZE(self.cache) + if n > self.stop - self.step: + return 1 + if self.update_cache_up_to(n): + self.stop = len(self.cache) if self.stop <= self.start: self.start = self.stop = 0 self.step = 1 if (self.start - self.stop) % self.step: self.stop += self.step + (self.start - self.stop) % self.step + return 1 + return 0 - def __call__(self, i): + cpdef get(self, Py_ssize_t i): r""" Return the element at position ``i``. @@ -635,24 +612,29 @@ cdef class lazy_list(object): ... TypeError: rational is not an integer """ - cdef Py_ssize_t j - - if not hasattr(i, '__index__'): - raise TypeError("indices must be integers, not %s"%type(i).__name__) - - j = i.__index__() - if j < 0: + if i < 0: raise ValueError("indices must be non negative") - j = self.start + j * self.step - if j >= self.stop: + i = self.start + i*self.step + if self._fit(i): raise IndexError("lazy list index out of range") - self._fit(j) - if j >= self.stop: - raise IndexError("lazy list index out of range") - return PyList_GET_ITEM(self.cache, j) + return self.cache[i] - get = __call__ + def __call__(self, i): + r""" + An alias for :meth:`get` + + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list + sage: from itertools import chain, repeat + sage: f = lazy_list(chain(iter([1,2,3]), repeat('a'))) + sage: f(2) + 3 + sage: f(3) + 'a' + """ + return self.get(i) def __iter__(self): r""" @@ -663,15 +645,31 @@ cdef class lazy_list(object): sage: from itertools import count sage: from sage.misc.lazy_list import lazy_list sage: iter(lazy_list(count())) - iterator of lazy list [0, 1, 2, ...] + + + :: + + sage: l = lazy_list(i^2 for i in xrange(5)) + sage: list(l) + [0, 1, 4, 9, 16] + sage: l._info() + cache length 5 + start 0 + stop 5 + step 1 """ - if self.stop == PY_SSIZE_T_MAX: - return lazy_list_iterator(self) - return stopped_lazy_list_iterator(self) + cdef Py_ssize_t i + + i = self.start + while i < self.stop: + if self._fit(i): + return + yield self.cache[i] + i += self.step def __getitem__(self, key): r""" - Returns a lazy list which shares the same cache. + Return a lazy list which shares the same cache. EXAMPLES:: @@ -702,24 +700,63 @@ cdef class lazy_list(object): stop are congruent modulo step:: sage: P = lazy_list(iter(Primes())) - sage: P[1:12:4].start_stop_step() - (1, 13, 4) - sage: P[1:13:4].start_stop_step() - (1, 13, 4) - sage: P[1:14:4].start_stop_step() - (1, 17, 4) + sage: P[1:12:4]._info() + cache length 0 + start 1 + stop 13 + step 4 + sage: P[1:13:4]._info() + cache length 0 + start 1 + stop 13 + step 4 + sage: P[1:14:4]._info() + cache length 0 + start 1 + stop 17 + step 4 + sage: Q = P[100:1042233:12] + sage: Q._info() + cache length 0 + start 100 + stop 1042240 + step 12 + sage: R = Q[233::3] + sage: R._info() + cache length 0 + start 2896 + stop 1042252 + step 36 + sage: 1042252%36 == 2896%36 + True We check commutation:: sage: l = lazy_list(iter(xrange(10000))) sage: l1 = l[::2][:3001] sage: l2 = l[:6002][::2] - sage: l1.start_stop_step() == l2.start_stop_step() - True + sage: l1._info() + cache length 0 + start 0 + stop 6002 + step 2 + sage: l2._info() + cache length 0 + start 0 + stop 6002 + step 2 sage: l3 = l1[13::2][:50:2] sage: l4 = l1[:200][13:113:4] - sage: l3.start_stop_step() == l4.start_stop_step() - True + sage: l3._info() + cache length 0 + start 26 + stop 226 + step 8 + sage: l4._info() + cache length 0 + start 26 + stop 226 + step 8 Further tests:: @@ -732,42 +769,17 @@ cdef class lazy_list(object): lazy list [0, 0] sage: l[::2][2::][2::3] lazy list [0, 0, 0] + sage: l[4:3][:] is l[18:2] # *the* empty_lazy_list + True """ if not isinstance(key, slice): - key = key.__index__() - if isinstance(key, (int,long)): return self.get(key) # the following make all terms > 0 - if key.start is None: - start = 0 - elif isinstance(key.start, (int,long)): - start = key.start - else: - try: - start = key.start.__index__() - except Exception: - raise TypeError("slice indices must be integers or None or have an __index__ method") - - if key.stop is None: - stop = PY_SSIZE_T_MAX - elif isinstance(key.stop, (int,long)): - stop = key.stop - else: - try: - stop = key.stop.__index__() - except Exception: - raise TypeError("slice indices must be integers or None or have an __index__ method") - - if key.step is None: - step = 1 - elif isinstance(key.step, (int,long)): - step = key.step - else: - try: - step = key.step.__index__() - except Exception: - raise TypeError("slice indices must be integers or None or have an __index__ method") + cdef Py_ssize_t start, stop, step + start = 0 if key.start is None else key.start + stop = PY_SSIZE_T_MAX if key.stop is None else key.stop + step = 1 if key.step is None else key.step if step == 0: raise TypeError("step may not be 0") @@ -783,9 +795,284 @@ cdef class lazy_list(object): if stop != PY_SSIZE_T_MAX and stop%step != start%step: stop = stop - (stop-start)%step + step - if start >= stop: - l = [] - return lazy_list(iter(l), l, 0, 0, 1) + if stop <= start: + return empty_lazy_list + + # here we return a slice of self. That is to say, a lazy list which + # shares the same cache of values + cdef lazy_list_generic l = lazy_list_generic.__new__(lazy_list_generic) + l.master = self + l.cache = self.cache + l.start = start + l.stop = stop + l.step = step + + return l + + cdef int update_cache_up_to(self, Py_ssize_t i) except -1: + r""" + Update the cache up to ``i``. + + This is the default implementation that calls ``_new_slice``. + + OUTPUT: + + - ``-1`` -- a Python error occurred + + - ``0`` -- the cache has now size larger than ``i`` + + - ``1`` -- the lazy list is actually finite and shorter than ``i`` + """ + if self.master is not None: # this is a slice + return self.master.update_cache_up_to(i) + + cdef list l + while len(self.cache) <= i: + l = self._new_slice() + if not l: + return 1 + self.cache.extend(l) + return 0 + +cdef class lazy_list_from_iterator(lazy_list_generic): + r""" + Lazy list built from an iterator. + + EXAMPLES:: + + sage: from sage.misc.lazy_list import lazy_list + sage: from itertools import count + sage: m = lazy_list(count()); m + lazy list [0, 1, 2, ...] + + sage: m2 = lazy_list(count(), start=8, stop=20551, step=2) + sage: m2 + lazy list [8, 10, 12, ...] + + sage: x = iter(m) + sage: print next(x), next(x), next(x) + 0 1 2 + sage: y = iter(m) + sage: print next(y), next(y), next(y) + 0 1 2 + sage: print next(x), next(y) + 3 3 + sage: loads(dumps(m)) + lazy list [0, 1, 2, ...] + """ + def __init__(self, iterator, cache=None, stop=None): + r""" + INPUT: + + - ``iterator`` -- an iterator + + - ``cache`` -- an optional list to be used as the cache. Be careful that + there is no copy. + + - ``stop`` -- an optional stop point + + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list_from_iterator + sage: from itertools import count + sage: lazy_list_from_iterator(count()) + lazy list [0, 1, 2, ...] + sage: lazy_list_from_iterator(count(), ['a'], 10) + lazy list ['a', 0, 1, ...] + sage: _.info() + cache length 4 + start 0 + stop 10 + step 1 + """ + self.iterator = iterator + lazy_list_generic.__init__(self, cache, None, stop, None) + + cdef int update_cache_up_to(self, Py_ssize_t i) except -1: + r""" + Update the cache up to ``i``. + + OUTPUT: + + - ``-1`` -- a Python error occurred + + - ``0`` -- everything went fine + + - ``1`` -- the iterator stopped before ``i` + """ + while len(self.cache) <= i: + try: + o = next(self.iterator) + except StopIteration: + return 1 + self.cache.append(o) + return 0 + + def __reduce__(self): + r""" + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list_from_iterator + sage: from itertools import count + sage: loads(dumps(lazy_list_from_iterator(count()))) + lazy list [0, 1, 2, ...] + sage: loads(dumps(lazy_list_from_iterator(count(), ['a']))) + lazy list ['a', 0, 1, ...] + """ + return lazy_list_from_iterator, (self.iterator, self.cache, self.stop) + +cdef class lazy_list_from_function(lazy_list_generic): + def __init__(self, function, cache=None, stop=None): + r""" + INPUT: + + - ``function`` -- a function that maps ``n`` to the element + at position ``n``. (This + function only needs to be defined for length larger than the length of + the cache.) + + - ``cache`` -- an optional list to be used as the cache. Be careful that + there is no copy. + + - ``stop`` -- an optional integer to specify the length of this lazy list. + (Otherwise it is considered infinite). + + EXAMPLES:: + + sage: from sage.misc.lazy_list import lazy_list_from_function + sage: lazy_list_from_function(euler_phi) + lazy list [0, 1, 1, ...] + sage: lazy_list_from_function(divisors, [None]) + lazy list [None, [1], [1, 2], ...] + + TESTS:: + + sage: def f(n): + ....: if n >= 5: raise StopIteration + ....: return 5 - n + sage: list(lazy_list_from_function(f)) + [5, 4, 3, 2, 1] + """ + self.callable = function + lazy_list_generic.__init__(self, cache) + + cdef int update_cache_up_to(self, Py_ssize_t i) except -1: + r""" + Update the cache up to ``i``. + + OUTPUT: - return lazy_list(self.iterator, self.cache, start, stop, step) + - ``-1`` -- a Python error occurred + - ``0`` -- everything went fine + + - ``1`` -- the iterator stopped before ``i` + """ + while len(self.cache) <= i: + self.cache.append(self.callable(len(self.cache))) + + def __reduce__(self): + r""" + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list_from_function + sage: loads(dumps(lazy_list_from_function(euler_phi))) + lazy list [0, 1, 1, ...] + sage: loads(dumps(lazy_list_from_function(divisors, [None]))) + lazy list [None, [1], [1, 2], ...] + """ + if self.start != 0 or self.step != 1: + raise RuntimeError + return lazy_list_from_function, (self.callable, self.cache, self.stop) + +cdef class lazy_list_from_update_function(lazy_list_generic): + def __init__(self, function, cache=None, stop=None): + r""" + INPUT: + + - ``function`` -- a function that updates a list of precomputed values. + The update function should take as input a list and make it longer + (using either the methods ``append`` or ``extend``). If after a call + to the update function the list of values is shorter a + ``RuntimeError`` will occurr. If no value is added then the lazy list + is considered finite. + + - ``cache`` -- an optional list to be used as the cache. Be careful that + there is no copy. + + - ``stop`` -- an optional integer to specify the length of this lazy list + (otherwise it is considered infinite) + + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list_from_update_function + sage: def update_function(values): + ....: n = len(values)+1 + ....: values.extend([n]*n) + sage: l = lazy_list_from_update_function(update_function) + sage: l[:20].list() + [1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16] + """ + self.update_function = function + lazy_list_generic.__init__(self, cache, None, stop, None) + + cdef int update_cache_up_to(self, Py_ssize_t i) except -1: + r""" + Update the cache up to ``i``. + + OUTPUT: + + - ``-1`` -- a Python error occurred + + - ``0`` -- everything went fine + + - ``1`` -- the iterator stopped before ``i` + """ + cdef Py_ssize_t l,ll + l = len(self.cache) + while l <= i: + self.update_function(self.cache) + ll = len(self.cache) + if ll < l: + raise RuntimeError("the update function made the cache shorter") + elif l == ll: + return 1 + l = ll + return 0 + + def __reduce__(self): + r""" + TESTS:: + + sage: from sage.misc.lazy_list import lazy_list + + sage: def my_update_function(values): values.append(ZZ(len(values)).is_prime()) + sage: l = lazy_list(update_function=my_update_function) + sage: l[4] + False + sage: loads(dumps(l)) # not tested (works in console though) + lazy list [False, False, True, ...] + + sage: def say_hey(cache): print "hey" + sage: l = lazy_list(update_function=say_hey, initial_values=range(10)) + sage: l._fit(10) + hey + 1 + sage: l._info() + cache length 10 + start 0 + stop 10 + step 1 + sage: l2 = loads(dumps(l)) # not tested + sage: l2._info() # not tested + sage: l2._info() # not tested + cache length 10 + start 0 + stop 10 + step 1 + sage: l.list() == l2.list() # not tested + True + """ + if self.start != 0 or self.step != 1: + raise RuntimeError + return lazy_list_from_update_function, (self.update_function, self.cache, self.stop) diff --git a/src/sage/misc/memory_info.py b/src/sage/misc/memory_info.py index 9a05f816721..46fb3853b8b 100644 --- a/src/sage/misc/memory_info.py +++ b/src/sage/misc/memory_info.py @@ -20,15 +20,19 @@ 15340593152 """ -######################################################################## +#***************************************************************************** # Copyright (C) 2012 Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -######################################################################## +#***************************************************************************** + import subprocess +from sys import maxsize from sage.structure.sage_object import SageObject memory_info_instance = None @@ -100,16 +104,20 @@ def rlimit_address_space(self): """ import resource try: - return resource.getrlimit(resource.RLIMIT_AS)[1] + limit = resource.getrlimit(resource.RLIMIT_AS)[0] except resource.error: return -1 + if limit == resource.RLIM_INFINITY: + return -1 + return limit def virtual_memory_limit(self): """ Return the upper limit for virtual memory usage - This is the value set by ``ulimit -v`` at the command line or - a practical limit if no limit is set. + This is the value set by ``ulimit -v`` at the command line + (bounded by ``sys.maxsize``) or a practical limit if no limit + is set. OUTPUT: @@ -121,18 +129,15 @@ def virtual_memory_limit(self): sage: mem = MemoryInfo() sage: mem.virtual_memory_limit() > 0 True + sage: mem.virtual_memory_limit() <= sys.maxsize + True """ limit = self.rlimit_address_space() - if limit >=0: - return limit - else: - avail = self.total_swap() + self.total_ram() - import platform - if platform.architecture()[0] == '32bit': - # 2GB is likely the single-process address space limit - return min(avail, 2 * 1024**3) - else: - return avail + if limit < 0: + limit = self.total_swap() + self.total_ram() + + # Use less than half of the addressable memory + return min(maxsize, limit) class MemoryInfo_proc(MemoryInfo_base): diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 9818e984bac..74b5744e226 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -18,10 +18,11 @@ Check that argspecs of extension function/methods appear correctly, see :trac:`12849`:: - sage: docfilename = os.path.join(SAGE_DOC, 'output', 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html') + sage: from sage.env import SAGE_DOC_OUTPUT + sage: docfilename = os.path.join(SAGE_DOC_OUTPUT, 'html', 'en', 'reference', 'calculus', 'sage', 'symbolic', 'expression.html') sage: for line in open(docfilename): - ... if "#sage.symbolic.expression.Expression.N" in line: - ... print line + ....: if "#sage.symbolic.expression.Expression.N" in line: + ....: print line N(prec=None, digits=None, algorithm=None)... """ #***************************************************************************** @@ -39,7 +40,7 @@ from sage.misc.viewer import browser from sage.misc.temporary_file import tmp_dir import sage.version -from sage.env import SAGE_DOC, SAGE_SRC +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT, SAGE_SRC # The detex function does two kinds of substitutions: math, which # should only be done on the command line -- in the notebook, these @@ -157,7 +158,6 @@ def _rmcmd(s, cmd, left='', right=''): ## + right + s[m.end():] ## return s -import re itempattern = re.compile(r"\\item\[?([^]]*)\]? *(.*)") itemreplace = r"* \1 \2" @@ -459,7 +459,7 @@ def process_extlinks(s, embedded=False): return s oldpath = sys.path sys.path = [os.path.join(SAGE_DOC, 'common')] + oldpath - from conf import pythonversion, extlinks + from conf import extlinks sys.path = oldpath for key in extlinks: while True: @@ -800,19 +800,14 @@ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='', module = '' exts = ['html'] title = 'Documentation' - base_path = os.path.join(SAGE_DOC, 'output') + base_path = SAGE_DOC_OUTPUT doc_path = SAGE_DOC - # We need to import stuff from SAGE_DOC/common - # To do this, we temporarily change sys.path - oldpath = sys.path - sys.path = oldpath + [os.path.join(SAGE_DOC, 'common')] - import build_options as builder + from sage_setup.docbuild.build_options import LANGUAGES, OMIT # List of languages - lang = builder.LANGUAGES + lang = LANGUAGES # Documents in SAGE_DOC/LANG/ to omit - omit = builder.OMIT - sys.path = oldpath + omit = OMIT # List of documents, minus the omitted ones documents = [] @@ -824,9 +819,9 @@ def _search_src_or_doc(what, string, extra1='', extra2='', extra3='', # Check to see if any documents are missing. This just # checks to see if the appropriate output directory exists, # not that it contains a complete build of the docs. - missing = [os.path.join(doc_path, 'output', 'html', doc) + missing = [os.path.join(SAGE_DOC_OUTPUT, 'html', doc) for doc in documents if not - os.path.exists(os.path.join(doc_path, 'output', 'html', doc))] + os.path.exists(os.path.join(SAGE_DOC_OUTPUT, 'html', doc))] num_missing = len(missing) if num_missing > 0: print("""Warning, the following Sage documentation hasn't been built, @@ -1107,7 +1102,7 @@ def search_doc(string, extra1='', extra2='', extra3='', extra4='', search is case-insensitive by default. The file paths in the output are relative to - ``$SAGE_DOC/output``. + ``$SAGE_DOC_OUTPUT``. INPUT: same as for :func:`search_src`. @@ -1314,7 +1309,7 @@ def __init__(self): 'http://localhost:8000/doc/live/' """ self._base_url = "http://localhost:8000/doc/live/" - self._base_path = os.path.join(SAGE_DOC, "output/html/en/") + self._base_path = os.path.join(SAGE_DOC_OUTPUT, "html", "en") def __call__(self, obj, output='html', view=True): r""" @@ -1381,7 +1376,7 @@ def __call__(self, obj, output='html', view=True): path = os.path.join(tmp_dir(), "temp.html") filed = open(path, 'w') - static_path = os.path.join(SAGE_DOC, 'output/html/en/_static') + static_path = os.path.join(SAGE_DOC_OUTPUT, "html", "en", "_static") if os.path.exists(static_path): title = obj_name + ' - Sage ' + sage.version.version + ' Documentation' template = """' + sage: formatvalue_reproducible([object(), object()]) + '=[, ]' + """ + s = _re_address.sub("", repr(obj)) + return "=" + s + + +def sage_formatargspec(*argspec): + """ + Format the argspec in a reproducible way. + + EXAMPLES:: + + sage: import inspect + sage: from sage.misc.sageinspect import sage_getargspec + sage: from sage.misc.sageinspect import sage_formatargspec + sage: def foo(f=lambda x:x): pass + sage: A = sage_getargspec(foo) + sage: print inspect.formatargspec(*A) + (f= at 0x...>) + sage: print sage_formatargspec(*A) + (f=>) + """ + s = inspect.formatargspec(*argspec, formatvalue=formatvalue_reproducible) + return s + + def sage_getdef(obj, obj_name=''): r""" Return the definition header for any callable object. @@ -1978,7 +2026,7 @@ def sage_getsourcelines(obj): sage: from sage.misc.sageinspect import sage_getsourcelines sage: sage_getsourcelines(matrix)[1] - 732 + 733 sage: sage_getsourcelines(matrix)[0][0][6:] 'MatrixFactory(object):\n' diff --git a/src/sage/modular/abvar/homology.py b/src/sage/modular/abvar/homology.py index 3e797d83c1e..2fc731b88d8 100644 --- a/src/sage/modular/abvar/homology.py +++ b/src/sage/modular/abvar/homology.py @@ -40,16 +40,19 @@ Hecke operator T_7 on Submodule of rank 2 of Integral Homology of Abelian variety J0(43) of dimension 3 """ -########################################################################### -# Copyright (C) 2007 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ # -########################################################################### +#***************************************************************************** +# Copyright (C) 2007 William Stein +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** from sage.modular.hecke.all import HeckeModule_free_module -from sage.rings.all import Integer, ZZ, QQ -from sage.rings.commutative_ring import is_CommutativeRing +from sage.rings.all import Integer, ZZ, QQ, CommutativeRing import abvar @@ -109,7 +112,7 @@ def __init__(self, abvar, base): sage: loads(dumps(H)) == H True """ - if not is_CommutativeRing(base): + if not isinstance(base, CommutativeRing): raise TypeError("base ring must be a commutative ring") HeckeModule_free_module.__init__( self, base, abvar.level(), weight=2) diff --git a/src/sage/modular/arithgroup/congroup_gamma1.py b/src/sage/modular/arithgroup/congroup_gamma1.py index 8217285bbf0..17dff84a4e6 100644 --- a/src/sage/modular/arithgroup/congroup_gamma1.py +++ b/src/sage/modular/arithgroup/congroup_gamma1.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Congruence Subgroup `\Gamma_1(N)` """ @@ -348,7 +349,7 @@ def dimension_modular_forms(self, k=2, eps=None, algorithm="CohenOesterle"): - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or - by Moebius inversion using the subgroups GammaH (a method due to + by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES:: @@ -397,7 +398,7 @@ def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or - by Moebius inversion using the subgroups GammaH (a method due to + by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES: @@ -491,7 +492,7 @@ def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or - by Moebius inversion using the subgroups GammaH (a method due to + by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: @@ -575,7 +576,7 @@ def dimension_new_cusp_forms(self, k=2, eps=None, p=0, algorithm="CohenOesterle" - ``algorithm`` - either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or - by Moebius inversion using the subgroups GammaH (a method due to + by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES:: diff --git a/src/sage/modular/arithgroup/congroup_gammaH.py b/src/sage/modular/arithgroup/congroup_gammaH.py index 35a70502ccd..15d502d0a2a 100644 --- a/src/sage/modular/arithgroup/congroup_gammaH.py +++ b/src/sage/modular/arithgroup/congroup_gammaH.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Congruence Subgroup `\Gamma_H(N)` @@ -1293,7 +1294,7 @@ def mumu(N): `(-2)^v` where `v` is the number of primes that exactly divide `N`. - This is similar to the Moebius function. + This is similar to the Möbius function. INPUT: diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp index 7c436629471..ddbea242caf 100644 --- a/src/sage/modular/arithgroup/farey.cpp +++ b/src/sage/modular/arithgroup/farey.cpp @@ -709,7 +709,7 @@ vector FareySymbol::init_cusps() const { } } } - // in earlier version: shift negative cusps to positve ones + // in earlier version: shift negative cusps to positive ones sort(c.begin(), c.end()); return c; } diff --git a/src/sage/modular/hecke/module.py b/src/sage/modular/hecke/module.py index 5c86ae3b727..8f90e746ffa 100644 --- a/src/sage/modular/hecke/module.py +++ b/src/sage/modular/hecke/module.py @@ -2,23 +2,17 @@ Hecke modules """ -########################################################################################## +#***************************************************************************** # Copyright (C) 2004,2005,2006 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -########################################################################################## +#***************************************************************************** import sage.rings.all -from sage.rings.commutative_ring import is_CommutativeRing import sage.arith.all as arith import sage.misc.misc as misc import sage.modules.module @@ -79,7 +73,7 @@ def __init__(self, base_ring, level, category=None): sage: ModularForms(3, 3).category() Category of Hecke modules over Rational Field """ - if not is_CommutativeRing(base_ring): + if not isinstance(base_ring, sage.rings.all.CommutativeRing): raise TypeError("base_ring must be commutative ring") from sage.categories.hecke_modules import HeckeModules diff --git a/src/sage/modular/modform/constructor.py b/src/sage/modular/modform/constructor.py index 44ec6faff0c..1bb40fff92b 100644 --- a/src/sage/modular/modform/constructor.py +++ b/src/sage/modular/modform/constructor.py @@ -19,13 +19,15 @@ """ -######################################################################### -# Copyright (C) 2004--2006 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) +#***************************************************************************** +# Copyright (C) 2004-2006 William Stein # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -######################################################################### +#***************************************************************************** import weakref import re @@ -34,8 +36,6 @@ import sage.modular.dirichlet as dirichlet import sage.rings.all as rings -from sage.rings.commutative_ring import is_CommutativeRing - import ambient_eps import ambient_g0 import ambient_g1 @@ -123,7 +123,7 @@ def canonical_parameters(group, level, weight, base_ring): raise ValueError("group and level do not match.") group = arithgroup.Gamma0(m) - if not is_CommutativeRing(base_ring): + if not isinstance(base_ring, rings.CommutativeRing): raise TypeError("base_ring (=%s) must be a commutative ring"%base_ring) # it is *very* important to include the level as part of the data diff --git a/src/sage/modular/modform/eis_series.py b/src/sage/modular/modform/eis_series.py index a5283679311..4f141cdf0cc 100644 --- a/src/sage/modular/modform/eis_series.py +++ b/src/sage/modular/modform/eis_series.py @@ -18,7 +18,7 @@ from sage.modular.arithgroup.congroup_gammaH import GammaH_class from sage.rings.all import Integer, CyclotomicField, ZZ, QQ, Integer from sage.arith.all import bernoulli, divisors, is_squarefree, lcm -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.power_series_ring import PowerSeriesRing from eis_series_cython import eisenstein_series_poly, Ek_ZZ diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index 276b7fc484a..dc813360ade 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -1524,7 +1524,7 @@ def _q_expansion_cached(self, prec, fix_d, subs_d, d_num_prec, fix_prec = False) if (fix_prec == False): #if (prec <1): - # print "Warning: non-positiv precision!" + # print "Warning: non-positive precision!" if ((not self.is_zero()) and prec <= self.order_at(infinity)): from warnings import warn warn("precision too low to determine any coefficient!") diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index 3e59dc9e98b..4fbc03ceae2 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -86,7 +86,6 @@ import sage.modular.dirichlet as dirichlet import sage.rings.rational_field as rational_field import sage.rings.all as rings -from sage.rings.commutative_ring import is_CommutativeRing def canonical_parameters(group, weight, sign, base_ring): @@ -129,7 +128,7 @@ def canonical_parameters(group, weight, sign, base_ring): if base_ring is None: base_ring = rational_field.RationalField() - if not is_CommutativeRing(base_ring): + if not isinstance(base_ring, rings.CommutativeRing): raise TypeError("base_ring (=%s) must be a commutative ring"%base_ring) if not base_ring.is_field(): diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 3ec5e47d435..4cbd969b822 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -78,7 +78,7 @@ from sage.functions.all import floor, ceil from sage.arith.all import valuation from sage.rings.all import ZZ, Zmod, Infinity, Integer -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.modular.modform.all import ModularForms, ModularFormsRing, delta_qexp, eisenstein_series_qexp from sage.modular.dims import dimension_modular_forms from sage.misc.functional import dimension,transpose,charpoly diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 5e7f03e56fb..53e4c7b7a54 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -208,9 +208,7 @@ class if `I=aJ` for some `a \in A^*`. (Left `\mathcal{O}`-ideals are # imports from sage.misc.all import prod, verbose -from sage.rings.all import Integer, ZZ, QQ, PolynomialRing, GF - -from sage.rings.commutative_ring import is_CommutativeRing +from sage.rings.all import Integer, ZZ, QQ, PolynomialRing, GF, CommutativeRing from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra, basis_for_quaternion_lattice from sage.algebras.quatalg.quaternion_algebra_cython import rational_matrix_from_rational_quaternions @@ -297,7 +295,7 @@ def BrandtModule(N, M=1, weight=2, base_ring=QQ, use_cache=True): raise ValueError("M must be coprime to N") if weight < 2: raise ValueError("weight must be at least 2") - if not is_CommutativeRing(base_ring): + if not isinstance(base_ring, CommutativeRing): raise TypeError("base_ring must be a commutative ring") key = (N, M, weight, base_ring) if use_cache: diff --git a/src/sage/modules/all.py b/src/sage/modules/all.py index a01a0147e94..0be9f6b4c31 100644 --- a/src/sage/modules/all.py +++ b/src/sage/modules/all.py @@ -29,3 +29,7 @@ lazy_import("sage.modules", ("vector_symbolic_dense", "vector_callable_symbolic_dense"), deprecation=18140) + +lazy_import('sage.modules.filtered_vector_space', 'FilteredVectorSpace') +lazy_import('sage.modules.multi_filtered_vector_space', 'MultiFilteredVectorSpace') + diff --git a/src/sage/modules/filtered_vector_space.py b/src/sage/modules/filtered_vector_space.py new file mode 100644 index 00000000000..b8624985742 --- /dev/null +++ b/src/sage/modules/filtered_vector_space.py @@ -0,0 +1,1250 @@ +r""" +`\ZZ`-Filtered Vector Spaces + +This module implements filtered vector spaces, that is, a descending +sequence of vector spaces + +.. math:: + + \cdots \supset F_d \supset F_{d+1} \supset F_{d+2} \supset \cdots + +with degrees `d\in \ZZ`. It is not required that `F_d` is the entire +ambient space for `d\ll 0` (see +:meth:`~FilteredVectorSpace_class.is_exhaustive`) nor that `F_d=0` for +`d\gg 0` (see :meth:`~FilteredVectorSpace_class.is_separating`). To +construct a filtered vector space, use the :func:`FilteredVectorSpace` +command. It supports easy creation of simple filtrations, for example +the trivial one:: + + sage: FilteredVectorSpace(2, base_ring=RDF) + RDF^2 + +The next-simplest filtration has a single non-trivial inclusion +between `V_d` and `V_{d+1}`:: + + sage: d = 1 + sage: V = FilteredVectorSpace(2, d); V + QQ^2 >= 0 + sage: [V.get_degree(i).dimension() for i in range(0,4)] + [2, 2, 0, 0] + +To construct general filtrations, you need tell Sage about generating +vectors for the nested subspaces. For example, a dictionary whose keys +are the degrees and values are a list of generators:: + + sage: r1 = (1, 0, 5) + sage: r2 = (0, 1, 2) + sage: r3 = (1, 2, 1) + sage: V = FilteredVectorSpace({0:[r1, r2, r3], 1:[r1, r2], 3:[r1]}); V + QQ^3 >= QQ^2 >= QQ^1 >= QQ^1 >= 0 + +For degrees `d` that are not specified, the associated vector subspace +is the same as the next-lower degree, that is, `V_d \simeq +V_{d-1}`. In the above example, this means that + +* `V_d \simeq \QQ^3` for `d<0` +* `V_0 = \mathop{span}(r_1, r_2) \simeq \QQ^2` +* `V_1 = V_2 = \mathop{span}(r_3) \simeq \QQ` +* `V_d = 0` for `d \geq 3` + +That is:: + + sage: V.get_degree(0) == V + True + sage: V.get_degree(1) == V.span([r1, r2]) + True + sage: V.get_degree(2) == V.get_degree(3) == V.span([r1]) + True + sage: V.get_degree(4) == V.get_degree(5) == V.span([]) + True + +If you have many generators you can just pass the generators once and +then refer to them by index:: + + sage: FilteredVectorSpace([r1, r2, r3], {0:[0,1,2], 1:[1,2], 3:[1]}) + QQ^3 >= QQ^2 >= QQ^1 >= QQ^1 >= 0 + +Note that generators for the degree-`d` subspace of the filtration are +automatically generators for all lower degrees. For example, here we +do not have to specify the ray `r_2` separately in degree 1:: + + sage: FilteredVectorSpace([r1, r2, r3], {0:[0 ], 1:[1]}) + QQ^2 >= QQ^1 >= 0 in QQ^3 + sage: FilteredVectorSpace([r1, r2, r3], {0:[0, 1], 1:[1]}) + QQ^2 >= QQ^1 >= 0 in QQ^3 + +The degree can be infinite (plus infinity), this allows construction +of filtered vector spaces that are not eventually zero in high +degree:: + + sage: FilteredVectorSpace([r1, r2, r3], {0:[0,1], oo:[1]}) + QQ^2 >= QQ^1 in QQ^3 + +Any field can be used as the vector space base. For example a finite +field:: + + sage: F. = GF(5^3) + sage: r1 = (a, 0, F(5)); r1 + (a, 0, 0) + sage: FilteredVectorSpace([r1, r2, r3], {0:[0,1], oo:[1]}, base_ring=F) + GF(125)^2 >= GF(125)^1 in GF(125)^3 + +Or the algebraic field:: + + sage: r1 = (1, 0, 1+QQbar(I)); r1 + (1, 0, I + 1) + sage: FilteredVectorSpace([r1, r2, r3], {0:[0,1], oo:[1]}, base_ring=QQbar) + Vector space of dimension 2 over Algebraic Field + >= Vector space of dimension 1 over Algebraic Field + in Vector space of dimension 3 over Algebraic Field +""" + +#***************************************************************************** +# Copyright (C) 2013 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.all import QQ, ZZ, RDF, RR, Integer +from sage.rings.infinity import InfinityRing, infinity, minus_infinity +from sage.categories.fields import Fields +from sage.modules.free_module import FreeModule_ambient_field, VectorSpace +from sage.matrix.constructor import vector, matrix +from sage.misc.all import uniq, cached_method + + +def is_FilteredVectorSpace(X): + """ + Test whether ``X`` is a filtered vector space. + + This function is for library use only. + + INPUT: + + - ``X`` -- anything. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.modules.filtered_vector_space import is_FilteredVectorSpace + sage: V = FilteredVectorSpace(2, 1) + sage: is_FilteredVectorSpace(V) + True + sage: is_FilteredVectorSpace('ceci n\'est pas une pipe') + False + """ + return isinstance(X, FilteredVectorSpace_class) + + +def FilteredVectorSpace(arg1, arg2=None, base_ring=QQ, check=True): + """ + Construct a filtered vector space. + + INPUT: + + This function accepts various input that determines the vector space and filtration. + + - Just the dimensionFilteredVectorSpace(dimension): Return the trivial filtration + (where all vector spaces are isomorphic). + + - Dimension and maximal degree, see + :func:`constructor_from_dim_degree` for arguments. Construct a + filtration with only one non-trivial step `V\supset 0` at the + given cutoff degree. + + - A dictionary containing the degrees as keys and a list of vector + space generators as values, see + :func:`FilteredVectorSpace_from_generators` + + - Generators and a dictionary containing the degrees as keys and + the indices of vector space generators as values, see + :func:`FilteredVectorSpace_from_generators_indices` + + In addition, the following keyword arguments are supported: + + - ``base_ring`` -- a field (optional, default `\QQ`). The base + field of the vector space. Must be a field. + + EXAMPLES: + + Just the dimension for the trivial filtration:: + + sage: FilteredVectorSpace(2) + QQ^2 + + Dimension and degree:: + + sage: FilteredVectorSpace(2, 1) + QQ^2 >= 0 + + Dictionary of generators:: + + sage: FilteredVectorSpace({1:[(1,0), (0,1)], 3:[(1,0)]}) + QQ^2 >= QQ^1 >= QQ^1 >= 0 + + Generators and a dictionary referring to them by index:: + + sage: FilteredVectorSpace([(1,0), (0,1)], {1:[0,1], 3:[0]}) + QQ^2 >= QQ^1 >= QQ^1 >= 0 + """ + if base_ring not in Fields(): + raise ValueError('the base_ring argument must be a field') + if arg1 in ZZ: + return construct_from_dim_degree(arg1, arg2, base_ring, check) + elif arg2 is None: + return construct_from_generators(arg1, base_ring, check) + else: + return construct_from_generators_indices(arg1, arg2, base_ring, check) + + +def normalize_degree(deg): + """ + Normalized the degree + + - ``deg`` -- something that defines the degree (either integer or + infinity). + + OUTPUT: + + Plus/minus infinity or a Sage integer. + + EXAMPLES:: + + sage: from sage.modules.filtered_vector_space import normalize_degree + sage: type(normalize_degree(int(1))) + + sage: normalize_degree(oo) + +Infinity + """ + try: + return ZZ(deg) + except TypeError: + pass + deg = InfinityRing(deg) + if deg == infinity: + return infinity + if deg == minus_infinity: + return minus_infinity + raise ValueError('not integer or infinity') + + +def construct_from_dim_degree(dim, max_degree, base_ring, check): + """ + Construct a filtered vector space. + + INPUT: + + - ``dim`` -- integer. The dimension. + + - ``max_degree`` -- integer or infinity. The maximal degree where + the vector subspace of the filtration is still the entire space. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 5); V + QQ^2 >= 0 + sage: V.get_degree(5) + Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + sage: V.get_degree(6) + Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + + sage: FilteredVectorSpace(2, oo) + QQ^2 + sage: FilteredVectorSpace(2, -oo) + 0 in QQ^2 + + TESTS:: + + sage: from sage.modules.filtered_vector_space import construct_from_dim_degree + sage: V = construct_from_dim_degree(2, 5, QQ, True); V + QQ^2 >= 0 + """ + if dim not in ZZ: + raise ValueError('dimension must be an integer') + dim = ZZ(dim) + from sage.matrix.constructor import identity_matrix + generators = identity_matrix(base_ring, dim).columns() + filtration = dict() + if max_degree is None: + max_degree = infinity + filtration[normalize_degree(max_degree)] = range(dim) + return construct_from_generators_indices(generators, filtration, base_ring, check) + + +def construct_from_generators(filtration, base_ring, check): + """ + Construct a filtered vector space. + + INPUT: + + - ``filtration`` -- a dictionary of filtration steps. Each + filtration step is a pair consisting of an integer degree and a + list/tuple/iterable of vector space generators. The integer + ``degree`` stipulates that all filtration steps of degree higher + or equal than ``degree`` (up to the next filtration step) are + said subspace. + + EXAMPLES:: + + sage: from sage.modules.filtered_vector_space import construct_from_generators + sage: r = [1, 2] + sage: construct_from_generators({1:[r]}, QQ, True) + QQ^1 >= 0 in QQ^2 + """ + def normalize_gen(v): + return tuple(map(base_ring, v)) + + # convert generator notation to generator+indices + if len(filtration) == 0: + raise ValueError('you need to specify at least one ray to deduce the dimension') + generators = [] + for gens in filtration.values(): + generators += map(normalize_gen, gens) + generators = tuple(uniq(generators)) + + # normalize filtration data + normalized = dict() + for deg, gens_deg in filtration.iteritems(): + indices = [generators.index(normalize_gen(v)) for v in gens_deg] + normalized[deg] = tuple(indices) + return construct_from_generators_indices(generators, normalized, base_ring, check) + + +def construct_from_generators_indices(generators, filtration, base_ring, check): + """ + Construct a filtered vector space. + + INPUT: + + - ``generators`` -- a list/tuple/iterable of vectors, or something + convertible to them. The generators spanning various + subspaces. + + - ``filtration`` -- a list or iterable of filtration steps. Each + filtration step is a pair ``(degree, ray_indices)``. The + ``ray_indices`` are a list or iterable of ray indices, which + span a subspace of the vector space. The integer ``degree`` + stipulates that all filtration steps of degree higher or equal + than ``degree`` (up to the next filtration step) are said + subspace. + + EXAMPLES:: + + sage: from sage.modules.filtered_vector_space import construct_from_generators_indices + sage: gens = [(1,0), (0,1), (-1,-1)] + sage: V = construct_from_generators_indices(gens, {1:[0,1], 3:[1]}, QQ, True); V + QQ^2 >= QQ^1 >= QQ^1 >= 0 + + TESTS:: + + sage: gens = [(int(1),int(0)), (0,1), (-1,-1)] + sage: construct_from_generators_indices(iter(gens), {int(0):[0, int(1)], 2:[2]}, QQ, True) + QQ^2 >= QQ^1 >= QQ^1 >= 0 + """ + # normalize generators + generators = map(list, generators) + + # deduce dimension + if len(generators) == 0: + dim = ZZ(0) + else: + dim = ZZ(len(generators[0])) + ambient = VectorSpace(base_ring, dim) + + # complete generators to a generating set + if matrix(base_ring, generators).rank() < dim: + complement = ambient.span(generators).complement() + generators = generators + list(complement.gens()) + # normalize generators II + generators = tuple(ambient(v) for v in generators) + + for v in generators: + v.set_immutable() + + # normalize filtration data + normalized = dict() + for deg, gens in filtration.iteritems(): + deg = normalize_degree(deg) + gens = map(ZZ, gens) + if any(i < 0 or i >= len(generators) for i in gens): + raise ValueError('generator index out of bounds') + normalized[deg] = tuple(sorted(gens)) + try: + del normalized[minus_infinity] + except KeyError: + pass + filtration = normalized + + return FilteredVectorSpace_class(base_ring, dim, generators, filtration, check=check) + + + + +class FilteredVectorSpace_class(FreeModule_ambient_field): + + def __init__(self, base_ring, dim, generators, filtration, check=True): + r""" + A descending filtration of a vector space + + INPUT: + + - ``base_ring`` -- a field. The base field of the ambient vector space. + + - ``dim`` -- integer. The dimension of the ambient vector space. + + - ``generators`` -- tuple of generators for the ambient vector + space. These will be used to span the subspaces of the + filtration. + + - ``filtration`` -- a dictionary of filtration steps in ray + index notation. See + :func:`construct_from_generators_indices` for details. + + - ``check`` -- boolean (optional; default: ``True``). Whether + to perform consistency checks. + + TESTS:: + + sage: from sage.modules.filtered_vector_space import FilteredVectorSpace_class + sage: gens = [(1,0,0), (1,1,0), (1,2,0), (-1,-1, 0), (0,0,1)] + sage: FilteredVectorSpace_class(QQ, 3, gens, {2:(0,1), oo:(4,)}) + QQ^3 >= QQ^1 + sage: FilteredVectorSpace_class(QQ, 3, gens, {2:(0,1), 3:(4,)}) + QQ^3 >= QQ^1 >= 0 + + The trivial filtration:: + + sage: FilteredVectorSpace_class(QQ, 3, gens, {}, QQ) + 0 in QQ^3 + + The empty vector space:: + + sage: FilteredVectorSpace_class(QQ, 0, [], {}) + 0 + + Higher-degree generators are automatically generators in lower degrees:: + + sage: FilteredVectorSpace_class(QQ, 3, gens, {2:(4,), 3:(1,)}) + QQ^2 >= QQ^1 >= 0 in QQ^3 + """ + if check: + assert isinstance(dim, Integer) + assert base_ring in Fields() + super(FilteredVectorSpace_class, self).__init__(base_ring, dim) + + if check: + assert matrix(generators).rank() == self.dimension() + assert isinstance(filtration, dict) + for degree, indices in filtration.iteritems(): + assert isinstance(degree, Integer) or degree == infinity + assert isinstance(indices, tuple) + assert all(isinstance(r, Integer) for r in indices) + + # Construct subspaces from the generators and store in self._filt + def make_subspace(indices): + return self.span([generators[i] for i in indices]) + + indices = set(filtration.pop(infinity, [])) + V = make_subspace(indices) + filtered_subspaces = [(infinity, V)] + for deg in reversed(sorted(filtration.keys())): + next_V = V + indices.update(filtration[deg]) + V = make_subspace(indices) + if V == next_V: # skip trivial filtrations + continue + filtered_subspaces.append((deg, V)) + filtered_subspaces.append((minus_infinity, V)) + filtered_subspaces.reverse() + self._filt = tuple(filtered_subspaces) + assert self._filt[0][0] is minus_infinity + + def change_ring(self, base_ring): + """ + Return the same filtration over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring. The new base ring. + + OUTPUT: + + This method returns a new filtered vector space whose + subspaces are defined by the same generators but over a + different base ring. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(1, 0); V + QQ^1 >= 0 + sage: V.change_ring(RDF) + RDF^1 >= 0 + """ + generators, filtration = self.presentation() + return FilteredVectorSpace(generators, filtration, base_ring=base_ring) + + def ambient_vector_space(self): + """ + Return the ambient (unfiltered) vector space. + + OUTPUT: + + A vector space. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(1, 0) + sage: V.ambient_vector_space() + Vector space of dimension 1 over Rational Field + """ + return VectorSpace(self.base_ring(), self.dimension()) + + @cached_method + def is_constant(self): + """ + Return whether the filtration is constant. + + OUTPUT: + + Boolean. Whether the filtered vector spaces are identical in + all degrees. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2); V + QQ^2 + sage: V.is_constant() + True + + sage: V = FilteredVectorSpace(1, 0); V + QQ^1 >= 0 + sage: V.is_constant() + False + + sage: V = FilteredVectorSpace({0:[(1,)]}); V + QQ^1 >= 0 + sage: V.is_constant() + False + """ + f = self._filt + return (len(f) == 1) or (len(f) == 2 and f[1][0] == infinity) + + def is_exhaustive(self): + """ + Return whether the filtration is exhaustive. + + A filtration $\{F_d\}$ in an ambient vector space $V$ is + exhaustive if $\cup F_d = V$. See also :meth:`is_separating`. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: F = FilteredVectorSpace({0:[(1,1)]}); F + QQ^1 >= 0 in QQ^2 + sage: F.is_exhaustive() + False + sage: G = FilteredVectorSpace(2, 0); G + QQ^2 >= 0 + sage: G.is_exhaustive() + True + """ + return self.get_degree(minus_infinity).dimension() == \ + self.ambient_vector_space().dimension() + + def is_separating(self): + """ + Return whether the filtration is separating. + + A filtration $\{F_d\}$ in an ambient vector space $V$ is + exhaustive if $\cap F_d = 0$. See also :meth:`is_exhaustive`. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: F = FilteredVectorSpace({0:[(1,1)]}); F + QQ^1 >= 0 in QQ^2 + sage: F.is_separating() + True + sage: G = FilteredVectorSpace({0:[(1,1,0)], oo:[(0,0,1)]}); G + QQ^2 >= QQ^1 in QQ^3 + sage: G.is_separating() + False + """ + return self.get_degree(infinity).dimension() == 0 + + @cached_method + def support(self): + """ + Return the degrees in which there are non-trivial generators. + + OUTPUT: + + A tuple of integers (and plus infinity) in ascending + order. The last entry is plus infinity if and only if the + flitration is not separating (see :meth:`is_separating`). + + EXAMPLES:: + + sage: G = FilteredVectorSpace({0:[(1,1,0)], 3:[(0,1,0)]}); G + QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 >= 0 in QQ^3 + sage: G.support() + (0, 3) + + sage: G = FilteredVectorSpace({0:[(1,1,0)], 3:[(0,1,0)], oo:[(0,0,1)]}); G + QQ^3 >= QQ^2 >= QQ^2 >= QQ^2 >= QQ^1 + sage: G.support() + (0, 3, +Infinity) + """ + if self.is_separating(): + filt = self._filt[1:-1] + else: + filt = self._filt[1:] + return tuple(f[0] for f in filt) + + @cached_method + def min_degree(self): + r""" + Return the lowest degree of the filtration. + + OUTPUT: + + Integer or plus infinity. The largest degree `d` of the + (descending) filtration such that the filtered vector space + `F_d` is still equal to `F_{-\infty}`. + + EXAMPLES:: + + sage: FilteredVectorSpace(1, 3).min_degree() + 3 + sage: FilteredVectorSpace(2).min_degree() + +Infinity + """ + if self.is_constant(): + return infinity + return self._filt[1][0] + + @cached_method + def max_degree(self): + r""" + Return the highest degree of the filtration. + + OUTPUT: + + Integer or minus infinity. The smallest degree of the + filtration such that the filtration is constant to the right. + + EXAMPLES:: + + sage: FilteredVectorSpace(1, 3).max_degree() + 4 + sage: FilteredVectorSpace({0:[[1]]}).max_degree() + 1 + sage: FilteredVectorSpace(3).max_degree() + -Infinity + """ + f = self._filt + if len(f) == 1: + return minus_infinity + d = f[-1][0] + if d == infinity: + if len(f) == 1: + return minus_infinity + else: + return f[-2][0] + 1 + else: + return d + 1 + + def get_degree(self, d): + r""" + Return the degree-``d`` entry of the filtration. + + INPUT: + + - ``d`` -- Integer. The desired degree of the filtration. + + OUTPUT: + + The degree-``d`` vector space in the filtration as subspace of + the ambient space. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2), (-1,-1)] + sage: F = FilteredVectorSpace(rays, {3:[1], 1:[1,2]}) + sage: F.get_degree(2) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 1] + sage: F.get_degree(oo) + Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + sage: F.get_degree(-oo) + Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + """ + d = normalize_degree(d) + for deg, Vdeg in self._filt: + if d <= deg: + return Vdeg + assert False # unreachable + + def graded(self, d): + r""" + Return the associated graded vectorspace. + + INPUT: + + - ``d`` -- integer. The degree. + + OUTPUT: + + The quotient `G_d = F_d / F_{d+1}`. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2)] + sage: F = FilteredVectorSpace(rays, {3:[1], 1:[1,2]}) + sage: F.graded(1) + Vector space quotient V/W of dimension 1 over Rational Field where + V: Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + W: Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 1] + """ + return self.get_degree(d).quotient(self.get_degree(d+1)) + + def presentation(self): + """ + Return a presentation in term of generators of various degrees. + + OUTPUT: + + A pair consisting of generators and a filtration suitable as + input to :func:`~construct_from_generators_indices`. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2), (-1,-1)] + sage: F = FilteredVectorSpace(rays, {0:[1, 2], 2:[3]}); F + QQ^2 >= QQ^1 >= QQ^1 >= 0 + sage: F.presentation() + (((0, 1), (1, 0), (1, 1)), {0: (1, 0), 2: (2,), +Infinity: ()}) + """ + # this could be done more efficiently with (potentially) less generators + generators = set() + filt = self._filt[1:] + for d, V in filt: + generators.update(V.echelonized_basis()) + generators = tuple(generators) + + filtration = dict() + for d, V in filt: + indices = [ZZ(generators.index(v)) for v in V.echelonized_basis()] + filtration[d] = tuple(indices) + return generators, filtration + + def _repr_field_name(self): + """ + Return an abbreviated field name as string + + RAISES: + + ``NotImplementedError``: The field does not have an + abbreviated name defined. + + EXAMPLES:: + + sage: FilteredVectorSpace(2, base_ring=QQ)._repr_field_name() + 'QQ' + + sage: F. = GF(9) + sage: FilteredVectorSpace(2, base_ring=F)._repr_field_name() + 'GF(9)' + + sage: FilteredVectorSpace(2, base_ring=AA)._repr_field_name() + Traceback (most recent call last): + ... + NotImplementedError + """ + if self.base_ring() == QQ: + return 'QQ' + elif self.base_ring() == RDF: + return 'RDF' + elif self.base_ring() == RR: + return 'RR' + from sage.categories.finite_fields import FiniteFields + if self.base_ring() in FiniteFields(): + return 'GF({0})'.format(len(self.base_ring())) + else: + raise NotImplementedError() + + def _repr_vector_space(self, dim): + """ + Return a string representation of the vector space of given dimension + + INPUT: + + - ``dim`` -- integer. + + OUTPUT: + + String representation of the vector space of dimension ``dim``. + + EXAMPLES:: + + sage: F = FilteredVectorSpace(3, base_ring=RDF) + sage: F._repr_vector_space(1234) + 'RDF^1234' + sage: F3 = FilteredVectorSpace(3, base_ring=GF(3)) + sage: F3._repr_vector_space(1234) + 'GF(3)^1234' + sage: F3 = FilteredVectorSpace(3, base_ring=AA) + sage: F3._repr_vector_space(1234) + 'Vector space of dimension 1234 over Algebraic Real Field' + """ + if dim == 0: + return '0' + try: + return self._repr_field_name() + '^' + str(dim) + except NotImplementedError: + return repr(VectorSpace(self.base_ring(), dim)) + + def _repr_degrees(self, min_deg, max_deg): + """ + Return a string representation + + This method is like :meth:`_repr_` except that the user can + select the range of degrees to be shown in the output. + + INPUT: + + - ``min_deg``, ``max_deg`` -- two integers. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2), (-1,-1)] + sage: F = FilteredVectorSpace(rays, {0:[1, 2], 2:[3]}) + sage: F._repr_degrees(-2, 4) + ['QQ^2', 'QQ^2', 'QQ^2', 'QQ^1', 'QQ^1', '0', '0', '0'] + """ + degrees = range(min_deg, max_deg+1) + dims = [] + for i in degrees + [infinity]: + d = self.get_degree(i).dimension() + dims.append(self._repr_vector_space(d)) + return dims + + def _repr_(self): + r""" + Return as string representation of ``self``. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2), (-1,-1)] + sage: FilteredVectorSpace(rays, {0:[1, 2], 2:[3]})._repr_() + 'QQ^2 >= QQ^1 >= QQ^1 >= 0' + sage: FilteredVectorSpace(rays, {0:[1, 2], oo:[3]}) + QQ^2 >= QQ^1 + sage: FilteredVectorSpace(rays, {oo:[3]}) + QQ^1 in QQ^2 + sage: FilteredVectorSpace(rays, {0:[3]}) + QQ^1 >= 0 in QQ^2 + sage: FilteredVectorSpace({1:[(1,0), (-1,1)], 3:[(1,0)]}, base_ring=GF(3)) + GF(3)^2 >= GF(3)^1 >= GF(3)^1 >= 0 + sage: FilteredVectorSpace({1:[(1,0), (-1,1)], 3:[(1,0)]}, base_ring=AA) + Vector space of dimension 2 over Algebraic Real Field + >= Vector space of dimension 1 over Algebraic Real Field + >= Vector space of dimension 1 over Algebraic Real Field >= 0 + """ + finite_support = [d for d in self.support() if d != infinity] + if len(finite_support) == 0: + dims = self._repr_degrees(0, -1) + else: + min_deg = finite_support[0] + max_deg = finite_support[-1] + dims = self._repr_degrees(min_deg, max_deg) + s = ' >= '.join(dims) + if not self.is_exhaustive(): + s += ' in ' + self._repr_vector_space(self.degree()) + return s + + def __cmp__(self, other): + """ + Compare two filtered vector spaces. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 0) + sage: W = FilteredVectorSpace([(1,0),(0,1)], {0:[0, 1]}) + sage: V == W + True + sage: V is W + False + + sage: W = FilteredVectorSpace([(1,0),(1,1)], {0:[1]}) + sage: V == W + False + + TESTS:: + + sage: P = toric_varieties.P2() + sage: T_P = P.sheaves.tangent_bundle() + sage: O_P = P.sheaves.trivial_bundle(1) + sage: S1 = T_P + O_P + sage: S2 = O_P + T_P + sage: S1._filt[0].is_isomorphic(S2._filt[0]) # known bug + True + sage: FilteredVectorSpace(2, base_ring=QQ) == FilteredVectorSpace(2, base_ring=GF(5)) + False + """ + c = cmp(type(self), type(other)) + if c!=0: return c + c = cmp(self.base_ring(), other.base_ring()) + if c!=0: return c + c = cmp(self.dimension(), other.dimension()) + if c!=0: return c + c = cmp(len(self._filt), len(other._filt)) + if c!=0: return c + for self_filt, other_filt in zip(self._filt, other._filt): + c = cmp(self_filt[0], other_filt[0]) # compare degree + if c!=0: return c + c = cmp(self_filt[1].echelonized_basis_matrix(), # compare vector subspace + other_filt[1].echelonized_basis_matrix()) + if c!=0: return c + return 0 + + def direct_sum(self, other): + """ + Return the direct sum. + + INPUT: + + - ``other`` -- a filtered vector space. + + OUTPUT: + + The direct sum as a filtered vector space. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 0) + sage: W = FilteredVectorSpace({0:[(1,-1),(2,1)], 1:[(1,1)]}) + sage: V.direct_sum(W) + QQ^4 >= QQ^1 >= 0 + sage: V + W # syntactic sugar + QQ^4 >= QQ^1 >= 0 + sage: V + V == FilteredVectorSpace(4, 0) + True + + sage: W = FilteredVectorSpace([(1,-1),(2,1)], {1:[0,1], 2:[1]}) + sage: V + W + QQ^4 >= QQ^2 >= QQ^1 >= 0 + + A suitable base ring is chosen if they do not match:: + + sage: v = [(1,0), (0,1)] + sage: F1 = FilteredVectorSpace(v, {0:[0], 1:[1]}, base_ring=QQ) + sage: F2 = FilteredVectorSpace(v, {0:[0], 1:[1]}, base_ring=RDF) + sage: F1 + F2 + RDF^4 >= RDF^2 >= 0 + """ + from sage.structure.element import get_coercion_model + base_ring = get_coercion_model().common_parent(self.base_ring(), other.base_ring()) + # construct the generators + self_gens, self_filt = self.presentation() + other_gens, other_filt = other.presentation() + generators = \ + [ list(v) + [base_ring.zero()]*other.dimension() for v in self_gens ] + \ + [ [base_ring.zero()]*self.dimension() + list(v) for v in other_gens ] + # construct the filtration dictionary + def join_indices(self_indices, other_indices): + self_indices = tuple(self_indices) + other_indices = tuple(i + len(self_gens) for i in other_indices) + return self_indices + other_indices + filtration = dict() + self_indices = set() + other_indices = set() + for deg in reversed(uniq(self_filt.keys() + other_filt.keys())): + self_indices.update(self_filt.get(deg, [])) + other_indices.update(other_filt.get(deg, [])) + gens = join_indices(self_indices, other_indices) + filtration[deg] = gens + return FilteredVectorSpace(generators, filtration, base_ring=base_ring) + + __add__ = direct_sum + + def tensor_product(self, other): + r""" + Return the graded tensor product. + + INPUT: + + - ``other`` -- a filtered vector space. + + OUTPUT: + + The graded tensor product, that is, the tensor product of a + generator of degree `d_1` with a generator in degree `d_2` has + degree `d_1 + d_2`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(1, 1) + sage: F2 = FilteredVectorSpace(1, 2) + sage: F1.tensor_product(F2) + QQ^1 >= 0 + sage: F1 * F2 + QQ^1 >= 0 + + sage: F1.min_degree() + 1 + sage: F2.min_degree() + 2 + sage: (F1*F2).min_degree() + 3 + + A suitable base ring is chosen if they do not match:: + + sage: v = [(1,0), (0,1)] + sage: F1 = FilteredVectorSpace(v, {0:[0], 1:[1]}, base_ring=QQ) + sage: F2 = FilteredVectorSpace(v, {0:[0], 1:[1]}, base_ring=RDF) + sage: F1 * F2 + RDF^4 >= RDF^3 >= RDF^1 >= 0 + """ + V = self + W = other + from sage.structure.element import get_coercion_model + base_ring = get_coercion_model().common_parent(V.base_ring(), W.base_ring()) + from sage.modules.tensor_operations import VectorCollection, TensorOperation + V_generators, V_indices = V.presentation() + W_generators, W_indices = W.presentation() + V_coll = VectorCollection(V_generators, base_ring, V.dimension()) + W_coll = VectorCollection(W_generators, base_ring, W.dimension()) + T = TensorOperation([V_coll, W_coll], 'product') + + filtration = dict() + for V_deg in V.support(): + for W_deg in W.support(): + deg = V_deg + W_deg + indices = filtration.get(deg, set()) + for i in V_indices[V_deg]: + for j in W_indices[W_deg]: + i_tensor_j = T.index_map(i, j) + indices.add(i_tensor_j) + filtration[deg] = indices + return FilteredVectorSpace(T.vectors(), filtration, base_ring=base_ring) + + __mul__ = tensor_product + + def _power_operation(self, n, operation): + """ + Return tensor power operation. + + INPUT: + + - ``n`` -- integer. the number of factors of ``self``. + + - ``operation`` -- string. See + :class:`~sage.modules.tensor_operations.TensorOperation` for + details. + + EXAMPLES:: + + sage: F = FilteredVectorSpace(1, 1) + FilteredVectorSpace(1, 2); F + QQ^2 >= QQ^1 >= 0 + sage: F._power_operation(2, 'symmetric') + QQ^3 >= QQ^2 >= QQ^1 >= 0 + sage: F._power_operation(2, 'antisymmetric') + QQ^1 >= 0 + """ + from sage.modules.tensor_operations import VectorCollection, TensorOperation + generators, indices = self.presentation() + V = VectorCollection(generators, self.base_ring(), self.dimension()) + T = TensorOperation([V] * n, operation) + + iters = [self.support()] * n + filtration = dict() + from sage.categories.cartesian_product import cartesian_product + for degrees in cartesian_product(iters): + deg = sum(degrees) + filt_deg = filtration.get(deg, set()) + for i in cartesian_product([indices.get(d) for d in degrees]): + pow_i = T.index_map(*i) + if pow_i is not None: + filt_deg.add(pow_i) + filtration[deg] = filt_deg + return FilteredVectorSpace(T.vectors(), filtration, base_ring=self.base_ring()) + + + def exterior_power(self, n): + """ + Return the `n`-th graded exterior power. + + INPUT: + + - ``n`` -- integer. Exterior product of how many copies of + ``self``. + + OUTPUT: + + The graded exterior product, that is, the wedge product of a + generator of degree `d_1` with a generator in degree `d_2` has + degree `d_1 + d_2`. + + EXAMPLES:: + + sage: F = FilteredVectorSpace(1, 1) + FilteredVectorSpace(1, 2); F + QQ^2 >= QQ^1 >= 0 + sage: F.exterior_power(1) + QQ^2 >= QQ^1 >= 0 + sage: F.exterior_power(2) + QQ^1 >= 0 + sage: F.exterior_power(3) + 0 + sage: F.wedge(2) + QQ^1 >= 0 + """ + return self._power_operation(n, 'antisymmetric') + + wedge = exterior_power + + def symmetric_power(self, n): + """ + Return the `n`-th graded symmetric power. + + INPUT: + + - ``n`` -- integer. Symmetric product of how many copies of + ``self``. + + OUTPUT: + + The graded symmetric product, that is, the symmetrization of a + generator of degree `d_1` with a generator in degree `d_2` has + degree `d_1 + d_2`. + + EXAMPLES:: + + sage: F = FilteredVectorSpace(1, 1) + FilteredVectorSpace(1, 2); F + QQ^2 >= QQ^1 >= 0 + sage: F.symmetric_power(2) + QQ^3 >= QQ^2 >= QQ^1 >= 0 + """ + return self._power_operation(n, 'symmetric') + + def dual(self): + """ + Return the dual filtered vector space. + + OUTPUT: + + The graded dual, that is, the dual of a degree-`d` subspace is + a set of linear constraints in degree `-d+1`. That is, the + dual generators live in degree `-d`. + + EXAMPLES:: + + sage: gens = identity_matrix(3).rows() + sage: F = FilteredVectorSpace(gens, {0:[0,1,2], 2:[0]}); F + QQ^3 >= QQ^1 >= QQ^1 >= 0 + sage: F.support() + (0, 2) + + sage: F.dual() + QQ^3 >= QQ^2 >= QQ^2 >= 0 + sage: F.dual().support() + (-2, 0) + """ + filtration = dict() + prev_deg = minus_infinity + for deg, V in self._filt[1:]: + filtration[-prev_deg] = V.complement().echelonized_basis() + prev_deg = deg + return FilteredVectorSpace(filtration, base_ring=self.base_ring()) + + def shift(self, deg): + """ + Return a filtered vector space with degrees shifted by a constant. + + EXAMPLES:: + + sage: gens = identity_matrix(3).rows() + sage: F = FilteredVectorSpace(gens, {0:[0,1,2], 2:[0]}); F + QQ^3 >= QQ^1 >= QQ^1 >= 0 + sage: F.support() + (0, 2) + sage: F.shift(-5).support() + (-5, -3) + """ + generators, filtration = self.presentation() + shifted = dict() + for d, indices in filtration.iteritems(): + shifted[d + deg] = indices + return FilteredVectorSpace(generators, shifted, base_ring=self.base_ring()) + + def random_deformation(self, epsilon=None): + """ + Return a random deformation + + INPUT: + + - ``epsilon`` -- a number in the base ring. + + OUTPUT: + + A new filtered vector space where the generators of the + subspaces are moved by ``epsilon`` times a random vector. + + EXAMPLES:: + + sage: gens = identity_matrix(3).rows() + sage: F = FilteredVectorSpace(gens, {0:[0,1,2], 2:[0]}); F + QQ^3 >= QQ^1 >= QQ^1 >= 0 + sage: F.get_degree(2) + Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [1 0 0] + sage: G = F.random_deformation(1/50); G + QQ^3 >= QQ^1 >= QQ^1 >= 0 + sage: G.get_degree(2) + Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [ 1 -15/304 0] + """ + from sage.modules.free_module_element import random_vector + R = self.base_ring() + if epsilon is None: + epsilon = R.one() + filtration = dict() + for deg, filt in self._filt[1:]: + generators = [v + epsilon * random_vector(R, self.rank()) + for v in filt.echelonized_basis()] + filtration[deg] = generators + return FilteredVectorSpace(filtration, base_ring=R, check=True) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 970c3da35ba..c32633334fc 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -167,8 +167,7 @@ import sage.misc.latex as latex from sage.modules.module import Module -import sage.rings.finite_rings.constructor as finite_field -import sage.rings.integral_domain as integral_domain +import sage.rings.finite_rings.finite_field_constructor as finite_field import sage.rings.ring as ring import sage.rings.integer_ring import sage.rings.rational_field @@ -378,7 +377,7 @@ def create_object(self, version, key): and base_ring.is_maximal() and base_ring.class_number() == 1: return FreeModule_ambient_pid(base_ring, rank, sparse=sparse) - elif isinstance(base_ring, integral_domain.IntegralDomain) or base_ring.is_integral_domain(): + elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): return FreeModule_ambient_domain(base_ring, rank, sparse=sparse) else: @@ -3038,8 +3037,7 @@ def _Hom_(self, Y, category): Returns a homspace whose morphisms have this vector space as domain. This is called by the general methods such as - :meth:`sage.structure.parent.Parent.Hom` and - :meth:`sage.structure.parent_base.ParentWithBase.Hom`. + :meth:`sage.structure.parent.Parent.Hom`. INPUT: diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index a57b3a09df7..d5dc47bc883 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -457,7 +457,7 @@ def BKZ(self, *args, **kwds): .. NOTE:: - If ``block_size == L.rank()`` where ``L`` is this latice, then + If ``block_size == L.rank()`` where ``L`` is this lattice, then this function performs Hermite-Korkine-Zolotareff (HKZ) reduction. """ basis = self.reduced_basis diff --git a/src/sage/modules/free_quadratic_module.py b/src/sage/modules/free_quadratic_module.py index fb856373160..ccc829f5149 100644 --- a/src/sage/modules/free_quadratic_module.py +++ b/src/sage/modules/free_quadratic_module.py @@ -58,30 +58,20 @@ """ #***************************************************************************** +# Copyright (C) 2008 David Kohel # -# Copyright (C) 2008 David Kohel -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# http://www.gnu.org/licenses/ -# -# All rights granted to distribute under the GPL, version 2, -# or (at your option) any later version of the license. -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** -# Python imports import weakref -# Sage imports - - import sage.matrix.matrix_space - import sage.misc.latex as latex - import sage.rings.ring as ring -import sage.rings.integral_domain as integral_domain import sage.rings.integer from sage.categories.principal_ideal_domains import PrincipalIdealDomains import free_module @@ -180,7 +170,7 @@ def FreeQuadraticModule( M = FreeQuadraticModule_ambient_pid( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) - elif isinstance(base_ring, integral_domain.IntegralDomain) or base_ring.is_integral_domain(): + elif isinstance(base_ring, ring.IntegralDomain) or base_ring.is_integral_domain(): M = FreeQuadraticModule_ambient_domain( base_ring, rank, sparse=sparse, inner_product_matrix=inner_product_matrix) else: diff --git a/src/sage/modules/multi_filtered_vector_space.py b/src/sage/modules/multi_filtered_vector_space.py new file mode 100644 index 00000000000..650a7ce721f --- /dev/null +++ b/src/sage/modules/multi_filtered_vector_space.py @@ -0,0 +1,712 @@ +r""" +Multiple `\ZZ`-Graded Filtrations of a Single Vector Space + +See :mod:`filtered_vector_space` for simply graded vector spaces. This +module implements the analog but for a collection of filtrations of +the same vector space. + +The basic syntax to use it is a dictionary whose keys are some +arbitrary indexing set and values are +:func:`~sage.modules.filtered_vector_space.FilteredVectorSpace` :: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace({0:[(1,0)], 2:[(2,3)]}) + sage: V = MultiFilteredVectorSpace({'first':F1, 'second':F2}) + sage: V + Filtrations + first: QQ^2 >= QQ^2 >= 0 >= 0 + second: QQ^2 >= QQ^1 >= QQ^1 >= 0 + + sage: V.index_set() # random output + {'second', 'first'} + sage: sorted(V.index_set()) + ['first', 'second'] + + sage: V.get_filtration('first') + QQ^2 >= 0 + sage: V.get_degree('second', 1) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 3/2] +""" + +#***************************************************************************** +# Copyright (C) 2013 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.all import QQ, ZZ, RDF, RR, Integer +from sage.rings.infinity import InfinityRing, infinity, minus_infinity +from sage.categories.fields import Fields +from sage.modules.free_module import FreeModule_ambient_field, VectorSpace +from sage.matrix.constructor import vector, matrix, block_matrix, zero_matrix, identity_matrix +from sage.misc.all import uniq, cached_method, prod +from sage.modules.filtered_vector_space import FilteredVectorSpace + + +def MultiFilteredVectorSpace(arg, base_ring=None, check=True): + """ + Contstruct a multi-filtered vector space. + + INPUT: + + - ``arg`` -- either a non-empty dictionary of filtrations or an + integer. The latter is interpreted as the vector space + dimension, and the indexing set of the filtrations is empty. + + - ``base_ring`` -- a field (optional, default ``'None'``). The + base field of the vector space. Must be a field. If not + specified, the base field is derived from the filtrations. + + - ``check`` -- boolean (optional; default: ``True``). Whether + to perform consistency checks. + + EXAMPLES:: + + sage: MultiFilteredVectorSpace(3, QQ) + Unfiltered QQ^3 + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}); V + Filtrations + 1: QQ^2 >= 0 >= 0 >= 0 + 2: QQ^2 >= QQ^2 >= QQ^2 >= 0 + """ + if arg in ZZ: + dim = ZZ(arg) + filtration = {} + if base_ring is None: + base_ring = QQ + else: + filtration = dict(arg) + F = arg.values()[0] # the first filtration + dim = F.dimension() + if base_ring is None: + base_ring = F.base_ring() + for deg in filtration.keys(): + filt = filtration[deg] + if filt.base_ring() != base_ring: + filt = filt.change_ring(base_ring) + filtration[deg] = filt + return MultiFilteredVectorSpace_class(base_ring, dim, filtration) + + +class MultiFilteredVectorSpace_class(FreeModule_ambient_field): + + def __init__(self, base_ring, dim, filtrations, check=True): + """ + Python constructor. + + .. warning:: + + Use :func:`MultiFilteredVectorSpace` to construct + multi-filtered vector spaces. + + INPUT: + + - ``base_ring`` -- a ring. the base ring. + + - ``dim`` -- integer. The dimension of the ambient vector space. + + - ``filtrations`` -- a dictionary whose values are + filtrations. + + - ``check`` -- boolean (optional). Whether to perform + additional consistency checks. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}); V + Filtrations + 1: QQ^2 >= 0 >= 0 >= 0 + 2: QQ^2 >= QQ^2 >= QQ^2 >= 0 + """ + if check: + assert isinstance(dim, Integer) + assert base_ring in Fields() + assert all(base_ring == f.base_ring() for f in filtrations.values()) + assert all(dim == f.dimension() for f in filtrations.values()) + super(MultiFilteredVectorSpace_class, self).__init__(base_ring, dim) + self._filt = dict(filtrations) + + @cached_method + def index_set(self): + """ + Return the allowed indices for the different filtrations. + + OUTPUT: + + Set. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.index_set() + {1, 2} + """ + from sage.sets.set import Set + return Set(self._filt.keys()) + + def change_ring(self, base_ring): + """ + Return the same multi-filtration over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring. The new base ring. + + OUTPUT: + + This method returns a new multi-filtered vector space whose + subspaces are defined by the same generators but over a + different base ring. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 0) + sage: W = FilteredVectorSpace(2, 2) + sage: F = MultiFilteredVectorSpace({'a':V, 'b':W}); F + Filtrations + a: QQ^2 >= 0 >= 0 >= 0 + b: QQ^2 >= QQ^2 >= QQ^2 >= 0 + sage: F.change_ring(RDF) + Filtrations + a: RDF^2 >= 0 >= 0 >= 0 + b: RDF^2 >= RDF^2 >= RDF^2 >= 0 + + sage: MultiFilteredVectorSpace(3, base_ring=QQ).change_ring(RR) + Unfiltered RR^3 + """ + if len(self._filt) == 0: + return MultiFilteredVectorSpace(self.dimension(), base_ring=base_ring) + filtrations = dict() + for key, F in self._filt.iteritems(): + filtrations[key] = F.change_ring(base_ring) + return MultiFilteredVectorSpace(filtrations, base_ring=base_ring) + + def ambient_vector_space(self): + """ + Return the ambient (unfiltered) vector space. + + OUTPUT: + + A vector space. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 0) + sage: W = FilteredVectorSpace(2, 2) + sage: F = MultiFilteredVectorSpace({'a':V, 'b':W}) + sage: F.ambient_vector_space() + Vector space of dimension 2 over Rational Field + """ + return VectorSpace(self.base_ring(), self.dimension()) + + @cached_method + def is_constant(self): + """ + Return whether the multi-filtration is constant. + + OUTPUT: + + Boolean. Whether the each filtration is constant, see + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.is_constant`. + + EXAMPLES:: + + sage: V = FilteredVectorSpace(2, 0) + sage: W = FilteredVectorSpace(2, 2) + sage: F = MultiFilteredVectorSpace({'a':V, 'b':W}); F + Filtrations + a: QQ^2 >= 0 >= 0 >= 0 + b: QQ^2 >= QQ^2 >= QQ^2 >= 0 + sage: F.is_constant() + False + """ + return all(F.is_constant() for F in self._filt.values()) + + def is_exhaustive(self): + """ + Return whether the multi-filtration is exhaustive. + + A filtration $\{F_d\}$ in an ambient vector space $V$ is + exhaustive if $\cup F_d = V$. See also :meth:`is_separating`. + + OUTPUT: + + Boolean. Whether each filtration is constant, see + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.is_exhaustive`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.is_exhaustive() + True + """ + return all(F.is_exhaustive() for F in self._filt.values()) + + def is_separating(self): + """ + Return whether the multi-filtration is separating. + + A filtration $\{F_d\}$ in an ambient vector space $V$ is + exhaustive if $\cap F_d = 0$. See also :meth:`is_exhaustive`. + + OUTPUT: + + Boolean. Whether each filtration is separating, see + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.is_separating`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.is_separating() + True + """ + return all(F.is_separating() for F in self._filt.values()) + + @cached_method + def support(self): + """ + Return the degrees in which there are non-trivial generators. + + OUTPUT: + + A tuple of integers (and plus infinity) in ascending + order. The last entry is plus infinity if and only if the + flitration is not separating (see :meth:`is_separating`). + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.support() + (1, 3) + """ + support = set() + for F in self._filt.values(): + support.update(F.support()) + return tuple(sorted(support)) + + @cached_method + def min_degree(self): + r""" + Return the lowest degree of the filtration. + + OUTPUT: + + Integer or plus infinity. The largest degree `d` of the + (descending) filtrations such that, for each individual + filtration, the filtered vector space `F_d` still equal to + `F_{-\infty}`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.min_degree() + 1 + """ + if len(self._filt) == 0: + return infinity + return min(F.min_degree() for F in self._filt.values()) + + @cached_method + def max_degree(self): + r""" + Return the highest degree of the filtration. + + OUTPUT: + + Integer or minus infinity. The smallest degree of the + filtrations such that the filtrations are constant to the + right. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.max_degree() + 4 + """ + if len(self._filt) == 0: + return minus_infinity + return max(F.max_degree() for F in self._filt.values()) + + def get_filtration(self, key): + """ + Return the filtration indexed by ``key``. + + OUTPUT: + + A filtered vectior space. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.get_filtration(2) + QQ^2 >= 0 + """ + return self._filt[key] + + def get_degree(self, key, deg): + r""" + Return one filtered vector space. + + INPUT: + + - ``key`` -- an element of the :meth:`index_set`. Specifies + which filtration. + + - ``d`` -- Integer. The desired degree of the filtration. + + OUTPUT: + + The vector space of degree ``deg`` in the filtration indexed + by ``key`` as subspace of the ambient space. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(2, 3) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.get_degree(2, 0) + Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + """ + return self._filt[key].get_degree(deg) + + def graded(self, key, deg): + r""" + Return the associated graded vector space. + + INPUT: + + - ``key`` -- an element of the :meth:`index_set`. Specifies + which filtration. + + - ``d`` -- Integer. The desired degree of the filtration. + + OUTPUT: + + The quotient `G_d = F_d / F_{d+1}` of the filtration `F` + corresponding to ``key``. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V.graded(2, 3) + Vector space quotient V/W of dimension 1 over Rational Field where + V: Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + W: Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + """ + return self.get_degree(key, deg).quotient(self.get_degree(key, deg + 1)) + + def _repr_(self): + r""" + Return as string representation of ``self``. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: rays = [(1,0), (1,1), (1,2), (-1,-1)] + sage: F1 = FilteredVectorSpace(rays, {0:[1, 2], 2:[3]}) + sage: F2 = FilteredVectorSpace(rays, {0:[1, 2], oo:[3]}) + sage: F3 = FilteredVectorSpace(rays, {oo:[3]}) + sage: F4 = FilteredVectorSpace(rays, {0:[3]}) + sage: MultiFilteredVectorSpace({'a':F1, 'b':F2, 'c': F3, 'd': F4}) + Filtrations + a: QQ^2 >= QQ^1 >= QQ^1 >= 0 + b: QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 + c: QQ^1 >= QQ^1 >= QQ^1 >= QQ^1 + d: QQ^1 >= 0 >= 0 >= 0 + + sage: MultiFilteredVectorSpace(123, base_ring=RR) + Unfiltered RR^123 + """ + if len(self._filt) == 0: + F = FilteredVectorSpace(self.dimension(), base_ring=self.base_ring()) + return 'Unfiltered ' + repr(F) + rows = [] + support = self.support() + min_deg, max_deg = self.min_degree(), self.max_degree() + for key in sorted(self.index_set()): + F = self.get_filtration(key) + r = [str(key)] + F._repr_degrees(min_deg, max_deg-1) + rows.append(r) + from sage.misc.table import table + t = table(rows) + w = t._widths() + lines = ['Filtrations'] + for r in rows: + s = ' ' + s += r[0].rjust(w[0]) + ': ' + s += ' >= '.join(r[i].center(w[i]) for i in range(1, len(w))) + lines.append(s) + return '\n'.join(lines) + + def __cmp__(self, other): + """ + Compare two multi-filtered vector spaces. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({1:F1, 2:F2}) + sage: V == MultiFilteredVectorSpace({2:F2, 1:F1}) + True + sage: V == MultiFilteredVectorSpace({'a':F1, 'b':F2}) + False + """ + return cmp(self._filt, other._filt) + + def direct_sum(self, other): + """ + Return the direct sum. + + INPUT: + + - ``other`` -- a multi-filtered vector space with the same + :meth:`index_set`. + + OUTPUT: + + The direct sum as a multi-filtered vector space. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.direct_sum`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: G1 = FilteredVectorSpace(1, 1) + sage: G2 = FilteredVectorSpace(1, 3) + sage: W = MultiFilteredVectorSpace({'a':G1, 'b':G2}) + sage: V.direct_sum(W) + Filtrations + a: QQ^3 >= QQ^3 >= 0 >= 0 >= 0 + b: QQ^3 >= QQ^2 >= QQ^2 >= QQ^2 >= 0 + sage: V + W # syntactic sugar + Filtrations + a: QQ^3 >= QQ^3 >= 0 >= 0 >= 0 + b: QQ^3 >= QQ^2 >= QQ^2 >= QQ^2 >= 0 + """ + if not self.index_set() == other.index_set(): + raise ValueError('the index sets of the two summands must be the same') + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key] + other._filt[key] + return MultiFilteredVectorSpace(filtrations) + + __add__ = direct_sum + + def tensor_product(self, other): + r""" + Return the graded tensor product. + + INPUT: + + - ``other`` -- a multi-filtered vector space with the same + :meth:`index_set`. + + OUTPUT: + + The tensor product of ``self`` and ``other`` as a + multi-filtered vector space. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.tensor_product`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: G1 = FilteredVectorSpace(1, 1) + sage: G2 = FilteredVectorSpace(1, 3) + sage: W = MultiFilteredVectorSpace({'a':G1, 'b':G2}) + sage: V.tensor_product(W) + Filtrations + a: QQ^2 >= 0 >= 0 >= 0 >= 0 >= 0 + b: QQ^2 >= QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 >= 0 + sage: V * W # syntactic sugar + Filtrations + a: QQ^2 >= 0 >= 0 >= 0 >= 0 >= 0 + b: QQ^2 >= QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 >= 0 + """ + if not self.index_set() == other.index_set(): + raise ValueError('the index sets of the two summands must be the same') + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key] * other._filt[key] + return MultiFilteredVectorSpace(filtrations) + + __mul__ = tensor_product + + def exterior_power(self, n): + """ + Return the `n`-th graded exterior power. + + INPUT: + + - ``n`` -- integer. Exterior product of how many copies of + ``self``. + + OUTPUT: + + The exterior power as a multi-filtered vector space. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.exterior_power`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: V.exterior_power(2) + Filtrations + a: QQ^1 >= 0 >= 0 + b: QQ^1 >= QQ^1 >= 0 + """ + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key].exterior_power(n) + return MultiFilteredVectorSpace(filtrations) + + wedge = exterior_power + + def symmetric_power(self, n): + """ + Return the `n`-th graded symmetric power. + + INPUT: + + - ``n`` -- integer. Symmetric product of how many copies of + ``self``. + + OUTPUT: + + The symmetric power as a multi-filtered vector space. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.symmetric_power`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: V.symmetric_power(2) + Filtrations + a: QQ^3 >= QQ^3 >= QQ^3 >= 0 >= 0 >= 0 >= 0 >= 0 + b: QQ^3 >= QQ^2 >= QQ^2 >= QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 >= 0 + """ + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key].symmetric_power(n) + return MultiFilteredVectorSpace(filtrations) + + def dual(self): + """ + Return the dual. + + OUTPUT: + + The dual as a multi-filtered vector space. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.dual`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: V.dual() + Filtrations + a: QQ^2 >= QQ^2 >= QQ^2 >= 0 >= 0 + b: QQ^2 >= QQ^1 >= QQ^1 >= QQ^1 >= 0 + """ + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key].dual() + return MultiFilteredVectorSpace(filtrations) + + def shift(self, deg): + """ + Return a filtered vector space with degrees shifted by a constant. + + OUTPUT: + + The shift of ``self``. See + :meth:`~sage.modules.filtered_vector_space.FilteredVectorSpace_class.shift`. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: V.support() + (0, 1, 3) + sage: V.shift(-5).support() + (-5, -4, -2) + """ + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key].shift(deg) + return MultiFilteredVectorSpace(filtrations) + + def random_deformation(self, epsilon=None): + """ + Return a random deformation + + INPUT: + + - ``epsilon`` -- a number in the base ring. + + OUTPUT: + + A new multi-filtered vector space where the generating vectors + of subspaces are moved by ``epsilon`` times a random vector. + + EXAMPLES:: + + sage: F1 = FilteredVectorSpace(2, 1) + sage: F2 = FilteredVectorSpace(1, 3) + FilteredVectorSpace(1,0) + sage: V = MultiFilteredVectorSpace({'a':F1, 'b':F2}) + sage: V.get_degree('b',1) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.random_deformation(1/100).get_degree('b',1) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 8/1197] + """ + filtrations = dict() + for key in self.index_set(): + filtrations[key] = self._filt[key].random_deformation(epsilon) + return MultiFilteredVectorSpace(filtrations) diff --git a/src/sage/modules/tensor_operations.py b/src/sage/modules/tensor_operations.py new file mode 100644 index 00000000000..7e6c5e73ede --- /dev/null +++ b/src/sage/modules/tensor_operations.py @@ -0,0 +1,582 @@ +""" +Helper Classes to implement Tensor Operations + +.. warning:: + + This module is not meant to be used directly. It just provides + functionality for other classes to implement tensor operations. + +The :class:`VectorCollection` constructs the basis of tensor products +(and symmetric/exterior powers) in terms of a chosen collection of +vectors that generate the vector space(s). + +EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection, TensorOperation + sage: V = VectorCollection([(1,0), (-1, 0), (1,2)], QQ, 2) + sage: W = VectorCollection([(1,1), (1,-1), (-1, 1)], QQ, 2) + sage: VW = TensorOperation([V, W], operation='product') + +Here is the tensor product of two vectors:: + + sage: V.vectors()[0] + (1, 0) + sage: W.vectors()[1] + (1, -1) + +In a convenient choice of basis, the tensor product is +$(a,b)\otimes(c,d)=(ac,ad,bc,bd)$. In this example, it is one of the +vectors of the vector collection ``VW`` :: + + sage: VW.index_map(0, 1) + 1 + sage: VW.vectors()[VW.index_map(0, 1)] + (1, -1, 0, 0) + + sage: rows = [] + sage: for i, j in cartesian_product((range(3), range(3))): + ....: v = V.vectors()[i] + ....: w = W.vectors()[j] + ....: i_tensor_j = VW.index_map(i, j) + ....: vw = VW.vectors()[i_tensor_j] + ....: rows.append([i, v, j, w, i_tensor_j, vw]) + sage: table(rows) + 0 (1, 0) 0 (1, 1) 0 (1, 1, 0, 0) + 0 (1, 0) 1 (1, -1) 1 (1, -1, 0, 0) + 0 (1, 0) 2 (-1, 1) 2 (-1, 1, 0, 0) + 1 (-1, 0) 0 (1, 1) 3 (-1, -1, 0, 0) + 1 (-1, 0) 1 (1, -1) 2 (-1, 1, 0, 0) + 1 (-1, 0) 2 (-1, 1) 1 (1, -1, 0, 0) + 2 (1, 2) 0 (1, 1) 4 (1, 1, 2, 2) + 2 (1, 2) 1 (1, -1) 5 (1, -1, 2, -2) + 2 (1, 2) 2 (-1, 1) 6 (-1, 1, -2, 2) +""" + +#***************************************************************************** +# Copyright (C) 2013 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.sage_object import SageObject +from sage.modules.free_module import FreeModule_ambient_field, VectorSpace +from sage.misc.all import cached_method, prod +from sage.matrix.constructor import vector, matrix +from sage.rings.all import ZZ + + + +def symmetrized_coordinate_sums(dim, n): + """ + Return formal symmetrized sum of multi-indices + + INPUT: + + - ``dim`` -- integer. The dimension (range of each index). + + - ``n`` -- integer. The total number of indices. + + OUTPUT: + + A symmetrized formal sum of multi-indices (tuples of integers) + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import symmetrized_coordinate_sums + sage: symmetrized_coordinate_sums(2, 2) + ((0, 1) + (1, 0), (0, 0), (1, 1)) + """ + from sage.structure.formal_sum import FormalSum + coordinates = [range(dim) for i in range(n)] + table = dict() + from sage.categories.cartesian_product import cartesian_product + for i in cartesian_product(coordinates): + sort_i = tuple(sorted(i)) + x = table.get(sort_i, []) + x.append([+1, tuple(i)]) + table[sort_i] = x + return tuple(FormalSum(x) for x in table.values()) + + +def antisymmetrized_coordinate_sums(dim, n): + """ + Return formal anti-symmetrized sum of multi-indices + + INPUT: + + - ``dim`` -- integer. The dimension (range of each index). + + - ``n`` -- integer. The total number of indices. + + OUTPUT: + + An anti-symmetrized formal sum of multi-indices (tuples of integers) + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import antisymmetrized_coordinate_sums + sage: antisymmetrized_coordinate_sums(3, 2) + ((0, 1) - (1, 0), (0, 2) - (2, 0), (1, 2) - (2, 1)) + """ + from sage.structure.formal_sum import FormalSum + table = [] + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + S_d = SymmetricGroup(n) + from sage.combinat.combination import Combinations + for i in Combinations(range(dim), n): + i = tuple(i) + x = [] + for g in S_d: + x.append([g.sign(), g(i)]) + x = FormalSum(x) + table.append(x) + return tuple(table) + + +class VectorCollection(FreeModule_ambient_field): + """ + An ordered collection of generators of a vector space. + + This is like a list of vectors, but with extra argument checking. + + .. warning:: + + This class is only used as a base class for filtered vector + spaces. You should not use it yourself. + + INPUT: + + - ``dim`` -- integer. The dimension of the ambient vector space. + + - ``base_ring`` -- a field. The base field of the ambient vector space. + + - ``rays`` -- any list/iterable of things than can be converted + into vectors of the ambient vector space. These will be used to + span the subspaces of the filtration. Must span the ambient + vector space. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection + sage: R = VectorCollection([(1,0), (0,1), (1,2)], QQ, 2); R + Vector space of dimension 2 over Rational Field + + TESTS:: + + sage: R.vectors() + ((1, 0), (0, 1), (1, 2)) + sage: r = R._vectors[0] + sage: type(r) + + sage: r.parent() is R + True + sage: r.is_immutable() + True + """ + def __init__(self, vector_collection, base_ring, dim): + """ + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection + sage: VectorCollection([(1,0), (4,1), (1,2)], QQ, 2) + Vector space of dimension 2 over Rational Field + """ + super(VectorCollection, self).__init__(base_ring, dim) + self._n_vectors = len(vector_collection) + self._vectors = tuple(self(r) for r in vector_collection) + for r in self._vectors: + r.set_immutable() + if matrix(base_ring, self._vectors).rank() != self.degree(): + raise ValueError('the vectors must span the ambient vector space') + self._all_indices = tuple(map(ZZ, range(0, self._n_vectors))) + + def vectors(self): + """ + Return the collection of vectors + + OUTPUT: + + A tuple of vectors. The vectors that were specified in the + constructor, in the same order. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection + sage: V = VectorCollection([(1,0), (0,1), (1,2)], QQ, 2) + sage: V.vectors() + ((1, 0), (0, 1), (1, 2)) + """ + return self._vectors + + def n_vectors(self): + """ + Return the number of vectors + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection + sage: V = VectorCollection([(1,0), (0,1), (1,2)], QQ, 2) + sage: V.n_vectors() + 3 + """ + return len(self._vectors) + + +class TensorOperation(VectorCollection): + """ + Auxiliary class to compute the tensor product of two + :class:`VectorCollection` objects. + + .. warning:: + + This class is only used as a base class for filtered vector + spaces. You should not use it yourself. + + INPUT: + + - ``vector_collections`` -- a nonempty list/tuple/iterable of + :class:`VectorCollection` objects. + + - ``operation`` -- string. The tensor operation. Currently allowed + values are ``product``, ``symmetric``, and ``antisymmetric``. + + .. todo:: + + More general tensor operations (specified by Young tableaux) + should be implemented. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: S = VectorCollection([(1,), (-1,)], QQ, 1) + sage: R_tensor_S = TensorOperation([R, S]) + sage: R_tensor_S.index_map(0, 0) + 0 + sage: matrix(ZZ, 3, 2, lambda i,j: R_tensor_S.index_map(i, j)) + [0 1] + [2 3] + [3 2] + sage: R_tensor_S.vectors() + ((1, 0), (-1, 0), (1, 2), (-1, -2)) + """ + def __init__(self, vector_collections, operation='product'): + """ + EXAMPLES:: + + sage: from sage.modules.tensor_operations import VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (5,2), (-1,-2)], QQ, 2) + sage: S = VectorCollection([(1,), (-1,)], QQ, 1) + sage: TensorOperation([S, R]) + Vector space of dimension 2 over Rational Field + """ + assert all(isinstance(V, VectorCollection) for V in vector_collections) + self._base_ring = base_ring = vector_collections[0].base_ring() + assert all(V.base_ring() is base_ring for V in vector_collections) + self._V = tuple(vector_collections) + self._vectors = [] + self._index_map = dict() + if operation == 'product': + self._init_product() + elif operation == 'symmetric': + assert all(V is self._V[0] for V in self._V) + self._init_symmetric() + elif operation == 'antisymmetric': + assert all(V is self._V[0] for V in self._V) + self._init_antisymmetric() + else: + raise ValueError('invalid operation') + vectors = self._vectors + dim = 0 if len(vectors) == 0 else len(vectors[0]) + del self._vectors + del self._base_ring + super(TensorOperation, self).__init__(vectors, base_ring, dim) + + def _init_product_vectors(self, i): + r""" + Helper to build up ``self._vectors`` incrementally during the + constructor. + + INPUT: + + - `i` -- list/tuple of integers. Multi-index of length equal + to the number of constituent vector collections. The $j$-th + entry $i[j]$ indexes a ray in the $j$-th vector + collection. Hence, $i$ specifies one element in each vector + collection. + + OUTPUT: + + This method mutates the :class:`TensorOperation` instance. In + particular, the tensor product of the vectors of the vector + collection is computed, and added to the elements of the + tensor operation if it has not been encountered before. + + The index of this tensor product vector is returned as an + integer. + + .. NOTE:: + + In a convenient choice of coordinates the tensor product + of, say, two vectors $(a,b)$ and $(c,d)$, is $(ac, ad, bc, + bd)$. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: S = VectorCollection([(1,), (-1,)], QQ, 1) + sage: R_tensor_S = TensorOperation([R,S]) + sage: R_tensor_S.index_map(1, 1) + 3 + sage: R_tensor_S.index_map(2, 0) + 3 + sage: R_tensor_S.vectors() # indirect doctest + ((1, 0), (-1, 0), (1, 2), (-1, -2)) + """ + # Pick out the i[j]-th vector + rays = [list(self._V[j].vectors()[k]) for j, k in enumerate(i)] + v = [] + # Note: convert to list, as cartesian_product of vectors is unrelated + from sage.categories.cartesian_product import cartesian_product + for r in cartesian_product(map(list, rays)): + v.append(prod(r)) # build up the tensor product + v = tuple(v) + # Use index of pre-existing tensor product vector if there is one + try: + result = self._vectors.index(v) + except ValueError: + self._vectors.append(v) + result = len(self._vectors) - 1 + return result + + def _init_power_operation_vectors(self, i, linear_combinations): + """ + Helper to build up ``self._vectors`` incrementally during the constructor. + + INPUT: + + - `i` -- list/tuple of integers. Specifies one element + (vector) in each vector collection as in + :meth:`_init_product_vector`. + + - ``linear_combination`` -- formal linear combination of + vector indices in the vectors specified by $i$. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: Sym2_R = TensorOperation([R,R], operation='symmetric') + sage: Sym2_R.vectors() # indirect doctest + ((0, 1, 0), (2, 1, 0), (-2, -1, 0), (4, 1, 4), (-4, -1, -4)) + sage: Alt2_R = TensorOperation([R, R], operation='antisymmetric') + sage: Alt2_R.vectors() # indirect doctest + ((2), (-2)) + """ + rays = [self._V[j].vectors()[k] for j, k in enumerate(i)] + v = [] + for coordinate_linear_combination in linear_combinations: + v_entry = self._base_ring.zero() + for coeff, index in coordinate_linear_combination: + v_entry += coeff * prod(rays[j][k] for j, k in enumerate(index)) + v.append(v_entry) + v = tuple(v) + if all(vi == 0 for vi in v): + return None + try: + result = self._vectors.index(v) + except ValueError: + self._vectors.append(v) + result = len(self._vectors) - 1 + return result + + def _init_product(self): + """ + Initialization for the tensor product + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: S = VectorCollection([(1,), (-1,)], QQ, 1) + sage: R_tensor_S = TensorOperation([R,S], operation='product') + sage: sorted(R_tensor_S._index_map.iteritems()) # indirect doctest + [((0, 0), 0), ((0, 1), 1), ((1, 0), 2), ((1, 1), 3), ((2, 0), 3), ((2, 1), 2)] + """ + V_list_indices = [range(V.n_vectors()) for V in self._V] + from sage.categories.cartesian_product import cartesian_product + for i in cartesian_product(V_list_indices): + self._index_map[tuple(i)] = self._init_product_vectors(i) + self._symmetrize_indices = False + + def _init_symmetric(self): + """ + Initialization for the symmetric product. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: Sym2_R = TensorOperation([R,R], operation='symmetric') # indirect doctest + sage: sorted(Sym2_R._index_map.iteritems()) + [((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((1, 1), 3), ((1, 2), 4), ((2, 2), 3)] + """ + V_list_indices = [range(V.n_vectors()) for V in self._V] + Sym = symmetrized_coordinate_sums(self._V[0].dimension(), len(self._V)) + from sage.categories.cartesian_product import cartesian_product + N = len(V_list_indices) + for i in cartesian_product(V_list_indices): + if any(i[j - 1] > i[j] for j in range(1, N)): + continue + self._index_map[tuple(i)] = self._init_power_operation_vectors(i, Sym) + self._symmetrize_indices = True + + def _init_antisymmetric(self): + """ + Initialization for the antisymmetric product + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: Alt2_R = TensorOperation([R, R], operation='antisymmetric') # indirect doctest + sage: sorted(Alt2_R._index_map.iteritems()) + [((0, 1), 0), ((0, 2), 1)] + """ + n = len(self._V) + dim = self._V[0].degree() + Alt = antisymmetrized_coordinate_sums(dim, n) + from sage.combinat.combination import Combinations + for i in Combinations(range(self._V[0].n_vectors()), n): + ray = self._init_power_operation_vectors(i, Alt) + if ray is not None: + self._index_map[tuple(i)] = ray + self._symmetrize_indices = True + + def index_map(self, *i): + """ + Return the result of the tensor operation. + + INPUT: + + - ``*i`` -- list of integers. The indices (in the + corresponding factor of the tensor operation) of the domain + vector. + + OUTPUT: + + The index (in :meth:`vectors`) of the image of the tensor + product/operation acting on the domain vectors indexed by `i`. + + ``None`` is returned if the tensor operation maps the + generators to zero (usually because of antisymmetry). + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (1,2), (-1,-2)], QQ, 2) + sage: Sym3_R = TensorOperation([R]*3, 'symmetric') + + The symmetric product of the first vector ``(1,0)``, the + second vector ``(1,2)``, and the third vector ``(-1,-2)`` + equals the vector with index number 4 (that is, the fifth) in + the symmetric product vector collection:: + + sage: Sym3_R.index_map(0, 1, 2) + 4 + + In suitable coordinates, this is the vector:: + + sage: Sym3_R.vectors()[4] + (-4, 0, -1, -4) + + The product is symmetric:: + + sage: Sym3_R.index_map(2, 0, 1) + 4 + sage: Sym3_R.index_map(2, 1, 0) + 4 + + As another example, here is the rank-2 determinant:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (0,1), (-2,-3)], QQ, 2) + sage: detR = TensorOperation([R]*2, 'antisymmetric') + sage: detR.index_map(1, 0) + 0 + sage: detR.index_map(0, 1) + 0 + + TESTS:: + + sage: sorted(detR._index_map.iteritems()) + [((0, 1), 0), ((0, 2), 1), ((1, 2), 2)] + sage: detR.vectors() + ((1), (-3), (2)) + """ + if len(i) == 1 and isinstance(i[0], (list, tuple)): + i = tuple(i[0]) + if self._symmetrize_indices: + i = tuple(sorted(i)) + try: + return self._index_map[i] + except KeyError: + return None + + def preimage(self): + """ + A choice of pre-image multi-indices. + + OUTPUT: + + A list of multi-indices (tuples of integers) whose image is + the entire image under the :meth:`index_map`. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (0,1), (-2,-3)], QQ, 2) + sage: detR = TensorOperation([R]*2, 'antisymmetric') + sage: sorted(detR.preimage()) + [(0, 1), (0, 2), (1, 2)] + sage: sorted(detR.codomain()) + [0, 1, 2] + """ + return self._index_map.keys() + + def codomain(self): + """ + The codomain of the index map. + + OUTPUT: + + A list of integers. The image of :meth:`index_map`. + + EXAMPLES:: + + sage: from sage.modules.tensor_operations import \ + ....: VectorCollection, TensorOperation + sage: R = VectorCollection([(1,0), (0,1), (-2,-3)], QQ, 2) + sage: detR = TensorOperation([R]*2, 'antisymmetric') + sage: sorted(detR.preimage()) + [(0, 1), (0, 2), (1, 2)] + sage: sorted(detR.codomain()) + [0, 1, 2] + """ + return self._index_map.values() diff --git a/src/sage/modules/with_basis/representation.py b/src/sage/modules/with_basis/representation.py new file mode 100644 index 00000000000..5f24d38054c --- /dev/null +++ b/src/sage/modules/with_basis/representation.py @@ -0,0 +1,554 @@ +""" +Representations Of A Semigroup + +AUTHORS: + +- Travis Scrimshaw (2015-11-21): Initial version +""" + +#################################################################################### +# Copyright (C) 2015 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# The full text of the GPL is available at: +# http://www.gnu.org/licenses/ +#################################################################################### + +from sage.misc.abstract_method import abstract_method +from sage.structure.element import Element +from sage.combinat.free_module import CombinatorialFreeModule +from sage.categories.groups import Groups +from sage.categories.semigroups import Semigroups +from sage.categories.modules import Modules +from sage.algebras.group_algebra import GroupAlgebra + +class Representation_abstract(CombinatorialFreeModule): + """ + Abstract base class for representations of semigroups. + + INPUT: + + - ``semigroup`` -- a semigroup + - ``base_ring`` -- a commutative ring + """ + def __init__(self, semigroup, base_ring, *args, **opts): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = FreeGroup(3) + sage: T = G.trivial_representation() + sage: TestSuite(T).run() + """ + self._semigroup = semigroup + self._semigroup_algebra = semigroup.algebra(base_ring) + CombinatorialFreeModule.__init__(self, base_ring, *args, **opts) + + def semigroup(self): + """ + Return the semigroup whose representation ``self`` is. + + EXAMPLES:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, g.sign()) + sage: R = Representation(G, M, on_basis) + sage: R.semigroup() + Symmetric group of order 4! as a permutation group + """ + return self._semigroup + + def semigroup_algebra(self): + """ + Return the semigroup algebra whose representation ``self`` is. + + EXAMPLES:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, g.sign()) + sage: R = Representation(G, M, on_basis) + sage: R.semigroup_algebra() + Symmetric group algebra of order 4 over Rational Field + """ + return self._semigroup_algebra + + @abstract_method + def side(self): + """ + Return whether ``self`` is a left, right, or two-sided representation. + + OUTPUT: + + - the string ``"left"``, ``"right"``, or ``"twosided"`` + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation() + sage: R.side() + 'left' + """ + +class Representation(Representation_abstract): + """ + Representation of a semigroup. + + INPUT: + + - ``semigroup`` -- a semigroup + - ``module`` -- a module with a basis + - ``on_basis`` -- function which takes as input ``g``, ``m``, where + ``g`` is an element of the semigroup and ``m`` is an element of the + indexing set for the basis, and returns the result of ``g`` acting + on ``m`` + - ``side`` -- (default: ``"left"``) whether this is a + ``"left"`` or ``"right"`` representation + + EXAMPLES: + + We construct the sign representation of a symmetric group:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, g.sign()) + sage: R = Representation(G, M, on_basis) + sage: x = R.an_element(); x + 2*B['v'] + sage: c,s = G.gens() + sage: c,s + ((1,2,3,4), (1,2)) + sage: c * x + -2*B['v'] + sage: s * x + -2*B['v'] + sage: c * s * x + 2*B['v'] + sage: (c * s) * x + 2*B['v'] + + This extends naturally to the corresponding group algebra:: + + sage: A = G.algebra(QQ) + sage: s,c = A.algebra_generators() + sage: c,s + ((1,2,3,4), (1,2)) + sage: c * x + -2*B['v'] + sage: s * x + -2*B['v'] + sage: c * s * x + 2*B['v'] + sage: (c * s) * x + 2*B['v'] + sage: (c + s) * x + -4*B['v'] + + REFERENCES: + + - :wikipedia:`Group_representation` + """ + def __init__(self, semigroup, module, on_basis, side="left"): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = SymmetricGroup(4) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, g.sign()) + sage: R = Representation(G, M, on_basis) + sage: R._test_representation() + """ + if side not in ["left", "right"]: + raise ValueError('side must be "left" or "right"') + self._left_repr = (side == "left") + self._on_basis = on_basis + self._module = module + indices = module.basis().keys() + cat = Modules(module.base_ring()).WithBasis() + if 'FiniteDimensional' in module.category().axioms(): + cat = cat.FiniteDimensional() + Representation_abstract.__init__(self, semigroup, module.base_ring(), indices, + category=cat, **module.print_options()) + + def _test_representation(self, **options): + """ + Check (on some elements) that ``self`` is a representation of the + given semigroup. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation() + sage: R._test_representation() + + sage: G = CoxeterGroup(['A',4,1], base_ring=ZZ) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, (-1)**g.length()) + sage: R = Representation(G, M, on_basis, side="right") + sage: R._test_representation(max_runs=500) + """ + from sage.functions.other import sqrt + tester = self._tester(**options) + S = tester.some_elements() + L = [] + max_len = int(sqrt(tester._max_runs)) + 1 + for i,x in enumerate(self._semigroup): + L.append(x) + if i >= max_len: + break + for x in L: + for y in L: + for elt in S: + if self._left_repr: + tester.assertEqual(x*(y*elt), (x*y)*elt) + else: + tester.assertEqual((elt*y)*x, elt*(y*x)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: P = Permutations(4) + sage: M = CombinatorialFreeModule(QQ, ['v']) + sage: from sage.modules.with_basis.representation import Representation + sage: on_basis = lambda g,m: M.term(m, g.sign()) + sage: Representation(P, M, on_basis) + Representation of Standard permutations of 4 indexed by {'v'} + over Rational Field + """ + return "Representation of {} indexed by {} over {}".format( + self._semigroup, self.basis().keys(), self.base_ring()) + + def _repr_term(self, b): + """ + Return a string representation of a basis index ``b`` of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: R = SGA.regular_representation() + sage: all(R._repr_term(b) == SGA._repr_term(b) for b in SGA.basis().keys()) + True + """ + return self._module._repr_term(b) + + def _latex_term(self, b): + """ + Return a LaTeX representation of a basis index ``b`` of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: R = SGA.regular_representation() + sage: all(R._latex_term(b) == SGA._latex_term(b) for b in SGA.basis().keys()) + True + """ + return self._module._latex_term(b) + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: A = G.algebra(ZZ) + sage: R = A.regular_representation() + sage: x = A.an_element(); x + B[()] + 4*B[(1,2,3,4)] + 2*B[(1,4)(2,3)] + sage: R(x) + B[()] + 4*B[(1,2,3,4)] + 2*B[(1,4)(2,3)] + """ + if isinstance(x, Element) and x.parent() is self._module: + return self._from_dict(x.monomial_coefficients(copy=False), remove_zeros=False) + return super(Representation, self)._element_constructor_(x) + + def side(self): + """ + Return whether ``self`` is a left or a right representation. + + OUTPUT: + + - the string ``"left"`` or ``"right"`` + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation() + sage: R.side() + 'left' + sage: S = G.regular_representation(side="right") + sage: S.side() + 'right' + """ + return "left" if self._left_repr else "right" + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: G = groups.misc.WeylGroup(['B',2], prefix='s') + sage: R = G.regular_representation() + sage: s1,s2 = G.gens() + sage: x = R.an_element(); x + 2*B[s2*s1*s2] + B[s1*s2] + 3*B[s2] + B[1] + sage: 2 * x + 4*B[s2*s1*s2] + 2*B[s1*s2] + 6*B[s2] + 2*B[1] + sage: s1 * x + 2*B[s2*s1*s2*s1] + 3*B[s1*s2] + B[s1] + B[s2] + sage: s2 * x + B[s2*s1*s2] + 2*B[s1*s2] + B[s2] + 3*B[1] + + sage: G = groups.misc.WeylGroup(['B',2], prefix='s') + sage: R = G.regular_representation(side="right") + sage: s1,s2 = G.gens() + sage: x = R.an_element(); x + 2*B[s2*s1*s2] + B[s1*s2] + 3*B[s2] + B[1] + sage: x * s1 + 2*B[s2*s1*s2*s1] + B[s1*s2*s1] + 3*B[s2*s1] + B[s1] + sage: x * s2 + 2*B[s2*s1] + B[s1] + B[s2] + 3*B[1] + + sage: G = groups.misc.WeylGroup(['B',2], prefix='s') + sage: R = G.regular_representation() + sage: R.base_ring() + Integer Ring + sage: A = G.algebra(ZZ) + sage: s1,s2 = A.algebra_generators() + sage: x = R.an_element(); x + 2*B[s2*s1*s2] + B[s1*s2] + 3*B[s2] + B[1] + sage: s1 * x + 2*B[s2*s1*s2*s1] + 3*B[s1*s2] + B[s1] + B[s2] + sage: s2 * x + B[s2*s1*s2] + 2*B[s1*s2] + B[s2] + 3*B[1] + sage: (2*s1 - s2) * x + 4*B[s2*s1*s2*s1] - B[s2*s1*s2] + 4*B[s1*s2] + + 2*B[s1] + B[s2] - 3*B[1] + sage: (3*s1 + s2) * R.zero() + 0 + + sage: A = G.algebra(QQ) + sage: s1,s2 = A.algebra_generators() + sage: a = 1/2 * s1 + sage: a * x + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for '*': + 'Group algebra of Weyl Group of type ['B', 2] ... over Rational Field' + and 'Left Regular Representation of Weyl Group of type ['B', 2] ... over Integer Ring' + """ + if isinstance(scalar, Element): + P = self.parent() + if scalar.parent() is P._semigroup: + if not self: + return self + if self_on_left == P._left_repr: + scalar = ~scalar + return P.linear_combination(((P._on_basis(scalar, m), c) + for m,c in self), not self_on_left) + + if scalar.parent() is P._semigroup_algebra: + if not self: + return self + ret = P.zero() + for ms,cs in scalar: + if self_on_left == P._left_repr: + ms = ~ms + ret += P.linear_combination(((P._on_basis(ms, m), cs*c) + for m,c in self), not self_on_left) + return ret + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + + _rmul_ = _lmul_ = _acted_upon_ + +class RegularRepresentation(Representation): + r""" + The regular representation of a semigroup. + + The left regular representation of a semigroup `S` over a commutative + ring `R` is the semigroup ring `R[S]` equipped with the left + `S`-action `x b_y = b_{xy}`, where `(b_z)_{z \in S}` is the natural + basis of `R[S]` and `x,y \in S`. + + INPUT: + + - ``semigroup`` -- a semigroup + - ``base_ring`` -- the base ring for the representation + - ``side`` -- (default: ``"left"``) whether this is a + ``"left"`` or ``"right"`` representation + + REFERENCES: + + - :wikipedia:`Regular_representation` + """ + def __init__(self, semigroup, base_ring, side="left"): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation() + sage: TestSuite(R).run() + """ + if side == "left": + on_basis = self._left_on_basis + else: + on_basis = self._right_on_basis + module = semigroup.algebra(base_ring) + Representation.__init__(self, semigroup, module, on_basis, side) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.regular_representation() + Left Regular Representation of Dihedral group of order 8 + as a permutation group over Integer Ring + sage: G.regular_representation(side="right") + Right Regular Representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + if self._left_repr: + base = "Left Regular Representation" + else: + base = "Right Regular Representation" + return base + " of {} over {}".format(self._semigroup, self.base_ring()) + + def _left_on_basis(self, g, m): + """ + Return the left action of ``g`` on ``m``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation() + sage: R._test_representation() # indirect doctest + """ + return self.monomial(g*m) + + def _right_on_basis(self, g, m): + """ + Return the right action of ``g`` on ``m``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.regular_representation(side="right") + sage: R._test_representation() # indirect doctest + """ + return self.monomial(m*g) + +class TrivialRepresentation(Representation_abstract): + """ + The trivial representation of a semigroup. + + The trivial representation of a semigroup `S` over a commutative ring + `R` is the `1`-dimensional `R`-module on which every element of `S` + acts by the identity. + + This is simultaneously a left and right representation. + + INPUT: + + - ``semigroup`` -- a semigroup + - ``base_ring`` -- the base ring for the representation + + REFERENCES: + + - :wikipedia:`Trivial_representation` + """ + def __init__(self, semigroup, base_ring): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.PGL(2, 3) + sage: V = G.trivial_representation() + sage: TestSuite(V).run() + """ + cat = Modules(base_ring).WithBasis().FiniteDimensional() + Representation_abstract.__init__(self, semigroup, base_ring, ['v'], category=cat) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: G.trivial_representation() + Trivial representation of Dihedral group of order 8 + as a permutation group over Integer Ring + """ + return "Trivial representation of {} over {}".format(self._semigroup, + self.base_ring()) + + def side(self): + """ + Return that ``self`` is a two-sided representation. + + OUTPUT: + + - the string ``"twosided"`` + + EXAMPLES:: + + sage: G = groups.permutation.Dihedral(4) + sage: R = G.trivial_representation() + sage: R.side() + 'twosided' + """ + return "twosided" + + class Element(CombinatorialFreeModule.Element): + def _acted_upon_(self, scalar, self_on_left=False): + """ + Return the action of ``scalar`` on ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: V = SGA.trivial_representation() + sage: x = V.an_element() + sage: 2 * x + 4*B['v'] + sage: all(x * b == x for b in SGA.basis()) + True + sage: all(b * x == x for b in SGA.basis()) + True + sage: z = V.zero() + sage: all(b * z == z for b in SGA.basis()) + True + """ + if isinstance(scalar, Element): + if scalar.parent() is self.parent()._semigroup: + return self + if scalar.parent() is self.parent()._semigroup_algebra: + if not self: + return self + d = self.monomial_coefficients(copy=True) + d['v'] *= sum(scalar.coefficients()) + return self.parent()._from_dict(d) + return CombinatorialFreeModule.Element._acted_upon_(self, scalar, self_on_left) + + _rmul_ = _lmul_ = _acted_upon_ + diff --git a/src/sage/plot/plot3d/all.py b/src/sage/plot/plot3d/all.py index 45f33b918ee..3f5349e7855 100644 --- a/src/sage/plot/plot3d/all.py +++ b/src/sage/plot/plot3d/all.py @@ -17,7 +17,7 @@ from shapes import arrow3d #from shapes import Box, ColorCube, Cone, Cylinder, LineSegment, Arrow, Sphere, Torus, Text as Text3D -#from parametric_surface import ParametricSurface, MobiusStrip +#from parametric_surface import ParametricSurface, MoebiusStrip #from plot3d import plot3d, axes as axes3d diff --git a/src/sage/plot/plot3d/parametric_plot3d.py b/src/sage/plot/plot3d/parametric_plot3d.py index 1aafa5c317b..e93a296d325 100644 --- a/src/sage/plot/plot3d/parametric_plot3d.py +++ b/src/sage/plot/plot3d/parametric_plot3d.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- """ Parametric Plots """ @@ -137,12 +138,12 @@ def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", ....: (u, 0, 2*pi), (v, -pi, pi), color=(cf, colormaps.PiYG), plot_points=[60,60]) sage: P.show(viewer='tachyon') - Another example, a colored Mobius band:: + Another example, a colored Möbius band:: sage: cm = colormaps.ocean sage: def c(x,y): return sin(x*y)**2 - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: MobiusStrip(5,1,plot_points=200, color=(c,cm)) + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: MoebiusStrip(5,1,plot_points=200, color=(c,cm)) Graphics3d Object Yet another colored example:: @@ -430,7 +431,7 @@ def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", sage: parametric_plot3d(f, (u,0,6*pi), (v,0,2*pi), plot_points=[40,40], texture=(0,0.5,0)) Graphics3d Object - A Mobius strip:: + A Möbius strip:: sage: u,v = var("u,v") sage: parametric_plot3d([cos(u)*(1+v*cos(u/2)), sin(u)*(1+v*cos(u/2)), 0.2*v*sin(u/2)], (u,0, 4*pi+0.5), (v,0, 0.3),plot_points=[50,50]) @@ -468,7 +469,7 @@ def parametric_plot3d(f, urange, vrange=None, plot_points="automatic", sage: plot3d(u^2-v^2, (u, -1, 1), (v, -1, 1), plot_points=[50,50]) Graphics3d Object - A weird looking surface - like a Mobius band but also an O:: + A weird looking surface - like a Möbius band but also an O:: sage: u, v = var('u,v') sage: parametric_plot3d([sin(u)*cos(u)*log(u^2)*sin(v), (u^2)^(1/6)*(cos(u)^2)^(1/4)*cos(v), sin(v)], (u, 0.001, 1), (v, -pi, pi+0.2), plot_points=[50,50]) diff --git a/src/sage/plot/plot3d/parametric_surface.pyx b/src/sage/plot/plot3d/parametric_surface.pyx index 126a1d375d4..66b4552c1c5 100644 --- a/src/sage/plot/plot3d/parametric_surface.pyx +++ b/src/sage/plot/plot3d/parametric_surface.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Parametric Surface @@ -16,11 +17,11 @@ AUTHORS: EXAMPLES:: - sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MobiusStrip + sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MoebiusStrip sage: def f(x,y): return x+y, sin(x)*sin(y), x*y sage: P = ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1))) sage: show(P) - sage: S = MobiusStrip(1,.2) + sage: S = MoebiusStrip(1,.2) sage: S.is_enclosed() False sage: S.show() @@ -201,8 +202,8 @@ cdef class ParametricSurface(IndexFaceSet): TEST:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: type(MobiusStrip(3,3).default_render_params()) + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: type(MoebiusStrip(3,3).default_render_params()) """ return RenderParams(ds=.075, crease_threshold=.35) @@ -304,8 +305,8 @@ cdef class ParametricSurface(IndexFaceSet): sage: Sphere(1).is_enclosed() True - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: MobiusStrip(1,0.2).is_enclosed() + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: MoebiusStrip(1,0.2).is_enclosed() False """ if self.fcount == 0: @@ -336,8 +337,8 @@ cdef class ParametricSurface(IndexFaceSet): Surfaces which are not enclosed, though, should raise an exception:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: M = MobiusStrip(3,1) + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: M = MoebiusStrip(3,1) sage: M.is_enclosed() False sage: M.dual() @@ -365,8 +366,8 @@ cdef class ParametricSurface(IndexFaceSet): EXAMPLES:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: M = MobiusStrip(7,3,2) + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: M = MoebiusStrip(7,3,2) sage: M.bounding_box() ((-10.0, -7.53907349250478..., -2.9940801852848145), (10.0, 7.53907349250478..., 2.9940801852848145)) """ @@ -389,11 +390,11 @@ cdef class ParametricSurface(IndexFaceSet): TESTS:: - sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MobiusStrip + sage: from sage.plot.plot3d.parametric_surface import ParametricSurface, MoebiusStrip sage: def f(x,y): return x+y, sin(x)*sin(y), x*y # indirect doctests sage: P = ParametricSurface(f, (srange(0,10,0.1), srange(-5,5.0,0.1))) # indirect doctests sage: P.show() # indirect doctests - sage: S = MobiusStrip(1,.2) # indirect doctests + sage: S = MoebiusStrip(1,.2) # indirect doctests sage: S.show() # indirect doctests """ cdef double u, v @@ -673,9 +674,9 @@ cdef class ParametricSurface(IndexFaceSet): raise NotImplementedError -class MobiusStrip(ParametricSurface): +class MoebiusStrip(ParametricSurface): """ - Base class for the :class:`MobiusStrip` graphics type. This sets the the + Base class for the :class:`MoebiusStrip` graphics type. This sets the the basic parameters of the object. INPUT: @@ -687,30 +688,30 @@ class MobiusStrip(ParametricSurface): width of the object. - ``twists`` - (default: 1) An integer, giving the number of twists in the - object (where one twist is the 'traditional' Mobius strip). + object (where one twist is the 'traditional' Möbius strip). EXAMPLES:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: M = MobiusStrip(3,3) + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: M = MoebiusStrip(3,3) sage: M.show() """ def __init__(self, r, width, twists=1, **kwds): """ - Create the graphics primitive MobiusStrip. See the docstring of + Create the graphics primitive MoebiusStrip. See the docstring of this class for full documentation. EXAMPLES: :: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: M = MobiusStrip(3,3); M # Same width and radius, roughly + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: M = MoebiusStrip(3,3); M # Same width and radius, roughly Graphics3d Object - sage: N = MobiusStrip(7,3,2); N # two twists, lots of open area in the middle + sage: N = MoebiusStrip(7,3,2); N # two twists, lots of open area in the middle Graphics3d Object - sage: O = MobiusStrip(5,1,plot_points=200,color='red'); O # keywords get passed to plot3d + sage: O = MoebiusStrip(5,1,plot_points=200,color='red'); O # keywords get passed to plot3d Graphics3d Object """ @@ -721,7 +722,7 @@ class MobiusStrip(ParametricSurface): def get_grid(self, ds): """ - Return appropriate `u` and `v` ranges for this MobiusStrip instance. + Return appropriate `u` and `v` ranges for this MoebiusStrip instance. This is intended for internal use in creating an actual plot. @@ -729,12 +730,12 @@ class MobiusStrip(ParametricSurface): - ``ds`` -- A number, typically coming from a RenderParams object, which helps determine the increment for the `v` range for the - MobiusStrip object. + MoebiusStrip object. EXAMPLE:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: N = MobiusStrip(7,3,2) # two twists + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: N = MoebiusStrip(7,3,2) # two twists sage: N.get_grid(N.default_render_params().ds) ([-1, 1], [0.0, 0.12566370614359174, 0.25132741228718347, 0.37699111843077515, ...]) """ @@ -748,12 +749,12 @@ class MobiusStrip(ParametricSurface): def eval(self, u, v): """ Return a tuple for `x,y,z` coordinates for the given ``u`` and ``v`` - for this MobiusStrip instance. + for this MoebiusStrip instance. EXAMPLE:: - sage: from sage.plot.plot3d.parametric_surface import MobiusStrip - sage: N = MobiusStrip(7,3,2) # two twists + sage: from sage.plot.plot3d.parametric_surface import MoebiusStrip + sage: N = MoebiusStrip(7,3,2) # two twists sage: N.eval(-1,0) (4.0, 0.0, -0.0) """ diff --git a/src/sage/plot/plot3d/shapes2.py b/src/sage/plot/plot3d/shapes2.py index eb4d3edab00..ff6af59d5ec 100644 --- a/src/sage/plot/plot3d/shapes2.py +++ b/src/sage/plot/plot3d/shapes2.py @@ -799,16 +799,22 @@ class Line(PrimitiveObject): INPUT: - - ``points`` -- list of points to pass through + - ``points`` -- list of points to pass through - - ``thickness`` -- diameter of the line + - ``thickness`` -- (optional, default 5) diameter of the line - - ``corner_cutoff`` -- threshold for smoothing (see - the corners() method) this is the minimum cosine between adjacent - segments to smooth + - ``corner_cutoff`` -- (optional, default 0.5) threshold for + smoothing (see :meth:`corners`). - - ``arrow_head`` -- if True make this curve into an - arrow + - ``arrow_head`` -- (optional, default ``False``) if ``True`` make + this curve into an arrow + + The parameter ``corner_cutoff`` is a bound for the cosine of the + angle made by two successive segments. This angle is close to `0` + (and the cosine close to 1) if the two successive segments are + almost aligned and close to `\pi` (and the cosine close to -1) if + the path has a strong peak. If the cosine is smaller than the + bound (which means a sharper peak) then no smoothing is done. EXAMPLES:: @@ -820,8 +826,19 @@ class Line(PrimitiveObject): sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=0) Graphics3d Object + + Make sure that the ``corner_cutoff`` keyword works (:trac:`3859`):: + + sage: N = 11 + sage: c = 0.4 + sage: sum([Line([(i,1,0), (i,0,0), (i,cos(2*pi*i/N), sin(2*pi*i/N))], + ....: corner_cutoff=c, + ....: color='red' if -cos(2*pi*i/N)<=c else 'blue') + ....: for i in range(N+1)]) + Graphics3d Object """ - def __init__(self, points, thickness=5, corner_cutoff=.5, arrow_head=False, **kwds): + def __init__(self, points, thickness=5, corner_cutoff=0.5, + arrow_head=False, **kwds): """ Create the graphics primitive :class:`Line` in 3-D. @@ -855,7 +872,8 @@ def bounding_box(self): sage: from sage.plot.plot3d.shapes2 import Line sage: L = Line([(i,i^2-1,-2*ln(i)) for i in [10,20,30]]) sage: L.bounding_box() - ((10.0, 99.0, -6.802394763324311), (30.0, 899.0, -4.605170185988092)) + ((10.0, 99.0, -6.802394763324311), + (30.0, 899.0, -4.605170185988092)) """ try: return self.__bounding_box @@ -863,7 +881,6 @@ def bounding_box(self): self.__bounding_box = point_list_bounding_box(self.points) return self.__bounding_box - def tachyon_repr(self, render_params): """ Return representation of the line suitable for plotting @@ -927,7 +944,7 @@ def jmol_repr(self, render_params): 'draw line_1 diameter 1 curve {1.0 0.0 0.0}' """ T = render_params.transform - corners = self.corners(max_len=255) # hardcoded limit in jmol + corners = self.corners(max_len=255) # hardcoded limit in jmol last_corner = corners[-1] corners = set(corners) cmds = [] @@ -954,8 +971,23 @@ def corners(self, corner_cutoff=None, max_len=None): INPUT: - Maximum cosine of angle between adjacent line segments before - adding a corner + - ``corner_cutoff`` -- (optional, default ``None``) If the + cosine of the angle between adjacent line segments is smaller than + this bound, then there will be a sharp corner in the path. + Otherwise, the path is smoothed. If ``None``, + then the default value 0.5 is used. + + - ``max_len`` -- (optional, default ``None``) Maximum number + of points allowed in a single path. If this is set, this + creates corners at smooth points in order to break the path + into smaller pieces. + + The parameter ``corner_cutoff`` is a bound for the cosine of the + angle made by two successive segments. This angle is close to `0` + (and the cosine close to 1) if the two successive segments are + almost aligned and close to `\pi` (and the cosine close to -1) if + the path has a strong peak. If the cosine is smaller than the + bound (which means a sharper peak) then there must be a corner. OUTPUT: @@ -964,39 +996,40 @@ def corners(self, corner_cutoff=None, max_len=None): EXAMPLES: - Every point:: + No corners, always smooth:: sage: from sage.plot.plot3d.shapes2 import Line - sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=1).corners() - [(0, 0, 0), (1, 0, 0), (2, 1, 0)] + sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=-1).corners() + [(0, 0, 0)] - Greater than 90 degrees:: + Smooth if the angle is greater than 90 degrees:: sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=0).corners() [(0, 0, 0), (2, 1, 0)] - No corners:: + Every point (corners everywhere):: - sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=-1).corners() - (0, 0, 0) - - An intermediate value:: - - sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=.5).corners() - [(0, 0, 0), (2, 1, 0)] + sage: Line([(0,0,0),(1,0,0),(2,1,0),(0,1,0)], corner_cutoff=1).corners() + [(0, 0, 0), (1, 0, 0), (2, 1, 0)] """ if corner_cutoff is None: corner_cutoff = self.corner_cutoff + if corner_cutoff >= 1: - if max_len: - self.points[:-1][::max_len-1] - else: - return self.points[:-1] + # corners everywhere + return self.points[:-1] + elif corner_cutoff <= -1: - return self.points[0] + # no corners + if not(max_len is None): + # forced by the maximal number of consecutive smooth points + return self.points[:-1][::max_len - 1] + else: + return [self.points[0]] + else: - if not max_len: - max_len = len(self.points)+1 + if max_len is None: + max_len = len(self.points) + 1 count = 2 # ... -- prev -- cur -- next -- ... cur = self.points[0] @@ -1009,7 +1042,7 @@ def corners(self, corner_cutoff=None, max_len=None): def dot(x0_y0_z0, x1_y1_z1): (x0, y0, z0) = x0_y0_z0 (x1, y1, z1) = x1_y1_z1 - return x0*x1 + y0*y1 + z0*z1 + return x0 * x1 + y0 * y1 + z0 * z1 for next in self.points[2:]: if next == cur: @@ -1018,8 +1051,10 @@ def dot(x0_y0_z0, x1_y1_z1): count = 1 continue next_dir = [next[i] - cur[i] for i in range(3)] - cos_angle = dot(prev_dir, next_dir) / math.sqrt(dot(prev_dir, prev_dir) * dot(next_dir, next_dir)) - if cos_angle <= corner_cutoff or count > max_len-1: + cos_angle = (dot(prev_dir, next_dir) / + math.sqrt(dot(prev_dir, prev_dir) * + dot(next_dir, next_dir))) + if cos_angle <= corner_cutoff or count > max_len - 1: corners.append(cur) count = 1 cur, prev_dir = next, next_dir diff --git a/src/sage/quadratic_forms/genera/genus.py b/src/sage/quadratic_forms/genera/genus.py index 19cb036af87..b9ef30a0fab 100644 --- a/src/sage/quadratic_forms/genera/genus.py +++ b/src/sage/quadratic_forms/genera/genus.py @@ -15,7 +15,7 @@ from sage.rings.integer_ring import IntegerRing from sage.rings.rational_field import RationalField from sage.rings.integer import Integer -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField def Genus(A): diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 94acaba2ffa..0bb110288bd 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -32,9 +32,8 @@ from sage.rings.ring import Ring from sage.misc.functional import denominator, is_even, is_field from sage.arith.all import GCD, LCM -from sage.rings.principal_ideal_domain import is_PrincipalIdealDomain from sage.rings.all import Ideal -from sage.rings.ring import is_Ring +from sage.rings.ring import is_Ring, PrincipalIdealDomain from sage.matrix.matrix import is_Matrix from sage.structure.sage_object import SageObject from sage.structure.element import is_Vector @@ -1436,7 +1435,7 @@ def level(self): except AttributeError: ## Check that the base ring is a PID - if not is_PrincipalIdealDomain(self.base_ring()): + if not isinstance(self.base_ring(), PrincipalIdealDomain): raise TypeError("Oops! The level (as a number) is only defined over a Principal Ideal Domain. Try using level_ideal().") diff --git a/src/sage/repl/interface_magic.py b/src/sage/repl/interface_magic.py new file mode 100644 index 00000000000..a2b99883152 --- /dev/null +++ b/src/sage/repl/interface_magic.py @@ -0,0 +1,299 @@ +""" +Magics for each of the Sage interfaces + +This module defines magic functions for interpreters. As an example, +consider the GAP interpreter which can evaluate a gap command given as +a string:: + + sage: gap('SymmetricGroup(4)') # not tested + SymmetricGroup( [ 1 .. 4 ] ) + +Magics are syntactic sugar to avoid writing the Python string. They +are either called as line magics:: + + sage: %gap SymmetricGroup(4) # not tested + +or as cell magics, that is, spanning multiple lines:: + + sage: %%gap # not tested + ....: G := SymmetricGroup(4); + ....: Display(G); + +Note that the cell magic needs semicolons, this is required by the GAP +language to separate multiple commands. +""" +# Note: no magics in doctests, hence # not tested + + +#***************************************************************************** +# Copyright (C) 2016 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from sage.repl.rich_output.display_manager import get_display_manager + + +LINE_DOCSTRING = """ +Interact with {name} + +The line magic %{name} sends a single line to the {name} interface. + +INPUT: + +Single {name} command + +OUTPUT: + +The result of the evaluated {name} command as an interface wrapper +object. + +EXAMPLES:: + + sage: %{name} 1 + 2 + 3 # not tested +""" + + +CELL_DOCSTRING = """ +Interact with {name} + +The cell magic %%{name} sends multiple lines to the {name} interface. + +INPUT: + +Multiple lines of valid {name}-commands + +OUTPUT: + +The result of the evaluated {name}-commands is printed. + +EXAMPLES:: + + sage: %%{name} # not tested + ....: 1 + 2 + 3; + ....: some_{name}_command(); +""" + + + +class InterfaceMagic(object): + + @classmethod + def all_iter(cls): + """ + Iterate over the available interfaces + + EXAMPLES:: + + sage: from sage.repl.interface_magic import InterfaceMagic + sage: next(InterfaceMagic.all_iter()) + + """ + import sage.interfaces.all + for name, obj in sage.interfaces.all.__dict__.items(): + if isinstance(obj, sage.interfaces.interface.Interface): + yield cls(name, obj) + + @classmethod + def register_all(cls, shell=None): + """ + Register all available interfaces + + EXAMPLES:: + + sage: class MockShell(): + ....: magics = set() + ....: def register_magic_function(self, fn, magic_name, magic_kind): + ....: self.magics.add(magic_name) + ....: print(magic_name, magic_kind) + sage: from sage.repl.interface_magic import InterfaceMagic + sage: InterfaceMagic.register_all(MockShell()) # random output + ('gp', 'line') + ('gp', 'cell') + ('mwrank', 'line') + ('mwrank', 'cell') + ... + ('maxima', 'line') + ('maxima', 'cell') + sage: 'gap' in MockShell.magics + True + sage: 'maxima' in MockShell.magics + True + """ + if shell is None: + shell = get_ipython() + for interface in cls.all_iter(): + shell.register_magic_function( + interface.line_magic_factory(), + magic_name=interface._name, + magic_kind='line' + ) + shell.register_magic_function( + interface.cell_magic_factory(), + magic_name=interface._name, + magic_kind='cell' + ) + + @classmethod + def find(cls, name): + """ + Find a particular magic by name + + This method is for doctesting purposes only. + + INPUT: + + - ``name`` -- string. The name of the interface magic to + search for. + + OUTPUT: + + The corresponding :class:`InterfaceMagic` instance. + + EXAMPLES:: + + sage: from sage.repl.interface_magic import InterfaceMagic + sage: InterfaceMagic.find('gap') + + """ + for magic in cls.all_iter(): + if magic._name == name: + return magic + + def __init__(self, name, interface): + """ + Interface Magic + + This class is a wrapper around interface objects to provide + them with magics. + + INPUT: + + - ``name`` -- string. The interface name + + - ``interface`` -- :class:`sage.interfaces.expect.Expect`. The + interface to wrap. + + EXAMPLES:: + + sage: from sage.repl.interface_magic import InterfaceMagic + sage: InterfaceMagic.find('gap') + + """ + self._name = name + self._interface = interface + + def line_magic_factory(self): + """ + Factory for line magic + + OUTPUT: + + A function suitable to be used as line magic. + + EXAMPLES:: + + sage: from sage.repl.interface_magic import InterfaceMagic + sage: line_magic = InterfaceMagic.find('gap').line_magic_factory() + sage: output = line_magic('1+1') + sage: output + 2 + sage: type(output) + + + This is how the built line magic is used in practice:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%gap 1+1') + 2 + sage: shell.run_cell('%gap?') + Docstring: + Interact with gap + + The line magic %gap sends a single line to the gap interface. + ... + """ + terminal = get_display_manager().is_in_terminal() + def line_magic(line): + if line: + return self._interface(line) + else: + if terminal: + self._interface.interact() + else: + raise SyntaxError('{0} command required'.format(self._name)) + line_magic.__doc__ = LINE_DOCSTRING.format(name=self._name) + return line_magic + + def cell_magic_factory(self): + r""" + Factory for cell magic + + OUTPUT: + + A function suitable to be used as cell magic. + + EXAMPLES:: + + sage: from sage.repl.interface_magic import InterfaceMagic + sage: cell_magic = InterfaceMagic.find('gap').cell_magic_factory() + sage: output = cell_magic('', '1+1;') + 2 + sage: output is None + True + sage: cell_magic('foo', '1+1;') + Traceback (most recent call last): + ... + SyntaxError: Interface magics have no options, got "foo" + + This is how the built cell magic is used in practice:: + + sage: from sage.repl.interpreter import get_test_shell + sage: shell = get_test_shell() + sage: shell.run_cell('%%gap\nG:=SymmetricGroup(5);\n1+1;Order(G);') + Sym( [ 1 .. 5 ] ) + 2 + 120 + sage: shell.run_cell('%%gap foo\n1+1;\n') + File "", line unknown + SyntaxError: Interface magics have no options, got "foo" + + sage: shell.run_cell('%%gap?') + Docstring: + Interact with gap + + The cell magic %%gap sends multiple lines to the gap interface. + ... + """ + def cell_magic(line, cell): + """ + Evaluate cell magic + + Docstring is overwritten in the instance + + INPUT: + + - ``line`` -- string. The option part of the cell magic. + + - ``cell`` -- string. The lines of the cell magic. + + OUTPUT: + + Prints the interface output. + + RAISES: + + ``SyntaxError`` if a line is specified; Interfaces have no + options. + """ + if line: + raise SyntaxError('Interface magics have no options, got "{0}"'.format(line)) + output = self._interface.eval(cell) + print(output) + cell_magic.__doc__ = CELL_DOCSTRING.format(name=self._name) + return cell_magic diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index 4a0b2e2aaca..fb7abc80d8a 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -352,31 +352,12 @@ def __init__(self, shell=None): if SAGE_IMPORTALL == 'yes': self.init_environment() - def register_interface_magics(self): """ Register magics for each of the Sage interfaces """ - from sage.misc.superseded import deprecation - import sage.interfaces.all - interfaces = [(name, obj) - for name, obj in sage.interfaces.all.__dict__.items() - if isinstance(obj, sage.interfaces.interface.Interface)] - - for real_name, obj in interfaces: - def tmp(line, name=real_name): - self.shell.run_cell('%s.interact()' % name) - tmp.__doc__ = "Interact with %s" % real_name - self.shell.register_magic_function(tmp, magic_name=real_name) - - obj_name = obj.name() - if real_name != obj_name: - def tmp_deprecated(line, name=real_name, badname=obj_name): - deprecation(6288, 'Use %%%s instead of %%%s.' % (name, - badname)) - self.shell.run_cell('%s.interact()' % name) - tmp_deprecated.__doc__ = "Interact with %s" % real_name - self.shell.register_magic_function(tmp_deprecated, magic_name=obj_name) + from sage.repl.interface_magic import InterfaceMagic + InterfaceMagic.register_all(self.shell) def set_quit_hook(self): """ diff --git a/src/sage/repl/ipython_kernel/install.py b/src/sage/repl/ipython_kernel/install.py index fe8aafbce85..1a12a4ecde4 100644 --- a/src/sage/repl/ipython_kernel/install.py +++ b/src/sage/repl/ipython_kernel/install.py @@ -10,7 +10,7 @@ import errno from sage.env import ( - SAGE_ROOT, SAGE_DOC, SAGE_LOCAL, SAGE_EXTCODE, + SAGE_ROOT, SAGE_DOC, SAGE_DOC_OUTPUT, SAGE_LOCAL, SAGE_EXTCODE, SAGE_VERSION ) from jupyter_core.paths import ENV_JUPYTER_PATH @@ -214,7 +214,7 @@ def _symlink_resources(self): os.path.join(self.kernel_dir, filename) ) self.symlink( - os.path.join(SAGE_DOC, 'output', 'html', 'en'), + os.path.join(SAGE_DOC_OUTPUT, 'html', 'en'), os.path.join(self.kernel_dir, 'doc') ) diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py index eb61032dbd7..9639322050a 100644 --- a/src/sage/repl/ipython_kernel/kernel.py +++ b/src/sage/repl/ipython_kernel/kernel.py @@ -81,22 +81,27 @@ def help_links(self): See the Jupyter documentation. + .. NOTE:: + + Urls starting with "kernelspecs" are prepended by the + browser with the appropriate path. + EXAMPLES:: sage: from sage.repl.ipython_kernel.kernel import SageKernel sage: sk = SageKernel.__new__(SageKernel) sage: sk.help_links [{'text': 'Sage Documentation', - 'url': '../kernelspecs/sagemath/doc/index.html'}, + 'url': 'kernelspecs/sagemath/doc/index.html'}, ...] """ from sage.repl.ipython_kernel.install import SageKernelSpec identifier = SageKernelSpec.identifier() - kernel_url = lambda x: '../kernelspecs/{0}/{1}'.format(identifier, x) + kernel_url = lambda x: 'kernelspecs/{0}/{1}'.format(identifier, x) return [ { 'text': 'Sage Documentation', - 'url': kernel_url('doc/index.html') + 'url': kernel_url('doc/index.html'), }, { 'text': 'Sage Tutorial', diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index 623772fd2f1..b177f25973b 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -186,6 +186,27 @@ def supported_output(self): """ raise NotImplementedError('derived classes must implement this method') + def is_in_terminal(self): + """ + Test whether the UI is meant to run in a terminal + + See + :meth:`sage.repl.rich_output.display_manager.DisplayManager.is_in_terminal` + for details. + + OUTPUT: + + Defaults to ``False``. + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_base import BackendBase + sage: backend = BackendBase() + sage: backend.is_in_terminal() + False + """ + return False + def max_width(self): """ Return the number of characters that fit into one output line diff --git a/src/sage/repl/rich_output/backend_ipython.py b/src/sage/repl/rich_output/backend_ipython.py index fbfd386baab..b7bae3f1afc 100644 --- a/src/sage/repl/rich_output/backend_ipython.py +++ b/src/sage/repl/rich_output/backend_ipython.py @@ -1,4 +1,4 @@ -# -*- encoding: utf-8 -*- +# -*- coding: utf-8 -*- """ IPython Backend for the Sage Rich Output System @@ -371,6 +371,26 @@ def launch_jmol(self, output_jmol, plain_text): .format(jmol_cmd, launch_script)) return 'Launched jmol viewer for {0}'.format(plain_text) + def is_in_terminal(self): + """ + Test whether the UI is meant to run in a terminal + + See + :meth:`sage.repl.rich_output.display_manager.DisplayManager.is_in_terminal` + for details. + + OUTPUT: + + ``True`` for the IPython commandline. + + EXAMPLES:: + + sage: from sage.repl.rich_output.backend_ipython import BackendIPythonCommandline + sage: backend = BackendIPythonCommandline() + sage: backend.is_in_terminal() + True + """ + return True class BackendIPythonNotebook(BackendIPython): """ diff --git a/src/sage/repl/rich_output/display_manager.py b/src/sage/repl/rich_output/display_manager.py index c2164cc0b25..2be66dddef4 100644 --- a/src/sage/repl/rich_output/display_manager.py +++ b/src/sage/repl/rich_output/display_manager.py @@ -343,6 +343,26 @@ def preferences(self): """ return self._preferences + def is_in_terminal(self): + """ + Test whether the UI is meant to run in a terminal + + When this method returns ``True``, you can assume that it is + possible to use ``raw_input`` or launch external programs that + take over the input. + + Otherwise, you should assume that the backend runs remotely or + in a pty controlled by another program. Then you should not + launch external programs with a (text or graphical) UI. + + This is used to enable/disable interpreter consoles. + + OUTPUT: + + Boolean. + """ + return self._backend.is_in_terminal() + def check_backend_class(self, backend_class): """ Check that the current backend is an instance of diff --git a/src/sage/rings/algebraic_closure_finite_field.py b/src/sage/rings/algebraic_closure_finite_field.py index 422819f1299..aa30b1e4934 100644 --- a/src/sage/rings/algebraic_closure_finite_field.py +++ b/src/sage/rings/algebraic_closure_finite_field.py @@ -700,7 +700,7 @@ def _subfield(self, n): if n == 1: return self.base_ring() else: - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField return FiniteField(self.base_ring().cardinality() ** n, name=self.variable_name() + str(n), modulus=self._get_polynomial(n), diff --git a/src/sage/rings/all.py b/src/sage/rings/all.py index 4444fe55220..96e17b920c4 100644 --- a/src/sage/rings/all.py +++ b/src/sage/rings/all.py @@ -13,12 +13,8 @@ #***************************************************************************** # Ring base classes -from ring import Ring, Field -from commutative_ring import CommutativeRing -from integral_domain import IntegralDomain -from dedekind_domain import DedekindDomain -from principal_ideal_domain import PrincipalIdealDomain -from euclidean_domain import EuclideanDomain +from ring import (Ring, Field, CommutativeRing, IntegralDomain, + DedekindDomain, PrincipalIdealDomain, EuclideanDomain) # Ring element base classes from sage.structure.element import (CommutativeAlgebraElement, diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index 20be6acd006..d63a0d82c3c 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -96,15 +96,6 @@ from sage.structure.sage_object import SageObject -class NotImplementedOZero(NotImplementedError): - r""" - A special :python:`NotImplementedError` - which is raised when the result is O(0) which means 0 - for sufficiently large values of the variable. - """ - pass - - class AsymptoticExpansionGenerators(SageObject): r""" A collection of constructors for several common asymptotic expansions. @@ -159,7 +150,7 @@ def Stirling(var, precision=None, skip_constant_factor=False): sage: expansion = asymptotic_expansions.Stirling('n', precision=5) sage: n = expansion.parent().gen() - sage: expansion.compare_with_values(n, lambda x: x.factorial(), [5, 10, 20]) + sage: expansion.compare_with_values(n, lambda x: x.factorial(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.00675841118?), (10, 0.0067589306?), (20, 0.006744925?)] sage: asymptotic_expansions.Stirling('n', precision=5, ....: skip_constant_factor=True) @@ -231,7 +222,7 @@ def log_Stirling(var, precision=None, skip_constant_summand=False): sage: expansion = asymptotic_expansions.log_Stirling('n', precision=7) sage: n = expansion.parent().gen() - sage: expansion.compare_with_values(n, lambda x: x.factorial().log(), [5, 10, 20]) + sage: expansion.compare_with_values(n, lambda x: x.factorial().log(), [5, 10, 20]) # rel tol 1e-6 [(5, 0.000564287?), (10, 0.0005870?), (20, 0.0006?)] sage: asymptotic_expansions.log_Stirling('n') n*log(n) - n + 1/2*log(n) + 1/2*log(2*pi) + 1/12*n^(-1) @@ -398,7 +389,7 @@ def HarmonicNumber(var, precision=None, skip_constant_summand=False): sage: ex = asymptotic_expansions.HarmonicNumber('n', precision=5) sage: n = ex.parent().gen() - sage: ex.compare_with_values(n, + sage: ex.compare_with_values(n, # rel tol 1e-6 ....: lambda x: sum(1/k for k in srange(1, x+1)), [5, 10, 20]) [(5, 0.0038125360?), (10, 0.00392733?), (20, 0.0039579?)] sage: asymptotic_expansions.HarmonicNumber('n') @@ -532,7 +523,7 @@ def Binomial_kn_over_n(var, k, precision=None, skip_constant_factor=False): sage: expansion = asymptotic_expansions.Binomial_kn_over_n('n', k=7/5, precision=3) sage: n = expansion.parent().gen() - sage: expansion.compare_with_values(n, lambda x: binomial(7/5*x, x), [5, 10, 20]) + sage: expansion.compare_with_values(n, lambda x: binomial(7/5*x, x), [5, 10, 20]) # rel tol 1e-6 [(5, -0.0287383845047?), (10, -0.030845971026?), (20, -0.03162833549?)] sage: asymptotic_expansions.Binomial_kn_over_n( ....: 'n', k=5, precision=3, skip_constant_factor=True) @@ -716,7 +707,8 @@ def SingularityAnalysis(var, zeta=1, alpha=0, beta=0, delta=0, sage: n = ex.parent().gen() sage: coefficients = ((1-x)^(1/2)).series( ....: x, 21).truncate().coefficients(x, sparse=False) - sage: ex.compare_with_values(n, lambda k: coefficients[k], [5, 10, 20]) + sage: ex.compare_with_values(n, # rel tol 1e-6 + ....: lambda k: coefficients[k], [5, 10, 20]) [(5, 0.015778873294?), (10, 0.01498952777?), (20, 0.0146264622?)] sage: asymptotic_expansions.SingularityAnalysis( ....: 'n', alpha=3, precision=2) @@ -881,9 +873,8 @@ def inverse_gamma_derivative(shift, r): if alpha > 0 and alpha <= precision: result = A(0) elif alpha <= 0 and precision > 0: - raise NotImplementedOZero( - 'The result is O(0) which means 0 for sufficiently ' - 'large {}'.format(var)) + from misc import NotImplementedOZero + raise NotImplementedOZero(A) for (k, r) in it: result += binomial(beta, r) * \ diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index 952eaad4bc5..848123c98a3 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -685,6 +685,22 @@ def __init__(self, parent, summands, simplify=True, convert=True): Asymptotic Ring over Integer Ring > *previous* ValueError: 1/2 is not a coefficient in Exact Term Monoid x^QQ with coefficients in Integer Ring. + + Check :trac:`19921`:: + + sage: CR. = QQ['Z'] + sage: CR_mod = CR.quotient((Z^2 - 1)*CR) + sage: R. = AsymptoticRing(growth_group='x^NN', coefficient_ring=CR) + sage: R_mod = R.change_parameter(coefficient_ring=CR_mod) + sage: e = 1 + x*(Z^2-1) + sage: R_mod(e) + 1 + + Check that :trac:`19999` is resolved:: + + sage: A. = AsymptoticRing('QQ^x * x^QQ', QQ) + sage: 1 + (-1)^x + 2^x + (-2)^x + (-2)^x + 2^x + (-1)^x + 1 """ super(AsymptoticExpansion, self).__init__(parent=parent) @@ -695,11 +711,13 @@ def __init__(self, parent, summands, simplify=True, convert=True): if convert: from misc import combine_exceptions - from term_monoid import TermMonoid + from term_monoid import TermMonoid, ZeroCoefficientError def convert_terms(element): - T = TermMonoid(term=element.parent(), asymptotic_ring=parent) + T = TermMonoid(term_monoid=element.parent(), asymptotic_ring=parent) try: return T(element) + except ZeroCoefficientError: + return None except (ValueError, TypeError) as e: raise combine_exceptions( ValueError('Cannot include %s with parent %s in %s' % @@ -1099,8 +1117,7 @@ def _mul_term_(self, term): sage: expr._mul_term_(t) O(x^3) """ - from term_monoid import ExactTerm - simplify = not isinstance(term, ExactTerm) + simplify = not term.is_exact() return self.parent()(self.summands.mapped(lambda element: term * element), simplify=simplify, convert=False) @@ -1132,9 +1149,17 @@ def _mul_(self, other): multiplication, or methods that exploit the structure of the underlying poset shall be implemented at a later point. + + TESTS:: + + sage: R(1) * R(0) + 0 + sage: _.parent() + Asymptotic Ring over Integer Ring """ - return sum(self._mul_term_(term_other) for - term_other in other.summands.elements()) + return sum(iter(self._mul_term_(term_other) for + term_other in other.summands.elements()), + self.parent().zero()) def _rmul_(self, other): @@ -1256,7 +1281,8 @@ def __invert__(self, precision=None): sage: ~A(0) Traceback (most recent call last): ... - ZeroDivisionError: Division by zero in 0. + ZeroDivisionError: Cannot invert 0 in + Asymptotic Ring over Integer Ring. :: @@ -1264,42 +1290,27 @@ def __invert__(self, precision=None): sage: ~(s + t) Traceback (most recent call last): ... - ValueError: Expansion s + t cannot be inverted since there are - several maximal elements s, t. + ValueError: Cannot determine main term of s + t since there + are several maximal elements s, t. """ if not self.summands: - raise ZeroDivisionError('Division by zero in %s.' % (self,)) - - elif len(self.summands) == 1: - element = next(self.summands.elements()) - return self.parent()._create_element_in_extension_( - ~element, element.parent()) - - max_elem = tuple(self.summands.maximal_elements()) - if len(max_elem) != 1: - raise ValueError('Expansion %s cannot be inverted since there ' - 'are several maximal elements %s.' % - (self, ', '.join(str(e) for e in - sorted(max_elem, key=str)))) - max_elem = max_elem[0] - - imax_elem = ~max_elem - if imax_elem.parent() is max_elem.parent(): - new_self = self + raise ZeroDivisionError( + 'Cannot invert {} in {}.'.format(self, self.parent())) + + (imax_elem, x) = self._main_term_relative_error_(return_inverse_main_term=True) + one = x.parent().one() + + if x: + import itertools + result = AsymptoticExpansion._power_series_( + coefficients=itertools.repeat(one), + start=one, + ratio=-x, + ratio_start=one, + precision=precision) else: - new_self = self.parent()._create_element_in_extension_( - imax_elem, max_elem.parent()).parent()(self) - - one = new_self.parent().one() - geom = one - new_self._mul_term_(imax_elem) + result = one - expanding = True - result = one - while expanding: - new_result = (geom*result + one).truncate(precision=precision) - if new_result.has_same_summands(result): - expanding = False - result = new_result return result._mul_term_(imax_elem) @@ -1352,7 +1363,7 @@ def convert_terms(element): if convert_terms.count < precision: convert_terms.count += 1 return element - T = TermMonoid(term='O', asymptotic_ring=self.parent()) + T = TermMonoid(term_monoid='O', asymptotic_ring=self.parent()) return T(element) convert_terms.count = 0 summands.map(convert_terms, topological=True, reverse=True) @@ -1388,9 +1399,12 @@ def exact_part(self): sage: O(x).exact_part() 0 """ - from term_monoid import ExactTerm - return self.parent([term for term in self.summands.elements_topological() - if isinstance(term, ExactTerm)]) + exact_terms = self.summands.copy() + for term in self.summands.elements_topological(): + if not term.is_exact(): + exact_terms.remove(term.growth) + + return self.parent(exact_terms) def __pow__(self, exponent, precision=None): @@ -1409,22 +1423,27 @@ def __pow__(self, exponent, precision=None): An asymptotic expansion. - TESTS:: + EXAMPLES:: - sage: R_QQ. = AsymptoticRing(growth_group='x^QQ', coefficient_ring=QQ) + sage: Q. = AsymptoticRing(growth_group='x^QQ', coefficient_ring=QQ) sage: x^(1/7) x^(1/7) - sage: R_ZZ. = AsymptoticRing(growth_group='y^ZZ', coefficient_ring=ZZ) + sage: (x^(1/2) + O(x^0))^15 + x^(15/2) + O(x^7) + + :: + + sage: Z. = AsymptoticRing(growth_group='y^ZZ', coefficient_ring=ZZ) sage: y^(1/7) y^(1/7) - sage: (y^(1/7)).parent() + sage: _.parent() Asymptotic Ring over Rational Field - sage: (x^(1/2) + O(x^0))^15 - x^(15/2) + O(x^7) - sage: (y^2 + O(y))^(1/2) # not tested, see #19316 + sage: (y^2 + O(y))^(1/2) y + O(1) sage: (y^2 + O(y))^(-2) y^(-4) + O(y^(-5)) + sage: (1 + 1/y + O(1/y^3))^pi + 1 + pi*y^(-1) + (1/2*pi*(pi - 1))*y^(-2) + O(y^(-3)) :: @@ -1446,6 +1465,8 @@ def __pow__(self, exponent, precision=None): sage: _.parent() Asymptotic Ring over Symbolic Ring + TESTS: + See :trac:`19110`:: sage: O(x)^(-1) @@ -1479,8 +1500,8 @@ def __pow__(self, exponent, precision=None): Traceback (most recent call last): ... ValueError: Cannot take s + t to the exponent s. - > *previous* ValueError: log(s + t) cannot be constructed since - there are several maximal elements s, t. + > *previous* ValueError: Cannot determine main term of s + t + since there are several maximal elements s, t. Check that :trac:`19946` is fixed:: @@ -1525,6 +1546,18 @@ def __pow__(self, exponent, precision=None): else: return super(AsymptoticExpansion, self).__pow__(exponent) + from sage.rings.rational_field import QQ + try: + exponent = QQ(exponent) + except (TypeError, ValueError): + pass + else: + return self.__pow_number__(exponent, precision=precision) + + from sage.symbolic.expression import Expression + if isinstance(exponent, Expression) and exponent.is_constant(): + return self.__pow_number__(exponent, precision=precision) + try: return (exponent * self.log(precision=precision)).exp(precision=precision) except (TypeError, ValueError, ZeroDivisionError) as e: @@ -1536,6 +1569,168 @@ def __pow__(self, exponent, precision=None): pow = __pow__ + def __pow_number__(self, exponent, precision=None): + r""" + Return the power of this asymptotic expansion to some + number (``exponent``). + + Let `m` be the maximal element of this asymptotic expansion + and `r` the remaining summands. This method calculates + + .. MATH:: + + (m + r)^{\mathit{exponent}} + = m^{\mathit{exponent}} \sum_{k=0}^K + \binom{\mathit{exponent}}{k} (r/m)^k + + where `K` is chosen such that adding an additional summand + does not change the result. + + INPUT: + + - ``exponent`` -- a numerical value (e.g. integer, rational) + or other constant. + + - ``precision`` -- a non-negative integer. + + OUTPUT: + + An asymptotic expansion. + + .. SEEALSO:: + + :meth:`pow` + + TESTS:: + + sage: R. = AsymptoticRing(growth_group='x^ZZ', coefficient_ring=ZZ) + sage: (1 + x).__pow_number__(4) + x^4 + 4*x^3 + 6*x^2 + 4*x + 1 + sage: _.parent() + Asymptotic Ring over Rational Field + sage: (x + 1).__pow_number__(1/2, precision=5) + x^(1/2) + 1/2*x^(-1/2) - 1/8*x^(-3/2) + 1/16*x^(-5/2) + - 5/128*x^(-7/2) + O(x^(-9/2)) + sage: _.parent() + Asymptotic Ring over Rational Field + sage: (8 + 1/x).__pow_number__(1/3, precision=5) + 2 + 1/12*x^(-1) - 1/288*x^(-2) + 5/20736*x^(-3) + - 5/248832*x^(-4) + O(x^(-5)) + sage: _.parent() + Asymptotic Ring over Rational Field + + :: + + sage: R(0).__pow_number__(-3/2) + Traceback (most recent call last): + ... + ZeroDivisionError: Cannot take 0 to the negative exponent -3/2. + sage: R(0).__pow_number__(RIF(-1,1)) + Traceback (most recent call last): + ... + ValueError: Possible division by zero, since sign of + the exponent 0.? cannot be determined. + sage: R(0)^0 + 1 + + :: + + sage: A. = AsymptoticRing(growth_group='a^ZZ * b^ZZ', coefficient_ring=QQ) + sage: (a + b).__pow_number__(3/2) + Traceback (most recent call last): + ... + ValueError: Cannot determine main term of a + b since + there are several maximal elements a, b. + """ + if not self.summands: + if exponent > 0: + return self.parent().zero() + elif exponent.is_zero(): + return self.parent().one() + elif exponent < 0: + raise ZeroDivisionError( + 'Cannot take {} to the negative ' + 'exponent {}.'.format(self, exponent)) + else: + raise ValueError( + 'Possible division by zero, since sign of the exponent ' + '{} cannot be determined.'.format(exponent)) + + elif len(self.summands) == 1: + element = next(self.summands.elements()) + return self.parent()._create_element_in_extension_( + element**exponent, element.parent()) + + (max_elem, x) = self._main_term_relative_error_() + + pmax_elem = max_elem**exponent + x = self.parent()._create_element_in_extension_( + pmax_elem, max_elem.parent()).parent()(x) + + one = x.parent().one() + + import itertools + + def binomials(a): + P = a.parent() + a = a + 1 + f = P(1) + for k in itertools.count(1): + k = P(k) + b = a - k + if b == 0: + return + f *= b / k + yield f + + result = AsymptoticExpansion._power_series_( + coefficients=binomials(exponent), + start=one, + ratio=x, + ratio_start=one, + precision=precision) + + return result._mul_term_(pmax_elem) + + + def sqrt(self, precision=None): + r""" + Return the square root of this asymptotic expansion. + + INPUT: + + - ``precision`` -- the precision used for truncating the + expansion. If ``None`` (default value) is used, the + default precision of the parent is used. + + OUTPUT: + + An asymptotic expansion. + + EXAMPLES:: + + sage: A. = AsymptoticRing(growth_group='s^QQ', coefficient_ring=QQ) + sage: s.sqrt() + s^(1/2) + sage: a = (1 + 1/s).sqrt(precision=6); a + 1 + 1/2*s^(-1) - 1/8*s^(-2) + 1/16*s^(-3) + - 5/128*s^(-4) + 7/256*s^(-5) + O(s^(-6)) + + .. SEEALSO:: + + :meth:`pow`, :meth:`rpow`, :meth:`exp`. + + TESTS:: + + sage: P.

= PowerSeriesRing(QQ, default_prec=6) + sage: bool(SR(a.exact_part()).subs(s=1/x) - + ....: SR((1+p).sqrt().polynomial()).subs(p=x) == 0) + True + """ + from sage.rings.rational_field import QQ + return self.pow(QQ(1)/QQ(2), precision=precision) + + def O(self): r""" Convert all terms in this asymptotic expansion to `O`-terms. @@ -1559,8 +1754,6 @@ def O(self): 42*x^42 + x^10 + O(x^2) sage: expr.O() O(x^42) - sage: O(AR(0)) - 0 sage: (2*x).O() O(x) @@ -1568,7 +1761,18 @@ def O(self): :func:`sage.rings.power_series_ring.PowerSeriesRing`, :func:`sage.rings.laurent_series_ring.LaurentSeriesRing`. + + TESTS:: + + sage: AR(0).O() + Traceback (most recent call last): + ... + NotImplementedOZero: The result is O(0) + which means 0 for sufficiently large x """ + if not self.summands: + from misc import NotImplementedOZero + raise NotImplementedOZero(self.parent()) return sum(self.parent().create_summand('O', growth=element) for element in self.summands.maximal_elements()) @@ -1631,8 +1835,8 @@ def log(self, base=None, precision=None): sage: log(s + t) Traceback (most recent call last): ... - ValueError: log(s + t) cannot be constructed since there are - several maximal elements s, t. + ValueError: Cannot determine main term of s + t since + there are several maximal elements s, t. """ P = self.parent() @@ -1646,44 +1850,53 @@ def log(self, base=None, precision=None): return sum(P._create_element_in_extension_(l, element.parent()) for l in element.log_term(base=base)) - max_elem = tuple(self.summands.maximal_elements()) - if len(max_elem) != 1: - raise ValueError('log(%s) cannot be constructed since there ' - 'are several maximal elements %s.' % - (self, ', '.join(str(e) for e in - sorted(max_elem, key=str)))) - max_elem = max_elem[0] - - imax_elem = ~max_elem - if imax_elem.parent() is max_elem.parent(): - new_self = self - else: - new_self = P._create_element_in_extension_( - imax_elem, max_elem.parent()).parent()(self) - - one = new_self.parent().one() - geom = one - new_self._mul_term_(imax_elem) + (max_elem, x) = self._main_term_relative_error_() + geom = -x from sage.rings.integer_ring import ZZ - expanding = True - result = -geom - geom_k = geom - k = ZZ(1) - while expanding: - k += ZZ(1) - geom_k *= geom - new_result = (result - geom_k * ~k).truncate(precision=precision) - if new_result.has_same_summands(result): - expanding = False - result = new_result + import itertools - result += new_self.parent()(max_elem).log() + result = - AsymptoticExpansion._power_series_( + coefficients=iter(1 / ZZ(k) + for k in itertools.count(2)), + start=geom, + ratio=geom, + ratio_start=geom, + precision=precision) + + result += x.parent()(max_elem).log() if base: from sage.functions.log import log result = result / log(base) return result + def is_exact(self): + r""" + Return whether all terms of this expansion are exact. + + OUTPUT: + + A boolean. + + EXAMPLES:: + + sage: A. = AsymptoticRing('x^QQ * log(x)^QQ', QQ) + sage: (x^2 + O(x)).is_exact() + False + sage: (x^2 - x).is_exact() + True + + TESTS:: + + sage: A(0).is_exact() + True + sage: A.one().is_exact() + True + """ + return all(T.is_exact() for T in self.summands) + + def is_little_o_of_one(self): r""" Return whether this expansion is of order `o(1)`. @@ -1804,22 +2017,173 @@ def rpow(self, base, precision=None): geom = expr_o * log(base) P = geom.parent() - expanding = True - result = P.one() - geom_k = P.one() from sage.rings.integer_ring import ZZ - k = ZZ(0) - while expanding: - k += ZZ(1) - geom_k *= geom - new_result = (result + geom_k / k.factorial()).truncate(precision=precision) - if new_result.has_same_summands(result): - expanding = False - result = new_result + import itertools + + def inverted_factorials(): + f = ZZ(1) + for k in itertools.count(1): + f /= ZZ(k) + yield f + + result = AsymptoticExpansion._power_series_( + coefficients=inverted_factorials(), + start=P.one(), + ratio=geom, + ratio_start=P.one(), + precision=precision) return result * large_result + def _main_term_relative_error_(self, return_inverse_main_term=False): + r""" + Split this asymptotic expansion into `m(1+x)` with `x=o(1)`. + + INPUT: + + - ``return_inverse_main_term`` -- (default: ``False``) a boolean. + If set, then the pair `(m^{-1},x)` is returned instead of `(m,x)`. + + OUTPUT: + + A pair (``m``, ``x``) consisting of + a :mod:`term ` ``m`` and + an :class:`asymptotic expansion ` ``x``. + + EXAMPLES:: + + sage: R. = AsymptoticRing('n^ZZ', QQ) + sage: ex = 2*n^2 + n + O(1/n) + sage: (m, x) = ex._main_term_relative_error_() + sage: m + 2*n^2 + sage: x + 1/2*n^(-1) + O(n^(-3)) + sage: ex = 2*n^2 + n + sage: (m, x) = ex._main_term_relative_error_() + sage: m + 2*n^2 + sage: x + 1/2*n^(-1) + sage: ex._main_term_relative_error_(return_inverse_main_term=True) + (1/2*n^(-2), 1/2*n^(-1)) + sage: R(0)._main_term_relative_error_() + Traceback (most recent call last): + ... + ArithmeticError: Cannot determine main term of 0. + + TESTS:: + + sage: R. = AsymptoticRing('n^ZZ*m^ZZ', QQ) + sage: (m + n)._main_term_relative_error_() + Traceback (most recent call last): + ... + ValueError: Cannot determine main term of m + n since + there are several maximal elements m, n. + """ + if not self.summands: + raise ArithmeticError("Cannot determine main term of 0.") + + max_elem = tuple(self.summands.maximal_elements()) + if len(max_elem) != 1: + raise ValueError('Cannot determine main term of {} since there ' + 'are several maximal elements {}.'.format( + self, ', '.join(str(e) for e in + sorted(max_elem, key=str)))) + max_elem = max_elem[0] + + imax_elem = ~max_elem + if imax_elem.parent() is max_elem.parent(): + new_self = self + else: + new_self = self.parent()._create_element_in_extension_( + imax_elem, max_elem.parent()).parent()(self) + + one = new_self.parent().one() + x = - one + new_self._mul_term_(imax_elem) + + if return_inverse_main_term: + return (imax_elem, x) + else: + return (max_elem, x) + + + @staticmethod + def _power_series_(coefficients, start, ratio, ratio_start, precision): + r""" + Return a taylor series. + + Let `c_k` be determined by the ``coefficients`` and set + + .. MATH:: + + s_k = c_k \cdot \mathit{ratio\_start} \cdot \mathit{ratio}^k. + + The result is + + .. MATH:: + + \mathit{start} + \sum_{k=1}^K s_k + + where `K` is chosen such that adding `s_{K+1}` does not change + the result. + + INPUT: + + - ``coefficients`` -- an iterator. + + - ``start`` -- an asymptotic expansion. + + - ``ratio`` -- an asymptotic expansion. + + - ``ratio_start`` -- an asymptotic expansion. + + - ``precision`` -- a non-negative integer. All intermediate + results are truncated to this precision. + + OUTPUT: + + An asymptotic expansion. + + TESTS:: + + sage: from sage.rings.asymptotic.asymptotic_ring import AsymptoticExpansion + sage: from itertools import count + sage: A. = AsymptoticRing('g^ZZ', QQ) + sage: AsymptoticExpansion._power_series_( + ....: coefficients=iter(ZZ(k) for k in count(1)), + ....: start=A(42), + ....: ratio=1/g, + ....: ratio_start=A(5), + ....: precision=4) + 42 + 5*g^(-1) + 10*g^(-2) + 15*g^(-3) + O(g^(-4)) + sage: AsymptoticExpansion._power_series_( + ....: coefficients=iter(ZZ(k) for k in count(1)), + ....: start=A(42), + ....: ratio=1/g+O(1/g^2), + ....: ratio_start=A(5), + ....: precision=4) + 42 + 5*g^(-1) + O(g^(-2)) + sage: AsymptoticExpansion._power_series_( + ....: coefficients=iter(ZZ(k) for k in count(1)), + ....: start=A(42), + ....: ratio=1/g+O(1/g^2), + ....: ratio_start=A(5), + ....: precision=1000000) + 42 + 5*g^(-1) + O(g^(-2)) + """ + result = start + g = ratio_start + for c in coefficients: + g *= ratio + new_result = (result + c*g).truncate(precision=precision) + if new_result.has_same_summands(result): + break + result = new_result + return result + + def exp(self, precision=None): r""" Return the exponential of (i.e., the power of `e` to) this asymptotic expansion. @@ -2475,7 +2839,7 @@ def mapping(term): coefficient_ring=new_coefficient_ring) if hasattr(term, 'coefficient'): c = f(term.coefficient) - if c == 0: + if c.is_zero(): return None return T(term.growth, c) else: @@ -3090,22 +3454,24 @@ def _element_constructor_(self, data, simplify=True, convert=True): raise combine_exceptions( ValueError('Symbolic expression %s is not in %s.' % (data, self)), e) - return sum(summands) + return sum(summands, self.zero()) elif is_PolynomialRing(P): p = P.gen() try: - return sum(self.create_summand('exact', growth=p**i, - coefficient=c) - for i, c in enumerate(data)) + return sum(iter(self.create_summand('exact', growth=p**i, + coefficient=c) + for i, c in enumerate(data)), + self.zero()) except ValueError as e: raise combine_exceptions( ValueError('Polynomial %s is not in %s' % (data, self)), e) elif is_MPolynomialRing(P): try: - return sum(self.create_summand('exact', growth=g, coefficient=c) - for c, g in iter(data)) + return sum(iter(self.create_summand('exact', growth=g, coefficient=c) + for c, g in iter(data)), + self.zero()) except ValueError as e: raise combine_exceptions( ValueError('Polynomial %s is not in %s' % (data, self)), e) @@ -3414,6 +3780,12 @@ def create_summand(self, type, data=None, **kwds): sage: R.create_summand('exact', data=12) 12 + :: + + sage: Z = R.change_parameter(coefficient_ring=Zmod(3)) + sage: Z.create_summand('exact', data=42) + 0 + :: sage: R.create_summand('O', growth=42*x^2, coefficient=1) @@ -3431,7 +3803,7 @@ def create_summand(self, type, data=None, **kwds): TypeError: Cannot create exact term: only 'growth' but no 'coefficient' specified. """ - from term_monoid import TermMonoid + from term_monoid import TermMonoid, ZeroCoefficientError TM = TermMonoid(type, asymptotic_ring=self) if data is None: @@ -3443,12 +3815,11 @@ def create_summand(self, type, data=None, **kwds): raise TypeError("Cannot create exact term: only 'growth' " "but no 'coefficient' specified.") - - if type == 'exact' and kwds.get('coefficient') == 0: + try: + return self(TM(data, **kwds), simplify=False, convert=False) + except ZeroCoefficientError: return self.zero() - return self(TM(data, **kwds), simplify=False, convert=False) - def variable_names(self): r""" diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index 6c0efa735b1..00e23792300 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -1514,8 +1514,17 @@ def __classcall__(cls, base, var=None, category=None, ignore_variables=None): Category of groups sage: ExponentialGrowthGroup(QQ, 'x', category=Monoids()).category() # indirect doctest Category of monoids + + :: + + sage: MonomialGrowthGroup(AsymptoticRing('z^ZZ', QQ), 'x') + Traceback (most recent call last): + ... + TypeError: Asymptotic Ring over Rational Field is not a valid base. """ - if not isinstance(base, sage.structure.parent.Parent): + from asymptotic_ring import AsymptoticRing + if not isinstance(base, sage.structure.parent.Parent) or \ + isinstance(base, AsymptoticRing): raise TypeError('%s is not a valid base.' % (base,)) if var is None: @@ -3432,8 +3441,14 @@ def _le_(self, other): sage: P_SR = GrowthGroup('SR^x') sage: P_ZZ(2^x) <= P_SR(sqrt(3)^x)^2 # indirect doctest True + + Check that :trac:`19999` is fixed:: + + sage: P_ZZ((-2)^x) <= P_ZZ(2^x) or P_ZZ(2^x) <= P_ZZ((-2)^x) + False """ - return bool(abs(self.base) <= abs(other.base)) + return bool(abs(self.base) < abs(other.base)) or \ + bool(self.base == other.base) def _substitute_(self, rules): diff --git a/src/sage/rings/asymptotic/misc.py b/src/sage/rings/asymptotic/misc.py index 6fffb5142a1..e2071b5c6eb 100644 --- a/src/sage/rings/asymptotic/misc.py +++ b/src/sage/rings/asymptotic/misc.py @@ -576,6 +576,43 @@ def log_string(element, base=None): return 'log(%s%s)' % (element, basestr) +class NotImplementedOZero(NotImplementedError): + r""" + A special :python:`NotImplementedError` + which is raised when the result is O(0) which means 0 + for sufficiently large values of the variable. + """ + def __init__(self, data): + r""" + INPUT: + + - ``data`` -- an :class:`AsymptoticRing` or a string. + + TESTS:: + + sage: A = AsymptoticRing('n^ZZ', ZZ) + doctest:...: FutureWarning: ... + sage: from sage.rings.asymptotic.misc import NotImplementedOZero + sage: raise NotImplementedOZero(A) + Traceback (most recent call last): + ... + NotImplementedOZero: The result is O(0) + which means 0 for sufficiently large n + sage: raise NotImplementedOZero('something') + Traceback (most recent call last): + ... + NotImplementedOZero: something + """ + from asymptotic_ring import AsymptoticRing + if isinstance(data, AsymptoticRing): + message = ('The result is O(0) which means 0 for sufficiently ' + 'large {}'.format( + ', '.join(str(g) for g in data.gens()))) + else: + message = data + super(NotImplementedOZero, self).__init__(message) + + def transform_category(category, subcategory_mapping, axiom_mapping, initial_category=None): diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 1d75df6123d..cabc89ee2c3 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -222,6 +222,8 @@ import sage +class ZeroCoefficientError(ValueError): + pass def absorption(left, right): r""" @@ -1091,6 +1093,24 @@ def is_constant(self): """ return False + def is_exact(self): + r""" + Return whether this term is an exact term. + + OUTPUT: + + A boolean. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid + sage: T = GenericTermMonoid(GrowthGroup('x^ZZ * log(x)^ZZ'), QQ) + sage: T.an_element().is_exact() + False + """ + return False + def is_little_o_of_one(self): r""" @@ -2548,7 +2568,7 @@ def __init__(self, parent, growth, coefficient): sage: t = CT_ZZ(x^42, 0) Traceback (most recent call last): ... - ValueError: Zero coefficient 0 is not allowed in + ZeroCoefficientError: Zero coefficient 0 is not allowed in Generic Term Monoid x^ZZ with (implicit) coefficients in Integer Ring. The conversion of growth elements also works for the creation @@ -2565,8 +2585,9 @@ def __init__(self, parent, growth, coefficient): raise ValueError('%s is not a coefficient in %s.' % (coefficient, parent)) if coefficient == 0: - raise ValueError('Zero coefficient %s is not allowed in %s.' % - (coefficient, parent)) + raise ZeroCoefficientError( + 'Zero coefficient %s is not allowed in %s.' % + (coefficient, parent)) self.coefficient = coefficient super(TermWithCoefficient, self).__init__(parent=parent, growth=growth) @@ -3151,7 +3172,7 @@ def can_absorb(self, other): sage: t1.can_absorb(t3) or t3.can_absorb(t1) False """ - return isinstance(other, ExactTerm) and self.growth == other.growth + return other.is_exact() and self.growth == other.growth def _absorb_(self, other): @@ -3333,6 +3354,33 @@ def is_little_o_of_one(self): return self.growth.is_lt_one() + def is_exact(self): + r""" + Return whether this term is an exact term. + + OUTPUT: + + A boolean. + + EXAMPLES:: + + sage: from sage.rings.asymptotic.growth_group import GrowthGroup + sage: from sage.rings.asymptotic.term_monoid import TermMonoid + sage: T = TermMonoid('exact', GrowthGroup('x^ZZ * log(x)^ZZ'), QQ) + sage: T('x * log(x)').is_exact() + True + sage: T('3 * x^2').is_exact() + True + + TESTS:: + + sage: T = TermMonoid('O', GrowthGroup('x^ZZ'), QQ) + sage: T('x').is_exact() + False + """ + return True + + def rpow(self, base): r""" Return the power of ``base`` to this exact term. @@ -3549,9 +3597,9 @@ class TermMonoidFactory(sage.structure.factory.UniqueFactory): INPUT: - - ``term`` -- the kind of term that shall be created. Either a string - ``'exact'`` or ``'O'`` (capital letter ``O``), - or an existing instance of a term. + - ``term_monoid`` -- the kind of terms held in the new term monoid. + Either a string ``'exact'`` or ``'O'`` (capital letter ``O``), + or an existing instance of a term monoid. - ``growth_group`` -- a growth group. @@ -3642,7 +3690,7 @@ class TermMonoidFactory(sage.structure.factory.UniqueFactory): running ._test_prod() . . . pass running ._test_some_elements() . . . pass """ - def create_key_and_extra_args(self, term, + def create_key_and_extra_args(self, term_monoid, growth_group=None, coefficient_ring=None, asymptotic_ring=None, **kwds): @@ -3679,16 +3727,16 @@ def create_key_and_extra_args(self, term, ... ValueError: Integer Ring has to be an asymptotic growth group """ - if isinstance(term, GenericTermMonoid): + if isinstance(term_monoid, GenericTermMonoid): from misc import underlying_class - term_class = underlying_class(term) - elif term == 'O': + term_class = underlying_class(term_monoid) + elif term_monoid == 'O': term_class = OTermMonoid - elif term == 'exact': + elif term_monoid == 'exact': term_class = ExactTermMonoid else: raise ValueError("Term specification '%s' has to be either 'exact' or 'O' " - "or an instance of an existing term." % term) + "or an instance of an existing term." % term_monoid) if asymptotic_ring is not None and \ (growth_group is not None or coefficient_ring is not None): @@ -3707,7 +3755,7 @@ def create_key_and_extra_args(self, term, if coefficient_ring is None: raise ValueError("A coefficient ring has to be specified to " - "create a term monoid of type '%s'" % (term,)) + "create a term monoid of type '%s'" % (term_monoid,)) return (term_class, growth_group, coefficient_ring), kwds diff --git a/src/sage/rings/bernmm.pyx b/src/sage/rings/bernmm.pyx index 5942ca9611e..3c6479ae538 100644 --- a/src/sage/rings/bernmm.pyx +++ b/src/sage/rings/bernmm.pyx @@ -125,6 +125,14 @@ def bernmm_bern_modp(long p, long k): sage: bernmm_bern_modp(p, k) 1972762 + TESTS: + + Check that bernmm works with the new NTL single precision modular + arithmetic from :trac:`19874`:: + + sage: from sage.rings.bernmm import bernmm_bern_modp + sage: bernmm_bern_modp(7, 128) == bernoulli(128) % 7 + True """ cdef long x diff --git a/src/sage/rings/bernmm/bern_modp.cpp b/src/sage/rings/bernmm/bern_modp.cpp index 197c372e9cb..b46a7de18bc 100644 --- a/src/sage/rings/bernmm/bern_modp.cpp +++ b/src/sage/rings/bernmm/bern_modp.cpp @@ -40,17 +40,17 @@ namespace bernmm { PRECONDITIONS: 5 <= p < NTL_SP_BOUND, p prime 2 <= k <= p-3, k even - pinv = 1 / ((double) p) + pinv = PrepMulMod(p) g = a multiplicative generator of GF(p), in [0, p) */ -long bernsum_powg(long p, double pinv, long k, long g) +long bernsum_powg(long p, mulmod_t pinv, long k, long g) { long half_gm1 = (g + ((g & 1) ? 0 : p) - 1) / 2; // (g-1)/2 mod p long g_to_jm1 = 1; long g_to_km1 = PowerMod(g, k-1, p, pinv); long g_to_km1_to_j = g_to_km1; long sum = 0; - double g_pinv = ((double) g) / ((double) p); + muldivrem_t g_pinv = PrepMulDivRem(g, p); mulmod_precon_t g_to_km1_pinv = PrepMulModPrecon(g_to_km1, p, pinv); for (long j = 1; j <= (p-1)/2; j++) @@ -210,7 +210,7 @@ class Expander PRECONDITIONS: 5 <= p < NTL_SP_BOUND, p prime 2 <= k <= p-3, k even - pinv = 1 / ((double) p) + pinv = PrepMulMod(p) g = a multiplicative generator of GF(p), in [0, p) n = multiplicative order of 2 in GF(p) */ @@ -224,7 +224,7 @@ class Expander #error Number of bits in a long must be divisible by TABLE_LG_SIZE #endif -long bernsum_pow2(long p, double pinv, long k, long g, long n) +long bernsum_pow2(long p, mulmod_t pinv, long k, long g, long n) { // In the main summation loop we accumulate data into the _tables_ array; // tables[y][z] contributes to the final answer with a weight of @@ -481,7 +481,7 @@ long PrepRedc(long n) (See bernsum_pow2() for code comments; we only add comments here where something is different from bernsum_pow2()) */ -long bernsum_pow2_redc(long p, double pinv, long k, long g, long n) +long bernsum_pow2_redc(long p, mulmod_t pinv, long k, long g, long n) { long pinv2 = PrepRedc(p); long F = (1L << (ULONG_BITS/2)) % p; @@ -651,11 +651,11 @@ long bernsum_pow2_redc(long p, double pinv, long k, long g, long n) PRECONDITIONS: 5 <= p < NTL_SP_BOUND, p prime 2 <= k <= p-3, k even - pinv = 1 / ((double) p) + pinv = PrepMulMod(p) Algorithm: uses bernsum_powg() to compute the main sum. */ -long _bern_modp_powg(long p, double pinv, long k) +long _bern_modp_powg(long p, mulmod_t pinv, long k) { Factorisation F(p-1); long g = primitive_root(p, pinv, F); @@ -679,13 +679,13 @@ long _bern_modp_powg(long p, double pinv, long k) PRECONDITIONS: 5 <= p < NTL_SP_BOUND, p prime 2 <= k <= p-3, k even - pinv = 1 / ((double) p) + pinv = PrepMulMod(p) 2^k != 1 mod p Algorithm: uses bernsum_pow2() (or bernsum_pow2_redc() if p is small enough) to compute the main sum. */ -long _bern_modp_pow2(long p, double pinv, long k) +long _bern_modp_pow2(long p, mulmod_t pinv, long k) { Factorisation F(p-1); long g = primitive_root(p, pinv, F); @@ -715,9 +715,9 @@ long _bern_modp_pow2(long p, double pinv, long k) PRECONDITIONS: 5 <= p < NTL_SP_BOUND, p prime 2 <= k <= p-3, k even - pinv = 1 / ((double) p) + pinv = PrepMulMod(p) */ -long _bern_modp(long p, double pinv, long k) +long _bern_modp(long p, mulmod_t pinv, long k) { if (PowerMod(2, k, p, pinv) != 1) // 2^k != 1 mod p, so we use the faster version @@ -765,9 +765,9 @@ long bern_modp(long p, long k) if (m == 0) return -1; - double pinv = 1 / ((double) p); + mulmod_t pinv = PrepMulMod(p); long x = _bern_modp(p, pinv, m); // = B_m/m mod p - return MulMod(x, k, p, pinv); + return MulMod(x, k%p, p, pinv); } diff --git a/src/sage/rings/bernmm/bern_modp.h b/src/sage/rings/bernmm/bern_modp.h index 7f2df380508..81bb98dfa82 100644 --- a/src/sage/rings/bernmm/bern_modp.h +++ b/src/sage/rings/bernmm/bern_modp.h @@ -12,6 +12,7 @@ #ifndef BERNMM_BERN_MODP_H #define BERNMM_BERN_MODP_H +#include namespace bernmm { @@ -29,8 +30,8 @@ long bern_modp(long p, long k); /* Exported for testing. */ -long _bern_modp_powg(long p, double pinv, long k); -long _bern_modp_pow2(long p, double pinv, long k); +long _bern_modp_powg(long p, NTL::mulmod_t pinv, long k); +long _bern_modp_pow2(long p, NTL::mulmod_t pinv, long k); }; diff --git a/src/sage/rings/bernmm/bern_modp_util.cpp b/src/sage/rings/bernmm/bern_modp_util.cpp index 9e37b4c86a1..f0e0b309ec6 100644 --- a/src/sage/rings/bernmm/bern_modp_util.cpp +++ b/src/sage/rings/bernmm/bern_modp_util.cpp @@ -20,7 +20,7 @@ NTL_CLIENT; namespace bernmm { -long PowerMod(long a, long ee, long n, double ninv) +long PowerMod(long a, long ee, long n, mulmod_t ninv) { long x, y; @@ -89,7 +89,7 @@ PrimeTable::PrimeTable(long bound) } -long order(long x, long p, double pinv, const Factorisation& F) +long order(long x, long p, mulmod_t pinv, const Factorisation& F) { // in the loop below, m is always some multiple of the order of x long m = p - 1; @@ -113,7 +113,7 @@ long order(long x, long p, double pinv, const Factorisation& F) -long primitive_root(long p, double pinv, const Factorisation& F) +long primitive_root(long p, mulmod_t pinv, const Factorisation& F) { if (p == 2) return 1; diff --git a/src/sage/rings/bernmm/bern_modp_util.h b/src/sage/rings/bernmm/bern_modp_util.h index 24c128cef9b..e232a47ae8e 100644 --- a/src/sage/rings/bernmm/bern_modp_util.h +++ b/src/sage/rings/bernmm/bern_modp_util.h @@ -18,6 +18,7 @@ #include #include +#include #if ULONG_MAX == 4294967295U #define ULONG_BITS 32 @@ -35,11 +36,11 @@ namespace bernmm { /* Same as NTL's PowerMod, but also accepts an _ninv_ parameter, which is the same as the ninv parameter for NTL's MulMod routines, i.e. should have - ninv = 1 / ((double) n). + ninv = PrepMulMod(n). (Implementation is adapted from ZZ.c in NTL 5.4.1.) */ -long PowerMod(long a, long ee, long n, double ninv); +long PowerMod(long a, long ee, long n, NTL::mulmod_t ninv); /* @@ -123,13 +124,13 @@ long next_prime(long p); /* Computes order of x mod p, given the factorisation F of p-1. */ -long order(long x, long p, double pinv, const Factorisation& F); +long order(long x, long p, NTL::mulmod_t pinv, const Factorisation& F); /* Finds the smallest primitive root mod p, given the factorisation F of p-1. */ -long primitive_root(long p, double pinv, const Factorisation& F); +long primitive_root(long p, NTL::mulmod_t pinv, const Factorisation& F); }; // end namespace diff --git a/src/sage/rings/bernmm/bernmm-test.cpp b/src/sage/rings/bernmm/bernmm-test.cpp index da8f754fb5d..06f381500de 100644 --- a/src/sage/rings/bernmm/bernmm-test.cpp +++ b/src/sage/rings/bernmm/bernmm-test.cpp @@ -70,7 +70,7 @@ void bern_naive(mpq_t* res, long n) */ int testcase__bern_modp_powg(long p, long k, mpq_t b) { - double pinv = 1 / ((double) p); + mulmod_t pinv = PrepMulMod(p); // compute B_k mod p using _bern_modp_powg() long x = _bern_modp_powg(p, pinv, k); @@ -147,7 +147,7 @@ int test__bern_modp_powg() */ int testcase__bern_modp_pow2(long p, long k) { - double pinv = 1 / ((double) p); + mulmod_t pinv = PrepMulMod(p); if (PowerMod(2, k, p, pinv) == 1) return 1; diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index de2b71fa056..14446d605a5 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -94,7 +94,7 @@ from sage.categories.fields import Fields from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -from sage.rings.commutative_ring import CommutativeRing +from sage.rings.ring import CommutativeRing from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ diff --git a/src/sage/rings/commutative_ring.py b/src/sage/rings/commutative_ring.py index 27017d5de31..bbd8e051de4 100644 --- a/src/sage/rings/commutative_ring.py +++ b/src/sage/rings/commutative_ring.py @@ -17,6 +17,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20011, "the module sage.rings.commutative_ring is deprecated and will be removed") + from sage.rings.ring import CommutativeRing def is_CommutativeRing(R): @@ -25,6 +28,9 @@ def is_CommutativeRing(R): EXAMPLES:: + sage: import sage.rings.commutative_ring + doctest:...: DeprecationWarning: the module sage.rings.commutative_ring is deprecated and will be removed + See http://trac.sagemath.org/20011 for details. sage: sage.rings.commutative_ring.is_CommutativeRing(ZZ) True """ diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index 43b130dc06b..706b4cbe73f 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -31,6 +31,13 @@ conveniently by splitting into ball operations on the real and imaginary parts. It also allows tracking when complex numbers have an exact (for example exactly zero) real part and an inexact imaginary part, or vice versa. +The parents of complex balls are instances of :class:`ComplexBallField`. +The name ``CBF`` is bound to the complex ball field with the default precision +of 53 bits:: + + sage: CBF is ComplexBallField() is ComplexBallField(53) + True + Comparison ========== @@ -100,6 +107,11 @@ TESTS:: sage: polygen(CBF, x)^3 x^3 +Check that :trac:`19839` is fixed:: + + sage: log(SR(CBF(0.42))).pyobject().parent() + Complex ball field with 53 bits precision + Classes and Methods =================== """ @@ -114,6 +126,8 @@ Classes and Methods include 'sage/ext/interrupt.pxi' include "sage/ext/stdsage.pxi" +import operator + import sage.categories.fields cimport sage.rings.integer @@ -131,8 +145,10 @@ from sage.libs.arb.arf cimport arf_init, arf_get_mpfr, arf_set_mpfr, arf_clear, from sage.libs.arb.mag cimport mag_init, mag_clear, mag_add, mag_set_d, MAG_BITS, mag_is_inf, mag_is_finite, mag_zero from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField +from sage.rings.integer_ring import ZZ from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi from sage.rings.real_arb import RealBallField from sage.rings.real_mpfr cimport RealField_class, RealField, RealNumber @@ -195,7 +211,6 @@ class ComplexBallField(UniqueRepresentation, Field): EXAMPLES:: - sage: CBF = ComplexBallField() # indirect doctest sage: CBF(1) 1.000000000000000 @@ -234,7 +249,6 @@ class ComplexBallField(UniqueRepresentation, Field): EXAMPLES:: - sage: CBF = ComplexBallField() sage: CBF(1) 1.000000000000000 @@ -286,7 +300,6 @@ class ComplexBallField(UniqueRepresentation, Field): base_ring=real_field, category=category or sage.categories.fields.Fields().Infinite()) self._prec = precision - from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_lazy import CLF self._populate_coercion_lists_([ZZ, QQ, real_field, CLF]) @@ -938,7 +951,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: a = CBF(1/3, 1/5) sage: a.real() [0.3333333333333333 +/- 7.04e-17] @@ -957,7 +969,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: a = CBF(1/3, 1/5) sage: a.imag() [0.2000000000000000 +/- 4.45e-17] @@ -1282,7 +1293,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: CBF(0).is_zero() True sage: CBF(RIF(-0.5, 0.5)).is_zero() @@ -1308,7 +1318,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: CBF(pi, 1/3).is_nonzero() True sage: CBF(RIF(-0.5, 0.5), 1/3).is_nonzero() @@ -1354,7 +1363,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: CBF(1).is_exact() True sage: CBF(1/3, 1/3).is_exact() @@ -1385,7 +1393,6 @@ cdef class ComplexBall(RingElement): EXAMPLES:: - sage: CBF = ComplexBallField() sage: a = CBF(1) sage: b = CBF(1) sage: a is b @@ -1842,4 +1849,803 @@ cdef class ComplexBall(RingElement): if _do_sig(prec(self)): sig_off() return res + def __pow__(base, expo, _): + """ + EXAMPLES:: + + sage: CBF(-1)**(1/2) + [+/- 2.84e-16] + [1.00000000000000 +/- 4.45e-16]*I + sage: CBF(e)**CBF(i*pi) + [-1.0000000000000 +/- 1.98e-15] + [+/- 2.32e-15]*I + sage: CBF(0, 1)**AA(2)**(1/2) + [-0.60569986707881 +/- 4.36e-15] + [0.79569320156748 +/- 2.53e-15]*I + + sage: CBF(i)**RBF(2**1000) + [+/- 2.51] + [+/- 2.87]*I + sage: CBF(i)**(2**1000) + 1.000000000000000 + + sage: CBF(0)^(1/3) + nan + nan*I + sage: CBF(0)^(-1) + [+/- inf] + sage: CBF(0)^(-2) + [+/- inf] + [+/- inf]*I + + TESTS:: + + sage: (CBF(e)**CBF(i))**RBF(pi) + [-1.0000000000000 +/- 5.48e-15] + [+/- 4.14e-15]*I + sage: CBF(2*i)**10r + -1024.000000000000 + """ + cdef fmpz_t tmpz + if not isinstance(base, ComplexBall): + return sage.structure.element.bin_op(base, expo, operator.pow) + cdef ComplexBall self = base + cdef ComplexBall res = self._new() + if isinstance(expo, int): + if _do_sig(prec(self)): sig_on() + acb_pow_ui(res.value, self.value, PyInt_AS_LONG(expo), prec(self)) + if _do_sig(prec(self)): sig_off() + elif isinstance(expo, sage.rings.integer.Integer): + if _do_sig(prec(self)): sig_on() + fmpz_init(tmpz) + fmpz_set_mpz(tmpz, ( expo).value) + acb_pow_fmpz(res.value, self.value, tmpz, prec(self)) + fmpz_clear(tmpz) + if _do_sig(prec(self)): sig_off() + elif isinstance(expo, ComplexBall): + if _do_sig(prec(self)): sig_on() + acb_pow(res.value, self.value, ( expo).value, prec(self)) + if _do_sig(prec(self)): sig_off() + elif isinstance(expo, RealBall): + if _do_sig(prec(self)): sig_on() + acb_pow_arb(res.value, self.value, ( expo).value, prec(self)) + if _do_sig(prec(self)): sig_off() + else: + return sage.structure.element.bin_op(base, expo, operator.pow) + return res + + def sqrt(self): + """ + Return the square root of this ball. + + If either the real or imaginary part is exactly zero, only a single + real square root is needed. + + EXAMPLES:: + + sage: CBF(-2).sqrt() + [1.414213562373095 +/- 2.99e-16]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_sqrt(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def rsqrt(self): + """ + Return the reciprocal square root of ``self``. + + If either the real or imaginary part is exactly zero, only a single + real reciprocal square root is needed. + + EXAMPLES:: + + sage: CBF(-2).rsqrt() + [-0.707106781186547 +/- 5.73e-16]*I + sage: CBF(0, 1/2).rsqrt() + 1.000000000000000 - 1.000000000000000*I + sage: CBF(0).rsqrt() + nan + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_rsqrt(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def cube(self): + """ + Return the cube of this ball. + + The result is computed efficiently using two real squarings, two real + multiplications, and scalar operations. + + EXAMPLES:: + + sage: CBF(1, 1).cube() + -2.000000000000000 + 2.000000000000000*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_cube(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def rising_factorial(self, n): + """ + Return the ``n``-th rising factorial of this ball. + + The `n`-th rising factorial of `x` is equal to `x (x+1) \cdots (x+n-1)`. + + EXAMPLES:: + + sage: CBF(1).rising_factorial(5) + 120.0000000000000 + sage: CBF(1/3, 1/2).rising_factorial(300) + [-3.87949484513701e+612 +/- 8.46e+597] + [-3.52042209762719e+612 +/- 7.70e+597]*I + + sage: CBF(1).rising_factorial(-1) + Traceback (most recent call last): + ... + ValueError: expected a nonnegative index + sage: CBF(1).rising_factorial(2**64) + Traceback (most recent call last): + ... + OverflowError: index too large + """ + cdef ComplexBall res = self._new() + cdef sage.rings.integer.Integer n_as_Integer = ZZ.coerce(n) + if mpz_fits_ulong_p(n_as_Integer.value): + if _do_sig(prec(self)): sig_on() + acb_rising_ui(res.value, self.value, mpz_get_ui(n_as_Integer.value), prec(self)) + if _do_sig(prec(self)): sig_off() + return res + elif n_as_Integer < 0: + raise ValueError("expected a nonnegative index") + else: + raise OverflowError("index too large") + + # Elementary functions + + def log(self, base=None): + """ + General logarithm (principal branch). + + INPUT: + + - ``base`` (optional, complex ball or number) -- if ``None``, return + the principal branch of the natural logarithm ``ln(self)``, + otherwise, return the general logarithm ``ln(self)/ln(base)`` + + EXAMPLES:: + + sage: CBF(2*i).log() + [0.6931471805599453 +/- 4.16e-17] + [1.570796326794897 +/- 6.65e-16]*I + sage: CBF(-1).log() + [3.141592653589793 +/- 5.61e-16]*I + + sage: CBF(2*i).log(2) + [1.000000000000000 +/- 8.01e-17] + [2.26618007091360 +/- 4.23e-15]*I + sage: CBF(2*i).log(CBF(i)) + [1.000000000000000 +/- 2.83e-16] + [-0.441271200305303 +/- 2.82e-16]*I + + sage: CBF('inf').log() + nan + nan*I + sage: CBF(2).log(0) + nan + nan*I + """ + cdef ComplexBall cst + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_log(res.value, self.value, prec(self)) + if base is not None: + cst = self._parent.coerce(base).log() + if _do_sig(prec(self)): sig_on() + acb_div(res.value, res.value, cst.value, prec(self)) + if _do_sig(prec(self)): sig_off() + if _do_sig(prec(self)): sig_off() + return res + + def log1p(self): + """ + Return ``log(1 + self)``, computed accurately when ``self`` is close to + zero. + + EXAMPLES:: + + sage: eps = RBF(1e-50) + sage: CBF(1+eps, eps).log() + [+/- 2.23e-16] + [1.000000000000000e-50 +/- 2.30e-66]*I + sage: CBF(eps, eps).log1p() + [1.000000000000000e-50 +/- 7.63e-68] + [1.00000000000000e-50 +/- 2.30e-66]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_log1p(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def exp(self): + """ + Return the exponential of this ball. + + .. SEEALSO:: :meth:`exppii` + + EXAMPLES:: + + sage: CBF(i*pi).exp() + [-1.00000000000000 +/- 6.67e-16] + [+/- 5.68e-16]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_exp(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def exppii(self): + """ + Return ``exp(pi*i*self)``. + + EXAMPLES:: + + sage: CBF(1/2).exppii() + 1.000000000000000*I + sage: CBF(0, -1/pi).exppii() + [2.71828182845904 +/- 6.05e-15] + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_exp_pi_i(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def sin(self): + """ + Return the sine of this ball. + + EXAMPLES:: + + sage: CBF(i*pi).sin() + [11.5487393572577 +/- 5.34e-14]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_sin(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def cos(self): + """ + Return the cosine of this ball. + + EXAMPLES:: + + sage: CBF(i*pi).cos() + [11.59195327552152 +/- 8.38e-15] + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_cos(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def tan(self): + """ + Return the tangent of this ball. + + EXAMPLES:: + + sage: CBF(pi/2, 1/10).tan() + [+/- 4.00e-14] + [10.0333111322540 +/- 3.60e-14]*I + sage: CBF(pi/2).tan() + [+/- inf] + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_tan(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def cot(self): + """ + Return the cotangent of this ball. + + EXAMPLES:: + + sage: CBF(pi, 1/10).cot() + [+/- 5.74e-14] + [-10.0333111322540 +/- 4.05e-14]*I + sage: CBF(pi).cot() + [+/- inf] + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_cot(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def arctan(self): + """ + Return the arctangent of this ball. + + EXAMPLES:: + + sage: CBF(1+i).arctan() + [1.017221967897851 +/- 4.93e-16] + [0.4023594781085251 +/- 8.52e-17]*I + sage: CBF(i).arctan() + nan + nan*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_atan(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + # Special functions + + def gamma(self, z=None): + """ + Return the image of this ball by the Euler Gamma function (if + ``z = None``) or the incomplete Gamma function (otherwise). + + EXAMPLES:: + + sage: CBF(1, 1).gamma() + [0.49801566811836 +/- 4.98e-15] + [-0.154949828301811 +/- 7.67e-16]*I + sage: CBF(-1).gamma() + nan + sage: CBF(1, 1).gamma(0) + [0.49801566811836 +/- 4.98e-15] + [-0.154949828301811 +/- 7.67e-16]*I + sage: CBF(1, 1).gamma(100) + [-3.6143867454139e-45 +/- 7.26e-59] + [-3.7022961377791e-44 +/- 4.71e-58]*I + sage: CBF(1, 1).gamma(CLF(i)) + [0.32886684193500 +/- 5.49e-15] + [-0.18974945045621 +/- 1.49e-15]*I + """ + cdef ComplexBall my_z + cdef ComplexBall res = self._new() + if z is None: + if _do_sig(prec(self)): sig_on() + acb_gamma(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + else: + my_z = self._parent.coerce(z) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_gamma_upper(res.value, self.value, my_z.value, 0, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def log_gamma(self): + r""" + Return the image of this ball by the logarithmic Gamma function. + + The branch cut of the logarithmic gamma function is placed on the + negative half-axis, which means that + ``log_gamma(z) + log z = log_gamma(z+1)`` holds for all `z`, + whereas ``log_gamma(z) != log(gamma(z))`` in general. + + EXAMPLES:: + + sage: CBF(1000, 1000).log_gamma() + [5466.22252162990 +/- 3.05e-12] + [7039.33429191119 +/- 3.81e-12]*I + sage: CBF(-1/2).log_gamma() + [1.265512123484645 +/- 8.82e-16] + [-3.141592653589793 +/- 5.68e-16]*I + sage: CBF(-1).log_gamma() + nan + [-3.141592653589793 +/- 5.68e-16]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_lgamma(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def psi(self): + """ + Compute the digamma function with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).psi() + [0.0946503206224770 +/- 7.34e-17] + [1.076674047468581 +/- 2.63e-16]*I + sage: CBF(-1).psi() + nan + """ + + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_digamma(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def zeta(self, a=None): + """ + Return the image of this ball by the Hurwitz zeta function. + + For ``a = None``, this computes the Riemann zeta function. + + EXAMPLES:: + + sage: CBF(1, 1).zeta() + [0.5821580597520036 +/- 5.27e-17] + [-0.9268485643308071 +/- 2.81e-17]*I + sage: CBF(1, 1).zeta(1) + [0.5821580597520036 +/- 5.27e-17] + [-0.9268485643308071 +/- 2.81e-17]*I + sage: CBF(1, 1).zeta(1/2) + [1.497919876084167 +/- 2.91e-16] + [0.2448655353684164 +/- 4.22e-17]*I + sage: CBF(1, 1).zeta(CBF(1, 1)) + [-0.3593983122202835 +/- 3.01e-17] + [-2.875283329756940 +/- 4.52e-16]*I + sage: CBF(1, 1).zeta(-1) + nan + nan*I + """ + cdef ComplexBall a_ball + cdef ComplexBall res = self._new() + if a is None: + if _do_sig(prec(self)): sig_on() + acb_zeta(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + else: + a_ball = self._parent.coerce(a) + if _do_sig(prec(self)): sig_on() + acb_hurwitz_zeta(res.value, self.value, a_ball.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def polylog(self, s): + """ + Return the polylogarithm `\operatorname{Li}_s(\mathrm{self})`. + + EXAMPLES:: + + sage: CBF(2).polylog(1) + [+/- 4.65e-15] + [-3.14159265358979 +/- 8.15e-15]*I + sage: CBF(1, 1).polylog(CBF(1, 1)) + [0.3708160030469 +/- 2.38e-14] + [2.7238016577979 +/- 4.22e-14]*I + + TESTS:: + + sage: CBF(2).polylog(1r) + [+/- 4.65e-15] + [-3.14159265358979 +/- 8.15e-15]*I + """ + cdef ComplexBall s_as_ball + cdef sage.rings.integer.Integer s_as_Integer + cdef ComplexBall res = self._new() + try: + s_as_Integer = ZZ.coerce(s) + if mpz_fits_slong_p(s_as_Integer.value): + if _do_sig(prec(self)): sig_on() + acb_polylog_si(res.value, mpz_get_si(s_as_Integer.value), self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + except TypeError: + pass + s_as_ball = self._parent.coerce(s) + if _do_sig(prec(self)): sig_on() + acb_polylog(res.value, s_as_ball.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def agm1(self): + """ + Return the arithmetic-geometric mean of 1 and ``self``. + + The arithmetic-geometric mean is defined such that the function is + continuous in the complex plane except for a branch cut along the + negative half axis (where it is continuous from above). This + corresponds to always choosing an "optimal" branch for the square root + in the arithmetic-geometric mean iteration. + + EXAMPLES:: + + sage: CBF(0, -1).agm1() + [0.5990701173678 +/- 1.14e-14] + [-0.5990701173678 +/- 1.22e-14]*I + """ + cdef ComplexBall res = self._new() + if _do_sig(prec(self)): sig_on() + acb_agm1(res.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def hypergeometric(self, a, b): + r""" + Return the generalized hypergeometric function of ``self``. + + INPUT: + + - ``a`` -- upper parameters, list of complex numbers that coerce into + this ball's parent; + + - ``b`` -- lower parameters, list of complex numbers that coerce into + this ball's parent. + + OUTPUT: + + The generalized hypergeometric function defined by + + .. math:: + + {}_pF_q(a_1,\ldots,a_p;b_1,\ldots,b_q;z) + = \sum_{k=0}^\infty \frac{(a_1)_k\dots(a_p)_k}{(b_1)_k\dots(b_q)_k} \frac {z^k} {k!} + + extended using analytic continuation or regularization when the sum + does not converge. + + EXAMPLES:: + + sage: CBF(1, pi/2).hypergeometric([], []) + [+/- 3.57e-15] + [2.7182818284590 +/- 5.37e-14]*I + + sage: CBF(1, pi).hypergeometric([1/4], [1/4]) + [-2.7182818284590 +/- 8.63e-14] + [+/- 3.69e-14]*I + + sage: CBF(1000, 1000).hypergeometric([100], [AA(sqrt(2))]) + [1.2796735556e+590 +/- 4.04e+579] + [-9.3233349199e+590 +/- 3.30e+579]*I + + sage: CBF(0, 1).hypergeometric([], [1/2, 1/3, 1/4]) + [-3.7991962344383 +/- 4.98e-14] + [23.8780971778049 +/- 5.40e-14]*I + + sage: CBF(0).hypergeometric([1], []) + 1.000000000000000 + sage: CBF(1, 1).hypergeometric([1], []) + [+/- inf] + [+/- inf]*I + + TESTS:: + + sage: CBF(0, 1).hypergeometric([QQbar(sqrt(2)), RLF(pi)], [1r, 1/2]) + [-8.7029449215408 +/- 6.89e-14] + [-0.8499070546106 +/- 4.98e-14]*I + """ + cdef ComplexBall tmp, my_a, my_b + cdef ComplexBall res = self._new() + cdef long p = len(a) + cdef long q = len(b) + if p == q == 1: + my_a = self._parent.coerce(a[0]) + my_b = self._parent.coerce(b[0]) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_m(res.value, my_a.value, my_b.value, self.value, 0, + prec(self)) + if _do_sig(prec(self)): sig_off() + return res + cdef long i1 = -1 + cdef long s + try: + i1 = a.index(1) + s = 1 + except ValueError: + s = 0 + cdef acb_ptr vec_a = _acb_vec_init(p - s) + cdef acb_ptr vec_b = _acb_vec_init(q + 1 - s) + cdef long j = 0 + for i in xrange(p): + if i != i1: + tmp = self._parent.coerce(a[i]) + acb_set(&(vec_a[j]), tmp.value) + j += 1 + for i in range(q): + tmp = self._parent.coerce(b[i]) + acb_set(&(vec_b[i]), tmp.value) + if s == 0: + acb_one(&(vec_b[q])) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_pfq_direct(res.value, vec_a, p - s, vec_b, q + 1 - s, + self.value, -1, prec(self)) + if _do_sig(prec(self)): sig_off() + _acb_vec_clear(vec_b, q + 1 - s) + _acb_vec_clear(vec_a, p - s) + return res + + def hypergeometric_U(self, a, b): + """ + Return the Tricomi confluent hypergeometric function U(a, b, self) of + this ball. + + EXAMPLES:: + + sage: CBF(1000, 1000).hypergeometric_U(RLF(pi), -100) + [-7.261605907166e-11 +/- 4.89e-24] + [-7.928136216391e-11 +/- 5.36e-24]*I + sage: CBF(1000, 1000).hypergeometric_U(0, -100) + 1.000000000000000 + """ + cdef ComplexBall res = self._new() + cdef ComplexBall my_a = self._parent.coerce(a) + cdef ComplexBall my_b = self._parent.coerce(b) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_u(res.value, my_a.value, my_b.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def erf(self): + """ + Return the error function with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).erf() + [1.31615128169795 +/- 8.80e-15] + [0.19045346923783 +/- 9.19e-15]*I + """ + + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_erf(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def erfc(self): + """ + Compute the complementary error function with argument ``self``. + + EXAMPLES:: + + sage: CBF(20).erfc() + [5.3958656116079e-176 +/- 1.08e-190] + sage: CBF(100, 100).erfc() + [0.00065234366376858 +/- 6.52e-18] + [-0.00393572636292141 +/- 5.16e-18]*I + """ + + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_erfc(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def bessel_J(self, nu): + """ + Return the Bessel function of the first kind with argument ``self`` + and index ``nu``. + + EXAMPLES:: + + sage: CBF(1, 1).bessel_J(1) + [0.614160334922903 +/- 8.48e-16] + [0.365028028827088 +/- 6.62e-16]*I + sage: CBF(100, -100).bessel_J(1/3) + [1.108431870251e+41 +/- 5.53e+28] + [-8.952577603125e+41 +/- 2.91e+28]*I + """ + cdef ComplexBall result = self._new() + cdef ComplexBall my_nu = self._parent.coerce(nu) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_bessel_j(result.value, my_nu.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def bessel_K(self, nu): + """ + Return the modified Bessel function of the second kind with argument + ``self`` and index ``nu``. + + EXAMPLES:: + + sage: CBF(1, 1).bessel_K(0) + [0.08019772694652 +/- 3.19e-15] + [-0.35727745928533 +/- 1.08e-15]*I + sage: CBF(1, 1).bessel_K(1) + [0.02456830552374 +/- 6.22e-15] + [-0.45971947380119 +/- 6.74e-15]*I + sage: CBF(100, 100).bessel_K(QQbar(i)) + [3.8693896656383e-45 +/- 2.38e-59] + [5.5071004234177e-46 +/- 5.86e-60]*I + """ + cdef ComplexBall result = self._new() + cdef ComplexBall my_nu = self._parent.coerce(nu) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_bessel_k(result.value, my_nu.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def exp_integral_e(self, s): + """ + Return the image of this ball by the generalized exponential integral + with index ``s``. + + EXAMPLES:: + + sage: CBF(1+i).exp_integral_e(1) + [0.00028162445198 +/- 2.78e-15] + [-0.17932453503936 +/- 2.56e-15]*I + sage: CBF(1+i).exp_integral_e(QQbar(i)) + [-0.10396361883964 +/- 4.92e-15] + [-0.16268401277783 +/- 4.78e-15]*I + """ + cdef ComplexBall res = self._new() + cdef ComplexBall my_s = self._parent.coerce(s) + if _do_sig(prec(self)): sig_on() + acb_hypgeom_expint(res.value, my_s.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return res + + def ei(self): + """ + Return the exponential integral with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).ei() + [1.76462598556385 +/- 6.65e-15] + [2.38776985151052 +/- 4.34e-15]*I + sage: CBF(0).ei() + nan + """ + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_ei(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def si(self): + """ + Return the sine integral with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).si() + [1.10422265823558 +/- 2.16e-15] + [0.88245380500792 +/- 3.15e-15]*I + sage: CBF(0).si() + 0 + """ + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_si(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def ci(self): + """ + Return the cosine integral with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).ci() + [0.882172180555936 +/- 4.85e-16] + [0.287249133519956 +/- 3.47e-16]*I + sage: CBF(0).ci() + nan + nan*I + """ + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_ci(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def shi(self): + """ + Return the hyperbolic sine integral with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).shi() + [0.88245380500792 +/- 3.15e-15] + [1.10422265823558 +/- 2.16e-15]*I + sage: CBF(0).shi() + 0 + """ + + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_shi(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def chi(self): + """ + Return the hyperbolic cosine integral with argument ``self``. + + EXAMPLES:: + + sage: CBF(1, 1).chi() + [0.882172180555936 +/- 4.85e-16] + [1.28354719327494 +/- 1.05e-15]*I + sage: CBF(0).chi() + nan + nan*I + """ + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_chi(result.value, self.value, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + + def li(self, bint offset=False): + """ + Return the logarithmic integral with argument ``self``. + + If ``offset`` is True, return the offset logarithmic integral. + + EXAMPLES:: + + sage: CBF(1, 1).li() + [0.61391166922119 +/- 7.03e-15] + [2.05958421419258 +/- 8.25e-15]*I + sage: CBF(0).li() + 0 + sage: CBF(0).li(offset=True) + [-1.045163780117493 +/- 5.54e-16] + sage: li(0).n() + 0.000000000000000 + sage: Li(0).n() + -1.04516378011749 + """ + cdef ComplexBall result = self._new() + if _do_sig(prec(self)): sig_on() + acb_hypgeom_li(result.value, self.value, offset, prec(self)) + if _do_sig(prec(self)): sig_off() + return result + CBF = ComplexBallField() diff --git a/src/sage/rings/complex_interval_field.py b/src/sage/rings/complex_interval_field.py index e147a410be5..5dea64c6f43 100644 --- a/src/sage/rings/complex_interval_field.py +++ b/src/sage/rings/complex_interval_field.py @@ -216,6 +216,39 @@ def __reduce__(self): """ return ComplexIntervalField, (self._prec, ) + def construction(self): + """ + Returns the functorial construction of this complex interval field, + namely as the algebraic closure of the real interval field with + the same precision. + + EXAMPLES:: + + sage: c, S = CIF.construction(); c, S + (AlgebraicClosureFunctor, + Real Interval Field with 53 bits of precision) + sage: CIF == c(S) + True + + TESTS: + + Test that :trac:`19922` is fixed:: + + sage: c = ComplexIntervalField(128).an_element() + sage: r = RealIntervalField(64).an_element() + sage: c + r + 1 + 1*I + sage: r + c + 1 + 1*I + sage: parent(c+r) + Complex Interval Field with 64 bits of precision + sage: R = ComplexIntervalField(128)['x'] + sage: (R.gen() * RIF.one()).parent() + Univariate Polynomial Ring in x over Complex Interval Field with 53 bits of precision + """ + from sage.categories.pushout import AlgebraicClosureFunctor + return (AlgebraicClosureFunctor(), self._real_field()) + def is_exact(self): """ The complex interval field is not exact. diff --git a/src/sage/rings/continued_fraction.py b/src/sage/rings/continued_fraction.py index bff825fdc73..60cbfec0291 100644 --- a/src/sage/rings/continued_fraction.py +++ b/src/sage/rings/continued_fraction.py @@ -803,8 +803,8 @@ def convergents(self): Return the list of partial convergents of ``self``. If ``self`` is an infinite continued fraction, then the object returned - is a :class:`~sage.misc.lazy_list.lazy_list` which behave like an - infinite list. + is a :class:`~sage.misc.lazy_list.lazy_list_generic` which + behave like an infinite list. EXAMPLES:: @@ -826,7 +826,7 @@ def quotients(self): Return the list of partial quotients of ``self``. If ``self`` is an infinite continued fraction, the the object returned - is a :class:``~sage.misc.lazy_list.lazy_list`` which behave like an + is a :class:`~sage.misc.lazy_list.lazy_list_generic` which behave like an infinite list. EXAMPLES:: @@ -2400,9 +2400,9 @@ def continued_fraction(x, value=None): return ContinuedFraction_periodic(x1, x2) # input for infinite partial quotient expansion - from sage.misc.lazy_list import lazy_list + from sage.misc.lazy_list import lazy_list_generic from sage.combinat.words.infinite_word import InfiniteWord_class - if isinstance(x, (lazy_list, InfiniteWord_class)): + if isinstance(x, (lazy_list_generic, InfiniteWord_class)): return ContinuedFraction_infinite(x, value) from sage.combinat.words.abstract_word import Word_class diff --git a/src/sage/rings/dedekind_domain.py b/src/sage/rings/dedekind_domain.py index 96c872b1283..fa0cc000d02 100644 --- a/src/sage/rings/dedekind_domain.py +++ b/src/sage/rings/dedekind_domain.py @@ -17,6 +17,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20011, "the module sage.rings.dedekind_domain is deprecated and will be removed") + from sage.rings.ring import DedekindDomain def is_DedekindDomain(R): @@ -25,6 +28,9 @@ def is_DedekindDomain(R): EXAMPLES:: + sage: import sage.rings.dedekind_domain + doctest:...: DeprecationWarning: the module sage.rings.dedekind_domain is deprecated and will be removed + See http://trac.sagemath.org/20011 for details. sage: sage.rings.dedekind_domain.is_DedekindDomain(DedekindDomain(QQ)) True """ diff --git a/src/sage/rings/euclidean_domain.py b/src/sage/rings/euclidean_domain.py index 2b893d26159..3a6291146e1 100644 --- a/src/sage/rings/euclidean_domain.py +++ b/src/sage/rings/euclidean_domain.py @@ -17,6 +17,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20011, "the module sage.rings.euclidean_domain is deprecated and will be removed") + from sage.rings.ring import EuclideanDomain def is_EuclideanDomain(R): @@ -25,6 +28,9 @@ def is_EuclideanDomain(R): EXAMPLES:: + sage: import sage.rings.euclidean_domain + doctest:...: DeprecationWarning: the module sage.rings.euclidean_domain is deprecated and will be removed + See http://trac.sagemath.org/20011 for details. sage: sage.rings.euclidean_domain.is_EuclideanDomain(EuclideanDomain(ZZ)) True """ diff --git a/src/sage/rings/field.py b/src/sage/rings/field.py index 9dda311dfb9..31bdd7f0378 100644 --- a/src/sage/rings/field.py +++ b/src/sage/rings/field.py @@ -47,7 +47,7 @@ def is_PrimeField(R): sage: sage.rings.field.is_PrimeField(GF(7^2,'t')) False """ - from finite_rings.constructor import is_FiniteField + from finite_rings.finite_field_constructor import is_FiniteField from rational_field import is_RationalField if is_RationalField(R): diff --git a/src/sage/rings/finite_rings/all.py b/src/sage/rings/finite_rings/all.py index df3a9d2b69c..cee946d1518 100644 --- a/src/sage/rings/finite_rings/all.py +++ b/src/sage/rings/finite_rings/all.py @@ -18,6 +18,6 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from constructor import FiniteField +from finite_field_constructor import FiniteField from conway_polynomials import conway_polynomial, exists_conway_polynomial GF = FiniteField diff --git a/src/sage/rings/finite_rings/constructor.py b/src/sage/rings/finite_rings/constructor.py index 564facec580..ed71084b2fc 100644 --- a/src/sage/rings/finite_rings/constructor.py +++ b/src/sage/rings/finite_rings/constructor.py @@ -1,668 +1,4 @@ -r""" -Finite Fields +from sage.misc.superseded import deprecation +deprecation(19941,"This module has been renamed to sage.rings.finite_rings.finite_field_constructor") -Sage supports arithmetic in finite prime and extension fields. -Several implementation for prime fields are implemented natively in -Sage for several sizes of primes `p`. These implementations -are - - -- ``sage.rings.finite_rings.integer_mod.IntegerMod_int``, - -- ``sage.rings.finite_rings.integer_mod.IntegerMod_int64``, and - -- ``sage.rings.finite_rings.integer_mod.IntegerMod_gmp``. - - -Small extension fields of cardinality `< 2^{16}` are -implemented using tables of Zech logs via the Givaro C++ library -(``sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro``). -While this representation is very fast it is limited to finite -fields of small cardinality. Larger finite extension fields of -order `q >= 2^{16}` are internally represented as -polynomials over smaller finite prime fields. If the -characteristic of such a field is 2 then NTL is used internally to -represent the field -(``sage.rings.finite_rings.finite_field_ntl_gf2e.FiniteField_ntl_gf2e``). -In all other case the PARI C library is used -(``sage.rings.finite_rings.finite_field_pari_ffelt.FiniteField_pari_ffelt``). - -However, this distinction is internal only and the user usually -does not have to worry about it because consistency across all -implementations is aimed for. In all extension field -implementations the user may either specify a minimal polynomial or -leave the choice to Sage. - -For small finite fields the default choice are Conway polynomials. - -The Conway polynomial `C_n` is the lexicographically first -monic irreducible, primitive polynomial of degree `n` over -`GF(p)` with the property that for a root `\alpha` -of `C_n` we have that -`\beta= -\alpha^{(p^n - 1)/(p^m - 1)}` is a root of -`C_m` for all `m` dividing `n`. Sage -contains a database of Conway polynomials which also can be queried -independently of finite field construction. - -While Sage supports basic arithmetic in finite fields some more -advanced features for computing with finite fields are still not -implemented. For instance, Sage does not calculate embeddings of -finite fields yet. - -EXAMPLES:: - - sage: k = GF(5); type(k) - - -:: - - sage: k = GF(5^2,'c'); type(k) - - -:: - - sage: k = GF(2^16,'c'); type(k) - - -:: - - sage: k = GF(3^16,'c'); type(k) - - -Finite Fields support iteration, starting with 0. - -:: - - sage: k = GF(9, 'a') - sage: for i,x in enumerate(k): print i,x - 0 0 - 1 a - 2 a + 1 - 3 2*a + 1 - 4 2 - 5 2*a - 6 2*a + 2 - 7 a + 2 - 8 1 - sage: for a in GF(5): - ... print a - 0 - 1 - 2 - 3 - 4 - -We output the base rings of several finite fields. - -:: - - sage: k = GF(3); type(k) - - sage: k.base_ring() - Finite Field of size 3 - -:: - - sage: k = GF(9,'alpha'); type(k) - - sage: k.base_ring() - Finite Field of size 3 - -:: - - sage: k = GF(3^40,'b'); type(k) - - sage: k.base_ring() - Finite Field of size 3 - -Further examples:: - - sage: GF(2).is_field() - True - sage: GF(next_prime(10^20)).is_field() - True - sage: GF(19^20,'a').is_field() - True - sage: GF(8,'a').is_field() - True - -AUTHORS: - -- William Stein: initial version - -- Robert Bradshaw: prime field implementation - -- Martin Albrecht: Givaro and ntl.GF2E implementations -""" - -#***************************************************************************** -# Copyright (C) 2006 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -#***************************************************************************** - -import random - -from sage.rings.finite_rings.finite_field_base import is_FiniteField -from sage.structure.category_object import normalize_names - -from sage.rings.integer import Integer - -import sage.rings.polynomial.polynomial_element as polynomial_element -import sage.rings.polynomial.multi_polynomial_element as multi_polynomial_element -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - -# We don't late import this because this means trouble with the Givaro library -# On a Macbook Pro OSX 10.5.8, this manifests as a Bus Error on exiting Sage. -# TODO: figure out why -from finite_field_givaro import FiniteField_givaro - -import sage.interfaces.gap - -from sage.structure.factory import UniqueFactory - -class FiniteFieldFactory(UniqueFactory): - """ - Return the globally unique finite field of given order with - generator labeled by the given name and possibly with given - modulus. - - INPUT: - - - ``order`` -- a prime power - - - ``name`` -- string; must be specified unless ``order`` is prime. - - - ``modulus`` -- (optional) either a defining polynomial for the - field, or a string specifying an algorithm to use to generate - such a polynomial. If ``modulus`` is a string, it is passed to - :meth:`~sage.rings.polynomial.irreducible_element()` as the - parameter ``algorithm``; see there for the permissible values of - this parameter. In particular, you can specify - ``modulus="primitive"`` to get a primitive polynomial. - - - ``impl`` -- (optional) a string specifying the implementation of - the finite field. Possible values are: - - - ``'modn'`` -- ring of integers modulo `p` (only for prime - fields). - - - ``'givaro'`` -- Givaro, which uses Zech logs (only for fields - of at most 65521 elements). - - - ``'ntl'`` -- NTL using GF2X (only in characteristic 2). - - - ``'pari_ffelt'`` -- PARI's ``FFELT`` type (only for extension - fields). - - - ``'pari_mod'`` -- Older PARI implementation using ``POLMOD``s - (slower than ``'pari_ffelt'``, only for extension fields). - - - ``elem_cache`` -- cache all elements to avoid creation time - (default: order < 500) - - - ``check_irreducible`` -- verify that the polynomial modulus is - irreducible - - - ``proof`` -- bool (default: ``True``): if ``True``, use provable - primality test; otherwise only use pseudoprimality test. - - - ``args`` -- additional parameters passed to finite field - implementations - - - ``kwds`` -- additional keyword parameters passed to finite field - implementations - - ALIAS: You can also use ``GF`` instead of ``FiniteField`` -- they - are identical. - - EXAMPLES:: - - sage: k. = FiniteField(9); k - Finite Field in a of size 3^2 - sage: parent(a) - Finite Field in a of size 3^2 - sage: charpoly(a, 'y') - y^2 + 2*y + 2 - - We illustrate the proof flag. The following example would hang - for a very long time if we didn't use ``proof=False``. - - .. NOTE:: - - Magma only supports ``proof=False`` for making finite fields, - so falsely appears to be faster than Sage -- see :trac:`10975`. - - :: - - sage: k = FiniteField(10^1000 + 453, proof=False) - sage: k = FiniteField((10^1000 + 453)^2, 'a', proof=False) # long time -- about 5 seconds - - :: - - sage: F. = GF(5)[] - sage: K. = GF(5**5, name='a', modulus=x^5 - x +1 ) - sage: f = K.modulus(); f - x^5 + 4*x + 1 - sage: type(f) - - - By default, the given generator is not guaranteed to be primitive - (a generator of the multiplicative group), use - ``modulus="primitive"`` if you need this:: - - sage: K. = GF(5^40) - sage: a.multiplicative_order() - 4547473508864641189575195312 - sage: a.is_square() - True - sage: K. = GF(5^40, modulus="primitive") - sage: b.multiplicative_order() - 9094947017729282379150390624 - - The modulus must be irreducible:: - - sage: K. = GF(5**5, name='a', modulus=x^5 - x) - Traceback (most recent call last): - ... - ValueError: finite field modulus must be irreducible but it is not - - You can't accidentally fool the constructor into thinking the - modulus is irreducible when it is not, since it actually tests - irreducibility modulo `p`. Also, the modulus has to be of the - right degree (this is always checked):: - - sage: F. = QQ[] - sage: factor(x^5 + 2) - x^5 + 2 - sage: K. = GF(5^5, modulus=x^5 + 2) - Traceback (most recent call last): - ... - ValueError: finite field modulus must be irreducible but it is not - sage: K. = GF(5^5, modulus=x^3 + 3*x + 3, check_irreducible=False) - Traceback (most recent call last): - ... - ValueError: the degree of the modulus does not equal the degree of the field - - Any type which can be converted to the polynomial ring `GF(p)[x]` - is accepted as modulus:: - - sage: K. = GF(13^3, modulus=[1,0,0,2]) - sage: K. = GF(13^10, modulus=pari("ffinit(13,10)")) - sage: var('x') - x - sage: K. = GF(13^2, modulus=x^2 - 2) - sage: K. = GF(13^2, modulus=sin(x)) - Traceback (most recent call last): - ... - TypeError: unable to convert sin(x) to an integer - - If you wish to live dangerously, you can tell the constructor not - to test irreducibility using ``check_irreducible=False``, but this - can easily lead to crashes and hangs -- so do not do it unless you - know that the modulus really is irreducible! - - :: - - sage: K. = GF(5**2, name='a', modulus=x^2 + 2, check_irreducible=False) - - Even for prime fields, you can specify a modulus. This will not - change how Sage computes in this field, but it will change the - result of the :meth:`modulus` and :meth:`gen` methods:: - - sage: k. = GF(5, modulus="primitive") - sage: k.modulus() - x + 3 - sage: a - 2 - - The order of a finite field must be a prime power:: - - sage: GF(1) - Traceback (most recent call last): - ... - ValueError: the order of a finite field must be at least 2 - sage: GF(100) - Traceback (most recent call last): - ... - ValueError: the order of a finite field must be a prime power - - Finite fields with explicit random modulus are not cached:: - - sage: k. = GF(5**10, modulus='random') - sage: n. = GF(5**10, modulus='random') - sage: n is k - False - sage: GF(5**10, 'a') is GF(5**10, 'a') - True - - We check that various ways of creating the same finite field yield - the same object, which is cached:: - - sage: K = GF(7, 'a') - sage: L = GF(7, 'b') - sage: K is L # name is ignored for prime fields - True - sage: K is GF(7, modulus=K.modulus()) - True - sage: K = GF(4,'a'); K.modulus() - x^2 + x + 1 - sage: L = GF(4,'a', K.modulus()) - sage: K is L - True - sage: M = GF(4,'a', K.modulus().change_variable_name('y')) - sage: K is M - True - - You may print finite field elements as integers. This currently - only works if the order of field is `<2^{16}`, though:: - - sage: k. = GF(2^8, repr='int') - sage: a - 2 - - The following demonstrate coercions for finite fields using Conway - polynomials:: - - sage: k = GF(5^2, conway=True, prefix='z'); a = k.gen() - sage: l = GF(5^5, conway=True, prefix='z'); b = l.gen() - sage: a + b - 3*z10^5 + z10^4 + z10^2 + 3*z10 + 1 - - Note that embeddings are compatible in lattices of such finite - fields:: - - sage: m = GF(5^3, conway=True, prefix='z'); c = m.gen() - sage: (a+b)+c == a+(b+c) - True - sage: (a*b)*c == a*(b*c) - True - sage: from sage.categories.pushout import pushout - sage: n = pushout(k, l) - sage: o = pushout(l, m) - sage: q = pushout(n, o) - sage: q(o(b)) == q(n(b)) - True - - Another check that embeddings are defined properly:: - - sage: k = GF(3**10, conway=True, prefix='z') - sage: l = GF(3**20, conway=True, prefix='z') - sage: l(k.gen()**10) == l(k.gen())**10 - True - - Check that :trac:`16934` has been fixed:: - - sage: k1. = GF(17^14, impl="pari_ffelt") - sage: _ = a/2 - sage: k2. = GF(17^14, impl="pari_ffelt") - sage: k1 is k2 - True - - """ - def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, - impl=None, proof=None, check_irreducible=True, **kwds): - """ - EXAMPLES:: - - sage: GF.create_key_and_extra_args(9, 'a') - ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) - sage: GF.create_key_and_extra_args(9, 'a', foo='value') - ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) - """ - import sage.arith.all - from sage.structure.proof.all import WithProof, arithmetic - if proof is None: - proof = arithmetic() - with WithProof('arithmetic', proof): - order = Integer(order) - if order <= 1: - raise ValueError("the order of a finite field must be at least 2") - - if order.is_prime(): - p = order - n = Integer(1) - if impl is None: - impl = 'modn' - name = ('x',) # Ignore name - # Every polynomial of degree 1 is irreducible - check_irreducible = False - elif order.is_prime_power(): - if names is not None: - name = names - if name is not None: - name = normalize_names(1, name) - - p, n = order.factor()[0] - - # The following is a temporary solution that allows us - # to construct compatible systems of finite fields - # until algebraic closures of finite fields are - # implemented in Sage. It requires the user to - # specify two parameters: - # - # - `conway` -- boolean; if True, this field is - # constructed to fit in a compatible system using - # a Conway polynomial. - # - `prefix` -- a string used to generate names for - # automatically constructed finite fields - # - # See the docstring of FiniteFieldFactory for examples. - # - # Once algebraic closures of finite fields are - # implemented, this syntax should be superseded by - # something like the following: - # - # sage: Fpbar = GF(5).algebraic_closure('z') - # sage: F, e = Fpbar.subfield(3) # e is the embedding into Fpbar - # sage: F - # Finite field in z3 of size 5^3 - # - # This temporary solution only uses actual Conway - # polynomials (no pseudo-Conway polynomials), since - # pseudo-Conway polynomials are not unique, and until - # we have algebraic closures of finite fields, there - # is no good place to store a specific choice of - # pseudo-Conway polynomials. - if name is None: - if not ('conway' in kwds and kwds['conway']): - raise ValueError("parameter 'conway' is required if no name given") - if 'prefix' not in kwds: - raise ValueError("parameter 'prefix' is required if no name given") - name = kwds['prefix'] + str(n) - - if 'conway' in kwds and kwds['conway']: - from conway_polynomials import conway_polynomial - if 'prefix' not in kwds: - raise ValueError("a prefix must be specified if conway=True") - if modulus is not None: - raise ValueError("no modulus may be specified if conway=True") - # The following raises a RuntimeError if no polynomial is found. - modulus = conway_polynomial(p, n) - - if impl is None: - if order < zech_log_bound: - impl = 'givaro' - elif p == 2: - impl = 'ntl' - else: - impl = 'pari_ffelt' - else: - raise ValueError("the order of a finite field must be a prime power") - - # Determine modulus. - # For the 'modn' implementation, we use the following - # optimization which we also need to avoid an infinite loop: - # a modulus of None is a shorthand for x-1. - if modulus is not None or impl != 'modn': - R = PolynomialRing(FiniteField(p), 'x') - if modulus is None: - modulus = R.irreducible_element(n) - if isinstance(modulus, str): - # A string specifies an algorithm to find a suitable modulus. - if modulus == "default": - from sage.misc.superseded import deprecation - deprecation(16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)") - modulus = None - modulus = R.irreducible_element(n, algorithm=modulus) - else: - if sage.rings.polynomial.polynomial_element.is_Polynomial(modulus): - modulus = modulus.change_variable_name('x') - modulus = R(modulus).monic() - - if modulus.degree() != n: - raise ValueError("the degree of the modulus does not equal the degree of the field") - if check_irreducible and not modulus.is_irreducible(): - raise ValueError("finite field modulus must be irreducible but it is not") - # If modulus is x - 1 for impl="modn", set it to None - if impl == 'modn' and modulus[0] == -1: - modulus = None - - return (order, name, modulus, impl, str(kwds), p, n, proof), kwds - - def create_object(self, version, key, **kwds): - """ - EXAMPLES:: - - sage: K = GF(19) # indirect doctest - sage: TestSuite(K).run() - - We try to create finite fields with various implementations:: - - sage: k = GF(2, impl='modn') - sage: k = GF(2, impl='givaro') - sage: k = GF(2, impl='ntl') - sage: k = GF(2, impl='pari_ffelt') - Traceback (most recent call last): - ... - ValueError: the degree must be at least 2 - sage: k = GF(2, impl='pari_mod') - Traceback (most recent call last): - ... - ValueError: The size of the finite field must not be prime. - sage: k = GF(2, impl='supercalifragilisticexpialidocious') - Traceback (most recent call last): - ... - ValueError: no such finite field implementation: 'supercalifragilisticexpialidocious' - sage: k. = GF(2^15, impl='modn') - Traceback (most recent call last): - ... - ValueError: the 'modn' implementation requires a prime order - sage: k. = GF(2^15, impl='givaro') - sage: k. = GF(2^15, impl='ntl') - sage: k. = GF(2^15, impl='pari_ffelt') - sage: k. = GF(2^15, impl='pari_mod') - sage: k. = GF(3^60, impl='modn') - Traceback (most recent call last): - ... - ValueError: the 'modn' implementation requires a prime order - sage: k. = GF(3^60, impl='givaro') - Traceback (most recent call last): - ... - ValueError: q must be < 2^16 - sage: k. = GF(3^60, impl='ntl') - Traceback (most recent call last): - ... - ValueError: q must be a 2-power - sage: k. = GF(3^60, impl='pari_ffelt') - sage: k. = GF(3^60, impl='pari_mod') - """ - # IMPORTANT! If you add a new class to the list of classes - # that get cached by this factor object, then you *must* add - # the following method to that class in order to fully support - # pickling: - # - # def __reduce__(self): # and include good doctests, please! - # return self._factory_data[0].reduce_data(self) - # - # This is not in the base class for finite fields, since some finite - # fields need not be created using this factory object, e.g., residue - # class fields. - - if len(key) == 5: - # for backward compatibility of pickles (see trac 10975). - order, name, modulus, impl, _ = key - p, n = Integer(order).factor()[0] - proof = True - else: - order, name, modulus, impl, _, p, n, proof = key - - if impl == 'modn': - if n != 1: - raise ValueError("the 'modn' implementation requires a prime order") - from finite_field_prime_modn import FiniteField_prime_modn - # Using a check option here is probably a worthwhile - # compromise since this constructor is simple and used a - # huge amount. - K = FiniteField_prime_modn(order, check=False, modulus=modulus) - else: - # We have to do this with block so that the finite field - # constructors below will use the proof flag that was - # passed in when checking for primality, factoring, etc. - # Otherwise, we would have to complicate all of their - # constructors with check options. - from sage.structure.proof.all import WithProof - with WithProof('arithmetic', proof): - if impl == 'givaro': - repr = kwds.get('repr', 'poly') - elem_cache = kwds.get('elem_cache', order < 500) - K = FiniteField_givaro(order, name, modulus, repr=repr, cache=elem_cache) - elif impl == 'ntl': - from finite_field_ntl_gf2e import FiniteField_ntl_gf2e - K = FiniteField_ntl_gf2e(order, name, modulus) - elif impl == 'pari_ffelt': - from finite_field_pari_ffelt import FiniteField_pari_ffelt - K = FiniteField_pari_ffelt(p, modulus, name) - elif (impl == 'pari_mod' - or impl == 'pari'): # for unpickling old pickles - # This implementation is deprecated, a warning will - # be given when this field is created. - # See http://trac.sagemath.org/ticket/17297 - from finite_field_ext_pari import FiniteField_ext_pari - K = FiniteField_ext_pari(order, name, modulus) - else: - raise ValueError("no such finite field implementation: %r" % impl) - - # Temporary; see create_key_and_extra_args() above. - if 'prefix' in kwds: - K._prefix = kwds['prefix'] - - return K - - -GF = FiniteField = FiniteFieldFactory("FiniteField") - - -def is_PrimeFiniteField(x): - """ - Returns True if x is a prime finite field. - - EXAMPLES:: - - sage: from sage.rings.finite_rings.constructor import is_PrimeFiniteField - sage: is_PrimeFiniteField(QQ) - False - sage: is_PrimeFiniteField(GF(7)) - True - sage: is_PrimeFiniteField(GF(7^2,'a')) - False - sage: is_PrimeFiniteField(GF(next_prime(10^90,proof=False))) - True - """ - from finite_field_prime_modn import FiniteField_prime_modn - from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic - - return isinstance(x, FiniteField_prime_modn) or \ - (isinstance(x, FiniteField_generic) and x.degree() == 1) - -zech_log_bound = 2**16 +from finite_field_constructor import * diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index e9059d29524..03a58343135 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -11,7 +11,7 @@ """ from sage.misc.fast_methods import WithEqualityById from sage.structure.sage_object import SageObject -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField import sage.databases.conway def conway_polynomial(p, n): diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 96690cbae0c..cec0e95ad95 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -63,15 +63,13 @@ from element_pari_ffelt import FiniteFieldElement_pari_ffelt from sage.structure.sage_object cimport SageObject import operator import sage.arith.all -import constructor as finite_field +import finite_field_constructor as finite_field import sage.interfaces.gap from sage.libs.pari.all import pari from sage.libs.pari.gen cimport gen from sage.structure.parent cimport Parent -from sage.structure.parent_base cimport ParentWithBase -from sage.structure.parent_gens cimport ParentWithGens from sage.misc.superseded import deprecated_function_alias @@ -112,7 +110,7 @@ cdef void late_import(): import sage.databases.conway ConwayPolynomials = sage.databases.conway.ConwayPolynomials - import sage.rings.finite_rings.constructor + import sage.rings.finite_rings.finite_field_constructor conway_polynomial = sage.rings.finite_rings.conway_polynomials.conway_polynomial import sage.rings.polynomial.multi_polynomial_element diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 19127de725a..9951f037cc7 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -29,8 +29,6 @@ from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, ModuleElement, RingElement from sage.structure.parent cimport Parent -from sage.structure.parent_base cimport ParentWithBase -from sage.structure.parent_gens cimport ParentWithGens from sage.rings.ring cimport Ring @@ -114,8 +112,8 @@ cdef int late_import() except -1: import sage.modules.free_module_element FreeModuleElement = sage.modules.free_module_element.FreeModuleElement - import sage.rings.finite_rings.constructor - GF = sage.rings.finite_rings.constructor.FiniteField + import sage.rings.finite_rings.finite_field_constructor + GF = sage.rings.finite_rings.finite_field_constructor.FiniteField GF2 = GF(2) GF2_0 = GF2(0) GF2_1 = GF2(1) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index fb2a990a9f5..12c4a7cdc43 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -862,7 +862,7 @@ cdef class FiniteField(Field): pass from sage.rings.all import PolynomialRing - from constructor import GF + from finite_field_constructor import GF R = PolynomialRing(GF(self.characteristic()), 'x') self._modulus = R((-1,1)) # Polynomial x - 1 return self._modulus @@ -979,7 +979,7 @@ cdef class FiniteField(Field): Univariate Polynomial Ring in alpha over Finite Field of size 3 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF if variable_name is None and self.__polynomial_ring is not None: return self.__polynomial_ring @@ -1172,7 +1172,7 @@ cdef class FiniteField(Field): sage: F.extension(int(3), 'aa') Finite Field in aa of size 2^12 """ - from constructor import GF + from finite_field_constructor import GF from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.integer import Integer if name is None and names is not None: @@ -1255,7 +1255,7 @@ cdef class FiniteField(Field): Defn: z21 |--> a)] """ from sage.rings.integer import Integer - from constructor import GF + from finite_field_constructor import GF p = self.characteristic() n = self.degree() if degree != 0: diff --git a/src/sage/rings/finite_rings/finite_field_constructor.py b/src/sage/rings/finite_rings/finite_field_constructor.py new file mode 100644 index 00000000000..7e3647dc5b6 --- /dev/null +++ b/src/sage/rings/finite_rings/finite_field_constructor.py @@ -0,0 +1,668 @@ +r""" +Finite Fields + +Sage supports arithmetic in finite prime and extension fields. +Several implementation for prime fields are implemented natively in +Sage for several sizes of primes `p`. These implementations +are + + +- ``sage.rings.finite_rings.integer_mod.IntegerMod_int``, + +- ``sage.rings.finite_rings.integer_mod.IntegerMod_int64``, and + +- ``sage.rings.finite_rings.integer_mod.IntegerMod_gmp``. + + +Small extension fields of cardinality `< 2^{16}` are +implemented using tables of Zech logs via the Givaro C++ library +(``sage.rings.finite_rings.finite_field_givaro.FiniteField_givaro``). +While this representation is very fast it is limited to finite +fields of small cardinality. Larger finite extension fields of +order `q >= 2^{16}` are internally represented as +polynomials over smaller finite prime fields. If the +characteristic of such a field is 2 then NTL is used internally to +represent the field +(``sage.rings.finite_rings.finite_field_ntl_gf2e.FiniteField_ntl_gf2e``). +In all other case the PARI C library is used +(``sage.rings.finite_rings.finite_field_pari_ffelt.FiniteField_pari_ffelt``). + +However, this distinction is internal only and the user usually +does not have to worry about it because consistency across all +implementations is aimed for. In all extension field +implementations the user may either specify a minimal polynomial or +leave the choice to Sage. + +For small finite fields the default choice are Conway polynomials. + +The Conway polynomial `C_n` is the lexicographically first +monic irreducible, primitive polynomial of degree `n` over +`GF(p)` with the property that for a root `\alpha` +of `C_n` we have that +`\beta= +\alpha^{(p^n - 1)/(p^m - 1)}` is a root of +`C_m` for all `m` dividing `n`. Sage +contains a database of Conway polynomials which also can be queried +independently of finite field construction. + +While Sage supports basic arithmetic in finite fields some more +advanced features for computing with finite fields are still not +implemented. For instance, Sage does not calculate embeddings of +finite fields yet. + +EXAMPLES:: + + sage: k = GF(5); type(k) + + +:: + + sage: k = GF(5^2,'c'); type(k) + + +:: + + sage: k = GF(2^16,'c'); type(k) + + +:: + + sage: k = GF(3^16,'c'); type(k) + + +Finite Fields support iteration, starting with 0. + +:: + + sage: k = GF(9, 'a') + sage: for i,x in enumerate(k): print i,x + 0 0 + 1 a + 2 a + 1 + 3 2*a + 1 + 4 2 + 5 2*a + 6 2*a + 2 + 7 a + 2 + 8 1 + sage: for a in GF(5): + ... print a + 0 + 1 + 2 + 3 + 4 + +We output the base rings of several finite fields. + +:: + + sage: k = GF(3); type(k) + + sage: k.base_ring() + Finite Field of size 3 + +:: + + sage: k = GF(9,'alpha'); type(k) + + sage: k.base_ring() + Finite Field of size 3 + +:: + + sage: k = GF(3^40,'b'); type(k) + + sage: k.base_ring() + Finite Field of size 3 + +Further examples:: + + sage: GF(2).is_field() + True + sage: GF(next_prime(10^20)).is_field() + True + sage: GF(19^20,'a').is_field() + True + sage: GF(8,'a').is_field() + True + +AUTHORS: + +- William Stein: initial version + +- Robert Bradshaw: prime field implementation + +- Martin Albrecht: Givaro and ntl.GF2E implementations +""" + +#***************************************************************************** +# Copyright (C) 2006 William Stein +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# This code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +#***************************************************************************** + +import random + +from sage.rings.finite_rings.finite_field_base import is_FiniteField +from sage.structure.category_object import normalize_names + +from sage.rings.integer import Integer + +import sage.rings.polynomial.polynomial_element as polynomial_element +import sage.rings.polynomial.multi_polynomial_element as multi_polynomial_element +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +# We don't late import this because this means trouble with the Givaro library +# On a Macbook Pro OSX 10.5.8, this manifests as a Bus Error on exiting Sage. +# TODO: figure out why +from finite_field_givaro import FiniteField_givaro + +import sage.interfaces.gap + +from sage.structure.factory import UniqueFactory + +class FiniteFieldFactory(UniqueFactory): + """ + Return the globally unique finite field of given order with + generator labeled by the given name and possibly with given + modulus. + + INPUT: + + - ``order`` -- a prime power + + - ``name`` -- string; must be specified unless ``order`` is prime. + + - ``modulus`` -- (optional) either a defining polynomial for the + field, or a string specifying an algorithm to use to generate + such a polynomial. If ``modulus`` is a string, it is passed to + :meth:`~sage.rings.polynomial.irreducible_element()` as the + parameter ``algorithm``; see there for the permissible values of + this parameter. In particular, you can specify + ``modulus="primitive"`` to get a primitive polynomial. + + - ``impl`` -- (optional) a string specifying the implementation of + the finite field. Possible values are: + + - ``'modn'`` -- ring of integers modulo `p` (only for prime + fields). + + - ``'givaro'`` -- Givaro, which uses Zech logs (only for fields + of at most 65521 elements). + + - ``'ntl'`` -- NTL using GF2X (only in characteristic 2). + + - ``'pari_ffelt'`` -- PARI's ``FFELT`` type (only for extension + fields). + + - ``'pari_mod'`` -- Older PARI implementation using ``POLMOD``s + (slower than ``'pari_ffelt'``, only for extension fields). + + - ``elem_cache`` -- cache all elements to avoid creation time + (default: order < 500) + + - ``check_irreducible`` -- verify that the polynomial modulus is + irreducible + + - ``proof`` -- bool (default: ``True``): if ``True``, use provable + primality test; otherwise only use pseudoprimality test. + + - ``args`` -- additional parameters passed to finite field + implementations + + - ``kwds`` -- additional keyword parameters passed to finite field + implementations + + ALIAS: You can also use ``GF`` instead of ``FiniteField`` -- they + are identical. + + EXAMPLES:: + + sage: k. = FiniteField(9); k + Finite Field in a of size 3^2 + sage: parent(a) + Finite Field in a of size 3^2 + sage: charpoly(a, 'y') + y^2 + 2*y + 2 + + We illustrate the proof flag. The following example would hang + for a very long time if we didn't use ``proof=False``. + + .. NOTE:: + + Magma only supports ``proof=False`` for making finite fields, + so falsely appears to be faster than Sage -- see :trac:`10975`. + + :: + + sage: k = FiniteField(10^1000 + 453, proof=False) + sage: k = FiniteField((10^1000 + 453)^2, 'a', proof=False) # long time -- about 5 seconds + + :: + + sage: F. = GF(5)[] + sage: K. = GF(5**5, name='a', modulus=x^5 - x +1 ) + sage: f = K.modulus(); f + x^5 + 4*x + 1 + sage: type(f) + + + By default, the given generator is not guaranteed to be primitive + (a generator of the multiplicative group), use + ``modulus="primitive"`` if you need this:: + + sage: K. = GF(5^40) + sage: a.multiplicative_order() + 4547473508864641189575195312 + sage: a.is_square() + True + sage: K. = GF(5^40, modulus="primitive") + sage: b.multiplicative_order() + 9094947017729282379150390624 + + The modulus must be irreducible:: + + sage: K. = GF(5**5, name='a', modulus=x^5 - x) + Traceback (most recent call last): + ... + ValueError: finite field modulus must be irreducible but it is not + + You can't accidentally fool the constructor into thinking the + modulus is irreducible when it is not, since it actually tests + irreducibility modulo `p`. Also, the modulus has to be of the + right degree (this is always checked):: + + sage: F. = QQ[] + sage: factor(x^5 + 2) + x^5 + 2 + sage: K. = GF(5^5, modulus=x^5 + 2) + Traceback (most recent call last): + ... + ValueError: finite field modulus must be irreducible but it is not + sage: K. = GF(5^5, modulus=x^3 + 3*x + 3, check_irreducible=False) + Traceback (most recent call last): + ... + ValueError: the degree of the modulus does not equal the degree of the field + + Any type which can be converted to the polynomial ring `GF(p)[x]` + is accepted as modulus:: + + sage: K. = GF(13^3, modulus=[1,0,0,2]) + sage: K. = GF(13^10, modulus=pari("ffinit(13,10)")) + sage: var('x') + x + sage: K. = GF(13^2, modulus=x^2 - 2) + sage: K. = GF(13^2, modulus=sin(x)) + Traceback (most recent call last): + ... + TypeError: unable to convert sin(x) to an integer + + If you wish to live dangerously, you can tell the constructor not + to test irreducibility using ``check_irreducible=False``, but this + can easily lead to crashes and hangs -- so do not do it unless you + know that the modulus really is irreducible! + + :: + + sage: K. = GF(5**2, name='a', modulus=x^2 + 2, check_irreducible=False) + + Even for prime fields, you can specify a modulus. This will not + change how Sage computes in this field, but it will change the + result of the :meth:`modulus` and :meth:`gen` methods:: + + sage: k. = GF(5, modulus="primitive") + sage: k.modulus() + x + 3 + sage: a + 2 + + The order of a finite field must be a prime power:: + + sage: GF(1) + Traceback (most recent call last): + ... + ValueError: the order of a finite field must be at least 2 + sage: GF(100) + Traceback (most recent call last): + ... + ValueError: the order of a finite field must be a prime power + + Finite fields with explicit random modulus are not cached:: + + sage: k. = GF(5**10, modulus='random') + sage: n. = GF(5**10, modulus='random') + sage: n is k + False + sage: GF(5**10, 'a') is GF(5**10, 'a') + True + + We check that various ways of creating the same finite field yield + the same object, which is cached:: + + sage: K = GF(7, 'a') + sage: L = GF(7, 'b') + sage: K is L # name is ignored for prime fields + True + sage: K is GF(7, modulus=K.modulus()) + True + sage: K = GF(4,'a'); K.modulus() + x^2 + x + 1 + sage: L = GF(4,'a', K.modulus()) + sage: K is L + True + sage: M = GF(4,'a', K.modulus().change_variable_name('y')) + sage: K is M + True + + You may print finite field elements as integers. This currently + only works if the order of field is `<2^{16}`, though:: + + sage: k. = GF(2^8, repr='int') + sage: a + 2 + + The following demonstrate coercions for finite fields using Conway + polynomials:: + + sage: k = GF(5^2, conway=True, prefix='z'); a = k.gen() + sage: l = GF(5^5, conway=True, prefix='z'); b = l.gen() + sage: a + b + 3*z10^5 + z10^4 + z10^2 + 3*z10 + 1 + + Note that embeddings are compatible in lattices of such finite + fields:: + + sage: m = GF(5^3, conway=True, prefix='z'); c = m.gen() + sage: (a+b)+c == a+(b+c) + True + sage: (a*b)*c == a*(b*c) + True + sage: from sage.categories.pushout import pushout + sage: n = pushout(k, l) + sage: o = pushout(l, m) + sage: q = pushout(n, o) + sage: q(o(b)) == q(n(b)) + True + + Another check that embeddings are defined properly:: + + sage: k = GF(3**10, conway=True, prefix='z') + sage: l = GF(3**20, conway=True, prefix='z') + sage: l(k.gen()**10) == l(k.gen())**10 + True + + Check that :trac:`16934` has been fixed:: + + sage: k1. = GF(17^14, impl="pari_ffelt") + sage: _ = a/2 + sage: k2. = GF(17^14, impl="pari_ffelt") + sage: k1 is k2 + True + + """ + def create_key_and_extra_args(self, order, name=None, modulus=None, names=None, + impl=None, proof=None, check_irreducible=True, **kwds): + """ + EXAMPLES:: + + sage: GF.create_key_and_extra_args(9, 'a') + ((9, ('a',), x^2 + 2*x + 2, 'givaro', '{}', 3, 2, True), {}) + sage: GF.create_key_and_extra_args(9, 'a', foo='value') + ((9, ('a',), x^2 + 2*x + 2, 'givaro', "{'foo': 'value'}", 3, 2, True), {'foo': 'value'}) + """ + import sage.arith.all + from sage.structure.proof.all import WithProof, arithmetic + if proof is None: + proof = arithmetic() + with WithProof('arithmetic', proof): + order = Integer(order) + if order <= 1: + raise ValueError("the order of a finite field must be at least 2") + + if order.is_prime(): + p = order + n = Integer(1) + if impl is None: + impl = 'modn' + name = ('x',) # Ignore name + # Every polynomial of degree 1 is irreducible + check_irreducible = False + elif order.is_prime_power(): + if names is not None: + name = names + if name is not None: + name = normalize_names(1, name) + + p, n = order.factor()[0] + + # The following is a temporary solution that allows us + # to construct compatible systems of finite fields + # until algebraic closures of finite fields are + # implemented in Sage. It requires the user to + # specify two parameters: + # + # - `conway` -- boolean; if True, this field is + # constructed to fit in a compatible system using + # a Conway polynomial. + # - `prefix` -- a string used to generate names for + # automatically constructed finite fields + # + # See the docstring of FiniteFieldFactory for examples. + # + # Once algebraic closures of finite fields are + # implemented, this syntax should be superseded by + # something like the following: + # + # sage: Fpbar = GF(5).algebraic_closure('z') + # sage: F, e = Fpbar.subfield(3) # e is the embedding into Fpbar + # sage: F + # Finite field in z3 of size 5^3 + # + # This temporary solution only uses actual Conway + # polynomials (no pseudo-Conway polynomials), since + # pseudo-Conway polynomials are not unique, and until + # we have algebraic closures of finite fields, there + # is no good place to store a specific choice of + # pseudo-Conway polynomials. + if name is None: + if not ('conway' in kwds and kwds['conway']): + raise ValueError("parameter 'conway' is required if no name given") + if 'prefix' not in kwds: + raise ValueError("parameter 'prefix' is required if no name given") + name = kwds['prefix'] + str(n) + + if 'conway' in kwds and kwds['conway']: + from conway_polynomials import conway_polynomial + if 'prefix' not in kwds: + raise ValueError("a prefix must be specified if conway=True") + if modulus is not None: + raise ValueError("no modulus may be specified if conway=True") + # The following raises a RuntimeError if no polynomial is found. + modulus = conway_polynomial(p, n) + + if impl is None: + if order < zech_log_bound: + impl = 'givaro' + elif p == 2: + impl = 'ntl' + else: + impl = 'pari_ffelt' + else: + raise ValueError("the order of a finite field must be a prime power") + + # Determine modulus. + # For the 'modn' implementation, we use the following + # optimization which we also need to avoid an infinite loop: + # a modulus of None is a shorthand for x-1. + if modulus is not None or impl != 'modn': + R = PolynomialRing(FiniteField(p), 'x') + if modulus is None: + modulus = R.irreducible_element(n) + if isinstance(modulus, str): + # A string specifies an algorithm to find a suitable modulus. + if modulus == "default": + from sage.misc.superseded import deprecation + deprecation(16983, "the modulus 'default' is deprecated, use modulus=None instead (which is the default)") + modulus = None + modulus = R.irreducible_element(n, algorithm=modulus) + else: + if sage.rings.polynomial.polynomial_element.is_Polynomial(modulus): + modulus = modulus.change_variable_name('x') + modulus = R(modulus).monic() + + if modulus.degree() != n: + raise ValueError("the degree of the modulus does not equal the degree of the field") + if check_irreducible and not modulus.is_irreducible(): + raise ValueError("finite field modulus must be irreducible but it is not") + # If modulus is x - 1 for impl="modn", set it to None + if impl == 'modn' and modulus[0] == -1: + modulus = None + + return (order, name, modulus, impl, str(kwds), p, n, proof), kwds + + def create_object(self, version, key, **kwds): + """ + EXAMPLES:: + + sage: K = GF(19) # indirect doctest + sage: TestSuite(K).run() + + We try to create finite fields with various implementations:: + + sage: k = GF(2, impl='modn') + sage: k = GF(2, impl='givaro') + sage: k = GF(2, impl='ntl') + sage: k = GF(2, impl='pari_ffelt') + Traceback (most recent call last): + ... + ValueError: the degree must be at least 2 + sage: k = GF(2, impl='pari_mod') + Traceback (most recent call last): + ... + ValueError: The size of the finite field must not be prime. + sage: k = GF(2, impl='supercalifragilisticexpialidocious') + Traceback (most recent call last): + ... + ValueError: no such finite field implementation: 'supercalifragilisticexpialidocious' + sage: k. = GF(2^15, impl='modn') + Traceback (most recent call last): + ... + ValueError: the 'modn' implementation requires a prime order + sage: k. = GF(2^15, impl='givaro') + sage: k. = GF(2^15, impl='ntl') + sage: k. = GF(2^15, impl='pari_ffelt') + sage: k. = GF(2^15, impl='pari_mod') + sage: k. = GF(3^60, impl='modn') + Traceback (most recent call last): + ... + ValueError: the 'modn' implementation requires a prime order + sage: k. = GF(3^60, impl='givaro') + Traceback (most recent call last): + ... + ValueError: q must be < 2^16 + sage: k. = GF(3^60, impl='ntl') + Traceback (most recent call last): + ... + ValueError: q must be a 2-power + sage: k. = GF(3^60, impl='pari_ffelt') + sage: k. = GF(3^60, impl='pari_mod') + """ + # IMPORTANT! If you add a new class to the list of classes + # that get cached by this factor object, then you *must* add + # the following method to that class in order to fully support + # pickling: + # + # def __reduce__(self): # and include good doctests, please! + # return self._factory_data[0].reduce_data(self) + # + # This is not in the base class for finite fields, since some finite + # fields need not be created using this factory object, e.g., residue + # class fields. + + if len(key) == 5: + # for backward compatibility of pickles (see trac 10975). + order, name, modulus, impl, _ = key + p, n = Integer(order).factor()[0] + proof = True + else: + order, name, modulus, impl, _, p, n, proof = key + + if impl == 'modn': + if n != 1: + raise ValueError("the 'modn' implementation requires a prime order") + from finite_field_prime_modn import FiniteField_prime_modn + # Using a check option here is probably a worthwhile + # compromise since this constructor is simple and used a + # huge amount. + K = FiniteField_prime_modn(order, check=False, modulus=modulus) + else: + # We have to do this with block so that the finite field + # constructors below will use the proof flag that was + # passed in when checking for primality, factoring, etc. + # Otherwise, we would have to complicate all of their + # constructors with check options. + from sage.structure.proof.all import WithProof + with WithProof('arithmetic', proof): + if impl == 'givaro': + repr = kwds.get('repr', 'poly') + elem_cache = kwds.get('elem_cache', order < 500) + K = FiniteField_givaro(order, name, modulus, repr=repr, cache=elem_cache) + elif impl == 'ntl': + from finite_field_ntl_gf2e import FiniteField_ntl_gf2e + K = FiniteField_ntl_gf2e(order, name, modulus) + elif impl == 'pari_ffelt': + from finite_field_pari_ffelt import FiniteField_pari_ffelt + K = FiniteField_pari_ffelt(p, modulus, name) + elif (impl == 'pari_mod' + or impl == 'pari'): # for unpickling old pickles + # This implementation is deprecated, a warning will + # be given when this field is created. + # See http://trac.sagemath.org/ticket/17297 + from finite_field_ext_pari import FiniteField_ext_pari + K = FiniteField_ext_pari(order, name, modulus) + else: + raise ValueError("no such finite field implementation: %r" % impl) + + # Temporary; see create_key_and_extra_args() above. + if 'prefix' in kwds: + K._prefix = kwds['prefix'] + + return K + + +GF = FiniteField = FiniteFieldFactory("FiniteField") + + +def is_PrimeFiniteField(x): + """ + Returns True if x is a prime finite field. + + EXAMPLES:: + + sage: from sage.rings.finite_rings.finite_field_constructor import is_PrimeFiniteField + sage: is_PrimeFiniteField(QQ) + False + sage: is_PrimeFiniteField(GF(7)) + True + sage: is_PrimeFiniteField(GF(7^2,'a')) + False + sage: is_PrimeFiniteField(GF(next_prime(10^90,proof=False))) + True + """ + from finite_field_prime_modn import FiniteField_prime_modn + from sage.rings.finite_rings.finite_field_base import FiniteField as FiniteField_generic + + return isinstance(x, FiniteField_prime_modn) or \ + (isinstance(x, FiniteField_generic) and x.degree() == 1) + +zech_log_bound = 2**16 diff --git a/src/sage/rings/finite_rings/finite_field_ext_pari.py b/src/sage/rings/finite_rings/finite_field_ext_pari.py index b2c60d49ead..1eeaf4284ce 100644 --- a/src/sage/rings/finite_rings/finite_field_ext_pari.py +++ b/src/sage/rings/finite_rings/finite_field_ext_pari.py @@ -176,7 +176,7 @@ def __init__(self, q, name, modulus=None): deprecation(17297, 'The "pari_mod" finite field implementation is deprecated') if element_ext_pari.dynamic_FiniteField_ext_pariElement is None: element_ext_pari._late_import() - from constructor import FiniteField as GF + from finite_field_constructor import FiniteField as GF q = integer.Integer(q) if q < 2: raise ArithmeticError("q must be a prime power") diff --git a/src/sage/rings/finite_rings/finite_field_givaro.py b/src/sage/rings/finite_rings/finite_field_givaro.py index d96fbf75299..27c0dab749a 100644 --- a/src/sage/rings/finite_rings/finite_field_givaro.py +++ b/src/sage/rings/finite_rings/finite_field_givaro.py @@ -146,7 +146,7 @@ def __init__(self, q, name="a", modulus=None, repr="poly", cache=False): if q >= 1<<16: raise ValueError("q must be < 2^16") - from constructor import GF + from finite_field_constructor import GF FiniteField.__init__(self, GF(p), name, normalize=False) self._kwargs['repr'] = repr @@ -441,7 +441,7 @@ def prime_subfield(self): try: return self._prime_subfield except AttributeError: - from constructor import GF + from finite_field_constructor import GF self._prime_subfield = GF(self.characteristic()) return self._prime_subfield diff --git a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py index 8946bf74662..db4044763f4 100644 --- a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py +++ b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py @@ -54,8 +54,8 @@ def late_import(): import sage.rings.finite_rings.element_ntl_gf2e Cache_ntl_gf2e = sage.rings.finite_rings.element_ntl_gf2e.Cache_ntl_gf2e - import sage.rings.finite_rings.constructor - GF = sage.rings.finite_rings.constructor.GF + import sage.rings.finite_rings.finite_field_constructor + GF = sage.rings.finite_rings.finite_field_constructor.GF GF2 = GF(2) import sage.rings.polynomial.polynomial_element diff --git a/src/sage/rings/finite_rings/finite_field_pari_ffelt.py b/src/sage/rings/finite_rings/finite_field_pari_ffelt.py index dbc5d8e45af..f11802b8507 100644 --- a/src/sage/rings/finite_rings/finite_field_pari_ffelt.py +++ b/src/sage/rings/finite_rings/finite_field_pari_ffelt.py @@ -19,7 +19,7 @@ from element_pari_ffelt import FiniteFieldElement_pari_ffelt from finite_field_base import FiniteField -from constructor import GF +from finite_field_constructor import GF class FiniteField_pari_ffelt(FiniteField): """ diff --git a/src/sage/rings/finite_rings/finite_field_prime_modn.py b/src/sage/rings/finite_rings/finite_field_prime_modn.py index 1dcee12e521..534ac5e377a 100644 --- a/src/sage/rings/finite_rings/finite_field_prime_modn.py +++ b/src/sage/rings/finite_rings/finite_field_prime_modn.py @@ -185,7 +185,7 @@ def polynomial(self, name=None): try: return self.__polynomial[name] except AttributeError: - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField R = FiniteField(self.characteristic())[name] f = self[name]([0,1]) try: diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 1ca9fd576c1..97bc170e5d5 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -109,7 +109,7 @@ from sage.structure.element cimport Element from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.morphism cimport RingHomomorphism, RingHomomorphism_im_gens, FrobeniusEndomorphism_generic -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.map cimport Section from sage.categories.morphism cimport Morphism diff --git a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx index cbb1f0d3655..c3460440d11 100644 --- a/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field_givaro.pyx @@ -25,7 +25,7 @@ AUTHOR: #**************************************************************************** -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField from hom_finite_field cimport SectionFiniteFieldHomomorphism_generic from hom_finite_field cimport FiniteFieldHomomorphism_generic diff --git a/src/sage/rings/finite_rings/integer_mod.pxd b/src/sage/rings/finite_rings/integer_mod.pxd index 7375b2223a0..9471aa6add6 100644 --- a/src/sage/rings/finite_rings/integer_mod.pxd +++ b/src/sage/rings/finite_rings/integer_mod.pxd @@ -31,7 +31,6 @@ cdef class IntegerMod_int(IntegerMod_abstract): cdef int_fast32_t get_int_value(IntegerMod_int self) cdef IntegerMod_int _new_c(self, int_fast32_t value) cdef shift(IntegerMod_int self, int k) - #cdef Element _make_new_with_parent_c(self, ParentWithBase parent) cdef class IntegerMod_int64(IntegerMod_abstract): cdef int_fast64_t ivalue diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 2b73ef60696..4e252aa9993 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -984,8 +984,8 @@ cdef class IntegerMod_abstract(FiniteRingElement): R = self.parent()['x'] modulus = R.gen()**2 - R(self) if self._parent.is_field(): - import constructor - Q = constructor.FiniteField(self.__modulus.sageInteger**2, y, modulus) + from finite_field_constructor import FiniteField + Q = FiniteField(self.__modulus.sageInteger**2, y, modulus) else: R = self.parent()['x'] Q = R.quotient(modulus, names=(y,)) @@ -1652,21 +1652,16 @@ cdef class IntegerMod_abstract(FiniteRingElement): return infinity return r - def __floordiv__(self, other): + cpdef RingElement _floordiv_(self, RingElement right): """ Exact division for prime moduli, for compatibility with other fields. - EXAMPLES: - sage: GF(7)(3) // GF(7)(5) - 2 + EXAMPLES:: + + sage: GF(7)(3) // 5 + 2 """ - # needs to be rewritten for coercion - if other.parent() is not self.parent(): - other = self.parent().coerce(other) - if self.parent().is_field(): - return self / other - else: - raise TypeError, "Floor division not defined for non-prime modulus" + return self._mul_(~right) def _repr_(self): return str(self.lift()) @@ -2195,13 +2190,6 @@ cdef class IntegerMod_int(IntegerMod_abstract): z = sage.rings.integer_ring.Z(value) self.set_from_mpz(z.value) - def _make_new_with_parent_c(self, parent): #ParentWithBase parent): - cdef IntegerMod_int x = IntegerMod_int.__new__(IntegerMod_int) - x._parent = parent - x.__modulus = parent._pyx_order - x.ivalue = self.ivalue - return x - cdef IntegerMod_int _new_c(self, int_fast32_t value): if self.__modulus.table is not None: return self.__modulus.lookup(value) diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 223ba3f43de..2b370f98cdf 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -62,13 +62,11 @@ import sage.misc.prandom as random from sage.arith.all import factor, primitive_root, CRT_basis -import sage.rings.commutative_ring as commutative_ring import sage.rings.ring as ring import integer_mod import sage.rings.integer as integer import sage.rings.integer_ring as integer_ring import sage.rings.quotient_ring as quotient_ring -from sage.structure.parent_gens import ParentWithGens from sage.libs.pari.all import pari, PariError @@ -124,7 +122,7 @@ class IntegerModFactory(UniqueFactory): Testing whether a quotient ring `\ZZ / n\ZZ` is a field can of course be very costly. By default, it is not tested whether `n` is prime or not, in contrast to - :func:`~sage.rings.finite_rings.constructor.GF`. If the user + :func:`~sage.rings.finite_rings.finite_field_constructor.GF`. If the user is sure that the modulus is prime and wants to avoid a primality test, (s)he can provide ``category=Fields()`` when constructing the quotient ring, and then the result will behave like a field. @@ -781,8 +779,8 @@ def field(self): except AttributeError: if not self.is_field(): raise ValueError("self must be a field") - import constructor - k = constructor.FiniteField(self.order()) + import finite_field_constructor + k = finite_field_constructor.FiniteField(self.order()) self.__field = k return k @@ -1419,7 +1417,7 @@ def unit_group(self, algorithm='sage'): sage: H = A.unit_group(algorithm='pari'); H Multiplicative Abelian group isomorphic to C4 x C2 x C2 sage: H.gens_values() - (17, 21, 11) + (17, 31, 21) sage: A = Zmod(192) sage: G = A.unit_group(); G @@ -1429,7 +1427,7 @@ def unit_group(self, algorithm='sage'): sage: H = A.unit_group(algorithm='pari'); H Multiplicative Abelian group isomorphic to C16 x C2 x C2 sage: H.gens_values() - (133, 31, 65) + (133, 127, 65) In the following examples, the cyclic factors are not even isomorphic:: @@ -1507,8 +1505,8 @@ def random_element(self, bound=None): sage: R.random_element(2) in [R(16), R(17), R(0), R(1), R(2)] True """ - if not (bound is None): - return commutative_ring.CommutativeRing.random_element(self, bound) + if bound is not None: + return ring.CommutativeRing.random_element(self, bound) a = random.randint(0,self.order()-1) return self(a) diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index 6c8fb487ce0..adb81039791 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -152,7 +152,7 @@ from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.categories.homset import Hom from sage.rings.all import ZZ, QQ, Integers -from sage.rings.finite_rings.constructor import zech_log_bound, FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import zech_log_bound, FiniteField as GF from sage.rings.finite_rings.finite_field_givaro import FiniteField_givaro from sage.rings.finite_rings.finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 36932590f97..98698e1af69 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -31,7 +31,6 @@ import sage.misc.latex as latex import sage.rings.ring -import commutative_ring from sage.structure.element import MonoidElement from sage.interfaces.singular import singular as singular_default import sage.rings.infinity @@ -189,7 +188,7 @@ def Ideal(*args, **kwds): R = first gens = args[1:] - if not commutative_ring.is_CommutativeRing(R): + if not isinstance(R, sage.rings.ring.CommutativeRing): raise TypeError("R must be a commutative ring") return R.ideal(*gens, **kwds) diff --git a/src/sage/rings/ideal_monoid.py b/src/sage/rings/ideal_monoid.py index b1d9a85a86e..42a3fdf581e 100644 --- a/src/sage/rings/ideal_monoid.py +++ b/src/sage/rings/ideal_monoid.py @@ -2,8 +2,6 @@ Monoid of ideals in a commutative ring """ -from commutative_ring import is_CommutativeRing -#from sage.structure.parent_base import ParentWithBase from sage.structure.parent import Parent import sage.rings.integer_ring import ideal diff --git a/src/sage/rings/infinity.py b/src/sage/rings/infinity.py index 64b5d78c147..7f4b3dedea9 100644 --- a/src/sage/rings/infinity.py +++ b/src/sage/rings/infinity.py @@ -207,8 +207,18 @@ [(+Infinity)] """ +#***************************************************************************** +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from sys import maxsize from sage.rings.ring import Ring -from sage.structure.element import RingElement, InfinityElement, PlusInfinityElement, MinusInfinityElement +from sage.structure.element import RingElement, InfinityElement from sage.structure.parent_gens import ParentWithGens import sage.rings.integer import sage.rings.rational @@ -862,6 +872,16 @@ def __init__(self): """ InfinityElement.__init__(self, UnsignedInfinityRing) + def __hash__(self): + r""" + TESTS:: + + sage: hash(unsigned_infinity) + 9223372036854775806 # 64-bit + 2147483646 # 32-bit + """ + return maxsize-1 + def _mul_(self, other): """ Can't rule out an attempt at multiplication by 0. @@ -1102,12 +1122,11 @@ def _element_constructor_(self, x): x = x._value # Handle all ways to represent infinity first - if isinstance(x, PlusInfinityElement): - return self.gen(0) - elif isinstance(x, MinusInfinityElement): - return self.gen(1) - elif isinstance(x, InfinityElement): - return self.gen(0) + if isinstance(x, InfinityElement): + if x < 0: + return self.gen(1) + else: + return self.gen(0) elif isinstance(x, float): if x == float('+inf'): return self.gen(0) @@ -1434,7 +1453,7 @@ def sqrt(self): raise SignError("cannot take square root of a negative number") return self -class MinusInfinity(_uniq, AnInfinity, MinusInfinityElement): +class MinusInfinity(_uniq, AnInfinity, InfinityElement): _sign = -1 _sign_char = '-' @@ -1450,6 +1469,16 @@ def __init__(self): """ InfinityElement.__init__(self, InfinityRing) + def __hash__(self): + r""" + TESTS:: + + sage: hash(-infinity) + -9223372036854775808 # 64-bit + -2147483648 # 32-bit + """ + return ~maxsize + def _neg_(self): """ EXAMPLES:: @@ -1504,7 +1533,7 @@ def _gap_init_(self): """ return '-infinity' -class PlusInfinity(_uniq, AnInfinity, PlusInfinityElement): +class PlusInfinity(_uniq, AnInfinity, InfinityElement): _sign = 1 _sign_char = '+' @@ -1520,6 +1549,16 @@ def __init__(self): """ InfinityElement.__init__(self, InfinityRing) + def __hash__(self): + r""" + TESTS:: + + sage: hash(+infinity) + 9223372036854775807 # 64-bit + 2147483647 # 32-bit + """ + return maxsize + def _neg_(self): """ TESTS:: diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 2f212ee6965..b5b899bdeef 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -1732,7 +1732,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): # we can't cimport rationals. return the_integer_ring._div(self, right) - def __floordiv__(x, y): + cpdef RingElement _floordiv_(self, RingElement right): r""" Computes the whole part of `\frac{x}{y}`. @@ -1769,34 +1769,17 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): sage: [int(a) // b for a,b in signs] == control True """ - cdef Integer z = PY_NEW(Integer) - cdef long yy, res - if type(x) is type(y): - if not mpz_sgn((y).value): - raise ZeroDivisionError, "Integer division by zero" - if mpz_size((x).value) > 100000: - sig_on() - mpz_fdiv_q(z.value, (x).value, (y).value) - sig_off() - else: - mpz_fdiv_q(z.value, (x).value, (y).value) - return z - - elif PyInt_CheckExact(y): - yy = PyInt_AS_LONG(y) - if yy > 0: - mpz_fdiv_q_ui(z.value, (x).value, yy) - elif yy == 0: - raise ZeroDivisionError, "Integer division by zero" - else: - res = mpz_fdiv_q_ui(z.value, (x).value, -yy) - mpz_neg(z.value, z.value) - if res: - mpz_sub_ui(z.value, z.value, 1) - return z + if not mpz_sgn((right).value): + raise ZeroDivisionError("Integer division by zero") + cdef Integer z = PY_NEW(Integer) + if mpz_size(self.value) > 1000: + sig_on() + mpz_fdiv_q(z.value, self.value, (right).value) + sig_off() else: - return bin_op(x, y, operator.floordiv) + mpz_fdiv_q(z.value, self.value, (right).value) + return z def __pow__(self, n, modulus): r""" diff --git a/src/sage/rings/integral_domain.py b/src/sage/rings/integral_domain.py index d7057b7f19a..716f94776c1 100644 --- a/src/sage/rings/integral_domain.py +++ b/src/sage/rings/integral_domain.py @@ -17,6 +17,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20011, "the module sage.rings.integral_domain is deprecated and will be removed") + from sage.rings.ring import IntegralDomain def is_IntegralDomain(R): @@ -25,6 +28,9 @@ def is_IntegralDomain(R): EXAMPLES:: + sage: import sage.rings.integral_domain + doctest:...: DeprecationWarning: the module sage.rings.integral_domain is deprecated and will be removed + See http://trac.sagemath.org/20011 for details. sage: sage.rings.integral_domain.is_IntegralDomain(QQ) True sage: sage.rings.integral_domain.is_IntegralDomain(ZZ) diff --git a/src/sage/rings/laurent_series_ring.py b/src/sage/rings/laurent_series_ring.py index d64288a10dd..5faa5b8fea1 100644 --- a/src/sage/rings/laurent_series_ring.py +++ b/src/sage/rings/laurent_series_ring.py @@ -23,14 +23,9 @@ import laurent_series_ring_element import power_series_ring import polynomial -import commutative_ring -import integral_domain -import ring +from . import ring -from sage.structure.parent_gens import ParentWithGens from sage.libs.pari.all import pari_gen - -from sage.structure.category_object import check_default_category from sage.categories.fields import Fields from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationFields @@ -113,9 +108,9 @@ def LaurentSeriesRing(base_ring, name=None, names=None, default_prec=None, spars if isinstance(base_ring, ring.Field): R = LaurentSeriesRing_field(base_ring, name, default_prec, sparse) - elif isinstance(base_ring, integral_domain.IntegralDomain): + elif isinstance(base_ring, ring.IntegralDomain): R = LaurentSeriesRing_domain(base_ring, name, default_prec, sparse) - elif isinstance(base_ring, commutative_ring.CommutativeRing): + elif isinstance(base_ring, ring.CommutativeRing): R = LaurentSeriesRing_generic(base_ring, name, default_prec, sparse) else: raise TypeError("base_ring must be a commutative ring") @@ -133,7 +128,7 @@ def is_LaurentSeriesRing(x): """ return isinstance(x, LaurentSeriesRing_generic) -class LaurentSeriesRing_generic(commutative_ring.CommutativeRing): +class LaurentSeriesRing_generic(ring.CommutativeRing): """ Univariate Laurent Series Ring @@ -171,7 +166,7 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, catego sage: 1 / (q-q^2) q^-1 + 1 + q + q^2 + O(q^3) """ - commutative_ring.CommutativeRing.__init__(self, base_ring, names=name, + ring.CommutativeRing.__init__(self, base_ring, names=name, category=getattr(self, '_default_category', Fields())) self._polynomial_ring = polynomial.polynomial_ring_constructor.PolynomialRing(self.base_ring(), self.variable_name(), @@ -595,13 +590,13 @@ def uniformizer(self): Return a uniformizer of this Laurent series field if it is a discrete valuation field (i.e. if the base ring is actually a field). Otherwise, an error is raised. - + EXAMPLES:: sage: R. = LaurentSeriesRing(QQ) sage: R.uniformizer() t - + sage: R. = LaurentSeriesRing(ZZ) sage: R.uniformizer() Traceback (most recent call last): @@ -669,7 +664,7 @@ def power_series_ring(self): """ return self._power_series_ring -class LaurentSeriesRing_domain(LaurentSeriesRing_generic, integral_domain.IntegralDomain): +class LaurentSeriesRing_domain(LaurentSeriesRing_generic, ring.IntegralDomain): def __init__(self, base_ring, name=None, default_prec=None, sparse=False): """ Initialization @@ -692,4 +687,3 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False): sage: TestSuite(LaurentSeriesRing(QQ,'t')).run() """ LaurentSeriesRing_generic.__init__(self, base_ring, name, default_prec, sparse) - diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 44401ba7ef0..8e27adf8f85 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -385,7 +385,12 @@ cdef class LaurentSeries(AlgebraElement): 0 sage: f = -5/t^(10) + 1/3 + t + t^2 - 10/3*t^3 + O(t^5); f -5*t^-10 + 1/3 + t + t^2 - 10/3*t^3 + O(t^5) + + Slicing is deprecated:: + sage: f[-10:2] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. -5*t^-10 + 1/3 + t + O(t^5) sage: f[0:] 1/3 + t + t^2 - 10/3*t^3 + O(t^5) @@ -394,14 +399,12 @@ cdef class LaurentSeries(AlgebraElement): start, stop, step = i.start, i.stop, i.step if start is None: start = 0 - if step is None: - step = 1 if stop > self.__u.degree() or stop is None: stop = self.__u.degree() - f = self.__u[start-self.__n:stop-self.__n:step] + f = self.__u[start-self.__n:stop-self.__n:step] # deprecation(18940) return LaurentSeries(self._parent, f, self.__n) - else: - return self.__u[i-self.__n] + + return self.__u[i - self.__n] def __iter__(self): """ diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index d03e420ade3..6f209520938 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1933,9 +1933,9 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): sage: FrobeniusEndomorphism_generic(K, 2) Frobenius endomorphism x |--> x^(5^2) of Power Series Ring in u over Finite Field of size 5 """ - from commutative_ring import is_CommutativeRing + from .ring import CommutativeRing from sage.categories.homset import Hom - if not is_CommutativeRing(domain): + if not isinstance(domain, CommutativeRing): raise TypeError("The base ring must be a commutative ring") self._p = domain.characteristic() if not self._p.is_prime(): diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 360ea52ebb9..41e382710e8 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -193,16 +193,18 @@ """ - #***************************************************************************** # Copyright (C) 2010 Niles Johnson # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.commutative_ring import is_CommutativeRing, CommutativeRing +from sage.rings.ring import CommutativeRing from sage.rings.polynomial.all import PolynomialRing from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.polynomial_ring import is_PolynomialRing @@ -682,7 +684,7 @@ def _is_valid_homomorphism_(self, codomain, im_gens): except NotImplementedError: B = all(v.valuation() > 0 for v in im_gens) return B - if is_CommutativeRing(codomain): + if isinstance(codomain, CommutativeRing): return all(v.is_nilpotent() for v in im_gens) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 133ac360d89..56962246aa3 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -7149,18 +7149,18 @@ def subfields(self, degree=0, name=None): From: Number Field in a0 with defining polynomial x To: Number Field in a with defining polynomial 2*x^4 + 6*x^2 + 1/2 Defn: 0 |--> 0, None), - (Number Field in a1 with defining polynomial x^2 + 4, Ring morphism: - From: Number Field in a1 with defining polynomial x^2 + 4 + (Number Field in a1 with defining polynomial x^2 - 2, Ring morphism: + From: Number Field in a1 with defining polynomial x^2 - 2 To: Number Field in a with defining polynomial 2*x^4 + 6*x^2 + 1/2 - Defn: a1 |--> 2*a^3 + 7*a, None), - (Number Field in a2 with defining polynomial x^2 + 2, Ring morphism: - From: Number Field in a2 with defining polynomial x^2 + 2 + Defn: a1 |--> a^2 + 3/2, None), + (Number Field in a2 with defining polynomial x^2 + 4, Ring morphism: + From: Number Field in a2 with defining polynomial x^2 + 4 To: Number Field in a with defining polynomial 2*x^4 + 6*x^2 + 1/2 - Defn: a2 |--> 2*a^3 + 5*a, None), - (Number Field in a3 with defining polynomial x^2 - 2, Ring morphism: - From: Number Field in a3 with defining polynomial x^2 - 2 + Defn: a2 |--> 2*a^3 + 7*a, None), + (Number Field in a3 with defining polynomial x^2 + 2, Ring morphism: + From: Number Field in a3 with defining polynomial x^2 + 2 To: Number Field in a with defining polynomial 2*x^4 + 6*x^2 + 1/2 - Defn: a3 |--> a^2 + 3/2, None), + Defn: a3 |--> 2*a^3 + 5*a, None), (Number Field in a4 with defining polynomial x^4 + 1, Ring morphism: From: Number Field in a4 with defining polynomial x^4 + 1 To: Number Field in a with defining polynomial 2*x^4 + 6*x^2 + 1/2 @@ -8141,7 +8141,11 @@ def relativize(self, alpha, names, structure=None): sage: L = NumberField(x^4 + 1, 'a') sage: [L.relativize(h, 'c') for (f,h,i) in L.subfields()] - [Number Field in c with defining polynomial x^4 + 1 over its base field, Number Field in c with defining polynomial x^2 - 1/2*a1 over its base field, Number Field in c with defining polynomial x^2 - a2*x - 1 over its base field, Number Field in c with defining polynomial x^2 - a3*x + 1 over its base field, Number Field in c with defining polynomial x - a4 over its base field] + [Number Field in c with defining polynomial x^4 + 1 over its base field, + Number Field in c with defining polynomial x^2 - a1*x + 1 over its base field, + Number Field in c with defining polynomial x^2 - 1/2*a2 over its base field, + Number Field in c with defining polynomial x^2 - a3*x - 1 over its base field, + Number Field in c with defining polynomial x - a4 over its base field] We can relativize over a relative field:: diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index d9151a9c114..fb635b12bcf 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -2121,23 +2121,6 @@ cdef class NumberFieldElement(FieldElement): sig_off() return x - def __floordiv__(self, other): - """ - Return the quotient of self and other. Since these are field - elements the floor division is exactly the same as usual division. - - EXAMPLES:: - - sage: m. = NumberField(x^4 + x^2 + 2/3) - sage: c = (1+b) // (1-b); c - 3/4*b^3 + 3/4*b^2 + 3/2*b + 1/2 - sage: (1+b) / (1-b) == c - True - sage: c * (1-b) - b + 1 - """ - return self / other - def __nonzero__(self): """ Return True if this number field element is nonzero. diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index af4997c31c9..b534aee1ee1 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -47,7 +47,7 @@ import sage.rings.integer_ring as integer_ring import sage.arith.all as arith import sage.misc.misc as misc -from sage.rings.finite_rings.constructor import FiniteField +from sage.rings.finite_rings.finite_field_constructor import FiniteField import number_field diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 0e0f841c63f..70abdcf4d85 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -1003,7 +1003,7 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, res_name = names + '0' if modulus is None: - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF modulus = PolynomialRing(base, 'x')(GF(p**k, res_name).modulus().change_ring(ZZ)) return ExtensionFactory(base=base, premodulus=modulus, prec=prec, print_mode=print_mode, halt=halt, names=names, res_name=res_name, ram_name=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_ram_terms=print_max_ram_terms, print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, check=check, unram=True) @@ -2003,7 +2003,7 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-abs', modulus = None, names=None, if res_name is None: res_name = names + '0' if modulus is None: - from sage.rings.finite_rings.constructor import FiniteField as GF + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF if ram_name is None: ram_name = str(F[0][0]) modulus = PolynomialRing(base, 'x')(GF(q, res_name).modulus().change_ring(ZZ)) diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index 48ff3ef2418..08ab098135d 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -310,7 +310,7 @@ def zeta(self, n=None): else: raise ValueError("No, %sth root of unity in self"%n) else: - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF return self.teichmuller(GF(self.prime()).zeta(n).lift()) def zeta_order(self): diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 02d56d65d96..0a57afcb397 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -313,7 +313,7 @@ def residue_class_field(self): sage: k Finite Field of size 3 """ - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF return GF(self.prime()) def residue_field(self): diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index ce5f60b0771..73521baeb32 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -269,7 +269,7 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: (a // b) * b + a % b 3 + 2*5^4 + 5^5 + 3*5^6 + 5^7 + O(5^16) - The alternative definition: + The alternative definition:: sage: a 3 + 2*5^4 + 5^5 + 3*5^6 + 5^7 + O(5^20) diff --git a/src/sage/rings/padics/unramified_extension_generic.py b/src/sage/rings/padics/unramified_extension_generic.py index d731901b789..4c5d5961833 100644 --- a/src/sage/rings/padics/unramified_extension_generic.py +++ b/src/sage/rings/padics/unramified_extension_generic.py @@ -21,7 +21,7 @@ from padic_extension_generic import pAdicExtensionGeneric -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF class UnramifiedExtensionGeneric(pAdicExtensionGeneric): """ diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index cc7f6d0e3fa..3cc1c106c95 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Fast calculation of cyclotomic polynomials @@ -49,7 +50,7 @@ def cyclotomic_coeffs(nn, sparse=None): \\Phi_n(x) = \\prod_{d|n} (1-x^{n/d})^{\\mu(d)} - where `\\mu(d)` is the Moebius function that is 1 if d has an even + where `\\mu(d)` is the Möbius function that is 1 if d has an even number of distinct prime divisors, -1 if it has an odd number of distinct prime divisors, and 0 if d is not squarefree. @@ -227,7 +228,7 @@ def cyclotomic_value(n, x): \Phi_n(x) = \prod_{d | n} (x^d - 1)^{\mu(n / d)}, - where `\mu` is the Moebius function. + where `\mu` is the Möbius function. - Handles the case that x^d = 1 for some d, but not the case that x^d - 1 is non-invertible: in this case polynomial evaluation is diff --git a/src/sage/rings/polynomial/evaluation.pxd b/src/sage/rings/polynomial/evaluation.pxd new file mode 100644 index 00000000000..5aa6928dfb0 --- /dev/null +++ b/src/sage/rings/polynomial/evaluation.pxd @@ -0,0 +1,11 @@ +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.libs.ntl.types cimport ZZX_c +from sage.libs.mpfr cimport mpfr_t +from sage.libs.mpfi cimport mpfi_t + +cdef fmpz_poly_evaluation_mpfr(mpfr_t res, const fmpz_poly_t poly, const mpfr_t a) +cdef fmpz_poly_evaluation_mpfi(mpfi_t res, const fmpz_poly_t poly, const mpfi_t a) + +cdef ZZX_evaluation_mpfr(mpfr_t res, ZZX_c poly, const mpfr_t a) +cdef ZZX_evaluation_mpfi(mpfi_t res, ZZX_c poly, const mpfi_t a) + diff --git a/src/sage/rings/polynomial/evaluation.pyx b/src/sage/rings/polynomial/evaluation.pyx new file mode 100644 index 00000000000..986fbf78aa9 --- /dev/null +++ b/src/sage/rings/polynomial/evaluation.pyx @@ -0,0 +1,98 @@ +r""" +Fast evaluation of polynomials (Horner's rule) + +This file provides fast evaluation of integer polynomials with a real value. We +consider flint and NTL polynomials and values mpfr_t and mpfi_t. If you intend +to implement more it would be better to find a template strategy instead of +duplicating the code. + +The code in this file is mostly Sage agnostic and only does library calls. + +For appropriate testing see +:mod:`~sage.rings.polynomial.polynomial_integer_dense_flint` and +:mod:`~sage.rings.polynomial.polynomial_integer_dense_ntl`. + +.. TODO:: + + Integrate these functions into + :mod:`~sage.rings.polynomial.polynomial_compiled` +""" +#***************************************************************************** +# Copyright (C) 2016 Vincent Delecroix <20100.delecroix@gmail.com> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.libs.mpfr cimport * +from sage.libs.mpfi cimport * +from sage.libs.gmp.mpz cimport * +from sage.libs.gmp.mpq cimport * +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_poly cimport * +from sage.libs.ntl.ZZ cimport * +from sage.libs.ntl.ZZX cimport * + +cdef fmpz_poly_evaluation_mpfr(mpfr_t res, const fmpz_poly_t poly, const mpfr_t a): + cdef mpz_t c + cdef long i + + mpfr_set_ui(res, 0, MPFR_RNDN) + mpz_init(c) + + for i in range(fmpz_poly_degree(poly), -1, -1): + mpfr_mul(res, res, a, MPFR_RNDN) + if not fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)): + fmpz_poly_get_coeff_mpz(c, poly, i) + mpfr_add_z(res, res, c, MPFR_RNDN) + + mpz_clear(c) + +cdef fmpz_poly_evaluation_mpfi(mpfi_t res, const fmpz_poly_t poly, const mpfi_t a): + cdef mpz_t c + cdef long i + + mpfi_set_ui(res, 0) + mpz_init(c) + + for i in range(fmpz_poly_degree(poly), -1, -1): + mpfi_mul(res, res, a) + if not fmpz_is_zero(fmpz_poly_get_coeff_ptr(poly, i)): + fmpz_poly_get_coeff_mpz(c, poly, i) + mpfi_add_z(res, res, c) + + mpz_clear(c) + + +cdef ZZX_evaluation_mpfr(mpfr_t res, ZZX_c poly, const mpfr_t a): + cdef mpz_t c + cdef long i + + mpfr_set_ui(res, 0, MPFR_RNDN) + mpz_init(c) + + for i in range(ZZX_deg(poly), -1, -1): + mpfr_mul(res, res, a, MPFR_RNDN) + if not ZZ_IsZero(ZZX_coeff(poly, i)): + ZZX_getitem_as_mpz(c, &poly, i) + mpfr_add_z(res, res, c, MPFR_RNDN) + + mpz_clear(c) + +cdef ZZX_evaluation_mpfi(mpfi_t res, ZZX_c poly, const mpfi_t a): + cdef mpz_t c + cdef long i + + mpfi_set_ui(res, 0) + mpz_init(c) + + for i in range(ZZX_deg(poly), -1, -1): + mpfi_mul(res, res, a) + if not ZZ_IsZero(ZZX_coeff(poly, i)): + ZZX_getitem_as_mpz(c, &poly, i) + mpfi_add_z(res, res, c) + + mpz_clear(c) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 75ac398f472..a2740da751e 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -2,7 +2,15 @@ r""" Elements of Laurent polynomial rings """ -from sage.rings.integer import Integer +#***************************************************************************** +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.rings.integer cimport Integer from sage.structure.element import is_Element, coerce_binop from sage.misc.latex import latex import sage.misc.latex @@ -320,10 +328,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial_generic): def __getitem__(self, i): """ - With a tuple (i,j) as argument, - return the Laurent polynomial `\sum_{k=i}^{j-1} c_k t^k` - where ``self`` is `\sum_k c_k t^k`, - otherwise return the coefficient of `t^i`. + Return the `i`-th coefficient of ``self``. EXAMPLES:: @@ -340,18 +345,29 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial_generic): 0 sage: f = -5/t^(10) + 1/3 + t + t^2 - 10/3*t^3; f -5*t^-10 + 1/3 + t + t^2 - 10/3*t^3 + + Slicing is deprecated:: + sage: f[-10:2] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. -5*t^-10 + 1/3 + t sage: f[0:] 1/3 + t + t^2 - 10/3*t^3 + sage: f[:3] + -5*t^-10 + 1/3 + t + t^2 + sage: f[-14:5:2] + Traceback (most recent call last): + ... + NotImplementedError: polynomial slicing with a step is not defined """ if isinstance(i, slice): - start = i.start if i.start is not None else 0 - stop = i.stop if i.stop is not None else self.__u.degree() - f = self.__u[start-self.__n:stop-self.__n] + start = i.start - self.__n if i.start is not None else 0 + stop = i.stop - self.__n if i.stop is not None else self.__u.degree() + 1 + f = self.__u[start:stop:i.step] # deprecation(18940) return LaurentPolynomial_univariate(self._parent, f, self.__n) - else: - return self.__u[i-self.__n] + + return self.__u[i - self.__n] def __iter__(self): """ @@ -641,19 +657,23 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial_generic): raise ValueError("exponent must be an integer") return LaurentPolynomial_univariate(self._parent, self.__u**right, self.__n*right) - def __floordiv__(LaurentPolynomial_univariate self, RingElement rhs): + cpdef RingElement _floordiv_(self, RingElement rhs): """ Perform division with remainder and return the quotient. EXAMPLES:: sage: L. = LaurentPolynomialRing(QQ) - sage: f = x**3 + x^-3 + sage: f = x^3 + x^-3 sage: g = x^-1 + x sage: f // g x^-2 - 1 + x^2 sage: g * (f // g) == f True + sage: f // 1 + x^-3 + x^3 + sage: 1 // f + 0 """ cdef LaurentPolynomial_univariate right = rhs return LaurentPolynomial_univariate(self._parent, @@ -2029,23 +2049,27 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): ans._poly = self._poly * (right)._poly return ans - def __floordiv__(LaurentPolynomial_mpair self, RingElement right): + cpdef RingElement _floordiv_(self, RingElement right): """ Perform division with remainder and return the quotient. EXAMPLES:: sage: L. = LaurentPolynomialRing(QQ) - sage: f = x**3 + y^-3 + sage: f = x^3 + y^-3 sage: g = y + x sage: f // g x^5*y^-3 - x^4*y^-2 + x^3*y^-1 - sage: h = x + y**(-1) + sage: h = x + y^(-1) sage: f // h x^2 - x*y^-1 + y^-2 sage: h * (f // h) == f True + sage: f // 1 + x^3 + y^-3 + sage: 1 // f + 0 TESTS: diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 2b236665a4e..59f4494f876 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -1443,7 +1443,7 @@ def __nonzero__(self): """ return self._MPolynomial_element__element.dict()!={} - def __floordiv__(self,right): + def _floordiv_(self, right): r""" Quotient of division of self by other. This is denoted //. @@ -1464,9 +1464,6 @@ def __floordiv__(self,right): sage: type(0//y) """ - if type(self) is not type(right) or self.parent() is not right.parent(): - self, right = canonical_coercion(self, right) - return self // right # this looks like recursion, but, in fact, it may be that self, right are a totally new composite type # handle division by monomials without using Singular if len(right.dict()) == 1: P = self.parent() diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx index d3dcf92883f..6bc895724fc 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx @@ -38,21 +38,13 @@ Two examples from the Mathematica documentation (done in Sage): """ #***************************************************************************** -# -# Sage: System for Algebra and Geometry Experimentation -# # Copyright (C) 2007 Martin Albrecht # Copyright (C) 2007 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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/ #***************************************************************************** @@ -66,8 +58,6 @@ from sage.libs.singular.decl cimport OPT_REDTAIL, singular_options, kInterRed, t from sage.libs.singular.decl cimport pp_Mult_nn, p_Delete, n_Delete from sage.libs.singular.decl cimport rIsPluralRing -from sage.structure.parent_base cimport ParentWithBase - from sage.rings.polynomial.multi_polynomial_libsingular cimport new_MP from sage.rings.polynomial.plural cimport new_NCP diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 00b2e6d1dc6..b086636bbba 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -222,8 +222,6 @@ from sage.arith.all import gcd from sage.structure.element import coerce_binop from sage.structure.parent cimport Parent -from sage.structure.parent_base cimport ParentWithBase -from sage.structure.parent_gens cimport ParentWithGens from sage.structure.category_object cimport CategoryObject from sage.structure.element cimport EuclideanDomainElement @@ -1634,9 +1632,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): cdef number *n cdef number *denom - if not self is f._parent: + if self is not f._parent: f = self._coerce_c(f) - if not self is g._parent: + if self is not g._parent: g = self._coerce_c(g) if not f._poly: @@ -1737,9 +1735,9 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): """ cdef poly *m = p_ISet(1,self._ring) - if not self is f._parent: + if self is not f._parent: f = self._coerce_c(f) - if not self is g._parent: + if self is not g._parent: g = self._coerce_c(g) if f._poly == NULL: @@ -1939,7 +1937,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn 0 """ self._poly = NULL - self._parent = parent + self._parent = parent self._parent_ring = singular_ring_reference(parent._ring) def __dealloc__(self): @@ -3889,7 +3887,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn else: return False - def __floordiv__(MPolynomial_libsingular self, right): + cpdef RingElement _floordiv_(self, RingElement right): """ Perform division with remainder and return the quotient. @@ -3935,20 +3933,15 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn cdef poly *temp cdef poly *p - _self = self - - if not isinstance(right, MPolynomial_libsingular) \ - or (parent is not (right)._parent): - _right = parent._coerce_c(right) - else: - _right = right - if right.is_zero(): raise ZeroDivisionError if self._parent._base.is_finite() and self._parent._base.characteristic() > 1<<29: raise NotImplementedError, "Division of multivariate polynomials over prime fields with characteristic > 2^29 is not implemented." + _self = self + _right = right + if r.ringtype != 0: if r.ringtype == 4: P = parent.change_ring(RationalField()) @@ -5402,7 +5395,7 @@ cdef inline MPolynomial_libsingular new_MP(MPolynomialRing_libsingular parent, p Singular data structure is used elsewhere. """ cdef MPolynomial_libsingular p = MPolynomial_libsingular.__new__(MPolynomial_libsingular) - p._parent = parent + p._parent = parent p._parent_ring = singular_ring_reference(parent._ring) p._poly = juice p_Normalize(p._poly, p._parent_ring) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index a7a2100d573..60146b8a43b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -52,27 +52,17 @@ """ #***************************************************************************** -# -# Sage: System for Algebra and Geometry Experimentation -# # Copyright (C) 2005 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# +# 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/ #***************************************************************************** -# Changed: -# Kiran Kedlaya (2006-02-12): added Macaulay2 names to TermOrder -import sage.rings.integral_domain as integral_domain +from sage.rings.ring import IntegralDomain import sage.rings.fraction_field_element as fraction_field_element from sage.rings.integer_ring import is_IntegerRing @@ -531,7 +521,7 @@ def __call__(self, x, check=True): c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple:c}) -class MPolynomialRing_polydict_domain(integral_domain.IntegralDomain, +class MPolynomialRing_polydict_domain(IntegralDomain, MPolynomialRing_polydict): def __init__(self, base_ring, n, names, order): order = TermOrder(order,n) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index 40cf4d02d69..f25a79df93b 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -2,9 +2,9 @@ r""" Base class for multivariate polynomial rings """ -from sage.structure.parent_gens cimport ParentWithGens import sage.misc.latex import multi_polynomial_ideal +from sage.structure.parent cimport Parent from term_order import TermOrder from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polydict import PolyDict @@ -332,7 +332,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): return D def __richcmp__(left, right, int op): - return (left)._richcmp(right, op) + return (left)._richcmp(right, op) cpdef int _cmp_(left, right) except -2: if not is_MPolynomialRing(right): diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 8a96961aaca..955d809f102 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -163,7 +163,7 @@ from sage.structure.sequence import Sequence, Sequence_generic from sage.rings.infinity import Infinity -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.quotient_ring import is_QuotientRing diff --git a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py index b44b9e2dfd4..93a023ac9d5 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic_capped_relative_dense.py @@ -203,14 +203,13 @@ def _comp_list(self): sage: K = Qp(13,7) sage: R. = K[] - sage: a = t[0:1] + sage: a = t[:1] sage: a._comp_list() sage: a 0 """ if self.degree() == -1 and self._valbase == infinity: self._list = [] - return self._list polylist = self._poly.list() polylen = len(polylist) self._list = [self.base_ring()(polylist[i], absprec = self._relprecs[i]) << self._valbase for i in range(polylen)] \ @@ -380,8 +379,7 @@ def lift(self): def __getitem__(self, n): """ - Returns the coefficient of x^n if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -390,40 +388,53 @@ def __getitem__(self, n): sage: a = 13^7*t^3 + K(169,4)*t - 13^4 sage: a[1] 13^2 + O(13^4) - sage: a[1:2] + + Slices can be used to truncate polynomials:: + + sage: a[:2] + (13^2 + O(13^4))*t + (12*13^4 + 12*13^5 + 12*13^6 + 12*13^7 + 12*13^8 + 12*13^9 + 12*13^10 + O(13^11)) + + Any other kind of slicing is deprecated or an error, see + :trac:`18940`:: + + sage: a[1:3] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. (13^2 + O(13^4))*t + sage: a[1:3:2] + Traceback (most recent call last): + ... + NotImplementedError: polynomial slicing with a step is not defined """ + d = len(self._relprecs) # = degree + 1 if isinstance(n, slice): - start, stop = n.start, n.stop + start, stop, step = n.start, n.stop, n.step + if step is not None: + raise NotImplementedError("polynomial slicing with a step is not defined") if start is None: start = 0 - elif start < 0: - start = len(self._relprecs) + start - if start < 0: - raise IndexError("list index out of range") - if stop > len(self._relprecs) or stop is None: - stop = len(self._relprecs) - elif stop < 0: - stop = len(self._relprecs) + stop - if stop < 0: - raise IndexError("list index out of range") - if start >= stop: - return Polynomial_padic_capped_relative_dense(self.parent(), []) else: - return Polynomial_padic_capped_relative_dense(self.parent(), - (self._poly[start:stop], self._valbase, - [infinity]*start + self._relprecs[start:stop], False, - None if self._valaddeds is None else [infinity]*start - + self._valaddeds[start:stop], - None if self._list is None else [self.base_ring()(0)] - * start + self._list[start:stop]), construct = True) - else: - if n >= len(self._relprecs): - return self.base_ring()(0) - if not self._list is None: - return self._list[n] - return self.base_ring()(self.base_ring().prime_pow(self._valbase) - * self._poly[n], absprec = self._valbase + self._relprecs[n]) + if start < 0: + start = 0 + from sage.misc.superseded import deprecation + deprecation(18940, "polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead") + if stop is None or stop > d: + stop = d + values = ([self.base_ring().zero()] * start + + [self[i] for i in xrange(start, stop)]) + return self.parent()(values) + + try: + n = n.__index__() + except AttributeError: + raise TypeError("list indices must be integers, not {0}".format(type(n).__name__)) + + if n < 0 or n >= d: + return self.base_ring().zero() + if self._list is not None: + return self._list[n] + return self.base_ring()(self.base_ring().prime_pow(self._valbase) + * self._poly[n], absprec = self._valbase + self._relprecs[n]) def _add_(self, right): """ diff --git a/src/sage/rings/polynomial/pbori.pxd b/src/sage/rings/polynomial/pbori.pxd index 7879f567871..89e9217c604 100644 --- a/src/sage/rings/polynomial/pbori.pxd +++ b/src/sage/rings/polynomial/pbori.pxd @@ -1,6 +1,3 @@ - -from sage.structure.parent_base cimport ParentWithBase -from sage.structure.parent_gens cimport ParentWithGens from sage.rings.polynomial.multi_polynomial_ring_generic cimport \ MPolynomialRing_generic from sage.rings.polynomial.multi_polynomial cimport MPolynomial diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index 393aadfa015..7dcd1f42265 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -194,7 +194,7 @@ from sage.misc.randstate import current_randstate from sage.misc.long cimport pyobject_to_long import sage.misc.weak_dict from sage.rings.integer import Integer -from sage.rings.finite_rings.constructor import FiniteField as GF +from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.rings.polynomial.polynomial_element cimport Polynomial from sage.rings.polynomial.multi_polynomial_ideal import MPolynomialIdeal @@ -1771,7 +1771,7 @@ def get_var_mapping(ring, other): """ my_names = list(ring._names) # we need .index(.) - if isinstance(other, (ParentWithGens,BooleanMonomialMonoid)): + if isinstance(other, (Parent, BooleanMonomialMonoid)): indices = range(other.ngens()) ovar_names = other._names else: @@ -2196,8 +2196,6 @@ cdef class BooleanMonomial(MonoidElement): See class documentation for parameters. """ - - _parent = parent self._ring = parent._ring self._pbmonom = PBMonom_Constructor((self._ring)._pbring) @@ -2898,10 +2896,9 @@ cdef class BooleanPolynomial(MPolynomial): use the appropriate ``__call__`` method in the parent. """ def __init__(self, parent): - self._parent = parent + self._parent = parent self._pbpoly = PBPoly_Constructor_ring((parent)._pbring) - def _repr_(self): """ EXAMPLE:: diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 650918aa013..88586a685be 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -131,7 +131,6 @@ from sage.rings.ring import check_default_category from sage.structure.element cimport CommutativeRingElement, Element, ModuleElement from sage.structure.factory import UniqueFactory from sage.structure.parent cimport Parent -from sage.structure.parent_base cimport ParentWithBase from sage.structure.parent_gens cimport ParentWithGens from sage.rings.polynomial.term_order import TermOrder @@ -977,9 +976,9 @@ cdef class NCPolynomialRing_plural(Ring): cdef number *n cdef number *denom - if not self is f._parent: + if self is not f._parent: f = self._coerce_c(f) - if not self is g._parent: + if self is not g._parent: g = self._coerce_c(g) if(r != currRing): rChangeCurrRing(r) @@ -1106,9 +1105,9 @@ cdef class NCPolynomialRing_plural(Ring): """ cdef poly *m = p_ISet(1,self._ring) - if not self is f._parent: + if self is not f._parent: f = self._coerce_c(f) - if not self is g._parent: + if self is not g._parent: g = self._coerce_c(g) if f._poly == NULL: @@ -1363,12 +1362,12 @@ cdef class NCPolynomial_plural(RingElement): 0 """ self._poly = NULL - self._parent = parent + self._parent = parent def __dealloc__(self): # TODO: Warn otherwise! # for some mysterious reason, various things may be NULL in some cases - if self._parent is not None and (self._parent)._ring != NULL and self._poly != NULL: + if self._parent is not None and (self._parent)._ring != NULL and self._poly != NULL: p_Delete(&self._poly, (self._parent)._ring) # def __call__(self, *x, **kwds): # ? @@ -2673,7 +2672,7 @@ cdef inline NCPolynomial_plural new_NCP(NCPolynomialRing_plural parent, """ cdef NCPolynomial_plural p = NCPolynomial_plural.__new__(NCPolynomial_plural) - p._parent = parent + p._parent = parent p._poly = juice p_Normalize(p._poly, parent._ring) return p diff --git a/src/sage/rings/polynomial/polynomial_element.pxd b/src/sage/rings/polynomial/polynomial_element.pxd index c9eedac5fb0..677138ae93d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pxd +++ b/src/sage/rings/polynomial/polynomial_element.pxd @@ -23,6 +23,8 @@ cdef class Polynomial(CommutativeAlgebraElement): # may return a new element if not possible to modify inplace cdef _inplace_truncate(self, long n) + cdef get_unsafe(self, Py_ssize_t i) + cdef class Polynomial_generic_dense(Polynomial): cdef Polynomial_generic_dense _new_c(self, list coeffs, Parent P) cdef list __coeffs diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index e6b71f2c9aa..4a06ddccd6d 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -43,6 +43,7 @@ TESTS:: cdef is_FractionField, is_RealField, is_ComplexField cdef ZZ, QQ, RR, CC, RDF, CDF +cimport cython from cpython.number cimport PyNumber_TrueDivide import operator, copy, re @@ -58,6 +59,7 @@ import sage.rings.fraction_field_element import sage.rings.infinity as infinity from sage.misc.sage_eval import sage_eval from sage.misc.latex import latex +from sage.misc.long cimport pyobject_to_long from sage.structure.factorization import Factorization from sage.structure.element import coerce_binop @@ -74,7 +76,7 @@ from sage.rings.complex_double import is_ComplexDoubleField, CDF from sage.rings.real_mpfi import is_RealIntervalField from sage.structure.element import generic_power -from sage.structure.element cimport parent_c as parent +from sage.structure.element cimport parent_c as parent, have_same_parent_c from sage.structure.element cimport (Element, RingElement, ModuleElement, MonoidElement, coercion_model) @@ -84,9 +86,7 @@ from sage.rings.integer cimport smallInteger from sage.rings.fraction_field import is_FractionField from sage.rings.padics.generic_nodes import is_pAdicRing, is_pAdicField -from sage.rings.integral_domain import is_IntegralDomain from sage.structure.category_object cimport normalize_names -from sage.structure.parent_gens cimport ParentWithGens from sage.misc.derivative import multi_derivative @@ -806,7 +806,7 @@ cdef class Polynomial(CommutativeAlgebraElement): ['push 0.0'] """ from sage.ext.fast_eval import fast_float_arg, fast_float_constant - var = (self._parent)._names[0] + var = self._parent._names[0] if len(vars) == 0: x = fast_float_arg(0) elif var in vars: @@ -942,6 +942,79 @@ cdef class Polynomial(CommutativeAlgebraElement): return self.degree() >= 0 def __getitem__(self, n): + r""" + Return the `n`-th coefficient of ``self``. + + .. WARNING:: + + If `P` is a polynomial of degree `d`, then ``P[i]`` + returns `0` when `i < 0` or `i > d`. This behaviour + intentionally differs from that of lists: if `L` is a list + of length `n`, then Python defines ``L[-i] = L[n - i]`` + for `0 < i \le n``. The definition used here is more + meaningful for polynomials, since it can be extended + immediately to Laurent series, for example. + + EXAMPLES: + + We illustrate the difference between polynomials and lists + when negative indices are involved:: + + sage: R. = QQ[] + sage: f = x + 2 + sage: f[-1] + 0 + sage: list(f)[-1] + 1 + + Slices can be used to truncate polynomials:: + + sage: pol = R(range(8)); pol + 7*x^7 + 6*x^6 + 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + sage: pol[:6] + 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + + Any other kind of slicing is deprecated or an error, see + :trac:`18940`:: + + sage: f[1:3] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. + x + sage: f[1:3:2] + Traceback (most recent call last): + ... + NotImplementedError: polynomial slicing with a step is not defined + """ + cdef Py_ssize_t d = self.degree() + 1 + if isinstance(n, slice): + start, stop, step = n.start, n.stop, n.step + if step is not None: + raise NotImplementedError("polynomial slicing with a step is not defined") + if start is None: + start = 0 + else: + if start < 0: + start = 0 + from sage.misc.superseded import deprecation + deprecation(18940, "polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead") + if stop is None or stop > d: + stop = d + values = ([self.base_ring().zero()] * start + + [self.get_unsafe(i) for i in xrange(start, stop)]) + return self.parent()(values) + + cdef long k = pyobject_to_long(n) + if k < 0 or k >= d: + return self.base_ring().zero() + return self.get_unsafe(k) + + cdef get_unsafe(self, Py_ssize_t i): + """ + Return the `i`-th coefficient of ``self``. + + Used as building block for a generic :meth:`__getitem__`. + """ raise NotImplementedError def __iter__(self): @@ -1020,7 +1093,7 @@ cdef class Polynomial(CommutativeAlgebraElement): for i from 0<= i <= self.degree(): if i == 1: # we delay the hashing until now to not waste it on a constant poly - var_name_hash = hash((self._parent)._names[0]) + var_name_hash = hash(self._parent._names[0]) # I'm assuming (incorrectly) that hashes of zero indicate that the element is 0. # This assumption is not true, but I think it is true enough for the purposes and it # it allows us to write fast code that omits terms with 0 coefficients. This is @@ -2269,8 +2342,7 @@ cdef class Polynomial(CommutativeAlgebraElement): """ raise IndexError("polynomials are immutable") - - def __floordiv__(self,right): + cpdef RingElement _floordiv_(self, RingElement right): """ Quotient of division of self by other. This is denoted //. @@ -3692,7 +3764,7 @@ cdef class Polynomial(CommutativeAlgebraElement): from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.number_field.number_field_rel import is_RelativeNumberField from sage.rings.number_field.all import NumberField - from sage.rings.finite_rings.constructor import is_FiniteField + from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing from sage.rings.integer_ring import is_IntegerRing @@ -7949,8 +8021,12 @@ cdef class Polynomial_generic_dense(Polynomial): def __hash__(self): return self._hash_c() - def __getitem__(self, n): + @cython.boundscheck(False) + @cython.wraparound(False) + cdef get_unsafe(self, Py_ssize_t n): """ + Return the `n`-th coefficient of ``self``. + EXAMPLES:: sage: R. = RDF[] @@ -7964,25 +8040,8 @@ cdef class Polynomial_generic_dense(Polynomial): 0.0 sage: f[:3] 40.0*x^2 + 10.0*x + 1.0 - sage: f[2:5] - 80.0*x^4 + 80.0*x^3 + 40.0*x^2 - sage: f[2:] - 32.0*x^5 + 80.0*x^4 + 80.0*x^3 + 40.0*x^2 """ - if isinstance(n, slice): - start, stop = n.start, n.stop - if start <= 0: - start = 0 - zeros = [] - elif start > 0: - zeros = [self._parent.base_ring().zero()] * start - if stop is None: - stop = len(self.__coeffs) - return self._parent(zeros + self.__coeffs[start:stop]) - else: - if n < 0 or n >= len(self.__coeffs): - return self.base_ring().zero() - return self.__coeffs[n] + return self.__coeffs[n] def _unsafe_mutate(self, n, value): """ @@ -8035,18 +8094,25 @@ cdef class Polynomial_generic_dense(Polynomial): TESTS: - Check that #13048 has been fixed:: + Check that :trac:`13048` and :trac:`2034` are fixed:: sage: R. = QQbar[] - sage: x//x + sage: x // x 1 - sage: x//1 + sage: x // 1 x - + sage: x // int(1) + x + sage: x //= int(1); x + x + sage: int(1) // x # check that this doesn't segfault + Traceback (most recent call last): + ... + AttributeError: type object 'int' has no attribute 'base_ring' """ - P = (self)._parent - if right.parent() == P: - return Polynomial.__floordiv__(self, right) + if have_same_parent_c(self, right): + return (self)._floordiv_(right) + P = parent(self) d = P.base_ring()(right) cdef Polynomial_generic_dense res = (self)._new_c([c // d for c in (self).__coeffs], P) res.__normalize() diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index eddd02fbb0f..ca82acfa529 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -372,8 +372,7 @@ def __normalize(self): def __getitem__(self,n): """ - Return the `n`-th coefficient of this polynomial if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of this polynomial. Negative indexes are allowed and always return 0 (so you can view the polynomial as embedding Laurent series). @@ -393,30 +392,53 @@ def __getitem__(self,n): sage: R. = PolynomialRing(RealField(19), sparse=True) sage: f = (2-3.5*x)^3; f -42.875*x^3 + 73.500*x^2 - 42.000*x + 8.0000 - sage: f[1:3] - 73.500*x^2 - 42.000*x + + Using slices, we can truncate polynomials:: + sage: f[:2] -42.000*x + 8.0000 - sage: f[2:] - -42.875*x^3 + 73.500*x^2 + + Any other kind of slicing is deprecated or an error:: + + sage: f[1:3] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. + 73.500*x^2 - 42.000*x + sage: f[1:3:2] + Traceback (most recent call last): + ... + NotImplementedError: polynomial slicing with a step is not defined + sage: f["hello"] + Traceback (most recent call last): + ... + TypeError: list indices must be integers, not str """ if isinstance(n, slice): - start, stop = n.start, n.stop - if start < 0: + d = self.degree() + 1 + start, stop, step = n.start, n.stop, n.step + if step is not None: + raise NotImplementedError("polynomial slicing with a step is not defined") + if start is None: start = 0 - if stop is None: - stop = len(self.__coeffs) + 1 - v = {} + else: + if start < 0: + start = 0 + from sage.misc.superseded import deprecation + deprecation(18940, "polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead") + if stop is None or stop > d: + stop = d x = self.__coeffs - for k in x.keys(): - if start <= k and k < stop: - v[k] = x[k] - P = self.parent() - return P(v) - else: - if n not in self.__coeffs: - return self.base_ring()(0) + v = {k: x[k] for k in x.keys() if start <= k < stop} + return self.parent()(v) + + try: + n = n.__index__() + except AttributeError: + raise TypeError("list indices must be integers, not {0}".format(type(n).__name__)) + try: return self.__coeffs[n] + except KeyError: + return self.base_ring().zero() def _unsafe_mutate(self, n, value): r""" diff --git a/src/sage/rings/polynomial/polynomial_gf2x.pyx b/src/sage/rings/polynomial/polynomial_gf2x.pyx index fce912da12a..c802beea58b 100644 --- a/src/sage/rings/polynomial/polynomial_gf2x.pyx +++ b/src/sage/rings/polynomial/polynomial_gf2x.pyx @@ -62,8 +62,10 @@ cdef class Polynomial_GF2X(Polynomial_template): pass Polynomial_template.__init__(self, parent, x, check, is_gen, construct) - def __getitem__(self, i): + cdef get_unsafe(self, Py_ssize_t i): """ + Return the `i`-th coefficient of ``self``. + EXAMPLES:: sage: P. = GF(2)[] @@ -73,31 +75,13 @@ cdef class Polynomial_GF2X(Polynomial_template): 1 sage: f[1] 0 - sage: f[-5:50] == f + sage: f[:50] == f True - sage: f[1:] - x^3 + x^2 + sage: f[:3] + x^2 + 1 """ - cdef type t - cdef long c = 0 - cdef Polynomial_template r - if isinstance(i, slice): - start, stop = i.start, i.stop - if start < 0: - start = 0 - if stop > celement_len(&self.x, (self)._cparent) or stop is None: - stop = celement_len(&self.x, (self)._cparent) - x = (self)._parent.gen() - v = [self[t] for t from start <= t < stop] - - t = type(self) - r = t.__new__(t) - Polynomial_template.__init__(r, (self)._parent, v) - return r << start - else: - if 0 <= i < GF2X_NumBits(self.x): - c = GF2_conv_to_long(GF2X_coeff(self.x, i)) - return self._parent.base_ring()(c) + cdef long c = GF2_conv_to_long(GF2X_coeff(self.x, i)) + return self._parent._base(c) def _pari_(self, variable=None): """ @@ -303,7 +287,7 @@ def GF2X_BuildIrred_list(n): sage: GF(2)['x'](GF2X_BuildIrred_list(33)) x^33 + x^6 + x^3 + x + 1 """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField cdef GF2X_c f GF2 = FiniteField(2) GF2X_BuildIrred(f, int(n)) @@ -323,7 +307,7 @@ def GF2X_BuildSparseIrred_list(n): sage: GF(2)['x'](GF2X_BuildSparseIrred_list(33)) x^33 + x^10 + 1 """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField cdef GF2X_c f GF2 = FiniteField(2) GF2X_BuildSparseIrred(f, int(n)) @@ -343,7 +327,7 @@ def GF2X_BuildRandomIrred_list(n): True """ from sage.misc.randstate import current_randstate - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField cdef GF2X_c tmp, f GF2 = FiniteField(2) current_randstate().set_seed_ntl(False) diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 46943a56c4e..18ed1a1732a 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -58,7 +58,10 @@ from sage.libs.flint.fmpz_poly cimport fmpz_poly_reverse, fmpz_poly_revert_serie from sage.libs.flint.ntl_interface cimport fmpz_set_ZZ, fmpz_poly_set_ZZX, fmpz_poly_get_ZZX from sage.libs.ntl.ZZX cimport * from sage.rings.integer cimport smallInteger +from sage.rings.real_mpfr cimport RealNumber, RealField_class +from sage.rings.real_mpfi cimport RealIntervalFieldElement +from sage.rings.polynomial.evaluation cimport fmpz_poly_evaluation_mpfr, fmpz_poly_evaluation_mpfi cdef extern from "limits.h": long LONG_MAX @@ -280,6 +283,65 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_poly_set_coeff_mpz(self.__poly, i, (a).value) sig_off() + def _eval_mpfr_(self, RealNumber a): + r""" + Evaluate this polynomial on the real number element ``a``. + + This method uses Horner's rule and might not be appropriate for + polynomials of large degree. + + TESTS:: + + sage: R. = PolynomialRing(ZZ, implementation='FLINT') + sage: (x+1)._eval_mpfr_(RR(1.2)) + 2.20000000000000 + sage: (x^2)._eval_mpfr_(RR(2.2)) + 4.84000000000000 + sage: R.zero()._eval_mpfr_(RR(2.1)) + 0.000000000000000 + sage: R.one()._eval_mpfr_(RR(2.1)) + 1.00000000000000 + + sage: p = x^3 - 2*x^2 + x -1 + sage: p._eval_mpfr_(RR(1.3)) + -0.883000000000000 + """ + cdef RealNumber res = a._new() + sig_on() + fmpz_poly_evaluation_mpfr(res.value, self.__poly, a.value) + sig_off() + return res + + def _eval_mpfi_(self, RealIntervalFieldElement a): + r""" + Evaluate this polynomial on the real interval ``a``. + + This method uses Horner's rule and might not be appropriate for + polynomials of large degree. + + TESTS:: + + sage: R. = PolynomialRing(ZZ, implementation='FLINT') + sage: (x+1)._eval_mpfi_(RIF(1.5)) + 2.5000000000000000? + sage: (x^2)._eval_mpfi_(RIF(1.333,1.334)) + 1.78? + sage: R.zero()._eval_mpfi_(RIF(2.1)) + 0 + sage: R.one()._eval_mpfi_(RIF(2.1)) + 1 + + sage: p = x^3 - x^2 - x - 1 + sage: r = p.roots(RIF, multiplicities=False)[0] + sage: p._eval_mpfi_(r) + 0.?e-27 + """ + cdef RealIntervalFieldElement res = a._new() + sig_on() + fmpz_poly_evaluation_mpfi(res.value, self.__poly, a.value) + sig_off() + return res + def __call__(self, *x, **kwds): """ Calls this polynomial with the given parameters, which can be @@ -287,8 +349,9 @@ cdef class Polynomial_integer_dense_flint(Polynomial): method. If the argument is not simply an integer (``int``, ``long`` or - ``Integer``) or a polynomial (of the same type as ``self``), - the call is passed on to the generic implementation in the + ``Integer``) a real number (``RealNumber``) a real interval + (``RealIntervalFieldElement``) or a polynomial (of the same type as + ``self``), the call is passed on to the generic implementation in the ``Polynomial`` class. EXAMPLES: @@ -347,6 +410,11 @@ cdef class Polynomial_integer_dense_flint(Polynomial): return z + if isinstance(x0, RealNumber): + return self._eval_mpfr_( x0) + if isinstance(x0, RealIntervalFieldElement): + return self._eval_mpfi_( x0) + return Polynomial.__call__(self, *x, **kwds) cpdef Integer content(self): @@ -412,9 +480,9 @@ cdef class Polynomial_integer_dense_flint(Polynomial): return Polynomial_integer_dense_flint, \ (self.parent(), self.list(), False, self.is_gen()) - def __getitem__(self, n): - r""" - Returns coefficient of x^n, or zero if n is negative. + cdef get_unsafe(self, Py_ssize_t n): + """ + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -431,29 +499,14 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sage: f[-1] 0 sage: f = 1 + x + 2*x^2 + 3*x^3 + 4*x^4 + 5*x^5 - sage: f[2:4] - 3*x^3 + 2*x^2 - sage: f[-2:4] + sage: f[:4] 3*x^3 + 2*x^2 + x + 1 - sage: f[4:100] - 5*x^5 + 4*x^4 + sage: f[:100] + 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + 1 """ - cdef long k cdef Integer z = PY_NEW(Integer) - if isinstance(n, slice): - start = max(0, n.start) - stop = n.stop - if stop is None or stop > self.degree()+1: - stop = self.degree() + 1 - v = [self[k] for k from start <= k < stop] - P = self.parent() - return P([0] * int(start) + v) - else: - if n < 0 or n > fmpz_poly_degree(self.__poly): - return z - else: - fmpz_poly_get_coeff_mpz(z.value, self.__poly, n) - return z + fmpz_poly_get_coeff_mpz(z.value, self.__poly, n) + return z def _repr(self, name=None, bint latex=False): """ @@ -846,8 +899,8 @@ cdef class Polynomial_integer_dense_flint(Polynomial): EXAMPLES:: sage: x = polygen(ZZ) - sage: p1 = 1 + x + x**2 + x**4 - sage: p2 = -2 + 3*x**2 + 5*x**4 + sage: p1 = 1 + x + x^2 + x^4 + sage: p2 = -2 + 3*x^2 + 5*x^4 sage: p1._mul_trunc_(p2, 4) 3*x^3 + x^2 - 2*x - 2 sage: (p1*p2).truncate(4) @@ -1026,7 +1079,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): EXAMPLES:: sage: x = polygen(ZZ) - sage: p = 1+x+2*x**2 + sage: p = 1+x+2*x^2 sage: q5 = p.inverse_series_trunc(5) sage: q5 -x^4 + 3*x^3 - x^2 - x + 1 @@ -1443,7 +1496,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sage: f.factor_mod(7) (2) * x * (x + 5)^2 """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField p = Integer(p) if not p.is_prime(): raise ValueError, "p must be prime" diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index dea138cc76f..7e1f7e3bdff 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -52,6 +52,8 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer from sage.rings.integer cimport Integer +from sage.rings.real_mpfr cimport RealNumber, RealField_class +from sage.rings.real_mpfi cimport RealIntervalFieldElement from sage.libs.all import pari, pari_gen from sage.structure.factorization import Factorization @@ -63,6 +65,8 @@ import sage.rings.polynomial.polynomial_ring from sage.libs.ntl.ZZX cimport * +from sage.rings.polynomial.evaluation cimport ZZX_evaluation_mpfr, ZZX_evaluation_mpfi + cdef class Polynomial_integer_dense_ntl(Polynomial): r""" A dense polynomial over the integers, implemented via NTL. @@ -253,6 +257,64 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): ZZ_to_mpz(z.value, &y) return z + def _eval_mpfr_(self, RealNumber a): + r""" + Evaluate this polynomial on the real number element ``a``. + + This method uses Horner's rule and might not be appropriate for + polynomials of large degree. + + TESTS:: + + sage: R. = PolynomialRing(ZZ, implementation='NTL') + sage: (x+1)._eval_mpfr_(RR(1.2)) + 2.20000000000000 + sage: (x^2)._eval_mpfr_(RR(2.2)) + 4.84000000000000 + sage: R.zero()._eval_mpfr_(RR(2.1)) + 0.000000000000000 + sage: R.one()._eval_mpfr_(RR(2.1)) + 1.00000000000000 + + sage: p = x^3 - 2*x^2 + x -1 + sage: p._eval_mpfr_(RR(1.3)) + -0.883000000000000 + """ + cdef RealNumber res = a._new() + sig_on() + ZZX_evaluation_mpfr(res.value, self.__poly, a.value) + sig_off() + return res + + def _eval_mpfi_(self, RealIntervalFieldElement a): + r""" + Evaluate this polynomial on the real interval ``a``. + + This method uses Horner's rule and might not be appropriate for + polynomials of large degree. + + TESTS:: + + sage: R. = PolynomialRing(ZZ, implementation='NTL') + sage: (x+1)._eval_mpfi_(RIF(1.5)) + 2.5000000000000000? + sage: (x^2)._eval_mpfi_(RIF(1.333,1.334)) + 1.78? + sage: R.zero()._eval_mpfi_(RIF(2.1)) + 0 + sage: R.one()._eval_mpfi_(RIF(2.1)) + 1 + + sage: p = x^3 - x^2 - x - 1 + sage: r = p.roots(RIF, multiplicities=False)[0] + sage: p._eval_mpfi_(r) + 0.?e-27 + """ + cdef RealIntervalFieldElement res = a._new() + sig_on() + ZZX_evaluation_mpfi(res.value, self.__poly, a.value) + sig_off() + return res def __reduce__(self): r""" @@ -270,10 +332,9 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): return Polynomial_integer_dense_ntl, \ (self.parent(), self.list(), False, self.is_gen()) - def __getitem__(self, n): - r""" - Returns coefficient of the monomial of degree `n` if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + cdef get_unsafe(self, Py_ssize_t n): + """ + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -290,31 +351,14 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): sage: f[-1] 0 sage: f = 1 + x + 2*x^2 + 3*x^3 + 4*x^4 + 5*x^5 - sage: f[2:4] - 3*x^3 + 2*x^2 - sage: f[-2:4] + sage: f[:4] 3*x^3 + 2*x^2 + x + 1 - sage: f[4:100] - 5*x^5 + 4*x^4 + sage: f[:100] + 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + 1 """ cdef Integer z = PY_NEW(Integer) - cdef long k - if isinstance(n, slice): - start, stop = n.start, n.stop - if stop > self.degree() + 1 or stop is None: - stop = self.degree() + 1 - start = max(0, start) - v = [self[k] for k from start <= k < stop] - P = self.parent() - return P([0] * int(start) + v) - else: - if n < 0 or n > ZZX_deg(self.__poly): - return z - else: - # Note that the NTL documentation blesses this direct access of the "rep" member in ZZX.txt. - # Check the "Miscellany" section. - ZZ_to_mpz(z.value, &self.__poly.rep.elts()[n]) - return z + ZZ_to_mpz(z.value, &self.__poly.rep.elts()[n]) + return z def _repr(self, name=None, bint latex=False): """ @@ -974,7 +1018,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): sage: f.factor_mod(7) (2) * x * (x + 5)^2 """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField p = Integer(p) if not p.is_prime(): raise ValueError, "p must be prime" diff --git a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx index 4f1d449ae57..f5bf5d46f87 100644 --- a/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_modn_dense_ntl.pyx @@ -180,10 +180,9 @@ cdef class Polynomial_dense_mod_n(Polynomial): """ return self.__poly - def __getitem__(self, n): + cdef get_unsafe(self, Py_ssize_t n): """ - Returns coefficient of the monomial of degree `n` if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -193,20 +192,10 @@ cdef class Polynomial_dense_mod_n(Polynomial): 4*x^4 + x^3 + 13*x^2 + 10*x + 5 sage: f[2] 13 - sage: f[1:3] - 13*x^2 + 10*x - """ - if isinstance(n, slice): - start, stop = n.start, n.stop - R = self.base_ring() - if start < 0: - start = 0 - if stop > self.__poly.degree()+1 or stop is None: - stop = self.__poly.degree()+1 - v = [R(self.__poly[k]._sage_()) for k in range(start,stop)] - return self.parent()([0]*int(start) + v) - else: - return self.parent().base_ring()(self.__poly[n]._sage_()) + sage: f[:3] + 13*x^2 + 10*x + 5 + """ + return self._parent._base(self.__poly[n]._sage_()) def _unsafe_mutate(self, n, value): n = int(n) @@ -657,10 +646,9 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): cdef long i return [ zz_p_rep(zz_pX_GetCoeff(self.x, i)) for i from 0 <= i <= zz_pX_deg(self.x) ] - def __getitem__(self, n): + cdef get_unsafe(self, Py_ssize_t n): """ - Returns coefficient of the monomial of degree `n` if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -669,28 +657,12 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): sage: f = Polynomial_dense_modn_ntl_zz(R,[2, 1])^7 sage: f[3] 60 - sage: f[3:6] - 84*x^5 + 80*x^4 + 60*x^3 - sage: f[-5:50] == f + sage: f[:6] + 84*x^5 + 80*x^4 + 60*x^3 + 72*x^2 + 48*x + 28 + sage: f[:50] == f True - sage: f[6:] - x^7 + 14*x^6 - """ - if isinstance(n, slice): - start, stop = n.start, n.stop - R = self.base_ring() - if start < 0: - start = 0 - if stop > zz_pX_deg(self.x)+1 or stop is None: - stop = zz_pX_deg(self.x)+1 - v = [ zz_p_rep(zz_pX_GetCoeff(self.x, t)) for t from start <= t < stop ] - return Polynomial_dense_modn_ntl_zz(self._parent, v, check=False) << start - else: - R = self._parent._base - if n < 0 or n > zz_pX_deg(self.x): - return R(0) - else: - return R(zz_p_rep(zz_pX_GetCoeff(self.x, n))) + """ + return self._parent._base(zz_p_rep(zz_pX_GetCoeff(self.x, n))) def _unsafe_mutate(self, n, value): self.c.restore_c() @@ -914,7 +886,7 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): sig_off() return q, r - def __floordiv__(self, right): + cpdef RingElement _floordiv_(self, RingElement right): """ Returns the whole part of self/right, without remainder. @@ -929,9 +901,6 @@ cdef class Polynomial_dense_modn_ntl_zz(Polynomial_dense_mod_n): sage: f - q*g x + 1 """ - if not have_same_parent_c(self, right): - self, right = canonical_coercion(self, right) - return self // right cdef Polynomial_dense_modn_ntl_zz numer = self cdef Polynomial_dense_modn_ntl_zz denom = right cdef Polynomial_dense_modn_ntl_zz q = numer._new() @@ -1218,10 +1187,9 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): def list(self): return [self._parent._base(self[n]) for n from 0 <= n <= self.degree()] - def __getitem__(self, n): + cdef get_unsafe(self, Py_ssize_t n): """ - Returns coefficient of the monomial of degree `n` if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of ``self``. EXAMPLES:: @@ -1230,34 +1198,16 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): sage: f = Polynomial_dense_modn_ntl_ZZ(R,[2,1])^7 sage: f[3] 560 - sage: f[3:6] - 84*x^5 + 280*x^4 + 560*x^3 - sage: f[-5:50] == f + sage: f[:6] + 84*x^5 + 280*x^4 + 560*x^3 + 672*x^2 + 448*x + 128 + sage: f[:50] == f True - sage: f[6:] - x^7 + 14*x^6 - """ - if isinstance(n, slice): - start, stop = n.start, n.stop - R = self.base_ring() - if start < 0: - start = 0 - if stop > ZZ_pX_deg(self.x)+1 or stop is None: - stop = ZZ_pX_deg(self.x)+1 - v = [ self[t] for t from start <= t < stop ] - return Polynomial_dense_modn_ntl_ZZ(self._parent, v, check=False) << start - else: - R = self._parent._base - if n < 0 or n > ZZ_pX_deg(self.x): - return R(0) - + """ self.c.restore_c() - cdef Integer z - # TODO, make this faster cdef ntl_ZZ_p ntl = ntl_ZZ_p(0, self.c) ntl.x = ZZ_pX_coeff(self.x, n) - return R(ntl._integer_()) + return self._parent._base(ntl._integer_()) def _unsafe_mutate(self, n, value): self.c.restore_c() @@ -1472,7 +1422,7 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): sig_off() return q, r - def __floordiv__(self, right): + cpdef RingElement _floordiv_(self, RingElement right): """ Returns the whole part of self/right, without remainder. @@ -1487,9 +1437,6 @@ cdef class Polynomial_dense_modn_ntl_ZZ(Polynomial_dense_mod_n): sage: f - q*g x + 1 """ - if not have_same_parent_c(self, right): - self, right = canonical_coercion(self, right) - return self // right cdef Polynomial_dense_modn_ntl_ZZ numer = self cdef Polynomial_dense_modn_ntl_ZZ denom = right cdef Polynomial_dense_modn_ntl_ZZ q = numer._new() diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 6567a86199e..77b2868d393 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -14,14 +14,26 @@ False sage: 1 in S True + +TESTS:: + + sage: Pol. = CBF[] + sage: Quo. = Pol.quotient(y^3) + sage: CBF.zero()*y + 0 + sage: ((x - 1)/(x + 1))(1 + y) + -0.2500000000000000*y^2 + 0.5000000000000000*y """ -################################################################################ +#***************************************************************************** # Copyright (C) 2005, 2006 William Stein -# Distributed under the terms of the GNU General Public License (GPL) -# The full text of the GPL is available at: +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -################################################################################ +#***************************************************************************** import six import sage.rings.number_field.all @@ -29,9 +41,7 @@ import sage.rings.rational_field import sage.rings.complex_field -import sage.rings.commutative_ring -import sage.rings.integral_domain -from sage.rings.ring import Field +from sage.rings.ring import Field, IntegralDomain, CommutativeRing from sage.misc.cachefunc import cached_method from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement @@ -40,11 +50,10 @@ from sage.categories.commutative_algebras import CommutativeAlgebras from sage.structure.category_object import normalize_names -from sage.structure.parent_gens import ParentWithGens from sage.rings.polynomial.infinite_polynomial_ring import GenDictWithBasering from sage.all import sage_eval, parent -from sage.structure.element import Element + def PolynomialQuotientRing(ring, polynomial, names=None): r""" @@ -170,7 +179,7 @@ def PolynomialQuotientRing(ring, polynomial, names=None): else: names = normalize_names(ring.ngens(), names) R = ring.base_ring() - if isinstance(R, sage.rings.integral_domain.IntegralDomain): + if isinstance(R, IntegralDomain): try: if polynomial.is_irreducible(): if isinstance(R, Field): @@ -186,7 +195,7 @@ def is_PolynomialQuotientRing(x): return isinstance(x, PolynomialQuotientRing_generic) -class PolynomialQuotientRing_generic(sage.rings.commutative_ring.CommutativeRing): +class PolynomialQuotientRing_generic(CommutativeRing): """ Quotient of a univariate polynomial ring by an ideal. @@ -320,7 +329,7 @@ def __init__(self, ring, polynomial, name=None, category=None): self.__ring = ring self.__polynomial = polynomial category = CommutativeAlgebras(ring.base_ring()).Quotients().or_subcategory(category) - sage.rings.commutative_ring.CommutativeRing.__init__(self, ring, names=name, category=category) + CommutativeRing.__init__(self, ring, names=name, category=category) def __reduce__(self): """ @@ -533,7 +542,7 @@ def _coerce_impl(self, x): ## retract = _coerce_impl - ambient = sage.rings.commutative_ring.CommutativeRing.base + ambient = CommutativeRing.base def lift(self, x): """ @@ -637,7 +646,7 @@ def construction(self): -- Simon King (2010-05) """ from sage.categories.pushout import QuotientFunctor - return QuotientFunctor([self.modulus()]*self.base(),self.variable_names(),self.is_field()), self.base() + return QuotientFunctor([self.modulus()]*self.base(),self.variable_names()), self.base() @cached_method def base_ring(self): @@ -1408,7 +1417,7 @@ def selmer_group(self, S, m, proof=True): return gens -class PolynomialQuotientRing_domain(PolynomialQuotientRing_generic, sage.rings.integral_domain.IntegralDomain): +class PolynomialQuotientRing_domain(PolynomialQuotientRing_generic, IntegralDomain): """ EXAMPLES:: diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 37d2dfa9678..7747fb235ec 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -376,38 +376,27 @@ cdef class Polynomial_rational_flint(Polynomial): """ return smallInteger(fmpq_poly_degree(self.__poly)) - def __getitem__(self, n): + cdef get_unsafe(self, Py_ssize_t n): """ - Returns coefficient of the monomial of degree `n` if `n` is an integer, - returns the monomials of self of degree in slice `n` if `n` is a slice. + Return the `n`-th coefficient of ``self``. INPUT: - - ``n`` - Degree of the monomial whose coefficient is to be returned - or a slice. + - ``n`` -- Degree of the monomial whose coefficient is to be + returned. EXAMPLES:: sage: R. = QQ[] sage: f = 1 + t + t^2/2 + t^3/3 + t^4/4 - sage: f[-1], f[0], f[3], f[5] # indirect doctest + sage: f[-1], f[0], f[3], f[5] # indirect doctest (0, 1, 1/3, 0) - sage: f[1:3] # indirect doctest - 1/2*t^2 + t + sage: f[:3] # indirect doctest + 1/2*t^2 + t + 1 """ cdef Rational z = Rational.__new__(Rational) - cdef Polynomial_rational_flint res = self._new() - cdef bint do_sig = _do_sig(self.__poly) - if isinstance(n, slice): - start, stop, step = n.indices(self.degree() + 1) - if do_sig: sig_str("FLINT exception") - fmpq_poly_get_slice(res.__poly, self.__poly, start, stop) - if do_sig: sig_off() - return res - else: - if 0 <= n and n < fmpq_poly_length(self.__poly): - fmpq_poly_get_coeff_mpq(z.value, self.__poly, n) - return z + fmpq_poly_get_coeff_mpq(z.value, self.__poly, n) + return z cpdef _unsafe_mutate(self, unsigned long n, value): """ @@ -829,8 +818,6 @@ cdef class Polynomial_rational_flint(Polynomial): sage: f = R.random_element(2000) sage: f - f/2 == 1/2 * f # indirect doctest True - sage: f[:1000] == f - f[1000:] # indirect doctest - True """ cdef Polynomial_rational_flint op2 = right cdef Polynomial_rational_flint res = self._new() @@ -2103,7 +2090,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: (x^5 + 2).factor_mod(5) (x + 2)^5 """ - from sage.rings.finite_rings.constructor import FiniteField + from sage.rings.finite_rings.finite_field_constructor import FiniteField p = Integer(p) if not p.is_prime(): diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index 193ef9922c2..3291e232174 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -181,8 +181,10 @@ cdef class PolynomialRealDense(Polynomial): self._coeffs = check_reallocarray(self._coeffs, i+1, sizeof(mpfr_t)) self._degree = i - def __getitem__(self, ix): + cdef get_unsafe(self, Py_ssize_t i): """ + Return the `i`-th coefficient of ``self``. + EXAMPLES:: sage: from sage.rings.polynomial.polynomial_real_mpfr_dense import PolynomialRealDense @@ -202,27 +204,9 @@ cdef class PolynomialRealDense(Polynomial): x^5 + 5.0*x^4 + 10.*x^3 + 10.*x^2 + 5.0*x + 1.0 sage: f[:3] 10.*x^2 + 5.0*x + 1.0 - sage: f[3:] - x^5 + 5.0*x^4 + 10.*x^3 - sage: f[1:4] - 10.*x^3 + 10.*x^2 + 5.0*x - """ - if isinstance(ix, slice): - if ix.stop is None: - chopped = self - else: - chopped = self.truncate(ix.stop) - if ix.start is None: - return chopped - else: - return (chopped >> ix.start) << ix.start - cdef RealNumber r = RealNumber(self._base_ring) - cdef Py_ssize_t i = ix - if 0 <= i <= self._degree: - mpfr_set(r.value, self._coeffs[i], self._base_ring.rnd) - else: - mpfr_set_ui(r.value, 0, self._base_ring.rnd) + cdef RealNumber r = RealNumber.__new__(RealNumber, self._base_ring) + mpfr_set(r.value, self._coeffs[i], self._base_ring.rnd) return r cdef PolynomialRealDense _new(self, Py_ssize_t degree): diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 9c469c9eb46..cae2fbe7324 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -168,12 +168,9 @@ from sage.structure.category_object import check_default_category import sage.algebras.algebra import sage.categories.basic as categories -import sage.rings.commutative_ring as commutative_ring import sage.rings.commutative_algebra as commutative_algebra import sage.rings.ring as ring from sage.structure.element import is_RingElement -import sage.rings.integral_domain as integral_domain -import sage.rings.principal_ideal_domain as principal_ideal_domain import sage.rings.polynomial.polynomial_element_generic as polynomial_element_generic import sage.rings.rational_field as rational_field from sage.rings.integer_ring import is_IntegerRing, IntegerRing @@ -1508,7 +1505,7 @@ def weyl_algebra(self): from sage.algebras.weyl_algebra import DifferentialWeylAlgebra return DifferentialWeylAlgebra(self) -class PolynomialRing_integral_domain(PolynomialRing_commutative, integral_domain.IntegralDomain): +class PolynomialRing_integral_domain(PolynomialRing_commutative, ring.IntegralDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, element_class=None): """ @@ -1558,8 +1555,7 @@ def _repr_(self): class PolynomialRing_field(PolynomialRing_integral_domain, PolynomialRing_singular_repr, - principal_ideal_domain.PrincipalIdealDomain, - ): + ring.PrincipalIdealDomain): def __init__(self, base_ring, name="x", sparse=False, element_class=None): """ TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index d4aa4428241..a3a67ce453b 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -29,7 +29,7 @@ import sage.rings.padics.padic_base_leaves as padic_base_leaves from sage.rings.integer import Integer -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing from sage.misc.cachefunc import weak_cached_function @@ -567,7 +567,7 @@ def _multi_variate(base_ring, names, n, sparse, order, implementation): return R from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular - if m.integral_domain.is_IntegralDomain(base_ring): + if isinstance(base_ring, ring.IntegralDomain): if n < 1: R = m.MPolynomialRing_polydict_domain(base_ring, n, names, order) else: diff --git a/src/sage/rings/polynomial/polynomial_singular_interface.py b/src/sage/rings/polynomial/polynomial_singular_interface.py index 6e7e53b3f9a..47846731560 100644 --- a/src/sage/rings/polynomial/polynomial_singular_interface.py +++ b/src/sage/rings/polynomial/polynomial_singular_interface.py @@ -53,7 +53,7 @@ from sage.rings.integer_ring import ZZ import sage.arith.all -import sage.rings.finite_rings.constructor +import sage.rings.finite_rings.finite_field_constructor class PolynomialRing_singular_repr: @@ -203,7 +203,7 @@ def _singular_(self, singular=singular): R._check_valid() if self.base_ring() is ZZ or self.base_ring().is_prime_field(): return R - if sage.rings.finite_rings.constructor.is_FiniteField(self.base_ring()) or\ + if sage.rings.finite_rings.finite_field_constructor.is_FiniteField(self.base_ring()) or\ (number_field.number_field_base.is_NumberField(self.base_ring()) and self.base_ring().is_absolute()): R.set_ring() #sorry for that, but needed for minpoly if singular.eval('minpoly') != "(" + self.__minpoly + ")": @@ -268,7 +268,7 @@ def _singular_init_(self, singular=singular): elif base_ring.is_prime_field(): self.__singular = singular.ring(self.characteristic(), _vars, order=order, check=False) - elif sage.rings.finite_rings.constructor.is_FiniteField(base_ring): + elif sage.rings.finite_rings.finite_field_constructor.is_FiniteField(base_ring): # not the prime field! gen = str(base_ring.gen()) r = singular.ring( "(%s,%s)"%(self.characteristic(),gen), _vars, order=order, check=False) @@ -355,7 +355,7 @@ def can_convert_to_singular(R): return False; base_ring = R.base_ring() - return ( sage.rings.finite_rings.constructor.is_FiniteField(base_ring) + return ( sage.rings.finite_rings.finite_field_constructor.is_FiniteField(base_ring) or is_RationalField(base_ring) or (base_ring.is_prime_field() and base_ring.characteristic() <= 2147483647) or is_RealField(base_ring) diff --git a/src/sage/rings/polynomial/polynomial_template.pxi b/src/sage/rings/polynomial/polynomial_template.pxi index 57f4550b27d..436df32923c 100644 --- a/src/sage/rings/polynomial/polynomial_template.pxi +++ b/src/sage/rings/polynomial/polynomial_template.pxi @@ -1,11 +1,15 @@ """ Polynomial Template for C/C++ Library Interfaces """ + #***************************************************************************** # Copyright (C) 2008 Martin Albrecht # Copyright (C) 2008 Robert Bradshaw # -# Distributed under the terms of the GNU General Public License (GPL) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** @@ -75,7 +79,7 @@ cdef class Polynomial_template(Polynomial): .. note:: Implementations using this template MUST implement coercion from base - ring elements and ``__getitem__``. See + ring elements and :meth:`get_unsafe`. See :class:`~sage.rings.polynomial.polynomial_gf2x.Polynomial_GF2X` for an example. """ @@ -408,21 +412,28 @@ cdef class Polynomial_template(Polynomial): #assert(t._parent(tp) == t) return r,s,t - def __floordiv__(self, right): + cpdef RingElement _floordiv_(self, RingElement right): """ - EXAMPLE:: + EXAMPLES:: sage: P. = GF(2)[] sage: x//(x + 1) 1 sage: (x + 1)//x 1 + sage: F = GF(47) + sage: R. = F[] + sage: x // 1 + x + sage: x // F(1) + x + sage: 1 // x + 0 + sage: parent(x // 1) + Univariate Polynomial Ring in x over Finite Field of size 47 + sage: parent(1 // x) + Univariate Polynomial Ring in x over Finite Field of size 47 """ - # We can't use @coerce_binop for operators in cython classes, - # so we use sage.structure.element.bin_op to handle coercion. - if type(self) is not type(right) or \ - (self)._parent is not (right)._parent: - return bin_op(self, right, operator.mod) cdef Polynomial_template _right = right if celement_is_zero(&_right.x, (self)._cparent): diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index 1ac2804d7b2..ebf2a204f0f 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -228,8 +228,10 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sig_off() return 0 - def __getitem__(self, i): + cdef get_unsafe(self, Py_ssize_t i): """ + Return the `i`-th coefficient of ``self``. + EXAMPLES:: sage: P. = GF(32003)[] @@ -242,31 +244,13 @@ cdef class Polynomial_zmod_flint(Polynomial_template): 2252 sage: f[-1] 0 - sage: f[1:3] - 24998*x^2 + 29761*x - sage: f[-5:50] == f + sage: f[:2] + 29761*x + 2252 + sage: f[:50] == f True """ - cdef type t - cdef unsigned long c = 0 - cdef Polynomial_template r - if isinstance(i, slice): - start, stop = i.start, i.stop - if start < 0: - start = 0 - if stop > celement_len(&self.x, (self)._cparent) or stop is None: - stop = celement_len(&self.x, (self)._cparent) - x = (self)._parent.gen() - v = [self[t] for t from start <= t < stop] - - t = type(self) - r = t.__new__(t) - Polynomial_template.__init__(r, (self)._parent, v) - return r << start - else: - if 0 <= i < nmod_poly_length(&self.x): - c = nmod_poly_get_coeff_ui(&self.x, i) - return self._parent.base_ring()(c) + cdef unsigned long c = nmod_poly_get_coeff_ui(&self.x, i) + return self._parent.base_ring()(c) def __call__(self, *x, **kwds): """ @@ -518,10 +502,14 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sage: P.=GF(7)[] sage: b = P(range(10)); c = P(range(5, 15)) - sage: (b._mul_trunc_opposite(c, 10))[10:18] - 5*a^17 + 2*a^16 + 6*a^15 + 4*a^14 + 4*a^13 + 5*a^10 - sage: (b._mul_trunc_opposite(c, 18))[18:] - 0 + sage: b._mul_trunc_opposite(c, 10) + 5*a^17 + 2*a^16 + 6*a^15 + 4*a^14 + 4*a^13 + 5*a^10 + 2*a^9 + 5*a^8 + 4*a^5 + 4*a^4 + 6*a^3 + 2*a^2 + 5*a + sage: list(b._mul_trunc_opposite(c, 10))[10:18] + [5, 0, 0, 4, 4, 6, 2, 5] + sage: list(b*c)[10:18] + [5, 0, 0, 4, 4, 6, 2, 5] + sage: list(b._mul_trunc_opposite(c, 18))[18:] + [] TESTS:: diff --git a/src/sage/rings/polynomial/polynomial_zz_pex.pyx b/src/sage/rings/polynomial/polynomial_zz_pex.pyx index 9fa9111fb62..02ad5f9b334 100644 --- a/src/sage/rings/polynomial/polynomial_zz_pex.pyx +++ b/src/sage/rings/polynomial/polynomial_zz_pex.pyx @@ -147,8 +147,10 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): Polynomial_template.__init__(self, parent, x, check, is_gen, construct) - def __getitem__(self,i): + cdef get_unsafe(self, Py_ssize_t i): """ + Return the `i`-th coefficient of ``self``. + EXAMPLES:: sage: K.=GF(next_prime(2**60)**3) @@ -160,33 +162,14 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): 2*a + 1 sage: f[2] 0 - sage: f[1:4] - x^3 + (2*a + 1)*x - sage: f[-5:50] == f + sage: f[:2] + (2*a + 1)*x + a + sage: f[:50] == f True """ - cdef type t - cdef ZZ_pE_c c_pE - cdef Polynomial_template r - if isinstance(i, slice): - start, stop = i.start, i.stop - if start < 0: - start = 0 - if stop > celement_len(&self.x, (self)._cparent) or stop is None: - stop = celement_len(&self.x, (self)._cparent) - x = (self)._parent.gen() - v = [self[t] for t from start <= t < stop] - - t = type(self) - r = t.__new__(t) - Polynomial_template.__init__(r, (self)._parent, v) - return r << start - else: - self._parent._modulus.restore() - c_pE = ZZ_pEX_coeff(self.x, i) - - K = self._parent.base_ring() - return K(ZZ_pE_c_to_list(c_pE)) + self._parent._modulus.restore() + cdef ZZ_pE_c c_pE = ZZ_pEX_coeff(self.x, i) + return self._parent._base(ZZ_pE_c_to_list(c_pE)) def list(self): """ diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index 1f28994cdf1..7eeda364c50 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -415,63 +415,28 @@ cdef class PowerSeries_poly(PowerSeries): ... IndexError: coefficient not known sage: f[1:4] + doctest:...: DeprecationWarning: polynomial slicing with a start index is deprecated, use list() and slice the resulting list instead + See http://trac.sagemath.org/18940 for details. -17/5*t^3 + O(t^5) sage: R. = ZZ[[]] sage: f = (2-t)^5; f 32 - 80*t + 80*t^2 - 40*t^3 + 10*t^4 - t^5 - sage: f[2:4] - 80*t^2 - 40*t^3 - sage: f[5:9] - -t^5 - sage: f[2:7:2] - 80*t^2 + 10*t^4 - sage: f[10:20] - 0 - sage: f[10:] - 0 sage: f[:4] 32 - 80*t + 80*t^2 - 40*t^3 - sage: f = 1 + t^3 - 4*t^4 + O(t^7) ; f 1 + t^3 - 4*t^4 + O(t^7) - sage: f[2:4] - t^3 + O(t^7) - sage: f[4:9] - -4*t^4 + O(t^7) - sage: f[2:7:2] - -4*t^4 + O(t^7) - sage: f[10:20] - O(t^7) - sage: f[10:] - O(t^7) sage: f[:4] 1 + t^3 + O(t^7) """ if isinstance(n, slice): - # get values from slice object - start = n.start if n.start is not None else 0 - stop = self.prec() if n.stop is None else n.stop - if stop is infinity: stop = self.degree()+1 - step = 1 if n.step is None else n.step - - # find corresponding polynomial - poly = self.__f[start:stop] - if step is not None: - coeffs = poly.padded_list(stop) - for i in range(start, stop): - if (i-start) % step: - coeffs[i] = 0 - poly = self.__f.parent()(coeffs) - - # return the power series - return PowerSeries_poly(self._parent, poly, + return PowerSeries_poly(self._parent, self.polynomial()[n], prec=self._prec, check=False) elif n < 0: - return self.base_ring()(0) + return self.base_ring().zero() elif n > self.__f.degree(): if self._prec > n: - return self.base_ring()(0) + return self.base_ring().zero() else: raise IndexError("coefficient not known") return self.__f[n] @@ -1217,12 +1182,20 @@ cdef class PowerSeries_poly(PowerSeries): 1 + 2*x + 3*x^2 + 4*x^3 + 5*x^4 sage: _.is_terminating_series() True + + TESTS: + + Check that :trac:``18094`` is fixed:: + + sage: R.=PolynomialRing(ZZ) + sage: SR(R(0).add_bigoh(20)) + Order(x^20) """ from sage.symbolic.ring import SR from sage.rings.infinity import PlusInfinity poly = self.polynomial() pex = SR(poly) - var = pex.variables()[0] + var = SR.var(self.variable()) if not isinstance(self.prec(), PlusInfinity): # GiNaC does not allow manual addition of bigoh, # so we use a trick. diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 63640130e24..14d7545357f 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -122,9 +122,8 @@ from polynomial.polynomial_ring_constructor import PolynomialRing import laurent_series_ring import laurent_series_ring_element -import commutative_ring -import integral_domain import integer +from . import ring from infinity import infinity import sage.misc.latex as latex from sage.structure.nonexact import Nonexact @@ -443,7 +442,7 @@ def is_PowerSeriesRing(R): else: return False -class PowerSeriesRing_generic(UniqueRepresentation, commutative_ring.CommutativeRing, Nonexact): +class PowerSeriesRing_generic(UniqueRepresentation, ring.CommutativeRing, Nonexact): """ A power series ring. """ @@ -521,7 +520,7 @@ def __init__(self, base_ring, name=None, default_prec=None, sparse=False, self.__mpoly_ring = PolynomialRing(K.base_ring(), names=names) assert is_MPolynomialRing(self.__mpoly_ring) self.Element = power_series_mpoly.PowerSeries_mpoly - commutative_ring.CommutativeRing.__init__(self, base_ring, names=name, + ring.CommutativeRing.__init__(self, base_ring, names=name, category=getattr(self,'_default_category', _CommutativeRings)) Nonexact.__init__(self, default_prec) @@ -1220,7 +1219,7 @@ def laurent_series_ring(self): self.base_ring(), self.variable_name(), default_prec=self.default_prec(), sparse=self.is_sparse()) return self.__laurent_series_ring -class PowerSeriesRing_domain(PowerSeriesRing_generic, integral_domain.IntegralDomain): +class PowerSeriesRing_domain(PowerSeriesRing_generic, ring.IntegralDomain): pass class PowerSeriesRing_over_field(PowerSeriesRing_domain): diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index e2e07924116..d3cac0a1125 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1401,7 +1401,7 @@ cdef class PowerSeries(AlgebraElement): sage: p.O(-5) Traceback (most recent call last): ... - ValueError: n must be at least 0 + ValueError: prec (= -5) must be non-negative """ if prec is infinity or prec >= self.prec(): return self diff --git a/src/sage/rings/principal_ideal_domain.py b/src/sage/rings/principal_ideal_domain.py index 39ae8ae7ced..17e21438d17 100644 --- a/src/sage/rings/principal_ideal_domain.py +++ b/src/sage/rings/principal_ideal_domain.py @@ -17,6 +17,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20011, "the module sage.rings.principal_ideal_domain is deprecated and will be removed") + from sage.rings.ring import PrincipalIdealDomain def is_PrincipalIdealDomain(R): @@ -25,6 +28,9 @@ def is_PrincipalIdealDomain(R): EXAMPLES:: + sage: import sage.rings.principal_ideal_domain + doctest:...: DeprecationWarning: the module sage.rings.principal_ideal_domain is deprecated and will be removed + See http://trac.sagemath.org/20011 for details. sage: sage.rings.principal_ideal_domain.is_PrincipalIdealDomain(ZZ) True sage: R. = QQ[] diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 16fe40f13f6..aceb833f12e 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -487,7 +487,6 @@ import sage.rings.ring from sage.misc.fast_methods import Singleton from sage.structure.sage_object import SageObject -from sage.structure.parent_gens import ParentWithGens from sage.rings.real_mpfr import RR from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement from sage.rings.complex_field import ComplexField @@ -495,7 +494,6 @@ from sage.rings.complex_interval import is_ComplexIntervalFieldElement from sage.rings.polynomial.all import PolynomialRing from sage.rings.polynomial.polynomial_element import is_Polynomial -from sage.rings.polynomial.multi_polynomial import is_MPolynomial from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.number_field.number_field import NumberField, QuadraticField, CyclotomicField diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 133754e30ca..65d922940d1 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -110,10 +110,8 @@ #***************************************************************************** -import quotient_ring_element import sage.misc.latex as latex -import commutative_ring, ring -import ideal +from . import ring, ideal, quotient_ring_element import sage.rings.polynomial.multi_polynomial_ideal from sage.structure.category_object import normalize_names import sage.structure.parent_gens @@ -304,7 +302,7 @@ def QuotientRing(R, I, names=None): if S == ZZ: return Integers((I_lift+J).gen()) return R.__class__(S, I_lift + J, names=names) - if isinstance(R, sage.rings.commutative_ring.CommutativeRing): + if isinstance(R, ring.CommutativeRing): return QuotientRing_generic(R, I, names) return QuotientRing_nc(R, I, names) @@ -937,7 +935,7 @@ def ideal(self, *gens, **kwds): if not isinstance(self.__R, MPolynomialRing_libsingular) and \ (not hasattr(self.__R, '_has_singular') or not self.__R._has_singular): # pass through - return commutative_ring.CommutativeRing.ideal(self, gens, **kwds) + return ring.CommutativeRing.ideal(self, gens, **kwds) if is_SingularElement(gens): gens = list(gens) coerce = True @@ -1254,7 +1252,7 @@ def term_order(self): """ return self.__R.term_order() -class QuotientRing_generic(QuotientRing_nc, sage.rings.commutative_ring.CommutativeRing): +class QuotientRing_generic(QuotientRing_nc, ring.CommutativeRing): r""" Creates a quotient ring of a *commutative* ring `R` by the ideal `I`. @@ -1283,7 +1281,7 @@ def __init__(self, R, I, names, category=None): sage: isinstance(ZZ.quo(2), sage.rings.ring.CommutativeRing) # indirect doctest True """ - if not isinstance(R, sage.rings.commutative_ring.CommutativeRing): + if not isinstance(R, ring.CommutativeRing): raise TypeError("This class is for quotients of commutative rings only.\n For non-commutative rings, use ") if not self._is_category_initialized(): category = check_default_category(_CommutativeRingsQuotients,category) diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 63d671eb955..69e3db8740d 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -1031,6 +1031,8 @@ cdef class RealBall(RingElement): [1.282427129100623 +/- 6.02e-16] sage: RealBall(RBF, sage.symbolic.constants.e) [2.718281828459045 +/- 5.35e-16] + sage: RealBall(RBF, sage.symbolic.constants.EulerGamma()) + [0.577215664901533 +/- 3.57e-16] """ import sage.symbolic.constants cdef fmpz_t tmpz @@ -1090,6 +1092,8 @@ cdef class RealBall(RingElement): arb_const_khinchin(self.value, prec(self)) elif isinstance(mid, sage.symbolic.constants.Glaisher): arb_const_glaisher(self.value, prec(self)) + elif isinstance(mid, sage.symbolic.constants.EulerGamma): + arb_const_euler(self.value, prec(self)) else: raise TypeError("unsupported constant") finally: @@ -2681,21 +2685,40 @@ cdef class RealBall(RingElement): # Elementary functions - def log(self): + def log(self, base=None): """ - Return the natural logarithm of this ball. + Return the logarithm of this ball. + + INPUT: + + - ``base`` (optional, positive real ball or number) -- if ``None``, + return the natural logarithm ``ln(self)``, otherwise, return the + general logarithm ``ln(self)/ln(base)`` EXAMPLES:: sage: RBF(3).log() [1.098612288668110 +/- 6.63e-16] + sage: RBF(3).log(2) + [1.584962500721156 +/- 7.53e-16] + sage: RBF(-1/3).log() nan + sage: RBF(3).log(-1) + nan + sage: RBF(2).log(0) + nan """ + cdef RealBall cst cdef RealBall res = self._new() if _do_sig(prec(self)): sig_on() arb_log(res.value, self.value, prec(self)) if _do_sig(prec(self)): sig_off() + if base is not None: + cst = self._parent.coerce(base).log() + if _do_sig(prec(self)): sig_on() + arb_div(res.value, res.value, cst.value, prec(self)) + if _do_sig(prec(self)): sig_off() return res def log1p(self): diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 54eb73ed439..e1e2f5bae2d 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -2775,28 +2775,6 @@ def time_alloc_list(n): return l -def time_alloc(n): - """ - Allocate ``n`` :class:`RealDoubleElement` instances. - - EXAMPLES: - - Since this does not store anything in a python object, the created - elements will not be sent to the garbage collector. Therefore they - remain in the pool:: - - sage: from sage.rings.real_double import time_alloc, pool_stats - sage: pool_stats() - Used pool 0 / 0 times - Pool contains 7 / 50 items - sage: time_alloc(25) - sage: pool_stats() - Used pool 0 / 0 times - Pool contains 7 / 50 items - """ - cdef int i - for i from 0 <= i < n: - z = PY_NEW(RealDoubleElement) def pool_stats(): """ diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 9c87df698a2..ddf0aadbfbb 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -668,6 +668,22 @@ cdef class RealIntervalField_class(sage.rings.ring.Field): x = (x,y) return RealIntervalFieldElement(self, x, base) + def algebraic_closure(self): + """ + Return the algebraic closure of this interval field, i.e., the + complex interval field with the same precision. + + EXAMPLES:: + + sage: RIF.algebraic_closure() + Complex Interval Field with 53 bits of precision + sage: RIF.algebraic_closure() is CIF + True + sage: RealIntervalField(100).algebraic_closure() + Complex Interval Field with 100 bits of precision + """ + return self.complex_field() + def construction(self): r""" Returns the functorial construction of ``self``, namely, completion of diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 1d0dcfecddb..6107c9d56f5 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -69,7 +69,6 @@ AUTHORS: from sage.misc.cachefunc import cached_method from sage.structure.element cimport coercion_model -from sage.structure.parent_gens cimport ParentWithGens from sage.structure.parent cimport Parent from sage.structure.category_object import check_default_category from sage.misc.prandom import randint @@ -835,7 +834,26 @@ cdef class Ring(ParentWithGens): True sage: ZZ.is_subring(GF(19)) False + + TESTS: + + Every ring is a subring of itself, :trac:`17287`:: + + sage: QQbar.is_subring(QQbar) + True + sage: RR.is_subring(RR) + True + sage: CC.is_subring(CC) + True + sage: K. = NumberField(x^3-x+1/10) + sage: K.is_subring(K) + True + sage: R. = RR[] + sage: R.is_subring(R) + True """ + if self is other: + return True try: return self.Hom(other).natural_map().is_injective() except TypeError: @@ -2179,7 +2197,7 @@ cdef class Field(PrincipalIdealDomain): import sage.rings.rational_field return sage.rings.rational_field.RationalField() else: - from sage.rings.finite_rings.constructor import GF + from sage.rings.finite_rings.finite_field_constructor import GF return GF(self.characteristic()) def algebraic_closure(self): diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 53cf088e099..14c33abed0e 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -241,6 +241,11 @@ def _call_(self, x): sage: UCFtoQQbar = UCF.coerce_embedding() sage: UCFtoQQbar(UCF.gen(3)) # indirect doctest -0.500000000000000? + 0.866025403784439?*I + + Test that the bug reported in :trac:`19912` has been fixed:: + + sage: UCFtoQQbar(UCF.gen(4)+1) + I + 1 """ obj = x._obj QQbar = self.codomain() @@ -249,7 +254,7 @@ def _call_(self, x): k = obj.Conductor().sage() coeffs = obj.CoeffsCyc(k).sage() zeta = QQbar.zeta(k) - return QQbar(sum(coeffs[a] * zeta**a for a in range(1,k))) + return QQbar(sum(coeffs[a] * zeta**a for a in range(k))) class UniversalCyclotomicFieldElement(FieldElement): def __init__(self, parent, obj): @@ -457,13 +462,18 @@ def _symbolic_(self, R): e^(2/7*I*pi) sage: SR(E(5) + 2*E(5,2) + 3*E(5,3)) -sqrt(5) + 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4*I*sqrt(-2*sqrt(5) + 10) - 3/2 + + Test that the bug reported in :trac:`19912` has been fixed:: + + sage: SR(1+E(4)) + I + 1 """ from sage.symbolic.constants import pi from sage.symbolic.all import i as I k = self._obj.Conductor().sage() coeffs = self._obj.CoeffsCyc(k).sage() s = R.zero() - for a in range(1,k): + for a in range(k): if coeffs[a]: s += coeffs[a] * (2*a*I*pi/k).exp() return s @@ -514,6 +524,13 @@ def to_cyclotomic_field(self, R=None): 0.309016994374947 + 0.951056516295154*I sage: CC(CF(x)) 0.309016994374947 + 0.951056516295154*I + + Test that the bug reported in :trac:`19912` has been fixed:: + + sage: a = 1+E(4); a + 1 + E(4) + sage: a.to_cyclotomic_field() + zeta4 + 1 """ from sage.rings.number_field.number_field import CyclotomicField k = self._obj.Conductor().sage() @@ -525,7 +542,7 @@ def to_cyclotomic_field(self, R=None): return R(obj.sage()) zeta = Rcan.gen() coeffs = obj.CoeffsCyc(k).sage() - return R(sum(coeffs[a] * zeta**a for a in range(1,k))) + return R(sum(coeffs[a] * zeta**a for a in range(k))) def __hash__(self): r""" diff --git a/src/sage/schemes/affine/affine_homset.py b/src/sage/schemes/affine/affine_homset.py index 5fe57cbbc62..c58379ff094 100644 --- a/src/sage/schemes/affine/affine_homset.py +++ b/src/sage/schemes/affine/affine_homset.py @@ -36,7 +36,7 @@ from sage.rings.rational_field import is_RationalField from sage.categories.fields import Fields from sage.categories.number_fields import NumberFields -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing import sage.schemes.generic.homset diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index 4107fcda2c9..927b6489d9c 100644 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -37,7 +37,7 @@ from sage.rings.all import Integer from sage.arith.all import lcm, gcd from sage.rings.complex_field import ComplexField -from sage.rings.finite_rings.constructor import GF, is_PrimeFiniteField +from sage.rings.finite_rings.finite_field_constructor import GF, is_PrimeFiniteField from sage.rings.fraction_field import FractionField from sage.rings.fraction_field_element import FractionFieldElement from sage.rings.integer_ring import ZZ diff --git a/src/sage/schemes/affine/affine_space.py b/src/sage/schemes/affine/affine_space.py index f5eac681837..631fb17e050 100644 --- a/src/sage/schemes/affine/affine_space.py +++ b/src/sage/schemes/affine/affine_space.py @@ -18,7 +18,7 @@ from sage.rings.ring import is_Ring from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields _Fields = Fields() diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index ebdd36b1e94..65669e7da39 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -40,6 +40,9 @@ is_fundamental_discriminant, PolynomialRing) +from sage.misc.all import cached_function + +@cached_function def hilbert_class_polynomial(D, algorithm=None): r""" Returns the Hilbert class polynomial for discriminant `D`. @@ -153,6 +156,7 @@ def hilbert_class_polynomial(D, algorithm=None): return IntegerRing()['x'](coeffs) +@cached_function def cm_j_invariants(K, proof=None): r""" Return a list of all CM `j`-invariants in the field `K`. @@ -192,6 +196,7 @@ def cm_j_invariants(K, proof=None): """ return list(sorted([j for D,f,j in cm_j_invariants_and_orders(K, proof=proof)])) +@cached_function def cm_j_invariants_and_orders(K, proof=None): r""" Return a list of all CM `j`-invariants in the field `K`, together with the associated orders. @@ -240,6 +245,7 @@ def cm_j_invariants_and_orders(K, proof=None): for j in hilbert_class_polynomial(D*f*f).roots(K, multiplicities=False)] +@cached_function def cm_orders(h, proof=None): """ Return a list of all pairs `(D,f)` where there is a CM order of @@ -363,6 +369,7 @@ class number `h`, and the number of fundamental discriminants with # nobody knows, since I guess Watkins's is state of the art. raise NotImplementedError("largest discriminant not known for class number %s"%h) +@cached_function def discriminants_with_bounded_class_number(hmax, B=None, proof=None): """ Return dictionary with keys class numbers `h\le hmax` and values the @@ -519,6 +526,7 @@ def lb(f): return T +@cached_function def is_cm_j_invariant(j): """ Returns whether or not this is a CM `j`-invariant. @@ -572,11 +580,11 @@ def is_cm_j_invariant(j): raise NotImplementedError("is_cm_j_invariant() is only implemented for number field elements") if not j.is_integral(): return False, None - h = 1 if j in QQ else j.absolute_minpoly().degree() + jpol = PolynomialRing(QQ,'x')([-j,1]) if j in QQ else j.absolute_minpoly() + h = jpol.degree() if h>100: raise NotImplementedError("CM data only available for class numbers up to 100") for d,f in cm_orders(h): - pol = hilbert_class_polynomial(d*f**2) - if pol(j)==0: + if jpol == hilbert_class_polynomial(d*f**2): return True, (d,f) return False, None diff --git a/src/sage/schemes/elliptic_curves/constructor.py b/src/sage/schemes/elliptic_curves/constructor.py index 4f58129adb0..bce0431a878 100644 --- a/src/sage/schemes/elliptic_curves/constructor.py +++ b/src/sage/schemes/elliptic_curves/constructor.py @@ -29,7 +29,7 @@ from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.number_field.number_field import is_NumberField from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial from sage.rings.ring import is_Ring diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 7e752b218b4..b3fa3f78755 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -552,23 +552,28 @@ def __call__(self, *args, **kwds): def _reduce_point(self, R, p): r""" - Reduces a point R on an ellipitc curve to the corresponding point on - the elliptic curve reduced modulo p. Used to coerce points between + Reduces a point R on an elliptic curve to the corresponding point on + the elliptic curve reduced modulo p. + + Used to coerce points between curves when p is a factor of the denominator of one of the coordinates. - This functionality is used internally in the \code{call} method for + This functionality is used internally in the ``call`` method for elliptic curves. INPUT: - R -- a point on an elliptic curve - p -- a prime + + - R -- a point on an elliptic curve + - p -- a prime OUTPUT: - S -- the corresponding point of the elliptic curve containing R, but - reduced modulo p + + S -- the corresponding point of the elliptic curve containing + R, but reduced modulo p EXAMPLES: + Suppose we have a point with large height on a rational elliptic curve whose denominator contains a factor of 11:: diff --git a/src/sage/schemes/elliptic_curves/ell_number_field.py b/src/sage/schemes/elliptic_curves/ell_number_field.py index 35d63eaa3cd..599b56903be 100644 --- a/src/sage/schemes/elliptic_curves/ell_number_field.py +++ b/src/sage/schemes/elliptic_curves/ell_number_field.py @@ -86,23 +86,11 @@ #***************************************************************************** from ell_field import EllipticCurve_field -import ell_point -import sage.matrix.all as matrix -from sage.rings.ring import Ring -from sage.arith.all import gcd, prime_divisors, valuation -from sage.misc.all import prod -import ell_torsion from ell_generic import is_EllipticCurve - -from gp_simon import simon_two_descent +from ell_point import EllipticCurvePoint_number_field from constructor import EllipticCurve -from sage.rings.all import PolynomialRing, ZZ, QQ, RealField -import sage.misc.misc -from sage.misc.misc import verbose, forall -from sage.rings.integer import Integer - -import gal_reps_number_field - +from sage.rings.all import Ring, PolynomialRing, ZZ, QQ, RealField, Integer +from sage.misc.all import cached_method, verbose, forall, prod, union, flatten from six import reraise as raise_ class EllipticCurve_number_field(EllipticCurve_field): @@ -134,7 +122,7 @@ def __init__(self, K, ainvs): self._known_points = [] EllipticCurve_field.__init__(self, K, ainvs) - _point = ell_point.EllipticCurvePoint_number_field + _point = EllipticCurvePoint_number_field def base_extend(self, R): """ @@ -331,6 +319,7 @@ def simon_two_descent(self, verbose=0, lim1=2, lim3=4, limtriv=2, except KeyError: pass + from gp_simon import simon_two_descent t = simon_two_descent(self, verbose=verbose, lim1=lim1, lim3=lim3, limtriv=limtriv, maxprob=maxprob, limbigprime=limbigprime, @@ -515,7 +504,7 @@ def division_field(self, p, names, map=False, **kwds): # of just one point. # First factor f over F and then compute a root X of f over K. - g = prime_divisors(f)[0] + g = f.factor()[0][0] X = g.map_coefficients(F_to_K).roots(multiplicities=False)[0] # Polynomial defining the corresponding Y-coordinate @@ -598,7 +587,9 @@ def height_pairing_matrix(self, points=None, precision=None): RR = RealField() else: RR = RealField(precision) - M = matrix.MatrixSpace(RR, r) + + from sage.matrix.all import MatrixSpace + M = MatrixSpace(RR, r) mat = M() for j in range(r): mat[j,j] = points[j].height(precision=precision) @@ -828,7 +819,7 @@ def global_integral_model(self): K = self.base_field() ai = self.a_invariants() Ps = [[ ff[0] for ff in a.denominator_ideal().factor() ] for a in ai if not a.is_integral() ] - Ps = sage.misc.misc.union(sage.misc.flatten.flatten(Ps)) + Ps = union(flatten(Ps)) for P in Ps: pi = K.uniformizer(P,'positive') e = min([(ai[i].valuation(P)/[1,2,3,4,6][i]) for i in range(5)]).floor() @@ -909,7 +900,7 @@ def _scale_by_units(self): OUTPUT: - A model for this elliptic curve, optimally scaled with repect + A model for this elliptic curve, optimally scaled with respect to scaling by units, with respect to the logarithmic embedding of |c4|^(1/4)+|c6|^(1/6). No scaling by roots of unity is carried out, so there is no change when the unit rank is 0. @@ -1409,7 +1400,7 @@ def tamagawa_numbers(self): sage: eK.tamagawa_numbers() [4, 6, 1] """ - return [self.tamagawa_number(p) for p in prime_divisors(self.conductor())] + return [self.tamagawa_number(p) for p in self.conductor().prime_factors()] def tamagawa_exponent(self, P, proof = None): r""" @@ -1510,11 +1501,11 @@ def tamagawa_product_bsd(self): if self.base_field().absolute_degree() == 1: p = pp.gens_reduced()[0] f = 1 - v = valuation(ZZ(uu),p) + v = ZZ(uu).valuation(p) else: p = pp.smallest_integer() f = pp.residue_class_degree() - v = valuation(uu,pp) + v = uu.valuation(pp) uu_abs_val = p**(f*v) pr *= cv * uu_abs_val return pr @@ -2062,7 +2053,7 @@ def _torsion_bound(self,number_of_places = 20): """ E = self - bound = 0 + bound = ZZ(0) k = 0 K = E.base_field() OK = K.ring_of_integers() @@ -2087,12 +2078,13 @@ def _torsion_bound(self,number_of_places = 20): if eqq < charqq - 1 and disc.valuation(qq) == 0: Etilda = E.reduction(qq) Npp = Etilda.cardinality() - bound = gcd(bound,Npp) + bound = bound.gcd(Npp) if bound == 1: return bound k += 1 return bound + @cached_method def torsion_subgroup(self): r""" Returns the torsion subgroup of this elliptic curve. @@ -2137,12 +2129,10 @@ def torsion_subgroup(self): sage: EK.torsion_subgroup () Torsion Subgroup isomorphic to Trivial group associated to the Elliptic Curve defined by y^2 = x^3 + i*x + (i+3) over Number Field in i with defining polynomial x^2 + 1 """ - try: - return self.__torsion_subgroup - except AttributeError: - self.__torsion_subgroup = ell_torsion.EllipticCurveTorsionSubgroup(self) - return self.__torsion_subgroup + from ell_torsion import EllipticCurveTorsionSubgroup + return EllipticCurveTorsionSubgroup(self) + @cached_method def torsion_order(self): r""" Returns the order of the torsion subgroup of this elliptic curve. @@ -2182,11 +2172,7 @@ def torsion_order(self): sage: EK.torsion_order() 1 """ - try: - return self.__torsion_order - except AttributeError: - self.__torsion_order = self.torsion_subgroup().order() - return self.__torsion_order + return self.torsion_subgroup().order() def torsion_points(self): r""" @@ -2260,7 +2246,7 @@ def torsion_points(self): sage: EK.torsion_points () [(-2 : -3*i : 1), (-2 : 3*i : 1), (0 : -i : 1), (0 : i : 1), (0 : 1 : 0), (1 : 0 : 1)] """ - T = self.torsion_subgroup() # make sure it is cached + T = self.torsion_subgroup() # cached return sorted(T.points()) # these are also cached in T def rank_bounds(self, **kwds): @@ -2856,7 +2842,7 @@ class number is only `3` is that the class also contains three number):: sage: EL = E.change_ring(L) - sage: CL = EL.isogeny_class(); len(CL) # long time (~21s) + sage: CL = EL.isogeny_class(); len(CL) # long time (~121s) 6 sage: Set([EE.j_invariant() for EE in CL.curves]) == Set(pol26.roots(L,multiplicities=False)) # long time True @@ -2868,7 +2854,7 @@ class number is only `3` is that the class also contains three forms of discriminant `-104`, from which we have selected a small prime:: - sage: CL.matrix() # long time + sage: CL.matrix() # long time # random (see :trac:`19229`) [1 2 3 3 5 5] [2 1 5 5 3 3] [3 5 1 3 2 5] @@ -2878,7 +2864,7 @@ class number is only `3` is that the class also contains three To see the array of binary quadratic forms:: - sage: CL.qf_matrix() # long time + sage: CL.qf_matrix() # long time # random (see :trac:`19229`) [[[1], [2, 0, 13], [3, -2, 9], [3, -2, 9], [5, -4, 6], [5, -4, 6]], [[2, 0, 13], [1], [5, -4, 6], [5, -4, 6], [3, -2, 9], [3, -2, 9]], [[3, -2, 9], [5, -4, 6], [1], [3, -2, 9], [2, 0, 13], [5, -4, 6]], @@ -2899,6 +2885,15 @@ class number is only `3` is that the class also contains three rotated!:: sage: G.show3d(color_by_label=True) # long time + + TESTS: + + An example which failed until fixed at :trac:`19229`:: + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([a+1,1,1,0,0]) + sage: C = E.isogeny_class(); len(C) + 4 """ try: return self._isoclass @@ -3370,11 +3365,13 @@ def galois_representation(self): sage: rho.non_surjective() [5] """ - return gal_reps_number_field.GaloisRepresentation(self) + from gal_reps_number_field import GaloisRepresentation + return GaloisRepresentation(self) + @cached_method def cm_discriminant(self): """ - Returns the CM discriminant of the `j`-invariant of this curve. + Returns the CM discriminant of the `j`-invariant of this curve, or 0. OUTPUT: @@ -3408,25 +3405,15 @@ def cm_discriminant(self): sage: EllipticCurve(j=31710790944000*a^2 + 39953093016000*a + 50337742902000).cm_discriminant() -108 """ - try: - D = self._CMD - except AttributeError: - pass - else: - if D: - return D - else: - raise ValueError("%s does not have CM"%self) - from sage.schemes.elliptic_curves.cm import is_cm_j_invariant flag, df = is_cm_j_invariant(self.j_invariant()) if flag: d, f = df - self._CMD = d*f**2 - return self._CMD - self._CMD = 0 # special cached value to indicate no CM - raise ValueError("%s does not have CM"%self) + return d*f**2 + else: # no CM + return ZZ(0) + @cached_method def has_cm(self): """ Returns whether or not this curve has a CM `j`-invariant. @@ -3440,7 +3427,7 @@ def has_cm(self): .. note:: Even if `E` has CM in this sense (that its `j`-invariant is - a CM `j`-invariant), since the associated negative + a CM `j`-invariant), if the associated negative discriminant `D` is not a square in the base field `K`, the extra endomorphisms will not be defined over `K`. See also the method :meth:`has_rational_cm` which tests whether `E` @@ -3464,12 +3451,9 @@ def has_cm(self): sage: EllipticCurve(j=31710790944000*a^2 + 39953093016000*a + 50337742902000).has_cm() True """ - try: - D = self.cm_discriminant() - except ValueError: - return False - return True + return not self.cm_discriminant().is_zero() + @cached_method def has_rational_cm(self, field=None): """ Returns whether or not this curve has CM defined over its @@ -3548,9 +3532,8 @@ def has_rational_cm(self, field=None): sage: E.has_rational_cm(K.extension(x^2+108,'b')) True """ - try: - D = self.cm_discriminant() - except ValueError: + D = self.cm_discriminant() + if D.is_zero(): return False if field is None: return self.base_field()(D).is_square() diff --git a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py index 88a1fc56847..6ec92e8e661 100644 --- a/src/sage/schemes/elliptic_curves/gal_reps_number_field.py +++ b/src/sage/schemes/elliptic_curves/gal_reps_number_field.py @@ -51,11 +51,11 @@ from sage.structure.sage_object import SageObject -from sage.rings.number_field.number_field import NumberField +from sage.rings.number_field.number_field import NumberField, QuadraticField from sage.schemes.elliptic_curves.cm import cm_j_invariants from sage.rings.rational_field import QQ from sage.modules.free_module import VectorSpace -from sage.rings.finite_rings.constructor import GF +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.integer import Integer from sage.misc.functional import cyclotomic_polynomial from sage.arith.all import legendre_symbol @@ -112,8 +112,16 @@ def __repr__(self): sage: rho = E.galois_representation() sage: rho Compatible family of Galois representations associated to the Elliptic Curve defined by y^2 + y = x^3 + (-1)*x^2 + (-10)*x + (-20) over Number Field in a with defining polynomial x^2 + 1 + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([0,0,0,a,0]) + sage: E.galois_representation() + Compatible family of Galois representations associated to the CM Elliptic Curve defined by y^2 = x^3 + a*x over Number Field in a with defining polynomial x^2 - x + 1 """ - return "Compatible family of Galois representations associated to the " + repr(self.E) + if self.E.has_cm(): + return "Compatible family of Galois representations associated to the CM " + repr(self.E) + else: + return "Compatible family of Galois representations associated to the " + repr(self.E) def __eq__(self,other): @@ -193,11 +201,21 @@ def non_surjective(self, A=100): sage: rho = E.galois_representation() sage: rho.non_surjective() # long time (3s on sage.math, 2014) [0] + + TESTS: + + An example which failed until fixed at :trac:`19229`:: + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([a+1,1,1,0,0]) + sage: rho = E.galois_representation() + sage: rho.non_surjective() + [2, 3] + """ - try: - return _non_surjective(self.E, A) - except ValueError: + if self.E.has_cm(): return [0] + return _non_surjective(self.E, A) def is_surjective(self, p, A=100): r""" @@ -236,8 +254,19 @@ def is_surjective(self, p, A=100): True sage: rhoQQ.is_surjective(5) == rhoK.is_surjective(5) True - """ + For CM curves, the mod-p representation is never surjective:: + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([0,0,0,0,a]) + sage: E.has_cm() + True + sage: rho = E.galois_representation() + sage: any(rho.is_surjective(p) for p in [2,3,5,7]) + False + """ + if self.E.has_cm(): + return False return (_exceptionals(self.E, [p], A) == []) def isogeny_bound(self, A=100): @@ -281,20 +310,34 @@ def isogeny_bound(self, A=100): sage: E = EllipticCurve_from_j(K(2268945/128)) # c.f. [Sutherland12] sage: E.galois_representation().isogeny_bound() # No 7-isogeny, but... [7] + + For curves with rational CM, there are infinitely many primes + `p` for which the mod-`p` representation is reducible, and [0] + is returned:: + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([0,0,0,0,a]) + sage: E.has_rational_cm() + True + sage: rho = E.galois_representation() + sage: rho.isogeny_bound() + [0] """ + if self.E.has_rational_cm(): + return [0] + E = _over_numberfield(self.E) K = E.base_field() char = lambda P: P.smallest_integer() # cheaper than constructing the residue field - # semistable reducible primes (function raises an error for CM curves) - try: - bad_primes = _semistable_reducible_primes(E) - except ValueError: - return [0] + # semistable reducible primes (we are now not in the CM case) + bad_primes = _semistable_reducible_primes(E) + # primes of additive reduction bad_primesK = (K.ideal(E.c4()) + K.ideal(E.discriminant())).prime_factors() bad_primes += [char(P) for P in bad_primesK] + # ramified primes bad_primes += K.absolute_discriminant().prime_factors() @@ -337,11 +380,23 @@ def reducible_primes(self): [7] sage: rho.reducible_primes() [] + + For curves with rational CM, there are infinitely many primes + `p` for which the mod-`p` representation is reducible, and [0] + is returned:: + + sage: K. = NumberField(x^2-x+1) + sage: E = EllipticCurve([0,0,0,0,a]) + sage: E.has_rational_cm() + True + sage: rho = E.galois_representation() + sage: rho.reducible_primes() + [0] """ - L = self.isogeny_bound() - if L == [0]: - return L - return [l for l in L if len(self.E.isogenies_prime_degree(l))>0] + if self.E.has_rational_cm(): + return [0] + + return [l for l in self.isogeny_bound() if self.E.isogenies_prime_degree(l)] def _non_surjective(E, patience=100): r""" @@ -373,6 +428,8 @@ def _non_surjective(E, patience=100): ... ValueError: The curve E should not have CM. """ + if E.has_cm(): + raise ValueError("The curve E should not have CM.") E = _over_numberfield(E) K = E.base_field() @@ -464,6 +521,7 @@ def _maybe_borels(E, L, patience=100): sage: [len(E.isogenies_prime_degree(l)) for l in [2,3]] [1, 1] + """ E = _over_numberfield(E) K = E.base_field() @@ -525,7 +583,17 @@ def _exceptionals(E, L, patience=1000): sage: E = EllipticCurve([1, 0, ((5 + a)/2)**2, 0, 0]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._exceptionals(E, [29, 31]) [29] + + For CM curves an error is raised:: + + sage: E = EllipticCurve_from_j(1728).change_ring(K) # CM + sage: sage.schemes.elliptic_curves.gal_reps_number_field._exceptionals(E,[2,3,5]) + Traceback (most recent call last): + ... + ValueError: The curve E should not have CM. """ + if E.has_cm(): + raise ValueError("The curve E should not have CM.") E = _over_numberfield(E) K = E.base_field() @@ -743,11 +811,10 @@ def _semistable_reducible_primes(E): if P.ramification_index() != 1: continue - try: - tr = E.change_ring(P.residue_field()).trace_of_frobenius() - except ArithmeticError: # Bad reduction at P. + if E.has_bad_reduction(P): continue + tr = E.reduction(P).trace_of_frobenius() x = P.gens_reduced()[0] precomp.append((x, _tr12(tr, det))) @@ -798,8 +865,9 @@ def _semistable_reducible_primes(E): a = (Integer(phi1x + phi2x)**2 - 4 * x.norm()).squarefree_part() - y = QQ['y'].gen() - F = NumberField(y**2 - a, 'a') + # See #19229: the name given here, which is not used, should + # not be the name of the generator of the base field. + F = QuadraticField(a, 'gal_rep_nf_sqrt_a') # Next, we turn K into relative number field over F. @@ -878,7 +946,19 @@ def _possible_normalizers(E, SA): sage: E = EllipticCurve([0,0,0,-56,4848]) sage: 5 in sage.schemes.elliptic_curves.gal_reps_number_field._possible_normalizers(E, [ZZ.ideal(2)]) True + + For CM curves, an error is raised:: + + sage: K. = QuadraticField(-1) + sage: E = EllipticCurve_from_j(1728).change_ring(K) # CM + sage: sage.schemes.elliptic_curves.gal_reps_number_field._possible_normalizers(E, []) + Traceback (most recent call last): + ... + ValueError: The curve E should not have CM. + """ + if E.has_cm(): + raise ValueError("The curve E should not have CM.") E = _over_numberfield(E) K = E.base_field() @@ -982,11 +1062,6 @@ def _possible_normalizers(E, SA): if tr == 0: patience -= 1 - if patience == 0: - # We suspect E has CM, so we check: - if E.j_invariant() in cm_j_invariants(K): - raise ValueError("The curve E should not have CM.") - else: for p in tr.prime_factors(): bad_primes.add(p) diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index 286315679da..e3723185996 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -838,6 +838,16 @@ def isogenies_5_1728(E): sage: isogenies_5_1728(E) [Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^4 + 20*x^2 - 80 to Elliptic Curve defined by y^2 = x^3 + (-753/4*a^2-4399)*x + (2779*a^3+65072*a) over Number Field in a with defining polynomial x^4 + 20*x^2 - 80, Isogeny of degree 5 from Elliptic Curve defined by y^2 = x^3 + x over Number Field in a with defining polynomial x^4 + 20*x^2 - 80 to Elliptic Curve defined by y^2 = x^3 + (-753/4*a^2-4399)*x + (-2779*a^3-65072*a) over Number Field in a with defining polynomial x^4 + 20*x^2 - 80] + + See :trac:`19840`:: + + sage: K. = NumberField(x^4 - 5*x^2 + 5) + sage: E = EllipticCurve([a^2 + a + 1, a^3 + a^2 + a + 1, a^2 + a, 17*a^3 + 34*a^2 - 16*a - 37, 54*a^3 + 105*a^2 - 66*a - 135]) + sage: len(E.isogenies_prime_degree(5)) + 2 + sage: from sage.schemes.elliptic_curves.isogeny_small_degree import isogenies_5_1728 + sage: [phi.codomain().j_invariant() for phi in isogenies_5_1728(E)] + [19691491018752*a^2 - 27212977933632, 19691491018752*a^2 - 27212977933632] """ F = E.base_field() if E.j_invariant() != 1728: @@ -865,7 +875,7 @@ def isogenies_5_1728(E): # Type 2: if 5 is a square we have up to 4 (non-endomorphism) isogenies if square5: betas = sorted((x**4+20*a*x**2-80*a**2).roots(multiplicities=False)) - gammas = [a*(beta**2-2)/6 for beta in betas] + gammas = [(beta**2-2*a)/6 for beta in betas] isogs += [Ew.isogeny(x**2+beta*x+gamma, model=model) for beta,gamma in zip(betas,gammas)] [isog.set_pre_isomorphism(iso) for isog in isogs] return isogs diff --git a/src/sage/schemes/elliptic_curves/lseries_ell.py b/src/sage/schemes/elliptic_curves/lseries_ell.py index 551984a5506..11eb6e10f5a 100644 --- a/src/sage/schemes/elliptic_curves/lseries_ell.py +++ b/src/sage/schemes/elliptic_curves/lseries_ell.py @@ -79,8 +79,6 @@ def taylor_series(self, a=1, prec=53, series_prec=6, var='z'): sage: L.taylor_series(series_prec=3) -1.27685190980159e-23 + (7.23588070754027e-24)*z + 0.759316500288427*z^2 + O(z^3) # 32-bit -2.72911738151096e-23 + (1.54658247036311e-23)*z + 0.759316500288427*z^2 + O(z^3) # 64-bit - sage: L.taylor_series(series_prec=3)[2:] - 0.000000000000000 + 0.000000000000000*z + 0.759316500288427*z^2 + O(z^3) """ D = self.dokchitser(prec) return D.taylor_series(a, series_prec, var) diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 7db1e72c54a..3afc109a169 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -138,8 +138,10 @@ from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField +from sage.rings.fraction_field import FractionField +from sage.misc.all import prod from sage.misc.cachefunc import cached_method from sage.misc.latex import latex from sage.misc.misc import is_iterator @@ -2651,7 +2653,7 @@ class AlgebraicScheme_subscheme_product_projective(AlgebraicScheme_subscheme_pro @cached_method def segre_embedding(self, PP=None): r""" - Return the Segre embedding of ``self`` into the appropriate projective + Return the Segre embedding of this subscheme into the appropriate projective space. INPUT: @@ -2661,15 +2663,11 @@ def segre_embedding(self, PP=None): OUTPUT: - Hom from ``self`` to the appropriate subscheme of projective space - - .. TODO:: - - products with more than two components + Hom from this subscheme to the appropriate subscheme of projective space EXAMPLES:: - sage: X. = ProductProjectiveSpaces([2,2],QQ) + sage: X. = ProductProjectiveSpaces([2,2], QQ) sage: P = ProjectiveSpace(QQ,8,'t') sage: L = (-w - v)*x + (-w*y - u*z) sage: Q = (-u*w - v^2)*x^2 + ((-w^2 - u*w + (-u*v - u^2))*y + (-w^2 - u*v)*z)*x + \ @@ -2678,12 +2676,66 @@ def segre_embedding(self, PP=None): sage: phi = W.segre_embedding(P) sage: phi.codomain().ambient_space() == P True + + :: + + sage: PP. = ProductProjectiveSpaces([1,1,1], CC) + sage: PP.subscheme([]).segre_embedding() + Scheme morphism: + From: Closed subscheme of Product of projective spaces P^1 x P^1 x P^1 + over Complex Field with 53 bits of precision defined by: + (no polynomials) + To: Closed subscheme of Projective Space of dimension 7 over Complex + Field with 53 bits of precision defined by: + -u5*u6 + u4*u7, + -u3*u6 + u2*u7, + -u3*u4 + u2*u5, + -u3*u5 + u1*u7, + -u3*u4 + u1*u6, + -u3*u4 + u0*u7, + -u2*u4 + u0*u6, + -u1*u4 + u0*u5, + -u1*u2 + u0*u3 + Defn: Defined by sending (x : y , u : v , s : t) to + (x*u*s : x*u*t : x*v*s : x*v*t : y*u*s : y*u*t : y*v*s : y*v*t). + + :: + + sage: PP. = ProductProjectiveSpaces([2,1,1], ZZ) + sage: PP.subscheme([x^3, u-v, s^2-t^2]).segre_embedding() + Scheme morphism: + From: Closed subscheme of Product of projective spaces P^2 x P^1 x P^1 + over Integer Ring defined by: + x^3, + u - v, + s^2 - t^2 + To: Closed subscheme of Projective Space of dimension 11 over + Integer Ring defined by: + u10^2 - u11^2, + u9 - u11, + u8 - u10, + -u7*u10 + u6*u11, + u6*u10 - u7*u11, + u6^2 - u7^2, + u5 - u7, + u4 - u6, + u3^3, + -u3*u10 + u2*u11, + u2*u10 - u3*u11, + -u3*u6 + u2*u7, + u2*u6 - u3*u7, + u2*u3^2, + u2^2 - u3^2, + u1 - u3, + u0 - u2 + Defn: Defined by sending (x : y : z , u : v , s : t) to + (x*v*s : x*v*t : x*v*s : x*v*t : y*v*s : y*v*t : y*v*s : y*v*t : + z*v*s : z*v*t : z*v*s : z*v*t). """ AS = self.ambient_space() + CR = AS.coordinate_ring() N = AS.dimension_relative_components() - if len(N) > 2: - raise NotImplementedError("Cannot have more than two components.") - M = (N[0]+1)*(N[1]+1)-1 + M = prod([n+1 for n in N]) - 1 vars = list(AS.coordinate_ring().variable_names()) + ['u' + str(i) for i in range(M+1)] from sage.rings.all import PolynomialRing @@ -2692,14 +2744,19 @@ def segre_embedding(self, PP=None): #set-up the elimination for the segre embedding mapping = [] k = AS.ngens() - for i in range(N[0]+1): - for j in range(N[0]+1, N[0]+N[1]+2): - mapping.append(R.gen(k)-R(AS.gen(i)*AS.gen(j))) - k+=1 + index = AS.num_components()*[0] + for count in range(M + 1): + mapping.append(R.gen(k+count)-prod([CR(AS[i].gen(index[i])) for i in range(len(index))])) + for i in range(len(index)-1, -1, -1): + if index[i] == N[i]: + index[i] = 0 + else: + index[i] += 1 + break #only increment once #change the defining ideal of the subscheme into the variables I = R.ideal(list(self.defining_polynomials()) + mapping) - J =I.groebner_basis() + J = I.groebner_basis() s = set(R.gens()[:AS.ngens()]) n = len(J)-1 L = [] @@ -2713,24 +2770,30 @@ def segre_embedding(self, PP=None): PS = ProjectiveSpace(self.base_ring(), M, R.gens()[AS.ngens():]) Y = PS.subscheme(L) else: - if PP.dimension_relative()!= M: - raise ValueError("Projective Space %s must be dimension %s")%(PP, M) + if PP.dimension_relative() != M: + raise ValueError("projective space %s must be dimension %s")%(PP, M) S = PP.coordinate_ring() - psi = R.hom([0]*(N[0]+N[1]+2) + list(S.gens()), S) + psi = R.hom([0]*k + list(S.gens()), S) L = [psi(l) for l in L] Y = PP.subscheme(L) #create embedding for points mapping = [] - for i in range(N[0]+1): - for j in range(N[0]+1,N[0]+N[1]+2): - mapping.append(AS.gen(i)*AS.gen(j)) + index = AS.num_components()*[0] + for count in range(M + 1): + mapping.append(prod([CR(AS[i].gen(index[i])) for i in range(len(index))])) + for i in range(len(index)-1, -1, -1): + if index[i] == N[i]: + index[i] = 0 + else: + index[i] += 1 + break #only increment once phi = self.hom(mapping, Y) return phi def dimension(self): - """ + r""" Return the dimension of the algebraic subscheme. OUTPUT: @@ -2746,12 +2809,49 @@ def dimension(self): sage: W = X.subscheme([L,Q]) sage: W.dimension() 2 + + :: + + sage: PP. = ProductProjectiveSpaces([2,1,1], QQ) + sage: X = PP.subscheme([x^3, x^5+y^5, z^6, x*u-v*y, s^2-t^2]) + sage: X.dimension() + -1 + + :: + + sage: PP = ProductProjectiveSpaces([2,1,3], CC, 't') + sage: PP.subscheme([]).dimension() + 6 + + :: + + sage: PP = ProductProjectiveSpaces([1,3,1], ZZ, 't') + sage: PP.subscheme([]).dimension() + 5 + + :: + + sage: PP. = ProductProjectiveSpaces([1,1,1], CC) + sage: X = PP.subscheme([x^2-y^2, u-v, s^2-t^2]) + sage: X.dimension() + 0 """ try: return self.__dimension except AttributeError: - phi = self.segre_embedding() - self.__dimension = phi.codomain().defining_ideal().dimension()-1 + try: + #move to field to compute radical + X = self.change_ring(FractionField(self.base_ring())) + PP = X.ambient_space() + I = X.defining_ideal().radical() + #check if the irrelevant ideal of any component is in the radical + if any([all([t in I for t in PS.gens()]) for PS in PP]): + self.__dimension = -1 + else: + self.__dimension = I.dimension() - PP.num_components() + except TypeError: #cannot compute radical for this base ring + phi = self.segre_embedding() + self.__dimension = phi.codomain().defining_ideal().dimension() - 1 return self.__dimension def is_smooth(self, point=None): diff --git a/src/sage/schemes/generic/ambient_space.py b/src/sage/schemes/generic/ambient_space.py index 72cfdaa000d..1d5d6db492b 100644 --- a/src/sage/schemes/generic/ambient_space.py +++ b/src/sage/schemes/generic/ambient_space.py @@ -5,16 +5,17 @@ #***************************************************************************** # Copyright (C) 2006 William Stein # -# Distributed under the terms of the GNU General Public License (GPL) -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.all import Integer, ZZ -from sage.rings.commutative_ring import is_CommutativeRing - +from sage.rings.all import Integer, ZZ, CommutativeRing from sage.schemes.generic.scheme import Scheme + def is_AmbientSpace(x): """ Return True if `x` is an ambient space. @@ -51,7 +52,7 @@ def __init__(self, n, R=ZZ): sage: A = AmbientSpace(5, ZZ) sage: TestSuite(A).run() # not tested (abstract scheme with no elements?) """ - if not is_CommutativeRing(R): + if not isinstance(R, CommutativeRing): raise TypeError("R (=%s) must be a commutative ring"%R) n = Integer(n) if n < 0: @@ -252,7 +253,7 @@ def base_extend(self, R): ValueError: no natural map from the base ring (=Rational Field) to R (=Finite Field of size 5)! """ - if is_CommutativeRing(R): + if isinstance(R, CommutativeRing): if self.base_ring() == R: return self if not R.has_coerce_map_from(self.base_ring()): diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index e7628c1c640..e435a243bf9 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -40,13 +40,12 @@ from sage.categories.homset import HomsetWithBase from sage.structure.factory import UniqueFactory -from sage.rings.all import ZZ, QQ +from sage.rings.all import ZZ, QQ, CommutativeRing from sage.arith.all import gcd from sage.rings.morphism import is_RingHomomorphism from sage.rings.rational_field import is_RationalField -from sage.rings.finite_rings.constructor import is_FiniteField -from sage.rings.commutative_ring import is_CommutativeRing +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.schemes.generic.scheme import AffineScheme, is_AffineScheme from sage.schemes.generic.morphism import ( @@ -154,14 +153,14 @@ def create_key_and_extra_args(self, X, Y, category=None, base=ZZ, 'base_ring': Integer Ring, 'check': False} """ - if is_CommutativeRing(X): + if isinstance(X, CommutativeRing): X = AffineScheme(X) - if is_CommutativeRing(Y): + if isinstance(Y, CommutativeRing): Y = AffineScheme(Y) if is_AffineScheme(base): base_spec = base base_ring = base.coordinate_ring() - elif is_CommutativeRing(base): + elif isinstance(base, CommutativeRing): base_spec = AffineScheme(base) base_ring = base else: diff --git a/src/sage/schemes/generic/morphism.py b/src/sage/schemes/generic/morphism.py index cb55100bca8..447a705326b 100644 --- a/src/sage/schemes/generic/morphism.py +++ b/src/sage/schemes/generic/morphism.py @@ -83,7 +83,6 @@ from sage.categories.homset import Homset, Hom, End from sage.categories.number_fields import NumberFields from sage.rings.all import Integer, CIF -from sage.rings.commutative_ring import is_CommutativeRing from sage.rings.fraction_field import FractionField from sage.rings.fraction_field_element import FractionFieldElement from sage.rings.morphism import is_RingHomomorphism diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index 8889deaa9d9..aa15ecaf8c6 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -25,9 +25,7 @@ from sage.misc.all import cached_method from sage.rings.all import (IntegerRing, ZZ, GF, PowerSeriesRing, - Rationals) - -from sage.rings.commutative_ring import is_CommutativeRing + Rationals, CommutativeRing) from sage.rings.ideal import is_Ideal from sage.rings.morphism import is_RingHomomorphism from sage.structure.unique_representation import UniqueRepresentation @@ -114,7 +112,7 @@ def __init__(self, X=None, category=None): self._base_scheme = X elif is_SchemeMorphism(X): self._base_morphism = X - elif is_CommutativeRing(X): + elif isinstance(X, CommutativeRing): self._base_ring = X elif is_RingHomomorphism(X): self._base_ring = X.codomain() @@ -257,7 +255,7 @@ def __call__(self, *args): if len(args) == 1: from sage.schemes.generic.morphism import SchemeMorphism_point S = args[0] - if is_CommutativeRing(S): + if isinstance(S, CommutativeRing): return self.point_homset(S) elif is_Scheme(S): return S.Hom(self) diff --git a/src/sage/schemes/hyperelliptic_curves/constructor.py b/src/sage/schemes/hyperelliptic_curves/constructor.py index 69678d4201d..c0ec6f2b656 100644 --- a/src/sage/schemes/hyperelliptic_curves/constructor.py +++ b/src/sage/schemes/hyperelliptic_curves/constructor.py @@ -22,7 +22,7 @@ from sage.rings.padics.all import is_pAdicField from sage.rings.rational_field import is_RationalField -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.polynomial.polynomial_element import is_Polynomial diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 703ffb8cf5e..94a01cecc6b 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -747,7 +747,7 @@ def points(self): $\mathbb{P}(1,3,1)$. The latter model has two points at infinity: $(1:1:0)$ and $(1:-1:0)$. """ - from sage.rings.finite_rings.constructor import zech_log_bound + from sage.rings.finite_rings.finite_field_constructor import zech_log_bound try: return self.__points except AttributeError: pass diff --git a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py index b18325f07f9..d79f7e55151 100644 --- a/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py +++ b/src/sage/schemes/hyperelliptic_curves/monsky_washnitzer.py @@ -1731,10 +1731,9 @@ def matrix_of_frobenius(Q, p, M, trace=None, compute_exact_forms=False): from sage.schemes.hyperelliptic_curves.constructor import HyperellipticCurve from sage.schemes.hyperelliptic_curves.hyperelliptic_generic import is_HyperellipticCurve from sage.rings.padics.all import pAdicField -from sage.rings.all import QQ +from sage.rings.all import QQ, IntegralDomain from sage.rings.laurent_series_ring import is_LaurentSeriesRing -from sage.rings.integral_domain import is_IntegralDomain from sage.modules.free_module import FreeModule from sage.modules.free_module_element import is_FreeModuleElement @@ -2911,7 +2910,7 @@ def helper_matrix(self): for i in range(n): L.append((y*x**i).diff().extract_pow_y(0)) A = matrix(L).transpose() - if not is_IntegralDomain(A.base_ring()): + if not isinstance(A.base_ring(), IntegralDomain): # must be using integer_mod or something to approximate self._helper_matrix = (~A.change_ring(QQ)).change_ring(A.base_ring()) else: diff --git a/src/sage/schemes/plane_conics/constructor.py b/src/sage/schemes/plane_conics/constructor.py index fa3e0e42bd1..27d3f8005fb 100644 --- a/src/sage/schemes/plane_conics/constructor.py +++ b/src/sage/schemes/plane_conics/constructor.py @@ -28,11 +28,11 @@ from sage.modules.free_module_element import vector from sage.quadratic_forms.quadratic_form import is_QuadraticForm from sage.rings.all import PolynomialRing -from sage.rings.finite_rings.constructor import is_PrimeFiniteField +from sage.rings.finite_rings.finite_field_constructor import is_PrimeFiniteField -from sage.rings.integral_domain import is_IntegralDomain +from sage.rings.ring import IntegralDomain from sage.rings.rational_field import is_RationalField -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial from sage.rings.number_field.number_field import is_NumberField @@ -139,7 +139,7 @@ def Conic(base_field, F=None, names=None, unique=True): sage: Conic([a([x,x^2]) for x in range(5)]) Projective Conic Curve over Finite Field of size 13 defined by x^2 - y*z """ - if not (is_IntegralDomain(base_field) or base_field is None): + if not (base_field is None or isinstance(base_field, IntegralDomain)): if names is None: names = F F = base_field @@ -169,7 +169,7 @@ def Conic(base_field, F=None, names=None, unique=True): if len(C) != 3: raise TypeError("points in F (=%s) must be planar" % F) P = C.universe() - if not is_IntegralDomain(P): + if not isinstance(P, IntegralDomain): raise TypeError("coordinates of points in F (=%s) must " \ "be in an integral domain" % F) L.append(Sequence([C[0]**2, C[0]*C[1], C[0]*C[2], C[1]**2, @@ -209,7 +209,7 @@ def Conic(base_field, F=None, names=None, unique=True): if base_field is None: base_field = F.base_ring() - if not is_IntegralDomain(base_field): + if not isinstance(base_field, IntegralDomain): raise ValueError("Base field (=%s) must be a field" % base_field) base_field = base_field.fraction_field() if names is None: diff --git a/src/sage/schemes/plane_curves/constructor.py b/src/sage/schemes/plane_curves/constructor.py index 884fc87403f..43f1dca2fe4 100644 --- a/src/sage/schemes/plane_curves/constructor.py +++ b/src/sage/schemes/plane_curves/constructor.py @@ -25,7 +25,7 @@ from sage.rings.polynomial.multi_polynomial_element import is_MPolynomial from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.structure.all import Sequence @@ -155,7 +155,7 @@ def Curve(F): sage: Curve(x^2+y^2+z) Traceback (most recent call last): ... - TypeError: x^2 + y^2 + z is not a homogeneous polynomial! + TypeError: x^2 + y^2 + z is not a homogeneous polynomial The defining polynomial must always be nonzero:: diff --git a/src/sage/schemes/product_projective/morphism.py b/src/sage/schemes/product_projective/morphism.py index 70fcb0c3d3f..109acb50ce4 100644 --- a/src/sage/schemes/product_projective/morphism.py +++ b/src/sage/schemes/product_projective/morphism.py @@ -9,7 +9,7 @@ sage: H = End(P1xP1) sage: H([x^2*u, y^2*v, x*v^2, y*u^2]) Scheme endomorphism of Product of projective spaces P^1 x P^1 over Rational Field - Defn: Defined by sending (x : y , u : v) to + Defn: Defined by sending (x : y , u : v) to (x^2*u : y^2*v , x*v^2 : y*u^2). """ #***************************************************************************** @@ -36,11 +36,11 @@ class ProductProjectiveSpaces_morphism_ring(SchemeMorphism_polynomial): sage: H = T.Hom(T) sage: H([x^2,y^2,z^2,w^2,u^2]) Scheme endomorphism of Product of projective spaces P^2 x P^1 over Rational Field - Defn: Defined by sending (x : y : z , w : u) to + Defn: Defined by sending (x : y : z , w : u) to (x^2 : y^2 : z^2 , w^2 : u^2). """ - def __init__(self, parent, polys, check = True): + def __init__(self, parent, polys, check=True): r""" The Python constructor. @@ -59,7 +59,7 @@ def __init__(self, parent, polys, check = True): sage: H = T.Hom(T) sage: H([x^2*u,y^2*w,z^2*u,w^2,u^2]) Scheme endomorphism of Product of projective spaces P^2 x P^1 over Rational Field - Defn: Defined by sending (x : y : z , w : u) to + Defn: Defined by sending (x : y : z , w : u) to (x^2*u : y^2*w : z^2*u , w^2 : u^2). :: @@ -71,6 +71,30 @@ def __init__(self, parent, polys, check = True): ... TypeError: polys (=[x^2*u, y^2*w, z^2*u, w^2, z*u]) must be multi-homogeneous of the same degrees (by component) + + :: + + sage: R. = PolynomialRing(QQ) + sage: Z. = ProductProjectiveSpaces([1,2],QQ) + sage: P. = ProductProjectiveSpaces([3,1],QQ) + sage: H = Hom(Z,P) + sage: f = H([a^2,b^2,a^2,a*b,a*x,b*z]); f + Scheme morphism: + From: Product of projective spaces P^1 x P^2 over Rational Field + To: Product of projective spaces P^3 x P^1 over Rational Field + Defn: Defined by sending (a : b , x : y : z) to + (a^2 : b^2 : a^2 : a*b , a*x : b*z). + + :: + + sage: Z. = ProductProjectiveSpaces([1,3],QQ) + sage: P. = ProductProjectiveSpaces([2,2],QQ) + sage: H = Hom(Z,P) + sage: f = H([a^2,b^2,c^2,x^2,y^2,z^2]) + Traceback (most recent call last): + ... + TypeError: polys (=[a^2, b^2, c^2, x^2, y^2, z^2]) must be + multi-homogeneous of the same degrees (by component) """ if check: #check multi-homogeneous @@ -81,12 +105,13 @@ def __init__(self, parent, polys, check = True): polys = [f.lift() for f in polys] target = parent.codomain().ambient_space() + dom = parent.domain().ambient_space() from sage.schemes.product_projective.space import is_ProductProjectiveSpaces if is_ProductProjectiveSpaces(target): splitpolys = target._factors(polys) for m in range(len(splitpolys)): - d = target._degree(splitpolys[m][0]) - if not all(d == target._degree(f) for f in splitpolys[m]): + d = dom._degree(splitpolys[m][0]) + if not all(d == dom._degree(f) for f in splitpolys[m]): raise TypeError("polys (=%s) must be multi-homogeneous of the same degrees (by component)"%polys) else: #we are mapping into some other kind of space @@ -121,7 +146,7 @@ def _repr_defn(self): Return a string representation of ``self``. OUTPUT: - + String. EXAMPLES:: @@ -139,7 +164,7 @@ def _repr_defn(self): s += '.' return s - def __call__(self, P, check = True): + def __call__(self, P, check=True): r""" Make morphisms of products of projective spaces callable. @@ -161,9 +186,52 @@ def __call__(self, P, check = True): sage: F = H([x^2*u,y^2*w,z^2*u,w^2,u^2]) sage: F(T([2,1,3,0,1])) (4/9 : 0 : 1 , 0 : 1) + + :: + + sage: PP. = ProductProjectiveSpaces(QQ,[1,1,1]) + sage: HP = End(PP) + sage: f = HP([v*x^2,w*y^2,z^2,u^2,v^2,w^2]) + sage: Q = PP([0,1,1,1,1,1]) + sage: f(Q) + (0 : 1 , 1 : 1 , 1 : 1) + + :: + + sage: PP. = ProductProjectiveSpaces([2,1], ZZ) + sage: Q = PP([1,1,1,2,1]) + sage: Z. = ProductProjectiveSpaces([1,2], ZZ) + sage: H = End(Z) + sage: f = H([a^3, b^3+a*b^2, x^2, y^2-z^2, z*y]) + sage: f(Q) + Traceback (most recent call last): + ... + TypeError: (1 : 1 : 1 , 2 : 1) fails to convert into the map's domain + Product of projective spaces P^1 x P^2 over Integer Ring, but a + `pushforward` method is not properly implemented + sage: f([1,1,1,2,1]) + (1 : 2 , 1 : 3 : 2) + + :: + + sage: PP. = ProductProjectiveSpaces(ZZ, [1,1]) + sage: HP = End(PP) + sage: g = HP([x^2, y^2, u^2, v^2]) + sage: g([0,0,0,0],check=False) + (0 : 0 , 0 : 0) """ + from sage.schemes.product_projective.point import ProductProjectiveSpaces_point_ring + if check: + if not isinstance(P, ProductProjectiveSpaces_point_ring): + try: + P = self.domain()(P) + except (TypeError, NotImplementedError): + raise TypeError("%s fails to convert into the map's domain %s, but a `pushforward` method is not properly implemented"%(P, self.domain())) + elif self.domain()!= P.codomain(): + raise TypeError("%s fails to convert into the map's domain %s, but a `pushforward` method is not properly implemented"%(P, self.domain())) + A = self.codomain() - Q = P[0]._coords + P[1]._coords + Q = list(P) newP = [f(Q) for f in self.defining_polynomials()] return(A.point(newP, check)) @@ -198,7 +266,7 @@ def is_morphism(self): sage: P. = ProductProjectiveSpaces([2,1],QQ) sage: Q. = ProductProjectiveSpaces([1,2],QQ) sage: H = Hom(P,Q) - sage: f = H([x^2,y^2,z^3,w^3,u^3]) + sage: f = H([x^2,y^2,u^3,w^3,u^3]) sage: f.is_morphism() False """ @@ -317,7 +385,7 @@ def nth_iterate_map(self, n): sage: f.nth_iterate_map(3) Scheme endomorphism of Product of projective spaces P^1 x P^2 over Rational Field - Defn: Defined by sending (a : b , x : y : z) to + Defn: Defined by sending (a : b , x : y : z) to (a^27 : b^27 , x^8 : y^8 : z^8). """ if not self.is_endomorphism(): diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index e275e400920..b959a0de6e5 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -27,20 +27,22 @@ Defining x0, x1, x2, y0, y1, y2 """ #***************************************************************************** -# Copyright (C) 2014 Volker Braun -# Ben Hutz +# Copyright (C) 2014 Volker Braun +# Copyright (C) 2014 Ben Hutz # -# Distributed under the terms of the GNU General Public License (GPL) -# as published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# http://www.gnu.org/licenses/ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ #***************************************************************************** + import six from sage.misc.cachefunc import cached_method -from sage.rings.all import (PolynomialRing, ZZ, QQ, Integer) -from sage.rings.commutative_ring import is_CommutativeRing +from sage.rings.all import (PolynomialRing, ZZ, QQ, Integer, + CommutativeRing) from sage.rings.polynomial.polydict import ETuple from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme_product_projective @@ -131,7 +133,7 @@ def ProductProjectiveSpaces(n, R=None, names='x'): n, R = R, n if not isinstance(n,(list,tuple)): raise ValueError("Need list or tuple of dimensions") - if not is_CommutativeRing(R): + if not isinstance(R, CommutativeRing): raise ValueError("Must be a commutative ring") from sage.structure.category_object import normalize_names n_vars=sum(d+1 for d in n) @@ -208,7 +210,7 @@ def __init__(self, N, R = QQ, names = None): """ assert isinstance(N, (tuple, list)) N = [Integer(n) for n in N] - assert is_CommutativeRing(R) + assert isinstance(R, CommutativeRing) if len(N) < 2: raise ValueError("Must be at least two components for a product") AmbientSpace.__init__(self, sum(N), R) @@ -512,6 +514,8 @@ def _degree(self, polynomial): ValueError: polynomial is not multi-homogeneous """ E = polynomial.exponents() + if len(E) == 0: + return [] d = [sum(t) for t in self._factors(E[0])] for k in range(len(E)): if not all([d == [sum(t) for t in self._factors(E[k])]]): diff --git a/src/sage/schemes/product_projective/wehlerK3.py b/src/sage/schemes/product_projective/wehlerK3.py index 005b26b9676..53d448cf3b0 100644 --- a/src/sage/schemes/product_projective/wehlerK3.py +++ b/src/sage/schemes/product_projective/wehlerK3.py @@ -29,6 +29,15 @@ """ +#***************************************************************************** +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + import sys from sage.calculus.functions import jacobian from sage.categories.fields import Fields @@ -37,9 +46,8 @@ from sage.functions.all import sqrt from sage.misc.cachefunc import cached_method from sage.misc.mrange import xmrange -from sage.rings.all import Integer -from sage.rings.commutative_ring import is_CommutativeRing -from sage.rings.finite_rings.constructor import GF +from sage.rings.all import Integer, CommutativeRing +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.fraction_field import FractionField from sage.rings.integer_ring import ZZ @@ -87,7 +95,7 @@ def WehlerK3Surface(polys): return WehlerK3Surface_finite_field(polys) else: return WehlerK3Surface_field(polys) - elif is_CommutativeRing(R): + elif isinstance(R, CommutativeRing): return WehlerK3Surface_ring(polys) else: raise TypeError( "R ( = %s) must be a commutative ring"%R) diff --git a/src/sage/schemes/projective/endPN_automorphism_group.py b/src/sage/schemes/projective/endPN_automorphism_group.py index 0bc5850bdca..09cc235d9ab 100644 --- a/src/sage/schemes/projective/endPN_automorphism_group.py +++ b/src/sage/schemes/projective/endPN_automorphism_group.py @@ -5,7 +5,7 @@ - Xander Faber, Michelle Manes, Bianca Viray: algorithm and original code "Computing Conjugating Sets and Automorphism Groups of Rational Functions" by - Xander Faber, Michelle Manes, and Bianca Viray [FMV] + Xander Faber, Michelle Manes, and Bianca Viray [FMV]_. - Joao de Faria, Ben Hutz, Bianca Thompson (11-2013): adaption for inclusion in Sage @@ -20,32 +20,32 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from copy import copy -from sage.combinat.subset import Subsets -from sage.functions.all import sqrt -from itertools import permutations, combinations -from sage.matrix.constructor import matrix -from sage.matrix.matrix import is_Matrix -from sage.misc.functional import squarefree_part -from sage.misc.misc_c import prod -from sage.rings.finite_rings.constructor import GF +from copy import copy +from sage.combinat.subset import Subsets +from sage.functions.all import sqrt +from itertools import permutations, combinations +from sage.matrix.constructor import matrix +from sage.matrix.matrix import is_Matrix +from sage.misc.functional import squarefree_part +from sage.misc.misc_c import prod +from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.finite_rings.integer_mod_ring import Integers -from sage.rings.fraction_field import FractionField -from sage.rings.integer_ring import ZZ +from sage.rings.fraction_field import FractionField +from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import NumberField from sage.arith.all import gcd, lcm, CRT, is_square, divisors from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.rational_field import QQ -from sage.sets.primes import Primes +from sage.rings.rational_field import QQ +from sage.sets.primes import Primes -def automorphism_group_QQ_fixedpoints(rational_function, return_functions = False, iso_type=False): +def automorphism_group_QQ_fixedpoints(rational_function, return_functions=False, iso_type=False): r""" This function will compute the automorphism group for ``rational_function`` via the method of fixed points ALGORITHM: - See Algorithm 3 in Faber-Manes-Viray [FMV] + See Algorithm 3 in Faber-Manes-Viray [FMV]_. INPUT: @@ -100,7 +100,7 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions = Fals F = R.base_ring() if F != QQ and F!= ZZ: - raise TypeError("Coefficient ring is not the rational numbers or the integers") + raise TypeError("coefficient ring is not the rational numbers or the integers") z = R.gen(0) phi = R.fraction_field()(rational_function) @@ -131,10 +131,10 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions = Fals #check if infinity is a fixed point if g.degree() < d: #then infinity is a fixed point - #find elements in W of the form (infinty, y) + #find elements in W of the form (infinity, y) #where W is the set of F-rational points (x,y) such that #x is fixed by phi and phi(y)=x - for T in g.roots(multiplicities = False): + for T in g.roots(multiplicities=False): alpha = T zeta = -1 s = (zeta*z + alpha*(1 - zeta)) @@ -168,7 +168,7 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions = Fals elements.append(s) else: elements.append(matrix(F, 2, [zeta, alpha*(1-zeta), 0, 1])) - for T in preimage.roots(multiplicities = False): + for T in preimage.roots(multiplicities=False): if T != S[0]: zeta = -1 alpha = S[0] @@ -202,7 +202,7 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions = Fals psi = phi(phi(z)) f2 = psi.numerator() g2 = psi.denominator() - period2_points = [x for x in (f2 - z*g2).roots(multiplicities = False) if not x in rational_roots] + period2_points = [x for x in (f2 - z*g2).roots(multiplicities=False) if not x in rational_roots] for S in Subsets(period2_points, 2): zeta = -1 alpha = S[0] @@ -280,25 +280,26 @@ def automorphism_group_QQ_fixedpoints(rational_function, return_functions = Fals def height_bound(polynomial): r""" - Compute the maximum height of the coefficients of an automorphism. This - sets the termination criteria for the Chinese Remainder Theorem step. + Compute the maximum height of the coefficients of an automorphism. + + This bounds sets the termination criteria for the Chinese Remainder Theorem step. Let `f` be a square-free polynomial with coefficients in `K` Let `F` be an automorphism of `\mathbb{P}^1_{Frac(R)}` that permutes the roots of `f` This function returns a bound on the height of `F`, when viewed as an element of `\mathbb{P}^3` - In [FMV] it is proven that `ht(F) <= 6^{[K:Q]}*M`, where `M` is the Mahler measure of `f` + In [FMV]_ it is proven that `ht(F) <= 6^{[K:Q]}*M`, where `M` is the Mahler measure of `f` M is bounded above by `H(f)`, so we return the floor of `6*H(f)` (since `ht(F)` is an integer) INPUT: - - ``polynomial`` -- a univariate polynomial + - ``polynomial`` -- a univariate polynomial. OUTPUT: - - a positive integer + - a positive integer. EXAMPLES:: @@ -318,7 +319,7 @@ def height_bound(polynomial): F = R.base_ring() if F != QQ and F!= ZZ: - raise TypeError("coefficient ring is not the rational numbers or the integers!") + raise TypeError("coefficient ring is not the rational numbers or the integers") # scale polynomial so that it has integer coefficients with gcd 1 # this ensures that H(f) = H_infinity(f) @@ -337,11 +338,11 @@ def PGL_repn(rational_function): INPUT: - - ``rational_function`` -- a linear fraction transformation + - ``rational_function`` -- a linear fraction transformation. OUTPUT: - - a 2x2 matrix representing ``rational_function`` + - a 2x2 matrix representing ``rational_function``. EXAMPLES:: @@ -366,16 +367,17 @@ def PGL_repn(rational_function): def PGL_order(A): r""" Find the multiplicative order of a linear fractional transformation that - has a finite order as an element of `PGL_2(R)`. ``A`` can be represented - either as a rational function or a 2x2 matrix + has a finite order as an element of `PGL_2(R)`. + + ``A`` can be represented either as a rational function or a 2x2 matrix INPUT: - - ``A`` -- a linear fractional transformation + - ``A`` -- a linear fractional transformation. OUTPUT: - - a positive integer + - a positive integer. EXAMPLES:: @@ -403,15 +405,17 @@ def PGL_order(A): def CRT_helper(automorphisms, moduli): r""" + Lift the given list of automorphisms to `Zmod(M)`. + Given a list of automorphisms over various `Zmod(p^k)` find a list of automorphisms over `Zmod(M)` where `M=\prod p^k` that surjects onto every tuple of automorphisms from the various `Zmod(p^k)`. INPUT: - - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)` + - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)`. - - ``moduli`` -- list of the various `p^k` + - ``moduli`` -- list of the various `p^k`. OUTPUT: @@ -450,21 +454,23 @@ def CRT_helper(automorphisms, moduli): def CRT_automorphisms(automorphisms, order_elts, degree, moduli): r""" + Compute a maximal list of automorphisms over `Zmod(M)`. + Given a list of automorphisms over various `Zmod(p^k)`, a list of the elements orders, an integer degree, and a list of the `p^k` values compute a maximal list of automorphisms over `Zmod(M)`, such that for every `j` in `len(moduli)`, - each element reduces mod ``moduli[j]`` to one of the elements in `automorphisms[j]` that + each element reduces mod ``moduli[j]`` to one of the elements in ``automorphisms[j]`` that has order = ``degree`` INPUT: - - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)` + - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)`. - - ``order_elts`` -- a list of lists of the orders of the elements of ``automorphisms`` + - ``order_elts`` -- a list of lists of the orders of the elements of ``automorphisms``. - - ``degree`` - a positive integer + - ``degree`` - a positive integer. - - ``moduli`` -- list of prime powers, i.e., `p^k` + - ``moduli`` -- list of prime powers, i.e., `p^k`. OUTPUT: @@ -472,7 +478,7 @@ def CRT_automorphisms(automorphisms, order_elts, degree, moduli): EXAMPLES:: - sage: aut = [[matrix([[1,0],[0,1]]),matrix([[0,1],[1,0]])]] + sage: aut = [[matrix([[1,0],[0,1]]), matrix([[0,1],[1,0]])]] sage: ords = [[1,2]] sage: degree = 2 sage: mods = [5] @@ -494,27 +500,29 @@ def CRT_automorphisms(automorphisms, order_elts, degree, moduli): return CRT_helper(degree_d_autos, moduli) def valid_automorphisms(automorphisms_CRT, rational_function, ht_bound, M, - return_functions = False): + return_functions=False): r""" + Check if automorphism mod `p^k` lifts to automorphism over `\ZZ`. + Checks whether an element that is an automorphism of ``rational_function`` modulo `p^k` for various - `p` s and `k` s can be lifted to an automorphism over `ZZ`. It uses the fact that every + `p` s and `k` s can be lifted to an automorphism over `\ZZ`. It uses the fact that every automorphism has height at most ``ht_bound`` INPUT: - - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)` + - ``automorphisms`` -- a list of lists of automorphisms over various `Zmod(p^k)`. - - ``rational_function`` -- A one variable rational function + - ``rational_function`` -- A one variable rational function. - - ``ht_bound`` - a positive integer + - ``ht_bound`` - a positive integer. - - ``M`` -- a positive integer, a product of prime powers + - ``M`` -- a positive integer, a product of prime powers. - - ``return_functions`` -- Boolean. default: False (optional) + - ``return_functions`` -- Boolean. default: False (optional). OUTPUT: - - a list of automorphisms over `ZZ`. + - a list of automorphisms over `\ZZ`. EXAMPLES:: @@ -548,21 +556,22 @@ def valid_automorphisms(automorphisms_CRT, rational_function, ht_bound, M, return valid_auto -def remove_redundant_automorphisms(automorphisms, order_elts, moduli,integral_autos): +def remove_redundant_automorphisms(automorphisms, order_elts, moduli, integral_autos): r""" - If an element of Aut_{F_p} has been lifted to `QQ` - remove that element from `Aut_{F_p}` so we don't - attempt to lift that element again unnecessarily + If an element of `Aut_{F_p}` has been lifted to `\QQ` + remove that element from `Aut_{F_p}`. + + We don't want to attempt to lift that element again unnecessarily. INPUT: - - ``automorphisms`` -- a list of lists of automorphisms + - ``automorphisms`` -- a list of lists of automorphisms. - - ``order_elts`` -- a list of lists of the orders of the elements of ``automorphisms`` + - ``order_elts`` -- a list of lists of the orders of the elements of ``automorphisms``. - - ``moduli`` -- a list of prime powers + - ``moduli`` -- a list of prime powers. - - ``integral_autos`` -- list of known automorphisms + - ``integral_autos`` -- list of known automorphisms. OUTPUT: @@ -593,8 +602,8 @@ def remove_redundant_automorphisms(automorphisms, order_elts, moduli,integral_au #The return_functions boolean determines if the automorphisms #are matricies or linear fractional transformations if is_Matrix(psi): - ppsi=psi.change_ring(GF(p)) - B=[ppsi[0,0],ppsi[0,1],ppsi[1,0],psi[1,1]] + ppsi = psi.change_ring(GF(p)) + B = [ppsi[0,0], ppsi[0,1], ppsi[1,0], psi[1,1]] else: ff = psi.numerator().change_ring(GF(p)) gg = psi.denominator().change_ring(GF(p)) @@ -616,34 +625,36 @@ def remove_redundant_automorphisms(automorphisms, order_elts, moduli,integral_au return(automorphisms) -def automorphism_group_QQ_CRT(rational_function, prime_lower_bound = 4, return_functions = True, iso_type=False): +def automorphism_group_QQ_CRT(rational_function, prime_lower_bound=4, return_functions=True, iso_type=False): r""" - Determines the complete group of rational automorphisms (under the conjugation action - of `PGL(2,QQ)`) for a rational function of one variable, see [FMV] for details. + Determines the complete group of rational automorphisms (under the conjugation action + of `PGL(2,\QQ)`) for a rational function of one variable. + + See [FMV]_ for details. INPUT: - - ``rational_function`` - a rational function of a univariate polynomial ring over `QQ` + - ``rational_function`` - a rational function of a univariate polynomial ring over `\QQ`. - - prime_lower_bound`` -- a positive integer - a lower bound for the primes to use for - the Chinese Remainder Theorem step. default: 4 (optional) + - ``prime_lower_bound`` -- a positive integer - a lower bound for the primes to use for + the Chinese Remainder Theorem step. default: 4 (optional). - ``return_functions`` -- Boolean - True returns linear fractional transformations - False returns elements of `PGL(2,QQ)` default: True (optional). + False returns elements of `PGL(2,\QQ)` default: True (optional). - ``iso_type`` -- Boolean - True returns the isomorphism type of the automorphism group. - default: False (optional) + default: False (optional). OUTPUT: - - a complete list of automorphisms of `rational_function` + - a complete list of automorphisms of ``rational_function``. EXAMPLES:: sage: R. = PolynomialRing(QQ) sage: f = (3*z^2 - 1)/(z^3 - 3*z) sage: from sage.schemes.projective.endPN_automorphism_group import automorphism_group_QQ_CRT - sage: automorphism_group_QQ_CRT(f, 4,True) + sage: automorphism_group_QQ_CRT(f, 4, True) [z, -z, 1/z, -1/z, (-z + 1)/(z + 1), (z + 1)/(z - 1), (z - 1)/(z + 1), (-z - 1)/(z - 1)] @@ -652,7 +663,7 @@ def automorphism_group_QQ_CRT(rational_function, prime_lower_bound = 4, return_f sage: R. = PolynomialRing(QQ) sage: f = (3*z^2 - 1)/(z^3 - 3*z) sage: from sage.schemes.projective.endPN_automorphism_group import automorphism_group_QQ_CRT - sage: automorphism_group_QQ_CRT(f, 4,False) + sage: automorphism_group_QQ_CRT(f, 4, False) [ [1 0] [-1 0] [0 1] [ 0 -1] [-1 1] [ 1 1] [ 1 -1] [-1 -1] [0 1], [ 0 1], [1 0], [ 1 0], [ 1 1], [ 1 -1], [ 1 1], [ 1 -1] @@ -668,7 +679,7 @@ def automorphism_group_QQ_CRT(rational_function, prime_lower_bound = 4, return_f F = R.base_ring() if F != QQ and F!= ZZ: - raise TypeError("coefficient ring is not the rational numbers or the integers!") + raise TypeError("coefficient ring is not the rational numbers or the integers") z = R.gen(0) phi = K(rational_function) @@ -825,7 +836,7 @@ def automorphism_group_FF(rational_function, absolute=False, iso_type=False, ret ALGORITHM: - See Algorithm 4 in Faber-Manes-Viray [FMV] + See Algorithm 4 in Faber-Manes-Viray [FMV]_. INPUT: @@ -843,7 +854,7 @@ def automorphism_group_FF(rational_function, absolute=False, iso_type=False, ret OUTPUT: - - List of automorphisms of ``rational_function`` + - List of automorphisms of ``rational_function``. EXAMPLES:: @@ -897,6 +908,7 @@ def automorphism_group_FF(rational_function, absolute=False, iso_type=False, ret def field_descent(sigma, y): r""" Function for descending an element in a field E to a subfield F. + Here F, E must be finite fields or number fields. This function determines the unique image of subfield which is ``y`` by the embedding ``sigma`` if it exists. Otherwise returns ``None``. @@ -906,11 +918,11 @@ def field_descent(sigma, y): - ``sigma``-- an embedding sigma: `F` -> `E` of fields. - - ``y`` --an element of the field `E` + - ``y`` --an element of the field `E`. OUTPUT: - - the unique element of the subfield if it exists, otherwise ``None`` + - the unique element of the subfield if it exists, otherwise ``None``. EXAMPLE:: @@ -955,14 +967,16 @@ def field_descent(sigma, y): def rational_function_coefficient_descent(rational_function, sigma, poly_ring): r""" Function for descending the coefficients of a rational function from field `E` - to a subfield `F`. Here `F`, `E` must be finite fields or number fields. + to a subfield `F`. + + Here `F`, `E` must be finite fields or number fields. It determines the unique rational function in fraction field of ``poly_ring`` which is the image of ``rational_function`` by ``ssigma``, if it exists, and otherwise returns ``None``. INPUT: - - ``rational_function``--a rational function with coefficients in a field `E`, + - ``rational_function``--a rational function with coefficients in a field `E`. - ``sigma``-- a field embedding sigma: `F` -> `E`. @@ -992,8 +1006,8 @@ def rational_function_coefficient_descent(rational_function, sigma, poly_ring): if rational_function == S(0): return poly_ring(0) - num=S(rational_function.numerator()) - denom=S(rational_function.denominator()) + num = S(rational_function.numerator()) + denom = S(rational_function.denominator()) f = num.coefficients() fe = num.exponents() g = denom.coefficients() @@ -1012,8 +1026,9 @@ def rational_function_coefficient_descent(rational_function, sigma, poly_ring): def rational_function_coerce(rational_function, sigma, S_polys): r""" Function for coercing a rational function defined over a ring `R` to have - coefficients in a second ring ``S_polys``. The fraction field of polynomial ring - ``S_polys`` will contain the new rational function. + coefficients in a second ring ``S_polys``. + + The fraction field of polynomial ring ``S_polys`` will contain the new rational function. INPUT: @@ -1080,7 +1095,7 @@ def rational_function_reduce(rational_function): def three_stable_points(rational_function, invariant_list): r""" Implementation of Algorithm 1 for automorphism groups from - Faber-Manes-Viray [FMV]. + Faber-Manes-Viray [FMV]_. INPUT: @@ -1092,7 +1107,7 @@ def three_stable_points(rational_function, invariant_list): OUTPUT: - - list of automorphisms + - list of automorphisms. EXAMPLES:: @@ -1155,7 +1170,7 @@ def three_stable_points(rational_function, invariant_list): def automorphism_group_FF_alg2(rational_function): r""" Implementation of algorithm for determining the absolute automorphism - group over a finite field, given an invariant set., see [FMV]. + group over a finite field, given an invariant set, see [FMV]_. INPUT: @@ -1195,7 +1210,7 @@ def automorphism_group_FF_alg2(rational_function): F = R.base_ring() if not F.is_finite() or not F.is_field(): - raise TypeError("Coefficient ring is not a finite field.") + raise TypeError("coefficient ring is not a finite field") p = F.characteristic() z = R.gen(0) phi = K(rational_function) @@ -1270,7 +1285,8 @@ def automorphism_group_FF_alg2(rational_function): def order_p_automorphisms(rational_function, pre_image): r""" Determine the order-p automorphisms given the input data. - This is algorithm 4 in Faber-Manes-Viray [FMV]. + + This is algorithm 4 in Faber-Manes-Viray [FMV]_. INPUT: @@ -1452,13 +1468,16 @@ def order_p_automorphisms(rational_function, pre_image): def automorphisms_fixing_pair(rational_function, pair, quad): r""" + Compute the set of automorphisms with order prime to the characteristic + that fix the pair, excluding the identity. + INPUT: - ``rational_function``-- rational function defined over finite field `E`. - ``pair``-- a pair of points of `\mathbb{P}^1(E)`. - - ``quad``-- Boolean: an indicator if this is a quadratic pair of points + - ``quad``-- Boolean: an indicator if this is a quadratic pair of points. OUTPUT: @@ -1467,11 +1486,11 @@ def automorphisms_fixing_pair(rational_function, pair, quad): EXAMPLES:: - sage: R. = PolynomialRing(GF(7^2,'t')) + sage: R. = PolynomialRing(GF(7^2, 't')) sage: f = (z^2 + 5*z + 5)/(5*z^2 + 5*z + 1) sage: L = [[4, 1], [2, 1]] sage: from sage.schemes.projective.endPN_automorphism_group import automorphisms_fixing_pair - sage: automorphisms_fixing_pair(f,L,False) + sage: automorphisms_fixing_pair(f, L, False) [(6*z + 6)/z, 4/(3*z + 3)] """ # define ground field and ambient function field @@ -1502,7 +1521,7 @@ def automorphisms_fixing_pair(rational_function, pair, quad): automorphisms_prime_to_p = [] # Quadratic automorphisms have order dividing q+1 and D, D-1, or D+1 - if quad==True: + if quad: #need sqrt to get the cardinality of the base field and not the #degree 2 extension q = sqrt(E.cardinality()) @@ -1531,7 +1550,7 @@ def automorphisms_fixing_pair(rational_function, pair, quad): def automorphism_group_FF_alg3(rational_function): r""" - Implementation of Algorithm 3 in the paper by Faber/Manes/Viray [FMV] + Implementation of Algorithm 3 in the paper by Faber/Manes/Viray [FMV]_ for computing the automorphism group over a finite field. INPUT: @@ -1560,7 +1579,7 @@ def automorphism_group_FF_alg3(rational_function): F = R.base_ring() if not F.is_finite() or not F.is_field(): - raise TypeError("Coefficient ring is not a finite field.") + raise TypeError("coefficient ring is not a finite field") p = F.characteristic() q = F.cardinality() z = R.gen(0) @@ -1701,12 +1720,13 @@ def automorphism_group_FF_alg3(rational_function): def which_group(list_of_elements): r""" Given a finite subgroup of `PGL2` determine its isomorphism class. - This function makes heavy use of the classification of finite subgroups of `PGL(2,K)` + + This function makes heavy use of the classification of finite subgroups of `PGL(2,K)`. INPUT: - ``list_of_elements``-- a finite list of elements of `PGL(2,K)` - that we know a priori form a group + that we know a priori form a group. OUTPUT: @@ -1731,7 +1751,7 @@ def which_group(list_of_elements): # invalid input if n == 0: - raise ValueError("Group must have at least one element") + raise ValueError("group must have at least one element") # define ground field and ambient function field rational_function = G[-1] diff --git a/src/sage/schemes/projective/endPN_minimal_model.py b/src/sage/schemes/projective/endPN_minimal_model.py index 6c007ef7ffa..0e7dcd59e64 100644 --- a/src/sage/schemes/projective/endPN_minimal_model.py +++ b/src/sage/schemes/projective/endPN_minimal_model.py @@ -29,20 +29,22 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.homset import End -from copy import copy -from sage.matrix.constructor import matrix -from sage.rings.finite_rings.integer_mod_ring import Zmod -from sage.rings.integer_ring import ZZ +from sage.categories.homset import End +from copy import copy +from sage.matrix.constructor import matrix +from sage.rings.finite_rings.integer_mod_ring import Zmod +from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.rational_field import QQ -from sage.schemes.affine.affine_space import AffineSpace +from sage.rings.rational_field import QQ +from sage.schemes.affine.affine_space import AffineSpace from sage.arith.all import gcd def bCheck(c, v, p, b): r""" - Compute a lower bound on the value of ``b`` needed, for a transformation + Compute a lower bound on the value of ``b``. + + This value is needed for a transformation `A(z) = z*p^k + b` to satisfy `ord_p(Res(\phi^A)) < ord_p(Res(\phi))` for a rational map `\phi`. See Theorem 3.3.5 in [Molnar]_. @@ -81,25 +83,27 @@ def bCheck(c, v, p, b): def scale(c,v,p): r""" + Create scaled integer polynomial with respect to prime ``p``. + Given an integral polynomial ``c``, we can write `c = p^i*c'`, where ``p`` does not divide ``c``. Returns ``c'`` and `v - i` where `i` is the smallest valuation of the coefficients of `c`. INPUT: - - ``c`` -- an integer polynomial + - ``c`` -- an integer polynomial. - - ``v`` -- an integer - the bound on the exponent from blift + - ``v`` -- an integer - the bound on the exponent from blift. - - ``p`` -- a prime + - ``p`` -- a prime. OUTPUT: - - Boolean -- the new exponent bound is 0 or negative + - Boolean -- the new exponent bound is 0 or negative. - - the scaled integer polynomial + - the scaled integer polynomial. - - an integer the new exponent bound + - an integer the new exponent bound. EXAMPLES:: @@ -119,24 +123,26 @@ def scale(c,v,p): return [flag,c,v] -def blift(LF,Li,p,S=None): +def blift(LF, Li, p, S=None): r""" - Search for a solution to the given list of inequalities. If found, lift the solution to + Search for a solution to the given list of inequalities. + + If found, lift the solution to an appropriate valuation. See Lemma 3.3.6 in [Molnar]_ INPUT: - - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) + - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients). - - ``Li`` -- an integer, the bound on coefficients + - ``Li`` -- an integer, the bound on coefficients. - - ``p`` -- a prime + - ``p`` -- a prime. OUTPUT: - - Boolean -- whether or not the lift is successful + - Boolean -- whether or not the lift is successful. - - integer -- the lift + - integer -- the lift. EXAMPLES:: @@ -163,22 +169,24 @@ def blift(LF,Li,p,S=None): #We need a solution for each polynomial on the left hand side of the inequalities, #so we need only find a solution for their gcd. g = gcd(keptScaledIneqs) - rts = g.roots(multiplicities=False) + rts = g.roots(multiplicities = False) for r in rts: #Recursively try to lift each root - r_initial=QQ(r) - newInput = P([r_initial,p]) + r_initial = QQ(r) + newInput = P([r_initial, p]) LG = [F(newInput) for F in LF] lift,lifted = blift(LG,Li,p,S=S) if lift: #Lift successful. - return True,r_initial+ p*lifted + return True,r_initial + p*lifted #Lift non successful. return False,0 -def affine_minimal(vp, return_transformation = False,D=None, quick = False): +def affine_minimal(vp, return_transformation=False, D=None, quick=False): r""" + Determine if given map is affine minimal. + Given vp a scheme morphisms on the projective line over the rationals, this procedure determines if `\phi` is minimal. In particular, it determines if the map is affine minimal, which is enough to decide if it is minimal @@ -193,7 +201,7 @@ def affine_minimal(vp, return_transformation = False,D=None, quick = False): - ``return_transformation`` -- a boolean value, default value True. This signals a return of the ``PGL_2`` transformation to conjugate ``vp`` to - the calculated minimal model. default: False + the calculated minimal model. default: False. - ``quick`` -- a boolean value. If true the algorithm terminates once algorithm determines F/G is not minimal, otherwise algorithm only @@ -203,15 +211,15 @@ def affine_minimal(vp, return_transformation = False,D=None, quick = False): - ``newvp`` -- scheme morphism on the projective line. - - ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp`` + - ``conj`` -- linear fractional transformation which conjugates ``vp`` to ``newvp``. EXAMPLES:: - sage: PS. = ProjectiveSpace(QQ,1) + sage: PS. = ProjectiveSpace(QQ, 1) sage: H = Hom(PS,PS) - sage: vp = H([X^2+9*Y^2,X*Y]) + sage: vp = H([X^2 + 9*Y^2, X*Y]) sage: from sage.schemes.projective.endPN_minimal_model import affine_minimal - sage: affine_minimal(vp,True) + sage: affine_minimal(vp, True) ( Scheme endomorphism of Projective Space of dimension 1 over Rational Field @@ -222,22 +230,22 @@ def affine_minimal(vp, return_transformation = False,D=None, quick = False): [0 1] ) """ - BR=vp.domain().base_ring() - conj=matrix(BR,2,2,1) + BR = vp.domain().base_ring() + conj = matrix(BR,2,2,1) flag = True d = vp.degree() vp.normalize_coordinates(); - Affvp=vp.dehomogenize(1) - R=Affvp.coordinate_ring() + Affvp = vp.dehomogenize(1) + R = Affvp.coordinate_ring() if R.is_field(): #want the polynomial ring not the fraction field - R=R.ring() - F=R(Affvp[0].numerator()) - G=R(Affvp[0].denominator()) - if G.degree()==0 or F.degree()==0: - raise TypeError("Affine minimality is only considered for maps not of the form f or 1/f for a polynomial f.") - z=F.parent().gen(0) + R = R.ring() + F = R(Affvp[0].numerator()) + G = R(Affvp[0].denominator()) + if G.degree() == 0 or F.degree() == 0: + raise TypeError("affine minimality is only considered for maps not of the form f or 1/f for a polynomial f") + z = F.parent().gen(0) minF,minG = F,G #If the valuation of a prime in the resultant is small enough, we can say the #map is affine minimal at that prime without using the local minimality loop. See @@ -251,12 +259,10 @@ def affine_minimal(vp, return_transformation = False,D=None, quick = False): #Some quantities needed for the local minimization loop, but we compute now #since the value is constant, so we do not wish to compute in every local loop. #See Theorem 3.3.3 in [Molnar, M.Sc thesis] - - H=F-z*minG - d1=F.degree() - A=AffineSpace(BR,1,H.parent().variable_name()) - end_ring=End(A) - + H = F-z*minG + d1 = F.degree() + A = AffineSpace(BR,1,H.parent().variable_name()) + end_ring = End(A) ubRes = end_ring([H/minG]).homogenize(1).resultant() #Set the primes to check minimality at, if not already prescribed if D is None: @@ -273,12 +279,12 @@ def affine_minimal(vp, return_transformation = False,D=None, quick = False): else: #The model may not be minimal at p. newvp,conj = Min(vp,p,ubRes,conj) - if newvp==vp: - min=True + if newvp == vp: + min = True else: - vp=newvp - Affvp=vp.dehomogenize(1) - min=False + vp = newvp + Affvp = vp.dehomogenize(1) + min = False if min: #The model is minimal at p break @@ -310,23 +316,23 @@ def Min(Fun, p, ubRes, conj): INPUT: - - ``Fun`` -- a projective space morphisms + - ``Fun`` -- a projective space morphisms. - ``p`` - a prime. - ``ubRes`` -- integer, the upper bound needed for Th. 3.3.3 in [Molnar]_. - - ``conj`` -- a 2x2 matrix keeping track of the conjugation + - ``conj`` -- a 2x2 matrix keeping track of the conjugation. OUTPUT: - - Boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise + - Boolean -- ``True`` if ``Fun`` is minimal at ``p``, ``False`` otherwise. - - a projective morphism minimal at ``p`` + - a projective morphism minimal at ``p``. EXAMPLES:: - sage: P. = ProjectiveSpace(QQ,1) + sage: P. = ProjectiveSpace(QQ, 1) sage: H = End(P) sage: f = H([149*x^2 + 39*x*y + y^2, -8*x^2 + 137*x*y + 33*y^2]) sage: from sage.schemes.projective.endPN_minimal_model import Min @@ -341,15 +347,14 @@ def Min(Fun, p, ubRes, conj): [0 1] ) """ - - d=Fun.degree() - AffFun=Fun.dehomogenize(1) - R=AffFun.coordinate_ring() + d = Fun.degree() + AffFun = Fun.dehomogenize(1) + R = AffFun.coordinate_ring() if R.is_field(): #want the polynomial ring not the fraction field - R=R.ring() - F=R(AffFun[0].numerator()) - G=R(AffFun[0].denominator()) + R = R.ring() + F = R(AffFun[0].numerator()) + G = R(AffFun[0].denominator()) dG = G.degree() if dG > (d+1)/2: lowerBound = (-2*(G[dG]).valuation(p)/(2*dG - d + 1) + 1).floor() @@ -365,9 +370,9 @@ def Min(Fun, p, ubRes, conj): #resultant of F/G k = lowerBound Qb = PolynomialRing(QQ,'b') - b=Qb.gen(0) + b = Qb.gen(0) Q = PolynomialRing(Qb,'z') - z=Q.gen(0) + z = Q.gen(0) while k <= upperBound: A = (p**k)*z + b Ft = Q(F(A) - b*G(A)) @@ -378,21 +383,16 @@ def Min(Fun, p, ubRes, conj): RHS = (d + 1)*k/2 #If there is some b such that Res(phi^A) < Res(phi), we must have ord_p(c) > #RHS for each c in coeffs. - #Make sure constant coefficients in coeffs satisfy the inequality. - if all( QQ(c).valuation(p) > RHS for c in coeffs if c.degree() ==0 ): #Constant coefficients in coeffs have large enough valuation, so check - #the rest. - #We start by checking if simply picking b=0 works + #the rest. We start by checking if simply picking b=0 works if all(c(0).valuation(p) > RHS for c in coeffs): #A = z*p^k satisfies the inequalities, and F/G is not minimal - #"Conjugating by", p,"^", k, "*z +", 0 - newconj=matrix(QQ,2,2,[p**k,0,0,1]) - minFun=Fun.conjugate(newconj) - conj=conj*newconj - + newconj = matrix(QQ,2,2,[p**k,0,0,1]) + minFun = Fun.conjugate(newconj) + conj = conj*newconj minFun.normalize_coordinates() return minFun, conj @@ -422,9 +422,9 @@ def Min(Fun, p, ubRes, conj): #Rescale, conjugate and return new map bsol = QQ(sol*(p**bval)) #"Conjugating by ", p,"^", k, "*z +", bsol - newconj=matrix(QQ,2,2,[p**k,bsol,0,1]) - minFun=Fun.conjugate(newconj) - conj=conj*newconj + newconj = matrix(QQ,2,2,[p**k,bsol,0,1]) + minFun = Fun.conjugate(newconj) + conj = conj*newconj minFun.normalize_coordinates() return minFun, conj diff --git a/src/sage/schemes/projective/projective_homset.py b/src/sage/schemes/projective/projective_homset.py index fe3870736d1..dea1d99a056 100644 --- a/src/sage/schemes/projective/projective_homset.py +++ b/src/sage/schemes/projective/projective_homset.py @@ -42,7 +42,7 @@ from sage.rings.rational_field import is_RationalField from sage.categories.fields import Fields from sage.categories.number_fields import NumberFields -from sage.rings.finite_rings.constructor import is_FiniteField +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField #******************************************************************* # Projective varieties @@ -67,10 +67,10 @@ def points(self, B=0, prec=53): INPUT: - - `B` - integer (optional, default=0). The bound for the + - ``B`` - integer (optional, default=0). The bound for the coordinates. - - ``prec`` - he precision to use to compute the elements of bounded height for number fields + - ``prec`` - he precision to use to compute the elements of bounded height for number fields. OUTPUT: @@ -139,19 +139,19 @@ def points(self, B=0, prec=53): R = self.value_ring() if is_RationalField(R): if not B > 0: - raise TypeError("A positive bound B (= %s) must be specified."%B) + raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) elif R in NumberFields(): if not B > 0: - raise TypeError("A positive bound B (= %s) must be specified."%B) + raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_number_field return enum_projective_number_field(self,B, prec=prec) elif is_FiniteField(R): from sage.schemes.projective.projective_rational_point import enum_projective_finite_field return enum_projective_finite_field(self.extended_codomain()) else: - raise TypeError("Unable to enumerate points over %s."%R) + raise TypeError("unable to enumerate points over %s"%R) class SchemeHomset_points_projective_ring(SchemeHomset_points): """ @@ -174,7 +174,7 @@ def points(self, B=0): INPUT: - - `B` -- integer (optional, default=0). The bound for the + - ``B`` -- integer (optional, default=0). The bound for the coordinates. EXAMPLES:: @@ -214,11 +214,11 @@ def points(self, B=0): R = self.value_ring() if R == ZZ: if not B > 0: - raise TypeError("A positive bound B (= %s) must be specified."%B) + raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.projective.projective_rational_point import enum_projective_rational_field return enum_projective_rational_field(self,B) else: - raise TypeError("Unable to enumerate points over %s."%R) + raise TypeError("unable to enumerate points over %s"%R) #******************************************************************* @@ -226,7 +226,7 @@ def points(self, B=0): #******************************************************************* class SchemeHomset_points_abelian_variety_field(SchemeHomset_points_projective_field): r""" - Set of rational points of an abelian variety. + Set of rational points of an Abelian variety. INPUT: @@ -258,7 +258,7 @@ class SchemeHomset_points_abelian_variety_field(SchemeHomset_points_projective_f def _element_constructor_(self, *v, **kwds): """ - The element contstructor. + The element constructor. INPUT: @@ -289,7 +289,7 @@ def _element_constructor_(self, *v, **kwds): def _repr_(self): """ - Return a string representation of ``self``. + Return a string representation of this homset. OUTPUT: @@ -328,11 +328,11 @@ def base_extend(self, R): Traceback (most recent call last): ... NotImplementedError: Abelian variety point sets are not - implemented as modules over rings other than ZZ. + implemented as modules over rings other than ZZ """ if R is not ZZ: raise NotImplementedError('Abelian variety point sets are not ' - 'implemented as modules over rings other than ZZ.') + 'implemented as modules over rings other than ZZ') return self diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 86c7b382f61..e186da8d588 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Morphisms on projective varieties @@ -38,42 +39,42 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.calculus.functions import jacobian +from sage.calculus.functions import jacobian from sage.categories.number_fields import NumberFields -from sage.categories.homset import Hom, End -from sage.combinat.sf.sf import SymmetricFunctions -from sage.functions.all import sqrt -from sage.libs.pari.all import PariError -from sage.matrix.constructor import matrix, identity_matrix -from sage.misc.all import prod -from sage.misc.cachefunc import cached_method -from sage.misc.misc import subsets -from sage.misc.mrange import xmrange +from sage.categories.homset import Hom, End +from sage.combinat.sf.sf import SymmetricFunctions +from sage.functions.all import sqrt +from sage.libs.pari.all import PariError +from sage.matrix.constructor import matrix, identity_matrix +from sage.misc.all import prod +from sage.misc.cachefunc import cached_method +from sage.misc.misc import subsets +from sage.misc.mrange import xmrange from sage.modules.free_module_element import vector -from sage.rings.all import Integer, CIF +from sage.rings.all import Integer, CIF from sage.arith.all import gcd, lcm, next_prime, binomial, primes, moebius -from sage.rings.complex_field import ComplexField_class,ComplexField +from sage.rings.complex_field import ComplexField_class,ComplexField from sage.rings.complex_interval_field import ComplexIntervalField_class -from sage.rings.finite_rings.constructor import GF, is_PrimeFiniteField +from sage.rings.finite_rings.finite_field_constructor import GF, is_PrimeFiniteField from sage.rings.finite_rings.integer_mod_ring import Zmod -from sage.rings.fraction_field import FractionField +from sage.rings.fraction_field import FractionField from sage.rings.fraction_field_element import is_FractionFieldElement, FractionFieldElement -from sage.rings.integer_ring import ZZ +from sage.rings.integer_ring import ZZ from sage.rings.number_field.order import is_NumberFieldOrder from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics -from sage.rings.quotient_ring import QuotientRing_generic -from sage.rings.qqbar import QQbar -from sage.rings.rational_field import QQ -from sage.rings.real_mpfr import RealField_class,RealField -from sage.rings.real_mpfi import RealIntervalField_class +from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics +from sage.rings.quotient_ring import QuotientRing_generic +from sage.rings.qqbar import QQbar +from sage.rings.rational_field import QQ +from sage.rings.real_mpfr import RealField_class,RealField +from sage.rings.real_mpfi import RealIntervalField_class from sage.schemes.generic.morphism import SchemeMorphism_polynomial -from sage.symbolic.constants import e +from sage.symbolic.constants import e from copy import copy -from sage.parallel.ncpus import ncpus -from sage.parallel.use_fork import p_iter_fork -from sage.ext.fast_callable import fast_callable -from sage.misc.lazy_attribute import lazy_attribute +from sage.parallel.ncpus import ncpus +from sage.parallel.use_fork import p_iter_fork +from sage.ext.fast_callable import fast_callable +from sage.misc.lazy_attribute import lazy_attribute from sage.schemes.projective.projective_morphism_helper import _fast_possible_periods import sys from sage.categories.number_fields import NumberFields @@ -205,13 +206,13 @@ def __init__(self, parent, polys, check=True): def __call__(self, x, check=True): """ - Compute the forward image of the point or subscheme ``x`` by the map ``self``. + Compute the forward image of the point or subscheme ``x`` by this map. For subschemes, the forward image is computed through elimination. - In particular, let $X = V(h_1,\ldots, h_t)$ and define the ideal - $I = (h_1,\ldots,h_t,y_0-f_0(\bar{x}), \ldots, y_n-f_n(\bar{x}))$. - Then the elimination ideal $I_{n+1} = I \cap K[y_0,\ldots,y_n]$ is a homogeneous - ideal and $self(X) = V(I_{n+1})$. + In particular, let `X = V(h_1,\ldots, h_t)` and define the ideal + `I = (h_1,\ldots,h_t,y_0-f_0(\bar{x}), \ldots, y_n-f_n(\bar{x}))`. + Then the elimination ideal `I_{n+1} = I \cap K[y_0,\ldots,y_n]` is a homogeneous + ideal and `self(X) = V(I_{n+1})`. The input boolean ``check`` can be set to false when fast iteration of points is desired. It bypasses all input checking and passes ``x`` straight @@ -219,9 +220,9 @@ def __call__(self, x, check=True): INPUT: - - ``x`` - a point or subscheme in domain of ``self`` + - ``x`` - a point or subscheme in domain of this map. - - ``check`` - Boolean - if `False` assume that ``x`` is a point + - ``check`` - Boolean - if `False` assume that ``x`` is a point. EXAMPLES:: @@ -322,7 +323,7 @@ def __call__(self, x, check=True): @lazy_attribute def _fastpolys(self): """ - Lazy attribute for fast_callable polynomials for ``self``. + Lazy attribute for fast_callable polynomials for this map. EXAMPLES:: @@ -361,9 +362,9 @@ def _fast_eval(self, x): EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,2) - sage: H=Hom(P,P) - sage: f=H([x^2+y^2,y^2,z^2 + y*z]) + sage: P. = ProjectiveSpace(QQ,2) + sage: H = Hom(P,P) + sage: f = H([x^2+y^2, y^2, z^2 + y*z]) sage: f._fast_eval([1,1,1]) [2, 1, 2] @@ -372,31 +373,30 @@ def _fast_eval(self, x): sage: T. = LaurentSeriesRing(ZZ) sage: P. = ProjectiveSpace(T,1) sage: H = End(P) - sage: f = H([x^2+x*y,y^2]) + sage: f = H([x^2+x*y, y^2]) sage: Q = P(z,1) sage: f._fast_eval(list(Q)) [z + z^2, 1] :: - sage: T.=PolynomialRing(CC) - sage: I=T.ideal(z^3) - sage: P.=ProjectiveSpace(T.quotient_ring(I),1) - sage: H=End(P) - sage: f=H([x^2+x*y,y^2]) - sage: Q=P(z^2,1) + sage: T. = PolynomialRing(CC) + sage: I = T.ideal(z^3) + sage: P. = ProjectiveSpace(T.quotient_ring(I),1) + sage: H = End(P) + sage: f = H([x^2+x*y, y^2]) + sage: Q = P(z^2, 1) sage: f._fast_eval(list(Q)) [zbar^2, 1.00000000000000] :: - sage: T.=LaurentSeriesRing(CC) - sage: R.=PolynomialRing(T) - sage: P.=ProjectiveSpace(R,1) - sage: H=End(P) - sage: f=H([x^2+x*y,y^2]) - sage: F=f.dehomogenize(1) - sage: Q=P(t^2,z) + sage: T. = LaurentSeriesRing(CC) + sage: R. = PolynomialRing(T) + sage: P. = ProjectiveSpace(R,1) + sage: H = End(P) + sage: f = H([x^2+x*y, y^2]) + sage: Q = P(t^2, z) sage: f._fast_eval(list(Q)) [t^4 + z*t^2, z^2] """ @@ -409,7 +409,7 @@ def __eq__(self, right): INPUT: - - ``right`` - a map on projective space + - ``right`` - a map on projective space. OUTPUT: @@ -457,7 +457,7 @@ def __ne__(self, right): INPUT: - - ``right`` -- a map on projective space + - ``right`` -- a map on projective space. OUTPUT: @@ -493,14 +493,14 @@ def __ne__(self, right): def scale_by(self, t): """ - Scales each coordinates by a factor of `t`. + Scales each coordinate by a factor of ``t``. A ``TypeError`` occurs if the point is not in the coordinate_ring of the parent after scaling. INPUT: - - ``t`` -- a ring element + - ``t`` -- a ring element. OUTPUT: @@ -556,8 +556,9 @@ def scale_by(self, t): def normalize_coordinates(self): """ - Scales by 1/gcd of the coordinate functions. Also, scales to clear any denominators from the coefficients. - This is done in place. + Scales by 1/gcd of the coordinate functions. + + Also, scales to clear any denominators from the coefficients. This is done in place. OUTPUT: @@ -567,7 +568,7 @@ def normalize_coordinates(self): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([5/4*x^3,5*x*y^2]) + sage: f = H([5/4*x^3, 5*x*y^2]) sage: f.normalize_coordinates(); f Scheme endomorphism of Projective Space of dimension 1 over Rational Field @@ -579,7 +580,7 @@ def normalize_coordinates(self): sage: P. = ProjectiveSpace(GF(7),2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^3+x*y^2,x*y^2,x*z^2]) + sage: f = H([x^3+x*y^2, x*y^2, x*z^2]) sage: f.normalize_coordinates(); f Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Finite Field of size 7 defined by: @@ -627,7 +628,7 @@ def dynatomic_polynomial(self, period): The dynatomic polynomial is the analog of the cyclotomic polynomial and its roots are the points of formal period `period`. If possible the division is - done in the coordinate ring of ``self`` and a polynomial is returned. In rings where that is not possible, + done in the coordinate ring of this map and a polynomial is returned. In rings where that is not possible, a FractionField element will be returned. In certain cases, when the conversion back to a polynomial fails, a SymbolRing element will be returned. @@ -640,7 +641,7 @@ def dynatomic_polynomial(self, period): \Phi^{\ast}_n(f)(x,y) = \sum_{d \mid n} (yF_d(x,y) - xG_d(x,y))^{\mu(n/d)} - where `\mu` is the Moebius function. + where `\mu` is the Möbius function. For a pair `[m,n]`, let `f^m = [F_m,G_m]`. Compute @@ -650,21 +651,21 @@ def dynatomic_polynomial(self, period): REFERENCES: - .. [Hutz] B. Hutz. Efficient determination of rational preperiodic - points for endomorphisms of projective space. - :arxiv:`1210.6246`, 2012. + .. [Hutz] B. Hutz. Determination of all rational preperiodic points + for morphisms of PN. Mathematics of Computation, 84:291 (2015), 289-308. .. [MoPa] P. Morton and P. Patel. The Galois theory of periodic points of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263. INPUT: - - ``period`` -- a positive integer or a list/tuple `[m,n]` where `m` is the preperiod and `n` is the period + - ``period`` -- a positive integer or a list/tuple `[m,n]` where + `m` is the preperiod and `n` is the period. OUTPUT: - - If possible, a two variable polynomial in the coordinate ring of ``self``. - Otherwise a fraction field element of the coordinate ring of ``self``. Or, + - If possible, a two variable polynomial in the coordinate ring of this map. + Otherwise a fraction field element of the coordinate ring of this map. Or, a Symbolic Ring element. .. TODO:: @@ -677,7 +678,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + 2*y^2 @@ -685,7 +686,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,x*y]) + sage: f = H([x^2+y^2, x*y]) sage: f.dynatomic_polynomial(4) 2*x^12 + 18*x^10*y^2 + 57*x^8*y^4 + 79*x^6*y^6 + 48*x^4*y^8 + 12*x^2*y^10 + y^12 @@ -693,7 +694,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(CC,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,3*x*y]) + sage: f = H([x^2+y^2, 3*x*y]) sage: f.dynatomic_polynomial(3) 13.0000000000000*x^6 + 117.000000000000*x^4*y^2 + 78.0000000000000*x^2*y^4 + y^6 @@ -702,7 +703,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2-10/9*y^2,y^2]) + sage: f = H([x^2-10/9*y^2, y^2]) sage: f.dynatomic_polynomial([2,1]) x^4*y^2 - 11/9*x^2*y^4 - 80/81*y^6 @@ -710,7 +711,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2-29/16*y^2,y^2]) + sage: f = H([x^2-29/16*y^2, y^2]) sage: f.dynatomic_polynomial([2,3]) x^12 - 95/8*x^10*y^2 + 13799/256*x^8*y^4 - 119953/1024*x^6*y^6 + 8198847/65536*x^4*y^8 - 31492431/524288*x^2*y^10 + @@ -720,7 +721,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2-y^2,y^2]) + sage: f = H([x^2-y^2, y^2]) sage: f.dynatomic_polynomial([1,2]) x^2 - x*y @@ -728,7 +729,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^3-y^3,3*x*y^2]) + sage: f = H([x^3-y^3, 3*x*y^2]) sage: f.dynatomic_polynomial([0,4])==f.dynatomic_polynomial(4) True @@ -736,17 +737,17 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,x*y,z^2]) + sage: f = H([x^2+y^2, x*y, z^2]) sage: f.dynatomic_polynomial(2) Traceback (most recent call last): ... - TypeError: Does not make sense in dimension >1 + TypeError: does not make sense in dimension >1 :: sage: P. = ProjectiveSpace(Qp(5),1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) (x^4*y + (2 + O(5^20))*x^2*y^3 - x*y^4 + (2 + O(5^20))*y^5)/(x^2*y - x*y^2 + y^3) @@ -756,7 +757,7 @@ def dynatomic_polynomial(self, period): sage: L. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(L,1) sage: H = Hom(P,P) - sage: f = H([x^2+t*y^2,y^2]) + sage: f = H([x^2+t*y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t + 1)*y^2 @@ -765,22 +766,22 @@ def dynatomic_polynomial(self, period): sage: K. = PolynomialRing(ZZ) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([x^2+ c*y^2,y^2]) - sage: f.dynatomic_polynomial([1,2]) + sage: f = H([x^2+ c*y^2, y^2]) + sage: f.dynatomic_polynomial([1, 2]) x^2 - x*y + (c + 1)*y^2 :: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + 2*y^2 sage: R. = PolynomialRing(QQ) sage: K. = NumberField(X^2 + X + 2) sage: PP = P.change_ring(K) sage: ff = f.change_ring(K) - sage: p = PP((c,1)) + sage: p = PP((c, 1)) sage: ff(ff(p)) == p True @@ -788,31 +789,31 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,x*y]) - sage: f.dynatomic_polynomial([2,2]) + sage: f = H([x^2+y^2, x*y]) + sage: f.dynatomic_polynomial([2, 2]) x^4 + 4*x^2*y^2 + y^4 sage: R. = PolynomialRing(QQ) sage: K. = NumberField(X^4 + 4*X^2 + 1) sage: PP = P.change_ring(K) sage: ff = f.change_ring(K) - sage: p = PP((c,1)) - sage: ff.nth_iterate(p,4) == ff.nth_iterate(p,2) + sage: p = PP((c, 1)) + sage: ff.nth_iterate(p, 4) == ff.nth_iterate(p, 2) True :: sage: P. = ProjectiveSpace(CC, 1) sage: H = Hom(P,P) - sage: f = H([x^2-CC.0/3*y^2,y^2]) + sage: f = H([x^2-CC.0/3*y^2, y^2]) sage: f.dynatomic_polynomial(2) 0.666666666666667*x^2 + 0.333333333333333*y^2 :: - sage: L. = PolynomialRing (QuadraticField(2).maximal_order()) - sage: P. = ProjectiveSpace (L.fraction_field() , 1 ) - sage: H = Hom (P, P ) - sage: f = H ([x^2 + (t ^ 2 + 1) * y^2 , y^2 ]) + sage: L. = PolynomialRing(QuadraticField(2).maximal_order()) + sage: P. = ProjectiveSpace(L.fraction_field() , 1) + sage: H = Hom(P,P) + sage: f = H([x^2 + (t^2 + 1)*y^2 , y^2]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t^2 + 2)*y^2 @@ -823,7 +824,7 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(QQbar,1) sage: H = End(P) sage: R = P.coordinate_ring() - sage: f = H([x^2-1/3*y^2,y^2]) + sage: f = H([x^2-1/3*y^2, y^2]) sage: f.dynatomic_polynomial(2).parent() Multivariate Polynomial Ring in x, y over Algebraic Field @@ -833,8 +834,8 @@ def dynatomic_polynomial(self, period): sage: S. = PolynomialRing(T) sage: P. = ProjectiveSpace(FractionField(S),1) sage: H = End(P) - sage: f = H([t*x^2-1/t*y^2,y^2]) - sage: f.dynatomic_polynomial([1,2]).parent() + sage: f = H([t*x^2-1/t*y^2, y^2]) + sage: f.dynatomic_polynomial([1, 2]).parent() Multivariate Polynomial Ring in x, y over Fraction Field of Univariate Polynomial Ring in t over Number Field in v with defining polynomial x^2 - 33 @@ -844,12 +845,12 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(S,1) sage: H = End(P) sage: R = P.coordinate_ring() - sage: f = H([t*x^2-1*y^2,t*y^2]) - sage: f.dynatomic_polynomial([1,2]).parent() + sage: f = H([t*x^2-1*y^2, t*y^2]) + sage: f.dynatomic_polynomial([1, 2]).parent() Symbolic Ring """ if self.domain().ngens() > 2: - raise TypeError("Does not make sense in dimension >1") + raise TypeError("does not make sense in dimension >1") if not isinstance(period, (list, tuple)): period = [0, period] x = self.domain().gen(0) @@ -899,8 +900,7 @@ def dynatomic_polynomial(self, period): def nth_iterate_map(self, n): r""" - For a map ``self`` this function returns the nth iterate of ``self`` as a - function on ``self.domain()`` + Returns the ``n``-th iterate of this map as a new map. ALGORITHM: @@ -915,13 +915,13 @@ def nth_iterate_map(self, n): OUTPUT: - - A map between projective spaces + - A map between projective spaces. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.nth_iterate_map(2) Scheme endomorphism of Projective Space of dimension 1 over Rational Field @@ -932,7 +932,7 @@ def nth_iterate_map(self, n): sage: P. = ProjectiveSpace(CC,1) sage: H = Hom(P,P) - sage: f = H([x^2-y^2,x*y]) + sage: f = H([x^2-y^2, x*y]) sage: f.nth_iterate_map(3) Scheme endomorphism of Projective Space of dimension 1 over Complex Field with 53 bits of precision @@ -945,7 +945,7 @@ def nth_iterate_map(self, n): sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2-y^2,x*y,z^2+x^2]) + sage: f = H([x^2-y^2, x*y, z^2+x^2]) sage: f.nth_iterate_map(2) Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to @@ -957,7 +957,7 @@ def nth_iterate_map(self, n): sage: P. = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x*z-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,x*z,z^2]) + sage: f = H([x^2, x*z, z^2]) sage: f.nth_iterate_map(2) Scheme endomorphism of Closed subscheme of Projective Space of dimension 2 over Rational Field defined by: @@ -967,11 +967,11 @@ def nth_iterate_map(self, n): """ E = self.domain() if E is not self.codomain(): - raise TypeError("Domain and Codomain of function not equal") + raise TypeError("domain and Codomain of function not equal") D = int(n) if D < 0: - raise TypeError("Iterate number must be a positive integer") + raise TypeError("iterate number must be a positive integer") N = self.codomain().ambient_space().dimension_relative() + 1 F = list(self._polys) Coord_ring = self.codomain().coordinate_ring() @@ -990,8 +990,7 @@ def nth_iterate_map(self, n): def nth_iterate(self, P, n, **kwds): r""" - For a map ``self`` and a point `P` in ``self.domain()`` - this function returns the nth iterate of `P` by ``self``. + Returns the ``n``-th iterate of the point ``P`` by this map. If ``normalize`` is ``True``, then the coordinates are automatically normalized. @@ -1000,23 +999,23 @@ def nth_iterate(self, P, n, **kwds): INPUT: - - ``P`` -- a point in ``self.domain()`` + - ``P`` -- a point in this map's domain. - ``n`` -- a positive integer. kwds: - - ``normalize`` - Boolean (optional Default: ``False``) + - ``normalize`` - Boolean (optional Default: ``False``). OUTPUT: - - A point in ``self.codomain()`` + - A point in this map's codomain. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,2*y^2]) + sage: f = H([x^2+y^2, 2*y^2]) sage: Q = P(1,1) sage: f.nth_iterate(Q,4) (32768 : 32768) @@ -1025,16 +1024,16 @@ def nth_iterate(self, P, n, **kwds): sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,2*y^2]) + sage: f = H([x^2+y^2, 2*y^2]) sage: Q = P(1,1) - sage: f.nth_iterate(Q,4,normalize = True) + sage: f.nth_iterate(Q, 4, normalize=True) (1 : 1) - Is this the right behavior? :: + :: sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2,2*y^2,z^2-x^2]) + sage: f = H([x^2, 2*y^2, z^2-x^2]) sage: Q = P(2,7,1) sage: f.nth_iterate(Q,2) (-16/7 : -2744 : 1) @@ -1044,7 +1043,7 @@ def nth_iterate(self, P, n, **kwds): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(R,2) sage: H = Hom(P,P) - sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2]) + sage: f = H([x^2+t*y^2, (2-t)*y^2, z^2]) sage: Q = P(2+t,7,t) sage: f.nth_iterate(Q,2) (t^4 + 2507*t^3 - 6787*t^2 + 10028*t + 16 : -2401*t^3 + 14406*t^2 - @@ -1055,8 +1054,8 @@ def nth_iterate(self, P, n, **kwds): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) - sage: f.nth_iterate(X(2,2,3),3) + sage: f = H([x^2, y^2, z^2]) + sage: f.nth_iterate(X(2,2,3), 3) (256 : 256 : 6561) :: @@ -1064,18 +1063,18 @@ def nth_iterate(self, P, n, **kwds): sage: K. = FunctionField(QQ) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([x^3-2*x*y^2 - c*y^3,x*y^2]) - sage: f.nth_iterate(P(c,1),2) + sage: f = H([x^3 - 2*x*y^2 - c*y^3, x*y^2]) + sage: f.nth_iterate(P(c,1), 2) ((c^6 - 9*c^4 + 25*c^2 - c - 21)/(c^2 - 3) : 1) """ return(P.nth_iterate(self, n, **kwds)) def degree(self): r""" - This function returns the degree of ``self``. + Return the degree of this map. The degree is defined as the degree of the homogeneous - polynomials that are the coordinates of ``self``. + polynomials that are the coordinates of this map. OUTPUT: @@ -1085,7 +1084,7 @@ def degree(self): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.degree() 2 @@ -1093,7 +1092,7 @@ def degree(self): sage: P. = ProjectiveSpace(CC,2) sage: H = Hom(P,P) - sage: f = H([x^3+y^3,y^2*z,z*x*y]) + sage: f = H([x^3+y^3, y^2*z, z*x*y]) sage: f.degree() 3 @@ -1102,7 +1101,7 @@ def degree(self): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(R,2) sage: H = Hom(P,P) - sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2]) + sage: f = H([x^2+t*y^2, (2-t)*y^2, z^2]) sage: f.degree() 2 @@ -1111,7 +1110,7 @@ def degree(self): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) + sage: f = H([x^2, y^2, z^2]) sage: f.degree() 2 """ @@ -1123,7 +1122,7 @@ def dehomogenize(self, n): and the ``n[1]`` coordinate for the codomain. Note that the new function is defined over the fraction field - of the base ring of ``self``. + of the base ring of this map. INPUT: @@ -1132,13 +1131,13 @@ def dehomogenize(self, n): OUTPUT: - - :class:`SchemeMorphism_polynomial_affine_space` + - :class:`SchemeMorphism_polynomial_affine_space`. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.dehomogenize(0) Scheme endomorphism of Affine Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x) to @@ -1148,7 +1147,7 @@ def dehomogenize(self, n): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2-y^2,y^2]) + sage: f = H([x^2-y^2, y^2]) sage: f.dehomogenize((0,1)) Scheme endomorphism of Affine Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x) to @@ -1158,7 +1157,7 @@ def dehomogenize(self, n): sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2-z^2,2*z^2]) + sage: f = H([x^2+y^2, y^2-z^2, 2*z^2]) sage: f.dehomogenize(2) Scheme endomorphism of Affine Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x0, x1) to @@ -1169,7 +1168,7 @@ def dehomogenize(self, n): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(FractionField(R),2) sage: H = Hom(P,P) - sage: f = H([x^2+t*y^2,t*y^2-z^2,t*z^2]) + sage: f = H([x^2+t*y^2, t*y^2-z^2, t*z^2]) sage: f.dehomogenize(2) Scheme endomorphism of Affine Space of dimension 2 over Fraction Field of Univariate Polynomial Ring in t over Rational Field @@ -1181,7 +1180,7 @@ def dehomogenize(self, n): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,x*z]) + sage: f = H([x^2, y^2, x*z]) sage: f.dehomogenize(2) Scheme endomorphism of Closed subscheme of Affine Space of dimension 2 over Integer Ring defined by: @@ -1212,7 +1211,7 @@ def dehomogenize(self, n): PS_domain = self.domain() A_domain = PS_domain.ambient_space() if self._polys[ind[1]].substitute({A_domain.gen(ind[0]):1}) == 0: - raise ValueError("Can't dehomogenize at 0 coordinate.") + raise ValueError("can't dehomogenize at 0 coordinate") else: Aff_domain = PS_domain.affine_patch(ind[0]) S = Aff_domain.ambient_space().coordinate_ring() @@ -1236,42 +1235,44 @@ def dehomogenize(self, n): def orbit(self, P, N, **kwds): r""" - Returns the orbit of `P` by ``self``. If `n` is an integer it returns `[P,self(P),\ldots,self^n(P)]`. - If `n` is a list or tuple `n=[m,k]` it returns `[self^m(P),\ldots,self^k(P)]`. + Return the orbit of the point ``P`` by this map. + + If ``N`` is an integer it returns `[P,self(P),\ldots,self^N(P)]`. + If ``N`` is a list or tuple `N=[m,k]` it returns `[self^m(P),\ldots,self^k(P)]`. Automatically normalize the points if ``normalize=True``. Perform the checks on point initialize if ``check=True`` INPUT: - - ``P`` -- a point in ``self.domain()`` + - ``P`` -- a point in this map's domain. - - ``n`` -- a non-negative integer or list or tuple of two non-negative integers + - ``n`` -- a non-negative integer or list or tuple of two non-negative integers. kwds: - - ``check`` -- boolean (optional - default: ``True``) + - ``check`` -- boolean (optional - default: ``True``). - - ``normalize`` -- boolean (optional - default: ``False``) + - ``normalize`` -- boolean (optional - default: ``False``). OUTPUT: - - a list of points in ``self.codomain()`` + - a list of points in this map's codomain. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2-z^2,2*z^2]) - sage: f.orbit(P(1,2,1),3) + sage: f = H([x^2+y^2, y^2-z^2, 2*z^2]) + sage: f.orbit(P(1,2,1), 3) [(1 : 2 : 1), (5 : 3 : 2), (34 : 5 : 8), (1181 : -39 : 128)] :: sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2-z^2,2*z^2]) - sage: f.orbit(P(1,2,1),[2,4]) + sage: f = H([x^2+y^2, y^2-z^2, 2*z^2]) + sage: f.orbit(P(1,2,1), [2,4]) [(34 : 5 : 8), (1181 : -39 : 128), (1396282 : -14863 : 32768)] :: @@ -1279,16 +1280,16 @@ def orbit(self, P, N, **kwds): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,x*z]) - sage: f.orbit(X(2,2,3),3,normalize=True) + sage: f = H([x^2, y^2, x*z]) + sage: f.orbit(X(2,2,3), 3, normalize=True) [(2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3)] :: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) - sage: f.orbit(P.point([1,2],False),4,check = False) + sage: f = H([x^2+y^2, y^2]) + sage: f.orbit(P.point([1,2],False), 4, check=False) [(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)] :: @@ -1296,8 +1297,8 @@ def orbit(self, P, N, **kwds): sage: K. = FunctionField(QQ) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([x^2+c*y^2,y^2]) - sage: f.orbit(P(0,1),3) + sage: f = H([x^2+c*y^2, y^2]) + sage: f.orbit(P(0,1), 3) [(0 : 1), (c : 1), (c^2 + c : 1), (c^4 + 2*c^3 + c^2 + c : 1)] """ return(P.orbit(self, N, **kwds)) @@ -1305,10 +1306,11 @@ def orbit(self, P, N, **kwds): @cached_method def is_morphism(self): r""" - returns ``True`` if self is a morphism (no common zero of defining polynomials). + returns ``True`` if this map is a morphism. The map is a morphism if and only if the ideal generated by - the defining polynomials is the unit ideal. + the defining polynomials is the unit ideal + (no common zeros of the defining polynomials). OUTPUT: @@ -1318,7 +1320,7 @@ def is_morphism(self): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.is_morphism() True @@ -1326,7 +1328,7 @@ def is_morphism(self): sage: P. = ProjectiveSpace(RR,2) sage: H = Hom(P,P) - sage: f = H([x*z-y*z,x^2-y^2,z^2]) + sage: f = H([x*z-y*z, x^2-y^2, z^2]) sage: f.is_morphism() False @@ -1335,7 +1337,7 @@ def is_morphism(self): sage: R. = PolynomialRing(GF(5)) sage: P. = ProjectiveSpace(R,2) sage: H = Hom(P,P) - sage: f = H([x*z-t*y^2,x^2-y^2,t*z^2]) + sage: f = H([x*z-t*y^2, x^2-y^2, t*z^2]) sage: f.is_morphism() True @@ -1344,7 +1346,7 @@ def is_morphism(self): sage: P. = ProjectiveSpace(RR,2) sage: X = P.subscheme([x*y + y*z]) sage: H = Hom(X,X) - sage: f = H([x*z-y*z,x^2-y^2,z^2]) + sage: f = H([x*z-y*z, x^2-y^2, z^2]) sage: f.is_morphism() True """ @@ -1366,24 +1368,24 @@ def is_morphism(self): def resultant(self, normalize=False): r""" - Computes the resultant of the defining polynomials of ``self`` if ``self`` is a map in `\mathbb{P}^n` + Computes the resultant of the defining polynomials of this map. If ``normalize`` is ``True``, then first normalize the coordinate functions with :meth:`normalize_coordinates`. INPUT: - - ``normalize`` -- Boolean (optional - default: ``False``) + - ``normalize`` -- Boolean (optional - default: ``False``). OUTPUT: - - an element of ``self.codomain().base_ring()`` + - an element of the base ring of this map. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,6*y^2]) + sage: f = H([x^2+y^2, 6*y^2]) sage: f.resultant() 36 @@ -1392,7 +1394,7 @@ def resultant(self, normalize=False): sage: R. = PolynomialRing(GF(17)) sage: P. = ProjectiveSpace(R,1) sage: H = Hom(P,P) - sage: f = H([t*x^2+t*y^2,6*y^2]) + sage: f = H([t*x^2+t*y^2, 6*y^2]) sage: f.resultant() 2*t^2 @@ -1401,7 +1403,7 @@ def resultant(self, normalize=False): sage: R. = PolynomialRing(GF(17)) sage: P. = ProjectiveSpace(R,2) sage: H = Hom(P,P) - sage: f = H([t*x^2+t*y^2,6*y^2,2*t*z^2]) + sage: f = H([t*x^2+t*y^2, 6*y^2, 2*t*z^2]) sage: f.resultant() 13*t^8 @@ -1419,14 +1421,14 @@ def resultant(self, normalize=False): sage: s = (t^3+t+1).roots(QQbar)[0][0] sage: P.=ProjectiveSpace(QQbar,1) sage: H = Hom(P,P) - sage: f = H([s*x^3-13*y^3,y^3-15*y^3]) + sage: f = H([s*x^3-13*y^3, y^3-15*y^3]) sage: f.resultant() 871.6925062959149? """ if self.domain().dimension_relative() != self.codomain().dimension_relative(): - raise ValueError("Domain and Codomain should be of same dimension") - if normalize is True: + raise ValueError("domain and codomain should be of same dimension") + if normalize: F = copy(self) F.normalize_coordinates() else: @@ -1455,7 +1457,7 @@ def resultant(self, normalize=False): @cached_method def primes_of_bad_reduction(self, check=True): r""" - Determines the primes of bad reduction for a map `self: \mathbb{P}^N \to \mathbb{P}^N` + Determines the primes of bad reduction for an endomorphism defined over number fields. If ``check`` is ``True``, each prime is verified to be of bad reduction. @@ -1470,11 +1472,11 @@ def primes_of_bad_reduction(self, check=True): power of each `x_i` is not in the ideal defined by the defining polynomials of self. This last condition is what is checked. The lcm of the coefficients of the monomials `x_i` in - a groebner basis is computed. This may return extra primes. + a Groebner basis is computed. This may return extra primes. INPUT: - - ``check`` -- Boolean (optional - default: ``True``) + - ``check`` -- Boolean (optional - default: ``True``). OUTPUT: @@ -1484,7 +1486,7 @@ def primes_of_bad_reduction(self, check=True): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([1/3*x^2+1/2*y^2,y^2]) + sage: f = H([1/3*x^2+1/2*y^2, y^2]) sage: print f.primes_of_bad_reduction() [2, 3] @@ -1492,7 +1494,7 @@ def primes_of_bad_reduction(self, check=True): sage: P. = ProjectiveSpace(QQ,3) sage: H = Hom(P,P) - sage: f = H([12*x*z-7*y^2,31*x^2-y^2,26*z^2,3*w^2-z*w]) + sage: f = H([12*x*z-7*y^2, 31*x^2-y^2, 26*z^2, 3*w^2-z*w]) sage: f.primes_of_bad_reduction() [2, 3, 7, 13, 31] @@ -1502,7 +1504,7 @@ def primes_of_bad_reduction(self, check=True): sage: K. = NumberField(z^2 - 2) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([1/3*x^2+1/a*y^2,y^2]) + sage: f = H([1/3*x^2+1/a*y^2, y^2]) sage: f.primes_of_bad_reduction() [Fractional ideal (a), Fractional ideal (3)] @@ -1510,7 +1512,8 @@ def primes_of_bad_reduction(self, check=True): sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([3*x*y^2 + 7*y^3 - 4*y^2*z + 5*z^3, -5*x^3 + x^2*y + y^3 + 2*x^2*z, -2*x^2*y + x*y^2 + y^3 - 4*y^2*z + x*z^2]) + sage: f = H([3*x*y^2 + 7*y^3 - 4*y^2*z + 5*z^3, -5*x^3 + x^2*y + y^3 + 2*x^2*z,\ + -2*x^2*y + x*y^2 + y^3 - 4*y^2*z + x*z^2]) sage: f.primes_of_bad_reduction(False) [2, 5, 37, 2239, 304432717] sage: f.primes_of_bad_reduction() @@ -1538,7 +1541,7 @@ def primes_of_bad_reduction(self, check=True): S = PolynomialRing(R.base_ring().fraction_field(), R.gens(), R.ngens()) J = S.ideal([S.coerce(F[i]) for i in range(R.ngens())]) if J.dimension() > 0: - raise TypeError("Not a morphism.") + raise TypeError("not a morphism") #normalize to coefficients in the ring not the fraction field. F = [F[i] * lcm([F[j].denominator() for j in range(len(F))]) for i in range(len(F))] @@ -1564,7 +1567,7 @@ def primes_of_bad_reduction(self, check=True): badprimes = sorted(set(badprimes)) #check to return only the truly bad primes - if check == True: + if check: index = 0 while index < len(badprimes): #figure out which primes are really bad primes... S = PolynomialRing(GF(badprimes[index]), R.gens(), R.ngens()) @@ -1575,30 +1578,30 @@ def primes_of_bad_reduction(self, check=True): index += 1 return(badprimes) else: - raise TypeError("Base Ring must be number field or number field ring") + raise TypeError("base ring must be number field or number field ring") def conjugate(self, M): r""" - Conjugates ``self`` by ``M``, i.e. `M^{-1} \circ f \circ M`. + Conjugates this map by ``M``, i.e. `M^{-1} \circ f \circ M`. - If possible the map will be defined over the same space as - ``self``. Otherwise, will try to coerce to the base_ring of + If possible the new map will be defined over the same space as + this map. Otherwise, will try to coerce to the base ring of ``M``. INPUT: - - ``M`` -- a square invertible matrix + - ``M`` -- a square invertible matrix. OUTPUT: - - a map from ``self.domain()`` to ``self.codomain()``. + - a map from the domain to the codomain of this map. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) - sage: f.conjugate(matrix([[1,2],[0,1]])) + sage: f = H([x^2+y^2, y^2]) + sage: f.conjugate(matrix([[1,2], [0,1]])) Scheme endomorphism of Projective Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x : y) to (x^2 + 4*x*y + 3*y^2 : y^2) @@ -1609,8 +1612,8 @@ def conjugate(self, M): sage: K. = NumberField(x^2+1) sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^3+y^3,y^3]) - sage: f.conjugate(matrix([[i,0],[0,-i]])) + sage: f = H([x^3+y^3, y^3]) + sage: f.conjugate(matrix([[i,0], [0,-i]])) Scheme endomorphism of Projective Space of dimension 1 over Integer Ring Defn: Defined on coordinates by sending (x : y) to (-x^3 + y^3 : -y^3) @@ -1619,8 +1622,8 @@ def conjugate(self, M): sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2,y*z]) - sage: f.conjugate(matrix([[1,2,3],[0,1,2],[0,0,1]])) + sage: f = H([x^2+y^2 ,y^2, y*z]) + sage: f.conjugate(matrix([[1,2,3], [0,1,2], [0,0,1]])) Scheme endomorphism of Projective Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x : y : z) to (x^2 + 4*x*y + 3*y^2 + 6*x*z + 9*y*z + 7*z^2 : y^2 + 2*y*z : y*z + 2*z^2) @@ -1629,8 +1632,8 @@ def conjugate(self, M): sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2]) - sage: f.conjugate(matrix([[2,0],[0,1/2]])) + sage: f = H([x^2+y^2, y^2]) + sage: f.conjugate(matrix([[2,0], [0,1/2]])) Scheme endomorphism of Projective Space of dimension 1 over Multivariate Polynomial Ring in x, y over Rational Field Defn: Defined on coordinates by sending (x : y) to @@ -1642,8 +1645,8 @@ def conjugate(self, M): sage: K. = NumberField(x^2+1) sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([1/3*x^2+1/2*y^2,y^2]) - sage: f.conjugate(matrix([[i,0],[0,-i]])) + sage: f = H([1/3*x^2+1/2*y^2, y^2]) + sage: f.conjugate(matrix([[i,0], [0,-i]])) Scheme endomorphism of Projective Space of dimension 1 over Multivariate Polynomial Ring in x, y over Number Field in i with defining polynomial x^2 + 1 @@ -1673,54 +1676,60 @@ def conjugate(self, M): def green_function(self, P, v, **kwds): r""" Evaluates the local Green's function at the place ``v`` for ``P`` with ``N`` terms of the - series or to within a given error bound. Must be over a number field - or order of a number field. Note that this is absolute local greens function - so is scaled by the degree of the base field. + series or to within a given error bound. + + Must be over a number field or order of a number field. Note that this is + the absolute local Green's function so is scaled by the degree of the base field. Use ``v=0`` for the archimedean place over `\QQ` or field embedding. Non-archimedean places are prime ideals for number fields or primes over `\QQ`. ALGORITHM: - See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007. + See Exercise 5.29 and Figure 5.6 of [Silverman-ADS]_. + + REFERENCES: + + .. [Silverman-ADS] Joseph H. Silverman. The Arithmetic of Dynamics Systems. Springer, GTM 241, 2007. INPUT: - - ``P`` - a projective point + - ``P`` - a projective point. - - ``v`` - non-negative integer. a place, use v=0 for the archimedean place + - ``v`` - non-negative integer. a place, use v=0 for the archimedean place. kwds: - - ``N`` - positive integer. number of terms of the series to use, (optional - default: 10) + - ``N`` - positive integer. number of terms of the series to use, (optional - default: 10). - - ``prec`` - positive integer, float point or p-adic precision, default: 100 + - ``prec`` - positive integer, float point or p-adic precision, default: 100. - - ``error_bound`` - a positive real number (optional) + - ``error_bound`` - a positive real number (optional). OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,x*y]) - sage: f.green_function(P.point([5,2],False),0,N=30) + sage: f = H([x^2+y^2, x*y]) + sage: f.green_function(P.point([5,2], False), 0, N=30) 1.7315451844777407992085512000 - sage: f.green_function(P.point([2,1],False),0,N=30) + sage: f.green_function(P.point([2,1], False), 0, N=30) 0.86577259223181088325226209926 - sage: f.green_function(P.point([1,1],False),0,N=30) + sage: f.green_function(P.point([1,1], False), 0, N=30) 0.43288629610862338612700146098 """ return(P.green_function(self, v, **kwds)) def canonical_height(self, P, **kwds): r""" - Evaluates the (absolute) canonical height of ``P`` with respect to ``self``. Must be over number field - or order of a number field. Specify either the number of terms of the series to evaluate or - the error bound required. + Evaluates the (absolute) canonical height of ``P`` with respect to this map. + + Must be over number field or order of a number field. Specify either + the number of terms of the series to evaluate or the error bound required. ALGORITHM: @@ -1728,18 +1737,18 @@ def canonical_height(self, P, **kwds): INPUT: - - ``P`` -- a projective point + - ``P`` -- a projective point. kwds: - - ``badprimes`` - a list of primes of bad reduction (optional) + - ``badprimes`` - a list of primes of bad reduction (optional). - ``N`` - positive integer. number of terms of the series to use in the local green functions - (optional - default: 10) + (optional - default: 10). - - ``prec`` - positive integer, float point or p-adic precision, default: 100 + - ``prec`` - positive integer, float point or p-adic precision, default: 100. - - ``error_bound`` - a positive real number (optional) + - ``error_bound`` - a positive real number (optional). OUTPUT: @@ -1749,7 +1758,7 @@ def canonical_height(self, P, **kwds): sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,2*x*y]); + sage: f = H([x^2+y^2, 2*x*y]); sage: f.canonical_height(P.point([5,4]), error_bound=0.001) 2.1970553519503404898926835324 sage: f.canonical_height(P.point([2,1]), error_bound=0.001) @@ -1759,7 +1768,7 @@ def canonical_height(self, P, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2-29/16*y^2,y^2]); + sage: f = H([x^2-29/16*y^2, y^2]); sage: f.canonical_height(P.point([1,4]), error_bound=0.000001) 1.9185995011736159021863458227e-7 @@ -1768,7 +1777,7 @@ def canonical_height(self, P, **kwds): sage: P. = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x^2-y^2); sage: H = Hom(X,X) - sage: f = H([x^2,y^2,4*z^2]); + sage: f = H([x^2,y^2, 4*z^2]); sage: Q = X([4,4,1]) sage: f.canonical_height(Q, badprimes=[2]) 0.0013538030870311431824555314882 @@ -1778,7 +1787,7 @@ def canonical_height(self, P, **kwds): def global_height(self, prec=None): r""" Returns the maximum of the absolute logarithmic heights of the coefficients - in any of the coordinate functions of ``self``. + in any of the coordinate functions of this map. INPUT: @@ -1787,13 +1796,13 @@ def global_height(self, prec=None): OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([1/1331*x^2+1/4000*y^2,210*x*y]); + sage: f = H([1/1331*x^2+1/4000*y^2, 210*x*y]); sage: f.global_height() 8.29404964010203 @@ -1801,7 +1810,7 @@ def global_height(self, prec=None): sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]); + sage: f = H([4*x^2+100*y^2, 210*x*y, 10000*z^2]); sage: f.global_height() 9.21034037197618 sage: f.normalize_coordinates() @@ -1815,7 +1824,7 @@ def global_height(self, prec=None): sage: O = K.maximal_order() sage: P. = ProjectiveSpace(O,1) sage: H = Hom(P,P) - sage: f = H([2*x^2 + 3*O(w)*y^2,O(w)*y^2]) + sage: f = H([2*x^2 + 3*O(w)*y^2, O(w)*y^2]) sage: f.global_height() 1.44518587894808 @@ -1824,7 +1833,7 @@ def global_height(self, prec=None): sage: P. = ProjectiveSpace(QQbar,1) sage: P2. = ProjectiveSpace(QQbar,2) sage: H = Hom(P,P2) - sage: f = H([x^2 + QQbar(I)*x*y + 3*y^2,y^2,QQbar(sqrt(5))*x*y]) + sage: f = H([x^2 + QQbar(I)*x*y + 3*y^2, y^2, QQbar(sqrt(5))*x*y]) sage: f.global_height() 1.09861228866811 """ @@ -1845,24 +1854,24 @@ def global_height(self, prec=None): def local_height(self, v, prec=None): r""" Returns the maximum of the local height of the coefficients in any - of the coordinate functions of ``self``. + of the coordinate functions of this map. INPUT: - - ``v`` -- a prime or prime ideal of the base ring + - ``v`` -- a prime or prime ideal of the base ring. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([1/1331*x^2+1/4000*y^2,210*x*y]); + sage: f = H([1/1331*x^2+1/4000*y^2, 210*x*y]); sage: f.local_height(1331) 7.19368581839511 @@ -1870,7 +1879,7 @@ def local_height(self, v, prec=None): sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([4*x^2+3/100*y^2,8/210*x*y,1/10000*z^2]); + sage: f = H([4*x^2+3/100*y^2, 8/210*x*y, 1/10000*z^2]); sage: f.local_height(2) 2.77258872223978 sage: f.normalize_coordinates() @@ -1883,36 +1892,36 @@ def local_height(self, v, prec=None): sage: K. = NumberField(z^2-2) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([2*x^2 + w/3*y^2,1/w*y^2]) + sage: f = H([2*x^2 + w/3*y^2, 1/w*y^2]) sage: f.local_height(K.ideal(3)) 1.09861228866811 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: - raise TypeError("Must be over a Numberfield or a Numberfield Order") + raise TypeError("must be over a number field or a number field order") return max([K(c).local_height(v, prec) for f in self for c in f.coefficients()]) def local_height_arch(self, i, prec=None): r""" Returns the maximum of the local height at the ``i``-th infinite place of the coefficients in any - of the coordinate functions of ``self``. + of the coordinate functions of this map. INPUT: - - ``i`` -- an integer + - ``i`` -- an integer. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([1/1331*x^2+1/4000*y^2,210*x*y]); + sage: f = H([1/1331*x^2+1/4000*y^2, 210*x*y]); sage: f.local_height_arch(0) 5.34710753071747 @@ -1922,13 +1931,13 @@ def local_height_arch(self, i, prec=None): sage: K. = NumberField(z^2-2) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([2*x^2 + w/3*y^2,1/w*y^2]) + sage: f = H([2*x^2 + w/3*y^2, 1/w*y^2]) sage: f.local_height_arch(1) 0.6931471805599453094172321214582 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: - raise TypeError("Must be over a Numberfield or a Numberfield Order") + raise TypeError("must be over a number field or a number field order") if K == QQ: return max([K(c).local_height_arch(prec=prec) for f in self for c in f.coefficients()]) else: @@ -1938,27 +1947,28 @@ def local_height_arch(self, i, prec=None): def height_difference_bound(self, prec=None): r""" Returns an upper bound on the different bewtween the canonical height of a point with - respect to ``self`` and the absolute height of the point. ``self`` must be a morphism. + respect to this map and the absolute height of the point. + + This map must be a morphism. ALGORITHM: Uses a Nullstellensatz argument to compute the constant. - For details: B. Hutz, Efficient determination of rational preperiodic points for endomorphisms of projective - space, arxiv:1210.6246 (2012). + For details: see [Hutz]_. INPUT: - - ``prec`` - positive integer, float point, default: RealField default + - ``prec`` - positive integer, float point, default: RealField default. OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2+y^2,x*y]); + sage: f = H([x^2+y^2, x*y]); sage: f.height_difference_bound() 1.38629436111989 @@ -1966,7 +1976,7 @@ def height_difference_bound(self, prec=None): sage: P. = ProjectiveSpace(ZZ,2) sage: H = End(P) - sage: f = H([4*x^2+100*y^2,210*x*y,10000*z^2]); + sage: f = H([4*x^2+100*y^2, 210*x*y, 10000*z^2]); sage: f.height_difference_bound() 11.0020998412042 sage: f.normalize_coordinates() @@ -1979,7 +1989,7 @@ def height_difference_bound(self, prec=None): sage: K. = NumberField(x^3 - 2) sage: P. = ProjectiveSpace(K,2) sage: H = End(P) - sage: f = H([1/(c+1)*x^2+c*y^2,210*x*y,10000*z^2]) + sage: f = H([1/(c+1)*x^2+c*y^2, 210*x*y, 10000*z^2]) sage: f.height_difference_bound() 11.0020998412042 @@ -1998,11 +2008,11 @@ def height_difference_bound(self, prec=None): #function is defined. f = self._number_field_from_algebraics() else: - raise NotImplementedError("Fraction field of the base ring must be a number field or QQbar") + raise NotImplementedError("fraction field of the base ring must be a number field or QQbar") else: f = self.change_ring(FF) if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") if prec is None: R = RealField() else: @@ -2036,27 +2046,29 @@ def height_difference_bound(self, prec=None): def multiplier(self, P, n, check=True): r""" - Returns the multiplier of ``self`` point ``P`` of period ``n``. - ``self`` must be an endomorphism. + Returns the multiplier of the point ``P`` of period ``n`` with respect to this map. + + This map must have same domain and codomain. INPUT: - - ``P`` - a point on domain of ``self`` + - ``P`` - a point on domain of this map. - - ``n`` - a positive integer, the period of ``P`` + - ``n`` - a positive integer, the period of ``P``. - - ``check`` -- verify that ``P`` has period ``n``, Default:True + - ``check`` -- verify that ``P`` has period ``n``, Default:True. OUTPUT: - - a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of ``self`` + - a square matrix of size ``self.codomain().dimension_relative()`` + in the ``base_ring`` of this map. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,2) sage: H = End(P) - sage: f = H([x^2,y^2,4*z^2]); - sage: Q = P.point([4,4,1],False); + sage: f = H([x^2,y^2, 4*z^2]); + sage: Q = P.point([4,4,1], False); sage: f.multiplier(Q,1) [2 0] [0 2] @@ -2065,32 +2077,32 @@ def multiplier(self, P, n, check=True): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([7*x^2 - 28*y^2,24*x*y]) - sage: f.multiplier(P(2,5),4) + sage: f = H([7*x^2 - 28*y^2, 24*x*y]) + sage: f.multiplier(P(2,5), 4) [231361/20736] :: sage: P. = ProjectiveSpace(CC,1) sage: H = End(P) - sage: f = H([x^3 - 25*x*y^2 + 12*y^3,12*y^3]) - sage: f.multiplier(P(1,1),5) + sage: f = H([x^3 - 25*x*y^2 + 12*y^3, 12*y^3]) + sage: f.multiplier(P(1,1), 5) [0.389017489711935] :: sage: P. = ProjectiveSpace(RR,1) sage: H = End(P) - sage: f = H([x^2-2*y^2,y^2]) - sage: f.multiplier(P(2,1),1) + sage: f = H([x^2-2*y^2, y^2]) + sage: f.multiplier(P(2,1), 1) [4.00000000000000] :: sage: P. = ProjectiveSpace(Qp(13),1) sage: H = End(P) - sage: f = H([x^2-29/16*y^2,y^2]) - sage: f.multiplier(P(5,4),3) + sage: f = H([x^2-29/16*y^2, y^2]) + sage: f.multiplier(P(5,4), 3) [6 + 8*13 + 13^2 + 8*13^3 + 13^4 + 8*13^5 + 13^6 + 8*13^7 + 13^8 + 8*13^9 + 13^10 + 8*13^11 + 13^12 + 8*13^13 + 13^14 + 8*13^15 + 13^16 + 8*13^17 + 13^18 + 8*13^19 + O(13^20)] @@ -2099,19 +2111,19 @@ def multiplier(self, P, n, check=True): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-y^2,y^2]) - sage: f.multiplier(P(0,1),1) + sage: f = H([x^2-y^2, y^2]) + sage: f.multiplier(P(0,1), 1) Traceback (most recent call last): ... ValueError: (0 : 1) is not periodic of period 1 """ if not self.is_endomorphism(): - raise TypeError("Must be an endomorphism") + raise TypeError("must be an endomorphism") if check: if self.nth_iterate(P, n) != P: - raise ValueError("%s is not periodic of period %s" % (P, n)) + raise ValueError("%s is not periodic of period %s"%(P, n)) if n < 1: - raise ValueError("Period must be a positive integer") + raise ValueError("period must be a positive integer") N = self.domain().ambient_space().dimension_relative() l = identity_matrix(FractionField(self.codomain().base_ring()), N, N) Q = P @@ -2131,26 +2143,29 @@ def multiplier(self, P, n, check=True): indexlist.append(index) #dehomogenize and compute multiplier F = self.dehomogenize((indexlist[i],indexlist[i+1])) - l = F.jacobian()(tuple(Q.dehomogenize(indexlist[i])))*l #get the correct order for chain rule matrix multiplication + #get the correct order for chain rule matrix multiplication + l = F.jacobian()(tuple(Q.dehomogenize(indexlist[i])))*l Q = R return l def _multipliermod(self, P, n, p, k): r""" - Returns the multiplier of ``self`` at the point ``P`` of period ``n`` modulo `p^k`. - self must be an endomorphism of projective space defined over `\QQ` or '\ZZ'. + Returns the multiplier of the point ``P`` of period ``n`` with respect to + this map modulo `p^k`. + + This map must be an endomorphism of projective space defined over `\QQ` or '\ZZ'. This function should not be used at the top level as it does not perform input checks. It is used primarily for the rational preperiodic and periodic point algorithms. INPUT: - - ``P`` - a point on domain of ``self`` + - ``P`` - a point on domain of this map. - - ``n`` - a positive integer, the period of ``P`` + - ``n`` - a positive integer, the period of ``P``. - - ``p`` - a positive integer + - ``p`` - a positive integer. - - ``k`` - a positive integer + - ``k`` - a positive integer. OUTPUT: @@ -2160,16 +2175,16 @@ def _multipliermod(self, P, n, p, k): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-29/16*y^2,y^2]) - sage: f._multipliermod(P(5,4),3,11,1) + sage: f = H([x^2-29/16*y^2, y^2]) + sage: f._multipliermod(P(5,4), 3, 11, 1) [3] :: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-29/16*y^2,y^2]) - sage: f._multipliermod(P(5,4),3,11,2) + sage: f = H([x^2-29/16*y^2, y^2]) + sage: f._multipliermod(P(5,4), 3, 11, 2) [80] """ N = self.domain().dimension_relative() @@ -2202,7 +2217,8 @@ def _multipliermod(self, P, n, p, k): def possible_periods(self, **kwds): r""" - Returns the set of possible periods for rational periodic points of self. + Returns the set of possible periods for rational periodic points of this map. + Must be defined over `\ZZ` or `\QQ`. ALGORITHM: @@ -2229,7 +2245,7 @@ def possible_periods(self, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-29/16*y^2,y^2]) + sage: f = H([x^2-29/16*y^2, y^2]) sage: f.possible_periods(ncpus=1) [1, 3] @@ -2241,7 +2257,7 @@ def possible_periods(self, **kwds): sage: f.possible_periods(prime_bound=[1,5]) Traceback (most recent call last): ... - ValueError: No primes of good reduction in that range + ValueError: no primes of good reduction in that range sage: f.possible_periods(prime_bound=[1,10]) [1, 4, 12] sage: f.possible_periods(prime_bound=[1,20]) @@ -2251,16 +2267,16 @@ def possible_periods(self, **kwds): sage: P. = ProjectiveSpace(ZZ,2) sage: H = End(P) - sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3]) + sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3, 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) sage: f.possible_periods(prime_bound=10) [1, 2, 6, 20, 42, 60, 140, 420] sage: f.possible_periods(prime_bound=20) # long time [1, 20] """ if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") if self.domain().base_ring() != ZZ and self.domain().base_ring() != QQ: - raise NotImplementedError("Must be ZZ or QQ") + raise NotImplementedError("must be ZZ or QQ") primebound = kwds.pop("prime_bound", [1, 20]) @@ -2271,13 +2287,13 @@ def possible_periods(self, **kwds): try: primebound = [1, ZZ(primebound)] except TypeError: - raise TypeError("Bound on primes must be an integer") + raise TypeError("prime bound must be an integer") else: try: primebound[0] = ZZ(primebound[0]) primebound[1] = ZZ(primebound[1]) except TypeError: - raise TypeError("Prime bounds must be integers") + raise TypeError("prime bounds must be integers") if badprimes is None: badprimes = self.primes_of_bad_reduction() @@ -2307,30 +2323,33 @@ def parallel_function(morphism): periods = periods.intersection(periodsq) if firstgood == 0: - raise ValueError("No primes of good reduction in that range") + raise ValueError("no primes of good reduction in that range") else: return(sorted(periods)) def _preperiodic_points_to_cyclegraph(self, preper): r""" - Given the complete set of periodic or preperiodic points returns the - digraph representing the orbit. If it is not the complete set, this function - will not fill in the gaps. + Given the complete set of periodic or preperiodic points return the + digraph representing the orbit. + + If ``preper`` is not the complete set, this function will not fill in the gaps. INPUT: - - ``preper`` - a list or tuple of projective points. The complete set of rational periodic or preperiodic points. + - ``preper`` - a list or tuple of projective points. The complete set + of rational periodic or preperiodic points. OUTPUT: - - a digraph representing the orbit the rational preperiodic points ``preper`` in projective space. + - a digraph representing the orbit the rational preperiodic points + ``preper`` in projective space. Examples:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-2*y^2,y^2]) + sage: f = H([x^2-2*y^2, y^2]) sage: preper = [P(-2, 1), P(1, 0), P(0, 1), P(1, 1), P(2, 1), P(-1, 1)] sage: f._preperiodic_points_to_cyclegraph(preper) Looped digraph on 6 vertices @@ -2360,22 +2379,23 @@ def _preperiodic_points_to_cyclegraph(self, preper): def is_PGL_minimal(self, prime_list=None): r""" - Checks if ``self`` is a minimal model in its conjugacy class. See [Bruin-Molnar] - and [Molnar] for a description of the algorithm. + Checks if this map is a minimal model in its conjugacy class. + + See [Bruin-Molnar]_ and [Molnar]_ for a description of the algorithm. INPUT: - - ``prime_list`` -- list of primes to check minimality, if None, check all places + - ``prime_list`` -- list of primes to check minimality, if None, check all places. OUTPUT: - - Boolean - True if ``self`` is minimal, False otherwise. + - Boolean - True if this map is minimal, False otherwise. EXAMPLES:: sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([X^2+3*Y^2,X*Y]) + sage: f = H([X^2+3*Y^2, X*Y]) sage: f.is_PGL_minimal() True @@ -2383,7 +2403,7 @@ def is_PGL_minimal(self, prime_list=None): sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y]) + sage: f = H([6*x^2+12*x*y+7*y^2, 12*x*y]) sage: f.is_PGL_minimal() False @@ -2391,29 +2411,30 @@ def is_PGL_minimal(self, prime_list=None): sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([6*x^2+12*x*y+7*y^2,y^2]) + sage: f = H([6*x^2+12*x*y+7*y^2, y^2]) sage: f.is_PGL_minimal() Traceback (most recent call last): ... - TypeError: Affine minimality is only considered for maps not of the form - f or 1/f for a polynomial f. + TypeError: affine minimality is only considered for maps not of the form + f or 1/f for a polynomial f """ if self.base_ring() != QQ and self.base_ring() != ZZ: - raise NotImplementedError("Minimal models only implemented over ZZ or QQ") + raise NotImplementedError("minimal models only implemented over ZZ or QQ") if not self.is_morphism(): - raise TypeError("The function is not a morphism") + raise TypeError("the function is not a morphism") if self.degree() == 1: - raise NotImplementedError("Minimality is only for degree 2 or higher") + raise NotImplementedError("minimality is only for degree 2 or higher") from endPN_minimal_model import affine_minimal return(affine_minimal(self, False , prime_list , True)) def minimal_model(self, return_transformation=False, prime_list=None): r""" - Given ``self`` a scheme morphism on the projective line over the rationals, - determine if ``self`` is minimal. In particular, determine - if ``self`` is affine minimal, which is enough to decide if it is minimal - or not. See Proposition 2.10 in [Bruin-Molnar]. + Determine if this morphisms is minimal. + + This map must be defined over the projective line over the rationals. + In particular, determine if this map is affine minimal, which is enough + to decide if it is minimal or not. See Proposition 2.10 in [Bruin-Molnar]. REFERENCES: @@ -2421,27 +2442,25 @@ def minimal_model(self, return_transformation=False, prime_list=None): INPUT: - - ``self`` -- scheme morphism on the projective line defined over `QQ`. - - ``return_transformation`` -- a boolean value, default value True. This - signals a return of the ``PGL_2`` transformation - to conjugate ``self`` to the calculated minimal - model. default: False + signals a return of the `PGL_2` transformation + to conjugate this map to the calculated minimal + model. default: False. - ``prime_list`` -- a list of primes, in case one only wants to determine minimality at those specific primes. OUTPUT: - - a scheme morphism on the projective line which is a minimal model of ``self``. + - a scheme morphism on the projective line which is a minimal model of this map. - - a `PGL(2,QQ)` element which conjugates ``self`` to a minimal model + - a `PGL(2,\QQ)` element which conjugates this map to a minimal model. EXAMPLES:: sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([X^2+3*Y^2,X*Y]) + sage: f = H([X^2+3*Y^2, X*Y]) sage: f.minimal_model(return_transformation=True) ( Scheme endomorphism of Projective Space of dimension 1 over Rational @@ -2457,7 +2476,8 @@ def minimal_model(self, return_transformation=False, prime_list=None): sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([7365/2*X^4 + 6282*X^3*Y + 4023*X^2*Y^2 + 1146*X*Y^3 + 245/2*Y^4, -12329/2*X^4 - 10506*X^3*Y - 6723*X^2*Y^2 - 1914*X*Y^3 - 409/2*Y^4]) + sage: f = H([7365/2*X^4 + 6282*X^3*Y + 4023*X^2*Y^2 + 1146*X*Y^3 + 245/2*Y^4,\ + -12329/2*X^4 - 10506*X^3*Y - 6723*X^2*Y^2 - 1914*X*Y^3 - 409/2*Y^4]) sage: f.minimal_model(return_transformation=True) ( Scheme endomorphism of Projective Space of dimension 1 over Rational @@ -2474,7 +2494,7 @@ def minimal_model(self, return_transformation=False, prime_list=None): sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y]) + sage: f = H([6*x^2+12*x*y+7*y^2, 12*x*y]) sage: f.minimal_model() Scheme endomorphism of Projective Space of dimension 1 over Rational Field @@ -2485,46 +2505,48 @@ def minimal_model(self, return_transformation=False, prime_list=None): sage: PS. = ProjectiveSpace(ZZ,1) sage: H = End(PS) - sage: f = H([6*x^2+12*x*y+7*y^2,12*x*y + 42*y^2]) + sage: f = H([6*x^2+12*x*y+7*y^2, 12*x*y + 42*y^2]) sage: g,M=f.minimal_model(return_transformation=True) - sage: f.conjugate(M)==g + sage: f.conjugate(M) == g True :: sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([X+Y,X-3*Y]) + sage: f = H([X+Y, X-3*Y]) sage: f.minimal_model() Traceback (most recent call last): ... - NotImplementedError: Minimality is only for degree 2 or higher + NotImplementedError: minimality is only for degree 2 or higher :: sage: PS. = ProjectiveSpace(QQ,1) sage: H = End(PS) - sage: f = H([X^2-Y^2,X^2+X*Y]) + sage: f = H([X^2-Y^2, X^2+X*Y]) sage: f.minimal_model() Traceback (most recent call last): ... - TypeError: The function is not a morphism + TypeError: the function is not a morphism """ if self.base_ring() != QQ and self.base_ring() != ZZ: - raise NotImplementedError("Minimal models only implemented over ZZ or QQ") + raise NotImplementedError("minimal models only implemented over ZZ or QQ") if not self.is_morphism(): - raise TypeError("The function is not a morphism") + raise TypeError("the function is not a morphism") if self.degree() == 1: - raise NotImplementedError("Minimality is only for degree 2 or higher") + raise NotImplementedError("minimality is only for degree 2 or higher") from endPN_minimal_model import affine_minimal return(affine_minimal(self, return_transformation, prime_list, False)) def automorphism_group(self, **kwds): r""" - Given a homogenous rational function, this calculates the subsgroup of `PGL2` that is - the automorphism group of ``self``. + Calculates the subgroup of `PGL2` that is the automorphism group of this map. + + Dimension 1 only. The automorphism group is the set of `PGL(2)` elements that fix this map + under conjugation. INPUT: @@ -2533,7 +2555,7 @@ def automorphism_group(self, **kwds): - ``starting_prime`` -- The first prime to use for CRT. default: 5.(optional) - ``algorithm``-- Choose ``CRT``-Chinese Remainder Theorem- or ``fixed_points`` algorithm. - default: depends on ``self``. (optional) + default: depends on this map. (optional) - ``return_functions``-- Boolean - True returns elements as linear fractional transformations. False returns elements as `PGL2` matrices. default: False. (optional) @@ -2553,14 +2575,14 @@ def automorphism_group(self, **kwds): REFERENCES: - .. [FMV] Computing Conjugating Sets and Automorphism Groups of Rational Functions - by Xander Faber, Michelle Manes, and Bianca Viray + .. [FMV] Xander Faber, Michelle Manes, and Bianca Viray. Computing Conjugating Sets + and Automorphism Groups of Rational Functions. Journal of Algebra, 423 (2014), 1161-1190. EXAMPLES:: sage: R. = ProjectiveSpace(QQ,1) sage: H = End(R) - sage: f = H([x^2-y^2,x*y]) + sage: f = H([x^2-y^2, x*y]) sage: f.automorphism_group(return_functions=True) [x, -x] @@ -2568,7 +2590,7 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(QQ,1) sage: H = End(R) - sage: f = H([x^2 + 5*x*y + 5*y^2,5*x^2 + 5*x*y + y^2]) + sage: f = H([x^2 + 5*x*y + 5*y^2, 5*x^2 + 5*x*y + y^2]) sage: f.automorphism_group() [ [1 0] [0 2] @@ -2579,7 +2601,7 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(QQ,1) sage: H = End(R) - sage: f=H([x^2-2*x*y-2*y^2,-2*x^2-2*x*y+y^2]) + sage: f=H([x^2-2*x*y-2*y^2, -2*x^2-2*x*y+y^2]) sage: f.automorphism_group(return_functions=True) [x, 2/(2*x), -x - 1, -2*x/(2*x + 2), (-x - 1)/x, -1/(x + 1)] @@ -2587,9 +2609,10 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(QQ,1) sage: H = End(R) - sage: f = H([3*x^2*y - y^3,x^3 - 3*x*y^2]) - sage: f.automorphism_group(algorithm='CRT',return_functions=True,iso_type=True) - ([x, (x + 1)/(x - 1), (-x + 1)/(x + 1), -x, 1/x, -1/x, (x - 1)/(x + 1), (-x - 1)/(x - 1)], 'Dihedral of order 8') + sage: f = H([3*x^2*y - y^3, x^3 - 3*x*y^2]) + sage: f.automorphism_group(algorithm='CRT', return_functions=True, iso_type=True) + ([x, (x + 1)/(x - 1), (-x + 1)/(x + 1), -x, 1/x, -1/x, + (x - 1)/(x + 1), (-x - 1)/(x - 1)], 'Dihedral of order 8') :: @@ -2604,13 +2627,13 @@ def automorphism_group(self, **kwds): ] """ - alg = kwds.get('algorithm',None) + alg = kwds.get('algorithm', None) p = kwds.get('starting_prime', 5) return_functions = kwds.get('return_functions', False) - iso_type = kwds.get('iso_type',False) + iso_type = kwds.get('iso_type', False) if self.domain().dimension_relative() != 1: - raise NotImplementedError("Must be dimension 1") + raise NotImplementedError("must be dimension 1") f = self.dehomogenize(1) R = PolynomialRing(f.base_ring(),'x') if is_FractionFieldElement(f[0]): @@ -2630,10 +2653,11 @@ def automorphism_group(self, **kwds): def wronskian_ideal(self): r""" Returns the ideal generated by the critical point locus. - This is the vanishing of the maximal minors of the jacobian matrix. + + This is the vanishing of the maximal minors of the Jacobian matrix. Not implemented for subvarieties. - OUTPUT: an ideal in ``self.domain().coordinate_ring()`` + OUTPUT: an ideal in the coordinate ring of the domain of this map. Examples:: @@ -2641,7 +2665,7 @@ def wronskian_ideal(self): sage: K. = NumberField(x^2+11) sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([x^2-w*y^2,w*y^2]) + sage: f = H([x^2-w*y^2, w*y^2]) sage: f.wronskian_ideal() Ideal ((4*w)*x*y) of Multivariate Polynomial Ring in x, y over Number Field in w with defining polynomial x^2 + 11 @@ -2651,7 +2675,7 @@ def wronskian_ideal(self): sage: P. = ProjectiveSpace(QQ,1) sage: P2. = ProjectiveSpace(K,2) sage: H = Hom(P,P2) - sage: f = H([x^2-2*y^2,y^2,x*y]) + sage: f = H([x^2-2*y^2, y^2, x*y]) sage: f.wronskian_ideal() Ideal (4*x*y, 2*x^2 + 4*y^2, -2*y^2) of Multivariate Polynomial Ring in x, y over Rational Field @@ -2659,22 +2683,24 @@ def wronskian_ideal(self): dom = self.domain() from sage.schemes.projective.projective_space import is_ProjectiveSpace if not (is_ProjectiveSpace(dom) and is_ProjectiveSpace(self.codomain())): - raise NotImplementedError("Not implemented for subschemes") + raise NotImplementedError("not implemented for subschemes") N = dom.dimension_relative()+1 R = dom.coordinate_ring() J = jacobian(self.defining_polynomials(),dom.gens()) return(R.ideal(J.minors(N))) - def critical_points(self, R = None): + def critical_points(self, R=None): r""" - Returns the critical points of the endomorphism ``self`` defined over the ring ``R`` - or the base ring of ``self``. Must be dimension 1. + Returns the critical points of this endomorphism defined over the ring ``R`` + or its the base ring of this map. + + Must be dimension 1. INPUT: - - ``R`` - a ring (optional) + - ``R`` - a ring (optional). - OUTPUT: a list of projective space points defined over ``R`` + OUTPUT: a list of projective space points defined over ``R``. Examples:: @@ -2700,11 +2726,11 @@ def critical_points(self, R = None): from sage.schemes.projective.projective_space import is_ProjectiveSpace PS = self.domain() if not is_ProjectiveSpace(PS): - raise NotImplementedError("Not implemented for subschemes") + raise NotImplementedError("not implemented for subschemes") if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism") + raise NotImplementedError("must be an endomorphism") if PS.dimension_relative() > 1: - raise NotImplementedError("Use .wronskian_ideal() for dimension > 1") + raise NotImplementedError("use .wronskian_ideal() for dimension > 1") if R is None: F = self @@ -2716,15 +2742,17 @@ def critical_points(self, R = None): crit_points = X.rational_points() return crit_points - def is_postcritically_finite(self, err = 0.01): + def is_postcritically_finite(self, err=0.01): r""" - Determine if ``self`` is post-critially finite for ``self`` an endomorphism of - `\mathbb{P}^1`, i.e., check if each critical point is preperiodic. The optional - parameter ``err`` is passed into ``is_preperiodic()`` as part of the preperiodic check. + Determine if this map is post-critically finite. + + Only for endomorphisms of `\mathbb{P}^1`. It checks if each critical point + is preperiodic. The optional parameter ``err`` is passed into + ``is_preperiodic()`` as part of the preperiodic check. INPUT: - - ``err`` - positive real number (optional, Default: 0.01) + - ``err`` - positive real number (optional, Default: 0.01). OUTPUT: Boolean @@ -2750,7 +2778,7 @@ def is_postcritically_finite(self, err = 0.01): sage: K. = NumberField(z^8 + 3*z^6 + 3*z^4 + z^2 + 1) sage: PS. = ProjectiveSpace(K,1) sage: H = End(PS) - sage: f = H([x^3+v*y^3,y^3]) + sage: f = H([x^3+v*y^3, y^3]) sage: f.is_postcritically_finite() # long time True @@ -2758,21 +2786,21 @@ def is_postcritically_finite(self, err = 0.01): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([6*x^2+16*x*y+16*y^2,-3*x^2-4*x*y-4*y^2]) + sage: f = H([6*x^2+16*x*y+16*y^2, -3*x^2-4*x*y-4*y^2]) sage: f.is_postcritically_finite() True """ if not self.is_endomorphism(): - raise TypeError("Must be an endomorphism") + raise TypeError("must be an endomorphism") #iteration of subschemes not yet implemented if self.domain().dimension_relative() > 1: - raise NotImplementedError("Only implemented in dimension 1") + raise NotImplementedError("only implemented in dimension 1") #Since is_preperiodic uses heights we need to be over a numberfield K = FractionField(self.codomain().base_ring()) if not K in _NumberFields and not K is QQbar: - raise NotImplementedError("Must be over a NumberField or a NumberField Order or QQbar") + raise NotImplementedError("must be over a number field or a number field order or QQbar") F = self.change_ring(QQbar) crit_points = F.critical_points() @@ -2786,16 +2814,17 @@ def is_postcritically_finite(self, err = 0.01): def critical_point_portrait(self, check=True): r""" - If ``self`` is post-critically finite, return the critical point portrait of ``self``. + If this map is post-critically finite, return it's critical point portrait. + This is the directed graph of iterates starting with the critical points. Must be dimension 1. If ``check`` is True, then the map is first checked to see if it is postcrtically finite. INPUT: - - check - Boolean + - check - Boolean. - OUTPUT: a digraph + OUTPUT: a digraph. Examples:: @@ -2803,32 +2832,32 @@ def critical_point_portrait(self, check=True): sage: K. = NumberField(z^6 + 2*z^5 + 2*z^4 + 2*z^3 + z^2 + 1) sage: PS. = ProjectiveSpace(K,1) sage: H = End(PS) - sage: f = H([x^2+v*y^2,y^2]) - sage: f.critical_point_portrait(check = False) # long time + sage: f = H([x^2+v*y^2, y^2]) + sage: f.critical_point_portrait(check=False) # long time Looped digraph on 6 vertices :: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^5 + 5/4*x*y^4,y^5]) - sage: f.critical_point_portrait(check = False) + sage: f = H([x^5 + 5/4*x*y^4, y^5]) + sage: f.critical_point_portrait(check=False) Looped digraph on 5 vertices :: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 + 2*y^2,y^2]) + sage: f = H([x^2 + 2*y^2, y^2]) sage: f.critical_point_portrait() Traceback (most recent call last): ... - TypeError: Map be be post-critically finite + TypeError: map be be post-critically finite """ #input checking done in is_postcritically_finite if check: if not self.is_postcritically_finite(): - raise TypeError("Map be be post-critically finite") + raise TypeError("map be be post-critically finite") F = self.change_ring(QQbar) crit_points = F.critical_points() N = len(crit_points) @@ -2845,7 +2874,9 @@ def critical_point_portrait(self, check=True): def critical_height(self, **kwds): r""" - Compute the critical height of ``self``. The critical height is defined by J. Silverman as + Compute the critical height of this map. + + The critical height is defined by J. Silverman as the sum of the canonical heights of the critical points. This must be dimension 1 and defined over a number field or number field order. @@ -2853,14 +2884,14 @@ def critical_height(self, **kwds): kwds: - - ``badprimes`` - a list of primes of bad reduction (optional) + - ``badprimes`` - a list of primes of bad reduction. (optional) - - ``N`` - positive integer. number of terms of the series to use in the local green functions + - ``N`` - positive integer. number of terms of the series to use in the local green functions. (optional - Default: 10) - - ``prec`` - positive integer, float point or p-adic precision, Default: 100 + - ``prec`` - positive integer, float point or p-adic precision, Default: 100. - - ``error_bound`` - a positive real number (optional) + - ``error_bound`` - a positive real number. (optional) OUTPUT: Real number @@ -2878,7 +2909,7 @@ def critical_height(self, **kwds): sage: O = K.maximal_order() sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([x^2+w*y^2,y^2]) + sage: f = H([x^2+w*y^2, y^2]) sage: f.critical_height() 0.16090842452312941163719755472 @@ -2891,14 +2922,14 @@ def critical_height(self, **kwds): 0.000011738508366948556443245983996 """ if not self.is_endomorphism(): - raise TypeError("Must be an endomorphism") + raise TypeError("must be an endomorphism") PS = self.codomain() if PS.dimension_relative() > 1: - raise NotImplementedError("Only implemented in dimension 1") + raise NotImplementedError("only implemented in dimension 1") K = FractionField(PS.base_ring()) if not K in _NumberFields and not K is QQbar: - raise NotImplementedError("Must be over a NumberField or a NumberField Order or QQbar") + raise NotImplementedError("must be over a number field or a number field order or QQbar") F = self.change_ring(QQbar) crit_points = F.critical_points() n = len(crit_points) @@ -2910,28 +2941,29 @@ def critical_height(self, **kwds): ch += P.canonical_height(F, **kwds) return(ch) - def periodic_points(self, n, minimal = True): + def periodic_points(self, n, minimal=True): r""" - Computes the periodic points of period ``n`` of ``self``. For now, ``self`` must be a projective morphism - over a number field. + Computes the periodic points of period ``n`` of this map. + + For now, this map must be a projective morphism over a number field. INPUT: - - ``n`` - a positive integer + - ``n`` - a positive integer. - ``minimal`` - Boolean. True specifies to find only the periodic points of minimal period ``n``. False specifies to find all periodic points of period ``n``. Default: True. OUTPUT: - - a list of periodic points of ``self`` + - a list of periodic points of this map. EXAMPLES:: sage: set_verbose(None) sage: P. = ProjectiveSpace(QQbar,1) sage: H = Hom(P,P) - sage: f = H([x^2-x*y+y^2,x^2-y^2+x*y]) + sage: f = H([x^2-x*y+y^2, x^2-y^2+x*y]) sage: f.periodic_points(1) [(-0.500000000000000? - 0.866025403784439?*I : 1), (-0.500000000000000? + 0.866025403784439?*I : 1), (1 : 1)] @@ -2940,7 +2972,7 @@ def periodic_points(self, n, minimal = True): sage: P. = ProjectiveSpace(QuadraticField(5,'t'),2) sage: H = Hom(P,P) - sage: f = H([x^2 - 21/16*z^2,y^2-z^2,z^2]) + sage: f = H([x^2 - 21/16*z^2, y^2-z^2, z^2]) sage: f.periodic_points(2) [(-5/4 : -1 : 1), (-5/4 : -1/2*t + 1/2 : 1), (-5/4 : 0 : 1), (-5/4 : 1/2*t + 1/2 : 1), (-3/4 : -1 : 1), (-3/4 : 0 : 1), (1/4 : -1 : 1), (1/4 : -1/2*t + 1/2 : 1), (1/4 : 0 : 1), (1/4 : 1/2*t + 1/2 : 1), @@ -2952,7 +2984,7 @@ def periodic_points(self, n, minimal = True): sage: K = NumberField(w^6 - 3*w^5 + 5*w^4 - 5*w^3 + 5*w^2 - 3*w + 1,'s') sage: P. = ProjectiveSpace(K,2) sage: H = Hom(P,P) - sage: f = H([x^2+z^2,y^2+x^2,z^2+y^2]) + sage: f = H([x^2+z^2, y^2+x^2, z^2+y^2]) sage: f.periodic_points(1) [(-s^5 + 3*s^4 - 5*s^3 + 4*s^2 - 3*s + 1 : s^5 - 2*s^4 + 3*s^3 - 3*s^2 + 4*s - 1 : 1), (2*s^5 - 6*s^4 + 9*s^3 - 8*s^2 + 7*s - 4 : 2*s^5 - 5*s^4 + 7*s^3 - 5*s^2 + 6*s - 2 : 1), @@ -2965,8 +2997,8 @@ def periodic_points(self, n, minimal = True): sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2]) - sage: f.periodic_points(2,False) + sage: f = H([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) + sage: f.periodic_points(2, False) [(-5/4 : -1 : 1), (-5/4 : 2 : 1), (-3/4 : -1 : 1), (-3/4 : 2 : 1), (0 : 1 : 0), (1/4 : -1 : 1), (1/4 : 2 : 1), (1 : 0 : 0), (1 : 1 : 0), (7/4 : -1 : 1), (7/4 : 2 : 1)] @@ -2974,12 +3006,12 @@ def periodic_points(self, n, minimal = True): sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2]) + sage: f = H([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: f.periodic_points(2) [(-5/4 : -1 : 1), (-5/4 : 2 : 1), (1/4 : -1 : 1), (1/4 : 2 : 1)] """ if n <= 0: - raise ValueError("A positive integer period must be specified") + raise ValueError("a positive integer period must be specified") if not self.is_endomorphism(): raise TypeError("self must be an endomorphism") PS = self.domain().ambient_space() @@ -3010,24 +3042,27 @@ def periodic_points(self, n, minimal = True): break return points - def multiplier_spectra(self,n,formal = True): + def multiplier_spectra(self, n, formal=True): r""" - Computes the formal ``n`` multiplier spectra of ``self``, which is the set of multipliers of the - periodic points of formal period ``n`` of ``self`` included with the appropriate multiplicity. + Computes the formal ``n`` multiplier spectra of this map. + + This is the set of multipliers of the periodic points of formal period + ``n`` included with the appropriate multiplicity. User can also specify to compute the ``n`` multiplier spectra instead which includes the - multipliers of all periodic points of period ``n`` of ``self``. ``self`` must be defined over + multipliers of all periodic points of period ``n``.The map must be defined over projective space of dimension 1 over a number field. INPUT: - - ``n`` - a positive integer, the period + - ``n`` - a positive integer, the period. - - ``formal`` - a Boolean. True specifies to find the formal ``n`` multiplier spectra of ``self``. False - specifies to find the ``n`` multiplier spectra of ``self``. Default: True + - ``formal`` - a Boolean. True specifies to find the formal ``n`` multiplier spectra + of this map. False specifies to find the ``n`` multiplier spectra + of this map. Default: True OUTPUT: - - a list of QQbar elements + - a list of `\QQbar` elements. EXAMPLES:: @@ -3048,8 +3083,8 @@ def multiplier_spectra(self,n,formal = True): sage: K. = NumberField(z^4 - 4*z^2 + 1,'z') sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([x^2 - w/4*y^2,y^2]) - sage: f.multiplier_spectra(2,False) + sage: f = H([x^2 - w/4*y^2, y^2]) + sage: f.multiplier_spectra(2, False) [0, 5.931851652578137? + 0.?e-17*I, 0.0681483474218635? - 1.930649271699173?*I, 0.0681483474218635? + 1.930649271699173?*I] @@ -3057,7 +3092,7 @@ def multiplier_spectra(self,n,formal = True): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 - 3/4*y^2,y^2]) + sage: f = H([x^2 - 3/4*y^2, y^2]) sage: f.multiplier_spectra(2) [1] @@ -3065,7 +3100,7 @@ def multiplier_spectra(self,n,formal = True): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 - 7/4*y^2,y^2]) + sage: f = H([x^2 - 7/4*y^2, y^2]) sage: f.multiplier_spectra(3) [1, 1] """ @@ -3073,12 +3108,12 @@ def multiplier_spectra(self,n,formal = True): n = Integer(n) if (n < 1): - raise ValueError("Period must be a positive integer") + raise ValueError("period must be a positive integer") from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(PS): - raise NotImplementedError("Not implemented for subschemes") + raise NotImplementedError("not implemented for subschemes") if (PS.dimension_relative() > 1): - raise NonImplementedError("Only implemented for dimension 1") + raise NonImplementedError("only implemented for dimension 1") if not self.is_endomorphism(): raise TypeError("self must be an endomorphism") if not PS.base_ring() in NumberFields() and not PS.base_ring() is QQbar: @@ -3136,9 +3171,11 @@ def multiplier_spectra(self,n,formal = True): def sigma_invariants(self,n,formal = True): r""" Computes the values of the elementary symmetric polynomials of the formal ``n`` multilpier spectra - of ``self``. Can specify to instead compute the values corresponding to the elementary symmetric - polynomials of the ``n`` multiplier spectra of ``self``, which include the multipliers of all periodic - points of period ``n`` of ``self``. `self`` must be defined over projective space of dimension 1 over + of this map. + + Can specify to instead compute the values corresponding to the elementary symmetric + polynomials of the ``n`` multiplier spectra, which includes the multipliers of all periodic + points of period ``n``. The map must be defined over projective space of dimension 1 over a number field. INPUT: @@ -3146,20 +3183,20 @@ def sigma_invariants(self,n,formal = True): - ``n`` - a positive integer, the period. - ``formal`` - a Boolean. True specifies to find the values of the elementary symmetric polynomials - corresponding to the formal ``n`` multiplier spectra of ``self``. False specifies to instead find - the values corresponding to the ``n`` multiplier spectra of ``self``, which includes the multipliers - of all periodic points of period ``n`` of ``self``. Default: True + corresponding to the formal ``n`` multiplier spectra of this map. False specifies to instead find + the values corresponding to the ``n`` multiplier spectra of this map, which includes the multipliers + of all periodic points of period ``n`` of this map. Default: True OUTPUT: - - a list of QQbar elements + - a list of `\QQbar` elements. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) sage: f = H([512*x^5 - 378128*x^4*y + 76594292*x^3*y^2 - 4570550136*x^2*y^3 - 2630045017*x*y^4\ - + 28193217129*y^5,512*y^5]) + + 28193217129*y^5, 512*y^5]) sage: f.sigma_invariants(1) [19575526074450617/1048576, -9078122048145044298567432325/2147483648, -2622661114909099878224381377917540931367/1099511627776, @@ -3173,8 +3210,8 @@ def sigma_invariants(self,n,formal = True): sage: K = NumberField(z^4 - 4*z^2 + 1,'z') sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([x^2 - 5/4*y^2,y^2]) - sage: f.sigma_invariants(2,False) + sage: f = H([x^2 - 5/4*y^2, y^2]) + sage: f.sigma_invariants(2, False) [13.00000000000000?, 11.00000000000000?, -25.00000000000000?, 0] """ polys = [] @@ -3194,12 +3231,13 @@ class SchemeMorphism_polynomial_projective_space_field(SchemeMorphism_polynomial def lift_to_rational_periodic(self, points_modp, B=None): r""" Given a list of points in projective space over `GF(p)`, determine if they lift to - `\QQ`-rational periodic points. ``self`` must be an endomorphism of projective - space defined over `\QQ` + `\QQ`-rational periodic points. + + The map must be an endomorphism of projective space defined over `\QQ` ALGORITHM: Use Hensel lifting to find a `p`-adic approximation for that rational point. The accuracy needed - is determined by the height bound `B`. Then apply the the LLL algorithm to determine if the lift + is determined by the height bound ``B``. Then apply the LLL algorithm to determine if the lift corresponds to a rational point. If the point is a point of high multiplicity (multiplier 1) then procedure can be very slow. @@ -3220,8 +3258,8 @@ def lift_to_rational_periodic(self, points_modp, B=None): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 - y^2,y^2]) - sage: f.lift_to_rational_periodic([[P(0,1).change_ring(GF(7)),4]]) + sage: f = H([x^2 - y^2, y^2]) + sage: f.lift_to_rational_periodic([[P(0,1).change_ring(GF(7)), 4]]) [[(0 : 1), 2]] :: @@ -3229,21 +3267,21 @@ def lift_to_rational_periodic(self, points_modp, B=None): There may be multiple points in the lift. sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([-5*x^2 + 4*y^2,4*x*y]) - sage: f.lift_to_rational_periodic([[P(1,0).change_ring(GF(3)),1]]) # long time + sage: f = H([-5*x^2 + 4*y^2, 4*x*y]) + sage: f.lift_to_rational_periodic([[P(1,0).change_ring(GF(3)), 1]]) # long time [[(1 : 0), 1], [(2/3 : 1), 1], [(-2/3 : 1), 1]] :: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([16*x^2 - 29*y^2,16*y^2]) + sage: f = H([16*x^2 - 29*y^2, 16*y^2]) sage: f.lift_to_rational_periodic([[P(3,1).change_ring(GF(13)), 3]]) [[(-1/4 : 1), 3]] :: - sage: P. = ProjectiveSpace(QQ,2) + sage: P. = ProjectiveSpace(QQ, 2) sage: H = End(P) sage: f = H([76*x^2 - 180*x*y + 45*y^2 + 14*x*z + 45*y*z - 90*z^2, 67*x^2 - 180*x*y - 157*x*z + 90*y*z, -90*z^2]) sage: f.lift_to_rational_periodic([[P(14,19,1).change_ring(GF(23)), 9]]) # long time @@ -3257,7 +3295,7 @@ def lift_to_rational_periodic(self, points_modp, B=None): p = points_modp[0][0].codomain().base_ring().characteristic() if p == 0: - raise TypeError("Must be positive characteristic") + raise TypeError("must be positive characteristic") PS = self.domain() N = PS.dimension_relative() R = RealField() @@ -3392,12 +3430,13 @@ def lift_to_rational_periodic(self, points_modp, B=None): def rational_periodic_points(self, **kwds): r""" - Determine the set of rational periodic points for self an endomorphism of projective space. + Determine the set of rational periodic points for an endomorphism of projective space. + Must be defined over `\QQ`. The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having - trouble getting a partiuclar map to finish, try first computing the possible periods, then - try various different ``lifting_prime``. + trouble getting a particular map to finish, try first computing the possible periods, then + try various different ``lifting_prime`` values. ALGORITHM: Modulo each prime of good reduction `p` determine the set of periodic points modulo `p`. @@ -3407,7 +3446,7 @@ def rational_periodic_points(self, **kwds): of these possible periods and try to lift it to a rational point with a combination of `p`-adic approximation and the LLL basis reducion algorithm. - See B. Hutz, Determination of all rational preperiodic points for morphisms of Pn, submitted, 2012. + See [Hutz]_. INPUT: @@ -3432,15 +3471,15 @@ def rational_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2-3/4*y^2,y^2]) - sage: sorted(f.rational_periodic_points(prime_bound=20,lifting_prime=7)) # long time + sage: f = H([x^2-3/4*y^2, y^2]) + sage: sorted(f.rational_periodic_points(prime_bound=20, lifting_prime=7)) # long time [(-1/2 : 1), (1 : 0), (3/2 : 1)] :: sage: P. = ProjectiveSpace(QQ,2) sage: H = End(P) - sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3]) + sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3, 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) sage: sorted(f.rational_periodic_points(prime_bound=[1,20])) # long time [(-3 : -1 : 1), (-3 : 0 : 1), (-3 : 1 : 1), (-3 : 3 : 1), (-1 : -1 : 1), (-1 : 0 : 1), (-1 : 1 : 1), (-1 : 3 : 1), (0 : 1 : 0), (1 : -1 : 1), (1 @@ -3452,22 +3491,14 @@ def rational_periodic_points(self, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([-5*x^2 + 4*y^2,4*x*y]) + sage: f = H([-5*x^2 + 4*y^2, 4*x*y]) sage: sorted(f.rational_periodic_points()) # long time [(-2 : 1), (-2/3 : 1), (2/3 : 1), (1 : 0), (2 : 1)] - - - .. TODO:: - - - move some of this to Cython so that it is faster especially the possible periods mod `p`. - - - have the last prime of good redution used also return the list of points instead of getting the - information again for all_points. """ if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") if self.domain().base_ring() != QQ: - raise NotImplementedError("Must be QQ") #for p-adic lifting + raise NotImplementedError("must be QQ") #for p-adic lifting primebound = kwds.pop("prime_bound", [1, 20]) p = kwds.pop("lifting_prime", 23) @@ -3478,13 +3509,13 @@ def rational_periodic_points(self, **kwds): try: primebound = [1, ZZ(primebound)] except TypeError: - raise TypeError("Bound on primes must be an integer") + raise TypeError("bound on primes must be an integer") else: try: primebound[0] = ZZ(primebound[0]) primebound[1] = ZZ(primebound[1]) except TypeError: - raise TypeError("Prime bounds must be integers") + raise TypeError("prime bounds must be integers") if badprimes is None: badprimes = self.primes_of_bad_reduction() @@ -3513,11 +3544,13 @@ def rational_periodic_points(self, **kwds): def rational_preimages(self, Q): r""" - Given a rational point `Q` in the domain of ``self``, return all the rational points `P` - in the domain of ``self`` with `self(P)==Q`. In other words, the set of first pre-images of `Q`. - ``self`` must be defined over number fields and be an endomorphism. + Determine all of the rational first preimages of ``Q`` by this map. + + Given a rational point `Q` in the domain of this map, return all the rational points `P` + in the domain with `self(P)==Q`. In other words, the set of first preimages of `Q`. + The map must be defined over number fields and be an endomorphism. - In ``Q`` is a subscheme, the return the subscheme that maps to ``Q`` by ``self``. + In ``Q`` is a subscheme, the return the subscheme that maps to ``Q`` by this map. In particular, `f^{-1}(V(h_1,\ldots,h_t)) = V(h_1 \circ f, \ldots, h_t \circ f)`. ALGORITHM: @@ -3525,17 +3558,17 @@ def rational_preimages(self, Q): INPUT: - - ``Q`` - a rational point or subscheme in the domain of ``self``. + - ``Q`` - a rational point or subscheme in the domain of this map. OUTPUT: - - a list of rational points or a subscheme in the domain of ``self``. + - a list of rational points or a subscheme in the domain of this map. Examples:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([16*x^2 - 29*y^2,16*y^2]) + sage: f = H([16*x^2 - 29*y^2, 16*y^2]) sage: f.rational_preimages(P(-1,4)) [(5/4 : 1), (-5/4 : 1)] @@ -3551,7 +3584,7 @@ def rational_preimages(self, Q): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 + y^2,2*x*y]) + sage: f = H([x^2 + y^2, 2*x*y]) sage: f.rational_preimages(P(17,15)) [(5/3 : 1), (3/5 : 1)] @@ -3567,11 +3600,11 @@ def rational_preimages(self, Q): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 + y^2,2*x*y]) + sage: f = H([x^2 + y^2, 2*x*y]) sage: f.rational_preimages([CC.0,1]) Traceback (most recent call last): ... - TypeError: Point must be in codomain of self + TypeError: point must be in codomain of self A number field example :: @@ -3579,7 +3612,7 @@ def rational_preimages(self, Q): sage: K. = NumberField(z^2 - 2); sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([x^2 + y^2,y^2]) + sage: f = H([x^2 + y^2, y^2]) sage: f.rational_preimages(P(3,1)) [(a : 1), (-a : 1)] @@ -3603,7 +3636,7 @@ def rational_preimages(self, Q): sage: f.rational_preimages(X([0,1,0])) Traceback (most recent call last): ... - NotImplementedError: Subschemes as Preimages not implemented + NotImplementedError: subschemes as preimages not implemented :: @@ -3623,11 +3656,11 @@ def rational_preimages(self, Q): #else assume a point BR = self.base_ring() if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") if (Q in self.codomain()) == False: - raise TypeError("Point must be in codomain of self") + raise TypeError("point must be in codomain of self") if isinstance(BR.base_ring(),(ComplexField_class, RealField_class,RealIntervalField_class, ComplexIntervalField_class)): - raise NotImplementedError("Not Implemented over precision fields") + raise NotImplementedError("not implemented over precision fields") Dom = self.domain() PS = self.domain().ambient_space() R = PS.coordinate_ring() @@ -3642,7 +3675,7 @@ def rational_preimages(self, Q): I.append(Q[i] * self[j] - Q[j] * self[i]) I = I * R if I.dimension() > 1: - raise NotImplementedError("Subschemes as Preimages not implemented") + raise NotImplementedError("subschemes as preimages not implemented") I0 = R.ideal(0) #Determine the points through elimination #This is much faster than using the I.variety() function on each affine chart. @@ -3677,7 +3710,7 @@ def rational_preimages(self, Q): #each dictionary entry P.update({R.gen(varindex):-BR(pol.coefficient({r:0})) / BR(pol.coefficient({r:1}))}) new_points.append(copy(P)) - if good == 1: + if good: points = new_points #the dictionary entries now have values for all coordinates #they are the rational solutions to the equations @@ -3692,25 +3725,27 @@ def rational_preimages(self, Q): def all_rational_preimages(self, points): r""" - Given a set of rational points in the domain of ``self``, return all the rational - pre-images of those points. In others words, all the rational points which have some + Given a set of rational points in the domain of this map, return all the rational + preimages of those points. + + In others words, all the rational points which have some iterate in the set points. This function repeatedly calls ``rational_preimages``. If the degree is at least two, by Northocott, this is always a finite set. - ``self`` must be defined over number fields and be an endomorphism. + The map must be defined over number fields and be an endomorphism. INPUT: - - ``points`` - a list of rational points in the domain of ``self`` + - ``points`` - a list of rational points in the domain of this map. OUTPUT: - - a list of rational points in the domain of ``self``. + - a list of rational points in the domain of this map. Examples:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([16*x^2 - 29*y^2,16*y^2]) + sage: f = H([16*x^2 - 29*y^2, 16*y^2]) sage: sorted(f.all_rational_preimages([P(-1,4)])) [(-7/4 : 1), (-5/4 : 1), (-3/4 : 1), (-1/4 : 1), (1/4 : 1), (3/4 : 1), (5/4 : 1), (7/4 : 1)] @@ -3728,7 +3763,7 @@ def all_rational_preimages(self, points): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2 + y^2,2*x*y]) + sage: f = H([x^2 + y^2, 2*x*y]) sage: sorted(f.all_rational_preimages([P(17,15)])) [(1/3 : 1), (3/5 : 1), (5/3 : 1), (3 : 1)] @@ -3738,7 +3773,7 @@ def all_rational_preimages(self, points): sage: K. = NumberField(z^3 + (z^2)/4 - (41/16)*z + 23/64); sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([16*x^2 - 29*y^2,16*y^2]) + sage: f = H([16*x^2 - 29*y^2, 16*y^2]) sage: f.all_rational_preimages([P(16*w^2 - 29,16)]) [(-w^2 + 21/16 : 1), (-w^2 - w + 33/16 : 1), (w + 1/2 : 1), (-w^2 - w + 25/16 : 1), (w^2 - 29/16 : 1), (w^2 - 21/16 : 1), (w^2 + w - 25/16 : 1), @@ -3746,9 +3781,9 @@ def all_rational_preimages(self, points): : 1)] """ if not self.is_endomorphism(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") if self.domain().base_ring() not in NumberFields(): - raise TypeError("Field won't return finite list of elements") + raise TypeError("field won't return finite list of elements") PS = self.domain() RPS = PS.base_ring() @@ -3764,10 +3799,11 @@ def all_rational_preimages(self, points): def rational_preperiodic_points(self, **kwds): r""" - Determine the set of rational preperiodic points for ``self``. - ``self`` must be defined over `\QQ` and be an endomorphism of projective space. - If ``self`` is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally - ramified fixed point, then the base ring can also be an absolute number field. + Determine the set of rational preperiodic points for this map. + + The map must be defined over `\QQ` and be an endomorphism of projective space. + If the map is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally + ramified fixed point, then the base ring can be an absolute number field. This is done by passing to the Weil restriction. The default parameter values are typically good choices for `\mathbb{P}^1`. If you are having @@ -3806,7 +3842,7 @@ def rational_preperiodic_points(self, **kwds): sage: PS. = ProjectiveSpace(1,QQ) sage: H = End(PS) - sage: f = H([x^2 -y^2,3*x*y]) + sage: f = H([x^2 -y^2, 3*x*y]) sage: sorted(f.rational_preperiodic_points()) [(-2 : 1), (-1 : 1), (-1/2 : 1), (0 : 1), (1/2 : 1), (1 : 0), (1 : 1), (2 : 1)] @@ -3823,8 +3859,8 @@ def rational_preperiodic_points(self, **kwds): sage: PS. = ProjectiveSpace(2,QQ) sage: H = End(PS) - sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2]) - sage: sorted(f.rational_preperiodic_points(prime_bound=[1,8],lifting_prime=7,periods=[2])) # long time + sage: f = H([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) + sage: sorted(f.rational_preperiodic_points(prime_bound=[1,8], lifting_prime=7, periods=[2])) # long time [(-5/4 : -2 : 1), (-5/4 : -1 : 1), (-5/4 : 0 : 1), (-5/4 : 1 : 1), (-5/4 : 2 : 1), (-1/4 : -2 : 1), (-1/4 : -1 : 1), (-1/4 : 0 : 1), (-1/4 : 1 : 1), (-1/4 : 2 : 1), (1/4 : -2 : 1), (1/4 : -1 : 1), (1/4 : 0 : 1), (1/4 @@ -3856,12 +3892,12 @@ def rational_preperiodic_points(self, **kwds): K = PS.base_ring() if K in _NumberFields: if not K.is_absolute(): - raise TypeError("Base field must be an absolute field") + raise TypeError("base field must be an absolute field") d = K.absolute_degree() #check that we are not over QQ if d > 1: if PS.dimension_relative() != 1: - raise NotImplementedError("Rational Preperiodic Points for Number Fields only implemented in dimension 1") + raise NotImplementedError("rational preperiodic points for number fields only implemented in dimension 1") w = K.absolute_generator() #we need to dehomogenize for the Weil restriction and will check that point at infty #separately. We also check here that we are working with a polynomial. If the map @@ -3875,7 +3911,7 @@ def rational_preperiodic_points(self, **kwds): inf = PS([0,1]) k = 0 if isinstance(g[0], FractionFieldElement): - raise NotImplementedError("Rational Preperiodic Points for Number Fields only implemented for polynomials") + raise NotImplementedError("rational preperiodic points for number fields only implemented for polynomials") #determine rational preperiodic points #infinity is a totally ramified fixed point for a polynomial preper = set([inf]) @@ -3914,14 +3950,15 @@ def rational_preperiodic_points(self, **kwds): preper = list(preper) return(preper) else: - raise TypeError("Base field must be an absolute number field") + raise TypeError("base field must be an absolute number field") def rational_preperiodic_graph(self, **kwds): r""" - Determine the directed graph of the rational preperiodic points for ``self``. - self must be defined over `\QQ` and be an endomorphism of projective space. - If ``self`` is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally - ramified fixed point, then the base ring can also be an absolute number field. + Determine the directed graph of the rational preperiodic points for this map. + + The map must be defined over `\QQ` and be an endomorphism of projective space. + If this map is a polynomial endomorphism of `\mathbb{P}^1`, i.e. has a totally + ramified fixed point, then the base ring can be an absolute number field. This is done by passing to the Weil restriction. ALGORITHM: @@ -3956,7 +3993,7 @@ def rational_preperiodic_graph(self, **kwds): sage: PS. = ProjectiveSpace(1,QQ) sage: H = End(PS) - sage: f = H([7*x^2 - 28*y^2,24*x*y]) + sage: f = H([7*x^2 - 28*y^2, 24*x*y]) sage: f.rational_preperiodic_graph() Looped digraph on 12 vertices @@ -3964,7 +4001,7 @@ def rational_preperiodic_graph(self, **kwds): sage: PS. = ProjectiveSpace(1,QQ) sage: H = End(PS) - sage: f = H([-3/2*x^3 +19/6*x*y^2,y^3]) + sage: f = H([-3/2*x^3 +19/6*x*y^2, y^3]) sage: f.rational_preperiodic_graph(prime_bound=[1,8]) Looped digraph on 12 vertices @@ -3972,8 +4009,8 @@ def rational_preperiodic_graph(self, **kwds): sage: PS. = ProjectiveSpace(2,QQ) sage: H = End(PS) - sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3,5*y^3 - 53*y*z^2 + 24*z^3,24*z^3]) - sage: f.rational_preperiodic_graph(prime_bound=[1,11],lifting_prime=13) # long time + sage: f = H([2*x^3 - 50*x*z^2 + 24*z^3, 5*y^3 - 53*y*z^2 + 24*z^3, 24*z^3]) + sage: f.rational_preperiodic_graph(prime_bound=[1,11], lifting_prime=13) # long time Looped digraph on 30 vertices :: @@ -3981,7 +4018,7 @@ def rational_preperiodic_graph(self, **kwds): sage: K. = QuadraticField(-3) sage: P. = ProjectiveSpace(K,1) sage: H = End(P) - sage: f = H([x^2+y^2,y^2]) + sage: f = H([x^2+y^2, y^2]) sage: f.rational_preperiodic_graph() # long time Looped digraph on 5 vertices """ @@ -3990,28 +4027,30 @@ def rational_preperiodic_graph(self, **kwds): g = self._preperiodic_points_to_cyclegraph(preper) return(g) - def connected_rational_component(self,P,n=0): + def connected_rational_component(self, P, n=0): r""" - Computes the connected component of a rational preperiodic point `P` of ``self``. Will work for - non-preperiodic points if `n` is positive. Otherwise this will not terminate. + Computes the connected component of a rational preperiodic point ``P`` by this map. + + Will work for non-preperiodic points if ``n`` is positive. + Otherwise this will not terminate. INPUT: - - ``P`` - A rational preperiodic point of ``self`` + - ``P`` - A rational preperiodic point of this map. - - ``n`` - Maximum distance from `P` to branch out. A value of 0 indicates no bound. Default: 0 + - ``n`` - Maximum distance from ``P`` to branch out. A value of 0 indicates no bound. Default: 0 OUTPUT: - - a list of points connected to `P` up to the specified distance + - a list of points connected to ``P`` up to the specified distance. Examples:: - sage: R.=PolynomialRing(QQ) - sage: K.= NumberField(x^3+1/4*x^2-41/16*x+23/64) + sage: R. = PolynomialRing(QQ) + sage: K. = NumberField(x^3+1/4*x^2-41/16*x+23/64) sage: PS. = ProjectiveSpace(1,K) sage: H = End(PS) - sage: f = H([x^2 - 29/16*y^2,y^2]) + sage: f = H([x^2 - 29/16*y^2, y^2]) sage: P = PS([w,1]) sage: f.connected_rational_component(P) [(w : 1), (w^2 - 29/16 : 1), (-w^2 - w + 25/16 : 1), (w^2 + w - 25/16 : 1), @@ -4022,7 +4061,7 @@ def connected_rational_component(self,P,n=0): sage: PS. = ProjectiveSpace(2,QQ) sage: H = End(PS) - sage: f = H([x^2 - 21/16*z^2,y^2-2*z^2,z^2]) + sage: f = H([x^2 - 21/16*z^2, y^2-2*z^2, z^2]) sage: P = PS([17/16,7/4,1]) sage: f.connected_rational_component(P,3) [(17/16 : 7/4 : 1), (-47/256 : 17/16 : 1), (-83807/65536 : -223/256 : 1), (17/16 : -7/4 : 1), @@ -4062,8 +4101,10 @@ def connected_rational_component(self,P,n=0): def _number_field_from_algebraics(self): r""" - Given a projective map defined over ``QQbar``, return the same map, but defined - over a number field. This is only implemented for maps of projective space. + Given a projective map defined over `\QQbar`, return the same map, but defined + over a number field. + + This is only implemented for maps of projective space. OUTPUT: scheme morphism @@ -4072,7 +4113,7 @@ def _number_field_from_algebraics(self): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(QQbar,1) sage: H = End(P) - sage: f = H([QQbar(3^(1/3))*x^2+QQbar(sqrt(-2))*y^2,y^2]) + sage: f = H([QQbar(3^(1/3))*x^2 + QQbar(sqrt(-2))*y^2, y^2]) sage: f._number_field_from_algebraics() Scheme endomorphism of Projective Space of dimension 1 over Number Field in a with defining polynomial y^6 + 6*y^4 + 6*y^3 + 12*y^2 - 36*y + 17 @@ -4088,7 +4129,7 @@ def _number_field_from_algebraics(self): sage: P. = ProjectiveSpace(QQbar,1) sage: P2. = ProjectiveSpace(QQbar,2) sage: H = Hom(P, P2) - sage: f = H([x^2 + QQbar(I)*x*y + 3*y^2,y^2,QQbar(sqrt(5))*x*y]) + sage: f = H([x^2 + QQbar(I)*x*y + 3*y^2, y^2, QQbar(sqrt(5))*x*y]) sage: f._number_field_from_algebraics() Scheme morphism: From: Projective Space of dimension 1 over Number Field in a with defining polynomial y^4 + 3*y^2 + 1 @@ -4098,7 +4139,7 @@ def _number_field_from_algebraics(self): """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if not (is_ProjectiveSpace(self.domain()) and is_ProjectiveSpace(self.domain())): - raise NotImplementedError("Not implemented for subschemes") + raise NotImplementedError("not implemented for subschemes") K,C,phi = number_field_elements_from_algebraics([c for f in self for c in f.coefficients()]) from sage.schemes.projective.projective_space import ProjectiveSpace @@ -4111,8 +4152,8 @@ def _number_field_from_algebraics(self): H = Hom(PS,PS2) R = PS.coordinate_ring() exps = [f.exponents() for f in self] - F=[] - j=0 + F = [] + j = 0 for t in exps: G = 0 for e in t: @@ -4129,9 +4170,9 @@ def _fast_eval(self, x): EXAMPLES:: - sage: P.=ProjectiveSpace(GF(7),2) - sage: H=Hom(P,P) - sage: f=H([x^2+y^2,y^2,z^2 + y*z]) + sage: P. = ProjectiveSpace(GF(7),2) + sage: H = Hom(P,P) + sage: f = H([x^2+y^2, y^2, z^2 + y*z]) sage: f._fast_eval([1,1,1]) [2, 1, 2] """ @@ -4144,22 +4185,25 @@ def _fast_eval(self, x): def orbit_structure(self, P): r""" - Every point is preperiodic over a finite field. This function returns the pair `[m,n]` where `m` is the - preperiod and `n` is the period of the point ``P`` by ``self``. + Return the pair `[m,n]` where `m` is the preperiod and `n` + is the period of the point ``P`` by this map. + + Every point is preperiodic over a finite field so every point + will be preperiodic. INPUT: - - ``P`` -- a point in ``self.domain()`` + - ``P`` -- a point in the domain of this map. OUTPUT: - - a list `[m,n]` of integers + - a list `[m,n]` of integers. EXAMPLES:: sage: P. = ProjectiveSpace(GF(5),2) sage: H = Hom(P,P) - sage: f = H([x^2 + y^2,y^2,z^2 + y*z]) + sage: f = H([x^2 + y^2,y^2, z^2 + y*z]) sage: f.orbit_structure(P(2,1,2)) [0, 6] @@ -4168,7 +4212,7 @@ def orbit_structure(self, P): sage: P. = ProjectiveSpace(GF(7),2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) + sage: f = H([x^2, y^2, z^2]) sage: f.orbit_structure(X(1,1,2)) [0, 2] @@ -4176,7 +4220,7 @@ def orbit_structure(self, P): sage: P. = ProjectiveSpace(GF(13),1) sage: H = Hom(P,P) - sage: f = H([x^2 - y^2,y^2]) + sage: f = H([x^2 - y^2, y^2]) sage: f.orbit_structure(P(3,4)) [2, 3] """ @@ -4184,10 +4228,10 @@ def orbit_structure(self, P): def cyclegraph(self): r""" - returns Digraph of all orbits of ``self`` mod `p`. + Return the digraph of all orbits of this map. - For subschemes, only points on the subscheme whose image are - also on the subscheme are in the digraph. + Over a finite field this is a finite graph. For subscheme domains, only points + on the subscheme whose image are also on the subscheme are in the digraph. OUTPUT: @@ -4197,7 +4241,7 @@ def cyclegraph(self): sage: P. = ProjectiveSpace(GF(13),1) sage: H = Hom(P,P) - sage: f = H([x^2-y^2,y^2]) + sage: f = H([x^2-y^2, y^2]) sage: f.cyclegraph() Looped digraph on 14 vertices @@ -4205,7 +4249,7 @@ def cyclegraph(self): sage: P. = ProjectiveSpace(GF(5^2,'t'),2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2,z^2+y*z]) + sage: f = H([x^2+y^2, y^2, z^2+y*z]) sage: f.cyclegraph() Looped digraph on 651 vertices @@ -4214,12 +4258,12 @@ def cyclegraph(self): sage: P. = ProjectiveSpace(GF(7),2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) + sage: f = H([x^2, y^2, z^2]) sage: f.cyclegraph() Looped digraph on 15 vertices """ if self.domain() != self.codomain(): - raise NotImplementedError("Domain and Codomain must be equal") + raise NotImplementedError("domain and codomain must be equal") V = [] E = [] from sage.schemes.projective.projective_space import is_ProjectiveSpace @@ -4249,10 +4293,14 @@ def possible_periods(self, return_points=False): Returns the list of possible minimal periods of a periodic point over `\QQ` and (optionally) a point in each cycle. + REFERENCES: + + .. [Hutz-gr] B. Hutz. Good reduction of periodic points, Illinois Journal of + Mathematics 53 (Winter 2009), no. 4, 1109-1126. + ALGORITHM: - The list comes from: Hutz, Good reduction of periodic points, Illinois Journal of - Mathematics 53 (Winter 2009), no. 4, 1109-1126. + See [Hutz-gr]_. INPUT: @@ -4266,7 +4314,7 @@ def possible_periods(self, return_points=False): sage: P. = ProjectiveSpace(GF(23),1) sage: H = End(P) - sage: f = H([x^2-2*y^2,y^2]) + sage: f = H([x^2-2*y^2, y^2]) sage: f.possible_periods() [1, 5, 11, 22, 110] @@ -4274,7 +4322,7 @@ def possible_periods(self, return_points=False): sage: P. = ProjectiveSpace(GF(13),1) sage: H = End(P) - sage: f = H([x^2-y^2,y^2]) + sage: f = H([x^2-y^2, y^2]) sage: sorted(f.possible_periods(True)) [[(0 : 1), 2], [(1 : 0), 1], [(3 : 1), 3], [(3 : 1), 36]] @@ -4291,14 +4339,15 @@ def possible_periods(self, return_points=False): - do not return duplicate points - improve hash to reduce memory of pointtable - """ return _fast_possible_periods(self,return_points) def automorphism_group(self, **kwds): r""" - Given a homogenous rational function, this calculates the subsgroup of `PGL2` that is - the automorphism group of ``self``, see [FMV] fir algorithm. + Return the subgroup of `PGL2` that is the automorphism group of this map. + + Only for dimension 1. The automorphism group is the set of `PGL2` elements that + fixed the map under conjugation. See [FMV]_ for the algorithm. INPUT: @@ -4325,7 +4374,7 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(GF(7^3,'t'),1) sage: H = End(R) - sage: f = H([x^2-y^2,x*y]) + sage: f = H([x^2-y^2, x*y]) sage: f.automorphism_group() [ [1 0] [6 0] @@ -4337,7 +4386,7 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(GF(3^2,'t'),1) sage: H = End(R) sage: f = H([x^3,y^3]) - sage: f.automorphism_group(return_functions=True,iso_type=True) # long time + sage: f.automorphism_group(return_functions=True, iso_type=True) # long time ([x, x/(x + 1), x/(2*x + 1), 2/(x + 2), (2*x + 1)/(2*x), (2*x + 2)/x, 1/(2*x + 2), x + 1, x + 2, x/(x + 2), 2*x/(x + 1), 2*x, 1/x, 2*x + 1, 2*x + 2, ((t + 2)*x + t + 2)/((2*t + 1)*x + t + 2), (t*x + 2*t)/(t*x + @@ -4350,14 +4399,14 @@ def automorphism_group(self, **kwds): sage: R. = ProjectiveSpace(GF(2^5,'t'),1) sage: H = End(R) sage: f=H([x^5,y^5]) - sage: f.automorphism_group(return_functions=True,iso_type=True) + sage: f.automorphism_group(return_functions=True, iso_type=True) ([x, 1/x], 'Cyclic of order 2') :: sage: R. = ProjectiveSpace(GF(3^4,'t'),1) sage: H = End(R) - sage: f=H([x^2+25*x*y+y^2,x*y+3*y^2]) + sage: f=H([x^2+25*x*y+y^2, x*y+3*y^2]) sage: f.automorphism_group(absolute=True) [Univariate Polynomial Ring in w over Finite Field in b of size 3^4, [ @@ -4365,18 +4414,18 @@ def automorphism_group(self, **kwds): [0 1] ]] """ - absolute=kwds.get('absolute',False) - iso_type=kwds.get('iso_type',False) - return_functions=kwds.get('return_functions',False) + absolute = kwds.get('absolute', False) + iso_type = kwds.get('iso_type', False) + return_functions=kwds.get('return_functions', False) if self.domain().dimension_relative()!=1: - raise NotImplementedError("Must be dimension 1") + raise NotImplementedError("must be dimension 1") else: - f=self.dehomogenize(1) - z=f[0].parent().gen() + f = self.dehomogenize(1) + z = f[0].parent().gen() if f[0].denominator()!=1: - F=(f[0].numerator().polynomial(z))/f[0].denominator().polynomial(z) + F = (f[0].numerator().polynomial(z))/f[0].denominator().polynomial(z) else: - F=f[0].numerator().polynomial(z) + F = f[0].numerator().polynomial(z) from endPN_automorphism_group import automorphism_group_FF return(automorphism_group_FF(F, absolute, iso_type, return_functions)) diff --git a/src/sage/schemes/projective/projective_morphism_helper.pyx b/src/sage/schemes/projective/projective_morphism_helper.pyx index e0add19c5ce..cd37d57ee32 100644 --- a/src/sage/schemes/projective/projective_morphism_helper.pyx +++ b/src/sage/schemes/projective/projective_morphism_helper.pyx @@ -20,19 +20,18 @@ AUTHORS: #***************************************************************************** from sage.arith.all import lcm -from sage.rings.finite_rings.constructor import GF -from sage.sets.all import Set -from sage.misc.misc import subsets +from sage.rings.finite_rings.finite_field_constructor import GF +from sage.sets.all import Set +from sage.misc.misc import subsets -def _fast_possible_periods(self,return_points=False): +def _fast_possible_periods(self, return_points=False): r""" Returns the list of possible minimal periods of a periodic point over `\QQ` and (optionally) a point in each cycle. ALGORITHM: - The list comes from B. Hutz. Good reduction of periodic points, Illinois Journal of - Mathematics 53 (Winter 2009), no. 4, 1109-1126.. + See [Hutz-gr]_ INPUT: @@ -45,10 +44,10 @@ def _fast_possible_periods(self,return_points=False): Examples:: sage: from sage.schemes.projective.projective_morphism_helper import _fast_possible_periods - sage: P.=ProjectiveSpace(GF(23),1) - sage: H=Hom(P,P) - sage: f=H([x^2-2*y^2,y^2]) - sage: _fast_possible_periods(f,False) + sage: P. = ProjectiveSpace(GF(23),1) + sage: H = Hom(P,P) + sage: f = H([x^2-2*y^2, y^2]) + sage: _fast_possible_periods(f, False) [1, 5, 11, 22, 110] :: @@ -56,8 +55,8 @@ def _fast_possible_periods(self,return_points=False): sage: from sage.schemes.projective.projective_morphism_helper import _fast_possible_periods sage: P. = ProjectiveSpace(GF(13),1) sage: H = End(P) - sage: f = H([x^2-y^2,y^2]) - sage: sorted(_fast_possible_periods(f,True)) + sage: f = H([x^2-y^2, y^2]) + sage: sorted(_fast_possible_periods(f, True)) [[(0 : 1), 2], [(1 : 0), 1], [(3 : 1), 3], [(3 : 1), 36]] :: @@ -66,7 +65,7 @@ def _fast_possible_periods(self,return_points=False): sage: PS. = ProjectiveSpace(2,GF(7)) sage: H = End(PS) sage: f = H([-360*x^3 + 760*x*z^2, y^3 - 604*y*z^2 + 240*z^3, 240*z^3]) - sage: _fast_possible_periods(f,False) + sage: _fast_possible_periods(f, False) [1, 2, 4, 6, 12, 14, 28, 42, 84] .. TODO:: @@ -77,10 +76,10 @@ def _fast_possible_periods(self,return_points=False): cdef list pointslist if not self._is_prime_finite_field: - raise TypeError("Must be prime field") + raise TypeError("must be prime field") from sage.schemes.projective.projective_space import is_ProjectiveSpace if is_ProjectiveSpace(self.domain()) == False or self.domain()!=self.codomain(): - raise NotImplementedError("Must be an endomorphism of projective space") + raise NotImplementedError("must be an endomorphism of projective space") PS = self.domain() p = PS.base_ring().order() @@ -102,59 +101,59 @@ def _fast_possible_periods(self,return_points=False): Q = _normalize_coordinates(Q, p, N+1) hash_q = _hash(Q, p) point_table[hash_p][0] = hash_q - P=Q - hash_p=hash_q - index+=1 + P = Q + hash_p = hash_q + index += 1 if point_table[hash_p][1] >= startindex: - P_proj=PS(P) - period=index-point_table[hash_p][1] + P_proj = PS(P) + period = index-point_table[hash_p][1] periods.add(period) - points_periods.append([P_proj,period]) - l=P_proj.multiplier(self,period,False) - lorders=set() + points_periods.append([P_proj, period]) + l = P_proj.multiplier(self, period, False) + lorders = set() for poly,_ in l.charpoly().factor(): if poly.degree() == 1: eig = -poly.constant_coefficient() if not eig: continue # exclude 0 else: - eig = GF(p ** poly.degree(), 't', modulus=poly).gen() + eig = GF(p**poly.degree(), 't', modulus=poly).gen() if eig: lorders.add(eig.multiplicative_order()) S = subsets(lorders) next(S) # get rid of the empty set - rvalues=set() + rvalues = set() for s in S: rvalues.add(lcm(s)) - rvalues=list(rvalues) - if N==1: + rvalues = list(rvalues) + if N == 1: for k in xrange(len(rvalues)): - r=rvalues[k] + r = rvalues[k] periods.add(period*r) - points_periods.append([P_proj,period*r]) + points_periods.append([P_proj, period*r]) if p == 2 or p == 3: #need e=1 for N=1, QQ periods.add(period*r*p) - points_periods.append([P_proj,period*r*p]) + points_periods.append([P_proj, period*r*p]) else: for k in xrange(len(rvalues)): - r=rvalues[k] + r = rvalues[k] periods.add(period*r) periods.add(period*r*p) - points_periods.append([P_proj,period*r]) - points_periods.append([P_proj,period*r*p]) - if p==2: #need e=3 for N>1, QQ + points_periods.append([P_proj, period*r]) + points_periods.append([P_proj, period*r*p]) + if p == 2: #need e=3 for N>1, QQ periods.add(period*r*4) - points_periods.append([P_proj,period*r*4]) + points_periods.append([P_proj, period*r*4]) periods.add(period*r*8) - points_periods.append([P_proj,period*r*8]) + points_periods.append([P_proj, period*r*8]) - if return_points==False: + if return_points == False: return sorted(periods) else: return(points_periods) -def _enum_points(int prime,int dimension): +def _enum_points(int prime, int dimension): """ Enumerate points in projective space over finite field with given prime and dimension. @@ -162,7 +161,8 @@ def _enum_points(int prime,int dimension): sage: from sage.schemes.projective.projective_morphism_helper import _enum_points sage: list(_enum_points(3,2)) - [[1, 0, 0], [0, 1, 0], [1, 1, 0], [2, 1, 0], [0, 0, 1], [1, 0, 1], [2, 0, 1], [0, 1, 1], [1, 1, 1], [2, 1, 1], [0, 2, 1], [1, 2, 1], [2, 2, 1]] + [[1, 0, 0], [0, 1, 0], [1, 1, 0], [2, 1, 0], [0, 0, 1], [1, 0, 1], + [2, 0, 1], [0, 1, 1], [1, 1, 1], [2, 1, 1], [0, 2, 1], [1, 2, 1], [2, 2, 1]] """ cdef int current_range cdef int highest_range @@ -176,7 +176,7 @@ def _enum_points(int prime,int dimension): yield _get_point_from_hash(value,prime,dimension) current_range = current_range*prime -def _hash(list Point,int prime): +def _hash(list Point, int prime): """ Hash point given as list to unique number. @@ -194,13 +194,13 @@ def _hash(list Point,int prime): hash_q = 0 for coefficient in Point: - hash_q = hash_q * prime + coefficient + hash_q = hash_q*prime + coefficient Point.reverse() return hash_q -def _get_point_from_hash(int value,int prime,int dimension): +def _get_point_from_hash(int value, int prime, int dimension): """ Hash unique number to point as a list. diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index fe47ee818b6..15e02b83abe 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -35,26 +35,26 @@ from sage.categories.integral_domains import IntegralDomains from sage.categories.number_fields import NumberFields _NumberFields = NumberFields() -from sage.rings.infinity import infinity -from sage.rings.integer_ring import ZZ +from sage.rings.infinity import infinity +from sage.rings.integer_ring import ZZ from sage.rings.fraction_field import FractionField -from sage.rings.morphism import RingHomomorphism_im_gens +from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.number_field.order import is_NumberFieldOrder from sage.rings.number_field.number_field_ideal import NumberFieldFractionalIdeal -from sage.rings.padics.all import Qp -from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics -from sage.rings.quotient_ring import QuotientRing_generic +from sage.rings.padics.all import Qp +from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics +from sage.rings.quotient_ring import QuotientRing_generic from sage.rings.rational_field import QQ -from sage.rings.real_double import RDF -from sage.rings.real_mpfr import RealField, RR, is_RealField +from sage.rings.real_double import RDF +from sage.rings.real_mpfr import RealField, RR, is_RealField from sage.arith.all import gcd, lcm, is_prime, binomial -from copy import copy +from copy import copy from sage.schemes.generic.morphism import (SchemeMorphism, is_SchemeMorphism, SchemeMorphism_point) -from sage.structure.element import AdditiveGroupElement -from sage.structure.sequence import Sequence +from sage.structure.element import AdditiveGroupElement +from sage.structure.sequence import Sequence @@ -67,9 +67,9 @@ class SchemeMorphism_point_projective_ring(SchemeMorphism_point): INPUT: - - ``X`` -- a homset of a subscheme of an ambient projective space over a field `K` + - ``X`` -- a homset of a subscheme of an ambient projective space over a field `K`. - - ``v`` -- a list or tuple of coordinates in `K` + - ``v`` -- a list or tuple of coordinates in `K`. - ``check`` -- boolean (optional, default:``True``). Whether to check the input for consistency. @@ -118,19 +118,19 @@ def __init__(self, X, v, check=True): It is possible to avoid the possibly time consuming checks, but be careful!! sage: P = ProjectiveSpace(3, QQ) - sage: P.point([0,0,0,0],check = False) + sage: P.point([0,0,0,0], check=False) (0 : 0 : 0 : 0) :: sage: P. = ProjectiveSpace(2, ZZ) sage: X = P.subscheme([x^2-y*z]) - sage: X([2,2,2]) + sage: X([2, 2, 2]) (2 : 2 : 2) :: - sage: R.=PolynomialRing(ZZ) + sage: R. = PolynomialRing(ZZ) sage: P = ProjectiveSpace(1, R.quo(t^2+1)) sage: P([2*t, 1]) (2*tbar : 1) @@ -145,7 +145,7 @@ def __init__(self, X, v, check=True): v = [0] * (d) v[1] = 1 if not isinstance(v,(list,tuple)): - raise TypeError("Argument v (= %s) must be a scheme point, list, or tuple."%str(v)) + raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) if len(v) != d and len(v) != d-1: raise TypeError("v (=%s) must have %s components"%(v, d)) @@ -174,7 +174,7 @@ def __eq__(self, right): INPUT: - - ``right`` -- a point on projective space + - ``right`` -- a point on projective space. OUTPUT: @@ -182,58 +182,58 @@ def __eq__(self, right): Examples:: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([1,2]) - sage: Q=PS([2,4]) - sage: P==Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([1, 2]) + sage: Q = PS([2, 4]) + sage: P == Q True :: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([1,2]) - sage: Q=PS([1,0]) - sage: P==Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([1, 2]) + sage: Q = PS([1, 0]) + sage: P == Q False :: - sage: PS=ProjectiveSpace(Zp(5),1,'x') - sage: P=PS([0,1]) - sage: P==0 + sage: PS = ProjectiveSpace(Zp(5), 1, 'x') + sage: P = PS([0, 1]) + sage: P == 0 True :: - sage: R.=PolynomialRing(QQ) - sage: PS=ProjectiveSpace(R,1,'x') - sage: P=PS([t,1+t^2]) - sage: Q=PS([t^2, t+t^3]) - sage: P==Q + sage: R. = PolynomialRing(QQ) + sage: PS = ProjectiveSpace(R, 1, 'x') + sage: P = PS([t, 1+t^2]) + sage: Q = PS([t^2, t+t^3]) + sage: P == Q True :: - sage: PS=ProjectiveSpace(ZZ,2,'x') - sage: P=PS([0,1,2]) - sage: P==0 + sage: PS = ProjectiveSpace(ZZ, 2, 'x') + sage: P = PS([0, 1, 2]) + sage: P == 0 False :: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([2,1]) - sage: PS2=ProjectiveSpace(Zp(7),1,'x') - sage: Q=PS2([2,1]) - sage: P==Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([2, 1]) + sage: PS2 = ProjectiveSpace(Zp(7), 1, 'x') + sage: Q = PS2([2, 1]) + sage: P == Q False :: - sage: PS=ProjectiveSpace(ZZ.quo(6),2,'x') - sage: P=PS([2,4,1]) - sage: Q=PS([0,1,3]) - sage: P==Q + sage: PS = ProjectiveSpace(ZZ.quo(6), 2, 'x') + sage: P = PS([2, 4, 1]) + sage: Q = PS([0, 1, 3]) + sage: P == Q False Check that :trac:`17433` is fixed:: @@ -246,20 +246,20 @@ def __eq__(self, right): :: - sage: R.=PolynomialRing(QQ) - sage: K.=NumberField(z^2+5) - sage: OK=K.ring_of_integers() - sage: t=OK.gen(1) - sage: PS.=ProjectiveSpace(OK,1) - sage: P=PS(2,1+t) - sage: Q=PS(1-t,3) - sage: P==Q + sage: R. = PolynomialRing(QQ) + sage: K. = NumberField(z^2+5) + sage: OK = K.ring_of_integers() + sage: t = OK.gen(1) + sage: PS. = ProjectiveSpace(OK,1) + sage: P = PS(2, 1+t) + sage: Q = PS(1-t, 3) + sage: P == Q True Check that :trac:`17429` is fixed:: sage: R. = PolynomialRing(QQ) - sage: r = (x^2-x-3).polynomial(x).roots(ComplexIntervalField(),multiplicities = False) + sage: r = (x^2-x-3).polynomial(x).roots(ComplexIntervalField(), multiplicities=False) sage: P. = ProjectiveSpace(ComplexIntervalField(), 1) sage: P1 = P(r[0], 1) sage: H = End(P) @@ -285,7 +285,7 @@ def __ne__(self,right): INPUT: - - ``right`` - a point on projective space + - ``right`` - a point on projective space. OUTPUT: @@ -293,58 +293,58 @@ def __ne__(self,right): Examples:: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([1,2]) - sage: Q=PS([2,4]) - sage: P!=Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([1, 2]) + sage: Q = PS([2, 4]) + sage: P != Q False :: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([1,2]) - sage: Q=PS([1,0]) - sage: P!=Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([1, 2]) + sage: Q = PS([1, 0]) + sage: P != Q True :: - sage: PS=ProjectiveSpace(Zp(5),1,'x') - sage: P=PS([0,1]) - sage: P!=0 + sage: PS = ProjectiveSpace(Zp(5), 1, 'x') + sage: P = PS([0, 1]) + sage: P != 0 False :: - sage: R.=PolynomialRing(QQ) - sage: PS=ProjectiveSpace(R,1,'x') - sage: P=PS([t,1+t^2]) - sage: Q=PS([t^2, t+t^3]) - sage: P!=Q + sage: R. = PolynomialRing(QQ) + sage: PS = ProjectiveSpace(R, 1, 'x') + sage: P = PS([t, 1+t^2]) + sage: Q = PS([t^2, t+t^3]) + sage: P != Q False :: - sage: PS=ProjectiveSpace(ZZ,2,'x') - sage: P=PS([0,1,2]) - sage: P!=0 + sage: PS = ProjectiveSpace(ZZ, 2, 'x') + sage: P = PS([0, 1, 2]) + sage: P != 0 True :: - sage: PS=ProjectiveSpace(ZZ,1,'x') - sage: P=PS([2,1]) - sage: PS2=ProjectiveSpace(Zp(7),1,'x') - sage: Q=PS2([2,1]) - sage: P!=Q + sage: PS = ProjectiveSpace(ZZ, 1, 'x') + sage: P = PS([2, 1]) + sage: PS2 = ProjectiveSpace(Zp(7), 1, 'x') + sage: Q = PS2([2, 1]) + sage: P != Q True :: - sage: PS=ProjectiveSpace(ZZ.quo(6),2,'x') - sage: P=PS([2,4,1]) - sage: Q=PS([0,1,3]) - sage: P!=Q + sage: PS = ProjectiveSpace(ZZ.quo(6), 2, 'x') + sage: P = PS([2, 4, 1]) + sage: Q = PS([0, 1, 3]) + sage: P != Q True """ if not isinstance(right, SchemeMorphism_point): @@ -352,9 +352,9 @@ def __ne__(self,right): right = self.codomain()(right) except TypeError: return True - if self.codomain()!=right.codomain(): + if self.codomain() != right.codomain(): return True - n=len(self._coords) + n = len(self._coords) for i in range(0,n): for j in range(i+1,n): if self._coords[i]*right._coords[j] != self._coords[j]*right._coords[i]: @@ -363,20 +363,22 @@ def __ne__(self,right): def __hash__(self): """ - Computes the hash value of ``self``. If the base ring has a fraction - field, normalize the point in the fraction field and then hash so - that equal points have equal hash values. If the base ring is not - an integral domain, return the hash of the parent. + Computes the hash value of this point. + + If the base ring has a fraction field, normalize the point in + the fraction field and then hash so that equal points have + equal hash values. If the base ring is not an integral domain, + return the hash of the parent. OUTPUT: Integer. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ, 1) - sage: hash(P([1,1])) + sage: hash(P([1, 1])) 1265304440 # 32-bit 7316841028997809016 # 64-bit - sage: hash(P.point([2,2], False)) + sage: hash(P.point([2, 2], False)) 1265304440 # 32-bit 7316841028997809016 # 64-bit @@ -396,7 +398,7 @@ def __hash__(self): :: sage: P. = ProjectiveSpace(Zmod(10), 1) - sage: hash(P([2,5])) + sage: hash(P([2, 5])) -479010389 # 32-bit 4677413289753502123 # 64-bit """ @@ -413,12 +415,14 @@ def __hash__(self): def scale_by(self,t): """ - Scale the coordinates of the point ``self`` by `t`. A ``TypeError`` occurs if - the point is not in the base_ring of the codomain after scaling. + Scale the coordinates of the point by ``t``. + + A ``TypeError`` occurs if the point is not in the + base_ring of the codomain after scaling. INPUT: - - ``t`` -- a ring element + - ``t`` -- a ring element. OUTPUT: None. @@ -435,14 +439,14 @@ def scale_by(self,t): sage: R. = PolynomialRing(QQ) sage: S = R.quo(R.ideal(t^3)) sage: P. = ProjectiveSpace(S, 2) - sage: Q = P(t,1,1) + sage: Q = P(t, 1, 1) sage: Q.scale_by(t);Q (tbar^2 : tbar : tbar) :: sage: P. = ProjectiveSpace(ZZ,2) - sage: Q = P(2,2,2) + sage: Q = P(2, 2, 2) sage: Q.scale_by(1/2);Q (1 : 1 : 1) """ @@ -451,14 +455,14 @@ def scale_by(self,t): R = self.codomain().base_ring() if isinstance(R, QuotientRing_generic): for i in range(self.codomain().ambient_space().dimension_relative()+1): - self._coords[i]=R(self._coords[i].lift()*t) + self._coords[i] = R(self._coords[i].lift()*t) else: for i in range(self.codomain().ambient_space().dimension_relative()+1): - self._coords[i]=R(self._coords[i]*t) + self._coords[i] = R(self._coords[i]*t) def normalize_coordinates(self): """ - Removes the gcd from the coordinates of ``self`` (including `-1`). + Removes the gcd from the coordinates of this point (including `-1`). .. WARNING:: The gcd will depend on the base ring. @@ -467,14 +471,14 @@ def normalize_coordinates(self): EXAMPLES:: sage: P = ProjectiveSpace(ZZ,2,'x') - sage: p = P([-5,-15,-20]) + sage: p = P([-5, -15, -20]) sage: p.normalize_coordinates(); p (1 : 3 : 4) :: sage: P = ProjectiveSpace(Zp(7),2,'x') - sage: p = P([-5,-15,-2]) + sage: p = P([-5, -15, -2]) sage: p.normalize_coordinates(); p (5 + O(7^20) : 1 + 2*7 + O(7^20) : 2 + O(7^20)) @@ -482,14 +486,14 @@ def normalize_coordinates(self): sage: R. = PolynomialRing(QQ) sage: P = ProjectiveSpace(R,2,'x') - sage: p = P([3/5*t^3,6*t, t]) + sage: p = P([3/5*t^3, 6*t, t]) sage: p.normalize_coordinates(); p (3/5*t^2 : 6 : 1) :: sage: P. = ProjectiveSpace(Zmod(20),1) - sage: Q = P(3,6) + sage: Q = P(3, 6) sage: Q.normalize_coordinates() sage: Q (1 : 2) @@ -499,7 +503,7 @@ def normalize_coordinates(self): sage: R. = PolynomialRing(QQ) sage: P = ProjectiveSpace(R,1) - sage: Q = P(2*c,4*c) + sage: Q = P(2*c, 4*c) sage: Q.normalize_coordinates();Q (2 : 4) @@ -507,7 +511,7 @@ def normalize_coordinates(self): sage: R. = PolynomialRing(ZZ) sage: P = ProjectiveSpace(R,1) - sage: Q = P(2*c,4*c) + sage: Q = P(2*c, 4*c) sage: Q.normalize_coordinates();Q (1 : 2) @@ -516,7 +520,7 @@ def normalize_coordinates(self): sage: R. = PolynomialRing(QQ,1) sage: S = R.quotient_ring(R.ideal(t^3)) sage: P. = ProjectiveSpace(S,1) - sage: Q = P(t,t^2) + sage: Q = P(t, t^2) sage: Q.normalize_coordinates() sage: Q (1 : tbar) @@ -553,96 +557,97 @@ def normalize_coordinates(self): def dehomogenize(self,n): r""" - Dehomogenizes at the nth coordinate + Dehomogenizes at the nth coordinate. INPUT: - - ``n`` -- non-negative integer + - ``n`` -- non-negative integer. OUTPUT: - - :class:`SchemeMorphism_point_affine` + - :class:`SchemeMorphism_point_affine`. EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,2) - sage: X=P.subscheme(x^2-y^2); - sage: Q=X(23,23,46) + sage: P. = ProjectiveSpace(QQ,2) + sage: X = P.subscheme(x^2-y^2); + sage: Q = X(23, 23, 46) sage: Q.dehomogenize(2) (1/2, 1/2) :: - sage: R.=PolynomialRing(QQ) - sage: S=R.quo(R.ideal(t^3)) - sage: P.=ProjectiveSpace(S,2) - sage: Q=P(t,1,1) + sage: R. = PolynomialRing(QQ) + sage: S = R.quo(R.ideal(t^3)) + sage: P. = ProjectiveSpace(S,2) + sage: Q = P(t, 1, 1) sage: Q.dehomogenize(1) (tbar, 1) :: - sage: P.=ProjectiveSpace(GF(5),2) - sage: Q=P(1,3,1) + sage: P. = ProjectiveSpace(GF(5),2) + sage: Q = P(1, 3, 1) sage: Q.dehomogenize(0) (3, 1) :: - sage: P.=ProjectiveSpace(GF(5),2) - sage: Q=P(1,3,0) + sage: P.= ProjectiveSpace(GF(5),2) + sage: Q = P(1, 3, 0) sage: Q.dehomogenize(2) Traceback (most recent call last): ... - ValueError: Can't dehomogenize at 0 coordinate. + ValueError: can't dehomogenize at 0 coordinate """ - if self[n]==0: - raise ValueError("Can't dehomogenize at 0 coordinate.") - PS=self.codomain() - A=PS.affine_patch(n) - Q=[] + if self[n] == 0: + raise ValueError("can't dehomogenize at 0 coordinate") + PS = self.codomain() + A = PS.affine_patch(n) + Q = [] for i in range(0,PS.ambient_space().dimension_relative()+1): if i !=n: Q.append(self[i]/self[n]) return(A.point(Q)) - def nth_iterate(self,f, n, **kwds): + def nth_iterate(self, f, n, **kwds): r""" - For a map ``self`` and a point `P` in ``self.domain()`` - this function returns the nth iterate of `P` by ``self``. If ``normalize==True``, - then the coordinates are automatically normalized. If ``check==True``, then - the initialization checks are performed on the new point. + Return the ``n``-th iterate of this point for the map ``f``. + + If ``normalize==True``, then the coordinates are automatically + normalized. If ``check==True``, then the initialization checks + are performed on the new point. INPUT: - - ``f`` -- a SchmemMorphism_polynomial with ``self`` in ``f.domain()`` + - ``f`` -- a SchmemMorphism_polynomial with the points in its domain. - ``n`` -- a positive integer. kwds: - - ``check`` -- boolean (optional - default: ``True``) + - ``check`` -- Boolean (optional - default: ``True``). - - ``normalize`` -- Boolean (optional Default: ``False``) + - ``normalize`` -- Boolean (optional Default: ``False``). OUTPUT: - - A point in ``self.codomain()`` + - A point in the domain of ``f``.` EXAMPLES:: sage: P. = ProjectiveSpace(ZZ, 1) sage: H = Hom(P,P) sage: f = H([x^2+y^2, 2*y^2]) - sage: P(1,1).nth_iterate(f,4) + sage: P(1, 1).nth_iterate(f, 4) (32768 : 32768) :: sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,2*y^2]) - sage: P(1,1).nth_iterate(f,4,normalize=True) + sage: f = H([x^2+y^2, 2*y^2]) + sage: P(1, 1).nth_iterate(f, 4, normalize=True) (1 : 1) :: @@ -650,8 +655,8 @@ def nth_iterate(self,f, n, **kwds): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(R,2) sage: H = Hom(P,P) - sage: f = H([x^2+t*y^2,(2-t)*y^2,z^2]) - sage: P(2+t,7,t).nth_iterate(f,2) + sage: f = H([x^2+t*y^2, (2-t)*y^2, z^2]) + sage: P(2+t, 7, t).nth_iterate(f, 2) (t^4 + 2507*t^3 - 6787*t^2 + 10028*t + 16 : -2401*t^3 + 14406*t^2 - 28812*t + 19208 : t^4) @@ -660,16 +665,16 @@ def nth_iterate(self,f, n, **kwds): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) - sage: X(2,2,3).nth_iterate(f,3) + sage: f = H([x^2,y^2, z^2]) + sage: X(2, 2, 3).nth_iterate(f,3) (256 : 256 : 6561) :: sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) - sage: f = H([x^2+3*y^2,2*y^2,z^2]) - sage: P(2,7,1).nth_iterate(f,-2) + sage: f = H([x^2+3*y^2, 2*y^2,z^2]) + sage: P(2, 7, 1).nth_iterate(f, -2) Traceback (most recent call last): ... TypeError: must be a forward orbit @@ -679,8 +684,8 @@ def nth_iterate(self,f, n, **kwds): sage: P. = ProjectiveSpace(QQ,2) sage: P2. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P2) - sage: f = H([x^2+3*y^2,2*y^2,z^2]) - sage: P(2,7,1).nth_iterate(f,2) + sage: f = H([x^2+3*y^2, 2*y^2, z^2]) + sage: P(2, 7, 1).nth_iterate(f, 2) Traceback (most recent call last): ... TypeError: map must be an endomorphism for iteration @@ -690,9 +695,9 @@ def nth_iterate(self,f, n, **kwds): sage: P. = ProjectiveSpace(QQ, 1) sage: H = End(P) sage: f = H([x^3, x*y^2]) - sage: P(0,1).nth_iterate(f,3, check = False) + sage: P(0, 1).nth_iterate(f, 3, check=False) (0 : 0) - sage: P(0,1).nth_iterate(f,3) + sage: P(0, 1).nth_iterate(f, 3) Traceback (most recent call last): ... ValueError: [0, 0] does not define a valid point since all entries are 0 @@ -702,9 +707,9 @@ def nth_iterate(self,f, n, **kwds): sage: P. = ProjectiveSpace(ZZ, 1) sage: H = End(P) sage: f = H([x^3, x*y^2]) - sage: P(2,1).nth_iterate(f,3, normalize = False) + sage: P(2,1).nth_iterate(f, 3, normalize=False) (134217728 : 524288) - sage: P(2,1).nth_iterate(f,3, normalize = True) + sage: P(2,1).nth_iterate(f, 3, normalize=True) (256 : 1) .. TODO:: Is there a more efficient way to do this? @@ -712,46 +717,48 @@ def nth_iterate(self,f, n, **kwds): n = ZZ(n) if n < 0: raise TypeError("must be a forward orbit") - return self.orbit(f,[n,n+1],**kwds)[0] + return self.orbit(f, [n,n+1], **kwds)[0] def orbit(self, f, N, **kwds): r""" - Returns the orbit of `P` by ``self``. If `n` is an integer it returns `[P,self(P),\ldots,self^n(P)]`. - If `n` is a list or tuple `n=[m,k]` it returns `[self^m(P),\ldots,self^k(P)`]. + Returns the orbit of this point by the map ``f``. + + If ``N`` is an integer it returns `[P,self(P),\ldots,self^N(P)]`. + If ``N`` is a list or tuple `N=[m,k]` it returns `[self^m(P),\ldots,self^k(P)`]. Automatically normalize the points if ``normalize=True``. Perform the checks on point initialization if ``check=True`` INPUT: - - ``f`` -- a :class:`SchemeMorphism_polynomial` with ``self`` in ``f.domain()`` + - ``f`` -- a :class:`SchemeMorphism_polynomial` with this point in the domain of ``f``. - - ``N`` -- a non-negative integer or list or tuple of two non-negative integers + - ``N`` -- a non-negative integer or list or tuple of two non-negative integers. kwds: - - ``check`` -- boolean (optional - default: ``True``) + - ``check`` -- boolean (optional - default: ``True``). - - ``normalize`` -- boolean (optional - default: ``False``) + - ``normalize`` -- boolean (optional - default: ``False``). OUTPUT: - - a list of points in ``self.codomain()`` + - a list of points in the domain of ``f``. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2-z^2,2*z^2]) - sage: P(1,2,1).orbit(f,3) + sage: f = H([x^2+y^2, y^2-z^2, 2*z^2]) + sage: P(1, 2, 1).orbit(f, 3) [(1 : 2 : 1), (5 : 3 : 2), (34 : 5 : 8), (1181 : -39 : 128)] :: sage: P. = ProjectiveSpace(ZZ,2) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,y^2-z^2,2*z^2]) - sage: P(1,2,1).orbit(f,[2,4]) + sage: f = H([x^2+y^2, y^2-z^2, 2*z^2]) + sage: P(1, 2, 1).orbit(f, [2, 4]) [(34 : 5 : 8), (1181 : -39 : 128), (1396282 : -14863 : 32768)] :: @@ -759,8 +766,8 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(ZZ,2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,x*z]) - sage: X(2,2,3).orbit(f,3,normalize=True) + sage: f = H([x^2, y^2, x*z]) + sage: X(2, 2, 3).orbit(f, 3, normalize=True) [(2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3), (2 : 2 : 3)] :: @@ -768,7 +775,7 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) sage: f = H([x^2+y^2,y^2]) - sage: P.point([1,2],False).orbit(f,4,check = False) + sage: P.point([1, 2], False).orbit(f, 4, check=False) [(1 : 2), (5 : 4), (41 : 16), (1937 : 256), (3817505 : 65536)] :: @@ -776,8 +783,8 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: P2. = ProjectiveSpace(CC,1) sage: H = Hom(P,P2) - sage: f = H([x^2,2*y^2]) - sage: P(2,1).orbit(f,2) + sage: f = H([x^2, 2*y^2]) + sage: P(2, 1).orbit(f, 2) Traceback (most recent call last): ... TypeError: map must be an endomorphism for iteration @@ -786,12 +793,12 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^2,2*y^2]) - sage: P(2,1).orbit(f,[-1,4]) + sage: f = H([x^2, 2*y^2]) + sage: P(2, 1).orbit(f,[-1, 4]) Traceback (most recent call last): ... TypeError: orbit bounds must be non-negative - sage: P(2,1).orbit(f,0.1) + sage: P(2, 1).orbit(f, 0.1) Traceback (most recent call last): ... TypeError: Attempt to coerce non-integral RealNumber to Integer @@ -800,12 +807,12 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) - sage: f = H([x^3,x*y^2]) - sage: P(0,1).orbit(f,3) + sage: f = H([x^3, x*y^2]) + sage: P(0, 1).orbit(f, 3) Traceback (most recent call last): ... ValueError: [0, 0] does not define a valid point since all entries are 0 - sage: P(0,1).orbit(f,3, check = False) + sage: P(0, 1).orbit(f, 3, check=False) [(0 : 1), (0 : 0), (0 : 0), (0 : 0)] :: @@ -813,9 +820,9 @@ def orbit(self, f, N, **kwds): sage: P. = ProjectiveSpace(ZZ, 1) sage: H = End(P) sage: f = H([x^3, x*y^2]) - sage: P(2,1).orbit(f,3, normalize = False) + sage: P(2,1).orbit(f, 3, normalize=False) [(2 : 1), (8 : 2), (512 : 32), (134217728 : 524288)] - sage: P(2,1).orbit(f,3, normalize = True) + sage: P(2, 1).orbit(f, 3, normalize=True) [(2 : 1), (4 : 1), (16 : 1), (256 : 1)] """ if not f.is_endomorphism(): @@ -850,9 +857,11 @@ def orbit(self, f, N, **kwds): def green_function(self, G, v, **kwds): r""" Evaluates the local Green's function with respect to the morphism ``G`` - at the place ``v`` for ``self`` with ``N`` terms of the - series or to within a given error bound. Must be over a number field - or order of a number field. Note that this is the absolute local Green's function + at the place ``v`` for this point with ``N`` terms of the + series or to within a given error bound. + + Must be over a number field or order of a number field. + Note that this is the absolute local Green's function so is scaled by the degree of the base field. Use ``v=0`` for the archimedean place over `\QQ` or field embedding. Non-archimedean @@ -860,42 +869,42 @@ def green_function(self, G, v, **kwds): ALGORITHM: - See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007. + See Exercise 5.29 and Figure 5.6 of [Silverman-ADS]_. INPUT: - - ``G`` - a projective morphism whose local Green's function we are computing + - ``G`` - a projective morphism whose local Green's function we are computing. - - ``v`` - non-negative integer. a place, use v=0 for the archimedean place + - ``v`` - non-negative integer. a place, use v=0 for the archimedean place. kwds: - - ``N`` - positive integer. number of terms of the series to use, default: 10 + - ``N`` - positive integer. number of terms of the series to use, default: 10. - - ``prec`` - positive integer, float point or p-adic precision, default: 100 + - ``prec`` - positive integer, float point or p-adic precision, default: 100. - - ``error_bound`` - a positive real number + - ``error_bound`` - a positive real number. OUTPUT: - - a real number + - a real number. EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,1) - sage: H=Hom(P,P) - sage: f=H([x^2+y^2,x*y]); - sage: Q=P(5,1) - sage: f.green_function(Q,0,N=30) + sage: P. = ProjectiveSpace(QQ,1) + sage: H = Hom(P,P) + sage: f = H([x^2+y^2, x*y]); + sage: Q = P(5, 1) + sage: f.green_function(Q, 0, N=30) 1.6460930159932946233759277576 :: - sage: P.=ProjectiveSpace(QQ,1) - sage: H=Hom(P,P) - sage: f=H([x^2+y^2,x*y]); - sage: Q=P(5,1) - sage: Q.green_function(f,0,N=200,prec=200) + sage: P. = ProjectiveSpace(QQ,1) + sage: H = Hom(P,P) + sage: f = H([x^2+y^2, x*y]); + sage: Q = P(5, 1) + sage: Q.green_function(f, 0, N=200, prec=200) 1.6460930160038721802875250367738355497198064992657997569827 :: @@ -903,12 +912,12 @@ def green_function(self, G, v, **kwds): sage: K. = QuadraticField(3) sage: P. = ProjectiveSpace(K,1) sage: H = Hom(P,P) - sage: f = H([17*x^2+1/7*y^2,17*w*x*y]) - sage: f.green_function(P.point([w,2],False), K.places()[1]) + sage: f = H([17*x^2+1/7*y^2, 17*w*x*y]) + sage: f.green_function(P.point([w, 2], False), K.places()[1]) 1.7236334013785676107373093775 - sage: print f.green_function(P([2,1]), K.ideal(7), N=7) + sage: print f.green_function(P([2, 1]), K.ideal(7), N=7) 0.48647753726382832627633818586 - sage: print f.green_function(P([w,1]), K.ideal(17), error_bound=0.001) + sage: print f.green_function(P([w, 1]), K.ideal(17), error_bound=0.001) -0.70691993106090157426711999977 .. TODO:: Implement general p-adic extensions so that the flip trick can be used @@ -923,9 +932,9 @@ def green_function(self, G, v, **kwds): GBR = G.change_ring(BR) #so the heights work if not BR in NumberFields(): - raise NotImplementedError("Must be over a NumberField or a NumberField Order") + raise NotImplementedError("must be over a number field or a number field order") if not BR.is_absolute(): - raise TypeError("Must be an absolute field") + raise TypeError("must be an absolute field") #For QQ the 'flip-trick' works better over RR or Qp if isinstance(v, (NumberFieldFractionalIdeal, RingHomomorphism_im_gens)): @@ -936,7 +945,7 @@ def green_function(self, G, v, **kwds): K = R v = BR.places(prec=prec)[0] else: - raise ValueError("Invalid valuation (=%s) entered."%v) + raise ValueError("invalid valuation (=%s) entered"%v) #Coerce all polynomials in F into polynomials with coefficients in K F = G.change_ring(K, check = False) @@ -947,9 +956,9 @@ def green_function(self, G, v, **kwds): if err is not None: err = R(err) if not err>0: - raise ValueError("Error bound (=%s) must be positive."%err) + raise ValueError("error bound (=%s) must be positive"%err) if G.is_endomorphism() == False: - raise NotImplementedError("Error bounds only for endomorphisms") + raise NotImplementedError("error bounds only for endomorphisms") #if doing error estimates, compute needed number of iterates D = (dim + 1) * (d - 1) + 1 @@ -1035,8 +1044,10 @@ def green_function(self, G, v, **kwds): def canonical_height(self, F, **kwds): r""" - Evaluates the (absolute) canonical height of ``self`` with respect to ``F``. Must be over number field - or order of a number field or ``QQbar``. Specify either the number of terms of the series to evaluate or + Evaluates the (absolute) canonical height of this point with respect to the map ``F``. + + Must be over number field or order of a number field or ``QQbar``. + Specify either the number of terms of the series to evaluate or the error bound required. ALGORITHM: @@ -1045,27 +1056,27 @@ def canonical_height(self, F, **kwds): INPUT: - - ``F`` - a projective morphism + - ``F`` - a projective morphism. kwds: - - ``badprimes`` - a list of primes of bad reduction (optional) + - ``badprimes`` - a list of primes of bad reduction (optional). - - ``N`` - positive integer. number of terms of the series to use in the local green functions + - ``N`` - positive integer. number of terms of the series to use in the local green functions. (optional - default:10) - - ``prec`` - positive integer, float point or p-adic precision, default:100 + - ``prec`` - positive integer, float point or p-adic precision, default:100. - - ``error_bound`` - a positive real number (optional) + - ``error_bound`` - a positive real number (optional). - OUTPUT: a real number + OUTPUT: a real number. EXAMPLES:: sage: P. = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) - sage: f = H([x^2+y^2,2*x*y]); - sage: Q = P(2,1) + sage: f = H([x^2+y^2, 2*x*y]); + sage: Q = P(2, 1) sage: f.canonical_height(f(Q)) 2.1965476757927038111992627081 sage: f.canonical_height(Q) @@ -1075,8 +1086,8 @@ def canonical_height(self, F, **kwds): sage: P. = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) - sage: f = H([x^2-29/16*y^2,y^2]); - sage: Q = P(5,4) + sage: f = H([x^2-29/16*y^2, y^2]); + sage: Q = P(5, 4) sage: f.canonical_height(Q, N=30) 1.4989058602918874235833076226e-9 @@ -1085,8 +1096,8 @@ def canonical_height(self, F, **kwds): sage: P. = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x^2-y^2); sage: H = Hom(X,X) - sage: f = H([x^2,y^2,30*z^2]); - sage: Q = X([4,4,1]) + sage: f = H([x^2,y^2, 30*z^2]); + sage: Q = X([4, 4, 1]) sage: f.canonical_height(Q, badprimes=[2,3,5], prec=200) 2.7054056208276961889784303469356774912979228770208655455481 """ @@ -1097,7 +1108,7 @@ def canonical_height(self, F, **kwds): if not K in _NumberFields: if not K is QQbar: - raise NotImplementedError("Must be over a NumberField or a NumberField Order or QQbar") + raise NotImplementedError("must be over a number field or a number field order or QQbar") else: #since this an absolute hieght, we can compute the height of a QQbar point #by choosing any number field it is defined over. @@ -1115,7 +1126,7 @@ def canonical_height(self, F, **kwds): f = f.change_ring(K, embedding=psi) else: if not K.is_absolute(): - raise TypeError("Must be an absolute field") + raise TypeError("must be an absolute field") P = self f = F @@ -1162,7 +1173,7 @@ def canonical_height(self, F, **kwds): def global_height(self, prec=None): r""" - Returns the absolute logarithmic height of the point ``self``. + Returns the absolute logarithmic height of the point. INPUT: @@ -1171,19 +1182,19 @@ def global_height(self, prec=None): OUTPUT: - - a real number + - a real number. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,2) - sage: Q = P.point([4,4,1/30]) + sage: Q = P.point([4, 4, 1/30]) sage: Q.global_height() 4.78749174278205 :: sage: P. = ProjectiveSpace(ZZ,2) - sage: Q = P([4,1,30]) + sage: Q = P([4, 1, 30]) sage: Q.global_height() 3.40119738166216 @@ -1191,14 +1202,14 @@ def global_height(self, prec=None): sage: R. = PolynomialRing(QQ) sage: k. = NumberField(x^2+5) - sage: A = ProjectiveSpace(k,2,'z') - sage: A([3,5*w+1,1]).global_height(prec=100) + sage: A = ProjectiveSpace(k, 2, 'z') + sage: A([3, 5*w+1, 1]).global_height(prec=100) 2.4181409534757389986565376694 :: sage: P. = ProjectiveSpace(QQbar,2) - sage: Q = P([QQbar(sqrt(3)),QQbar(sqrt(-2)),1]) + sage: Q = P([QQbar(sqrt(3)), QQbar(sqrt(-2)), 1]) sage: Q.global_height() 0.549306144334055 """ @@ -1208,119 +1219,122 @@ def global_height(self, prec=None): elif K is QQbar: P = self._number_field_from_algebraics() else: - raise TypeError("Must be over a Numberfield or a Numberfield Order or QQbar") + raise TypeError("must be over a number field or a number field order or QQbar") return(max([P[i].global_height(prec=prec) for i in range(self.codomain().ambient_space().dimension_relative()+1)])) def local_height(self, v, prec=None): r""" - Returns the maximum of the local height of the coordinates of ``self``. + Returns the maximum of the local height of the coordinates of this point. INPUT: - - ``v`` -- a prime or prime ideal of the base ring + - ``v`` -- a prime or prime ideal of the base ring. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - - a real number + - a real number. EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,2) - sage: Q=P.point([4,4,1/150],False) + sage: P.= ProjectiveSpace(QQ,2) + sage: Q = P.point([4,4,1/150], False) sage: Q.local_height(5) 3.21887582486820 :: - sage: P.=ProjectiveSpace(QQ,2) - sage: Q=P([4,1,30]) + sage: P. = ProjectiveSpace(QQ,2) + sage: Q = P([4, 1, 30]) sage: Q.local_height(2) 0.693147180559945 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: - raise("Must be over a Numberfield or a Numberfield Order") + raise("must be over a number field or a number field order") return max([K(c).local_height(v, prec=prec) for c in self]) def local_height_arch(self, i, prec=None): r""" - Returns the maximum of the local heights at the ``i``-th infinite place of ``self``. + Returns the maximum of the local heights at the ``i``-th infinite place of this point. INPUT: - - ``i`` -- an integer + - ``i`` -- an integer. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - - a real number + - a real number. EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,2) - sage: Q = P.point([4,4,1/150], False) + sage: P. = ProjectiveSpace(QQ,2) + sage: Q = P.point([4, 4, 1/150], False) sage: Q.local_height_arch(0) 1.38629436111989 :: - sage: P.=ProjectiveSpace(QuadraticField(5, 'w'),2) - sage: Q = P.point([4,1,30], False) + sage: P. = ProjectiveSpace(QuadraticField(5, 'w'), 2) + sage: Q = P.point([4, 1, 30], False) sage: Q.local_height_arch(1) 3.401197381662155375413236691607 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: - raise("Must be over a Numberfield or a Numberfield Order") + raise("must be over a number field or a number field order") if K == QQ: return max([K(c).local_height_arch(prec=prec) for c in self]) else: return max([K(c).local_height_arch(i, prec=prec) for c in self]) - def multiplier(self,f,n,check=True): + def multiplier(self, f, n, check=True): r""" - Returns the multiplier of the projective point ``self`` of period `n` by the function `f`. - `f` must be an endomorphism of projective space + Returns the multiplier of this point of period ``n`` by the function ``f``. + + ``f`` must be an endomorphism of projective space. INPUT: - - ``f`` - a endomorphism of ``self.codomain()`` + - ``f`` - a endomorphism of this point's codomain. - - ``n`` - a positive integer, the period of ``self`` + - ``n`` - a positive integer, the period of this point. - - ``check`` -- check if ``P`` is periodic of period ``n``, Default:True + - ``check`` -- check if ``P`` is periodic of period ``n``, Default:True. OUTPUT: - - a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of ``self`` + - a square matrix of size ``self.codomain().dimension_relative()`` in the + ``base_ring`` of this point. EXAMPLES:: - sage: P.=ProjectiveSpace(QQ,3) - sage: H=Hom(P,P) - sage: f=H([x^2,y^2,4*w^2,4*z^2]); - sage: Q=P.point([4,4,1,1],False); - sage: Q.multiplier(f,1) + sage: P. = ProjectiveSpace(QQ,3) + sage: H = Hom(P,P) + sage: f = H([x^2, y^2, 4*w^2, 4*z^2]); + sage: Q = P.point([4, 4, 1, 1], False); + sage: Q.multiplier(f, 1) [ 2 0 -8] [ 0 2 -8] [ 0 0 -2] """ - return(f.multiplier(self,n,check)) + return(f.multiplier(self, n, check)) - def is_preperiodic(self, f, err = 0.1, return_period=False): + def is_preperiodic(self, f, err=0.1, return_period=False): r""" - Determine if the point ``self`` is preperiodic with respect to the map ``f``, i.e., - if ``self`` has a finite forward orbit by ``f``. This is only implemented for - projective space (not subschemes). There are two optional keyword arguments: + Determine if the point is preperiodic with respect to the map ``f``. + + This is only implemented for projective space (not subschemes). + There are two optional keyword arguments: ``error_bound`` sets the error_bound used in the canonical height computation and ``return_period`` a boolean which controls if the period is returned if the - point is preperiodic. If ``return_period`` is ``True`` and the ``self`` is not + point is preperiodic. If ``return_period`` is ``True`` and this point is not preperiodic, then `(0,0)` is returned for the period. ALGORITHM: @@ -1338,28 +1352,28 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): INPUT: - - ``f`` -- an endomorphism of ``self.codomain()`` + - ``f`` -- an endomorphism of this point's codomain. kwds: - - ``error_bound`` -- a positive real number (optional - default: 0.1) + - ``error_bound`` -- a positive real number (optional - default: 0.1). - - ``return_period`` -- boolean (optional - default: ``False``) + - ``return_period`` -- boolean (optional - default: ``False``). OUTPUT: - - boolean - ``True`` if preperiodic + - boolean - ``True`` if preperiodic. - if return_period is ``True``, then ``(0,0)`` if wandering, and ``(m,n)`` - if preperiod ``m`` and period ``n`` + if preperiod ``m`` and period ``n``. EXAMPLES:: sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) sage: f = H([x^3-3*x*y^2, y^3]) - sage: Q = P(-1,1) + sage: Q = P(-1, 1) sage: Q.is_preperiodic(f) True @@ -1368,10 +1382,10 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): sage: P. = ProjectiveSpace(QQ,1) sage: H = End(P) sage: f = H([x^2-29/16*y^2, y^2]) - sage: Q = P(1,4) + sage: Q = P(1, 4) sage: Q.is_preperiodic(f, return_period=True) (1, 3) - sage: Q = P(1,1) + sage: Q = P(1, 1) sage: Q.is_preperiodic(f, return_period=True) (0, 0) @@ -1391,12 +1405,12 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): :: - sage: P. = ProjectiveSpace(QQ, 2) + sage: P. = ProjectiveSpace(QQ,2) sage: H = Hom(P,P) sage: f = H([-38/45*x^2 + (2*y - 7/45*z)*x + (-1/2*y^2 - 1/2*y*z + z^2),\ -67/90*x^2 + (2*y + z*157/90)*x - y*z, z^2]) - sage: Q = P([1,3,1]) - sage: Q.is_preperiodic(f, return_period = True) + sage: Q = P([1, 3, 1]) + sage: Q.is_preperiodic(f, return_period=True) (0, 9) :: @@ -1406,7 +1420,7 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): sage: f = H([(-y - w)*x + (-13/30*y^2 + 13/30*w*y + w^2),-1/2*x^2 + (-y + 3/2*w)*x\ + (-1/3*y^2 + 4/3*w*y),-3/2*z^2 + 5/2*z*w + w^2,w^2]) sage: Q = P([3,0,4/3,1]) - sage: Q.is_preperiodic(f, return_period = True) + sage: Q.is_preperiodic(f, return_period=True) (2, 24) :: @@ -1414,8 +1428,8 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): sage: set_verbose(-1) sage: P. = ProjectiveSpace(QQbar,2) sage: H = End(P) - sage: f = H([x^2,QQbar(sqrt(-1))*y^2,z^2]) - sage: Q = P([1,1,1]) + sage: f = H([x^2, QQbar(sqrt(-1))*y^2, z^2]) + sage: Q = P([1, 1, 1]) sage: Q.is_preperiodic(f) True @@ -1424,24 +1438,24 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): sage: set_verbose(-1) sage: P. = ProjectiveSpace(QQbar,2) sage: H = End(P) - sage: f = H([x^2,y^2,z^2]) - sage: Q = P([QQbar(sqrt(-1)),1,1]) + sage: f = H([x^2, y^2, z^2]) + sage: Q = P([QQbar(sqrt(-1)), 1, 1]) sage: Q.is_preperiodic(f) True """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(self.codomain()): - raise NotImplementedError("Must be over projective space") + raise NotImplementedError("must be over projective space") if not f.is_endomorphism(): - raise TypeError("Map must be an endomorphism") + raise TypeError("map must be an endomorphism") if not f.is_morphism(): - raise TypeError("Must be a morphism") + raise TypeError("must be a morphism") if not self.codomain() is f.domain(): - raise TypeError("Point must be in domain of map") + raise TypeError("point must be in domain of map") K = FractionField(self.codomain().base_ring()) if not K in _NumberFields and not K is QQbar: - raise NotImplementedError("Must be over a NumberField or a NumberField Order or QQbar") + raise NotImplementedError("must be over a number field or a number field order or QQbar") h = self.canonical_height(f, error_bound = err) # we know canonical height 0 if and only if preperiodic @@ -1457,7 +1471,7 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): orbit = [self] n = 1 # to compute period P = f(self) - H= P.global_height() + H = P.global_height() while P not in orbit and H <= B: orbit.append(P) P = f(P) @@ -1465,8 +1479,8 @@ def is_preperiodic(self, f, err = 0.1, return_period=False): n += 1 if H <= B: #it must have been in the cycle if return_period: - m=orbit.index(P) - return((m,n-m)) + m = orbit.index(P) + return((m, n-m)) else: return True if return_period: @@ -1481,9 +1495,9 @@ class SchemeMorphism_point_projective_field(SchemeMorphism_point_projective_ring INPUT: - ``X`` -- a homset of a subscheme of an ambient projective space - over a field `K` + over a field `K`. - - ``v`` -- a list or tuple of coordinates in `K` + - ``v`` -- a list or tuple of coordinates in `K`. - ``check`` -- boolean (optional, default:``True``). Whether to check the input for consistency. @@ -1491,7 +1505,7 @@ class SchemeMorphism_point_projective_field(SchemeMorphism_point_projective_ring EXAMPLES:: sage: P = ProjectiveSpace(3, RR) - sage: P(2,3,4,5) + sage: P(2, 3, 4, 5) (0.400000000000000 : 0.600000000000000 : 0.800000000000000 : 1.00000000000000) """ @@ -1501,8 +1515,10 @@ def __init__(self, X, v, check=True): See :class:`SchemeMorphism_point_projective_ring` for details. - This function still normalized points so that the rightmost non-zero coordinate is 1. The is to maintain current functionality with current - implementations of curves in projectives space (plane, conic, elliptic, etc). The :class:`SchemeMorphism_point_projective_ring` is for general use. + This function still normalizes points so that the rightmost non-zero coordinate is 1. + This is to maintain functionality with current + implementations of curves in projectives space (plane, conic, elliptic, etc). + The :class:`SchemeMorphism_point_projective_ring` is for general use. EXAMPLES:: @@ -1513,7 +1529,7 @@ def __init__(self, X, v, check=True): :: sage: P = ProjectiveSpace(3, QQ) - sage: P(0,0,0,0) + sage: P(0, 0, 0, 0) Traceback (most recent call last): ... ValueError: [0, 0, 0, 0] does not define a valid point since all entries are 0 @@ -1522,7 +1538,7 @@ def __init__(self, X, v, check=True): sage: P. = ProjectiveSpace(2, QQ) sage: X = P.subscheme([x^2-y*z]) - sage: X([2,2,2]) + sage: X([2, 2, 2]) (1 : 1 : 1) :: @@ -1542,7 +1558,7 @@ def __init__(self, X, v, check=True): v = [0] * (d) v[1] = 1 if not isinstance(v,(list,tuple)): - raise TypeError("Argument v (= %s) must be a scheme point, list, or tuple."%str(v)) + raise TypeError("argument v (= %s) must be a scheme point, list, or tuple"%str(v)) if len(v) != d and len(v) != d-1: raise TypeError("v (=%s) must have %s components"%(v, d)) @@ -1573,7 +1589,7 @@ def __init__(self, X, v, check=True): def __hash__(self): """ - Computes the hash value of ``self``. + Computes the hash value of this point. OUTPUT: Integer. @@ -1593,7 +1609,7 @@ def __hash__(self): def normalize_coordinates(self): r""" - Normalizes ``self`` so that the last non-zero coordinate is `1`. + Normalizes the point so that the last non-zero coordinate is `1`. OUTPUT: None. @@ -1608,7 +1624,7 @@ def normalize_coordinates(self): :: sage: P. = ProjectiveSpace(QQ, 2) - sage: X =P.subscheme(x^2-y^2); + sage: X = P.subscheme(x^2-y^2); sage: Q = X.point([23, 23, 46], False); Q (23 : 23 : 46) sage: Q.normalize_coordinates(); Q @@ -1622,7 +1638,9 @@ def normalize_coordinates(self): def _number_field_from_algebraics(self): r""" Given a projective point defined over ``QQbar``, return the same point, but defined - over a number field. This is only implemented for points of proejctive space. + over a number field. + + This is only implemented for points of projective space. OUTPUT: scheme point @@ -1630,7 +1648,7 @@ def _number_field_from_algebraics(self): sage: R. = PolynomialRing(QQ) sage: P. = ProjectiveSpace(QQbar,1) - sage: Q = P([-1/2*QQbar(sqrt(2))+QQbar(I),1]) + sage: Q = P([-1/2*QQbar(sqrt(2)) + QQbar(I), 1]) sage: S = Q._number_field_from_algebraics(); S (-1/2*a^3 - a^2 + 1/2*a : 1) sage: S.codomain() @@ -1638,7 +1656,7 @@ def _number_field_from_algebraics(self): """ from sage.schemes.projective.projective_space import is_ProjectiveSpace if not is_ProjectiveSpace(self.codomain()): - raise NotImplementedError("Not implemented for subschemes") + raise NotImplementedError("not implemented for subschemes") K,P,phi = number_field_elements_from_algebraics(list(self)) from sage.schemes.projective.projective_space import ProjectiveSpace @@ -1690,14 +1708,14 @@ class SchemeMorphism_point_projective_finite_field(SchemeMorphism_point_projecti def __hash__(self): r""" - Returns the integer hash of ``self`` + Returns the integer hash of this point. OUTPUT: Integer. EXAMPLES:: sage: P. = ProjectiveSpace(GF(5), 2) - sage: hash(P(2,1,2)) + sage: hash(P(2, 1, 2)) 41 :: @@ -1710,13 +1728,13 @@ def __hash__(self): :: sage: P. = ProjectiveSpace(GF(13), 1) - sage: hash(P(3,4)) + sage: hash(P(3, 4)) 17 :: sage: P. = ProjectiveSpace(GF(13^3,'t'), 1) - sage: hash(P(3,4)) + sage: hash(P(3, 4)) 2201 """ p = self.codomain().base_ring().order() @@ -1725,23 +1743,25 @@ def __hash__(self): def orbit_structure(self,f): r""" - Every point is preperiodic over a finite field. This function returns the pair `[m,n]` where `m` is the - preperiod and `n` is the period of the point ``self`` by ``f``. + This function returns the pair `[m,n]` where `m` is the + preperiod and `n` is the period of the point by the map ``f``. + + Every point is preperiodic over a finite field so this is always possible. INPUT: - - ``f`` -- a :class:`ScemeMorphism_polynomial` with ``self`` in ``f.domain()`` + - ``f`` -- a :class:`ScemeMorphism_polynomial` with this point in ``f.domain()``. OUTPUT: - - a list `[m,n]` of integers + - a list `[m,n]` of integers. EXAMPLES:: sage: P. = ProjectiveSpace(GF(5),2) sage: H = Hom(P,P) - sage: f = H([x^2 + y^2,y^2,z^2 + y*z]) - sage: P(1,0,1).orbit_structure(f) + sage: f = H([x^2 + y^2, y^2, z^2 + y*z]) + sage: P(1, 0, 1).orbit_structure(f) [0, 1] :: @@ -1749,8 +1769,8 @@ def orbit_structure(self,f): sage: P. = ProjectiveSpace(GF(17),2) sage: X = P.subscheme(x^2-y^2) sage: H = Hom(X,X) - sage: f = H([x^2,y^2,z^2]) - sage: X(1,1,2).orbit_structure(f) + sage: f = H([x^2, y^2, z^2]) + sage: X(1, 1, 2).orbit_structure(f) [3, 1] :: @@ -1758,23 +1778,23 @@ def orbit_structure(self,f): sage: R. = GF(13^3) sage: P. = ProjectiveSpace(R,1) sage: H = Hom(P,P) - sage: f = H([x^2 - y^2,y^2]) - sage: P(t,4).orbit_structure(f) + sage: f = H([x^2 - y^2, y^2]) + sage: P(t, 4).orbit_structure(f) [11, 6] """ - Orbit=[] - index=1 - P=copy(self) + orbit = [] + index = 1 + P = copy(self) P.normalize_coordinates() - F=copy(f) + F = copy(f) F.normalize_coordinates() - while not P in Orbit: - Orbit.append(P) - P=F(P) + while not P in orbit: + orbit.append(P) + P = F(P) P.normalize_coordinates() - index+=1 - I=Orbit.index(P) - return([I,index-I-1]) + index += 1 + I = orbit.index(P) + return([I, index-I-1]) #******************************************************************* # Abelian varieties diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index 88c092582d5..fe77712cccd 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -17,7 +17,7 @@ sage: from sage.schemes.projective.projective_rational_point import enum_projective_rational_field sage: P. = ProjectiveSpace(2,QQ) sage: C = P.subscheme([X+Y-Z]) - sage: enum_projective_rational_field(C,3) + sage: enum_projective_rational_field(C, 3) [(-2 : 3 : 1), (-1 : 1 : 0), (-1 : 2 : 1), (-1/2 : 3/2 : 1), (0 : 1 : 1), (1/3 : 2/3 : 1), (1/2 : 1/2 : 1), (2/3 : 1/3 : 1), (1 : 0 : 1), (3/2 : -1/2 : 1), (2 : -1 : 1), (3 : -2 : 1)] @@ -63,7 +63,8 @@ def enum_projective_rational_field(X,B): INPUT: - - ``X`` - a scheme or set of abstract rational points of a scheme; + - ``X`` - a scheme or set of abstract rational points of a scheme. + - ``B`` - a positive integer bound. OUTPUT: @@ -73,10 +74,10 @@ def enum_projective_rational_field(X,B): EXAMPLES:: - sage: P. = ProjectiveSpace(2,QQ) + sage: P. = ProjectiveSpace(2, QQ) sage: C = P.subscheme([X+Y-Z]) sage: from sage.schemes.projective.projective_rational_point import enum_projective_rational_field - sage: enum_projective_rational_field(C(QQ),6) + sage: enum_projective_rational_field(C(QQ), 6) [(-5 : 6 : 1), (-4 : 5 : 1), (-3 : 4 : 1), (-2 : 3 : 1), (-3/2 : 5/2 : 1), (-1 : 1 : 0), (-1 : 2 : 1), (-2/3 : 5/3 : 1), (-1/2 : 3/2 : 1), (-1/3 : 4/3 : 1), (-1/4 : 5/4 : 1), @@ -92,8 +93,8 @@ def enum_projective_rational_field(X,B): :: - sage: P3. = ProjectiveSpace(3,QQ) - sage: enum_projective_rational_field(P3,1) + sage: P3. = ProjectiveSpace(3, QQ) + sage: enum_projective_rational_field(P3, 1) [(-1 : -1 : -1 : 1), (-1 : -1 : 0 : 1), (-1 : -1 : 1 : 0), (-1 : -1 : 1 : 1), (-1 : 0 : -1 : 1), (-1 : 0 : 0 : 1), (-1 : 0 : 1 : 0), (-1 : 0 : 1 : 1), (-1 : 1 : -1 : 1), (-1 : 1 : 0 : 0), (-1 : 1 : 0 : 1), (-1 : 1 : 1 : 0), @@ -117,11 +118,11 @@ def enum_projective_rational_field(X,B): from sage.schemes.projective.projective_space import is_ProjectiveSpace if(is_Scheme(X)): if (not is_ProjectiveSpace(X.ambient_space())): - raise TypeError("Ambient space must be projective space over the rational field") + raise TypeError("ambient space must be projective space over the rational field") X = X(X.base_ring()) else: if (not is_ProjectiveSpace(X.codomain().ambient_space())): - raise TypeError("Codomain must be projective space over the rational field") + raise TypeError("codomain must be projective space over the rational field") n = X.codomain().ambient_space().ngens() zero = (0,) * n @@ -138,16 +139,18 @@ def enum_projective_rational_field(X,B): def enum_projective_number_field(X,B, prec=53): """ - Enumerates projective points on scheme ``X`` defined over a number field. Simply checks all of the - points of absolute height of at most ``B`` and adds those that are on the scheme to the list. + Enumerates projective points on scheme ``X`` defined over a number field. + + Simply checks all of the points of absolute height of at most ``B`` + and adds those that are on the scheme to the list. INPUT: - - ``X`` - a scheme defined over a number field + - ``X`` - a scheme defined over a number field. - - ``B`` - a real number + - ``B`` - a real number. - - ``prec`` - the precision to use for computing the elements of bounded height of number fields + - ``prec`` - the precision to use for computing the elements of bounded height of number fields. OUTPUT: @@ -178,7 +181,7 @@ def enum_projective_number_field(X,B, prec=53): sage: u = QQ['u'].0 sage: K = NumberField(u^2 + 3, 'v') sage: A. = ProjectiveSpace(K,1) - sage: X=A.subscheme(x-y) + sage: X = A.subscheme(x-y) sage: from sage.schemes.projective.projective_rational_point import enum_projective_number_field sage: enum_projective_number_field(X, 2) [(1 : 1)] @@ -186,11 +189,11 @@ def enum_projective_number_field(X,B, prec=53): from sage.schemes.projective.projective_space import is_ProjectiveSpace if(is_Scheme(X)): if (not is_ProjectiveSpace(X.ambient_space())): - raise TypeError("Ambient space must be projective space over a number field") + raise TypeError("ambient space must be projective space over a number field") X = X(X.base_ring()) else: if (not is_ProjectiveSpace(X.codomain().ambient_space())): - raise TypeError("Codomain must be projective space over a number field") + raise TypeError("codomain must be projective space over a number field") R = X.codomain().ambient_space() @@ -253,7 +256,7 @@ def enum_projective_finite_field(X): ALGORITHM: - Checks all points in projective space to see if they lie on X. + Checks all points in projective space to see if they lie on ``X``. .. WARNING:: @@ -266,11 +269,11 @@ def enum_projective_finite_field(X): from sage.schemes.projective.projective_space import is_ProjectiveSpace if(is_Scheme(X)): if (not is_ProjectiveSpace(X.ambient_space())): - raise TypeError("Ambient space must be projective space over a finite") + raise TypeError("ambient space must be projective space over a finite") X = X(X.base_ring()) else: if (not is_ProjectiveSpace(X.codomain().ambient_space())): - raise TypeError("Codomain must be projective space over a finite field") + raise TypeError("codomain must be projective space over a finite field") n = X.codomain().ambient_space().ngens()-1 F = X.value_ring() diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 61b42ed1120..6d5aa6c588c 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -81,11 +81,10 @@ Integer, ZZ) -from sage.rings.ring import is_Ring +from sage.rings.ring import CommutativeRing from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing -from sage.rings.finite_rings.constructor import is_FiniteField -from sage.rings.commutative_ring import is_CommutativeRing +from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields _Fields = Fields() @@ -108,15 +107,16 @@ from sage.schemes.projective.projective_point import (SchemeMorphism_point_projective_ring, SchemeMorphism_point_projective_field, SchemeMorphism_point_projective_finite_field) -from sage.schemes.projective.projective_morphism import (SchemeMorphism_polynomial_projective_space, - SchemeMorphism_polynomial_projective_space_field, - SchemeMorphism_polynomial_projective_space_finite_field) +from sage.schemes.projective.projective_morphism import (SchemeMorphism_polynomial_projective_space, + SchemeMorphism_polynomial_projective_space_field, + SchemeMorphism_polynomial_projective_space_finite_field) def is_ProjectiveSpace(x): r""" - Return True if `x` is a projective space, i.e., an ambient space - `\mathbb{P}^n_R`, where `R` is a ring and `n\geq 0` is an - integer. + Return True if ``x`` is a projective space. + + In other words, if ``x`` is an ambient space `\mathbb{P}^n_R`, + where `R` is a ring and `n\geq 0` is an integer. EXAMPLES:: @@ -132,7 +132,7 @@ def is_ProjectiveSpace(x): def ProjectiveSpace(n, R=None, names='x'): r""" - Return projective space of dimension `n` over the ring `R`. + Return projective space of dimension ``n`` over the ring ``R``. EXAMPLES: The dimension and ring can be given in either order. @@ -208,7 +208,7 @@ def ProjectiveSpace(n, R=None, names='x'): return ProjectiveSpace_rational_field(n, R, names) else: return ProjectiveSpace_field(n, R, names) - elif is_CommutativeRing(R): + elif isinstance(R, CommutativeRing): return ProjectiveSpace_ring(n, R, names) else: raise TypeError("R (=%s) must be a commutative ring"%R) @@ -240,6 +240,8 @@ class ProjectiveSpace_ring(AmbientSpace): """ def __init__(self, n, R=ZZ, names=None): """ + Initialization function. + EXAMPLES:: sage: ProjectiveSpace(3, Zp(5), 'y') @@ -251,8 +253,9 @@ def __init__(self, n, R=ZZ, names=None): def ngens(self): """ - Return the number of generators of self, i.e. the number of - variables in the coordinate ring of self. + Return the number of generators of this projective space. + + This is the number of variables in the coordinate ring of self. EXAMPLES:: @@ -265,7 +268,7 @@ def ngens(self): def _check_satisfies_equations(self, v): """ - Return True if `v` defines a point on the scheme self; raise a + Return True if ``v`` defines a point on the scheme; raise a TypeError otherwise. EXAMPLES:: @@ -286,7 +289,7 @@ def _check_satisfies_equations(self, v): sage: P._check_satisfies_equations([0, 0, 0]) Traceback (most recent call last): ... - TypeError: The zero vector is not a point in projective space + TypeError: the zero vector is not a point in projective space :: @@ -294,7 +297,7 @@ def _check_satisfies_equations(self, v): sage: P._check_satisfies_equations((1, 0)) Traceback (most recent call last): ... - TypeError: The list v=(1, 0) must have 3 components + TypeError: the list v=(1, 0) must have 3 components :: @@ -302,20 +305,20 @@ def _check_satisfies_equations(self, v): sage: P._check_satisfies_equations([1/2, 0, 1]) Traceback (most recent call last): ... - TypeError: The components of v=[1/2, 0, 1] must be elements of Integer Ring + TypeError: the components of v=[1/2, 0, 1] must be elements of Integer Ring """ if not isinstance(v, (list, tuple)): - raise TypeError('The argument v=%s must be a list or tuple'%v) + raise TypeError('the argument v=%s must be a list or tuple'%v) n = self.ngens() if not len(v) == n: - raise TypeError('The list v=%s must have %s components'%(v, n)) + raise TypeError('the list v=%s must have %s components'%(v, n)) R = self.base_ring() for coord in v: if not coord in R: - raise TypeError('The components of v=%s must be elements of %s'%(v, R)) + raise TypeError('the components of v=%s must be elements of %s'%(v, R)) zero = [R(0)]*n if v == zero: - raise TypeError('The zero vector is not a point in projective space') + raise TypeError('the zero vector is not a point in projective space') return True def coordinate_ring(self): @@ -354,11 +357,11 @@ def _validate(self, polynomials): INPUT: - ``polynomials`` -- tuple of polynomials in the coordinate ring of - self + this space. OUTPUT: - - tuple of polynomials in the coordinate ring of self + - tuple of polynomials in the coordinate ring of this space. EXAMPLES:: @@ -372,7 +375,7 @@ def _validate(self, polynomials): sage: P._validate((x*y - z, x)) Traceback (most recent call last): ... - TypeError: x*y - z is not a homogeneous polynomial! + TypeError: x*y - z is not a homogeneous polynomial :: @@ -380,17 +383,19 @@ def _validate(self, polynomials): sage: P._validate(x*y - z) Traceback (most recent call last): ... - TypeError: The argument polynomials=x*y - z must be a list or tuple + TypeError: the argument polynomials=x*y - z must be a list or tuple """ if not isinstance(polynomials, (list, tuple)): - raise TypeError('The argument polynomials=%s must be a list or tuple'%polynomials) + raise TypeError('the argument polynomials=%s must be a list or tuple'%polynomials) for f in polynomials: if not f.is_homogeneous(): - raise TypeError("%s is not a homogeneous polynomial!" % f) + raise TypeError("%s is not a homogeneous polynomial" % f) return polynomials def __cmp__(self, right): """ + Compare equality of two projective spaces. + EXAMPLES:: sage: ProjectiveSpace(QQ, 3, 'a') == ProjectiveSpace(ZZ, 3, 'a') @@ -426,17 +431,17 @@ def _latex_(self): def _linear_system_as_kernel(self, d, pt, m): """ Return a matrix whose kernel consists of the coefficient vectors - of the degree d hypersurfaces (wrt lexicographic ordering of its - monomials) with multiplicity at least m at pt. + of the degree ``d`` hypersurfaces (wrt lexicographic ordering of its + monomials) with multiplicity at least ``m`` at ``pt``. INPUT: - - ``d`` -- a nonnegative integer + - ``d`` -- a nonnegative integer. - ``pt`` -- a point of self (possibly represented by a list with at \ - least one component equal to 1) + least one component equal to 1). - - ``m`` -- a nonnegative integer + - ``m`` -- a nonnegative integer. OUTPUT: @@ -459,7 +464,7 @@ def _linear_system_as_kernel(self, d, pt, m): [0] [0] - If the multiplcity `m` is 0, then the a matrix with zero rows is returned:: + If the multiplcity ``m`` is 0, then the a matrix with zero rows is returned:: sage: P = ProjectiveSpace(GF(5), 2, names='x') sage: pt = P([1, 1, 1]) @@ -488,7 +493,7 @@ def _linear_system_as_kernel(self, d, pt, m): sage: P._linear_system_as_kernel(3, pt, 2) Traceback (most recent call last): ... - TypeError: At least one component of pt=[3, 3, 0] must be equal + TypeError: at least one component of pt=[3, 3, 0] must be equal to 1 The components of the list do not have to be elements of the base ring @@ -513,36 +518,36 @@ def _linear_system_as_kernel(self, d, pt, m): """ if not isinstance(d, (int, Integer)): - raise TypeError('The argument d=%s must be an integer'%d) + raise TypeError('the argument d=%s must be an integer'%d) if d < 0: - raise ValueError('The integer d=%s must be nonnegative'%d) + raise ValueError('the integer d=%s must be nonnegative'%d) if not isinstance(pt, (list, tuple, \ SchemeMorphism_point_projective_ring)): - raise TypeError('The argument pt=%s must be a list, tuple, or ' + raise TypeError('the argument pt=%s must be a list, tuple, or ' 'point on a projective space'%pt) pt, R = prepare(pt, None) n = self.dimension_relative() if not len(pt) == n+1: - raise TypeError('The sequence pt=%s must have %s ' + raise TypeError('the sequence pt=%s must have %s ' 'components'%(pt, n + 1)) if not R.has_coerce_map_from(self.base_ring()): - raise TypeError('Unable to find a common ring for all elements') + raise TypeError('unable to find a common ring for all elements') try: i = pt.index(1) except Exception: - raise TypeError('At least one component of pt=%s must be equal ' + raise TypeError('at least one component of pt=%s must be equal ' 'to 1'%pt) pt = pt[:i] + pt[i+1:] if not isinstance(m, (int, Integer)): - raise TypeError('The argument m=%s must be an integer'%m) + raise TypeError('the argument m=%s must be an integer'%m) if m < 0: - raise ValueError('The integer m=%s must be nonnegative'%m) + raise ValueError('the integer m=%s must be nonnegative'%m) # the components of partials correspond to partial derivatives # of order at most m-1 with respect to n variables - partials = IntegerVectors(m-1,n+1).list() + partials = IntegerVectors(m-1, n+1).list() # the components of monoms correspond to monomials of degree # at most d in n variables - monoms = IntegerVectors(d,n+1).list() + monoms = IntegerVectors(d, n+1).list() M = matrix(R,len(partials),len(monoms)) for row in range(M.nrows()): e = partials[row][:i] + partials[row][i+1:] @@ -617,9 +622,9 @@ def _repr_(self): def _repr_generic_point(self, v=None): """ Return a string representation of the generic point - corresponding to the list of polys on this projective space. + corresponding to the list of polys ``v`` on this projective space. - If polys is None, the representation of the generic point of + If ``v`` is None, the representation of the generic point of the projective space is returned. EXAMPLES:: @@ -637,9 +642,9 @@ def _repr_generic_point(self, v=None): def _latex_generic_point(self, v=None): """ Return a LaTeX representation of the generic point - corresponding to the list of polys on this projective space. + corresponding to the list of polys ``v`` on this projective space. - If polys is None, the representation of the generic point of + If ``v`` is None, the representation of the generic point of the projective space is returned. EXAMPLES:: @@ -656,20 +661,20 @@ def _latex_generic_point(self, v=None): def change_ring(self, R): r""" - Return a projective space over ring `R` and otherwise the same as self. + Return a projective space over ring ``R``. INPUT: - - ``R`` -- commutative ring + - ``R`` -- commutative ring. OUTPUT: - - projective space over ``R`` + - projective space over ``R``. .. NOTE:: - There is no need to have any relation between `R` and the base ring - of self, if you want to have such a relation, use + There is no need to have any relation between ``R`` and the base ring + of this space, if you want to have such a relation, use ``self.base_extend(R)`` instead. EXAMPLES:: @@ -685,7 +690,7 @@ def change_ring(self, R): def is_projective(self): """ - Return that this ambient space is projective n-space. + Return that this ambient space is projective `n`-space. EXAMPLES:: @@ -696,11 +701,11 @@ def is_projective(self): def subscheme(self, X): """ - Return the closed subscheme defined by X. + Return the closed subscheme defined by ``X``. INPUT: - - ``X`` - a list or tuple of equations + - ``X`` - a list or tuple of equations. EXAMPLES:: @@ -731,14 +736,18 @@ def subscheme(self, X): To: Spectrum of Rational Field Defn: Structure map - sage: TestSuite(X).run(skip=["_test_an_element", "_test_elements", "_test_elements_eq", "_test_some_elements", "_test_elements_eq_reflexive", "_test_elements_eq_symmetric", "_test_elements_eq_transitive", "_test_elements_neq"]) + sage: TestSuite(X).run(skip=["_test_an_element", "_test_elements",\ + "_test_elements_eq", "_test_some_elements", "_test_elements_eq_reflexive",\ + "_test_elements_eq_symmetric", "_test_elements_eq_transitive",\ + "_test_elements_neq"]) """ from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme_projective return AlgebraicScheme_subscheme_projective(self, X) - def affine_patch(self, i, AA = None): + def affine_patch(self, i, AA=None): r""" Return the `i^{th}` affine patch of this projective space. + This is an ambient affine space `\mathbb{A}^n_R,` where `R` is the base ring of self, whose "projective embedding" map is `1` in the `i^{th}` factor. @@ -782,7 +791,7 @@ def affine_patch(self, i, AA = None): i = int(i) # implicit type checking n = self.dimension_relative() if i < 0 or i > n: - raise ValueError("Argument i (= %s) must be between 0 and %s."%(i, n)) + raise ValueError("argument i (= %s) must be between 0 and %s"%(i, n)) try: A = self.__affine_patches[i] #assume that if you've passed in a new affine space you want to override @@ -798,7 +807,7 @@ def affine_patch(self, i, AA = None): from sage.schemes.affine.affine_space import AffineSpace AA = AffineSpace(n, self.base_ring(), names = 'x') elif AA.dimension_relative() != n: - raise ValueError("Affine Space must be of the dimension %s"%(n)) + raise ValueError("affine space must be of the dimension %s"%(n)) AA._default_embedding_index = i phi = AA.projective_embedding(i, self) self.__affine_patches[i] = AA @@ -806,18 +815,18 @@ def affine_patch(self, i, AA = None): def _an_element_(self): r""" - Returns a (preferably typical) element of ``self``. + Returns a (preferably typical) element of this space. This is used both for illustration and testing purposes. - OUTPUT: a point in the projective space ``self``. + OUTPUT: a point in this projective space. EXAMPLES:: - sage: ProjectiveSpace(ZZ,3,'x').an_element() + sage: ProjectiveSpace(ZZ, 3, 'x').an_element() (7 : 6 : 5 : 1) - sage: ProjectiveSpace(PolynomialRing(ZZ,'y'),3,'x').an_element() + sage: ProjectiveSpace(PolynomialRing(ZZ,'y'), 3, 'x').an_element() (7*y : 6*y : 5*y : 1) """ n = self.dimension_relative() @@ -826,27 +835,31 @@ def _an_element_(self): def Lattes_map(self, E, m): r""" - Given an elliptic curve `E` and an integer `m` return the Lattes map associated to multiplication by `m`. - In other words, the rational map on the quotient `E/\{\pm 1\} \cong \mathbb{P}^1` associated to `[m]:E \to E`. + Given an elliptic curve ``E`` and an integer ``m`` return + the Lattes map associated to multiplication by `m`. + + In other words, the rational map on the quotient + `E/\{\pm 1\} \cong \mathbb{P}^1` associated to `[m]:E \to E`. INPUT: - - ``E`` -- an elliptic curve - - ``m`` -- an integer + - ``E`` -- an elliptic curve. + + - ``m`` -- an integer. - OUTPUT: an endomorphism of ``self``. + OUTPUT: an endomorphism of this projective space. Examples:: sage: P. = ProjectiveSpace(QQ,1) sage: E = EllipticCurve(QQ,[-1, 0]) - sage: P.Lattes_map(E,2) + sage: P.Lattes_map(E, 2) Scheme endomorphism of Projective Space of dimension 1 over Rational Field Defn: Defined on coordinates by sending (x : y) to (x^4 + 2*x^2*y^2 + y^4 : 4*x^3*y - 4*x*y^3) """ if self.dimension_relative() != 1: - raise TypeError("Must be dimension 1") + raise TypeError("must be dimension 1") L = E.multiplication_by_m(m, x_only = True) F = [L.numerator(), L.denominator()] @@ -859,21 +872,21 @@ def Lattes_map(self, E, m): def cartesian_product(self, other): r""" - Return the Cartesian product of the projective spaces ``self`` and + Return the Cartesian product of this projective space and ``other``. INPUT: - - ``other`` - A projective space with the same base ring as ``self`` + - ``other`` - A projective space with the same base ring as this space. OUTPUT: - - A Cartesian product of projective spaces + - A Cartesian product of projective spaces. EXAMPLES:: - sage: P1 = ProjectiveSpace(QQ,1,'x') - sage: P2 = ProjectiveSpace(QQ,2,'y') + sage: P1 = ProjectiveSpace(QQ, 1, 'x') + sage: P2 = ProjectiveSpace(QQ, 2, 'y') sage: PP = P1.cartesian_product(P2); PP Product of projective spaces P^1 x P^2 over Rational Field sage: PP.gens() @@ -929,21 +942,23 @@ def _morphism(self, *args, **kwds): """ return SchemeMorphism_polynomial_projective_space_field(*args, **kwds) - def points_of_bounded_height(self,bound, prec=53): + def points_of_bounded_height(self, bound, prec=53): r""" - Returns an iterator of the points in self of absolute height of at most the given bound. Bound check - is strict for the rational field. Requires self to be projective space over a number field. Uses the - Doyle-Krumm algorithm for computing algebraic numbers up to a given height [Doyle-Krumm]. + Returns an iterator of the points in self of absolute height of at most the given bound. + + Bound check is strict for the rational field. Requires self to be projective space + over a number field. Uses the Doyle-Krumm algorithm for computing algebraic numbers + up to a given height [Doyle-Krumm]. INPUT: - - ``bound`` - a real number + - ``bound`` - a real number. - - ``prec`` - the precision to use to compute the elements of bounded height for number fields + - ``prec`` - the precision to use to compute the elements of bounded height for number fields. OUTPUT: - - an iterator of points in self + - an iterator of points in this space. .. WARNING:: @@ -955,7 +970,7 @@ def points_of_bounded_height(self,bound, prec=53): EXAMPLES:: - sage: P. = ProjectiveSpace(QQ,1) + sage: P. = ProjectiveSpace(QQ, 1) sage: list(P.points_of_bounded_height(5)) [(0 : 1), (1 : 1), (-1 : 1), (1/2 : 1), (-1/2 : 1), (2 : 1), (-2 : 1), (1/3 : 1), (-1/3 : 1), (3 : 1), (-3 : 1), (2/3 : 1), (-2/3 : 1), (3/2 : 1), (-3/2 : 1), (1/4 : 1), @@ -964,7 +979,7 @@ def points_of_bounded_height(self,bound, prec=53): :: sage: u = QQ['u'].0 - sage: P. = ProjectiveSpace(NumberField(u^2 - 2,'v'), 2) + sage: P. = ProjectiveSpace(NumberField(u^2 - 2, 'v'), 2) sage: len(list(P.points_of_bounded_height(1.5))) 57 """ @@ -973,7 +988,7 @@ def points_of_bounded_height(self,bound, prec=53): elif (self.base_ring() in NumberFields()): # true for rational field as well, so check is_RationalField first ftype = True else: - raise NotImplementedError("self must be projective space over a number field.") + raise NotImplementedError("self must be projective space over a number field") bound = bound**(self.base_ring().absolute_degree()) # convert to relative height @@ -1107,8 +1122,8 @@ def __iter__(self): def rational_points(self, F=None): """ - Return the list of `F`-rational points on the affine space self, - where `F` is a given finite field, or the base ring of self. + Return the list of ``F``-rational points on this projective space, + where ``F`` is a given finite field, or the base ring of this space. EXAMPLES:: @@ -1121,7 +1136,7 @@ def rational_points(self, F=None): if F is None: return [ P for P in self ] elif not is_FiniteField(F): - raise TypeError("Second argument (= %s) must be a finite field."%F) + raise TypeError("second argument (= %s) must be a finite field"%F) return [ P for P in self.base_extend(F) ] def rational_points_dictionary(self): @@ -1134,7 +1149,7 @@ def rational_points_dictionary(self): EXAMPLES:: - sage: P1=ProjectiveSpace(GF(7),1,'x') + sage: P1 = ProjectiveSpace(GF(7),1,'x') sage: P1.rational_points_dictionary() {(0 : 1): 0, (1 : 0): 7, @@ -1173,26 +1188,35 @@ def rational_points_dictionary(self): return(D) class ProjectiveSpace_rational_field(ProjectiveSpace_field): - def rational_points(self,bound=0): + def rational_points(self, bound=0): r""" Returns the projective points `(x_0:\cdots:x_n)` over `\QQ` with `|x_i| \leq` bound. - INPUT: + ALGORITHM: + The very simple algorithm works as follows: every point + `(x_0:\cdots:x_n)` in projective space has a unique + largest index `i` for which `x_i` is not + zero. The algorithm then iterates downward on this + index. We normalize by choosing `x_i` positive. Then, + the points `x_0,\ldots,x_{i-1}` are the points of + affine `i`-space that are relatively prime to + `x_i`. We access these by using the Tuples method. - - ``bound`` - integer + INPUT: + - ``bound`` - integer. EXAMPLES:: - sage: PP = ProjectiveSpace(0,QQ) + sage: PP = ProjectiveSpace(0, QQ) sage: PP.rational_points(1) [(1)] - sage: PP = ProjectiveSpace(1,QQ) + sage: PP = ProjectiveSpace(1, QQ) sage: PP.rational_points(2) [(-2 : 1), (-1 : 1), (0 : 1), (1 : 1), (2 : 1), (-1/2 : 1), (1/2 : 1), (1 : 0)] - sage: PP = ProjectiveSpace(2,QQ) + sage: PP = ProjectiveSpace(2, QQ) sage: PP.rational_points(2) [(-2 : -2 : 1), (-1 : -2 : 1), (0 : -2 : 1), (1 : -2 : 1), (2 : -2 : 1), (-2 : -1 : 1), (-1 : -1 : 1), (0 : -1 : 1), (1 : -1 : 1), (2 : -1 : 1), @@ -1206,49 +1230,35 @@ def rational_points(self,bound=0): 0), (1 : 1 : 0), (2 : 1 : 0), (-1/2 : 1 : 0), (1/2 : 1 : 0), (1 : 0 : 0)] - - .. note:: - - The very simple algorithm works as follows: every point - `(x_0:\cdots:x_n)` in projective space has a unique - largest index `i` for which `x_i` is not - zero. The algorithm then iterates downward on this - index. We normalize by choosing `x_i` positive. Then, - the points `x_0,\ldots,x_{i-1}` are the points of - affine `i`-space that are relatively prime to - `x_i`. We access these by using the Tuples method. - AUTHORS: - Benjamin Antieau (2008-01-12) """ if not bound > 0: - raise ValueError("Argument bound (= %s) must be a positive integer.") + raise ValueError("argument bound (= %s) must be a positive integer") n = self.dimension_relative() - - Q = [ k-bound for k in range(2*bound+1) ] # the affine coordinates - R = [ (k+1) for k in range(bound) ] # the projective coordinate - S = [ Tuples(Q,(k+1)) for k in range(n) ] + Q = [k-bound for k in range(2*bound+1)] # the affine coordinates + R = [(k+1) for k in range(bound)] # the projective coordinate + S = [Tuples(Q, (k+1)) for k in range(n)] pts = [] - i=n - + i = n while i > 0: P = [ 0 for _ in range(n+1) ] for ai in R: - P[i]=ai + P[i] = ai for tup in S[i-1]: - if gcd([ai]+tup)==1: + if gcd([ai] + tup) == 1: for j in range(i): - P[j]=tup[j] + P[j] = tup[j] pts.append(self(P)) - i-=1 + i -= 1 # now do i=0; this is treated as a special case so that # we don't have all points (1:0),(2,0),(3,0),etc. - P = [ 0 for _ in range(n+1) ]; P[0]=1 + P = [ 0 for _ in range(n+1) ]; P[0] = 1 pts.append(self(P)) return pts diff --git a/src/sage/schemes/toric/sheaf/__init__.py b/src/sage/schemes/toric/sheaf/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/schemes/toric/sheaf/constructor.py b/src/sage/schemes/toric/sheaf/constructor.py new file mode 100644 index 00000000000..cf2e4a35463 --- /dev/null +++ b/src/sage/schemes/toric/sheaf/constructor.py @@ -0,0 +1,321 @@ +r""" +Construct sheaves on toric varieties. + +A toric vector bundle (on a toric variety) is a vector bundle that is +equivariant with respect to the algebraic torus action. +""" + + +#***************************************************************************** +# Copyright (C) 2013 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.schemes.toric.variety import is_ToricVariety +from sage.modules.filtered_vector_space import FilteredVectorSpace + + +def TangentBundle(X): + r""" + Construct the tangent bundle of a toric variety. + + INPUT: + + - ``X`` -- a toric variety. The base space of the bundle. + + OUTPUT: + + The tangent bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: dP7 = toric_varieties.dP7() + sage: from sage.schemes.toric.sheaf.constructor import TangentBundle + sage: TangentBundle(dP7) + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 5 affine patches. + """ + if not is_ToricVariety(X): + raise ValueError('not a toric variety') + base_ring = X.base_ring() + fan = X.fan() + filtrations = dict() + from sage.modules.filtered_vector_space import FilteredVectorSpace + for i, ray in enumerate(fan.rays()): + F = FilteredVectorSpace(fan.rays(), {0:range(fan.nrays()), 1:[i]}) + filtrations[ray] = F + import klyachko + return klyachko.Bundle(X, filtrations, check=True) + + +def CotangentBundle(X): + r""" + Construct the cotangent bundle of a toric variety. + + INPUT: + + - ``X`` -- a toric variety. The base space of the bundle. + + OUTPUT: + + The cotangent bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: dP7 = toric_varieties.dP7() + sage: from sage.schemes.toric.sheaf.constructor import CotangentBundle + sage: CotangentBundle(dP7) + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 5 affine patches. + """ + return TangentBundle(X).dual() + + +def TrivialBundle(X, rank=1): + r""" + Return the trivial bundle of rank ``r``. + + INPUT: + + - ``X`` -- a toric variety. The base space of the bundle. + + - ``rank`` -- the rank of the bundle. + + OUTPUT: + + The trivial bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: P2 = toric_varieties.P2() + sage: from sage.schemes.toric.sheaf.constructor import TrivialBundle + sage: I3 = TrivialBundle(P2, 3); I3 + Rank 3 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + sage: I3.cohomology(weight=(0,0), dim=True) + (3, 0, 0) + """ + if not is_ToricVariety(X): + raise ValueError('not a toric variety') + from sage.modules.free_module import VectorSpace + base_ring = X.base_ring() + filtrations = dict([ray, FilteredVectorSpace(rank, 0, base_ring=base_ring)] + for ray in X.fan().rays()) + import klyachko + return klyachko.Bundle(X, filtrations, check=True) + + +def LineBundle(X, D): + """ + Construct the rank-1 bundle `O(D)`. + + INPUT: + + - ``X`` -- a toric variety. The base space of the bundle. + + - ``D`` -- a toric divisor. + + OUTPUT: + + The line bundle `O(D)` as a Klyachko bundle of rank 1. + + EXAMPLES:: + + sage: X = toric_varieties.dP8() + sage: D = X.divisor(0) + sage: from sage.schemes.toric.sheaf.constructor import LineBundle + sage: O_D = LineBundle(X, D) + sage: O_D.cohomology(dim=True, weight=(0,0)) + (1, 0, 0) + """ + if not is_ToricVariety(X): + raise ValueError('not a toric variety') + from sage.modules.free_module import VectorSpace + base_ring = X.base_ring() + filtrations = dict([X.fan().ray(i), + FilteredVectorSpace(1, D.function_value(i), base_ring=base_ring)] + for i in range(X.fan().nrays())) + import klyachko + return klyachko.Bundle(X, filtrations, check=True) + + + +class SheafLibrary(object): + + def __init__(self, toric_variety): + """ + Utility object to construct sheaves on toric varieties. + + .. warning:: + + You should never constuct instances manually. Can be + accessed from a toric variety via the + :attr:`sage.schemes.toric.variety.ToricVariety_field.sheaves` + attribute. + + EXAMPLES:: + + sage: type(toric_varieties.P2().sheaves) + + """ + self._variety = toric_variety + + def __repr__(self): + """ + Return a string representation. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: toric_varieties.P2().sheaves # indirect doctest + Sheaf constructor on 2-d CPR-Fano toric variety covered by 3 affine patches + """ + return 'Sheaf constructor on ' + repr(self._variety) + + def trivial_bundle(self, rank=1): + r""" + Return the trivial bundle of rank ``r``. + + INPUT: + + - ``rank`` -- integer (optional; default: `1`). The rank of + the bundle. + + OUTPUT: + + The trivial bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: P2 = toric_varieties.P2() + sage: I3 = P2.sheaves.trivial_bundle(3); I3 + Rank 3 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + sage: I3.cohomology(weight=(0,0), dim=True) + (3, 0, 0) + """ + return TrivialBundle(self._variety, rank) + + def line_bundle(self, divisor): + """ + Construct the rank-1 bundle `O(D)`. + + INPUT: + + - ``divisor`` -- a toric divisor. + + OUTPUT: + + The line bundle `O(D)` for the given divisor as a Klyachko + bundle of rank 1. + + EXAMPLES:: + + sage: X = toric_varieties.dP8() + sage: D = X.divisor(0) + sage: O_D = X.sheaves.line_bundle(D) + sage: O_D.cohomology(dim=True, weight=(0,0)) + (1, 0, 0) + """ + return LineBundle(self._variety, divisor) + + def tangent_bundle(self): + r""" + Return the tangent bundle of the toric variety. + + OUTPUT: + + The tangent bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: toric_varieties.dP6().sheaves.tangent_bundle() + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 6 affine patches. + """ + return TangentBundle(self._variety) + + def cotangent_bundle(self): + r""" + Return the cotangent bundle of the toric variety. + + OUTPUT: + + The cotangent bundle as a Klyachko bundle. + + EXAMPLES:: + + sage: dP6 = toric_varieties.dP6() + sage: TX = dP6.sheaves.tangent_bundle() + sage: TXdual = dP6.sheaves.cotangent_bundle() + sage: TXdual == TX.dual() + True + """ + return CotangentBundle(self._variety) + + def Klyachko(self, multi_filtration): + """ + Construct a Klyachko bundle (sheaf) from filtration data. + + INPUT: + + - ``multi_filtration`` -- a multi-filtered vectors space with + multiple filtrations being indexed by the rays of the + fan. Either an instance of + :func:`~sage.modules.multi_filtered_vector_space.MultiFilteredVectorSpace` + or something (like a dictionary of ordinary filtered vector + spaces). + + OUTPUT: + + The Klyachko bundle defined by the filtrations, one for each + ray, of a vector space. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: v1, v2, v3 = [(1,0,0),(0,1,0),(0,0,1)] + sage: F1 = FilteredVectorSpace({1:[v1, v2, v3], 3:[v1]}) + sage: F2 = FilteredVectorSpace({0:[v1, v2, v3], 2:[v2, v3]}) + sage: P1 = toric_varieties.P1() + sage: r1, r2 = P1.fan().rays() + sage: F = MultiFilteredVectorSpace({r1:F1, r2:F2}); F + Filtrations + N(-1): QQ^3 >= QQ^2 >= QQ^2 >= 0 >= 0 + N(1): QQ^3 >= QQ^3 >= QQ^1 >= QQ^1 >= 0 + sage: P1.sheaves.Klyachko(F) + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + """ + from klyachko import Bundle + return Bundle(self._variety, multi_filtration, check=True) + + def divisor(self, *args, **kwds): + """ + Return a toric divisor. + + INPUT: + + This is just an alias for + :meth:`sage.schemes.toric.variety.ToricVariety_field.divisor`, + see there for details. + + By abuse of notation, you can usually use the divisor `D` + interchangeably with the line bundle `O(D)`. + + OUTPUT: + + A toric divisor. + + EXAMPLES:: + + sage: dP6 = toric_varieties.dP6() + sage: dP6.inject_variables() + Defining x, u, y, v, z, w + sage: D = dP6.sheaves.divisor(x*u^3); D + V(x) + 3*V(u) + sage: D == dP6.divisor(x*u^3) + True + """ + return self._variety.divisor(*args, **kwds) diff --git a/src/sage/schemes/toric/sheaf/klyachko.py b/src/sage/schemes/toric/sheaf/klyachko.py new file mode 100644 index 00000000000..09a97328cce --- /dev/null +++ b/src/sage/schemes/toric/sheaf/klyachko.py @@ -0,0 +1,965 @@ +""" +Klyachko Bundles and Sheaves. + +Klyachko bundles are torus-equivariant bundles on toric +varieties. That is, the action of the maximal torus on the toric +variety lifts to an action on the bundle. There is an equivalence of +categories between [Klyachko]_ bundles and multiple filtrations (one for +each ray of the fan) of a vector space. The multi-filtrations are +implemented in :mod:`sage.modules.multi_filtered_vector_space`. + +EXAMPLES:: + + sage: X = toric_varieties.dP6xdP6() + sage: TX = X.sheaves.tangent_bundle() + sage: Alt2TX = TX.exterior_power(2); Alt2TX + Rank 6 bundle on 4-d CPR-Fano toric variety covered by 36 affine patches. + + sage: K = X.sheaves.line_bundle(X.K()) + sage: antiK = X.sheaves.line_bundle(-X.K()) + sage: (Alt2TX * K).cohomology(dim=True, weight=(0,0,0,0)) # long time + (0, 0, 18, 0, 0) + + sage: G_sum = TX + X.sheaves.trivial_bundle(2) + sage: V_sum = G_sum.wedge(2) * K # long time + sage: V_sum.cohomology(dim=True, weight=(0,0,0,0)) # long time + (0, 0, 18, 16, 1) + sage: Gtilde = G_sum.random_deformation() + sage: V = Gtilde.wedge(2) * K # long time + sage: V.cohomology(dim=True, weight=(0,0,0,0)) # long time + (0, 0, 3, 0, 0) + +REFERENCES: + +.. [Klyachko] + Klyachko, Aleksandr Anatolevich: + Equivariant Bundles on Toral Varieties, + Math USSR Izv. 35 (1990), 337-375. + http://iopscience.iop.org/0025-5726/35/2/A04/pdf/0025-5726_35_2_A04.pdf + +.. [BirknerIltenPetersen] + Rene Birkner, Nathan Owen Ilten, and Lars Petersen: + Computations with equivariant toric vector bundles, + The Journal of Software for Algebra and Geometry: Macaulay2. + http://msp.org/jsag/2010/2-1/p03.xhtml + http://www.math.uiuc.edu/Macaulay2/doc/Macaulay2-1.8.2/share/doc/Macaulay2/ToricVectorBundles/html/ +""" + +#***************************************************************************** +# Copyright (C) 2013 Volker Braun +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + + +from sage.structure.all import SageObject +from sage.rings.all import QQ, ZZ +from sage.misc.all import uniq, cached_method +from sage.matrix.constructor import vector, matrix, block_matrix, zero_matrix +from sage.geometry.cone import is_Cone, IntegralRayCollection + +from sage.modules.filtered_vector_space import FilteredVectorSpace, is_FilteredVectorSpace +from sage.modules.multi_filtered_vector_space import MultiFilteredVectorSpace + + +def is_KlyachkoBundle(X): + """ + Test whether ``X`` is a Klyachko bundle + + INPUT: + + - ``X`` -- anything. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.schemes.toric.sheaf.klyachko import is_KlyachkoBundle + sage: is_KlyachkoBundle('test') + False + """ + return isinstance(X, KlyachkoBundle_class) + + +def Bundle(toric_variety, multi_filtration, check=True): + r""" + Construct a Klyacho bundle + + INPUT: + + - ``toric_variety`` -- a toric variety. The base space of the bundle. + + - ``multi_filtration`` -- a multi-filtered vectors space with + multiple filtrations being indexed by the one-dimensional cones + of the fan. Either an instance of + :func:`~sage.modules.multi_filtered_vector_space.MultiFilteredVectorSpace` + or something (like a dictionary of ordinary filtered vector + spaces). + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: v1, v2, v3 = [(1,0,0),(0,1,0),(0,0,1)] + sage: F1 = FilteredVectorSpace({1:[v1, v2, v3], 3:[v1]}) + sage: F2 = FilteredVectorSpace({0:[v1, v2, v3], 2:[v2, v3]}) + sage: P1 = toric_varieties.P1() + sage: r1, r2 = P1.fan().rays() + sage: F = MultiFilteredVectorSpace({r1:F1, r2:F2}); F + Filtrations + N(-1): QQ^3 >= QQ^2 >= QQ^2 >= 0 >= 0 + N(1): QQ^3 >= QQ^3 >= QQ^1 >= QQ^1 >= 0 + + You should use the + :meth:`~sage.schemes.toric.sheaf.constructor.SheafLibrary.Klyachko` + method to construct instances:: + + sage: P1.sheaves.Klyachko(F) + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + + sage: P1.sheaves.Klyachko({r1:F1, r2:F2}) # alternative + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + + The above is just a shorthand for:: + + sage: from sage.schemes.toric.sheaf.klyachko import Bundle + sage: Bundle(P1, F) + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + """ + base_ring = toric_variety.base_ring() + if not hasattr(multi_filtration, 'get_filtration'): + # try to construct a MultiFilteredVectorSpace + multi_filtration = MultiFilteredVectorSpace( + multi_filtration, base_ring=base_ring, check=check) + if multi_filtration.base_ring() != base_ring: + multi_filtration = multi_filtration.change_ring(base_ring) + return KlyachkoBundle_class(toric_variety, multi_filtration, check=check) + + + +class KlyachkoBundle_class(SageObject): + + def __init__(self, toric_variety, multi_filtration, check=True): + r""" + A toric bundle using Klyachko's representation. + + .. warning:: + + You should always use the :func:`Bundle` factory function + to construct instances. + + INPUT: + + - ``toric_variety`` -- a toric variety. The base space of the bundle. + + - ``multi_filtration`` -- a + :func:`~sage.modules.multi_filtered_vector_space.MultiFilteredVectorSpace` + with index set the rays of the fan. + + - ``check`` -- boolean (default: ``True``). Whether to perform + consistency checks. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: r1, r2 = P1.fan().rays() + sage: F = MultiFilteredVectorSpace({ + ....: r1:FilteredVectorSpace(3,1), + ....: r2:FilteredVectorSpace(3,0)}); F + Filtrations + N(-1): QQ^3 >= 0 >= 0 + N(1): QQ^3 >= QQ^3 >= 0 + sage: from sage.schemes.toric.sheaf.klyachko import Bundle + sage: Bundle(P1, F) + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + """ + self._variety = toric_variety + self._filt = multi_filtration + if not check: return + from sage.sets.set import Set + if multi_filtration.index_set() != Set(list(toric_variety.fan().rays())): + raise ValueError('the index set of the multi-filtration must be' + ' all rays of the fan.') + if not multi_filtration.is_exhaustive(): + raise ValueError('multi-filtration must be exhaustive') + if not multi_filtration.is_separating(): + raise ValueError('multi-filtration must be separating') + + def variety(self): + r""" + Return the base toric variety. + + OUTPUT: + + A toric variety. + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: V = X.sheaves.tangent_bundle(); V + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + sage: V.variety() is X + True + """ + return self._variety + + def base_ring(self): + r""" + Return the base field. + + OUTPUT: + + A field. + + EXAMPLES:: + + sage: T_P2 = toric_varieties.P2().sheaves.tangent_bundle() + sage: T_P2.base_ring() + Rational Field + """ + return self._filt.base_ring() + + def fiber(self): + r""" + Return the generic fiber of the vector bundle. + + OUTPUT: + + A vector space over :meth:`base_ring`. + + EXAMPLES:: + + sage: T_P2 = toric_varieties.P2().sheaves.tangent_bundle() + sage: T_P2.fiber() + Vector space of dimension 2 over Rational Field + """ + from sage.modules.all import VectorSpace + return VectorSpace(self.base_ring(), self.rank()) + + def rank(self): + r""" + Return the rank of the vector bundle. + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: T_P2 = toric_varieties.P2().sheaves.tangent_bundle() + sage: T_P2.rank() + 2 + """ + return self._filt.dimension() + + def _repr_(self): + r""" + Return a string representation. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: toric_varieties.P2().sheaves.tangent_bundle() + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + """ + s = 'Rank '+str(self.rank())+' bundle on '+str(self._variety)+'.' + return s + + def get_filtration(self, ray=None): + r""" + Return the filtration associated to the ``ray``. + + INPUT: + + - ``ray`` -- Integer, a `N`-lattice point, a one-dimensional + cone, or ``None`` (default). Specifies a ray of the fan of + the toric variety, either via its index or its generator. + + OUTPUT: + + The filtered vector space associated to the given ``ray``. If + no ray is specified, all filtrations are returned. + + EXAMPLES:: + + sage: TX = toric_varieties.dP6().sheaves.tangent_bundle() + sage: TX.get_filtration(0) + QQ^2 >= QQ^1 >= 0 + sage: TX.get_filtration([-1, -1]) + QQ^2 >= QQ^1 >= 0 + sage: TX.get_filtration(TX.variety().fan(1)[0]) + QQ^2 >= QQ^1 >= 0 + sage: TX.get_filtration() + Filtrations + N(-1, -1): QQ^2 >= QQ^1 >= 0 + N(-1, 0): QQ^2 >= QQ^1 >= 0 + N(0, -1): QQ^2 >= QQ^1 >= 0 + N(0, 1): QQ^2 >= QQ^1 >= 0 + N(1, 0): QQ^2 >= QQ^1 >= 0 + N(1, 1): QQ^2 >= QQ^1 >= 0 + """ + if ray is None: + return self._filt + X = self.variety() + fan = X.fan() + if is_Cone(ray): + if ray.dim() != 1: + raise ValueError('not a one-dimensional cone') + ray = ray.ray(0) + elif ray in ZZ: + ray = fan.ray(ray) + else: + N = fan.lattice() + ray = N(ray) + ray.set_immutable() + return self._filt.get_filtration(ray) + + def get_degree(self, ray, i): + r""" + Return the vector subspace ``E^\alpha(i)``. + + - ``ray`` -- Integer, a `N`-lattice point, a one-dimensional + cone, or ``None`` (default). Specifies a ray of the fan of + the toric variety, either via its index or its generator. + + - ``i`` -- integer. The filtration degree. + + OUTPUT: + + A subspace of the :meth:`fiber` vector space. The defining + data of a Klyachko bundle. + + EXAMPLES:: + + sage: TX = toric_varieties.dP6().sheaves.tangent_bundle() + sage: TX.get_degree(0, 1) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [0 1] + """ + return self.get_filtration(ray).get_degree(i) + + def filtration_intersection(self, sigma, i): + r""" + Return the intersection of the filtered subspaces. + + INPUT: + + - ``sigma`` -- a cone of the fan of the base toric variety. + + - ``i`` -- integer. The filtration degree. + + OUPUT: + + Let the cone be spanned by the rays `\sigma=\langle r_1,\dots, + r_k\rangle`. This method returns the intersection + + .. math:: + + \bigcap_{r\in \{r_1,\dots,r_k\}} + E^{r}(i) + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: fan = X.fan() + sage: V = X.sheaves.tangent_bundle() + sage: V.filtration_intersection(fan(1)[0], 1) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.filtration_intersection(fan(2)[0], 1) + Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + """ + sigma = self._variety.fan().embed(sigma) + V = self.fiber() + for alpha in sigma.ambient_ray_indices(): + V = V.intersection(self.get_degree(alpha, i)) + return V + + def E_degree(self, alpha, m): + r""" + Return the vector subspace `E^\alpha(m)`. + + INPUT: + + - ``alpha`` -- a ray of the fan. Can be specified by its index + (an integer), a one-dimensional cone, or a `N`-lattice + point. + + - ``m`` -- tuple of integers or `M`-lattice point. A point in + the dual lattice of the fan. + + OUTPUT: + + The subspace $E^\alpha(\alpha m)$ of the filtration indexed by + the ray $\alpha$ and at the filtration degree $\alpha * m$ + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: M = X.fan().dual_lattice() + sage: V = X.sheaves.tangent_bundle() + sage: V.E_degree(X.fan().ray(0), (1,0)) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.E_degree(X.fan(1)[0], (1,0)) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.E_degree(0, (1,0)) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + """ + fan = self.variety().fan() + N = fan.lattice() + M = fan.dual_lattice() + m = M(m) + if alpha in ZZ: + ray = fan.ray(alpha) + elif alpha in N: + ray = alpha + else: + cone = fan.cone_containing(alpha) + if cone.dim() != 1: + raise ValueError('does not determine one-dimensional cone') + ray = cone.ray(0) + return self.get_degree(ray, ray*m) + + @cached_method + def E_intersection(self, sigma, m): + r""" + Return the vector subspace ``E^\sigma(m)``. + + See [Klyachko]_, equation 4.1. + + INPUT: + + - ``sigma`` -- a cone of the fan of the base toric variety. + + - ``m`` -- tuple of integers or `M`-lattice point. A point in + the dual lattice of the fan. Must be immutable. + + OUPUT: + + The subspace `E^\sigma(m)` + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: fan = X.fan() + sage: V = X.sheaves.tangent_bundle() + sage: V.E_intersection(fan(1)[0], (1,0)) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.E_intersection(fan(2)[0], (-1,1)) + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [0 1] + + For the empty cone, this is always the whole vector space:: + + sage: V.E_intersection(fan(0)[0], (1,0)) + Vector space of dimension 2 over Rational Field + """ + sigma = self._variety.fan().embed(sigma) + V = self.fiber() + for alpha in sigma.rays(): + V = V.intersection(self.E_degree(alpha, m)) + return V + + @cached_method + def E_quotient(self, sigma, m): + r""" + Return the vector space quotient `E_\sigma(m)`. + + See [Klyachko]_, equation 4.1. + + INPUT: + + - ``sigma`` -- a cone of the fan of the base toric variety. + + - ``m`` -- tuple of integers or `M`-lattice point. A point in + the dual lattice of the fan. Must be immutable. + + OUPUT: + + The subspace `E_\sigma(m)` + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: fan = X.fan() + sage: M = fan.dual_lattice() + sage: cone = fan(1)[0] + sage: V = X.sheaves.tangent_bundle() + sage: m = M(1, 0) + sage: m.set_immutable() + sage: V.E_quotient(cone, m) + Vector space quotient V/W of dimension 1 over Rational Field where + V: Vector space of dimension 2 over Rational Field + W: Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + sage: V.E_quotient(fan(2)[0], (-1,1)) + Vector space quotient V/W of dimension 0 over Rational Field where + V: Vector space of dimension 2 over Rational Field + W: Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + """ + sigma = self._variety.fan().embed(sigma) + V = self.fiber() + generators = [] + for alpha in sigma.rays(): + generators.extend(self.E_degree(alpha, m).gens()) + return V.quotient(V.span(generators)) + + @cached_method + def E_quotient_projection(self, sigma, tau, m): + r""" + Return the projection map `E_\sigma(m) \to E_\tau(m)` where + `\sigma` is a face of `\tau`. + + INPUT: + + - ``sigma`` -- a cone of the fan of the base toric variety. + + - ``tau`` -- a cone of the fan containing ``sigma``. + + - ``m`` -- tuple of integers or `M`-lattice point. A point in + the dual lattice of the fan. Must be immutable. + + OUTPUT: + + The restriction map + + .. math:: + + E_\sigma(m) \to E_\tau(m) + + EXAMPLES:: + + sage: P3 = toric_varieties.P(3) + sage: rays = [(1,0,0), (0,1,0), (0,0,1)] + sage: F1 = FilteredVectorSpace(rays, {0:[0], 1:[2], 2:[1]}) + sage: F2 = FilteredVectorSpace(3, 0) + sage: r = P3.fan().rays() + sage: V = P3.sheaves.Klyachko({r[0]:F1, r[1]:F2, r[2]:F2, r[3]:F2}) + sage: tau = Cone([(1,0,0), (0,1,0)]) + sage: sigma = Cone([(1,0,0)]) + sage: M = P3.fan().dual_lattice() + sage: m = M(2,1,0) + sage: m.set_immutable() + sage: V.E_quotient(sigma, m) + Vector space quotient V/W of dimension 2 over Rational Field where + V: Vector space of dimension 3 over Rational Field + W: Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [0 1 0] + sage: V.E_quotient(tau, m) + Vector space quotient V/W of dimension 2 over Rational Field where + V: Vector space of dimension 3 over Rational Field + W: Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [0 1 0] + sage: V.E_quotient_projection(sigma, tau, m) + Vector space morphism represented by the matrix: + [1 0] + [0 1] + Domain: Vector space quotient V/W of dimension 2 over Rational Field where + V: Vector space of dimension 3 over Rational Field + W: Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [0 1 0] + Codomain: Vector space quotient V/W of dimension 2 over Rational Field where + V: Vector space of dimension 3 over Rational Field + W: Vector space of degree 3 and dimension 1 over Rational Field + Basis matrix: + [0 1 0] + """ + if not sigma.is_face_of(tau): + raise ValueError('the cone sigma is not a face of the cone tau') + E_sigma = self.E_quotient(sigma, m) + E_tau = self.E_quotient(tau, m) + images = [E_tau(E_sigma.lift(g)) for g in E_sigma.gens()] + return E_sigma.hom(images, codomain=E_tau) + + def cohomology_complex(self, m): + r""" + Return the "cohomology complex" `C^*(m)` + + See [Klyachko]_, equation 4.2. + + INPUT: + + - ``m`` -- tuple of integers or `M`-lattice point. A point in + the dual lattice of the fan. Must be immutable. + + OUTPUT: + + The "cohomology complex" as a chain complex over the + :meth:`base_ring`. + + EXAMPLES:: + + sage: P3 = toric_varieties.P(3) + sage: rays = [(1,0,0), (0,1,0), (0,0,1)] + sage: F1 = FilteredVectorSpace(rays, {0:[0], 1:[2], 2:[1]}) + sage: F2 = FilteredVectorSpace(rays, {0:[1,2], 1:[0]}) + sage: r = P3.fan().rays() + sage: V = P3.sheaves.Klyachko({r[0]:F1, r[1]:F2, r[2]:F2, r[3]:F2}) + sage: tau = Cone([(1,0,0), (0,1,0)]) + sage: sigma = Cone([(1, 0, 0)]) + sage: M = P3.fan().dual_lattice() + sage: m = M(1, 1, 0); m.set_immutable() + sage: V.cohomology_complex(m) + Chain complex with at most 2 nonzero terms over Rational Field + + sage: F = CyclotomicField(3) + sage: P3 = toric_varieties.P(3).change_ring(F) + sage: V = P3.sheaves.Klyachko({r[0]:F1, r[1]:F2, r[2]:F2, r[3]:F2}) + sage: V.cohomology_complex(m) + Chain complex with at most 2 nonzero terms over Cyclotomic + Field of order 3 and degree 2 + """ + fan = self._variety.fan() + C = fan.complex() + CV = [] + F = self.base_ring() + for dim in range(1,fan.dim()+1): + codim = fan.dim() - dim + d_C = C.differential(codim) + d_V = [] + for j in range(0, d_C.ncols()): + tau = fan(dim)[j] + d_V_row = [] + for i in range(0, d_C.nrows()): + sigma = fan(dim-1)[i] + if sigma.is_face_of(tau): + pr = self.E_quotient_projection(sigma, tau, m) + d = d_C[i,j] * pr.matrix().transpose() + else: + E_sigma = self.E_quotient(sigma, m) + E_tau = self.E_quotient(tau, m) + d = zero_matrix(F, E_tau.dimension(), E_sigma.dimension()) + d_V_row.append(d) + d_V.append(d_V_row) + # print dim, ':\n', d_V, '\n' + d_V = block_matrix(d_V, ring=F) + CV.append(d_V) + # print dim, ': ', d_V.nrows(), 'x', d_V.ncols(), '\n', d_V + from sage.homology.chain_complex import ChainComplex + return ChainComplex(CV, base_ring=self.base_ring()) + + def cohomology(self, degree=None, weight=None, dim=False): + r""" + Return the bundle cohomology groups. + + INPUT: + + - ``degree`` -- ``None`` (default) or an integer. The degree of + the cohomology group. + + - ``weight`` -- ``None`` (default) or a tuple of integers or a + `M`-lattice point. A point in the dual lattice of the fan + defining a torus character. The weight of the cohomology + group. + + - ``dim`` -- Boolean (default: ``False``). Whether to return + vector spaces or only their dimension. + + OUTPUT: + + The cohomology group of given cohomological ``degree`` and + torus ``weight``. + + * If no ``weight`` is specified, the unweighted group (sum + over all weights) is returned. + + * If no ``degree`` is specified, a dictionary whose keys are + integers and whose values are the cohomology groups is + returned. If, in addition, ``dim=True``, then an integral + vector of the dimensions is returned. + + EXAMPLES:: + + sage: V = toric_varieties.P2().sheaves.tangent_bundle() + sage: V.cohomology(degree=0, weight=(0,0)) + Vector space of dimension 2 over Rational Field + sage: V.cohomology(weight=(0,0), dim=True) + (2, 0, 0) + sage: for i,j in cartesian_product((range(-2,3), range(-2,3))): + ....: HH = V.cohomology(weight=(i,j), dim=True) + ....: if HH.is_zero(): continue + ....: print 'H^*i(P^2, TP^2)_M('+str(i)+','+str(j)+') =', HH + H^*i(P^2, TP^2)_M(-1,0) = (1, 0, 0) + H^*i(P^2, TP^2)_M(-1,1) = (1, 0, 0) + H^*i(P^2, TP^2)_M(0,-1) = (1, 0, 0) + H^*i(P^2, TP^2)_M(0,0) = (2, 0, 0) + H^*i(P^2, TP^2)_M(0,1) = (1, 0, 0) + H^*i(P^2, TP^2)_M(1,-1) = (1, 0, 0) + H^*i(P^2, TP^2)_M(1,0) = (1, 0, 0) + """ + from sage.modules.all import FreeModule + if weight is None: + raise NotImplementedError('sum over weights is not implemented') + else: + weight = self.variety().fan().dual_lattice()(weight) + weight.set_immutable() + if degree is not None: + return self.cohomology(weight=weight, dim=dim)[degree] + C = self.cohomology_complex(weight) + space_dim = self._variety.dimension() + C_homology = C.homology() + HH = dict() + for d in range(0, space_dim+1): + try: + HH[d] = C_homology[d] + except KeyError: + HH[d] = FreeModule(self.base_ring(), 0) + if dim: + HH = vector(ZZ, [HH[i].rank() for i in range(0, space_dim+1) ]) + return HH + + def __cmp__(self, other): + """ + Compare ``self`` and ``other`` + + .. warning:: + + This method tests whether the underlying representation is + the same. Use :meth:`is_isomorphic` to test for + mathematical equivalence. + + INPUT: + + - ``other`` -- anything. + + OUTPUT: + + `-1`, `0`, or `+1`. + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: V1 = X.sheaves.trivial_bundle(1) + sage: V2 = X.sheaves.trivial_bundle(2) + sage: abs(cmp(V2, V1)) + 1 + sage: cmp(V2, V1+V1) + 0 + + sage: T_X = X.sheaves.tangent_bundle() + sage: O_X = X.sheaves.trivial_bundle(1) + sage: T_X + O_X == O_X + T_X + False + """ + c = cmp(type(self), type(other)) + if c!=0: return c + c = cmp(self.variety(), other.variety()) + if c!=0: return c + c = cmp(self._filt, other._filt) + if c!=0: return c + return 0 + + def is_isomorphic(self, other): + """ + Test whether two bundles are isomorphic. + + INPUT: + + - ``other`` -- anything. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: T_X = X.sheaves.tangent_bundle() + sage: O_X = X.sheaves.trivial_bundle(1) + sage: T_X + O_X == O_X + T_X + False + sage: (T_X + O_X).is_isomorphic(O_X + T_X) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def direct_sum(self, other): + """ + Return the sum of two vector bundles. + + INPUT: + + - ``other`` -- a Klyachko bundle over the same base. + + OUTPUT: + + The direct sum as a new Klyachko bundle. + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: V1 = X.sheaves.trivial_bundle(1) + sage: V2 = X.sheaves.trivial_bundle(2) + sage: V2.direct_sum(V1) + Rank 3 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + + sage: V1 = X.sheaves.trivial_bundle(1) + sage: V2 = X.sheaves.trivial_bundle(2) + sage: V2 == V1 + V1 + True + """ + if not self.variety() == other.variety(): + raise ValueError('the bundles must be over the same base toric variety') + filt = self._filt + other._filt + return self.__class__(self.variety(), filt, check=True) + + __add__ = direct_sum + + def tensor_product(self, other): + """ + Return the sum of two vector bundles. + + INPUT: + + - ``other`` -- a Klyachko bundle over the same base. + + OUTPUT: + + The tensor product as a new Klyachko bundle. + + EXAMPLES:: + + sage: X = toric_varieties.P2() + sage: OX = X.sheaves.trivial_bundle(1) + sage: X.sheaves.tangent_bundle().tensor_product(OX) + Rank 2 bundle on 2-d CPR-Fano toric variety covered by 3 affine patches. + sage: OX == OX * OX + True + """ + if not self.variety() == other.variety(): + raise ValueError('the bundles must be over the same base toric variety') + filt = self._filt * other._filt + return self.__class__(self.variety(), filt, check=True) + + __mul__ = tensor_product + + def exterior_power(self, n): + """ + Return the `n`-th exterior power. + + INPUT: + + - ``n`` -- integer. + + OUTPUT: + + The `n`-th exterior power `\wedge_{i=1}^n V` of the bundle `V` + as a new Klyachko bundle. + + EXAMPLES:: + + sage: X = toric_varieties.P2_123() + sage: TX = X.sheaves.tangent_bundle() + sage: antiK = X.sheaves.line_bundle(-X.K()) + sage: TX.exterior_power(2) == antiK + True + sage: TX.wedge(2) == antiK # alias + True + """ + filt = self._filt.exterior_power(n) + return self.__class__(self.variety(), filt, check=True) + + wedge = exterior_power + + def symmetric_power(self, n): + """ + Return the `n`-th symmetric power. + + INPUT: + + - ``n`` -- integer. + + OUTPUT: + + The `n`-th symmetric power as a new Klyachko bundle. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: H = P1.divisor(0) + sage: L = P1.sheaves.line_bundle(H) + sage: (L+L).symmetric_power(2) + Rank 3 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + sage: (L+L).symmetric_power(2) == L*L+L*L+L*L + True + """ + filt = self._filt.symmetric_power(n) + return self.__class__(self.variety(), filt, check=True) + + def dual(self): + """ + Return the dual bundle. + + OUTPUT: + + The dual bundle as a new Klyachko bundle. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: H = P1.divisor(0) + sage: L = P1.sheaves.line_bundle(H) + sage: L.dual() + Rank 1 bundle on 1-d CPR-Fano toric variety covered by 2 affine patches. + sage: L.dual() == P1.sheaves.line_bundle(-H) + True + """ + filt = self._filt.dual() + return self.__class__(self.variety(), filt, check=True) + + def random_deformation(self, epsilon=None): + """ + Return a generic torus-equivariant deformation of the bundle. + + INPUT: + + - ``epsilon`` -- an element of the base ring. Scales the + random deformation. + + OUTPUT: + + A new Klyachko bundle with randomly perturbed moduly. In + particular, the same Chern classes. + + EXAMPLES:: + + sage: P1 = toric_varieties.P1() + sage: H = P1.divisor(0) + sage: V = P1.sheaves.line_bundle(H) + P1.sheaves.line_bundle(-H) + sage: V.cohomology(dim=True, weight=(0,)) + (1, 0) + sage: Vtilde = V.random_deformation() + sage: Vtilde.cohomology(dim=True, weight=(0,)) + (1, 0) + """ + filt = self._filt.random_deformation(epsilon) + return self.__class__(self.variety(), filt, check=True) diff --git a/src/sage/schemes/toric/variety.py b/src/sage/schemes/toric/variety.py index 9b223bb152e..7b4ef03745c 100644 --- a/src/sage/schemes/toric/variety.py +++ b/src/sage/schemes/toric/variety.py @@ -2145,6 +2145,25 @@ def integrate(self, cohomology_class): if top_form.is_zero(): return 0 return top_form.lc() / self.volume_class().lc() + @property + def sheaves(self): + r""" + Return the factory object for sheaves on the toric variety. + + See :class:`sage.schemes.toric.sheaf.constructor.SheafLibrary` + for details. + + EXAMPLES:: + + sage: dP6 = toric_varieties.dP6() + sage: dP6.sheaves + Sheaf constructor on 2-d CPR-Fano toric variety covered by 6 affine patches + sage: dP6.sheaves.trivial_bundle() + Rank 1 bundle on 2-d CPR-Fano toric variety covered by 6 affine patches. + """ + from sage.schemes.toric.sheaf.constructor import SheafLibrary + return SheafLibrary(self) + @cached_method def Chern_class(self, deg=None): """ diff --git a/src/sage/structure/element.pxd b/src/sage/structure/element.pxd index 47b0903e391..87b586c7d80 100644 --- a/src/sage/structure/element.pxd +++ b/src/sage/structure/element.pxd @@ -37,7 +37,6 @@ cdef class Element(SageObject): cdef Parent _parent cpdef _richcmp_(left, Element right, int op) cpdef int _cmp_(left, Element right) except -2 - cdef _set_parent_c(self, Parent parent) cpdef base_extend(self, R) cpdef _act_on_(self, x, bint self_on_left) @@ -78,6 +77,7 @@ cdef class AdditiveGroupElement(ModuleElement): cdef class RingElement(ModuleElement): cpdef RingElement _mul_(self, RingElement right) cpdef RingElement _div_(self, RingElement right) + cpdef RingElement _floordiv_(self, RingElement right) cdef RingElement _add_long(self, long n) diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 51b02cc1192..a64dd81de81 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -43,8 +43,6 @@ abstract base classes. CommutativeAlgebra ??? (should be removed from element.pxd) Matrix InfinityElement - PlusInfinityElement - MinusInfinityElement AdditiveGroupElement Vector @@ -137,8 +135,13 @@ from cpython.ref cimport PyObject from cpython.number cimport PyNumber_TrueDivide import types -cdef add, sub, mul, div, truediv, iadd, isub, imul, idiv -from operator import add, sub, mul, div, truediv, iadd, isub, imul, idiv +cdef add, sub, mul, div, truediv, floordiv +cdef iadd, isub, imul, idiv, itruediv, ifloordiv +from operator import (add, sub, mul, div, truediv, floordiv, + iadd, isub, imul, idiv, itruediv, ifloordiv) +cdef dict _coerce_op_symbols = dict( + add='+', sub='-', mul='*', div='/', truediv='/', floordiv='//', + iadd='+', isub='-', imul='*', idiv='/', itruediv='/', ifloordiv='//') cdef MethodType from types import MethodType @@ -263,8 +266,6 @@ def have_same_parent(left, right): return have_same_parent_c(left, right) -cdef dict _coerce_op_symbols = {'mul':'*', 'add':'+', 'sub':'-', 'div':'/', 'imul': '*', 'iadd': '+', 'isub':'-', 'idiv':'/'} - cdef str arith_error_message(x, y, op): name = op.__name__ try: @@ -320,14 +321,6 @@ cdef class Element(SageObject): """ self._parent = parent - cdef _set_parent_c(self, Parent parent): - self._parent = parent - - def _make_new_with_parent_c(self, Parent parent): - self._parent = parent - return self - - def __getattr__(self, str name): """ Lookup a method or attribute from the category abstract classes. @@ -1920,7 +1913,7 @@ cdef class RingElement(ModuleElement): EXAMPLES:: - 1/3*pisage: operator.truediv(2, 3) + sage: operator.truediv(2, 3) 2/3 sage: operator.truediv(pi, 3) 1/3*pi @@ -1929,6 +1922,26 @@ cdef class RingElement(ModuleElement): return (self)._div_(right) return coercion_model.bin_op(self, right, truediv) + def __itruediv__(self, right): + """ + Top-level in-place true division operator for ring elements. + See extensive documentation at the top of element.pyx. + + If two elements have the same parent, we just call ``_div_`` + because all divisions of Sage elements are really true + divisions. + + EXAMPLES:: + + sage: operator.itruediv(2, 3) + 2/3 + sage: operator.itruediv(pi, 3) + 1/3*pi + """ + if have_same_parent_c(self, right): + return (self)._div_(right) + return coercion_model.bin_op(self, right, itruediv) + def __div__(self, right): """ Top-level division operator for ring elements. @@ -1960,6 +1973,58 @@ cdef class RingElement(ModuleElement): return (self)._div_(right) return coercion_model.bin_op(self, right, idiv) + def __floordiv__(self, right): + """ + Top-level floor division operator for ring elements. + See extensive documentation at the top of element.pyx. + + EXAMPLES:: + + sage: 7 // 3 + 2 + sage: 7 // int(3) + 2 + sage: int(7) // 3 + 2 + sage: p = Parent() + sage: e = RingElement(p) + sage: e // e + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for '//': '' and '' + """ + if have_same_parent_c(self, right): + return (self)._floordiv_(right) + return coercion_model.bin_op(self, right, floordiv) + + cpdef RingElement _floordiv_(self, RingElement right): + """ + Cython classes should override this function to implement floor + division. See extensive documentation at the top of element.pyx. + + EXAMPLES:: + + sage: 23._floordiv_(5) + 4 + """ + raise TypeError(arith_error_message(self, right, floordiv)) + + def __ifloordiv__(self, right): + """ + Top-level in-place floor division operator for ring elements. + See extensive documentation at the top of element.pyx. + + EXAMPLES:: + + sage: x = 23 + sage: x //= 7 + sage: x + 3 + """ + if have_same_parent_c(self, right): + return (self)._floordiv_(right) + return coercion_model.bin_op(self, right, ifloordiv) + def __invert__(self): if self.is_one(): return self @@ -2421,12 +2486,12 @@ cdef class CommutativeRingElement(RingElement): #This code is very general, it works for all integral domains that have the #is_square(root = True) option - from sage.rings.integral_domain import is_IntegralDomain + from sage.rings.ring import IntegralDomain P=self._parent is_sqr, sq_rt = self.is_square( root = True ) if is_sqr: if all: - if not is_IntegralDomain(P): + if not isinstance(P, IntegralDomain): raise NotImplementedError('sqrt() with all=True is only implemented for integral domains, not for %s' % P) if P.characteristic()==2 or sq_rt==0: #0 has only one square root, and in charasteristic 2 everything also has only 1 root @@ -2434,7 +2499,7 @@ cdef class CommutativeRingElement(RingElement): return [ sq_rt, -sq_rt ] return sq_rt #from now on we know that self is not a square - if not is_IntegralDomain(P): + if not isinstance(P, IntegralDomain): raise NotImplementedError('sqrt() of non squares is only implemented for integral domains, not for %s' % P) if not extend: #all square roots of a non-square should be an empty list @@ -3070,9 +3135,24 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): x, y = canonical_coercion(self, other) return x.quo_rem(y) - def __floordiv__(self,right): + cpdef RingElement _floordiv_(self, RingElement right): """ Quotient of division of ``self`` by other. This is denoted //. + + This default implementation assumes that ``quo_rem`` has been + implemented. + + EXAMPLES:: + + sage: cython(''' + ....: from sage.structure.element cimport EuclideanDomainElement + ....: cdef class MyElt(EuclideanDomainElement): + ....: def quo_rem(self, other): + ....: return self._parent.var('quo,rem') + ....: ''') + sage: e = MyElt(SR) + sage: e // e + quo """ Q, _ = self.quo_rem(right) return Q @@ -3081,13 +3161,28 @@ cdef class EuclideanDomainElement(PrincipalIdealDomainElement): """ Remainder of division of ``self`` by other. + This default implementation assumes that ``quo_rem`` has been + implemented. + EXAMPLES:: sage: R. = ZZ[] sage: x % (x+1) -1 - sage: (x**3 + x - 1) % (x**2 - 1) + sage: (x^3 + x - 1) % (x^2 - 1) 2*x - 1 + + :: + + sage: cython(''' + ....: from sage.structure.element cimport EuclideanDomainElement + ....: cdef class MyElt(EuclideanDomainElement): + ....: def quo_rem(self, other): + ....: return self._parent.var('quo,rem') + ....: ''') + sage: e = MyElt(SR) + sage: e % e + rem """ _, R = self.quo_rem(other) return R @@ -3099,9 +3194,22 @@ def is_FieldElement(x): return isinstance(x, FieldElement) cdef class FieldElement(CommutativeRingElement): + cpdef RingElement _floordiv_(self, RingElement right): + """ + Return the quotient of self and other. Since these are field + elements, the floor division is exactly the same as usual division. - def __floordiv__(self, other): - return self / other + EXAMPLES:: + + sage: K. = NumberField(x^4 + x^2 + 2/3) + sage: c = (1+b) // (1-b); c + 3/4*b^3 + 3/4*b^2 + 3/2*b + 1/2 + sage: (1+b) / (1-b) == c + True + sage: c * (1-b) + b + 1 + """ + return self._div_(right) def is_unit(self): r""" @@ -3229,28 +3337,6 @@ cdef class InfinityElement(RingElement): from sage.rings.all import ZZ return ZZ(0) -cdef class PlusInfinityElement(InfinityElement): - def __hash__(self): - r""" - TESTS:: - - sage: hash(+infinity) - 9223372036854775807 # 64-bit - 2147483647 # 32-bit - """ - return LONG_MAX - -cdef class MinusInfinityElement(InfinityElement): - def __hash__(self): - r""" - TESTS:: - - sage: hash(-infinity) - -9223372036854775808 # 64-bit - -2147483648 # 32-bit - """ - return LONG_MIN - ################################################################################# # diff --git a/src/sage/structure/factory.pyx b/src/sage/structure/factory.pyx index 74a5870d724..bfd2a185df0 100644 --- a/src/sage/structure/factory.pyx +++ b/src/sage/structure/factory.pyx @@ -151,7 +151,7 @@ cdef class UniqueFactory(SageObject): The below examples are rather artificial and illustrate particular aspects. For a "real-life" usage case of ``UniqueFactory``, see - the finite field factory in :mod:`sage.rings.finite_rings.constructor`. + the finite field factory in :mod:`sage.rings.finite_rings.finite_field_constructor`. In many cases, a factory class is implemented by providing the two methods :meth:`create_key` and :meth:`create_object`. In our example, diff --git a/src/sage/structure/formal_sum.py b/src/sage/structure/formal_sum.py index d8c0a73156e..bf820f29368 100644 --- a/src/sage/structure/formal_sum.py +++ b/src/sage/structure/formal_sum.py @@ -381,8 +381,6 @@ def _element_constructor_(self, x, check=True, reduce=True): P = x.parent() if P is self: return x - elif P == self: - return self.element_class(x._data, check=False, reduce=False, parent=self) else: x = x._data if isinstance(x, list): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 8ae4ad0545f..3a5c8647269 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -141,6 +141,7 @@ import sage.rings.integer import sage.rings.rational from sage.structure.element cimport ModuleElement, RingElement, Element from sage.symbolic.getitem cimport OperandsWrapper +from sage.symbolic.series cimport SymbolicSeries from sage.symbolic.complexity_measures import string_length from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction from sage.rings.rational import Rational # Used for sqrt. @@ -1531,6 +1532,17 @@ cdef class Expression(CommutativeRingElement): ... print "deriv: %s, hash:%s"%(deriv,h) ... else: ... hashes.add(n) + + Check whether `oo` keeps its hash in `SR` (:trac:`19918`):: + + sage: hash(oo) == hash(SR(oo)) + True + sage: hash(oo) == hash((-x).subs(x=-oo)) + True + sage: hash(-oo) == hash(SR(-oo)) + True + sage: hash(-oo) == hash((-x).subs(x=oo)) + True """ return self._gobj.gethash() @@ -2031,9 +2043,10 @@ cdef class Expression(CommutativeRingElement): """ return is_a_symbol(self._gobj) - def is_constant(self): + def _is_registered_constant_(self): """ - Return True if this symbolic expression is a constant. + Return True if this symbolic expression is interally represented as + a constant. This function is intended to provide an interface to query the internal representation of the expression. In this sense, the word ``constant`` @@ -2042,22 +2055,52 @@ cdef class Expression(CommutativeRingElement): EXAMPLES:: - sage: pi.is_constant() + sage: pi._is_registered_constant_() True - sage: x.is_constant() + sage: x._is_registered_constant_() False - sage: SR(1).is_constant() + sage: SR(1)._is_registered_constant_() False Note that the complex I is not a constant:: - sage: I.is_constant() + sage: I._is_registered_constant_() False sage: I.is_numeric() True """ return is_a_constant(self._gobj) + def is_constant(self): + """ + Return whether this symbolic expression is a constant. + + A symbolic expression is constant if it does not contain + any variables. + + EXAMPLES:: + + sage: pi.is_constant() + True + sage: SR(1).is_constant() + True + sage: SR(2).is_constant() + True + sage: log(2).is_constant() + True + sage: I.is_constant() + True + sage: x.is_constant() + False + + TESTS:: + + sage: P.

= ZZ[] + sage: SR(42).is_constant() == P(2).is_constant() + True + """ + return not self.variables() + def is_numeric(self): """ A Pynac numeric is an object you can do arithmetic with @@ -2079,60 +2122,23 @@ cdef class Expression(CommutativeRingElement): def is_series(self): """ - Return True if ``self`` is a series. - - Series are special kinds of symbolic expressions that are - constructed via the :meth:`series` method. They usually have - an ``Order()`` term unless the series representation is exact, - see :meth:`is_terminating_series`. - - OUTPUT: - - Boolean. Whether ``self`` is a series symbolic - expression. Usually, this means that it was constructed by the - :meth:`series` method. - - Returns ``False`` if only a subexpression of the symbolic - expression is a series. - - EXAMPLES:: + TESTS:: - sage: SR(5).is_series() - False - sage: var('x') - x sage: x.is_series() - False - sage: exp(x).is_series() - False - sage: exp(x).series(x,10).is_series() - True - - Laurent series are series, too:: - - sage: laurent_series = (cos(x)/x).series(x, 5) - sage: laurent_series - 1*x^(-1) + (-1/2)*x + 1/24*x^3 + Order(x^5) - sage: laurent_series.is_series() - True - - Something only containing a series as a subexpression is not a - series:: - - sage: sum_expr = 1 + exp(x).series(x,5); sum_expr - (1 + 1*x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + Order(x^5)) + 1 - sage: sum_expr.is_series() + doctest:...: DeprecationWarning: ex.is_series() is deprecated. Use isinstance(ex, sage.symbolic.series.SymbolicSeries) instead + See http://trac.sagemath.org/17659 for details. False """ - return is_a_series(self._gobj) + from sage.misc.superseded import deprecation + deprecation(17659, "ex.is_series() is deprecated. Use isinstance(ex, sage.symbolic.series.SymbolicSeries) instead") + return False def is_terminating_series(self): """ Return True if ``self`` is a series without order term. A series is terminating if it can be represented exactly, - without requiring an order term. See also :meth:`is_series` - for general series. + without requiring an order term. OUTPUT: @@ -2143,8 +2149,6 @@ cdef class Expression(CommutativeRingElement): sage: (x^5+x^2+1).series(x,10) 1 + 1*x^2 + 1*x^5 - sage: (x^5+x^2+1).series(x,10).is_series() - True sage: (x^5+x^2+1).series(x,10).is_terminating_series() True sage: SR(5).is_terminating_series() @@ -2156,7 +2160,7 @@ cdef class Expression(CommutativeRingElement): sage: exp(x).series(x,10).is_terminating_series() False """ - return g_is_a_terminating_series(self._gobj) + return False cpdef bint is_polynomial(self, var): """ @@ -2488,6 +2492,13 @@ cdef class Expression(CommutativeRingElement): sage: f(x) = matrix() sage: bool(f(x) - f(x) == 0) True + + Check that we catch exceptions from Pynac (:trac:`19904`):: + + sage: bool(SR(QQbar(I)) == I) + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s)... """ if self.is_relational(): # constants are wrappers around Sage objects, compare directly @@ -3992,6 +4003,7 @@ cdef class Expression(CommutativeRingElement): """ cdef Expression symbol0 = self.coerce_in(symbol) cdef GEx x + cdef SymbolicSeries nex cdef int prec if order is None: from sage.misc.defaults import series_precision @@ -4001,9 +4013,12 @@ cdef class Expression(CommutativeRingElement): sig_on() try: x = self._gobj.expand(0).series(symbol0._gobj, prec, 0) + nex = SymbolicSeries.__new__(SymbolicSeries) + nex._parent = self._parent + GEx_construct_ex(&nex._gobj, x) finally: sig_off() - return new_Expression_from_GEx(self._parent, x) + return nex def residue(self, symbol): """ @@ -4168,9 +4183,7 @@ cdef class Expression(CommutativeRingElement): sage: f.series(x==1,3).truncate().expand() -2*x^2*cos(1) + 5/2*x^2*sin(1) + 5*x*cos(1) - 7*x*sin(1) - 3*cos(1) + 11/2*sin(1) """ - if not is_a_series(self._gobj): - return self - return new_Expression_from_GEx(self._parent, series_to_poly(self._gobj)) + return self def expand(Expression self, side=None): """ @@ -5712,6 +5725,7 @@ cdef class Expression(CommutativeRingElement): Series coefficients are now handled correctly (:trac:`17399`):: + sage: s=(1/(1-x)).series(x,6); s 1 + 1*x + 1*x^2 + 1*x^3 + 1*x^4 + 1*x^5 + Order(x^6) sage: s.coefficients() @@ -5732,19 +5746,15 @@ cdef class Expression(CommutativeRingElement): [[t, 0], [3, 1], [1, 2]] """ + f = self._maxima_() + maxima = f.parent() + maxima._eval_line('load(coeflist)') if x is None: x = self.default_variable() - if is_a_series(self._gobj): - l = [[self.coefficient(x, d), d] for d in xrange(self.degree(x))] - else: - f = self._maxima_() - maxima = f.parent() - maxima._eval_line('load(coeflist)') - G = f.coeffs(x) - from sage.calculus.calculus import symbolic_expression_from_maxima_string - S = symbolic_expression_from_maxima_string(repr(G)) - l = S[1:] - + G = f.coeffs(x) + from sage.calculus.calculus import symbolic_expression_from_maxima_string + S = symbolic_expression_from_maxima_string(repr(G)) + l = S[1:] if sparse is True: return l else: @@ -11840,3 +11850,4 @@ cdef operators compatible_relation(operators lop, operators rop) except " (GEx e) # you must ensure that is_a_series(e) is true before calling this: bint g_is_a_terminating_series(GEx e) except + + GEx g_series_var(GEx e) except + # Relations ctypedef enum operators "relational::operators": @@ -163,7 +164,7 @@ cdef extern from "sage/symbolic/ginac_wrap.h": bint is_negative(GEx x) except + bint is_a_relational "is_a" (GEx e) - unsigned decide_relational(GEx e) + unsigned decide_relational(GEx e) except + operators relational_operator(GEx e) operators switch_operator(operators op) GEx relational(GEx lhs, GEx rhs, operators o) @@ -367,6 +368,7 @@ cdef extern from "sage/symbolic/ginac_wrap.h": GEx g_H "GiNaC::H" (GEx m, GEx x) except + # harmonic polylogarithm GEx g_zeta "GiNaC::zeta" (GEx m) except + # Riemann's zeta function as well as multiple zeta value GEx g_zeta2 "GiNaC::zeta" (GEx m, GEx s) except + # alternating Euler sum + GEx g_stieltjes "GiNaC::stieltjes" (GEx m) except + # Stieltjes constants GEx g_zetaderiv "GiNaC::zetaderiv" (GEx n, GEx x) except + # derivatives of Riemann's zeta function GEx g_tgamma "GiNaC::tgamma" (GEx x) except + # gamma function GEx g_lgamma "GiNaC::lgamma" (GEx x) except + # logarithm of gamma function @@ -473,6 +475,7 @@ cdef extern from "sage/symbolic/ginac_wrap.h": unsigned H_serial "GiNaC::H_SERIAL::serial" # harmonic polylogarithm unsigned zeta1_serial "GiNaC::zeta1_SERIAL::serial" # Riemann's zeta function as well as multiple zeta value unsigned zeta2_serial "GiNaC::zeta2_SERIAL::serial" # alternating Euler sum + unsigned stieltjes1_serial "GiNaC::stieltjes1_SERIAL::serial" # Stieltjes constants unsigned zetaderiv_serial "GiNaC::zetaderiv_SERIAL::serial" # derivatives of Riemann's zeta function unsigned tgamma_serial "GiNaC::tgamma_SERIAL::serial" # gamma function unsigned lgamma_serial "GiNaC::lgamma_SERIAL::serial" # logarithm of gamma function @@ -527,6 +530,7 @@ cdef extern from "sage/symbolic/ginac_wrap.h": object (*py_bernoulli)(object x) except + object (*py_sin)(object x) except + object (*py_cos)(object x) except + + object (*py_stieltjes)(object x) except + object (*py_zeta)(object x) except + object (*py_exp)(object x) except + object (*py_log)(object x) except + diff --git a/src/sage/symbolic/ginac_wrap.h b/src/sage/symbolic/ginac_wrap.h index 37dcdf3d4e9..076f5a55e96 100644 --- a/src/sage/symbolic/ginac_wrap.h +++ b/src/sage/symbolic/ginac_wrap.h @@ -68,6 +68,13 @@ bool g_is_a_terminating_series(const ex& e) { return false; } +ex g_series_var(const ex& e) { + if (is_a(e)) { + return (ex_to(e)).get_var(); + } + return NULL; +} + relational::operators relational_operator(const ex& e) { // unsafe cast -- be damn sure the input is a relational. return (ex_to(e)).the_operator(); diff --git a/src/sage/symbolic/pynac.pyx b/src/sage/symbolic/pynac.pyx index f5165bb774a..41b00d55d8b 100644 --- a/src/sage/symbolic/pynac.pyx +++ b/src/sage/symbolic/pynac.pyx @@ -1457,6 +1457,48 @@ cdef object py_cos(object x) except +: except (TypeError, ValueError): return CC(x).cos() +cdef object py_stieltjes(object x) except +: + """ + Return the Stieltjes constant of the given index. + + The value is expected to be a non-negative integer. + + TESTS:: + + sage: from sage.symbolic.pynac import py_stieltjes_for_doctests as py_stieltjes + sage: py_stieltjes(0) + 0.577215664901533 + sage: py_stieltjes(1.0) + -0.0728158454836767 + sage: py_stieltjes(RealField(100)(5)) + 0.00079332381730106270175333487744 + sage: py_stieltjes(-1) + Traceback (most recent call last): + ... + ValueError: Stieltjes constant of negative index + """ + n = ZZ(x) + if n < 0: + raise ValueError("Stieltjes constant of negative index") + import mpmath + if isinstance(x, Element) and hasattr((x)._parent, 'prec'): + prec = (x)._parent.prec() + else: + prec = 53 + return mpmath_utils.call(mpmath.stieltjes, n, prec=prec) + +def py_stieltjes_for_doctests(x): + """ + This function is for testing py_stieltjes(). + + EXAMPLES:: + + sage: from sage.symbolic.pynac import py_stieltjes_for_doctests + sage: py_stieltjes_for_doctests(0.0) + 0.577215664901533 + """ + return py_stieltjes(x) + cdef object py_zeta(object x) except +: """ Return the value of the zeta function at the given value. @@ -2264,6 +2306,7 @@ def init_function_table(): py_funcs.py_bernoulli = &py_bernoulli py_funcs.py_sin = &py_sin py_funcs.py_cos = &py_cos + py_funcs.py_stieltjes = &py_stieltjes py_funcs.py_zeta = &py_zeta py_funcs.py_exp = &py_exp py_funcs.py_log = &py_log diff --git a/src/sage/symbolic/series.pxd b/src/sage/symbolic/series.pxd new file mode 100644 index 00000000000..ef15506ad16 --- /dev/null +++ b/src/sage/symbolic/series.pxd @@ -0,0 +1,4 @@ +from sage.symbolic.expression cimport Expression + +cdef class SymbolicSeries(Expression): + pass \ No newline at end of file diff --git a/src/sage/symbolic/series.pyx b/src/sage/symbolic/series.pyx new file mode 100644 index 00000000000..e523354a396 --- /dev/null +++ b/src/sage/symbolic/series.pyx @@ -0,0 +1,234 @@ +""" +Symbolic Series + +Symbolic series are special kinds of symbolic expressions that are +constructed via the +:meth:`Expression.series ` +method. +They usually have an ``Order()`` term unless the series representation +is exact, see +:meth:`~sage.symbolic.series.SymbolicSeries.is_terminating_series`. + +For series over general rings see +:class:`power series ` +and +:class:`Laurent series`. + +EXAMPLES: + +We expand a polynomial in `x` about `0`, about `1`, and also truncate +it back to a polynomial:: + + sage: var('x,y') + (x, y) + sage: f = (x^3 - sin(y)*x^2 - 5*x + 3); f + x^3 - x^2*sin(y) - 5*x + 3 + sage: g = f.series(x, 4); g + 3 + (-5)*x + (-sin(y))*x^2 + 1*x^3 + sage: g.truncate() + x^3 - x^2*sin(y) - 5*x + 3 + sage: g = f.series(x==1, 4); g + (-sin(y) - 1) + (-2*sin(y) - 2)*(x - 1) + (-sin(y) + 3)*(x - 1)^2 + 1*(x - 1)^3 + sage: h = g.truncate(); h + (x - 1)^3 - (x - 1)^2*(sin(y) - 3) - 2*(x - 1)*(sin(y) + 1) - sin(y) - 1 + sage: h.expand() + x^3 - x^2*sin(y) - 5*x + 3 + +We compute another series expansion of an analytic function:: + + sage: f = sin(x)/x^2 + sage: f.series(x,7) + 1*x^(-1) + (-1/6)*x + 1/120*x^3 + (-1/5040)*x^5 + Order(x^7) + sage: f.series(x==1,3) + (sin(1)) + (cos(1) - 2*sin(1))*(x - 1) + (-2*cos(1) + 5/2*sin(1))*(x - 1)^2 + Order((x - 1)^3) + sage: f.series(x==1,3).truncate().expand() + -2*x^2*cos(1) + 5/2*x^2*sin(1) + 5*x*cos(1) - 7*x*sin(1) - 3*cos(1) + 11/2*sin(1) + +Following the GiNaC tutorial, we use John Machin's amazing +formula `\pi = 16 \mathrm{tan}^{-1}(1/5) - 4 \mathrm{tan}^{-1}(1/239)` +to compute digits of `\pi`. We expand the arc tangent around 0 and insert +the fractions 1/5 and 1/239. + +:: + + sage: x = var('x') + sage: f = atan(x).series(x, 10); f + 1*x + (-1/3)*x^3 + 1/5*x^5 + (-1/7)*x^7 + 1/9*x^9 + Order(x^10) + sage: (16*f.subs(x==1/5) - 4*f.subs(x==1/239)).n() + 3.14159268240440 + +Note: The result of an operation or function of series is not automatically +expanded to a series. This must be explicitly done by the user:: + + sage: ex1 = sin(x).series(x, 4); ex1 + 1*x + (-1/6)*x^3 + Order(x^4) + sage: ex2 = cos(x).series(x, 4); ex2 + 1 + (-1/2)*x^2 + Order(x^4) + sage: ex1 + ex2 + (1 + (-1/2)*x^2 + Order(x^4)) + (1*x + (-1/6)*x^3 + Order(x^4)) + sage: (ex1 + ex2).series(x,4) + 1 + 1*x + (-1/2)*x^2 + (-1/6)*x^3 + Order(x^4) + sage: x*ex1 + x*(1*x + (-1/6)*x^3 + Order(x^4)) + sage: (x*ex1).series(x,5) + 1*x^2 + (-1/6)*x^4 + Order(x^5) + sage: sin(ex1) + sin(1*x + (-1/6)*x^3 + Order(x^4)) + sage: sin(ex1).series(x,9) + 1*x + (-1/3)*x^3 + 11/120*x^5 + (-53/2520)*x^7 + Order(x^9) +""" +######################################################################## +# Copyright (C) 2015 Ralf Stephan +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +######################################################################## + +from ginac cimport * +from sage.symbolic.expression cimport Expression, new_Expression_from_GEx + +cdef class SymbolicSeries(Expression): + def __init__(self, SR): + """ + Trivial constructor. + + EXAMPLES:: + + sage: loads(dumps((x+x^3).series(x,2))) + 1*x + Order(x^2) + """ + Expression.__init__(self, SR, 0) + self._parent = SR + + def is_series(self): + """ + TESTS:: + + sage: ex = sin(x).series(x,5) + sage: ex.is_series() + doctest:...: DeprecationWarning: ex.is_series() is deprecated. Use isinstance(ex, sage.symbolic.series.SymbolicSeries) instead + See http://trac.sagemath.org/17659 for details. + True + """ + from sage.misc.superseded import deprecation + deprecation(17659, "ex.is_series() is deprecated. Use isinstance(ex, sage.symbolic.series.SymbolicSeries) instead") + return True + + def is_terminating_series(self): + """ + Return True if the series is without order term. + + A series is terminating if it can be represented exactly, + without requiring an order term. + + OUTPUT: + + Boolean. ``True`` if the series has no order term. + + EXAMPLES:: + + sage: (x^5+x^2+1).series(x,10) + 1 + 1*x^2 + 1*x^5 + sage: (x^5+x^2+1).series(x,10).is_terminating_series() + True + sage: SR(5).is_terminating_series() + False + sage: exp(x).series(x,10).is_terminating_series() + False + """ + return g_is_a_terminating_series((self)._gobj) + + def truncate(self): + """ + Given a power series or expression, return the corresponding + expression without the big oh. + + OUTPUT: + + A symbolic expression. + + EXAMPLES:: + + sage: f = sin(x)/x^2 + sage: f.truncate() + sin(x)/x^2 + sage: f.series(x,7) + 1*x^(-1) + (-1/6)*x + 1/120*x^3 + (-1/5040)*x^5 + Order(x^7) + sage: f.series(x,7).truncate() + -1/5040*x^5 + 1/120*x^3 - 1/6*x + 1/x + sage: f.series(x==1,3).truncate().expand() + -2*x^2*cos(1) + 5/2*x^2*sin(1) + 5*x*cos(1) - 7*x*sin(1) - 3*cos(1) + 11/2*sin(1) + """ + return new_Expression_from_GEx(self._parent, series_to_poly(self._gobj)) + + def default_variable(self): + """ + Return the expansion variable of this symbolic series. + + EXAMPLES:: + + sage: s=(1/(1-x)).series(x,3); s + 1 + 1*x + 1*x^2 + Order(x^3) + sage: s.default_variable() + x + """ + cdef GEx x = g_series_var(self._gobj) + cdef Expression ex = new_Expression_from_GEx(self._parent, x) + return ex + + def coefficients(self, x=None, sparse=True): + r""" + Return the coefficients of this symbolic series as a list of pairs. + + INPUT: + + - ``x`` -- optional variable. + + - ``sparse`` -- Boolean. If ``False`` return a list with as much + entries as the order of the series. + + OUTPUT: + + Depending on the value of ``sparse``, + + - A list of pairs ``(expr, n)``, where ``expr`` is a symbolic + expression and ``n`` is a power (``sparse=True``, default) + + - A list of expressions where the ``n``-th element is the coefficient of + ``x^n`` when self is seen as polynomial in ``x`` (``sparse=False``). + + EXAMPLES:: + + sage: s=(1/(1-x)).series(x,6); s + 1 + 1*x + 1*x^2 + 1*x^3 + 1*x^4 + 1*x^5 + Order(x^6) + sage: s.coefficients() + [[1, 0], [1, 1], [1, 2], [1, 3], [1, 4], [1, 5]] + sage: s.coefficients(x, sparse=False) + [1, 1, 1, 1, 1, 1] + sage: x,y = var("x,y") + sage: s=(1/(1-y*x-x)).series(x,3); s + 1 + (y + 1)*x + ((y + 1)^2)*x^2 + Order(x^3) + sage: s.coefficients(x, sparse=False) + [1, y + 1, (y + 1)^2] + + """ + if x is None: + x = self.default_variable() + l = [[self.coefficient(x, d), d] for d in xrange(self.degree(x))] + if sparse: + return l + else: + from sage.rings.integer_ring import ZZ + if any(not c[1] in ZZ for c in l): + raise ValueError("Cannot return dense coefficient list with noninteger exponents.") + val = l[0][1] + if val < 0: + raise ValueError("Cannot return dense coefficient list with negative valuation.") + deg = l[-1][1] + ret = [ZZ(0)] * int(deg+1) + for c in l: + ret[c[1]] = c[0] + return ret + diff --git a/src/sage/symbolic/subring.py b/src/sage/symbolic/subring.py index 1f7f5b0004f..083a5ad96ce 100644 --- a/src/sage/symbolic/subring.py +++ b/src/sage/symbolic/subring.py @@ -107,7 +107,7 @@ class SymbolicSubringFactory(UniqueFactory): INPUT: - Choose one of the following keywords to create a subring. + Specify one of the following keywords to create a subring. - ``accepting_variables`` (default: ``None``) -- a tuple or other iterable of variables. If specified, then a symbolic subring of @@ -179,7 +179,7 @@ def create_key_and_extra_args( sage: SymbolicSubring.create_key_and_extra_args() Traceback (most recent call last): ... - ValueError: Cannot create a symbolic subring since nothing specified. + ValueError: Cannot create a symbolic subring since nothing is specified. sage: SymbolicSubring.create_key_and_extra_args( ....: accepting_variables=('a',), rejecting_variables=('r',)) Traceback (most recent call last): @@ -198,15 +198,12 @@ def create_key_and_extra_args( """ if accepting_variables is None and \ rejecting_variables is None and \ - no_variables == False: + not no_variables: raise ValueError('Cannot create a symbolic subring ' - 'since nothing specified.') - if accepting_variables is not None and \ - rejecting_variables is not None or \ - rejecting_variables is not None and \ - no_variables == True or \ - no_variables == True and \ - accepting_variables is not None: + 'since nothing is specified.') + if accepting_variables is not None and rejecting_variables is not None or \ + rejecting_variables is not None and no_variables or \ + no_variables and accepting_variables is not None: raise ValueError('Cannot create a symbolic subring ' 'since input is ambiguous.') @@ -269,9 +266,20 @@ def __init__(self, vars): Symbolic Constants Subring sage: SymbolicSubring(rejecting_variables=tuple()) # indirect doctest Symbolic Ring + + :: + + sage: SR.subring(accepting_variables=(0, pi, sqrt(2), 'zzz', I)) + Traceback (most recent call last): + ... + ValueError: Invalid variables: 0, I, pi, sqrt(2) """ super(GenericSymbolicSubring, self).__init__() self._vars_ = set(vars) + if not all(v.is_symbol() for v in self._vars_): + raise ValueError('Invalid variables: {}'.format( + ', '.join(str(v) for v in sorted(self._vars_, key=str) + if not v.is_symbol()))) def _repr_variables_(self): @@ -301,7 +309,7 @@ def _repr_variables_(self): return s + ', '.join(str(v) for v in sorted(self._vars_, key=str)) - def is_variable_valid(self, variable): + def has_valid_variable(self, variable): r""" Return whether the given ``variable`` is valid in this subring. @@ -316,7 +324,7 @@ def is_variable_valid(self, variable): EXAMPLES:: sage: from sage.symbolic.subring import GenericSymbolicSubring - sage: GenericSymbolicSubring(vars=tuple()).is_variable_valid(x) + sage: GenericSymbolicSubring(vars=tuple()).has_valid_variable(x) Traceback (most recent call last): ... NotImplementedError: Not implemented in this abstract base class @@ -351,7 +359,7 @@ def _element_constructor_(self, x): """ expression = super(GenericSymbolicSubring, self)._element_constructor_(x) assert(expression.parent() is self) - if not all(self.is_variable_valid(var) + if not all(self.has_valid_variable(var) for var in expression.variables()): raise TypeError('%s is not contained in %s' % (x, self)) return expression @@ -416,8 +424,8 @@ def _coerce_map_from_(self, P): QQbar.has_coerce_map_from(P): return True - elif (P is InfinityRing - or is_RealIntervalField(P) or is_ComplexIntervalField(P)): + elif (P is InfinityRing or + is_RealIntervalField(P) or is_ComplexIntervalField(P)): return True elif ComplexField(mpfr_prec_min()).has_coerce_map_from(P): @@ -561,7 +569,7 @@ def merge(self, other): def __eq__(self, other): r""" - Return if this functor is equal to ``other``. + Return whether this functor is equal to ``other``. INPUT: @@ -583,7 +591,7 @@ def __eq__(self, other): def __ne__(self, other): r""" - Return if this functor is not equal to ``other``. + Return whether this functor is not equal to ``other``. INPUT: @@ -626,7 +634,7 @@ def _repr_(self): (self._repr_variables_()) - def is_variable_valid(self, variable): + def has_valid_variable(self, variable): r""" Return whether the given ``variable`` is valid in this subring. @@ -642,11 +650,11 @@ def is_variable_valid(self, variable): sage: from sage.symbolic.subring import SymbolicSubring sage: S = SymbolicSubring(accepting_variables=('a',)) - sage: S.is_variable_valid('a') + sage: S.has_valid_variable('a') True - sage: S.is_variable_valid('r') + sage: S.has_valid_variable('r') False - sage: S.is_variable_valid('x') + sage: S.has_valid_variable('x') False """ return SR(variable) in self._vars_ @@ -811,7 +819,7 @@ def _repr_(self): (self._repr_variables_()) - def is_variable_valid(self, variable): + def has_valid_variable(self, variable): r""" Return whether the given ``variable`` is valid in this subring. @@ -827,11 +835,11 @@ def is_variable_valid(self, variable): sage: from sage.symbolic.subring import SymbolicSubring sage: S = SymbolicSubring(rejecting_variables=('r',)) - sage: S.is_variable_valid('a') + sage: S.has_valid_variable('a') True - sage: S.is_variable_valid('r') + sage: S.has_valid_variable('r') False - sage: S.is_variable_valid('x') + sage: S.has_valid_variable('x') True """ return SR(variable) not in self._vars_ @@ -918,7 +926,7 @@ def _an_element_(self): Symbolic Subring rejecting the variables some_some_variable, some_variable """ v = SR.an_element() - while not self.is_variable_valid(v): + while not self.has_valid_variable(v): v = SR('some_' + str(v)) return self(v) @@ -1016,7 +1024,7 @@ def _repr_(self): return 'Symbolic Constants Subring' - def is_variable_valid(self, variable): + def has_valid_variable(self, variable): r""" Return whether the given ``variable`` is valid in this subring. @@ -1032,11 +1040,11 @@ def is_variable_valid(self, variable): sage: from sage.symbolic.subring import SymbolicSubring sage: S = SymbolicSubring(no_variables=True) - sage: S.is_variable_valid('a') + sage: S.has_valid_variable('a') False - sage: S.is_variable_valid('r') + sage: S.has_valid_variable('r') False - sage: S.is_variable_valid('x') + sage: S.has_valid_variable('x') False """ return False diff --git a/src/sage/version.py b/src/sage/version.py index b9dd9bf2423..62a499133d8 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,4 +1,4 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '7.0' -date = '2016-01-19' +version = '7.1.beta3' +date = '2016-02-11' diff --git a/src/sage_setup/autogen/interpreters.py b/src/sage_setup/autogen/interpreters.py index 9b60cef031e..1c7a9325bb9 100644 --- a/src/sage_setup/autogen/interpreters.py +++ b/src/sage_setup/autogen/interpreters.py @@ -4082,9 +4082,3 @@ def rebuild(dir): with open(os.path.join(dir, '__init__.py'), 'w') as f: f.write("# " + autogen_warn) - - -# This list of modules gets added to the list in module_list.py. -modules = [ - Extension('*', ['sage/ext/interpreters/*.pyx']) -] diff --git a/src/sage_setup/autogen/pari/args.py b/src/sage_setup/autogen/pari/args.py index 57356b4333a..f7e2a29eb6d 100644 --- a/src/sage_setup/autogen/pari/args.py +++ b/src/sage_setup/autogen/pari/args.py @@ -254,6 +254,20 @@ def convert_code(self): s = " {name} = prec_bits_to_words({name})\n" return s.format(name=self.name) +class PariArgumentBitprec(PariArgumentClass): + def _typerepr(self): + return "bitprec" + def ctype(self): + return "long" + def always_default(self): + return "0" + def get_argument_name(self, namesiter): + return "precision" + def convert_code(self): + s = " if not {name}:\n" + s += " {name} = default_bitprec()\n" + return s.format(name=self.name) + class PariArgumentSeriesPrec(PariArgumentClass): def _typerepr(self): return "serprec" @@ -277,6 +291,7 @@ def convert_code(self): 'U': PariArgumentULong, 'n': PariArgumentVariable, 'p': PariArgumentPrec, + 'b': PariArgumentBitprec, 'P': PariArgumentSeriesPrec, # Codes which are known but not actually supported for Sage diff --git a/src/sage_setup/autogen/pari/doc.py b/src/sage_setup/autogen/pari/doc.py index 449c65f80eb..e554da6b345 100644 --- a/src/sage_setup/autogen/pari/doc.py +++ b/src/sage_setup/autogen/pari/doc.py @@ -262,7 +262,7 @@ def get_rest_doc(function): sage: from sage_setup.autogen.pari.doc import get_rest_doc sage: print get_rest_doc("teichmuller") Teichmüller character of the :math:`p`-adic number :math:`x`, i.e. the unique - :math:`(p-1)`-th root of unity congruent to :math:`x / p^{v_p(x)}` modulo :math:`p`. + :math:`(p-1)`-th root of unity congruent to :math:`x / p^{v_p(x)}` modulo :math:`p`... :: @@ -295,12 +295,15 @@ def get_rest_doc(function): :: sage: print get_rest_doc("ellap") - Let :math:`E` be an :emphasis:`ell` structure as output by :literal:`ellinit`, defined over + Let :math:`E` be an :literal:`ell` structure as output by :literal:`ellinit`, defined over :math:`\mathbb{Q}` or a finite field :math:`\mathbb{F}_q`. The argument :math:`p` is best left omitted if the curve is defined over a finite field, and must be a prime number otherwise. This function computes the trace of Frobenius :math:`t` for the elliptic curve :math:`E`, defined by the equation :math:`\#E(\mathbb{F}_q) = q+1 - t`. + When the characteristic of the finite field is large, the availability of + the :literal:`seadata` package will speed the computation. + If the curve is defined over :math:`\mathbb{Q}`, :math:`p` must be explicitly given and the function computes the trace of the reduction over :math:`\mathbb{F}_p`. The trace of Frobenius is also the :math:`a_p` coefficient in the curve :math:`L`-series diff --git a/src/sage_setup/autogen/pari/generator.py b/src/sage_setup/autogen/pari/generator.py index 7aa99903142..9b78d88d77a 100644 --- a/src/sage_setup/autogen/pari/generator.py +++ b/src/sage_setup/autogen/pari/generator.py @@ -233,9 +233,9 @@ def __call__(self): D = sorted(D.values(), key=lambda d: d['function']) sys.stdout.write("Generating PARI functions:") - self.gen_file = open(self.gen_filename, 'w') + self.gen_file = open(self.gen_filename + '.tmp', 'w') self.gen_file.write(gen_banner) - self.instance_file = open(self.instance_filename, 'w') + self.instance_file = open(self.instance_filename + '.tmp', 'w') self.instance_file.write(instance_banner) for v in D: @@ -249,3 +249,7 @@ def __call__(self): self.gen_file.close() self.instance_file.close() + + # All done? Let's commit. + os.rename(self.gen_filename + '.tmp', self.gen_filename) + os.rename(self.instance_filename + '.tmp', self.instance_filename) diff --git a/src/sage_setup/autogen/pari/parser.py b/src/sage_setup/autogen/pari/parser.py index 639673bd024..2b60a609659 100644 --- a/src/sage_setup/autogen/pari/parser.py +++ b/src/sage_setup/autogen/pari/parser.py @@ -111,7 +111,7 @@ def read_decl(): sage: from sage_setup.autogen.pari.parser import read_decl sage: read_decl() - {'ABC_to_bnr', ..., 'zx_to_ZX'} + {'ABC_to_bnr', ..., 'zx_to_zv'} """ s = set() with open(os.path.join(sage_src_pari(), "paridecl.pxd")) as f: diff --git a/src/doc/common/builder.py b/src/sage_setup/docbuild/__init__.py old mode 100755 new mode 100644 similarity index 89% rename from src/doc/common/builder.py rename to src/sage_setup/docbuild/__init__.py index 4e8015298bb..98e831f39f1 --- a/src/doc/common/builder.py +++ b/src/sage_setup/docbuild/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python """ The documentation builder @@ -11,51 +10,30 @@ * The sphinx subprocesses are configured in conf.py """ +from __future__ import absolute_import +from __future__ import print_function + import logging, optparse, os, shutil, subprocess, sys, re import sphinx.cmdline import sphinx.util.console import sphinx.ext.intersphinx -#We remove the current directory from sys.path right away -#so that we import sage from the proper spot -try: - sys.path.remove(os.path.realpath(os.getcwd())) -except ValueError: - pass - +import sage.all from sage.misc.cachefunc import cached_method -from sage.misc.misc import sage_makedirs as mkdir -from sage.env import SAGE_DOC, SAGE_SRC - -# Load the options, including -# SAGE_DOC, LANGUAGES, SPHINXOPTS, PAPER, OMIT, -# PAPEROPTS, ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS -# from build_options.py. -fpath = os.path.join(SAGE_DOC, 'common', 'build_options.py') -exec(compile(open(fpath).read(), fpath, 'exec')) - +from sage.misc.misc import sage_makedirs +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT, SAGE_SRC -def delete_empty_directories(root_dir, verbose=True): - """ - Delete all empty directories found under ``root_dir`` - - INPUT: - - - ``root_dir`` -- string. A valid directory name. - """ - for dirpath, dirnames, filenames in os.walk(root_dir, topdown=False): - if not dirnames + filenames: - if verbose: - print('Deleting empty directory {0}'.format(dirpath)) - os.rmdir(dirpath) +from .build_options import (LANGUAGES, SPHINXOPTS, PAPER, OMIT, + PAPEROPTS, ALLSPHINXOPTS, NUM_THREADS, WEBSITESPHINXOPTS, + INCREMENTAL_BUILD, ABORT_ON_ERROR) -def print_build_error(): +def excepthook(*exc_info): """ Print docbuild error and hint how to solve it """ - logger.error('Error building the documentation.') + logger.error('Error building the documentation.', exc_info=exc_info) if INCREMENTAL_BUILD: logger.error(''' Note: incremental documentation builds sometimes cause spurious @@ -104,27 +82,21 @@ def f(self, *args, **kwds): output_dir) logger.debug(build_command) - # Execute custom-sphinx-build.py - sys.argv = [os.path.join(SAGE_DOC, 'common', 'custom-sphinx-build.py')] - sys.argv.extend(build_command.split()) - eval(compile(open(sys.argv[0]).read(), sys.argv[0], 'exec')) - - # Print message about location of output: - # - by default if html output - # - if verbose and if not pdf output - # - if pdf: print custom message here if verbose, and print - # full message below (see pdf method) after 'make all-pdf' - # is done running - - if 'output/html' in output_dir: - logger.warning("Build finished. The built documents can be found in %s", - output_dir) - elif 'output/pdf' not in output_dir: - logger.info("Build finished. The built documents can be found in %s", - output_dir) + # Run Sphinx with Sage's special logger + sys.argv = ["sphinx-build"] + build_command.split() + from .sphinxbuild import runsphinx + try: + runsphinx() + except Exception: + if ABORT_ON_ERROR: + raise + + if "/latex" in output_dir: + logger.warning("LaTeX file written to {}".format(output_dir)) else: - logger.info("LaTeX file written to %s; now making PDF.", - output_dir) + logger.warning( + "Build finished. The built documents can be found in {}". + format(output_dir)) f.is_output_format = True return f @@ -150,10 +122,6 @@ def __init__(self, name, lang='en'): self.lang = lang self.dir = os.path.join(SAGE_DOC, self.lang, self.name) - #Make sure the .static and .templates directories are there - mkdir(os.path.join(self.dir, "static")) - mkdir(os.path.join(self.dir, "templates")) - def _output_dir(self, type): """ Returns the directory where the output of type ``type`` is stored. @@ -162,13 +130,13 @@ def _output_dir(self, type): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.DocBuilder('tutorial') + sage: from sage_setup.docbuild import DocBuilder + sage: b = DocBuilder('tutorial') sage: b._output_dir('html') - '.../doc/output/html/en/tutorial' + '.../html/en/tutorial' """ - d = os.path.join(SAGE_DOC, "output", type, self.lang, self.name) - mkdir(d) + d = os.path.join(SAGE_DOC_OUTPUT, type, self.lang, self.name) + sage_makedirs(d) return d def _doctrees_dir(self): @@ -179,13 +147,13 @@ def _doctrees_dir(self): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.DocBuilder('tutorial') + sage: from sage_setup.docbuild import DocBuilder + sage: b = DocBuilder('tutorial') sage: b._doctrees_dir() - '.../doc/output/doctrees/en/tutorial' + '.../doctrees/en/tutorial' """ - d = os.path.join(SAGE_DOC, "output", 'doctrees', self.lang, self.name) - mkdir(d) + d = os.path.join(SAGE_DOC_OUTPUT, 'doctrees', self.lang, self.name) + sage_makedirs(d) return d def _output_formats(self): @@ -194,8 +162,8 @@ def _output_formats(self): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.DocBuilder('tutorial') + sage: from sage_setup.docbuild import DocBuilder + sage: b = DocBuilder('tutorial') sage: b._output_formats() ['changes', 'html', 'htmlhelp', 'inventory', 'json', 'latex', 'linkcheck', 'pickle', 'web'] @@ -219,8 +187,8 @@ def pdf(self): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.DocBuilder('tutorial') + sage: from sage_setup.docbuild import DocBuilder + sage: b = DocBuilder('tutorial') sage: b.pdf() #not tested """ self.latex() @@ -331,8 +299,8 @@ def get_all_documents(self): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: documents = builder.AllBuilder().get_all_documents() + sage: from sage_setup.docbuild import AllBuilder + sage: documents = AllBuilder().get_all_documents() sage: 'en/tutorial' in documents True sage: documents[0] == 'en/reference' @@ -401,7 +369,7 @@ def create_html_redirects(self): document_name = document.split('/')[1] # the sage directory within a subdocument, for example - # /path/to/.../output/html/en/reference/algebras/sage + # local/share/doc/sage/html/en/reference/algebras/sage sage_directory = os.path.join(path, document, 'sage') # Walk through all of the files in the sage_directory @@ -485,13 +453,13 @@ def _output_dir(self, type, lang='en'): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.ReferenceBuilder('reference') + sage: from sage_setup.docbuild import ReferenceBuilder + sage: b = ReferenceBuilder('reference') sage: b._output_dir('html') - '.../doc/output/html/en/reference' + '.../html/en/reference' """ - d = os.path.join(SAGE_DOC, "output", type, lang, self.name) - mkdir(d) + d = os.path.join(SAGE_DOC_OUTPUT, type, lang, self.name) + sage_makedirs(d) return d def _wrapper(self, format, *args, **kwds): @@ -533,11 +501,11 @@ def _wrapper(self, format, *args, **kwds): # few seconds.) getattr(get_builder('website'), 'html')() # Copy the relevant pieces of - # output/html/en/website/_static to output_dir. + # SAGE_DOC_OUTPUT/html/en/website/_static to output_dir. # (Don't copy all of _static to save some space: we # don't need all of the MathJax stuff, and in # particular we don't need the fonts.) - website_dir = os.path.join(SAGE_DOC, 'output', 'html', + website_dir = os.path.join(SAGE_DOC_OUTPUT, 'html', 'en', 'website') static_files = ['COPYING.txt', 'basic.css', 'blank.gif', 'default.css', 'doctools.js', 'favicon.ico', @@ -545,7 +513,7 @@ def _wrapper(self, format, *args, **kwds): 'pdf.png', 'plus.png', 'pygments.css', 'sage.css', 'sageicon.png', 'sagelogo.png', 'searchtools.js', 'sidebar.js', 'underscore.js'] - mkdir(os.path.join(output_dir, '_static')) + sage_makedirs(os.path.join(output_dir, '_static')) for f in static_files: try: shutil.copyfile(os.path.join(website_dir, '_static', f), @@ -639,8 +607,8 @@ def get_all_documents(self, refdir): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: b = builder.ReferenceBuilder('reference') + sage: from sage_setup.docbuild import ReferenceBuilder + sage: b = ReferenceBuilder('reference') sage: refdir = os.path.join(os.environ['SAGE_DOC'], 'en', b.name) sage: sorted(b.get_all_documents(refdir)) ['reference/algebras', @@ -864,7 +832,7 @@ def print_modified_modules(self): the documentation was last built. """ for module_name in self.get_modified_modules(): - print module_name + print(module_name) def get_all_rst_files(self, exclude_sage=True): """ @@ -910,7 +878,7 @@ def print_newly_included_modules(self): don't appear in the cache. """ for module_name in self.get_newly_included_modules(): - print module_name + print(module_name) def get_modules(self, filename): """ @@ -960,9 +928,8 @@ def auto_rest_filename(self, module_name): EXAMPLES:: - sage: import os, sys; sys.path.append(os.environ['SAGE_DOC']+'/common/'); import builder - sage: import builder - sage: builder.ReferenceSubBuilder("reference").auto_rest_filename("sage.combinat.partition") + sage: from sage_setup.docbuild import ReferenceSubBuilder + 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' @@ -974,7 +941,7 @@ def write_auto_rest_file(self, module_name): if not module_name.startswith('sage'): return filename = self.auto_rest_filename(module_name) - mkdir(os.path.dirname(filename)) + sage_makedirs(os.path.dirname(filename)) outfile = open(filename, 'w') @@ -1061,7 +1028,7 @@ def print_unincluded_modules(self): reference manual. """ for module_name in self.get_unincluded_modules(): - print module_name + print(module_name) def print_included_modules(self): """ @@ -1069,8 +1036,7 @@ def print_included_modules(self): manual. """ for module_name in self.get_all_included_modules(): - print module_name - + print(module_name) class SingleFileBuilder(DocBuilder): @@ -1098,7 +1064,6 @@ def __init__(self, path): # the static and templates directories in the output directory. # By default, this is DOT_SAGE/docbuild/MODULE_NAME, but can # also be specified at the command line. - orig_dir = os.path.join(SAGE_DOC, self.lang, self.name) module_name = os.path.splitext(os.path.basename(path))[0] latex_name = module_name.replace('_', r'\_') @@ -1115,49 +1080,54 @@ def __init__(self, path): pass self.dir = os.path.join(base_dir, 'source') - mkdir(self.dir) - mkdir(os.path.join(self.dir, "static")) - mkdir(os.path.join(self.dir, "templates")) + sage_makedirs(os.path.join(self.dir, "static")) + sage_makedirs(os.path.join(self.dir, "templates")) # Write self.dir/conf.py conf = """# -*- coding: utf-8 -*- +# This file is automatically generated by {}, do not edit! import sys, os sys.path.append(os.environ['SAGE_DOC']) -sys.path.append('%s') +sys.path.append({!r}) from common.conf import * -project = u'Documentation for %s' +project = u'Documentation for {}' release = 'unknown' -name = u'%s' +name = {!r} html_title = project html_short_title = project htmlhelp_basename = name latex_domain_indices = False latex_documents = [ - ('index', name + '.tex', u'Documentation for %s', + ('index', name + '.tex', u'Documentation for {}', u'unknown', 'manual'), ] -""" % (self.dir, module_name, module_name, latex_name) - conffile = open(os.path.join(self.dir, 'conf.py'), 'w') - conffile.write(conf) - conffile.close() +""".format(__file__, self.dir, module_name, module_name, latex_name) + + if 'SAGE_DOC_UNDERSCORE' in os.environ: + conf +=""" +def setup(app): + app.connect('autodoc-skip-member', skip_member) +""" + + with open(os.path.join(self.dir, 'conf.py'), 'w') as conffile: + conffile.write(conf) # Write self.dir/index.rst - module_name = os.path.splitext(os.path.basename(path))[0] - index = """ -.. automodule:: %s + title = 'Docs for file %s' % path + heading = title + "\n" + ("=" * len(title)) + index = """{} + +.. This file is automatically generated by {}, do not edit! + +.. automodule:: {} :members: :undoc-members: :show-inheritance: -""" % module_name - indexfile = open(os.path.join(self.dir, 'index.rst'), 'w') - title = 'Docs for file %s' % path - indexfile.write(title + '\n') - indexfile.write('='*len(title) + "\n\n") - indexfile.write('.. This file has been autogenerated.\n\n') - indexfile.write(index) - indexfile.close() +""".format(heading, __file__, module_name) + with open(os.path.join(self.dir, 'index.rst'), 'w') as indexfile: + indexfile.write(index) # Create link from original file to self.dir. Note that we # append self.dir to sys.path in conf.py. This is reasonably @@ -1175,7 +1145,7 @@ def _output_dir(self, type): """ base_dir = os.path.split(self.dir)[0] d = os.path.join(base_dir, "output", type) - mkdir(d) + sage_makedirs(d) return d def _doctrees_dir(self): @@ -1209,8 +1179,8 @@ 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 "of documents, or 'sage -docbuild --help' for more help." + 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): @@ -1242,7 +1212,7 @@ def help_usage(s=u"", compact=False): documentation builder. If 'compact' is False, the function adds a final newline character. """ - s += "sage -docbuild [OPTIONS] DOCUMENT (FORMAT | COMMAND)" + s += "sage --docbuild [OPTIONS] DOCUMENT (FORMAT | COMMAND)" if not compact: s += "\n" return s @@ -1271,12 +1241,12 @@ def help_examples(s=u""): builder. """ s += "Examples:\n" - s += " sage -docbuild -FDC 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 -FDC 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" return s def get_documents(): @@ -1354,7 +1324,7 @@ def help_message_long(option, opt_str, value, parser): help_formats, help_commands, parser.format_option_help, help_examples ] for f in help_funcs: - print f() + print(f()) sys.exit(0) def help_message_short(option=None, opt_str=None, value=None, parser=None, @@ -1381,11 +1351,11 @@ def help_wrapper(option, opt_str, value, parser): formats, and document-specific commands. """ if option.dest == 'commands': - print help_commands(value), + print(help_commands(value), end="") if option.dest == 'documents': - print help_documents(), + print(help_documents(), end="") if option.dest == 'formats': - print help_formats(), + print(help_formats(), end="") setattr(parser.values, 'printed_list', 1) @@ -1535,7 +1505,7 @@ def setup_logger(verbose=1, color=True): format_debug += " " # Documentation: http://docs.python.org/library/logging.html - logger = logging.getLogger('doc.common.builder') + logger = logging.getLogger('docbuild') # Note: There's also Handler.setLevel(). The argument is the # lowest severity message that the respective logger or handler @@ -1583,9 +1553,11 @@ def fetch_inventory(self, app, uri, inv): self.inventories[t] = i return i -if __name__ == '__main__': + +def main(): # Parse the command-line. parser = setup_parser() + global options options, args = parser.parse_args() # Get the name and type (target format) of the document we are @@ -1595,11 +1567,13 @@ def fetch_inventory(self, app, uri, inv): except ValueError: help_message_short(parser=parser, error=True) sys.exit(1) - delete_empty_directories(SAGE_DOC, verbose=False) # Set up module-wide logging. + global logger logger = setup_logger(options.verbose, options.color) + sys.excepthook = excepthook + # Process selected options. # # MathJax: this check usually has no practical effect, since @@ -1615,6 +1589,7 @@ def fetch_inventory(self, app, uri, inv): if options.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: @@ -1628,20 +1603,8 @@ def fetch_inventory(self, app, uri, inv): ABORT_ON_ERROR = not options.keep_going - # Make sure common/static exists. - mkdir(os.path.join(SAGE_DOC, 'common', 'static')) - - import sage.all - # Set up Intersphinx cache C = IntersphinxCache() - # Get the builder and build. - try: - getattr(get_builder(name), type)() - except Exception: - print_build_error() - raise - - # Clean up empty directories. - delete_empty_directories(SAGE_DOC, verbose=False) + builder = getattr(get_builder(name), type) + builder() diff --git a/src/sage_setup/docbuild/__main__.py b/src/sage_setup/docbuild/__main__.py new file mode 100644 index 00000000000..e3f6db6e65d --- /dev/null +++ b/src/sage_setup/docbuild/__main__.py @@ -0,0 +1,13 @@ +from . import main +main() + +# Replace old documentation output by symbolic link to SAGE_DOC_OUTPUT +import os +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT +old_doc = os.path.join(SAGE_DOC, "output") + +if not os.path.islink(old_doc): + if os.path.exists(old_doc): + from shutil import rmtree + rmtree(old_doc) + os.symlink(SAGE_DOC_OUTPUT, old_doc) diff --git a/src/doc/common/build_options.py b/src/sage_setup/docbuild/build_options.py similarity index 83% rename from src/doc/common/build_options.py rename to src/sage_setup/docbuild/build_options.py index c816af7e835..324f3bd08f1 100644 --- a/src/doc/common/build_options.py +++ b/src/sage_setup/docbuild/build_options.py @@ -3,7 +3,9 @@ ############################################### import os, re -SAGE_DOC = os.environ['SAGE_DOC'] + +from sage.env import SAGE_DOC, SAGE_DOC_OUTPUT + LANGUAGES = [d for d in os.listdir(SAGE_DOC) if re.match('^[a-z][a-z]$', d)] SPHINXOPTS = "" PAPER = "" @@ -15,7 +17,7 @@ PAPEROPTS = "" #Note that this needs to have the doctrees dir -ALLSPHINXOPTS = SPHINXOPTS + " " + PAPEROPTS + " " +ALLSPHINXOPTS = SPHINXOPTS + " " + PAPEROPTS + " " WEBSITESPHINXOPTS = "" # Number of threads to use for parallel-building the documentation. @@ -25,7 +27,7 @@ from sage.interfaces.gap import set_gap_memory_pool_size set_gap_memory_pool_size(80 * 1024 * 1024) -INCREMENTAL_BUILD = os.path.exists(os.path.join(SAGE_DOC, 'output')) +INCREMENTAL_BUILD = os.path.isdir(os.path.join(SAGE_DOC_OUTPUT)) # Error out on errors ABORT_ON_ERROR = True diff --git a/src/sage_setup/docbuild/ext/__init__.py b/src/sage_setup/docbuild/ext/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/doc/common/inventory_builder.py b/src/sage_setup/docbuild/ext/inventory_builder.py similarity index 100% rename from src/doc/common/inventory_builder.py rename to src/sage_setup/docbuild/ext/inventory_builder.py diff --git a/src/doc/common/multidocs.py b/src/sage_setup/docbuild/ext/multidocs.py similarity index 93% rename from src/doc/common/multidocs.py rename to src/sage_setup/docbuild/ext/multidocs.py index 04e20c25875..5c7f2266ec9 100644 --- a/src/doc/common/multidocs.py +++ b/src/sage_setup/docbuild/ext/multidocs.py @@ -23,6 +23,8 @@ import cPickle, os, sys, shutil, re, tempfile import sphinx from sphinx.util.console import bold +from sage.env import SAGE_DOC_OUTPUT +from sage.misc.misc import sage_makedirs CITE_FILENAME = 'citations.pickle' @@ -195,11 +197,22 @@ def sage_pathto(otheruri, *args, **opts): def citation_dir(app): - citedir = re.sub('/doc/output/[^/]*/', '/doc/output/inventory/', app.outdir) - if not os.path.isdir(citedir): - os.makedirs(os.path.abspath(citedir)) + # Split app.outdir in 3 parts: SAGE_DOC_OUTPUT/TYPE/TAIL where TYPE + # is a single directory and TAIL can contain multiple directories. + # The citation dir is then SAGE_DOC_OUTPUT/inventory/TAIL. + assert app.outdir.startswith(SAGE_DOC_OUTPUT) + rel = app.outdir[len(SAGE_DOC_OUTPUT):] + dirs = rel.split(os.sep) + # If SAGE_DOC_OUTPUT does not end with a slash, rel will start with + # a slash giving an empty dirs[0]. Remove this: + if not dirs[0]: + dirs.pop(0) + dirs = [SAGE_DOC_OUTPUT, "inventory"] + dirs[1:] + citedir = os.path.join(*dirs) + sage_makedirs(citedir) return citedir + def write_citations(app, citations): """ Pickle the citation in a file. diff --git a/src/doc/common/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py similarity index 99% rename from src/doc/common/sage_autodoc.py rename to src/sage_setup/docbuild/ext/sage_autodoc.py index 67e05f109d8..54da7dcd4bc 100644 --- a/src/doc/common/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -42,7 +42,8 @@ from sphinx.util.pycompat import base_exception, class_types from sphinx.util.docstrings import prepare_docstring -from sage.misc.sageinspect import sage_getdoc_original, sage_getargspec, isclassinstance +from sage.misc.sageinspect import (sage_getdoc_original, + sage_getargspec, sage_formatargspec, isclassinstance) from sage.misc.lazy_import import LazyImport #: extended signature RE: with explicit module name separated by :: @@ -866,7 +867,7 @@ def format_args(self): argspec = self.args_on_obj(self.object) if argspec is None: return None - args = inspect.formatargspec(*argspec) + args = sage_formatargspec(*argspec) # escape backslashes for reST args = args.replace('\\', '\\\\') return args @@ -957,7 +958,7 @@ def format_args(self): argspec = sage_getargspec(initmeth) if argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] - return inspect.formatargspec(*argspec) + return sage_formatargspec(*argspec) def format_signature(self): if self.doc_as_attr: @@ -1114,7 +1115,9 @@ def args_on_obj(self, obj): def format_args(self): argspec = self.args_on_obj(self.object) - return inspect.formatargspec(*argspec) if argspec is not None else None + if argspec is None: + return None + return sage_formatargspec(*argspec) def document_members(self, all_members=False): pass diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py new file mode 100644 index 00000000000..f26133cdd9e --- /dev/null +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -0,0 +1,213 @@ +""" +This is Sage's version of the sphinx-build script + +We redirect stdout to our own logger, and remove some unwanted chatter. +""" + +import os, sys, re, sphinx + +# override the fancy multi-line formatting +def term_width_line(text): + return text + '\n' + +sphinx.util.console.term_width_line = term_width_line + + +class SageSphinxLogger(object): + """ + This implements the file object interface to serve as sys.stdout + replacement. + """ + ansi_color = re.compile(r'\x1b\[[0-9;]*m') + ansi_reset = re.compile(r'\x1b\[39;49;00m') + prefix_len = 9 + + def __init__(self, stream, prefix): + self._init_chatter() + self._stream = stream + self._color = stream.isatty() + prefix = prefix[0:self.prefix_len] + prefix = ('[{0:'+str(self.prefix_len)+'}]').format(prefix) + self._is_stdout = (stream.fileno() == 1) + self._is_stderr = (stream.fileno() == 2) + if self._is_stdout: + color = 'darkgreen' + elif self._is_stderr: + color = 'red' + else: + color = 'lightgray' + self._prefix = sphinx.util.console.colorize(color, prefix) + + def _init_chatter(self): + # useless_chatter: regular expressions to be filtered from + # Sphinx output. + self.useless_chatter = ( + re.compile('^$'), + re.compile('^Running Sphinx v'), + re.compile('^loading intersphinx inventory from '), + re.compile('^Compiling a sub-document'), + re.compile('^updating environment: 0 added, 0 changed, 0 removed'), + re.compile('^looking for now-outdated files... none found'), + re.compile('^building \[.*\]: targets for 0 source files that are out of date'), + re.compile('^loading pickled environment... done'), + re.compile('^loading cross citations... done \([0-9]* citations\).'), + re.compile('WARNING: favicon file \'favicon.ico\' does not exist'), + re.compile('.*WARNING: html_static_path entry .* does not exist'), + ) + + # replacements: pairs of regular expressions and their replacements, + # to be applied to Sphinx output. + self.replacements = [(re.compile('build succeeded, [0-9]+ warning[s]?.'), + 'build succeeded.')] + + if 'inventory' in sys.argv: + # When building the inventory, ignore warnings about missing + # citations and the search index. + self.useless_chatter += ( + re.compile('^None:[0-9]*: WARNING: citation not found: '), + re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') + ) + + # warnings: regular expressions (or strings) indicating a problem with + # docbuilding. Raise an exception if any of these occur. + self.warnings = (re.compile('Segmentation fault'), + re.compile('SEVERE'), + re.compile('ERROR'), + re.compile('^make.*Error'), + re.compile('Exception occurred'), + re.compile('Sphinx error')) + + # We want all warnings to actually be errors. + # Exceptions: + # - warnings upon building the LaTeX documentation + # - undefined labels upon the first pass of the compilation: some + # cross links may legitimately not yet be resolvable at this point. + if 'latex' not in sys.argv: + if 'multidoc_first_pass=1' in sys.argv: + # Catch all warnings except 'WARNING: undefined label' + self.warnings += (re.compile('WARNING: (?!undefined label)'),) + else: + self.warnings += (re.compile('WARNING:'),) + + def _filter_out(self, line): + if exception is not None and self._is_stdout: + # swallow non-errors after an error occurred + return True + line = re.sub(self.ansi_color, '', line) + for regex in self.useless_chatter: + if regex.match(line) is not None: + return True + return False + + def _check_warnings(self, line): + global exception + if exception is not None: + return # we already have found an error + for regex in self.warnings: + if regex.search(line) is not None: + exception = OSError(line) + return + + def _log_line(self, line): + if self._filter_out(line): + return + for (old, new) in self.replacements: + line = old.sub(new, line) + line = self._prefix + ' ' + line.strip() + '\n' + if not self._color: + line = self.ansi_color.sub('', line) + self._stream.write(line) + self._stream.flush() + self._check_warnings(line) + + _line_buffer = '' + + def _break_long_lines(self): + """ + Break text that has been formated with string.ljust() back + into individual lines. Return partial output. Do nothing if + the filter rule matches, otherwise subsequent lines would be + not filtered out. + """ + if self._filter_out(self._line_buffer): + return + cols = sphinx.util.console._tw + lines = [] + buf = self._line_buffer + while len(buf) > cols: + lines.append(buf[0:cols]) + buf = buf[cols:] + lines.append(buf) + self._line_buffer = '\n'.join(lines) + + def _write(self, string): + self._line_buffer += string + #self._break_long_lines() + lines = self._line_buffer.splitlines() + for i, line in enumerate(lines): + last = (i == len(lines)-1) + if last and not self._line_buffer.endswith('\n'): + self._line_buffer = line + return + self._log_line(line) + self._line_buffer = '' + + + # file object interface follows + + closed = False + encoding = None + mode = 'w' + name = '' + newlines = None + softspace = 0 + + def isatty(self): + return True + + def close(self): + if self._line_buffer != '': + self._log_line(self._line_buffer) + self._line_buffer = '' + + def flush(self): + self._stream.flush() + + def write(self, str): + try: + self._write(str) + except OSError: + raise + except Exception: + import traceback + traceback.print_exc(file=self._stream) + + def writelines(self, sequence): + for line in sequence: + self.write(line) + + +def runsphinx(): + # Do not error out at the first warning, sometimes there is more + # information. So we run until the end of the file and only then + # raise the error. + global exception + exception = None + + output_dir = sys.argv[-1] + + saved_stdout = sys.stdout + saved_stderr = sys.stderr + + try: + sys.stdout = SageSphinxLogger(sys.stdout, os.path.basename(output_dir)) + sys.stderr = SageSphinxLogger(sys.stderr, os.path.basename(output_dir)) + sphinx.cmdline.main(sys.argv) + finally: + sys.stdout = saved_stdout + sys.stderr = saved_stderr + sys.stdout.flush() + sys.stderr.flush() + + if exception is not None: + raise exception