diff --git a/Makefile b/Makefile index 2b6f00934ab..1e92b858b78 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ build: all-build # Defer unknown targets to build/make/Makefile %:: + @if [ -x relocate-once.py ]; then ./relocate-once.py; fi $(MAKE) build/make/Makefile +build/bin/sage-logger \ "cd build/make && ./install '$@'" logs/install.log @@ -90,34 +91,37 @@ test: all check: test testall: all - $(TESTALL) --optional=all --logfile=logs/testall.log + $(TESTALL) --optional=sage,optional,external --logfile=logs/testall.log testlong: all $(TESTALL) --long --logfile=logs/testlong.log testalllong: all - $(TESTALL) --long --optional=all --logfile=logs/testalllong.log + $(TESTALL) --long --optional=sage,optional,external --logfile=logs/testalllong.log ptest: all $(PTESTALL) --logfile=logs/ptest.log ptestall: all - $(PTESTALL) --optional=all --logfile=logs/ptestall.log + $(PTESTALL) --optional=sage,optional,external --logfile=logs/ptestall.log ptestlong: all $(PTESTALL) --long --logfile=logs/ptestlong.log ptestalllong: all - $(PTESTALL) --long --optional=all --logfile=logs/ptestalllong.log + $(PTESTALL) --long --optional=sage,optional,external --logfile=logs/ptestalllong.log +testoptional: all + $(TESTALL) --optional=sage,optional --logfile=logs/testoptional.log -testoptional: testall # just an alias +testoptionallong: all + $(TESTALL) --long --optional=sage,optional --logfile=logs/testoptionallong.log -testoptionallong: testalllong # just an alias +ptestoptional: all + $(PTESTALL) --optional=sage,optional --logfile=logs/ptestoptional.log -ptestoptional: ptestall # just an alias - -ptestoptionallong: ptestalllong # just an alias +ptestoptionallong: all + $(PTESTALL) --long --optional=sage,optional --logfile=logs/ptestoptionallong.log configure: configure.ac src/bin/sage-version.sh m4/*.m4 ./bootstrap -d diff --git a/VERSION.txt b/VERSION.txt index 6c1859778de..e37de5854bd 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -Sage version 7.1.rc0, released 2016-03-10 +SageMath version 7.2.beta6, Release Date: 2016-04-28 diff --git a/build/bin/sage-uncompress-spkg b/build/bin/sage-uncompress-spkg index 0f88a21dfb0..81bc1db2b5e 100755 --- a/build/bin/sage-uncompress-spkg +++ b/build/bin/sage-uncompress-spkg @@ -18,28 +18,29 @@ if __name__ == '__main__': filename = sys.argv[1] if tarfile.is_tarfile(filename): # tar file, possibly compressed: - with tarfile.open(filename, 'r:*') as archive: - if len(sys.argv) == 2: - archive.extractall() - else: - member = sys.argv[2] - if member in archive.getnames(): - SPKG_TXT = archive.extractfile(member) - sys.stdout.write(SPKG_TXT.read()) - else: - exit(1) + archive = tarfile.open(filename, 'r:*') + if len(sys.argv) == 2: + archive.extractall() + else: + member = sys.argv[2] + if member in archive.getnames(): + SPKG_TXT = archive.extractfile(member) + sys.stdout.write(SPKG_TXT.read()) + exit(1) + archive.close() exit(0) if zipfile.is_zipfile(filename): # zip file: - with zipfile.ZipFile(filename, 'r') as archive: - if len(sys.argv) == 1: - archive.extractall() + archive = zipfile.ZipFile(filename, 'r') + if len(sys.argv) == 2: + archive.extractall() + else: + member = sys.argv[2] + if member in archive.namelist(): + sys.stdout.write(archive.read(member)) else: - member = sys.argv[2] - if member in archive.namelist(): - sys.stdout.write(archive.read(member)) - else: - exit(1) + exit(1) + archive.close() exit(0) else: print ('Error: Unknown file type: {}'.format(filename)) diff --git a/build/make/deps b/build/make/deps index 7473e103fa3..3195b87eeac 100644 --- a/build/make/deps +++ b/build/make/deps @@ -3,6 +3,9 @@ # $SAGE_ROOT/build/make/Makefile by $SAGE_ROOT/build/make/install ############################################################################### +# We need to be able to override this to support ./sage -i -c PKG +SAGE_SPKG = sage-spkg + STARTED = $(SAGE_LOCAL)/etc/sage-started.txt # Tell make not to look for files with these names: @@ -146,7 +149,7 @@ sagelib: \ $(EXTCODE) if [ -z "$$SAGE_INSTALL_FETCH_ONLY" ]; then \ cd $(SAGE_SRC) && source bin/sage-env && \ - sage-logger 'time $(MAKE) sage' '$(SAGE_LOGS)/sage-$(SAGE_VERSION).log'; \ + sage-logger 'time $(MAKE) sage' '$(SAGE_LOGS)/sagelib-$(SAGE_VERSION).log'; \ fi diff --git a/build/pkgs/alabaster/SPKG.txt b/build/pkgs/alabaster/SPKG.txt new file mode 100644 index 00000000000..b6f8231441d --- /dev/null +++ b/build/pkgs/alabaster/SPKG.txt @@ -0,0 +1,13 @@ += alabaster = + +== Description == + +Alabaster is a visually (c)lean, responsive, configurable theme for the Sphinx +documentation system. It is Python 2+3 compatible. + +It began as a third-party theme, and is still maintained separately, but as of +Sphinx 1.3, Alabaster is an install-time dependency of Sphinx and is selected +as the default theme. + +Live examples of this theme can be seen on paramiko.org, fabfile.org and +pyinvoke.org. diff --git a/build/pkgs/alabaster/checksums.ini b/build/pkgs/alabaster/checksums.ini new file mode 100644 index 00000000000..85eadfd1bd5 --- /dev/null +++ b/build/pkgs/alabaster/checksums.ini @@ -0,0 +1,4 @@ +tarball=alabaster-VERSION.tar.gz +sha1=ff3e575ee7eb4ba1721f17d91ec5a54b16283603 +md5=957c665d7126dea8121f98038debcba7 +cksum=738605178 diff --git a/build/pkgs/alabaster/dependencies b/build/pkgs/alabaster/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/alabaster/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/alabaster/package-version.txt b/build/pkgs/alabaster/package-version.txt new file mode 100644 index 00000000000..879be8a98fc --- /dev/null +++ b/build/pkgs/alabaster/package-version.txt @@ -0,0 +1 @@ +0.7.7 diff --git a/build/pkgs/alabaster/spkg-install b/build/pkgs/alabaster/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/alabaster/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/alabaster/type b/build/pkgs/alabaster/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/alabaster/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/babel/SPKG.txt b/build/pkgs/babel/SPKG.txt new file mode 100644 index 00000000000..ae0451d58be --- /dev/null +++ b/build/pkgs/babel/SPKG.txt @@ -0,0 +1,7 @@ += babel = + +== Description == + +Internationalization utilities + +A collection of tools for internationalizing Python applications. diff --git a/build/pkgs/babel/checksums.ini b/build/pkgs/babel/checksums.ini new file mode 100644 index 00000000000..adfcefcf9db --- /dev/null +++ b/build/pkgs/babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Babel-VERSION.tar.gz +sha1=e02392bc9a16f7672686bce23e4e3cdadcc1b1c8 +md5=1b69e4b2ab3795119266ccaa36b36f15 +cksum=2897183559 diff --git a/build/pkgs/babel/dependencies b/build/pkgs/babel/dependencies new file mode 100644 index 00000000000..247fe4de4d9 --- /dev/null +++ b/build/pkgs/babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip pytz + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/babel/package-version.txt b/build/pkgs/babel/package-version.txt new file mode 100644 index 00000000000..ccbccc3dc62 --- /dev/null +++ b/build/pkgs/babel/package-version.txt @@ -0,0 +1 @@ +2.2.0 diff --git a/build/pkgs/babel/spkg-install b/build/pkgs/babel/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/babel/type b/build/pkgs/babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/brial/patches/build/cygwin-gnucpp.patch b/build/pkgs/brial/patches/build/cygwin-gnucpp.patch new file mode 100644 index 00000000000..6fe26c130dd --- /dev/null +++ b/build/pkgs/brial/patches/build/cygwin-gnucpp.patch @@ -0,0 +1,10 @@ +--- a/common.mk 2016-04-07 15:53:44.907758600 +0200 ++++ b/common.mk 2016-04-07 15:54:02.599770500 +0200 +@@ -3,6 +3,6 @@ + -I$(top_srcdir)/libpolybori/include + + AM_CFLAGS = -std=c99 +-AM_CXXFLAGS = -std=c++98 -ftemplate-depth-100 ++AM_CXXFLAGS = -std=gnu++98 -ftemplate-depth-100 + + AM_DEFAULT_SOURCE_EXT = .cc diff --git a/build/pkgs/brial/patches/cygwin-gnucpp.patch b/build/pkgs/brial/patches/cygwin-gnucpp.patch new file mode 100644 index 00000000000..13593a51267 --- /dev/null +++ b/build/pkgs/brial/patches/cygwin-gnucpp.patch @@ -0,0 +1,51 @@ +diff --git a/Cudd/cudd/Makefile.in b/Cudd/cudd/Makefile.in +index 645eb7d..760278b +--- a/Cudd/cudd/Makefile.in ++++ b/Cudd/cudd/Makefile.in +@@ -350,7 +350,7 @@ AM_CPPFLAGS = \ + -I$(top_srcdir)/libpolybori/include + + AM_CFLAGS = -std=c99 +-AM_CXXFLAGS = -std=c++98 -ftemplate-depth-100 ++AM_CXXFLAGS = -std=gnu++98 -ftemplate-depth-100 + AM_DEFAULT_SOURCE_EXT = .cc + noinst_LTLIBRARIES = libcudd.la + libcudd_la_SOURCES = \ +diff --git a/common.mk b/common.mk +index 7915587..0afda02 100755 +--- a/common.mk ++++ b/common.mk +@@ -3,6 +3,6 @@ AM_CPPFLAGS = \ + -I$(top_srcdir)/libpolybori/include + + AM_CFLAGS = -std=c99 +-AM_CXXFLAGS = -std=c++98 -ftemplate-depth-100 ++AM_CXXFLAGS = -std=gnu++98 -ftemplate-depth-100 + + AM_DEFAULT_SOURCE_EXT = .cc +diff --git a/groebner/src/Makefile.in b/groebner/src/Makefile.in +index ef7882c..cb3b04f +--- a/groebner/src/Makefile.in ++++ b/groebner/src/Makefile.in +@@ -372,7 +372,7 @@ AM_CPPFLAGS = \ + -I$(top_srcdir)/libpolybori/include + + AM_CFLAGS = -std=c99 +-AM_CXXFLAGS = -std=c++98 -ftemplate-depth-100 ++AM_CXXFLAGS = -std=gnu++98 -ftemplate-depth-100 + AM_DEFAULT_SOURCE_EXT = .cc + lib_LTLIBRARIES = libpolybori_groebner.la + libpolybori_groebner_la_CXXFLAGS = $(AM_CXXFLAGS) $(SIMMD_CFLAGS) +diff --git a/libpolybori/src/Makefile.in b/libpolybori/src/Makefile.in +index 4e4e60a..4d110c2 +--- a/libpolybori/src/Makefile.in ++++ b/libpolybori/src/Makefile.in +@@ -323,7 +323,7 @@ AM_CPPFLAGS = \ + -I$(top_srcdir)/libpolybori/include + + AM_CFLAGS = -std=c99 +-AM_CXXFLAGS = -std=c++98 -ftemplate-depth-100 ++AM_CXXFLAGS = -std=gnu++98 -ftemplate-depth-100 + AM_DEFAULT_SOURCE_EXT = .cc + noinst_LTLIBRARIES = libpolybori_base.la + libpolybori_base_la_SOURCES = \ diff --git a/build/pkgs/cddlib/spkg-install b/build/pkgs/cddlib/spkg-install index 65c7b7d678e..a36d9de6684 100755 --- a/build/pkgs/cddlib/spkg-install +++ b/build/pkgs/cddlib/spkg-install @@ -28,7 +28,7 @@ cd src cp ../patches/random.{c,h} lib-src/ cp ../patches/random.{c,h} lib-src-gmp/ -# Required by sage.geometry.polyhedra +# Required by sage.geometry.polyhedron cp ../patches/cdd_both_reps.c src/ cp ../patches/cdd_both_reps.c src-gmp/cdd_both_reps.c diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index f6b8e7a6157..8631429cdde 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=4d12a9be7cddd42242462dbbc9457d7ed2d96217 -md5=929501b778370fb5c490260a924caa39 -cksum=1604852641 +sha1=dedd72dbd12d4b2d3101616a7946cacd7704046d +md5=fc0e4afaf65bd5f241380893d5f72d30 +cksum=3500657664 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index fa8f08cb6ff..a76256037d6 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -150 +160 diff --git a/build/pkgs/csdp/type b/build/pkgs/csdp/type index 9839eb20815..134d9bc32d5 100644 --- a/build/pkgs/csdp/type +++ b/build/pkgs/csdp/type @@ -1 +1 @@ -experimental +optional diff --git a/build/pkgs/cysignals/checksums.ini b/build/pkgs/cysignals/checksums.ini index 517a2c181c0..31292349524 100644 --- a/build/pkgs/cysignals/checksums.ini +++ b/build/pkgs/cysignals/checksums.ini @@ -1,4 +1,4 @@ tarball=cysignals-VERSION.tar.bz2 -sha1=392505d05d3cd849a94da614bd6d52aadf5a0db5 -md5=a2538b783d654f35d0c40d8a465470b0 -cksum=3111713488 +sha1=26b5fa9ac855af6ab33157fe7c8f095ca086063b +md5=31e95c68349b33b7781969e0d1ecab91 +cksum=939390582 diff --git a/build/pkgs/cysignals/package-version.txt b/build/pkgs/cysignals/package-version.txt index 7dea76edb3d..9084fa2f716 100644 --- a/build/pkgs/cysignals/package-version.txt +++ b/build/pkgs/cysignals/package-version.txt @@ -1 +1 @@ -1.0.1 +1.1.0 diff --git a/build/pkgs/cython/checksums.ini b/build/pkgs/cython/checksums.ini index d72432ed2d0..c2bc16bccae 100644 --- a/build/pkgs/cython/checksums.ini +++ b/build/pkgs/cython/checksums.ini @@ -1,4 +1,4 @@ tarball=Cython-VERSION.tar.gz -sha1=fc574c5050cd5a8e34435432e2a4a693353ed807 -md5=157df1f69bcec6b56fd97e0f2e057f6e -cksum=346066359 +sha1=32f12b8dd976111668ef8ba7e716a850869a0bf6 +md5=14fbc970f4a856845e633cbc09e61048 +cksum=2292891014 diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index be5da5b2830..baaee52d9b2 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.23.4.p0 +0.24.p0 diff --git a/build/pkgs/cython/patches/sig_includes.patch b/build/pkgs/cython/patches/sig_includes.patch deleted file mode 100644 index 8e5056e1947..00000000000 --- a/build/pkgs/cython/patches/sig_includes.patch +++ /dev/null @@ -1,66 +0,0 @@ -Additions to includes - -diff --git a/Cython/Includes/libc/setjmp.pxd b/Cython/Includes/libc/setjmp.pxd -index 74d1c59..f2283fe 100644 ---- a/Cython/Includes/libc/setjmp.pxd -+++ b/Cython/Includes/libc/setjmp.pxd -@@ -3,3 +3,8 @@ cdef extern from "setjmp.h" nogil: - pass - int setjmp(jmp_buf state) - void longjmp(jmp_buf state, int value) -+ -+ ctypedef struct sigjmp_buf: -+ pass -+ int sigsetjmp(sigjmp_buf state, int savesigs) -+ void siglongjmp(sigjmp_buf state, int value) -diff --git a/Cython/Includes/posix/select.pxd b/Cython/Includes/posix/select.pxd -new file mode 100644 -index 0000000..37bc9d3 ---- /dev/null -+++ b/Cython/Includes/posix/select.pxd -@@ -0,0 +1,19 @@ -+from .types cimport sigset_t -+from .time cimport timeval, timespec -+ -+cdef extern from "sys/select.h" nogil: -+ ctypedef struct fd_set: -+ pass -+ -+ int FD_SETSIZE -+ void FD_SET(int, fd_set*) -+ void FD_CLR(int, fd_set*) -+ bint FD_ISSET(int, fd_set*) -+ void FD_ZERO(fd_set*) -+ -+ int select(int nfds, fd_set *readfds, fd_set *writefds, -+ fd_set *exceptfds, const timeval *timeout) -+ -+ int pselect(int nfds, fd_set *readfds, fd_set *writefds, -+ fd_set *exceptfds, const timespec *timeout, -+ const sigset_t *sigmask) -diff --git a/Cython/Includes/posix/signal.pxd b/Cython/Includes/posix/signal.pxd -index 9168b2f..2d6cce8 100644 ---- a/Cython/Includes/posix/signal.pxd -+++ b/Cython/Includes/posix/signal.pxd -@@ -12,7 +12,7 @@ cdef extern from "signal.h" nogil: - int sigev_notify - int sigev_signo - sigval sigev_value -- void *sigev_notify_function(sigval) -+ void sigev_notify_function(sigval) - - ctypedef struct siginfo_t: - int si_signo -@@ -26,10 +26,10 @@ cdef extern from "signal.h" nogil: - sigval si_value - - cdef struct sigaction_t "sigaction": -- void *sa_handler(int) -+ void sa_handler(int) -+ void sa_sigaction(int, siginfo_t *, void *) - sigset_t sa_mask - int sa_flags -- void sa_sigaction(int, siginfo_t *, void *) - - enum: SA_NOCLDSTOP - enum: SIG_BLOCK diff --git a/build/pkgs/database_gap/checksums.ini b/build/pkgs/database_gap/checksums.ini index 422bc3bc2ad..aa3fa68ed29 100644 --- a/build/pkgs/database_gap/checksums.ini +++ b/build/pkgs/database_gap/checksums.ini @@ -1,4 +1,4 @@ tarball=database_gap-VERSION.tar.bz2 -sha1=46760a6a606eb1eea55b86f12d429a86c97740d7 -md5=87273df691a15bcf6b4b75b372cb6d0c -cksum=1428556919 +sha1=30f30b05f83eda0e092002e509b47768862a743f +md5=b443d7e7852471f83d19dd409b19a221 +cksum=4104886394 diff --git a/build/pkgs/database_gap/package-version.txt b/build/pkgs/database_gap/package-version.txt index 3c31ca93456..f99c6583c35 100644 --- a/build/pkgs/database_gap/package-version.txt +++ b/build/pkgs/database_gap/package-version.txt @@ -1 +1 @@ -4.7.9 +4.8.3 diff --git a/build/pkgs/database_gap/spkg-src b/build/pkgs/database_gap/spkg-src index bc1ad184900..2eacdbdd2ba 100755 --- a/build/pkgs/database_gap/spkg-src +++ b/build/pkgs/database_gap/spkg-src @@ -13,11 +13,11 @@ shopt -s extglob # Remove old sources and download new rm -rf src if [ -z "$UPSTREAM_SOURCE_TARBALL" ]; then - tar xjf <( curl http://www.gap-system.org/pub/gap/gap47/tar.bz2/gap4r7p8_2015_06_09-20_27.tar.bz2) + tar xf <( curl http://www.gap-system.org/pub/gap/gap47/tar.bz2/gap4r7p8_2015_06_09-20_27.tar.bz2) else - tar xjf "$UPSTREAM_SOURCE_TARBALL" + tar xf "$UPSTREAM_SOURCE_TARBALL" fi -GAP=`pwd`/gap4r7 +GAP=`pwd`/gap4r8 # Make everything writable chmod -R u+w "$GAP" diff --git a/build/pkgs/database_mutation_class/SPKG.txt b/build/pkgs/database_mutation_class/SPKG.txt new file mode 100644 index 00000000000..ae3facd5d3d --- /dev/null +++ b/build/pkgs/database_mutation_class/SPKG.txt @@ -0,0 +1,23 @@ += Mutation class database = + +== Description == + + Contains a database of all exceptional mutation classes of quivers. + + Every file in the database is of the form ``mutation_classes_n.dig6`` for some ``n`` and + - contains a ``cPickle.dump`` of a dictionary where + - the keys are tuples representing irreducible exceptional quiver mutation types of rank ``n``, and + - the values are all quivers in the given mutation class stored in canonical form as ``(dig6,edges)`` where + - ``dig6`` is the dig6 data of the given ``DiGraph``, and + - ``edges`` are the non-simply-laced edges thereof. + - is obtained by running the function + ``sage.combinat.cluster_algebra_quiver.quiver_mutation_type._save_data_dig6(n, types='Exceptional', verbose=False)`` + +== SPKG Maintainers == + + * C. Stump + +== Dependencies == + + * None + diff --git a/build/pkgs/database_mutation_class/checksums.ini b/build/pkgs/database_mutation_class/checksums.ini new file mode 100644 index 00000000000..cc4443b0928 --- /dev/null +++ b/build/pkgs/database_mutation_class/checksums.ini @@ -0,0 +1,4 @@ +tarball=database_mutation_class-VERSION.tar.gz +sha1=0e7eb2e89e1d4a612cb60e3d6be14d42f3e55678 +md5=8186b8f75eae76825399f3a9e6378c7b +cksum=364985299 diff --git a/build/pkgs/database_mutation_class/dependencies b/build/pkgs/database_mutation_class/dependencies new file mode 100644 index 00000000000..2f9f3849682 --- /dev/null +++ b/build/pkgs/database_mutation_class/dependencies @@ -0,0 +1 @@ +# no dependencies diff --git a/build/pkgs/database_mutation_class/package-version.txt b/build/pkgs/database_mutation_class/package-version.txt new file mode 100644 index 00000000000..d3827e75a5c --- /dev/null +++ b/build/pkgs/database_mutation_class/package-version.txt @@ -0,0 +1 @@ +1.0 diff --git a/build/pkgs/database_mutation_class/spkg-install b/build/pkgs/database_mutation_class/spkg-install new file mode 100755 index 00000000000..006731d5fef --- /dev/null +++ b/build/pkgs/database_mutation_class/spkg-install @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +cd src + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "SAGE_LOCAL undefined database_mutation_class-1.0 exiting" + echo >&2 "Maybe run 'sage --sh'?" + exit 1 +fi + +mkdir -p $SAGE_SHARE/cluster_algebra_quiver && cp src/* $SAGE_SHARE/cluster_algebra_quiver + +if [ $? -ne 0 ]; then + echo >&2 "Error: Failed to copy the data to $SAGE_SHARE/cluster_algebra_quiver. database_mutation_class-1.0 exiting" + exit 1 +fi + diff --git a/build/pkgs/database_mutation_class/type b/build/pkgs/database_mutation_class/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/database_mutation_class/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/ecl/checksums.ini b/build/pkgs/ecl/checksums.ini index 7b0e8c978e8..145f1351a3d 100644 --- a/build/pkgs/ecl/checksums.ini +++ b/build/pkgs/ecl/checksums.ini @@ -1,4 +1,4 @@ tarball=ecl-VERSION.tar.bz2 -sha1=d5b9f2f19847697f3cbe54e69daf609d1ea1b9ca -md5=ba1d8acd05b2921c556a488191ff4b6b -cksum=1247067343 +sha1=a2ba6f17df11e77149869d7d71c2175c284778ce +md5=fdca3688a9171518f301e4200f82532a +cksum=3217462385 diff --git a/build/pkgs/ecl/package-version.txt b/build/pkgs/ecl/package-version.txt index 74a10628100..43747ab3f96 100644 --- a/build/pkgs/ecl/package-version.txt +++ b/build/pkgs/ecl/package-version.txt @@ -1 +1 @@ -15.3.7p0 +15.3.7.p1 diff --git a/build/pkgs/ecl/patches/fix-cc.patch b/build/pkgs/ecl/patches/fix-cc.patch new file mode 100644 index 00000000000..5615a5e1c3b --- /dev/null +++ b/build/pkgs/ecl/patches/fix-cc.patch @@ -0,0 +1,29 @@ +From d23d974654fbef889aa1c110d6e2968e07b58930 Mon Sep 17 00:00:00 2001 +From: "Erik M. Bray" +Date: Thu, 17 Mar 2016 15:57:16 +0100 +Subject: [PATCH] Allow CC and other environment variables passed to + safe-run-program to be a command with arguments + +--- + src/cmp/cmpos-run.lsp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/cmp/cmpos-run.lsp b/src/cmp/cmpos-run.lsp +index 49f6605..b824409 100755 +--- a/src/cmp/cmpos-run.lsp ++++ b/src/cmp/cmpos-run.lsp +@@ -54,7 +54,10 @@ + (cmpnote "Invoking external command:~% ~A ~{~A ~}" program args) + (multiple-value-bind (stream result process) + (let* ((*standard-output* ext:+process-standard-output+) +- (*error-output* ext:+process-error-output+)) ++ (*error-output* ext:+process-error-output+) ++ (program (split-program-options program)) ++ (args `(,@(cdr program) ,@args)) ++ (program (car program))) + (with-current-directory + #-(and cygwin (not ecl-min)) + (ext:run-program program args :input nil :output t :error t :wait t) +-- +1.9.1 + diff --git a/build/pkgs/ecl/patches/gmp.patch b/build/pkgs/ecl/patches/src/gmp.patch similarity index 100% rename from build/pkgs/ecl/patches/gmp.patch rename to build/pkgs/ecl/patches/src/gmp.patch diff --git a/build/pkgs/ecl/patches/implib.patch b/build/pkgs/ecl/patches/src/implib.patch similarity index 100% rename from build/pkgs/ecl/patches/implib.patch rename to build/pkgs/ecl/patches/src/implib.patch diff --git a/build/pkgs/ecl/spkg-install b/build/pkgs/ecl/spkg-install index fafdf062527..202f03e7401 100755 --- a/build/pkgs/ecl/spkg-install +++ b/build/pkgs/ecl/spkg-install @@ -8,6 +8,17 @@ fi cd src +# Patch sources. Note that some patches (in the patches/src directory) +# have been applied to the source tarball. +for patch in ../patches/*.patch; do + [ -r "$patch" ] || continue # Skip non-existing or non-readable patches + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + exit 1 + fi +done + if [ -z "$CFLAG64" ] ; then CFLAG64=-m64 fi @@ -16,10 +27,6 @@ if [ -z "$CXXFLAG64" ] ; then CXXFLAG64=-m64 fi -# Do NOT quote SAGE_LOCAL here, as has caused problems. -# See: http://trac.sagemath.org/sage_trac/ticket/10187#comment:117 - -CPPFLAGS="$CPPFLAGS -I$SAGE_LOCAL/include" # Compile for 64-bit if SAGE64 is set to 'yes' if [ "x$SAGE64" = "xyes" ] ; then @@ -37,6 +44,11 @@ else CXXFLAGS="-g -O2 $CXXFLAGS" fi +export CFLAGS +export CXXFLAGS +export LDFLAGS + + # These are all used by GNU to specify compilers. echo "Using CC=$CC" echo "Using CXX=$CXX" @@ -49,17 +61,7 @@ echo "Using CPPFLAGS=$CPPFLAGS" echo "Using LDFLAGS=$LDFLAGS" echo "configure scripts and/or makefiles might override these later" echo "" -echo "Note that the patches were applied by spkg-src" -echo "" - -# export everything. Probably not necessary in most cases. -export CFLAGS -export CXXFLAGS -export CPPFLAGS -export LDFLAGS -# Building ECL in parallel doesn't work, so use only 1 thread -MAKE="$MAKE -j1" if [ "`uname -sm`" = "SunOS i86pc" ] && [ "x$SAGE64" = xyes ]; then # Need to add --with-dffi=no to disable assembly code on OpenSolaris x64. @@ -85,6 +87,10 @@ fi # Before running make we touch build/TAGS so its building process is never triggered touch build/TAGS +# Ensure that ECL will not ask interactive questions (for example, when +# pressing CTRL-C during the build) +exec &2 "Error - Failed to build ECL ... exiting" diff --git a/build/pkgs/ecl/spkg-src b/build/pkgs/ecl/spkg-src index 587331c6f72..d0cb70c6a0c 100755 --- a/build/pkgs/ecl/spkg-src +++ b/build/pkgs/ecl/spkg-src @@ -6,59 +6,52 @@ # and its subdirectories! # # HOW TO MAKE THE TARBALL: -# 1) copy upstream the tarball from gitlab; it will be named by the commit -# hash, e.g. ecl-a014bd2c23a9ba863ecdd28c1c48d67de04d3620.tar.gz -# Untar ECL tarball; the root dir will be named ecl.git/ -# 2) ./spkg-src +# 1) sage --sh build/pkgs/ecl/spkg-src # # needs autotools and sage in your PATH. # # AUTHOR: Jeroen Demeyer (November 2011) # Dima Pasechnik (July 2015) -# Sanity check: must be run from current directory -if ! [ -f spkg-src ]; then - echo >&2 "This script must be run from its own source directory!" +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" exit 1 fi # Exit on failure set -e -# now we automate the task: -ECLVERSION=`cat package-version.txt` -ECLTARBALL=ecl-"$ECLVERSION".tar +cd build/pkgs/ecl -mv ecl.git ecl-"$ECLVERSION" -cd ecl-"$ECLVERSION" +ECLVERSION=`cat package-version.txt |sed 's/[.]p.*//'` +ECLTARBALL="$SAGE_DISTFILES/ecl-$ECLVERSION.tar.bz2" -# the patches are applied here, as it has to be done before +# Remove old cruft +rm -rf ecl-* + +# Download and extract upstream tarball +sage-download-file https://common-lisp.net/project/ecl/files/release/$ECLVERSION/ecl-$ECLVERSION.tgz |tar xz +cd ecl-$ECLVERSION + +# Some patches are applied here, if they need to be applied before # running autoconf. -cd src echo "applying patches in ecl-$ECLVERSION/src" # For some of the patches, Cygwin also has upstream fixes that are -# closely related, keep track. See Trac 11119, for example. -for patch in ../../patches/*.patch; do - patch --verbose -p2 <"$patch" - if [ $? -ne 0 ]; then - echo >&2 "Error applying '$patch'" - exit 1 - fi +# closely related, keep track. See Trac #11119, for example. +for patch in ../patches/src/*.patch; do + patch -p1 <"$patch" done - -cd .. # Remove unneeded files to save space -rm -rf msvc/ +rm -r msvc src/gc-unstable src/gmp/ src/libffi + +# Run autotools cd src -rm -rf gc-unstable/ gmp/ libffi/ -libtoolize # to generate configure script +libtoolize autoreconf -ivf -cd ../../ -rm -f "$ECLTARBALL".* "$ECLTARBALL" -tar cf "$ECLTARBALL" ecl-"$ECLVERSION"/ -bzip2 "$ECLTARBALL" -mv -f "$ECLTARBALL".bz2 ../../../upstream/ -sage -sh sage-fix-pkg-checksums -rm -rf ecl-"$ECLVERSION"/ +cd ../.. +tar c ecl-$ECLVERSION | bzip2 -c >"$ECLTARBALL" +sage-fix-pkg-checksums "$ECLTARBALL" +rm -rf ecl-* diff --git a/build/pkgs/flask/SPKG.txt b/build/pkgs/flask/SPKG.txt new file mode 100644 index 00000000000..cf46f1da69e --- /dev/null +++ b/build/pkgs/flask/SPKG.txt @@ -0,0 +1,8 @@ += Flask = + +== Description == + +A microframework based on Werkzeug, Jinja2 and good intentions + +Flask is a microframework for Python based on Werkzeug, Jinja 2 and good +intentions. And before you ask: It’s BSD licensed! diff --git a/build/pkgs/flask/checksums.ini b/build/pkgs/flask/checksums.ini new file mode 100644 index 00000000000..f9167d09e4a --- /dev/null +++ b/build/pkgs/flask/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask-VERSION.tar.gz +sha1=d3d078262b053f4438e2ed3fd6f9b923c2c92172 +md5=378670fe456957eb3c27ddaef60b2b24 +cksum=2901487846 diff --git a/build/pkgs/flask/dependencies b/build/pkgs/flask/dependencies new file mode 100644 index 00000000000..4c9f9037ab5 --- /dev/null +++ b/build/pkgs/flask/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip werkzeug jinja2 itsdangerous + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask/package-version.txt b/build/pkgs/flask/package-version.txt new file mode 100644 index 00000000000..571215736a6 --- /dev/null +++ b/build/pkgs/flask/package-version.txt @@ -0,0 +1 @@ +0.10.1 diff --git a/build/pkgs/flask/spkg-install b/build/pkgs/flask/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/flask/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/flask/type b/build/pkgs/flask/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_autoindex/SPKG.txt b/build/pkgs/flask_autoindex/SPKG.txt new file mode 100644 index 00000000000..6ef8325067a --- /dev/null +++ b/build/pkgs/flask_autoindex/SPKG.txt @@ -0,0 +1,9 @@ += Flask-AutoIndex = + +== Description == + +The mod_autoindex for Flask + +Flask-AutoIndex generates an index page for your Flask application +automatically. The result just like mod_autoindex, but the look is more +awesome! diff --git a/build/pkgs/flask_autoindex/checksums.ini b/build/pkgs/flask_autoindex/checksums.ini new file mode 100644 index 00000000000..032dbb85894 --- /dev/null +++ b/build/pkgs/flask_autoindex/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_AutoIndex-VERSION.tar.gz +sha1=15e08bd3a516aa327ab4af767a29154a4bebdae3 +md5=24984602365704737468bb4d2586a739 +cksum=1140907747 diff --git a/build/pkgs/flask_autoindex/dependencies b/build/pkgs/flask_autoindex/dependencies new file mode 100644 index 00000000000..3904350ca14 --- /dev/null +++ b/build/pkgs/flask_autoindex/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask_silk + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_autoindex/package-version.txt b/build/pkgs/flask_autoindex/package-version.txt new file mode 100644 index 00000000000..2eb3c4fe4ee --- /dev/null +++ b/build/pkgs/flask_autoindex/package-version.txt @@ -0,0 +1 @@ +0.5 diff --git a/build/pkgs/flask_autoindex/spkg-install b/build/pkgs/flask_autoindex/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_autoindex/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_autoindex/type b/build/pkgs/flask_autoindex/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_autoindex/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_babel/SPKG.txt b/build/pkgs/flask_babel/SPKG.txt new file mode 100644 index 00000000000..f96f6411918 --- /dev/null +++ b/build/pkgs/flask_babel/SPKG.txt @@ -0,0 +1,6 @@ += Flask-Babel = + +== Description == + +Adds i18n/l10n support to Flask applications with the help of the Babel +library. diff --git a/build/pkgs/flask_babel/checksums.ini b/build/pkgs/flask_babel/checksums.ini new file mode 100644 index 00000000000..49652906818 --- /dev/null +++ b/build/pkgs/flask_babel/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Babel-VERSION.tar.gz +sha1=977d3b152f876e06c215f6bb72616b4ce138fa49 +md5=4762e0392303f464d53cbebedfb87ded +cksum=2286190911 diff --git a/build/pkgs/flask_babel/dependencies b/build/pkgs/flask_babel/dependencies new file mode 100644 index 00000000000..06da0f070bc --- /dev/null +++ b/build/pkgs/flask_babel/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask speaklater + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_babel/package-version.txt b/build/pkgs/flask_babel/package-version.txt new file mode 100644 index 00000000000..b63ba696b7a --- /dev/null +++ b/build/pkgs/flask_babel/package-version.txt @@ -0,0 +1 @@ +0.9 diff --git a/build/pkgs/flask_babel/spkg-install b/build/pkgs/flask_babel/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_babel/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_babel/type b/build/pkgs/flask_babel/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_babel/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_oldsessions/SPKG.txt b/build/pkgs/flask_oldsessions/SPKG.txt new file mode 100644 index 00000000000..2af9b2ebb23 --- /dev/null +++ b/build/pkgs/flask_oldsessions/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OldSessions = + +== Description == + +Provides a session class that works like the one in Flask before 0.10. diff --git a/build/pkgs/flask_oldsessions/checksums.ini b/build/pkgs/flask_oldsessions/checksums.ini new file mode 100644 index 00000000000..c5fc0993ff0 --- /dev/null +++ b/build/pkgs/flask_oldsessions/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OldSessions-VERSION.tar.gz +sha1=1c0bbcd79b4fc626da2fd34ce9b59b2d43f6d81e +md5=3d731d343d5380bb9f502742ad62df50 +cksum=709943870 diff --git a/build/pkgs/flask_oldsessions/dependencies b/build/pkgs/flask_oldsessions/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/flask_oldsessions/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_oldsessions/package-version.txt b/build/pkgs/flask_oldsessions/package-version.txt new file mode 100644 index 00000000000..68c123cf10e --- /dev/null +++ b/build/pkgs/flask_oldsessions/package-version.txt @@ -0,0 +1 @@ +0.10 diff --git a/build/pkgs/flask_oldsessions/spkg-install b/build/pkgs/flask_oldsessions/spkg-install new file mode 100755 index 00000000000..56301585aa0 --- /dev/null +++ b/build/pkgs/flask_oldsessions/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd *flask-oldsessions* && python setup.py install diff --git a/build/pkgs/flask_oldsessions/type b/build/pkgs/flask_oldsessions/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_oldsessions/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_openid/SPKG.txt b/build/pkgs/flask_openid/SPKG.txt new file mode 100644 index 00000000000..77aa5b1c152 --- /dev/null +++ b/build/pkgs/flask_openid/SPKG.txt @@ -0,0 +1,5 @@ += Flask-OpenID = + +== Description == + +OpenID support for Flask diff --git a/build/pkgs/flask_openid/checksums.ini b/build/pkgs/flask_openid/checksums.ini new file mode 100644 index 00000000000..a17be5fa13a --- /dev/null +++ b/build/pkgs/flask_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_OpenID-VERSION.tar.gz +sha1=18d39e03417cd2b577cd5c1f5c2ac117493f3fef +md5=a40c63df701ec634450d03490ddfb6c1 +cksum=995771756 diff --git a/build/pkgs/flask_openid/dependencies b/build/pkgs/flask_openid/dependencies new file mode 100644 index 00000000000..5367d7a02a6 --- /dev/null +++ b/build/pkgs/flask_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask python_openid + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_openid/package-version.txt b/build/pkgs/flask_openid/package-version.txt new file mode 100644 index 00000000000..c813fe116c9 --- /dev/null +++ b/build/pkgs/flask_openid/package-version.txt @@ -0,0 +1 @@ +1.2.5 diff --git a/build/pkgs/flask_openid/spkg-install b/build/pkgs/flask_openid/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_openid/type b/build/pkgs/flask_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flask_silk/SPKG.txt b/build/pkgs/flask_silk/SPKG.txt new file mode 100644 index 00000000000..92d3009a886 --- /dev/null +++ b/build/pkgs/flask_silk/SPKG.txt @@ -0,0 +1,5 @@ += Flask-Silk = + +== Description == + +Adds silk icons to your Flask application or blueprint, or extension. diff --git a/build/pkgs/flask_silk/checksums.ini b/build/pkgs/flask_silk/checksums.ini new file mode 100644 index 00000000000..a54c85cdfd6 --- /dev/null +++ b/build/pkgs/flask_silk/checksums.ini @@ -0,0 +1,4 @@ +tarball=Flask_Silk-VERSION.tar.gz +sha1=cef42b469c9ebb69a766d0cd33ad27480800d518 +md5=aca545a94063dc4acd21779ea5dde330 +cksum=1557295080 diff --git a/build/pkgs/flask_silk/dependencies b/build/pkgs/flask_silk/dependencies new file mode 100644 index 00000000000..05fe89830e0 --- /dev/null +++ b/build/pkgs/flask_silk/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip flask + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/flask_silk/package-version.txt b/build/pkgs/flask_silk/package-version.txt new file mode 100644 index 00000000000..3b04cfb60da --- /dev/null +++ b/build/pkgs/flask_silk/package-version.txt @@ -0,0 +1 @@ +0.2 diff --git a/build/pkgs/flask_silk/spkg-install b/build/pkgs/flask_silk/spkg-install new file mode 100755 index 00000000000..2ae379ae348 --- /dev/null +++ b/build/pkgs/flask_silk/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd Flask* && python setup.py install diff --git a/build/pkgs/flask_silk/type b/build/pkgs/flask_silk/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/flask_silk/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/flint/package-version.txt b/build/pkgs/flint/package-version.txt index f225a78adf0..aec037fdf29 100644 --- a/build/pkgs/flint/package-version.txt +++ b/build/pkgs/flint/package-version.txt @@ -1 +1 @@ -2.5.2 +2.5.2.p0 diff --git a/build/pkgs/flint/patches/flintxx-include.patch b/build/pkgs/flint/patches/flintxx-include.patch new file mode 100644 index 00000000000..cb6744e2364 --- /dev/null +++ b/build/pkgs/flint/patches/flintxx-include.patch @@ -0,0 +1,19 @@ +Fix include errors with flintxx + +https://github.com/wbhart/flint2/commit/c0768dc54b5ece38252857f8e7423d5ce81a43f7 + + + +diff --git a/flintxx/flint_classes.h b/flintxx/flint_classes.h +index eac2d1a..6baa392 100644 +--- a/flintxx/flint_classes.h ++++ b/flintxx/flint_classes.h +@@ -27,7 +27,7 @@ + // Contrary to other parts of this library, they are tailored very + // specifically towards FLINT. + +-#include "flint.h" ++#include "../flint.h" + #include "mp.h" + #include "expression.h" + #include "expression_traits.h" diff --git a/build/pkgs/gap/checksums.ini b/build/pkgs/gap/checksums.ini index 23f00dc5b40..f564f9e0d9f 100644 --- a/build/pkgs/gap/checksums.ini +++ b/build/pkgs/gap/checksums.ini @@ -1,4 +1,4 @@ tarball=gap-VERSION.tar.bz2 -sha1=a200b6c5188e5c3464b14cbf37b32dabb78d8687 -md5=0e2a80bc711601d7f3320fe563fff86a -cksum=2560320934 +sha1=2d7ca54d7c38dbfdc5bad6d5fe3304119f71cbc9 +md5=61fe8c83b7ca80a5212a79bbe5803467 +cksum=2414776374 diff --git a/build/pkgs/gap/package-version.txt b/build/pkgs/gap/package-version.txt index 3c31ca93456..f99c6583c35 100644 --- a/build/pkgs/gap/package-version.txt +++ b/build/pkgs/gap/package-version.txt @@ -1 +1 @@ -4.7.9 +4.8.3 diff --git a/build/pkgs/gap/patches/testall.patch b/build/pkgs/gap/patches/testall.patch deleted file mode 100644 index aae2c1e1130..00000000000 --- a/build/pkgs/gap/patches/testall.patch +++ /dev/null @@ -1,29 +0,0 @@ -Remove tests that rely on the (non-GPL) small group library - -diff -ur old/tst/testinstall.g src/tst/testinstall.g ---- old/tst/testinstall.g 2012-06-04 23:58:40.000000000 +0100 -+++ src/tst/testinstall.g 2012-09-02 10:43:10.321694748 +0100 -@@ -49,7 +49,6 @@ - [ "ctblfuns.tst", 3900000 ], - [ "ctblmoli.tst", 98500000 ], - [ "ctblmono.tst", 33400000 ], -- [ "ctblsolv.tst", 54000000 ], - [ "cyclotom.tst", 900000 ], - [ "ffe.tst", 3600000 ], - [ "ffeconway.tst", 50200000 ], -@@ -57,7 +56,6 @@ - [ "grpfp.tst", 146700000 ], - [ "grpfree.tst", 700000 ], - [ "grpmat.tst", 481000000 ], -- [ "grppc.tst", 45300000 ], - [ "grppcnrm.tst", 2333400000 ], - [ "infinity.tst", 1000 ], - [ "intarith.tst", 2300000 ], -@@ -65,7 +63,6 @@ - [ "mapping.tst", 37300000 ], - [ "mgmring.tst", 1800000 ], - [ "modfree.tst", 5800000 ], -- [ "morpheus.tst", 87200000 ], - [ "onecohom.tst", 50600000 ], - [ "oprt.tst", 2000000 ], - [ "ratfun.tst", 800000 ], diff --git a/build/pkgs/gap/spkg-install b/build/pkgs/gap/spkg-install index f3eaf4cf2a7..840ba077f5a 100755 --- a/build/pkgs/gap/spkg-install +++ b/build/pkgs/gap/spkg-install @@ -112,3 +112,8 @@ if [[ "$SAGE_SPKG_INSTALL_DOCS" = yes ]]; then exit 1 fi fi + +# Delete tests that rely on the non-GPL small group library +rm "$INSTALL_DIR"/tst/testinstall/ctblsolv.tst +rm "$INSTALL_DIR"/tst/testinstall/grppc.tst +rm "$INSTALL_DIR"/tst/testinstall/morpheus.tst diff --git a/build/pkgs/gap/spkg-src b/build/pkgs/gap/spkg-src index 8073688c563..b6eae67dc5a 100755 --- a/build/pkgs/gap/spkg-src +++ b/build/pkgs/gap/spkg-src @@ -13,11 +13,11 @@ shopt -s extglob # Remove old sources and download new rm -rf src if [ -z "$UPSTREAM_SOURCE_TARBALL" ]; then - tar xjf <( curl http://www.gap-system.org/pub/gap/gap47/tar.bz2/gap4r7p8_2015_06_09-20_27.tar.bz2) + tar xf <( curl http://www.gap-system.org/pub/gap/gap47/tar.bz2/gap4r7p8_2015_06_09-20_27.tar.bz2) else - tar xjf "$UPSTREAM_SOURCE_TARBALL" + tar xf "$UPSTREAM_SOURCE_TARBALL" fi -mv gap4r7 src +mv gap4r8 src # Make everything writable diff --git a/build/pkgs/gap3/SPKG.txt b/build/pkgs/gap3/SPKG.txt new file mode 100644 index 00000000000..64ab64243cc --- /dev/null +++ b/build/pkgs/gap3/SPKG.txt @@ -0,0 +1,73 @@ += Jean Michel's GAP 3 distribution = + +== Description == + +This package installs Jean Michel's pre-packaged GAP3, which is a minimal GAP3 +distribution containing packages that have no equivalent in GAP4. + +Below is the full description from Jean Michel's webpage +(accessed 23 July 2015). + + A pre-packaged GAP3 with everything you need + + To help people who are just interested in GAP3 because they need a package + which has not been ported to GAP4, I have prepared an easy-to install + minimal GAP3 distribution containing an up-to-date versions of the + packages: + + anusq, arep, autag, chevie, cryst, dce, grim, matrix, meataxe, monoid, + nq, pcqa, sisyphos, specht, ve, vkcurve. + + These packages have been chosen since most have no equivalent in GAP4. They + are autoloaded when starting gap. + + This distribution includes only partial lists of small groups, 2-groups, + 3-groups, character tables from the Atlas and tables of marks. It does not + include either the packages: + + anupq, grape, kbmag, xgap, cohomolo, gliss, guava, xmod + + which have some equivalent in GAP4. You can get these extra features at + + http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3 + + In this distribution: + + - The on-line help includes the documentation of the included packages. + - The html documentation (htm/index.html) also does. + - The manual (manual.pdf) also does. + +== License == + +Most parts of the GAP distribution, including the core part of the GAP system, +are distributed under the terms of the GNU General Public License (see +http://www.gnu.org/licenses/gpl.html or the file GPL in the etc directory of +the GAP installation). + +== SPKG Maintainers == + +* Christian Stump + +== Upstream Contact == + +Jean Michel +http://webusers.imj-prg.fr/~jean.michel/ + +== Special Update/Build Instructions == + +The difference between the distributed tarball and Jean Michel's +original tarball is that all binary files have been deleted one by one +from the listing obtained by + + grep -r -m 1 "^" . | grep "^Binary file" + +=== Patches === + + * gap3_makefile.patch: modifies src/src/Makefile such that if autodetects + the system + + * gap3_startup.patch: modifies src/bin/gap.sh to set GAP_DIR and GAP_PRG + +== Dependencies === + +None diff --git a/build/pkgs/gap3/checksums.ini b/build/pkgs/gap3/checksums.ini new file mode 100644 index 00000000000..3515bf26703 --- /dev/null +++ b/build/pkgs/gap3/checksums.ini @@ -0,0 +1,4 @@ +tarball=gap3-VERSION.tar.gz +sha1=0d64737250f3e028716881c9d7e82338c80244d0 +md5=569bf72d516c1da183d43f09851fa1bb +cksum=3760972699 diff --git a/build/pkgs/gap3/dependencies b/build/pkgs/gap3/dependencies new file mode 100644 index 00000000000..2f9f3849682 --- /dev/null +++ b/build/pkgs/gap3/dependencies @@ -0,0 +1 @@ +# no dependencies diff --git a/build/pkgs/gap3/package-version.txt b/build/pkgs/gap3/package-version.txt new file mode 100644 index 00000000000..fc668243c5a --- /dev/null +++ b/build/pkgs/gap3/package-version.txt @@ -0,0 +1 @@ +jm5-2015-02-01 diff --git a/build/pkgs/gap3/patches/gap3_init.patch b/build/pkgs/gap3/patches/gap3_init.patch new file mode 100644 index 00000000000..fbe72a5bdc5 --- /dev/null +++ b/build/pkgs/gap3/patches/gap3_init.patch @@ -0,0 +1,33 @@ +Commenting out the optional packages +RequirePackage("arep"); +RequirePackage("meataxe"); +RequirePackage("nq"); +RequirePackage("sisyphos"); +RequirePackage("ve"); + +diff -ur src/gap3/lib/init.g.orig src/gap3/lib/init.g +--- src/gap3/lib/init.g.orig 2016-03-30 17:58:09.557517250 +0200 ++++ src/gap3/lib/init.g 2016-03-30 17:58:23.265517363 +0200 +@@ -1142,17 +1142,17 @@ + Print(" gap3-jm5 final 21Mar2016 -- see webusers.imj-prg.fr/~jmichel/gap3\n"); + Print(" Minimal distribution -- loading the following packages:\n"); + fi; +-RequirePackage("anusq"); ++#RequirePackage("anusq"); + RequirePackage("arep"); + RequirePackage("autag"); + RequirePackage("cryst"); + RequirePackage("dce"); + RequirePackage("grim"); + RequirePackage("matrix"); +-RequirePackage("meataxe"); ++#RequirePackage("meataxe"); + RequirePackage("monoid"); +-RequirePackage("nq"); ++#RequirePackage("nq"); + RequirePackage("pcqa"); +-RequirePackage("sisyphos"); +-RequirePackage("ve"); ++#RequirePackage("sisyphos"); ++#RequirePackage("ve"); + RequirePackage("chevie"); # requires vkcurve, specht, algebra diff --git a/build/pkgs/gap3/patches/gap3_makefile.patch b/build/pkgs/gap3/patches/gap3_makefile.patch new file mode 100644 index 00000000000..57b5a336712 --- /dev/null +++ b/build/pkgs/gap3/patches/gap3_makefile.patch @@ -0,0 +1,153 @@ +Adding the option to autodetect the system and running "make" without +any options specifying the system. + +diff -ur src/gap3/src/Makefile.orig src/gap3/src/Makefile +--- src/gap3/src/Makefile.orig 2016-03-24 11:35:24.027893512 +0100 ++++ src/gap3/src/Makefile 2016-03-24 11:35:07.515893376 +0100 +@@ -48,6 +48,9 @@ + ## DJGPP differently you have to change this below. + ## + #H $Log: Makefile,v $ ++#H Revision 2016/03/11 Christian Stump ++#H Autouse Linux/Darvin and 32bit/64bit. ++#H + #H Revision 2014/08/22 Jean Michel + #H remove obsolete targets. Add 64bit target. + #H +@@ -105,103 +108,40 @@ + ## + SHELL = /bin/sh + +-############################################################################# +-## +-#T Targets . . . . . targets of the form --- +-## +-## Following are the targets that the user can specify. Each target calls +-## 'make' recursively twice. +-## +-## The first time it calls +-## +-## @$(MAKE) .o CFLAGS="$(COPTS) " +-## +-## where is either 'system' or the name of another system file +-## and where are the options to pass to pass to the C compiler, +-## e.g., 'SYS_IS_BSD' or 'SYS_IS_MSDOS'. +-## +-## The second time it calls +-## +-## @$(MAKE) gap CC= CFLAGS="$(COPTS) " \ +-## SYS_FILE=.o LOPTS="$(LOPTS)" +-## +-## where is the same file as above, is the name +-## of the C compiler to use ($(CC) is used if this is not present), +-## are the options to pass ot the C compiler, e.g., '-O2'. +-## +-unknown: +- @echo "usage: 'make ' where target is one of" +- @echo "'x86linux-gcc64' for x86linux with gcc" +- @echo "'x86linux-gcc' for x86linux with gcc in 32bit mode" +- @echo "'macosx-gcc' for mac os x with gcc" +- @echo "'x86bsd-gcc' for x86BSD with gcc" +- @echo "'sun-sparc-solaris-gcc' for SUN under Solaris with GNU cc" +- @echo "'sun-sparc-solaris-cc' for SUN under Solaris with cc" +- @echo "'bsd' for others under Berkeley UNIX with cc" +- @echo " " +- @echo " additional C compiler and linker flags can be passed with" +- @echo " 'make COPTS= LOPTS='," +- @echo " i.e., 'make x86linux-gcc COPTS=-g LOPTS=-g for debug'" +- +-x86linux-gcc: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -m32 -O2 -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO -DSYS_HAS_IOCTL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -m32 -O " SYS_FILE=system.o LOPTS="$(LOPTS) -m32 -static" +- strip gap +- mv gap ../bin/gap.x86linux +- +-x86linux-gccd: +- @$(MAKE) system.o CC=gcc CFLAGS="-g -m32 -O2 -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO -DSYS_HAS_IOCTL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="-g -m32 -O " SYS_FILE=system.o LOPTS="-g -m32 -static" +- +-x86linux-gcc64: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -O -DSYS_IS_64_BIT -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO -DSYS_HAS_IOCTL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O -DSYS_IS_64_BIT " SYS_FILE=system.o LOPTS="$(LOPTS) -static" +- strip gap +- mv gap ../bin/gap.x86linux64 +- +-x86-dos-djgpp: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -DSYS_IS_MSDOS_DJGPP -DSYS_HAS_MISC_PROTO" +- @$(MAKE) gapdjg.exe CC=gcc CFLAGS="$(COPTS) -O1" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-x86-dos-djgppcross: +- @$(MAKE) system.o CC=dos-gcc CFLAGS="$(COPTS) -DSYS_IS_MSDOS_DJGPP -DSYS_HAS_MISC_PROTO" +- @$(MAKE) gapdjg.exe CC=dos-gcc CFLAGS="$(COPTS) -O1" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-# sbrk doesn't work in macosx so we need to use vm_allocate +-macosx-gcc: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -m32 -DSYS_IS_MACOSX -DARCH_INCLUDE -DSYS_HAS_IOCTL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -m32 -O2" SYS_FILE=system.o LOPTS="$(LOPTS) -m32" ++ARCH = $(shell getconf LONG_BIT) ++UNAME_S = $(shell uname -s) + +-# sbrk doesn't work in macosx so we need to use vm_allocate +-# This is using Frank Luebeck's optimized compile....but it doesn't +-# change the GAPstones for the files in ../tst +-macosx-gcc-686-optimized: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -m32 -fomit-frame-pointer -pipe -fno-strength-reduce -march=i686 -falign-loops=2 -falign-jumps=2 -falign-functions=2 -DCPU=686 -g -O2 -DSYS_IS_MACOSX -DARCH_INCLUDE -DSYS_HAS_IOCTL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -m32 -fomit-frame-pointer -pipe -fno-strength-reduce -march=i686 -falign-loops=2 -falign-jumps=2 -falign-functions=2 -DCPU=686 -g -O3" SYS_FILE=system.o LOPTS="$(LOPTS) -m32" +- +-sun-sparc-solaris-cc: +- @$(MAKE) system.o CC=cc CFLAGS="$(COPTS) -O -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSOLARIS2" +- @echo "Don't worry about 'out of range' and 'overflow' warnings" +- @echo "(29 in total)" +- @$(MAKE) gap CC=cc CFLAGS="$(COPTS) -O2" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-sun-sparc-solaris-gcc2: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -O6 -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSOLARIS2" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O6" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-# 'sys/times.h' claims 'times' returns 'clock_t' (how shall it return -1?) +-sun-sparc-sunos-gcc: +- @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO" +- @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O2" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-# 'sys/times.h' claims 'times' returns 'clock_t' (how shall it return -1?) +-sun-sparc-sunos-cc: +- @$(MAKE) system.o CFLAGS="$(COPTS) -DSYS_IS_USG -DSYS_HAS_TIME_PROTO" +- @$(MAKE) gap CFLAGS="$(COPTS) -O" SYS_FILE=system.o LOPTS="$(LOPTS)" +- +-bsd: +- @$(MAKE) system.o CC=$(CC) CFLAGS="$(COPTS) -DSYS_IS_BSD" +- @$(MAKE) gap CC=$(CC) CFLAGS="$(COPTS) -O" SYS_FILE=system.o LOPTS="$(LOPTS)" ++unknown: ++ @echo "###########################################################" ++ @echo "# Building GAP3 for '$(UNAME_S)' and '$(ARCH) bit'" ++ @echo "###########################################################" ++ ++ifeq ($(UNAME_S),Linux) ++ ifeq ($(ARCH),64) ++ @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -O -DSYS_IS_64_BIT -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO -DSYS_HAS_IOCTL_PROTO" ++ @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O -DSYS_IS_64_BIT " SYS_FILE=system.o LOPTS="$(LOPTS) -static" ++ mv gap ../bin/gap3 ++ else ++ @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -O2 -DSYS_IS_USG -DSYS_HAS_TIME_PROTO -DSYS_HAS_SIGNAL_PROTO -DSYS_HAS_IOCTL_PROTO" ++ @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O " SYS_FILE=system.o LOPTS="$(LOPTS) -static" ++ mv gap ../bin/gap3 ++ endif ++endif ++ifeq ($(UNAME_S),Darwin) ++ ifeq ($(ARCH),64) ++ @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -DSYS_IS_MACOSX -DSYS_IS_64_BIT -DARCH_INCLUDE -DSYS_HAS_IOCTL_PROTO" ++ @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O2 -DSYS_IS_MACOSX -DSYS_IS_64_BIT " SYS_FILE=system.o LOPTS="$(LOPTS)" ++ mv gap ../bin/gap3 ++ else ++ @$(MAKE) system.o CC=gcc CFLAGS="$(COPTS) -DSYS_IS_MACOSX -DARCH_INCLUDE -DSYS_HAS_IOCTL_PROTO" ++ @$(MAKE) gap CC=gcc CFLAGS="$(COPTS) -O2 -DSYS_IS_MACOSX " SYS_FILE=system.o LOPTS="$(LOPTS)" ++ mv gap ../bin/gap3 ++ endif ++endif ++ ++ @echo "###########################################################" ++ @echo "# Building complete, you can run GAP3 from './../bin/gap3'" ++ @echo "###########################################################" + + ############################################################################# + ## diff --git a/build/pkgs/gap3/patches/gap3_startup.patch b/build/pkgs/gap3/patches/gap3_startup.patch new file mode 100644 index 00000000000..33377b55307 --- /dev/null +++ b/build/pkgs/gap3/patches/gap3_startup.patch @@ -0,0 +1,54 @@ +Set +* GAP_DIR (using the SAGE_LOCAL environment variable), +* GAP_MEM, and +* GAP_PRG +in the startup script gap.sh. +GAP_DIR is set + +diff -ur src/gap3/bin/gap.sh.orig src/gap3/bin/gap.sh +--- src/gap3/bin/gap.sh.orig 2016-03-10 22:02:49.836772326 +0100 ++++ src/gap3/bin/gap.sh 2016-03-10 22:13:15.096777500 +0100 +@@ -1,4 +1,10 @@ + #!/bin/bash ++ ++if [ $SAGE_LOCAL"" = "" ]; then ++ echo "Set the environment variable SAGE_LOCAL." ++ exit 1 ++fi ++ + ############################################################################# + ## + ## gap.sh GAP Martin Schoenert +@@ -19,7 +25,7 @@ + ## You have to change this unless you have installed GAP in this location. + ## + #GAP_DIR=/usr/local/lib/gap3-jm5 +-GAP_DIR=/home/jmichel/gap3-jm5 ++GAP_DIR="$SAGE_LOCAL/gap3/latest/gap3" + + ############################################################################# + ## +@@ -31,7 +37,13 @@ + ## If you are not going to run GAP in parallel with other programs you may + ## want to set this value close to the amount of memory your computer has. + ## +-GAP_MEM=512m ++ ++ARCH=`getconf LONG_BIT` ++if [[ "$ARCH" == '32' ]]; then ++ GAP_MEM=512m ++elif [[ "$ARCH" == '64' ]]; then ++ GAP_MEM=1024m ++fi + + ############################################################################# + ## +@@ -44,7 +56,7 @@ + ## gap.mac for Mac OSX on powerPC + ## gap.macx86 for Mac OSX on X86 + ## +-GAP_PRG=gap.x86linux64 ++GAP_PRG=gap3 + + ############################################################################# + ## diff --git a/build/pkgs/gap3/spkg-install b/build/pkgs/gap3/spkg-install new file mode 100755 index 00000000000..adf45381476 --- /dev/null +++ b/build/pkgs/gap3/spkg-install @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +# Installation script for gap3; based on the installation script for gap4. + +########################################### +## SETUP +########################################### + +PACKAGE_SHORTNAME=gap3 +VERSION=`cat package-version.txt` +GAP3_DIR="gap-$VERSION" +INSTALL_DIR="$SAGE_LOCAL/$PACKAGE_SHORTNAME/$GAP3_DIR" + +echo "spkg-install is using" +echo "VERSION = $VERSION" +echo "GAP3_DIR = $GAP3_DIR" +echo "INSTALL_DIR = $INSTALL_DIR" + +########################################### +## MODIFY UPSTREAM +########################################### + +echo "Applying patches..." +for patch in patches/*.patch; do + [ -r "$patch" ] || continue # Skip non-existing or non-readable patches + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + exit 1 + fi +done + +########################################### +## INSTALLATION +########################################### + +cd gap3/src +$MAKE +if [ $? -ne 0 ]; then + echo >&2 "Error building $PACKAGE_SHORTNAME." + exit 1 +fi +cd ../.. + +echo "Installing (copying) files..." +mkdir -p "$INSTALL_DIR" && +cp -rf * "$INSTALL_DIR" +if [[ $? -ne 0 ]]; then + echo >&2 "Error copying $PACKAGE_SHORTNAME files." + exit 1 +fi + +echo "Creating symlink to new $PACKAGE_SHORTNAME installation..." +rm -f "$SAGE_LOCAL/$PACKAGE_SHORTNAME/latest" +ln -s "$GAP3_DIR" "$SAGE_LOCAL/$PACKAGE_SHORTNAME/latest" + +echo "Copying GAP3 startup script..." +rm -f "$SAGE_LOCAL/bin/gap3" +cp gap3/bin/gap.sh "$SAGE_LOCAL/bin/gap3" +if [[ $? -ne 0 ]]; then + echo >&2 "Error copying customized $PACKAGE_SHORTNAME startup script." + exit 1 +fi diff --git a/build/pkgs/gap3/spkg-src b/build/pkgs/gap3/spkg-src new file mode 100644 index 00000000000..0908464c064 --- /dev/null +++ b/build/pkgs/gap3/spkg-src @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# Script to prepare a GAP3 tarball for Sage from the semi-official +# version on Jean Michel's website. +# +# This script is only for the package maintainer, +# not for building GAP3 during a Sage install. +# +# WARNING: This script will delete/overwrite files in this directory +# and its subdirectories! +# +# HOW TO MAKE THE TARBALL: +# 1) sage --sh build/pkgs/gap3/spkg-src +# +# needs sage in your PATH. +# +# AUTHOR: Christian Stump (March 2016) + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +PACKAGENAME="gap3" +DOWNLOADVERSION="$PACKAGENAME-jm5" +TARSOURCE="$DOWNLOADVERSION.tar.gz" + +cd build/pkgs/$PACKAGENAME + +UPLOADVERSION=`cat package-version.txt` +TARTARGET="$SAGE_DISTFILES/$PACKAGENAME-$UPLOADVERSION.tar.gz" + +echo "### Remove old gap3 folder and tarball ###" +rm -f $TARSOURCE +rm -rf $DOWNLOADVERSION + +echo "### Download and extract upstream tarball ###" +wget --no-check-certificate https://webusers.imj-prg.fr/~jean.michel/$PACKAGENAME/$TARSOURCE +echo "### Untar'ing the upstream tarball ###" +tar -zxvf $TARSOURCE +echo "### Remove downloaded tarball ###" +rm -f $TARSOURCE + +echo "### Moving the files to its place $PACKAGENAME ###" +mv $DOWNLOADVERSION $PACKAGENAME +cd $PACKAGENAME + +echo "### Removing the packages ###" +echo "### anusq, arep, meataxe, nq, sisyphos, ve###" +rm -r pkg/anusq +# The function MovedPointsPerm? defined in arep/lib/tools.g is used in chevie, so it cannot be deleted completely +rm -r pkg/arep/bin pkg/arep/src +rm -r pkg/meataxe +rm -r pkg/nq +rm -r pkg/sisyphos +rm -r pkg/ve +echo "### Removing all remaining binaries ###" +grep -r -m 1 "^" . | grep "^Binary file" | xargs rm -f +cd .. + +echo "### Tar'ing the archive to $TARTARGET ###" +tar -cvzf $TARTARGET $PACKAGENAME +echo "### Fixing the checksum ###" +sage-fix-pkg-checksums "$TARTARGET" +echo "### Removing untar'ed archive ###" +rm -rf $PACKAGENAME diff --git a/build/pkgs/gap3/type b/build/pkgs/gap3/type new file mode 100644 index 00000000000..9839eb20815 --- /dev/null +++ b/build/pkgs/gap3/type @@ -0,0 +1 @@ +experimental diff --git a/build/pkgs/gap_packages/checksums.ini b/build/pkgs/gap_packages/checksums.ini index 324be6397cc..1b0c126a2e3 100644 --- a/build/pkgs/gap_packages/checksums.ini +++ b/build/pkgs/gap_packages/checksums.ini @@ -1,4 +1,4 @@ tarball=gap_packages-VERSION.tar.bz2 -sha1=cede660a6a38c8f5308fda85726a8fb871258b97 -md5=1707c0c0a34e9431fad5a5e573bb150b -cksum=3250869172 +sha1=0edefeb2049c073687a139126e845f20be5b0b3d +md5=5d394a7898a0b41ed43a7a6fe84de904 +cksum=3488636948 diff --git a/build/pkgs/gap_packages/package-version.txt b/build/pkgs/gap_packages/package-version.txt index 3c31ca93456..f99c6583c35 100644 --- a/build/pkgs/gap_packages/package-version.txt +++ b/build/pkgs/gap_packages/package-version.txt @@ -1 +1 @@ -4.7.9 +4.8.3 diff --git a/build/pkgs/gap_packages/patches/decoders_gi.patch b/build/pkgs/gap_packages/patches/decoders_gi.patch deleted file mode 100644 index 4eecf913312..00000000000 --- a/build/pkgs/gap_packages/patches/decoders_gi.patch +++ /dev/null @@ -1,64 +0,0 @@ -diff --git b/guava-3.12/lib/decoders.gi a/guava-3.12/lib/decoders.gi -index 1ef83da..230ffe2 100644 ---- b/guava-3.12/lib/decoders.gi -+++ a/guava-3.12/lib/decoders.gi -@@ -61,7 +61,7 @@ end); - InstallMethod(Decode, "method for linear code, codeword", true, - [IsLinearCode, IsCodeword], 0, - function(C, v) -- local c, S, syn, index, corr, Gt, i, x, F; -+ local ok, c, S, syn, index, corr, Gt, i, x, F; - if v in C then - return InformationWord(C,v); - fi; -@@ -72,10 +72,16 @@ function(C, v) - F := LeftActingDomain(C); - S := SyndromeTable(C); - syn := Syndrome(C, c); -- index := 0; -- repeat -- index := index + 1; -- until S[index][2] = syn; -+ ok := false; -+ for index in [1..Length(S)] do -+ if IsBound(S[index]) and S[index][2] = syn then -+ ok := true; -+ break; -+ fi; -+ od; -+ if not ok then # this should never happen! -+ Error("In Decodeword: index not found"); -+ fi; - #This is a hack. The subtraction operation for codewords is causing an error - #and rather than trying to understand the method selection process, I'm brute- - #forcing things... -@@ -135,7 +141,7 @@ end); - InstallMethod(Decodeword, "method for linear code, codeword", true, - [IsLinearCode, IsCodeword], 0, - function(C, v) -- local c, c0, S, syn, index, corr, Gt, i, x, F; -+ local ok, c, c0, S, syn, index, corr, Gt, i, x, F; - if v in C then - return v; - fi; -@@ -153,10 +159,16 @@ function(C, v) - F := LeftActingDomain(C); - S := SyndromeTable(C); - syn := Syndrome(C, c); -- index := 0; -- repeat -- index := index + 1; -- until S[index][2] = syn; -+ ok := false; -+ for index in [1..Length(S)] do -+ if IsBound(S[index]) and S[index][2] = syn then -+ ok := true; -+ break; -+ fi; -+ od; -+ if not ok then # this should never happen! -+ Error("In Decodeword: index not found"); -+ fi; - corr := VectorCodeword(c - S[index][1]); # correct codeword - return Codeword(corr,F); - end); diff --git a/build/pkgs/gap_packages/patches/guava/Makefile.in b/build/pkgs/gap_packages/patches/guava/Makefile.in deleted file mode 100644 index 96ab49ac201..00000000000 --- a/build/pkgs/gap_packages/patches/guava/Makefile.in +++ /dev/null @@ -1,186 +0,0 @@ -CC = gcc -CFLAGS = -O2 -I/usr/include/ # needed for osx -DEFINES = -DINT_SIZE=32 -SRCDIR = ../../src/leon/src - -GAP_PATH=../.. -PKG_PATH=.. -SRCDISTFILE=guava - -all: - ( test -d bin || mkdir bin; \ - test -d bin/@GAPARCH@ || mkdir bin/@GAPARCH@; cd bin/@GAPARCH@; \ - $(MAKE) -f ../../Makefile all2 CC="$(CC)" CFLAGS="$(CFLAGS)"; \ - cp wtdist ../wtdist; cp desauto ../desauto ) - -all2: leonconv desauto wtdist - -# rules to make the executables, just link them together -leonconv: leonconv.o - $(CC) $(CFLAGS) -o leonconv leonconv.o -desauto: desauto.o addsgen.o bitmanp.o cdesauto.o chbase.o cmatauto.o \ - code.o compcrep.o compsg.o copy.o cparstab.o cstborb.o cstrbas.o \ - errmesg.o essentia.o factor.o field.o inform.o matrix.o new.o \ - oldcopy.o optsvec.o orbit.o orbrefn.o partn.o permgrp.o permut.o \ - primes.o ptstbref.o randgrp.o randschr.o readdes.o readgrp.o \ - readper.o rprique.o storage.o token.o util.o - $(CC) $(CFLAGS) -o desauto \ - desauto.o addsgen.o bitmanp.o cdesauto.o chbase.o cmatauto.o \ - code.o compcrep.o compsg.o copy.o cparstab.o cstborb.o cstrbas.o \ - errmesg.o essentia.o factor.o field.o inform.o matrix.o new.o \ - oldcopy.o optsvec.o orbit.o orbrefn.o partn.o permgrp.o permut.o \ - primes.o ptstbref.o randgrp.o randschr.o readdes.o readgrp.o \ - readper.o rprique.o storage.o token.o util.o -wtdist: wtdist.o bitmanp.o code.o copy.o errmesg.o essentia.o factor.o \ - field.o new.o partn.o permgrp.o permut.o primes.o readdes.o \ - storage.o token.o util.o - $(CC) $(CFLAGS) -o wtdist \ - wtdist.o bitmanp.o code.o copy.o errmesg.o essentia.o factor.o \ - field.o new.o partn.o permgrp.o permut.o primes.o readdes.o \ - storage.o token.o util.o - -# rules to make the .o files, just compile the .c file -# cannot use implicit rule, because .c files are in a different directory -leonconv.o: ../../src/leonconv.c - $(CC) -c $(CFLAGS) -o leonconv.o -c ../../src/leonconv.c -addsgen.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/essentia.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/cstborb.h $(SRCDIR)/addsgen.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/addsgen.c -bitmanp.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/bitmanp.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/bitmanp.c -ccent.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/cparstab.h $(SRCDIR)/errmesg.h $(SRCDIR)/inform.h $(SRCDIR)/new.h $(SRCDIR)/orbrefn.h $(SRCDIR)/permut.h $(SRCDIR)/randgrp.h $(SRCDIR)/storage.h $(SRCDIR)/ccent.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/ccent.c -ccommut.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/addsgen.h $(SRCDIR)/copy.h $(SRCDIR)/chbase.h $(SRCDIR)/new.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/storage.h $(SRCDIR)/ccommut.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/ccommut.c -cdesauto.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/code.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/matrix.h $(SRCDIR)/storage.h $(SRCDIR)/cdesauto.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cdesauto.c -cent.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/ccent.h $(SRCDIR)/errmesg.h $(SRCDIR)/permgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readper.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/cent.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cent.c -chbase.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/addsgen.h $(SRCDIR)/cstborb.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/factor.h $(SRCDIR)/new.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/randgrp.h $(SRCDIR)/storage.h $(SRCDIR)/repinimg.h $(SRCDIR)/settoinv.h $(SRCDIR)/chbase.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/chbase.c -cinter.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/orbrefn.h $(SRCDIR)/cinter.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cinter.c -cjrndper.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/code.h $(SRCDIR)/copy.h $(SRCDIR)/errmesg.h $(SRCDIR)/matrix.h $(SRCDIR)/new.h $(SRCDIR)/permut.h $(SRCDIR)/readdes.h $(SRCDIR)/randgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readpar.h $(SRCDIR)/readper.h $(SRCDIR)/readpts.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/cjrndper.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cjrndper.c -cmatauto.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/code.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/matrix.h $(SRCDIR)/storage.h $(SRCDIR)/cmatauto.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cmatauto.c -code.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/storage.h $(SRCDIR)/code.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/code.c -commut.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/ccommut.h $(SRCDIR)/errmesg.h $(SRCDIR)/factor.h $(SRCDIR)/permgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readper.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/commut.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/commut.c -compcrep.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/cputime.h $(SRCDIR)/chbase.h $(SRCDIR)/cstrbas.h $(SRCDIR)/errmesg.h $(SRCDIR)/inform.h $(SRCDIR)/new.h $(SRCDIR)/optsvec.h $(SRCDIR)/orbit.h $(SRCDIR)/orbrefn.h $(SRCDIR)/partn.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/rprique.h $(SRCDIR)/storage.h $(SRCDIR)/compcrep.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/compcrep.c -compgrp.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/readgrp.h $(SRCDIR)/util.h $(SRCDIR)/compgrp.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/compgrp.c -compsg.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/cputime.h $(SRCDIR)/addsgen.h $(SRCDIR)/chbase.h $(SRCDIR)/copy.h $(SRCDIR)/cstrbas.h $(SRCDIR)/errmesg.h $(SRCDIR)/inform.h $(SRCDIR)/new.h $(SRCDIR)/optsvec.h $(SRCDIR)/orbit.h $(SRCDIR)/orbrefn.h $(SRCDIR)/partn.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/ptstbref.h $(SRCDIR)/rprique.h $(SRCDIR)/storage.h $(SRCDIR)/compsg.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/compsg.c -copy.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/essentia.h $(SRCDIR)/storage.h $(SRCDIR)/copy.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/copy.c -cparstab.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/orbrefn.h $(SRCDIR)/cparstab.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cparstab.c -csetstab.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/orbrefn.h $(SRCDIR)/csetstab.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/csetstab.c -cstborb.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/factor.h $(SRCDIR)/storage.h $(SRCDIR)/cstborb.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cstborb.c -cstrbas.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/chbase.h $(SRCDIR)/cstborb.h $(SRCDIR)/inform.h $(SRCDIR)/new.h $(SRCDIR)/orbrefn.h $(SRCDIR)/permgrp.h $(SRCDIR)/ptstbref.h $(SRCDIR)/optsvec.h $(SRCDIR)/storage.h $(SRCDIR)/cstrbas.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cstrbas.c -cuprstab.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/compcrep.h $(SRCDIR)/compsg.h $(SRCDIR)/errmesg.h $(SRCDIR)/orbrefn.h $(SRCDIR)/cuprstab.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/cuprstab.c -desauto.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/cdesauto.h $(SRCDIR)/errmesg.h $(SRCDIR)/permgrp.h $(SRCDIR)/readdes.h $(SRCDIR)/readgrp.h $(SRCDIR)/readper.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/desauto.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/desauto.c -errmesg.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/errmesg.c -essentia.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/essentia.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/essentia.c -factor.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/factor.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/factor.c -field.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/storage.h $(SRCDIR)/field.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/field.c -fndelt.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/oldcopy.h $(SRCDIR)/permut.h $(SRCDIR)/readgrp.h $(SRCDIR)/readper.h $(SRCDIR)/randgrp.h $(SRCDIR)/util.h $(SRCDIR)/fndelt.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/fndelt.c -generate.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/enum.h $(SRCDIR)/storage.h $(SRCDIR)/cputime.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/readgrp.h $(SRCDIR)/randschr.h $(SRCDIR)/stcs.h $(SRCDIR)/util.h $(SRCDIR)/generate.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/generate.c -inform.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/cputime.h $(SRCDIR)/readgrp.h $(SRCDIR)/inform.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/inform.c -inter.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/cinter.h $(SRCDIR)/errmesg.h $(SRCDIR)/permgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readper.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/inter.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/inter.c -matrix.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/storage.h $(SRCDIR)/matrix.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/matrix.c -new.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/partn.h $(SRCDIR)/storage.h $(SRCDIR)/new.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/new.c -oldcopy.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/storage.h $(SRCDIR)/oldcopy.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/oldcopy.c -optsvec.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/cstborb.h $(SRCDIR)/essentia.h $(SRCDIR)/new.h $(SRCDIR)/permut.h $(SRCDIR)/permgrp.h $(SRCDIR)/storage.h $(SRCDIR)/optsvec.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/optsvec.c -orbdes.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/chbase.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/oldcopy.h $(SRCDIR)/permut.h $(SRCDIR)/readdes.h $(SRCDIR)/readgrp.h $(SRCDIR)/storage.h $(SRCDIR)/util.h $(SRCDIR)/orbdes.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/orbdes.c -orbit.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/cstborb.h $(SRCDIR)/orbit.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/orbit.c -orblist.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/addsgen.h $(SRCDIR)/chbase.h $(SRCDIR)/cstborb.h $(SRCDIR)/errmesg.h $(SRCDIR)/factor.h $(SRCDIR)/new.h $(SRCDIR)/randgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readpar.h $(SRCDIR)/readpts.h $(SRCDIR)/storage.h $(SRCDIR)/util.h $(SRCDIR)/orblist.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/orblist.c -orbrefn.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/partn.h $(SRCDIR)/storage.h $(SRCDIR)/orbrefn.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/orbrefn.c -partn.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/storage.h $(SRCDIR)/permgrp.h $(SRCDIR)/partn.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/partn.c -permgrp.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/copy.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/new.h $(SRCDIR)/permut.h $(SRCDIR)/storage.h $(SRCDIR)/permgrp.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/permgrp.c -permut.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/factor.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/storage.h $(SRCDIR)/repimg.h $(SRCDIR)/repinimg.h $(SRCDIR)/settoinv.h $(SRCDIR)/enum.h $(SRCDIR)/permut.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/permut.c -primes.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/primes.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/primes.c -ptstbref.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/partn.h $(SRCDIR)/cstrbas.h $(SRCDIR)/errmesg.h $(SRCDIR)/ptstbref.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/ptstbref.c -randgrp.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/new.h $(SRCDIR)/permut.h $(SRCDIR)/randgrp.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/randgrp.c -randobj.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/randgrp.h $(SRCDIR)/readpar.h $(SRCDIR)/readpts.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/randobj.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/randobj.c -randschr.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/addsgen.h $(SRCDIR)/cstborb.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/factor.h $(SRCDIR)/new.h $(SRCDIR)/oldcopy.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/randgrp.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/randschr.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/randschr.c -readdes.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/code.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/token.h $(SRCDIR)/readdes.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/readdes.c -readgrp.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/chbase.h $(SRCDIR)/cstborb.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/factor.h $(SRCDIR)/permut.h $(SRCDIR)/permgrp.h $(SRCDIR)/randschr.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/readgrp.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/readgrp.c -readpar.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/errmesg.h $(SRCDIR)/readpar.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/readpar.c -readper.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/readgrp.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/readper.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/readper.c -readpts.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/errmesg.h $(SRCDIR)/readpts.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/readpts.c -relator.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/enum.h $(SRCDIR)/errmesg.h $(SRCDIR)/new.h $(SRCDIR)/permut.h $(SRCDIR)/stcs.h $(SRCDIR)/storage.h $(SRCDIR)/relator.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/relator.c -rprique.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/storage.h $(SRCDIR)/rprique.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/rprique.c -setstab.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/cparstab.h $(SRCDIR)/csetstab.h $(SRCDIR)/cuprstab.h $(SRCDIR)/errmesg.h $(SRCDIR)/permgrp.h $(SRCDIR)/readgrp.h $(SRCDIR)/readpar.h $(SRCDIR)/readper.h $(SRCDIR)/readpts.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/setstab.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/setstab.c -stcs.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/enum.h $(SRCDIR)/repimg.h $(SRCDIR)/addsgen.h $(SRCDIR)/cstborb.h $(SRCDIR)/errmesg.h $(SRCDIR)/essentia.h $(SRCDIR)/factor.h $(SRCDIR)/new.h $(SRCDIR)/oldcopy.h $(SRCDIR)/permgrp.h $(SRCDIR)/permut.h $(SRCDIR)/randschr.h $(SRCDIR)/relator.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/stcs.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/stcs.c -storage.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/errmesg.h $(SRCDIR)/storage.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/storage.c -token.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/token.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/token.c -util.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/util.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/util.c -wtdist.o: $(SRCDIR)/group.h $(SRCDIR)/extname.h $(SRCDIR)/groupio.h $(SRCDIR)/errmesg.h $(SRCDIR)/field.h $(SRCDIR)/readdes.h $(SRCDIR)/storage.h $(SRCDIR)/token.h $(SRCDIR)/util.h $(SRCDIR)/wt.h $(SRCDIR)/swt.h $(SRCDIR)/wtdist.c - $(CC) -c $(CFLAGS) -I $(SRCDIR) $(DEFINES) $(SRCDIR)/wtdist.c - -# pseudo targets -clean: - ( cd bin/@GAPARCH@; rm -f *.o ) - -distclean: - ( rm -rf bin/@GAPARCH@ ) - -# for GAP distribution -src_dist: - @(cmp ${PKG_PATH}/guava/doc/guava.tex \ - ${GAP_PATH}/doc/guava.tex \ - || echo \ - "*** WARNING: current 'guava.tex' and 'doc/guava.tex' differ ***") - @zoo ah ${SRCDISTFILE}.zoo \ - ${PKG_PATH}/guava/Makefile \ - ${PKG_PATH}/guava/doc/guava.tex \ - ${PKG_PATH}/guava/init.g \ - `find ${PKG_PATH}/guava/lib -name "*.g" -print` \ - `find ${PKG_PATH}/guava/tbl -name "*.g" -print` \ - `find ${PKG_PATH}/guava/src -print` - @zoo PE ${SRCDISTFILE}.zoo - diff --git a/build/pkgs/gap_packages/patches/leongroup_h.patch b/build/pkgs/gap_packages/patches/leongroup_h.patch deleted file mode 100644 index 1a80bf6f6b9..00000000000 --- a/build/pkgs/gap_packages/patches/leongroup_h.patch +++ /dev/null @@ -1,18 +0,0 @@ -diff --git b/guava-3.12/src/leon/src/group.h a/guava-3.12/src/leon/src/group.h -index 1e86e00..0f6c021 100644 ---- b/guava-3.12/src/leon/src/group.h -+++ a/guava-3.12/src/leon/src/group.h -@@ -103,11 +103,11 @@ that special symbol. */ - #define SCANF_Int_FORMAT "%u" - - #ifndef MAX_NAME_LENGTH --#define MAX_NAME_LENGTH 64 -+#define MAX_NAME_LENGTH 256 - #endif - - #ifndef MAX_FILE_NAME_LENGTH --#define MAX_FILE_NAME_LENGTH 60 -+#define MAX_FILE_NAME_LENGTH 256 - #endif - - #ifndef DEFAULT_MAX_BASE_SIZE diff --git a/build/pkgs/gap_packages/spkg-install b/build/pkgs/gap_packages/spkg-install index 1f83910ea17..a4334c55642 100755 --- a/build/pkgs/gap_packages/spkg-install +++ b/build/pkgs/gap_packages/spkg-install @@ -39,10 +39,9 @@ for patch in ../patches/*.patch; do done -# HAPprime should be added in the future? for p in \ - crime ctbllib design factint grape \ - guava-3.12 Hap1.11 HAPcryst laguna polymaking \ + aclib crime ctbllib cryst crystcat design factint grape \ + guava-3.13 Hap1.11 HAPcryst happrime laguna polymaking \ sonata toric1.8 polycyclic-2.11 autpgrp Alnuth-3.0.0 atlasrep do echo "Copying package $p" @@ -70,7 +69,7 @@ fi # Build GUAVA package -cd "$PKG_DIR/guava-3.12" +cd "$PKG_DIR/guava-3*" ./configure "$GAP_DIR" if [ $? -ne 0 ]; then echo >&2 "Error configuring GUAVA packagae." diff --git a/build/pkgs/gap_packages/spkg-src b/build/pkgs/gap_packages/spkg-src index 4ef3747241d..276e2260718 100755 --- a/build/pkgs/gap_packages/spkg-src +++ b/build/pkgs/gap_packages/spkg-src @@ -13,11 +13,11 @@ shopt -s extglob # Remove old sources and download new rm -rf src if [ -z "$UPSTREAM_SOURCE_TARBALL" ]; then - tar xjf <( curl http://www.gap-system.org/pub/gap/gap47/tar.bz2/gap4r7p8_2015_06_09-20_27.tar.bz2) + tar xjf <( curl http://www.gap-system.org/pub/gap/gap48/tar.bz2/gap4r8p3_2016_03_19-22_17.tar.bz2) else - tar xjf "$UPSTREAM_SOURCE_TARBALL" + tar xf "$UPSTREAM_SOURCE_TARBALL" fi -GAP=`pwd`/gap4r7 +GAP=`pwd`/gap4r8 # Make everything writable chmod -R u+w "$GAP" @@ -25,7 +25,7 @@ chmod -R u+w "$GAP" mkdir src for pkg in \ - crime ctbllib design factint grape \ + aclib crime ctbllib design factint grape \ guava Hap HAPcryst laguna polymaking \ sonata toric polycyclic autpgrp Alnuth atlasrep do @@ -33,7 +33,14 @@ do pkg_dir=`ls -d "$GAP/pkg/$pkg"*` pkg_lower=`echo $pkg | tr [:upper:] [:lower:]` cp -rap "$pkg_dir" src/ - cp -p "$GAP/pkg/README.$pkg_lower" src/ +done + +for pkg in \ + happrime cryst crystcat +do + echo "Copying package $pkg" + pkg_dir=`ls -d "$GAP/pkg/$pkg"` + cp -rap "$pkg_dir" src/ done # Delete unnecessary crap diff --git a/build/pkgs/giac/checksums.ini b/build/pkgs/giac/checksums.ini index 5887e3e1e1a..c9a80d969eb 100644 --- a/build/pkgs/giac/checksums.ini +++ b/build/pkgs/giac/checksums.ini @@ -1,4 +1,4 @@ tarball=giac-VERSION.tar.gz -sha1=b5eccec59cd0cc1c302c0e0eb55a4e506e0ec199 -md5=09179e0c32fbb019b70c1713aeb82cc8 -cksum=1123738831 +sha1=815c55649a3d000a25e83f7448252526e1a78add +md5=37b80267c6a4d2d573c60838ac9b34bf +cksum=1328209448 diff --git a/build/pkgs/giac/package-version.txt b/build/pkgs/giac/package-version.txt index ecab4b56453..605d2267dee 100644 --- a/build/pkgs/giac/package-version.txt +++ b/build/pkgs/giac/package-version.txt @@ -1 +1 @@ -1.2.0.19 +1.2.2.37 diff --git a/build/pkgs/giac/patches/cSolveorder-check.patch b/build/pkgs/giac/patches/cSolveorder-check.patch new file mode 100644 index 00000000000..922d2efc11d --- /dev/null +++ b/build/pkgs/giac/patches/cSolveorder-check.patch @@ -0,0 +1,11 @@ +--- a/check/TP16-sol.cas.out1 2016-03-04 16:45:42.000000000 +0100 ++++ b/check/TP16-sol.cas.out1 2016-03-24 17:58:52.913308919 +0100 +@@ -48,7 +48,7 @@ + "Done", + [-sqrt(13)-1,sqrt(13)-1,4], + y^2+6*sqrt(13)+18,y^2-6*sqrt(13)+18,y^2, +--sqrt(6)*I*sqrt(sqrt(13)+3),sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*sqrt(sqrt(13)-3),sqrt(6)*sqrt(sqrt(13)-3),0, ++sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*I*sqrt(sqrt(13)+3),-sqrt(6)*sqrt(sqrt(13)-3),sqrt(6)*sqrt(sqrt(13)-3),0, + "No such variable u", + x^2+1/4*y^2+1/9*z^2-1, + x^2+y^2+z^2-u, diff --git a/build/pkgs/giac/spkg-src b/build/pkgs/giac/spkg-src index a57098d4ae2..a730a989e8b 100755 --- a/build/pkgs/giac/spkg-src +++ b/build/pkgs/giac/spkg-src @@ -14,8 +14,8 @@ fi set -e # TODO on the next update: l71 switch from gz to bz2 as wished in #18826 -VERSION="1.2.0" -VERSIONREV="19" +VERSION="1.2.2" +VERSIONREV="37" # The upstream tarball name is: giac"$SOURCEORIG".tar.gz SOURCEORIG=_"$VERSION"-"$VERSIONREV" diff --git a/build/pkgs/giacpy/checksums.ini b/build/pkgs/giacpy/checksums.ini index 0d5300e00c8..06016617840 100644 --- a/build/pkgs/giacpy/checksums.ini +++ b/build/pkgs/giacpy/checksums.ini @@ -1,4 +1,4 @@ tarball=giacpy-VERSION.tar.gz -sha1=a27b4684c3e456246a04d44c12461ce9bd157763 -md5=8cd131d34313671848f3f8294b72418e -cksum=1582753592 +sha1=67693f9a60601fc8c4a0c0ba41300f88679ed070 +md5=6ebb254d597eb00b95b2b36ae4dbbdbf +cksum=3344860417 diff --git a/build/pkgs/giacpy/dependencies b/build/pkgs/giacpy/dependencies index 8abac09ba9e..ca9f5905e73 100644 --- a/build/pkgs/giacpy/dependencies +++ b/build/pkgs/giacpy/dependencies @@ -1,4 +1,4 @@ -cython giac | sympy maxima +cython giac cysignals | sympy maxima The doctest suite (SAGE_CHECK=yes) also uses Sympy and Maxima ---------- diff --git a/build/pkgs/giacpy/package-version.txt b/build/pkgs/giacpy/package-version.txt index 4b9fcbec101..d1d899fa33a 100644 --- a/build/pkgs/giacpy/package-version.txt +++ b/build/pkgs/giacpy/package-version.txt @@ -1 +1 @@ -0.5.1 +0.5.5 diff --git a/build/pkgs/giacpy/spkg-check b/build/pkgs/giacpy/spkg-check index 921f2ca2e5d..6b67299ac5f 100755 --- a/build/pkgs/giacpy/spkg-check +++ b/build/pkgs/giacpy/spkg-check @@ -6,10 +6,9 @@ if [ -z "$SAGE_LOCAL" ]; then exit 1 fi -cd src # --force-lib because we dont want sage -t to compile giacpy.pyx # we only use the test suite to test giacpy.so -sage -t --verbose --force-lib giacpy.pyx +sage -t --verbose --force-lib src/giacpy.pyx if [ $? -ne 0 ]; then echo "There was a problem during the giacpy tests." diff --git a/build/pkgs/glpk/package-version.txt b/build/pkgs/glpk/package-version.txt index 3ff3c47de84..276bf7be8e4 100644 --- a/build/pkgs/glpk/package-version.txt +++ b/build/pkgs/glpk/package-version.txt @@ -1 +1 @@ -4.55.p0 +4.55.p1 diff --git a/build/pkgs/glpk/patches/have_error.patch b/build/pkgs/glpk/patches/have_error.patch new file mode 100644 index 00000000000..34ac080a64f --- /dev/null +++ b/build/pkgs/glpk/patches/have_error.patch @@ -0,0 +1,96 @@ +Add a function glp_at_error() to check whether we are currently +handling an error message. + +diff -ru glpk-4.55/doc/glpk06.tex glpk-4.55-patched//doc/glpk06.tex +--- glpk-4.55/doc/glpk06.tex 2014-08-22 08:00:00.000000000 +0200 ++++ glpk-4.55-patched//doc/glpk06.tex 2015-11-05 10:01:32.244898918 +0100 +@@ -303,6 +303,23 @@ + a global jump using the standard function \verb|longjmp|, in which case + the application program {\it must} call the routine \verb|glp_free_env|. + ++\subsection{glp\_have\_error --- are we currently handling an error?} ++ ++\synopsis ++ ++\begin{verbatim} ++ int glp_at_error(void); ++\end{verbatim} ++ ++\description ++ ++The routine \verb|glp_at_error| returns 0 in normal operation and 1 ++if GLPK is currently handling an error. ++ ++This can be used by a custom output handler (installed with ++\verb|glp_term_hook|) to determine whether or not the message to be ++displayed is an error message. ++ + \subsection{glp\_alloc --- allocate memory block} + + \synopsis +diff -ru glpk-4.55/src/env/env.h glpk-4.55-patched//src/env/env.h +--- glpk-4.55/src/env/env.h 2014-08-22 08:00:00.000000000 +0200 ++++ glpk-4.55-patched//src/env/env.h 2015-11-05 09:57:19.049622663 +0100 +@@ -161,6 +161,9 @@ + void glp_error_hook(void (*func)(void *info), void *info); + /* install hook to intercept abnormal termination */ + ++int glp_at_error(void); ++/* are we currently handling an error? */ ++ + #define put_err_msg _glp_put_err_msg + void put_err_msg(const char *msg); + /* provide error message string */ +diff -ru glpk-4.55/src/env/error.c glpk-4.55-patched//src/env/error.c +--- glpk-4.55/src/env/error.c 2014-08-22 08:00:00.000000000 +0200 ++++ glpk-4.55-patched//src/env/error.c 2015-11-05 09:59:57.588718768 +0100 +@@ -47,6 +47,7 @@ + va_end(arg); + xprintf("Error detected in file %s at line %d\n", + env->err_file, env->err_line); ++ env->err_file = NULL; + if (env->err_hook != NULL) + env->err_hook(env->err_info); + abort(); +@@ -120,6 +121,28 @@ + } + + /*********************************************************************** ++* NAME ++* ++* glp_at_error - are we currently handling an error? ++* ++* SYNOPSIS ++* ++* int glp_at_error(void); ++* ++* DESCRIPTION ++* ++* The routine glp_at_error returns 0 in normal operation and 1 if ++* GLPK is currently handling an error. ++* ++* This can be used by a term hook to determine whether or not the ++* message to be displayed is an error message. */ ++ ++int glp_at_error(void) ++{ ENV *env = get_env_ptr(); ++ return env->err_file != NULL; ++} ++ ++/*********************************************************************** + * NAME + * + * put_err_msg - provide error message string +diff -ru glpk-4.55/src/glpk.h glpk-4.55-patched//src/glpk.h +--- glpk-4.55/src/glpk.h 2014-08-22 08:00:00.000000000 +0200 ++++ glpk-4.55-patched//src/glpk.h 2015-11-05 09:57:11.291421102 +0100 +@@ -839,6 +839,9 @@ + void glp_error_hook(void (*func)(void *info), void *info); + /* install hook to intercept abnormal termination */ + ++int glp_at_error(void); ++/* are we currently handling an error? */ ++ + #define glp_malloc(size) glp_alloc(1, size) + /* allocate memory block (obsolete) */ + diff --git a/build/pkgs/imagesize/SPKG.txt b/build/pkgs/imagesize/SPKG.txt new file mode 100644 index 00000000000..2ec0272eda7 --- /dev/null +++ b/build/pkgs/imagesize/SPKG.txt @@ -0,0 +1,5 @@ += imagesize = + +== Description == + +It parses image files' header and return image size. diff --git a/build/pkgs/imagesize/checksums.ini b/build/pkgs/imagesize/checksums.ini new file mode 100644 index 00000000000..c4b13a984c2 --- /dev/null +++ b/build/pkgs/imagesize/checksums.ini @@ -0,0 +1,4 @@ +tarball=imagesize-VERSION.tar.gz +sha1=cf95cf234c88d5400acfa922cdc3c1f5b6a7e713 +md5=23cb4b0b5ec76fb26942d6cc0b1cf31c +cksum=40173098 diff --git a/build/pkgs/imagesize/dependencies b/build/pkgs/imagesize/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/imagesize/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/imagesize/package-version.txt b/build/pkgs/imagesize/package-version.txt new file mode 100644 index 00000000000..faef31a4357 --- /dev/null +++ b/build/pkgs/imagesize/package-version.txt @@ -0,0 +1 @@ +0.7.0 diff --git a/build/pkgs/imagesize/spkg-install b/build/pkgs/imagesize/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/imagesize/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/imagesize/type b/build/pkgs/imagesize/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/imagesize/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/itsdangerous/SPKG.txt b/build/pkgs/itsdangerous/SPKG.txt new file mode 100644 index 00000000000..32264811471 --- /dev/null +++ b/build/pkgs/itsdangerous/SPKG.txt @@ -0,0 +1,6 @@ += itsdangerous = + +== Description == + +Various helpers to pass data to untrusted environments and to get it back +safe and sound. diff --git a/build/pkgs/itsdangerous/checksums.ini b/build/pkgs/itsdangerous/checksums.ini new file mode 100644 index 00000000000..f34dca2926d --- /dev/null +++ b/build/pkgs/itsdangerous/checksums.ini @@ -0,0 +1,4 @@ +tarball=itsdangerous-VERSION.tar.gz +sha1=0a6ae9c20cd72e89d75314ebc7b0f390f93e6a0d +md5=a3d55aa79369aef5345c036a8a26307f +cksum=2767256127 diff --git a/build/pkgs/itsdangerous/dependencies b/build/pkgs/itsdangerous/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/itsdangerous/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/itsdangerous/package-version.txt b/build/pkgs/itsdangerous/package-version.txt new file mode 100644 index 00000000000..fd137eb17eb --- /dev/null +++ b/build/pkgs/itsdangerous/package-version.txt @@ -0,0 +1 @@ +0.24 diff --git a/build/pkgs/itsdangerous/spkg-install b/build/pkgs/itsdangerous/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/itsdangerous/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/itsdangerous/type b/build/pkgs/itsdangerous/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/itsdangerous/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index f7776967317..d47d5085aa5 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=9678a519bde0739f0bbbe4b18346ab651fa46f82 -md5=e6a6da231c8a456e95a09ddded594b32 -cksum=3591998425 +sha1=7a1c20000f7e088e65f35e7fc994d1240b4b148d +md5=f887b2c076297ed8216d89b17a2000ea +cksum=4189008094 diff --git a/build/pkgs/jupyter_client/package-version.txt b/build/pkgs/jupyter_client/package-version.txt index 6aba2b245a8..af8c8ec7c13 100644 --- a/build/pkgs/jupyter_client/package-version.txt +++ b/build/pkgs/jupyter_client/package-version.txt @@ -1 +1 @@ -4.2.0 +4.2.2 diff --git a/build/pkgs/jupyter_core/checksums.ini b/build/pkgs/jupyter_core/checksums.ini index 6498e1b8bb7..d3c69242bf4 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=4366f00b33d59fa9ff69fc2baa90ac218590a88b -md5=af6eec7c93b692db007b209a9ce8e52f -cksum=638636473 +sha1=7836ef4bbe750048361624c6a3968ed733bf8f6a +md5=78f6add824eef72b12e0f6e6940372b3 +cksum=3147890695 diff --git a/build/pkgs/jupyter_core/package-version.txt b/build/pkgs/jupyter_core/package-version.txt index d13e837c8ec..ee74734aa22 100644 --- a/build/pkgs/jupyter_core/package-version.txt +++ b/build/pkgs/jupyter_core/package-version.txt @@ -1 +1 @@ -4.0.6 +4.1.0 diff --git a/build/pkgs/libfplll/checksums.ini b/build/pkgs/libfplll/checksums.ini index 0796175109d..a3434773354 100644 --- a/build/pkgs/libfplll/checksums.ini +++ b/build/pkgs/libfplll/checksums.ini @@ -1,4 +1,4 @@ -tarball=libfplll-VERSION.tar.bz2 -sha1=0810bb8283671b7bb84392494cc84a20162dd40f -md5=281bbe2b95572401b6cd5264b5694863 -cksum=3698765896 +tarball=libfplll-VERSION.tar.gz +sha1=4f03734645abe800cd4d7838fcec0cbd2c551633 +md5=661440d1249e1f8d6346ab91a8658de6 +cksum=1133822281 diff --git a/build/pkgs/libfplll/package-version.txt b/build/pkgs/libfplll/package-version.txt index d6344119dc3..1528ea5fdf3 100644 --- a/build/pkgs/libfplll/package-version.txt +++ b/build/pkgs/libfplll/package-version.txt @@ -1 +1 @@ -20160107 +20160331 diff --git a/build/pkgs/libfplll/spkg-src b/build/pkgs/libfplll/spkg-src new file mode 100755 index 00000000000..99534d180e2 --- /dev/null +++ b/build/pkgs/libfplll/spkg-src @@ -0,0 +1,34 @@ +#!/usr/bin/env bash + +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +FPLLL_REVISION="b50fd91ba0aeea2067dc9d82e6c352dbe0210eb3" +FPLLL_VERSION="4.0.5" +SPKG_ROOT="$SAGE_ROOT/build/pkgs/libfplll" + +set -e + +cd "$SPKG_ROOT" + +FPLLL_SAGE_VERSION=`cat package-version.txt |sed 's/[.]p.*//'` + +rm -rf fplll-checkout +git clone git://github.com/dstehle/fplll fplll-checkout + +cd fplll-checkout +git checkout "$FPLLL_REVISION" +./autogen.sh +./configure +make dist +tar xvfz libfplll-"$FPLLL_VERSION".tar.gz +rm libfplll-"$FPLLL_VERSION".tar.gz +mv libfplll-"$FPLLL_VERSION" libfplll-"$FPLLL_SAGE_VERSION" +tar cvfz libfplll-"$FPLLL_SAGE_VERSION".tar.gz libfplll-"$FPLLL_SAGE_VERSION" + +mv libfplll-"$FPLLL_SAGE_VERSION".tar.gz "$SAGE_DISTFILES/" +cd .. +rm -rf fplll-checkout diff --git a/build/pkgs/libgap/checksums.ini b/build/pkgs/libgap/checksums.ini index 155f647c2cf..ce8808f2d99 100644 --- a/build/pkgs/libgap/checksums.ini +++ b/build/pkgs/libgap/checksums.ini @@ -1,4 +1,4 @@ tarball=libgap-VERSION.tar.gz -sha1=24cc0c739ef2bb809ac5f182d889cc84b3f99ade -md5=2b4e3adcd6e21feeeb9475e80f4e3581 -cksum=1990698227 +sha1=22d2f099d4743678bfb2bc7da7a215be3b8ec03e +md5=3e283da0397286b98e680804d93c6e61 +cksum=159804790 diff --git a/build/pkgs/libgap/package-version.txt b/build/pkgs/libgap/package-version.txt index 3c31ca93456..f99c6583c35 100644 --- a/build/pkgs/libgap/package-version.txt +++ b/build/pkgs/libgap/package-version.txt @@ -1 +1 @@ -4.7.9 +4.8.3 diff --git a/build/pkgs/m4rie/dependencies b/build/pkgs/m4rie/dependencies index 64969c24f08..4756344de51 100644 --- a/build/pkgs/m4rie/dependencies +++ b/build/pkgs/m4rie/dependencies @@ -1,4 +1,4 @@ -m4ri givaro ntl +m4ri ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index 7ea96bd9de2..42b838e8213 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,7 +1,4 @@ -$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | sagenb setuptools - -The sagenb dependency is because of the "pytz" package. If that package -is ever pulled out from sagenb, replace "sagenb" by "pytz" above. +$(PYTHON) numpy freetype libpng dateutil pyparsing tornado six cycler | pytz setuptools ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch new file mode 100644 index 00000000000..74ec30faf63 --- /dev/null +++ b/build/pkgs/mpfr/patches/mpfr-3.1.4-1.src.patch @@ -0,0 +1,25 @@ +Patch from upstream needed to fix an error when compiling on Cygwin +See http://trac.sagemath.org/ticket/20423 for discussion and link to +the original upstream patch. +--- trunk/src/mpfr-impl.h 2016/04/08 15:55:03 10257 ++++ trunk/src/mpfr-impl.h 2016/04/08 23:17:07 10260 +@@ -208,19 +208,6 @@ + # define MPFR_WIN_THREAD_SAFE_DLL 1 + #endif + +-/* Detect some possible inconsistencies under Unix. */ +-#if defined(__unix__) +-# if defined(_WIN32) +-# error "Both __unix__ and _WIN32 are defined" +-# endif +-# if __GMP_LIBGMP_DLL +-# error "__unix__ is defined and __GMP_LIBGMP_DLL is true" +-# endif +-# if defined(MPFR_WIN_THREAD_SAFE_DLL) +-# error "Both __unix__ and MPFR_WIN_THREAD_SAFE_DLL are defined" +-# endif +-#endif +- + #if defined(__MPFR_WITHIN_MPFR) || !defined(MPFR_WIN_THREAD_SAFE_DLL) + extern MPFR_THREAD_ATTR mpfr_flags_t __gmpfr_flags; + extern MPFR_THREAD_ATTR mpfr_exp_t __gmpfr_emin; diff --git a/build/pkgs/mpfr/spkg-install b/build/pkgs/mpfr/spkg-install index 99a5ba66752..3317b027ecf 100755 --- a/build/pkgs/mpfr/spkg-install +++ b/build/pkgs/mpfr/spkg-install @@ -202,5 +202,14 @@ mpfr_build() cd src +for patch in ../patches/*.patch; do + [ -f "$patch" ] || continue + patch -p1 <"$patch" + if [ $? -ne 0 ]; then + echo >&2 "Error applying '$patch'" + exit 1 + fi +done + mpfr_build # All potential errors catched in the above function. diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index 212e5fa11aa..06666ba7e64 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,4 +1,4 @@ tarball=nbconvert-VERSION.tar.gz -sha1=079fd51c75e5c8b76f9679aa7fc5ad8176004720 -md5=58ee16f5da0dd2976ff992511d6bfb43 -cksum=75346878 +sha1=0f3c792cc624e09b2524cd0a010618036647a8cd +md5=710e356c4effcfddd29a3c10f1d8c3e6 +cksum=137274062 diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/notebook/checksums.ini b/build/pkgs/notebook/checksums.ini index e73b5a93040..8ba4997e1b6 100644 --- a/build/pkgs/notebook/checksums.ini +++ b/build/pkgs/notebook/checksums.ini @@ -1,4 +1,4 @@ tarball=notebook-VERSION.tar.gz -sha1=cf953bcaf73ba43e7ef0ce829126b6605105dd0f -md5=d390cf1a0785a43711a2548c8f3c91b8 -cksum=2636243005 +sha1=f5466b2f04e1b9c913dcf79f7e03812eba718d0d +md5=3a72bb93f6f01caec69e5e65718f5f7f +cksum=3858938777 diff --git a/build/pkgs/notebook/package-version.txt b/build/pkgs/notebook/package-version.txt index ee74734aa22..6aba2b245a8 100644 --- a/build/pkgs/notebook/package-version.txt +++ b/build/pkgs/notebook/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.0 diff --git a/build/pkgs/notebook/patches/help_link_url_fix.patch b/build/pkgs/notebook/patches/help_link_url_fix.patch deleted file mode 100644 index cfa07dee3d3..00000000000 --- a/build/pkgs/notebook/patches/help_link_url_fix.patch +++ /dev/null @@ -1,24 +0,0 @@ -Use require.toUrl for help_links - -Dirty patch for the minified js, real PR is at -https://github.com/jupyter/notebook/pull/958 - - ---- a/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:30.769442884 +0100 -+++ b/notebook/static/notebook/js/main.min.js 2016-01-15 10:20:53.073164049 +0100 -@@ -28358,7 +28358,7 @@ - .append($("") - .attr('target', '_blank') - .attr('title', 'Opens in a new window') -- .attr('href', link.url) -+ .attr('href', require.toUrl(link.url)) - .append($("") - .addClass("fa fa-external-link menu-icon pull-right") - ) -@@ -30547,4 +30547,4 @@ - define("notebook/js/main", function(){}); - - --//# sourceMappingURL=main.min.js.map -\ No newline at end of file -+//# sourceMappingURL=main.min.js.map diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 8afe164d6cd..33d0cc00e8d 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,4 +1,4 @@ tarball=numpy-VERSION.tar.gz -sha1=46a653d3a3e474bf284cbf213c2ce5fa85c39371 -md5=aed294de0aa1ac7bd3f9745f4f1968ad -cksum=4106390035 +sha1=3e43596cba1d5df4002dd0c87d4041f31ea6e1b5 +md5=bc56fb9fc2895aa4961802ffbdb31d0b +cksum=906704492 diff --git a/build/pkgs/numpy/dependencies b/build/pkgs/numpy/dependencies index 0a66dd4567a..693803cbe3d 100644 --- a/build/pkgs/numpy/dependencies +++ b/build/pkgs/numpy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) atlas +$(PYTHON) atlas | pkgconfig ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/numpy/lapack_conf.py b/build/pkgs/numpy/lapack_conf.py new file mode 100644 index 00000000000..08f00688ea7 --- /dev/null +++ b/build/pkgs/numpy/lapack_conf.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python + +import pkgconfig, os + +conf_file=open('site.cfg', 'w') + +conf_file.write('[ALL]\n') +conf_file.write('library_dirs = '+ os.environ['SAGE_LOCAL']+ '/lib\n') +conf_file.write('include_dirs = '+ os.environ['SAGE_LOCAL']+ '/include\n') + +pc_blas = pkgconfig.parse('cblas blas') +pc_lapack = pkgconfig.parse('lapack') + +if not (os.environ['UNAME'] == 'Darwin'): + conf_file.write('[blas]\n') + inc_dir = pc_blas['include_dirs'] + if len(inc_dir) > 0 : + conf_file.write('include_dirs = '+ ':'.join(list(inc_dir))+'\n') + lib_dir = pc_blas['library_dirs'] + if len(lib_dir) > 0 : + conf_file.write('library_dirs = '+ ':'.join(list(lib_dir))+'\n') + conf_file.write('blas_libs = '+', '.join(list(pc_blas['libraries']))+'\n') + conf_file.write('[lapack]\n') + lib_dir = pc_lapack['library_dirs'] + if len(lib_dir) > 0 : + conf_file.write('library_dirs = '+ ':'.join(list(lib_dir))+'\n') + conf_file.write('lapack_libs = '+', '.join(list(pc_lapack['libraries']))+'\n') + +conf_file.close() diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 18b31142065..1cac385c6cb 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.10.4 +1.11.0 diff --git a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch index 7a65280efe7..e5f04a4194e 100644 --- a/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch +++ b/build/pkgs/numpy/patches/numpy-1.10.1-asarray_conversion.patch @@ -1,3 +1,8 @@ +BUG: Let linspace accept input that has an array_interface but is not otherwise a numpy or python type. + +Upstream PR: https://github.com/numpy/numpy/pull/6659 + + diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core/function_base.py --- numpy-1.10.1.orig/numpy/core/function_base.py 2015-11-11 10:12:45.583322683 +1300 +++ numpy-1.10.1/numpy/core/function_base.py 2015-11-11 10:14:05.813343880 +1300 @@ -5,8 +10,8 @@ diff -Naur numpy-1.10.1.orig/numpy/core/function_base.py numpy-1.10.1/numpy/core __all__ = ['logspace', 'linspace'] from . import numeric as _nx --from .numeric import result_type, NaN -+from .numeric import result_type, NaN, asanyarray +-from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError ++from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError, asanyarray def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): diff --git a/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch b/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch new file mode 100644 index 00000000000..05e10fbbc30 --- /dev/null +++ b/build/pkgs/numpy/patches/numpy-1.10.2-no-hardcode-blas.patch @@ -0,0 +1,36 @@ + numpy/distutils/system_info.py | 29 +++++------------------------ + 1 file changed, 5 insertions(+), 24 deletions(-) + +diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py +index d7eb49e..aa62b09 100644 +--- a/numpy/distutils/system_info.py ++++ b/numpy/distutils/system_info.py +@@ -1690,7 +1671,7 @@ class blas_info(system_info): + lib = self.has_cblas(info) + if lib is not None: + info['language'] = 'c' +- info['libraries'] = [lib] ++ info['libraries'] = lib + info['define_macros'] = [('HAVE_CBLAS', None)] + self.set_info(**info) + +@@ -1722,16 +1703,16 @@ class blas_info(system_info): + # check for cblas lib, and if not present check for blas lib. + try: + c.link_executable(obj, os.path.join(tmpdir, "a.out"), +- libraries=["cblas"], ++ libraries=info["libraries"], + library_dirs=info['library_dirs'], + extra_postargs=info.get('extra_link_args', [])) +- res = "cblas" ++ res = info["libraries"] + except distutils.ccompiler.LinkError: + c.link_executable(obj, os.path.join(tmpdir, "a.out"), + libraries=["blas"], + library_dirs=info['library_dirs'], + extra_postargs=info.get('extra_link_args', [])) +- res = "blas" ++ res = ["blas"] + except distutils.ccompiler.CompileError: + res = None + finally: diff --git a/build/pkgs/numpy/spkg-install b/build/pkgs/numpy/spkg-install index b61cf6cb2f1..e6674110ce1 100755 --- a/build/pkgs/numpy/spkg-install +++ b/build/pkgs/numpy/spkg-install @@ -40,19 +40,17 @@ done set -e -echo "[DEFAULT]" > site.cfg -echo "library_dirs = $SAGE_LOCAL/lib" >> site.cfg -echo "include_dirs = $SAGE_LOCAL/include" >> site.cfg -echo "" >> site.cfg - if [ `uname` = "Darwin" ]; then unset ATLAS unset BLAS unset LAPACK else + export {ATLAS,PTATLAS,OPENBLAS,MKL}=None export LDFLAGS="${LDFLAGS} -shared" fi +python "${CUR}"/lapack_conf.py + # Make sure that the fortran objects are compiled with -fPIC export FFLAGS="$FFLAGS -fPIC" export FCFLAGS="$FCFLAGS -fPIC" diff --git a/build/pkgs/openblas/checksums.ini b/build/pkgs/openblas/checksums.ini index dd68f120ce9..c12a9922bed 100644 --- a/build/pkgs/openblas/checksums.ini +++ b/build/pkgs/openblas/checksums.ini @@ -1,4 +1,4 @@ -tarball=OpenBLAS-0.2.15.tar.gz +tarball=OpenBLAS-VERSION.tar.gz sha1=b9b53c644bcd31dfed7176f8c78654f482bc4845 md5=d7f94dc1f4f37285a7d00c679a0cef40 cksum=811668433 diff --git a/build/pkgs/patchbot/checksums.ini b/build/pkgs/patchbot/checksums.ini index 4e41a995ea7..8afb63fbbe9 100644 --- a/build/pkgs/patchbot/checksums.ini +++ b/build/pkgs/patchbot/checksums.ini @@ -1,4 +1,4 @@ tarball=patchbot-VERSION.tar.bz2 -sha1=efb57bc7a49ed5007de1938637ea046096ca5599 -md5=5b9ed92d7e284f4ca3a959fa592a05b2 -cksum=167678257 +sha1=a04afc25f06c3c745763efc01fa5afc3e37bef31 +md5=b0aae84440ef0b0490f5f6ac040b9d9c +cksum=4013745041 diff --git a/build/pkgs/patchbot/package-version.txt b/build/pkgs/patchbot/package-version.txt index aedc15bb0c6..0cadbc1e33b 100644 --- a/build/pkgs/patchbot/package-version.txt +++ b/build/pkgs/patchbot/package-version.txt @@ -1 +1 @@ -2.5.3 +2.5.5 diff --git a/build/pkgs/pathlib2/SPKG.txt b/build/pkgs/pathlib2/SPKG.txt new file mode 100644 index 00000000000..3330269b541 --- /dev/null +++ b/build/pkgs/pathlib2/SPKG.txt @@ -0,0 +1,11 @@ += pathlib = + +== Description == + +Object-oriented filesystem paths + +The old pathlib module on bitbucket is in bugfix-only mode. The goal +of pathlib2 is to provide a backport of standard pathlib module which +tracks the standard library module, so all the newest features of the +standard pathlib can be used also on older Python versions. + diff --git a/build/pkgs/pathlib2/checksums.ini b/build/pkgs/pathlib2/checksums.ini new file mode 100644 index 00000000000..b5c3632d898 --- /dev/null +++ b/build/pkgs/pathlib2/checksums.ini @@ -0,0 +1,4 @@ +tarball=pathlib2-VERSION.tar.gz +sha1=a4329faa7d2f0ba2430eeb57372d3a72ccce6ca2 +md5=38e4f58b4d69dfcb9edb49a54a8b28d2 +cksum=512183712 diff --git a/build/pkgs/pathlib2/dependencies b/build/pkgs/pathlib2/dependencies new file mode 100644 index 00000000000..ae35f4f0477 --- /dev/null +++ b/build/pkgs/pathlib2/dependencies @@ -0,0 +1,5 @@ +setuptools + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pathlib2/package-version.txt b/build/pkgs/pathlib2/package-version.txt new file mode 100644 index 00000000000..7ec1d6db408 --- /dev/null +++ b/build/pkgs/pathlib2/package-version.txt @@ -0,0 +1 @@ +2.1.0 diff --git a/build/pkgs/pathlib2/spkg-install b/build/pkgs/pathlib2/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/pathlib2/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/pathlib2/type b/build/pkgs/pathlib2/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pathlib2/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pickleshare/checksums.ini b/build/pkgs/pickleshare/checksums.ini index a385506705a..69926ba959d 100644 --- a/build/pkgs/pickleshare/checksums.ini +++ b/build/pkgs/pickleshare/checksums.ini @@ -1,4 +1,4 @@ tarball=pickleshare-VERSION.tar.gz -sha1=3f9176870b015f5c84ab4e7afb42470f78a0ed34 -md5=7fadddce8b1b0110c4ef905be795001a -cksum=2251024212 +sha1=94e109ff8b35768388d5a2466b841cd47853f81e +md5=29d74cde0255546b6b2e1b48a0b31a54 +cksum=3883458288 diff --git a/build/pkgs/pickleshare/dependencies b/build/pkgs/pickleshare/dependencies index 945c50635dd..7f16f620fe4 100644 --- a/build/pkgs/pickleshare/dependencies +++ b/build/pkgs/pickleshare/dependencies @@ -1,4 +1,4 @@ -setuptools pathpy +setuptools pathpy pathlib2 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/pickleshare/package-version.txt b/build/pkgs/pickleshare/package-version.txt index 5a2a5806df6..7486fdbc50b 100644 --- a/build/pkgs/pickleshare/package-version.txt +++ b/build/pkgs/pickleshare/package-version.txt @@ -1 +1 @@ -0.6 +0.7.2 diff --git a/build/pkgs/pillow/checksums.ini b/build/pkgs/pillow/checksums.ini index 6cb77b8fa0f..1d6e55ce60d 100644 --- a/build/pkgs/pillow/checksums.ini +++ b/build/pkgs/pillow/checksums.ini @@ -1,4 +1,4 @@ tarball=Pillow-VERSION.tar.gz -sha1=71d8dc1dd38ba2582f7cca8b5ce70af03d19db23 -md5=d382a86c4b9b1c8de684bd00dad43bb8 -cksum=4161496668 +sha1=5381cdd06dc00a86b0221110c768d7b49c27dc56 +md5=7cfd093c11205d9e2ebe3c51dfcad510 +cksum=4253553307 diff --git a/build/pkgs/pillow/package-version.txt b/build/pkgs/pillow/package-version.txt index 94ff29cc4de..944880fa15e 100644 --- a/build/pkgs/pillow/package-version.txt +++ b/build/pkgs/pillow/package-version.txt @@ -1 +1 @@ -3.1.1 +3.2.0 diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index fc4d68bb68c..c861d51f360 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,4 +1,4 @@ tarball=pip-VERSION.tar.gz -sha1=9f52c9f9d6da954e4d7e623ff7bc6d54a3c18f62 -md5=e9c3844db343f47d16040b32ad9072be -cksum=3244403349 +sha1=6c6076664a51ac14849696df65ace8d44e42dea7 +md5=6b86f11841e89c8241d689956ba99ed7 +cksum=2628111587 diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 8104cabd36f..0e79152459e 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -8.1.0 +8.1.1 diff --git a/build/pkgs/pkgconf/patches/pkg-config.in b/build/pkgs/pkgconf/patches/pkg-config.in index ccda2230cac..a53c6c6fa8d 100644 --- a/build/pkgs/pkgconf/patches/pkg-config.in +++ b/build/pkgs/pkgconf/patches/pkg-config.in @@ -15,4 +15,4 @@ dnl Launch system pkg-config or our own pkgconf define(NEWLINE,` ') define(PKG_CONFIG_COMMAND, translit(esyscmd(`command -v pkg-config'), NEWLINE)) -exec ifelse(sysval, `0', PKG_CONFIG_COMMAND, `"$SAGE_LOCAL/bin/pkgconf"') "$@" +exec ifelse(sysval, `0', PKG_CONFIG_COMMAND, `"$SAGE_LOCAL/bin/pkgconf" --keep-system-libs --keep-system-cflags') "$@" diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index 4b4253e8b07..64b6151487f 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,4 @@ tarball=pynac-VERSION.tar.bz2 -sha1=fe11b0fe2345eb16b3988077707d86c1b8fa0fd3 -md5=71edb1a2fb003b6c16abda4a0d73c3cf -cksum=1406756294 +sha1=fb0c8f83159b1c9c92dcb335409f12250726a32d +md5=593ac3752f8293199ffc097becbebfcf +cksum=4163433737 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index b6160487433..d2b13eb644d 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.6.2 +0.6.4 diff --git a/build/pkgs/pyparsing/checksums.ini b/build/pkgs/pyparsing/checksums.ini index 82a95a1527a..42b08e2c300 100644 --- a/build/pkgs/pyparsing/checksums.ini +++ b/build/pkgs/pyparsing/checksums.ini @@ -1,4 +1,4 @@ tarball=pyparsing-VERSION.tar.gz -sha1=bad71a5374860893962ae61262b7edc3b9b32c05 -md5=6fc363eb77331f9cf435d65f63f364ea -cksum=134497267 +sha1=88d1e80c9a42d884ae00cade206204b42ff543af +md5=5ce9096d94f553e2bc1fd366fba65558 +cksum=2374549924 diff --git a/build/pkgs/pyparsing/package-version.txt b/build/pkgs/pyparsing/package-version.txt index 7ec1d6db408..3e3c2f1e5ed 100644 --- a/build/pkgs/pyparsing/package-version.txt +++ b/build/pkgs/pyparsing/package-version.txt @@ -1 +1 @@ -2.1.0 +2.1.1 diff --git a/build/pkgs/python_openid/SPKG.txt b/build/pkgs/python_openid/SPKG.txt new file mode 100644 index 00000000000..390a18695b1 --- /dev/null +++ b/build/pkgs/python_openid/SPKG.txt @@ -0,0 +1,11 @@ += python-openid = + +== Description == + +OpenID support for servers and consumers. + +This is a set of Python packages to support use of the OpenID decentralized +identity system in your application. Want to enable single sign-on for your +web site? Use the openid.consumer package. Want to run your own OpenID server? +Check out openid.server. Includes example code and support for a variety of +storage back-ends. diff --git a/build/pkgs/python_openid/checksums.ini b/build/pkgs/python_openid/checksums.ini new file mode 100644 index 00000000000..6e35600da54 --- /dev/null +++ b/build/pkgs/python_openid/checksums.ini @@ -0,0 +1,4 @@ +tarball=python_openid-VERSION.tar.gz +sha1=e1a8e9502abf45e22dbc5f5ef76e56e139ea1b3d +md5=393f48b162ec29c3de9e2973548ea50d +cksum=2866118713 diff --git a/build/pkgs/python_openid/dependencies b/build/pkgs/python_openid/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/python_openid/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/python_openid/package-version.txt b/build/pkgs/python_openid/package-version.txt new file mode 100644 index 00000000000..21bb5e156fb --- /dev/null +++ b/build/pkgs/python_openid/package-version.txt @@ -0,0 +1 @@ +2.2.5 diff --git a/build/pkgs/python_openid/spkg-install b/build/pkgs/python_openid/spkg-install new file mode 100755 index 00000000000..9d8788038ec --- /dev/null +++ b/build/pkgs/python_openid/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd python-openid-* && python setup.py install diff --git a/build/pkgs/python_openid/type b/build/pkgs/python_openid/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/python_openid/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/pytz/SPKG.txt b/build/pkgs/pytz/SPKG.txt new file mode 100644 index 00000000000..94190513f91 --- /dev/null +++ b/build/pkgs/pytz/SPKG.txt @@ -0,0 +1,12 @@ += pytz = + +== Description == + +World Timezone Definitions for Python + +== Special Update/Build Instructions == + +The upstream tarball was repackaged after sanitizing the file +permissions with + +$ chmod go-w diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini new file mode 100644 index 00000000000..5cdbb099c98 --- /dev/null +++ b/build/pkgs/pytz/checksums.ini @@ -0,0 +1,4 @@ +tarball=pytz-VERSION.tar.bz2 +sha1=5859c52c8a01da679c0047c3e463180d7d0de4bf +md5=bdad4eee126bfa2f1f6ad5aaf4e22ee0 +cksum=2104846726 diff --git a/build/pkgs/pytz/dependencies b/build/pkgs/pytz/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/pytz/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt new file mode 100644 index 00000000000..648f66d05bd --- /dev/null +++ b/build/pkgs/pytz/package-version.txt @@ -0,0 +1 @@ +2016.3 diff --git a/build/pkgs/pytz/spkg-install b/build/pkgs/pytz/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/pytz/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/pytz/type b/build/pkgs/pytz/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pytz/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/r/checksums.ini b/build/pkgs/r/checksums.ini index aac01020c13..9d30a3c85ab 100644 --- a/build/pkgs/r/checksums.ini +++ b/build/pkgs/r/checksums.ini @@ -1,4 +1,4 @@ tarball=R-VERSION.tar.gz -sha1=f24fce2b48859f538c267d89602befe66f930d75 -md5=1ba3dac113efab69e706902810cc2970 -cksum=3188491083 +sha1=54d8b02b4564655f1be84b4a830813f8452b5e9c +md5=552b0c8088bab08ca4188797b919a58f +cksum=3467446071 diff --git a/build/pkgs/r/package-version.txt b/build/pkgs/r/package-version.txt index 1d8f5fb3ab5..bc738ca130e 100644 --- a/build/pkgs/r/package-version.txt +++ b/build/pkgs/r/package-version.txt @@ -1 +1 @@ -3.2.3.p1 +3.2.4-revised.p0 diff --git a/build/pkgs/r/spkg-check b/build/pkgs/r/spkg-check index 798e2984284..871bc56b750 100755 --- a/build/pkgs/r/spkg-check +++ b/build/pkgs/r/spkg-check @@ -5,6 +5,9 @@ if [ "$SAGE_LOCAL" = "" ]; then exit 1 fi +## Saturday night special for the ill-formed R-3.2.4-revised.tar.gz release +ln -s R-revised src + cd src $MAKE check if [ $? -ne 0 ]; then diff --git a/build/pkgs/r/spkg-install b/build/pkgs/r/spkg-install index 86011089d81..abd6f0aa0e1 100755 --- a/build/pkgs/r/spkg-install +++ b/build/pkgs/r/spkg-install @@ -105,7 +105,10 @@ elif [ "$UNAME" = "SunOS" ]; then R_CONFIGURE="--without-ICU $R_CONFIGURE" fi -cd src +## cd src +## Saturday night special : quick and dirty fix for the ill-formed +## R-3.2.4-revised.tar.gz release +cd R-revised # Apply patches. See SPKG.txt for information about what each patch # does. diff --git a/build/pkgs/sagenb/SPKG.txt b/build/pkgs/sagenb/SPKG.txt index 83cf729472b..92ab236a0e8 100644 --- a/build/pkgs/sagenb/SPKG.txt +++ b/build/pkgs/sagenb/SPKG.txt @@ -12,104 +12,29 @@ GPLv3+ == Upstream Contact == * Keshav Kini - * Homepage: http://nb.sagemath.org/ + * Homepage: https://github.com/sagemath/sagenb == Dependencies == -Included dependencies (for specific version numbers, see the file -util/fetch_deps.py in the sagenb directory): +Build-time dependencies: - * zope.interface + * Python + * setuptools * twisted - * pytz - * Babel - * Werkzeug - * speaklater - * python-openid - * itsdangerous - * Flask - * Flask-Silk - * Flask-AutoIndex - * Flask-Babel - * Flask-OpenID - * pyOpenSSL - * webassets + * flask + - flask-autoindex + - flask-babel + - flask-openid + - flask-oldsessions -Dependencies that are not included in this SPKG but are packaged for -Sage: +Run-time dependencies: - * Python - * setuptools - * pexpect + * Sage * jinja2 - * sphinx + * pexpect * docutils + * sphinx -Dependencies that are not included in this SPKG, and are not packaged -for Sage by default for licensing reasons: +Optional dependency: * OpenSSL (including headers) - -For this last category, please install systemwide using your operating -system's package manager, or manually install the OpenSSL optional SPKG -before attempting to install this sagenb SPKG (see the Installation -section of the Sage documentation for more information). - -== Packaging Instructions == - -To produce a new spkg: - - * Go to your sagenb repository - * Check out the version of sagenb you want to package - * Run dist.sh, optionally with the switch -g to include a copy of the - git repo - * Replace the src/ directory in this SPKG with the newly created dist/ - directory that has now appeared in your sagenb repository - * `cd .. && sage --pkg --no-compress sagenb-x.y.z` - * A winner is you! - -== Changelog == - -=== sagenb-0.10.7.2 (Punarbasu Purkayastha, 2013-08-08) === - - * Upgraded to 0.10.7.2 (minor fix for trac ticket 14469) - -=== sagenb-0.10.7.1 (Keshav Kini, 2013-06-25) === - - * Upgraded to 0.10.7.1 (revert published worksheet sanitization) - -=== sagenb-0.10.7 (Keshav Kini, 2013-06-22) === - - * Upgraded to 0.10.7 (MathJax 2.2, GPLv3+, bugfixes) - -=== sagenb-0.10.6 (Keshav Kini, 2013-04-27) === - - * Upgraded to 0.10.6 (bugfixes) - -=== sagenb-0.10.5 (Keshav Kini, 2013-04-01) === - - * Upgraded to 0.10.5 (bugfixes, LDAP) - -=== sagenb-0.10.4 (Keshav Kini, 2013-01-16) === - - * Upgraded to 0.10.4 (bugfixes, #13504, #13678) - -=== sagenb-0.10.3 (Jason Grout, 2012-11-16) === - - * Upgraded to 0.10.3 (bugfixes) - * Include zope.interface tarball - -=== sagenb-0.10.2 (Keshav Kini, 2012-09-01) === - - * Upgraded to 0.10.2 (bugfixes) - -=== sagenb-0.10.1 (Keshav Kini, 2012-07-08) === - - * Upgraded to 0.10.1 (bugfixes) - -=== sagenb-0.10.0 (Keshav Kini, 2012-06-21) === - - * Upgraded to 0.10.0 - -For an full log of changes, look at the history on github: -http://github.com/sagemath/sagenb diff --git a/build/pkgs/sagenb/checksums.ini b/build/pkgs/sagenb/checksums.ini index 5f53c7fc1dd..9c0d2484fc1 100644 --- a/build/pkgs/sagenb/checksums.ini +++ b/build/pkgs/sagenb/checksums.ini @@ -1,4 +1,4 @@ -tarball=sagenb-VERSION.tar -sha1=8e15129e2014d8ba9a06630ea4269251f2daac61 -md5=c0975aa7af1a0b9d7d36d3ddae95d043 -cksum=616807847 +tarball=sagenb-VERSION.tar.bz2 +sha1=7bde7c50ed46ef75b4f8c107e29aba8aef6711e1 +md5=f421c5c4d347226534ca0ca95ac726e2 +cksum=1196088822 diff --git a/build/pkgs/sagenb/dependencies b/build/pkgs/sagenb/dependencies index 7f09065715b..d5481b6b4e8 100644 --- a/build/pkgs/sagenb/dependencies +++ b/build/pkgs/sagenb/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools pexpect jinja2 sphinx docutils +$(PYTHON) | pip babel flask flask_autoindex flask_babel flask_oldsessions flask_openid mathjax twisted ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sagenb/package-version.txt b/build/pkgs/sagenb/package-version.txt index b80f98e66f1..c43e1055fd3 100644 --- a/build/pkgs/sagenb/package-version.txt +++ b/build/pkgs/sagenb/package-version.txt @@ -1 +1 @@ -0.11.7 +0.12 diff --git a/build/pkgs/sagenb/spkg-install b/build/pkgs/sagenb/spkg-install index 769ce70a2df..8c0eced55f2 100755 --- a/build/pkgs/sagenb/spkg-install +++ b/build/pkgs/sagenb/spkg-install @@ -1,79 +1,29 @@ #!/usr/bin/env bash -# This file is no longer autogenerated. -# don't build on python 3 -if [ $(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1) = "3" ]; then - exit -fi - - -# Begin boilerplate - -CUR=$PWD - -die () { - echo >&2 "$@" +if [ -z "$SAGE_LOCAL" ]; then + echo >&2 "Error: SAGE_LOCAL undefined - exiting..." + echo >&2 "Maybe run 'sage -sh'?" exit 1 -} - -[ -n "$SAGE_LOCAL" ] || die 'Error: $SAGE_LOCAL not set. Source sage-env or run this script from `sage -sh`.' - -[ -z "$CPATH" ] || CPATH="$CPATH": -[ -z "$LIBRARY_PATH" ] || LIBRARY_PATH="$LIBRARY_PATH": -export CPATH="$CPATH""$SAGE_LOCAL"/include -export LIBRARY_PATH="$LIBRARY_PATH""$SAGE_LOCAL"/lib - -# note: the -D_XOPEN_SOURCE=500 is to fix build errors in Twisted 12.1 on -# Solaris. See http://trac.sagemath.org/sage_trac/ticket/11080#comment:314 and -# the subsequent few comments. -export CPPFLAGS="-I$SAGE_LOCAL/include -D_XOPEN_SOURCE=500 $CPPFLAGS" - -if [ $SAGE64 = "yes" ]; then - echo "Building with extra 64-bit flags for MacOS X and Open Solaris." - if [ -z $CFLAG64 ]; then - CFLAG64=-m64 - fi - export CFLAGS="$CFLAGS $CFLAG64" - export CPPFLAGS="$CPPFLAGS $CFLAG64" - export CXXFLAGS="$CXXFLAGS $CFLAG64" - export LDFLAGS="$LDFLAGS $CFLAG64" fi -# End boilerplate - -# delete old sagenb install, if any -rm -rf "${SAGE_LOCAL}/lib/python/site-packages"/sagenb* - - -# TODO: clean up this crap -# * dependencies should not be part of the sagenb tarball at all -# * get rid of easy_install -# * note that pip currently depends on ssl -# Install dependencies -for PKG in $(cat src/install_order); do - easy_install -Z -H None "src/$PKG" - if [ $? -ne 0 ]; then - echo >&2 "Error: Installing $PKG failed." - exit 1 - fi -done +cd src - -# Install sagenb into site-packages -PKG=$(ls -1 src | GREP_OPTIONS= grep sagenb-) -easy_install -Z -H None "src/$PKG" +# Install a flat package (not an egg), which is the same as how pip +# installs packages. +python setup.py install --single-version-externally-managed --record dummy if [ $? -ne 0 ]; then - echo >&2 "Error: Installing SageNB failed." + echo >&2 "Error installing SageNB" exit 1 fi - -# let sagenb use mathjax spkg -cd "${SAGE_LOCAL}/lib/python/site-packages"/sagenb-*.egg/sagenb/data +# let sagenb use mathjax +cd "${SAGE_LOCAL}/lib/python/site-packages/sagenb/data" if [ $? -ne 0 ]; then echo >&2 "Error: Cannot find SageNB data directory." exit 1 fi -# the following line can be removed once sagenb does not ship mathjax anymore. -rm -rf mathjax -ln -s ../../../../../../share/mathjax/ mathjax +ln -s ../../../../../share/mathjax/ mathjax +if [ ! -d mathjax ]; then + echo >&2 "Error: Cannot symlink mathjax into the SageNB data directory." + exit 1 +fi diff --git a/build/pkgs/sagetex/SPKG.txt b/build/pkgs/sagetex/SPKG.txt index 1d454520ab0..43f0ba56ab8 100644 --- a/build/pkgs/sagetex/SPKG.txt +++ b/build/pkgs/sagetex/SPKG.txt @@ -21,16 +21,13 @@ this license, visit [[http://creativecommons.org/licenses/by-sa/3.0/]] or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. -The included files `tkz-berge.sty` and `tkz-graph.sty` are from -[[http://altermundus.com/pages/tkz/]] and are licensed under the "LaTeX -Project Public License Distributed from CTAN archives in directory -macros/latex/base/lppl.txt"; see -[[http://mirror.ctan.org/macros/latex/base/lppl.txt]]. +== SPKG Maintainers == + +Dan Drake (dr.dan.drake at gmail) == Upstream Contact == -Author: Dan Drake. Web: [[http://www.bitbucket.org/ddrake/sagetex]] and -[[http://mathsci.kaist.ac.kr/~drake]]. +Author: Dan Drake. Web: [[https://github.com/dandrake/sagetex]] == Dependencies == @@ -43,97 +40,15 @@ it cannot find "latex" in your path. To use SageTeX, both Sage and LaTeX need to know about it. SageTeX comes standard with Sage, so you only need to make sure LaTeX can find what it needs. Full details are in the Sage installation guide at -http://sagemath.org/doc/installation/ . +http://sagemath.org/doc/installation/ and +http://doc.sagemath.org/html/en/tutorial/sagetex.html . -The directory `$SAGE_ROOT/local/share/texmf/tex/generic/sagetex` -contains documentation and an example file. If you have problems or -suggestions see [[http://groups.google.com/group/sage-support|the -sage-support group]]. +The directory `$SAGE_ROOT/local/share/doc/sagetex` contains +documentation and an example file. See +`$SAGE_ROOT/local/share/texmf/tex/latex/sagetex` for the source code and +some possibly useful scripts. If you have problems or suggestions see +[[http://groups.google.com/group/sage-support|the sage-support group]]. -If you want to help develop SageTeX, please clone the bitbucket -Mercurial repository (see the "Upstream Contact" above) and send me +If you want to help develop SageTeX, please clone the github +repository (see the "Upstream Contact" above) and send me patches based on that. - -Finally, note that you can actually use SageTeX without Sage! The -`remote-sagetex.py` script uses any remote Sage server to do the -computations for you. If you can't or don't want to install Sage on your -computer, you can use the `remote-sagetex` script to use SageTeX, -although if you are in such a situation, you probably want to get -SageTeX [[http://tug.ctan.org/pkg/sagetex|from CTAN]] instead of -downloading this spkg. - -== Changelog == - -=== sagetex-2.3.4 (Dan Drake, 20 Mar 2013) === - * Copy example.tex into the doc directory too. (Ticket #14246) - -=== sagetex-2.3.3.p2 (John Palmieri, 11 Feb 2012) === - * Use "#!/usr/bin/env bash" at the top of spkg-check so it works on - OpenSolaris (ticket #12500). - -=== sagetex-2.3.3.p1 (Dan Drake, 16 Jan 2012) === - - * Improve mechanism for preventing multiply-defined labels (ticket - 12267), and also keep track of label names when recording maximum - counter seen. Also improve version mismatch checking to be backwards - compatible (fixes ticket 8035). - -=== sagetex-2.3.1.p1 (Dan Drake, 18 Nov 2011) === - - * Comment out tkz-berge and other TikZ stuff from example file; don't - distribute tkz-* with SageTeX; fix spkg-check so that we can actually - run and pass checks on this spkg. See #11583. - -=== sagetex-2.3.1 (Dan Drake, 27 May 2011) === - - * Include various fixes; see #10146. This now installs the PDFs of the - documentation to SAGE_ROOT/local/share/doc/sagetex as a sort of - halfway-not-really implementation of the SAGE_SPKG_INSTALL_DOCS - stuff. (See #10908 and #11197.) - -=== sagetex-2.3 (Dan Drake, 20 Oct 2010) === - - * Now includes the sagecommandline environment by Volker Braun. - -=== sagetex-2.2.5 (Dan Drake, 25 Mar 2010) === - - * Better implementation of the sageexample environment; 3D plots now - automatically fall back to using PNG. - -=== sagetex-2.2.4 (Dan Drake, 14 Mar 2010) === - - * Special spkg for Nicolas Thiery; this includes the sageexample - environment and some basic documentation. This spkg won't get - distributed with Sage. - -=== sagetex-2.2.3.p0 (Dan Drake, 2 Mar 2010) === - - * Couple documentation fixes; spkg-check now bails if it can't find - tkz-berge.sty (since the example file needs that) - -=== sagetex-2.2.3 (Dan Drake, 30 Dec 2009) === - - * New spkg, with instructions that reflect SageTeX's inclusion as a - standard spkg. - -=== sagetex-2.2.1 (Dan Drake, 20 June 2009) === - - * Update to version 2.2.1, which includes the remote-sagetex script and - synchronizes with the current version available on CTAN. - -=== sagetex-2.1.1 (Dan Drake, 28 May 2009) === - - * Update to version 2.1.1, which adds pause/unpause support. - -=== sagetex-2.0.2 (Dan Drake, 21 Apr 2008) === - - * Update source, improve spkg-check. - -=== sagetex-2.0.p0 (Dan Drake, 9 Jan 2008) === - - * Proper SPKG.txt, spkg-check, md5sums - * Split repos, get directory structure right - -=== sagetex-2.0 (Dan Drake, 18 Dec 2008) === - - * Initial release diff --git a/build/pkgs/sagetex/checksums.ini b/build/pkgs/sagetex/checksums.ini index ae971e6e533..15d70cc9dc8 100644 --- a/build/pkgs/sagetex/checksums.ini +++ b/build/pkgs/sagetex/checksums.ini @@ -1,4 +1,4 @@ -tarball=sagetex-VERSION.tar.bz2 -sha1=0d4ff45b97e591a22b225f0d7d1469ff903d4846 -md5=55d2293116a391631daaf7847e618646 -cksum=1506200788 +tarball=sagetex-VERSION.tar.gz +sha1=b14a1be391d7697b83179d451ea9405a9186c3b6 +md5=89f539ac31dd35555eb4f7959280ff89 +cksum=43653548 diff --git a/build/pkgs/sagetex/package-version.txt b/build/pkgs/sagetex/package-version.txt index 3f684d2d905..9f55b2ccb5f 100644 --- a/build/pkgs/sagetex/package-version.txt +++ b/build/pkgs/sagetex/package-version.txt @@ -1 +1 @@ -2.3.4 +3.0 diff --git a/build/pkgs/sagetex/spkg-install b/build/pkgs/sagetex/spkg-install index 8712950b47a..c280e113611 100755 --- a/build/pkgs/sagetex/spkg-install +++ b/build/pkgs/sagetex/spkg-install @@ -15,3 +15,5 @@ if [ $? -ne 0 ]; then exit 1 fi +echo "Removing old SageTex version(s)" +rm -rf $SAGE_LOCAL/share/texmf/tex/generic/sagetex diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index c5523bd09b1..b8da6498a4b 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -0.17.0 +0.17.0.p1 diff --git a/build/pkgs/scipy/spkg-install b/build/pkgs/scipy/spkg-install index ea7e0c33fcf..8dafd648147 100755 --- a/build/pkgs/scipy/spkg-install +++ b/build/pkgs/scipy/spkg-install @@ -19,9 +19,7 @@ if [ "$UNAME" = "Darwin" ]; then export LDFLAGS="-bundle -undefined dynamic_lookup $LDFLAGS" export CPPFLAGS="-D__ACCELERATE__ $CPPFLAGS" else - export ATLAS="$SAGE_LOCAL" - export BLAS="$SAGE_LOCAL" - export LAPACK="$SAGE_LOCAL" + export {ATLAS,PTATLAS,OPENBLAS,MKL}=None export LDFLAGS="-shared $LDFLAGS" fi diff --git a/build/pkgs/setuptools/checksums.ini b/build/pkgs/setuptools/checksums.ini index 447aafbd8c8..600e624f174 100644 --- a/build/pkgs/setuptools/checksums.ini +++ b/build/pkgs/setuptools/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools-VERSION.tar.gz -sha1=9350ce6a92ec5d4cfdac487b6612400a6069e0ab -md5=bf37191cb4c1472fb61e6f933d2006b1 -cksum=3910507905 +sha1=98c43f7f8249896e9cb5717757482708e7321e33 +md5=7e4ba5cdebc02710d3ab748c103fc673 +cksum=2338160407 diff --git a/build/pkgs/setuptools/package-version.txt b/build/pkgs/setuptools/package-version.txt index 5e83434edb2..dd0fe95cce5 100644 --- a/build/pkgs/setuptools/package-version.txt +++ b/build/pkgs/setuptools/package-version.txt @@ -1 +1 @@ -20.2.2 +20.3.1 diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index 79cc040e560..a228ef1930b 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,4 +1,4 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=c953cc4228654d151be4cd84db6391fd050eb3bf -md5=99823e2cd564b996f18820a065f0a974 -cksum=3074680780 +sha1=423fe07da9f27e0a4676f7e98f8e9f4839b6d4a4 +md5=4c5c896ba52e134bbc3507bac6400087 +cksum=1357969954 diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 4dae2985b58..1cac385c6cb 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -1.10.1 +1.11.0 diff --git a/build/pkgs/singular/package-version.txt b/build/pkgs/singular/package-version.txt index ec88d2ff4e7..a7b0131c690 100644 --- a/build/pkgs/singular/package-version.txt +++ b/build/pkgs/singular/package-version.txt @@ -1 +1 @@ -3.1.7p1.p0 +3.1.7p1.p1 diff --git a/build/pkgs/singular/patches/include_dirs.patch b/build/pkgs/singular/patches/include_dirs.patch new file mode 100644 index 00000000000..5709462903e --- /dev/null +++ b/build/pkgs/singular/patches/include_dirs.patch @@ -0,0 +1,14 @@ +Fix Singular include directory paths for use with Sage +See http://trac.sagemath.org/ticket/20386 + +--- a/kernel/Makefile.in 2016-04-11 10:21:06.310672691 +0200 ++++ b/kernel/Makefile.in 2016-04-11 10:52:11.049904340 +0200 +@@ -274,7 +274,7 @@ + install-libsingular: install + -${MKINSTALLDIRS} ${includedir} + -${MKINSTALLDIRS} ${includedir}/singular +- -for file in *.h kInline.cc; do sed -e "s: ${includedir}/singular/$$file; done ++ -for file in *.h kInline.cc; do sed -e "s: ${includedir}/singular/$$file; done + + install: all installbin + diff --git a/build/pkgs/singular/spkg-install b/build/pkgs/singular/spkg-install index 1cf970e8f07..b56090ae096 100755 --- a/build/pkgs/singular/spkg-install +++ b/build/pkgs/singular/spkg-install @@ -123,6 +123,7 @@ remove_old_version() rm -f "$SAGE_LOCAL"/bin/Singular* rm -f "$SAGE_LOCAL/include/factory.h" rm -f "$SAGE_LOCAL/include/factoryconf.h" + rm -rf "$SAGE_LOCAL/include/factory" rm -rf "$SAGE_LOCAL/include/singular" } diff --git a/build/pkgs/snowballstemmer/SPKG.txt b/build/pkgs/snowballstemmer/SPKG.txt new file mode 100644 index 00000000000..8a649d28043 --- /dev/null +++ b/build/pkgs/snowballstemmer/SPKG.txt @@ -0,0 +1,25 @@ += snowballstemmer = + +== Description == + +This package provides 16 stemmer algorithms (15 + Poerter English stemmer) generated from Snowball algorithms. + +It includes following language algorithms: + + Danish + Dutch + English (Standard, Porter) + Finnish + French + German + Hungarian + Italian + Norwegian + Portuguese + Romanian + Russian + Spanish + Swedish + Turkish + +This is a pure Python stemming library. If PyStemmer is available, this module uses it to accelerate. diff --git a/build/pkgs/snowballstemmer/checksums.ini b/build/pkgs/snowballstemmer/checksums.ini new file mode 100644 index 00000000000..6068faf5d09 --- /dev/null +++ b/build/pkgs/snowballstemmer/checksums.ini @@ -0,0 +1,4 @@ +tarball=snowballstemmer-VERSION.tar.gz +sha1=377be08ed935d401a53cba79319d1812cfe46b81 +md5=643b019667a708a922172e33a99bf2fa +cksum=4243688392 diff --git a/build/pkgs/snowballstemmer/dependencies b/build/pkgs/snowballstemmer/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/snowballstemmer/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/snowballstemmer/package-version.txt b/build/pkgs/snowballstemmer/package-version.txt new file mode 100644 index 00000000000..6085e946503 --- /dev/null +++ b/build/pkgs/snowballstemmer/package-version.txt @@ -0,0 +1 @@ +1.2.1 diff --git a/build/pkgs/snowballstemmer/spkg-install b/build/pkgs/snowballstemmer/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/snowballstemmer/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/snowballstemmer/type b/build/pkgs/snowballstemmer/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/snowballstemmer/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/speaklater/SPKG.txt b/build/pkgs/speaklater/SPKG.txt new file mode 100644 index 00000000000..3143efa59f3 --- /dev/null +++ b/build/pkgs/speaklater/SPKG.txt @@ -0,0 +1,12 @@ += speaklater = + +== Description == + +Implements a lazy string for python useful for use with gettext + +A module that provides lazy strings for translations. Basically you get an +object that appears to be a string but changes the value every time the value +is evaluated based on a callable you provide. + +For example you can have a global lazy_gettext function that returns a lazy +string with the value of the current set language. diff --git a/build/pkgs/speaklater/checksums.ini b/build/pkgs/speaklater/checksums.ini new file mode 100644 index 00000000000..7b88b7ec37e --- /dev/null +++ b/build/pkgs/speaklater/checksums.ini @@ -0,0 +1,4 @@ +tarball=speaklater-VERSION.tar.gz +sha1=65551c6896de20e58dbae795392b7e551ccfe318 +md5=e8d5dbe36e53d5a35cff227e795e8bbf +cksum=75663587 diff --git a/build/pkgs/speaklater/dependencies b/build/pkgs/speaklater/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/speaklater/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/speaklater/package-version.txt b/build/pkgs/speaklater/package-version.txt new file mode 100644 index 00000000000..7e32cd56983 --- /dev/null +++ b/build/pkgs/speaklater/package-version.txt @@ -0,0 +1 @@ +1.3 diff --git a/build/pkgs/speaklater/spkg-install b/build/pkgs/speaklater/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/speaklater/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/speaklater/type b/build/pkgs/speaklater/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/speaklater/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/sphinx/SPKG.txt b/build/pkgs/sphinx/SPKG.txt index 0fa724f05b9..fce17ba0963 100644 --- a/build/pkgs/sphinx/SPKG.txt +++ b/build/pkgs/sphinx/SPKG.txt @@ -20,51 +20,18 @@ Home Page: http://sphinx.pocoo.org, see also http://pypi.python.org/pypi/Sphinx == Dependencies == - * Jinja2 >= 2.2 - * Pygments >= 0.8 - * docutils >= 0.5 + * six >= 1.4 + * Jinja2 >= 2.3 + * Pygments >= 2.0 + * docutils >= 0.11 + * snowballstemmer >= 1.1 + * babel >= 1.3 * setuptools / distribute * Python * GNU patch (shipped with Sage) == Special Update/Build Instructions == - * Here is a description of patches on top of upstream Sphinx: - - - patches/autodoc.patch: This patches - src/sphinx/ext/autodoc.py. This adds an option to Sphinx to - provide a function to get the argspecs of built-in functions. - - - patches/environment.patch: This patches the file - src/sphinx/environment.py. Patching it should prevent Sphinx - from rebuilding documentation that is already built. - - - patches/highlighting.patch: This patches the file - src/sphinx/highlighting.py. The patch should allow Sphinx to - recognize the Sage prompt "sage:" the same way it recognizes the - ">>>" from Python. - - - patches/inline-latex.patch: see Trac #16396 and - https://bitbucket.org/birkenfeld/sphinx/issue/1480/latex-math-output-is-broken-for-inline - - - patches/pngmath.patch: This replaces \usepackage[utf8x]{inputenc} - by \usepackage[utf8]{inputenc} in the LaTeX preamble for building - images in the HTML documentation. This change is done because - some LaTeX installations have only utf8, not utf8x (utf8x adds - support for much more Unicode characters, but these are not - needed to typeset the mathematics in the Sage documentation). - - - patches/Makefile.patch: This patches the file - src/sphinx/texinputs/Makefile to increase the memory sizes for - LaTeX. These increased stack sizes should be sufficient for LaTeX - to handle the huge index of the Sage reference manual when - building the PDF version of that manual. - - - patches/nested.patch (submitted upstream: pull request #250, issue #777): - This changes the sphinx's customized Verbatim environment to use - \trivlist instead of \list. This fixes the pdf docbuild for - deeply nested classes/methods revealed in #9107. - * The script create_grammar_pickle.py creates the file Grammar2.7.pickle in site-packages/Sphinx-.../sphinx/pycode/. This helps to avoid race conditions when building the documentation in diff --git a/build/pkgs/sphinx/checksums.ini b/build/pkgs/sphinx/checksums.ini index 7eb5be5ea80..70b234a26bf 100644 --- a/build/pkgs/sphinx/checksums.ini +++ b/build/pkgs/sphinx/checksums.ini @@ -1,4 +1,4 @@ tarball=Sphinx-VERSION.tar.gz -sha1=9e424b03fe1f68e0326f3905738adcf27782f677 -md5=3dc73ccaa8d0bfb2d62fb671b1f7e8a4 -cksum=3972992129 +sha1=d18b856710b22ae9740147e21754ca5b851af9b2 +md5=4c4988e0306a04cef8dccc384281e585 +cksum=282370994 diff --git a/build/pkgs/sphinx/dependencies b/build/pkgs/sphinx/dependencies index c7304ef9c38..cc22818e6d1 100644 --- a/build/pkgs/sphinx/dependencies +++ b/build/pkgs/sphinx/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) setuptools docutils jinja2 pygments +$(PYTHON) | setuptools docutils jinja2 pygments six snowballstemmer imagesize babel alabaster ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/sphinx/package-version.txt b/build/pkgs/sphinx/package-version.txt index aae5f783ddf..cc6f9934c2e 100644 --- a/build/pkgs/sphinx/package-version.txt +++ b/build/pkgs/sphinx/package-version.txt @@ -1 +1 @@ -1.2.2.p0 +1.4.1.p0 diff --git a/build/pkgs/sphinx/patches/Makefile.patch b/build/pkgs/sphinx/patches/Makefile.patch index 8dfd02f3ba1..8b695e32a57 100644 --- a/build/pkgs/sphinx/patches/Makefile.patch +++ b/build/pkgs/sphinx/patches/Makefile.patch @@ -1,42 +1,21 @@ -diff -ur src/sphinx/texinputs/Makefile src.patched/sphinx/texinputs/Makefile ---- src/sphinx/texinputs/Makefile 2011-11-01 08:38:44.000000000 +0100 -+++ src.patched/sphinx/texinputs/Makefile 2012-02-28 22:04:56.438373064 +0100 -@@ -8,6 +8,7 @@ - ARCHIVEPRREFIX = - # Additional LaTeX options - LATEXOPTS = -+LATEXENV = pool_size=4000000 save_size=50000 extra_mem_top=2000000 +Increase the memory sizes for LaTeX + +These increased stack sizes should be sufficient for LaTeX to handle the +huge index of the Sage PDF reference manual. + +diff -ru a/sphinx/texinputs/Makefile b/sphinx/texinputs/Makefile +--- a/sphinx/texinputs/Makefile 2016-03-06 06:25:30.000000000 +0100 ++++ b/sphinx/texinputs/Makefile 2016-04-06 19:33:07.248103397 +0200 +@@ -11,8 +11,10 @@ + # format: pdf or dvi + FMT = pdf - all: $(ALLPDF) - all-pdf: $(ALLPDF) -@@ -43,20 +44,20 @@ - # The number of LaTeX runs is quite conservative, but I don't expect it - # to get run often, so the little extra time won't hurt. - %.dvi: %.tex -- latex $(LATEXOPTS) '$<' -- latex $(LATEXOPTS) '$<' -- latex $(LATEXOPTS) '$<' -+ env >>" from Python + +diff -ru a/sphinx/highlighting.py b/sphinx/highlighting.py +--- a/sphinx/highlighting.py 2016-03-28 15:16:34.000000000 +0200 ++++ b/sphinx/highlighting.py 2016-04-22 22:34:29.642929758 +0200 +@@ -96,15 +96,19 @@ + source = source.decode() # find out which lexer to use - if lang in ('py', 'python'): -- if source.startswith('>>>'): -+ if source.startswith('>>>') or source.startswith('sage: '): +- if lang in ('py', 'python'): ++ elif lang in ('py', 'python'): + if source.startswith('>>>'): # interactive session lexer = lexers['pycon'] - elif not force: ++ elif source.startswith('sage: '): ++ lexer = lexers['pycon'] + else: + lexer = lexers['python'] + elif lang in ('py3', 'python3', 'default'): + if source.startswith('>>>'): + lexer = lexers['pycon3'] ++ elif source.startswith('sage: '): ++ lexer = lexers['pycon'] + else: + lexer = lexers['python3'] + elif lang == 'guess': diff --git a/build/pkgs/sphinx/patches/inline-latex.patch b/build/pkgs/sphinx/patches/inline-latex.patch deleted file mode 100644 index fb292858682..00000000000 --- a/build/pkgs/sphinx/patches/inline-latex.patch +++ /dev/null @@ -1,40 +0,0 @@ ---- src/sphinx/writers/latex.py.orig 2014-06-05 23:38:03.808669401 -0500 -+++ src/sphinx/writers/latex.py 2014-06-05 23:27:22.752376713 -0500 -@@ -264,6 +264,7 @@ - self.next_figure_ids = set() - self.next_table_ids = set() - # flags -+ self.verbatim = False - self.in_title = 0 - self.in_production_list = 0 - self.in_footnote = 0 -@@ -1319,6 +1320,7 @@ - (self.curfilestack[-1], node.line)) - if node.rawsource != node.astext(): - # most probably a parsed-literal block -- don't highlight -+ self.verbatim = True - self.body.append('\\begin{alltt}\n') - else: - code = node.astext().rstrip('\n') -@@ -1351,6 +1353,7 @@ - raise nodes.SkipNode - def depart_literal_block(self, node): - self.body.append('\n\\end{alltt}\n') -+ self.verbatim = False - visit_doctest_block = visit_literal_block - depart_doctest_block = depart_literal_block - ---- src/sphinx/ext/mathbase.py.orig 2014-06-05 23:39:36.773293241 -0500 -+++ src/sphinx/ext/mathbase.py 2014-06-05 23:18:53.949020605 -0500 -@@ -88,7 +88,10 @@ - - - def latex_visit_math(self, node): -- self.body.append('\\(' + node['latex'] + '\\)') -+ if self.verbatim: -+ self.body.append('\\(' + node['latex'] + '\\)') -+ else: -+ self.body.append('$' + node['latex'] + '$') - raise nodes.SkipNode - - def latex_visit_displaymath(self, node): diff --git a/build/pkgs/sphinx/patches/latex_list.patch b/build/pkgs/sphinx/patches/latex_list.patch new file mode 100644 index 00000000000..b5b7e87998b --- /dev/null +++ b/build/pkgs/sphinx/patches/latex_list.patch @@ -0,0 +1,23 @@ +Fix https://github.com/sphinx-doc/sphinx/issues/777 + +diff -ru a/sphinx/texinputs/sphinx.sty b/sphinx/texinputs/sphinx.sty +--- a/sphinx/texinputs/sphinx.sty 2016-04-12 15:07:10.000000000 +0200 ++++ b/sphinx/texinputs/sphinx.sty 2016-04-22 22:24:13.671897443 +0200 +@@ -264,7 +264,7 @@ + \global\Sphinx@myfirstframedpasstrue + % The list environement is needed to control perfectly the vertical + % space. +- \list{}{% ++ \trivlist{}{% + \setlength\parskip{0pt}% + \setlength\itemsep{0ex}% + \setlength\topsep{0ex}% +@@ -278,7 +278,7 @@ + \renewcommand{\endVerbatim}{% + \endOriginalVerbatim + \endMakeFramed +- \endlist ++ \endtrivlist + % LaTeX environments always revert local changes on exit, here e.g. \parskip + } + diff --git a/build/pkgs/sphinx/patches/pngmath.patch b/build/pkgs/sphinx/patches/latex_utf8.patch similarity index 65% rename from build/pkgs/sphinx/patches/pngmath.patch rename to build/pkgs/sphinx/patches/latex_utf8.patch index 1356e67d6a4..63d266192a3 100644 --- a/build/pkgs/sphinx/patches/pngmath.patch +++ b/build/pkgs/sphinx/patches/latex_utf8.patch @@ -1,3 +1,8 @@ +Some LaTeX installations have only utf8, not utf8x + +utf8x adds support for much more Unicode characters, but these are not +needed to typeset the mathematics in the Sage documentation + --- Sphinx-1.2.2/sphinx/ext/pngmath.py.orig 2014-03-02 20:38:09.000000000 +1300 +++ Sphinx-1.2.2/sphinx/ext/pngmath.py 2014-10-19 23:35:17.000000000 +1300 @@ -42,7 +42,7 @@ diff --git a/build/pkgs/sphinx/patches/nested.patch b/build/pkgs/sphinx/patches/nested.patch deleted file mode 100644 index 1866110e38a..00000000000 --- a/build/pkgs/sphinx/patches/nested.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- Sphinx-1.2.2/sphinx/texinputs/sphinx.sty.orig 2014-01-12 02:52:54.000000000 +1300 -+++ Sphinx-1.2.2/sphinx/texinputs/sphinx.sty 2014-10-19 23:34:18.000000000 +1300 -@@ -167,7 +167,7 @@ - \smallskip% - % The list environement is needed to control perfectly the vertical - % space. -- \list{}{% -+ \trivlist{}{% using trivlist rather than list fixes #777 (too deeply nested error) - \setlength\parskip{0pt}% - \setlength\itemsep{0ex}% - \setlength\topsep{0ex}% -@@ -181,7 +181,7 @@ - \renewcommand{\endVerbatim}{% - \endOriginalVerbatim% - \endMakeFramed% -- \endlist% -+ \endtrivlist% - % close group to restore \parskip - \egroup% - } diff --git a/build/pkgs/sympy/checksums.ini b/build/pkgs/sympy/checksums.ini index 5fc9c7e0f02..49126dbe8d2 100644 --- a/build/pkgs/sympy/checksums.ini +++ b/build/pkgs/sympy/checksums.ini @@ -1,4 +1,4 @@ tarball=sympy-VERSION.tar.gz -sha1=2ea14b715ac22b0231759b536617a78450829204 -md5=92e5b8e2f51c41ecac47b3458da1f505 -cksum=4059476521 +sha1=370707a29cda59b696433107d44048dfa3ebafcd +md5=43e797de799f00f9e8fd2307dba9fab1 +cksum=58295624 diff --git a/build/pkgs/sympy/package-version.txt b/build/pkgs/sympy/package-version.txt index ec95582f51b..f9c24f5d4ec 100644 --- a/build/pkgs/sympy/package-version.txt +++ b/build/pkgs/sympy/package-version.txt @@ -1 +1 @@ -0.7.6.1 +1.0.p0 diff --git a/build/pkgs/sympy/patches/01_no-mpmath.patch b/build/pkgs/sympy/patches/01_no-mpmath.patch deleted file mode 100644 index 1c136434eb0..00000000000 --- a/build/pkgs/sympy/patches/01_no-mpmath.patch +++ /dev/null @@ -1,903 +0,0 @@ -diff --git a/README.rst b/README.rst -index 7407b25..a97bb94 100644 ---- a/README.rst -+++ b/README.rst -@@ -81,7 +81,13 @@ if SymPy is installed. - Installation - ------------ - --To install SymPy, simply run:: -+SymPy has a hard dependency on the `mpmath ` -+library (version >= 0.17). You should install it first, please refer to -+the mpmath installation guide: -+ -+http://mpmath.googlecode.com/svn/trunk/doc/build/setup.html#download-and-installation -+ -+To install SymPy itself, then simply run:: - - $ python setup.py install - -diff --git a/doc/src/modules/index.rst b/doc/src/modules/index.rst -index e4fef3b..4aa2f22 100644 ---- a/doc/src/modules/index.rst -+++ b/doc/src/modules/index.rst -@@ -29,7 +29,6 @@ access any SymPy module, or use this contens: - integrals/integrals.rst - logic.rst - matrices/index.rst -- Mpmath - polys/index.rst - printing.rst - plotting.rst -diff --git a/examples/advanced/autowrap_ufuncify.py b/examples/advanced/autowrap_ufuncify.py -index 0b3bdff..762370b 100755 ---- a/examples/advanced/autowrap_ufuncify.py -+++ b/examples/advanced/autowrap_ufuncify.py -@@ -25,7 +25,7 @@ np = import_module('numpy') - if not np: - sys.exit("Cannot import numpy. Exiting.") - --import sympy.mpmath as mpmath -+import mpmath - from sympy.utilities.autowrap import ufuncify - from sympy.utilities.lambdify import implemented_function - from sympy import symbols, legendre, Plot, pprint -diff --git a/examples/advanced/pidigits.py b/examples/advanced/pidigits.py -index c501b30..6a961b6 100755 ---- a/examples/advanced/pidigits.py -+++ b/examples/advanced/pidigits.py -@@ -6,8 +6,8 @@ Example shows arbitrary precision using mpmath with the - computation of the digits of pi. - """ - --from sympy.mpmath import libmp, pi --from sympy.mpmath import functions as mpf_funs -+from mpmath import libmp, pi -+from mpmath import functions as mpf_funs - - import math - from time import clock -diff --git a/setup.py b/setup.py -index 2e3fbe1..52cd1ac 100755 ---- a/setup.py -+++ b/setup.py -@@ -73,11 +73,6 @@ modules = [ - 'sympy.matrices', - 'sympy.matrices.benchmarks', - 'sympy.matrices.expressions', -- 'sympy.mpmath', -- 'sympy.mpmath.calculus', -- 'sympy.mpmath.functions', -- 'sympy.mpmath.libmp', -- 'sympy.mpmath.matrices', - 'sympy.ntheory', - 'sympy.parsing', - 'sympy.physics', -@@ -135,10 +130,7 @@ class audit(Command): - except ImportError: - print("In order to run the audit, you need to have PyFlakes installed.") - sys.exit(-1) -- # We don't want to audit external dependencies -- ext = ('mpmath',) -- dirs = (os.path.join(*d) for d in -- (m.split('.') for m in modules) if d[1] not in ext) -+ dirs = (os.path.join(*d) for d in (m.split('.') for m in modules)) - warns = 0 - for dir in dirs: - for filename in os.listdir(dir): -@@ -246,7 +238,6 @@ tests = [ - 'sympy.logic.tests', - 'sympy.matrices.expressions.tests', - 'sympy.matrices.tests', -- 'sympy.mpmath.tests', - 'sympy.ntheory.tests', - 'sympy.parsing.tests', - 'sympy.physics.hep.tests', -diff --git a/sympy/combinatorics/permutations.py b/sympy/combinatorics/permutations.py -index 1d1534e..56c4b8f 100644 ---- a/sympy/combinatorics/permutations.py -+++ b/sympy/combinatorics/permutations.py -@@ -9,7 +9,7 @@ from sympy.utilities.iterables import (flatten, has_variety, minlex, - has_dups, runs) - from sympy.polys.polytools import lcm - from sympy.matrices import zeros --from sympy.mpmath.libmp.libintmath import ifac -+from mpmath.libmp.libintmath import ifac - - - def _af_rmul(a, b): -diff --git a/sympy/core/evalf.py b/sympy/core/evalf.py -index c388c6c..297eabe 100644 ---- a/sympy/core/evalf.py -+++ b/sympy/core/evalf.py -@@ -6,20 +6,19 @@ from __future__ import print_function, division - - import math - --import sympy.mpmath.libmp as libmp --from sympy.mpmath import ( -- make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc, workprec) --from sympy.mpmath import inf as mpmath_inf --from sympy.mpmath.libmp import (from_int, from_man_exp, from_rational, fhalf, -+import mpmath.libmp as libmp -+from mpmath import make_mpc, make_mpf, mp, mpc, mpf, nsum, quadts, quadosc, workprec -+from mpmath import inf as mpmath_inf -+from mpmath.libmp import (from_int, from_man_exp, from_rational, fhalf, - fnan, fnone, fone, fzero, mpf_abs, mpf_add, - mpf_atan, mpf_atan2, mpf_cmp, mpf_cos, mpf_e, mpf_exp, mpf_log, mpf_lt, - mpf_mul, mpf_neg, mpf_pi, mpf_pow, mpf_pow_int, mpf_shift, mpf_sin, - mpf_sqrt, normalize, round_nearest, to_int, to_str) --from sympy.mpmath.libmp import bitcount as mpmath_bitcount --from sympy.mpmath.libmp.backend import MPZ --from sympy.mpmath.libmp.libmpc import _infs_nan --from sympy.mpmath.libmp.libmpf import dps_to_prec --from sympy.mpmath.libmp.gammazeta import mpf_bernoulli -+from mpmath.libmp import bitcount as mpmath_bitcount -+from mpmath.libmp.backend import MPZ -+from mpmath.libmp.libmpc import _infs_nan -+from mpmath.libmp.libmpf import dps_to_prec -+from mpmath.libmp.gammazeta import mpf_bernoulli - - from .compatibility import SYMPY_INTS - from .sympify import sympify -diff --git a/sympy/core/expr.py b/sympy/core/expr.py -index 9c76f68..a330c8f 100644 ---- a/sympy/core/expr.py -+++ b/sympy/core/expr.py -@@ -8,7 +8,7 @@ from .evalf import EvalfMixin, pure_complex - from .decorators import _sympifyit, call_highest_priority - from .cache import cacheit - from .compatibility import reduce, as_int, default_sort_key, xrange --from sympy.mpmath.libmp import mpf_log, prec_to_dps -+from mpmath.libmp import mpf_log, prec_to_dps - - from collections import defaultdict - -@@ -384,7 +384,7 @@ class Expr(Basic, EvalfMixin): - # increase the precision up to the default maximum - # precision to see if we can get any significance - -- from sympy.mpmath.libmp.libintmath import giant_steps -+ from mpmath.libmp.libintmath import giant_steps - from sympy.core.evalf import DEFAULT_MAXPREC as target - - # evaluate -diff --git a/sympy/core/function.py b/sympy/core/function.py -index 23d4f59..31bc9fc 100644 ---- a/sympy/core/function.py -+++ b/sympy/core/function.py -@@ -51,8 +51,8 @@ from sympy.utilities import default_sort_key - from sympy.utilities.iterables import uniq - from sympy.core.evaluate import global_evaluate - --from sympy import mpmath --import sympy.mpmath.libmp as mlib -+import mpmath -+import mpmath.libmp as mlib - - import inspect - -@@ -460,7 +460,7 @@ class Function(Application, Expr): - try: - args = [arg._to_mpmath(prec + 5) for arg in self.args] - def bad(m): -- from sympy.mpmath import mpf, mpc -+ from mpmath import mpf, mpc - # the precision of an mpf value is the last element - # if that is 1 (and m[1] is not 1 which would indicate a - # power of 2), then the eval failed; so check that none of -@@ -1223,7 +1223,7 @@ class Derivative(Expr): - When we can represent derivatives at a point, this should be folded - into the normal evalf. For now, we need a special method. - """ -- from sympy import mpmath -+ import mpmath - from sympy.core.expr import Expr - if len(self.free_symbols) != 1 or len(self.variables) != 1: - raise NotImplementedError('partials and higher order derivatives') -diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py -index b2a6539..7970e46 100644 ---- a/sympy/core/numbers.py -+++ b/sympy/core/numbers.py -@@ -16,11 +16,11 @@ from .cache import cacheit, clear_cache - from sympy.core.compatibility import ( - as_int, integer_types, long, string_types, with_metaclass, HAS_GMPY, - SYMPY_INTS) --import sympy.mpmath as mpmath --import sympy.mpmath.libmp as mlib --from sympy.mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed --from sympy.mpmath.ctx_mp import mpnumeric --from sympy.mpmath.libmp.libmpf import ( -+import mpmath -+import mpmath.libmp as mlib -+from mpmath.libmp import mpf_pow, mpf_pi, mpf_e, phi_fixed -+from mpmath.ctx_mp import mpnumeric -+from mpmath.libmp.libmpf import ( - finf as _mpf_inf, fninf as _mpf_ninf, - fnan as _mpf_nan, fzero as _mpf_zero, _normalize as mpf_normalize, - prec_to_dps) -diff --git a/sympy/core/power.py b/sympy/core/power.py -index 829e904..ed144aa 100644 ---- a/sympy/core/power.py -+++ b/sympy/core/power.py -@@ -14,7 +14,7 @@ from .logic import fuzzy_bool - from .compatibility import as_int, xrange - from .evaluate import global_evaluate - --from sympy.mpmath.libmp import sqrtrem as mpmath_sqrtrem -+from mpmath.libmp import sqrtrem as mpmath_sqrtrem - from sympy.utilities.iterables import sift - - -diff --git a/sympy/core/tests/test_evalf.py b/sympy/core/tests/test_evalf.py -index 7f71ee4..3ddf225 100644 ---- a/sympy/core/tests/test_evalf.py -+++ b/sympy/core/tests/test_evalf.py -@@ -4,9 +4,9 @@ from sympy import (Add, ceiling, cos, E, Eq, exp, factorial, fibonacci, floor, - Sum, Product, Integral) - from sympy.core.evalf import complex_accuracy, PrecisionExhausted, scaled_zero - from sympy.core.compatibility import long --from sympy.mpmath import inf, ninf, nan -+from mpmath import inf, ninf, nan - from sympy.abc import n, x, y --from sympy.mpmath.libmp.libmpf import from_float -+from mpmath.libmp.libmpf import from_float - from sympy.utilities.pytest import raises, XFAIL - - -diff --git a/sympy/core/tests/test_numbers.py b/sympy/core/tests/test_numbers.py -index f8bd86f..f87fcda 100644 ---- a/sympy/core/tests/test_numbers.py -+++ b/sympy/core/tests/test_numbers.py -@@ -6,9 +6,9 @@ from sympy.core.basic import _aresame - from sympy.core.compatibility import long, u - from sympy.core.power import integer_nthroot - from sympy.core.numbers import igcd, ilcm, igcdex, seterr, _intcache, mpf_norm --from sympy.mpmath import mpf -+from mpmath import mpf - from sympy.utilities.pytest import XFAIL, slow, raises --from sympy import mpmath -+import mpmath - - - def test_integers_cache(): -@@ -1355,8 +1355,8 @@ def test_issue_4172(): - - @XFAIL - def test_mpmath_issues(): -- from sympy.mpmath.libmp.libmpf import _normalize -- import sympy.mpmath.libmp as mlib -+ from mpmath.libmp.libmpf import _normalize -+ import mpmath.libmp as mlib - rnd = mlib.round_nearest - mpf = (0, long(0), -123, -1, 53, rnd) # nan - assert _normalize(mpf, 53) != (0, long(0), 0, 0) -@@ -1365,7 +1365,7 @@ def test_mpmath_issues(): - mpf = (1, long(0), -789, -3, 53, rnd) # -inf - assert _normalize(mpf, 53) != (0, long(0), 0, 0) - -- from sympy.mpmath.libmp.libmpf import fnan -+ from mpmath.libmp.libmpf import fnan - assert mlib.mpf_eq(fnan, fnan) - - -@@ -1396,7 +1396,7 @@ def test_int_NumberSymbols(): - - - def test_issue_6640(): -- from sympy.mpmath.libmp.libmpf import ( -+ from mpmath.libmp.libmpf import ( - _normalize as mpf_normalize, finf, fninf, fzero) - # fnan is not included because Float no longer returns fnan, - # but otherwise, the same sort of test could apply -diff --git a/sympy/core/tests/test_sympify.py b/sympy/core/tests/test_sympify.py -index 6f087b3..d72837e 100644 ---- a/sympy/core/tests/test_sympify.py -+++ b/sympy/core/tests/test_sympify.py -@@ -11,7 +11,7 @@ from sympy.functions.combinatorial.factorials import factorial, factorial2 - from sympy.abc import _clash, _clash1, _clash2 - from sympy.core.compatibility import exec_, HAS_GMPY - --from sympy import mpmath -+import mpmath - - - def test_issue_3538(): -diff --git a/sympy/core/tests/test_wester.py b/sympy/core/tests/test_wester.py -index 1bd9e28..5256a3e 100644 ---- a/sympy/core/tests/test_wester.py -+++ b/sympy/core/tests/test_wester.py -@@ -13,7 +13,7 @@ from sympy import (Rational, symbols, factorial, sqrt, log, exp, oo, zoo, - bernoulli, hyper, hyperexpand, besselj, asin, assoc_legendre, Function, re, - im, DiracDelta, chebyshevt, legendre_poly, polylog, series, O, - atan, sinh, cosh, tanh, floor, ceiling, solve, asinh, acot, csc, sec, -- LambertW, N, apart, sqrtdenest, factorial2, powdenest, Mul, S, mpmath, ZZ, -+ LambertW, N, apart, sqrtdenest, factorial2, powdenest, Mul, S, ZZ, - Poly, expand_func, E, Q, And, Or, Ne, Eq, Le, Lt, - ask, refine, AlgebraicNumber, continued_fraction_iterator as cf_i, - continued_fraction_periodic as cf_p, continued_fraction_convergents as cf_c, -@@ -26,7 +26,8 @@ from sympy.functions.special.zeta_functions import zeta - from sympy.integrals.deltafunctions import deltaintegrate - from sympy.utilities.pytest import XFAIL, slow, SKIP, skip, ON_TRAVIS - from sympy.utilities.iterables import partitions --from sympy.mpmath import mpi, mpc -+import mpmath -+from mpmath import mpi, mpc - from sympy.matrices import Matrix, GramSchmidt, eye - from sympy.matrices.expressions.blockmatrix import BlockMatrix, block_collapse - from sympy.matrices.expressions import MatrixSymbol, ZeroMatrix -diff --git a/sympy/external/tests/test_numpy.py b/sympy/external/tests/test_numpy.py -index afe85a1..c49738e 100644 ---- a/sympy/external/tests/test_numpy.py -+++ b/sympy/external/tests/test_numpy.py -@@ -21,7 +21,7 @@ from sympy import (Rational, Symbol, list2numpy, matrix2numpy, sin, Float, - Matrix, lambdify, symarray, symbols, Integer) - import sympy - --from sympy import mpmath -+import mpmath - from sympy.abc import x, y, z - from sympy.utilities.decorator import conserve_mpmath_dps - -diff --git a/sympy/functions/combinatorial/numbers.py b/sympy/functions/combinatorial/numbers.py -index 6ce1ec2..a201421 100644 ---- a/sympy/functions/combinatorial/numbers.py -+++ b/sympy/functions/combinatorial/numbers.py -@@ -21,8 +21,8 @@ from sympy.functions.elementary.exponential import log - from sympy.functions.elementary.trigonometric import sin, cos, cot - from sympy.functions.combinatorial.factorials import factorial - --from sympy.mpmath import bernfrac, workprec --from sympy.mpmath.libmp import ifib as _ifib -+from mpmath import bernfrac, workprec -+from mpmath.libmp import ifib as _ifib - - - def _product(a, b): -@@ -706,7 +706,7 @@ class euler(Function): - if m.is_odd: - return S.Zero - if m.is_Integer and m.is_nonnegative: -- from sympy.mpmath import mp -+ from mpmath import mp - m = m._to_mpmath(mp.prec) - res = mp.eulernum(m, exact=True) - return Integer(res) -@@ -725,7 +725,7 @@ class euler(Function): - m = self.args[0] - - if m.is_Integer and m.is_nonnegative: -- from sympy.mpmath import mp -+ from mpmath import mp - from sympy import Expr - m = m._to_mpmath(prec) - with workprec(prec): -diff --git a/sympy/functions/special/bessel.py b/sympy/functions/special/bessel.py -index 3bdef53..7e63b91 100644 ---- a/sympy/functions/special/bessel.py -+++ b/sympy/functions/special/bessel.py -@@ -706,8 +706,8 @@ def jn_zeros(n, k, method="sympy", dps=15): - from math import pi - - if method == "sympy": -- from sympy.mpmath import besseljzero -- from sympy.mpmath.libmp.libmpf import dps_to_prec -+ from mpmath import besseljzero -+ from mpmath.libmp.libmpf import dps_to_prec - from sympy import Expr - prec = dps_to_prec(dps) - return [Expr._from_mpmath(besseljzero(S(n + 0.5)._to_mpmath(prec), -@@ -1209,7 +1209,7 @@ class airyaiprime(AiryBase): - raise ArgumentIndexError(self, argindex) - - def _eval_evalf(self, prec): -- from sympy.mpmath import mp, workprec -+ from mpmath import mp, workprec - from sympy import Expr - z = self.args[0]._to_mpmath(prec) - with workprec(prec): -@@ -1365,7 +1365,7 @@ class airybiprime(AiryBase): - raise ArgumentIndexError(self, argindex) - - def _eval_evalf(self, prec): -- from sympy.mpmath import mp, workprec -+ from mpmath import mp, workprec - from sympy import Expr - z = self.args[0]._to_mpmath(prec) - with workprec(prec): -diff --git a/sympy/functions/special/error_functions.py b/sympy/functions/special/error_functions.py -index 548615c..8c6bd90 100644 ---- a/sympy/functions/special/error_functions.py -+++ b/sympy/functions/special/error_functions.py -@@ -1339,7 +1339,7 @@ class li(Function): - - We can even compute Soldner's constant by the help of mpmath: - -- >>> from sympy.mpmath import findroot -+ >>> from mpmath import findroot - >>> findroot(li, 2) - 1.45136923488338 - -diff --git a/sympy/functions/special/gamma_functions.py b/sympy/functions/special/gamma_functions.py -index 6532b69..c9288b9 100644 ---- a/sympy/functions/special/gamma_functions.py -+++ b/sympy/functions/special/gamma_functions.py -@@ -296,7 +296,7 @@ class lowergamma(Function): - return (cls(a + 1, x) + x**a * C.exp(-x))/a - - def _eval_evalf(self, prec): -- from sympy.mpmath import mp, workprec -+ from mpmath import mp, workprec - from sympy import Expr - a = self.args[0]._to_mpmath(prec) - z = self.args[1]._to_mpmath(prec) -@@ -395,7 +395,7 @@ class uppergamma(Function): - raise ArgumentIndexError(self, argindex) - - def _eval_evalf(self, prec): -- from sympy.mpmath import mp, workprec -+ from mpmath import mp, workprec - from sympy import Expr - a = self.args[0]._to_mpmath(prec) - z = self.args[1]._to_mpmath(prec) -diff --git a/sympy/functions/special/hyper.py b/sympy/functions/special/hyper.py -index dc92c29..fad7ece 100644 ---- a/sympy/functions/special/hyper.py -+++ b/sympy/functions/special/hyper.py -@@ -600,7 +600,8 @@ class meijerg(TupleParametersBase): - # (carefully so as not to loose the branch information), and evaluate - # G(z'**(1/r)) = G(z'**n) = G(z). - from sympy.functions import exp_polar, ceiling -- from sympy import mpmath, Expr -+ from sympy import Expr -+ import mpmath - z = self.argument - znum = self.argument._eval_evalf(prec) - if znum.has(exp_polar): -diff --git a/sympy/functions/special/spherical_harmonics.py b/sympy/functions/special/spherical_harmonics.py -index 2465636..43f8d71 100644 ---- a/sympy/functions/special/spherical_harmonics.py -+++ b/sympy/functions/special/spherical_harmonics.py -@@ -219,7 +219,7 @@ class Ynm(Function): - # Note: works without this function by just calling - # mpmath for Legendre polynomials. But using - # the dedicated function directly is cleaner. -- from sympy.mpmath import mp, workprec -+ from mpmath import mp, workprec - from sympy import Expr - n = self.args[0]._to_mpmath(prec) - m = self.args[1]._to_mpmath(prec) -diff --git a/sympy/geometry/ellipse.py b/sympy/geometry/ellipse.py -index d51e1e2..6f73bda 100644 ---- a/sympy/geometry/ellipse.py -+++ b/sympy/geometry/ellipse.py -@@ -24,7 +24,7 @@ from .entity import GeometryEntity - from .point import Point - from .line import LinearEntity, Line - from .util import _symbol, idiff --from sympy.mpmath import findroot as nroot -+from mpmath import findroot as nroot - - - import random -diff --git a/sympy/liealgebras/weyl_group.py b/sympy/liealgebras/weyl_group.py -index 6a24d23..077ddd4 100644 ---- a/sympy/liealgebras/weyl_group.py -+++ b/sympy/liealgebras/weyl_group.py -@@ -3,7 +3,7 @@ - from sympy.core import Basic, Rational - from sympy.core.numbers import igcd - from .cartan_type import CartanType --from sympy.mpmath import fac -+from mpmath import fac - from operator import itemgetter - from itertools import groupby - from sympy.matrices import Matrix, eye -diff --git a/sympy/matrices/matrices.py b/sympy/matrices/matrices.py -index 3927d98..be1c494 100644 ---- a/sympy/matrices/matrices.py -+++ b/sympy/matrices/matrices.py -@@ -1238,7 +1238,7 @@ class MatrixBase(object): - """Solve the linear system Ax = rhs for x where A = self. - - This is for symbolic matrices, for real or complex ones use -- sympy.mpmath.lu_solve or sympy.mpmath.qr_solve. -+ mpmath.lu_solve or mpmath.qr_solve. - - See Also - ======== -@@ -1615,7 +1615,7 @@ class MatrixBase(object): - to use QRsolve. - - This is mainly for educational purposes and symbolic matrices, for real -- (or complex) matrices use sympy.mpmath.qr_solve. -+ (or complex) matrices use mpmath.qr_solve. - - See Also - ======== -diff --git a/sympy/ntheory/partitions_.py b/sympy/ntheory/partitions_.py -index ddd6381..c8bbbd0 100644 ---- a/sympy/ntheory/partitions_.py -+++ b/sympy/ntheory/partitions_.py -@@ -1,6 +1,6 @@ - from __future__ import print_function, division - --from sympy.mpmath.libmp import (fzero, -+from mpmath.libmp import (fzero, - from_man_exp, from_int, from_rational, - fone, fhalf, bitcount, to_int, to_str, mpf_mul, mpf_div, mpf_sub, - mpf_add, mpf_sqrt, mpf_pi, mpf_cosh_sinh, pi_fixed, mpf_cos) -diff --git a/sympy/physics/quantum/constants.py b/sympy/physics/quantum/constants.py -index 13e8190..05df46b 100644 ---- a/sympy/physics/quantum/constants.py -+++ b/sympy/physics/quantum/constants.py -@@ -6,7 +6,7 @@ from sympy.core.numbers import NumberSymbol - from sympy.core.singleton import Singleton - from sympy.core.compatibility import u, with_metaclass - from sympy.printing.pretty.stringpict import prettyForm --import sympy.mpmath.libmp as mlib -+import mpmath.libmp as mlib - - #----------------------------------------------------------------------------- - # Constants -diff --git a/sympy/physics/quantum/qubit.py b/sympy/physics/quantum/qubit.py -index f849886..bcbc08c 100644 ---- a/sympy/physics/quantum/qubit.py -+++ b/sympy/physics/quantum/qubit.py -@@ -24,7 +24,7 @@ from sympy.physics.quantum.represent import represent - from sympy.physics.quantum.matrixutils import ( - numpy_ndarray, scipy_sparse_matrix - ) --from sympy.mpmath.libmp.libintmath import bitcount -+from mpmath.libmp.libintmath import bitcount - - __all__ = [ - 'Qubit', -diff --git a/sympy/polys/domains/groundtypes.py b/sympy/polys/domains/groundtypes.py -index b0bcf44..0ef8492 100644 ---- a/sympy/polys/domains/groundtypes.py -+++ b/sympy/polys/domains/groundtypes.py -@@ -69,7 +69,7 @@ else: - gmpy_qdiv = None - - --import sympy.mpmath.libmp as mlib -+import mpmath.libmp as mlib - - - def python_sqrt(n): -diff --git a/sympy/polys/domains/mpelements.py b/sympy/polys/domains/mpelements.py -index bf3d697..d44a6f3 100644 ---- a/sympy/polys/domains/mpelements.py -+++ b/sympy/polys/domains/mpelements.py -@@ -4,11 +4,11 @@ from __future__ import print_function, division - - from sympy.polys.domains.domainelement import DomainElement - --from sympy.mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant --from sympy.mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan, -+from mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant -+from mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan, - round_nearest, mpf_mul, mpf_abs, mpf_lt, mpc_abs, repr_dps, int_types, - from_int, from_float, from_str, to_rational) --from sympy.mpmath.rational import mpq -+from mpmath.rational import mpq - - from sympy.utilities import public - -diff --git a/sympy/polys/modulargcd.py b/sympy/polys/modulargcd.py -index 2183d25..84b8a41 100644 ---- a/sympy/polys/modulargcd.py -+++ b/sympy/polys/modulargcd.py -@@ -7,7 +7,7 @@ from sympy.polys.polyerrors import ModularGCDFailed - from sympy.polys.domains import PolynomialRing - - from sympy.core.compatibility import xrange --from sympy.mpmath import sqrt -+from mpmath import sqrt - from sympy import Dummy - import random - -diff --git a/sympy/polys/numberfields.py b/sympy/polys/numberfields.py -index e174b44..7841676 100644 ---- a/sympy/polys/numberfields.py -+++ b/sympy/polys/numberfields.py -@@ -47,7 +47,7 @@ from sympy.core.exprtools import Factors - from sympy.simplify.simplify import _mexpand, _is_sum_surds - from sympy.ntheory import sieve - from sympy.ntheory.factor_ import divisors --from sympy.mpmath import pslq, mp -+from mpmath import pslq, mp - - from sympy.core.compatibility import reduce - from sympy.core.compatibility import xrange -diff --git a/sympy/polys/polytools.py b/sympy/polys/polytools.py -index c53553e..e7c81e2 100644 ---- a/sympy/polys/polytools.py -+++ b/sympy/polys/polytools.py -@@ -47,8 +47,8 @@ from sympy.polys.polyerrors import ( - from sympy.utilities import group, sift, public - - import sympy.polys --import sympy.mpmath --from sympy.mpmath.libmp.libhyper import NoConvergence -+import mpmath -+from mpmath.libmp.libhyper import NoConvergence - - from sympy.polys.domains import FF, QQ, ZZ - from sympy.polys.constructor import construct_domain -@@ -3391,18 +3391,18 @@ class Poly(Expr): - coeffs = [coeff.evalf(n=n).as_real_imag() - for coeff in f.all_coeffs()] - try: -- coeffs = [sympy.mpmath.mpc(*coeff) for coeff in coeffs] -+ coeffs = [mpmath.mpc(*coeff) for coeff in coeffs] - except TypeError: - raise DomainError("Numerical domain expected, got %s" % \ - f.rep.dom) - -- dps = sympy.mpmath.mp.dps -- sympy.mpmath.mp.dps = n -+ dps = mpmath.mp.dps -+ mpmath.mp.dps = n - - try: - # We need to add extra precision to guard against losing accuracy. - # 10 times the degree of the polynomial seems to work well. -- roots = sympy.mpmath.polyroots(coeffs, maxsteps=maxsteps, -+ roots = mpmath.polyroots(coeffs, maxsteps=maxsteps, - cleanup=cleanup, error=False, extraprec=f.degree()*10) - - # Mpmath puts real roots first, then complex ones (as does all_roots) -@@ -3414,7 +3414,7 @@ class Poly(Expr): - 'convergence to root failed; try n < %s or maxsteps > %s' % ( - n, maxsteps)) - finally: -- sympy.mpmath.mp.dps = dps -+ mpmath.mp.dps = dps - - return roots - -diff --git a/sympy/polys/ring_series.py b/sympy/polys/ring_series.py -index 695d231..67667ad 100644 ---- a/sympy/polys/ring_series.py -+++ b/sympy/polys/ring_series.py -@@ -3,10 +3,10 @@ - from sympy.polys.domains import QQ - from sympy.polys.rings import ring, PolyElement - from sympy.polys.monomials import monomial_min, monomial_mul --from sympy.mpmath.libmp.libintmath import ifac -+from mpmath.libmp.libintmath import ifac - from sympy.core.numbers import Rational - from sympy.core.compatibility import as_int --from sympy.mpmath.libmp.libintmath import giant_steps -+from mpmath.libmp.libintmath import giant_steps - import math - - def _invert_monoms(p1): -diff --git a/sympy/polys/rootoftools.py b/sympy/polys/rootoftools.py -index 4415ff1..69f1d73 100644 ---- a/sympy/polys/rootoftools.py -+++ b/sympy/polys/rootoftools.py -@@ -28,8 +28,8 @@ from sympy.polys.polyerrors import ( - - from sympy.polys.domains import QQ - --from sympy.mpmath import mp, mpf, mpc, findroot, workprec --from sympy.mpmath.libmp.libmpf import prec_to_dps -+from mpmath import mp, mpf, mpc, findroot, workprec -+from mpmath.libmp.libmpf import prec_to_dps - - from sympy.utilities import lambdify, public - -diff --git a/sympy/polys/tests/test_polyroots.py b/sympy/polys/tests/test_polyroots.py -index 66ec76e..4a6e82a 100644 ---- a/sympy/polys/tests/test_polyroots.py -+++ b/sympy/polys/tests/test_polyroots.py -@@ -584,7 +584,7 @@ def test_nroots1(): - n = 64 - p = legendre_poly(n, x, polys=True) - -- raises(sympy.mpmath.mp.NoConvergence, lambda: p.nroots(n=3, maxsteps=5)) -+ raises(mpmath.mp.NoConvergence, lambda: p.nroots(n=3, maxsteps=5)) - - roots = p.nroots(n=3) - # The order of roots matters. They are ordered from smallest to the -diff --git a/sympy/printing/latex.py b/sympy/printing/latex.py -index ef47c04..5ef500e 100644 ---- a/sympy/printing/latex.py -+++ b/sympy/printing/latex.py -@@ -15,8 +15,8 @@ from .printer import Printer - from .conventions import split_super_sub, requires_partial - from .precedence import precedence, PRECEDENCE - --import sympy.mpmath.libmp as mlib --from sympy.mpmath.libmp import prec_to_dps -+import mpmath.libmp as mlib -+from mpmath.libmp import prec_to_dps - - from sympy.core.compatibility import default_sort_key, xrange - from sympy.utilities.iterables import has_variety -diff --git a/sympy/printing/repr.py b/sympy/printing/repr.py -index 87ebec4..777ff9a 100644 ---- a/sympy/printing/repr.py -+++ b/sympy/printing/repr.py -@@ -9,8 +9,8 @@ from __future__ import print_function, division - - from sympy.core.function import AppliedUndef - from .printer import Printer --import sympy.mpmath.libmp as mlib --from sympy.mpmath.libmp import prec_to_dps, repr_dps -+import mpmath.libmp as mlib -+from mpmath.libmp import prec_to_dps, repr_dps - - - class ReprPrinter(Printer): -diff --git a/sympy/printing/str.py b/sympy/printing/str.py -index aec1671..4c0b8bf 100644 ---- a/sympy/printing/str.py -+++ b/sympy/printing/str.py -@@ -10,8 +10,8 @@ from sympy.core.numbers import Integer - from .printer import Printer - from sympy.printing.precedence import precedence, PRECEDENCE - --import sympy.mpmath.libmp as mlib --from sympy.mpmath.libmp import prec_to_dps -+import mpmath.libmp as mlib -+from mpmath.libmp import prec_to_dps - - from sympy.utilities import default_sort_key - -diff --git a/sympy/sets/sets.py b/sympy/sets/sets.py -index 5d4d4a3..a1ba163 100644 ---- a/sympy/sets/sets.py -+++ b/sympy/sets/sets.py -@@ -13,7 +13,7 @@ from sympy.core.decorators import deprecated - from sympy.core.mul import Mul - from sympy.sets.contains import Contains - --from sympy.mpmath import mpi, mpf -+from mpmath import mpi, mpf - from sympy.logic.boolalg import And, Or, Not, true, false - from sympy.utilities import default_sort_key, subsets - -diff --git a/sympy/sets/tests/test_sets.py b/sympy/sets/tests/test_sets.py -index 0394a1d..cc793aa 100644 ---- a/sympy/sets/tests/test_sets.py -+++ b/sympy/sets/tests/test_sets.py -@@ -2,7 +2,7 @@ from sympy import (Symbol, Set, Union, Interval, oo, S, sympify, nan, - GreaterThan, LessThan, Max, Min, And, Or, Eq, Ge, Le, Gt, Lt, Float, - FiniteSet, Intersection, imageset, I, true, false, ProductSet, E, - sqrt, Complement, EmptySet, sin, cos, Lambda, ImageSet, pi) --from sympy.mpmath import mpi -+from mpmath import mpi - - from sympy.utilities.pytest import raises - from sympy.utilities.pytest import raises, XFAIL -diff --git a/sympy/simplify/simplify.py b/sympy/simplify/simplify.py -index 17732c6..8c43312 100755 ---- a/sympy/simplify/simplify.py -+++ b/sympy/simplify/simplify.py -@@ -34,7 +34,7 @@ from sympy.ntheory.factor_ import multiplicity - from sympy.polys import (Poly, together, reduced, cancel, factor, - ComputationFailed, lcm, gcd) - --import sympy.mpmath as mpmath -+import mpmath - - - def _mexpand(expr): -diff --git a/sympy/solvers/solvers.py b/sympy/solvers/solvers.py -index 3e1694e..72d5f53 100644 ---- a/sympy/solvers/solvers.py -+++ b/sympy/solvers/solvers.py -@@ -47,7 +47,7 @@ from sympy.utilities.lambdify import lambdify - from sympy.utilities.misc import filldedent - from sympy.utilities.iterables import uniq, generate_bell, flatten - --from sympy.mpmath import findroot -+from mpmath import findroot - - from sympy.solvers.polysys import solve_poly_system - from sympy.solvers.inequalities import reduce_inequalities -@@ -2418,7 +2418,8 @@ def nsolve(*args, **kwargs): - - >>> from sympy import Symbol, nsolve - >>> import sympy -- >>> sympy.mpmath.mp.dps = 15 -+ >>> import mpmath -+ >>> mpmath.mp.dps = 15 - >>> x1 = Symbol('x1') - >>> x2 = Symbol('x2') - >>> f1 = 3 * x1**2 - 2 * x2**2 - 1 -diff --git a/sympy/solvers/tests/test_numeric.py b/sympy/solvers/tests/test_numeric.py -index 5a8a091..8effd56 100644 ---- a/sympy/solvers/tests/test_numeric.py -+++ b/sympy/solvers/tests/test_numeric.py -@@ -1,5 +1,5 @@ - from sympy import Eq, Matrix, pi, sin, sqrt, Symbol, Integral, Piecewise, symbols --from sympy.mpmath import mnorm, mpf -+from mpmath import mnorm, mpf - from sympy.solvers import nsolve - from sympy.utilities.lambdify import lambdify - from sympy.utilities.pytest import raises, XFAIL -diff --git a/sympy/utilities/decorator.py b/sympy/utilities/decorator.py -index 847534e..b4dfaca 100644 ---- a/sympy/utilities/decorator.py -+++ b/sympy/utilities/decorator.py -@@ -81,7 +81,7 @@ def conserve_mpmath_dps(func): - """After the function finishes, resets the value of mpmath.mp.dps to - the value it had before the function was run.""" - import functools -- from sympy import mpmath -+ import mpmath - - def func_wrapper(): - dps = mpmath.mp.dps -diff --git a/sympy/utilities/lambdify.py b/sympy/utilities/lambdify.py -index 82ce0fb..3d054a2 100644 ---- a/sympy/utilities/lambdify.py -+++ b/sympy/utilities/lambdify.py -@@ -93,7 +93,7 @@ NUMEXPR_TRANSLATIONS = {} - # Available modules: - MODULES = { - "math": (MATH, MATH_DEFAULT, MATH_TRANSLATIONS, ("from math import *",)), -- "mpmath": (MPMATH, MPMATH_DEFAULT, MPMATH_TRANSLATIONS, ("from sympy.mpmath import *",)), -+ "mpmath": (MPMATH, MPMATH_DEFAULT, MPMATH_TRANSLATIONS, ("from mpmath import *",)), - "numpy": (NUMPY, NUMPY_DEFAULT, NUMPY_TRANSLATIONS, ("import_module('numpy')",)), - "sympy": (SYMPY, SYMPY_DEFAULT, {}, ( - "from sympy.functions import *", -diff --git a/sympy/utilities/runtests.py b/sympy/utilities/runtests.py -index 07e0ade..ae9218a 100644 ---- a/sympy/utilities/runtests.py -+++ b/sympy/utilities/runtests.py -@@ -606,8 +606,6 @@ def _doctest(*paths, **kwargs): - blacklist = kwargs.get("blacklist", []) - split = kwargs.get('split', None) - blacklist.extend([ -- "doc/src/modules/mpmath", # needs to be fixed upstream -- "sympy/mpmath", # needs to be fixed upstream - "doc/src/modules/plotting.rst", # generates live plots - "sympy/utilities/compilef.py", # needs tcc - "sympy/physics/gaussopt.py", # raises deprecation warning -diff --git a/sympy/utilities/tests/diagnose_imports.py b/sympy/utilities/tests/diagnose_imports.py -index f5f9535..8eb8d90 100755 ---- a/sympy/utilities/tests/diagnose_imports.py -+++ b/sympy/utilities/tests/diagnose_imports.py -@@ -117,7 +117,7 @@ if __name__ == "__main__": - """Is module relevant for import checking? - - Only imports between relevant modules will be checked.""" -- return in_module(module, 'sympy') and not in_module(module, 'sympy.mpmath') -+ return in_module(module, 'sympy') - - sorted_messages = [] - -diff --git a/sympy/utilities/tests/test_code_quality.py b/sympy/utilities/tests/test_code_quality.py -index e5673f5..d2419eb 100644 ---- a/sympy/utilities/tests/test_code_quality.py -+++ b/sympy/utilities/tests/test_code_quality.py -@@ -152,9 +152,7 @@ def test_files(): - "setupegg.py", - ]] - # Files to exclude from all tests -- exclude = set([ -- "%(sep)smpmath%(sep)s" % sepd, -- ]) -+ exclude = set() - # Files to exclude from the implicit import test - import_exclude = set([ - # glob imports are allowed in top-level __init__.py: -diff --git a/sympy/utilities/tests/test_lambdify.py b/sympy/utilities/tests/test_lambdify.py -index a4a1a95..27b0506 100644 ---- a/sympy/utilities/tests/test_lambdify.py -+++ b/sympy/utilities/tests/test_lambdify.py -@@ -3,7 +3,7 @@ from sympy import ( - symbols, lambdify, sqrt, sin, cos, tan, pi, atan, acos, acosh, Rational, - Float, Matrix, Lambda, exp, Integral, oo, I, Abs, Function, true, false) - from sympy.printing.lambdarepr import LambdaPrinter --from sympy import mpmath -+import mpmath - from sympy.utilities.lambdify import implemented_function - from sympy.utilities.pytest import skip - from sympy.utilities.decorator import conserve_mpmath_dps diff --git a/build/pkgs/sympy/patches/01_undeffun_sage.patch b/build/pkgs/sympy/patches/01_undeffun_sage.patch new file mode 100644 index 00000000000..2f31487d16d --- /dev/null +++ b/build/pkgs/sympy/patches/01_undeffun_sage.patch @@ -0,0 +1,18 @@ +diff --git a/sympy/core/function.py b/sympy/core/function.py +index 17f7d40..b62da2b 100644 +--- a/sympy/core/function.py ++++ b/sympy/core/function.py +@@ -723,13 +716,6 @@ def __new__(cls, *args, **options): + def _eval_as_leading_term(self, x): + return self + +- def _sage_(self): +- import sage.all as sage +- fname = str(self.func) +- args = [arg._sage_() for arg in self.args] +- func = sage.function(fname)(*args) +- return func +- + class UndefinedFunction(FunctionClass): + """ + The (meta)class of undefined functions. diff --git a/build/pkgs/sympy/spkg-install b/build/pkgs/sympy/spkg-install index 59786152737..25bf02aee46 100755 --- a/build/pkgs/sympy/spkg-install +++ b/build/pkgs/sympy/spkg-install @@ -8,9 +8,6 @@ fi cd src/ -rm -rf sympy/mpmath -rm -rf doc/src/modules/mpmath - for patch in ../patches/*.patch; do [ -r "$patch" ] || continue # Skip non-existing or non-readable patches patch -p1 <"$patch" @@ -20,13 +17,8 @@ for patch in ../patches/*.patch; do fi done -# sympy imports itself, and therefore it indirectly imports sage in -# setup.py. We need to set PYTHONPATH=. and use the -S option to avoid -# importing the currently installed version of sympy, and to avoid -# importing sage. -export PYTHONPATH="." echo "building sympy" -python -S setup.py build +python setup.py build if [ $? -ne 0 ]; then echo >&2 "Error building sympy" @@ -39,7 +31,7 @@ echo "Deleting $SAGE_LOCAL/lib/python*/site-packages/sympy*" rm -rf "$SAGE_LOCAL"/lib/python*/site-packages/sympy* echo "installing sympy" -python -S setup.py install +python setup.py install if [ $? -ne 0 ]; then echo >&2 "Error installing sympy" diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index 7746f78a1e7..b64f26cd80b 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,4 +1,4 @@ tarball=traitlets-VERSION.tar.gz -sha1=06118412dd55f4996d2504e449430d6730a06abc -md5=2ebf5e11a19f82f25395b4a793097080 -cksum=362227070 +sha1=07ada203e89cb8be31550225e4947230efba5a17 +md5=f9cc1ad00a793a65d7bc88d69ee65920 +cksum=3474275251 diff --git a/build/pkgs/traitlets/package-version.txt b/build/pkgs/traitlets/package-version.txt index ee74734aa22..fae6e3d04b2 100644 --- a/build/pkgs/traitlets/package-version.txt +++ b/build/pkgs/traitlets/package-version.txt @@ -1 +1 @@ -4.1.0 +4.2.1 diff --git a/build/pkgs/twisted/SPKG.txt b/build/pkgs/twisted/SPKG.txt new file mode 100644 index 00000000000..d2122e44f7e --- /dev/null +++ b/build/pkgs/twisted/SPKG.txt @@ -0,0 +1,8 @@ += twisted = + +== Description == + +An asynchronous networking framework written in Python + +An extensible framework for Python programming, with special focus on +event-based network programming and multiprotocol integration. diff --git a/build/pkgs/twisted/checksums.ini b/build/pkgs/twisted/checksums.ini new file mode 100644 index 00000000000..58cda2684f5 --- /dev/null +++ b/build/pkgs/twisted/checksums.ini @@ -0,0 +1,4 @@ +tarball=Twisted-VERSION.tar.bz2 +sha1=c7db4b949fc27794ca94677f66082f49be43f283 +md5=0831d7c90d0020062de0f7287530a285 +cksum=3874291662 diff --git a/build/pkgs/twisted/dependencies b/build/pkgs/twisted/dependencies new file mode 100644 index 00000000000..7d60db4e7a0 --- /dev/null +++ b/build/pkgs/twisted/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | setuptools pip zope_interface + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/twisted/package-version.txt b/build/pkgs/twisted/package-version.txt new file mode 100644 index 00000000000..188dd74f5f5 --- /dev/null +++ b/build/pkgs/twisted/package-version.txt @@ -0,0 +1 @@ +15.5.0 diff --git a/build/pkgs/twisted/spkg-install b/build/pkgs/twisted/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/twisted/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/twisted/type b/build/pkgs/twisted/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/twisted/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/vcversioner/checksums.ini b/build/pkgs/vcversioner/checksums.ini index b6fc1fbc8ca..4d294a20994 100644 --- a/build/pkgs/vcversioner/checksums.ini +++ b/build/pkgs/vcversioner/checksums.ini @@ -1,4 +1,4 @@ tarball=vcversioner-VERSION.tar.gz -sha1=723c0915665aa1f01831731605acdd6afe0af9b6 -md5=7848a365ced9941053bc25d9a9f8f4b4 -cksum=343721373 +sha1=ce076b62e8f0772bf79f29762bfc3cf09f6781b5 +md5=aab6ef5e0cf8614a1b1140ed5b7f107d +cksum=1650555311 diff --git a/build/pkgs/vcversioner/package-version.txt b/build/pkgs/vcversioner/package-version.txt index a82ac72a25f..53c62eb78b9 100644 --- a/build/pkgs/vcversioner/package-version.txt +++ b/build/pkgs/vcversioner/package-version.txt @@ -1 +1 @@ -2.14.0.0 +2.16.0.0 diff --git a/build/pkgs/werkzeug/SPKG.txt b/build/pkgs/werkzeug/SPKG.txt new file mode 100644 index 00000000000..fac6f309dee --- /dev/null +++ b/build/pkgs/werkzeug/SPKG.txt @@ -0,0 +1,18 @@ += Werkzeug = + +== Description == + +The Swiss Army knife of Python web development + +Werkzeug started as simple collection of various utilities for WSGI +applications and has become one of the most advanced WSGI utility modules. It +includes a powerful debugger, full featured request and response objects, HTTP +utilities to handle entity tags, cache control headers, HTTP dates, cookie +handling, file uploads, a powerful URL routing system and a bunch of community +contributed addon modules. + +Werkzeug is unicode aware and doesn't enforce a specific template engine, +database adapter or anything else. It doesn’t even enforce a specific way of +handling requests and leaves all that up to the developer. It's most useful +for end user applications which should work on as many server environments as +possible (such as blogs, wikis, bulletin boards, etc.). diff --git a/build/pkgs/werkzeug/checksums.ini b/build/pkgs/werkzeug/checksums.ini new file mode 100644 index 00000000000..55b68e03b80 --- /dev/null +++ b/build/pkgs/werkzeug/checksums.ini @@ -0,0 +1,4 @@ +tarball=Werkzeug-VERSION.tar.gz +sha1=9e1dca470691ae2912d51d382ff086590e3e1453 +md5=daf443a939459e12f14fd2e4658a26df +cksum=606026512 diff --git a/build/pkgs/werkzeug/dependencies b/build/pkgs/werkzeug/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/werkzeug/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/werkzeug/package-version.txt b/build/pkgs/werkzeug/package-version.txt new file mode 100644 index 00000000000..62d5dbdf3c7 --- /dev/null +++ b/build/pkgs/werkzeug/package-version.txt @@ -0,0 +1 @@ +0.11.5 diff --git a/build/pkgs/werkzeug/spkg-install b/build/pkgs/werkzeug/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/werkzeug/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/werkzeug/type b/build/pkgs/werkzeug/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/werkzeug/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/zope_interface/SPKG.txt b/build/pkgs/zope_interface/SPKG.txt new file mode 100644 index 00000000000..ca529666746 --- /dev/null +++ b/build/pkgs/zope_interface/SPKG.txt @@ -0,0 +1,13 @@ += zope.interface = + +== Description == + +This package is intended to be independently reusable in any Python project. +It is maintained by the Zope Toolkit project. + +This package provides an implementation of "object interfaces" for Python. +Interfaces are a mechanism for labeling objects as conforming to a given API +or contract. So, this package can be considered as implementation of the +Design By Contract methodology support in Python. + +For detailed documentation, please see http://docs.zope.org/zope.interface diff --git a/build/pkgs/zope_interface/checksums.ini b/build/pkgs/zope_interface/checksums.ini new file mode 100644 index 00000000000..f552e0133dc --- /dev/null +++ b/build/pkgs/zope_interface/checksums.ini @@ -0,0 +1,4 @@ +tarball=zope.interface-VERSION.tar.gz +sha1=207161e27880d07679aff6d712ed12f55e3d91b6 +md5=9ae3d24c0c7415deb249dd1a132f0f79 +cksum=3834987228 diff --git a/build/pkgs/zope_interface/dependencies b/build/pkgs/zope_interface/dependencies new file mode 100644 index 00000000000..d5dab729e18 --- /dev/null +++ b/build/pkgs/zope_interface/dependencies @@ -0,0 +1,5 @@ +$(PYTHON) | pip + +---------- +All lines of this file are ignored except the first. +It is copied by SAGE_ROOT/build/make/install into SAGE_ROOT/build/make/Makefile. diff --git a/build/pkgs/zope_interface/package-version.txt b/build/pkgs/zope_interface/package-version.txt new file mode 100644 index 00000000000..de197cc337f --- /dev/null +++ b/build/pkgs/zope_interface/package-version.txt @@ -0,0 +1 @@ +4.1.3 diff --git a/build/pkgs/zope_interface/spkg-install b/build/pkgs/zope_interface/spkg-install new file mode 100755 index 00000000000..afb3f302fd1 --- /dev/null +++ b/build/pkgs/zope_interface/spkg-install @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +cd src && python setup.py install diff --git a/build/pkgs/zope_interface/type b/build/pkgs/zope_interface/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/zope_interface/type @@ -0,0 +1 @@ +standard diff --git a/configure.ac b/configure.ac index 505a2e3acbb..e33bae717f9 100644 --- a/configure.ac +++ b/configure.ac @@ -442,9 +442,11 @@ else # Add the .0 because Debian/Ubuntu gives version numbers like # 4.6 instead of 4.6.4 (Trac #18885) case "$GXX_VERSION.0" in - [[0-3]].*|4.[[0-7]].*) - # Install our own GCC if the system-provided one is older than gcc-4.8. - SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is quite old]);; + [[0-3]].*|4.[[0-8]].*) + # Install our own GCC if the system-provided one is older than GCC-4.9. + # GCC-4.8.x breaks because of http://trac.sagemath.org/ticket/14460 + # which is GCC bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56982 + SAGE_SHOULD_INSTALL_GCC([you have $CXX version $GXX_VERSION, which is too old]);; esac # The following tests check that the version of the compilers @@ -875,7 +877,7 @@ while read PKG_NAME PKG_VERSION PKG_VAR; do else # Normal Sage packages echo >&7 "\$(INST)/$PKG_VERSION:$DEPS" - echo >&7 " +sage-logger 'sage-spkg $PKG_VERSION' '\$(SAGE_LOGS)/$PKG_VERSION.log'" + echo >&7 " +sage-logger '\$(SAGE_SPKG) $PKG_VERSION' '\$(SAGE_LOGS)/$PKG_VERSION.log'" echo >&7 # Add a target with just the bare package name for "sage -i" diff --git a/src/bin/sage b/src/bin/sage index 23d74e7bf1f..53e12600788 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -15,6 +15,7 @@ usage() { echo " -c -- Evaluates cmd as sage code" echo " -experimental -- list all experimental packages that can be installed" echo " -gap [...] -- run Sage's Gap with given arguments" + echo " -gap3 [...] -- run Sage's Gap3 with given arguments" echo " -gp [...] -- run Sage's PARI/GP calculator with given arguments" echo " -h, -? -- print this help message" echo " -i [packages] -- install the given Sage packages" @@ -92,6 +93,7 @@ usage_advanced() { echo " -cython [...] -- run Cython with given arguments" echo " -ecl [...] -- run Common Lisp" echo " -gap [...] -- run Sage's Gap with given arguments" + echo " -gap3 [...] -- run Sage's Gap3 with given arguments" echo " -gdb -- run Sage under the control of gdb" echo " -gp [...] -- run Sage's PARI/GP calculator with given arguments" echo " -ipython [...] -- run Sage's IPython using the default environment (not" @@ -363,7 +365,9 @@ sage_setup() { fi # Display the startup banner - if [ "$SAGE_BANNER" != "no" ]; then + if [ "$SAGE_BANNER" = "bare" ]; then + cat "$SAGE_ROOT/VERSION.txt" + elif [ "$SAGE_BANNER" != "no" ]; then cat "$SAGE_LOCAL/bin/sage-banner" fi @@ -406,7 +410,7 @@ fi if [ "$1" = '-q' ]; then shift - export SAGE_BANNER="no" + export SAGE_BANNER=no fi if [ $# -eq 0 ]; then @@ -429,7 +433,7 @@ fi if [ "$1" = '-v' -o "$1" = '-version' -o "$1" = '--version' ]; then . "$SAGE_LOCAL"/bin/sage-version.sh - echo "SageMath Version ${SAGE_VERSION}, Release Date: ${SAGE_RELEASE_DATE}" + echo "SageMath version ${SAGE_VERSION}, Release Date: ${SAGE_RELEASE_DATE}" exit 0 fi @@ -452,6 +456,11 @@ if [ "$1" = '-gap' -o "$1" = '--gap' ]; then exec gap "$@" fi +if [ "$1" = '-gap3' -o "$1" = '--gap3' ]; then + shift + exec gap3 "$@" +fi + if [ "$1" = '-gp' -o "$1" = '--gp' ]; then shift exec gp "$@" @@ -771,8 +780,7 @@ if [ "$1" = '-t' -o "$1" = '-bt' -o "$1" = '-tp' -o "$1" = '-btp' ]; then echo >&2 "init.sage does not exist ... creating" touch "$DOT_SAGE"/init.sage fi - SAGE_BANNER="no" - sage_setup + SAGE_BANNER=no sage_setup export PYTHONIOENCODING="utf-8" # Fix encoding for doctests if [ "$1" = '-tp' -o "$1" = '-btp' ]; then shift @@ -788,24 +796,21 @@ if [ "$1" = '-tnew' -o "$1" = '-btnew' ]; then build_sage fi shift - SAGE_BANNER="no" - sage_setup + SAGE_BANNER=no sage_setup export PYTHONIOENCODING="utf-8" # Fix encoding for doctests exec sage-runtests --new "$@" fi if [ "$1" = '-testall' -o "$1" = "--testall" ]; then shift - SAGE_BANNER="no" - sage_setup + SAGE_BANNER=no sage_setup export PYTHONIOENCODING="utf-8" # Fix encoding for doctests exec sage-runtests -a "$@" fi if [ "$1" = '-c' ]; then shift - SAGE_BANNER="no" - sage_setup + SAGE_BANNER=no sage_setup unset TERM # See Trac #12263 exec sage-eval "$@" fi @@ -996,8 +1001,7 @@ if [ $# -ge 1 ]; then if [ "$T" = "spkg" ]; then install "$@" fi - SAGE_BANNER="no" - sage_setup + SAGE_BANNER=no sage_setup unset TERM # See Trac #12263 exec sage-run "$@" fi diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 8989f40d4e9..87d57163446 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath Version 7.1.rc0, Release Date: 2016-03-10 │ +│ SageMath version 7.2.beta6, Release Date: 2016-04-28 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index ddb8656803f..5a0fda63d65 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -29,9 +29,10 @@ if __name__ == "__main__": metavar="SECONDS", help="warn if tests take more time than SECONDS") parser.add_option("--optional", metavar="PKGS", default="sage,optional", help='only run tests including one of the "# optional" tags listed in PKGS; ' - 'if "sage" is listed will also test the standard doctests; ' - 'if "optional" is listed will also test all available optional (new-style) packages; ' - 'if set to "all", then all tests will be run; ') + 'if "sage" is listed, will also run the standard doctests; ' + 'if "optional" is listed, will also run tests for installed optional (new-style) packages; ' + 'if "external" is listed, will also run tests for available external software; ' + 'if set to "all", then all tests will be run') parser.add_option("--randorder", type=int, metavar="SEED", help="randomize order of tests") parser.add_option("--global-iterations", "--global_iterations", type=int, default=0, help="repeat the whole testing process this many times") parser.add_option("--file-iterations", "--file_iterations", type=int, default=0, help="repeat each file this many times, stopping on the first failure") diff --git a/src/bin/sage-update-version b/src/bin/sage-update-version index 4cb36dba7a0..c747aeb39d3 100755 --- a/src/bin/sage-update-version +++ b/src/bin/sage-update-version @@ -47,7 +47,7 @@ EOF "$SAGE_ROOT/sage" -b # Update Sage version file -echo "Sage version $SAGE_VERSION, released $SAGE_RELEASE_DATE" \ +echo "SageMath version $SAGE_VERSION, Release Date: $SAGE_RELEASE_DATE" \ > "$SAGE_ROOT/VERSION.txt" # Update Sage version file for shell scripts in SAGE_SRC/bin/sage-version.sh @@ -67,7 +67,7 @@ EOF "$SAGE_ROOT/bootstrap" -i -s # Commit auto-generated changes -git commit -m "Updated Sage version to $SAGE_VERSION" -- \ +git commit -m "Updated SageMath version to $SAGE_VERSION" -- \ "$SAGE_ROOT/VERSION.txt" \ "$SAGE_SRC/sage/version.py" \ "$SAGE_SRC/bin/sage-version.sh" \ @@ -76,5 +76,5 @@ git commit -m "Updated Sage version to $SAGE_VERSION" -- \ "$SAGE_ROOT/build/pkgs/configure/package-version.txt" \ || die "Error committing to the repository." -git tag -a "$SAGE_VERSION" -m "Sage version $SAGE_VERSION" \ +git tag -a "$SAGE_VERSION" -m "SageMath version $SAGE_VERSION" \ || die "Error tagging the repository." diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index c09f06829af..83b9efe6228 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.1.rc0' -SAGE_RELEASE_DATE='2016-03-10' +SAGE_VERSION='7.2.beta6' +SAGE_RELEASE_DATE='2016-04-28' diff --git a/src/doc/common/conf.py b/src/doc/common/conf.py index c783b8a7888..ee3f23ae1c1 100644 --- a/src/doc/common/conf.py +++ b/src/doc/common/conf.py @@ -75,12 +75,11 @@ def sphinx_plot(plot): # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' -# List of documents that shouldn't be included in the build. -#unused_docs = [] - -# List of directories, relative to source directory, that shouldn't be searched -# for source files. -exclude_trees = ['.build'] +# List of glob-style patterns that should be excluded when looking for +# source files. [1] They are matched against the source file names +# relative to the source directory, using slashes as directory +# separators on all platforms. +exclude_patterns = ['.build'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = 'math' @@ -227,8 +226,9 @@ def set_intersphinx_mappings(app): mathjax_static = os.path.join(sagenb_path, mathjax_relative) html_static_path.append(mathjax_static) - exclude_patterns=['**/'+os.path.join(mathjax_relative, i) for i in ('docs', 'README*', 'test', - 'unpacked', 'LICENSE')] + exclude_patterns += ['**/'+os.path.join(mathjax_relative, i) + for i in ('docs', 'README*', 'test', + 'unpacked', 'LICENSE')] else: extensions.append('sphinx.ext.pngmath') @@ -352,7 +352,7 @@ def set_intersphinx_mappings(app): \DeclareUnicodeCharacter{03A5}{\ensuremath{\Upsilon}} \DeclareUnicodeCharacter{2113}{\ell} -\DeclareUnicodeCharacter{221A}{\sqrt} +\DeclareUnicodeCharacter{221A}{\ensuremath{\sqrt{}}} \DeclareUnicodeCharacter{2264}{\leq} \DeclareUnicodeCharacter{2265}{\geq} \DeclareUnicodeCharacter{221E}{\infty} @@ -615,7 +615,7 @@ def call_intersphinx(app, env, node, contnode): sage: for line in open(thematic_index).readlines(): ....: if "padics" in line: ....: sys.stdout.write(line) - +
  • Introduction to the -adics
  • """ debug_inf(app, "???? Trying intersphinx for %s"%node['reftarget']) builder = app.builder @@ -724,9 +724,9 @@ def find_sage_dangling_links(app, env, node, contnode): # link to the Python documentation several links where broken because there # where class listed as functions. Expand the list 'base_class_as_func' # above instead of marking the link as broken. -nitpick_ignore = ( +nitpick_ignore = [ ('py:class', 'twisted.web2.resource.Resource'), - ('py:class', 'twisted.web2.resource.PostableResource')) + ('py:class', 'twisted.web2.resource.PostableResource')] def nitpick_patch_config(app): """ diff --git a/src/doc/common/themes/sage/theme.conf b/src/doc/common/themes/sage/theme.conf index 4fbb8e4bb16..3d8462793bd 100644 --- a/src/doc/common/themes/sage/theme.conf +++ b/src/doc/common/themes/sage/theme.conf @@ -1,5 +1,5 @@ [theme] -inherit = default +inherit = classic stylesheet = sage.css pygments_style = sphinx diff --git a/src/doc/common/themes/sageref/theme.conf b/src/doc/common/themes/sageref/theme.conf index 4fbb8e4bb16..3d8462793bd 100644 --- a/src/doc/common/themes/sageref/theme.conf +++ b/src/doc/common/themes/sageref/theme.conf @@ -1,5 +1,5 @@ [theme] -inherit = default +inherit = classic stylesheet = sage.css pygments_style = sphinx diff --git a/src/doc/de/thematische_anleitungen/sage_gymnasium.rst b/src/doc/de/thematische_anleitungen/sage_gymnasium.rst index 9b7da4cc927..7d2d175f70a 100644 --- a/src/doc/de/thematische_anleitungen/sage_gymnasium.rst +++ b/src/doc/de/thematische_anleitungen/sage_gymnasium.rst @@ -572,7 +572,7 @@ Tupel besteht, welches das Interval des Definitionsbereichs angibt, also z.B. `` `[-\infty, 0]` und einer für das Interval geltende Funktionsgleichung. Als letztes Argument muss angegeben werden, welche Variable durch die Funktion gebunden werden soll:: - sage: f = Piecewise([[(-oo,0), -x^2],[(0,oo), x^2]], x) + sage: f = piecewise([[(-oo,0), -x^2],[(0,oo), x^2]], var=x) sage: f(3) 9 sage: f(-3) diff --git a/src/doc/de/tutorial/bibliography.rst b/src/doc/de/tutorial/bibliography.rst index c760e5cc6a2..7ce77e2e5e7 100644 --- a/src/doc/de/tutorial/bibliography.rst +++ b/src/doc/de/tutorial/bibliography.rst @@ -41,7 +41,7 @@ Literaturverzeichnis .. [SA] Sage web site, http://www.sagemath.org/. -.. [Si] W. Decker, G.-M. Greuel, G. Pfister, and +.. [Si] \W. Decker, G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.3.1. A Computer Algebra System for Polynomial Computations. University of Kaiserslautern (2010), http://www.singular.uni-kl.de/. diff --git a/src/doc/de/tutorial/tour_advanced.rst b/src/doc/de/tutorial/tour_advanced.rst index 9308ac790f0..aa2af4de61b 100644 --- a/src/doc/de/tutorial/tour_advanced.rst +++ b/src/doc/de/tutorial/tour_advanced.rst @@ -325,8 +325,8 @@ Faktorisierung des Moduls entsprechen. :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -334,10 +334,8 @@ Faktorisierung des Moduls entsprechen. sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] Als nächstes konstruieren wir die Gruppe der Dirichlet-Charaktere @@ -348,7 +346,7 @@ mod 20, jedoch mit Werten in :math:`\QQ(i)`: sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 Nun berechnen wir mehrere Invarianten von ``G``: @@ -381,8 +379,7 @@ Argument von ``DirichletGroup`` an. sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/doc/en/constructions/calculus.rst b/src/doc/en/constructions/calculus.rst index 5db12300385..039dbd6a2ce 100644 --- a/src/doc/en/constructions/calculus.rst +++ b/src/doc/en/constructions/calculus.rst @@ -81,7 +81,7 @@ You can find critical points of a piecewise defined function: sage: f2 = 1-x sage: f3 = 2*x sage: f4 = 10*x-x^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + sage: f = piecewise([((0,1),f1), ((1,2),f2), ((2,3),f3), ((3,10),f4)]) sage: f.critical_points() [5.0] @@ -177,9 +177,10 @@ where :math:`f(x)=1`, :math:`0 1/3*x^3], [(1, 2), x |--> -1/3*x^3 + 5*x - 13/3]] - sage: f.integral(definite=True) - 3 + sage: f = piecewise([[[0,1], f1], [RealSet.open_closed(1,2), f2]]) + sage: t = f.trapezoid(2); t + piecewise(x|-->1/2*x on (0, 1/2), x|-->3/2*x - 1/2 on (1/2, 1), x|-->7/2*x - 5/2 on (1, 3/2), x|-->-7/2*x + 8 on (3/2, 2); x) + sage: t.integral() + piecewise(x|-->1/4*x^2 on (0, 1/2), x|-->3/4*x^2 - 1/2*x + 1/8 on (1/2, 1), x|-->7/4*x^2 - 5/2*x + 9/8 on (1, 3/2), x|-->-7/4*x^2 + 8*x - 27/4 on (3/2, 2); x) + sage: t.integral(definite=True) + 9/4 .. index: Laplace transform @@ -242,7 +238,7 @@ computation. (x, s) sage: f1(x) = 1 sage: f2(x) = 1-x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + sage: f = piecewise([((0,1),f1), ((1,2),f2)]) sage: f.laplace(x, s) -e^(-s)/s + (s + 1)*e^(-2*s)/s^2 + 1/s - e^(-s)/s^2 @@ -382,7 +378,7 @@ illustrating how the Gibbs phenomenon is mollified). sage: f1 = lambda x: -1 sage: f2 = lambda x: 2 - sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) + sage: f = piecewise([((0,pi/2),f1), ((pi/2,pi),f2)]) sage: f.fourier_series_cosine_coefficient(5,pi) -3/5/pi sage: f.fourier_series_sine_coefficient(2,pi) diff --git a/src/doc/en/constructions/linear_codes.rst b/src/doc/en/constructions/linear_codes.rst index 57f488a9104..c5b8568d1b1 100644 --- a/src/doc/en/constructions/linear_codes.rst +++ b/src/doc/en/constructions/linear_codes.rst @@ -22,9 +22,9 @@ Sage can compute Hamming codes :: - sage: C = codes.HammingCode(3,GF(3)) + sage: C = codes.HammingCode(GF(3), 3) sage: C - Linear code of length 13, dimension 10 over Finite Field of size 3 + [13, 10] Hamming Code over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C.generator_matrix() @@ -74,10 +74,10 @@ a check matrix, and the dual code: :: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: Cperp = C.dual_code() sage: C; Cperp - Linear code of length 7, dimension 4 over Finite Field of size 2 + [7, 4] Hamming Code over Finite Field of size 2 Linear code of length 7, dimension 3 over Finite Field of size 2 sage: C.generator_matrix() [1 0 0 0 0 1 1] @@ -90,7 +90,7 @@ a check matrix, and the dual code: [0 0 0 1 1 1 1] sage: C.dual_code() Linear code of length 7, dimension 3 over Finite Field of size 2 - sage: C = codes.HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(GF(4,'a'), 3) sage: C.dual_code() Linear code of length 21, dimension 3 over Finite Field in a of size 2^2 @@ -102,7 +102,7 @@ implemented. :: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: MS = MatrixSpace(GF(2),1,7) sage: F = GF(2); a = F.gen() sage: v = vector([a,a,F(0),a,a,F(0),a]) @@ -116,9 +116,9 @@ can use the matplotlib package included with Sage: :: - sage: C = codes.HammingCode(4,GF(2)) + sage: C = codes.HammingCode(GF(2), 4) sage: C - Linear code of length 15, dimension 11 over Finite Field of size 2 + [15, 11] Hamming Code over Finite Field of size 2 sage: w = C.weight_distribution(); w [1, 0, 0, 35, 105, 168, 280, 435, 435, 280, 168, 105, 35, 0, 0, 1] sage: J = range(len(w)) diff --git a/src/doc/en/constructions/plotting.rst b/src/doc/en/constructions/plotting.rst index f46f3b3d4af..1be93b6690a 100644 --- a/src/doc/en/constructions/plotting.rst +++ b/src/doc/en/constructions/plotting.rst @@ -34,13 +34,13 @@ You can plot piecewise-defined functions: :: - sage: f1 = lambda x:1 - sage: f2 = lambda x:1-x - sage: f3 = lambda x:exp(x) - sage: f4 = lambda x:sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f.plot() - Graphics object consisting of 4 graphics primitives + sage: f1 = 1 + sage: f2 = 1-x + sage: f3 = exp(x) + sage: f4 = sin(2*x) + sage: f = piecewise([((0,1),f1), ((1,2),f2), ((2,3),f3), ((3,10),f4)]) + sage: f.plot(x,0,10) + Graphics object consisting of 1 graphics primitive Other function plots can be produced as well: diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index 2e03f8128bd..d94118f1305 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -308,7 +308,7 @@ information. You can use the existing functions of Sage as templates. REFERENCES: - .. [Nat2000] M.B. Nathanson. Elementary Methods in Number Theory. + .. [Nat2000] \M. B. Nathanson. Elementary Methods in Number Theory. Springer, 2000. - A **NOTE** block for tips/tricks (optional). :: @@ -370,6 +370,13 @@ information. You can use the existing functions of Sage as templates. .. [SC] Conventions for coding in sage. http://www.sagemath.org/doc/developer/conventions.html. + When abbreviating the first name of an author, be sure to put a + backslash in front of it. This ensures that the letter (``C.`` + in the example below) will not be interpreted as a list + enumerator:: + + .. [Gauss] \C. F. Gauss, Disquisitiones Arithmeticae, 1801. + See the `Sphinx/ReST markup for citations `_. For links toward trac tickets or wikipedia, see :ref:`chapter-sage_manuals_links`. - A **TESTS** block (optional) @@ -724,6 +731,29 @@ written. Special Markup to Influence Tests --------------------------------- +Overly complicated output in the example code can be shortened +by an ellipsis marker ``...``:: + + sage: [ZZ(n).ordinal_str() for n in range(25)] + ['0th', + '1st', + '2nd', + '3rd', + '4th', + '5th', + ... + '21st', + '22nd', + '23rd', + '24th'] + sage: ZZ('sage') + Traceback (most recent call last): + ... + TypeError: unable to convert 'sage' to an integer + +On the proper usage of the ellipsis marker, see :python:`Python's documentation +`. + There are a number of magic comments that you can put into the example code that change how the output is verified by the Sage doctest framework. Here is a comprehensive list: @@ -977,7 +1007,7 @@ The Pickle Jar Sage maintains a pickle jar at ``SAGE_ROOT/src/ext/pickle_jar/pickle_jar.tar.bz2`` which is a tar file of "standard" pickles created by ``sage``. This pickle jar is -used to ensure that sage maintains backward compatibility by have +used to ensure that sage maintains backward compatibility by having :func:`sage.structure.sage_object.unpickle_all` check that ``sage`` can always unpickle all of the pickles in the pickle jar as part of the standard doc testing framework. diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 60ae52a4dd1..dbe87f4e9de 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -830,6 +830,25 @@ In order to just run the tests that are marked as requiring magma, omit ``sage`` cpu time: 0.1 seconds cumulative wall time: 2.0 seconds +If you want Sage to detect external software or other capabilities +(such as magma, latex, internet) automatically and run all of the +relevant tests, then add ``external``:: + + $ sage -t --optional=external src/sage/rings/real_mpfr.pyx + Running doctests with ID 2016-03-16-14-10-21-af2ebb67. + Using --optional=external + External software to be detected: cplex,gurobi,internet,latex,macaulay2,magma,maple,mathematica,matlab,octave,scilab + Doctesting 1 file. + sage -t --warn-long 28.0 src/sage/rings/real_mpfr.pyx + [5 tests, 0.04 s] + ---------------------------------------------------------------------- + All tests passed! + ---------------------------------------------------------------------- + Total time for all tests: 0.5 seconds + cpu time: 0.0 seconds + cumulative wall time: 0.0 seconds + External software detected for doctesting: magma + To run all tests, regardless of whether they are marked optional, pass ``all`` as the ``optional`` tag:: [roed@sage sage-6.0]$ sage -t --optional=all src/sage/rings/real_mpfr.pyx diff --git a/src/doc/en/developer/sagenb/set_up_fork.rst b/src/doc/en/developer/sagenb/set_up_fork.rst index 66bd918a4da..b0c82043b49 100644 --- a/src/doc/en/developer/sagenb/set_up_fork.rst +++ b/src/doc/en/developer/sagenb/set_up_fork.rst @@ -15,7 +15,7 @@ Overview cd sagenb git remote add upstream git://github.com/sagemath/sagenb.git -In etail +In Detail ========= Clone Your Fork diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index a2d7980a026..4ef74f60188 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -40,7 +40,9 @@ Named associative algebras :maxdepth: 2 sage/algebras/affine_nil_temperley_lieb + sage/combinat/diagram_algebras sage/algebras/clifford_algebra + sage/combinat/descent_algebra sage/algebras/hall_algebra sage/algebras/iwahori_hecke_algebra sage/combinat/posets/incidence_algebras @@ -48,6 +50,7 @@ Named associative algebras sage/combinat/posets/moebius_algebra sage/algebras/nil_coxeter_algebra sage/algebras/orlik_solomon + sage/combinat/partition_algebra sage/algebras/quatalg/quaternion_algebra sage/algebras/schur_algebra sage/algebras/steenrod/steenrod_algebra diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index 34cf4165a73..b7deab4fb19 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -22,6 +22,7 @@ Maps and Morphisms sage/categories/map sage/categories/homset sage/categories/morphism + sage/categories/pushout Individual Categories --------------------- @@ -29,6 +30,7 @@ Individual Categories .. toctree:: :maxdepth: 2 + sage/categories/action sage/categories/additive_groups sage/categories/additive_magmas sage/categories/additive_monoids @@ -53,6 +55,7 @@ Individual Categories sage/categories/commutative_ring_ideals sage/categories/commutative_rings sage/categories/complete_discrete_valuation + sage/categories/complex_reflection_groups sage/categories/coxeter_group_algebras sage/categories/coxeter_groups sage/categories/crystals @@ -68,6 +71,7 @@ Individual Categories sage/categories/filtered_algebras_with_basis sage/categories/filtered_modules sage/categories/filtered_modules_with_basis + sage/categories/finite_complex_reflection_groups sage/categories/finite_coxeter_groups sage/categories/finite_crystals sage/categories/finite_dimensional_algebras_with_basis @@ -86,9 +90,12 @@ Individual Categories sage/categories/finite_semigroups sage/categories/finite_sets sage/categories/finite_weyl_groups + sage/categories/finitely_generated_magmas + sage/categories/finitely_generated_semigroups sage/categories/function_fields sage/categories/g_sets sage/categories/gcd_domains + sage/categories/generalized_coxeter_groups sage/categories/graded_algebras sage/categories/graded_algebras_with_basis sage/categories/graded_bialgebras @@ -144,7 +151,13 @@ Individual Categories sage/categories/sets_cat sage/categories/sets_with_grading sage/categories/sets_with_partial_maps + sage/categories/shephard_groups sage/categories/simplicial_complexes + sage/categories/super_algebras + sage/categories/super_algebras_with_basis + sage/categories/super_hopf_algebras_with_basis + sage/categories/super_modules + sage/categories/super_modules_with_basis sage/categories/topological_spaces sage/categories/unique_factorization_domains sage/categories/unital_algebras diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index 38612cbff15..dfcf1248722 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -26,6 +26,7 @@ Linear codes and related constructions sage/coding/binary_code sage/coding/grs + sage/coding/hamming_code sage/coding/guruswami_sudan/gs_decoder sage/coding/guruswami_sudan/interpolation sage/coding/guruswami_sudan/rootfinding diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 4456ff7d980..16c87d06180 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -221,6 +221,8 @@ Comprehensive Module list sage/combinat/root_system/non_symmetric_macdonald_polynomials sage/combinat/root_system/pieri_factors sage/combinat/root_system/plot + sage/combinat/root_system/reflection_group_complex + sage/combinat/root_system/reflection_group_real sage/combinat/root_system/root_lattice_realization_algebras sage/combinat/root_system/root_lattice_realizations sage/combinat/root_system/root_space diff --git a/src/doc/en/reference/conf.py b/src/doc/en/reference/conf.py index 261ded54e2b..9e557175453 100644 --- a/src/doc/en/reference/conf.py +++ b/src/doc/en/reference/conf.py @@ -53,7 +53,7 @@ ''' #Ignore all .rst in the _sage subdirectory -exclude_trees = exclude_trees + ['_sage'] +exclude_patterns = exclude_patterns + ['_sage'] multidocs_is_master = True @@ -67,6 +67,6 @@ # List of directories, relative to source directory, that shouldn't be # searched for source files. -exclude_trees += multidocs_subdoc_list + [ +exclude_patterns += multidocs_subdoc_list + [ 'sage', 'sagenb', 'options' ] diff --git a/src/doc/en/reference/conf_sub.py b/src/doc/en/reference/conf_sub.py index 15cc05564f0..078e0dc7de9 100644 --- a/src/doc/en/reference/conf_sub.py +++ b/src/doc/en/reference/conf_sub.py @@ -62,6 +62,6 @@ ] #Ignore all .rst in the _sage subdirectory -exclude_trees = exclude_trees + ['_sage'] +exclude_patterns = exclude_patterns + ['_sage'] multidocs_is_master = False diff --git a/src/doc/en/reference/doctest/index.rst b/src/doc/en/reference/doctest/index.rst index a537ed878ec..88ec7a54e90 100644 --- a/src/doc/en/reference/doctest/index.rst +++ b/src/doc/en/reference/doctest/index.rst @@ -9,6 +9,7 @@ Sage's Doctesting Framework sage/doctest/forker sage/doctest/parsing sage/doctest/reporting + sage/doctest/external sage/doctest/test sage/doctest/util sage/doctest/fixtures diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index 0524030d31c..6b36969475b 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -23,6 +23,7 @@ cell complexes. sage/homology/cubical_complex sage/homology/cell_complex sage/homology/koszul_complex + sage/homology/hochschild_complex sage/homology/homology_group sage/homology/homology_vector_space_with_basis sage/homology/algebraic_topological_model diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 104cf4e2684..ce41b5d970d 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -88,6 +88,7 @@ Geometry and Topology * :doc:`Differential Forms ` * :doc:`Manifolds ` * :doc:`Parametrized Surfaces ` +* :doc:`Knot Theory ` Number Theory, Algebraic Geometry --------------------------------- diff --git a/src/doc/en/reference/knots/conf.py b/src/doc/en/reference/knots/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/knots/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/knots/index.rst b/src/doc/en/reference/knots/index.rst new file mode 100644 index 00000000000..ecaa3832288 --- /dev/null +++ b/src/doc/en/reference/knots/index.rst @@ -0,0 +1,10 @@ +Knot Theory +=========== + +.. toctree:: + :maxdepth: 2 + + sage/knots/knot + sage/knots/link + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/matrices/index.rst b/src/doc/en/reference/matrices/index.rst index 382d5e81602..04af1a46cd6 100644 --- a/src/doc/en/reference/matrices/index.rst +++ b/src/doc/en/reference/matrices/index.rst @@ -85,6 +85,7 @@ objects like operation tables (e.g. the multiplication table of a group). sage/matrix/matrix_real_double_dense sage/matrix/matrix_complex_double_dense + sage/matrix/matrix_complex_ball_dense sage/matrix/matrix_mpolynomial_dense @@ -94,7 +95,6 @@ objects like operation tables (e.g. the multiplication table of a group). sage/matrix/change_ring sage/matrix/echelon_matrix sage/matrix/matrix_cyclo_dense - sage/matrix/matrix_integer_2x2 sage/matrix/matrix_integer_dense_hnf sage/matrix/matrix_integer_dense_saturation sage/matrix/matrix_integer_sparse diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index deadc2cd23c..10a0a1d0c60 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -125,7 +125,6 @@ Caching .. toctree:: :maxdepth: 1 - sage/misc/cache sage/misc/cachefunc sage/misc/weak_dict @@ -227,6 +226,7 @@ Interactive Sage Sessions sage/misc/viewer sage/misc/pager sage/misc/sagedoc + sage/misc/sphinxify Distribution ~~~~~~~~~~~~ diff --git a/src/doc/en/reference/numerical/index.rst b/src/doc/en/reference/numerical/index.rst index 68eb094d3c4..383db1a2bb8 100644 --- a/src/doc/en/reference/numerical/index.rst +++ b/src/doc/en/reference/numerical/index.rst @@ -14,14 +14,16 @@ Numerical Optimization sage/numerical/optimize sage/numerical/interactive_simplex_method -Linear Optimization (LP) Solver backends ----------------------------------------- +Linear Optimization (LP) and Mixed Integer Linear Optimization (MIP) Solver backends +------------------------------------------------------------------------------------ .. toctree:: :maxdepth: 1 sage/numerical/backends/generic_backend + sage/numerical/backends/interactivelp_backend sage/numerical/backends/glpk_backend + sage/numerical/backends/glpk_exact_backend sage/numerical/backends/glpk_graph_backend sage/numerical/backends/ppl_backend sage/numerical/backends/cvxopt_backend diff --git a/src/doc/en/reference/parallel/index.rst b/src/doc/en/reference/parallel/index.rst index fe2fecd9703..491ca04ac9c 100644 --- a/src/doc/en/reference/parallel/index.rst +++ b/src/doc/en/reference/parallel/index.rst @@ -7,6 +7,7 @@ Parallel Computing sage/parallel/decorate sage/parallel/reference sage/parallel/use_fork + sage/parallel/map_reduce sage/parallel/multiprocessing_sage sage/parallel/parallelism sage/parallel/ncpus diff --git a/src/doc/en/reference/parallel/media/map_reduce_arch.fig b/src/doc/en/reference/parallel/media/map_reduce_arch.fig new file mode 100644 index 00000000000..1fe5d94cab5 --- /dev/null +++ b/src/doc/en/reference/parallel/media/map_reduce_arch.fig @@ -0,0 +1,277 @@ +#FIG 3.2 Produced by xfig version 3.2.5c +Landscape +Center +Metric +A4 +100.00 +Single +-2 +1200 2 +6 6028 1350 6210 2430 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 6119.000 1448.500 6029 1411 6119 1351 6209 1411 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 6119.000 2406.500 6029 2369 6119 2309 6209 2369 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 6119.000 2331.500 6029 2369 6119 2429 6209 2369 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6029 1411 6029 2369 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 6209 1411 6209 2369 +-6 +6 -630 3510 3855 6480 +6 -630 6030 270 6300 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 160.330 6207.000 223 6117 270 6207 223 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 -598.670 6207.000 -536 6117 -489 6207 -536 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 -473.330 6207.000 -536 6117 -583 6207 -536 6297 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 223 6117 -536 6117 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 223 6297 -536 6297 +-6 +6 2880 5580 3780 5850 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 2989.670 5757.000 2927 5667 2880 5757 2927 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 3748.670 5757.000 3686 5667 3639 5757 3686 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 3623.330 5757.000 3686 5667 3733 5757 3686 5847 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2927 5667 3686 5667 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 2927 5847 3686 5847 +-6 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5940 900 5940 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5580 900 5580 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5040 900 5040 +2 2 0 1 0 7 50 -1 45 0.000 0 0 -1 0 0 5 + 450 4050 900 4050 900 5040 450 5040 450 4050 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 450 4050 900 4050 900 6300 450 6300 450 4050 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 1800 4140 2340 4140 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 2610 4680 1800 4680 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 900 6210 1800 6210 +2 1 0 3 1 7 50 -1 -1 0.000 0 0 7 1 1 2 + 0 0 3.00 90.00 120.00 + 0 0 3.00 90.00 120.00 + 891 5123 1440 4590 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 0 6210 450 6210 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 3150 5760 2700 5760 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 0 3510 3150 3510 3150 6480 0 6480 0 3510 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1800 5490 2700 5490 2700 6300 1800 6300 1800 5490 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 6120 900 6120 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5760 900 5760 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5220 900 5220 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 450 5400 900 5400 +2 4 0 3 1 7 50 -1 -1 0.000 0 0 7 0 0 5 + 2790 4680 1440 4680 1440 4140 2790 4140 2790 4680 +4 0 0 50 -1 2 20 0.0000 0 225 1380 990 3870 Worker 0\001 +4 0 0 50 -1 2 14 0.0000 0 210 1005 1620 4500 main loop\001 +4 0 0 50 -1 2 14 0.0000 0 165 630 1260 5040 Nodes\001 +4 0 0 50 -1 2 14 0.0000 0 165 540 1980 5850 Thief\001 +4 0 0 50 -1 2 14 0.0000 0 165 765 1890 6120 Thread\001 +4 0 0 50 -1 2 14 0.0000 0 165 465 1080 6030 theft\001 +4 0 0 50 -1 2 14 0.0000 0 165 960 -630 6030 Steal Ans\001 +4 0 0 50 -1 2 14 0.0000 0 210 975 2880 5490 Steal Req\001 +4 1 0 50 -1 2 14 0.0000 0 165 135 675 4950 S\001 +4 1 0 50 -1 2 14 0.0000 0 165 165 675 5235 T\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 675 5520 A\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 675 5805 C\001 +4 1 0 50 -1 2 14 0.0000 0 165 195 675 6090 K\001 +-6 +6 3600 3510 8085 6480 +6 3600 6030 4500 6300 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 4390.330 6207.000 4453 6117 4500 6207 4453 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 3631.330 6207.000 3694 6117 3741 6207 3694 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 3756.670 6207.000 3694 6117 3647 6207 3694 6297 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4453 6117 3694 6117 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 4453 6297 3694 6297 +-6 +6 7110 5580 8010 5850 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 7219.670 5757.000 7157 5667 7110 5757 7157 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 7978.670 5757.000 7916 5667 7869 5757 7916 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 7853.330 5757.000 7916 5667 7963 5757 7916 5847 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7157 5667 7916 5667 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 7157 5847 7916 5847 +-6 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5940 5130 5940 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5580 5130 5580 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5040 5130 5040 +2 2 0 1 0 7 50 -1 45 0.000 0 0 -1 0 0 5 + 4680 4050 5130 4050 5130 5040 4680 5040 4680 4050 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4680 4050 5130 4050 5130 6300 4680 6300 4680 4050 +2 4 0 3 0 7 50 -1 -1 0.000 0 0 7 0 0 5 + 7020 4680 7020 4140 5670 4140 5670 4680 7020 4680 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 6030 4140 6570 4140 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 6840 4680 6030 4680 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 5130 6210 6030 6210 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 1 2 + 0 0 3.00 90.00 120.00 + 0 0 3.00 90.00 120.00 + 5121 5123 5670 4590 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 4230 6210 4680 6210 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 7380 5760 6930 5760 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 4230 3510 7380 3510 7380 6480 4230 6480 4230 3510 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6030 5490 6930 5490 6930 6300 6030 6300 6030 5490 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 6120 5130 6120 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5760 5130 5760 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5220 5130 5220 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 4680 5400 5130 5400 +4 0 0 50 -1 2 14 0.0000 0 210 1005 5850 4500 main loop\001 +4 0 0 50 -1 2 14 0.0000 0 165 630 5490 5040 Nodes\001 +4 0 0 50 -1 2 14 0.0000 0 165 540 6210 5850 Thief\001 +4 0 0 50 -1 2 14 0.0000 0 165 765 6120 6120 Thread\001 +4 0 0 50 -1 2 14 0.0000 0 165 465 5310 6030 theft\001 +4 0 0 50 -1 2 14 0.0000 0 165 960 3600 6030 Steal Ans\001 +4 0 0 50 -1 2 14 0.0000 0 210 975 7110 5490 Steal Req\001 +4 1 0 50 -1 2 14 0.0000 0 165 135 4905 4950 S\001 +4 1 0 50 -1 2 14 0.0000 0 165 165 4905 5235 T\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 4905 5520 A\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 4905 5805 C\001 +4 1 0 50 -1 2 14 0.0000 0 165 195 4905 6090 K\001 +4 0 0 50 -1 2 20 0.0000 0 225 1380 5220 3870 Worker 1\001 +-6 +6 7920 4860 8550 5040 +1 3 0 3 0 0 50 -1 20 0.000 1 0.0000 8010 4950 45 45 8010 4950 8055 4950 +1 3 0 3 0 0 50 -1 20 0.000 1 0.0000 8235 4950 45 45 8235 4950 8280 4950 +1 3 0 3 0 0 50 -1 20 0.000 1 0.0000 8460 4950 45 45 8460 4950 8505 4950 +-6 +6 8550 3510 13035 6480 +6 8550 6030 9450 6300 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 9340.330 6207.000 9403 6117 9450 6207 9403 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 8581.330 6207.000 8644 6117 8691 6207 8644 6297 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 8706.670 6207.000 8644 6117 8597 6207 8644 6297 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9403 6117 8644 6117 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 9403 6297 8644 6297 +-6 +6 12060 5580 12960 5850 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 12169.670 5757.000 12107 5667 12060 5757 12107 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 1 0 0 12928.670 5757.000 12866 5667 12819 5757 12866 5847 +5 1 0 1 0 7 50 -1 -1 0.000 0 0 0 0 12803.330 5757.000 12866 5667 12913 5757 12866 5847 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 12107 5667 12866 5667 +2 1 0 1 0 7 50 -1 20 0.000 0 0 -1 0 0 2 + 12107 5847 12866 5847 +-6 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5940 10080 5940 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5580 10080 5580 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5040 10080 5040 +2 2 0 1 0 7 50 -1 45 0.000 0 0 -1 0 0 5 + 9630 4050 10080 4050 10080 5040 9630 5040 9630 4050 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9630 4050 10080 4050 10080 6300 9630 6300 9630 4050 +2 4 0 3 0 7 50 -1 -1 0.000 0 0 7 0 0 5 + 11970 4680 11970 4140 10620 4140 10620 4680 11970 4680 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 10980 4140 11520 4140 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 -1 1 0 2 + 0 0 1.00 90.00 120.00 + 11790 4680 10980 4680 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 10080 6210 10980 6210 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 1 2 + 0 0 3.00 90.00 120.00 + 0 0 3.00 90.00 120.00 + 10071 5123 10620 4590 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 9180 6210 9630 6210 +2 1 0 3 0 7 50 -1 -1 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 12330 5760 11880 5760 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9180 3510 12330 3510 12330 6480 9180 6480 9180 3510 +2 2 0 3 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 10980 5490 11880 5490 11880 6300 10980 6300 10980 5490 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 6120 10080 6120 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5760 10080 5760 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5220 10080 5220 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 9630 5400 10080 5400 +4 0 0 50 -1 2 14 0.0000 0 210 1005 10800 4500 main loop\001 +4 0 0 50 -1 2 14 0.0000 0 165 630 10440 5040 Nodes\001 +4 0 0 50 -1 2 14 0.0000 0 165 540 11160 5850 Thief\001 +4 0 0 50 -1 2 14 0.0000 0 165 765 11070 6120 Thread\001 +4 0 0 50 -1 2 14 0.0000 0 165 465 10260 6030 theft\001 +4 0 0 50 -1 2 14 0.0000 0 165 960 8550 6030 Steal Ans\001 +4 0 0 50 -1 2 14 0.0000 0 210 975 12060 5490 Steal Req\001 +4 1 0 50 -1 2 14 0.0000 0 165 135 9855 4950 S\001 +4 1 0 50 -1 2 14 0.0000 0 165 165 9855 5235 T\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 9855 5520 A\001 +4 1 0 50 -1 2 14 0.0000 0 165 180 9855 5805 C\001 +4 1 0 50 -1 2 14 0.0000 0 165 195 9855 6090 K\001 +4 0 0 50 -1 2 20 0.0000 0 225 1455 10170 3870 Worker N\001 +-6 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 2700 450 7200 450 7200 2250 2700 2250 2700 450 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3600 1350 4050 1350 4050 1800 3600 1800 3600 1350 +2 1 0 3 0 0 50 -1 20 0.000 0 0 7 1 0 2 + 0 0 3.00 90.00 120.00 + 6120 2655 6120 1350 +2 1 0 3 0 0 50 -1 20 0.000 0 0 -1 0 0 2 + 2700 3510 6120 2655 +2 1 0 3 0 0 50 -1 20 0.000 0 0 -1 0 0 2 + 5850 3510 6120 2655 +2 1 0 3 0 0 50 -1 20 0.000 0 0 -1 0 0 2 + 9630 3510 6120 2655 +2 1 0 3 4 -1 50 -1 -1 0.000 0 0 -1 1 0 3 + 3 1 3.00 90.00 120.00 + 4230 4950 3780 5760 3150 5760 +2 1 0 3 4 -1 50 -1 -1 0.000 0 0 7 1 0 2 + 3 1 3.00 90.00 120.00 + 945 6210 1755 6210 +2 1 0 3 4 -1 50 -1 -1 0.000 0 0 -1 1 0 2 + 3 1 3.00 90.00 120.00 + 2700 6210 4230 6210 +4 0 0 50 -1 2 20 0.0000 0 225 1020 4500 900 Master\001 +4 0 0 50 -1 2 16 0.0000 0 180 1485 3150 1260 Active Tasks\001 +4 0 0 50 -1 2 16 0.0000 0 180 840 5760 1260 Results\001 +4 1 4 50 -1 2 16 0.0000 0 180 135 3825 1665 4\001 diff --git a/src/doc/en/reference/parallel/media/map_reduce_arch.pdf b/src/doc/en/reference/parallel/media/map_reduce_arch.pdf new file mode 100644 index 00000000000..e8590b8c32b Binary files /dev/null and b/src/doc/en/reference/parallel/media/map_reduce_arch.pdf differ diff --git a/src/doc/en/reference/parallel/media/map_reduce_arch.png b/src/doc/en/reference/parallel/media/map_reduce_arch.png new file mode 100644 index 00000000000..9305dd79247 Binary files /dev/null and b/src/doc/en/reference/parallel/media/map_reduce_arch.png differ diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 1ddae8bf0e3..eb5a575d6a8 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -87,8 +87,6 @@ Miscellaneous .. toctree:: :maxdepth: 2 - sage/ext/interactive_constructors_c - sage/repl/readline_extra_commands sage/repl/interpreter diff --git a/src/doc/en/reference/rings_standard/index.rst b/src/doc/en/reference/rings_standard/index.rst index 4e8cb50e828..64857695c4c 100644 --- a/src/doc/en/reference/rings_standard/index.rst +++ b/src/doc/en/reference/rings_standard/index.rst @@ -14,7 +14,7 @@ Integers sage/rings/factorint sage/rings/fast_arith sage/rings/sum_of_squares - sage/ext/multi_modular + sage/arith/multi_modular sage/arith/misc .. SEEALSO:: 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 681f38cb3d9..151c18d5bca 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 @@ -216,8 +216,7 @@ cyclotomic field. :: sage: G = DirichletGroup(8); G - Group of Dirichlet characters of modulus 8 over Cyclotomic - Field of order 2 and degree 1 + Group of Dirichlet characters modulo 8 with values in Cyclotomic Field of order 2 and degree 1 sage: v = G.list(); v [Dirichlet character modulo 8 of conductor 1 mapping 7 |--> 1, 5 |--> 1, Dirichlet character modulo 8 of conductor 4 mapping 7 |--> -1, 5 |--> 1, @@ -230,7 +229,7 @@ cyclotomic field. Sage both represents Dirichlet characters by giving a "matrix", i.e., the list of images of canonical generators of -:math:`(\ZZ/N\ZZ)^*`, and as vectors modulo and +:math:`(\ZZ/N\ZZ)^*`, and as vectors modulo an integer :math:`n`. For years, I was torn between these two representations, until J. Quer and I realized that the best approach is to use both and make it easy to convert between them. diff --git a/src/doc/en/thematic_tutorials/lie/bibliography.rst b/src/doc/en/thematic_tutorials/lie/bibliography.rst index 6ffef9e8e0f..7d9f183c37e 100644 --- a/src/doc/en/thematic_tutorials/lie/bibliography.rst +++ b/src/doc/en/thematic_tutorials/lie/bibliography.rst @@ -5,75 +5,75 @@ Bibliography .. [Bourbaki46] Nicolas Bourbaki. *Lie Groups and Lie Algebras: Chapters 4-6*. Springer, reprint edition, 1998. -.. [BumpNakasuji2010] D. Bump and M. Nakasuji. Casselman's basis of +.. [BumpNakasuji2010] \D. Bump and M. Nakasuji. Casselman's basis of Iwahori vectors and the Bruhat order. arXiv:1002.2996, http://arxiv.org/abs/1002.2996. -.. [Carrell1994] J. B. Carrell. The Bruhat graph of a Coxeter group, a +.. [Carrell1994] \J. B. Carrell. The Bruhat graph of a Coxeter group, a conjecture of Deodhar, and rational smoothness of Schubert varieties. In *Algebraic Groups and Their Generalizations: Classical Methods*, AMS Proceedings of Symposia in Pure Mathematics, 56, 53--61, 1994. -.. [Deodhar1977] V. V. Deodhar. Some characterizations of Bruhat +.. [Deodhar1977] \V. V. Deodhar. Some characterizations of Bruhat ordering on a Coxeter group and determination of the relative Moebius function. Inventiones Mathematicae, 39(2):187--198, 1977. -.. [Dyer1993] M. J. Dyer. The nil Hecke ring and Deodhar's conjecture +.. [Dyer1993] \M. J. Dyer. The nil Hecke ring and Deodhar's conjecture on Bruhat intervals. Inventiones Mathematicae, 111(1):571--574, 1993. -.. [Dynkin1952] E. B. Dynkin, +.. [Dynkin1952] \E. B. Dynkin, Semisimple subalgebras of semisimple Lie algebras. (Russian) Mat. Sbornik N.S. 30(72):349–462, 1952. -.. [FauserEtAl2006] B. Fauser, P. D. Jarvis, R. C. King, and +.. [FauserEtAl2006] \B. Fauser, P. D. Jarvis, R. C. King, and B. G. Wybourne. New branching rules induced by plethysm. *Journal of Physics A*. 39(11):2611--2655, 2006. -.. [Fulton1997] W. Fulton. *Young Tableaux*. Cambridge University +.. [Fulton1997] \W. Fulton. *Young Tableaux*. Cambridge University Press, 1997. -.. [FourierEtAl2009] G. Fourier, M. Okado, A. Schilling. +.. [FourierEtAl2009] \G. Fourier, M. Okado, A. Schilling. Kirillov--Reshetikhin crystal for nonexceptional types. *Advances in Mathematics*, 222:1080--1116, 2009. -.. [FourierEtAl2010] G. Fourier, M. Okado, A. Schilling. +.. [FourierEtAl2010] \G. Fourier, M. Okado, A. Schilling. Perfectness of Kirillov-Reshetikhin crystals for nonexceptional types. *Contemp. Math.*, 506:127--143, 2010. -.. [HatayamaEtAl2001] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. +.. [HatayamaEtAl2001] \G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. Paths, crystals and fermionic formulae. in MathPhys Odyssey 2001, in : Prog. Math. Phys., vol 23, Birkhauser Boston, Boston, MA 2002, pp. 205--272. -.. [HainesEtAl2009] T. J. Haines, R. E. Kottwitz, and +.. [HainesEtAl2009] \T. J. Haines, R. E. Kottwitz, and A. Prasad. Iwahori-Hecke Algebras. arXiv:math/0309168, http://arxiv.org/abs/math/0309168. -.. [HongKang2002] J. Hong and S.-J. Kang. *Introduction to Quantum +.. [HongKang2002] \J. Hong and S.-J. Kang. *Introduction to Quantum Groups and Crystal Bases*. AMS Graduate Studies in Mathematics, American Mathematical Society, 2002. -.. [HongLee2008] J. Hong and H. Lee. Young tableaux and crystal +.. [HongLee2008] \J. Hong and H. Lee. Young tableaux and crystal `B(\infty)` for finite simple Lie algebras. *J. Algebra*, 320:3680--3693, 2008. -.. [HoweEtAl2005] R. Howe, E.-C.Tan, and J. F. Willenbring. Stable +.. [HoweEtAl2005] \R. Howe, E.-C.Tan, and J. F. Willenbring. Stable branching rules for classical symmetric pairs. *Transactions of the American Mathematical Society*, 357(4):1601--1626, 2005. -.. [Iwahori1964] N. Iwahori. On the structure of a Hecke ring of a +.. [Iwahori1964] \N. Iwahori. On the structure of a Hecke ring of a Chevalley group over a finite field. *J. Fac. Sci. Univ. Tokyo Sect. I*, 10:215--236, 1964. -.. [Jimbo1986] M. A. Jimbo. `q`-analogue of `U(\mathfrak{gl}(N+1))`, +.. [Jimbo1986] \M. A. Jimbo. `q`-analogue of `U(\mathfrak{gl}(N+1))`, Hecke algebra, and the Yang-Baxter equation. *Lett. Math. Phys*, 11(3):247--252, 1986. -.. [JonesEtAl2010] B. Jones, A. Schilling. +.. [JonesEtAl2010] \B. Jones, A. Schilling. Affine structures and a tableau model for E_6 crystals *J. Algebra*, 324:2512-2542, 2010. -.. [Joseph1995] A. Joseph. *Quantum Groups and Their Primitive Ideals*. +.. [Joseph1995] \A. Joseph. *Quantum Groups and Their Primitive Ideals*. Springer-Verlag, 1995. .. [Kac] Victor G. Kac. *Infinite Dimensional Lie algebras* @@ -84,75 +84,75 @@ Bibliography Affine crystals and vertex models. *Int. J. Mod. Phys.* A 7 (suppl. 1A): 449--484, 1992. -.. [KKS2007] S.-J. Kang, J.-A. Kim, and D.-U. Shin. +.. [KKS2007] \S.-J. Kang, J.-A. Kim, and D.-U. Shin. Modified Nakajima monomials and the crystal `B(\infty)`. *J. Algebra*, **308** (2007), 524-535. -.. [Kashiwara1993] M. Kashiwara. The crystal base and Littelmann's refined +.. [Kashiwara1993] \M. Kashiwara. The crystal base and Littelmann's refined Demazure character formula. *Duke Math. J.*, 71(3):839--858, 1993. -.. [Kashiwara1995] M. Kashiwara. On crystal bases. Representations of +.. [Kashiwara1995] \M. Kashiwara. On crystal bases. Representations of groups (Banff, AB, 1994), 155--197, CMS Conference Proceedings, 16, American Mathematical Society, Providence, RI, 1995. -.. [KashiwaraNakashima1994] M. Kashiwara and T. Nakashima. Crystal +.. [KashiwaraNakashima1994] \M. Kashiwara and T. Nakashima. Crystal graphs for representations of the `q`-analogue of classical Lie algebras. *Journal Algebra*, 165(2):295--345, 1994. -.. [KimShin2010] J.-A. Kim and D.-U. Shin. Generalized Young walls and +.. [KimShin2010] \J.-A. Kim and D.-U. Shin. Generalized Young walls and crystal bases for quantum affine algebra of type `A`. *Proc. Amer. Math. Soc.*, 138(11):3877--3889, 2010. -.. [King1975] R. C. King. Branching rules for classical Lie groups +.. [King1975] \R. C. King. Branching rules for classical Lie groups using tensor and spinor methods. *Journal of Physics A*, 8:429--449, 1975. -.. [Knuth1970] D. Knuth. Permutations, matrices, and generalized Young +.. [Knuth1970] \D. Knuth. Permutations, matrices, and generalized Young tableaux. *Pacific Journal of Mathematics*, 34(3):709--727, 1970. -.. [Knuth1998] D. Knuth. *The Art of Computer +.. [Knuth1998] \D. Knuth. *The Art of Computer Programming. Volume 3. Sorting and Searching*. Addison Wesley Longman, 1998. -.. [LNSSS14I] C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. +.. [LNSSS14I] \C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. A uniform model for for Kirillov-Reshetikhin crystals I: Lifting the parabolic quantum Bruhat graph. (2014) :arxiv:`1211.2042` -.. [LNSSS14II] C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. +.. [LNSSS14II] \C. Lenart, S. Naito, D. Sagaki, A. Schilling, and M. Shimozono. A uniform model for for Kirillov-Reshetikhin crystals II: Alcove model, path model, and `P = X`. (2014) :arxiv:`1402.2203` -.. [L1995] P. Littelmann. *Paths and root operators in representation theory*. +.. [L1995] \P. Littelmann. *Paths and root operators in representation theory*. Ann. of Math. (2) 142 (1995), no. 3, 499-525. -.. [McKayPatera1981] W. G. McKay and J. Patera. *Tables of Dimensions, +.. [McKayPatera1981] \W. G. McKay and J. Patera. *Tables of Dimensions, Indices and Branching Rules for Representations of Simple Lie Algebras*. Marcel Dekker, 1981. -.. [OkadoSchilling2008] M. Okado, A.Schilling. Existence of crystal bases for +.. [OkadoSchilling2008] \M. Okado, A.Schilling. Existence of crystal bases for Kirillov--Reshetikhin crystals for nonexceptional types. *Representation Theory* 12:186--207, 2008. -.. [Seitz1991] G. Seitz, +.. [Seitz1991] \G. Seitz, Maximal subgroups of exceptional algebraic groups. Mem. Amer. Math. Soc. 90 (1991), no. 441. -.. [Rubenthaler2008] H. Rubenthaler, +.. [Rubenthaler2008] \H. Rubenthaler, The (A2,G2) duality in E6, octonions and the triality principle. Trans. Amer. Math. Soc. 360 (2008), no. 1, 347–367. -.. [SalisburyScrimshaw2015] B. Salisbury and T. Scrimshaw. A rigged +.. [SalisburyScrimshaw2015] \B. Salisbury and T. Scrimshaw. A rigged configuration model for `B(\infty)`. *J. Combin. Theory Ser. A*, 133:29--57, 2015. -.. [Schilling2006] A. Schilling. Crystal structure on rigged configurations. +.. [Schilling2006] \A. Schilling. Crystal structure on rigged configurations. *Int. Math. Res. Not.*, Volume 2006. (2006) Article ID 97376. Pages 1-27. -.. [SchillingTingley2011] A. Schilling, P. Tingley. +.. [SchillingTingley2011] \A. Schilling, P. Tingley. Demazure crystals, Kirillov-Reshetikhin crystals, and the energy function. preprint arXiv:1104.2359 -.. [Stanley1999] R. P. Stanley. *Enumerative Combinatorics, Volume +.. [Stanley1999] \R. P. Stanley. *Enumerative Combinatorics, Volume 2*. Cambridge University Press, 1999. .. [Testerman1989] Testerman, Donna M. diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index 1b822dc51d0..a237896ecc1 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -206,7 +206,7 @@ at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -227,7 +227,16 @@ where `(a_{ij})` is a Cartan matrix. Then Monomial crystals depend on the choice of positive integers `C = (c_{ij})_{i\neq j}` satisfying the condition `c_{ij}+c_{ji}=1`. This choice has been made in Sage such that `c_{ij} = 1` if - `i < j` and `c_{ij} = 0` if `i>j`. + `i < j` and `c_{ij} = 0` if `i>j`, but other choices may be used if + deliberately stated at the intialization of the crystal:: + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['C',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() + [0 0 1] + [1 0 0] + [0 1 0] It is shown in [KKS2007]_ that the connected component of `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we denote by @@ -242,16 +251,18 @@ containing the element `\boldsymbol{1}`, which we denote by sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] -We can also model `B(\infty)` using the monomials `A_{i,k}` instead:: +We can also model `B(\infty)` using the variables `A_{i,k}` instead:: - sage: Ninf = crystals.infinity.NakajimaMonomials(['C',3,1], use_Y = False) - sage: ninf = Ninf.highest_weight_vector() - sage: n = ninf.f_string([0,1,2,3,2,1,0]); n + sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: minf = Minf.highest_weight_vector() + sage: Minf.set_variables('A') + sage: m = minf.f_string([0,1,2,3,2,1,0]); m A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1 - sage: n.weight() + sage: m.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta - sage: n.weight_in_root_lattice() + sage: m.weight_in_root_lattice() -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3] + sage: Minf.set_variables('Y') Building the crystal graph output for these monomial crystals is the same as the constructions above:: diff --git a/src/doc/en/thematic_tutorials/lie/weyl_groups.rst b/src/doc/en/thematic_tutorials/lie/weyl_groups.rst index 04213b643f1..b5e48a1384d 100644 --- a/src/doc/en/thematic_tutorials/lie/weyl_groups.rst +++ b/src/doc/en/thematic_tutorials/lie/weyl_groups.rst @@ -95,8 +95,8 @@ construct a list (e.g., using the ``list`` function) or use the method sage: ref[a1+a2+a3] s1*s2*s3*s2*s1 sage: list(ref) - [s1*s2*s3*s2*s1, s3*s2*s3, s2, s3, s1, s2*s3*s1*s2*s3*s1*s2, - s1*s2*s1, s3*s1*s2*s3*s1, s2*s3*s2] + [s1, s2, s3, s3*s2*s3, s2*s3*s2, s1*s2*s1, s3*s1*s2*s3*s1, + s1*s2*s3*s2*s1, s2*s3*s1*s2*s3*s1*s2] If instead you want a family whose keys are the reflections and whose values are the roots, you may use the inverse family:: @@ -107,8 +107,8 @@ and whose values are the roots, you may use the inverse family:: sage: altref = W.reflections().inverse_family() sage: pprint(altref) Finite family {s1*s2*s1: (1, 0, -1), s2: (0, 1, -1), s3*s2*s3: (0, 1, 1), - s1*s2*s3*s2*s1: (1, 0, 0), s1: (1, -1, 0), - s2*s3*s1*s2*s3*s1*s2: (1, 1, 0), s3*s1*s2*s3*s1: (1, 0, 1), + s3*s1*s2*s3*s1: (1, 0, 1), s1: (1, -1, 0), + s1*s2*s3*s2*s1: (1, 0, 0), s2*s3*s1*s2*s3*s1*s2: (1, 1, 0), s2*s3*s2: (0, 1, 0), s3: (0, 0, 1)} sage: altref[s3*s2*s3] (0, 1, 1) diff --git a/src/doc/en/thematic_tutorials/numtheory_rsa.rst b/src/doc/en/thematic_tutorials/numtheory_rsa.rst index edacce034af..7595ff76d47 100644 --- a/src/doc/en/thematic_tutorials/numtheory_rsa.rst +++ b/src/doc/en/thematic_tutorials/numtheory_rsa.rst @@ -475,17 +475,17 @@ Acknowledgements Bibliography ============ -.. [CormenEtAl2001] T. H. Cormen, C. E. Leiserson, R. L. Rivest, and +.. [CormenEtAl2001] \T. H. Cormen, C. E. Leiserson, R. L. Rivest, and C. Stein. *Introduction to Algorithms*. The MIT Press, USA, 2nd edition, 2001. -.. [MenezesEtAl1996] A. J. Menezes, P. C. van Oorschot, and +.. [MenezesEtAl1996] \A. J. Menezes, P. C. van Oorschot, and S. A. Vanstone. *Handbook of Applied Cryptography*. CRC Press, Boca Raton, FL, USA, 1996. -.. [Stinson2006] D. R. Stinson. *Cryptography: Theory and Practice*. +.. [Stinson2006] \D. R. Stinson. *Cryptography: Theory and Practice*. Chapman & Hall/CRC, Boca Raton, USA, 3rd edition, 2006. -.. [TrappeWashington2006] W. Trappe and L. C. Washington. *Introduction +.. [TrappeWashington2006] \W. Trappe and L. C. Washington. *Introduction to Cryptography with Coding Theory*. Pearson Prentice Hall, Upper Saddle River, New Jersey, USA, 2nd edition, 2006. diff --git a/src/doc/en/thematic_tutorials/sandpile.rst b/src/doc/en/thematic_tutorials/sandpile.rst index 737121dc7b6..f5a419601ee 100644 --- a/src/doc/en/thematic_tutorials/sandpile.rst +++ b/src/doc/en/thematic_tutorials/sandpile.rst @@ -4938,7 +4938,9 @@ Help Documentation for each method is available through the Sage online help system: -.. code-block:: python +.. skip + +:: sage: SandpileConfig.fire_vertex? Base Class: diff --git a/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst b/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst index 118e03c69c2..8db67d0b016 100644 --- a/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst +++ b/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst @@ -304,7 +304,7 @@ Some particular actions modify the data structure of ``el``:: sage: id4 = SymmetricGroup(4).one() sage: type(id4) - + sage: id4.__dict__ }> diff --git a/src/doc/en/tutorial/bibliography.rst b/src/doc/en/tutorial/bibliography.rst index 46311a59c57..930735beab4 100644 --- a/src/doc/en/tutorial/bibliography.rst +++ b/src/doc/en/tutorial/bibliography.rst @@ -42,7 +42,7 @@ Bibliography .. [SA] Sage web site http://www.sagemath.org/. -.. [Si] G.-M. Greuel, G. Pfister, and H. Schönemann. Singular +.. [Si] \G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.0. A Computer Algebra System for Polynomial Computations. Center for Computer Algebra, University of Kaiserslautern (2005). http://www.singular.uni-kl.de. diff --git a/src/doc/en/tutorial/tour_advanced.rst b/src/doc/en/tutorial/tour_advanced.rst index 32717e43892..4e959706243 100644 --- a/src/doc/en/tutorial/tour_advanced.rst +++ b/src/doc/en/tutorial/tour_advanced.rst @@ -323,8 +323,8 @@ factorization of the modulus. :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -332,10 +332,8 @@ factorization of the modulus. sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] Next, we construct the group of Dirichlet characters mod 20, but @@ -346,7 +344,7 @@ with values in :math:`\QQ(i)`: sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 We next compute several invariants of ``G``: @@ -379,8 +377,7 @@ the third argument to ``DirichletGroup`` below. sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/doc/en/tutorial/tour_numtheory.rst b/src/doc/en/tutorial/tour_numtheory.rst index b5b30bc9827..013238e6876 100644 --- a/src/doc/en/tutorial/tour_numtheory.rst +++ b/src/doc/en/tutorial/tour_numtheory.rst @@ -127,8 +127,9 @@ precision. 10*11^-2 + 5*11^-1 + 4 + 2*11 + O(11^18) Much work has been done implementing rings of integers in -:math:`p`-adic fields or number fields other than . The -interested reader is invited to ask the experts on the +:math:`p`-adic fields and number fields. The +interested reader is invited to read +:ref:`sage.rings.padics.tutorial` and ask the experts on the ``sage-support`` Google group for further details. A number of related methods are already implemented in the diff --git a/src/doc/fr/tutorial/bibliography.rst b/src/doc/fr/tutorial/bibliography.rst index b0dcb1b6dae..0450e9b3f04 100644 --- a/src/doc/fr/tutorial/bibliography.rst +++ b/src/doc/fr/tutorial/bibliography.rst @@ -42,7 +42,7 @@ Bibliographie .. [SA] Sage web site http://www.sagemath.org/. -.. [Si] G.-M. Greuel, G. Pfister, and H. Schönemann. Singular +.. [Si] \G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.0. A Computer Algebra System for Polynomial Computations. Center for Computer Algebra, University of Kaiserslautern (2005). http://www.singular.uni-kl.de. diff --git a/src/doc/fr/tutorial/tour_advanced.rst b/src/doc/fr/tutorial/tour_advanced.rst index 02aeef6f71a..d7e086c09e4 100644 --- a/src/doc/fr/tutorial/tour_advanced.rst +++ b/src/doc/fr/tutorial/tour_advanced.rst @@ -324,8 +324,8 @@ caractères, de même qu'une décomposition en produit direct correspondant :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -333,10 +333,8 @@ caractères, de même qu'une décomposition en produit direct correspondant sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] Construisons à present le groupe de caractères de Dirichlet modulo 20, @@ -347,7 +345,7 @@ mais à valeur dans :math:`\QQ(i)`: sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 Nous calculons ensuite différents invariants de ``G``: @@ -380,8 +378,7 @@ de la racine de l'unité par le troisième argument de la fonction sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/doc/ja/tutorial/bibliography.rst b/src/doc/ja/tutorial/bibliography.rst index db092568660..a772b477a06 100644 --- a/src/doc/ja/tutorial/bibliography.rst +++ b/src/doc/ja/tutorial/bibliography.rst @@ -42,7 +42,7 @@ Bibliography .. [SA] Sage web site http://www.sagemath.org/. -.. [Si] G.-M. Greuel, G. Pfister, and H. Schönemann. Singular +.. [Si] \G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.0. A Computer Algebra System for Polynomial Computations. Center for Computer Algebra, University of Kaiserslautern (2005). http://www.singular.uni-kl.de. diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index 7eb10a5272b..c479b133bc0 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -1,5 +1,5 @@ ********************************* -Sage,LaTexと仲間たち +Sage,LaTeXと仲間たち ********************************* 著者: Rob Beezer (2010-05-23) @@ -302,8 +302,8 @@ LaTeX処理のカスタマイズ 変換が実際にどう実行されるかについては,いくつもの要因が影響している. しかし大勢は,TeX実行形式として何が指定されているか,そして利用可能な変換ユーティリティは何かによって決まるようだ. ``dvips``, ``ps2pdf``, ``dvipng`` そして ``ImageMagick`` に含まれる ``convert`` の四種の優秀な変換ユーティリティがあれば,あらゆる状況に対処できるだろう. -目標はワークシートに挿入して表示可能なPNGファイルを生成することで,LaTeX表式からdvi形式へのLatexエンジンによる変換が成功していれば,dvipngが変換を仕上げてくれる. -LaTeX表式とLatexエンジンの生成するdvi形式にdvipngが扱えないspecial命令が入っている場合には,dvipsでポストスクリプトファイルへ変換する. +目標はワークシートに挿入して表示可能なPNGファイルを生成することで,LaTeX表式からdvi形式へのLaTeXエンジンによる変換が成功していれば,dvipngが変換を仕上げてくれる. +LaTeX表式とLaTeXエンジンの生成するdvi形式にdvipngが扱えないspecial命令が入っている場合には,dvipsでポストスクリプトファイルへ変換する. ポストスクリプトあるいは ``pdflatex`` エンジンによって出力されたPDFファイルは ``convert`` ユーティリティによってPNG形式へ変換される. ここで紹介した二つの変換ユーティリティは ``have_dvipng()`` と ``have_convert()`` ルーチンを使って存在を確認することができる. diff --git a/src/doc/ja/tutorial/sagetex.rst b/src/doc/ja/tutorial/sagetex.rst index 7c36bd3e8b7..139358ba36a 100644 --- a/src/doc/ja/tutorial/sagetex.rst +++ b/src/doc/ja/tutorial/sagetex.rst @@ -59,7 +59,7 @@ SageTeXの動作を体験するために,まずSageTeXのインストール手 \end{document} -この ``st_example.tex`` をいつも通りにLaTexで処理する. +この ``st_example.tex`` をいつも通りにLaTeXで処理する. するとLaTeXは以下のような文句をつけてくるだろう: diff --git a/src/doc/ja/tutorial/tour_advanced.rst b/src/doc/ja/tutorial/tour_advanced.rst index ffa2b4a29e5..a9b295dd4af 100644 --- a/src/doc/ja/tutorial/tour_advanced.rst +++ b/src/doc/ja/tutorial/tour_advanced.rst @@ -308,8 +308,8 @@ Cremonaのデータベースへ直接にアクセスすることも可能だ. :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -317,10 +317,8 @@ Cremonaのデータベースへ直接にアクセスすることも可能だ. sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] 次に,mod 20,ただし値が :math:`\QQ(i)` 上に収まるディリクレ指標の群を作成する: @@ -330,7 +328,7 @@ Cremonaのデータベースへ直接にアクセスすることも可能だ. sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 ついで, ``G`` の不変量をいくつか計算してみよう: @@ -361,8 +359,7 @@ Cremonaのデータベースへ直接にアクセスすることも可能だ. sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/doc/pt/tutorial/bibliography.rst b/src/doc/pt/tutorial/bibliography.rst index 9bced0635bf..6a6f5d2c020 100644 --- a/src/doc/pt/tutorial/bibliography.rst +++ b/src/doc/pt/tutorial/bibliography.rst @@ -41,7 +41,7 @@ Bibliografia .. [SA] Sage web site http://www.sagemath.org/ -.. [Si] G.-M. Greuel, G. Pfister, and H. Schönemann. Singular +.. [Si] \G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.0. A Computer Algebra System for Polynomial Computations. Center for Computer Algebra, University of Kaiserslautern (2005). http://www.singular.uni-kl.de/ diff --git a/src/doc/pt/tutorial/tour_advanced.rst b/src/doc/pt/tutorial/tour_advanced.rst index ba5fa8e55d0..6b98ccb1f91 100644 --- a/src/doc/pt/tutorial/tour_advanced.rst +++ b/src/doc/pt/tutorial/tour_advanced.rst @@ -325,8 +325,8 @@ módulo. :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -334,10 +334,8 @@ módulo. sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] A seguir, construímos o grupo de caracteres de Dirichlet mod 20, mas @@ -348,7 +346,7 @@ com valores em :math:`\QQ(i)`: sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 Agora calculamos diversos invariantes de ``G``: @@ -380,8 +378,7 @@ unidade no terceiro argumento do comando ``DirichletGroup`` abaixo. sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/doc/ru/tutorial/bibliography.rst b/src/doc/ru/tutorial/bibliography.rst index f3af67bc445..e4b6b22972f 100644 --- a/src/doc/ru/tutorial/bibliography.rst +++ b/src/doc/ru/tutorial/bibliography.rst @@ -42,7 +42,7 @@ .. [SA] Sage web site http://www.sagemath.org/. -.. [Si] G.-M. Greuel, G. Pfister, and H. Schönemann. Singular +.. [Si] \G.-M. Greuel, G. Pfister, and H. Schönemann. Singular 3.0. A Computer Algebra System for Polynomial Computations. Center for Computer Algebra, University of Kaiserslautern (2005). http://www.singular.uni-kl.de. diff --git a/src/doc/ru/tutorial/tour_advanced.rst b/src/doc/ru/tutorial/tour_advanced.rst index b7a64a75b9a..04e9d10871a 100644 --- a/src/doc/ru/tutorial/tour_advanced.rst +++ b/src/doc/ru/tutorial/tour_advanced.rst @@ -288,8 +288,8 @@ Sage может вычислить тороидальный идеал непл :: sage: chi.galois_orbit() - [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6, - Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1] + [Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> -zeta6 + 1, + Dirichlet character modulo 21 of conductor 7 mapping 8 |--> 1, 10 |--> zeta6] sage: go = G.galois_orbits() sage: [len(orbit) for orbit in go] @@ -297,10 +297,8 @@ Sage может вычислить тороидальный идеал непл sage: G.decomposition() [ - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order - 6 and degree 2, - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order - 6 and degree 2 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 6 and degree 2, + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 ] Далее надо построить группу символов Дирихле по модулю 20, но со значениями @@ -311,7 +309,7 @@ Sage может вычислить тороидальный идеал непл sage: K. = NumberField(x^2+1) sage: G = DirichletGroup(20,K) sage: G - Group of Dirichlet characters of modulus 20 over Number Field in i with defining polynomial x^2 + 1 + Group of Dirichlet characters modulo 20 with values in Number Field in i with defining polynomial x^2 + 1 Теперь посчитаем несколько инвариант ``G``: @@ -343,8 +341,7 @@ Sage может вычислить тороидальный идеал непл sage: K Number Field in a with defining polynomial x^4 + 1 sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with - defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: chi = G.0; chi Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2 sage: [(chi^i)(2) for i in range(4)] diff --git a/src/ext/gap/console.g b/src/ext/gap/console.g index 57043ff310e..db8b925d3f5 100644 --- a/src/ext/gap/console.g +++ b/src/ext/gap/console.g @@ -1,6 +1,6 @@ -# If we are loaded with a workspace then $SAGE will be defined and in +# If we are loaded with a workspace then \$SAGE will be defined and in # that case we need to call StartInteract so that the pager will be # set correctly. See trac #5043. -if IsBound($SAGE) then - $SAGE.StartInteract(); +if IsBound(\$SAGE) then + \$SAGE.StartInteract(); fi; diff --git a/src/ext/gap/sage.g b/src/ext/gap/sage.g index 8535b7da619..2216fdae0dc 100644 --- a/src/ext/gap/sage.g +++ b/src/ext/gap/sage.g @@ -2,15 +2,15 @@ # # SAGE support utilities to read into the GAP session. # -$SAGE := rec(); +\$SAGE := rec(); -$SAGE.OldPager := Pager; +\$SAGE.OldPager := Pager; -$SAGE.NewPager := +\$SAGE.NewPager := function( data ) local str, lines, line, fn, start; - str := OutputTextFile($SAGE.tempfile,false); + str := OutputTextFile(\$SAGE.tempfile,false); start := 1; if IsRecord(data) then lines := data.lines; @@ -30,38 +30,38 @@ $SAGE.NewPager := Print("Page from ",start,"\n"); end; -$SAGE.StartInteract := function() +\$SAGE.StartInteract := function() MakeReadWriteGlobal("Pager"); - Pager := $SAGE.OldPager; - HELP_VIEWER_INFO.screen.show := $SAGE.OldPager; + Pager := \$SAGE.OldPager; + HELP_VIEWER_INFO.screen.show := \$SAGE.OldPager; MakeReadOnlyGlobal("Pager"); end; -$SAGE.StopInteract := function() +\$SAGE.StopInteract := function() MakeReadWriteGlobal("Pager"); - Pager := $SAGE.NewPager; - HELP_VIEWER_INFO.screen.show := $SAGE.NewPager; + Pager := \$SAGE.NewPager; + HELP_VIEWER_INFO.screen.show := \$SAGE.NewPager; MakeReadOnlyGlobal("Pager"); end; -$SAGE.StopInteract(); +\$SAGE.StopInteract(); -#$SAGE.ErrorHandler := function(m,a,m2,mode) +#\$SAGE.ErrorHandler := function(m,a,m2,mode) # PrintTo("*errout*", m); # if a <> fail then # PrintTo("*errout*",a); # fi; -# SetErrorHandler($SAGE.ErrorHandler); +# SetErrorHandler(\$SAGE.ErrorHandler); # return true; #end; -#SetErrorHandler($SAGE.ErrorHandler); +#SetErrorHandler(\$SAGE.ErrorHandler); SetAllInfoLevels(0); -$SAGE.OperationsAdmittingFirstArgument := function(obj) +\$SAGE.OperationsAdmittingFirstArgument := function(obj) local hits, myflags, i, flagss, flags; hits := []; myflags := FlagsType(TypeObj(obj)); @@ -78,7 +78,7 @@ $SAGE.OperationsAdmittingFirstArgument := function(obj) end; -$SAGE.CleanOperationName := function(name) +\$SAGE.CleanOperationName := function(name) local lt, ls; lt := Length("Tester("); if Length(name) > lt and name{[1..lt]} = "Tester(" then @@ -91,7 +91,7 @@ $SAGE.CleanOperationName := function(name) return name; end; -$SAGE.HasAtLeastOneMethodAsFirstArgument := function(op,obj) +\$SAGE.HasAtLeastOneMethodAsFirstArgument := function(op,obj) local t, f, n, meths, i; t := TypeObj(obj); f := FlagsType(t); @@ -107,11 +107,11 @@ $SAGE.HasAtLeastOneMethodAsFirstArgument := function(op,obj) end; -$SAGE.PlausibleTabCompletionsForSage := function(o) +\$SAGE.PlausibleTabCompletionsForSage := function(o) local ops, opnames; - ops := Filtered($SAGE.OperationsAdmittingFirstArgument(o), op -> - $SAGE.HasAtLeastOneMethodAsFirstArgument(op,o)); - opnames := List(ops, op -> $SAGE.CleanOperationName(NameFunction(op))); + ops := Filtered(\$SAGE.OperationsAdmittingFirstArgument(o), op -> + \$SAGE.HasAtLeastOneMethodAsFirstArgument(op,o)); + opnames := List(ops, op -> \$SAGE.CleanOperationName(NameFunction(op))); return Concatenation(opnames, GLOBAL_FUNCTION_NAMES); end; diff --git a/src/ext/valgrind/sage-additional.supp b/src/ext/valgrind/sage-additional.supp index 9c0108e9ed0..2858bc98b41 100644 --- a/src/ext/valgrind/sage-additional.supp +++ b/src/ext/valgrind/sage-additional.supp @@ -370,7 +370,7 @@ mpfr_init2 Memcheck:Leak fun:malloc - fun:sage_malloc + fun:sig_malloc fun:sage_mpir_malloc fun:mpfr_init2 } @@ -379,7 +379,7 @@ mpir_realloc Memcheck:Leak fun:realloc - fun:sage_realloc + fun:sig_realloc fun:sage_mpir_realloc fun:__gmpz_realloc fun:__gmpz_add_ui diff --git a/src/mac-app/AppController.h b/src/mac-app/AppController.h index f6b2c1a2c27..6e1bd5599e5 100644 --- a/src/mac-app/AppController.h +++ b/src/mac-app/AppController.h @@ -25,6 +25,7 @@ NSString *sageBinary; NSString *logPath; + NSString *jupyterURL; NSMutableArray *URLQueue; NSUserDefaults *defaults; @@ -32,6 +33,7 @@ NSTask *theTask; NSTask *launchTask; NSPipe *taskPipe; + NSTask *jupyterTask; int port; BOOL myIsInDock, haveStatusItem, useSystemBrowser, neverOpenedFileBrowser; @@ -39,6 +41,9 @@ } // Server control +-(IBAction)startJupyter:(id)sender; +-(IBAction)stopJupyter:(id)sender; +-(void)receivedData:(NSNotification *)notif; -(IBAction)startServer:(id)sender; -(IBAction)stopServer:(id)sender; -(BOOL)serverIsRunning:(BOOL)wait; @@ -62,6 +67,8 @@ -(IBAction)showPreferences:(id)sender; -(void)ensureReadWrite; +-(void)offerNotebookUpgrade; +-(IBAction)upgradeNotebook:(id)sender; -(void)setupPaths; // Quit diff --git a/src/mac-app/AppController.m b/src/mac-app/AppController.m index 1335a160972..204645825e1 100644 --- a/src/mac-app/AppController.m +++ b/src/mac-app/AppController.m @@ -34,6 +34,7 @@ - (void) awakeFromNib{ // Find sageBinary etc. [self setupPaths]; [self ensureReadWrite]; + [self offerNotebookUpgrade]; // Initialize the StatusItem if desired. // If we are on Tiger, then showing in the dock doesn't work @@ -59,6 +60,7 @@ - (void) awakeFromNib{ } // indicate that we haven't started the server yet + jupyterURL = nil; port = 0; neverOpenedFileBrowser = YES; URLQueue = [[NSMutableArray arrayWithCapacity:3] retain]; @@ -87,15 +89,115 @@ - (void) dealloc { [sageBinary release]; [logPath release]; [theTask release]; + [launchTask release]; + [jupyterTask release]; [taskPipe release]; [URLQueue release]; + [jupyterURL release]; [super dealloc]; } +-(IBAction)startJupyter:(id)sender{ + + if ( jupyterTask != nil ) { + if ( jupyterURL != nil ) { + NSLog(@"Going to browse to %@",jupyterURL); + [self browseRemoteURL:jupyterURL]; + } + return; + } + + NSLog(@"Starting Jupyter server"); + if (haveStatusItem) [statusItem setImage:statusImageGreen]; + + // Add SAGE_BROWSER to environment to point back to this application + if ( !useSystemBrowser ) { + NSString *browserPath = [[NSBundle mainBundle] pathForResource:@"open-location" ofType:@"sh"]; + setenv("SAGE_BROWSER", [browserPath UTF8String], 1); // this overwrites, should it? + } + + // Get any default options they might have for this session + [defaults synchronize]; + NSString *jupyterPath = [defaults objectForKey:@"defaultJupyterPath"]; + NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] + objectForKey:@"jupyter"]; + + // Escape the arguments + NSMutableString *escSageBin = [NSMutableString stringWithString:sageBinary]; + [escSageBin replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escSageBin length])]; + + NSMutableString * escLogPath = [NSMutableString stringWithString:logPath]; + [escLogPath replaceOccurrencesOfString:@"'" + withString:@"'\\''" + options:0 + range:NSMakeRange(0, [escLogPath length])]; + + // Compile the command. + // We have to run it through a shell so that the default arguments are parsed properly + NSString *command = [NSString stringWithFormat: + @"'%@' --notebook=jupyter %@ 2>&1 | tee -a '%@' |" + " grep --line-buffered -i 'ipython notebook is running at' |" + " grep --line-buffered -o http://.*", + escSageBin, + // default args are ready to be + (defArgs == nil) ? @"" : defArgs, + logPath]; + NSLog(@"Command: %@",command); + + // Create a task that starts the server + jupyterTask = [[[NSTask alloc] init] retain]; + [jupyterTask setLaunchPath:@"/bin/bash"]; + [jupyterTask setArguments:[NSArray arrayWithObjects: @"-c", command, nil]]; + [jupyterTask setCurrentDirectoryPath:jupyterPath]; + + // set up std out to + NSPipe *outputPipe = [NSPipe pipe]; + [jupyterTask setStandardOutput:outputPipe]; + + NSFileHandle *fh = [outputPipe fileHandleForReading]; + [fh waitForDataInBackgroundAndNotify]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedData:) name:NSFileHandleDataAvailableNotification object:fh]; + + [jupyterTask launch]; + + if (haveStatusItem) [statusItem setImage:statusImageBlue]; +} + + +- (void)receivedData:(NSNotification *)notif { + NSFileHandle *fh = [notif object]; + NSData *data = [fh availableData]; + if (data.length > 0) { + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + jupyterURL = [[str stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]] + retain]; + [str release]; + } +} + + +-(IBAction)stopJupyter:(id)sender{ + + if (jupyterTask == nil ) { + return; + } + if (haveStatusItem) [statusItem setImage:statusImageRed]; + + [jupyterTask terminate]; + [jupyterTask terminate]; // We have to send it twice to actually stop -- actually do I? + [jupyterTask release]; + jupyterTask = nil; + if (haveStatusItem) [statusItem setImage:statusImageGrey]; +} + -(IBAction)startServer:(id)sender{ // TODO: Check to see if it's running before attempting to start - NSLog(@"Starting server"); + NSLog(@"Starting SageNB server"); if (haveStatusItem) [statusItem setImage:statusImageGreen]; NSString *scriptPath = [[NSBundle mainBundle] pathForResource:@"start-sage" ofType:@"sh"]; @@ -106,7 +208,7 @@ -(IBAction)startServer:(id)sender{ } // Create a task to start the server - + // Get any default options they might have for this session [defaults synchronize]; NSString *defArgs = [[defaults dictionaryForKey:@"DefaultArguments"] @@ -114,6 +216,7 @@ -(IBAction)startServer:(id)sender{ launchTask = [[NSTask launchedTaskWithLaunchPath:scriptPath arguments:[NSArray arrayWithObjects:sageBinary, logPath, + @"sagenb", defArgs, // May be nil, but that's okay nil]] retain]; @@ -155,6 +258,7 @@ -(void)serverStartedWithPort:(int)p{ } - (void)taskTerminated:(NSNotification *)aNotification { + NSLog(@"Task stopped\n"); NSTask *theObject = [aNotification object]; if (theObject == theTask) { @@ -181,13 +285,13 @@ - (void)taskTerminated:(NSNotification *)aNotification { [taskPipe release]; taskPipe = nil; } else if (theObject == launchTask ) { - + const int status = [theObject terminationStatus]; - if (status != 0) { + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; // We failed, so tell the user - if (haveStatusItem) { - [statusItem setImage:statusImageGrey]; - } port = 0; NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Server failed to start" defaultButton:@"View Log" @@ -207,12 +311,40 @@ - (void)taskTerminated:(NSNotification *)aNotification { // Reset for next time. [launchTask release]; launchTask = nil; - + } else if (theObject == jupyterTask ) { + NSLog(@"jupyterTask stopped\n"); + const int status = [theObject terminationStatus]; + if (status == 0) { + if (haveStatusItem) [statusItem setImage:statusImageGrey]; + } else { + if (haveStatusItem) [statusItem setImage:statusImageRed]; + // We failed, so tell the user + NSAlert *alert = [NSAlert alertWithMessageText:@"Jupyter Server failed to start" + defaultButton:@"View Log" + alternateButton:@"Cancel" + otherButton:nil + informativeTextWithFormat:@"For some reason the Jupyter server failed to start. " + "Please check the log for clues, and have that information handy when asking for help."]; + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { + // View Log + [self viewSageLog:self]; + } else { + // Cancel + } + } + // Reset for next time. + [jupyterTask release]; + jupyterTask = nil; + } else { // NSLog(@"Got called for a different task."); } } +// TODO: Test upgrading... + -(IBAction)stopServer:(id)sender{ if (haveStatusItem) [statusItem setImage:statusImageRed]; @@ -243,6 +375,7 @@ -(IBAction)stopServer:(id)sender{ -(IBAction)stopServerAndQuit:(id)sender{ [self stopServer:self]; + [self stopJupyter:self]; // Tell the application to quit [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.0]; @@ -372,22 +505,70 @@ -(void)ensureReadWrite { } } +-(void)offerNotebookUpgrade { + NSFileManager *filemgr = [NSFileManager defaultManager]; + NSLog(@"Checking if sagenb exists %d.", [defaults boolForKey:@"askToUpgradeNB"]); + if ( ! [filemgr fileExistsAtPath:@"~/.sage/sage_notebook.sagenb/users.pickle"] + && [defaults boolForKey:@"askToUpgradeNB"]) { + + NSAlert *alert = [NSAlert alertWithMessageText:@"Sage Notebook Upgrade" + defaultButton:@"Upgrade" + alternateButton:@"Ask me Later" + otherButton:@"Don't ask again" + informativeTextWithFormat:@"You appear to have data in the old notebook format.\n" + "Sage has changed to use Jupyter notebooks by default.\n" + "Unfortunately, they are not completely compatible.\n" + "We can attempt to upgrade, .\n" + ]; + + [alert setAlertStyle:NSWarningAlertStyle]; + NSInteger resp = [alert runModal]; + if (resp == NSAlertDefaultReturn) { // Upgrade + [self upgradeNotebook:self]; + } else if ( resp == NSAlertAlternateReturn) { // Ask Me Later + // nothing + NSLog(@"Ask to upgrade later."); + } else { // Don't ask again + NSLog(@"Don't ask to upgrade again."); + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"sagenb" forKey:@"preferredNotebookType"]; + NSLog(@"synchronizing defaults: %@",defaults); + } + } +} + +-(IBAction)upgradeNotebook:(id)sender{ + NSLog(@"Upgrade Notebook."); + [self sageTerminalRun:@"notebook=export" withArguments:nil]; + [defaults setBool:NO forKey:@"askToUpgradeNB"]; + [defaults setObject:@"jupyter" forKey:@"preferredNotebookType"]; +} + -(IBAction)revealInFinder:(id)sender{ if ( [[sender title] isEqualToString:@"Reveal in Shell"] ) { [self terminalRun:[NSString stringWithFormat:@"cd '%@' && $SHELL", [sageBinary stringByDeletingLastPathComponent]]]; } else { [[NSWorkspace sharedWorkspace] selectFile:[sageBinary stringByDeletingLastPathComponent] - inFileViewerRootedAtPath:nil]; + inFileViewerRootedAtPath:@""]; } } -(IBAction)openNotebook:(id)sender{ - [self browseLocalSageURL:@""]; + if ( jupyterURL != nil ) { + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@""]; + } } -(IBAction)newWorksheet:(id)sender{ - [self browseLocalSageURL:@"new_worksheet"]; + if ( jupyterURL != nil ) { + // AFAICT you can't create a new worksheet via curl + [self browseRemoteURL:jupyterURL]; + } else { + [self browseLocalSageURL:@"new_worksheet"]; + } } -(IBAction)showPreferences:(id)sender{ diff --git a/src/mac-app/Defaults.plist b/src/mac-app/Defaults.plist index 15381d6e0f7..033784c7e2d 100644 --- a/src/mac-app/Defaults.plist +++ b/src/mac-app/Defaults.plist @@ -50,7 +50,7 @@ end tell set the_script to "%@; exit" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -70,7 +70,7 @@ end tell set the_script to "%@" tell application "System Events" - set is_running to ("iTerm" is in name of every application process) + set is_running to ("iTerm2" is in name of every application process) end tell tell application "iTerm" @@ -90,6 +90,38 @@ end tell do shell script "xterm -e '%@' &" xterm - don't exit do shell script "xterm -hold -e '%@' &" + iTerm v3 + set the_script to "%@; exit" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell + iTerm v3 - don't exit + set the_script to "%@" + +tell application "System Events" + set is_running to ("iTerm2" is in name of every application process) +end tell + +tell application "iTerm" + if is_running then + set w to (create window with default profile) + end if + tell the (current session of window 1) + write text the_script + end tell + activate +end tell alsoShowMenuExtra @@ -124,5 +156,11 @@ end tell autoStartServer + askToUpgradeNB + + preferSageNB + + defaultJupyterPath + ~/Documents/Sage/ diff --git a/src/mac-app/English.lproj/MainMenu.nib/designable.nib b/src/mac-app/English.lproj/MainMenu.nib/designable.nib index 2e2957f963e..ee3be91782f 100644 --- a/src/mac-app/English.lproj/MainMenu.nib/designable.nib +++ b/src/mac-app/English.lproj/MainMenu.nib/designable.nib @@ -1,8 +1,8 @@ - + - + @@ -181,8 +181,7 @@ - - + @@ -336,18 +335,55 @@ - + + + + NSNegateBoolean + + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -567,7 +603,8 @@ - + + @@ -683,7 +720,7 @@ - + + + + + + + Modern Sage has 3 notebook interfaces. All three offer slightly different user experiences and there are some incompatibilities between them (e.g. interacts). + +1. SageNB. These are the old notebooks of which you may have many. Development of this interface has stalled. + +2. Jupyter. This is the successor of IPython and is intended to be the successor of SageNB on the desktop. + +3. SageMathCloud. This is (currently) a cloud-only offering, and hence not available on the desktop. + + + + + + + + + + + + + + + + + ns placeholder + + + + + + + + + + + + + + + + + + @@ -945,6 +1045,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1081,16 +1182,52 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. - + - + + + + NSNegateBoolean + + - + - + + + + NSNegateBoolean + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1293,6 +1430,7 @@ Tip: Dragging a file to the Sage Executable text box will paste the path. + @@ -1369,5 +1507,11 @@ The command will be run as + + + + + + diff --git a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib index f7b68125f1b..6bc5b3c19b2 100644 Binary files a/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib and b/src/mac-app/English.lproj/MainMenu.nib/keyedobjects.nib differ diff --git a/src/mac-app/MyDocument.m b/src/mac-app/MyDocument.m index ca38b29ab3f..9f2362e5767 100644 --- a/src/mac-app/MyDocument.m +++ b/src/mac-app/MyDocument.m @@ -152,7 +152,7 @@ - (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)fr - (void)webView:(WebView *)wv runOpenPanelForFileButtonWithResultListener:(id )listener { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; [openPanel beginSheetForDirectory:nil - file:nil + file:@"" modalForWindow:[[self webView] window] modalDelegate:self didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) diff --git a/src/mac-app/start-sage.sh b/src/mac-app/start-sage.sh index ce1e6e0fba7..6c60965755b 100755 --- a/src/mac-app/start-sage.sh +++ b/src/mac-app/start-sage.sh @@ -1,19 +1,19 @@ #!/bin/bash # start-sage.sh -# Fluidium # # Created by Ivan Andrus on 16/1/10. # Copyright 2010 Ivan Andrus. All rights reserved. # Ensure we have enough arguments -if [ $# -lt 2 ]; then +if [ $# -lt 3 ]; then echo "usage: $0 SAGE_EXECUTABLE LOG [ARGS_FOR_NOTEBOOK]" exit 1; fi SAGE_EXECUTABLE="$1" SAGE_LOG="$2" +NB_TYPE="$3" # Read environment variables cd $(dirname $SAGE_EXECUTABLE) @@ -24,16 +24,23 @@ source local/bin/sage-env # So always run first the respective script handling this # This should also catch Intel vs. PPC or 32Bit vs. 64Bit conflicts echo Checking install location >> "$SAGE_LOG" -# TODO: If relocate-once.py is present run it with some sort of progress +# TODO: If relocate-once.py is present run it with some sort of progress # display, e.g. in a terminal +if [ "$NB_TYPE" = jupyter ]; then + +./sage --notebook=jupyter $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" & + + +else + echo Checking existence of SageNB directory >> "$SAGE_LOG" if [ -e $DOT_SAGE/sage_notebook.sagenb/users.pickle ]; then echo Starting Notebook >> "$SAGE_LOG" - # $3 is not quoted because it comes as one argument from the app, + # $4 is not quoted because it comes as one argument from the app, # so we need the shell to parse it here. - ./sage --notebook $3 >> "$SAGE_LOG" 2>> "$SAGE_LOG" + ./sage --notebook=sagenb $4 >> "$SAGE_LOG" 2>> "$SAGE_LOG" else false fi @@ -52,9 +59,12 @@ if [ $? != 0 ]; then sage-native-execute osascript \ -e 'tell app "Terminal"' \ -e ' activate' \ - -e " do script \"'$SAGE_EXECUTABLE' --notebook\"" \ + -e " do script \"'$SAGE_EXECUTABLE' --notebook=sagenb\"" \ -e 'end' - # We don't include $3 here since this should only happen the first time + # We don't include $4 here since this should only happen the first time # they run it, and this way we don't have to worry about quoting it. fi + +fi + exit $? diff --git a/src/module_list.py b/src/module_list.py index b1f059d6e7b..a49ed36d4b7 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -19,7 +19,7 @@ # TODO: Remove Cygwin hack by installing a suitable cblas.pc if os.path.exists('/usr/lib/libblas.dll.a'): - cblas_libs = 'gslcblas' + cblas_libs = ['gslcblas'] # LAPACK can be one of multiple implementations lapack_pc = pkgconfig.parse('lapack') @@ -57,8 +57,6 @@ ### Commonly used definitions and aliases ######################################################### -singular_incs = [SAGE_INC + '/singular', SAGE_INC + '/factory'] - aliases = dict( GSL_LIBRARIES=gsl_libs, GSL_LIBDIR=gsl_library_dirs, @@ -75,6 +73,10 @@ m4ri_include_dirs = list(m4ri_pc['include_dirs']) m4ri_extra_compile_args = pkgconfig.cflags('m4ri').split() +try: + m4ri_extra_compile_args.remove("-pedantic") +except ValueError: + pass ######################################################### ### Singular @@ -165,20 +167,17 @@ def uname_specific(name, value, alternative): Extension('sage.algebras.letterplace.free_algebra_letterplace', sources = ['sage/algebras/letterplace/free_algebra_letterplace.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.algebras.letterplace.free_algebra_element_letterplace', sources = ['sage/algebras/letterplace/free_algebra_element_letterplace.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.algebras.letterplace.letterplace_ideal', sources = ['sage/algebras/letterplace/letterplace_ideal.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.algebras.quatalg.quaternion_algebra_cython', sources = ['sage/algebras/quatalg/quaternion_algebra_cython.pyx'], @@ -446,6 +445,8 @@ def uname_specific(name, value, alternative): Extension('sage.groups.semimonomial_transformations.semimonomial_transformation', sources = ['sage/groups/semimonomial_transformations/semimonomial_transformation.pyx']), + Extension('sage.groups.matrix_gps.group_element', + sources = ['sage/groups/matrix_gps/group_element.pyx']), ################################### ## @@ -581,6 +582,9 @@ def uname_specific(name, value, alternative): Extension('sage.libs.fplll.fplll', sources = ['sage/libs/fplll/fplll.pyx']), + Extension("sage.libs.glpk.error", + ["sage/libs/glpk/error.pyx"]), + Extension('sage.libs.gmp.pylong', sources = ['sage/libs/gmp/pylong.pyx']), @@ -643,39 +647,33 @@ def uname_specific(name, value, alternative): sources = ['sage/libs/singular/singular.pyx'], libraries = ['givaro'] + singular_libs, language="c++", - include_dirs = singular_incs, extra_compile_args = givaro_extra_compile_args), Extension('sage.libs.singular.polynomial', sources = ['sage/libs/singular/polynomial.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.libs.singular.ring', sources = ['sage/libs/singular/ring.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.libs.singular.groebner_strategy', sources = ['sage/libs/singular/groebner_strategy.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.libs.singular.function', sources = ['sage/libs/singular/function.pyx'], libraries = singular_libs, language="c++", - include_dirs = singular_incs, extra_compile_args = givaro_extra_compile_args), Extension('sage.libs.singular.option', sources = ['sage/libs/singular/option.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.libs.symmetrica.symmetrica', sources = ["sage/libs/symmetrica/symmetrica.pyx"], @@ -870,6 +868,12 @@ def uname_specific(name, value, alternative): Extension('sage.matrix.matrix2', sources = ['sage/matrix/matrix2.pyx']), + Extension("sage.matrix.matrix_complex_ball_dense", + ["sage/matrix/matrix_complex_ball_dense.pyx"], + libraries=['arb', 'mpfi', 'mpfr'], + include_dirs=[SAGE_INC + '/flint'], + language = "c++"), + Extension('sage.matrix.matrix_complex_double_dense', sources = ['sage/matrix/matrix_complex_double_dense.pyx']), @@ -930,7 +934,7 @@ def uname_specific(name, value, alternative): libraries = ['ntl', 'linbox', 'givaro', 'mpfr', 'gmpxx', 'gmp'] + cblas_libs, library_dirs = cblas_library_dirs, include_dirs = cblas_include_dirs, - extra_compile_args = ['-std=c99', "-D_XPG6", "-DDISABLE_COMMENTATOR"] + extra_compile_args = ["-D_XPG6", "-DDISABLE_COMMENTATOR"] + m4ri_extra_compile_args + givaro_extra_compile_args), Extension('sage.matrix.matrix_modn_sparse', @@ -939,8 +943,7 @@ def uname_specific(name, value, alternative): Extension('sage.matrix.matrix_mpolynomial_dense', sources = ['sage/matrix/matrix_mpolynomial_dense.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.matrix.matrix_rational_dense', sources = ['sage/matrix/matrix_rational_dense.pyx'], @@ -1125,6 +1128,9 @@ def uname_specific(name, value, alternative): Extension("sage.numerical.backends.glpk_backend", ["sage/numerical/backends/glpk_backend.pyx"]), + Extension("sage.numerical.backends.glpk_exact_backend", + ["sage/numerical/backends/glpk_exact_backend.pyx"]), + Extension("sage.numerical.backends.ppl_backend", ["sage/numerical/backends/ppl_backend.pyx"], libraries=["stdc++"]), @@ -1138,6 +1144,9 @@ def uname_specific(name, value, alternative): Extension("sage.numerical.backends.glpk_graph_backend", ["sage/numerical/backends/glpk_graph_backend.pyx"]), + Extension("sage.numerical.backends.interactivelp_backend", + ["sage/numerical/backends/interactivelp_backend.pyx"]), + OptionalExtension("sage.numerical.backends.gurobi_backend", ["sage/numerical/backends/gurobi_backend.pyx"], libraries = ["stdc++", "gurobi"], @@ -1255,7 +1264,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.complex_arb", ["sage/rings/complex_arb.pyx"], - libraries=['mpfi', 'mpfr', 'gmp']), + libraries=['mpfi', 'mpfr', 'gmp'], + language = 'c++'), Extension('sage.rings.complex_double', sources = ['sage/rings/complex_double.pyx'], @@ -1326,7 +1336,8 @@ def uname_specific(name, value, alternative): Extension("sage.rings.real_arb", ["sage/rings/real_arb.pyx"], - libraries = ['mpfi', 'mpfr']), + libraries = ['mpfi', 'mpfr'], + language = 'c++'), Extension('sage.rings.real_lazy', sources = ['sage/rings/real_lazy.pyx']), @@ -1407,7 +1418,7 @@ def uname_specific(name, value, alternative): Extension('sage.rings.number_field.number_field_element', sources = ['sage/rings/number_field/number_field_element.pyx'], - libraries=['ntl'], + libraries=['ntl', 'mpfr', 'mpfi'], language = 'c++'), Extension('sage.rings.number_field.number_field_element_quadratic', @@ -1492,6 +1503,23 @@ def uname_specific(name, value, alternative): libraries = ["ntl", "gmp", "gmpxx", "m"], language='c++'), + Extension('sage.rings.padics.pow_computer_flint', + sources = ['sage/rings/padics/pow_computer_flint.pyx'], + libraries = ["flint", "gmpxx", "gmp", "ntl"], + language='c++'), + + Extension('sage.rings.padics.qadic_flint_CR', + sources = ['sage/rings/padics/qadic_flint_CR.pyx'], + libraries = ["flint"]), + + Extension('sage.rings.padics.qadic_flint_CA', + sources = ['sage/rings/padics/qadic_flint_CA.pyx'], + libraries = ["flint"]), + + Extension('sage.rings.padics.qadic_flint_FM', + sources = ['sage/rings/padics/qadic_flint_FM.pyx'], + libraries = ["flint"]), + ################################ ## ## sage.rings.polynomial @@ -1515,21 +1543,18 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.multi_polynomial_ideal_libsingular', sources = ['sage/rings/polynomial/multi_polynomial_ideal_libsingular.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.rings.polynomial.plural', sources = ['sage/rings/polynomial/plural.pyx'], libraries = ['m', 'readline', 'singular', 'givaro', 'gmpxx', 'gmp'], language="c++", - include_dirs = singular_incs, extra_compile_args = givaro_extra_compile_args), Extension('sage.rings.polynomial.multi_polynomial_libsingular', sources = ['sage/rings/polynomial/multi_polynomial_libsingular.pyx'], libraries = singular_libs, - language="c++", - include_dirs = singular_incs), + language="c++"), Extension('sage.rings.polynomial.multi_polynomial_ring_generic', sources = ['sage/rings/polynomial/multi_polynomial_ring_generic.pyx']), @@ -1549,7 +1574,7 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.polynomial_gf2x', sources = ['sage/rings/polynomial/polynomial_gf2x.pyx'], libraries = ['gmp', 'ntl'], - extra_compile_args = ['-std=c99'] + m4ri_extra_compile_args, + extra_compile_args = m4ri_extra_compile_args, language = 'c++'), Extension('sage.rings.polynomial.polynomial_zz_pex', @@ -1592,7 +1617,7 @@ def uname_specific(name, value, alternative): include_dirs = m4ri_include_dirs + png_include_dirs, depends = [SAGE_INC + "/polybori/" + hd + ".h" for hd in ["polybori", "config"] ] + [SAGE_INC + '/m4ri/m4ri.h'], - extra_compile_args = ['-std=c99'] + m4ri_extra_compile_args, + extra_compile_args = m4ri_extra_compile_args, language = 'c++'), Extension('sage.rings.polynomial.polynomial_real_mpfr_dense', diff --git a/src/sage/algebras/affine_nil_temperley_lieb.py b/src/sage/algebras/affine_nil_temperley_lieb.py index 4bc0cde250b..92d3a90ab7c 100644 --- a/src/sage/algebras/affine_nil_temperley_lieb.py +++ b/src/sage/algebras/affine_nil_temperley_lieb.py @@ -21,7 +21,7 @@ class AffineNilTemperleyLiebTypeA(CombinatorialFreeModule): REFERENCES: - .. [P2005] A. Postnikov, Affine approach to quantum Schubert calculus, Duke Math. J. 128 (2005) 473-509 + .. [P2005] \A. Postnikov, Affine approach to quantum Schubert calculus, Duke Math. J. 128 (2005) 473-509 INPUT: diff --git a/src/sage/algebras/associated_graded.py b/src/sage/algebras/associated_graded.py index 750cbe33df1..0f7c76c4663 100644 --- a/src/sage/algebras/associated_graded.py +++ b/src/sage/algebras/associated_graded.py @@ -139,10 +139,10 @@ class AssociatedGradedAlgebra(CombinatorialFreeModule): ``grA`` are isomorphic:: sage: grA(A.an_element()) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']^2*U['y']^2*U['z']^3) + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: elt = A.an_element() + A.algebra_generators()['x'] + 2 sage: grelt = grA(elt); grelt - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) sage: A(grelt) == elt True @@ -241,8 +241,10 @@ def _element_constructor_(self, x): sage: grA = A.graded_algebra() sage: grA(A.an_element()) bar(U['x']^2*U['y']^2*U['z']^3) + + 2*bar(U['x']) + 3*bar(U['y']) + bar(1) sage: grA(A.an_element() + A.algebra_generators()['x'] + 2) - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + + 3*bar(U['x']) + 3*bar(U['y']) + 3*bar(1) """ if isinstance(x, CombinatorialFreeModule.Element): if x.parent() is self._A: diff --git a/src/sage/algebras/catalog.py b/src/sage/algebras/catalog.py index 570f3019f93..7d3d06d4ce2 100644 --- a/src/sage/algebras/catalog.py +++ b/src/sage/algebras/catalog.py @@ -9,6 +9,7 @@ Let ```` indicate pressing the tab key. So begin by typing ``algebras.`` to the see the currently implemented named algebras. +- :class:`algebras.Brauer ` - :class:`algebras.Clifford ` - :class:`algebras.DifferentialWeyl ` @@ -32,12 +33,15 @@ ` - :class:`algebras.OrlikSolomon ` +- :class:`algebras.Partition ` +- :class:`algebras.PlanarPartition ` - :func:`algebras.Quaternion ` - :class:`algebras.Schur ` - :class:`algebras.Shuffle ` - :class:`algebras.Steenrod ` +- :class:`algebras.TemperleyLieb ` - :class:`algebras.YokonumaHecke ` """ @@ -63,6 +67,10 @@ lazy_import('sage.algebras.commutative_dga', 'GradedCommutativeAlgebra', 'GradedCommutative') lazy_import('sage.algebras.yokonuma_hecke_algebra', 'YokonumaHeckeAlgebra', 'YokonumaHecke') lazy_import('sage.combinat.posets.incidence_algebras', 'IncidenceAlgebra', 'Incidence') +lazy_import('sage.combinat.diagram_algebras', 'BrauerAlgebra', 'Brauer') +lazy_import('sage.combinat.diagram_algebras', 'PartitionAlgebra', 'Partition') +lazy_import('sage.combinat.diagram_algebras', 'PlanarAlgebra', 'PlanarPartition') +lazy_import('sage.combinat.diagram_algebras', 'TemperleyLiebAlgebra', 'TemperleyLieb') 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.py b/src/sage/algebras/free_algebra.py index d339165978f..02dc1cfdd69 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -986,8 +986,6 @@ def lie_polynomial(self, w): ret = ret * (self(x * y) - self(y * x)) return ret -from sage.misc.cache import Cache -cache = Cache(FreeAlgebra_generic) class PBWBasisOfFreeAlgebra(CombinatorialFreeModule): """ diff --git a/src/sage/algebras/group_algebra_new.py b/src/sage/algebras/group_algebra_new.py deleted file mode 100644 index 0dd419875eb..00000000000 --- a/src/sage/algebras/group_algebra_new.py +++ /dev/null @@ -1,15 +0,0 @@ -""" -This module is deprecated. Use ``group_algebra`` instead. - -TESTS:: - - sage: from sage.algebras.group_algebra_new import GroupAlgebra - sage: GroupAlgebra(GL(3, QQ), ZZ) - doctest:...: DeprecationWarning: - Importing GroupAlgebra from here is deprecated. If you need to use it, please import it directly from sage.algebras.group_algebra - See http://trac.sagemath.org/17779 for details. - Group algebra of group "General Linear Group of degree 3 over Rational Field" over base ring Integer Ring -""" - -from sage.misc.lazy_import import lazy_import -lazy_import('sage.algebras.group_algebra', '*', deprecation=17779) diff --git a/src/sage/algebras/hall_algebra.py b/src/sage/algebras/hall_algebra.py index c890ff67ec0..461619dc705 100644 --- a/src/sage/algebras/hall_algebra.py +++ b/src/sage/algebras/hall_algebra.py @@ -5,13 +5,14 @@ - Travis Scrimshaw (2013-10-17): Initial version """ - #***************************************************************************** # Copyright (C) 2013 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division from sage.misc.misc_c import prod from sage.misc.cachefunc import cached_method @@ -379,7 +380,7 @@ def antipode_on_basis(self, la): """ if all(x == 1 for x in la): r = len(la) - q = (-1)**r * self._q**(-r*(r-1)/2) + q = (-1) ** r * self._q ** (-(r * (r - 1)) // 2) return self._from_dict({p: q for p in Partitions(r)}) I = HallAlgebraMonomials(self.base_ring(), self._q) @@ -598,7 +599,7 @@ def __init__(self, base_ring, q, prefix='I'): # Coercions if hopf_structure: e = SymmetricFunctions(base_ring).e() - f = lambda la: q**sum(-(r*(r-1)/2) for r in la) + f = lambda la: q ** sum(-((r * (r - 1)) // 2) for r in la) M = self.module_morphism(diagonal=f, codomain=e) M.register_as_coercion() (~M).register_as_coercion() @@ -713,7 +714,7 @@ def antipode_on_basis(self, a): H = HallAlgebra(self.base_ring(), self._q) cur = self.one() for r in a: - q = (-1)**r * self._q**(-r*(r-1)/2) + q = (-1) ** r * self._q ** (-(r * (r - 1)) // 2) cur *= self(H._from_dict({p: q for p in Partitions(r)})) return cur diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index 81d71c94a10..553268fe18b 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -281,15 +281,15 @@ class IwahoriHeckeAlgebra(Parent, UniqueRepresentation): REFERENCES: - .. [I64] N. Iwahori, On the structure of a Hecke ring of a + .. [I64] \N. Iwahori, On the structure of a Hecke ring of a Chevalley group over a finite field, J. Fac. Sci. Univ. Tokyo Sect. I, 10 (1964), 215--236 (1964). :mathscinet:`MR0165016` - .. [HKP] T. J. Haines, R. E. Kottwitz, A. Prasad, + .. [HKP] \T. J. Haines, R. E. Kottwitz, A. Prasad, Iwahori-Hecke Algebras, J. Ramanujan Math. Soc., 25 (2010), 113--145. :arxiv:`0309168v3` :mathscinet:`MR2642451` - .. [J87] V. Jones, Hecke algebra representations of braid groups and + .. [J87] \V. Jones, Hecke algebra representations of braid groups and link polynomials. Ann. of Math. (2) 126 (1987), no. 2, 335--388. :doi:`10.2307/1971403` :mathscinet:`MR0908150` diff --git a/src/sage/algebras/jordan_algebra.py b/src/sage/algebras/jordan_algebra.py index e9cc4d0f76d..9dd45525eca 100644 --- a/src/sage/algebras/jordan_algebra.py +++ b/src/sage/algebras/jordan_algebra.py @@ -144,16 +144,16 @@ class JordanAlgebra(Parent, UniqueRepresentation): - :wikipedia:`Jordan_algebra` - .. [Jacobson71] N. Jacobson. *Exceptional Lie Algebras*. + .. [Jacobson71] \N. Jacobson. *Exceptional Lie Algebras*. Marcel Dekker, Inc. New York. 1971. IBSN No. 0-8247-1326-5. .. [Chu2012] Cho-Ho Chu. *Jordan Structures in Geometry and Analysis*. Cambridge University Press, New York. 2012. IBSN 978-1-107-01617-0. - .. [McCrimmon78] K. McCrimmon. *Jordan algebras and their applications*. + .. [McCrimmon78] \K. McCrimmon. *Jordan algebras and their applications*. Bull. Amer. Math. Soc. **84** 1978. - .. [Albert47] A. A. Albert, *A Structure Theory for Jordan Algebras*. + .. [Albert47] \A. A. Albert, *A Structure Theory for Jordan Algebras*. Annals of Mathematics, Second Series, Vol. 48, No. 3 (Jul., 1947), pp. 546--567. """ @@ -287,7 +287,7 @@ def _an_element_(self): sage: F. = FreeAlgebra(QQ) sage: J = JordanAlgebra(F) sage: J.an_element() - x + 2 + 2*x + 3*y """ return self.element_class(self, self._A.an_element()) diff --git a/src/sage/algebras/nil_coxeter_algebra.py b/src/sage/algebras/nil_coxeter_algebra.py index b84bae29dc1..545d8731cf2 100644 --- a/src/sage/algebras/nil_coxeter_algebra.py +++ b/src/sage/algebras/nil_coxeter_algebra.py @@ -147,7 +147,7 @@ def k_schur_noncommutative_variables(self, la): REFERENCES: - .. [Lam2005] T. Lam, Affine Stanley symmetric functions, Amer. J. Math. 128 (2006), no. 6, 1553--1586. + .. [Lam2005] \T. Lam, Affine Stanley symmetric functions, Amer. J. Math. 128 (2006), no. 6, 1553--1586. This function is currently only defined in type `A^{(1)}`. diff --git a/src/sage/algebras/quatalg/quaternion_algebra.py b/src/sage/algebras/quatalg/quaternion_algebra.py index 4729da631eb..84ec903912a 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra.py +++ b/src/sage/algebras/quatalg/quaternion_algebra.py @@ -704,10 +704,10 @@ def maximal_order(self, take_shortcuts = True): REFERENCES: - .. [Piz1980] A. Pizer. An Algorithm for Computing Modular Forms + .. [Piz1980] \A. Pizer. An Algorithm for Computing Modular Forms on `\Gamma_0(N)`, J. Algebra 64 (1980), 340-390. - .. [Voi2012] J. Voight. Identifying the matrix ring: algorithms + .. [Voi2012] \J. Voight. Identifying the matrix ring: algorithms for quaternion algebras and quadratic forms, to appear. """ try: return self.__maximal_order diff --git a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx index 849bb1bf6b3..92752061629 100644 --- a/src/sage/algebras/quatalg/quaternion_algebra_element.pyx +++ b/src/sage/algebras/quatalg/quaternion_algebra_element.pyx @@ -213,7 +213,7 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): sage: int(A(-3)) -3 sage: int(A(-3/2)) - -2 + -1 sage: int(-3 + i) Traceback (most recent call last): ... @@ -234,7 +234,7 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): sage: long(A(-3)) -3L sage: long(A(-3/2)) - -2L + -1L sage: long(-3 + i) Traceback (most recent call last): ... @@ -617,7 +617,7 @@ cdef class QuaternionAlgebraElement_abstract(AlgebraElement): v = [(self*a).coefficient_tuple() for a in self._parent.basis()] else: raise ValueError, "action must be either 'left' or 'right'" - return matrix(self.base_ring(), 4, v, check=False) + return matrix(self.base_ring(), 4, v) def coefficient_tuple(self): """ diff --git a/src/sage/algebras/schur_algebra.py b/src/sage/algebras/schur_algebra.py index f3092e9cf3f..53d0763fb8d 100644 --- a/src/sage/algebras/schur_algebra.py +++ b/src/sage/algebras/schur_algebra.py @@ -19,7 +19,7 @@ REFERENCES: -.. [GreenPoly] J. Green, Polynomial representations of `GL_n`, Springer Verlag. +.. [GreenPoly] \J. Green, Polynomial representations of `GL_n`, Springer Verlag. """ #***************************************************************************** diff --git a/src/sage/algebras/shuffle_algebra.py b/src/sage/algebras/shuffle_algebra.py index 734ab9ec226..6a9eb4c957e 100644 --- a/src/sage/algebras/shuffle_algebra.py +++ b/src/sage/algebras/shuffle_algebra.py @@ -610,7 +610,7 @@ class DualPBWBasis(CombinatorialFreeModule): REFERENCES: - .. [Reuten1993] C. Reutenauer. *Free Lie Algebras*. Number 7 in + .. [Reuten1993] \C. Reutenauer. *Free Lie Algebras*. Number 7 in London Math. Soc. Monogr. (N.S.). Oxford University Press. (1993). EXAMPLES:: diff --git a/src/sage/algebras/yokonuma_hecke_algebra.py b/src/sage/algebras/yokonuma_hecke_algebra.py index 06f9cf42453..7560133e2e0 100644 --- a/src/sage/algebras/yokonuma_hecke_algebra.py +++ b/src/sage/algebras/yokonuma_hecke_algebra.py @@ -119,7 +119,7 @@ class YokonumaHeckeAlgebra(CombinatorialFreeModule): *Cell structures for the Yokonuma-Hecke algebra and the algebra of braids and ties*. (2015) :arxiv:`1506.00715`. - .. [JPdA15] N. Jacon and L. Poulain d'Andecy. + .. [JPdA15] \N. Jacon and L. Poulain d'Andecy. *An isomorphism theorem for Yokonuma-Hecke algebras and applications to link invariants*. (2015) :arxiv:`1501.06389v3`. """ diff --git a/src/sage/all.py b/src/sage/all.py index 9f54bd58d22..669c77be334 100644 --- a/src/sage/all.py +++ b/src/sage/all.py @@ -175,6 +175,8 @@ from sage.game_theory.all import * +from sage.knots.all import * + from sage.manifolds.all import * from cysignals.alarm import alarm, cancel_alarm @@ -266,7 +268,9 @@ def quit_sage(verbose=True): from sage.libs.all import symmetrica symmetrica.end() -from sage.ext.interactive_constructors_c import inject_on, inject_off +# A deprecation(20442) warning will be given when this module is +# imported, in particular when these functions are used. +lazy_import("sage.ext.interactive_constructors_c", ["inject_on", "inject_off"]) sage.structure.sage_object.register_unpickle_override('sage.categories.category', 'Sets', Sets) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'HeckeModules', HeckeModules) @@ -276,7 +280,6 @@ def quit_sage(verbose=True): sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'VectorSpaces', VectorSpaces) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'Schemes_over_base', sage.categories.schemes.Schemes_over_base) sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', 'ModularAbelianVarieties', ModularAbelianVarieties) -#sage.structure.sage_object.register_unpickle_override('sage.categories.category_types', '', ) # Cache the contents of star imports. sage.misc.lazy_import.save_cache_file() diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index b705bc52940..a7d38ed4f5a 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -2806,7 +2806,7 @@ def __call__(self, n): return ZZ(0) if n<=2: return ZZ(1) - return ZZ(pari(n).phi()) + return ZZ(pari(n).eulerphi()) def plot(self, xmin=1, xmax=50, pointsize=30, rgbcolor=(0,0,1), join=True, **kwds): @@ -3784,6 +3784,8 @@ def nth_prime(n): 5 sage: nth_prime(10) 29 + sage: nth_prime(10^7) + 179424673 :: @@ -3796,9 +3798,10 @@ def nth_prime(n): sage: all(prime_pi(nth_prime(j)) == j for j in range(1, 1000, 10)) True - """ - return ZZ(pari.nth_prime(n)) + if n <= 0: + raise ValueError("nth prime meaningless for non-positive n (=%s)" % n) + return ZZ(pari.prime(n)) def quadratic_residues(n): r""" @@ -5313,7 +5316,7 @@ def dedekind_sum(p, q, algorithm='default'): REFERENCES: - .. [Apostol] T. Apostol, Modular functions and Dirichlet series + .. [Apostol] \T. Apostol, Modular functions and Dirichlet series in number theory, Springer, 1997 (2nd ed), section 3.7--3.9. - :wikipedia:`Dedekind\_sum` diff --git a/src/sage/ext/multi_modular.pxd b/src/sage/arith/multi_modular.pxd similarity index 90% rename from src/sage/ext/multi_modular.pxd rename to src/sage/arith/multi_modular.pxd index b0273941958..292a6cf62ad 100644 --- a/src/sage/ext/multi_modular.pxd +++ b/src/sage/arith/multi_modular.pxd @@ -2,9 +2,8 @@ from sage.ext.mod_int cimport * from sage.libs.gmp.types cimport mpz_t -from sage.rings.integer cimport Integer -cdef class MultiModularBasis_base: +cdef class MultiModularBasis_base(object): cdef int n cdef mod_int* moduli cdef mpz_t* partial_products @@ -37,6 +36,5 @@ cdef class MultiModularBasis(MultiModularBasis_base): cdef class MutableMultiModularBasis(MultiModularBasis): cdef mod_int __last_prime - cdef mod_int next_prime_c(self) except -1 - cdef mod_int replace_prime_c(self, int i) except -1 - + cpdef mod_int next_prime(self) except -1 + cpdef mod_int replace_prime(self, int ix) except -1 diff --git a/src/sage/ext/multi_modular.pyx b/src/sage/arith/multi_modular.pyx similarity index 71% rename from src/sage/ext/multi_modular.pyx rename to src/sage/arith/multi_modular.pyx index c443e6afabf..33b80aebd4c 100644 --- a/src/sage/ext/multi_modular.pyx +++ b/src/sage/arith/multi_modular.pyx @@ -2,22 +2,23 @@ Utility classes for multi-modular algorithms """ -###################################################################### +#***************************************************************************** # Copyright (C) 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/ -###################################################################### - +#***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.libs.gmp.mpz cimport * -from sage.rings.integer_ring import ZZ +from sage.rings.integer cimport Integer, smallInteger from sage.arith.all import random_prime from types import GeneratorType +from sage.ext.stdsage cimport PY_NEW # should I have mod_int versions of these functions? # c_inverse_mod_longlong modular inverse used exactly once in _refresh_precomputations @@ -31,23 +32,23 @@ ai = arith_llong() MAX_MODULUS = MOD_INT_MAX -cdef class MultiModularBasis_base: +cdef class MultiModularBasis_base(object): r""" This class stores a list of machine-sized prime numbers, and can do reduction and Chinese Remainder Theorem lifting modulo these primes. Lifting implemented via Garner's algorithm, which has the advantage - that all reductions are word-sized. For each $i$ precompute - - $\prod_j=1^{i-1} m_j$ and $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$ - + that all reductions are word-sized. For each `i`, precompute + `\prod_j=1^{i-1} m_j` and `\prod_j=1^{i-1} m_j^{-1} (mod m_i)`. This class can be initialized in two ways, either with a list of prime moduli or an upper bound for the product of the prime moduli. The prime - moduli is generated automatically in the second case:: + moduli are generated automatically in the second case. + + EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([3, 5, 7]); mm MultiModularBasis with moduli [3, 5, 7] @@ -69,9 +70,9 @@ cdef class MultiModularBasis_base: sage: mm = MultiModularBasis_base(primes(10,20)); mm MultiModularBasis with moduli [11, 13, 17, 19] - There is no overflow if the modulus is below ``MAX_MODULUS`` :: + There is no overflow if the modulus is below ``MAX_MODULUS``:: - sage: from sage.ext.multi_modular import MAX_MODULUS + sage: from sage.arith.multi_modular import MAX_MODULUS sage: p0 = previous_prime(MAX_MODULUS) sage: p1 = previous_prime(p0) sage: MultiModularBasis_base([p0, p1]).crt([p0-1, p1-1]) @@ -85,9 +86,8 @@ cdef class MultiModularBasis_base: sage: MultiModularBasis_base([p0, p1]).crt([p0-1, p1-1]) Traceback (most recent call last): ... - ValueError: given modulus cannot be manipulated in a single machine word + OverflowError: given modulus 6074000981 is larger than 3037000498 """ - def __cinit__(self): r""" Allocate the space for the moduli and precomputation lists @@ -95,62 +95,56 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([1099511627791]) Traceback (most recent call last): ... - ValueError: given modulus cannot be manipulated in a single machine word + OverflowError: given modulus 1099511627791 is larger than 3037000498 """ mpz_init(self.product) mpz_init(self.half_product) cdef _realloc_to_new_count(self, new_count): - self.moduli = sage_realloc(self.moduli, sizeof(mod_int)*new_count) - self.partial_products = sage_realloc(self.partial_products, sizeof(mpz_t)*new_count) - self.C = sage_realloc(self.C, sizeof(mod_int)*new_count) - if self.moduli == NULL or self.partial_products == NULL or self.C == NULL: - raise MemoryError, "out of memory allocating multi-modular prime list" + self.moduli = check_reallocarray(self.moduli, new_count, sizeof(mod_int)) + self.partial_products = check_reallocarray(self.partial_products, new_count, sizeof(mpz_t)) + self.C = check_reallocarray(self.C, new_count, sizeof(mod_int)) def __dealloc__(self): """ - sage: from sage.ext.multi_modular import MultiModularBasis_base - sage: mm = MultiModularBasis_base([1099511627791]) - Traceback (most recent call last): - ... - ValueError: given modulus cannot be manipulated in a single machine word + TESTS:: + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base(1099511627791); mm MultiModularBasis with moduli [4561, 17351, 28499] sage: del mm """ - sage_free(self.moduli) - for i from 0 <= i < self.n: + sig_free(self.moduli) + for i in range(self.n): mpz_clear(self.partial_products[i]) - sage_free(self.partial_products) - sage_free(self.C) + sig_free(self.partial_products) + sig_free(self.C) mpz_clear(self.product) mpz_clear(self.half_product) - def __init__(self, val, l_bound=0, u_bound=0): + def __init__(self, val, unsigned long l_bound=2**10, unsigned long u_bound=2**15): r""" Initialize a multi-modular basis and perform precomputations. INPUT: - - ``val`` - as integer + - ``val`` -- as integer determines how many primes are computed (their product will be at least 2*val) as list, tuple or generator a list of prime moduli to start with - - ``l_bound`` - an integer - lower bound for the random primes (default: 2^10) - - ``u_bound`` - an integer - upper bound for the random primes (default: 2^15) - + - ``l_bound`` -- an integer: lower bound for the random primes + (default: 2^10) + - ``u_bound`` -- an integer: upper bound for the random primes + (default: 2^15) EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([1009, 10007]); mm MultiModularBasis with moduli [1009, 10007] sage: mm.prod() @@ -166,7 +160,7 @@ cdef class MultiModularBasis_base: sage: mm = MultiModularBasis_base([1000000000000000000000000000057]) Traceback (most recent call last): ... - ValueError: given modulus cannot be manipulated in a single machine word + OverflowError: given modulus 1000000000000000000000000000057 is larger than 3037000498 sage: mm = MultiModularBasis_base(0); mm MultiModularBasis with moduli [28499] @@ -174,74 +168,62 @@ cdef class MultiModularBasis_base: sage: mm = MultiModularBasis_base([6, 10]) Traceback (most recent call last): ... - ValueError: the values provided are not relatively prime + ArithmeticError: The inverse of 6 modulo 10 is not defined. """ - if l_bound == 0: - self._l_bound = 2**10 - elif l_bound < 2: - raise ValueError("minimum value for lower bound is 2, given: %s"%(l_bound)) - else: - self._l_bound = l_bound + if l_bound < 2: + raise ValueError(f"minimum value for lower bound is 2, given: {l_bound}") + if u_bound > MAX_MODULUS: + raise ValueError(f"upper bound cannot be greater than {MAX_MODULUS}, given: {u_bound}") - if u_bound == 0: - self._u_bound = 2**15 - elif u_bound > MAX_MODULUS: - raise ValueError("upper bound cannot be greater than MAX_MODULUS: %s, given: %s"%(MAX_MODULUS, u_bound)) - else: - self._u_bound = u_bound + self._l_bound = l_bound + self._u_bound = u_bound from sage.functions.prime_pi import prime_pi # must be here to avoid circular import self._num_primes = prime_pi(self._u_bound) - prime_pi(self._l_bound-1) - cdef int i if isinstance(val, (list, tuple, GeneratorType)): - try: - self.extend_with_primes(val, check=True) - except ArithmeticError: - # See trac #10217 - raise ValueError("the values provided are not relatively prime") + self.extend_with_primes(val, check=True) else: self._extend_moduli_to_height(val) cdef mod_int _new_random_prime(self, set known_primes) except 1: """ Choose a new random prime for inclusion in the list of moduli, - or raise a RuntimeError if there are no more primes. + or raise a ``RuntimeError`` if there are no more primes. INPUT: - - `known_primes` -- Python set of already known primes in - the allowed interval; we will not return a prime in - known_primes. + - ``known_primes`` -- Python set of already known primes in + the allowed interval; we will not return a prime in + known_primes. """ cdef Py_ssize_t i cdef mod_int p while True: if len(known_primes) >= self._num_primes: - raise RuntimeError, "there are not enough primes in the interval [%s, %s] to complete this multimodular computation"%(self._l_bound, self._u_bound) + raise RuntimeError("there are not enough primes in the interval [%s, %s] to complete this multimodular computation"%(self._l_bound, self._u_bound)) p = random_prime(self._u_bound, lbound =self._l_bound) if p not in known_primes: return p def extend_with_primes(self, plist, partial_products = None, check=True): """ - Extend the stored list of moduli with the given primes in `plist`. + Extend the stored list of moduli with the given primes in ``plist``. EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([1009, 10007]); mm MultiModularBasis with moduli [1009, 10007] sage: mm.extend_with_primes([10037, 10039]) 4 sage: mm MultiModularBasis with moduli [1009, 10007, 10037, 10039] - """ if isinstance(plist, GeneratorType): plist = list(plist) elif not isinstance(plist, (tuple, list)): - raise ValueError, "plist should be a list, tuple or a generator, got: %s"%(str(type(plist))) + raise TypeError("plist should be a list, tuple or a generator, got: %s"%(str(type(plist)))) cdef Py_ssize_t len_plist = len(plist) @@ -250,7 +232,7 @@ cdef class MultiModularBasis_base: if check: for p in plist: if p > MAX_MODULUS: - raise ValueError, "given modulus cannot be manipulated in a single machine word" + raise OverflowError(f"given modulus {p} is larger than {MAX_MODULUS}") self._realloc_to_new_count(self.n + len_plist) cdef Py_ssize_t i @@ -274,7 +256,7 @@ cdef class MultiModularBasis_base: """ EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007]) sage: nn = MultiModularBasis_base([10007]) sage: mm == nn @@ -291,12 +273,12 @@ cdef class MultiModularBasis_base: def __setstate__(self, state): """ - Initialize a new MultiModularBasis_base object from a state stored - in a pickle. + Initialize a new :class:`MultiModularBasis_base` object from a + state stored in a pickle. TESTS:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007, 10009]) sage: mm == loads(dumps(mm)) True @@ -306,10 +288,7 @@ cdef class MultiModularBasis_base: sage: mm MultiModularBasis with moduli [10007, 10009] - """ - if len(state) != 3: - raise ValueError, "cannot read state tuple" nmoduli, lbound, ubound = state self._realloc_to_new_count(len(nmoduli)) self._l_bound = lbound @@ -322,7 +301,7 @@ cdef class MultiModularBasis_base: TESTS:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007, 10009]) sage: mm.__getstate__() ([10007, 10009], 1024L, 32768L) @@ -331,10 +310,9 @@ cdef class MultiModularBasis_base: def _extend_moduli_to_height(self, height): """ - EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base(0); mm MultiModularBasis with moduli [4561] @@ -350,29 +328,28 @@ cdef class MultiModularBasis_base: TESTS: - Verify that Trac #11358 is fixed:: + Verify that :trac:`11358` is fixed:: - sage: set_random_seed(0); m = sage.ext.multi_modular.MultiModularBasis_base(0) + sage: set_random_seed(0); m = sage.arith.multi_modular.MultiModularBasis_base(0) sage: m._extend_moduli_to_height(prod(prime_range(50))) - sage: m = sage.ext.multi_modular.MultiModularBasis_base([],2,100) + sage: m = sage.arith.multi_modular.MultiModularBasis_base([],2,100) sage: m._extend_moduli_to_height(prod(prime_range(90))) sage: m._extend_moduli_to_height(prod(prime_range(150))) Traceback (most recent call last): ... RuntimeError: there are not enough primes in the interval [2, 100] to complete this multimodular computation - Another check (which fails horribly before #11358 is fixed):: + Another check (which fails horribly before :trac:`11358` is fixed):: - sage: set_random_seed(0); m = sage.ext.multi_modular.MultiModularBasis_base(0); m._extend_moduli_to_height(10**10000) + sage: set_random_seed(0); m = sage.arith.multi_modular.MultiModularBasis_base(0); m._extend_moduli_to_height(10**10000) sage: len(set(m)) == len(m) True sage: len(m) 2438 """ - cdef Integer h - h = ZZ(height) + cdef Integer h = Integer(height) if h < self._l_bound: - h = ZZ(self._l_bound) + h = Integer(self._l_bound) self._extend_moduli_to_height_c(h.value) cdef int _extend_moduli_to_height_c(self, mpz_t height0) except -1: @@ -381,7 +358,7 @@ cdef class MultiModularBasis_base: INPUT: - - ``height`` - determines how many primes are computed + - ``height`` -- determines how many primes are computed (their product must be at least 2*height) """ # real height we use is twice the given, set height to 2*height0 @@ -401,7 +378,7 @@ cdef class MultiModularBasis_base: cdef mod_int p # keeps current prime moduli if self.n == 0: - M = ZZ(1) + M = smallInteger(1) else: M = PY_NEW(Integer) mpz_set(M.value, self.partial_products[self.n-1]) @@ -423,11 +400,11 @@ cdef class MultiModularBasis_base: INPUT: - - ``count`` - the minimum number of moduli in the resulting list + - ``count`` -- the minimum number of moduli in the resulting list EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307]); mm MultiModularBasis with moduli [46307] sage: mm._extend_moduli_to_count(3) @@ -442,7 +419,7 @@ cdef class MultiModularBasis_base: cdef int i cdef mod_int p known_primes = set(self) - for i from self.n <= i < count: + for i in range(self.n, count): p = self._new_random_prime(known_primes) known_primes.add(p) new_moduli.append(p) @@ -455,7 +432,7 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307]); mm MultiModularBasis with moduli [46307] sage: mm._extend_moduli(2); mm @@ -465,14 +442,14 @@ cdef class MultiModularBasis_base: cdef void _refresh_products(self, int start): r""" - Compute and store $\prod_j=1^{i-1} m_j$ of i > start. + Compute and store `\prod_j=1^{i-1} m_j` for i > start. """ cdef mpz_t z mpz_init(z) if start == 0: mpz_set_si(self.partial_products[0], self.moduli[0]) start += 1 - for i from start <= i < self.n: + for i in range(start, self.n): mpz_set_si(z, self.moduli[i]) mpz_mul(self.partial_products[i], self.partial_products[i-1], z) mpz_clear(z) @@ -485,12 +462,12 @@ cdef class MultiModularBasis_base: cdef void _refresh_precomputations(self, int start) except *: r""" - Compute and store $\prod_j=1^{i-1} m_j^{-1} (mod m_i)$ of i >= start. + Compute and store `\prod_j=1^{i-1} m_j^{-1} (mod m_i)` for i >= start. """ if start == 0: start = 1 # first one is trivial, never used self.C[0] = 1 - for i from start <= i < self.n: + for i in range(start, self.n): self.C[i] = ai.c_inverse_mod_longlong(mpz_fdiv_ui(self.partial_products[i-1], self.moduli[i]), self.moduli[i]) cdef int min_moduli_count(self, mpz_t height) except -1: @@ -515,9 +492,9 @@ cdef class MultiModularBasis_base: cdef int mpz_reduce_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1: r""" - Performs reduction mod $m_i$ for offset <= i < len + Perform reduction mod `m_i` for offset <= i < len. - b[i] = z mod $m_{i+offset}$ for 0 <= i < len + `b[i] = z mod m_{i+offset}` for 0 <= i < len INPUT: @@ -530,15 +507,15 @@ cdef class MultiModularBasis_base: cdef int i cdef mod_int* m m = self.moduli + offset - for i from 0 <= i < len: + for i in range(len): b[i] = mpz_fdiv_ui(z, m[i]) return 0 cdef int mpz_reduce_vec_tail(self, mpz_t* z, mod_int** b, int vn, int offset, int len) except -1: r""" - Performs reduction mod $m_i$ for offset <= i < len + Perform reduction mod `m_i` for offset <= i < len. - b[i][j] = z[j] mod $m_{i+offset}$ for 0 <= i < len + `b[i][j] = z[j] mod m_{i+offset}` for 0 <= i < len INPUT: @@ -553,20 +530,20 @@ cdef class MultiModularBasis_base: cdef int i, j cdef mod_int* m m = self.moduli + offset - for i from 0 <= i < len: + for i in range(len): mi = m[i] - for j from 0 <= j < vn: + for j in range(vn): b[i][j] = mpz_fdiv_ui(z[j], mi) return 0 cdef int mpz_crt_tail(self, mpz_t z, mod_int* b, int offset, int len) except -1: r""" - Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$. + Calculate lift mod `\prod_{i=0}^{offset+len-1} m_i`. - z = b[i] mod $m_{i+offset}$ for 0 <= i < len + z = b[i] mod `m_{i+offset}` for 0 <= i < len In the case that offset > 0, - z remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ + z remains unchanged mod `\prod_{i=0}^{offset-1} m_i` INPUT: @@ -590,7 +567,7 @@ cdef class MultiModularBasis_base: s += 1 else: s = 0 - for i from s <= i < len: + for i in range(s, len): mpz_set_si(u, ((b[i] + m[i] - mpz_fdiv_ui(z, m[i])) * self.C[i]) % m[i]) mpz_mul(u, u, self.partial_products[i-1]) mpz_add(z, z, u) @@ -603,12 +580,12 @@ cdef class MultiModularBasis_base: cdef int mpz_crt_vec_tail(self, mpz_t* z, mod_int** b, int vc, int offset, int len) except -1: r""" - Calculate lift mod $\prod_{i=0}^{offset+len-1} m_i$. + Calculate lift mod `\prod_{i=0}^{offset+len-1} m_i`. - z[j] = b[i][j] mod $m_{i+offset}$ for 0 <= i < len + `z[j] = b[i][j] mod m_{i+offset}` for 0 <= i < len In the case that offset > 0, - z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ + z[j] remains unchanged mod `\prod_{i=0}^{offset-1} m_i` INPUT: @@ -632,7 +609,7 @@ cdef class MultiModularBasis_base: else: s = 0 - for j from 0 <= j < vc: + for j in range(vc): i = s if offset == 0: mpz_set_si(z[j], b[0][j]) @@ -659,10 +636,10 @@ cdef class MultiModularBasis_base: def crt(self, b): r""" - Calculate lift mod $\prod_{i=0}^{len(b)-1} m_i$. + Calculate lift mod `\prod_{i=0}^{len(b)-1} m_i`. In the case that offset > 0, - z[j] remains unchanged mod $\prod_{i=0}^{offset-1} m_i$ + z[j] remains unchanged mod `\prod_{i=0}^{offset-1} m_i` INPUT: @@ -670,11 +647,11 @@ cdef class MultiModularBasis_base: OUTPUT: - Integer z where z = b[i] mod $m_i$ for 0 <= i < len(b) + Integer z where `z = b[i] mod m_i` for 0 <= i < len(b) EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007, 10009, 10037, 10039, 17351]) sage: res = mm.crt([3,5,7,9]); res 8474803647063985 @@ -691,44 +668,40 @@ cdef class MultiModularBasis_base: cdef int i, n n = len(b) if n > self.n: - raise IndexError, "beyond bound for multi-modular prime list" + raise IndexError("beyond bound for multi-modular prime list") cdef mod_int* bs - bs = sage_malloc(sizeof(mod_int)*n) - if bs == NULL: - raise MemoryError, "out of memory allocating multi-modular prime list" - for i from 0 <= i < n: + bs = check_allocarray(n, sizeof(mod_int)) + for i in range(n): bs[i] = b[i] cdef Integer z z = PY_NEW(Integer) self.mpz_crt_tail(z.value, bs, 0, n) - sage_free(bs) + sig_free(bs) return z def precomputation_list(self): """ - Returns a list of the precomputed coefficients - `$\prod_j=1^{i-1} m_j^{-1} (mod m_i)$` + Return a list of the precomputed coefficients + `\prod_j=1^{i-1} m_j^{-1} (mod m_i)` where `m_i` are the prime moduli. EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307, 10007]); mm MultiModularBasis with moduli [46307, 10007] sage: mm.precomputation_list() [1, 4013] - """ - cdef int i - return [ZZ(self.C[i]) for i from 0 <= i < self.n] + return [Integer(self.C[i]) for i in range(self.n)] def partial_product(self, n): """ - Returns a list containing precomputed partial products. + Return a list containing precomputed partial products. EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307, 10007]); mm MultiModularBasis with moduli [46307, 10007] sage: mm.partial_product(0) @@ -749,9 +722,9 @@ cdef class MultiModularBasis_base: """ if n >= self.n: - raise IndexError, "beyond bound for multi-modular prime list" + raise IndexError("beyond bound for multi-modular prime list") if n < 0: - raise IndexError, "negative index not valid" + raise IndexError("negative index not valid") cdef Integer z z = PY_NEW(Integer) mpz_set(z.value, self.partial_products[n]) @@ -759,11 +732,11 @@ cdef class MultiModularBasis_base: def prod(self): """ - Returns the product of the prime moduli. + Return the product of the prime moduli. EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307]); mm MultiModularBasis with moduli [46307] sage: mm.prod() @@ -795,13 +768,12 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([46307, 10007]) sage: mm.list() [46307, 10007] """ - cdef Py_ssize_t i - return [ZZ(self.moduli[i]) for i in range(self.n)] + return [Integer(self.moduli[i]) for i in range(self.n)] def __len__(self): """ @@ -809,7 +781,7 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007]) sage: len(mm) 1 @@ -822,17 +794,16 @@ cdef class MultiModularBasis_base: def __iter__(self): """ - Returns an iterator over the prime moduli. + Return an iterator over the prime moduli. EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007, 10009]) sage: t = iter(mm); t sage: list(mm.__iter__()) [10007, 10009] - """ return iter(self.list()) @@ -842,7 +813,7 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: mm = MultiModularBasis_base([10007, 10009]) sage: mm[1] 10009 # 64-bit @@ -852,19 +823,18 @@ cdef class MultiModularBasis_base: ... IndexError: index out of range - #sage: mm[:1] + sage: mm[:1] MultiModularBasis with moduli [10007] """ if isinstance(ix, slice): return self.__class__(self.list()[ix], l_bound = self._l_bound, u_bound = self._u_bound) - cdef int i - i = ix + cdef Py_ssize_t i = ix if i != ix: - raise ValueError, "index must be an integer" + raise TypeError("index must be an integer") if i < 0 or i >= self.n: - raise IndexError, "index out of range" + raise IndexError("index out of range") return self.moduli[i] def __repr__(self): @@ -873,7 +843,7 @@ cdef class MultiModularBasis_base: EXAMPLES:: - sage: from sage.ext.multi_modular import MultiModularBasis_base + sage: from sage.arith.multi_modular import MultiModularBasis_base sage: MultiModularBasis_base([10007]) MultiModularBasis with moduli [10007] """ @@ -884,67 +854,65 @@ cdef class MultiModularBasis(MultiModularBasis_base): """ Class used for storing a MultiModular bases of a fixed length. """ - - cdef int mpz_reduce(self, mpz_t z, mod_int* b) except -1: r""" - Performs reduction mod $m_i$ for each modulus $m_i$ + Perform reduction mod `m_i` for each modulus `m_i`. - b[i] = z mod $m_i$ for 0 <= i < len(self) + `b[i] = z mod m_i` for 0 <= i < len(self) INPUT: - - ``z`` - the integer being reduced - - ``b`` - array to hold the reductions mod each m_i. + - ``z`` -- the integer being reduced + - ``b`` -- array to hold the reductions mod each m_i. It MUST be allocated and have length at least len """ self.mpz_reduce_tail(z, b, 0, self.n) cdef int mpz_reduce_vec(self, mpz_t* z, mod_int** b, int vn) except -1: r""" - Performs reduction mod $m_i$ for each modulus $m_i$ + Perform reduction mod `m_i` for each modulus `m_i`. - b[i][j] = z[j] mod $m_i$ for 0 <= i < len(self) + `b[i][j] = z[j] mod m_i` for 0 <= i < len(self) INPUT: - - ``z`` - an array of integers being reduced - - ``b`` - array to hold the reductions mod each m_i. + - ``z`` -- an array of integers being reduced + - ``b`` -- array to hold the reductions mod each m_i. It MUST be fully allocated and each have length at least len - - ``vn`` - length of z and each b[i] + - ``vn`` -- length of z and each b[i] """ self.mpz_reduce_vec_tail(z, b, vn, 0, self.n) cdef int mpz_crt(self, mpz_t z, mod_int* b) except -1: r""" - Calculate lift mod $\prod m_i$. + Calculate lift mod `\prod m_i`. - z = b[i] mod $m_{i+offset}$ for 0 <= i < len(self) + `z = b[i] mod m_{i+offset}` for 0 <= i < len(self) INPUT: - - ``z`` - a placeholder for the constructed integer + - ``z`` -- a placeholder for the constructed integer z MUST NOT be initialized - - ``b`` - array holding the reductions mod each $m_i$. + - ``b`` -- array holding the reductions mod each `m_i`. It MUST have length at least len(self) """ self.mpz_crt_tail(z, b, 0, self.n) cdef int mpz_crt_vec(self, mpz_t* z, mod_int** b, int vn) except -1: r""" - Calculate lift mod $\prod m_i$. + Calculate lift mod `\prod m_i`. - z[j] = b[i][j] mod $m_i$ for 0 <= i < len(self) + `z[j] = b[i][j] mod m_i` for 0 <= i < len(self) INPUT: - - ``z`` - a placeholder for the constructed integers + - ``z`` -- a placeholder for the constructed integers z MUST be allocated and have length at least vn, but each z[j] MUST NOT be initialized - - ``b`` - array holding the reductions mod each $m_i$. + - ``b`` -- array holding the reductions mod each `m_i`. It MUST have length at least len(self) - - ``vn`` - length of z and each b[i] + - ``vn`` -- length of z and each b[i] """ self.mpz_crt_vec_tail(z, b, vn, 0, self.n) @@ -954,37 +922,39 @@ cdef class MutableMultiModularBasis(MultiModularBasis): Class used for performing multi-modular methods, with the possibility of removing bad primes. """ - def next_prime(self): + cpdef mod_int next_prime(self) except -1: """ - Picks a new random prime between the bounds given during the - initialization of this object, updates the precomputed data, - and returns the new prime modulus. + Pick a new random prime between the bounds given during the + initialization of this object, update the precomputed data, + and return the new prime modulus. EXAMPLES:: - sage: from sage.ext.multi_modular import MutableMultiModularBasis + sage: from sage.arith.multi_modular import MutableMultiModularBasis sage: mm = MutableMultiModularBasis([10007]) sage: mm.next_prime() 4561 # 64-bit 4561L # 32-bit sage: mm MultiModularBasis with moduli [10007, 4561] - """ - return self.next_prime_c() - - cdef mod_int next_prime_c(self) except -1: self._extend_moduli(1) return self.moduli[self.n-1] - def replace_prime(self, ix): + cpdef mod_int replace_prime(self, int ix) except -1: """ Replace the prime moduli at the given index with a different one, update the precomputed data accordingly, and return the new prime. + INPUT: + + - ``ix`` -- index into list of moduli + + OUTPUT: the new prime modulus + EXAMPLES:: - sage: from sage.ext.multi_modular import MutableMultiModularBasis + sage: from sage.arith.multi_modular import MutableMultiModularBasis sage: mm = MutableMultiModularBasis([10007, 10009, 10037, 10039]) sage: mm MultiModularBasis with moduli [10007, 10009, 10037, 10039] @@ -1005,28 +975,11 @@ cdef class MutableMultiModularBasis(MultiModularBasis): [1, 2314, 3274, 3013] sage: mm.partial_product(2) 458108021299 - - """ - return self.replace_prime_c(ix) - - cdef mod_int replace_prime_c(self, int ix) except -1: - r""" - Replace $m_{ix}$ in the list of moduli with a new - prime number greater than all others in the list, - and recompute all precomputations. - - INPUT: - - - ``ix`` - index into list of moduli - - OUTPUT: - - p -- the new prime modulus """ cdef mod_int new_p if ix < 0 or ix >= self.n: - raise IndexError, "index out of range" + raise IndexError("index out of range") new_p = self._new_random_prime(set(self)) self.moduli[ix] = new_p diff --git a/src/sage/arith/srange.pyx b/src/sage/arith/srange.pyx index 4cc04b4fad8..41299569b90 100644 --- a/src/sage/arith/srange.pyx +++ b/src/sage/arith/srange.pyx @@ -25,7 +25,7 @@ from sage.rings.integer cimport Integer from sage.structure.element cimport parent_c as parent from sage.structure.sequence import Sequence -include "sage/ext/stdsage.pxi" +include "cysignals/signals.pxi" def xsrange(start, end=None, step=1, universe=None, *, coerce=True, bint include_endpoint=False, endpoint_tolerance=1e-5): diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index 535c85812ab..95fd22add37 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -987,7 +987,7 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): :: expr.limit(x = a) - expr.limit(x = a, dir='above') + expr.limit(x = a, dir='+') INPUT: diff --git a/src/sage/calculus/riemann.pyx b/src/sage/calculus/riemann.pyx index c9fc29a9b63..a7a3bdb5f0c 100644 --- a/src/sage/calculus/riemann.pyx +++ b/src/sage/calculus/riemann.pyx @@ -70,8 +70,7 @@ cdef FLOAT_T TWOPI = 2*PI cdef COMPLEX_T I = complex(0,1) cdef class Riemann_Map: - """ - + r""" The ``Riemann_Map`` class computes an interior or exterior Riemann map, or an Ahlfors map of a region given by the supplied boundary curve(s) and center point. The class also provides various methods to @@ -196,14 +195,13 @@ cdef class Riemann_Map: REFERENCES: - .. [KT] N. Kerzman and M. R. Trummer. "Numerical Conformal Mapping via + .. [KT] \N. Kerzman and M. R. Trummer. "Numerical Conformal Mapping via the Szego kernel". Journal of Computational and Applied Mathematics, 14(1-2): 111--123, 1986. - .. [BSV] M. Bolt, S. Snoeyink, E. Van Andel. "Visual representation of + .. [BSV] \M. Bolt, S. Snoeyink, E. Van Andel. "Visual representation of the Riemann map and Ahlfors map via the Kerzman-Stein equation". Involve 3-4 (2010), 405-420. - """ cdef int N, B, ncorners cdef f diff --git a/src/sage/calculus/wester.py b/src/sage/calculus/wester.py index f5e9b614951..8b2003f30fc 100644 --- a/src/sage/calculus/wester.py +++ b/src/sage/calculus/wester.py @@ -526,7 +526,7 @@ sage: # Verify(Simplify(Integrate(x) sage: # if(x<0) (-x) else x), sage: # Simplify(if(x<0) (-x^2/2) else x^2/2)); - sage: f = piecewise([ [[-10,0], -x], [[0,10], x]]) + sage: f = piecewise([ ((-10,0), -x), ((0,10), x)]) sage: f.integral(definite=True) 100 diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 6d8dc6e24cc..c688f8fb3d7 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -196,6 +196,28 @@ def _product_from_combinatorial_algebra_multiply(self,left,right): # tester.assert_(self.product is not None) # could check that self.product is in Hom( self x self, self) + def hochschild_complex(self, M): + """ + Return the Hochschild complex of ``self`` with coefficients + in ``M``. + + .. SEEALSO:: + + :class:`~sage.homology.hochschild_complex.HochschildComplex` + + EXAMPLES:: + + sage: R. = QQ[] + sage: A = algebras.DifferentialWeyl(R) + sage: H = A.hochschild_complex(A) + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + """ + from sage.homology.hochschild_complex import HochschildComplex + return HochschildComplex(self, M) + class ElementMethods: def __invert__(self): diff --git a/src/sage/categories/all.py b/src/sage/categories/all.py index 279174d751a..7a274c2e589 100644 --- a/src/sage/categories/all.py +++ b/src/sage/categories/all.py @@ -1,3 +1,5 @@ +from sage.misc.lazy_import import lazy_import + from category import Category from category_types import( @@ -117,10 +119,9 @@ from graded_bialgebras_with_basis import GradedBialgebrasWithBasis from graded_hopf_algebras_with_basis import GradedHopfAlgebrasWithBasis - # Coxeter groups from coxeter_groups import CoxeterGroups -from finite_coxeter_groups import FiniteCoxeterGroups +lazy_import('sage.categories.finite_coxeter_groups', 'FiniteCoxeterGroups') from weyl_groups import WeylGroups from finite_weyl_groups import FiniteWeylGroups from affine_weyl_groups import AffineWeylGroups @@ -133,5 +134,4 @@ from classical_crystals import ClassicalCrystals # polyhedra -from sage.misc.lazy_import import lazy_import lazy_import('sage.categories.polyhedra', 'PolyhedralSets') diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 2d50eac827c..862c895904e 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1078,8 +1078,6 @@ def additional_structure(self): Category of left modules over Integer Ring sage: Coalgebras(QQ).additional_structure() # coproduct Category of coalgebras over Rational Field - sage: CoxeterGroups().additional_structure() # distinguished generators - Category of coxeter groups sage: Crystals().additional_structure() # crystal operators Category of crystals @@ -1176,9 +1174,15 @@ def additional_structure(self): This method together with the methods overloading it provide the basic data to determine, for a given category, the super categories that define some structure (see - :meth:`structure`), and to test whether a - category is a full subcategory of some other category (see - :meth:`is_full_subcategory`). + :meth:`structure`), and to test whether a category is a + full subcategory of some other category (see + :meth:`is_full_subcategory`). For example, the category of + Coxeter groups is not full subcategory of the category of + groups since morphisms need to perserve the distinguished + generators:: + + sage: CoxeterGroups().is_full_subcategory(Groups()) + False The support for modeling full subcategories has been introduced in :trac:`16340`. @@ -1837,7 +1841,7 @@ def or_subcategory(self, category = None, join = False): sage: Monoids().or_subcategory(EnumeratedSets()) Traceback (most recent call last): ... - AssertionError: Subcategory of `Category of enumerated sets` required; got `Category of monoids` + ValueError: Subcategory of `Category of monoids` required; got `Category of enumerated sets` Otherwise, the two categories are joined together:: @@ -1852,7 +1856,8 @@ def or_subcategory(self, category = None, join = False): if join: return Category.join([self, category]) else: - assert category.is_subcategory(self), "Subcategory of `{}` required; got `{}`".format(category, self) + if not category.is_subcategory(self): + raise ValueError("Subcategory of `{}` required; got `{}`".format(self, category)) return category def _is_subclass(self, c): diff --git a/src/sage/categories/category_with_axiom.py b/src/sage/categories/category_with_axiom.py index cd52e3d8f4a..45bf9d2b34e 100644 --- a/src/sage/categories/category_with_axiom.py +++ b/src/sage/categories/category_with_axiom.py @@ -1675,6 +1675,7 @@ class ``Sets.Finite``), or in a separate file (typically in a class "Compact", "Differentiable", "Smooth", "Analytic", "AlmostComplex", "FinitelyGeneratedAsMagma", + "WellGenerated", "Facade", "Finite", "Infinite", "Complete", "FiniteDimensional", "Connected", "WithBasis", @@ -1682,7 +1683,7 @@ class ``Sets.Finite``), or in a separate file (typically in a class "Commutative", "Associative", "Inverse", "Unital", "Division", "NoZeroDivisors", "AdditiveCommutative", "AdditiveAssociative", "AdditiveInverse", "AdditiveUnital", "Distributive", - "Endset" + "Endset", ) def uncamelcase(s,separator=" "): diff --git a/src/sage/categories/classical_crystals.py b/src/sage/categories/classical_crystals.py index 36cb7fe4368..31ddccf46cc 100644 --- a/src/sage/categories/classical_crystals.py +++ b/src/sage/categories/classical_crystals.py @@ -182,7 +182,7 @@ def demazure_character(self, w, f = None): REFERENCES: - .. [D1974] M. Demazure, Desingularisation des varietes de Schubert, + .. [D1974] \M. Demazure, Desingularisation des varietes de Schubert, Ann. E. N. S., Vol. 6, (1974), p. 163-172 .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms, diff --git a/src/sage/categories/complex_reflection_groups.py b/src/sage/categories/complex_reflection_groups.py new file mode 100644 index 00000000000..1ad97841231 --- /dev/null +++ b/src/sage/categories/complex_reflection_groups.py @@ -0,0 +1,149 @@ +r""" +Complex reflection groups +""" +#***************************************************************************** +# Copyright (C) 2011-2015 Christian Stump +# +# 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.misc.lazy_import import LazyImport +from sage.categories.category_singleton import Category_singleton +from sage.categories.complex_reflection_or_generalized_coxeter_groups import ComplexReflectionOrGeneralizedCoxeterGroups + +class ComplexReflectionGroups(Category_singleton): + r""" + The category of complex reflection groups. + + Let `V` be a complex vector space. A *complex reflection* is an + element of `\operatorname{GL}(V)` fixing an hyperplane pointwise + and acting by multiplication by a root of unity on a complementary + line. + + A *complex reflection group* is a group `W` that is (isomorphic + to) a subgroup of some general linear group `\operatorname{GL}(V)` + generated by a distinguished set of complex reflections. + + The dimension of `V` is the *rank* of `W`. + + For a comprehensive treatment of complex reflection groups and + many definitions and theorems used here, we refer to [LT2009]_. + See also :wikipedia:`Reflection_group`. + + .. SEEALSO:: + + :func:`ReflectionGroup` for usage examples of this category. + + REFERENCES: + + .. [LT2009] G.I. Lehrer and D.E. Taylor. *Unitary reflection groups*. + Australian Mathematical Society Lecture Series, 2009. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups() + Category of complex reflection groups + sage: ComplexReflectionGroups().super_categories() + [Category of complex reflection or generalized coxeter groups] + sage: ComplexReflectionGroups().all_super_categories() + [Category of complex reflection groups, + Category of complex reflection or generalized coxeter groups, + Category of groups, + Category of monoids, + Category of finitely generated semigroups, + Category of semigroups, + Category of finitely generated magmas, + Category of inverse unital magmas, + Category of unital magmas, + Category of magmas, + Category of enumerated sets, + Category of sets, + Category of sets with partial maps, + Category of objects] + + An example of a reflection group:: + + sage: W = ComplexReflectionGroups().example(); W + 5-colored permutations of size 3 + + ``W`` is in the category of complex reflection groups:: + + sage: W in ComplexReflectionGroups() + True + + TESTS:: + + sage: TestSuite(W).run() + sage: TestSuite(ComplexReflectionGroups()).run() + """ + + @cached_method + def super_categories(self): + r""" + Return the super categories of ``self``. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().super_categories() + [Category of complex reflection or generalized coxeter groups] + """ + return [ComplexReflectionOrGeneralizedCoxeterGroups()] + + def additional_structure(self): + r""" + Return ``None``. + + Indeed, all the structure complex reflection groups have in + addition to groups (simple reflections, ...) is already + defined in the super category. + + .. SEEALSO:: :meth:`Category.additional_structure` + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().additional_structure() + """ + return None + + def example(self): + r""" + Return an example of a complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().example() + 5-colored permutations of size 3 + """ + from sage.combinat.colored_permutations import ColoredPermutations + return ColoredPermutations(5, 3) + + class ParentMethods: + + @cached_method + def rank(self): + r""" + Return the rank of ``self``. + + The rank of ``self`` is the dimension of the smallest + faithfull reflection representation of ``self``. + + EXAMPLES:: + + sage: W = CoxeterGroups().example(); W + The symmetric group on {0, ..., 3} + sage: W.rank() + 3 + """ + + Finite = LazyImport('sage.categories.finite_complex_reflection_groups', + 'FiniteComplexReflectionGroups', as_name='Finite') + diff --git a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py new file mode 100644 index 00000000000..e35c187d70b --- /dev/null +++ b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py @@ -0,0 +1,1216 @@ +# -*- coding: utf-8 -*- +r""" +Common category for Generalized Coxeter Groups or Complex Reflection Groups +""" +#***************************************************************************** +# Copyright (C) 2016 Nicolas M. Thiéry +# +# 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 itertools +from sage.misc.abstract_method import abstract_method +from sage.misc.cachefunc import cached_method +from sage.categories.category_singleton import Category_singleton +from sage.categories.category_with_axiom import CategoryWithAxiom +from sage.categories.groups import Groups + +class ComplexReflectionOrGeneralizedCoxeterGroups(Category_singleton): + r""" + The category of complex reflection groups or generalized Coxeter groups. + + Finite Coxeter groups can be defined equivalently as groups + generated by reflections, or by presentations. Over the last + decades, the theory has been generalized in both directions, + leading to the study of (finite) complex reflection groups on the + one hand, and (finite) generalized Coxeter groups on the other + hand. Many of the features remain similar, yet, in the current + state of the art, there is no general theory covering both + directions. + + This is reflected by the name of this category which is about + factoring out the common code, tests, and declarations. + + A group in this category has: + + - A distinguished finite set of generators `(s_i)_I`, called + *simple reflections*. The set `I` is called the *index set*. The + name "reflection" is somewhat of an abuse as they can have + higher order; still, they are all of finite order: `s_i^k=1` for + some `k`. + + - A collection of *distinguished reflections* which are the + conjugates of the simple reflections. For complex reflection + groups, they are in one-to-one correspondence with the + reflection hyperplanes and share the same index set. + + - A collection of *reflections* which are the conjugates of all + the non trivial powers of the simple reflections. + + The usual notions of reduced words, length, irreducibility, etc + can be canonically defined from the above. + + The following methods must be implemented: + + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ParentMethods.index_set` + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ParentMethods.simple_reflection` + + Optionally one can define analog methods for distinguished + reflections and reflections (see below). + + At least one of the following methods must be implemented: + + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection` + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection_left` + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection_right` + - :meth:`ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods._mul_` + + It's recommended to implement either `_mul` or both + `apply_simple_reflection_left` and `apply_simple_reflection_right`. + + .. SEEALSO:: + + - :class:`complex_reflection_groups.ComplexReflectionGroups` + - :class:`generalized_coxeter_groups.GeneralizedCoxeterGroups` + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_or_generalized_coxeter_groups import ComplexReflectionOrGeneralizedCoxeterGroups + sage: C = ComplexReflectionOrGeneralizedCoxeterGroups(); C + Category of complex reflection or generalized coxeter groups + sage: C.super_categories() + [Category of finitely generated groups] + + sage: C.required_methods() + {'element': {'optional': ['reflection_length'], + 'required': []}, + 'parent': {'optional': ['distinguished_reflection', 'hyperplane_index_set', + 'irreducible_components', + 'reflection', 'reflection_index_set'], + 'required': ['__contains__', 'index_set']}} + + TESTS:: + + sage: TestSuite(C).run() + """ + + @cached_method + def super_categories(self): + r""" + Return the super categories of ``self``. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().super_categories() + [Category of complex reflection or generalized coxeter groups] + """ + return [Groups().FinitelyGenerated()] + + class SubcategoryMethods: + def Irreducible(self): + r""" + Return the full subcategory of irreducible objects of ``self``. + + A complex reflection group, or generalized coxeter group + is *reducible* if its simple reflections can be split in + two sets `X` and `Y` such that the elements of `X` commute + with that of `Y`. In particular, the group is then direct + product of `\langle X\rangle` and `\langle Y\rangle`. It's + *irreducible* otherwise. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Irreducible() + Category of irreducible complex reflection groups + sage: CoxeterGroups().Irreducible() + Category of irreducible coxeter groups + + TESTS:: + + sage: TestSuite(ComplexReflectionGroups().Irreducible()).run() + sage: CoxeterGroups().Irreducible.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + return self._with_axiom('Irreducible') + + class ParentMethods: + @abstract_method + def index_set(self): + r""" + Return the index set of (the simple reflections of) + ``self``, as a list (or iterable). + + .. SEEALSO:: + + - :meth:`simple_reflection` + - :meth:`simple_reflections` + + EXAMPLES:: + + sage: W = CoxeterGroups().Finite().example(); W + The 5-th dihedral group of order 10 + sage: W.index_set() + (1, 2) + + sage: W = ColoredPermutations(1, 4) + sage: W.index_set() + (1, 2, 3) + sage: W = ReflectionGroup((1,1,4), index_set=[1,3,'asdf']) # optional - gap3 + sage: W.index_set() # optional - gap3 + (1, 3, 'asdf') + sage: W = ReflectionGroup((1,1,4), index_set=('a','b','c')) # optional - gap3 + sage: W.index_set() # optional - gap3 + ('a', 'b', 'c') + """ + # return self.simple_reflections().keys() + + def simple_reflection(self, i): + """ + Return the `i`-th simple reflection `s_i` of ``self``. + + INPUT: + + - ``i`` -- an element from the index set + + .. SEEALSO:: + + - :meth:`index_set` + - :meth:`simple_reflections` + + EXAMPLES:: + + sage: W = CoxeterGroups().example() + sage: W + The symmetric group on {0, ..., 3} + sage: W.simple_reflection(1) + (0, 2, 1, 3) + sage: s = W.simple_reflections() + sage: s[1] + (0, 2, 1, 3) + + sage: W = ReflectionGroup((1,1,4), index_set=[1,3,'asdf']) # optional - gap3 + sage: for i in W.index_set(): # optional - gap3 + ....: print('%s %s'%(i, W.simple_reflection(i))) # optional - gap3 + 1 (1,7)(2,4)(5,6)(8,10)(11,12) + 3 (1,4)(2,8)(3,5)(7,10)(9,11) + asdf (2,5)(3,9)(4,6)(8,11)(10,12) + """ + if not i in self.index_set(): + raise ValueError("%s is not in the Dynkin node set %s" % (i, self.index_set())) + return self.one().apply_simple_reflection(i) # don't care about left/right + + @cached_method + def simple_reflections(self): + r""" + Return the simple reflections `(s_i)_{i\in I}` of ``self`` as a family + indexed by :meth:`index_set`. + + .. SEEALSO:: + + - :meth:`simple_reflection` + - :meth:`index_set` + + EXAMPLES: + + For the symmetric group, we recognize the simple transpositions:: + + sage: W = SymmetricGroup(4); W + Symmetric group of order 4! as a permutation group + sage: s = W.simple_reflections() + sage: s + Finite family {1: (1,2), 2: (2,3), 3: (3,4)} + sage: s[1] + (1,2) + sage: s[2] + (2,3) + sage: s[3] + (3,4) + + Here are the simple reflections for a colored symmetric + group and a reflection group:: + + sage: W = ColoredPermutations(1,3) + sage: W.simple_reflections() + Finite family {1: [[0, 0, 0], [2, 1, 3]], 2: [[0, 0, 0], [1, 3, 2]]} + + sage: W = ReflectionGroup((1,1,3), index_set=['a','b']) # optional - gap3 + sage: W.simple_reflections() # optional - gap3 + Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6)} + + This default implementation uses :meth:`.index_set` and + :meth:`.simple_reflection`. + """ + from sage.sets.family import Family + return Family(self.index_set(), self.simple_reflection) + + def number_of_simple_reflections(self): + r""" + Return the number of simple reflections of ``self``. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.number_of_simple_reflections() + 2 + sage: W = ColoredPermutations(2,3) + sage: W.number_of_simple_reflections() + 3 + sage: W = ColoredPermutations(4,3) + sage: W.number_of_simple_reflections() + 3 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.number_of_simple_reflections() # optional - gap3 + 4 + """ + return len(self.index_set()) + + ########################################################################## + # Group generators, etc from simple reflections + ########################################################################## + + def group_generators(self): + r""" + Return the simple reflections of ``self``, as + distinguished group generators. + + .. SEEALSO:: + + - :meth:`simple_reflections` + - :meth:`Groups.ParentMethods.group_generators` + - :meth:`Semigroups.ParentMethods.semigroup_generators` + + EXAMPLES:: + + sage: D10 = FiniteCoxeterGroups().example(10) + sage: D10.group_generators() + Finite family {1: (1,), 2: (2,)} + sage: SymmetricGroup(5).group_generators() + Finite family {1: (1,2), 2: (2,3), 3: (3,4), 4: (4,5)} + + sage: W = ColoredPermutations(3,2) + sage: W.group_generators() + Finite family {1: [[0, 0], + [2, 1]], + 2: [[0, 1], + [1, 2]]} + + The simple reflections are also semigroup generators, even + for an infinite group:: + + sage: W = WeylGroup(["A",2,1]) + sage: W.semigroup_generators() + Finite family {0: [-1 1 1] + [ 0 1 0] + [ 0 0 1], + 1: [ 1 0 0] + [ 1 -1 1] + [ 0 0 1], + 2: [ 1 0 0] + [ 0 1 0] + [ 1 1 -1]} + """ + return self.simple_reflections() + + semigroup_generators = group_generators + + def simple_reflection_orders(self): + """ + Return the orders of the simple reflections. + + EXAMPLES:: + + sage: W = WeylGroup(['B',3]) + sage: W.simple_reflection_orders() + [2, 2, 2] + sage: W = CoxeterGroup(['C',4]) + sage: W.simple_reflection_orders() + [2, 2, 2, 2] + sage: SymmetricGroup(5).simple_reflection_orders() + [2, 2, 2, 2] + sage: C = ColoredPermutations(4, 3) + sage: C.simple_reflection_orders() + [2, 2, 4] + """ + one = self.one() + s = self.simple_reflections() + from sage.rings.all import ZZ + + def mult_order(x): + ct = ZZ.one() + cur = x + while cur != one: + cur *= x + ct += ZZ.one() + return ZZ(ct) + return [mult_order(s[i]) for i in self.index_set()] + + def _an_element_(self): + """ + Implement: :meth:`Sets.ParentMethods.an_element` by + returning the product of the simple reflections (a Coxeter + element). + + EXAMPLES:: + + sage: W = SymmetricGroup(4); W + Symmetric group of order 4! as a permutation group + sage: W.an_element() # indirect doctest + (1,2,3,4) + + For a complex reflection group:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().example(); W + 5-colored permutations of size 3 + sage: W.an_element() + [[1, 0, 0], [3, 1, 2]] + """ + return self.prod(self.simple_reflections()) + + def some_elements(self): + r""" + Implement :meth:`Sets.ParentMethods.some_elements` by + returning some typical elements of ``self``. + + The result is currently composed of the simple reflections + together with the unit and the result of :meth:`an_element`. + + EXAMPLES:: + + sage: W = WeylGroup(['A',3]) + sage: W.some_elements() + [ + [0 1 0 0] [1 0 0 0] [1 0 0 0] [1 0 0 0] [0 0 0 1] + [1 0 0 0] [0 0 1 0] [0 1 0 0] [0 1 0 0] [1 0 0 0] + [0 0 1 0] [0 1 0 0] [0 0 0 1] [0 0 1 0] [0 1 0 0] + [0 0 0 1], [0 0 0 1], [0 0 1 0], [0 0 0 1], [0 0 1 0] + ] + + sage: W = ColoredPermutations(1,4) + sage: W.some_elements() + [[[0, 0, 0, 0], [2, 1, 3, 4]], + [[0, 0, 0, 0], [1, 3, 2, 4]], + [[0, 0, 0, 0], [1, 2, 4, 3]], + [[0, 0, 0, 0], [1, 2, 3, 4]], + [[0, 0, 0, 0], [4, 1, 2, 3]]] + """ + return list(self.simple_reflections()) + [self.one(), self.an_element()] + + ########################################################################## + # Reflections + ########################################################################## + + @abstract_method(optional=True) + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + (1, 2, 3, 4, 5, 6) + sage: W = ReflectionGroup((1,1,4), reflection_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + (1, 3, 'asdf', 7, 9, 11) + sage: W = ReflectionGroup((1,1,4), reflection_index_set=('a','b','c','d','e','f')) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + ('a', 'b', 'c', 'd', 'e', 'f') + """ + + @abstract_method(optional=True) + def reflection(self, i): + r""" + Return the `i`-th reflection of ``self``. + + For `i` in `1,\dots,N`, this gives the `i`-th reflection of + ``self``. + + .. SEEALSO:: + + - :meth:`reflections_index_set` + - :meth:`reflections` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: for i in W.reflection_index_set(): # optional - gap3 + ....: print('%s %s'%(i, W.reflection(i))) # optional - gap3 + 1 (1,7)(2,4)(5,6)(8,10)(11,12) + 2 (1,4)(2,8)(3,5)(7,10)(9,11) + 3 (2,5)(3,9)(4,6)(8,11)(10,12) + 4 (1,8)(2,7)(3,6)(4,10)(9,12) + 5 (1,6)(2,9)(3,8)(5,11)(7,12) + 6 (1,11)(3,10)(4,9)(5,7)(6,12) + """ + + @cached_method + def reflections(self): + r""" + Return a finite family containing the reflections of + ``self``, indexed by :meth:`reflection_index_set`. + + .. SEEALSO:: + + - :meth:`reflection` + - :meth:`reflections_index_set` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: reflections = W.reflections() # optional - gap3 + sage: for index in sorted(reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, reflections[index])) # optional - gap3 + 1 (1,4)(2,3)(5,6) + 2 (1,3)(2,5)(4,6) + 3 (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) # optional - gap3 + sage: reflections = W.reflections() # optional - gap3 + sage: for index in sorted(reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, reflections[index])) # optional - gap3 + a (1,4)(2,3)(5,6) + b (1,3)(2,5)(4,6) + c (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((3,1,1)) # optional - gap3 + sage: reflections = W.reflections() # optional - gap3 + sage: for index in sorted(reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, reflections[index])) # optional - gap3 + 1 (1,2,3) + 2 (1,3,2) + + sage: W = ReflectionGroup((1,1,3), (3,1,2)) # optional - gap3 + sage: reflections = W.reflections() # optional - gap3 + sage: for index in sorted(reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, reflections[index])) # optional - gap3 + 1 (1,6)(2,5)(7,8) + 2 (1,5)(2,7)(6,8) + 3 (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30) + 4 (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30) + 5 (1,7)(2,6)(5,8) + 6 (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26) + 7 (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29) + 8 (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28) + 9 (3,15,9)(4,16,10)(12,23,17)(14,24,18)(20,29,25)(21,26,22)(27,30,28) + 10 (4,27,21)(10,28,22)(11,19,13)(12,20,14)(16,30,26)(17,25,18)(23,29,24) + """ + from sage.sets.family import Family + return Family(self.reflection_index_set(), self.reflection) + + ########################################################################## + # distinguished reflections + ########################################################################## + + @abstract_method(optional=True) + def hyperplane_index_set(self): + r""" + Return the index set of the distinguished reflections of ``self``. + + This is also the index set of the reflection hyperplanes + of ``self``, hence the name. This name is slightly abusive + since the concept of reflection hyperplanes is not defined + for all generalized Coxeter groups. However for all + practical purposes this is only used for complex + reflection groups, and there this is the desirable name. + + .. SEEALSO:: + + - :meth:`distinguished_reflection` + - :meth:`distinguished_reflections` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + (1, 2, 3, 4, 5, 6) + sage: W = ReflectionGroup((1,1,4), hyperplane_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + (1, 3, 'asdf', 7, 9, 11) + sage: W = ReflectionGroup((1,1,4), hyperplane_index_set=('a','b','c','d','e','f')) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + ('a', 'b', 'c', 'd', 'e', 'f') + """ + + @abstract_method(optional=True) + def distinguished_reflection(self, i): + r""" + Return the `i`-th distinguished reflection of ``self``. + + INPUT: + + - ``i`` -- an element of the index set of the distinguished reflections. + + .. SEEALSO:: + + - :meth:`distinguished_reflections` + - :meth:`hyperplane_index_set` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4), hyperplane_index_set=('a','b','c','d','e','f')) # optional - gap3 + sage: for i in W.hyperplane_index_set(): # optional - gap3 + ....: print('%s %s'%(i, W.distinguished_reflection(i))) # optional - gap3 + a (1,7)(2,4)(5,6)(8,10)(11,12) + b (1,4)(2,8)(3,5)(7,10)(9,11) + c (2,5)(3,9)(4,6)(8,11)(10,12) + d (1,8)(2,7)(3,6)(4,10)(9,12) + e (1,6)(2,9)(3,8)(5,11)(7,12) + f (1,11)(3,10)(4,9)(5,7)(6,12) + """ + + @cached_method + def distinguished_reflections(self): + r""" + Return a finite family containing the distinguished + reflections of ``self``, indexed by + :meth:`hyperplane_index_set`. + + A *distinguished reflection* is a conjugate of a simple + reflection. For a Coxeter group, reflections and + distinguished reflections coincide. For a Complex + reflection groups this is a reflection acting on the + complement of the fixed hyperplane `H` as + `\operatorname{exp}(2 \pi i / n)`, where `n` is the order + of the reflection subgroup fixing `H`. + + .. SEEALSO:: + + - :meth:`distinguished_reflection` + - :meth:`hyperplane_index_set` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: distinguished_reflections = W.distinguished_reflections() # optional - gap3 + sage: for index in sorted(distinguished_reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, distinguished_reflections[index])) # optional - gap3 + 1 (1,4)(2,3)(5,6) + 2 (1,3)(2,5)(4,6) + 3 (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) # optional - gap3 + sage: distinguished_reflections = W.distinguished_reflections() # optional - gap3 + sage: for index in sorted(distinguished_reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, distinguished_reflections[index])) # optional - gap3 + a (1,4)(2,3)(5,6) + b (1,3)(2,5)(4,6) + c (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((3,1,1)) # optional - gap3 + sage: distinguished_reflections = W.distinguished_reflections() # optional - gap3 + sage: for index in sorted(distinguished_reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, distinguished_reflections[index])) # optional - gap3 + 1 (1,2,3) + + sage: W = ReflectionGroup((1,1,3), (3,1,2)) # optional - gap3 + sage: distinguished_reflections = W.distinguished_reflections() # optional - gap3 + sage: for index in sorted(distinguished_reflections.keys()): # optional - gap3 + ....: print('%s %s'%(index, distinguished_reflections[index])) # optional - gap3 + 1 (1,6)(2,5)(7,8) + 2 (1,5)(2,7)(6,8) + 3 (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30) + 4 (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30) + 5 (1,7)(2,6)(5,8) + 6 (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26) + 7 (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29) + 8 (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28) + """ + from sage.sets.family import Family + return Family(self.hyperplane_index_set(), self.distinguished_reflection) + + ########################################################################## + # from_reduced_word + ########################################################################## + + def from_reduced_word(self, word, word_type='simple'): + r""" + Return an element of ``self`` from its (reduced) word. + + INPUT: + + - ``word`` -- a list (or iterable) of elements of the + index set of ``self`` (resp. of the distinguished + or of all reflections) + - ``word_type`` -- (optional, default: ``'simple'``): + either ``'simple'``, ``'distinguished'``, or ``'all'`` + + If ``word`` is `[i_1,i_2,\ldots,i_k]`, then this returns + the corresponding product of simple reflections + `s_{i_1} s_{i_2} \cdots s_{i_k}`. + + If ``word_type`` is ``'distinguished'`` (resp. ``'all'``), + then the product of the distinguished reflections (resp. all + reflections) is returned. + + .. NOTE:: + + The main use case is for constructing elements from + reduced words, hence the name of this method. + However, the input word need *not* be reduced. + + .. SEEALSO:: + + - :meth:`index_set` + - :meth:`reflections_index_set` + - :meth:`hyperplane_index_set` + - :meth:`~ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflections` + - :meth:`~CoxeterGroup.ElementMethods.reduced_word` + - :meth:`~CoxeterGroup.ParentMethods._test_reduced_word` + + EXAMPLES:: + + sage: W = CoxeterGroups().example() + sage: W + The symmetric group on {0, ..., 3} + sage: s = W.simple_reflections() + sage: W.from_reduced_word([0,2,0,1]) + (0, 3, 1, 2) + sage: W.from_reduced_word((0,2,0,1)) + (0, 3, 1, 2) + sage: s[0]*s[2]*s[0]*s[1] + (0, 3, 1, 2) + + We now experiment with the the different values for + ``word_type`` for the colored symmetric group:: + + sage: W = ColoredPermutations(1,4) + sage: W.from_reduced_word([1,2,1,2,1,2]) + [[0, 0, 0, 0], [1, 2, 3, 4]] + + sage: W.from_reduced_word([1, 2, 3]).reduced_word() + [1, 2, 3] + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.from_reduced_word([1,2,3], word_type='all').reduced_word() # optional - gap3 + [1, 2, 3] + + sage: W.from_reduced_word([1,2,3], word_type='all').reduced_word_in_reflections() # optional - gap3 + [1, 2, 3] + + sage: W.from_reduced_word([1,2,3]).reduced_word_in_reflections() # optional - gap3 + [1, 2, 3] + + TESTS:: + + sage: W=WeylGroup(['E',6]) + sage: W.from_reduced_word([2,3,4,2]) + [ 0 1 0 0 0 0 0 0] + [ 0 0 -1 0 0 0 0 0] + [-1 0 0 0 0 0 0 0] + [ 0 0 0 1 0 0 0 0] + [ 0 0 0 0 1 0 0 0] + [ 0 0 0 0 0 1 0 0] + [ 0 0 0 0 0 0 1 0] + [ 0 0 0 0 0 0 0 1] + """ + if word_type == 'simple': + return self.one().apply_simple_reflections(word) + else: + return self.one().apply_reflections(word, word_type=word_type) + + ########################################################################## + # Irreducible components + ########################################################################## + + def irreducible_component_index_sets(self): + r""" + Return a list containing the index sets of the irreducible components of + ``self`` as finite reflection groups. + + EXAMPLES:: + + sage: W = ReflectionGroup([1,1,3], [3,1,3], 4); W # optional - gap3 + Reducible complex reflection group of rank 7 and type A2 x G(3,1,3) x ST4 + sage: sorted(W.irreducible_component_index_sets()) # optional - gap3 + [[1, 2], [3, 4, 5], [6, 7]] + + ALGORITHM: + + Take the connected components of the graph on the + index set with edges (i,j) where s[i] and s[j] don't + commute. + """ + I = self.index_set() + s = self.simple_reflections() + from sage.graphs.graph import Graph + G = Graph([I, + [[i,j] + for i,j in itertools.combinations(I,2) + if s[i]*s[j] != s[j]*s[i] ]], + format="vertices_and_edges") + return G.connected_components() + + @abstract_method(optional=True) + def irreducible_components(self): + r""" + Return the irreducible components of ``self`` as finite + reflection groups. + + EXAMPLES:: + + sage: W = ReflectionGroup([1,1,3], [3,1,3], 4) # optional - gap3 + sage: W.irreducible_components() # optional - gap3 + [Irreducible real reflection group of rank 2 and type A2, + Irreducible complex reflection group of rank 3 and type G(3,1,3), + Irreducible complex reflection group of rank 2 and type ST4] + """ + # TODO: provide a default implementation using the above and parabolic subgroups + + def number_of_irreducible_components(self): + r""" + Return the number of irreducible components of ``self``. + + EXAMPLES:: + + sage: SymmetricGroup(3).number_of_irreducible_components() + 1 + + sage: ColoredPermutations(1,3).number_of_irreducible_components() + 1 + + sage: ReflectionGroup((1,1,3),(2,1,3)).number_of_irreducible_components() # optional - gap3 + 2 + + TESTS:: + + sage: SymmetricGroup(3).number_of_irreducible_components.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + return len(self.irreducible_component_index_sets()) + + def is_irreducible(self): + r""" + Return ``True`` if ``self`` is irreducible. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3); W + 1-colored permutations of size 3 + sage: W.is_irreducible() + True + + sage: W = ReflectionGroup((1,1,3),(2,1,3)); W # optional - gap3 + Reducible real reflection group of rank 5 and type A2 x B3 + sage: W.is_irreducible() # optional - gap3 + False + """ + return self.number_of_irreducible_components() == 1 + + def is_reducible(self): + r""" + Return ``True`` if ``self`` is not irreducible. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3); W + 1-colored permutations of size 3 + sage: W.is_reducible() + False + + sage: W = ReflectionGroup((1,1,3), (2,1,3)); W # optional - gap3 + Reducible real reflection group of rank 5 and type A2 x B3 + sage: W.is_reducible() # optional - gap3 + True + """ + return not self.is_irreducible() + + + class ElementMethods: + def apply_simple_reflection_left(self, i): + r""" + Return ``self`` multiplied by the simple reflection ``s[i]`` + on the left. + + This low level method is used intensively. Coxeter groups + are encouraged to override this straightforward + implementation whenever a faster approach exists. + + EXAMPLES:: + + sage: W = CoxeterGroups().example() + sage: w = W.an_element(); w + (1, 2, 3, 0) + sage: w.apply_simple_reflection_left(0) + (0, 2, 3, 1) + sage: w.apply_simple_reflection_left(1) + (2, 1, 3, 0) + sage: w.apply_simple_reflection_left(2) + (1, 3, 2, 0) + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().example() + sage: w = W.an_element(); w + [[1, 0, 0], [3, 1, 2]] + sage: w.apply_simple_reflection_left(1) + [[0, 1, 0], [1, 3, 2]] + sage: w.apply_simple_reflection_left(2) + [[1, 0, 0], [3, 2, 1]] + sage: w.apply_simple_reflection_left(3) + [[1, 0, 1], [3, 1, 2]] + + TESTS:: + + sage: w.apply_simple_reflection_left.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + s = self.parent().simple_reflections() + return s[i] * self + + def apply_simple_reflection_right(self, i): + """ + Return ``self`` multiplied by the simple reflection ``s[i]`` + on the right. + + This low level method is used intensively. Coxeter groups + are encouraged to override this straightforward + implementation whenever a faster approach exists. + + EXAMPLES:: + + sage: W=CoxeterGroups().example() + sage: w = W.an_element(); w + (1, 2, 3, 0) + sage: w.apply_simple_reflection_right(0) + (2, 1, 3, 0) + sage: w.apply_simple_reflection_right(1) + (1, 3, 2, 0) + sage: w.apply_simple_reflection_right(2) + (1, 2, 0, 3) + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().example() + sage: w = W.an_element(); w + [[1, 0, 0], [3, 1, 2]] + sage: w.apply_simple_reflection_right(1) + [[1, 0, 0], [3, 2, 1]] + sage: w.apply_simple_reflection_right(2) + [[1, 0, 0], [2, 1, 3]] + sage: w.apply_simple_reflection_right(3) + [[2, 0, 0], [3, 1, 2]] + + TESTS:: + + sage: w.apply_simple_reflection_right.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + s = self.parent().simple_reflections() + return self * s[i] + + def apply_simple_reflection(self, i, side='right'): + """ + Return ``self`` multiplied by the simple reflection ``s[i]``. + + INPUT: + + - ``i`` -- an element of the index set + - ``side`` -- (default: ``"right"``) ``"left"`` or ``"right"`` + + This default implementation simply calls + :meth:`apply_simple_reflection_left` or + :meth:`apply_simple_reflection_right`. + + EXAMPLES:: + + sage: W = CoxeterGroups().example() + sage: w = W.an_element(); w + (1, 2, 3, 0) + sage: w.apply_simple_reflection(0, side = "left") + (0, 2, 3, 1) + sage: w.apply_simple_reflection(1, side = "left") + (2, 1, 3, 0) + sage: w.apply_simple_reflection(2, side = "left") + (1, 3, 2, 0) + + sage: w.apply_simple_reflection(0, side = "right") + (2, 1, 3, 0) + sage: w.apply_simple_reflection(1, side = "right") + (1, 3, 2, 0) + sage: w.apply_simple_reflection(2, side = "right") + (1, 2, 0, 3) + + By default, ``side`` is ``"right"``:: + + sage: w.apply_simple_reflection(0) + (2, 1, 3, 0) + + Some tests with a complex reflection group:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().example(); W + 5-colored permutations of size 3 + sage: w = W.an_element(); w + [[1, 0, 0], [3, 1, 2]] + sage: w.apply_simple_reflection(1, side="left") + [[0, 1, 0], [1, 3, 2]] + sage: w.apply_simple_reflection(2, side="left") + [[1, 0, 0], [3, 2, 1]] + sage: w.apply_simple_reflection(3, side="left") + [[1, 0, 1], [3, 1, 2]] + + sage: w.apply_simple_reflection(1, side="right") + [[1, 0, 0], [3, 2, 1]] + sage: w.apply_simple_reflection(2, side="right") + [[1, 0, 0], [2, 1, 3]] + sage: w.apply_simple_reflection(3, side="right") + [[2, 0, 0], [3, 1, 2]] + + TESTS:: + + sage: w.apply_simple_reflection_right.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + if side == 'right': + return self.apply_simple_reflection_right(i) + else: + return self.apply_simple_reflection_left(i) + + def apply_simple_reflections(self, word, side='right', type='simple'): + r""" + Return the result of the (left/right) multiplication of + ``self`` by ``word``. + + INPUT: + + - ``word`` -- a sequence of indices of simple reflections + - ``side`` -- (default: ``'right'``) indicates multiplying + from left or right + + This is a specialized implementation of + :meth:`apply_reflections` for the simple reflections. The + rationale for its existence are: + + - It can take advantage of ``apply_simple_reflection``, + which often is less expensive than computing a product. + + - It reduced burden on implementations that would want to + provide an optimized version of this method. + + EXAMPLES:: + + sage: W = CoxeterGroups().example() + sage: w = W.an_element(); w + (1, 2, 3, 0) + sage: w.apply_simple_reflections([0,1]) + (2, 3, 1, 0) + sage: w + (1, 2, 3, 0) + sage: w.apply_simple_reflections([0,1],side='left') + (0, 1, 3, 2) + """ + for i in word: + self = self.apply_simple_reflection(i, side) + return self + + def apply_reflections(self, word, side='right', word_type='all'): + r""" + Return the result of the (left/right) multiplication of + ``self`` by ``word``. + + INPUT: + + - ``word`` -- a sequence of indices of reflections + - ``side`` -- (default: ``'right'``) indicates multiplying + from left or right + - ``word_type`` -- (optional, default: ``'all'``): + either ``'simple'``, ``'distinguished'``, or ``'all'`` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.one().apply_reflections([1]) # optional - gap3 + (1,4)(2,3)(5,6) + sage: W.one().apply_reflections([2]) # optional - gap3 + (1,3)(2,5)(4,6) + sage: W.one().apply_reflections([2,1]) # optional - gap3 + (1,2,6)(3,4,5) + + + sage: W = CoxeterGroups().example() + sage: w = W.an_element(); w + (1, 2, 3, 0) + sage: w.apply_reflections([0,1], word_type='simple') + (2, 3, 1, 0) + sage: w + (1, 2, 3, 0) + sage: w.apply_reflections([0,1], side='left', word_type='simple') + (0, 1, 3, 2) + + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.one().apply_reflections([1], word_type='distinguished') # optional - gap3 + (1,4)(2,3)(5,6) + sage: W.one().apply_reflections([2], word_type='distinguished') # optional - gap3 + (1,3)(2,5)(4,6) + sage: W.one().apply_reflections([3], word_type='distinguished') # optional - gap3 + (1,5)(2,4)(3,6) + sage: W.one().apply_reflections([2,1], word_type='distinguished') # optional - gap3 + (1,2,6)(3,4,5) + + sage: W = ReflectionGroup((1,1,3), hyperplane_index_set=['A','B','C']); W # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: W.one().apply_reflections(['A'], word_type='distinguished') # optional - gap3 + (1,4)(2,3)(5,6) + """ + if word_type == 'simple': + reflections = self.parent().simple_reflections() + elif word_type == 'distinguished': + reflections = self.parent().distinguished_reflections() + else: + reflections = self.parent().reflections() + if side == 'left': + for i in word: + self = reflections[i] * self + else: + for i in word: + self = self * reflections[i] + return self + + def _mul_(self, other): + r""" + Return the product of ``self`` and ``other`` + + This default implementation computes a reduced word of + ``other`` using :meth:`reduced_word`, and applies the + corresponding simple reflections on ``self`` using + :meth:`apply_simple_reflections`. + + EXAMPLES:: + + sage: W = FiniteCoxeterGroups().example(); W + The 5-th dihedral group of order 10 + sage: w = W.an_element() + sage: w + (1, 2) + sage: w._mul_(w) + (1, 2, 1, 2) + sage: w._mul_(w)._mul_(w) + (2, 1, 2, 1) + + This method is called when computing ``self * other``:: + + sage: w * w + (1, 2, 1, 2) + + TESTS:: + + sage: w._mul_.__module__ + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' + """ + return self.apply_simple_reflections(other.reduced_word()) + + def inverse(self): + """ + Return the inverse of ``self``. + + EXAMPLES:: + + sage: W = WeylGroup(['B',7]) + sage: w = W.an_element() + sage: u = w.inverse() + sage: u == ~w + True + sage: u * w == w * u + True + sage: u * w + [1 0 0 0 0 0 0] + [0 1 0 0 0 0 0] + [0 0 1 0 0 0 0] + [0 0 0 1 0 0 0] + [0 0 0 0 1 0 0] + [0 0 0 0 0 1 0] + [0 0 0 0 0 0 1] + """ + return self.parent().one().apply_simple_reflections(self.reduced_word_reverse_iterator()) + + __invert__ = inverse + + def apply_conjugation_by_simple_reflection(self, i): + r""" + Conjugate ``self`` by the ``i``-th simple reflection. + + EXAMPLES:: + + sage: W = WeylGroup(['A',3]) + sage: w = W.from_reduced_word([3,1,2,1]) + sage: w.apply_conjugation_by_simple_reflection(1).reduced_word() + [3, 2] + """ + return self.apply_simple_reflection(i).apply_simple_reflection(i, side='left') + + @abstract_method(optional=True) + def reflection_length(self): + r""" + Return the reflection length of ``self``. + + This is the minimal length of a factorization of ``self`` + into reflections. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1] + + sage: W = ReflectionGroup((2,1,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 1, 1, 2, 2, 2] + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + + sage: W = ReflectionGroup((2,2,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 2] + """ + + def is_reflection(self): + r""" + Return whether ``self`` is a reflection. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: [t.is_reflection() for t in W.reflections()] # optional - gap3 + [True, True, True, True, True, True] + sage: len([t for t in W.reflections() if t.is_reflection()]) # optional - gap3 + 6 + + sage: W = ReflectionGroup((2,1,3)) # optional - gap3 + sage: [t.is_reflection() for t in W.reflections()] # optional - gap3 + [True, True, True, True, True, True, True, True, True] + sage: len([t for t in W.reflections() if t.is_reflection()]) # optional - gap3 + 9 + """ + return self.reflection_length() == 1 + + + class Irreducible(CategoryWithAxiom): + class ParentMethods: + def irreducible_components(self): + r""" + Return a list containing all irreducible components of + ``self`` as finite reflection groups. + + EXAMPLES:: + + sage: W = ColoredPermutations(4, 3) + sage: W.irreducible_components() + [4-colored permutations of size 3] + """ + return [self] + diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index a6e3d32d9f8..9e8fa948ff2 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -13,13 +13,12 @@ from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.misc.lazy_import import LazyImport -from sage.misc.abstract_method import abstract_method from sage.misc.constant_function import ConstantFunction from sage.misc.misc import attrcall, uniq from sage.categories.category_singleton import Category_singleton -from sage.categories.groups import Groups from sage.categories.enumerated_sets import EnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups from sage.structure.element import have_same_parent from sage.misc.flatten import flatten from copy import copy @@ -41,7 +40,7 @@ class CoxeterGroups(Category_singleton): sage: C = CoxeterGroups(); C Category of coxeter groups sage: C.super_categories() - [Category of finitely generated groups] + [Category of generalized coxeter groups] sage: W = C.example(); W The symmetric group on {0, ..., 3} @@ -67,7 +66,11 @@ class CoxeterGroups(Category_singleton): .. TODO:: add a demo of usual computations on Coxeter groups. - .. SEEALSO:: :class:`WeylGroups`, :mod:`sage.combinat.root_system` + .. SEEALSO:: + + - :mod:`sage.combinat.root_system` + - :class:`WeylGroups` + - :class:`GeneralizedCoxeterGroups` .. WARNING:: @@ -81,38 +84,14 @@ class CoxeterGroups(Category_singleton): sage: CoxeterGroups().is_full_subcategory(Groups()) False + sage: from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + sage: CoxeterGroups().is_full_subcategory(GeneralizedCoxeterGroups()) + True TESTS:: - sage: W = CoxeterGroups().example(); TestSuite(W).run(verbose = "True") - running ._test_an_element() . . . pass - running ._test_associativity() . . . pass - running ._test_cardinality() . . . pass - running ._test_category() . . . pass - running ._test_elements() . . . - Running the test suite of self.an_element() - running ._test_category() . . . pass - running ._test_eq() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_pickling() . . . pass - pass - running ._test_elements_eq_reflexive() . . . pass - running ._test_elements_eq_symmetric() . . . pass - running ._test_elements_eq_transitive() . . . pass - running ._test_elements_neq() . . . pass - running ._test_enumerated_set_contains() . . . pass - running ._test_enumerated_set_iter_cardinality() . . . pass - running ._test_enumerated_set_iter_list() . . . pass - running ._test_eq() . . . pass - running ._test_has_descent() . . . pass - running ._test_inverse() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_one() . . . pass - running ._test_pickling() . . . pass - running ._test_prod() . . . pass - running ._test_reduced_word() . . . pass - running ._test_simple_projections() . . . pass - running ._test_some_elements() . . . pass + sage: W = CoxeterGroups().example() + sage: TestSuite(W).run() """ def super_categories(self): @@ -120,67 +99,30 @@ def super_categories(self): EXAMPLES:: sage: CoxeterGroups().super_categories() - [Category of finitely generated groups] + [Category of generalized coxeter groups] """ - return [Groups().FinitelyGenerated()] + return [GeneralizedCoxeterGroups()] - Finite = LazyImport('sage.categories.finite_coxeter_groups', 'FiniteCoxeterGroups') - Algebras = LazyImport('sage.categories.coxeter_group_algebras', 'CoxeterGroupAlgebras') - - class ParentMethods: - - @abstract_method - def index_set(self): - """ - Returns the index set of (the simple reflections of) - ``self``, as a list (or iterable). - - EXAMPLES:: + def additional_structure(self): + r""" + Return ``None``. - sage: W = FiniteCoxeterGroups().example(); W - The 5-th dihedral group of order 10 - sage: W.index_set() - [1, 2] - """ - # return self.simple_reflections().keys() - - def _an_element_(self): - """ - Implements: :meth:`Sets.ParentMethods.an_element` by - returning the product of the simple reflections (a Coxeter - element). - - EXAMPLES:: - - sage: W=CoxeterGroups().example() - sage: W - The symmetric group on {0, ..., 3} - sage: W.an_element() # indirect doctest - (1, 2, 3, 0) + Indeed, all the structure Coxeter groups have in addition to + groups (simple reflections, ...) is already defined in the + super category. - """ - return self.prod(self.simple_reflections()) + .. SEEALSO:: :meth:`Category.additional_structure` - def some_elements(self): - """ - Implements :meth:`Sets.ParentMethods.some_elements` by - returning some typical element of `self`. + EXAMPLES:: - EXAMPLES:: + sage: CoxeterGroups().additional_structure() + """ + return None - sage: W=WeylGroup(['A',3]) - sage: W.some_elements() - [ - [0 1 0 0] [1 0 0 0] [1 0 0 0] [1 0 0 0] [0 0 0 1] - [1 0 0 0] [0 0 1 0] [0 1 0 0] [0 1 0 0] [1 0 0 0] - [0 0 1 0] [0 1 0 0] [0 0 0 1] [0 0 1 0] [0 1 0 0] - [0 0 0 1], [0 0 0 1], [0 0 1 0], [0 0 0 1], [0 0 1 0] - ] - sage: W.order() - 24 - """ - return list(self.simple_reflections()) + [ self.one(), self.an_element() ] + Finite = LazyImport('sage.categories.finite_coxeter_groups', 'FiniteCoxeterGroups') + Algebras = LazyImport('sage.categories.coxeter_group_algebras', 'CoxeterGroupAlgebras') + class ParentMethods: def __iter__(self): r""" Returns an iterator over the elements of this Coxeter group. @@ -300,6 +242,95 @@ def succ(u): return SearchForest((self.one(),), succ, algorithm='breadth', category = default_category.or_subcategory(category)) + @cached_method + def coxeter_element(self): + """ + Return a Coxeter element. + + The result is the product of the simple reflections, in some order. + + .. NOTE:: + + This implementation is shared with well generated + complex reflection groups. It would be nicer to put it + in some joint super category; however, in the current + state of the art, there is none where it's clear that + this is the right construction for obtaining a Coxeter + element. + + In this context, this is an element having a regular + eigenvector (a vector not contained in any reflection + hyperplane of ``self``). + + EXAMPLES:: + + sage: CoxeterGroup(['A', 4]).coxeter_element().reduced_word() + [1, 2, 3, 4] + sage: CoxeterGroup(['B', 4]).coxeter_element().reduced_word() + [1, 2, 3, 4] + sage: CoxeterGroup(['D', 4]).coxeter_element().reduced_word() + [1, 2, 4, 3] + sage: CoxeterGroup(['F', 4]).coxeter_element().reduced_word() + [1, 2, 3, 4] + sage: CoxeterGroup(['E', 8]).coxeter_element().reduced_word() + [1, 3, 2, 4, 5, 6, 7, 8] + sage: CoxeterGroup(['H', 3]).coxeter_element().reduced_word() + [1, 2, 3] + + This method is also used for well generated finite complex + reflection groups:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.coxeter_element().reduced_word() # optional - gap3 + [1, 2, 3] + + sage: W = ReflectionGroup((2,1,4)) # optional - gap3 + sage: W.coxeter_element().reduced_word() # optional - gap3 + [1, 2, 3, 4] + + sage: W = ReflectionGroup((4,1,4)) # optional - gap3 + sage: W.coxeter_element().reduced_word() # optional - gap3 + [1, 2, 3, 4] + + sage: W = ReflectionGroup((4,4,4)) # optional - gap3 + sage: W.coxeter_element().reduced_word() # optional - gap3 + [1, 2, 3, 4] + + TESTS:: + + sage: WeylGroup(['A', 4]).coxeter_element().reduced_word() + [1, 2, 3, 4] + sage: SymmetricGroup(3).coxeter_element() + (1,3,2) + """ + return self.prod(self.simple_reflections()) + + @cached_method + def standard_coxeter_elements(self): + r""" + Return all standard Coxeter elements in ``self``. + + This is the set of all elements in self obtained from any + product of the simple reflections in ``self``. + + .. NOTE:: + + - ``self`` is assumed to be well-generated. + - This works even beyond real reflection groups, but the conjugacy + class is not unique and we only obtain one such class. + + EXAMPLES:: + + sage: W = ReflectionGroup(4) # optional - gap3 + sage: sorted(W.standard_coxeter_elements()) # optional - gap3 + [(1,7,6,12,23,20)(2,8,17,24,9,5)(3,16,10,19,15,21)(4,14,11,22,18,13), + (1,10,4,12,21,22)(2,11,19,24,13,3)(5,15,7,17,16,23)(6,18,8,20,14,9)] + """ + if not self.is_irreducible() or not self.is_well_generated(): + raise ValueError("this method is available for irreducible, well-generated complex reflection groups") + from sage.combinat.permutation import Permutations + return set(self.from_reduced_word(w) for w in Permutations(self._index_set)) + def grassmannian_elements(self, side="right"): """ Return the left or right grassmanian elements of ``self`` @@ -331,58 +362,10 @@ def grassmannian_elements(self, side="right"): return self.weak_order_ideal(attrcall("is_grassmannian", side=side), side=order_side) - def from_reduced_word(self, word): - r""" - INPUT: - - - ``word`` - a list (or iterable) of elements of ``self.index_set()`` - - Returns the group element corresponding to the given - word. Namely, if ``word`` is `[i_1,i_2,\ldots,i_k]`, then - this returns the corresponding product of simple - reflections `s_{i_1} s_{i_2} \cdots s_{i_k}`. - - Note: the main use case is for constructing elements from - reduced words, hence the name of this method. But actually - the input word need *not* be reduced. - - EXAMPLES:: - - sage: W = CoxeterGroups().example() - sage: W - The symmetric group on {0, ..., 3} - sage: s = W.simple_reflections() - sage: W.from_reduced_word([0,2,0,1]) - (0, 3, 1, 2) - sage: W.from_reduced_word((0,2,0,1)) - (0, 3, 1, 2) - sage: s[0]*s[2]*s[0]*s[1] - (0, 3, 1, 2) - - See also :meth:'._test_reduced_word':: - - sage: W._test_reduced_word() - - TESTS:: - - sage: W=WeylGroup(['E',6]) - sage: W.from_reduced_word([2,3,4,2]) - [ 0 1 0 0 0 0 0 0] - [ 0 0 -1 0 0 0 0 0] - [-1 0 0 0 0 0 0 0] - [ 0 0 0 1 0 0 0 0] - [ 0 0 0 0 1 0 0 0] - [ 0 0 0 0 0 1 0 0] - [ 0 0 0 0 0 0 1 0] - [ 0 0 0 0 0 0 0 1] - - """ - return self.one().apply_simple_reflections(word, side = 'right') - def _test_reduced_word(self, **options): """ Runs sanity checks on :meth:'CoxeterGroups.ElementMethods.reduced_word' and - :meth:'.from_reduced_word`. + :meth:`~sage.categories.complex_reflection_or_generalized_coxeter_groups.ComplexReflectionOrGeneralizedCoxeterGroups.ParentMethods.from_reduced_word` EXAMPLES:: @@ -397,101 +380,6 @@ def _test_reduced_word(self, **options): tester.assertEquals(self.from_reduced_word(red), x) tester.assertEquals(self.prod((s[i] for i in red)), x) - def simple_reflection(self, i): - """ - INPUT: - - - ``i`` - an element from the index set. - - Returns the simple reflection `s_i` - - EXAMPLES:: - - sage: W = CoxeterGroups().example() - sage: W - The symmetric group on {0, ..., 3} - sage: W.simple_reflection(1) - (0, 2, 1, 3) - sage: s = W.simple_reflections() - sage: s[1] - (0, 2, 1, 3) - - """ - if not i in self.index_set(): - raise ValueError("%s is not in the Dynkin node set %s"%(i,self.index_set())) - return self.one().apply_simple_reflection(i) # don't care about left/right - - @cached_method - def simple_reflections(self): - r""" - Returns the simple reflections `(s_i)_{i\in I}`, as a family. - - EXAMPLES:: - - sage: W = CoxeterGroups().example() - sage: W - The symmetric group on {0, ..., 3} - sage: s = W.simple_reflections() - sage: s - Finite family {0: (1, 0, 2, 3), 1: (0, 2, 1, 3), 2: (0, 1, 3, 2)} - sage: s[0] - (1, 0, 2, 3) - sage: s[1] - (0, 2, 1, 3) - sage: s[2] - (0, 1, 3, 2) - - - This default implementation uses :meth:`.index_set` and - :meth:`.simple_reflection`. - """ - from sage.sets.family import Family - return Family(self.index_set(), self.simple_reflection) - - @cached_method - def rank(self): - r""" - Return the rank of ``self``. - - EXAMPLES:: - - sage: W = CoxeterGroups().example() - sage: W.rank() - 3 - """ - return len(self.simple_reflections()) - - def group_generators(self): - r""" - Implements :meth:`Groups.ParentMethods.group_generators` - by returning the simple reflections of ``self``. - - EXAMPLES:: - - sage: D10 = FiniteCoxeterGroups().example(10) - sage: D10.group_generators() - Finite family {1: (1,), 2: (2,)} - sage: SymmetricGroup(5).group_generators() - Finite family {1: (1,2), 2: (2,3), 3: (3,4), 4: (4,5)} - - Those give semigroup generators, even for an infinite group:: - - sage: W = WeylGroup(["A",2,1]) - sage: W.semigroup_generators() - Finite family {0: [-1 1 1] - [ 0 1 0] - [ 0 0 1], - 1: [ 1 0 0] - [ 1 -1 1] - [ 0 0 1], - 2: [ 1 0 0] - [ 0 1 0] - [ 1 1 -1]} - """ - return self.simple_reflections() - - semigroup_generators = group_generators - def simple_projection(self, i, side = 'right', length_increasing = True): r""" INPUT: @@ -950,7 +838,7 @@ def is_grassmannian(self, side = "right"): Tests whether ``self`` is Grassmannian, i.e. it has at most one descent on the right (resp. on the left). -v EXAMPLES:: + EXAMPLES:: sage: W = CoxeterGroups().example(); W The symmetric group on {0, ..., 3} @@ -998,8 +886,7 @@ def reduced_word_reverse_iterator(self): Default implementation: recursively remove the first right descent until the identity is reached (see :meth:`.first_descent` and - :meth:`apply_simple_reflection`). - + :meth:`~sage.categories.complex_reflection_or_generalized_coxeter_groups.ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection`). """ while True: i = self.first_descent() @@ -1030,8 +917,8 @@ def reduced_word(self): .. SEEALSO:: - :meth:`.reduced_words`, :meth:`.reduced_word_reverse_iterator`, - :meth:`length`, :meth:`reduced_word_graph` + - :meth:`.reduced_words`, :meth:`.reduced_word_reverse_iterator`, + - :meth:`length`, :meth:`reduced_word_graph` """ result = list(self.reduced_word_reverse_iterator()) return list(reversed(result)) @@ -1199,8 +1086,10 @@ def reduced_word_graph(self): def length(self): r""" - Returns the length of self, that is the minimal length of - a product of simple reflections giving self. + Return the length of ``self``. + + This is the minimal length of + a product of simple reflections giving ``self``. EXAMPLES:: @@ -1232,14 +1121,18 @@ def length(self): def absolute_length(self): """ - Return the absolute length of ``self`` + Return the absolute length of ``self``. The absolute length is the length of the shortest expression of the element as a product of reflections. + For permutations in the symmetric groups, the absolute + length is the size minus the number of its disjoint + cycles. + .. SEEALSO:: - :meth:`absolute_le`. + :meth:`absolute_le` EXAMPLES:: @@ -1247,6 +1140,11 @@ def absolute_length(self): sage: s = W.simple_reflections() sage: (s[1]*s[2]*s[3]).absolute_length() 3 + + sage: W = SymmetricGroup(4) + sage: s = W.simple_reflections() + sage: (s[3]*s[2]*s[1]).absolute_length() + 3 """ M = self.canonical_matrix() return (M - 1).image().dimension() @@ -1264,7 +1162,9 @@ def absolute_le(self, other): This partial order can be used to define noncrossing partitions associated with this Coxeter group. - .. SEEALSO:: :meth:`absolute_length` + .. SEEALSO:: + + :meth:`absolute_length` EXAMPLES:: @@ -1285,6 +1185,32 @@ def absolute_le(self, other): return False return self.absolute_length() + (self.inverse() * other).absolute_length() == other.absolute_length() + def absolute_covers(self): + r""" + Return the list of covers of ``self`` in absolute order. + + .. SEEALSO:: + + :meth:`absolute_length` + + EXAMPLES:: + + sage: W = WeylGroup(["A", 3]) + sage: s = W.simple_reflections() + sage: w0 = s[1] + sage: w1 = s[1]*s[2]*s[3] + sage: w0.absolute_covers() + [ + [0 0 1 0] [0 1 0 0] [0 0 0 1] [0 1 0 0] [0 1 0 0] + [1 0 0 0] [1 0 0 0] [1 0 0 0] [0 0 1 0] [0 0 0 1] + [0 1 0 0] [0 0 0 1] [0 0 1 0] [1 0 0 0] [0 0 1 0] + [0 0 0 1], [0 0 1 0], [0 1 0 0], [0 0 0 1], [1 0 0 0] + ] + """ + W = self.parent() + return [self * t for t in W.reflections() + if self.absolute_length() < (self * t).absolute_length()] + def canonical_matrix(self): r""" Return the matrix of ``self`` in the canonical faithful @@ -1454,198 +1380,6 @@ def succ(u_v): yield (u1, s[i]*v) return SearchForest(((W.one(), self),), succ, category = FiniteEnumeratedSets()) - # TODO: standardize / cleanup - def apply_simple_reflections(self, word, side = 'right'): - """ - INPUT: - - - ``word`` -- A sequence of indices of Coxeter generators - - ``side`` -- Indicates multiplying from left or right - - Returns the result of the (left/right) multiplication of - word to self. ``self`` is not changed. - - EXAMPLES:: - - sage: W=CoxeterGroups().example() - sage: w=W.an_element(); w - (1, 2, 3, 0) - sage: w.apply_simple_reflections([0,1]) - (2, 3, 1, 0) - sage: w - (1, 2, 3, 0) - sage: w.apply_simple_reflections([0,1],side='left') - (0, 1, 3, 2) - """ - for i in word: - self = self.apply_simple_reflection(i, side) - return self - - - def apply_simple_reflection_left(self, i): - """ - Returns ``self`` multiplied by the simple reflection ``s[i]`` on the left - - This low level method is used intensively. Coxeter groups - are encouraged to override this straightforward - implementation whenever a faster approach exists. - - EXAMPLES:: - - sage: W=CoxeterGroups().example() - sage: w = W.an_element(); w - (1, 2, 3, 0) - sage: w.apply_simple_reflection_left(0) - (0, 2, 3, 1) - sage: w.apply_simple_reflection_left(1) - (2, 1, 3, 0) - sage: w.apply_simple_reflection_left(2) - (1, 3, 2, 0) - - TESTS:: - - sage: w.apply_simple_reflection_left.__module__ - 'sage.categories.coxeter_groups' - """ - s = self.parent().simple_reflections() - return s[i] * self - - def apply_simple_reflection_right(self, i): - """ - Returns ``self`` multiplied by the simple reflection ``s[i]`` on the right - - This low level method is used intensively. Coxeter groups - are encouraged to override this straightforward - implementation whenever a faster approach exists. - - EXAMPLES:: - - sage: W=CoxeterGroups().example() - sage: w = W.an_element(); w - (1, 2, 3, 0) - sage: w.apply_simple_reflection_right(0) - (2, 1, 3, 0) - sage: w.apply_simple_reflection_right(1) - (1, 3, 2, 0) - sage: w.apply_simple_reflection_right(2) - (1, 2, 0, 3) - - TESTS:: - - sage: w.apply_simple_reflection_right.__module__ - 'sage.categories.coxeter_groups' - """ - s = self.parent().simple_reflections() - return self * s[i] - - def apply_simple_reflection(self, i, side = 'right'): - """ - Returns ``self`` multiplied by the simple reflection ``s[i]`` - - INPUT: - - - ``i`` -- an element of the index set - - ``side`` -- "left" or "right" (default: "right") - - This default implementation simply calls - :meth:`apply_simple_reflection_left` or - :meth:`apply_simple_reflection_right`. - - EXAMPLES:: - - sage: W=CoxeterGroups().example() - sage: w = W.an_element(); w - (1, 2, 3, 0) - sage: w.apply_simple_reflection(0, side = "left") - (0, 2, 3, 1) - sage: w.apply_simple_reflection(1, side = "left") - (2, 1, 3, 0) - sage: w.apply_simple_reflection(2, side = "left") - (1, 3, 2, 0) - - sage: w.apply_simple_reflection(0, side = "right") - (2, 1, 3, 0) - sage: w.apply_simple_reflection(1, side = "right") - (1, 3, 2, 0) - sage: w.apply_simple_reflection(2, side = "right") - (1, 2, 0, 3) - - By default, ``side`` is "right":: - - sage: w.apply_simple_reflection(0) - (2, 1, 3, 0) - - TESTS:: - - sage: w.apply_simple_reflection_right.__module__ - 'sage.categories.coxeter_groups' - """ - if side == 'right': - return self.apply_simple_reflection_right(i) - else: - return self.apply_simple_reflection_left(i) - - def _mul_(self, other): - r""" - Returns the product of ``self`` and ``other`` - - This default implementation computes a reduced word of - ``other`` using :meth:`reduced_word`, and applies the - corresponding simple reflections on ``self`` using - :meth:`apply_simple_reflections`. - - EXAMPLES:: - - sage: W = FiniteCoxeterGroups().example(); W - The 5-th dihedral group of order 10 - sage: w = W.an_element() - sage: w - (1, 2) - sage: w._mul_(w) - (1, 2, 1, 2) - sage: w._mul_(w)._mul_(w) - (2, 1, 2, 1) - - This method is called when computing ``self*other``:: - - sage: w * w - (1, 2, 1, 2) - - TESTS:: - - sage: w._mul_.__module__ - 'sage.categories.coxeter_groups' - """ - return self.apply_simple_reflections(other.reduced_word()) - - def inverse(self): - """ - Returns the inverse of self - - EXAMPLES:: - - sage: W=WeylGroup(['B',7]) - sage: w=W.an_element() - sage: u=w.inverse() - sage: u==~w - True - sage: u*w==w*u - True - sage: u*w - [1 0 0 0 0 0 0] - [0 1 0 0 0 0 0] - [0 0 1 0 0 0 0] - [0 0 0 1 0 0 0] - [0 0 0 0 1 0 0] - [0 0 0 0 0 1 0] - [0 0 0 0 0 0 1] - - """ - - return self.parent().one().apply_simple_reflections(self.reduced_word_reverse_iterator()) - - __invert__ = inverse - @cached_in_parent_method def bruhat_lower_covers(self): """ @@ -1863,8 +1597,9 @@ def bruhat_le(self, other): Complexity: `O(l * c)`, where `l` is the minimum of the lengths of `u` and of `v`, and `c` is the cost of the low level methods :meth:`first_descent`, :meth:`has_descent`, - :meth:`apply_simple_reflection`, etc. Those are typically - `O(n)`, where `n` is the rank of the Coxeter group. + :meth:`~sage.categories.complex_reflection_or_generalized_coxeter_groups.ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection`), + etc. Those are typically `O(n)`, where `n` is the rank of the + Coxeter group. TESTS: @@ -1885,7 +1620,6 @@ def bruhat_le(self, other): True sage: all( P.is_lequal(u,v) == Q.is_lequal(u,v) for u in W for v in W) # long time (9s) True - """ if not have_same_parent(self, other): raise TypeError("%s and %s do not have the same parent"%(self, other)) @@ -1938,8 +1672,9 @@ def weak_le(self, other, side = 'right'): Complexity: `O(l * c)`, where `l` is the minimum of the lengths of `u` and of `v`, and `c` is the cost of the low level methods :meth:`first_descent`, :meth:`has_descent`, - :meth:`apply_simple_reflection`. Those are typically - `O(n)`, where `n` is the rank of the Coxeter group. + :meth:`~sage.categories.complex_reflection_or_generalized_coxeter_groups.ComplexReflectionOrGeneralizedCoxeterGroups.ElementMethods.apply_simple_reflection`), + etc. Those are typically `O(n)`, where `n` is the rank of the + Coxeter group. We now run consistency tests with permutations:: @@ -1947,9 +1682,9 @@ def weak_le(self, other, side = 'right'): sage: P4 = Permutations(4) sage: def P4toW(w): return W.from_reduced_word(w.reduced_word()) sage: for u in P4: # long time (5s on sage.math, 2011) - ... for v in P4: - ... assert u.permutohedron_lequal(v) == P4toW(u).weak_le(P4toW(v)) - ... assert u.permutohedron_lequal(v, side='left') == P4toW(u).weak_le(P4toW(v), side='left') + ....: for v in P4: + ....: assert u.permutohedron_lequal(v) == P4toW(u).weak_le(P4toW(v)) + ....: assert u.permutohedron_lequal(v, side='left') == P4toW(u).weak_le(P4toW(v), side='left') """ if not have_same_parent(self, other): raise TypeError("%s and %s do not have the same parent"%(self,other)) @@ -2241,7 +1976,7 @@ def deodhar_factor_element(self, w, index_set): REFERENCES: - .. [Deodhar] V. Deodhar, A splitting criterion for the Bruhat orderings on Coxeter groups. Comm. Algebra, 15:1889-1894, 1987. + .. [Deodhar] \V. Deodhar, A splitting criterion for the Bruhat orderings on Coxeter groups. Comm. Algebra, 15:1889-1894, 1987. """ @@ -2337,21 +2072,6 @@ def deodhar_lift_down(self, w, index_set): dsp = wmin.deodhar_factor_element(vmin,index_set) return wmin * dsp.apply_demazure_product(vJ) - def apply_conjugation_by_simple_reflection(self, i): - r""" - Conjugates ``self`` by the ``i``-th simple reflection. - - EXAMPLES:: - - sage: W = WeylGroup(['A',3]) - sage: w = W.from_reduced_word([3,1,2,1]) - sage: w.apply_conjugation_by_simple_reflection(1).reduced_word() - [3, 2] - - """ - - return (self.apply_simple_reflection(i)).apply_simple_reflection(i,side='left') - @cached_in_parent_method def inversions_as_reflections(self): r""" @@ -2453,3 +2173,4 @@ def upper_covers(self, side = 'right', index_set = None): [[1, 2, 3]] """ return self.weak_covers(side = side, index_set = index_set, positive = True) + diff --git a/src/sage/categories/discrete_valuation.py b/src/sage/categories/discrete_valuation.py index 0147303f0ea..2cd61e3705c 100644 --- a/src/sage/categories/discrete_valuation.py +++ b/src/sage/categories/discrete_valuation.py @@ -11,7 +11,7 @@ from sage.misc.abstract_method import abstract_method from sage.categories.category_singleton import Category_singleton -from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.fields import Fields class DiscreteValuationRings(Category_singleton): @@ -29,9 +29,9 @@ def super_categories(self): EXAMPLES:: sage: DiscreteValuationRings().super_categories() - [Category of principal ideal domains] + [Category of euclidean domains] """ - return [PrincipalIdealDomains()] + return [EuclideanDomains()] class ParentMethods: @abstract_method @@ -77,6 +77,51 @@ def valuation(self): 2 """ + def euclidean_degree(self): + """ + Return the Euclidean degree of this element. + + TESTS:: + + sage: R. = GF(5)[[]] + sage: (q^3).euclidean_degree() + 3 + sage: R(0).euclidean_degree() + Traceback (most recent call last): + ... + ValueError: Euclidean degree of the zero element not defined + + """ + if not self: + raise ValueError("Euclidean degree of the zero element not defined") + return self.valuation() + + def quo_rem(self, other): + """ + Return the quotient and remainder for Euclidean division + of ``self`` by ``other``. + + TESTS:: + + sage: R. = GF(5)[[]] + sage: (q^2 + q).quo_rem(q) + (1 + q, 0) + sage: (q + 1).quo_rem(q^2) + (0, 1 + q) + sage: q.quo_rem(0) + Traceback (most recent call last): + ... + ZeroDivisionError: Euclidean division by the zero element not defined + + """ + if not other: + raise ZeroDivisionError("Euclidean division by the zero element not defined") + P = self.parent() + if self.valuation() >= other.valuation(): + return P(self / other), P.zero() + else: + return P.zero(), self + def is_unit(self): """ Return True if self is invertible. diff --git a/src/sage/categories/enumerated_sets.py b/src/sage/categories/enumerated_sets.py index 72b937db7aa..3dea3a60a33 100644 --- a/src/sage/categories/enumerated_sets.py +++ b/src/sage/categories/enumerated_sets.py @@ -217,7 +217,7 @@ def __iter__(self): [5, 6, 7] """ - #Check to see if .first() and .next() are overridden in the subclass + # Check if .first() and .next(x) are overridden in the subclass if ( self.first != self._first_from_iterator and self.next != self._next_from_iterator ): return self._iterator_from_next() @@ -703,7 +703,8 @@ def rank(self): sage: F = FiniteSemigroups().example(('a','b','c')) sage: L = list(F); L - ['a', 'c', 'ac', 'b', 'ba', 'bc', 'cb', 'ca', 'bca', 'ab', 'bac', 'cab', 'acb', 'cba', 'abc'] + ['a', 'b', 'c', 'ac', 'ab', 'ba', 'bc', 'cb', 'ca', + 'acb', 'abc', 'bca', 'cba', 'bac', 'cab'] sage: L[7].rank() 7 """ diff --git a/src/sage/categories/examples/filtered_algebras_with_basis.py b/src/sage/categories/examples/filtered_algebras_with_basis.py index 9e7765aefa8..3de3456a2b6 100644 --- a/src/sage/categories/examples/filtered_algebras_with_basis.py +++ b/src/sage/categories/examples/filtered_algebras_with_basis.py @@ -114,8 +114,12 @@ def degree_on_basis(self, m): sage: A.degree_on_basis((x^4).leading_support()) 4 sage: a = A.an_element(); a - U['x']^2*U['y']^2*U['z']^3 + U['x']^2*U['y']^2*U['z']^3 + 2*U['x'] + 3*U['y'] + 1 sage: A.degree_on_basis(a.leading_support()) + 1 + sage: s = sorted(a.support(), key=str)[2]; s + U['x']^2*U['y']^2*U['z']^3 + sage: A.degree_on_basis(s) 7 """ return len(m) diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index 2242c5f487d..bd14802cf1b 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -66,35 +66,7 @@ class DihedralGroup(UniqueRepresentation, Parent): TESTS:: - sage: TestSuite(G).run(verbose = True) - running ._test_an_element() . . . pass - running ._test_associativity() . . . pass - running ._test_cardinality() . . . pass - running ._test_category() . . . pass - running ._test_elements() . . . - Running the test suite of self.an_element() - running ._test_category() . . . pass - running ._test_eq() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_pickling() . . . pass - pass - running ._test_elements_eq_reflexive() . . . pass - running ._test_elements_eq_symmetric() . . . pass - running ._test_elements_eq_transitive() . . . pass - running ._test_elements_neq() . . . pass - running ._test_enumerated_set_contains() . . . pass - running ._test_enumerated_set_iter_cardinality() . . . pass - running ._test_enumerated_set_iter_list() . . . pass - running ._test_eq() . . . pass - running ._test_has_descent() . . . pass - running ._test_inverse() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_one() . . . pass - running ._test_pickling() . . . pass - running ._test_prod() . . . pass - running ._test_reduced_word() . . . pass - running ._test_simple_projections() . . . pass - running ._test_some_elements() . . . pass + sage: TestSuite(G).run() sage: c = FiniteCoxeterGroups().example(3).cayley_graph() sage: sorted(c.edges()) @@ -179,9 +151,21 @@ def index_set(self): sage: D4 = FiniteCoxeterGroups().example(4) sage: D4.index_set() - [1, 2] + (1, 2) """ - return [1,2] + return (1, 2) + + def degrees(self): + """ + Return the degrees of ``self``. + + EXAMPLES:: + + sage: FiniteCoxeterGroups().example(6).degrees() + (2, 6) + """ + from sage.rings.integer_ring import ZZ + return (ZZ(2), ZZ(self.n)) class Element(ElementWrapper): wrapped_class = tuple diff --git a/src/sage/categories/examples/finite_semigroups.py b/src/sage/categories/examples/finite_semigroups.py index 843ac16f646..57d22e034ae 100644 --- a/src/sage/categories/examples/finite_semigroups.py +++ b/src/sage/categories/examples/finite_semigroups.py @@ -42,15 +42,14 @@ class LeftRegularBand(UniqueRepresentation, Parent): It follows that the elements of `S` are strings without repetitions over the alphabet `a`, `b`, `c`, `d`:: - sage: S.list() - ['a', 'c', 'b', 'bd', 'bda', 'd', 'bdc', 'bc', 'bcd', 'cb', - 'ca', 'ac', 'cba', 'ba', 'cbd', 'bdca', 'db', 'dc', 'cd', - 'bdac', 'ab', 'abd', 'da', 'ad', 'cbad', 'acb', 'abc', - 'abcd', 'acbd', 'cda', 'cdb', 'dac', 'dba', 'dbc', 'dbca', - 'dcb', 'abdc', 'cdab', 'bcda', 'dab', 'acd', 'dabc', 'cbda', - 'bca', 'dacb', 'bad', 'adb', 'bac', 'cab', 'adc', 'cdba', - 'dca', 'cad', 'adbc', 'adcb', 'dbac', 'dcba', 'acdb', 'bacd', - 'cabd', 'cadb', 'badc', 'bcad', 'dcab'] + sage: sorted(S.list()) + ['a', 'ab', 'abc', 'abcd', 'abd', 'abdc', 'ac', 'acb', 'acbd', 'acd', + 'acdb', 'ad', 'adb', 'adbc', 'adc', 'adcb', 'b', 'ba', 'bac', + 'bacd', 'bad', 'badc', 'bc', 'bca', 'bcad', 'bcd', 'bcda', 'bd', + 'bda', 'bdac', 'bdc', 'bdca', 'c', 'ca', 'cab', 'cabd', 'cad', + 'cadb', 'cb', 'cba', 'cbad', 'cbd', 'cbda', 'cd', 'cda', 'cdab', + 'cdb', 'cdba', 'd', 'da', 'dab', 'dabc', 'dac', 'dacb', 'db', + 'dba', 'dbac', 'dbc', 'dbca', 'dc', 'dca', 'dcab', 'dcb', 'dcba'] It also follows that there are finitely many of them:: diff --git a/src/sage/categories/examples/finite_weyl_groups.py b/src/sage/categories/examples/finite_weyl_groups.py index c36cd8f094c..765583d5cf9 100644 --- a/src/sage/categories/examples/finite_weyl_groups.py +++ b/src/sage/categories/examples/finite_weyl_groups.py @@ -16,7 +16,6 @@ class SymmetricGroup(UniqueRepresentation, Parent): r""" - An example of finite Weyl group: the symmetric group, with elements in list notation. @@ -69,11 +68,13 @@ class SymmetricGroup(UniqueRepresentation, Parent): TESTS:: - sage: TestSuite(S).run(verbose = True) + sage: TestSuite(S).run(verbose=True) running ._test_an_element() . . . pass running ._test_associativity() . . . pass running ._test_cardinality() . . . pass running ._test_category() . . . pass + running ._test_codegrees() . . . pass + running ._test_degrees() . . . pass running ._test_elements() . . . Running the test suite of self.an_element() running ._test_category() . . . pass @@ -98,6 +99,7 @@ class SymmetricGroup(UniqueRepresentation, Parent): running ._test_reduced_word() . . . pass running ._test_simple_projections() . . . pass running ._test_some_elements() . . . pass + running ._test_well_generated() . . . pass """ def __init__(self, n = 4): @@ -171,6 +173,19 @@ def product(self, x, y): assert y in self return self(tuple(x.value[i] for i in y.value)) + def degrees(self): + """ + Return the degrees of ``self``. + + EXAMPLES:: + + sage: W = FiniteWeylGroups().example() + sage: W.degrees() + (2, 3, 4) + """ + from sage.rings.integer_ring import ZZ + return tuple(ZZ(i) for i in range(2, self.n + 1)) + class Element(ElementWrapper): def has_right_descent(self, i): diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index 7b40f116de8..ca176f1e24c 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -235,7 +235,7 @@ def _from_integer_(self, i): def next(self, i): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: @@ -250,7 +250,7 @@ def next(self, i): def some_elements(self): """ - Returns some prime numbers + Return some prime numbers. EXAMPLES:: @@ -268,7 +268,7 @@ def some_elements(self): class Element(Element): def is_prime(self): """ - Returns if a prime number is prime = True ! + Return whether ``self`` is a prime number. EXAMPLES:: @@ -281,13 +281,20 @@ def is_prime(self): def next(self): """ - Returns the next prime number + Return the next prime number. EXAMPLES:: sage: P = Sets().example("inherits") - sage: next(P.an_element()) + sage: p = P.an_element(); p + 47 + sage: p.next() 53 + + .. NOTE:: + + This method is not meant to implement the protocol iterator, + and thus not subject to Python 2 vs Python 3 incompatibilities. """ return self.parent().next(self) @@ -587,15 +594,15 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: z.parent() Integer Ring - The disadvantage of this implementation is that the element doesn't know - that they are primes so that prime testing is slow:: + The disadvantage of this implementation is that the elements do not know + that they are prime, so that prime testing is slow:: sage: pf = Sets().example("facade").an_element() sage: timeit("pf.is_prime()") # random 625 loops, best of 3: 4.1 us per loop compared to the other implementations where prime testing is only done if - needed during the construction of the element. Then the elements themselve + needed during the construction of the element, and later on the elements "know" that they are prime:: sage: pw = Sets().example("wrapper").an_element() @@ -606,18 +613,18 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract): sage: timeit("pw.is_prime()") # random 625 loops, best of 3: 854 ns per loop - And moreover, the next methods for the element does not exist:: + Note also that the ``next`` method for the elements does not exist:: sage: pf.next() Traceback (most recent call last): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute 'next' - whereas:: + unlike in the other implementations:: - sage: next(pw) + sage: pw.next() 53 - sage: next(pi) + sage: pi.next() 53 TESTS:: diff --git a/src/sage/categories/filtered_algebras_with_basis.py b/src/sage/categories/filtered_algebras_with_basis.py index 053d119a040..f8cdfbadfe2 100644 --- a/src/sage/categories/filtered_algebras_with_basis.py +++ b/src/sage/categories/filtered_algebras_with_basis.py @@ -131,9 +131,10 @@ def to_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p); q - bar(U['x']^2*U['y']^2*U['z']^3) + bar(U['x']) + 2*bar(1) + bar(U['x']^2*U['y']^2*U['z']^3) + 3*bar(U['x']) + + 3*bar(U['y']) + 3*bar(1) sage: q.parent() is A.graded_algebra() True """ @@ -159,7 +160,7 @@ def from_graded_conversion(self): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.to_graded_conversion()(p) sage: A.from_graded_conversion()(q) == p True @@ -190,7 +191,7 @@ def projection(self, i): sage: A = Algebras(QQ).WithBasis().Filtered().example() sage: p = A.an_element() + A.algebra_generators()['x'] + 2; p - U['x']^2*U['y']^2*U['z']^3 + U['x'] + 2 + U['x']^2*U['y']^2*U['z']^3 + 3*U['x'] + 3*U['y'] + 3 sage: q = A.projection(7)(p); q bar(U['x']^2*U['y']^2*U['z']^3) sage: q.parent() is A.graded_algebra() diff --git a/src/sage/categories/filtered_modules_with_basis.py b/src/sage/categories/filtered_modules_with_basis.py index 7f2320f7f19..640b619930e 100644 --- a/src/sage/categories/filtered_modules_with_basis.py +++ b/src/sage/categories/filtered_modules_with_basis.py @@ -105,7 +105,6 @@ class in order to fully utilize the methods of this category. sage: TestSuite(C).run() """ class ParentMethods: - # TODO: which syntax do we prefer? # A.basis(degree = 3) # A.basis().subset(degree=3) @@ -154,14 +153,14 @@ def basis(self, d=None): over Integer Ring(i))_{i in Partitions} Checking this method on a filtered algebra. Note that this - will typically raise an ``AttributeError`` when this feature - is not implemented. :: + will typically raise a ``NotImplementedError`` when this + feature is not implemented. :: sage: A = AlgebrasWithBasis(ZZ).Filtered().example() sage: A.basis(4) Traceback (most recent call last): ... - AttributeError: 'IndexedFreeAbelianMonoid_with_category' object has no attribute 'subset' + NotImplementedError: infinite list Without arguments, the full basis is returned:: @@ -180,11 +179,66 @@ def basis(self, d=None): The exterior algebra of rank 2 over Rational Field(i))_{i in Subsets of {0, 1}} """ - from sage.sets.family import Family if d is None: + from sage.sets.family import Family return Family(self._indices, self.monomial) else: - return Family(self._indices.subset(size=d), self.monomial) + return self.homogeneous_component_basis(d) + + # TODO: Change `list(self._indices)` to `self._indices` and move + # this fallback to the category of finite-dimensional filtered + # modules with basis when it is implemented and the MRO issues + # are fixed (see trac #19397) + def homogeneous_component_basis(self, d): + """ + Return a basis for the ``d``-th homogeneous component of ``self``. + + EXAMPLES:: + + sage: A = GradedModulesWithBasis(ZZ).example() + sage: A.homogeneous_component_basis(4) + Lazy family (Term map from Partitions to An example of a graded module with basis: + the free module on partitions over Integer Ring(i))_{i in Partitions of the integer 4} + + sage: cat = GradedModulesWithBasis(ZZ) + sage: C = CombinatorialFreeModule(ZZ, ['a', 'b'], category=cat) + sage: C.degree_on_basis = lambda x: 1 if x == 'a' else 2 + sage: C.homogeneous_component_basis(1) + Finite family {'a': B['a']} + sage: C.homogeneous_component_basis(2) + Finite family {'b': B['b']} + """ + from sage.sets.family import Family + try: + S = self._indices.subset(size=d) + except (AttributeError, ValueError, TypeError): + S = [i for i in list(self._indices) if self.degree_on_basis(i) == d] + return Family(S, self.monomial) + + def homogeneous_component(self, d): + """ + Return the ``d``-th homogeneous component of ``self``. + + EXAMPLES:: + + sage: A = GradedModulesWithBasis(ZZ).example() + sage: A.homogeneous_component(4) + Degree 4 homogeneous component of An example of a graded module + with basis: the free module on partitions over Integer Ring + """ + from sage.categories.modules_with_basis import ModulesWithBasis + from sage.categories.filtered_algebras import FilteredAlgebras + if self.base_ring() in FilteredAlgebras: + raise NotImplementedError("this is only a natural module over" + " the degree 0 component of the filtered" + " algebra and coordinate rings are not" + " yet implemented for submodules") + category = ModulesWithBasis(self.category().base_ring()) + M = self.submodule(self.homogeneous_component_basis(d), + category=category, + already_echelonized=True) + M.rename("Degree {} homogeneous component of {}".format(d, self)) + return M def graded_algebra(self): r""" @@ -832,12 +886,14 @@ def homogeneous_component(self, n): 0 sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 sage: g.homogeneous_component(-1) 0 sage: g.homogeneous_component(0) - 0 + 1 sage: g.homogeneous_component(2) -2*U['x']*U['y'] sage: g.homogeneous_component(5) @@ -896,22 +952,25 @@ def truncate(self, n): 2*P[] + 2*P[1] + 3*P[2] sage: A = AlgebrasWithBasis(ZZ).Filtered().example() - sage: g = A.an_element() - 2 * A.algebra_generators()['x'] * A.algebra_generators()['y']; g + sage: G = A.algebra_generators() + sage: g = A.an_element() - 2 * G['x'] * G['y']; g U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(-1) 0 sage: g.truncate(0) 0 sage: g.truncate(2) - 0 + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(3) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(5) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(7) - -2*U['x']*U['y'] + -2*U['x']*U['y'] + 2*U['x'] + 3*U['y'] + 1 sage: g.truncate(8) U['x']^2*U['y']^2*U['z']^3 - 2*U['x']*U['y'] + + 2*U['x'] + 3*U['y'] + 1 TESTS: @@ -926,3 +985,4 @@ def truncate(self, n): return self.parent().sum_of_terms((i, c) for (i, c) in self if degree_on_basis(i) < n) + diff --git a/src/sage/categories/finite_complex_reflection_groups.py b/src/sage/categories/finite_complex_reflection_groups.py new file mode 100644 index 00000000000..7ef2892cc3e --- /dev/null +++ b/src/sage/categories/finite_complex_reflection_groups.py @@ -0,0 +1,936 @@ +r""" +Finite Complex Reflection Groups +""" +#***************************************************************************** +# Copyright (C) 2011-2015 Christian Stump +# +# 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.abstract_method import abstract_method +from sage.misc.all import prod +from sage.misc.cachefunc import cached_method +from sage.categories.category_with_axiom import CategoryWithAxiom +from sage.categories.coxeter_groups import CoxeterGroups + +class FiniteComplexReflectionGroups(CategoryWithAxiom): + r""" + The category of finite complex reflection groups. + + See :class:`ComplexReflectionGroups` for the definition of complex + reflection group. In the finite case, most of the information + about the group can be recovered from its *degrees* and + *codegrees*, and to a lesser extent to the explicit realization as + subgroup of `GL(V)`. Hence the most important optional methods to + implement are: + + - :meth:`ComplexReflectionGroups.Finite.ParentMethods.degrees`, + - :meth:`ComplexReflectionGroups.Finite.ParentMethods.codegrees`, + - :meth:`ComplexReflectionGroups.Finite.ElementMethods.to_matrix`. + + Finite complex reflection groups are completely classified. In + particular, if the group is irreducible, then it's uniquely + determined by its degrees and codegrees and whether it's + reflection representation is *primitive* or not (see [LT2009]_ + Chapter 2.1 for the definition of primitive). + + .. SEEALSO:: :wikipedia:`Complex_reflection_groups` + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Finite() + Category of finite complex reflection groups + sage: ComplexReflectionGroups().Finite().super_categories() + [Category of complex reflection groups, + Category of finite groups, + Category of finite finitely generated semigroups] + + An example of a finite reflection group:: + + sage: W = ComplexReflectionGroups().Finite().example(); W # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x B2 + + sage: W.reflections() # optional - gap3 + Finite family {1: (1,8)(2,5)(9,12), 2: (1,5)(2,9)(8,12), + 3: (3,10)(4,7)(11,14), 4: (3,6)(4,11)(10,13), + 5: (1,9)(2,8)(5,12), 6: (4,14)(6,13)(7,11), + 7: (3,13)(6,10)(7,14)} + + ``W`` is in the category of complex reflection groups:: + + sage: W in ComplexReflectionGroups().Finite() # optional - gap3 + True + """ + def example(self): + r""" + Return an example of a complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Finite().example() # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x B2 + """ + from sage.combinat.root_system.reflection_group_real import ReflectionGroup + return ReflectionGroup((1,1,3), (2,1,2)) + + class SubcategoryMethods: + + @cached_method + def WellGenerated(self): + r""" + Return the full subcategory of well-generated objects of ``self``. + + A finite complex generated group is *well generated* if it + is isomorphic to a subgroup of the general linear group + `GL_n` generated by `n` reflections. + + .. SEEALSO:: + + :meth:`ComplexRelfectionGroups.Finite.ParentMethods.is_well_generated` + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: C = ComplexReflectionGroups().Finite().WellGenerated(); C + Category of well generated finite complex reflection groups + + Here is an example of a finite well-generated complex + reflection group:: + + sage: W = C.example(); W # optional - gap3 + Reducible complex reflection group of rank 4 and type A2 x G(3,1,2) + + All finite Coxeter groups are well generated:: + + sage: CoxeterGroups().Finite().is_subcategory(C) + True + sage: SymmetricGroup(3) in C + True + + .. NOTE:: + + The category of well generated finite complex + reflection groups is currently implemented as an + axiom. See discussion on :trac:`11187`. This may be a + bit of overkill. Still it's nice to have a full + subcategory. + + TESTS:: + + sage: TestSuite(W).run() # optional - gap3 + sage: TestSuite(ComplexReflectionGroups().Finite().WellGenerated()).run() # optional - gap3 + sage: CoxeterGroups().Finite().WellGenerated.__module__ + 'sage.categories.finite_complex_reflection_groups' + + We check that the axioms are properly ordered in + ``sage.categories.category_with_axiom.axioms`` and yield + desired output (well generated does not appear):: + + sage: CoxeterGroups().Finite() + Category of finite coxeter groups + """ + return self._with_axiom('WellGenerated') + + class ParentMethods: + @abstract_method(optional=True) + def degrees(self): + r""" + Return the degrees of ``self``. + + OUTPUT: a tuple of Sage integers + + EXAMPLES:: + + sage: W = ColoredPermutations(1,4) + sage: W.degrees() + (2, 3, 4) + + sage: W = ColoredPermutations(3,3) + sage: W.degrees() + (3, 6, 9) + + sage: W = ReflectionGroup(31) # optional - gap3 + sage: W.degrees() # optional - gap3 + (8, 12, 20, 24) + """ + + @abstract_method(optional=True) + def codegrees(self): + r""" + Return the codegrees of ``self``. + + OUTPUT: a tuple of Sage integers + + EXAMPLES:: + + sage: W = ColoredPermutations(1,4) + sage: W.codegrees() + (2, 1, 0) + + sage: W = ColoredPermutations(3,3) + sage: W.codegrees() + (6, 3, 0) + + sage: W = ReflectionGroup(31) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (28, 16, 12, 0) + """ + + def _test_degrees(self, **options): + """ + Test the method :meth:`degrees`. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().Finite().example(); W # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x B2 + sage: W._test_degrees() # optional - gap3 + + sage: W = SymmetricGroup(5) + sage: W._test_degrees() + + We now break the implementation of W.degrees and check that this is caught:: + + sage: W.degrees = lambda: (1/1,5) + sage: W._test_degrees() + Traceback (most recent call last): + ... + AssertionError: the degrees should be integers + + sage: W.degrees = lambda: (1,2,3) + sage: W._test_degrees() + Traceback (most recent call last): + ... + AssertionError: the degrees should be larger than 2 + + We restore W to its normal state:: + + sage: del W.degrees + sage: W._test_degrees() + + See the documentation for :class:`TestSuite` for more information. + """ + from sage.structure.element import parent + from sage.rings.integer_ring import ZZ + + tester = self._tester(**options) + degrees = self.degrees() + tester.assertIsInstance(degrees, tuple, + "the degrees method should return a tuple") + tester.assertTrue(all(parent(d) is ZZ for d in degrees), + "the degrees should be integers") + tester.assertTrue(all(d >= 2 for d in degrees), + "the degrees should be larger than 2") + tester.assertEqual(len(degrees), self.rank(), + "the number of degrees should coincide with the rank") + tester.assertEqual(sum(d-1 for d in degrees), self.number_of_reflections(), + "the sum of the degrees should be consistent with the number of reflections") + + def _test_codegrees(self, **options): + """ + Test the method :meth:`degrees`. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().Finite().example(); W # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x B2 + sage: W._test_codegrees() # optional - gap3 + + sage: W = SymmetricGroup(5) + sage: W._test_codegrees() + + We now break the implementation of W.degrees and check that this is caught:: + + sage: W.codegrees = lambda: (1/1,5) + sage: W._test_codegrees() + Traceback (most recent call last): + ... + AssertionError: the codegrees should be integers + + sage: W.codegrees = lambda: (2,1,-1) + sage: W._test_codegrees() + Traceback (most recent call last): + ... + AssertionError: the codegrees should be nonnegative + + We restore W to its normal state:: + + sage: del W.codegrees + sage: W._test_codegrees() + + See the documentation for :class:`TestSuite` for more information. + """ + from sage.structure.element import parent + from sage.rings.integer_ring import ZZ + + tester = self._tester(**options) + codegrees = self.codegrees() + tester.assertIsInstance(codegrees, tuple, + "the codegrees method should return a tuple") + tester.assertTrue(all(parent(d) is ZZ for d in codegrees), + "the codegrees should be integers") + tester.assertTrue(all(d >= 0 for d in codegrees), + "the codegrees should be nonnegative") + tester.assertEqual(len(codegrees), self.rank(), + "the number of codegrees should coincide with the rank") + tester.assertEqual(sum(d+1 for d in codegrees), + self.number_of_reflection_hyperplanes(), + "the sum of the codegrees should be consistent with the number of reflection hyperplanes") + + def number_of_reflection_hyperplanes(self): + r""" + Return the number of reflection hyperplanes of ``self``. + + This is also the number of distinguished reflections. For + real groups, this coincides with the number of + reflections. + + This implementation uses that it is given by the sum of + the codegrees of ``self`` plus its rank. + + .. SEEALSO:: :meth:`number_of_reflections` + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.number_of_reflection_hyperplanes() + 3 + sage: W = ColoredPermutations(2,3) + sage: W.number_of_reflection_hyperplanes() + 9 + sage: W = ColoredPermutations(4,3) + sage: W.number_of_reflection_hyperplanes() + 15 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.number_of_reflection_hyperplanes() # optional - gap3 + 15 + """ + from sage.rings.all import ZZ + return ZZ.sum(self.codegrees()) + self.rank() + + def number_of_reflections(self): + r""" + Return the number of reflections of ``self``. + + For real groups, this coincides with the number of + reflection hyperplanes. + + This implementation uses that it is given by the sum of + the degrees of ``self`` minus its rank. + + .. SEEALSO:: :meth:`number_of_reflection_hyperplanes` + + EXAMPLES:: + + sage: [SymmetricGroup(i).number_of_reflections() for i in range(int(8))] + [0, 0, 1, 3, 6, 10, 15, 21] + + sage: W = ColoredPermutations(1,3) + sage: W.number_of_reflections() + 3 + sage: W = ColoredPermutations(2,3) + sage: W.number_of_reflections() + 9 + sage: W = ColoredPermutations(4,3) + sage: W.number_of_reflections() + 21 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.number_of_reflections() # optional - gap3 + 15 + """ + from sage.rings.all import ZZ + return ZZ.sum(self.degrees()) - self.rank() + + def rank(self): + r""" + Return the rank of ``self``. + + The rank of ``self`` is the dimension of the smallest + faithfull reflection representation of ``self``. + + This default implementation uses that the rank is the + number of :meth:`degrees`. + + .. SEEALSO:: :meth:`ComplexReflectionGroups.rank` + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.rank() + 2 + sage: W = ColoredPermutations(2,3) + sage: W.rank() + 3 + sage: W = ColoredPermutations(4,3) + sage: W.rank() + 3 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.rank() # optional - gap3 + 3 + """ + return len(self.degrees()) + + @cached_method + def cardinality(self): + r""" + Return the cardinality of ``self``. + + It is given by the product of the degrees of ``self``. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.cardinality() + 6 + sage: W = ColoredPermutations(2,3) + sage: W.cardinality() + 48 + sage: W = ColoredPermutations(4,3) + sage: W.cardinality() + 384 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.cardinality() # optional - gap3 + 192 + """ + from sage.rings.all import ZZ + return ZZ.prod(self.degrees()) + + def is_well_generated(self): + r""" + Return whether ``self`` is well-generated. + + A finite complex reflection group is *well generated* if + the number of its simple reflections coincides with its rank. + + .. SEEALSO:: :meth:`ComplexReflectionGroups.Finite.WellGenerated` + + .. NOTE:: + + - All finite real reflection groups are well generated. + - The complex reflection groups of type `G(r,1,n)` and + of type `G(r,r,n)` are well generated. + - The complex reflection groups of type `G(r,p,n)` + with `1 < p < r` are *not* well generated. + + - The direct product of two well generated finite + complex reflection group is still well generated. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.is_well_generated() + True + + sage: W = ColoredPermutations(4,3) + sage: W.is_well_generated() + True + + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.is_well_generated() # optional - gap3 + False + + sage: W = ReflectionGroup((4,4,3)) # optional - gap3 + sage: W.is_well_generated() # optional - gap3 + True + """ + return self.number_of_simple_reflections() == self.rank() + + def is_real(self): + r""" + Return whether ``self`` is real. + + A complex reflection group is *real* if it is isomorphic + to a reflection group in `GL(V)` over a real vector space `V`. + Equivalently its character table has real entries. + + This implementation uses the following statement: an + irreducible complex reflection group is real if and only + if `2` is a degree of ``self`` with multiplicity one. + Hence, in general we just need to compare the number of + occurences of `2` as degree of ``self`` and the number of + irreducible components. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.is_real() + True + + sage: W = ColoredPermutations(4,3) + sage: W.is_real() + False + + .. TODO:: + + Add an example of non real finite complex reflection + group that is generated by order 2 reflections. + """ + return self.degrees().count(2) == self.number_of_irreducible_components() + + class ElementMethods: + + @abstract_method(optional=True) + def to_matrix(self): + r""" + Return the matrix presentation of ``self`` acting on a + vector space `V`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: [t.to_matrix() for t in W] # optional - gap3 + [ + [1 0] [ 1 1] [-1 0] [-1 -1] [ 0 1] [ 0 -1] + [0 1], [ 0 -1], [ 1 1], [ 1 0], [-1 -1], [-1 0] + ] + + sage: W = ColoredPermutations(1,3) + sage: [t.to_matrix() for t in W] + [ + [1 0 0] [1 0 0] [0 1 0] [0 0 1] [0 1 0] [0 0 1] + [0 1 0] [0 0 1] [1 0 0] [1 0 0] [0 0 1] [0 1 0] + [0 0 1], [0 1 0], [0 0 1], [0 1 0], [1 0 0], [1 0 0] + ] + + A different representation is given by the + colored permutations:: + + sage: W = ColoredPermutations(3, 1) + sage: [t.to_matrix() for t in W] + [[1], [zeta3], [-zeta3 - 1]] + """ + + def _matrix_(self): + """ + Return ``self`` as a matrix. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: [matrix(t) for t in W] # optional - gap3 + [ + [1 0] [ 1 1] [-1 0] [-1 -1] [ 0 1] [ 0 -1] + [0 1], [ 0 -1], [ 1 1], [ 1 0], [-1 -1], [-1 0] + ] + """ + return self.to_matrix() + + def character_value(self): + r""" + Return the value at ``self`` of the character of the + reflection representation given by :meth:`to_matrix`. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3); W + 1-colored permutations of size 3 + sage: [t.character_value() for t in W] + [3, 1, 1, 0, 0, 1] + + Note that this could be a different (faithful) + representation than that given by the corresponding root + system:: + + sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: [t.character_value() for t in W] # optional - gap3 + [2, 0, 0, -1, -1, 0] + + sage: W = ColoredPermutations(2,2); W + 2-colored permutations of size 2 + sage: [t.character_value() for t in W] + [2, 0, 0, -2, 0, 0, 0, 0] + + sage: W = ColoredPermutations(3,1); W + 3-colored permutations of size 1 + sage: [t.character_value() for t in W] + [1, zeta3, -zeta3 - 1] + """ + return self.to_matrix().trace() + + class Irreducible(CategoryWithAxiom): + + def example(self): + r""" + Return an example of an irreducible complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Finite().Irreducible().example() # optional - gap3 + Irreducible complex reflection group of rank 3 and type G(4,2,3) + """ + from sage.combinat.root_system.reflection_group_real import ReflectionGroup + return ReflectionGroup((4,2,3)) + + class ParentMethods: + def coxeter_number(self): + r""" + Return the Coxeter number of an irreducible + reflection group. + + This is defined as `\frac{N + N^*}{n}` where + `N` is the number of reflections, `N^*` is the + number of reflection hyperplanes, and `n` is the + rank of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(31) # optional - gap3 + sage: W.coxeter_number() # optional - gap3 + 30 + """ + return (self.number_of_reflection_hyperplanes() + + self.number_of_reflections()) // self.rank() + + class WellGenerated(CategoryWithAxiom): + + def example(self): + r""" + Return an example of a well-generated complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Finite().WellGenerated().example() # optional - gap3 + Reducible complex reflection group of rank 4 and type A2 x G(3,1,2) + """ + from sage.combinat.root_system.reflection_group_real import ReflectionGroup + return ReflectionGroup((1,1,3), (3,1,2)) + + class ParentMethods: + def _test_well_generated(self, **options): + """ + Check if ``self`` is well-generated. + + EXAMPLES:: + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W._test_well_generated() # optional - gap3 + """ + tester = self._tester(**options) + tester.assertEqual(self.number_of_simple_reflections(), self.rank()) + + def is_well_generated(self): + r""" + Return ``True`` as ``self`` is well-generated. + + EXAMPLES:: + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.is_well_generated() # optional - gap3 + True + """ + return True + + coxeter_element = CoxeterGroups.ParentMethods.coxeter_element + standard_coxeter_elements = CoxeterGroups.ParentMethods.standard_coxeter_elements + + @cached_method + def coxeter_elements(self): + r""" + Return the (unique) conjugacy class in ``self`` containing all + Coxeter elements. + + .. NOTE:: + + Beyond real reflection groups, the conjugacy class + is not unique and we only obtain one such class. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: sorted(c.reduced_word() for c in W.coxeter_elements()) # optional - gap3 + [[1, 2], [2, 1]] + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: sorted(c.reduced_word() for c in W.coxeter_elements()) # optional - gap3 + [[1, 2, 1, 3, 2], [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 1, 3, 2, 1], [3, 2, 1]] + """ + return self.coxeter_element().conjugacy_class() + + class Irreducible(CategoryWithAxiom): + r""" + The category of finite irreducible well-generated + finite complex reflection groups. + """ + def example(self): + r""" + Return an example of an irreducible well-generated + complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: ComplexReflectionGroups().Finite().WellGenerated().Irreducible().example() + 4-colored permutations of size 3 + """ + from sage.combinat.colored_permutations import ColoredPermutations + return ColoredPermutations(4, 3) + + class ParentMethods: + def coxeter_number(self): + r""" + Return the Coxeter number of a well-generated, + irreducible reflection group. This is defined to be + the order of a regular element in ``self``, and is + equal to the highest degree of ``self``. + + .. SEEALSO:: :meth:`ComplexReflectionGroups.Finite.Irreducible` + + .. NOTE:: + + This method overwrites the more general + method for complex reflection groups since + the expression given here is quicker to + compute. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: W.coxeter_number() + 3 + + sage: W = ColoredPermutations(4,3) + sage: W.coxeter_number() + 12 + + sage: W = ReflectionGroup((4,4,3)) # optional - gap3 + sage: W.coxeter_number() # optional - gap3 + 8 + """ + return max(self.degrees()) + + def number_of_reflections_of_full_support(self): + r""" + Return the number of reflections with full + support. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,4) + sage: W.number_of_reflections_of_full_support() + 1 + + sage: W = ColoredPermutations(3,3) + sage: W.number_of_reflections_of_full_support() + 3 + """ + n = self.rank() + h = self.coxeter_number() + l = self.cardinality() + codegrees = self.codegrees()[:-1] + return (n * h * prod(codegrees)) // l + + @cached_method + def rational_catalan_number(self, p, polynomial=False): + r""" + Return the ``p``-th rational Catalan number + associated to ``self``. + + It is defined by + + .. MATH:: + + \prod_{i = 1}^n \frac{p + (p(d_i-1)) \mod h)}{d_i}, + + where `d_1, \ldots, d_n` are the degrees and + `h` is the Coxeter number. See [STW2016]_ + for this formula. + + INPUT: + + - ``polynomial`` -- optional boolean (default ``False``) + if ``True``, return instead the `q`-analogue as a + polynomial in `q` + + REFERENCES: + + .. [STW2016] C. Stump, H. Thomas, N. Williams. + *Cataland II*, in preparation, 2016. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: [W.rational_catalan_number(p) for p in [5,7,8]] + [7, 12, 15] + + sage: W = ColoredPermutations(2,2) + sage: [W.rational_catalan_number(p) for p in [7,9,11]] + [10, 15, 21] + + TESTS:: + + sage: W = ColoredPermutations(1,4) + sage: W.rational_catalan_number(3, polynomial=True) + q^6 + q^4 + q^3 + q^2 + 1 + """ + from sage.arith.all import gcd + from sage.combinat.q_analogues import q_int + + h = self.coxeter_number() + if not gcd(h,p) == 1: + raise ValueError("parameter p = %s is not coprime to the Coxeter number %s" % (p, h)) + + if polynomial: + f = q_int + else: + f = lambda n: n + + num = prod(f(p + (p * (deg - 1)) % h) + for deg in self.degrees()) + den = prod(f(deg) for deg in self.degrees()) + return num // den + + def fuss_catalan_number(self, m, positive=False, + polynomial=False): + r""" + Return the ``m``-th Fuss-Catalan number + associated to ``self``. + + This is defined by + + .. MATH:: + + \prod_{i = 1}^n \frac{d_i + mh}{d_i}, + + where `d_1, \ldots, d_n` are the degrees and + `h` is the Coxeter number. + + INPUT: + + - ``positive`` -- optional boolean (default ``False``) + if ``True``, return instead the positive Fuss-Catalan + number + - ``polynomial`` -- optional boolean (default ``False``) + if ``True``, return instead the `q`-analogue as a + polynomial in `q` + + See [Arm2006]_ for further information. + + .. NOTE:: + + - For the symmetric group `S_n`, it reduces to the + Fuss-Catalan number `\frac{1}{mn+1}\binom{(m+1)n}{n}`. + - The Fuss-Catalan numbers for `G(r, 1, n)` all + coincide for `r > 1`. + + REFERENCES: + + .. [Arm2006] D. Armstrong. *Generalized noncrossing + partitions and combinatorics of Coxeter groups*. + Mem. Amer. Math. Soc., 2006. + + EXAMPLES:: + + sage: W = ColoredPermutations(1,3) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [5, 12, 22] + + sage: W = ColoredPermutations(1,4) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [14, 55, 140] + + sage: W = ColoredPermutations(1,5) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [42, 273, 969] + + sage: W = ColoredPermutations(2,2) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [6, 15, 28] + + sage: W = ColoredPermutations(2,3) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [20, 84, 220] + + sage: W = ColoredPermutations(2,4) + sage: [W.fuss_catalan_number(i) for i in [1,2,3]] + [70, 495, 1820] + + TESTS:: + + sage: W = ColoredPermutations(2,4) + sage: W.fuss_catalan_number(2,positive=True) + 330 + sage: W = ColoredPermutations(2,2) + sage: W.fuss_catalan_number(2,polynomial=True) + q^16 + q^14 + 2*q^12 + 2*q^10 + 3*q^8 + 2*q^6 + + 2*q^4 + q^2 + 1 + """ + h = self.coxeter_number() + if positive: + p = m * h - 1 + else: + p = m * h + 1 + + return self.rational_catalan_number(p, polynomial=polynomial) + + def catalan_number(self, positive=False, polynomial=False): + r""" + Return the Catalan number associated to ``self``. + + It is defined by + + .. MATH:: + + \prod_{i = 1}^n \frac{d_i + h}{d_i}, + + where `d_1, \ldots, d_n` are the degrees and where + `h` is the Coxeter number. + See [Arm2006]_ for further information. + + INPUT: + + - ``positive`` -- optional boolean (default ``False``) + if ``True``, return instead the positive Catalan + number + - ``polynomial`` -- optional boolean (default ``False``) + if ``True``, return instead the `q`-analogue as a + polynomial in `q` + + .. NOTE:: + + - For the symmetric group `S_n`, it reduces to the + Catalan number `\frac{1}{n+1} \binom{2n}{n}`. + - The Catalan numbers for `G(r,1,n)` all coincide + for `r > 1`. + + EXAMPLES:: + + sage: [ColoredPermutations(1,n).catalan_number() for n in [3,4,5]] + [5, 14, 42] + + sage: [ColoredPermutations(2,n).catalan_number() for n in [3,4,5]] + [20, 70, 252] + + sage: [ReflectionGroup((2,2,n)).catalan_number() for n in [3,4,5]] # optional - gap3 + [14, 50, 182] + + TESTS:: + + sage: W = ColoredPermutations(3,6) + sage: W.catalan_number(positive=True) + 462 + sage: W = ColoredPermutations(2,2) + sage: W.catalan_number(polynomial=True) + q^8 + q^6 + 2*q^4 + q^2 + 1 + """ + return self.fuss_catalan_number(1, positive=positive, + polynomial=polynomial) + diff --git a/src/sage/categories/finite_coxeter_groups.py b/src/sage/categories/finite_coxeter_groups.py index 30f0d33ed8e..79678513059 100644 --- a/src/sage/categories/finite_coxeter_groups.py +++ b/src/sage/categories/finite_coxeter_groups.py @@ -20,20 +20,19 @@ class FiniteCoxeterGroups(CategoryWithAxiom): EXAMPLES:: - sage: FiniteCoxeterGroups() + sage: CoxeterGroups.Finite() Category of finite coxeter groups sage: FiniteCoxeterGroups().super_categories() - [Category of coxeter groups, - Category of finite groups, - Category of finite finitely generated semigroups] + [Category of finite generalized coxeter groups, + Category of coxeter groups] - sage: G = FiniteCoxeterGroups().example() + sage: G = CoxeterGroups().Finite().example() sage: G.cayley_graph(side = "right").plot() Graphics object consisting of 40 graphics primitives Here are some further examples:: - sage: FiniteWeylGroups().example() + sage: WeylGroups().Finite().example() The symmetric group on {0, ..., 3} sage: WeylGroup(["B", 3]) @@ -46,9 +45,18 @@ class FiniteCoxeterGroups(CategoryWithAxiom): sage: DihedralGroup(5) Dihedral group of order 10 as a permutation group """ + def extra_super_categories(self): + r""" + EXAMPLES:: - class ParentMethods: + sage: CoxeterGroups().Finite().super_categories() + [Category of finite generalized coxeter groups, + Category of coxeter groups] + """ + from sage.categories.complex_reflection_groups import ComplexReflectionGroups + return [ComplexReflectionGroups().Finite().WellGenerated()] + class ParentMethods: """ Ambiguity resolution: the implementation of ``some_elements`` is preferable to that of :class:`FiniteGroups`. The same holds @@ -61,7 +69,7 @@ class ParentMethods: sage: W = FiniteCoxeterGroups().example(3) sage: W.some_elements.__module__ - 'sage.categories.coxeter_groups' + 'sage.categories.complex_reflection_or_generalized_coxeter_groups' sage: W.__iter__.__module__ 'sage.categories.coxeter_groups' @@ -70,8 +78,7 @@ class ParentMethods: sage: list(W) [(), (1,), (2,), (1, 2), (2, 1), (1, 2, 1)] """ - some_elements = CoxeterGroups.ParentMethods.__dict__["some_elements"] - __iter__ = CoxeterGroups.ParentMethods.__dict__["__iter__"] + __iter__ = CoxeterGroups.ParentMethods.__dict__["__iter__"] @lazy_attribute def w0(self): @@ -202,8 +209,83 @@ def bruhat_poset(self, facade = False): covers = tuple([u, v] for v in self for u in v.bruhat_lower_covers() ) return Poset((self, covers), cover_relations = True, facade=facade) + def degrees(self): + """ + Return the degrees of the Coxeter group. + + The output is an increasing list of integers. + + EXAMPLES:: + + sage: CoxeterGroup(['A', 4]).degrees() + (2, 3, 4, 5) + sage: CoxeterGroup(['B', 4]).degrees() + (2, 4, 6, 8) + sage: CoxeterGroup(['D', 4]).degrees() + (2, 4, 4, 6) + sage: CoxeterGroup(['F', 4]).degrees() + (2, 6, 8, 12) + sage: CoxeterGroup(['E', 8]).degrees() + (2, 8, 12, 14, 18, 20, 24, 30) + sage: CoxeterGroup(['H', 3]).degrees() + (2, 6, 10) + + sage: WeylGroup([["A",3], ["A",3], ["B",2]]).degrees() + (2, 3, 4, 2, 3, 4, 2, 4) + + TESTS:: + + sage: CoxeterGroup(['A', 4]).degrees() + (2, 3, 4, 5) + sage: SymmetricGroup(3).degrees() + (2, 3) + """ + from sage.rings.qqbar import QQbar + from sage.rings.integer_ring import ZZ + def degrees_of_irreducible_component(I): + """Return the degrees for the irreducible component indexed by I""" + # A Coxeter element + s = self.simple_reflections() + c = self.prod(s[i] for i in I) + roots = c.matrix().change_ring(QQbar).charpoly().roots() + args = [(z.rational_argument(), m) for z, m in roots] + args = [(z if z >=0 else 1 + z, m) for z, m in args] + h = max(z.denominator() for z, m in args) + return tuple(sorted(ZZ(z * h + 1) + for z, m in args if z + for i in range(m))) + + return sum((degrees_of_irreducible_component(I) + for I in self.irreducible_component_index_sets()), ()) + + def codegrees(self): + """ + Return the codegrees of the Coxeter group. + + These are just the degrees minus 2. + + EXAMPLES:: + + sage: CoxeterGroup(['A', 4]).codegrees() + (0, 1, 2, 3) + sage: CoxeterGroup(['B', 4]).codegrees() + (0, 2, 4, 6) + sage: CoxeterGroup(['D', 4]).codegrees() + (0, 2, 2, 4) + sage: CoxeterGroup(['F', 4]).codegrees() + (0, 4, 6, 10) + sage: CoxeterGroup(['E', 8]).codegrees() + (0, 6, 10, 12, 16, 18, 22, 28) + sage: CoxeterGroup(['H', 3]).codegrees() + (0, 4, 8) + + sage: WeylGroup([["A",3], ["A",3], ["B",2]]).codegrees() + (0, 1, 2, 0, 1, 2, 0, 2) + """ + return tuple(d - 2 for d in self.degrees()) + @cached_method - def weak_poset(self, side = "right", facade = False): + def weak_poset(self, side="right", facade=False): """ INPUT: @@ -612,3 +694,4 @@ def coxeter_knuth_graph(self): G.add_vertices(R) G.add_edges([v,vp] for v in R for vp in self.coxeter_knuth_neighbor(v)) return G + diff --git a/src/sage/categories/finite_dimensional_modules_with_basis.py b/src/sage/categories/finite_dimensional_modules_with_basis.py index 6667754a94b..efb627df714 100644 --- a/src/sage/categories/finite_dimensional_modules_with_basis.py +++ b/src/sage/categories/finite_dimensional_modules_with_basis.py @@ -252,6 +252,12 @@ def quotient_module(self, submodule, check=True, already_echelonized=False, cate sage: Y.retract(x[0]+2*x[1]) 3*y[2] + sage: R. = QQ[] + sage: C = CombinatorialFreeModule(R, range(3), prefix='x') + sage: x = C.basis() + sage: gens = [x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]] + sage: Y = X.quotient_module(gens) + .. SEEALSO:: - :meth:`Modules.WithBasis.ParentMethods.submodule` @@ -261,6 +267,7 @@ def quotient_module(self, submodule, check=True, already_echelonized=False, cate from sage.modules.with_basis.subquotient import SubmoduleWithBasis, QuotientModuleWithBasis if not isinstance(submodule, SubmoduleWithBasis): submodule = self.submodule(submodule, check=check, + unitriangular=True, already_echelonized=already_echelonized) return QuotientModuleWithBasis(submodule, category=category) diff --git a/src/sage/categories/finite_lattice_posets.py b/src/sage/categories/finite_lattice_posets.py index f149cee0524..a70997e6eca 100644 --- a/src/sage/categories/finite_lattice_posets.py +++ b/src/sage/categories/finite_lattice_posets.py @@ -24,8 +24,9 @@ class FiniteLatticePosets(CategoryWithAxiom): sage: FiniteLatticePosets().example() NotImplemented - .. seealso:: :class:`FinitePosets`, :class:`LatticePosets`, - :class:`LatticePoset` + .. SEEALSO:: + + :class:`FinitePosets`, :class:`LatticePosets`, :class:`LatticePoset` TESTS:: @@ -40,7 +41,7 @@ class ParentMethods: def join_irreducibles(self): r""" - Returns the join-irreducible elements of this finite lattice. + Return the join-irreducible elements of this finite lattice. A *join-irreducible element* of ``self`` is an element `x` that is not minimal and that can not be written as @@ -52,13 +53,17 @@ def join_irreducibles(self): sage: L.join_irreducibles() [1, 2, 4] - .. seealso:: :meth:`meet_irreducibles`, :meth:`join_irreducibles_poset` + .. SEEALSO:: + + :meth:`meet_irreducibles`, + :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.double_irreducibles`, + :meth:`meet_irreducibles_poset` """ return [x for x in self if len(self.lower_covers(x)) == 1] def join_irreducibles_poset(self): r""" - Returns the poset of join-irreducible elements of this finite lattice. + Return the poset of join-irreducible elements of this finite lattice. A *join-irreducible element* of ``self`` is an element `x` that is not minimal and can not be written as the join of two @@ -70,13 +75,13 @@ def join_irreducibles_poset(self): sage: L.join_irreducibles_poset() Finite poset containing 3 elements - .. seealso:: :meth:`join_irreducibles` + .. SEEALSO:: :meth:`join_irreducibles` """ return self.subposet(self.join_irreducibles()) def meet_irreducibles(self): r""" - Returns the meet-irreducible elements of this finite lattice. + Return the meet-irreducible elements of this finite lattice. A *meet-irreducible element* of ``self`` is an element `x` that is not maximal and that can not be written as @@ -88,13 +93,17 @@ def meet_irreducibles(self): sage: L.meet_irreducibles() [1, 3, 4] - .. seealso:: :meth:`join_irreducibles`, :meth:`meet_irreducibles_poset` + .. SEEALSO:: + + :meth:`join_irreducibles`, + :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.double_irreducibles`, + :meth:`meet_irreducibles_poset` """ return [x for x in self if len(self.upper_covers(x)) == 1] def meet_irreducibles_poset(self): r""" - Returns the poset of join-irreducible elements of this finite lattice. + Return the poset of join-irreducible elements of this finite lattice. A *meet-irreducible element* of ``self`` is an element `x` that is not maximal and can not be written as the meet of two @@ -106,7 +115,7 @@ def meet_irreducibles_poset(self): sage: L.join_irreducibles_poset() Finite poset containing 3 elements - .. seealso:: :meth:`meet_irreducibles` + .. SEEALSO:: :meth:`meet_irreducibles` """ return self.subposet(self.meet_irreducibles()) @@ -116,21 +125,27 @@ def meet_irreducibles_poset(self): def is_lattice_morphism(self, f, codomain): r""" + Return whether ``f`` is a morphism of posets from ``self`` + to ``codomain``. + + A map `f : P \to Q` is a poset morphism if + + .. MATH:: + + x \leq y \Rightarrow f(x) \leq f(y) + + for all `x,y \in P`. + INPUT: - ``f`` -- a function from ``self`` to ``codomain`` - ``codomain`` -- a lattice - Returns whether `f` is a morphism of posets form ``self`` - to ``codomain``, that is - - .. math:: x\leq y \Rightarrow f(x) \leq f(y) - EXAMPLES: We build the boolean lattice of `\{2,2,3\}` and the lattice of divisors of `60`, and check that the map - `b \mapsto 5\prod_{x\in b} x` is a morphism of lattices:: + `b \mapsto 5 \prod_{x\in b} x` is a morphism of lattices:: sage: D = LatticePoset((divisors(60), attrcall("divides"))) sage: B = LatticePoset((Subsets([2,2,3]), attrcall("issubset"))) @@ -163,7 +178,7 @@ def is_lattice_morphism(self, f, codomain): sage: B.is_lattice_morphism(f, L) False - .. seealso:: + .. SEEALSO:: :meth:`~sage.categories.finite_posets.FinitePosets.ParentMethods.is_poset_morphism` """ @@ -178,3 +193,4 @@ def is_lattice_morphism(self, f, codomain): if f(self.meet(x,y)) != codomain.meet(f(x), f(y)): return False return True + diff --git a/src/sage/categories/finite_permutation_groups.py b/src/sage/categories/finite_permutation_groups.py index 4e3df1fab56..3ec4b31b6a8 100644 --- a/src/sage/categories/finite_permutation_groups.py +++ b/src/sage/categories/finite_permutation_groups.py @@ -186,7 +186,7 @@ def cycle_index(self, parent = None): REFERENCES: - .. [Ker1991] A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. + .. [Ker1991] \A. Kerber. Algebraic combinatorics via finite group actions, 2.2 p. 70. BI-Wissenschaftsverlag, Mannheim, 1991. AUTHORS: diff --git a/src/sage/categories/finite_weyl_groups.py b/src/sage/categories/finite_weyl_groups.py index a06677f75e6..4420827c833 100644 --- a/src/sage/categories/finite_weyl_groups.py +++ b/src/sage/categories/finite_weyl_groups.py @@ -27,35 +27,7 @@ class FiniteWeylGroups(CategoryWithAxiom): TESTS:: sage: W = FiniteWeylGroups().example() - sage: TestSuite(W).run(verbose = "True") - running ._test_an_element() . . . pass - running ._test_associativity() . . . pass - running ._test_cardinality() . . . pass - running ._test_category() . . . pass - running ._test_elements() . . . - Running the test suite of self.an_element() - running ._test_category() . . . pass - running ._test_eq() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_pickling() . . . pass - pass - running ._test_elements_eq_reflexive() . . . pass - running ._test_elements_eq_symmetric() . . . pass - running ._test_elements_eq_transitive() . . . pass - running ._test_elements_neq() . . . pass - running ._test_enumerated_set_contains() . . . pass - running ._test_enumerated_set_iter_cardinality() . . . pass - running ._test_enumerated_set_iter_list() . . . pass - running ._test_eq() . . . pass - running ._test_has_descent() . . . pass - running ._test_inverse() . . . pass - running ._test_not_implemented_methods() . . . pass - running ._test_one() . . . pass - running ._test_pickling() . . . pass - running ._test_prod() . . . pass - running ._test_reduced_word() . . . pass - running ._test_simple_projections() . . . pass - running ._test_some_elements() . . . pass + sage: TestSuite(W).run() """ class ParentMethods: diff --git a/src/sage/categories/finitely_generated_magmas.py b/src/sage/categories/finitely_generated_magmas.py index 52033ee2057..fe845e8b92a 100644 --- a/src/sage/categories/finitely_generated_magmas.py +++ b/src/sage/categories/finitely_generated_magmas.py @@ -45,7 +45,7 @@ def magma_generators(self): OUTPUT: a finite family This method should be implemented by all - :class:`finitely generated magmas `_. + :class:`finitely generated magmas `. EXAMPLES:: diff --git a/src/sage/categories/finitely_generated_semigroups.py b/src/sage/categories/finitely_generated_semigroups.py index 3d3267fa8c0..0e5e48e70c0 100644 --- a/src/sage/categories/finitely_generated_semigroups.py +++ b/src/sage/categories/finitely_generated_semigroups.py @@ -103,8 +103,10 @@ def succ_generators(self, side="twosided"): - ``side``: "left", "right", or "twosided" - FIXME: find a better name for this method - FIXME: should we return a set? a family? + .. TODO:: Design choice: + + - find a better name for this method + - should we return a set? a family? EXAMPLES:: @@ -136,10 +138,12 @@ def __iter__(self): sage: S = FiniteSemigroups().example(alphabet=('x','y')) sage: it = S.__iter__() sage: list(it) - ['y', 'x', 'xy', 'yx'] + ['x', 'y', 'yx', 'xy'] """ - from sage.combinat.backtrack import TransitiveIdeal - return iter(TransitiveIdeal(self.succ_generators(side="right"), self.semigroup_generators())) + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet + return iter(RecursivelyEnumeratedSet(self.semigroup_generators(), + self.succ_generators(side="right"), + enumeration='breadth')) def ideal(self, gens, side="twosided"): r""" @@ -160,29 +164,28 @@ def ideal(self, gens, side="twosided"): sage: S = FiniteSemigroups().example() sage: list(S.ideal([S('cab')], side="left")) - ['cab', 'dcab', 'adcb', 'acb', 'bdca', 'bca', 'abdc', - 'cadb', 'acdb', 'bacd', 'abcd', 'cbad', 'abc', 'acbd', - 'dbac', 'dabc', 'cbda', 'bcad', 'cabd', 'dcba', - 'bdac', 'cba', 'badc', 'bac', 'cdab', 'dacb', 'dbca', - 'cdba', 'adbc', 'bcda'] + ['cab', 'acb', 'dcab', 'bca', 'abc', 'adcb', 'bdca', + 'cba', 'cdab', 'bac', 'dacb', 'dbca', 'adbc', 'bcda', + 'dbac', 'dabc', 'cbda', 'cdba', 'abdc', 'bdac', 'dcba', + 'cadb', 'badc', 'acdb', 'abcd', 'cbad', 'bacd', 'acbd', + 'bcad', 'cabd'] sage: list(S.ideal([S('cab')], side="right")) ['cab', 'cabd'] sage: list(S.ideal([S('cab')], side="twosided")) - ['cab', 'dcab', 'acb', 'adcb', 'acbd', 'bdca', 'bca', - 'cabd', 'abdc', 'cadb', 'acdb', 'bacd', 'abcd', 'cbad', - 'abc', 'dbac', 'dabc', 'cbda', 'bcad', 'dcba', 'bdac', - 'cba', 'cdab', 'bac', 'badc', 'dacb', 'dbca', 'cdba', - 'adbc', 'bcda'] + ['cab', 'acb', 'dcab', 'bca', 'cabd', 'abc', 'adcb', + 'acbd', 'bdca', 'bcad', 'cba', 'cdab', 'bac', 'dacb', + 'dbca', 'abcd', 'cbad', 'bacd', 'bcda', 'dbac', 'dabc', + 'cbda', 'cdba', 'abdc', 'adbc', 'bdac', 'dcba', 'cadb', + 'badc', 'acdb'] sage: list(S.ideal([S('cab')])) - ['cab', 'dcab', 'acb', 'adcb', 'acbd', 'bdca', 'bca', - 'cabd', 'abdc', 'cadb', 'acdb', 'bacd', 'abcd', 'cbad', - 'abc', 'dbac', 'dabc', 'cbda', 'bcad', 'dcba', 'bdac', - 'cba', 'cdab', 'bac', 'badc', 'dacb', 'dbca', 'cdba', - 'adbc', 'bcda'] - + ['cab', 'acb', 'dcab', 'bca', 'cabd', 'abc', 'adcb', + 'acbd', 'bdca', 'bcad', 'cba', 'cdab', 'bac', 'dacb', + 'dbca', 'abcd', 'cbad', 'bacd', 'bcda', 'dbac', 'dabc', + 'cbda', 'cdba', 'abdc', 'adbc', 'bdac', 'dcba', 'cadb', + 'badc', 'acdb'] """ - from sage.combinat.backtrack import TransitiveIdeal - return TransitiveIdeal(self.succ_generators(side = side), gens) + from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet + return RecursivelyEnumeratedSet(gens, self.succ_generators(side=side)) class Finite(CategoryWithAxiom): @@ -197,9 +200,10 @@ def some_elements(self): sage: S = FiniteSemigroups().example(alphabet=('x','y')) sage: S.some_elements() - ['y', 'x', 'xy', 'yx'] + ['x', 'y', 'yx', 'xy'] sage: S = FiniteSemigroups().example(alphabet=('x','y','z')) sage: S.some_elements() - ['y', 'x', 'xy', 'xyz', 'xz', 'yx', 'yxz', 'xzy', 'yz', 'z'] + ['x', 'y', 'z', 'xz', 'yx', 'yz', 'zx', 'zy', 'xy', 'yxz'] """ return list(itertools.islice(self, 10)) + diff --git a/src/sage/categories/functor.pyx b/src/sage/categories/functor.pyx index dfce689b84a..f44debee4b6 100644 --- a/src/sage/categories/functor.pyx +++ b/src/sage/categories/functor.pyx @@ -41,7 +41,7 @@ def _Functor_unpickle(Cl, D, domain, codomain): AUTHOR: - - Simon King (2010-12): Trac ticket #10460 + - Simon King (2010-12): :trac:`10460` EXAMPLES:: @@ -198,7 +198,7 @@ cdef class Functor(SageObject): AUTHOR: - - Simon King (2010-12): Trac ticket #10460 + - Simon King (2010-12): :trac:`10460` TESTS:: @@ -356,7 +356,7 @@ cdef class Functor(SageObject): The last example shows that it is tested whether the result of applying the functor lies in the functor's codomain. Note that the matrix functor used to be defined similar to this example, - which was fixed in trac ticket #8807:: + which was fixed in :trac:`8807`:: sage: class IllFunctor(Functor): ... def __init__(self, m,n): @@ -500,7 +500,7 @@ class ForgetfulFunctor_generic(Functor): sage: F1 = ForgetfulFunctor(FiniteFields(),Fields()) This is to test against a bug occuring in a previous version - (see ticket 8800):: + (see :trac:`8800`):: sage: F1 == QQ #indirect doctest False diff --git a/src/sage/categories/generalized_coxeter_groups.py b/src/sage/categories/generalized_coxeter_groups.py new file mode 100644 index 00000000000..ab8e6831fd1 --- /dev/null +++ b/src/sage/categories/generalized_coxeter_groups.py @@ -0,0 +1,94 @@ +r""" +Generalized Coxeter Groups +""" +#***************************************************************************** +# Copyright (C) 2016 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.categories.category_singleton import Category_singleton +from sage.categories.category_with_axiom import CategoryWithAxiom +from sage.categories.complex_reflection_or_generalized_coxeter_groups import ComplexReflectionOrGeneralizedCoxeterGroups + +class GeneralizedCoxeterGroups(Category_singleton): + r""" + The category of generalized Coxeter groups. + + A generalized Coxeter group is a group with a presentation of + the following form: + + .. MATH:: + + \langle s_i \mid s_i^{p_i}, s_i s_j \cdots = s_j s_i \cdots \rangle, + + where `p_i > 1`, `i \in I`, and the factors in the braid relation + occur `m_{ij} = m_{ji}` times for all `i \neq j \in I`. + + EXAMPLES:: + + sage: from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + sage: C = GeneralizedCoxeterGroups(); C + Category of generalized coxeter groups + + TESTS:: + + sage: TestSuite(C).run() + """ + @cached_method + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + sage: GeneralizedCoxeterGroups().super_categories() + [Category of complex reflection or generalized coxeter groups] + """ + return [ComplexReflectionOrGeneralizedCoxeterGroups()] + + def additional_structure(self): + r""" + Return ``None``. + + Indeed, all the structure generalized Coxeter groups have in + addition to groups (simple reflections, ...) is already + defined in the super category. + + .. SEEALSO:: :meth:`Category.additional_structure` + + EXAMPLES:: + + sage: from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + sage: GeneralizedCoxeterGroups().additional_structure() + """ + return None + + + class Finite(CategoryWithAxiom): + """ + The category of finite generalized Coxeter groups. + """ + def extra_super_categories(self): + """ + Implement that a finite generalized Coxeter group is a + well-generated complex reflection group. + + EXAMPLES:: + + sage: from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + + sage: Cat = GeneralizedCoxeterGroups().Finite() + sage: Cat.extra_super_categories() + [Category of well generated finite complex reflection groups] + sage: Cat.is_subcategory(ComplexReflectionGroups().Finite().WellGenerated()) + True + """ + from sage.categories.complex_reflection_groups import ComplexReflectionGroups + return [ComplexReflectionGroups().Finite().WellGenerated()] + diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index 00fc6ec3d61..5c42f1a03e0 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -696,7 +696,7 @@ cdef class Map(Element): We test that the map can be applied to something that converts (but not coerces) into the domain and can *not* be dealt with - by :meth:`pushforward` (see trac ticket #10496):: + by :meth:`pushforward` (see :trac:`10496`):: sage: D = {(0, 2): -1, (0, 0): -1, (1, 1): 7, (2, 0): 1/3} sage: phi(D) diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 9f0cf24d175..2a7f257438a 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -26,6 +26,7 @@ from sage.categories.tensor import tensor, TensorProductsCategory from sage.categories.dual import DualObjectsCategory from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring +from sage.categories.fields import Fields from sage.categories.modules import Modules from sage.categories.poor_man_map import PoorManMap from sage.rings.infinity import Infinity @@ -590,14 +591,16 @@ def _repr_(self): name = "Free module generated by {}".format(self.basis().keys()) return name + " over {}".format(self.base_ring()) - def echelon_form(self, elements): + def echelon_form(self, elements, row_reduced=False): r""" Return a basis in echelon form of the subspace spanned by a finite set of elements. INPUT: - - ``elements`` -- a list or finite iterable of elements of ``self``. + - ``elements`` -- a list or finite iterable of elements of ``self`` + - ``row_reduced`` -- (default: ``False``) whether to compute the + basis for the row reduced echelon form OUTPUT: @@ -630,14 +633,29 @@ def echelon_form(self, elements): sage: a,b,c = F.basis() sage: F.echelon_form([8*a+b+10*c, -3*a+b-c, a-b-c]) [B['a'] + B['c'], B['b'] + 2*B['c']] + + :: + + sage: R. = QQ[] + sage: C = CombinatorialFreeModule(R, range(3), prefix='x') + sage: x = C.basis() + sage: C.echelon_form([x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]]) + [x[0] - x[2], x[1] - x[2]] """ from sage.matrix.constructor import matrix - mat = matrix([g._vector_() for g in elements]) - mat.echelonize() + mat = matrix(self.base_ring(), [g._vector_() for g in elements]) + # Echelonizing a matrix over a field returned the rref + if row_reduced and self.base_ring() not in Fields: + try: + mat = mat.rref().change_ring(self.base_ring()) + except (ValueError, TypeError): + raise ValueError("unable to compute the row reduced echelon form") + else: + mat.echelonize() return [self.from_vector(vec) for vec in mat if vec] - def submodule(self, gens, - check=True, already_echelonized=False, category=None): + def submodule(self, gens, check=True, already_echelonized=False, + unitriangular=False, category=None): r""" The submodule spanned by a finite set of elements. @@ -646,11 +664,14 @@ def submodule(self, gens, - ``gens`` -- a list or family of elements of ``self`` - ``check`` -- (default: ``True``) whether to verify that the - elements of ``gens`` are in ``self``. + elements of ``gens`` are in ``self`` - ``already_echelonized`` -- (default: ``False``) whether the elements of ``gens`` are already in (not necessarily - reduced) echelon form. + reduced) echelon form + + - ``unitrangular`` -- (default: ``False``) whether + the lift morphism is unitrangular If ``already_echelonized`` is ``False``, then the generators are put in reduced echelon form using @@ -662,6 +683,10 @@ def submodule(self, gens, dimensional submodules and if matrices can be echelonized over the base ring. + If in addition ``unitriangular`` is ``True``, then + the generators are made such that the coefficients of + the pivots are 1, so that lifting map is unitriangular. + The basis of the submodule uses the same index set as the generators, and the lifting map sends `y_i` to `gens[i]`. @@ -763,15 +788,30 @@ def submodule(self, gens, sage: center = S3A.center() + We can also automatically construct a basis such that + the lift morphism is (lower) unitriangular:: + + sage: R. = QQ[] + sage: C = CombinatorialFreeModule(R, range(3), prefix='x') + sage: x = C.basis() + sage: gens = [x[0] - x[1], 2*x[1] - 2*x[2], x[0] - x[2]] + sage: Y = C.submodule(gens, unitriangular=True) + sage: Y.lift.matrix() + [ 1 0] + [ 0 1] + [-1 -1] + TESTS:: sage: TestSuite(Y).run() sage: TestSuite(center).run() """ if not already_echelonized: - gens = self.echelon_form(gens) + gens = self.echelon_form(gens, unitriangular) from sage.modules.with_basis.subquotient import SubmoduleWithBasis - return SubmoduleWithBasis(gens, ambient=self, category=category) + return SubmoduleWithBasis(gens, ambient=self, + unitriangular=unitriangular, + category=category) def tensor(*parents): """ diff --git a/src/sage/categories/morphism.pyx b/src/sage/categories/morphism.pyx index a76d6ab4aa3..e8619f74f57 100644 --- a/src/sage/categories/morphism.pyx +++ b/src/sage/categories/morphism.pyx @@ -28,7 +28,6 @@ import operator import homset -include "sage/ext/stdsage.pxi" from sage.structure.element cimport Element diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index 6aa28a7107f..efa56ba7f61 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -462,16 +462,18 @@ class implements: of classes for organizing the code. As we have seen above, the design of the class hierarchy is easy since it can be modelled upon the hierarchy of categories (bookshelves). Here is for example a piece of -the hierarchy of classes for an element of a group of matrices:: +the hierarchy of classes for an element of a group of permutations:: - sage: G = GL(2,ZZ) - sage: m = G.an_element() + sage: P = Permutations(4) + sage: m = P.an_element() sage: for cls in m.__class__.mro(): print cls - - + + + ... + ... ... diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 1b29f0d9b54..d0b649321c6 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -730,7 +730,7 @@ class PolynomialFunctor(ConstructionFunctor): sage: P(f)((x+y)*P(R).0) (-x + y)*t - By trac ticket #9944, the construction functor distinguishes sparse and + By :trac:`9944`, the construction functor distinguishes sparse and dense polynomial rings. Before, the following example failed:: sage: R. = PolynomialRing(GF(5), sparse=True) @@ -1470,7 +1470,7 @@ def _apply_functor(self, R): TEST: - The following is a test against a bug discussed at ticket #8800 + The following is a test against a bug discussed at :trac:`8800`:: sage: F = MatrixSpace(ZZ,2,3).construction()[0] sage: F(RR) # indirect doctest @@ -2606,7 +2606,7 @@ def merge(self, other): sage: pushout(Q1,Q2) # indirect doctest Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^4 + 2*x^2 + 1 - The following was fixed in trac ticket #8800:: + The following was fixed in :trac:`8800`:: sage: pushout(GF(5), Integers(5)) Finite Field of size 5 diff --git a/src/sage/categories/quotient_fields.py b/src/sage/categories/quotient_fields.py index 416effb29c5..c31f0788847 100644 --- a/src/sage/categories/quotient_fields.py +++ b/src/sage/categories/quotient_fields.py @@ -343,7 +343,7 @@ def factor(self, *args, **kwds): sage: f.factor() (x - 3)^-1 * x * (x^2 + 1) - Here is an example to show that ticket #7868 has been resolved:: + Here is an example to show that :trac:`7868` has been resolved:: sage: R. = GF(2)[] sage: f = x*y/(x+y) @@ -463,7 +463,7 @@ def partial_fraction_decomposition(self, decompose_powers=True): sage: whole + sum(parts) == q True - And also over finite fields (see trac #6052, #9945):: + And also over finite fields (see :trac:`6052`, :trac:`9945`):: sage: R. = GF(2)[] sage: q = (x+1)/(x^3+x+1) diff --git a/src/sage/categories/shephard_groups.py b/src/sage/categories/shephard_groups.py new file mode 100644 index 00000000000..e04d17cdf24 --- /dev/null +++ b/src/sage/categories/shephard_groups.py @@ -0,0 +1,42 @@ +r""" +Shephard Groups +""" +#***************************************************************************** +# Copyright (C) 2016 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.category_singleton import Category_singleton +from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups +from sage.misc.cachefunc import cached_method + +class ShephardGroups(Category_singleton): + r""" + The category of Shephard groups. + + EXAMPLES:: + + sage: from sage.categories.shephard_groups import ShephardGroups + sage: C = ShephardGroups(); C + Category of shephard groups + + TESTS:: + + sage: TestSuite(C).run() + """ + @cached_method + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.shephard_groups import ShephardGroups + sage: ShephardGroups().super_categories() + [Category of finite generalized coxeter groups] + """ + return [GeneralizedCoxeterGroups().Finite()] + diff --git a/src/sage/categories/unique_factorization_domains.py b/src/sage/categories/unique_factorization_domains.py index ddedb674d3b..8cf9a521c16 100644 --- a/src/sage/categories/unique_factorization_domains.py +++ b/src/sage/categories/unique_factorization_domains.py @@ -176,11 +176,9 @@ def _gcd_univariate_polynomial(self, f, g): if b.is_one(): break - parent = f.parent() - d = a.gcd(b) - A = parent(A/a) - B = parent(B/b) + A = A // a + B = B // b g = h = 1 delta = A.degree()-B.degree() @@ -188,9 +186,9 @@ def _gcd_univariate_polynomial(self, f, g): while R.degree() > 0: A = B - B = parent(R/(g*h**delta)) + B = R // (g*h**delta) g = A.leading_coefficient() - h = self(h*g**delta/h**delta) + h = h*g**delta // h**delta delta = A.degree() - B.degree() _, R = A.pseudo_quo_rem(B) @@ -201,7 +199,7 @@ def _gcd_univariate_polynomial(self, f, g): if b.is_one(): break - return parent(d*B/b) + return d*B // b return d diff --git a/src/sage/categories/weyl_groups.py b/src/sage/categories/weyl_groups.py index 272085a4b17..ab149a7d934 100644 --- a/src/sage/categories/weyl_groups.py +++ b/src/sage/categories/weyl_groups.py @@ -122,15 +122,18 @@ def pieri_factors(self, *args, **keywords): raise NotImplementedError("Pieri factors for type {}".format(ct)) @cached_method - def quantum_bruhat_graph(self, index_set = ()): + def quantum_bruhat_graph(self, index_set=()): r""" - Returns the quantum Bruhat graph of the quotient of the Weyl group by a parabolic subgroup `W_J`. + Return the quantum Bruhat graph of the quotient of the Weyl + group by a parabolic subgroup `W_J`. INPUT: - - ``index_set`` -- a tuple `J` of nodes of the Dynkin diagram (default: ()) + - ``index_set`` -- (default: ()) a tuple `J` of nodes of + the Dynkin diagram - By default, the value for ``index_set`` indicates that the subgroup is trivial and the quotient is the full Weyl group. + By default, the value for ``index_set`` indicates that the + subgroup is trivial and the quotient is the full Weyl group. EXAMPLES:: @@ -141,22 +144,69 @@ def quantum_bruhat_graph(self, index_set = ()): sage: g.vertices() [s2*s3*s1*s2, s3*s1*s2, s1*s2, s3*s2, s2, 1] sage: g.edges() - [(s2*s3*s1*s2, s2, alpha[2]), (s3*s1*s2, s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3]), - (s3*s1*s2, 1, alpha[2]), (s1*s2, s3*s1*s2, alpha[2] + alpha[3]), - (s3*s2, s3*s1*s2, alpha[1] + alpha[2]), (s2, s1*s2, alpha[1] + alpha[2]), - (s2, s3*s2, alpha[2] + alpha[3]), (1, s2, alpha[2])] + [(s2*s3*s1*s2, s2, alpha[2]), + (s3*s1*s2, s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3]), + (s3*s1*s2, 1, alpha[2]), + (s1*s2, s3*s1*s2, alpha[2] + alpha[3]), + (s3*s2, s3*s1*s2, alpha[1] + alpha[2]), + (s2, s1*s2, alpha[1] + alpha[2]), + (s2, s3*s2, alpha[2] + alpha[3]), + (1, s2, alpha[2])] sage: W = WeylGroup(['A',3,1], prefix="s") sage: g = W.quantum_bruhat_graph() Traceback (most recent call last): ... - ValueError: The Cartan type ['A', 3, 1] is not finite + ValueError: the Cartan type ['A', 3, 1] is not finite """ if not self.cartan_type().is_finite(): - raise ValueError("The Cartan type {} is not finite".format(self.cartan_type())) + raise ValueError("the Cartan type {} is not finite".format(self.cartan_type())) + + # Find all the minimal length coset representatives + WP = [x for x in self if all(not x.has_descent(i) for i in index_set)] + + # This is a modified form of quantum_bruhat_successors. + # It does not do any error checking and also is more efficient + # with how it handles memory and checks by using data stored + # at this function level rather than recomputing everything. + lattice = self.cartan_type().root_system().root_lattice() + NPR = lattice.nonparabolic_positive_roots(index_set) + NPR_sum = sum(NPR) + NPR_data = {} + full_NPR_sum = lattice.nonparabolic_positive_root_sum(()) + for alpha in NPR: + ref = alpha.associated_reflection() + alphacheck = alpha.associated_coroot() + NPR_data[alpha] = [self.from_reduced_word(ref), # the element + len(ref) == full_NPR_sum.scalar(alphacheck) - 1, # is_quantum + NPR_sum.scalar(alphacheck)] # the scalar + # We also create a temporary cache of lengths as they are + # relatively expensive to compute and needed frequently + len_cache = {} + def length(x): + # It is sufficient and faster to use the matrices as the keys + m = x.matrix() + if m in len_cache: + return len_cache[m] + len_cache[m] = x.length() + return len_cache[m] + def succ(x): + w_length_plus_one = length(x) + 1 + successors = [] + for alpha in NPR: + elt, is_quantum, scalar = NPR_data[alpha] + wr = x * elt + wrc = wr.coset_representative(index_set) + # coset_representative returns wr if nothing gets changed + if wrc is wr and length(wr) == w_length_plus_one: + successors.append((wr, alpha)) + elif is_quantum and length(wrc) == w_length_plus_one - scalar: + successors.append((wrc, alpha)) + return successors + from sage.graphs.digraph import DiGraph - WP = [x for x in self if x==x.coset_representative(index_set)] - return DiGraph([[x,i[0],i[1]] for x in WP for i in x.quantum_bruhat_successors(index_set, roots = True)], - name="Parabolic Quantum Bruhat Graph of %s for nodes %s"%(self, index_set)) + return DiGraph([[x,i[0],i[1]] for x in WP for i in succ(x)], + name="Parabolic Quantum Bruhat Graph of %s for nodes %s"%(self, index_set), + format="list_of_edges") class ElementMethods: @@ -389,15 +439,15 @@ def stanley_symmetric_function(self): REFERENCES: - .. [BH1994] S. Billey, M. Haiman. *Schubert polynomials for the + .. [BH1994] \S. Billey, M. Haiman. *Schubert polynomials for the classical groups*. J. Amer. Math. Soc., 1994. - .. [Lam2008] T. Lam. *Schubert polynomials for the affine + .. [Lam2008] \T. Lam. *Schubert polynomials for the affine Grassmannian*. J. Amer. Math. Soc., 2008. - .. [LSS2009] T. Lam, A. Schilling, M. Shimozono. *Schubert + .. [LSS2009] \T. Lam, A. Schilling, M. Shimozono. *Schubert polynomials for the affine Grassmannian of the symplectic group*. Mathematische Zeitschrift 264(4) (2010) 765-811 (:arxiv:`0710.2720`) - .. [Pon2010] S. Pon. *Types B and D affine Stanley symmetric + .. [Pon2010] \S. Pon. *Types B and D affine Stanley symmetric functions*, unpublished PhD Thesis, UC Davis, 2010. """ import sage.combinat.sf @@ -560,24 +610,26 @@ def inversion_arrangement(self, side='right'): def bruhat_lower_covers_coroots(self): r""" - Returns all 2-tuples (``v``, `\alpha`) where ``v`` is covered by ``self`` and `\alpha` - is the positive coroot such that ``self`` = ``v`` `s_\alpha` where `s_\alpha` is + Return all 2-tuples (``v``, `\alpha`) where ``v`` is covered + by ``self`` and `\alpha` is the positive coroot such that + ``self`` = ``v`` `s_\alpha` where `s_\alpha` is the reflection orthogonal to `\alpha`. ALGORITHM: - See :meth:`.bruhat_lower_covers` and :meth:`.bruhat_lower_covers_reflections` for Coxeter groups. + See :meth:`.bruhat_lower_covers` and + :meth:`.bruhat_lower_covers_reflections` for Coxeter groups. EXAMPLES:: sage: W = WeylGroup(['A',3], prefix="s") sage: w = W.from_reduced_word([3,1,2,1]) sage: w.bruhat_lower_covers_coroots() - [(s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3]), (s3*s2*s1, alphacheck[2]), (s3*s1*s2, alphacheck[1])] - + [(s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3]), + (s3*s2*s1, alphacheck[2]), (s3*s1*s2, alphacheck[1])] """ - - return [(x[0],x[1].reflection_to_coroot()) for x in self.bruhat_lower_covers_reflections()] + return [(x[0],x[1].reflection_to_coroot()) + for x in self.bruhat_lower_covers_reflections()] def bruhat_upper_covers_coroots(self): r""" @@ -594,41 +646,52 @@ def bruhat_upper_covers_coroots(self): sage: W = WeylGroup(['A',4], prefix="s") sage: w = W.from_reduced_word([3,1,2,1]) sage: w.bruhat_upper_covers_coroots() - [(s1*s2*s3*s2*s1, alphacheck[3]), (s2*s3*s1*s2*s1, alphacheck[2] + alphacheck[3]), (s3*s4*s1*s2*s1, alphacheck[4]), (s4*s3*s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3] + alphacheck[4])] - + [(s1*s2*s3*s2*s1, alphacheck[3]), + (s2*s3*s1*s2*s1, alphacheck[2] + alphacheck[3]), + (s3*s4*s1*s2*s1, alphacheck[4]), + (s4*s3*s1*s2*s1, alphacheck[1] + alphacheck[2] + alphacheck[3] + alphacheck[4])] """ + return [(x[0],x[1].reflection_to_coroot()) + for x in self.bruhat_upper_covers_reflections()] - return [(x[0],x[1].reflection_to_coroot()) for x in self.bruhat_upper_covers_reflections()] - - def quantum_bruhat_successors(self, index_set = None, roots = False, quantum_only = False): + def quantum_bruhat_successors(self, index_set=None, roots=False, quantum_only=False): r""" - Returns the successors of ``self`` in the parabolic quantum Bruhat graph. + Return the successors of ``self`` in the quantum Bruhat graph + on the parabolic quotient of the Weyl group determined by the + subset of Dynkin nodes ``index_set``. INPUT: - - ``self`` -- a Weyl group element, which is assumed to be of minimum length in its coset with respect to the parabolic subgroup + - ``self`` -- a Weyl group element, which is assumed to + be of minimum length in its coset with respect to the + parabolic subgroup - - ``index_set`` -- (default: None) indicates the set of simple reflections used to generate the parabolic subgroup; - the default value indicates that the subgroup is the identity + - ``index_set`` -- (default: ``None``) indicates the set of + simple reflections used to generate the parabolic subgroup; + the default value indicates that the subgroup is the identity - - ``roots`` -- (default: False) if True, returns the list of 2-tuples (``w``, `\alpha`) where ``w`` is a - successor and `\alpha` is the positive root associated with the successor relation. + - ``roots`` -- (default: ``False``) if ``True``, returns the + list of 2-tuples (``w``, `\alpha`) where ``w`` is a successor + and `\alpha` is the positive root associated with the + successor relation - - ``quantum_only`` -- (default: False) if True, returns only the quantum successors - - Returns the successors of ``self`` in the quantum Bruhat graph on the parabolic - quotient of the Weyl group determined by the subset of Dynkin nodes ``index_set``. + - ``quantum_only`` -- (default: ``False``) if ``True``, returns + only the quantum successors EXAMPLES:: sage: W = WeylGroup(['A',3], prefix="s") sage: w = W.from_reduced_word([3,1,2]) sage: w.quantum_bruhat_successors([1], roots = True) - [(s3, alpha[2]), (s1*s2*s3*s2, alpha[3]), (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] + [(s3, alpha[2]), (s1*s2*s3*s2, alpha[3]), + (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] sage: w.quantum_bruhat_successors([1,3]) [1, s2*s3*s1*s2] sage: w.quantum_bruhat_successors(roots = True) - [(s3*s1*s2*s1, alpha[1]), (s3*s1, alpha[2]), (s1*s2*s3*s2, alpha[3]), (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] + [(s3*s1*s2*s1, alpha[1]), + (s3*s1, alpha[2]), + (s1*s2*s3*s2, alpha[3]), + (s2*s3*s1*s2, alpha[1] + alpha[2] + alpha[3])] sage: w.quantum_bruhat_successors() [s3*s1*s2*s1, s3*s1, s1*s2*s3*s2, s2*s3*s1*s2] sage: w.quantum_bruhat_successors(quantum_only = True) @@ -638,11 +701,10 @@ def quantum_bruhat_successors(self, index_set = None, roots = False, quantum_onl Traceback (most recent call last): ... ValueError: s2*s3 is not of minimum length in its coset of the parabolic subgroup generated by the reflections (1, 3) - """ W = self.parent() if not W.cartan_type().is_finite(): - raise ValueError("The Cartan type {} is not finite".format(W.cartan_type())) + raise ValueError("the Cartan type {} is not finite".format(W.cartan_type())) if index_set is None: index_set = [] else: diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 5912596f906..c40ba2f7fc7 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -36,7 +36,7 @@ "bounds_minimum_distance", "self_orthogonal_binary_codes"]) -lazy_import("sage.coding.delsarte_bounds", ["Krawtchouk", +lazy_import("sage.coding.delsarte_bounds", ["Krawtchouk", "Kravchuk", "delsarte_bound_hamming_space", "delsarte_bound_additive_hamming_space"]) diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 89d9c5157a0..d8fa5e73e0f 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -39,7 +39,7 @@ AUTHOR: include 'sage/ext/cdefs.pxi' from cpython.mem cimport * -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from sage.structure.element import is_Matrix from sage.misc.misc import cputime from sage.rings.integer cimport Integer @@ -63,9 +63,9 @@ cdef inline int min(int a, int b): cdef int *hamming_weights(): cdef int *ham_wts cdef int i - ham_wts = sage_malloc( 65536 * sizeof(int) ) + ham_wts = sig_malloc( 65536 * sizeof(int) ) if ham_wts is NULL: - sage_free(ham_wts) + sig_free(ham_wts) raise MemoryError("Memory.") ham_wts[0] = 0 ham_wts[1] = 1 @@ -118,8 +118,8 @@ def weight_dist(M): cdef bitset_t word cdef int i,j,k, dim=M.nrows(), deg=M.ncols() cdef list L - cdef int *LL = sage_malloc((deg+1) * sizeof(int)) - cdef bitset_s *basis = sage_malloc(dim * sizeof(bitset_s)) + cdef int *LL = sig_malloc((deg+1) * sizeof(int)) + cdef bitset_s *basis = sig_malloc(dim * sizeof(bitset_s)) for i from 0 <= i < dim: bitset_init(&basis[i], deg) bitset_zero(&basis[i]) @@ -145,8 +145,8 @@ def weight_dist(M): L = [int(LL[i]) for i from 0 <= i < deg+1] for i from 0 <= i < dim: bitset_free(&basis[i]) - sage_free(LL) - sage_free(basis) + sig_free(LL) + sig_free(basis) return L def test_word_perms(t_limit=5.0): @@ -192,7 +192,7 @@ def test_word_perms(t_limit=5.0): cdef codeword cw1, cw2, cw3 cdef int n = sizeof(codeword) << 3 cdef int j - cdef int *arr = sage_malloc(n * sizeof(int)) + cdef int *arr = sig_malloc(n * sizeof(int)) if arr is NULL: raise MemoryError("Error allocating memory.") from sage.misc.prandom import randint @@ -269,7 +269,7 @@ def test_word_perms(t_limit=5.0): dealloc_word_perm(g) dealloc_word_perm(h) dealloc_word_perm(i) - sage_free(arr) + sig_free(arr) cdef WordPermutation *create_word_perm(object list_perm): r""" @@ -279,16 +279,16 @@ cdef WordPermutation *create_word_perm(object list_perm): cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1 cdef codeword *images_i cdef codeword image - cdef WordPermutation *word_perm = sage_malloc( sizeof(WordPermutation) ) + cdef WordPermutation *word_perm = sig_malloc( sizeof(WordPermutation) ) if word_perm is NULL: raise RuntimeError("Error allocating memory.") word_perm.degree = len(list_perm) list_perm = copy(list_perm) while num_chunks*chunk_size < word_perm.degree: num_chunks += 1 - word_perm.images = sage_malloc(num_chunks * sizeof(codeword *)) + word_perm.images = sig_malloc(num_chunks * sizeof(codeword *)) if word_perm.images is NULL: - sage_free(word_perm) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.chunk_num = num_chunks words_per_chunk = 1 << chunk_size @@ -296,12 +296,12 @@ cdef WordPermutation *create_word_perm(object list_perm): list_perm += range(len(list_perm), chunk_size*num_chunks) word_perm.chunk_words = words_per_chunk for i from 0 <= i < num_chunks: - images_i = sage_malloc(words_per_chunk * sizeof(codeword)) + images_i = sig_malloc(words_per_chunk * sizeof(codeword)) if images_i is NULL: for j from 0 <= j < i: - sage_free(word_perm.images[j]) - sage_free(word_perm.images) - sage_free(word_perm) + sig_free(word_perm.images[j]) + sig_free(word_perm.images) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.images[i] = images_i for j from 0 <= j < chunk_size: @@ -329,27 +329,27 @@ cdef WordPermutation *create_array_word_perm(int *array, int start, int degree): cdef int i, j, cslim, parity, comb, words_per_chunk, num_chunks = 1 cdef codeword *images_i cdef codeword image - cdef WordPermutation *word_perm = sage_malloc( sizeof(WordPermutation) ) + cdef WordPermutation *word_perm = sig_malloc( sizeof(WordPermutation) ) if word_perm is NULL: raise RuntimeError("Error allocating memory.") word_perm.degree = degree while num_chunks*chunk_size < word_perm.degree: num_chunks += 1 - word_perm.images = sage_malloc(num_chunks * sizeof(codeword *)) + word_perm.images = sig_malloc(num_chunks * sizeof(codeword *)) if word_perm.images is NULL: - sage_free(word_perm) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.chunk_num = num_chunks words_per_chunk = 1 << chunk_size word_perm.gate = ( (1) << chunk_size ) - 1 word_perm.chunk_words = words_per_chunk for i from 0 <= i < num_chunks: - images_i = sage_malloc(words_per_chunk * sizeof(codeword)) + images_i = sig_malloc(words_per_chunk * sizeof(codeword)) if images_i is NULL: for j from 0 <= j < i: - sage_free(word_perm.images[j]) - sage_free(word_perm.images) - sage_free(word_perm) + sig_free(word_perm.images[j]) + sig_free(word_perm.images) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.images[i] = images_i cslim = min(chunk_size, degree - i*chunk_size) @@ -378,27 +378,27 @@ cdef WordPermutation *create_id_word_perm(int degree): cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1 cdef codeword *images_i cdef codeword image - cdef WordPermutation *word_perm = sage_malloc( sizeof(WordPermutation) ) + cdef WordPermutation *word_perm = sig_malloc( sizeof(WordPermutation) ) if word_perm is NULL: raise RuntimeError("Error allocating memory.") word_perm.degree = degree while num_chunks*chunk_size < degree: num_chunks += 1 - word_perm.images = sage_malloc(num_chunks * sizeof(codeword *)) + word_perm.images = sig_malloc(num_chunks * sizeof(codeword *)) if word_perm.images is NULL: - sage_free(word_perm) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.chunk_num = num_chunks words_per_chunk = 1 << chunk_size word_perm.gate = ( (1) << chunk_size ) - 1 word_perm.chunk_words = words_per_chunk for i from 0 <= i < num_chunks: - images_i = sage_malloc(words_per_chunk * sizeof(codeword)) + images_i = sig_malloc(words_per_chunk * sizeof(codeword)) if images_i is NULL: for j from 0 <= j < i: - sage_free(word_perm.images[j]) - sage_free(word_perm.images) - sage_free(word_perm) + sig_free(word_perm.images[j]) + sig_free(word_perm.images) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.images[i] = images_i for j from 0 <= j < chunk_size: @@ -426,27 +426,27 @@ cdef WordPermutation *create_comp_word_perm(WordPermutation *g, WordPermutation cdef int i, j, parity, comb, words_per_chunk, num_chunks = 1 cdef codeword *images_i cdef codeword image - cdef WordPermutation *word_perm = sage_malloc( sizeof(WordPermutation) ) + cdef WordPermutation *word_perm = sig_malloc( sizeof(WordPermutation) ) if word_perm is NULL: raise RuntimeError("Error allocating memory.") word_perm.degree = g.degree while num_chunks*chunk_size < word_perm.degree: num_chunks += 1 - word_perm.images = sage_malloc(num_chunks * sizeof(codeword *)) + word_perm.images = sig_malloc(num_chunks * sizeof(codeword *)) if word_perm.images is NULL: - sage_free(word_perm) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.chunk_num = num_chunks words_per_chunk = 1 << chunk_size word_perm.gate = ( (1) << chunk_size ) - 1 word_perm.chunk_words = words_per_chunk for i from 0 <= i < num_chunks: - images_i = sage_malloc(words_per_chunk * sizeof(codeword)) + images_i = sig_malloc(words_per_chunk * sizeof(codeword)) if images_i is NULL: for j from 0 <= j < i: - sage_free(word_perm.images[j]) - sage_free(word_perm.images) - sage_free(word_perm) + sig_free(word_perm.images[j]) + sig_free(word_perm.images) + sig_free(word_perm) raise RuntimeError("Error allocating memory.") word_perm.images[i] = images_i for j from 0 <= j < chunk_size: @@ -475,7 +475,7 @@ cdef WordPermutation *create_inv_word_perm(WordPermutation *g): Create the inverse $g^{-1}$ of the word permutation of $g$. """ cdef int i, j - cdef int *array = sage_malloc( g.degree * sizeof(int) ) + cdef int *array = sig_malloc( g.degree * sizeof(int) ) cdef codeword temp cdef WordPermutation *w for i from 0 <= i < g.degree: @@ -485,7 +485,7 @@ cdef WordPermutation *create_inv_word_perm(WordPermutation *g): j += 1 array[j] = i w = create_array_word_perm(array, 0, g.degree) - sage_free(array) + sig_free(array) return w cdef int dealloc_word_perm(WordPermutation *wp): @@ -494,9 +494,9 @@ cdef int dealloc_word_perm(WordPermutation *wp): """ cdef int i for i from 0 <= i < wp.chunk_num: - sage_free(wp.images[i]) - sage_free(wp.images) - sage_free(wp) + sig_free(wp.images[i]) + sig_free(wp.images) + sig_free(wp) cdef codeword permute_word_by_wp(WordPermutation *wp, codeword word): """ @@ -563,7 +563,7 @@ def test_expand_to_ortho_basis(B=None): k += 1 for i from 0 <= i < k: print ''.join(reversed(Integer(output[i]).binary().zfill(C.ncols))) - sage_free(output) + sig_free(output) cdef codeword *expand_to_ortho_basis(BinaryCode B, int n): r""" @@ -585,7 +585,7 @@ cdef codeword *expand_to_ortho_basis(BinaryCode B, int n): cdef codeword n_gate = (~0) >> ( (sizeof(codeword)<<3) - n) cdef int i, j, m, k = B.nrows, dead, d cdef WordPermutation *wp - basis = sage_malloc( (n+1) * sizeof(codeword) ) + basis = sig_malloc( (n+1) * sizeof(codeword) ) if basis is NULL: raise MemoryError() for i from 0 <= i < k: @@ -765,11 +765,11 @@ cdef class BinaryCode: if self.nrows >= self.radix or self.ncols > self.radix: raise NotImplementedError("Columns and rows are stored as ints. This code is too big.") - self.words = sage_malloc( nwords * sizeof(int) ) - self.basis = sage_malloc( nrows * sizeof(int) ) + self.words = sig_malloc( nwords * sizeof(int) ) + self.basis = sig_malloc( nrows * sizeof(int) ) if self.words is NULL or self.basis is NULL: - if self.words is not NULL: sage_free(self.words) - if self.basis is not NULL: sage_free(self.basis) + if self.words is not NULL: sig_free(self.words) + if self.basis is not NULL: sig_free(self.basis) raise MemoryError("Memory.") self_words = self.words self_basis = self.basis @@ -810,8 +810,8 @@ cdef class BinaryCode: self_words[combination+other_nwords] = self_words[combination] ^ glue_word def __dealloc__(self): - sage_free(self.words) - sage_free(self.basis) + sig_free(self.words) + sig_free(self.basis) def __reduce__(self): """ @@ -861,7 +861,7 @@ cdef class BinaryCode: """ cdef int i, j from sage.matrix.constructor import matrix - from sage.rings.all import GF + from sage.rings.finite_rings.finite_field_constructor import GF rows = [] for i from 0 <= i < self.nrows: row = [0]*self.ncols @@ -1084,19 +1084,19 @@ cdef class BinaryCode: cdef int i cdef int *_col_gamma cdef int *_word_gamma - _word_gamma = sage_malloc(self.nwords * sizeof(int)) - _col_gamma = sage_malloc(self.ncols * sizeof(int)) + _word_gamma = sig_malloc(self.nwords * sizeof(int)) + _col_gamma = sig_malloc(self.ncols * sizeof(int)) if _col_gamma is NULL or _word_gamma is NULL: - if _word_gamma is not NULL: sage_free(_word_gamma) - if _col_gamma is not NULL: sage_free(_col_gamma) + if _word_gamma is not NULL: sig_free(_word_gamma) + if _col_gamma is not NULL: sig_free(_col_gamma) raise MemoryError("Memory.") for i from 0 <= i < self.nwords: _word_gamma[i] = word_gamma[i] for i from 0 <= i < self.ncols: _col_gamma[i] = col_gamma[i] result = self.is_automorphism(_col_gamma, _word_gamma) - sage_free(_col_gamma) - sage_free(_word_gamma) + sig_free(_col_gamma) + sig_free(_word_gamma) return result cdef int is_automorphism(self, int *col_gamma, int *word_gamma): @@ -1256,25 +1256,25 @@ cdef class OrbitPartition: nwords = (1 << nrows) self.nwords = nwords self.ncols = ncols - self.wd_parent = sage_malloc( nwords * sizeof(int) ) - self.wd_rank = sage_malloc( nwords * sizeof(int) ) - self.wd_min_cell_rep = sage_malloc( nwords * sizeof(int) ) - self.wd_size = sage_malloc( nwords * sizeof(int) ) - self.col_parent = sage_malloc( ncols * sizeof(int) ) - self.col_rank = sage_malloc( ncols * sizeof(int) ) - self.col_min_cell_rep = sage_malloc( ncols * sizeof(int) ) - self.col_size = sage_malloc( ncols * sizeof(int) ) + self.wd_parent = sig_malloc( nwords * sizeof(int) ) + self.wd_rank = sig_malloc( nwords * sizeof(int) ) + self.wd_min_cell_rep = sig_malloc( nwords * sizeof(int) ) + self.wd_size = sig_malloc( nwords * sizeof(int) ) + self.col_parent = sig_malloc( ncols * sizeof(int) ) + self.col_rank = sig_malloc( ncols * sizeof(int) ) + self.col_min_cell_rep = sig_malloc( ncols * sizeof(int) ) + self.col_size = sig_malloc( ncols * sizeof(int) ) if self.wd_parent is NULL or self.wd_rank is NULL or self.wd_min_cell_rep is NULL \ or self.wd_size is NULL or self.col_parent is NULL or self.col_rank is NULL \ or self.col_min_cell_rep is NULL or self.col_size is NULL: - if self.wd_parent is not NULL: sage_free(self.wd_parent) - if self.wd_rank is not NULL: sage_free(self.wd_rank) - if self.wd_min_cell_rep is not NULL: sage_free(self.wd_min_cell_rep) - if self.wd_size is not NULL: sage_free(self.wd_size) - if self.col_parent is not NULL: sage_free(self.col_parent) - if self.col_rank is not NULL: sage_free(self.col_rank) - if self.col_min_cell_rep is not NULL: sage_free(self.col_min_cell_rep) - if self.col_size is not NULL: sage_free(self.col_size) + if self.wd_parent is not NULL: sig_free(self.wd_parent) + if self.wd_rank is not NULL: sig_free(self.wd_rank) + if self.wd_min_cell_rep is not NULL: sig_free(self.wd_min_cell_rep) + if self.wd_size is not NULL: sig_free(self.wd_size) + if self.col_parent is not NULL: sig_free(self.col_parent) + if self.col_rank is not NULL: sig_free(self.col_rank) + if self.col_min_cell_rep is not NULL: sig_free(self.col_min_cell_rep) + if self.col_size is not NULL: sig_free(self.col_size) raise MemoryError("Memory.") for word from 0 <= word < nwords: self.wd_parent[word] = word @@ -1288,14 +1288,14 @@ cdef class OrbitPartition: self.col_size[col] = 1 def __dealloc__(self): - sage_free(self.wd_parent) - sage_free(self.wd_rank) - sage_free(self.wd_min_cell_rep) - sage_free(self.wd_size) - sage_free(self.col_parent) - sage_free(self.col_rank) - sage_free(self.col_min_cell_rep) - sage_free(self.col_size) + sig_free(self.wd_parent) + sig_free(self.wd_rank) + sig_free(self.wd_min_cell_rep) + sig_free(self.wd_size) + sig_free(self.col_parent) + sig_free(self.col_rank) + sig_free(self.col_min_cell_rep) + sig_free(self.col_size) def __repr__(self): """ @@ -1516,19 +1516,19 @@ cdef class OrbitPartition: cdef int i cdef int *_col_gamma cdef int *_wd_gamma - _wd_gamma = sage_malloc(self.nwords * sizeof(int)) - _col_gamma = sage_malloc(self.ncols * sizeof(int)) + _wd_gamma = sig_malloc(self.nwords * sizeof(int)) + _col_gamma = sig_malloc(self.ncols * sizeof(int)) if _col_gamma is NULL or _wd_gamma is NULL: - if _wd_gamma is not NULL: sage_free(_wd_gamma) - if _col_gamma is not NULL: sage_free(_col_gamma) + if _wd_gamma is not NULL: sig_free(_wd_gamma) + if _col_gamma is not NULL: sig_free(_col_gamma) raise MemoryError("Memory.") for i from 0 <= i < self.nwords: _wd_gamma[i] = wd_gamma[i] for i from 0 <= i < self.ncols: _col_gamma[i] = col_gamma[i] result = self.merge_perm(_col_gamma, _wd_gamma) - sage_free(_col_gamma) - sage_free(_wd_gamma) + sig_free(_col_gamma) + sig_free(_wd_gamma) return result cdef int merge_perm(self, int *col_gamma, int *wd_gamma): @@ -1585,33 +1585,33 @@ cdef class PartitionStack: self.flag = (1 << (self.radix-1)) # data - self.wd_ents = sage_malloc( self.nwords * sizeof_int ) - self.wd_lvls = sage_malloc( self.nwords * sizeof_int ) - self.col_ents = sage_malloc( self.ncols * sizeof_int ) - self.col_lvls = sage_malloc( self.ncols * sizeof_int ) + self.wd_ents = sig_malloc( self.nwords * sizeof_int ) + self.wd_lvls = sig_malloc( self.nwords * sizeof_int ) + self.col_ents = sig_malloc( self.ncols * sizeof_int ) + self.col_lvls = sig_malloc( self.ncols * sizeof_int ) # scratch space - self.col_degs = sage_malloc( self.ncols * sizeof_int ) - self.col_counts = sage_malloc( self.nwords * sizeof_int ) - self.col_output = sage_malloc( self.ncols * sizeof_int ) - self.wd_degs = sage_malloc( self.nwords * sizeof_int ) - self.wd_counts = sage_malloc( (self.ncols+1) * sizeof_int ) - self.wd_output = sage_malloc( self.nwords * sizeof_int ) + self.col_degs = sig_malloc( self.ncols * sizeof_int ) + self.col_counts = sig_malloc( self.nwords * sizeof_int ) + self.col_output = sig_malloc( self.ncols * sizeof_int ) + self.wd_degs = sig_malloc( self.nwords * sizeof_int ) + self.wd_counts = sig_malloc( (self.ncols+1) * sizeof_int ) + self.wd_output = sig_malloc( self.nwords * sizeof_int ) if self.wd_ents is NULL or self.wd_lvls is NULL or self.col_ents is NULL \ or self.col_lvls is NULL or self.col_degs is NULL or self.col_counts is NULL \ or self.col_output is NULL or self.wd_degs is NULL or self.wd_counts is NULL \ or self.wd_output is NULL: - if self.wd_ents is not NULL: sage_free(self.wd_ents) - if self.wd_lvls is not NULL: sage_free(self.wd_lvls) - if self.col_ents is not NULL: sage_free(self.col_ents) - if self.col_lvls is not NULL: sage_free(self.col_lvls) - if self.col_degs is not NULL: sage_free(self.col_degs) - if self.col_counts is not NULL: sage_free(self.col_counts) - if self.col_output is not NULL: sage_free(self.col_output) - if self.wd_degs is not NULL: sage_free(self.wd_degs) - if self.wd_counts is not NULL: sage_free(self.wd_counts) - if self.wd_output is not NULL: sage_free(self.wd_output) + if self.wd_ents is not NULL: sig_free(self.wd_ents) + if self.wd_lvls is not NULL: sig_free(self.wd_lvls) + if self.col_ents is not NULL: sig_free(self.col_ents) + if self.col_lvls is not NULL: sig_free(self.col_lvls) + if self.col_degs is not NULL: sig_free(self.col_degs) + if self.col_counts is not NULL: sig_free(self.col_counts) + if self.col_output is not NULL: sig_free(self.col_output) + if self.wd_degs is not NULL: sig_free(self.wd_degs) + if self.wd_counts is not NULL: sig_free(self.wd_counts) + if self.wd_output is not NULL: sig_free(self.wd_output) raise MemoryError("Memory.") nwords = self.nwords @@ -1655,17 +1655,17 @@ cdef class PartitionStack: wd_output[k]=0 def __dealloc__(self): - if self.basis_locations: sage_free(self.basis_locations) - sage_free(self.wd_ents) - sage_free(self.wd_lvls) - sage_free(self.col_ents) - sage_free(self.col_lvls) - sage_free(self.col_degs) - sage_free(self.col_counts) - sage_free(self.col_output) - sage_free(self.wd_degs) - sage_free(self.wd_counts) - sage_free(self.wd_output) + if self.basis_locations: sig_free(self.basis_locations) + sig_free(self.wd_ents) + sig_free(self.wd_lvls) + sig_free(self.col_ents) + sig_free(self.col_lvls) + sig_free(self.col_degs) + sig_free(self.col_counts) + sig_free(self.col_output) + sig_free(self.wd_degs) + sig_free(self.wd_counts) + sig_free(self.wd_output) def print_data(self): """ @@ -2500,7 +2500,7 @@ cdef class PartitionStack: """ cdef int *ham_wts = hamming_weights() result = self.wd_degree(C, wd, col_ptr, k, ham_wts) - sage_free(ham_wts) + sig_free(ham_wts) return result cdef int wd_degree(self, BinaryCode CG, int wd, int col_ptr, int k, int *ham_wts): @@ -2704,7 +2704,7 @@ cdef class PartitionStack: """ cdef int i, alpha_length = len(alpha) - cdef int *_alpha = sage_malloc( (self.nwords + self.ncols) * sizeof(int) ) + cdef int *_alpha = sig_malloc( (self.nwords + self.ncols) * sizeof(int) ) cdef int *ham_wts = hamming_weights() if _alpha is NULL: raise MemoryError("Memory.") @@ -2714,8 +2714,8 @@ cdef class PartitionStack: else: _alpha[i] = alpha[i][1] result = self.refine(k, _alpha, alpha_length, CG, ham_wts) - sage_free(_alpha) - sage_free(ham_wts) + sig_free(_alpha) + sig_free(ham_wts) return result cdef int refine(self, int k, int *alpha, int alpha_length, BinaryCode CG, int *ham_wts): @@ -2965,13 +2965,13 @@ cdef class PartitionStack: cdef int i cdef int *ham_wts = hamming_weights() self.find_basis(ham_wts) - sage_free(ham_wts) + sig_free(ham_wts) cdef int find_basis(self, int *ham_wts): cdef int i = 0, j, k, nwords = self.nwords, weight, basis_elts = 0, nrows = self.nrows cdef int *self_wd_ents = self.wd_ents if self.basis_locations is NULL: - self.basis_locations = sage_malloc( 2 * nrows * sizeof(int) ) + self.basis_locations = sig_malloc( 2 * nrows * sizeof(int) ) if self.basis_locations is NULL: raise MemoryError("Memory.") while i < nwords: @@ -3030,17 +3030,17 @@ cdef class PartitionStack: """ cdef int i - cdef int *word_g = sage_malloc( self.nwords * sizeof(int) ) - cdef int *col_g = sage_malloc( self.ncols * sizeof(int) ) + cdef int *word_g = sig_malloc( self.nwords * sizeof(int) ) + cdef int *col_g = sig_malloc( self.ncols * sizeof(int) ) if word_g is NULL or col_g is NULL: - if word_g is not NULL: sage_free(word_g) - if col_g is not NULL: sage_free(col_g) + if word_g is not NULL: sig_free(word_g) + if col_g is not NULL: sig_free(col_g) raise MemoryError("Memory.") self.get_permutation(other, word_g, col_g) word_l = [word_g[i] for i from 0 <= i < self.nwords] col_l = [col_g[i] for i from 0 <= i < self.ncols] - sage_free(word_g) - sage_free(col_g) + sig_free(word_g) + sig_free(col_g) return word_l, col_l cdef void get_permutation(self, PartitionStack other, int *word_gamma, int *col_gamma): @@ -3067,64 +3067,64 @@ cdef class BinaryCodeClassifier: self.alpha_size = self.w_gamma_size + self.radix self.Phi_size = self.w_gamma_size/self.radix + 1 - self.w_gamma = sage_malloc( self.w_gamma_size * sizeof(int) ) - self.alpha = sage_malloc( self.alpha_size * sizeof(int) ) - self.Phi = sage_malloc( self.Phi_size * (self.L+1) * sizeof(unsigned int) ) - self.Omega = sage_malloc( self.Phi_size * self.L * sizeof(unsigned int) ) - self.W = sage_malloc( self.Phi_size * self.radix * 2 * sizeof(unsigned int) ) - - self.base = sage_malloc( self.radix * sizeof(int) ) - self.aut_gp_gens = sage_malloc( self.aut_gens_size * sizeof(int) ) - self.c_gamma = sage_malloc( self.radix * sizeof(int) ) - self.labeling = sage_malloc( self.radix * 3 * sizeof(int) ) - self.Lambda1 = sage_malloc( self.radix * 2 * sizeof(int) ) - self.Lambda2 = sage_malloc( self.radix * 2 * sizeof(int) ) - self.Lambda3 = sage_malloc( self.radix * 2 * sizeof(int) ) - self.v = sage_malloc( self.radix * 2 * sizeof(int) ) - self.e = sage_malloc( self.radix * 2 * sizeof(int) ) + self.w_gamma = sig_malloc( self.w_gamma_size * sizeof(int) ) + self.alpha = sig_malloc( self.alpha_size * sizeof(int) ) + self.Phi = sig_malloc( self.Phi_size * (self.L+1) * sizeof(unsigned int) ) + self.Omega = sig_malloc( self.Phi_size * self.L * sizeof(unsigned int) ) + self.W = sig_malloc( self.Phi_size * self.radix * 2 * sizeof(unsigned int) ) + + self.base = sig_malloc( self.radix * sizeof(int) ) + self.aut_gp_gens = sig_malloc( self.aut_gens_size * sizeof(int) ) + self.c_gamma = sig_malloc( self.radix * sizeof(int) ) + self.labeling = sig_malloc( self.radix * 3 * sizeof(int) ) + self.Lambda1 = sig_malloc( self.radix * 2 * sizeof(int) ) + self.Lambda2 = sig_malloc( self.radix * 2 * sizeof(int) ) + self.Lambda3 = sig_malloc( self.radix * 2 * sizeof(int) ) + self.v = sig_malloc( self.radix * 2 * sizeof(int) ) + self.e = sig_malloc( self.radix * 2 * sizeof(int) ) if self.Phi is NULL or self.Omega is NULL or self.W is NULL or self.Lambda1 is NULL \ or self.Lambda2 is NULL or self.Lambda3 is NULL or self.w_gamma is NULL \ or self.c_gamma is NULL or self.alpha is NULL or self.v is NULL or self.e is NULL \ or self.aut_gp_gens is NULL or self.labeling is NULL or self.base is NULL: - if self.Phi is not NULL: sage_free(self.Phi) - if self.Omega is not NULL: sage_free(self.Omega) - if self.W is not NULL: sage_free(self.W) - if self.Lambda1 is not NULL: sage_free(self.Lambda1) - if self.Lambda2 is not NULL: sage_free(self.Lambda2) - if self.Lambda3 is not NULL: sage_free(self.Lambda3) - if self.w_gamma is not NULL: sage_free(self.w_gamma) - if self.c_gamma is not NULL: sage_free(self.c_gamma) - if self.alpha is not NULL: sage_free(self.alpha) - if self.v is not NULL: sage_free(self.v) - if self.e is not NULL: sage_free(self.e) - if self.aut_gp_gens is not NULL: sage_free(self.aut_gp_gens) - if self.labeling is not NULL: sage_free(self.labeling) - if self.base is not NULL: sage_free(self.base) + if self.Phi is not NULL: sig_free(self.Phi) + if self.Omega is not NULL: sig_free(self.Omega) + if self.W is not NULL: sig_free(self.W) + if self.Lambda1 is not NULL: sig_free(self.Lambda1) + if self.Lambda2 is not NULL: sig_free(self.Lambda2) + if self.Lambda3 is not NULL: sig_free(self.Lambda3) + if self.w_gamma is not NULL: sig_free(self.w_gamma) + if self.c_gamma is not NULL: sig_free(self.c_gamma) + if self.alpha is not NULL: sig_free(self.alpha) + if self.v is not NULL: sig_free(self.v) + if self.e is not NULL: sig_free(self.e) + if self.aut_gp_gens is not NULL: sig_free(self.aut_gp_gens) + if self.labeling is not NULL: sig_free(self.labeling) + if self.base is not NULL: sig_free(self.base) raise MemoryError("Memory.") def __dealloc__(self): - sage_free(self.ham_wts) - sage_free(self.Phi) - sage_free(self.Omega) - sage_free(self.W) - sage_free(self.Lambda1) - sage_free(self.Lambda2) - sage_free(self.Lambda3) - sage_free(self.c_gamma) - sage_free(self.w_gamma) - sage_free(self.alpha) - sage_free(self.v) - sage_free(self.e) - sage_free(self.aut_gp_gens) - sage_free(self.labeling) - sage_free(self.base) + sig_free(self.ham_wts) + sig_free(self.Phi) + sig_free(self.Omega) + sig_free(self.W) + sig_free(self.Lambda1) + sig_free(self.Lambda2) + sig_free(self.Lambda3) + sig_free(self.c_gamma) + sig_free(self.w_gamma) + sig_free(self.alpha) + sig_free(self.v) + sig_free(self.e) + sig_free(self.aut_gp_gens) + sig_free(self.labeling) + sig_free(self.base) cdef void record_automorphism(self, int *gamma, int ncols): cdef int i, j if self.aut_gp_index + ncols > self.aut_gens_size: self.aut_gens_size *= 2 - self.aut_gp_gens = sage_realloc( self.aut_gp_gens, self.aut_gens_size * sizeof(int) ) + self.aut_gp_gens = sig_realloc( self.aut_gp_gens, self.aut_gens_size * sizeof(int) ) if self.aut_gp_gens is NULL: raise MemoryError("Memory.") j = self.aut_gp_index @@ -3357,17 +3357,17 @@ cdef class BinaryCodeClassifier: self.w_gamma_size *= 2 self.alpha_size = self.w_gamma_size + self.radix self.Phi_size = self.w_gamma_size/self.radix + 1 - self.w_gamma = sage_realloc(self.w_gamma, self.w_gamma_size * sizeof(int) ) - self.alpha = sage_realloc(self.alpha, self.alpha_size * sizeof(int) ) - self.Phi = sage_realloc(self.Phi, self.Phi_size * self.L * sizeof(int) ) - self.Omega = sage_realloc(self.Omega, self.Phi_size * self.L * sizeof(int) ) - self.W = sage_realloc(self.W, self.Phi_size * self.radix * 2 * sizeof(int) ) + self.w_gamma = sig_realloc(self.w_gamma, self.w_gamma_size * sizeof(int) ) + self.alpha = sig_realloc(self.alpha, self.alpha_size * sizeof(int) ) + self.Phi = sig_realloc(self.Phi, self.Phi_size * self.L * sizeof(int) ) + self.Omega = sig_realloc(self.Omega, self.Phi_size * self.L * sizeof(int) ) + self.W = sig_realloc(self.W, self.Phi_size * self.radix * 2 * sizeof(int) ) if self.w_gamma is NULL or self.alpha is NULL or self.Phi is NULL or self.Omega is NULL or self.W is NULL: - if self.w_gamma is not NULL: sage_free(self.w_gamma) - if self.alpha is not NULL: sage_free(self.alpha) - if self.Phi is not NULL: sage_free(self.Phi) - if self.Omega is not NULL: sage_free(self.Omega) - if self.W is not NULL: sage_free(self.W) + if self.w_gamma is not NULL: sig_free(self.w_gamma) + if self.alpha is not NULL: sig_free(self.alpha) + if self.Phi is not NULL: sig_free(self.Phi) + if self.Omega is not NULL: sig_free(self.Omega) + if self.W is not NULL: sig_free(self.W) raise MemoryError("Memory.") for i from 0 <= i < self.Phi_size * self.L: self.Omega[i] = 0 @@ -4026,14 +4026,14 @@ cdef class BinaryCodeClassifier: # print 'parent:' # print B aut_gp_gens, labeling, size, base = self._aut_gp_and_can_label(B) - B_can_lab = sage_malloc(B.nrows * sizeof(codeword)) + B_can_lab = sig_malloc(B.nrows * sizeof(codeword)) can_lab = create_word_perm(labeling[:B.ncols]) if B_can_lab is NULL or can_lab is NULL: - sage_free(ortho_basis) + sig_free(ortho_basis) if B_can_lab is not NULL: - sage_free(B_can_lab) + sig_free(B_can_lab) if can_lab is not NULL: - sage_free(can_lab) + sig_free(can_lab) raise MemoryError() for i from 0 <= i < B.nrows: B_can_lab[i] = permute_word_by_wp(can_lab, B.basis[i]) @@ -4064,8 +4064,8 @@ cdef class BinaryCodeClassifier: # for g in aut_gp_gens: # print g - parent_generators = sage_malloc( len(aut_gp_gens) * sizeof(WordPermutation*) ) - temp_basis = sage_malloc( self.radix * sizeof(codeword) ) + parent_generators = sig_malloc( len(aut_gp_gens) * sizeof(WordPermutation*) ) + temp_basis = sig_malloc( self.radix * sizeof(codeword) ) output = [] @@ -4098,7 +4098,7 @@ cdef class BinaryCodeClassifier: orb_chx_size = 0 else: orb_chx_size = k - log_2_radix - orbit_checks = sage_malloc( ((1) << orb_chx_size) * sizeof(codeword) ) + orbit_checks = sig_malloc( ((1) << orb_chx_size) * sizeof(codeword) ) if orbit_checks is NULL: raise MemoryError() for temp from 0 <= temp < ((1) << orb_chx_size): @@ -4226,7 +4226,7 @@ cdef class BinaryCodeClassifier: break if bingo2: from sage.matrix.constructor import Matrix - from sage.rings.all import GF + from sage.rings.finite_rings.finite_field_constructor import GF M = Matrix(GF(2), B_aug.nrows, B_aug.ncols) for i from 0 <= i < B_aug.ncols: for j from 0 <= j < B_aug.nrows: @@ -4272,11 +4272,11 @@ cdef class BinaryCodeClassifier: for i from 0 <= i < len(aut_gp_gens): dealloc_word_perm(parent_generators[i]) - sage_free(B_can_lab) - sage_free(parent_generators) - sage_free(orbit_checks) - sage_free(ortho_basis) - sage_free(temp_basis) + sig_free(B_can_lab) + sig_free(parent_generators) + sig_free(orbit_checks) + sig_free(ortho_basis) + sig_free(temp_basis) return output diff --git a/src/sage/coding/code_constructions.py b/src/sage/coding/code_constructions.py index 64ae0672ccc..07d138541a9 100644 --- a/src/sage/coding/code_constructions.py +++ b/src/sage/coding/code_constructions.py @@ -7,8 +7,9 @@ All codes available here can be accessed through the ``codes`` object:: - sage: codes.HammingCode(3,GF(2)) - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: codes.HammingCode(GF(2), 3) + [7, 4] Hamming Code over Finite Field of size 2 + Let `F` be a finite field with `q` elements. Here's a constructive definition of a cyclic code of length @@ -119,7 +120,7 @@ REFERENCES: -.. [HP] W. C. Huffman, V. Pless, Fundamentals of Error-Correcting +.. [HP] \W. C. Huffman, V. Pless, Fundamentals of Error-Correcting Codes, Cambridge Univ. Press, 2003. AUTHOR: @@ -907,53 +908,6 @@ def ExtendedTernaryGolayCode(): # C = TernaryGolayCode() # return C.extended_code() -def HammingCode(r,F): - r""" - Implements the Hamming codes. - - The `r^{th}` Hamming code over `F=GF(q)` is an - `[n,k,d]` code with length `n=(q^r-1)/(q-1)`, - dimension `k=(q^r-1)/(q-1) - r` and minimum distance - `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. - - INPUT: - - - - ``r`` - an integer 2 - - - ``F`` - a finite field. - - - OUTPUT: Returns the r-th q-ary 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.minimum_distance(algorithm='gap') # long time, check d=3 - 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 - """ - q = F.order() - n = (q**r-1)/(q-1) - k = n-r - MS = MatrixSpace(F,n,r) - X = ProjectiveSpace(r-1,F) - PFn = [list(p) for p in X.point_set(F).points(F)] - H = MS(PFn).transpose() - Cd = LinearCode(H) - # Hamming code always has distance 3, so we provide the distance. - return LinearCode(Cd.dual_code().generator_matrix(), d=3) - - def LinearCodeFromCheckMatrix(H): r""" A linear [n,k]-code C is uniquely determined by its generator @@ -975,18 +929,22 @@ def LinearCodeFromCheckMatrix(H): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: H = C.parity_check_matrix(); H [1 0 1 0 1 0 1] [0 1 1 0 0 1 1] [0 0 0 1 1 1 1] - sage: codes.LinearCodeFromCheckMatrix(H) == C + sage: Gh = codes.LinearCodeFromCheckMatrix(H).generator_matrix() + sage: Gc = C.generator_matrix_systematic() + sage: Gh == Gc True - sage: C = codes.HammingCode(2,GF(3)) + sage: C = codes.HammingCode(GF(3), 2) sage: H = C.parity_check_matrix(); H [1 0 1 1] [0 1 1 2] - sage: codes.LinearCodeFromCheckMatrix(H) == C + sage: Gh = codes.LinearCodeFromCheckMatrix(H).generator_matrix() + sage: Gc = C.generator_matrix_systematic() + sage: Gh == Gc True sage: C = codes.RandomLinearCode(10,5,GF(4,"a")) sage: H = C.parity_check_matrix() @@ -1315,7 +1273,7 @@ def ToricCode(P,F): REFERENCES: - .. [J] D. Joyner, Toric codes over finite fields, Applicable + .. [J] \D. Joyner, Toric codes over finite fields, Applicable Algebra in Engineering, Communication and Computing, 15, (2004), p. 63-79. """ from sage.combinat.all import Tuples diff --git a/src/sage/coding/codecan/autgroup_can_label.pyx b/src/sage/coding/codecan/autgroup_can_label.pyx index 8ba1397c562..5045f545dac 100644 --- a/src/sage/coding/codecan/autgroup_can_label.pyx +++ b/src/sage/coding/codecan/autgroup_can_label.pyx @@ -40,14 +40,14 @@ AUTHORS: REFERENCES: -.. [Feu2009] T. Feulner. The Automorphism Groups of Linear Codes and +.. [Feu2009] \T. Feulner. The Automorphism Groups of Linear Codes and Canonical Representatives of Their Semilinear Isometry Classes. Advances in Mathematics of Communications 3 (4), pp. 363-383, Nov 2009 EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(3)).dual_code() + sage: C = codes.HammingCode(GF(3), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: P.get_canonical_form().generator_matrix() [1 0 0 0 0 1 1 1 1 1 1 1 1] @@ -63,7 +63,7 @@ EXAMPLES:: If the dimension of the dual code is smaller, we will work on this code:: - sage: C2 = codes.HammingCode(3, GF(3)) + sage: C2 = codes.HammingCode(GF(3), 3) sage: P2 = LinearCodeAutGroupCanLabel(C2) sage: P2.get_canonical_form().parity_check_matrix() == P.get_canonical_form().generator_matrix() True @@ -72,7 +72,7 @@ There is a specialization of this algorithm to pass a coloring on the coordinates. This is just a list of lists, telling the algorithm which columns do share the same coloring:: - sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code() + sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C, P=[ [0], [1], range(2, C.length()) ]) sage: P.get_autom_order() 864 @@ -169,7 +169,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(3)).dual_code() + sage: C = codes.HammingCode(GF(3), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: P.get_canonical_form().generator_matrix() [1 0 0 0 0 1 1 1 1 1 1 1 1] @@ -206,7 +206,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(2)).dual_code() + sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: P.get_canonical_form().generator_matrix() [1 0 0 0 1 1 1] @@ -220,9 +220,9 @@ class LinearCodeAutGroupCanLabel: [0 0 1 0 1 1 1] """ from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup - from sage.coding.linear_code import LinearCode + from sage.coding.linear_code import LinearCode, AbstractLinearCode - if not isinstance(C, LinearCode): + if not isinstance(C, AbstractLinearCode): raise TypeError("%s is not a linear code"%C) self.C = C @@ -531,7 +531,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(3)).dual_code() + sage: C = codes.HammingCode(GF(3), 3).dual_code() sage: CF1 = LinearCodeAutGroupCanLabel(C).get_canonical_form() sage: s = SemimonomialTransformationGroup(GF(3), C.length()).an_element() sage: C2 = LinearCode(s*C.generator_matrix()) @@ -548,7 +548,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(2)).dual_code() + sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: g = P.get_transporter() sage: D = P.get_canonical_form() @@ -564,7 +564,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(2)).dual_code() + sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: A = LinearCodeAutGroupCanLabel(C).get_autom_gens() sage: Gamma = C.generator_matrix().echelon_form() sage: all([(g*Gamma).echelon_form() == Gamma for g in A]) @@ -579,7 +579,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(2)).dual_code() + sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: LinearCodeAutGroupCanLabel(C).get_autom_order() 168 """ @@ -599,7 +599,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code() + sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code() sage: A = LinearCodeAutGroupCanLabel(C).get_PGammaL_gens() sage: Gamma = C.generator_matrix() sage: N = [ x.monic() for x in Gamma.columns() ] @@ -628,7 +628,7 @@ class LinearCodeAutGroupCanLabel: EXAMPLES:: sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel - sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code() + sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code() sage: LinearCodeAutGroupCanLabel(C).get_PGammaL_order() == GL(3, GF(4, 'a')).order()*2/3 True """ diff --git a/src/sage/coding/codecan/codecan.pyx b/src/sage/coding/codecan/codecan.pyx index b8ca45c6cde..977ce77b947 100644 --- a/src/sage/coding/codecan/codecan.pyx +++ b/src/sage/coding/codecan/codecan.pyx @@ -60,7 +60,7 @@ EXAMPLES: Get the canonical form of the Simplex code:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) sage: cf = P.get_canonical_form(); cf [1 0 0 0 0 1 1 1 1 1 1 1 1] @@ -466,7 +466,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) sage: cf = P.get_canonical_form(); cf [1 0 0 0 0 1 1 1 1 1 1 1 1] @@ -493,7 +493,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) """ self._k = generator_matrix.nrows() @@ -533,7 +533,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) """ self._run(P, algorithm_type) @@ -549,17 +549,17 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): for i in range(self._hyp_part.degree): bitset_free(self._hyp2points[i]) - sage_free(self._hyp2points) - sage_free(self._points2hyp) + sig_free(self._hyp2points) + sig_free(self._points2hyp) PS_dealloc(self._hyp_part) - sage_free(self._hyp_refine_vals_scratch) + sig_free(self._hyp_refine_vals_scratch) def __repr__(self): """ EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: PartitionRefinementLinearCode(mat.ncols(), mat) Canonical form algorithm for linear code generated by [1 0 1 1 0 1 0 1 1 1 0 1 1] @@ -577,7 +577,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) #indirect doctest sage: P.get_canonical_form() [1 0 0 0 0 1 1 1 1 1 1 1 1] @@ -663,7 +663,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P1 = PartitionRefinementLinearCode(mat.ncols(), mat) sage: CF1 = P1.get_canonical_form() sage: s = SemimonomialTransformationGroup(GF(3), mat.ncols()).an_element() @@ -681,7 +681,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) sage: CF = P.get_canonical_form() sage: t = P.get_transporter() @@ -697,7 +697,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) sage: A = P.get_autom_gens() sage: all( [(a*mat).echelon_form() == mat.echelon_form() for a in A]) @@ -713,7 +713,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): EXAMPLES:: sage: from sage.coding.codecan.codecan import PartitionRefinementLinearCode - sage: mat = codes.HammingCode(3, GF(3)).dual_code().generator_matrix() + sage: mat = codes.HammingCode(GF(3), 3).dual_code().generator_matrix() sage: P = PartitionRefinementLinearCode(mat.ncols(), mat) sage: P.get_autom_order_inner_stabilizer() 2 @@ -781,12 +781,12 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): if s >= 0: self._hyp_part.levels[s] = 0 - self._hyp2points = < bitset_t *> sage_malloc(self._hyp_part.degree * sizeof(bitset_t)) + self._hyp2points = < bitset_t *> sig_malloc(self._hyp_part.degree * sizeof(bitset_t)) if self._hyp2points is NULL: raise MemoryError('allocating PartitionRefinementLinearCode') - self._points2hyp = < bitset_t *> sage_malloc(self._n * sizeof(bitset_t)) + self._points2hyp = < bitset_t *> sig_malloc(self._n * sizeof(bitset_t)) if self._hyp2points is NULL: - sage_free(self._hyp2points) + sig_free(self._hyp2points) raise MemoryError('allocating PartitionRefinementLinearCode') for i in range(self._n): @@ -800,7 +800,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): bitset_add(self._hyp2points[i], j) bitset_add(self._points2hyp[j], i) - self._hyp_refine_vals_scratch = sage_malloc( + self._hyp_refine_vals_scratch = sig_malloc( self._hyp_part.degree * sizeof(long)) if self._hyp_refine_vals_scratch is NULL: raise MemoryError('allocating PartitionRefinementLinearCode') @@ -972,7 +972,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): cdef bitset_t *nonsingletons cdef bitset_t scratch bitset_init(scratch, self._hyp_part.degree) - nonsingletons = < bitset_t *> sage_malloc(0) + nonsingletons = < bitset_t *> sig_malloc(0) cdef int nr_cells = PS_all_new_cells(self._hyp_part, & nonsingletons) for i in range(self._n): @@ -984,7 +984,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): for j in range(nr_cells): bitset_free(nonsingletons[j]) - sage_free(nonsingletons) + sig_free(nonsingletons) bitset_free(scratch) # provide some space to store the result (if not already exists) @@ -1017,7 +1017,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): cdef bitset_t *nonsingletons cdef bitset_t scratch bitset_init(scratch, self._part.degree) - nonsingletons = < bitset_t *> sage_malloc(0) + nonsingletons = < bitset_t *> sig_malloc(0) cdef int nr_cells = PS_all_new_cells(self._part, & nonsingletons) for i in range(self._hyp_part.degree): @@ -1029,7 +1029,7 @@ cdef class PartitionRefinementLinearCode(PartitionRefinement_generic): for j in range(nr_cells): bitset_free(nonsingletons[j]) - sage_free(nonsingletons) + sig_free(nonsingletons) bitset_free(scratch) # provide some space to store the result (if not already exists) diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index dae184932d7..d17b58ac7da 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -22,7 +22,7 @@ CyclicCode, CyclicCodeFromCheckPolynomial, DuadicCodeEvenPair, DuadicCodeOddPair, ExtendedBinaryGolayCode, ExtendedQuadraticResidueCode, ExtendedTernaryGolayCode, - HammingCode, LinearCode, LinearCodeFromCheckMatrix, + LinearCode, LinearCodeFromCheckMatrix, QuadraticResidueCode, QuadraticResidueCodeEvenPair, QuadraticResidueCodeOddPair, RandomLinearCode, ReedSolomonCode, TernaryGolayCode, @@ -31,6 +31,7 @@ from grs import GeneralizedReedSolomonCode from guava import BinaryReedMullerCode, QuasiQuadraticResidueCode, RandomLinearCodeGuava +from hamming_code import HammingCode import decoders_catalog as decoders import encoders_catalog as encoders diff --git a/src/sage/coding/delsarte_bounds.py b/src/sage/coding/delsarte_bounds.py index 884002a57f9..40c1e614f9a 100644 --- a/src/sage/coding/delsarte_bounds.py +++ b/src/sage/coding/delsarte_bounds.py @@ -2,23 +2,27 @@ Delsarte, a.k.a. Linear Programming (LP), upper bounds 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. +The exact LP solver PPL is used by default, ensuring that no +rounding/overflow problems occur. AUTHORS: - Dmitrii V. (Dima) Pasechnik (2012-10): initial implementation. Minor fixes (2015) """ + #***************************************************************************** # Copyright (C) 2012 Dima Pasechnik # -# 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/ #***************************************************************************** + def Krawtchouk(n,q,l,x,check=True): """ - Compute ``K^{n,q}_l(x)``, the Krawtchouk polynomial. + Compute ``K^{n,q}_l(x)``, the Krawtchouk (a.k.a. Kravchuk) polynomial. See :wikipedia:`Kravchuk_polynomials`; It is defined by the generating function `(1+(q-1)z)^{n-x}(1-z)^x=\sum_{l} K^{n,q}_l(x)z^l` and is equal to @@ -53,6 +57,8 @@ def Krawtchouk(n,q,l,x,check=True): -1 sage: Krawtchouk(int(3),int(2),int(3),int(3),check=False) -5 + sage: Kravchuk(24,2,5,4) + 2224 other unusual inputs :: @@ -85,6 +91,8 @@ def Krawtchouk(n,q,l,x,check=True): kraw += jth_term return kraw +Kravchuk = Krawtchouk + def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc = 0): """ LP builder - common for the two functions; not exported. @@ -102,33 +110,18 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc = 0): constraint_2: 0 <= x_2 <= 0 constraint_3: -7 x_0 - 5 x_1 - 3 x_2 - x_3 + x_4 + 3 x_5 + 5 x_6 + 7 x_7 <= 0 constraint_4: -7 x_0 - 5 x_1 - 3 x_2 - x_3 + x_4 + 3 x_5 + 5 x_6 + 7 x_7 <= 0 - constraint_5: -21 x_0 - 9 x_1 - x_2 + 3 x_3 + 3 x_4 - x_5 - 9 x_6 - 21 x_7 <= 0 - constraint_6: -21 x_0 - 9 x_1 - x_2 + 3 x_3 + 3 x_4 - x_5 - 9 x_6 - 21 x_7 <= 0 - constraint_7: -35 x_0 - 5 x_1 + 5 x_2 + 3 x_3 - 3 x_4 - 5 x_5 + 5 x_6 + 35 x_7 <= 0 - constraint_8: -35 x_0 - 5 x_1 + 5 x_2 + 3 x_3 - 3 x_4 - 5 x_5 + 5 x_6 + 35 x_7 <= 0 - constraint_9: -35 x_0 + 5 x_1 + 5 x_2 - 3 x_3 - 3 x_4 + 5 x_5 + 5 x_6 - 35 x_7 <= 0 - constraint_10: -35 x_0 + 5 x_1 + 5 x_2 - 3 x_3 - 3 x_4 + 5 x_5 + 5 x_6 - 35 x_7 <= 0 - constraint_11: -21 x_0 + 9 x_1 - x_2 - 3 x_3 + 3 x_4 + x_5 - 9 x_6 + 21 x_7 <= 0 - constraint_12: -21 x_0 + 9 x_1 - x_2 - 3 x_3 + 3 x_4 + x_5 - 9 x_6 + 21 x_7 <= 0 - constraint_13: -7 x_0 + 5 x_1 - 3 x_2 + x_3 + x_4 - 3 x_5 + 5 x_6 - 7 x_7 <= 0 - constraint_14: -7 x_0 + 5 x_1 - 3 x_2 + x_3 + x_4 - 3 x_5 + 5 x_6 - 7 x_7 <= 0 - constraint_15: - x_0 + x_1 - x_2 + x_3 - x_4 + x_5 - x_6 + x_7 <= 0 + ... constraint_16: - x_0 + x_1 - x_2 + x_3 - x_4 + x_5 - x_6 + x_7 <= 0 Variables: x_0 is a continuous variable (min=0, max=+oo) - x_1 is a continuous variable (min=0, max=+oo) - x_2 is a continuous variable (min=0, max=+oo) - x_3 is a continuous variable (min=0, max=+oo) - x_4 is a continuous variable (min=0, max=+oo) - x_5 is a continuous variable (min=0, max=+oo) - x_6 is a continuous variable (min=0, max=+oo) + ... x_7 is a continuous variable (min=0, max=+oo) """ from sage.numerical.mip import MixedIntegerLinearProgram p = MixedIntegerLinearProgram(maximization=True, solver=solver) - A = p.new_variable(integer=isinteger, nonnegative=not isinteger) # A>=0 is assumed + A = p.new_variable(integer=isinteger, nonnegative=True) p.set_objective(sum([A[r] for r in xrange(n+1)])) p.add_constraint(A[0]==1) for i in xrange(1,d): @@ -145,10 +138,9 @@ def _delsarte_LP_building(n, d, d_star, q, isinteger, solver, maxc = 0): p.add_constraint(sum([A[r] for r in xrange(n+1)]), max=maxc) return A, p -def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): +def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL", isinteger=False): """ - Find the classical Delsarte bound [1]_ on codes in Hamming space - ``H_q^n`` of minimal distance ``d`` + Find the Delsarte bound [1]_ on codes in Hamming space ``H_q^n`` of minimal distance ``d`` INPUT: @@ -160,13 +152,15 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): - ``q`` -- the size of the alphabet - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is - a weights vector, and ``LP`` the Delsarte bound LP; both of them are Sage LP + a weights vector, and ``LP`` the Delsarte upper bound LP; both of them are Sage LP data. ``W`` need not be a weight distribution of a code. - ``solver`` -- the LP/ILP solver to be used. Defaults to ``PPL``. It is arbitrary precision, thus there will be no rounding errors. With other solvers (see :class:`MixedIntegerLinearProgram` for the list), you are on your own! + - ``isinteger`` -- if ``True``, uses an integer programming solver (ILP), rather + that an LP solver. Can be very slow if set to ``True``. EXAMPLES: @@ -192,26 +186,38 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): sage: delsarte_bound_hamming_space(11,3,4) 327680/3 + An improvement of a known upper bound (150) from http://www.win.tue.nl/~aeb/codes/binary-1.html :: + + sage: a,p,x= delsarte_bound_hamming_space(23,10,2,return_data=True,isinteger=True); x # long time + 148 + sage: [j for i,j in p.get_values(a).iteritems()] # long time + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 2, 0, 36, 0, 14, 0, 0, 0, 0, 0, 0, 0] + + Note that a usual LP, without integer variables, won't do the trick :: + + sage: delsarte_bound_hamming_space(23,10,2).n(20) + 151.86 + Such an input is invalid:: sage: delsarte_bound_hamming_space(11,3,-4) - Solver exception: 'PPL : There is no feasible solution' () + Solver exception: PPL : There is no feasible solution False REFERENCES: - .. [1] P. Delsarte, An algebraic approach to the association schemes of coding theory, + .. [1] \P. Delsarte, An algebraic approach to the association schemes of coding theory, Philips Res. Rep., Suppl., vol. 10, 1973. """ from sage.numerical.mip import MIPSolverException - A, p = _delsarte_LP_building(n, d, 0, q, False, solver) + A, p = _delsarte_LP_building(n, d, 0, q, isinteger, solver) try: bd=p.solve() except MIPSolverException as exc: - print "Solver exception: ", exc, exc.args + print "Solver exception:", exc if return_data: return A,p,False return False @@ -224,6 +230,8 @@ def delsarte_bound_hamming_space(n, d, q, return_data=False, solver="PPL"): def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, return_data=False, solver="PPL", isinteger=False): """ + Find a modified Delsarte bound on additive codes in Hamming space ``H_q^n`` of minimal distance ``d`` + Find the Delsarte LP bound on ``F_{q_base}``-dimension of additive codes in Hamming space ``H_q^n`` of minimal distance ``d`` with minimal distance of the dual code at least ``d_star``. If ``q_base`` is set to @@ -242,7 +250,7 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, - ``d_star`` -- the (lower bound on) minimal distance of the dual code; only makes sense for additive codes. - - ``q_base`` -- if ``0``, the code is assumed to be nonlinear. Otherwise, + - ``q_base`` -- if ``0``, the code is assumed to be linear. Otherwise, ``q=q_base^m`` and the code is linear over ``F_{q_base}``. - ``return_data`` -- if ``True``, return a triple ``(W,LP,bound)``, where ``W`` is @@ -282,9 +290,18 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, Such a d_star is not possible:: sage: delsarte_bound_additive_hamming_space(11,3,4,d_star=9) - Solver exception: 'PPL : There is no feasible solution' () + Solver exception: PPL : There is no feasible solution False + TESTS:: + + sage: a,p,x=delsarte_bound_additive_hamming_space(19,15,7,return_data=True,isinteger=True) + sage: [j for i,j in p.get_values(a).iteritems()] + [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 307, 0, 0, 1, 34] + sage: delsarte_bound_additive_hamming_space(19,15,7,solver='glpk') + 3 + sage: delsarte_bound_additive_hamming_space(19,15,7,isinteger=True,solver='glpk') + 3 """ from sage.numerical.mip import MIPSolverException if q_base == 0: @@ -312,7 +329,7 @@ def delsarte_bound_additive_hamming_space(n, d, q, d_star=1, q_base=0, try: bd=p.solve() except MIPSolverException as exc: - print "Solver exception: ", exc, exc.args + print "Solver exception:", exc if return_data: return A,p,False return False diff --git a/src/sage/coding/encoders_catalog.py b/src/sage/coding/encoders_catalog.py index 2c079a16ec3..d63b8c15c1c 100644 --- a/src/sage/coding/encoders_catalog.py +++ b/src/sage/coding/encoders_catalog.py @@ -6,6 +6,7 @@ **Generic encoders** :class:`linear_code.LinearCodeGeneratorMatrixEncoder ` +:class:`linear_code.LinearCodeParityCheckEncoder ` **Generalized Reed-Solomon code encoders** @@ -29,5 +30,6 @@ #***************************************************************************** from sage.misc.lazy_import import lazy_import as _lazy_import -_lazy_import('sage.coding.linear_code', 'LinearCodeGeneratorMatrixEncoder') +_lazy_import('sage.coding.linear_code', ['LinearCodeGeneratorMatrixEncoder', + 'LinearCodeParityCheckEncoder']) _lazy_import('sage.coding.grs', ['GRSEvaluationVectorEncoder', 'GRSEvaluationPolynomialEncoder']) diff --git a/src/sage/coding/grs.py b/src/sage/coding/grs.py index 1fb276cbf24..7dbfa1ee19f 100644 --- a/src/sage/coding/grs.py +++ b/src/sage/coding/grs.py @@ -14,6 +14,10 @@ - :class:`GeneralizedReedSolomonCode`, the class for GRS codes - :class:`GRSEvaluationVectorEncoder`, an encoder with a vectorial message space - :class:`GRSEvaluationPolynomialEncoder`, an encoder with a polynomial message space + - :class:`GRSBerlekampWelchDecoder`, a decoder which corrects errors using Berlekamp-Welch algorithm + - :class:`GRSGaoDecoder`, a decoder which corrects errors using Gao algorithm + - :class:`GRSErrorErasureDecoder`, a decoder which corrects both errors and erasures + - :class:`GRSKeyEquationSyndromeDecoder`, a decoder which corrects errors using the key equation on syndrome polynomials """ #***************************************************************************** @@ -483,6 +487,8 @@ def decode_to_message(self, r): +####################### encoders ############################### + ####################### encoders ############################### @@ -860,15 +866,18 @@ class GRSBerlekampWelchDecoder(Decoder): def __init__(self, code): r""" - EXAMPLES:: + TESTS: - sage: F = GF(59) - sage: n, k = 40, 12 - sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) - sage: D = codes.decoders.GRSBerlekampWelchDecoder(C) - sage: D - Berlekamp-Welch decoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + If ``code`` is not a GRS code, an error is raised:: + + sage: C = codes.RandomLinearCode(10, 4, GF(11)) + sage: codes.decoders.GRSBerlekampWelchDecoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a generalized Reed-Solomon code """ + if not isinstance(code, GeneralizedReedSolomonCode): + raise ValueError("code has to be a generalized Reed-Solomon code") super(GRSBerlekampWelchDecoder, self).__init__(code, code.ambient_space(), "EvaluationPolynomial") @@ -923,15 +932,10 @@ def _latex_(self): return "\\textnormal{Berlekamp Welch decoder for }%s"\ % self.code()._latex_() - def decode_to_message(self, r): + def _decode_to_code_and_message(self, r): r""" - Decodes ``r`` to an element in message space of ``self``. - - .. NOTE:: - - If the code associated to ``self`` has the same length as its - dimension, ``r`` will be unencoded as is. In that case, - if ``r`` is not a codeword, the output is unspecified. + Decodes ``r`` to an element in message space of ``self`` and its + representation in the ambient space of the code associated to ``self``. INPUT: @@ -939,7 +943,9 @@ def decode_to_message(self, r): OUTPUT: - - a vector of ``self`` message space + - ``(c, f)`` -- ``c`` is the representation of ``r`` decoded in the ambient + space of the associated code of ``self``, ``f`` its representation in + the message space of ``self``. EXAMPLES:: @@ -950,37 +956,20 @@ def decode_to_message(self, r): sage: c = C.random_element() sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()) sage: y = Chan(c) - sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) + sage: c_dec, f_dec = D._decode_to_code_and_message(y) + sage: f_dec == D.connected_encoder().unencode(c) + True + sage: c_dec == c True - - TESTS: - - If one tries to decode a word with too many errors, it returns - an exception:: - - sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()+1) - sage: y = Chan(c) - sage: D.decode_to_message(y) - Traceback (most recent call last): - ... - DecodingError: Decoding failed because the number of errors exceeded the decoding radius - - If one tries to decode something which is not in the ambient space of the code, - an exception is raised:: - - sage: D.decode_to_message(42) - Traceback (most recent call last): - ... - ValueError: The word to decode has to be in the ambient space of the code """ C = self.code() if r not in C.ambient_space(): raise ValueError("The word to decode has to be in the ambient space of the code") n, k = C.length(), C.dimension() if n == k: - return self.connected_encoder().unencode_nocheck(r) + return r, self.connected_encoder().unencode_nocheck(r) if r in C: - return self.connected_encoder().unencode_nocheck(r) + return r, self.connected_encoder().unencode_nocheck(r) col_mults = C.column_multipliers() r_list = copy(r) @@ -997,17 +986,137 @@ def decode_to_message(self, r): R = C.base_field()['x'] Q0 = R(S.list_from_positions(xrange(0, l0+1))) - Q1 = R(S.list_from_positions(xrange(l0+1 , l0+l1+2))) + Q1 = R(S.list_from_positions(xrange(l0+1, l0+l1+2))) f, rem = (-Q0).quo_rem(Q1) if not rem.is_zero(): raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") if f not in R: raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") - if (R(r.list()) - f).degree() < self.decoding_radius(): + c = self.connected_encoder().encode(f) + if (c - r).hamming_weight() > self.decoding_radius(): raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + return c, f - return f + def decode_to_message(self, r): + r""" + Decodes ``r`` to an element in message space of ``self``. + + .. NOTE:: + + If the code associated to ``self`` has the same length as its + dimension, ``r`` will be unencoded as is. In that case, + if ``r`` is not a codeword, the output is unspecified. + + INPUT: + + - ``r`` -- a codeword of ``self`` + + OUTPUT: + + - a vector of ``self`` message space + + EXAMPLES:: + + sage: F = GF(59) + sage: n, k = 40, 12 + sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) + sage: D = codes.decoders.GRSBerlekampWelchDecoder(C) + sage: c = C.random_element() + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()) + sage: y = Chan(c) + sage: D.connected_encoder().unencode(c) == D.decode_to_message(y) + True + + TESTS: + + If one tries to decode a word which is too far from any codeword, an exception is raised:: + + sage: e = vector(F,[0, 0, 54, 23, 1, 0, 0, 0, 53, 21, 0, 0, 0, 34, 6, 11, 0, 0, 16, 0, 0, 0, 9, 0, 10, 27, 35, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 44, 0]); e.hamming_weight() + 15 + sage: D.decode_to_message(c + e) + Traceback (most recent call last): + ... + DecodingError: Decoding failed because the number of errors exceeded the decoding radius + + If one tries to decode something which is not in the ambient space of the code, + an exception is raised:: + + sage: D.decode_to_message(42) + Traceback (most recent call last): + ... + ValueError: The word to decode has to be in the ambient space of the code + + The bug detailed in :trac:`20340` has been fixed:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12) + sage: c = C.random_element() + sage: D = C.decoder("BerlekampWelch") + sage: E = D.connected_encoder() + sage: m = E.message_space().random_element() + sage: c = E.encode(m) + sage: D.decode_to_message(c) == m + True + """ + return self._decode_to_code_and_message(r)[1] + + def decode_to_code(self, r): + r""" + Corrects the errors in ``r`` and returns a codeword. + + .. NOTE:: + + If the code associated to ``self`` has the same length as its + dimension, ``r`` will be returned as is. + + INPUT: + + - ``r`` -- a vector of the ambient space of ``self.code()`` + + OUTPUT: + + - a vector of ``self.code()`` + + EXAMPLES:: + + sage: F = GF(59) + sage: n, k = 40, 12 + sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) + sage: D = codes.decoders.GRSBerlekampWelchDecoder(C) + sage: c = C.random_element() + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()) + sage: y = Chan(c) + sage: c == D.decode_to_code(y) + True + + TESTS: + + If one tries to decode a word which is too far from any codeword, an exception is raised:: + + sage: e = vector(F,[0, 0, 54, 23, 1, 0, 0, 0, 53, 21, 0, 0, 0, 34, 6, 11, 0, 0, 16, 0, 0, 0, 9, 0, 10, 27, 35, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 44, 0]); e.hamming_weight() + 15 + sage: D.decode_to_code(c + e) + Traceback (most recent call last): + ... + DecodingError: Decoding failed because the number of errors exceeded the decoding radius + + If one tries to decode something which is not in the ambient space of the code, + an exception is raised:: + + sage: D.decode_to_code(42) + Traceback (most recent call last): + ... + ValueError: The word to decode has to be in the ambient space of the code + + The bug detailed in :trac:`20340` has been fixed:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12) + sage: c = C.random_element() + sage: D = C.decoder("BerlekampWelch") + sage: D.decode_to_code(c) == c + True + """ + return self._decode_to_code_and_message(r)[0] def decoding_radius(self): r""" @@ -1072,15 +1181,18 @@ class GRSGaoDecoder(Decoder): def __init__(self, code): r""" - EXAMPLES:: + TESTS: - sage: F = GF(59) - sage: n, k = 40, 12 - sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) - sage: D = codes.decoders.GRSGaoDecoder(C) - sage: D - Gao decoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + If ``code`` is not a GRS code, an error is raised:: + + sage: C = codes.RandomLinearCode(10, 4, GF(11)) + sage: codes.decoders.GRSGaoDecoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a generalized Reed-Solomon code """ + if not isinstance(code, GeneralizedReedSolomonCode): + raise ValueError("code has to be a generalized Reed-Solomon code") super(GRSGaoDecoder, self).__init__(code, code.ambient_space(), "EvaluationPolynomial") @@ -1207,9 +1319,67 @@ def _partial_xgcd(self, a, b, PolRing): return (r, s) + def _decode_to_code_and_message(self, r): + r""" + Decodes ``r`` to an element in message space of ``self`` and its + representation in the ambient space of the code associated to ``self``. + + INPUT: + + - ``r`` -- a codeword of ``self`` + + OUTPUT: + + - ``(c, h)`` -- ``c`` is the representation of ``r`` decoded in the ambient + space of the associated code of ``self``, ``h`` its representation in + the message space of ``self``. + + EXAMPLES:: + + sage: F = GF(59) + sage: n, k = 40, 12 + sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) + sage: D = codes.decoders.GRSGaoDecoder(C) + sage: c = C.random_element() + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()) + sage: y = Chan(c) + sage: c_dec, h_dec = D._decode_to_code_and_message(y) + sage: h_dec == D.connected_encoder().unencode(c) + True + sage: c_dec == c + True + """ + C = self.code() + if r not in C.ambient_space(): + raise ValueError("The word to decode has to be in the ambient space of the code") + alphas = C.evaluation_points() + col_mults = C.column_multipliers() + PolRing = C.base_field()['x'] + G = self._polynomial_vanishing_at_alphas(PolRing) + n = C.length() + + if n == C.dimension() or r in C: + return r, self.connected_encoder().unencode_nocheck(r) + + points = [(alphas[i], r[i]/col_mults[i]) for i in + range(0, n)] + R = PolRing.lagrange_polynomial(points) + + (Q1, Q0) = self._partial_xgcd(G, R, PolRing) + + h, rem = Q1.quo_rem(Q0) + if not rem.is_zero(): + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + if h not in PolRing: + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + c = self.connected_encoder().encode(h) + if (c - r).hamming_weight() > self.decoding_radius(): + raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") + return c, h + def decode_to_message(self, r): r""" - Decodes ``r`` to an element in message space of ``self`` + Decodes ``r`` to an element in message space of ``self``. .. NOTE:: @@ -1239,12 +1409,11 @@ def decode_to_message(self, r): TESTS: - If one tries to decode a word with too many errors, it returns - an exception:: + If one tries to decode a word which is too far from any codeword, an exception is raised:: - sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()+1) - sage: y = Chan(c) - sage: D.decode_to_message(y) + sage: e = vector(F,[0, 0, 54, 23, 1, 0, 0, 0, 53, 21, 0, 0, 0, 34, 6, 11, 0, 0, 16, 0, 0, 0, 9, 0, 10, 27, 35, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 44, 0]); e.hamming_weight() + 15 + sage: D.decode_to_message(c + e) Traceback (most recent call last): ... DecodingError: Decoding failed because the number of errors exceeded the decoding radius @@ -1256,33 +1425,79 @@ def decode_to_message(self, r): Traceback (most recent call last): ... ValueError: The word to decode has to be in the ambient space of the code + + The bug detailed in :trac:`20340` has been fixed:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12) + sage: c = C.random_element() + sage: D = C.decoder("Gao") + sage: E = D.connected_encoder() + sage: m = E.message_space().random_element() + sage: c = E.encode(m) + sage: D.decode_to_message(c) == m + True """ - C = self.code() - if r not in C.ambient_space(): - raise ValueError("The word to decode has to be in the ambient space of the code") - alphas = C.evaluation_points() - col_mults = C.column_multipliers() - PolRing = C.base_field()['x'] - G = self._polynomial_vanishing_at_alphas(PolRing) - n = C.length() + return self._decode_to_code_and_message(r)[1] - if n == C.dimension() or r in C: - return self.connected_encoder().unencode_nocheck(r) + def decode_to_code(self, r): + r""" + Corrects the errors in ``r`` and returns a codeword. - points = [(alphas[i], r[i]/col_mults[i]) for i in - range(0, n)] - R = PolRing.lagrange_polynomial(points) + .. NOTE:: - (Q1, Q0) = self._partial_xgcd(G, R, PolRing) + If the code associated to ``self`` has the same length as its + dimension, ``r`` will be returned as is. + + INPUT: + + - ``r`` -- a vector of the ambient space of ``self.code()`` + + OUTPUT: + + - a vector of ``self.code()`` + + EXAMPLES:: + + sage: F = GF(59) + sage: n, k = 40, 12 + sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) + sage: D = codes.decoders.GRSGaoDecoder(C) + sage: c = C.random_element() + sage: Chan = channels.StaticErrorRateChannel(C.ambient_space(), D.decoding_radius()) + sage: y = Chan(c) + sage: c == D.decode_to_code(y) + True + + TESTS: - h, rem = Q1.quo_rem(Q0) - if not rem.is_zero(): - raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") - if h not in PolRing: - raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") - if (PolRing(r.list()) - h).degree() < self.decoding_radius(): - raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") - return h + + If one tries to decode a word which is too far from any codeword, an exception is raised:: + + sage: e = vector(F,[0, 0, 54, 23, 1, 0, 0, 0, 53, 21, 0, 0, 0, 34, 6, 11, 0, 0, 16, 0, 0, 0, 9, 0, 10, 27, 35, 0, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 44, 0]); e.hamming_weight() + 15 + sage: D.decode_to_code(c + e) + Traceback (most recent call last): + ... + DecodingError: Decoding failed because the number of errors exceeded the decoding radius + + If one tries to decode something which is not in the ambient space of the code, + an exception is raised:: + + sage: D.decode_to_code(42) + Traceback (most recent call last): + ... + ValueError: The word to decode has to be in the ambient space of the code + + The bug detailed in :trac:`20340` has been fixed:: + + sage: C = codes.GeneralizedReedSolomonCode(GF(59).list()[:40], 12) + sage: c = C.random_element() + sage: D = C.decoder("Gao") + sage: c = C.random_element() + sage: D.decode_to_code(c) == c + True + """ + return self._decode_to_code_and_message(r)[0] def decoding_radius(self): r""" @@ -1339,15 +1554,18 @@ class GRSErrorErasureDecoder(Decoder): def __init__(self, code): r""" - EXAMPLES:: + TESTS: - sage: F = GF(59) - sage: n, k = 40, 12 - sage: C = codes.GeneralizedReedSolomonCode(F.list()[:n], k) - sage: D = codes.decoders.GRSErrorErasureDecoder(C) - sage: D - Error-Erasure decoder for [40, 12, 29] Generalized Reed-Solomon Code over Finite Field of size 59 + If ``code`` is not a GRS code, an error is raised:: + + sage: C = codes.RandomLinearCode(10, 4, GF(11)) + sage: codes.decoders.GRSErrorErasureDecoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a generalized Reed-Solomon code """ + if not isinstance(code, GeneralizedReedSolomonCode): + raise ValueError("code has to be a generalized Reed-Solomon code") input_space = cartesian_product([code.ambient_space(), VectorSpace(GF(2), code.ambient_space().dimension())]) super(GRSErrorErasureDecoder, self).__init__(code, input_space, "EvaluationVector") @@ -1584,7 +1802,17 @@ def __init__(self, code): Traceback (most recent call last): ... ValueError: Impossible to use this decoder over a GRS code which contains 0 amongst its evaluation points + + If ``code`` is not a GRS code, an error is raised:: + + sage: C = codes.RandomLinearCode(10, 4, GF(11)) + sage: codes.decoders.GRSKeyEquationSyndromeDecoder(C) + Traceback (most recent call last): + ... + ValueError: code has to be a generalized Reed-Solomon code """ + if not isinstance(code, GeneralizedReedSolomonCode): + raise ValueError("code has to be a generalized Reed-Solomon code") if code.base_field().zero() in code.evaluation_points(): raise ValueError("Impossible to use this decoder over a GRS code which contains 0 amongst its evaluation points") super(GRSKeyEquationSyndromeDecoder, self).__init__(code, code.ambient_space(), @@ -1828,8 +2056,6 @@ def decode_to_code(self, r): dec = r - e if dec not in C: raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") - if (r - dec).hamming_weight() > self.decoding_radius(): - raise DecodingError("Decoding failed because the number of errors exceeded the decoding radius") return dec def decode_to_message(self, r): diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index 29fcf0cac79..2ef61d92533 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -115,7 +115,7 @@ def QuasiQuadraticResidueCode(p): .. [BM] Bazzi and Mitter, {\it Some constructions of codes from group actions}, (preprint March 2003, available on Mitter's MIT website). - .. [Jresidue] D. Joyner, {\it On quadratic residue codes and hyperelliptic curves}, + .. [Jresidue] \D. Joyner, {\it On quadratic residue codes and hyperelliptic curves}, (preprint 2006) These are self-orthogonal in general and self-dual when $p \\equiv 3 \\pmod 4$. diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index 25c877cd4f5..396943b4e0f 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -30,7 +30,7 @@ from sage.modules.free_module_element import vector from sage.rings.integer_ring import ZZ from sage.coding.decoder import Decoder -from sage.coding.guruswami_sudan.interpolation import gs_interpolation_linalg +from sage.coding.guruswami_sudan.interpolation import gs_interpolation_linalg, gs_interpolation_lee_osullivan from sage.coding.guruswami_sudan.rootfinding import rootfind_roth_ruckenstein from sage.coding.guruswami_sudan.utils import (johnson_radius, gilt, @@ -127,7 +127,8 @@ class GRSGuruswamiSudanDecoder(Decoder): - ``interpolation_alg`` -- (default: ``None``) the interpolation algorithm that will be used. The following possibilities are currently available: - * ``LinearAlgebra`` -- uses a linear system solver. + * ``"LinearAlgebra"`` -- uses a linear system solver. + * ``"LeeOSullivan"`` -- uses Lee O'Sullivan method based on row reduction of a matrix * ``None`` -- one of the above will be chosen based on the size of the code and the parameters. @@ -137,7 +138,7 @@ class GRSGuruswamiSudanDecoder(Decoder): - ``root_finder`` -- (default: ``None``) the rootfinding algorithm that will be used. The following possibilities are currently available: - * ``RothRuckenstein`` -- uses Roth-Ruckenstein algorithm. + * ``"RothRuckenstein"`` -- uses Roth-Ruckenstein algorithm. * ``None`` -- one of the above will be chosen based on the size of the code and the parameters. @@ -183,6 +184,16 @@ class GRSGuruswamiSudanDecoder(Decoder): sage: D Guruswami-Sudan decoder for [250, 70, 181] Generalized Reed-Solomon Code over Finite Field of size 251 decoding 97 errors with parameters (1, 2) + If one wants to use the native Sage algorithms for the root finding step, + one can directly pass the string given in the ``Input`` block of this class. + This works for ``interpolation_alg`` as well:: + + + sage: from sage.coding.guruswami_sudan.rootfinding import rootfind_roth_ruckenstein + sage: rf = rootfind_roth_ruckenstein + sage: D = codes.decoders.GRSGuruswamiSudanDecoder(C, parameters = (1,2), root_finder="RothRuckenstein") + sage: D + Guruswami-Sudan decoder for [250, 70, 181] Generalized Reed-Solomon Code over Finite Field of size 251 decoding 97 errors with parameters (1, 2) Actually, we can construct the decoder from ``C`` directly:: @@ -529,7 +540,17 @@ def __init__(self, code, tau = None, parameters = None, interpolation_alg = None Traceback (most recent call last): ... ValueError: Impossible parameters for the Guruswami-Sudan algorithm + + If ``code`` is not a GRS code, an error is raised:: + + sage: C = codes.RandomLinearCode(10, 4, GF(11)) + sage: codes.decoders.GRSGuruswamiSudanDecoder(C, tau = 2) + Traceback (most recent call last): + ... + ValueError: code has to be a generalized Reed-Solomon code """ + if not isinstance(code, GeneralizedReedSolomonCode): + raise ValueError("code has to be a generalized Reed-Solomon code") n, k = code.length(), code.dimension() if tau and parameters: if not GRSGuruswamiSudanDecoder.gs_satisfactory(tau, parameters[0], parameters[1], C = code): @@ -546,13 +567,15 @@ def __init__(self, code, tau = None, parameters = None, interpolation_alg = None raise ValueError("Specify either tau or parameters") if hasattr(interpolation_alg, '__call__'): self._interpolation_alg = interpolation_alg - elif interpolation_alg == None or interpolation_alg == "LinearAlgebra": + elif interpolation_alg == None or interpolation_alg == "LeeOSullivan": + self._interpolation_alg = gs_interpolation_lee_osullivan + elif interpolation_alg == "LinearAlgebra": self._interpolation_alg = gs_interpolation_linalg else: raise ValueError("Please provide a method or one of the allowed strings for interpolation_alg") if hasattr(root_finder, '__call__'): self._root_finder = root_finder - elif root_finder == None or interpolation_alg == "RothRuckenstein": + elif root_finder == None or root_finder == "RothRuckenstein": self._root_finder = rootfind_roth_ruckenstein else: raise ValueError("Please provide a method or one of the allowed strings for root_finder") diff --git a/src/sage/coding/guruswami_sudan/interpolation.py b/src/sage/coding/guruswami_sudan/interpolation.py index b3fc6b27d1c..c1cf64ab449 100644 --- a/src/sage/coding/guruswami_sudan/interpolation.py +++ b/src/sage/coding/guruswami_sudan/interpolation.py @@ -21,7 +21,9 @@ from sage.functions.other import ceil, binomial from sage.matrix.constructor import matrix +from sage.misc.misc_c import prod +####################### Linear algebra system solving ############################### def _flatten_once(lstlst): r""" Flattens a list of list into a list, but only flattening one layer and @@ -247,7 +249,7 @@ def gs_interpolation_linalg(points, tau, parameters, wy): EXAMPLES: The following parameters arise from Guruswami-Sudan decoding of an [6,2,5] - GRS code over F(11) with multiplicity 2 and list size 4. + GRS code over F(11) with multiplicity 2 and list size 4:: sage: from sage.coding.guruswami_sudan.interpolation import gs_interpolation_linalg sage: F = GF(11) @@ -258,7 +260,7 @@ def gs_interpolation_linalg(points, tau, parameters, wy): sage: Q = gs_interpolation_linalg(points, tau, params, wy); Q 4*x^5 - 4*x^4*y - 2*x^2*y^3 - x*y^4 + 3*x^4 - 4*x^2*y^2 + 5*y^4 - x^3 + x^2*y + 5*x*y^2 - 5*y^3 + 3*x*y - 2*y^2 + x - 4*y + 1 - We verify that the interpolation polynomial has a zero of multiplicity at least 2 in each point: + We verify that the interpolation polynomial has a zero of multiplicity at least 2 in each point:: sage: all( Q(x=a, y=b).is_zero() for (a,b) in points ) True @@ -279,3 +281,117 @@ def gs_interpolation_linalg(points, tau, parameters, wy): x, y = PF.gens() Q = sum([x**monomials[i][0] * y**monomials[i][1] * sol[i] for i in range(0, len(monomials))]) return Q + +####################### Lee-O'Sullivan's method ############################### + +def lee_osullivan_module(points, parameters, wy): + r""" + Returns the analytically straight-forward basis for the `\GF q[x]` module + containing all interpolation polynomials, as according to Lee and + O'Sullivan. + + The module is constructed in the following way: Let `R(x)` be the Lagrange + interpolation polynomial through the sought interpolation points `(x_i, + y_i)`, i.e. `R(x_i) = y_i`. Let `G(x) = \prod_{i=1}^n (x-x_i)`. Then the + `i`'th row of the basis matrix of the module is the coefficient-vector of + the following polynomial in `\GF q[x][y]`: + + `P_i(x,y) = G(x)^{[i-s]} (y - R(x))^{i - [i-s]} y^{[i-s]}` , + + where `[a]` for real `a` is `a` when `a > 0` and 0 otherwise. It is easily + seen that `P_i(x,y)` is an interpolation polynomial, i.e. it is zero with + multiplicity at least `s` on each of the points `(x_i, y_i)`. + + + INPUT: + + - ``points`` -- a list of tuples ``(xi, yi)`` such that we seek ``Q`` with + ``(xi,yi)`` being a root of ``Q`` with multiplicity ``s``. + + - ``parameters`` -- (default: ``None``) a pair of integers, where: + - the first integer is the multiplicity parameter `s` of Guruswami-Sudan algorithm and + - the second integer is the list size parameter. + + - ``wy`` -- an integer, the `y`-weight, where we seek ``Q`` of low + ``(1,wy)`` weighted degree. + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.interpolation import lee_osullivan_module + sage: F = GF(11) + sage: points = [(F(0), F(2)), (F(1), F(5)), (F(2), F(0)), (F(3), F(4)), (F(4), F(9))\ + , (F(5), F(1)), (F(6), F(9)), (F(7), F(10))] + sage: params = (1, 1) + sage: wy = 1 + sage: lee_osullivan_module(points, params, wy) + [x^8 + 5*x^7 + 3*x^6 + 9*x^5 + 4*x^4 + 2*x^3 + 9*x 0] + [ 10*x^7 + 4*x^6 + 9*x^4 + 7*x^3 + 2*x^2 + 9*x + 9 1] + """ + s, l = parameters[0], parameters[1] + F = points[0][0].parent() + PF = F['x'] + x = PF.gens()[0] + R = PF.lagrange_polynomial(points) + G = prod(x - points[i][0] for i in range(0, len(points))) + PFy = PF['y'] + y = PFy.gens()[0] + ybasis = [(y-R)**i * G**(s-i) for i in range(0, s+1)] \ + + [y**(i-s) * (y-R)**s for i in range(s+1, l+1)] + def pad(lst): + return lst + [0]*(l+1-len(lst)) + modbasis = [pad(yb.coefficients(sparse=False)) for yb in ybasis] + return matrix(PF, modbasis) + +def gs_interpolation_lee_osullivan(points, tau, parameters, wy): + r""" + Returns an interpolation polynomial Q(x,y) for the given input using the + module-based algorithm of Lee and O'Sullivan. + + This algorithm constructs an explicit `(\ell+1) \times (\ell+1)` polynomial + matrix whose rows span the `\GF q[x]` module of all interpolation + polynomials. It then runs a row reduction algorithm to find a low-shifted + degree vector in this row space, corresponding to a low weighted-degree + interpolation polynomial. + + INPUT: + + - ``points`` -- a list of tuples ``(xi, yi)`` such that we seek ``Q`` with + ``(xi,yi)`` being a root of ``Q`` with multiplicity ``s``. + + - ``tau`` -- an integer, the number of errors one wants to decode. + + - ``parameters`` -- (default: ``None``) a pair of integers, where: + - the first integer is the multiplicity parameter of Guruswami-Sudan algorithm and + - the second integer is the list size parameter. + + - ``wy`` -- an integer, the `y`-weight, where we seek ``Q`` of low + ``(1,wy)`` weighted degree. + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.interpolation import gs_interpolation_lee_osullivan + sage: F = GF(11) + sage: points = [(F(0), F(2)), (F(1), F(5)), (F(2), F(0)), (F(3), F(4)), (F(4), F(9))\ + , (F(5), F(1)), (F(6), F(9)), (F(7), F(10))] + sage: tau = 1 + sage: params = (1, 1) + sage: wy = 1 + sage: gs_interpolation_lee_osullivan(points, tau, params, wy) + x^3*y + 2*x^3 - x^2*y + 5*x^2 + 5*x*y - 5*x + 2*y - 4 + """ + from utils import apply_shifts, remove_shifts, leading_term + s, l = parameters[0], parameters[1] + F = points[0][0].parent() + M = lee_osullivan_module(points, (s,l), wy) + shifts = [i * wy for i in range(0,l+1)] + apply_shifts(M, shifts) + Mnew = M.row_reduced_form(transformation=False, old_call=False) + # Construct Q as the element of the row with the lowest weighted degree + degs = [(i, leading_term(Mnew.row(i)).degree()) for i in range(0,l+1)] + best = min(degs, key=lambda (i,d): d)[0] + remove_shifts(Mnew, shifts) + Qlist = Mnew.row(best) + PFxy = F['x,y'] + xx, yy = PFxy.gens() + Q = sum(yy**i * PFxy(Qlist[i]) for i in range(0,l+1)) + return Q diff --git a/src/sage/coding/guruswami_sudan/rootfinding.py b/src/sage/coding/guruswami_sudan/rootfinding.py index 90e65d11d62..bbd6bf5ec2d 100644 --- a/src/sage/coding/guruswami_sudan/rootfinding.py +++ b/src/sage/coding/guruswami_sudan/rootfinding.py @@ -218,7 +218,7 @@ def _roth_ruckenstein_i(Q, F, Rx, x, maxd, precision): checks and parent-extraction have been done. Most of the inputs corresponds to the output of ``_sanitise_rootfinding_input``. - INPUT:: + INPUT: - ``Q``, a modified version of ``Q``, where all monomials have been truncated to ``precision``. Represented as an `F[x]` list. @@ -238,7 +238,7 @@ def _roth_ruckenstein_i(Q, F, Rx, x, maxd, precision): sought precision for modular roots of `Q`. Otherwise, we will find unconditional roots. - OUTPUT:: + OUTPUT: - a list, containing all `F[x]` roots of `Q(x,y)`, possibly modular. If ``precision`` is given, we return a list of pairs `(f, h)`, where `f \in diff --git a/src/sage/coding/guruswami_sudan/utils.py b/src/sage/coding/guruswami_sudan/utils.py index 197fd06df13..e8128699775 100644 --- a/src/sage/coding/guruswami_sudan/utils.py +++ b/src/sage/coding/guruswami_sudan/utils.py @@ -22,6 +22,8 @@ from sage.functions.other import binomial, floor, sqrt from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer +from sage.arith.all import lcm +from sage.combinat.permutation import Permutation def polynomial_to_list(p, len): r""" @@ -132,4 +134,134 @@ def solve_degree2_to_integer_range(a,b,c): if mini > maxi: return (-2,-1) else: - return (mini, maxi) + return (mini,maxi) + +def apply_shifts(M, shifts): + r""" + Applies column shifts inplace to the polynomial matrix `M`. + + This is equivalent to multiplying the `n`th column of `M` with + `x^{shifts[n]}`. + + INPUT: + + - ``M`` -- a polynomial matrix + + - ``shifts`` -- a list of non-negative integer shifts + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.utils import apply_shifts + sage: F. = GF(7)[] + sage: M = matrix(F, [[2*x^2 + x, 5*x^2 + 2*x + 1, 4*x^2 + x],\ + [x^2 + 3*x + 3, 5*x^2 + 5*x + 1, 6*x^2 + 5*x + 4],\ + [5*x^2 + 2*x + 4, 4*x^2 + 2*x, 5*x^2 + x + 2]]) + sage: shifts = [1, 2, 3] + sage: apply_shifts(M, shifts) + sage: M + [ 2*x^3 + x^2 5*x^4 + 2*x^3 + x^2 4*x^5 + x^4] + [ x^3 + 3*x^2 + 3*x 5*x^4 + 5*x^3 + x^2 6*x^5 + 5*x^4 + 4*x^3] + [ 5*x^3 + 2*x^2 + 4*x 4*x^4 + 2*x^3 5*x^5 + x^4 + 2*x^3] + """ + x = M.base_ring().gen() + for j in range(M.ncols()): + M.set_col_to_multiple_of_col(j,j, x**shifts[j]) + +def remove_shifts(M, shifts): + r""" + Removes the shifts inplace to the matrix `M` as they were introduced by + :func:`apply_shifts`. + + If `M` was not earlier called with :func:`apply_shifts` using the same + shifts, then the least significant coefficients of the entries of `M`, + corresponding to how much we are shifting down, will be lost. + + INPUT: + + - ``M`` -- a polynomial matrix + + - ``shifts`` -- a list of non-negative integer shifts + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.utils import remove_shifts + sage: F. = GF(7)[] + sage: M = matrix(F, [[2*x^3 + x^2, 5*x^4 + 2*x^3 + x^2, 4*x^5 + x^4],\ + [x^3 + 3*x^2 + 3*x, 5*x^4 + 5*x^3 + x^2, 6*x^5 + 5*x^4 + 4*x^3],\ + [5*x^3 + 2*x^2 + 4*x, 4*x^4 + 2*x^3, 5*x^5 + x^4 + 2*x^3]]) + + sage: shifts = [1, 2, 3] + sage: remove_shifts(M, shifts) + sage: M + [ 2*x^2 + x 5*x^2 + 2*x + 1 4*x^2 + x] + [ x^2 + 3*x + 3 5*x^2 + 5*x + 1 6*x^2 + 5*x + 4] + [5*x^2 + 2*x + 4 4*x^2 + 2*x 5*x^2 + x + 2] + """ + for i in range(M.nrows()): + for j in range(M.ncols()): + M[i,j] = M[i,j].shift(-shifts[j]) + +def _leading_position(v, shifts=None): + r""" + Returns the position of the highest-degree term of ``v``. + + This methods can manage shifted degree, by providing ``shift`` to it. + + In case of several positions having the same, highest degree, the right-most + position is given. + + INPUT: + + - ``v`` -- a vector of polynomials + + - ``shifts`` -- (default: ``None``) a list of integer shifts to consider ``v`` under. + If ``None``, all shifts are considered as ``0``. + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.utils import _leading_position + sage: F. = GF(7)[] + sage: v = vector(F, [3*x^2 + 3*x + 4, 4*x + 3, 4*x^2 + 4*x + 5, x^2 + 2*x + 5, 3*x^2 + 5*x]) + sage: _leading_position(v) + 4 + """ + if not shifts: + shifts=[0]*len(v) + best=-1 + bestp=-1 + for p in range(0,len(v)): + if not v[p].is_zero(): + vpdeg = v[p].degree() + shifts[p] + if vpdeg >= best: + best=vpdeg + bestp = p + if best==-1: + return -1 + else: + return bestp + +def leading_term(v, shifts=None): + r""" + Returns the term of ``v`` with the highest degree. + + This methods can manage shifted degree, by providing ``shift`` to it. + + In case of several positions having the same, highest degree, the term with + the right-most position is returned. + + INPUT: + + - ``v`` -- a vector of polynomials + + - ``shifts`` -- (default: ``None``) a list of integer shifts to consider ``v`` under. + If ``None``, all shifts are considered as ``0``. + + EXAMPLES:: + + sage: from sage.coding.guruswami_sudan.utils import leading_term + sage: F. = GF(7)[] + sage: v = vector(F, [3*x^2 + 3*x + 4, 4*x + 3, 4*x^2 + 4*x + 5, x^2 + 2*x + 5, 3*x^2 + 5*x]) + sage: leading_term(v) + 3*x^2 + 5*x + """ + return v[_leading_position(v, shifts=shifts)] diff --git a/src/sage/coding/hamming_code.py b/src/sage/coding/hamming_code.py new file mode 100644 index 00000000000..58e0abc04fa --- /dev/null +++ b/src/sage/coding/hamming_code.py @@ -0,0 +1,177 @@ +r""" +Hamming Code + +Given an integer `r` and a field `F`, such that `F=GF(q)`, +the `[n, k, d]` code with length `n=\frac{q^{r}-1}{q-1}`, +dimension `k=\frac{q^{r}-1}{q-1} - r` and minimum distance +`d=3` is called the Hamming Code of order `r`. + +REFERENCES: + + .. [R] Introduction to Coding Theory, Ron Roth, Cambridge University Press, 2006 +""" + +#***************************************************************************** +# Copyright (C) 2016 David Lucas +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from linear_code import (AbstractLinearCode, + LinearCodeParityCheckEncoder, + LinearCodeSyndromeDecoder, + LinearCodeNearestNeighborDecoder) +from sage.matrix.matrix_space import MatrixSpace +from sage.schemes.projective.projective_space import ProjectiveSpace +from sage.rings.integer import Integer +from sage.rings.ring import Field +from copy import copy + +class HammingCode(AbstractLinearCode): + r""" + Representation of a Hamming code. + + INPUT: + + - ``base_field`` -- the base field over which ``self`` is defined. + + - ``order`` -- the order of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(7), 3) + sage: C + [57, 54] Hamming Code over Finite Field of size 7 + """ + _registered_encoders = {} + _registered_decoders = {} + + def __init__(self, base_field, order): + r""" + TESTS: + + If ``base_field`` is not a finite field, an exception is raised:: + + sage: codes.HammingCode(RR, 3) + Traceback (most recent call last): + ... + ValueError: base_field has to be a finite field + + If ``order`` is not a Sage Integer or a Python int, an exception is raised:: + sage: codes.HammingCode(GF(3), 3.14) + Traceback (most recent call last): + ... + ValueError: order has to be a Sage Integer or a Python int + """ + if isinstance(base_field, (Integer, int)) and isinstance(order, Field): + from sage.misc.superseded import deprecation + deprecation(19930, "codes.HammingCode(r, F) is now deprecated. Please use codes.HammingCode(F, r) instead.") + tmp = copy(order) + order = copy(base_field) + base_field = copy(tmp) + + if not base_field.is_finite(): + raise ValueError("base_field has to be a finite field") + if not isinstance(order, (Integer, int)): + raise ValueError("order has to be a Sage Integer or a Python int") + + q = base_field.order() + length = Integer((q ** order - 1) / (q - 1)) + super(HammingCode, self).__init__(base_field, length, "ParityCheck", "Syndrome") + self._dimension = length - order + + def __eq__(self, other): + r""" + Tests equality of Hamming Code objects. + + EXAMPLES:: + + sage: C1 = codes.HammingCode(GF(7), 3) + sage: C2 = codes.HammingCode(GF(7), 3) + sage: C1 == C2 + True + """ + return isinstance(other, HammingCode)\ + and self.length() == other.length()\ + and self.dimension() == other.dimension() + + def _repr_(self): + r""" + Returns a string representation of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(7), 3) + sage: C + [57, 54] Hamming Code over Finite Field of size 7 + """ + return "[%s, %s] Hamming Code over %s"\ + % (self.length(), self.dimension(), self.base_field()) + + def _latex_(self): + r""" + Returns a latex representation of ``self``. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(7), 3) + sage: latex(C) + [57, 54] \textnormal{ Hamming Code over Finite Field of size 7} + """ + return "[%s, %s] \\textnormal{ Hamming Code over %s}"\ + % (self.length(), self.dimension(), self.base_field()) + + + def parity_check_matrix(self): + r""" + Returns a parity check matrix of ``self``. + + The construction of the parity check matrix in case ``self`` + is not a binary code is not really well documented. + Regarding the choice of projective geometry, one might check: + + - the note over section 2.3 in [R]_, pages 47-48 + - the dedicated paragraph in [HP]_, page 30 + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(3), 3) + sage: C.parity_check_matrix() + [1 0 1 1 0 1 0 1 1 1 0 1 1] + [0 1 1 2 0 0 1 1 2 0 1 1 2] + [0 0 0 0 1 1 1 1 1 2 2 2 2] + """ + n = self.length() + F = self.base_field() + m = n - self.dimension() + MS = MatrixSpace(F,n,m) + X = ProjectiveSpace(m-1,F) + PFn = [list(p) for p in X.point_set(F).points(F)] + + H = MS(PFn).transpose() + H = H[::-1, :] + + return H + + def minimum_distance(self): + r""" + Returns the minimum distance of ``self``. + It is always 3 as ``self`` is a Hamming Code. + + EXAMPLES:: + + sage: C = codes.HammingCode(GF(7), 3) + sage: C.minimum_distance() + 3 + """ + return 3 + +####################### registration ############################### + +HammingCode._registered_encoders["ParityCheck"] = LinearCodeParityCheckEncoder +HammingCode._registered_decoders["Syndrome"] = LinearCodeSyndromeDecoder +HammingCode._registered_decoders["NearestNeighbor"] = LinearCodeNearestNeighborDecoder diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 22186a71d20..a1e65186abe 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -211,6 +211,8 @@ # # http://www.gnu.org/licenses/ #****************************************************************************** +# python3 +from __future__ import division import sage.modules.module as module from sage.categories.modules import Modules @@ -265,8 +267,8 @@ def code2leon(C): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)); C - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: C = codes.HammingCode(GF(2), 3); C + [7, 4] Hamming Code over Finite Field of size 2 sage: file_loc = sage.coding.linear_code.code2leon(C) sage: f = open(file_loc); print f.read() LIBRARY code; @@ -378,7 +380,7 @@ def min_wt_vec_gap(Gmat, n, k, F, algorithm=None): We check that :trac:`18480` is fixed:: - sage: codes.HammingCode(2, GF(2)).minimum_distance() + sage: codes.HammingCode(GF(2), 2).minimum_distance() 3 AUTHORS: @@ -860,7 +862,7 @@ def __init__(self, base_field, length, default_encoder_name, default_decoder_nam sage: codes.LinearCode(IntegerModRing(4),matrix.ones(4)) Traceback (most recent call last): ... - ValueError: 'generator_matrix' must be defined on a field (not a ring) + ValueError: 'generator' must be defined on a field (not a ring) """ if not isinstance(length, (int, Integer)): raise ValueError("length must be a Python int or a Sage Integer") @@ -900,7 +902,7 @@ def _an_element_(self): EXAMPLES:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.an_element() (1, 0, 0, 0, 0, 1, 1) sage: C2 = C.cartesian_product(C) @@ -937,7 +939,7 @@ def add_decoder(self, name, decoder): We now create a new code:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) We can add our new decoder to the list of available decoders of C:: @@ -947,7 +949,7 @@ def add_decoder(self, name, decoder): We can verify that any new code will not know MyDecoder:: - sage: C2 = codes.HammingCode(3, GF(3)) + sage: C2 = codes.HammingCode(GF(2), 3) sage: C2.decoders_available() ['Syndrome', 'NearestNeighbor'] @@ -999,25 +1001,25 @@ def add_encoder(self, name, encoder): We now create a new code:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) We can add our new encoder to the list of available encoders of C:: sage: C.add_encoder("MyEncoder", MyEncoder) sage: C.encoders_available() - ['MyEncoder', 'GeneratorMatrix'] + ['MyEncoder', 'ParityCheck'] We can verify that any new code will not know MyEncoder:: - sage: C2 = codes.HammingCode(3, GF(3)) + sage: C2 = codes.HammingCode(GF(2), 3) sage: C2.encoders_available() - ['GeneratorMatrix'] + ['ParityCheck'] TESTS: It is impossible to use a name which is in the dictionary of available encoders:: - sage: C.add_encoder("GeneratorMatrix", MyEncoder) + sage: C.add_encoder("ParityCheck", MyEncoder) Traceback (most recent call last): ... ValueError: There is already a registered encoder with this name @@ -1054,7 +1056,7 @@ def automorphism_group_gens(self, equivalence="semilinear"): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,"z")); + sage: C = codes.HammingCode(GF(4, 'z'), 3) sage: C.automorphism_group_gens() ([((1, 1, 1, z, z + 1, z + 1, z + 1, z, z, 1, 1, 1, z, z, z + 1, z, z, z + 1, z + 1, z + 1, 1); (1,6,12,17)(2,16,4,5,11,8,14,13)(3,21,19,10,20,18,15,9), Ring endomorphism of Finite Field in z of size 2^2 Defn: z |--> z + 1), ((1, 1, 1, z, z + 1, 1, 1, z, z, z + 1, z, z, z + 1, z + 1, z + 1, 1, z + 1, z, z, 1, 1); (1,6,9,13,15,18)(2,21)(3,16,7)(4,5,11,10,12,14)(17,19), Ring endomorphism of Finite Field in z of size 2^2 @@ -1082,7 +1084,7 @@ def ambient_space(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.ambient_space() Vector space of dimension 7 over Finite Field of size 2 """ @@ -1184,7 +1186,7 @@ def assmus_mattson_designs(self, t, mode=None): for w in nonzerowts: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)/binomial(n,t),wts[w])) + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) wtsp = Cp.spectrum() dp = min([i for i in range(1,len(wtsp)) if wtsp[i]!=0]) nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i]!=0 and i<=n-t and i>=dp] @@ -1193,11 +1195,11 @@ def assmus_mattson_designs(self, t, mode=None): for w in nonzerowtsp: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)/binomial(n,t),wts[w])) + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) if s<=d-t: - des = [[t,(n,w,wts[w]*binomial(w,t)/binomial(n,t))] for w in nonzerowts] + des = [[t,(n,w,wts[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowts] ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des] - desp = [[t,(n,w,wtsp[w]*binomial(w,t)/binomial(n,t))] for w in nonzerowtsp] + desp = [[t,(n,w,wtsp[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowtsp] ans = ans + ["weights from C*: ",nonzerowtsp,"designs from C*: ",desp] return ans return 0 @@ -1221,7 +1223,7 @@ def basis(self): EXAMPLES:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.basis() [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] """ @@ -1246,7 +1248,7 @@ def binomial_moment(self, i): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.binomial_moment(2) 0 sage: C.binomial_moment(4) # long time @@ -1273,7 +1275,7 @@ def binomial_moment(self, i): if in-dp and i<=n: - return binomial(n,i)*(q**(i+k-n) -1)/(q-1) + return binomial(n,i)*(q**(i+k-n) -1)//(q-1) P = SetPartitions(J,2).list() b = QQ(0) for p in P: @@ -1282,7 +1284,7 @@ def binomial_moment(self, i): if len(S)==n-i: C_S = self.shortened(S) k_S = C_S.dimension() - b = b + (q**(k_S) -1)/(q-1) + b = b + (q**(k_S) -1)//(q-1) return b @cached_method @@ -1303,16 +1305,18 @@ def _canonize(self, equivalence): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,"z")); + sage: C = codes.HammingCode(GF(4, 'z'), 3) sage: aut_group_can_label = C._canonize("semilinear") sage: C_iso = LinearCode(aut_group_can_label.get_transporter()*C.generator_matrix()) sage: C_iso == aut_group_can_label.get_canonical_form() True sage: aut_group_can_label.get_autom_gens() [((z, z + 1, 1, z + 1, z, z, z, z + 1, 1, z + 1, z + 1, z, z + 1, 1, z + 1, 1, z, z + 1, 1, z + 1, z + 1); (1,12,21,18,15,20)(2,19,16)(3,4,11,6,13,7)(5,8)(10,14,17), Ring endomorphism of Finite Field in z of size 2^2 - Defn: z |--> z + 1), ((z + 1, 1, z, 1, 1, z, 1, z + 1, z, z + 1, z, z + 1, z, 1, z, z + 1, z, z, z + 1, z + 1, 1); (1,20,2,9,13,21,11,17,10,16,3,5,18,8)(4,12,6,15,14,19,7), Ring endomorphism of Finite Field in z of size 2^2 - Defn: z |--> z + 1), ((z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z); (), Ring endomorphism of Finite Field in z of size 2^2 - Defn: z |--> z)] + Defn: z |--> z + 1), + ((z + 1, 1, z, 1, 1, z, 1, z + 1, z, z + 1, z, z + 1, z, 1, z, z + 1, z, z, z + 1, z + 1, 1); (1,20,2,9,13,21,11,17,10,16,3,5,18,8)(4,12,6,15,14,19,7), Ring endomorphism of Finite Field in z of size 2^2 + Defn: z |--> z + 1), + ((z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z); (), Ring endomorphism of Finite Field in z of size 2^2 + Defn: z |--> z)] """ from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel return LinearCodeAutGroupCanLabel(self, algorithm_type=equivalence) @@ -1346,7 +1350,7 @@ def canonical_representative(self, equivalence="semilinear"): EXAMPLES:: sage: F. = GF(4) - sage: C = codes.HammingCode(3,F) + sage: C = codes.HammingCode(F, 3) sage: CanRep, transp = C.canonical_representative() Check that the transporter element is correct:: @@ -1380,7 +1384,7 @@ def __contains__(self, v): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: vector((1, 0, 0, 0, 0, 1, 1)) in C # indirect doctest True sage: vector((1, 0, 0, 0, 2, 1, 1)) in C # indirect doctest @@ -1398,7 +1402,7 @@ def characteristic(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.characteristic() 2 """ @@ -1436,7 +1440,7 @@ def chinen_polynomial(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.chinen_polynomial() # long time 1/5*(2*sqrt(2)*t^3 + 2*sqrt(2)*t^2 + 2*t^2 + sqrt(2)*t + 2*t + 1)/(sqrt(2) + 1) sage: C = codes.TernaryGolayCode() @@ -1493,34 +1497,6 @@ def chinen_polynomial(self): f = CP/CP(1,s) return f(t,sqrt(q)) - def __cmp__(self, right): - r""" - Returns True if the generator matrices of `self` and `right` are - equal. - - EXAMPLES:: - - sage: C = codes.HammingCode(3,GF(2)) - sage: MS = MatrixSpace(GF(2),4,7) - sage: G = MS([1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]) - sage: G - [1 0 0 0 0 1 1] - [0 1 0 0 1 0 1] - [0 0 1 0 1 1 0] - [0 0 0 1 1 1 1] - sage: D = LinearCode(G) - sage: C == D - True - - sage: Cperp = C.dual_code() - sage: Cperpperp = Cperp.dual_code() - sage: C == Cperpperp - True - """ - if not isinstance(right, LinearCode): - return cmp(type(self), type(right)) - return cmp(self.generator_matrix(), right.generator_matrix()) - def parity_check_matrix(self): r""" Returns the parity check matrix of ``self``. @@ -1530,10 +1506,10 @@ def parity_check_matrix(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: Cperp = C.dual_code() sage: C; Cperp - Linear code of length 7, dimension 4 over Finite Field of size 2 + [7, 4] Hamming Code over Finite Field of size 2 Linear code of length 7, dimension 3 over Finite Field of size 2 sage: C.generator_matrix() [1 0 0 0 0 1 1] @@ -1578,7 +1554,7 @@ def covering_radius(self): EXAMPLES:: - sage: C = codes.HammingCode(5,GF(2)) + sage: C = codes.HammingCode(GF(2), 5) sage: C.covering_radius() # optional - gap_packages (Guava package) 1 """ @@ -1813,7 +1789,7 @@ def is_projective(self): REFERENCE: - .. [BS11] E. Byrne and A. Sneyd, + .. [BS11] \E. Byrne and A. Sneyd, On the Parameters of Codes with Two Homogeneous Weights. WCC 2011-Workshop on coding and cryptography, pp. 81-90. 2011. https://hal.inria.fr/inria-00607341/document @@ -1849,10 +1825,10 @@ def dual_code(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.dual_code() Linear code of length 7, dimension 3 over Finite Field of size 2 - sage: C = codes.HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(GF(4, 'a'), 3) sage: C.dual_code() Linear code of length 21, dimension 3 over Finite Field in a of size 2^2 """ @@ -1878,7 +1854,7 @@ def direct_sum(self, other): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(GF(2), 3) sage: C2 = C1.direct_sum(C1); C2 Linear code of length 14, dimension 8 over Finite Field of size 2 sage: C3 = C1.direct_sum(C2); C3 @@ -1907,20 +1883,16 @@ def __eq__(self, right): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)) - sage: C2 = codes.HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(GF(2), 3) + sage: C2 = codes.HammingCode(GF(2), 3) sage: C1 == C2 True - sage: C2 = C1.extended_code() - sage: C3 = C2.punctured([7]) - sage: C1 == C3 - True TESTS: We check that :trac:`16644` is fixed:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C == ZZ False """ @@ -2096,9 +2068,9 @@ def extended_code(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(GF(4,'a'), 3) sage: C - Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 + [21, 18] Hamming Code over Finite Field in a of size 2^2 sage: Cx = C.extended_code() sage: Cx Linear code of length 22, dimension 18 over Finite Field in a of size 2^2 @@ -2120,10 +2092,10 @@ def galois_closure(self, F0): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(GF(4,'a'), 3) sage: Cc = C.galois_closure(GF(2)) sage: C; Cc - Linear code of length 21, dimension 18 over Finite Field in a of size 2^2 + [21, 18] Hamming Code over Finite Field in a of size 2^2 Linear code of length 21, dimension 20 over Finite Field in a of size 2^2 sage: c = C.basis()[2] sage: V = VectorSpace(GF(4,'a'),21) @@ -2313,7 +2285,7 @@ def gens(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.gens() [(1, 0, 0, 0, 0, 1, 1), (0, 1, 0, 0, 1, 0, 1), (0, 0, 1, 0, 1, 1, 0), (0, 0, 0, 1, 1, 1, 1)] """ @@ -2325,12 +2297,12 @@ def genus(self): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)); C1 - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: C1 = codes.HammingCode(GF(2), 3); C1 + [7, 4] Hamming Code over Finite Field of size 2 sage: C1.genus() 1 - sage: C2 = codes.HammingCode(2,GF(4,"a")); C2 - Linear code of length 5, dimension 3 over Finite Field in a of size 2^2 + sage: C2 = codes.HammingCode(GF(4,"a"), 2); C2 + [5, 3] Hamming Code over Finite Field in a of size 2^2 sage: C2.genus() 0 @@ -2349,7 +2321,7 @@ def __iter__(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: [list(c) for c in C if c.hamming_weight() < 4] [[0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 1], [0, 1, 0, 0, 1, 0, 1], [0, 0, 1, 0, 1, 1, 0], @@ -2358,7 +2330,7 @@ def __iter__(self): TESTS:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: L = list(C) sage: L[10].is_immutable() True @@ -2402,7 +2374,7 @@ def is_permutation_automorphism(self,g): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(3)) + sage: C = codes.HammingCode(GF(3), 3) sage: g = SymmetricGroup(13).random_element() sage: C.is_permutation_automorphism(g) 0 @@ -2442,8 +2414,8 @@ def is_permutation_equivalent(self,other,algorithm=None): sage: g = x^3+x+1 sage: C1 = codes.CyclicCodeFromGeneratingPolynomial(7,g); C1 Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C2 = codes.HammingCode(3,GF(2)); C2 - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: C2 = codes.HammingCode(GF(2), 3); C2 + [7, 4] Hamming Code over Finite Field of size 2 sage: C1.is_permutation_equivalent(C2) True sage: C1.is_permutation_equivalent(C2,algorithm="verbose") @@ -2486,7 +2458,7 @@ def is_self_dual(self): sage: C = codes.ExtendedBinaryGolayCode() sage: C.is_self_dual() True - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.is_self_dual() False """ @@ -2505,7 +2477,7 @@ def is_self_orthogonal(self): sage: C = codes.ExtendedBinaryGolayCode() sage: C.is_self_orthogonal() True - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.is_self_orthogonal() False sage: C = codes.QuasiQuadraticResidueCode(11) # optional - gap_packages (Guava package) @@ -2520,7 +2492,7 @@ def is_galois_closed(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,"a")) + sage: C = codes.HammingCode(GF(4,"a"), 3) sage: C.is_galois_closed() False """ @@ -2534,7 +2506,7 @@ def is_subcode(self, other): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(GF(2), 3) sage: G1 = C1.generator_matrix() sage: G2 = G1.matrix_from_rows([0,1,2]) sage: C2 = LinearCode(G2) @@ -2551,7 +2523,7 @@ def is_subcode(self, other): sage: C5 = C1.shortened([1]) sage: C5.is_subcode(C1) False - sage: C1 = codes.HammingCode(3,GF(9,"z")) + sage: C1 = codes.HammingCode(GF(9,"z"), 3) sage: G1 = C1.generator_matrix() sage: G2 = G1.matrix_from_rows([0,1,2]) sage: C2 = LinearCode(G2) @@ -2570,7 +2542,7 @@ def cardinality(self): EXAMPLES:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.cardinality() 16 sage: len(C) @@ -2586,7 +2558,7 @@ def length(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.length() 7 """ @@ -2598,7 +2570,7 @@ def list(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: Clist = C.list() sage: Clist[5]; Clist[5] in C (1, 0, 1, 0, 1, 0, 1) @@ -2612,7 +2584,7 @@ def _magma_init_(self, magma): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: Cm = magma(C) # optional - magma, indirect doctest sage: Cm.MinimumWeight() # optional - magma 3 @@ -2675,14 +2647,14 @@ def minimum_distance(self, algorithm=None): Another example.:: - sage: C = codes.HammingCode(2,GF(4,"a")); C - Linear code of length 5, dimension 3 over Finite Field in a of size 2^2 + sage: C = codes.HammingCode(GF(4,"a"), 2); C + [5, 3] Hamming Code over Finite Field in a of size 2^2 sage: C.minimum_distance() 3 TESTS:: - sage: C = codes.HammingCode(2,GF(4,"a")) + sage: C = codes.RandomLinearCode(5, 2, GF(4,"a")) sage: C.minimum_distance(algorithm='something') Traceback (most recent call last): ... @@ -2806,16 +2778,16 @@ def permutation_automorphism_group(self, algorithm="partition"): sage: G = C.permutation_automorphism_group() sage: G.order() 244823040 - sage: C = codes.HammingCode(5, GF(2)) + sage: C = codes.HammingCode(GF(2), 5) sage: G = C.permutation_automorphism_group() sage: G.order() 9999360 - sage: C = codes.HammingCode(2,GF(3)); C - Linear code of length 4, dimension 2 over Finite Field of size 3 + sage: C = codes.HammingCode(GF(3), 2); C + [4, 2] Hamming Code over Finite Field of size 3 sage: C.permutation_automorphism_group(algorithm="partition") Permutation Group with generators [(1,3,4)] - sage: C = codes.HammingCode(2,GF(4,"z")); C - Linear code of length 5, dimension 3 over Finite Field in z of size 2^2 + sage: C = codes.HammingCode(GF(4,"z"), 2); C + [5, 3] Hamming Code over Finite Field in z of size 2^2 sage: G = C.permutation_automorphism_group(algorithm="partition"); G Permutation Group with generators [(1,3)(4,5), (1,4)(3,5)] sage: GG = C.permutation_automorphism_group(algorithm="codecan") # long time @@ -2922,14 +2894,14 @@ def permuted_code(self, p): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: G = C.permutation_automorphism_group(); G Permutation Group with generators [(4,5)(6,7), (4,6)(5,7), (2,3)(6,7), (2,4)(3,5), (1,2)(5,6)] sage: g = G("(2,3)(6,7)") sage: Cg = C.permuted_code(g) sage: Cg Linear code of length 7, dimension 4 over Finite Field of size 2 - sage: C == Cg + sage: C.generator_matrix() == Cg.generator_matrix_systematic() True """ F = self.base_ring() @@ -2967,7 +2939,7 @@ def punctured(self, L): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.punctured([1,2]) Linear code of length 5, dimension 4 over Finite Field of size 2 """ @@ -2990,7 +2962,7 @@ def random_element(self, *args, **kwds): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(4,'a')) + sage: C = codes.HammingCode(GF(4,'a'), 3) sage: C.random_element() # random test (1, 0, 0, a + 1, 1, a, a, a + 1, a + 1, 1, 1, 0, a + 1, a, 0, a, a, 0, a, a, 1) @@ -3037,7 +3009,7 @@ def redundancy_matrix(C): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.generator_matrix() [1 0 0 0 0 1 1] [0 1 0 0 1 0 1] @@ -3053,7 +3025,7 @@ def redundancy_matrix(C): [0 1 0 0 1 0 1] [0 0 1 0 1 1 0] [0 0 0 1 1 1 1] - sage: C = codes.HammingCode(2,GF(3)) + sage: C = codes.HammingCode(GF(3), 2) sage: C.generator_matrix() [1 0 1 1] [0 1 1 2] @@ -3083,7 +3055,7 @@ def sd_duursma_data(C, i): REFERENCES: - .. [D] I. Duursma, "Extremal weight enumerators and ultraspherical + .. [D] \I. Duursma, "Extremal weight enumerators and ultraspherical polynomials" EXAMPLES:: @@ -3135,7 +3107,7 @@ def sd_duursma_q(C,i,d0): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(GF(2), 3) sage: C2 = C1.extended_code(); C2 Linear code of length 8, dimension 4 over Finite Field of size 2 sage: C2.is_self_dual() @@ -3202,7 +3174,7 @@ def sd_zeta_polynomial(C, typ=1): EXAMPLES:: - sage: C1 = codes.HammingCode(3,GF(2)) + sage: C1 = codes.HammingCode(GF(2), 3) sage: C2 = C1.extended_code(); C2 Linear code of length 8, dimension 4 over Finite Field of size 2 sage: C2.is_self_dual() @@ -3265,7 +3237,7 @@ def shortened(self, L): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.shortened([1,2]) Linear code of length 5, dimension 2 over Finite Field of size 2 """ @@ -3303,28 +3275,28 @@ def spectrum(self, algorithm=None): sage: C.spectrum() [1, 0, 0, 7, 7, 0, 0, 1] sage: F. = GF(2^2,"z") - sage: C = codes.HammingCode(2, F); C - Linear code of length 5, dimension 3 over Finite Field in z of size 2^2 + sage: C = codes.HammingCode(F, 2); C + [5, 3] Hamming Code over Finite Field in z of size 2^2 sage: C.spectrum() [1, 0, 0, 30, 15, 18] - sage: C = codes.HammingCode(3,GF(2)); C - Linear code of length 7, dimension 4 over Finite Field of size 2 + sage: C = codes.HammingCode(GF(2), 3); C + [7, 4] Hamming Code over Finite Field of size 2 sage: C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) [1, 0, 0, 7, 7, 0, 0, 1] sage: C.spectrum(algorithm="gap") [1, 0, 0, 7, 7, 0, 0, 1] sage: C.spectrum(algorithm="binary") [1, 0, 0, 7, 7, 0, 0, 1] - sage: C = codes.HammingCode(3,GF(3)); C - Linear code of length 13, dimension 10 over Finite Field of size 3 + sage: C = codes.HammingCode(GF(3), 3); C + [13, 10] Hamming Code over Finite Field of size 3 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True - sage: C = codes.HammingCode(2,GF(5)); C - Linear code of length 6, dimension 4 over Finite Field of size 5 + sage: C = codes.HammingCode(GF(5), 2); C + [6, 4] Hamming Code over Finite Field of size 5 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True - sage: C = codes.HammingCode(2,GF(7)); C - Linear code of length 8, dimension 6 over Finite Field of size 7 + sage: C = codes.HammingCode(GF(7), 2); C + [8, 6] Hamming Code over Finite Field of size 7 sage: C.spectrum() == C.spectrum(algorithm="leon") # optional - gap_packages (Guava package) True @@ -3383,7 +3355,7 @@ def standard_form(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.generator_matrix() [1 0 0 0 0 1 1] [0 1 0 0 1 0 1] @@ -3436,7 +3408,7 @@ def support(self): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.spectrum() [1, 0, 0, 7, 7, 0, 0, 1] sage: C.support() @@ -3548,7 +3520,7 @@ def weight_enumerator(self, names="xy", name2=None): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.weight_enumerator() x^7 + 7*x^4*y^3 + 7*x^3*y^4 + y^7 sage: C.weight_enumerator(names="st") @@ -3575,11 +3547,11 @@ def weight_enumerator(self, names="xy", name2=None): @cached_method def zero(self): r""" - Return the zero vector. + Returns the zero vector of ``self``. EXAMPLES:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.zero() (0, 0, 0, 0, 0, 0, 0) sage: C.sum(()) # indirect doctest @@ -3587,9 +3559,7 @@ def zero(self): sage: C.sum((C.gens())) # indirect doctest (1, 1, 1, 1, 1, 1, 1) """ - v = 0*self.gens()[0] - v.set_immutable() - return v + return self.ambient_space().zero() def zeta_polynomial(self, name="T"): r""" @@ -3608,7 +3578,7 @@ def zeta_polynomial(self, name="T"): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.zeta_polynomial() 2/5*T^2 + 2/5*T + 1/5 sage: C = best_known_linear_code(6,3,GF(2)) # optional - gap_packages (Guava package) @@ -3616,7 +3586,7 @@ def zeta_polynomial(self, name="T"): 3 sage: C.zeta_polynomial() # optional - gap_packages (Guava package) 2/5*T^2 + 2/5*T + 1/5 - sage: C = codes.HammingCode(4,GF(2)) + sage: C = codes.HammingCode(GF(2), 4) sage: C.zeta_polynomial() 16/429*T^6 + 16/143*T^5 + 80/429*T^4 + 32/143*T^3 + 30/143*T^2 + 2/13*T + 1/13 sage: F. = GF(4,"z") @@ -3672,7 +3642,7 @@ def zeta_function(self, name="T"): EXAMPLES:: - sage: C = codes.HammingCode(3,GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: C.zeta_function() (2/5*T^2 + 2/5*T + 1/5)/(2*T^2 - 3*T + 1) """ @@ -3736,17 +3706,17 @@ class LinearCode(AbstractLinearCode): minimum distance, will use generic, slow algorithms. If you are looking for constructing a code from a more specific family, see - if the family has been implemented by investigating codes.. These - more specific classes use properties particular for that family to allow + if the family has been implemented by investigating `codes.`. These + more specific classes use properties particular to that family to allow faster algorithms, and could also have family-specific methods. See :wikipedia:`Linear_code` for more information on unstructured linear codes. INPUT: - - ``generator_matrix`` -- a generator matrix over a finite field (``G`` can be + - ``generator`` -- a generator matrix over a finite field (``G`` can be defined over a finite ring but the matrices over that ring must have - certain attributes, such as ``rank``) + certain attributes, such as ``rank``); or a code over a finite field - ``d`` -- (optional, default: ``None``) the minimum distance of the code @@ -3755,7 +3725,6 @@ class LinearCode(AbstractLinearCode): The veracity of the minimum distance ``d``, if provided, is not checked. - EXAMPLES:: sage: MS = MatrixSpace(GF(2),4,7) @@ -3790,12 +3759,28 @@ class LinearCode(AbstractLinearCode): sage: C = LinearCode(G) sage: C Linear code of length 7, dimension 4 over Finite Field of size 5 + + Providing a code as the parameter in order to "forget" its structure (see + :trac:`20198`):: + + sage: C = codes.GeneralizedReedSolomonCode(GF(23).list(), 12) + sage: LinearCode(C) + Linear code of length 23, dimension 12 over Finite Field of size 23 + + Another example:: + + sage: C = codes.HammingCode(GF(7), 3) + sage: C + [57, 54] Hamming Code over Finite Field of size 7 + sage: LinearCode(C) + Linear code of length 57, dimension 54 over Finite Field of size 7 AUTHORS: - David Joyner (11-2005) + - Charles Prior (03-2016): :trac:`20198`, LinearCode from a code """ - def __init__(self, generator_matrix, d=None): + def __init__(self, generator, d=None): r""" See the docstring for :meth:`LinearCode`. @@ -3816,7 +3801,7 @@ def __init__(self, generator_matrix, d=None): TESTS:: - sage: C = codes.HammingCode(3, GF(2)) + sage: C = codes.HammingCode(GF(2), 3) sage: TestSuite(C).run() Check that it works even with input matrix with non full rank (see @@ -3846,22 +3831,28 @@ def __init__(self, generator_matrix, d=None): ... ValueError: this linear code contains no non-zero vector """ - base_ring = generator_matrix.base_ring() + + base_ring = generator.base_ring() if not base_ring.is_field(): - raise ValueError("'generator_matrix' must be defined on a field (not a ring)") - - # if the matrix does not have full rank we replace it - if generator_matrix.rank() != generator_matrix.nrows(): - from sage.matrix.constructor import matrix - basis = generator_matrix.row_space().basis() - generator_matrix = matrix(base_ring, basis) - - if generator_matrix.nrows() == 0: - raise ValueError("this linear code contains no non-zero vector") - - super(LinearCode, self).__init__(base_ring, generator_matrix.ncols(), "GeneratorMatrix", "Syndrome") - self._generator_matrix = generator_matrix - self._dimension = generator_matrix.rank() + raise ValueError("'generator' must be defined on a field (not a ring)") + + try: + basis = generator.row_space().basis() # generator matrix case + + # if the matrix does not have full rank we replace it + if len(basis) != generator.nrows(): + from sage.matrix.constructor import matrix + generator = matrix(base_ring, basis) + + if generator.nrows() == 0: + raise ValueError("this linear code contains no non-zero vector") + except AttributeError: + # Assume input is an AbstractLinearCode, extract its generator matrix + generator = generator.generator_matrix() + + super(LinearCode, self).__init__(base_ring, generator.ncols(), "GeneratorMatrix", "Syndrome") + self._generator_matrix = generator + self._dimension = generator.rank() self._minimum_distance = d def _repr_(self): @@ -4025,6 +4016,83 @@ def generator_matrix(self): return self.code().generator_matrix() + + + + + + + + +class LinearCodeParityCheckEncoder(Encoder): + r""" + Encoder based on :meth:`parity_check_matrix` for Linear codes. + + It constructs the generator matrix through the parity check matrix. + + INPUT: + + - ``code`` -- The associated code of this encoder. + """ + + def __init__(self, code): + r""" + 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: E = codes.encoders.LinearCodeParityCheckEncoder(C) + sage: E + Parity check matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + """ + super(LinearCodeParityCheckEncoder, self).__init__(code) + + def _repr_(self): + r""" + Return a string representation 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: E = codes.encoders.LinearCodeParityCheckEncoder(C) + sage: E + Parity check matrix-based encoder for the Linear code of length 7, dimension 4 over Finite Field of size 2 + """ + return "Parity check matrix-based encoder for the %s" % self.code() + + def _latex_(self): + r""" + Return a latex representation 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: E = codes.encoders.LinearCodeParityCheckEncoder(C) + sage: latex(E) + \textnormal{Parity check matrix-based encoder for the }[7, 4]\textnormal{ Linear code over }\Bold{F}_{2} + """ + return "\\textnormal{Parity check matrix-based encoder for the }%s" % self.code()._latex_() + + @cached_method + def generator_matrix(self): + r""" + Returns a generator matrix of the associated code 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: E = codes.encoders.LinearCodeParityCheckEncoder(C) + sage: E.generator_matrix() + [1 0 0 0 0 1 1] + [0 1 0 0 1 0 1] + [0 0 1 0 1 1 0] + [0 0 0 1 1 1 1] + """ + return self.code().parity_check_matrix().right_kernel_matrix() + ####################### decoders ############################### class LinearCodeSyndromeDecoder(Decoder): r""" @@ -4496,16 +4564,15 @@ def _latex_(self): def decode_to_code(self, r): r""" - Decode the received word ``r`` to the nearest 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:: @@ -4514,18 +4581,17 @@ def decode_to_code(self, r): sage: D = codes.decoders.LinearCodeNearestNeighborDecoder(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) + sage: D.decode_to_code(w_err) (1, 1, 0, 0, 1, 1, 0) """ - V = self.input_space() - if not isinstance(r, list): - r = r.list() - r = V(r) - diffs = [[c - r, (c - r).hamming_weight()] for c in self.code()] - diffs.sort(key=lambda x: x[1]) - c = diffs[0][0] + r - c.set_immutable() - return c + c_min = self.code().zero() + h_min = r.hamming_weight() + for c in self.code(): + if (c-r).hamming_weight() < h_min: + h_min = (c-r).hamming_weight() + c_min = c + c_min.set_immutable() + return c_min def decoding_radius(self): r""" diff --git a/src/sage/coding/two_weight_db.py b/src/sage/coding/two_weight_db.py index 07f3f5516f5..be13efe245a 100644 --- a/src/sage/coding/two_weight_db.py +++ b/src/sage/coding/two_weight_db.py @@ -7,7 +7,7 @@ REFERENCE: -.. [BS03] I. Bouyukliev and J. Simonis, +.. [BS03] \I. Bouyukliev and J. Simonis, Some new results on optimal codes over `F_5`, Designs, Codes and Cryptography 30, no. 1 (2003): 97-111, http://www.moi.math.bas.bg/moiuser/~iliya/pdf_site/gf5srev.pdf, @@ -16,12 +16,12 @@ Online database of two-weight codes, http://moodle.tec.hkr.se/~chen/research/2-weight-codes/search.php -.. [Kohnert07] A. Kohnert, +.. [Kohnert07] \A. Kohnert, Constructing two-weight codes with prescribed groups of automorphisms, Discrete applied mathematics 155, no. 11 (2007): 1451-1457. http://linearcodes.uni-bayreuth.de/twoweight/ -.. [Disset00] L. Dissett, +.. [Disset00] \L. Dissett, Combinatorial and computational aspects of finite geometries, 2000, https://tspace.library.utoronto.ca/bitstream/1807/14575/1/NQ49844.pdf diff --git a/src/sage/combinat/abstract_tree.py b/src/sage/combinat/abstract_tree.py index 5d41c17716b..1fa6143f03d 100644 --- a/src/sage/combinat/abstract_tree.py +++ b/src/sage/combinat/abstract_tree.py @@ -62,6 +62,8 @@ - Florent Hivert (2010-2011): initial revision - Frédéric Chapoton (2011): contributed some methods """ +# python3 +from __future__ import division from sage.structure.list_clone import ClonableArray from sage.rings.integer import Integer @@ -979,7 +981,7 @@ def _ascii_art_(self): if len(l_repr) == 0: lf_sep += "_"*(t_repr._root+1) else: lf_sep += "_"*(t_repr._l+1) ls_sep += " "*(t_repr._root) + "/" + " "*(t_repr._l-t_repr._root) - mid = whitesep + int((len(lf_sep)-whitesep)/2) + mid = whitesep + (len(lf_sep) - whitesep) // 2 node = node_to_str( self ) t_repr = AsciiArt([lf_sep[:mid-1] + node + lf_sep[mid+len(node)-1:], ls_sep]) * acc t_repr._root = mid diff --git a/src/sage/combinat/affine_permutation.py b/src/sage/combinat/affine_permutation.py index 61dd29a2226..be468c08368 100644 --- a/src/sage/combinat/affine_permutation.py +++ b/src/sage/combinat/affine_permutation.py @@ -1772,7 +1772,7 @@ def AffinePermutationGroup(cartan_type): REFERENCES: .. [BjBr] Bjorner and Brenti. Combinatorics of Coxeter Groups. Springer, 2005. - .. [Erik] H. Erikson. Computational and Combinatorial Aspects of Coxeter + .. [Erik] \H. Erikson. Computational and Combinatorial Aspects of Coxeter Groups. Thesis, 1995. EXAMPLES:: diff --git a/src/sage/combinat/algebraic_combinatorics.py b/src/sage/combinat/algebraic_combinatorics.py index 7dae1b0847f..d241a8b7bd5 100644 --- a/src/sage/combinat/algebraic_combinatorics.py +++ b/src/sage/combinat/algebraic_combinatorics.py @@ -34,8 +34,7 @@ Groups and Algebras ------------------- -.. TODO:: add link to the catalog of algebras when it exists - +- :ref:`Catalog of algebras ` - :ref:`Groups ` - :class:`SymmetricGroup`, :class:`CoxeterGroup`, :class:`WeylGroup` - :class:`~sage.combinat.diagram_algebras.PartitionAlgebra` diff --git a/src/sage/combinat/alternating_sign_matrix.py b/src/sage/combinat/alternating_sign_matrix.py index 040b97d6ce7..a6e288d26a5 100644 --- a/src/sage/combinat/alternating_sign_matrix.py +++ b/src/sage/combinat/alternating_sign_matrix.py @@ -27,6 +27,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division import itertools import copy @@ -50,9 +52,9 @@ from sage.combinat.non_decreasing_parking_function import NonDecreasingParkingFunction from sage.combinat.permutation import Permutation from sage.combinat.six_vertex_model import SquareIceModel - from sage.misc.decorators import rename_keyword + class AlternatingSignMatrix(Element): r""" An alternating sign matrix. @@ -65,7 +67,7 @@ class AlternatingSignMatrix(Element): REFERENCES: - .. [MiRoRu] W. H. Mills, David P Robbins, Howard Rumsey Jr., + .. [MiRoRu] \W. H. Mills, David P Robbins, Howard Rumsey Jr., *Alternating sign matrices and descending plane partitions*, Journal of Combinatorial Theory, Series A, Volume 34, Issue 3, May 1983, Pages 340--359. @@ -669,7 +671,7 @@ def ASM_compatible(self, B): REFERENCES: - .. [EKLP92] N. Elkies, G. Kuperberg, M. Larsen, J. Propp, + .. [EKLP92] \N. Elkies, G. Kuperberg, M. Larsen, J. Propp, *Alternating-Sign Matrices and Domino Tilings*, Journal of Algebraic Combinatorics, volume 1 (1992), p. 111-132. @@ -980,10 +982,10 @@ def left_key(self): REFERENCES: - .. [Aval07] J.-C. Aval. *Keys and alternating sign matrices*. + .. [Aval07] \J.-C. Aval. *Keys and alternating sign matrices*. Sem. Lothar. Combin. 59 (2007/10), Art. B59f, 13 pp. - .. [LascouxPreprint] A. Lascoux. *Chern and Yang through ice*. + .. [LascouxPreprint] \A. Lascoux. *Chern and Yang through ice*. Preprint. EXAMPLES:: @@ -1273,7 +1275,7 @@ def from_height_function(self,height): [ 1 -1 1] [ 0 1 0] """ - return self.from_corner_sum(matrix( [[((i+j-height[i][j])/int(2)) + return self.from_corner_sum(matrix( [[((i+j-height[i][j])//2) for i in range(len(list(height)))] for j in range(len(list(height)))] )) diff --git a/src/sage/combinat/backtrack.py b/src/sage/combinat/backtrack.py index 49195772a85..0d55330bfbe 100644 --- a/src/sage/combinat/backtrack.py +++ b/src/sage/combinat/backtrack.py @@ -23,32 +23,28 @@ - ``SearchForest(seeds, succ)`` keeps the same behavior as before :trac:`6637` and is now the same as ``RecursivelyEnumeratedSet(seeds, - succ, structure='forest', enumeration='depth')``. + succ, structure='forest', enumeration='depth')``. - ``TransitiveIdeal(succ, seeds)`` keeps the same behavior as before :trac:`6637` and is now the same as ``RecursivelyEnumeratedSet(seeds, - succ, structure=None, enumeration='naive')``. + succ, structure=None, enumeration='naive')``. - ``TransitiveIdealGraded(succ, seeds, max_depth)`` keeps the same behavior as before :trac:`6637` and is now the same as ``RecursivelyEnumeratedSet(seeds, succ, structure=None, - enumeration='breadth', max_depth=max_depth)``. + enumeration='breadth', max_depth=max_depth)``. -TODO: +.. todo:: -- For now the code of ``SearchForest`` is still in - ``sage/combinat/backtrack.py``. It should be moved in - ``sage/sets/recursively_enumerated_set.pyx`` into a class named - ``RecursivelyEnumeratedSet_forest`` in a later ticket. + - For now the code of :class:`SearchForest` is still in + ``sage/combinat/backtrack.py``. It should be moved in + ``sage/sets/recursively_enumerated_set.pyx`` into a class named + :class:`RecursivelyEnumeratedSet_forest` in a later ticket. -- ``TransitiveIdeal`` and ``TransitiveIdealGraded`` are used in the code of - ``categories/finitely_generated_semigroups.py`` (at least). - These should be updated to use ``RecursivelyEnumeratedSet`` in a later - ticket for speed improvements and ``TransitiveIdeal`` and - ``TransitiveIdealGraded`` may be deprecated. + - Deprecate ``TransitiveIdeal`` and ``TransitiveIdealGraded``. -- Once the deprecation has been there for enough time: delete - ``TransitiveIdeal`` and ``TransitiveIdealGraded``. + - Once the deprecation has been there for enough time: delete + ``TransitiveIdeal`` and ``TransitiveIdealGraded``. """ #***************************************************************************** @@ -425,6 +421,8 @@ def __init__(self, roots = None, children = None, post_process = None, self._algorithm = algorithm Parent.__init__(self, facade = facade, category = EnumeratedSets().or_subcategory(category)) + __len__ = None + def _repr_(self): r""" TESTS:: @@ -689,6 +687,58 @@ def __contains__(self, elt): stack.append( iter(self.children(node)) ) return False + def map_reduce(self, map_function = None, + reduce_function = None, + reduce_init = None): + r""" + Apply a Map/Reduce algorithm on ``self`` + + INPUT: + + - ``map_function`` -- a function from the element of ``self`` to some + set with a reduce operation (e.g.: a monoid). The default value is + the constant function ``1``. + + - ``reduce_function`` -- the reduce function (e.g.: the addition of a + monoid). The default value is ``+``. + + - ``reduce_init`` -- the initialisation of the reduction (e.g.: the + neutral element of the monoid). The default value is ``0``. + + .. note:: + + the effect of the default values is to compute the cardinality + of ``self``. + + EXAMPLES:: + + sage: seeds = [([i],i, i) for i in range(1,10)] + sage: succ = (lambda (list, sum, last): + ....: [(list + [i], sum + i, i) for i in range(1,last)]) + sage: F = RecursivelyEnumeratedSet(seeds, succ, + ....: structure='forest', enumeration='depth') + + sage: y = var('y') + sage: map_function = lambda (li, sum, _): y**sum + sage: reduce_function = lambda x,y: x + y + sage: F.map_reduce(map_function, reduce_function, 0) + y^45 + y^44 + y^43 + 2*y^42 + 2*y^41 + 3*y^40 + 4*y^39 + 5*y^38 + 6*y^37 + 8*y^36 + 9*y^35 + 10*y^34 + 12*y^33 + 13*y^32 + 15*y^31 + 17*y^30 + 18*y^29 + 19*y^28 + 21*y^27 + 21*y^26 + 22*y^25 + 23*y^24 + 23*y^23 + 23*y^22 + 23*y^21 + 22*y^20 + 21*y^19 + 21*y^18 + 19*y^17 + 18*y^16 + 17*y^15 + 15*y^14 + 13*y^13 + 12*y^12 + 10*y^11 + 9*y^10 + 8*y^9 + 6*y^8 + 5*y^7 + 4*y^6 + 3*y^5 + 2*y^4 + 2*y^3 + y^2 + y + + Here is an example with the default values:: + + sage: F.map_reduce() + 511 + + .. SEEALSO:: :mod:`sage.parallel.map_reduce` + """ + import sage.parallel.map_reduce + return sage.parallel.map_reduce.RESetMapReduce( + forest = self, + map_function = map_function, + reduce_function = reduce_function, + reduce_init = reduce_init).run() + + class PositiveIntegerSemigroup(UniqueRepresentation, SearchForest): r""" The commutative additive semigroup of positive integers. diff --git a/src/sage/combinat/baxter_permutations.py b/src/sage/combinat/baxter_permutations.py index 96c927e90de..1b05eaa57e3 100644 --- a/src/sage/combinat/baxter_permutations.py +++ b/src/sage/combinat/baxter_permutations.py @@ -186,7 +186,7 @@ def __iter__(self): REFERENCES: - .. [BBF08] N. Bonichon, M. Bousquet-Melou, E. Fusy. + .. [BBF08] \N. Bonichon, M. Bousquet-Melou, E. Fusy. Baxter permutations and plane bipolar orientations. Seminaire Lotharingien de combinatoire 61A, article B61Ah, 2008. """ diff --git a/src/sage/combinat/binary_tree.py b/src/sage/combinat/binary_tree.py index 8f966281cf9..91893ed3392 100644 --- a/src/sage/combinat/binary_tree.py +++ b/src/sage/combinat/binary_tree.py @@ -13,6 +13,7 @@ AUTHORS: - Florent Hivert (2010-2011): initial implementation. +- Adrien Boussicault (2015): Hook statistics. REFERENCES: @@ -38,6 +39,9 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division + from sage.structure.list_clone import ClonableArray from sage.combinat.abstract_tree import (AbstractClonableTree, AbstractLabelledClonableTree) @@ -421,7 +425,7 @@ def _ascii_art_( self ): lr_tree = self[0]._ascii_art_() rr_tree = self[1]._ascii_art_() nb_ = lr_tree._l - lr_tree._root + rr_tree._root - 1 - nb_L = int( nb_ / 2 ) + nb_L = nb_ // 2 nb_R = nb_L + ( 1 if nb_ % 2 == 1 else 0 ) f_line = " " ** Integer( lr_tree._root + 1 ) + "_" ** Integer( nb_L ) + node f_line += "_" ** Integer( nb_R ) @@ -1457,7 +1461,7 @@ def canopee(self): REFERENCES: - .. [DG94] S. Dulucq and O. Guibert. Mots de piles, tableaux + .. [DG94] \S. Dulucq and O. Guibert. Mots de piles, tableaux standards et permutations de Baxter, proceedings of Formal Power Series and Algebraic Combinatorics, 1994. """ @@ -2035,6 +2039,226 @@ def single_edge_cut_shapes(self): resu += [(L + 1, L + 2, R)] return resu + def comb(self, side='left'): + r""" + Return the comb of a tree. + + There are two combs in a binary tree: a left comb and a right comb. + + Consider all the vertices of the leftmost (resp. rightmost) branch of + the root. The left (resp. right) comb is the list of right (resp. left) + subtrees of each of these vertices. + + INPUT: + + - ``side`` -- (default: 'left') set to 'left' to obtain a left + comb, and to 'right' to obtain a right comb. + + OUTPUT: + + A list of binary trees. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: [BT.comb('left'), BT.comb('right')] + [[], []] + sage: BT = BinaryTree( '[.,.]' ) + sage: [BT.comb('left'), BT.comb('right')] + [[], []] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ) + sage: BT.comb('left') + [., .] + sage: BT.comb('right') + [.] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.comb('left') + [[[., .], [[[., .], [., .]], [., .]]], ., [., .]] + sage: ascii_art(BT.comb('left')) + [ __o___ , , o ] + [ / \ ] + [ o _o_ ] + [ / \ ] + [ o o ] + [ / \ ] + [ o o ] + sage: BT.comb('right') + [., [[., .], [[[., .], [., .]], .]]] + sage: ascii_art(BT.comb('right')) + [ , __o__ ] + [ / \ ] + [ o o ] + [ / ] + [ o ] + [ / \ ] + [ o o ] + """ + + def _comb(side): + if self.is_empty(): + return [] + tree = self[side] + res = [] + while not tree.is_empty(): + res.append(tree[1 - side]) + tree = tree[side] + return res + if side == 'left': + return _comb(0) + elif side == 'right': + return _comb(1) + + def hook_number(self): + r""" + Return the number of hooks. + + Recalling that a branch is a path from a vertex of the tree to a leaf, + the leftmost (resp. rightmost) branch of a vertex `v` is the branch from + `v` made only of left (resp. right) edges. + + The hook of a vertex `v` is a set of vertices formed by the + union of `{v}`, and the vertices of its leftmost and rightmost branches. + + There is a unique way to partition the set of vertices in hooks. + The number of hooks in such a partition is the hook number of the tree. + + We can obtain this partition recursively by extracting the root's hook + and iterating the processus on each tree of the remaining forest. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: BT.hook_number() + 0 + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o + sage: BT.hook_number() + 1 + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.hook_number() + 6 + """ + if self.is_empty(): + return 0 + return 1 + sum(t.hook_number() + for t in self.comb('left') + self.comb('right')) + + def twisting_number(self): + r""" + Return a pair (number of maximal left branches, number of maximal right + branches). + + Recalling that a branch of a vertex `v` is a path from a vertex of the + tree to a leaf, a left (resp. right) branch is a branch made only of + left (resp. right) edges. The length of a branch is the number of edges + composing it. A left (resp. right) branch is maximal if it is not + included in a strictly longer left (resp. right) branch. + + + OUTPUT : + + A list of two integers. + + EXAMPLES:: + + sage: BT = BinaryTree( '.' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[.,.]' ) + sage: BT.twisting_number() + [0, 0] + sage: BT = BinaryTree( '[[[.,.], .], [.,.]]' ); ascii_art(BT) + o + / \ + o o + / + o + sage: BT.twisting_number() + [1, 1] + sage: BT = BinaryTree( '[[[[., [., .]], .], [[., .], [[[., .], [., .]], [., .]]]], [., [[[., .], [[[., .], [., .]], .]], .]]]' ) + sage: ascii_art(BT) + ________o________ + / \ + __o__ o + / \ \ + o __o___ o + / / \ / + o o _o_ __o__ + \ / \ / \ + o o o o o + / \ / + o o o + / \ + o o + sage: BT.twisting_number() + [5, 6] + sage: BT = BinaryTree( '[.,[[[.,.],.],.]]' ); ascii_art(BT) + o + \ + o + / + o + / + o + sage: BT.twisting_number() + [1, 1] + """ + tn = [0, 0] + if self.node_number() <= 1: + return tn + + L = self.comb('left') + if len(L): + tn[0] += 1 + for h in L: + tw = BinaryTree([None, h]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] + + R = self.comb('right') + if len(R): + tn[1] += 1 + for l in R: + tw = BinaryTree([l, None]).twisting_number() + tn[0] += tw[0] + tn[1] += tw[1] + return tn + def q_hook_length_fraction(self, q=None, q_factor=False): r""" Compute the ``q``-hook length fraction of the binary tree ``self``, diff --git a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py index 873db468f00..66bc8604289 100644 --- a/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py +++ b/src/sage/combinat/cluster_algebra_quiver/cluster_seed.py @@ -391,7 +391,7 @@ def use_c_vectors(self, use=True, bot_is_c=False, force=False): self._BC = copy(self._M) if self._bot_is_c != bot_is_c: # If we need to do this. It overrides the previous designations. self._bot_is_c = bot_is_c - if self._bot_is_c == True: + if self._bot_is_c: self._use_c_vec = True if self._m == self._n: # in this case, the second half of a 2n x n matrix is a c-matrix. self._C = copy(self._M[self._n:(self._n+self._m),:self._n]) @@ -623,7 +623,7 @@ def use_fpolys(self, use=True, user_labels=None, user_labels_prefix=None): self._yhat = dict([ (self._U.gen(j),prod([self._R.gen(i)**self._M[i,j] for i in xrange(self._n+self._m)])) for j in xrange(self._n)]) elif self._cluster: raise ValueError("should not be possible to have cluster variables without f-polynomials") # added this as a sanity check. This error should never appear however. - elif self._track_mut == True: # If we can navigate from the root to where we are + elif self._track_mut: # If we can navigate from the root to where we are if not self._use_g_vec: self.use_g_vectors(True) catchup = ClusterSeed(self._b_initial, user_labels=user_labels, user_labels_prefix=user_labels_prefix) @@ -725,19 +725,17 @@ def _sanitize_init_vars(self, user_labels, user_labels_prefix = 'x'): EXAMPLES:: - sage: S = ClusterSeed(['A',4]); S._init_vars - {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} - sage: S._sanitize_init_vars([1,2,3,4],'z') - sage: S._init_vars - {0: 'z1', 1: 'z2', 2: 'z3', 3: 'z4'} - - sage: S = ClusterSeed(['A',4]); S._init_vars - {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} - sage: S._sanitize_init_vars(['a', 'b', 'c', 'd']) - sage: S._init_vars - {0: 'a', 1: 'b', 2: 'c', 3: 'd'} - - + sage: S = ClusterSeed(['A',4]); S._init_vars + {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} + sage: S._sanitize_init_vars([1,2,3,4],'z') + sage: S._init_vars + {0: 'z1', 1: 'z2', 2: 'z3', 3: 'z4'} + + sage: S = ClusterSeed(['A',4]); S._init_vars + {0: 'x0', 1: 'x1', 2: 'x2', 3: 'x3', 4: 'y0', 5: 'y1', 6: 'y2', 7: 'y3'} + sage: S._sanitize_init_vars(['a', 'b', 'c', 'd']) + sage: S._init_vars + {0: 'a', 1: 'b', 2: 'c', 3: 'd'} """ if isinstance(user_labels,list): self._init_vars = {} @@ -1508,7 +1506,9 @@ def _g_mutate(self, k): r""" An internal procedure that returns ``self`` with g-vectors mutated at k. - WARNING: This function assumes you are sending it good data + .. WARNING:: + + This function assumes you are sending it good data. EXAMPLES:: @@ -1520,8 +1520,8 @@ def _g_mutate(self, k): REFERENCES: .. [NaZe2011] Tomoki Nakanishi and Andrei Zelevinsky - *On Tropical Dualities In Cluster Algebras* - :arxiv:`1101.3736v3` + *On Tropical Dualities In Cluster Algebras* + :arxiv:`1101.3736v3` """ from sage.matrix.all import identity_matrix @@ -4188,10 +4188,11 @@ def is_LeeLiZel_allowable(T,n,m,b,c): nEA1 += 1 if nAF1 == b*nAF2 or nEA2 == c*nEA1: uv_okay = True - if uv_okay == False: + if not uv_okay: return False return True + def get_green_vertices(C): r""" Get the green vertices from a matrix. Will go through each clumn and return diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index 0d6876e1270..7cf737d1150 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -1139,8 +1139,8 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): else: long_cycle = [ cycle, ['A',n-1,1] ] # if we haven't found a "long_cycle", we are in finite type A - if long_cycle == False: - long_cycle = [ [], QuiverMutationType(['A',n]) ] + if not long_cycle: + long_cycle = [[], QuiverMutationType(['A', n])] # The 'connected vertices' are now computed. # Attention: 0-1-2 in type A_3 has connecting vertices 0 and 2, while in type D_3 it has connecting vertex 1; @@ -1227,14 +1227,47 @@ def load_data(n): containing all mutation equivalent quivers as dig6 data. We check - - if the data is stored by the user, and if this is not the case - - if the data is stored by the optional package install + + - if the data is stored by the user, and if this is not the case + - if the data is stored by the optional package install. EXAMPLES:: sage: from sage.combinat.cluster_algebra_quiver.mutation_type import load_data - sage: load_data(2) # random + sage: load_data(2) # random - depends on the data the user has stored + {('G', 2): [('AO', (((0, 1), (1, -3)),)), ('AO', (((0, 1), (3, -1)),))]} + + TESTS: + + We test data from the ``database_mutation_class`` optional package:: + + sage: def test_database(n): + ....: import os.path + ....: import cPickle + ....: from sage.env import SAGE_SHARE + ....: relative_filename = 'cluster_algebra_quiver/mutation_classes_%s.dig6'%n + ....: filename = os.path.join(SAGE_SHARE, relative_filename) + ....: f = open(filename,'r') + ....: data = cPickle.load(f) + ....: f.close() + ....: return data + sage: test_database(2) # optional - database_mutation_class {('G', 2): [('AO', (((0, 1), (1, -3)),)), ('AO', (((0, 1), (3, -1)),))]} + sage: sorted(test_database(3).items()) # optional - database_mutation_class + [(('G', 2, -1), + [('BH?', (((1, 2), (1, -3)),)), + ('BGO', (((2, 1), (3, -1)),)), + ('BW?', (((0, 1), (3, -1)),)), + ('BP?', (((0, 1), (1, -3)),)), + ('BP_', (((0, 1), (1, -3)), ((2, 0), (3, -1)))), + ('BP_', (((0, 1), (3, -1)), ((1, 2), (1, -3)), ((2, 0), (2, -2))))]), + (('G', 2, 1), + [('BH?', (((1, 2), (3, -1)),)), + ('BGO', (((2, 1), (1, -3)),)), + ('BW?', (((0, 1), (1, -3)),)), + ('BP?', (((0, 1), (3, -1)),)), + ('BKO', (((1, 0), (3, -1)), ((2, 1), (1, -3)))), + ('BP_', (((0, 1), (2, -2)), ((1, 2), (1, -3)), ((2, 0), (3, -1))))])] """ import os.path import cPickle diff --git a/src/sage/combinat/cluster_complex.py b/src/sage/combinat/cluster_complex.py index 448544219ca..014444560e6 100644 --- a/src/sage/combinat/cluster_complex.py +++ b/src/sage/combinat/cluster_complex.py @@ -163,7 +163,7 @@ class ClusterComplex(SubwordComplex): REFERENCES: - .. [CLS] C. Ceballos, J.-P. Labbe, C. Stump, *Subword complexes, + .. [CLS] \C. Ceballos, J.-P. Labbe, C. Stump, *Subword complexes, cluster complexes, and generalized multi-associahedra*, J. Algebr. Comb. **39** (2014) pp. 17-51. :doi:`10.1007/s10801-013-0437-x`, :arxiv:`1108.1776`. diff --git a/src/sage/combinat/colored_permutations.py b/src/sage/combinat/colored_permutations.py index 6afbe4c768b..c9109cf9245 100644 --- a/src/sage/combinat/colored_permutations.py +++ b/src/sage/combinat/colored_permutations.py @@ -8,9 +8,6 @@ """ import itertools -from sage.categories.groups import Groups -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups from sage.structure.element import MultiplicativeGroupElement from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -244,6 +241,35 @@ def to_matrix(self): D = diagonal_matrix(Cp, [g ** i for i in self._colors]) return self._perm.to_matrix() * D + def has_left_descent(self, i): + """ + Return ``True`` if ``i`` is a left descent of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(2, 4) + sage: s1,s2,s3,s4 = C.gens() + sage: x = s4*s1*s2*s3*s4 + sage: [x.has_left_descent(i) for i in C.index_set()] + [True, False, False, True] + + sage: C = ColoredPermutations(1, 5) + sage: s1,s2,s3,s4 = C.gens() + sage: x = s4*s1*s2*s3*s4 + sage: [x.has_left_descent(i) for i in C.index_set()] + [True, False, False, True] + """ + if self.parent()._m == 1: + return self._perm[i - 1] > self._perm[i] + + if self.parent()._m == 2: + if i == self.parent()._n: + return self._colors[-1] == 1 + if self._colors[i - 1] == 1: + return self._colors[i] == 0 or self._perm[i - 1] < self._perm[i] + return self._colors[i] == 0 and self._perm[i - 1] > self._perm[i] + + raise ValueError("descents are undefined") # TODO: Parts of this should be put in the category of complex # reflection groups @@ -301,7 +327,7 @@ class ColoredPermutations(Parent, UniqueRepresentation): - :wikipedia:`Generalized_symmetric_group` - :wikipedia:`Complex_reflection_group` """ - def __init__(self, m, n, category=None): + def __init__(self, m, n): """ Initialize ``self``. @@ -316,12 +342,17 @@ def __init__(self, m, n, category=None): """ if m <= 0: raise ValueError("m must be a positive integer") - self._m = m - self._n = n + self._m = ZZ(m) + self._n = ZZ(n) self._C = IntegerModRing(self._m) self._P = Permutations(self._n) - if category is None: - category = (Groups(), FiniteEnumeratedSets()) + + if self._m == 1 or self._m == 2: + from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups + category = FiniteCoxeterGroups().Irreducible() + else: + from sage.categories.generalized_coxeter_groups import GeneralizedCoxeterGroups + category = GeneralizedCoxeterGroups().Finite().Irreducible() Parent.__init__(self, category=category) def _repr_(self): @@ -335,6 +366,65 @@ def _repr_(self): """ return "{}-colored permutations of size {}".format(self._m, self._n) + @cached_method + def index_set(self): + """ + Return the index set of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(3, 4) + sage: C.index_set() + (1, 2, 3, 4) + + sage: C = ColoredPermutations(1, 4) + sage: C.index_set() + (1, 2, 3) + + TESTS:: + + sage: S = SignedPermutations(4) + sage: S.index_set() + (1, 2, 3, 4) + """ + n = self._n + if self._m != 1: + n += 1 + return tuple(range(1, n)) + + def coxeter_matrix(self): + """ + Return the Coxeter matrix of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(3, 4) + sage: C.coxeter_matrix() + [1 3 2 2] + [3 1 3 2] + [2 3 1 4] + [2 2 4 1] + + sage: C = ColoredPermutations(1, 4) + sage: C.coxeter_matrix() + [1 3 2] + [3 1 3] + [2 3 1] + + TESTS:: + + sage: S = SignedPermutations(4) + sage: S.coxeter_matrix() + [1 3 2 2] + [3 1 3 2] + [2 3 1 4] + [2 2 4 1] + """ + from sage.combinat.root_system.cartan_type import CartanType + if self._m == 1: + return CartanType(['A', self._n-1]).coxeter_matrix() + return CartanType(['B', self._n]).coxeter_matrix() + @cached_method def one(self): """ @@ -349,6 +439,37 @@ def one(self): return self.element_class(self, [self._C.zero()] * self._n, self._P.identity()) + def simple_reflection(self, i): + r""" + Return the ``i``-th simple reflection of ``self``. + + EXAMPLES:: + + sage: C = ColoredPermutations(4, 3) + sage: C.gens() + ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) + sage: C.simple_reflection(2) + [[0, 0, 0], [1, 3, 2]] + sage: C.simple_reflection(3) + [[0, 0, 1], [1, 2, 3]] + + sage: S = SignedPermutations(4) + sage: S.simple_reflection(1) + [2, 1, 3, 4] + sage: S.simple_reflection(4) + [1, 2, 3, -4] + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + colors = [self._C.zero()] * self._n + if i < self._n: + p = range(1, self._n + 1) + p[i - 1] = i + 1 + p[i] = i + return self.element_class(self, colors, self._P(p)) + colors[-1] = self._C.one() + return self.element_class(self, colors, self._P.identity()) + @cached_method def gens(self): """ @@ -361,17 +482,12 @@ def gens(self): ([[0, 0, 0], [2, 1, 3]], [[0, 0, 0], [1, 3, 2]], [[0, 0, 1], [1, 2, 3]]) + + sage: S = SignedPermutations(4) + sage: S.gens() + ([2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3], [1, 2, 3, -4]) """ - zero = [self._C.zero()] * self._n - g = [] - for i in range(self._n - 1): - p = range(1, self._n + 1) - p[i] = i + 2 - p[i + 1] = i + 1 - g.append(self.element_class(self, zero, self._P(p))) - zero[-1] = self._C.one() - g.append(self.element_class(self, zero, self._P.identity())) - return tuple(g) + return tuple(self.simple_reflection(i) for i in self.index_set()) def matrix_group(self): """ @@ -491,6 +607,8 @@ def cardinality(self): """ return self._m ** self._n * self._P.cardinality() + order = cardinality + def rank(self): """ Return the rank of ``self``. @@ -529,10 +647,10 @@ def degrees(self): sage: C = ColoredPermutations(4, 3) sage: C.degrees() - [4, 8, 12] + (4, 8, 12) sage: S = ColoredPermutations(1, 3) sage: S.degrees() - [2, 3] + (2, 3) We now check that the product of the degrees is equal to the cardinality of ``self``:: @@ -542,9 +660,9 @@ def degrees(self): sage: prod(S.degrees()) == S.cardinality() True """ - if self._m == 1: # Special case for the usual symmetric group - return range(2, self._n + 1) - return [self._m * i for i in range(1, self._n + 1)] + # For the usual symmetric group (self._m=1) we need to start at 2 + start = 2 if self._m == 1 else 1 + return tuple(self._m * i for i in range(start, self._n + 1)) def codegrees(self): r""" @@ -570,10 +688,10 @@ def codegrees(self): sage: C = ColoredPermutations(4, 3) sage: C.codegrees() - [8, 4, 0] + (8, 4, 0) sage: S = ColoredPermutations(1, 3) sage: S.codegrees() - [1, 0] + (1, 0) TESTS: @@ -587,9 +705,9 @@ def codegrees(self): sage: f == g True """ - if self._m == 1: # Special case for the usual symmetric group - return list(reversed(range(self._n - 1))) - return [self._m * i for i in reversed(range(self._n))] + # Special case for the usual symmetric group + last = self._n-1 if self._m == 1 else self._n + return tuple(self._m * i for i in reversed(range(last))) def number_of_reflection_hyperplanes(self): """ @@ -852,7 +970,7 @@ class SignedPermutations(ColoredPermutations): sage: S(C(S.an_element())) == S.an_element() True sage: S(C.an_element()) - [1, 2, 3] + [-3, 1, 2] There is also the natural lift from permutations:: @@ -875,7 +993,7 @@ def __init__(self, n): sage: S = SignedPermutations(4) sage: TestSuite(S).run() """ - ColoredPermutations.__init__(self, 2, n, FiniteCoxeterGroups()) + ColoredPermutations.__init__(self, 2, n) def _repr_(self): """ @@ -925,19 +1043,6 @@ def simple_reflection(self, i): temp[-1] = -ZZ.one() return self.element_class(self, temp, self._P.identity()) - @cached_method - def gens(self): - """ - Return the generators of ``self``. - - EXAMPLES:: - - sage: S = SignedPermutations(4) - sage: S.gens() - ([2, 1, 3, 4], [1, 3, 2, 4], [1, 2, 4, 3], [1, 2, 3, -4]) - """ - return tuple(self.simple_reflection(i) for i in self.index_set()) - def _element_constructor_(self, x): """ Construct an element of ``self`` from ``x``. @@ -1031,35 +1136,6 @@ def _coerce_map_from_(self, C): x._perm) return super(SignedPermutations, self)._coerce_map_from_(C) - @cached_method - def index_set(self): - """ - Return the index set of ``self``. - - EXAMPLES:: - - sage: S = SignedPermutations(4) - sage: S.index_set() - (1, 2, 3, 4) - """ - return tuple(range(1, self._n + 1)) - - def coxeter_matrix(self): - """ - Return the Coxeter matrix of ``self``. - - EXAMPLES:: - - sage: S = SignedPermutations(4) - sage: S.coxeter_matrix() - [1 3 2 2] - [3 1 3 2] - [2 3 1 4] - [2 2 4 1] - """ - from sage.combinat.root_system.cartan_type import CartanType - return CartanType(['B', self._n]).coxeter_matrix() - def long_element(self, index_set=None): """ Return the longest element of ``self``, or of the diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 403121dbe4e..9a342077220 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -1553,7 +1553,7 @@ def __list_from_iterator(self): def __iterator_from_next(self): """ - An iterator to use when .first() and .next() are provided. + An iterator to use when the .first() and .next(x) methods are provided. EXAMPLES:: @@ -1569,7 +1569,7 @@ def __iterator_from_next(self): while True: try: f = self.next(f) - except (TypeError, ValueError ): + except (TypeError, ValueError): break if f is None or f is False : @@ -1670,18 +1670,18 @@ def __iter__(self): ... NotImplementedError: iterator called but not implemented """ - #Check to see if .first() and .next() are overridden in the subclass + # Check whether .first() and .next(x) are overridden in the subclass if ( self.first != self.__first_from_iterator and self.next != self.__next_from_iterator ): return self.__iterator_from_next() - #Check to see if .last() and .previous() are overridden in the subclass + # Check whether .last() and .previous() are overridden in the subclass elif ( self.last != self.__last_from_iterator and self.previous != self.__previous_from_iterator): return self.__iterator_from_previous() - #Check to see if .unrank() is overridden in the subclass + # Check whether .unrank() is overridden in the subclass elif self.unrank != self.__unrank_from_iterator: return self.__iterator_from_unrank() - #Finally, check to see if .list() is overridden in the subclass + # Check whether .list() is overridden in the subclass elif self.list != self.__list_from_iterator: return self.__iterator_from_list() else: diff --git a/src/sage/combinat/combinat_cython.pyx b/src/sage/combinat/combinat_cython.pyx index 43c0aed3b62..f4c3ef65c1e 100644 --- a/src/sage/combinat/combinat_cython.pyx +++ b/src/sage/combinat/combinat_cython.pyx @@ -93,7 +93,7 @@ cdef mpz_stirling_s2(mpz_t s, unsigned long n, unsigned long k): mpz_init(t) mpz_init(u) max_bc = (k+1)//2 - bc = sage_malloc((max_bc+1) * sizeof(mpz_t)) + bc = sig_malloc((max_bc+1) * sizeof(mpz_t)) mpz_init_set_ui(bc[0], 1) for j in range(1, max_bc+1): mpz_init_set(bc[j], bc[j-1]) @@ -115,7 +115,7 @@ cdef mpz_stirling_s2(mpz_t s, unsigned long n, unsigned long k): mpz_mul_2exp(u, u, n) for j in range(max_bc+1): # careful: 0 ... max_bc mpz_clear(bc[j]) - sage_free(bc) + sig_free(bc) mpz_fac_ui(t, k) mpz_tdiv_q(s, s, t) mpz_clear(t) diff --git a/src/sage/combinat/combinatorial_map.py b/src/sage/combinat/combinatorial_map.py index be6fa1e0417..5f176ba9095 100644 --- a/src/sage/combinat/combinatorial_map.py +++ b/src/sage/combinat/combinatorial_map.py @@ -257,8 +257,8 @@ def __repr__(self): return "Combinatorial map: %s" %self.name() def _sage_src_lines_(self): - """ - Returns the source code location for the wrapped function. + r""" + Return the source code location for the wrapped function. EXAMPLES:: diff --git a/src/sage/combinat/core.py b/src/sage/combinat/core.py index 066c3749134..99b27c21cb1 100644 --- a/src/sage/combinat/core.py +++ b/src/sage/combinat/core.py @@ -149,7 +149,7 @@ def __hash__(self): return self._hash def _latex_(self): - """ + r""" Output the LaTeX representation of this core as a partition. See the ``_latex_`` method of :class:`Partition`. diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index 29bd7a5bb65..74f3e33e816 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -26,6 +26,7 @@ from sage.structure.element_wrapper import ElementWrapper from sage.structure.unique_representation import UniqueRepresentation from sage.categories.finite_crystals import FiniteCrystals +from sage.categories.classical_crystals import ClassicalCrystals from sage.graphs.all import DiGraph from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem @@ -217,7 +218,7 @@ class CrystalOfAlcovePaths(UniqueRepresentation, Parent): REFERENCES: - .. [LP2008] C. Lenart and A. Postnikov. *A combinatorial model for + .. [LP2008] \C. Lenart and A. Postnikov. *A combinatorial model for crystals of Kac-Moody algebras*. Trans. Amer. Math. Soc. 360 (2008), 4349-4381. """ @@ -256,7 +257,9 @@ def __classcall_private__(cls, starting_weight, cartan_type=None, #switch positional arguments cartan_type, starting_weight = CartanType(starting_weight), cartan_type - if highest_weight_crystal == False: + if highest_weight_crystal is False: + if not cartan_type.is_affine(): + raise ValueError("non-highest weight crystals only valid for affine types") cartan_type = cartan_type.classical() if cartan_type.is_affine(): @@ -296,6 +299,12 @@ def __init__(self, starting_weight, highest_weight_crystal): sage: C = crystals.AlcovePaths(['A',2,1],[1,0],False) sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") #long time + + Check that :trac:`20292` is fixed:: + + sage: A = crystals.AlcovePaths(['A',2], [1,0]) + sage: A.category() + Category of classical crystals """ ########################################################################## # NOTE: @@ -314,17 +323,18 @@ def __init__(self, starting_weight, highest_weight_crystal): self._cartan_type = cartan_type - if cartan_type.is_finite() and highest_weight_crystal == True: - Parent.__init__(self, category = FiniteCrystals() ) + if cartan_type.is_finite() and highest_weight_crystal: + Parent.__init__(self, category=ClassicalCrystals() ) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True - elif cartan_type.is_finite() and highest_weight_crystal == False: - Parent.__init__(self, category = FiniteCrystals() ) + elif cartan_type.is_finite() and not highest_weight_crystal: + Parent.__init__(self, category=FiniteCrystals() ) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True self._cartan_type = cartan_type.affine() else: - Parent.__init__(self, category = HighestWeightCrystals()) + assert highest_weight_crystal + Parent.__init__(self, category=HighestWeightCrystals()) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = False @@ -407,7 +417,7 @@ def vertices(self): s = W.simple_reflections() highest_weight_crystal = self._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' @@ -511,10 +521,14 @@ def weight_lattice_realization(self): sage: C = crystals.AlcovePaths("B3",[1,0,0]) sage: C.weight_lattice_realization() Ambient space of the Root system of type ['B', 3] + + sage: A = crystals.AlcovePaths(['A',2,1], [1,0], highest_weight_crystal=False) + sage: A.weight_lattice_realization() + Weight lattice of the Root system of type ['A', 2, 1] """ F = self.cartan_type().root_system() if self.cartan_type().is_affine(): - return F.weight_lattice(extended=True) + return F.weight_lattice(extended=self._highest_weight_crystal) if self.cartan_type().is_finite() and F.ambient_space() is not None: return F.ambient_space() return F.weight_lattice() @@ -608,7 +622,7 @@ def is_admissible(self): s = W.simple_reflections() highest_weight_crystal = self.parent()._highest_weight_crystal - if highest_weight_crystal == True: + if highest_weight_crystal: successors = 'bruhat_upper_covers' else: successors = 'quantum_bruhat_successors' @@ -670,9 +684,9 @@ def phi(self, i): sage: C = crystals.AlcovePaths(['A',2],[1,1]) sage: [c.phi(1) for c in C] - [1, 0, 2, 1, 0, 1, 0, 0] + [1, 0, 0, 1, 0, 2, 1, 0] sage: [c.phi(2) for c in C] - [1, 2, 0, 0, 1, 0, 1, 0] + [1, 2, 1, 0, 0, 0, 0, 1] """ highest_weight_crystal = self.parent()._highest_weight_crystal positions, gi = self._gi(i) @@ -694,9 +708,9 @@ def epsilon(self, i): sage: C = crystals.AlcovePaths(['A',2],[1,1]) sage: [c.epsilon(1) for c in C] - [0, 1, 0, 1, 0, 0, 2, 1] + [0, 1, 0, 0, 1, 0, 1, 2] sage: [c.epsilon(2) for c in C] - [0, 0, 1, 0, 1, 2, 0, 1] + [0, 0, 1, 2, 1, 1, 0, 0] """ #crude but functional j = 0 @@ -716,12 +730,12 @@ def weight(self): sage: C = crystals.AlcovePaths(['A',2],[2,0]) sage: for i in C: i.weight() + (2, 0, 0) + (1, 1, 0) (0, 2, 0) - (0, 0, 1) - (0, -2, 2) - (0, 1, -1) (0, -1, 0) - (0, 0, -2) + (-1, 0, 0) + (-2, -2, 0) sage: B = crystals.AlcovePaths(['A',2,1],[1,0,0]) sage: p = B.module_generators[0].f_string([0,1,2]) sage: p.weight() @@ -736,6 +750,13 @@ def weight(self): sage: phi = C1.crystal_morphism(C2.module_generators, scaling_factors={1:2, 2:2}) sage: [phi(x) for x in C1] [(), ((alpha[1], 0),), ((alpha[1], 0), (alpha[1] + alpha[2], 0))] + + Check that all weights are of level 0 in the KR crystal setting + (:trac:`20292`):: + + sage: A = crystals.AlcovePaths(['A',2,1], [1,0], highest_weight_crystal=False) + sage: all(x.weight().level() == 0 for x in A) + True """ root_space = self.parent().R.root_space() weight = -self.parent().weight @@ -744,9 +765,17 @@ def weight(self): weight = -i.height*root + weight.reflection(root) WLR = self.parent().weight_lattice_realization() - B = WLR.basis() - return WLR._from_dict({i: Integer(c) for i,c in -weight}, - remove_zeros=False) + if self.cartan_type().is_affine() and self.parent()._highest_weight_crystal: + # We assume that WLR is the (extended) weight lattice + wt = WLR._from_dict({i: Integer(c) for i,c in -weight}, + remove_zeros=False) + return wt + La = WLR.fundamental_weights() + wt = WLR.sum(Integer(c) * La[i] for i,c in -weight) + if self.cartan_type().is_affine(): + assert not self.parent()._highest_weight_crystal + wt -= La[0] * wt.level() + return wt #def __repr__(self): #return str(self.integer_sequence()) @@ -1694,14 +1723,14 @@ def compare_graphs(g1, g2, node1, node2): matched = False for o2 in g2.outgoing_edges( node2 ): if o2[2] == out_edge[2]: - if matched == True: + if matched: print "ERROR: Two edges with the same label for ", out_edge, " exist." return False matched = True result = compare_graphs(g1, g2, out_edge[1], o2[1]) - if result == False: + if not result: return False - if matched == False: + if not matched: print "ERROR: No matching edge for ", out_edge, "." return False return True diff --git a/src/sage/combinat/crystals/crystals.py b/src/sage/combinat/crystals/crystals.py index 6344394b974..03655a24f76 100644 --- a/src/sage/combinat/crystals/crystals.py +++ b/src/sage/combinat/crystals/crystals.py @@ -67,7 +67,7 @@ REFERENCES: -.. [St2003] J. Stembridge, *A local characterization of simply-laced crystals*, +.. [St2003] \J. Stembridge, *A local characterization of simply-laced crystals*, Trans. Amer. Math. Soc. 355 (2003), no. 12, 4807-4823. EXAMPLES: diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index f4be84941c9..ae62f25080b 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -48,11 +48,11 @@ REFERENCES: -.. [Kashiwara93] M. Kashiwara. +.. [Kashiwara93] \M. Kashiwara. The Crystal Base and Littelmann's Refined Demazure Character Formula. Duke Math. J. **71** (3), pp. 839--858, 1993. -.. [NZ97] T. Nakashima and A. Zelevinsky. +.. [NZ97] \T. Nakashima and A. Zelevinsky. Polyhedral Realizations of Crystal Bases for Quantized Kac-Moody Algebras. Adv. Math. **131**, pp. 253--278, 1997. """ diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index 59b9d1ab814..a67f051b690 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -15,12 +15,12 @@ REFERENCES: -.. [KS10] J.-A. Kim and D.-U. Shin. +.. [KS10] \J.-A. Kim and D.-U. Shin. Generalized Young walls and crystal bases for quantum affine algebra of type `A`. Proc. Amer. Math. Soc. 138(11), pp. 3877--3889, 2010. -.. [KLRS] S.-J. Kang, K.-H. Lee, H. Ryu, and B. Salisbury. +.. [KLRS] \S.-J. Kang, K.-H. Lee, H. Ryu, and B. Salisbury. A combinatorial description of the affine Gindikin-Karpelevich formula of type `A_n^{(1)}`. :arXiv:`1203.1640`. diff --git a/src/sage/combinat/crystals/induced_structure.py b/src/sage/combinat/crystals/induced_structure.py index ba05ede86ad..ec01e07b651 100644 --- a/src/sage/combinat/crystals/induced_structure.py +++ b/src/sage/combinat/crystals/induced_structure.py @@ -206,7 +206,7 @@ def _element_constructor_(self, x): sage: I([[1,1,0,0],[1,0,0],[0,1],[1]]) Traceback (most recent call last): ... - ValueError: unable to convert [[1, 1, 0, 0], [1, 0, 0], [0, 1], [1]] + TypeError: unable to convert [[1, 1, 0, 0], [1, 0, 0], [0, 1], [1]] to Crystal of Gelfand-Tsetlin patterns of width 4 and max value 1 induced by """ if x in self._set: return self.element_class(self, self._set(x)) @@ -214,7 +214,7 @@ def _element_constructor_(self, x): try: return self.element_class(self, self._inverse(x)) except (TypeError, ValueError, AttributeError): - raise ValueError("unable to convert {}".format(x)) + raise TypeError("unable to convert {!r} to {}".format(x, self)) def __contains__(self, x): """ @@ -511,7 +511,7 @@ def _element_constructor_(self, x): try: return self.element_class(self, self._phi(self._inverse(x))) except (TypeError, ValueError, AttributeError): - raise ValueError("unable to convert {}".format(x)) + raise TypeError("unable to convert {!r} to {}".format(x, self)) def __contains__(self, x): """ diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index ab9d9ac316b..48360942d4e 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -124,19 +124,19 @@ class InfinityCrystalOfTableaux(CrystalOfWords): REFERENCES: - .. [BN10] D. Bump and M. Nakasuji. + .. [BN10] \D. Bump and M. Nakasuji. Integration on `p`-adic groups and crystal bases. Proc. Amer. Math. Soc. 138(5), pp. 1595--1605. - .. [LS12] K.-H. Lee and B. Salisbury. + .. [LS12] \K.-H. Lee and B. Salisbury. Young tableaux, canonical bases, and the Gindikin-Karpelevich formula. :arXiv:`1205.6006`. - .. [HL08] J. Hong and H. Lee. + .. [HL08] \J. Hong and H. Lee. Young tableaux and crystal `B(\infty)` for finite simple Lie algebras. J. Algebra 320, pp. 3680--3693, 2008. - .. [KM94] S.-J. Kang and K. C. Misra. + .. [KM94] \S.-J. Kang and K. C. Misra. Crystal bases and tensor product decompositions of `U_q(G_2)`-modules. J. Algebra 163, pp. 675--691, 1994. diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 6936fa304f9..51528acf63d 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -312,27 +312,27 @@ def KirillovReshetikhinCrystal(cartan_type, r, s, model='KN'): REFERENCES: - .. [Shimozono02] M. Shimozono + .. [Shimozono02] \M. Shimozono *Affine type A crystal structure on tensor products of rectangles, Demazure characters, and nilpotent varieties*, J. Algebraic Combin. **15** (2002). no. 2. 151-187. :arxiv:`math.QA/9804039`. - .. [Schilling08] A. Schilling. "Combinatorial structure of + .. [Schilling08] \A. Schilling. "Combinatorial structure of Kirillov-Reshetikhin crystals of type `D_n(1)`, `B_n(1)`, `A_{2n-1}(2)`". J. Algebra. **319** (2008). 2938-2962. :arxiv:`0704.2046`. - .. [JS2010] B. Jones, A. Schilling. + .. [JS2010] \B. Jones, A. Schilling. "Affine structures and a tableau model for `E_6` crystals", J. Algebra. **324** (2010). 2512-2542. :doi:`10.1016/j.bbr.2011.03.031`, :arxiv:`0909.2442`. - .. [FOS09] G. Fourier, M. Okado, A. Schilling. + .. [FOS09] \G. Fourier, M. Okado, A. Schilling. *Kirillov-Reshetikhin crystals for nonexceptional types*. Advances in Mathematics. **222** (2009). Issue 3. 1080-1116. :arxiv:`0810.5067`. - .. [LOS12] C. Lecouvey, M. Okado, M. Shimozono. + .. [LOS12] \C. Lecouvey, M. Okado, M. Shimozono. "Affine crystals, one-dimensional sums and parabolic Lusztig `q`-analogues". Mathematische Zeitschrift. **271** (2012). Issue 3-4. 819-865. :doi:`10.1007/s00209-011-0892-9`, :arxiv:`1002.3715`. @@ -576,7 +576,7 @@ def is_perfect(self): REFERENCES: - .. [FOS2010] G. Fourier, M. Okado, A. Schilling. + .. [FOS2010] \G. Fourier, M. Okado, A. Schilling. Perfectness of Kirillov-Reshetikhin crystals for nonexceptional types Contemp. Math. 506 (2010) 127-143 ( arXiv:0811.1604 [math.RT] ) @@ -3250,7 +3250,7 @@ class KR_type_D_tri1(KirillovReshetikhinGenericCrystal): REFERENCES: - .. [KMOY07] M. Kashiwara, K. C. Misra, M. Okado, D. Yamada. + .. [KMOY07] \M. Kashiwara, K. C. Misra, M. Okado, D. Yamada. *Perfect crystals for* `U_q(D_4^{(3)})`, J. Algebra. **317** (2007). """ def __init__(self, ct, s): diff --git a/src/sage/combinat/crystals/letters.pyx b/src/sage/combinat/crystals/letters.pyx index d2f294dabc8..fdcbe2cccfc 100644 --- a/src/sage/combinat/crystals/letters.pyx +++ b/src/sage/combinat/crystals/letters.pyx @@ -48,7 +48,7 @@ def CrystalOfLetters(cartan_type, element_print_style=None, dual=None): REFERENCES: - .. [KN94] M. Kashiwara and T. Nakashima. + .. [KN94] \M. Kashiwara and T. Nakashima. Crystal graphs for representations of the `q`-analogue of classical Lie algebras. J. Algebra **165**, no. 2, pp. 295--345, 1994. @@ -534,6 +534,34 @@ cdef class EmptyLetter(Element): """ return hash(self.value) + cpdef _richcmp_(left, Element right, int op): + """ + Return ``True`` if ``left`` compares with ``right`` based on ``op``. + + EXAMPLES:: + + sage: C = crystals.Letters(['C', 3]) + sage: C('E') == C(2) + False + sage: C('E') < C(2) + False + sage: C('E') <= C(2) + False + sage: C('E') != C(2) + True + sage: C('E') == C('E') + True + sage: C('E') != C('E') + False + sage: C('E') >= C('E') + True + sage: C('E') < C('E') + False + """ + if isinstance(left, EmptyLetter) and isinstance(right, EmptyLetter): + return op == Py_EQ or op == Py_LE or op == Py_GE + return op == Py_NE + def weight(self): """ Return the weight of ``self``. diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 0797003bb5f..3de0691b322 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -116,7 +116,7 @@ class CrystalOfLSPaths(UniqueRepresentation, Parent): REFERENCES: - .. [Littelmann95] P. Littelmann, Paths and root operators in representation + .. [Littelmann95] \P. Littelmann, Paths and root operators in representation theory. Ann. of Math. (2) 142 (1995), no. 3, 499-525. """ @@ -972,7 +972,7 @@ def energy_function(self): REFERENCES: - .. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, + .. [LNSSS2013] \C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, A uniform model for Kirillov-Reshetikhin crystals. Extended abstract. DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} ) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 0cfaf8ba261..80f1957ae1f 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -33,7 +33,7 @@ where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple coroots and fundamental weights, respectively. With a chosen set of integers -`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines +`C = (c_{ij})_{i\neq j}` such that `c_{ij}+c_{ji} =1`, one defines .. MATH:: @@ -76,11 +76,11 @@ REFERENCES: -.. [KKS07] S.-J. Kang, J.-A. Kim, and D.-U. Shin. +.. [KKS07] \S.-J. Kang, J.-A. Kim, and D.-U. Shin. Modified Nakajima Monomials and the Crystal `B(\infty)`. J. Algebra **308**, pp. 524--535, 2007. -.. [Kash03] M. Kashiwara. +.. [Kash03] \M. Kashiwara. Realizations of Crystals. Combinatorial and geometric representation theory (Seoul, 2001), Contemp. Math. **325**, Amer. Math. Soc., pp. 133--139, 2003. @@ -108,12 +108,17 @@ from sage.combinat.root_system.root_system import RootSystem from sage.rings.integer import Integer from sage.rings.infinity import Infinity +from sage.rings.integer_ring import ZZ +from sage.matrix.matrix import is_Matrix +from sage.matrix.matrix_space import MatrixSpace -class NakajimaYMonomial(Element): +class NakajimaMonomial(Element): r""" - Monomials of the form `Y_{i_1,k_1}^{a_1}\cdots Y_{i_t,k_t}^{y_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `y_1,\dots,y_t` are integers. + An element of the monomial crystal. + + Monomials of the form `Y_{i_1,k_1}^{y_1} \cdots Y_{i_t,k_t}^{y_t}`, + where `i_1, \dots, i_t` are elements of the index set, `k_1, \dots, k_t` + are nonnegative integers, and `y_1, \dots, y_t` are integers. EXAMPLES:: @@ -122,14 +127,26 @@ class NakajimaYMonomial(Element): sage: mg 1 sage: mg.f_string([1,3,2,0,1,2,3,0,0,1]) - Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + Y(0,0)^-1 Y(0,1)^-1 Y(0,2)^-1 Y(0,3)^-1 Y(1,0)^-3 + Y(1,1)^-2 Y(1,2) Y(2,0)^3 Y(2,2) Y(3,0) Y(3,2)^-1 + + An example using the `A` variables:: + + sage: M = crystals.infinity.NakajimaMonomials("A3") + sage: M.set_variables('A') + sage: mg = M.module_generators[0] + sage: mg.f_string([1,2,3,2,1]) + A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 + sage: mg.f_string([3,2,1]) + A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 + sage: M.set_variables('Y') """ - def __init__(self,parent,dict): + def __init__(self, parent, Y, A): r""" INPUT: - - ``dict`` -- a dictionary of with pairs of the form ``{(i,k):y}`` + - ``d`` -- a dictionary of with pairs of the form ``{(i,k): y}`` EXAMPLES:: @@ -137,30 +154,74 @@ def __init__(self,parent,dict): sage: mg = M.module_generators[0] sage: TestSuite(mg).run() """ - self._dict = dict + self._Y = Y + self._A = A Element.__init__(self, parent) def _repr_(self): r""" Return a string representation of ``self``. + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) + sage: x = M({(1,0):1, (2,2):-2, (0,5):10}); x + Y(0,5)^10 Y(1,0) Y(2,2)^-2 + sage: M.set_variables('A') + sage: x + A(1,0)^-2 A(1,1)^-2 A(2,0)^-4 A(2,1)^-2 A(3,0)^-2 + sage: M.set_variables('Y') + """ + return getattr(self, '_repr_' + self.parent()._variable)() + + def _repr_Y(self): + r""" + Return a string representation of ``self`` in the `Y` variables. + EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['A',5,2]) sage: M({(1,0):1,(2,2):-2,(0,5):10}) Y(0,5)^10 Y(1,0) Y(2,2)^-2 """ - if self._dict == {}: + if not self._Y: return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "Y(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + return ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + + def _repr_A(self): + r""" + Return a string representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['B',4,1]) + sage: m = M.module_generators[0].f_string([4,2,1]) + sage: m._repr_A() + 'A(1,1)^-1 A(2,0)^-1 A(4,0)^-1' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "1" + + L = sorted(Y.iteritems(), key=lambda x: (x[0][0], x[0][1])) + exp = lambda e: "^{}".format(e) if e != 1 else "" + ret = ' '.join("Y({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) + if not self._A: + return ret + if Y: + ret += ' ' + L = sorted(self._A.iteritems(), key=lambda x: (x[0][0], x[0][1])) + return ret + ' '.join("A({},{})".format(mon[0][0], mon[0][1]) + exp(mon[1]) + for mon in L) def __hash__(self): r""" @@ -172,9 +233,9 @@ def __hash__(self): 4715601665014767730 # 64-bit -512614286 # 32-bit """ - return hash(frozenset(tuple(self._dict.iteritems()))) + return hash(frozenset(tuple(self._Y.iteritems()))) - def __eq__(self,other): + def __eq__(self, other): r""" EXAMPLES:: @@ -186,11 +247,11 @@ def __eq__(self,other): sage: m1.__eq__(m1) True """ - if isinstance(other, NakajimaYMonomial): - return self._dict == other._dict - return self._dict == other + if isinstance(other, NakajimaMonomial): + return self._Y == other._Y + return self._Y == other - def __ne__(self,other): + def __ne__(self, other): r""" EXAMPLES:: @@ -205,7 +266,7 @@ def __ne__(self,other): """ return not self == other - def __lt__(self,other): + def __lt__(self, other): r""" EXAMPLES:: @@ -226,20 +287,71 @@ def _latex_(self): EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) - sage: M.module_generators[0].f_string([1,0,2])._latex_() + sage: x = M.module_generators[0].f_string([1,0,2]) + sage: latex(x) + Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} + sage: M.set_variables('A') + sage: latex(x) + A_{0,0}^{-1} A_{1,0}^{-1} A_{2,0}^{-1} + sage: M.set_variables('Y') + """ + return getattr(self, '_latex_' + self.parent()._variable)() + + def _latex_Y(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `Y` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['G',2,1]) + sage: M.module_generators[0].f_string([1,0,2])._latex_Y() 'Y_{0,0}^{-1} Y_{1,0}^{-1} Y_{1,1}^{2} Y_{2,0} Y_{2,1}^{-1} ' """ - if self._dict == {}: + if not self._Y: return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "Y_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "Y_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str + + L = sorted(self._Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + return return_str + + def _latex_A(self): + r""" + Return a `\LaTeX` representation of ``self`` in the `A` variables. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: m._latex_A() + 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' + """ + try: + Y = {(i,0): c for i,c in self.parent().hw} + except: + Y = {} + + if not Y and not self._A: + return "\\boldsymbol{1}" + + L = sorted(Y.iteritems(), key=lambda x:(x[0][0],x[0][1])) + return_str = '' + for x in L: + if x[1] != 1: + return_str += "Y_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "Y_{%s,%s} "%(x[0][0],x[0][1]) + L = sorted(self._A.iteritems(), key=lambda x:(x[0][0],x[0][1])) + for x in L: + if x[1] != 1: + return_str += "A_{%s,%s}"%(x[0][0],x[0][1]) + "^{%s} "%x[1] + else: + return_str += "A_{%s,%s} "%(x[0][0],x[0][1]) + return return_str def _classical_weight(self): r""" @@ -260,7 +372,7 @@ def _classical_weight(self): """ P = self.parent().weight_lattice_realization() La = P.fundamental_weights() - return P(sum(v*La[k[0]] for k,v in self._dict.iteritems())) + return P(sum(v*La[k[0]] for k,v in self._Y.iteritems())) def weight_in_root_lattice(self): r""" @@ -278,11 +390,15 @@ def weight_in_root_lattice(self): sage: m = mg.f_string([1,3,2,0,1,2,3,0,0,1]) sage: m.weight_in_root_lattice() -3*alpha[0] - 3*alpha[1] - 2*alpha[2] - 2*alpha[3] + + sage: M = crystals.infinity.NakajimaMonomials(['C',3,1]) + sage: m = M.module_generators[0].f_string([3,0,1,2,0]) + sage: m.weight_in_root_lattice() + -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] """ Q = RootSystem(self.parent().cartan_type()).root_lattice() - alpha = Q.simple_roots() - path = self.to_highest_weight() - return Q(sum(-alpha[j] for j in path[1])) + al = Q.simple_roots() + return Q.sum(e*al[k[0]] for k,e in self._A.iteritems()) def weight(self): r""" @@ -291,14 +407,19 @@ def weight(self): EXAMPLES:: sage: C = crystals.infinity.NakajimaMonomials(['A',1,1]) - sage: v=C.highest_weight_vector() - sage: v.f(1).weight()+v.f(0).weight() + sage: v = C.highest_weight_vector() + sage: v.f(1).weight() + v.f(0).weight() -delta + + sage: M = crystals.infinity.NakajimaMonomials(['A',4,2]) + sage: m = M.highest_weight_vector().f_string([1,2,0,1]) + sage: m.weight() + 2*Lambda[0] - Lambda[1] - delta """ P = self.parent().weight_lattice_realization() return P(self.weight_in_root_lattice()) - def epsilon(self,i): + def epsilon(self, i): r""" Return the value of `\varepsilon_i` on ``self``. @@ -312,13 +433,18 @@ def epsilon(self,i): sage: m = M.module_generators[0].f(2) sage: [m.epsilon(i) for i in M.index_set()] [0, 0, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.epsilon(i) for i in M.index_set()] + [0, 0, 0, 1, 0] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") h = self.parent().weight_lattice_realization().simple_coroots() return self.phi(i) - self._classical_weight().scalar(h[i]) - def phi(self,i): + def phi(self, i): r""" Return the value of `\varphi_i` on ``self``. @@ -332,28 +458,28 @@ def phi(self,i): sage: m = M.module_generators[0].f(1) sage: [m.phi(i) for i in M.index_set()] [1, -1, 1] + + sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) + sage: m = M.module_generators[0].f_string([4,2,3]) + sage: [m.phi(i) for i in M.index_set()] + [0, 1, -1, 2, -1] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - dict = self._dict - if dict == {}: - return 0 - else: - L = [x[0] for x in dict.keys()] - if i not in L: - return 0 + if not self._Y or all(x[0] != i for x in self._Y): + return ZZ.zero() + + d = copy(self._Y) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1)) - def _ke(self,i): + def _ke(self, i): r""" Return the value `k_e` with respect to ``i`` and ``self``. @@ -368,31 +494,29 @@ def _ke(self,i): sage: [m._ke(i) for i in M.index_set()] [+Infinity, 0, +Infinity] """ - dict = self._dict - sum = 0 - L = [] + h = self.parent().weight_lattice_realization().simple_coroots() phi = self.phi(i) - if self.epsilon(i) == 0: + if phi == self._classical_weight().scalar(h[i]): # self.epsilon(i) == 0 return Infinity - else: - d = copy(dict) - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - for var,exp in S: - sum += exp - if sum == phi: - L.append(var[1]) - if L == []: - return 0 + + d = copy(self._Y) + K = max(x[1] for x in d if x[0] == i) + for a in range(K): + if (i,a) in d: + continue else: - return max(L) + d[(i,a)] = 0 + total = ZZ.zero() + L = [] + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + for var,exp in S: + total += exp + if total == phi: + L.append(var[1]) + + return max(L) if L else ZZ.zero() - def _kf(self,i): + def _kf(self, i): r""" Return the value `k_f` with respect to ``i`` and ``self``. @@ -407,26 +531,25 @@ def _kf(self,i): sage: [m._kf(i) for i in M.index_set()] [0, 0, 2, 0, 0] """ - d = copy(self._dict) - I = [x[0] for x in d] - if i not in I: - return 0 - else: - K = max(x[1] for x in list(d) if x[0] ==i) - for a in range(K): - if (i,a) in d: - continue - else: - d[(i,a)] = 0 - S = sorted((x for x in d.iteritems() if x[0][0]==i), key=lambda x: x[0][1]) - sum = 0 - phi = self.phi(i) - for var,exp in S: - sum += exp - if sum == phi: - return var[1] - - def e(self,i): + if all(i != x[0] for x in self._Y): + return ZZ.zero() + + d = copy(self._Y) + K = max(key[1] for key in d if key[0] == i) + for a in range(K): + if (i,a) in d: + continue + else: + d[(i,a)] = 0 + S = sorted((x for x in d.iteritems() if x[0][0] == i), key=lambda x: x[0][1]) + sum = 0 + phi = self.phi(i) + for var,exp in S: + sum += exp + if sum == phi: + return var[1] + + def e(self, i): r""" Return the action of `e_i` on ``self``. @@ -442,7 +565,7 @@ def e(self,i): [None, None, None, - Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0) , + Y(0,0)^-1 Y(1,1)^-1 Y(2,1) Y(3,0) Y(3,1) Y(4,0)^-1 Y(4,1)^-1 Y(5,0), None, None, None, @@ -451,46 +574,58 @@ def e(self,i): sage: M = crystals.infinity.NakajimaMonomials("C5") sage: m = M.module_generators[0].f_string([1,3]) sage: [m.e(i) for i in M.index_set()] - [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0) , + [Y(2,1) Y(3,0)^-1 Y(3,1)^-1 Y(4,0), None, - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) , + Y(1,0)^-1 Y(1,1)^-1 Y(2,0), None, None] + + sage: M = crystals.infinity.NakajimaMonomials(['D',4,1]) + sage: M.set_variables('A') + sage: m = M.module_generators[0].f_string([4,2,3,0]) + sage: [m.e(i) for i in M.index_set()] + [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1, + None, + None, + A(0,2)^-1 A(2,1)^-1 A(4,0)^-1, + None] + sage: M.set_variables('Y') """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") if self.epsilon(i) == 0: return None - newdict = copy(self._dict) + newdict = copy(self._Y) ke = self._ke(i) - Aik = {(i, ke):1, (i, ke+1):1} + Aik = {(i, ke): 1, (i, ke+1): 1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if self.parent().cartan_type().is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, ke+c)] = cm[j-shift][i-shift] + c = self.parent()._c[j_index,i-shift] + if cm[j_index,i-shift] != 0: + Aik[(j, ke+c)] = cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) + A = copy(self._A) + A[(i,ke)] = A.get((i,ke),0) + 1 + if not A[(i,ke)]: + del A[(i,ke)] + return self.__class__(self.parent(), newdict, A) - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -503,309 +638,86 @@ def f(self,i): sage: M = crystals.infinity.NakajimaMonomials("B4") sage: m = M.module_generators[0].f_string([1,3,4]) sage: [m.f(i) for i in M.index_set()] - [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1 , - Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2 ] + [Y(1,0)^-2 Y(1,1)^-2 Y(2,0)^2 Y(2,1) Y(3,0)^-1 Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(1,2) Y(2,0) Y(2,2)^-1 Y(3,0)^-1 Y(3,1) Y(4,0) Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1)^2 Y(3,0)^-2 Y(3,1)^-1 Y(4,0)^3 Y(4,1)^-1, + Y(1,0)^-1 Y(1,1)^-1 Y(2,0) Y(2,1) Y(3,0)^-1 Y(3,1) Y(4,1)^-2] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") - newdict = copy(self._dict) + newdict = copy(self._Y) kf = self._kf(i) - Aik = {(i, kf):-1, (i, kf+1):-1} + Aik = {(i, kf): -1, (i, kf+1): -1} ct = self.parent().cartan_type() cm = ct.cartan_matrix() shift = 0 if ct.is_finite(): shift = 1 - for j in self.parent().index_set(): + for j_index,j in enumerate(self.parent().index_set()): if i == j: continue - c = 0 - if i > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(i-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][i-shift] != 0: - Aik[(j, kf+c)] = -cm[j-shift][i-shift] + c = self.parent()._c[j_index,i-shift] + if cm[j_index,i-shift] != 0: + Aik[(j, kf+c)] = -cm[j_index,i-shift] + # Multiply by Aik for key,value in Aik.iteritems(): if key in newdict: - newdict[key] +=value + if newdict[key] == -value: # The result would be a 0 exponent + del newdict[key] + else: + newdict[key] += value else: newdict[key] = value - for k in list(newdict): - if newdict[k] == 0: - newdict.pop(k) - return self.__class__(self.parent(),newdict) - -class NakajimaAMonomial(NakajimaYMonomial): - r""" - Monomials of the form `A_{i_1,k_1}^{a_1}\cdots A_{i_t,k_t}^{a_t}`, where - `i_1,\dots,i_t` are elements of the index set, `k_1,\dots,k_t` are - nonnegative integers, and `a_1,\dots,a_t` are integers. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("A3",use_Y=False) - sage: mg = M.module_generators[0] - sage: mg.f_string([1,2,3,2,1]) - A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 A(3,0)^-1 - sage: mg.f_string([3,2,1]) - A(1,2)^-1 A(2,1)^-1 A(3,0)^-1 - """ - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['B',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,1]) - sage: m - A(1,1)^-1 A(2,0)^-1 A(4,0)^-1 - """ - if self._dict == {}: - return "1" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] != 1: - return_str += "A(%s,%s)"%(L[x][0][0],L[x][0][1]) + "^%s "%L[x][1] - else: - return_str += "A(%s,%s) "%(L[x][0][0],L[x][0][1]) - return return_str - - def _latex_(self): - r""" - Return a `\LaTeX` representation of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: m._latex_() - 'A_{2,0}^{-1} A_{3,1}^{-1} A_{4,0}^{-1} ' - """ - if self._dict == {}: - return "\\boldsymbol{1}" - else: - L = sorted(self._dict.iteritems(), key=lambda x:(x[0][0],x[0][1])) - return_str = '' - for x in range(len(L)): - if L[x][1] !=1: - return_str += "A_{%s,%s}"%(L[x][0][0],L[x][0][1]) + "^{%s} "%L[x][1] - else: - return_str += "A_{%s,%s} "%(L[x][0][0],L[x][0][1]) - return return_str - - def to_Y_monomial(self): - r""" - Represent `\prod_{(i,k)} A_{i,k}^{a_{i}(k)}` in the form - `\prod_{(i,k)} Y_{i,k}^{y_i(k)}` using the formula - - .. MATH:: - - A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{\substack{j \in I \\ j\neq i}} - Y_{i,k+c_{ji}}^{a_{ji}}. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',2,1],use_Y=False) - sage: m = M.module_generators[0].f_string([2,0,1,2,1]) - sage: m - A(0,0)^-1 A(1,0)^-1 A(1,1)^-1 A(2,0)^-1 A(2,1)^-1 - sage: m.to_Y_monomial() - Y(0,1) Y(0,2) Y(1,1)^-1 Y(2,2)^-1 - """ - Y = {} - d = self._dict - ct = self.parent().cartan_type() - cm = ct.cartan_matrix() - for k,v in d.iteritems(): - Y[k] = Y.get(k,0) + v - Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v - shift = 0 - if ct.is_finite(): - shift = 1 - for j in self.parent().index_set(): - if k[0] == j: - continue - c = 0 - if k[0] > j: - c = 1 - if ct.is_affine() and ct.type() == 'A' and abs(k[0]-j) == ct.rank() - 1: - c = 1 - c - if cm[j-shift][k[0]-shift] != 0: - Y[(j, k[1]+c)] = Y.get((j,k[1]+c),0) + v*cm[j-shift][k[0]-shift] - for k in Y.keys(): - if Y[k] == 0: - Y.pop(k) - return NakajimaYMonomial(self.parent(), Y) - - def weight(self): - r""" - Return the weight of ``self`` as an element of - ``self.parent().weight_lattice_realization()``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',4,2],use_Y=False) - sage: m = M.module_generators[0].f_string([1,2,0,1]) - sage: m.weight() - 2*Lambda[0] - Lambda[1] - delta - """ - return self.to_Y_monomial().weight() - - def weight_in_root_lattice(self): - r""" - Return the weight of ``self`` as an element of the root lattice. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',3,1],use_Y=False) - sage: m = M.module_generators[0].f_string([3,0,1,2,0]) - sage: m.weight_in_root_lattice() - -2*alpha[0] - alpha[1] - alpha[2] - alpha[3] - """ - return self.to_Y_monomial().weight_in_root_lattice() - - def epsilon(self,i): - r""" - Return the action of `\varepsilon_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.epsilon(i) for i in M.index_set()] - [0, 0, 0, 1, 0] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().epsilon(i) - - def phi(self,i): - r""" - Return the action of `\varphi_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['C',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3]) - sage: [m.phi(i) for i in M.index_set()] - [0, 1, -1, 2, -1] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - return self.to_Y_monomial().phi(i) - - def e(self,i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['D',4,1],use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,0]) - sage: [m.e(i) for i in M.index_set()] - [A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 , - None, - None, - A(0,2)^-1 A(2,1)^-1 A(4,0)^-1 , - None] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - if self.epsilon(i) == 0: - return None - ke = self.to_Y_monomial()._ke(i) - d = copy(self._dict) - d[(i,ke)] = d.get((i,ke),0)+1 - for k in list(d): - if d[k] == 0: - d.pop(k) - return self.__class__(self.parent(), d) - - def f(self,i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- an element of the index set - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials("E8",use_Y=False) - sage: m = M.module_generators[0].f_string([4,2,3,8]) - sage: m - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 - sage: [m.f(i) for i in M.index_set()] - [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1 , - A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2 ] - """ - if i not in self.parent().index_set(): - raise ValueError("i must be an element of the index set") - kf = self.to_Y_monomial()._kf(i) - d = copy(self._dict) - d[(i,kf)] = d.get((i,kf),0) - 1 - return self.__class__(self.parent(), d) + A = copy(self._A) + A[(i,kf)] = A.get((i,kf),0) - 1 + if not A[(i,kf)]: + del A[(i,kf)] + return self.__class__(self.parent(), newdict, A) class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): r""" + Crystal `B(\infty)` in terms of (modified) Nakajima monomials. + Let `Y_{i,k}`, for `i \in I` and `k \in \ZZ`, be a commuting set of - variables, and let `\boldsymbol{1}` be a new variable which commutes with - each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan datum.) One - may endow the structure of a crystal on the set `\widehat{\mathcal{M}}` of - monomials of the form + variables, and let `\boldsymbol{1}` be a new variable which commutes + with each `Y_{i,k}`. (Here, `I` represents the index set of a Cartan + datum.) One may endow the structure of a crystal on the + set `\widehat{\mathcal{M}}` of monomials of the form .. MATH:: M = \prod_{(i,k) \in I\times \ZZ_{\ge0}} Y_{i,k}^{y_i(k)}\boldsymbol{1}. - Elements of `\widehat{\mathcal{M}}` are called *modified Nakajima monomials*. - We will omit the `\boldsymbol{1}` from the end of a monomial if there exists - at least one `y_i(k) \neq 0`. The crystal structure on this set is defined by + Elements of `\widehat{\mathcal{M}}` are called + *modified Nakajima monomials*. We will omit the `\boldsymbol{1}` + from the end of a monomial if there exists at least one `y_i(k) \neq 0`. + The crystal structure on this set is defined by .. MATH:: \begin{aligned} - \mathrm{wt}(M) &= \sum_{i\in I} \Bigl( \sum_{k\ge 0} y_i(k) \Bigr) \Lambda_i, \\ - \varphi_i(M) &= \max\Bigl\{ \sum_{0\le j \le k} y_i(j) : k\ge 0 \Bigr\}, \\ - \varepsilon_i(M) &= \varphi_i(M) - \langle h_i, \mathrm{wt}(M) \rangle, \\ - k_f = k_f(M) &= \min\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, \\ - k_e = k_e(M) &= \max\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, + \mathrm{wt}(M) & = \sum_{i\in I} \Bigl( \sum_{k \ge 0} + y_i(k) \Bigr) \Lambda_i, \\ + \varphi_i(M) & = \max\Bigl\{ \sum_{0 \le j \le k} y_i(j) : + k \ge 0 \Bigr\}, \\ + \varepsilon_i(M) & = \varphi_i(M) - + \langle h_i, \mathrm{wt}(M) \rangle, \\ + k_f = k_f(M) & = \min\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \\ + k_e = k_e(M) & = \max\Bigl\{ k \ge 0 : + \varphi_i(M) = \sum_{0 \le j \le k} y_i(j) \Bigr\}, \end{aligned} where `\{h_i : i \in I\}` and `\{\Lambda_i : i \in I \}` are the simple - coroots and fundamental weights, respectively. With a chosen set of integers - `C = (c_{ij})_{i\neq j}` such that `c_{ij}+c{ji} =1`, one defines + coroots and fundamental weights, respectively. With a chosen set of + non-negative integers `C = (c_{ij})_{i\neq j}` such that + `c_{ij} + c_{ji} = 1`, one defines .. MATH:: A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{j\neq i} Y_{j,k+c_{ji}}^{a_{ji}}, - where `(a_{ij})` is a Cartan matrix. Then + where `(a_{ij})_{i,j \in I}` is a Cartan matrix. Then .. MATH:: @@ -816,15 +728,18 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): \end{aligned} It is shown in [KKS07]_ that the connected component of - `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, which we - denote by `\mathcal{M}(\infty)`, is crystal isomorphic to the crystal - `B(\infty)`. + `\widehat{\mathcal{M}}` containing the element `\boldsymbol{1}`, + which we denote by `\mathcal{M}(\infty)`, is crystal isomorphic + to the crystal `B(\infty)`. INPUT: - ``cartan_type`` -- a Cartan type - - ``use_Y`` -- choice of monomials in terms of `A` or `Y` + - ``c`` -- (optional) the matrix `(c_{ij})_{i,j \in I}` such that + `c_{ii} = 0` for all `i \in I`, `c_{ij} \in \ZZ_{>0}` for all + `i,j \in I`, and `c_{ij} + c_{ji} = 1` for all `i \neq j`; the + default is `c_{ij} = 0` if `i < j` and `0` otherwise EXAMPLES:: @@ -855,9 +770,66 @@ class InfinityCrystalOfNakajimaMonomials(UniqueRepresentation, Parent): sage: BG.is_isomorphic(MG,edge_labels=True) # long time True """ + @staticmethod + def _normalize_c(c, n): + """ + Normalize the input ``c``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.monomial_crystals import InfinityCrystalOfNakajimaMonomials + sage: InfinityCrystalOfNakajimaMonomials._normalize_c(None, 4) + [0 1 1 1] + [0 0 1 1] + [0 0 0 1] + [0 0 0 0] + sage: c = matrix([[0,1,1],[0,0,0],[0,1,0]]); c + [0 1 1] + [0 0 0] + [0 1 0] + sage: c.is_mutable() + True + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 3); C + [0 1 1] + [0 0 0] + [0 1 0] + sage: C.is_mutable() + False + + TESTS:: + + sage: c = matrix([[0,1],[0,1]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have 0's on the diagonal + sage: c = matrix([[0,2],[-1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: the c matrix must have non-negative entries + sage: c = matrix([[0,1],[1,0]]) + sage: C = InfinityCrystalOfNakajimaMonomials._normalize_c(c, 2) + Traceback (most recent call last): + ... + ValueError: transpose entries do not sum to 1 + """ + if c is None: + # Default is i < j <=> c_{ij} = 1 (0 otherwise) + c = [[1 if i < j else 0 for j in range(n)] for i in range(n)] + MS = MatrixSpace(ZZ, n, n) + c = MS(c) + c.set_immutable() + if any(c[i,i] != 0 for i in range(n)): + raise ValueError("the c matrix must have 0's on the diagonal") + if any(c[i,j] + c[j,i] != 1 for i in range(n) for j in range(i)): + raise ValueError("transpose entries do not sum to 1") + if any(c[i,j] < 0 or c[j,i] < 0 for i in range(n) for j in range(i)): + raise ValueError("the c matrix must have non-negative entries") + return c @staticmethod - def __classcall_private__(cls, ct, category=None, use_Y=True): + def __classcall_private__(cls, ct, c=None, use_Y=None): r""" Normalize input to ensure a unique representation. @@ -873,17 +845,23 @@ def __classcall_private__(cls, ct, category=None, use_Y=True): sage: M is M1 is M2 True """ - if isinstance(use_Y, bool): - if use_Y: - elt_class = NakajimaYMonomial - else: - elt_class = NakajimaAMonomial + if use_Y is not None: + from sage.misc.superseded import deprecation + deprecation(18895, 'use_Y is deprecated; use the set_variables() method instead.') else: - elt_class = use_Y + use_Y = True + cartan_type = CartanType(ct) - return super(InfinityCrystalOfNakajimaMonomials,cls).__classcall__(cls,cartan_type,category,elt_class) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + M = super(InfinityCrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, c) + if not use_Y: + M.set_variables('A') + else: + M.set_variables('Y') + return M - def __init__(self, ct, category, elt_class): + def __init__(self, ct, c, category=None): r""" EXAMPLES:: @@ -891,20 +869,24 @@ def __init__(self, ct, category, elt_class): sage: TestSuite(Minf).run() # long time """ self._cartan_type = ct + self._c = c + self._variable = 'Y' - self.Element = elt_class if category is None: category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) Parent.__init__(self, category=category) - self.module_generators = (self.element_class(self,{}),) + self.module_generators = (self.element_class(self, {}, {}),) - def _element_constructor_(self,dict): + def _element_constructor_(self, Y=None, A=None): r""" - Construct an element of ``self`` from ``dict``. + Construct an element of ``self`` from ``Y``. INPUT: - - ``dict`` -- a dictionary whose key is a pair and whose value is an integer + - ``Y`` -- a dictionary whose key is a pair and whose value + is an integer + - ``A`` -- a dictionary whose key is a pair and whose value + is an integer EXAMPLES:: @@ -912,8 +894,46 @@ def _element_constructor_(self,dict): sage: m = M({(1,0):-1,(1,1):-1,(2,0):1}) sage: m Y(1,0)^-1 Y(1,1)^-1 Y(2,0) + + sage: M = crystals.infinity.NakajimaMonomials(['A',2,1]) + sage: m = M(A={(0,1): -1, (1,1): -2, (2,0): -1, (2,1): -1}) + sage: m._repr_A() + 'A(0,1)^-1 A(1,1)^-2 A(2,0)^-1 A(2,1)^-1' + sage: m + Y(0,2)^2 Y(1,2)^-1 Y(2,0)^-1 Y(2,1) Y(2,2)^-1 + sage: m == M.highest_weight_vector().f_string([2,0,1,2,1]) + True """ - return self.element_class(self,dict) + if A is None: + if Y is None: + return self.module_generators[0] + # This is a crude way to determine the A, but it works + hw,path = self.element_class(self, Y, {}).to_highest_weight() + hw._A = {} + return hw.f_string(reversed(path)) + elif Y is None or Y == 0: + # The Y == 0 check is because the parent's __call__ has that + # as the first default value + ct = self.cartan_type() + cm = ct.cartan_matrix() + I = self.index_set() + shift = 0 + if ct.is_finite(): + shift = 1 + Y = {} + for k,v in A.iteritems(): + Y[k] = Y.get(k, 0) + v + Y[(k[0],k[1]+1)] = Y.get((k[0],k[1]+1), 0) + v + for j_index,j in enumerate(I): + if k[0] == j: + continue + c = self._c[j_index,k[0]-shift] + if cm[j_index,k[0]-shift] != 0: + Y[(j,k[1]+c)] = Y.get((j,k[1]+c), 0) + v*cm[j_index,k[0]-shift] + for k in Y.keys(): + if Y[k] == 0: + del Y[k] + return self.element_class(self, Y, A) def _repr_(self): r""" @@ -926,6 +946,27 @@ def _repr_(self): """ return "Infinity Crystal of modified Nakajima monomials of type {}".format(self._cartan_type) + def c(self): + """ + Return the matrix `c_{ij}` of ``self``. + + EXAMPLES:: + + sage: La = RootSystem(['B',3]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: M.c() + [0 1 1] + [0 0 1] + [0 0 0] + + sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: M.c() == c + True + """ + return self._c + def cardinality(self): r""" Return the cardinality of ``self``, which is always `\infty`. @@ -962,13 +1003,70 @@ def weight_lattice_realization(self): return F.weight_lattice(extended=True) return F.weight_lattice() -class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): + def set_variables(self, letter): + r""" + Set the type of monomials to use for the element output. + + If the `A` variables are used, the output is written as + `\prod_{i\in I} Y_{i,0}^{\lambda_i} \prod_{i,k} A_{i,k}^{c_{i,k}}`, where + `\sum_{i \in I} \lambda_i \Lambda_i` is the corresponding + dominant weight. + + INPUT: + + - ``letter`` -- can be one of the following: + + * ``'Y'`` - use `Y_{i,k}`, corresponds to fundamental weights + * ``'A'`` - use `A_{i,k}`, corresponds to simple roots + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: elt = M.highest_weight_vector().f_string([2,1,3,2,3,2,4,3]) + sage: elt + Y(1,2) Y(2,0)^-1 Y(2,2)^-1 Y(3,0)^-1 Y(3,2)^-1 Y(4,0) + sage: M.set_variables('A') + sage: elt + A(1,1)^-1 A(2,0)^-1 A(2,1)^-2 A(3,0)^-2 A(3,1)^-1 A(4,0)^-1 + sage: M.set_variables('Y') + + :: + + sage: La = RootSystem(['A',2]).weight_lattice().fundamental_weights() + sage: M = crystals.NakajimaMonomials(La[1]+La[2]) + sage: lw = M.lowest_weight_vectors()[0] + sage: lw + Y(1,2)^-1 Y(2,1)^-1 + sage: M.set_variables('A') + sage: lw + Y(1,0) Y(2,0) A(1,0)^-1 A(1,1)^-1 A(2,0)^-2 + sage: M.set_variables('Y') + """ + if letter not in ['Y', 'A']: + raise ValueError("invalid monomial type") + self._variable = letter + + def get_variables(self): + """ + Return the type of monomials to use for the element output. + + EXAMPLES:: + + sage: M = crystals.infinity.NakajimaMonomials(['A', 4]) + sage: M.get_variables() + 'Y' + """ + return self._variable + + Element = NakajimaMonomial + +class CrystalOfNakajimaMonomialsElement(NakajimaMonomial): r""" Element class for :class:`~sage.combinat.crystals.monomial_crystals.CrystalOfNakajimaMonomials`. The `f_i` operators need to be modified from the version in - :class:`~sage.combinat.crystals.monomial_crystalsNakajimaYMonomial` + :class:`~sage.combinat.crystals.monomial_crystalsNakajimaMonomial` in order to create irreducible highest weight realizations. This modified `f_i` is defined as @@ -985,7 +1083,7 @@ class CrystalOfNakajimaMonomialsElement(NakajimaYMonomial): Y(0,0)^2 Y(0,1)^-1 Y(2,0) sage: TestSuite(m).run() """ - def f(self,i): + def f(self, i): r""" Return the action of `f_i` on ``self``. @@ -999,7 +1097,25 @@ def f(self,i): sage: M = crystals.NakajimaMonomials(['A',5,2],3*La[0]) sage: m = M.module_generators[0] sage: [m.f(i) for i in M.index_set()] - [Y(0,0)^2 Y(0,1)^-1 Y(2,0) , None, None, None] + [Y(0,0)^2 Y(0,1)^-1 Y(2,0), None, None, None] + + :: + + sage: M = crystals.infinity.NakajimaMonomials("E8") + sage: M.set_variables('A') + sage: m = M.module_generators[0].f_string([4,2,3,8]) + sage: m + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1 + sage: [m.f(i) for i in M.index_set()] + [A(1,2)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,0)^-1 A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,0)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(4,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(5,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(6,0)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(7,1)^-1 A(8,0)^-1, + A(2,1)^-1 A(3,1)^-1 A(4,0)^-1 A(8,0)^-2] + sage: M.set_variables('Y') """ if self.phi(i) == 0: return None @@ -1087,9 +1203,23 @@ class CrystalOfNakajimaMonomials(InfinityCrystalOfNakajimaMonomials): sage: GB = B.digraph(subset=SB) sage: GM.is_isomorphic(GB,edge_labels=True) True + + sage: c = matrix([[0,1,0],[0,0,1],[1,0,0]]) + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: M = crystals.NakajimaMonomials(2*La[1], c=c) + sage: list(M.subcrystal(max_depth=3)) + [Y(1,0)^2, + Y(0,1) Y(1,0) Y(1,1)^-1 Y(2,0), + Y(0,2)^-1 Y(1,0) Y(2,0) Y(2,2), + Y(0,1)^2 Y(1,1)^-2 Y(2,0)^2, + Y(0,0) Y(0,1) Y(1,0) Y(2,1)^-1, + Y(0,0) Y(0,2)^-1 Y(1,0) Y(1,1) Y(2,1)^-1 Y(2,2), + Y(0,1) Y(0,2)^-1 Y(1,1)^-1 Y(2,0)^2 Y(2,2), + Y(0,0) Y(0,1)^2 Y(1,1)^-1 Y(2,0) Y(2,1)^-1, + Y(1,0) Y(1,3) Y(2,0) Y(2,3)^-1] """ @staticmethod - def __classcall_private__(cls, cartan_type, La): + def __classcall_private__(cls, cartan_type, La=None, c=None): r""" Normalize input to ensure a unique representation. @@ -1102,14 +1232,19 @@ def __classcall_private__(cls, cartan_type, La): sage: M is M1 is M2 True """ + if La is None: + La = cartan_type + cartan_type = La.parent().cartan_type() cartan_type = CartanType(cartan_type) if cartan_type.is_affine(): La = RootSystem(cartan_type).weight_lattice(extended=True)(La) else: La = RootSystem(cartan_type).weight_lattice()(La) - return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La) + n = len(cartan_type.index_set()) + c = InfinityCrystalOfNakajimaMonomials._normalize_c(c, n) + return super(CrystalOfNakajimaMonomials, cls).__classcall__(cls, cartan_type, La, c) - def __init__(self, ct, La): + def __init__(self, ct, La, c): r""" EXAMPLES:: @@ -1125,14 +1260,11 @@ def __init__(self, ct, La): cat = ClassicalCrystals() else: cat = (RegularCrystals(), HighestWeightCrystals(), InfiniteEnumeratedSets()) - InfinityCrystalOfNakajimaMonomials.__init__( self, ct, cat, - CrystalOfNakajimaMonomialsElement ) + InfinityCrystalOfNakajimaMonomials.__init__(self, ct, c, cat) self._cartan_type = ct self.hw = La - gen = {} - for j in range(len(La.support())): - gen[(La.support()[j],0)] = La.coefficients()[j] - self.module_generators = (self.element_class(self,gen),) + gen = {(i,0): c for i,c in La} + self.module_generators = (self.element_class(self, gen, {}),) def _repr_(self): r""" @@ -1167,3 +1299,5 @@ def cardinality(self): return Infinity return super(InfinityCrystalOfNakajimaMonomials, self).cardinality() + Element = CrystalOfNakajimaMonomialsElement + diff --git a/src/sage/combinat/crystals/polyhedral_realization.py b/src/sage/combinat/crystals/polyhedral_realization.py index 03a863c2fe0..4707a0b7a1b 100644 --- a/src/sage/combinat/crystals/polyhedral_realization.py +++ b/src/sage/combinat/crystals/polyhedral_realization.py @@ -108,7 +108,7 @@ class InfinityCrystalAsPolyhedralRealization(TensorProductOfCrystals): REFERENCES: - .. [K93] M. Kashiwara. *The crystal base and Littelmann's refined Demazure + .. [K93] \M. Kashiwara. *The crystal base and Littelmann's refined Demazure character formula*. Duke Math. J. **71**. 1993. INPUT: diff --git a/src/sage/combinat/crystals/star_crystal.py b/src/sage/combinat/crystals/star_crystal.py index b29bd3531f4..29c36818ffe 100644 --- a/src/sage/combinat/crystals/star_crystal.py +++ b/src/sage/combinat/crystals/star_crystal.py @@ -89,7 +89,7 @@ class StarCrystal(UniqueRepresentation, Parent): REFERENCES: - .. [Kash95] M. Kashiwara. + .. [Kash95] \M. Kashiwara. The crystal base and Littelmann's refined Demazure character formula. Duke Math. J. **71** (1993), no. 3, 839-858. """ diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index f3d277bf71a..5f6db69927b 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -436,10 +436,9 @@ class TensorProductOfCrystals(CrystalOfWords): .. MATH:: - e_i(b \otimes b^{\prime}) = \begin{cases} - e_i(b) \otimes b^{\prime} & \text{if } \varepsilon_i(b) > - \varphi_i(b^{\prime}) \\ - b \otimes e_i(b^{\prime}) & \text{otherwise.} + e_i(b \otimes b') = \begin{cases} + e_i(b) \otimes b' & \text{if } \varepsilon_i(b) > + \varphi_i(b') \\ b \otimes e_i(b') & \text{otherwise.} \end{cases} We also define: @@ -447,11 +446,12 @@ class TensorProductOfCrystals(CrystalOfWords): .. MATH:: \begin{aligned} - \varphi_i(b \otimes b^{\prime}) & = \max\left( \varphi_i(b), - \varphi_i(b) + \varphi_i(b^{\prime}) - \varepsilon_i(b) \right) - \\ \varepsilon_i(b \otimes b^{\prime}) & = \max\left( - \varepsilon_i(b^{\prime}), \varepsilon_i(b^{\prime}) + - \varepsilon_i(b) - \varphi_i(b^{\prime}) \right). + \varphi_i(b \otimes b') & = \max\left( \varphi_i(b), + \varphi_i(b') + \langle \alpha_i^{\vee}, \mathrm{wt}(b) \rangle + \right), + \\ \varepsilon_i(b \otimes b') & = \max\left( \varepsilon_i(b'), + \varepsilon_i(b) - \langle \alpha_i^{\vee}, \mathrm{wt}(b') \rangle + \right). \end{aligned} .. NOTE:: @@ -954,9 +954,9 @@ def __lt__(self, other): if len(self) != len(other): return False for i in range(len(self)): - if (self[i] < other[i]) == True: + if (self[i] < other[i]): return True - if (other[i] < self[i]) == True: + if (other[i] < self[i]): return False return False @@ -1360,9 +1360,9 @@ def positions_of_unmatched_minus(self, i, dual=False, reverse=False): """ unmatched_plus = [] height = 0 - if reverse == True: + if reverse: self = self.reversed() - if dual == False: + if not dual: for j in range(len(self)): minus = self[j].phi(i) plus = self[j].epsilon(i) @@ -1416,7 +1416,7 @@ def energy_function(self): REFERENCES: - .. [SchillingTingley2011] A. Schilling, P. Tingley. + .. [SchillingTingley2011] \A. Schilling, P. Tingley. Demazure crystals, Kirillov-Reshetikhin crystals, and the energy function. Electronic Journal of Combinatorics. **19(2)**. 2012. :arXiv:`1104.2359` diff --git a/src/sage/combinat/degree_sequences.pyx b/src/sage/combinat/degree_sequences.pyx index c6638a2b6c7..6357e972721 100644 --- a/src/sage/combinat/degree_sequences.pyx +++ b/src/sage/combinat/degree_sequences.pyx @@ -265,7 +265,7 @@ Checking the consistency of enumeration and test:: from libc.string cimport memset from sage.rings.integer cimport Integer -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" @@ -383,7 +383,7 @@ class DegreeSequences: Freeing the memory """ if seq != NULL: - sage_free(seq) + sig_free(seq) cdef init(int n): """ @@ -399,7 +399,7 @@ cdef init(int n): return [[0]] sig_on() - seq = sage_malloc((n+1)*sizeof(unsigned char)) + seq = sig_malloc((n+1)*sizeof(unsigned char)) memset(seq,0,(n+1)*sizeof(unsigned char)) sig_off() @@ -409,7 +409,7 @@ cdef init(int n): N = n sequences = [] enum(1,0) - sage_free(seq) + sig_free(seq) return sequences cdef inline add_seq(): diff --git a/src/sage/combinat/descent_algebra.py b/src/sage/combinat/descent_algebra.py index 6030acd213e..6db98f16fc5 100644 --- a/src/sage/combinat/descent_algebra.py +++ b/src/sage/combinat/descent_algebra.py @@ -65,15 +65,15 @@ class DescentAlgebra(UniqueRepresentation, Parent): REFERENCES: - .. [GR1989] C. Reutenauer, A. M. Garsia. *A decomposition of Solomon's + .. [GR1989] \C. Reutenauer, A. M. Garsia. *A decomposition of Solomon's descent algebra.* Adv. Math. **77** (1989). http://www.lacim.uqam.ca/~christo/Publi%C3%A9s/1989/Decomposition%20Solomon.pdf - .. [Atkinson] M. D. Atkinson. *Solomon's descent algebra revisited.* + .. [Atkinson] \M. D. Atkinson. *Solomon's descent algebra revisited.* Bull. London Math. Soc. 24 (1992) 545-551. http://www.cs.otago.ac.nz/staffpriv/mike/Papers/Descent/DescAlgRevisited.pdf - .. [MR-Desc] C. Malvenuto, C. Reutenauer, *Duality between + .. [MR-Desc] \C. Malvenuto, C. Reutenauer, *Duality between quasi-symmetric functions and the Solomon descent algebra*, Journal of Algebra 177 (1995), no. 3, 967-982. http://www.lacim.uqam.ca/~christo/Publi%C3%A9s/1995/Duality.pdf diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 8e20cf2837a..e805ef21d00 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -1162,7 +1162,7 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): REFERENCE: - .. [Denniston69] R. H. F. Denniston, + .. [Denniston69] \R. H. F. Denniston, Some maximal arcs in finite projective planes. Journal of Combinatorial Theory 6, no. 3 (1969): 317-319. http://dx.doi.org/10.1016/S0021-9800(69)80095-5 diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 7373d1889f3..7aa351808eb 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -499,7 +499,7 @@ def HughesPlane(q2, check=True): while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() - sage: line = lambda p,q: (b for b in blocks if p in b and q in b).next() + sage: line = lambda p,q: next(b for b in blocks if p in b and q in b) sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) @@ -949,7 +949,7 @@ def Hadamard3Design(n): REFERENCES: - .. [CvL] P. Cameron, J. H. van Lint, Designs, graphs, codes and + .. [CvL] \P. Cameron, J. H. van Lint, Designs, graphs, codes and their links, London Math. Soc., 1991. """ if n == 1 or n == 4: diff --git a/src/sage/combinat/designs/covering_design.py b/src/sage/combinat/designs/covering_design.py index 8249b3ebe2f..624ad343f87 100644 --- a/src/sage/combinat/designs/covering_design.py +++ b/src/sage/combinat/designs/covering_design.py @@ -309,12 +309,11 @@ def is_covering(self): tset[tuple(y)] = True for i in Svt: - if tset[tuple(i)] == False: # uncovered + if not tset[tuple(i)]: # uncovered return False return True # everything was covered - def v(self): """ Return `v`, the number of points in the covering design. diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index e2bf95441d8..c22fa0bb791 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -178,7 +178,7 @@ def MOLS_14_4(): REFERENCE: - .. [Todorov12] D.T. Todorov, + .. [Todorov12] \D.T. Todorov, Four mutually orthogonal Latin squares of order 14, Journal of Combinatorial Designs 2012, vol.20 n.8 pp.363-367 """ @@ -4152,7 +4152,7 @@ def BIBD_45_9_8(from_code=False): REFERENCE: - .. [HT95] W. Huffman and V. Tonchev, + .. [HT95] \W. Huffman and V. Tonchev, The existence of extremal self-dual `[50, 25, 10]` codes and quasi-symmetric `2-(49, 9, 6)` designs, Designs, Codes and Cryptography @@ -4469,7 +4469,7 @@ def HigmanSimsDesign(): REFERENCE: - .. [KY04] S. Klee and L. Yates, + .. [KY04] \S. Klee and L. Yates, Tight Subdesigns of the Higman-Sims Design, Rose-Hulman Undergraduate Math. J 5.2 (2004). https://www.rose-hulman.edu/mathjournal/archives/2004/vol5-n2/paper9/v5n2-9pd.pdf diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index fec6d6d5ddb..f6b1f26aa96 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -7,7 +7,7 @@ Functions --------- """ include "sage/data_structures/bitset.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from libc.string cimport memset from sage.misc.unknown import Unknown @@ -105,7 +105,7 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O cdef int i,j,l # A copy of OA - cdef unsigned short * OAc = sage_malloc(k*n2*sizeof(unsigned short)) + cdef unsigned short * OAc = sig_malloc(k*n2*sizeof(unsigned short)) cdef unsigned short * C1 cdef unsigned short * C2 @@ -121,7 +121,7 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O if verbose: print {"OA" : "{} is not in the interval [0..{}]".format(x,n-1), "MOLS" : "Entry {} was expected to be in the interval [0..{}]".format(x,n-1)}[terminology] - sage_free(OAc) + sig_free(OAc) return False OAc[j*n2+i] = x @@ -138,14 +138,14 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O bitset_add(seen,n*C1[l]+C2[l]) if bitset_len(seen) != n2: # Have we seen all pairs ? - sage_free(OAc) + sig_free(OAc) bitset_free(seen) if verbose: print {"OA" : "Columns {} and {} are not orthogonal".format(i,j), "MOLS" : "Squares {} and {} are not orthogonal".format(i,j)}[terminology] return False - sage_free(OAc) + sig_free(OAc) bitset_free(seen) return True @@ -267,7 +267,7 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals print "{} does not belong to [0,...,{}]".format(x,n-1) return False - cdef unsigned short * matrix = sage_calloc(n*n,sizeof(unsigned short)) + cdef unsigned short * matrix = sig_calloc(n*n,sizeof(unsigned short)) if matrix is NULL: raise MemoryError @@ -298,7 +298,7 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals if not len(g) in G: if verbose: print "a group has size {} while G={}".format(len(g),list(G)) - sage_free(matrix) + sig_free(matrix) return False # Checks that two points of the same group were never covered @@ -311,7 +311,7 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals if matrix[ii*n+jj] != 0: if verbose: print "the pair ({},{}) belongs to a group but appears in some block".format(ii,jj) - sage_free(matrix) + sig_free(matrix) return False # We fill the entries with what is expected by the next loop @@ -324,10 +324,10 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals if matrix[i*n+j] != l: if verbose: print "the pair ({},{}) has been seen {} times but lambda={}".format(i,j,matrix[i*n+j],l) - sage_free(matrix) + sig_free(matrix) return False - sage_free(matrix) + sig_free(matrix) return True if not guess_groups else (True, groups) @@ -627,15 +627,15 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): cdef dict group_to_int = {v:i for i,v in enumerate(int_to_group)} # Allocations - cdef int ** x_minus_y = sage_malloc((n+1)*sizeof(int *)) - cdef int * x_minus_y_data = sage_malloc((n+1)*(n+1)*sizeof(int)) - cdef int * M_c = sage_malloc(k*M_nrows*sizeof(int)) - cdef int * G_seen = sage_malloc((n+1)*sizeof(int)) + cdef int ** x_minus_y = sig_malloc((n+1)*sizeof(int *)) + cdef int * x_minus_y_data = sig_malloc((n+1)*(n+1)*sizeof(int)) + cdef int * M_c = sig_malloc(k*M_nrows*sizeof(int)) + cdef int * G_seen = sig_malloc((n+1)*sizeof(int)) if (x_minus_y == NULL or x_minus_y_data == NULL or M_c == NULL or G_seen == NULL): - sage_free(x_minus_y) - sage_free(x_minus_y_data) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y) + sig_free(x_minus_y_data) + sig_free(G_seen) + sig_free(M_c) raise MemoryError # The "x-y" table. If g_i, g_j \in G, then x_minus_y[i][j] is equal to @@ -674,10 +674,10 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): if bit: if verbose: print "Row {} contains more than one empty entry".format(i) - sage_free(x_minus_y_data) - sage_free(x_minus_y) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y_data) + sig_free(x_minus_y) + sig_free(G_seen) + sig_free(M_c) return False bit = True @@ -691,10 +691,10 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): if verbose: print ("Column {} contains {} empty entries instead of the expected " "lambda.u={}.{}={}".format(j,ii,lmbda,u,lmbda*u)) - sage_free(x_minus_y_data) - sage_free(x_minus_y) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y_data) + sig_free(x_minus_y) + sig_free(G_seen) + sig_free(M_c) return False # We are now ready to test every pair of columns @@ -708,10 +708,10 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): if verbose: print ("Columns {} and {} generate 0 exactly {} times " "instead of the expected mu(={})".format(i,j,G_seen[0],mu)) - sage_free(x_minus_y_data) - sage_free(x_minus_y) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y_data) + sig_free(x_minus_y) + sig_free(G_seen) + sig_free(M_c) return False for ii in range(1,n): # bad number of g_ii\in G @@ -720,23 +720,23 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): print ("Columns {} and {} do not generate all elements of G " "exactly lambda(={}) times. The element {} appeared {} " "times as a difference.".format(i,j,lmbda,int_to_group[ii],G_seen[ii])) - sage_free(x_minus_y_data) - sage_free(x_minus_y) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y_data) + sig_free(x_minus_y) + sig_free(G_seen) + sig_free(M_c) return False - sage_free(x_minus_y_data) - sage_free(x_minus_y) - sage_free(G_seen) - sage_free(M_c) + sig_free(x_minus_y_data) + sig_free(x_minus_y) + sig_free(G_seen) + sig_free(M_c) return True # Cached information for OA constructions (see .pxd file for more info) -_OA_cache = sage_malloc(2*sizeof(cache_entry)) +_OA_cache = sig_malloc(2*sizeof(cache_entry)) if (_OA_cache == NULL): - sage_free(_OA_cache) + sig_free(_OA_cache) raise MemoryError _OA_cache[0].max_true = -1 _OA_cache[1].max_true = -1 @@ -756,9 +756,9 @@ cpdef _OA_cache_set(int k,int n,truth_value): cdef int i if _OA_cache_size <= n: new_cache_size = n+100 - _OA_cache = sage_realloc(_OA_cache,new_cache_size*sizeof(cache_entry)) + _OA_cache = sig_realloc(_OA_cache,new_cache_size*sizeof(cache_entry)) if _OA_cache == NULL: - sage_free(_OA_cache) + sig_free(_OA_cache) raise MemoryError for i in range(_OA_cache_size,new_cache_size): diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 23cabe7d4da..b71e633d6d9 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -13,21 +13,21 @@ REFERENCES: -.. [BJL99-1] T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. I." +.. [BJL99-1] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. I." Second edition. Encyclopedia of Mathematics and its Applications, 69. Cambridge University Press, (1999). -.. [BLJ99-2] T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. II." +.. [BLJ99-2] \T. Beth, D. Jungnickel, H. Lenz "Design theory Vol. II." Second edition. Encyclopedia of Mathematics and its Applications, 78. Cambridge University Press, (1999). -.. [Bo39] R. C. Bose, "On the construction of balanced incomplete block +.. [Bo39] \R. C. Bose, "On the construction of balanced incomplete block designs", Ann. Eugenics, vol. 9, (1939), 353--399. -.. [Bu95] M. Buratti "On simple radical difference families", J. of +.. [Bu95] \M. Buratti "On simple radical difference families", J. of Combinatorial Designs, vol. 3, no. 2 (1995) -.. [Wi72] R. M. Wilson "Cyclotomy and difference families in elementary Abelian +.. [Wi72] \R. M. Wilson "Cyclotomy and difference families in elementary Abelian groups", J. of Num. Th., 4 (1972), pp. 17-47. Functions diff --git a/src/sage/combinat/designs/evenly_distributed_sets.pyx b/src/sage/combinat/designs/evenly_distributed_sets.pyx index 5a094fc89e3..95f29d85609 100644 --- a/src/sage/combinat/designs/evenly_distributed_sets.pyx +++ b/src/sage/combinat/designs/evenly_distributed_sets.pyx @@ -16,14 +16,12 @@ Classes and methods ------------------- """ -include "sage/ext/stdsage.pxi" - cimport cython from libc.limits cimport UINT_MAX from libc.string cimport memset, memcpy -from sage.ext.memory cimport check_malloc, check_calloc +include "cysignals/memory.pxi" from sage.rings.integer cimport Integer,smallInteger @@ -172,15 +170,15 @@ cdef class EvenlyDistributedSetsBacktracker: def __dealloc__(self): if self.diff != NULL: - sage_free(self.diff[0]) - sage_free(self.diff) + sig_free(self.diff[0]) + sig_free(self.diff) if self.ratio != NULL: - sage_free(self.ratio[0]) - sage_free(self.ratio) - sage_free(self.min_orb) - sage_free(self.B) - sage_free(self.cosets) - sage_free(self.t) + sig_free(self.ratio[0]) + sig_free(self.ratio) + sig_free(self.min_orb) + sig_free(self.B) + sig_free(self.cosets) + sig_free(self.t) def __init__(self, K, k, up_to_isomorphism=True, check=False): r""" diff --git a/src/sage/combinat/designs/ext_rep.py b/src/sage/combinat/designs/ext_rep.py index e6e269b9df6..c07e4488bf3 100644 --- a/src/sage/combinat/designs/ext_rep.py +++ b/src/sage/combinat/designs/ext_rep.py @@ -11,7 +11,7 @@ REFERENCES: -.. [D2009] P. Dobcsanyi et al. DesignTheory.org +.. [D2009] \P. Dobcsanyi et al. DesignTheory.org http://designtheory.org/database/ .. TODO:: diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index f83dd30aa93..1f64b04f001 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -12,7 +12,7 @@ :wikipedia:`Block_design` :wikipedia:`Incidence_structure` -.. [2] E. Assmus, J. Key, Designs and their codes, CUP, 1992. +.. [2] \E. Assmus, J. Key, Designs and their codes, CUP, 1992. AUTHORS: diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index ee7d9c36ba2..942541e7876 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1651,7 +1651,7 @@ def OA_n_times_2_pow_c_from_matrix(k,c,G,A,Y,check=True): University of New South Wales, 1995 - .. [AbelCheng1994] R.J.R. Abel and Y.W. Cheng, + .. [AbelCheng1994] \R.J.R. Abel and Y.W. Cheng, Some new MOLS of order 2np for p a prime power, The Australasian Journal of Combinatorics, vol 10 (1994) """ diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index e378052e865..4c7095caf8d 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -28,18 +28,18 @@ References: -.. [Stinson91] D.R. Stinson, +.. [Stinson91] \D.R. Stinson, A survey of Kirkman triple systems and related designs, Volume 92, Issues 1-3, 17 November 1991, Pages 371-393, Discrete Mathematics, http://dx.doi.org/10.1016/0012-365X(91)90294-C. -.. [RCW71] D. K. Ray-Chaudhuri, R. M. Wilson, +.. [RCW71] \D. K. Ray-Chaudhuri, R. M. Wilson, Solution of Kirkman's schoolgirl problem, Volume 19, Pages 187-203, Proceedings of Symposia in Pure Mathematics -.. [BJL99] T. Beth, D. Jungnickel, H. Lenz, +.. [BJL99] \T. Beth, D. Jungnickel, H. Lenz, Design Theory 2ed. Cambridge University Press 1999 diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index f14bae6a5dc..cf3c6b5ca9a 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -119,7 +119,7 @@ Methods from libc.stdlib cimport qsort from libc.stdint cimport uint64_t -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" ctypedef struct hypergraph: int n @@ -165,9 +165,9 @@ cdef void h_free(hypergraph h): r""" Free the hypergraph """ - sage_free(h.names) - sage_free(h.set_space) - sage_free(h.sets) + sig_free(h.names) + sig_free(h.set_space) + sig_free(h.sets) h.names = NULL h.set_space = NULL h.sets = NULL @@ -181,9 +181,9 @@ cdef hypergraph h_init(int n,list H): h.n = n h.m = len(H) h.limbs = (n+63)/64 # =ceil(n/64) - h.names = sage_malloc(sizeof(int)*n) - h.sets = sage_malloc(h.m*sizeof(uint64_t *)) - h.set_space = sage_calloc(h.m*(h.limbs+1),sizeof(uint64_t)) + h.names = sig_malloc(sizeof(int)*n) + h.sets = sig_malloc(h.m*sizeof(uint64_t *)) + h.set_space = sig_calloc(h.m*(h.limbs+1),sizeof(uint64_t)) # Consistency check for S in H: @@ -370,13 +370,13 @@ cdef class SubHypergraphSearch: self.tmp1 = h_init(n1,H1._blocks) # No actual need to fill them, self.tmp2 = h_init(n2,H2._blocks) # only allocate the memory - self.step = sage_malloc((n2+1)*sizeof(int)) + self.step = sig_malloc((n2+1)*sizeof(int)) # all possible traces/induced subgraphs for h2 # # (calloc sets all internal pointers to NULL) - self.h2_traces = sage_calloc(n2+1,sizeof(hypergraph)) - self.h2_induced = sage_calloc(n2+1,sizeof(hypergraph)) + self.h2_traces = sig_calloc(n2+1,sizeof(hypergraph)) + self.h2_induced = sig_calloc(n2+1,sizeof(hypergraph)) if (self.h1.n == -1 or self.h2.n == -1 or @@ -419,9 +419,9 @@ cdef class SubHypergraphSearch: h_free(self.h2) h_free(self.tmp1) h_free(self.tmp2) - sage_free(self.step) - sage_free(self.h2_traces) - sage_free(self.h2_induced) + sig_free(self.step) + sig_free(self.h2_traces) + sig_free(self.h2_induced) def relabel_heuristic(self): r""" diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 9a7f5a68a1b..5eab7152af2 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -18,7 +18,9 @@ # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ -#***************************************************************************** +#**************************************************************************** +# python3 +from __future__ import division from sage.categories.algebras import Algebras from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -40,6 +42,7 @@ from sage.misc.flatten import flatten from sage.rings.all import ZZ + BrauerDiagramOptions = GlobalOptions(name='Brauer diagram', doc=r""" Set and display the global options for Brauer diagram (algebras). If no @@ -282,7 +285,7 @@ def check(self): """ if self._base_diagram: tst = sorted(flatten(self._base_diagram)) - if len(tst) % 2 != 0 or tst != range(-len(tst)/2,0) + range(1,len(tst)/2+1): + if len(tst) % 2 or tst != range(-len(tst)//2,0) + range(1,len(tst)//2+1): raise ValueError("this does not represent two rows of vertices") def __eq__(self, other): @@ -502,7 +505,7 @@ def involution_permutation_triple(self, curt=True): r""" Return the involution permutation triple of ``self``. - From Graham-Lehrer (see `class: BrauerDiagrams`), a Brauer diagram + From Graham-Lehrer (see :class:`BrauerDiagrams`), a Brauer diagram is a triple `(D_1, D_2, \pi)`, where: - `D_1` is a partition of the top nodes; @@ -744,7 +747,7 @@ def __contains__(self, obj): return False if len(obj.base_diagram()) > 0: tst = sorted(flatten(obj.base_diagram())) - if len(tst)%2 != 0 or tst != range(-len(tst)/2,0) + range(1,len(tst)/2+1): + if len(tst) % 2 or tst != range(-len(tst)//2,0) + range(1,len(tst)//2+1): return False return True return self.order == 0 @@ -814,7 +817,7 @@ class BrauerDiagrams(AbstractPartitionDiagrams): r""" This class represents all Brauer diagrams of integer or integer `+1/2` order. For more information on Brauer diagrams, - see `class: BrauerAlgebra`. + see :class:`BrauerAlgebra`. EXAMPLES:: @@ -965,7 +968,7 @@ def from_involution_permutation_triple(self, D1_D2_pi): REFERENCES: - .. [GL1996] J.J. Graham and G.I. Lehrer, Cellular algebras. + .. [GL1996] \J.J. Graham and G.I. Lehrer, Cellular algebras. Inventiones mathematicae 123 (1996), 1--34. EXAMPLES:: @@ -999,7 +1002,7 @@ class TemperleyLiebDiagrams(AbstractPartitionDiagrams): All Temperley-Lieb diagrams of integer or integer `+1/2` order. For more information on Temperley-Lieb diagrams, see - `class: TemperleyLiebAlgebra`. + :class:`TemperleyLiebAlgebra`. EXAMPLES:: diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index d123a49e3b5..57f434350ba 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -1926,7 +1926,7 @@ def to_312_avoiding_permutation(self): REFERENCES: - .. [BK2001] J. Bandlow, K. Killpatrick -- An area-to_inv bijection + .. [BK2001] \J. Bandlow, K. Killpatrick -- An area-to_inv bijection between Dyck paths and 312-avoiding permutations, Electronic Journal of Combinatorics, Volume 8, Issue 1 (2001). @@ -1973,7 +1973,7 @@ def to_noncrossing_permutation(self): REFERENCES: - .. [Stu2008] C. Stump -- More bijective Catalan combinatorics on + .. [Stu2008] \C. Stump -- More bijective Catalan combinatorics on permutations and on colored permutations, Preprint. :arXiv:`0808.2822`. @@ -2024,12 +2024,12 @@ def to_321_avoiding_permutation(self): REFERENCES: - .. [EP2004] S. Elizalde, I. Pak. *Bijections for refined restricted + .. [EP2004] \S. Elizalde, I. Pak. *Bijections for refined restricted permutations**. JCTA 105(2) 2004. - .. [CK2008] A. Claesson, S. Kitaev. *Classification of bijections + .. [CK2008] \A. Claesson, S. Kitaev. *Classification of bijections between `321`- and `132`- avoiding permutations*. Seminaire Lotharingien de Combinatoire **60** 2008. :arxiv:`0805.1325`. - .. [Knu1973] D. Knuth. *The Art of Computer Programming, Vol. III*. + .. [Knu1973] \D. Knuth. *The Art of Computer Programming, Vol. III*. Addison-Wesley. Reading, MA. 1973. EXAMPLES:: @@ -2084,7 +2084,7 @@ def to_132_avoiding_permutation(self): REFERENCES: - .. [Kra2001] C. Krattenthaler -- Permutations with restricted + .. [Kra2001] \C. Krattenthaler -- Permutations with restricted patterns and Dyck paths, Adv. Appl. Math. 27 (2001), 510--530. EXAMPLES:: @@ -2369,7 +2369,7 @@ def to_triangulation(self): REFERENCES: - .. [Cha2005] F. Chapoton, Une Base Symétrique de l'algèbre des + .. [Cha2005] \F. Chapoton, Une Base Symétrique de l'algèbre des Coinvariants Quasi-Symétriques, Electronic Journal of Combinatorics Vol 12(1) (2005) N16. """ @@ -2497,7 +2497,7 @@ def pyramid_weight(self): REFERENCES: - .. [DS1992] A. Denise, R. Simion, Two combinatorial statistics on + .. [DS1992] \A. Denise, R. Simion, Two combinatorial statistics on Dyck paths, Discrete Math 137 (1992), 155--176. """ aseq = self.to_area_sequence() + [0] diff --git a/src/sage/combinat/e_one_star.py b/src/sage/combinat/e_one_star.py index 9c07f2c55a8..dfea9e103d7 100644 --- a/src/sage/combinat/e_one_star.py +++ b/src/sage/combinat/e_one_star.py @@ -46,11 +46,11 @@ REFERENCES: -.. [AI] P. Arnoux, S. Ito, +.. [AI] \P. Arnoux, S. Ito, Pisot substitutions and Rauzy fractals, Bull. Belg. Math. Soc. 8 (2), 2001, pp. 181--207 -.. [SAI] Y. Sano, P. Arnoux, S. Ito, +.. [SAI] \Y. Sano, P. Arnoux, S. Ito, Higher dimensional extensions of substitutions and their dual maps, J. Anal. Math. 83, 2001, pp. 183--206 diff --git a/src/sage/combinat/enumerated_sets.py b/src/sage/combinat/enumerated_sets.py index 0357930ebb3..cf0ad8a8ff5 100644 --- a/src/sage/combinat/enumerated_sets.py +++ b/src/sage/combinat/enumerated_sets.py @@ -119,6 +119,7 @@ - :func:`~sage.sets.recursively_enumerated_set.RecursivelyEnumeratedSet` - :class:`~sage.combinat.backtrack.GenericBacktracker` +- :mod:`sage.parallel.map_reduce` - :ref:`sage.combinat.tiling` - :ref:`sage.combinat.dlx` - :ref:`sage.combinat.matrices.dlxcpp` diff --git a/src/sage/combinat/expnums.pyx b/src/sage/combinat/expnums.pyx index b8646178f87..3fedb1ce92d 100644 --- a/src/sage/combinat/expnums.pyx +++ b/src/sage/combinat/expnums.pyx @@ -6,7 +6,7 @@ AUTHORS: - Nick Alexander """ -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "sage/ext/cdefs.pxi" from sage.rings.integer cimport Integer @@ -81,7 +81,7 @@ def expnums(int n, int aa): r.append(z) cdef mpz_t *bell - bell = sage_malloc(sizeof(mpz_t) * (n+1)) + bell = sig_malloc(sizeof(mpz_t) * (n+1)) if bell == NULL: raise MemoryError("out of memory allocating temporary " "storage in expnums") @@ -102,7 +102,7 @@ def expnums(int n, int aa): for i from 1 <= i <= n: mpz_clear(bell[i]) - sage_free(bell) + sig_free(bell) return r diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index e6528330cef..40aafeb96a1 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -1612,8 +1612,8 @@ def is_final(self, is_final): sage: A.state((0, 0)).final_word_out = [] sage: A.state((0, 0)).is_final = False - sage: A.state((0, 0)).is_final == False - True + sage: A.state((0, 0)).is_final + False sage: A = FSMState('A', is_final=True, final_word_out=[]) sage: A.is_final = False @@ -3897,7 +3897,7 @@ def __call__(self, *args, **kwargs): kwargs['automatic_output_type'] = not 'format_output' in kwargs input_tape = args[0] if hasattr(input_tape, 'is_finite') and \ - input_tape.is_finite() == False: + not input_tape.is_finite(): if not 'iterator_type' in kwargs: kwargs['iterator_type'] = 'simple' return self.iter_process(*args, **kwargs) @@ -6283,7 +6283,7 @@ class is created and is used during the processing. for out in it_output] # process output: cannot return output to due input parameters - if options['list_of_outputs'] == False: + if options['list_of_outputs'] is False: if not it_output and only_accepted: raise ValueError('No accepting output was found but according ' 'to the given options, an accepting output ' @@ -11984,8 +11984,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (options['list_of_outputs'] is False and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13171,8 +13171,8 @@ class is created and is used during the processing. options = copy(self._process_default_options_) options.update(kwargs) - condensed_output = (options['list_of_outputs'] == False and - options['full_output'] == False) + condensed_output = (options['list_of_outputs'] is False and + not options['full_output']) if condensed_output: options['list_of_outputs'] = True @@ -13190,7 +13190,6 @@ class is created and is used during the processing. return result[0] return result - def _process_convert_output_(self, output_data, **kwargs): """ Helper function which converts the output of diff --git a/src/sage/combinat/free_prelie_algebra.py b/src/sage/combinat/free_prelie_algebra.py index 80dd0299ed1..9789a7ec212 100644 --- a/src/sage/combinat/free_prelie_algebra.py +++ b/src/sage/combinat/free_prelie_algebra.py @@ -105,9 +105,9 @@ class FreePreLieAlgebra(CombinatorialFreeModule): REFERENCES: - .. [ChLi] F. Chapoton and M. Livernet, *Pre-Lie algebras and the rooted trees + .. [ChLi] \F. Chapoton and M. Livernet, *Pre-Lie algebras and the rooted trees operad*, International Math. Research Notices (2001) no 8, pages 395-408. - .. [Liv] M. Livernet, *A rigidity theorem for pre-Lie algebras*, J. Pure Appl. + .. [Liv] \M. Livernet, *A rigidity theorem for pre-Lie algebras*, J. Pure Appl. Algebra 207 (2006), no 1, pages 1-18. """ @staticmethod diff --git a/src/sage/combinat/fully_packed_loop.py b/src/sage/combinat/fully_packed_loop.py index 686b805d1d3..cbd2852e828 100644 --- a/src/sage/combinat/fully_packed_loop.py +++ b/src/sage/combinat/fully_packed_loop.py @@ -1,6 +1,9 @@ r""" Fully packed loops """ +# python3 +from __future__ import division + from sage.misc.classcall_metaclass import ClasscallMetaclass from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.unique_representation import UniqueRepresentation @@ -19,6 +22,7 @@ from sage.misc.all import prod from sage.misc.lazy_attribute import lazy_attribute + class FullyPackedLoop(Element): r""" A class for fully packed loops. @@ -490,7 +494,7 @@ def __init__(self, parent, generator): self._six_vertex_model = generator self.configuration = matrix(list(self._six_vertex_model)) - self._n = len(self._end_point_dictionary)/2 + self._n = len(self._end_point_dictionary) // 2 Element.__init__(self, parent) def _repr_(self): @@ -885,7 +889,7 @@ def gyration(self): REFERENCES: - .. [Wieland00] B. Wieland. *A large dihedral symmetry of the set of + .. [Wieland00] \B. Wieland. *A large dihedral symmetry of the set of alternating sign matrices*. Electron. J. Combin. 7 (2000). EXAMPLES:: @@ -1204,30 +1208,30 @@ def _end_point_dictionary(self): for k in range(n): if k % 2 == 0: # top row - end_points[1 + k/2] = (0, k) + end_points[1 + k // 2] = (0, k) # bottom row - end_points[n + 1 + k/2] = (n-1, n-1-k) + end_points[n + 1 + k // 2] = (n-1, n-1-k) # sides for even case if n % 2 == 0: for k in range(n): if k % 2 == 0: # left side - end_points[((3*n + 2 + k)/2)] = (n-1-k, 0) + end_points[((3*n + 2 + k) // 2)] = (n-1-k, 0) # right side - end_points[(n + 2 + k)/2] = (k, n-1) + end_points[(n + 2 + k) // 2] = (k, n-1) # side for odd case if n % 2 == 1: for k in range(n): if k % 2 == 1: # left side - end_points[(3*n + 2 + k)/2] = (n-1-k, 0) + end_points[(3*n + 2 + k) // 2] = (n-1-k, 0) # right side - end_points[(n + 2 + k)/2] = (k, n-1) + end_points[(n + 2 + k) // 2] = (k, n-1) return end_points diff --git a/src/sage/combinat/gelfand_tsetlin_patterns.py b/src/sage/combinat/gelfand_tsetlin_patterns.py index c08f65958e5..d459a3f17db 100644 --- a/src/sage/combinat/gelfand_tsetlin_patterns.py +++ b/src/sage/combinat/gelfand_tsetlin_patterns.py @@ -7,15 +7,15 @@ REFERENCES: -.. [BBF] B. Brubaker, D. Bump, and S. Friedberg. +.. [BBF] \B. Brubaker, D. Bump, and S. Friedberg. Weyl Group Multiple Dirichlet Series: Type A Combinatorial Theory. Ann. of Math. Stud., vol. 175, Princeton Univ. Press, New Jersey, 2011. -.. [GC50] I. M. Gelfand and M. L. Cetlin. +.. [GC50] \I. M. Gelfand and M. L. Cetlin. Finite-Dimensional Representations of the Group of Unimodular Matrices. Dokl. Akad. Nauk SSSR **71**, pp. 825--828, 1950. -.. [Tok88] T. Tokuyama. +.. [Tok88] \T. Tokuyama. A Generating Function of Strict Gelfand Patterns and Some Formulas on Characters of General Linear Groups. J. Math. Soc. Japan **40** (4), pp. 671--685, 1988. @@ -51,6 +51,7 @@ from sage.combinat.combinatorial_map import combinatorial_map from sage.misc.all import prod + class GelfandTsetlinPattern(ClonableArray): r""" A Gelfand-Tsetlin (sometimes written as Gelfand-Zetlin or Gelfand-Cetlin) @@ -502,8 +503,8 @@ def Tokuyama_coefficient(self, name='t'): """ R = PolynomialRing(ZZ, name) t = R.gen(0) - if self.is_strict() == False: - return R(0) + if not self.is_strict(): + return R.zero() return (t+1)**(self.number_of_special_entries()) * t**(self.number_of_boxes()) @@ -1017,12 +1018,21 @@ def _cftp(self, start_row): ALGORITHM: - The set of Gelfand-Tsetlin patterns can partially ordered by elementwise - domination. The partial order has unique maximum and minimum elements - that are computed by the methods ``_cftp_upper`` and ``_cftp_lower``. - We then run the Markov chain that randomly toggles each element up or - down from the past until the state reached from the upper and lower start + The set of Gelfand-Tsetlin patterns can partially ordered by + elementwise domination. The partial order has unique maximum + and minimum elements that are computed by the methods + :meth:`_cftp_upper` and :meth:`_cftp_lower`. We then run the Markov + chain that randomly toggles each element up or down from the + past until the state reached from the upper and lower start points coalesce as described in [Propp1997]_. + + EXAMPLES:: + + sage: G = GelfandTsetlinPatterns(3, 5) + sage: G._cftp(0) # random + [[5, 3, 2], [4, 2], [3]] + sage: G._cftp(0) in G + True """ from sage.misc.randstate import current_randstate from sage.misc.randstate import seed diff --git a/src/sage/combinat/gray_codes.py b/src/sage/combinat/gray_codes.py index 2a600f9c702..a4c123ec530 100644 --- a/src/sage/combinat/gray_codes.py +++ b/src/sage/combinat/gray_codes.py @@ -3,10 +3,10 @@ REFERENCES: -.. [Knuth-TAOCP2A] D. Knuth "The art of computer programming", fascicules 2A, +.. [Knuth-TAOCP2A] \D. Knuth "The art of computer programming", fascicules 2A, "generating all n-tuples" -.. [Knuth-TAOCP3A] D. Knuth "The art of computer programming", fascicule 3A +.. [Knuth-TAOCP3A] \D. Knuth "The art of computer programming", fascicule 3A "generating all combinations" Functions diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index fc52464cf88..435e3c75d86 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -299,9 +299,9 @@ def gale_ryser_theorem(p1, p2, algorithm="gale"): REFERENCES: - .. [Ryser63] H. J. Ryser, Combinatorial Mathematics, + .. [Ryser63] \H. J. Ryser, Combinatorial Mathematics, Carus Monographs, MAA, 1963. - .. [Gale57] D. Gale, A theorem on flows in networks, Pacific J. Math. + .. [Gale57] \D. Gale, A theorem on flows in networks, Pacific J. Math. 7(1957)1073-1082. """ from sage.combinat.partition import Partition @@ -779,10 +779,21 @@ def __iter__(self): yield [self.n] return - for nbar in range(self.n+1): - n = self.n-nbar - for rest in IntegerVectors_nk(nbar , self.k-1): - yield [n] + rest + rem = -1 # Amount remaining + cur = [self.n+1] + k = int(self.k) + while cur: + cur[-1] -= 1 + rem += 1 + if rem == 0: + yield cur + [Integer(0)] * (k - len(cur)) + elif cur[-1] < 0: + rem += cur.pop() + elif len(cur) == k - 1: + yield cur + [Integer(rem)] + else: + cur.append(rem + 1) + rem = -1 def __repr__(self): """ diff --git a/src/sage/combinat/integer_vector_weighted.py b/src/sage/combinat/integer_vector_weighted.py index e9c5b56cf4f..39ce24252f6 100644 --- a/src/sage/combinat/integer_vector_weighted.py +++ b/src/sage/combinat/integer_vector_weighted.py @@ -23,6 +23,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from __builtin__ import list as builtinlist +from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -155,9 +156,9 @@ def __contains__(self, x): sage: [3,-1,0] in WeightedIntegerVectors([2,1,1]) False """ - return isinstance(x, (builtinlist, Permutation)) and \ - len(x) == len(self._weights) and \ - all(isinstance(i, (int, Integer)) and i>=0 for i in x) + return (isinstance(x, (builtinlist, Permutation)) + and len(x) == len(self._weights) + and all(isinstance(i, (int, Integer)) and i >= 0 for i in x)) def subset(self, size = None): """ @@ -245,27 +246,6 @@ def __contains__(self, x): return True - def _recfun(self, n, l): - """ - EXAMPLES:: - - sage: w = WeightedIntegerVectors(3, [2,1,1]) - sage: list(w._recfun(3, [1,1,2])) - [[0, 1, 1], [1, 0, 1], [0, 3, 0], [1, 2, 0], [2, 1, 0], [3, 0, 0]] - """ - w = l[-1] - l = l[:-1] - if l == []: - d = int(n) // int(w) - if n % w == 0: - yield [d] - # Otherwise: bad branch - return - - for d in range(int(n)//int(w), -1, -1): - for x in self._recfun(n-d*w, l): - yield x + [d] - def __iter__(self): """ TESTS:: @@ -288,13 +268,72 @@ def __iter__(self): sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ - if len(self._weights) == 0: + if not self._weights: if self._n == 0: yield [] return perm = Word(self._weights).standard_permutation() - l = [x for x in sorted(self._weights)] - for x in self._recfun(self._n, l): - yield perm.action(x) + perm = [len(self._weights)-i for i in perm] + l = [x for x in sorted(self._weights, reverse=True)] + for x in iterator_fast(self._n, l): + yield [x[i] for i in perm] + #.action(x) #_left_to_right_multiply_on_right(Permutation(x)) + +def iterator_fast(n, l): + """ + Iterate over all ``l`` weighted integer vectors with total weight ``n``. + + INPUT: + + - ``n`` -- an integer + - ``l`` -- the weights in weakly decreasing order + + EXAMPLES:: + + sage: from sage.combinat.integer_vector_weighted import iterator_fast + sage: list(iterator_fast(3, [2,1,1])) + [[1, 1, 0], [1, 0, 1], [0, 3, 0], [0, 2, 1], [0, 1, 2], [0, 0, 3]] + sage: list(iterator_fast(2, [2])) + [[1]] + + Test that :trac:`20491` is fixed:: + + sage: type(list(iterator_fast(2, [2]))[0][0]) + + """ + if n < 0: + return + + zero = ZZ.zero() + one = ZZ.one() + + if not l: + if n == 0: + yield [] + return + if len(l) == 1: + if n % l[0] == 0: + yield [n // l[0]] + return + + k = 0 + cur = [n // l[k] + one] + rem = n - cur[-1] * l[k] # Amount remaining + while cur: + cur[-1] -= one + rem += l[k] + if rem == zero: + yield cur + [zero] * (len(l) - len(cur)) + elif cur[-1] < zero or rem < zero: + rem += cur.pop() * l[k] + k -= 1 + elif len(l) == len(cur) + 1: + if rem % l[-1] == zero: + yield cur + [rem // l[-1]] + else: + k += 1 + cur.append(rem // l[k] + one) + rem -= cur[-1] * l[k] + diff --git a/src/sage/combinat/k_tableau.py b/src/sage/combinat/k_tableau.py index 1d4e02f96b3..11979f2d490 100644 --- a/src/sage/combinat/k_tableau.py +++ b/src/sage/combinat/k_tableau.py @@ -11,11 +11,11 @@ REFERENCES: -.. [LLMS2006] T. Lam, L. Lapointe, J. Morse, M. Shimozono, +.. [LLMS2006] \T. Lam, L. Lapointe, J. Morse, M. Shimozono, Affine insertion and Pieri rules for the affine Grassmannian, Memoirs of the AMS, 208 (2010), no. 977, :arxiv:`math.CO/0609110` -.. [LLMSSZ2013] T. Lam, L. Lapointe, J. Morse, A. Schilling, M. Shimozono, M. Zabrocki, +.. [LLMSSZ2013] \T. Lam, L. Lapointe, J. Morse, A. Schilling, M. Shimozono, M. Zabrocki, `k`-Schur functions and affine Schubert calculus, preprint :arXiv:`1301.3569` diff --git a/src/sage/combinat/kazhdan_lusztig.py b/src/sage/combinat/kazhdan_lusztig.py index 915adcde9f9..1c891905224 100644 --- a/src/sage/combinat/kazhdan_lusztig.py +++ b/src/sage/combinat/kazhdan_lusztig.py @@ -43,14 +43,14 @@ class KazhdanLusztigPolynomial(UniqueRepresentation, SageObject): REFERENCES: - .. [KL79] D. Kazhdan and G. Lusztig. *Representations of Coxeter + .. [KL79] \D. Kazhdan and G. Lusztig. *Representations of Coxeter groups and Hecke algebras*. Invent. Math. **53** (1979). no. 2, 165--184. :doi:`10.1007/BF01390031` :mathscinet:`MR0560412` - .. [Dy93] M. J. Dyer. *Hecke algebras and shellings of Bruhat + .. [Dy93] \M. J. Dyer. *Hecke algebras and shellings of Bruhat intervals*. Compositio Mathematica, 1993, 89(1): 91-115. - .. [BB05] A. Bjorner, F. Brenti. *Combinatorics of Coxeter + .. [BB05] \A. Bjorner, F. Brenti. *Combinatorics of Coxeter groups*. New York: Springer, 2005. EXAMPLES:: diff --git a/src/sage/combinat/knutson_tao_puzzles.py b/src/sage/combinat/knutson_tao_puzzles.py index ef701da509b..e0d37c925de 100644 --- a/src/sage/combinat/knutson_tao_puzzles.py +++ b/src/sage/combinat/knutson_tao_puzzles.py @@ -835,7 +835,7 @@ def K_grassmannian_pieces(): REFERENCES: - .. [Buch00] A. Buch, A Littlewood-Richardson rule for the K-theory of Grassmannians, :arXiv:`math.AG/0004137` + .. [Buch00] \A. Buch, A Littlewood-Richardson rule for the K-theory of Grassmannians, :arXiv:`math.AG/0004137` EXAMPLES:: @@ -858,7 +858,7 @@ def H_two_step_pieces(): REFERENCES: - .. [BuchKreschTamvakis03] A. Buch, A. Kresch, H. Tamvakis, Gromov-Witten invariants on Grassmannian, :arXiv:`math/0306388` + .. [BuchKreschTamvakis03] \A. Buch, A. Kresch, H. Tamvakis, Gromov-Witten invariants on Grassmannian, :arXiv:`math/0306388` EXAMPLES:: @@ -887,7 +887,7 @@ def HT_two_step_pieces(): REFERENCES: - .. [CoskunVakil06] I. Coskun, R. Vakil, Geometric positivity in the cohomology of homogeneous spaces + .. [CoskunVakil06] \I. Coskun, R. Vakil, Geometric positivity in the cohomology of homogeneous spaces and generalized Schubert calculus, :arXiv:`math/0610538` EXAMPLES:: @@ -932,7 +932,7 @@ def BK_pieces(max_letter): REFERENCES: - .. [KnutsonPurbhoo10] A. Knutson, K. Purbhoo, Product and puzzle formulae + .. [KnutsonPurbhoo10] \A. Knutson, K. Purbhoo, Product and puzzle formulae for `GL_n` Belkale-Kumar coefficients, :arXiv:`1008.4979` EXAMPLES:: diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index a5cceaeba3c..495d44dfe6b 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -39,10 +39,10 @@ REFERENCES: -.. [HadaSloa] N.J.A. Sloane's Library of Hadamard Matrices, at +.. [HadaSloa] \N.J.A. Sloane's Library of Hadamard Matrices, at http://neilsloane.com/hadamard/ .. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` -.. [Hora] K. J. Horadam, Hadamard Matrices and Their Applications, +.. [Hora] \K. J. Horadam, Hadamard Matrices and Their Applications, Princeton University Press, 2006. """ @@ -567,12 +567,12 @@ def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False REFERENCE: - .. [BH12] A. Brouwer and W. Haemers, + .. [BH12] \A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf - .. [HX10] W. Haemers and Q. Xiang, + .. [HX10] \W. Haemers and Q. Xiang, Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`, European Journal of Combinatorics, Volume 31, Issue 6, August 2010, Pages 1553-1559, @@ -704,7 +704,7 @@ def RSHCD_324(e): REFERENCE: - .. [CP16] N. Cohen, D. Pasechnik, + .. [CP16] \N. Cohen, D. Pasechnik, Implementing Brouwer's database of strongly regular graphs, http://arxiv.org/abs/1601.00181 """ @@ -903,13 +903,13 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): REFERENCES: - .. [GS70s] J.M. Goethals and J. J. Seidel, + .. [GS70s] \J.M. Goethals and J. J. Seidel, A skew Hadamard matrix of order 36, J. Aust. Math. Soc. 11(1970), 343-344 - .. [Wall71] J. Wallis, + .. [Wall71] \J. Wallis, A skew-Hadamard matrix of order 92, Bull. Aust. Math. Soc. 5(1971), 203-204 - .. [KoSt08] C. Koukouvinos, S. Stylianou + .. [KoSt08] \C. Koukouvinos, S. Stylianou On skew-Hadamard matrices, Discrete Math. 308(2008) 2723-2731 @@ -1063,7 +1063,7 @@ def skew_hadamard_matrix(n,existence=False, skew_normalize=True, check=True): REFERENCES: - .. [Ha83] M. Hall, + .. [Ha83] \M. Hall, Combinatorial Theory, 2nd edition, Wiley, 1983 diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 7ffc73c8b85..07598d56748 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -3,11 +3,11 @@ REFERENCES: -.. [Ges] I. Gessel, *Multipartite P-partitions and inner products of skew Schur +.. [Ges] \I. Gessel, *Multipartite P-partitions and inner products of skew Schur functions*, Contemp. Math. **34** (1984), 289-301. http://people.brandeis.edu/~gessel/homepage/papers/multipartite.pdf -.. [MR] C. Malvenuto and C. Reutenauer, *Duality between quasi-symmetric +.. [MR] \C. Malvenuto and C. Reutenauer, *Duality between quasi-symmetric functions and the Solomon descent algebra*, J. Algebra **177** (1995), no. 3, 967-982. http://www.mat.uniroma1.it/people/malvenuto/Duality.pdf @@ -32,7 +32,7 @@ *Noncommutative symmetric functions*. :arxiv:`hep-th/9407124v1` -.. [NCSF2] D. Krob, B. Leclerc, J.-Y. Thibon, +.. [NCSF2] \D. Krob, B. Leclerc, J.-Y. Thibon, *Noncommutative symmetric functions II: Transformations of alphabets*. http://www-igm.univ-mlv.fr/~jyt/ARTICLES/NCSF2.ps diff --git a/src/sage/combinat/ncsym/dual.py b/src/sage/combinat/ncsym/dual.py index 11f6a79be99..78227356027 100644 --- a/src/sage/combinat/ncsym/dual.py +++ b/src/sage/combinat/ncsym/dual.py @@ -451,7 +451,7 @@ def expand(self, n, letter='x'): REFERENCES: - .. [HNT06] F. Hivert, J.-C. Novelli, J.-Y. Thibon. + .. [HNT06] \F. Hivert, J.-C. Novelli, J.-Y. Thibon. *Commutative combinatorial Hopf algebras*. (2006). :arxiv:`0605262v1`. diff --git a/src/sage/combinat/ncsym/ncsym.py b/src/sage/combinat/ncsym/ncsym.py index 7923ecf42c4..2909f073abb 100644 --- a/src/sage/combinat/ncsym/ncsym.py +++ b/src/sage/combinat/ncsym/ncsym.py @@ -179,25 +179,25 @@ class SymmetricFunctionsNonCommutingVariables(UniqueRepresentation, Parent): REFERENCES: - .. [BZ05] N. Bergeron, M. Zabrocki. *The Hopf algebra of symmetric + .. [BZ05] \N. Bergeron, M. Zabrocki. *The Hopf algebra of symmetric functions and quasisymmetric functions in non-commutative variables are free and cofree*. (2005). :arxiv:`math/0509265v3`. - .. [BHRZ06] N. Bergeron, C. Hohlweg, M. Rosas, M. Zabrocki. + .. [BHRZ06] \N. Bergeron, C. Hohlweg, M. Rosas, M. Zabrocki. *Grothendieck bialgebras, partition lattices, and symmetric functions in noncommutative variables*. Electronic Journal of Combinatorics. **13** (2006). - .. [RS06] M. Rosas, B. Sagan. *Symmetric functions in noncommuting + .. [RS06] \M. Rosas, B. Sagan. *Symmetric functions in noncommuting variables*. Trans. Amer. Math. Soc. **358** (2006). no. 1, 215-232. :arxiv:`math/0208168`. - .. [BRRZ08] N. Bergeron, C. Reutenauer, M. Rosas, M. Zabrocki. + .. [BRRZ08] \N. Bergeron, C. Reutenauer, M. Rosas, M. Zabrocki. *Invariants and coinvariants of the symmetric group in noncommuting variables*. Canad. J. Math. **60** (2008). 266-296. :arxiv:`math/0502082` - .. [BT13] N. Bergeron, N. Thiem. *A supercharacter table decomposition + .. [BT13] \N. Bergeron, N. Thiem. *A supercharacter table decomposition via power-sum symmetric functions*. Int. J. Algebra Comput. **23**, 763 (2013). :doi:`10.1142/S0218196713400171`. :arxiv:`1112.4901`. diff --git a/src/sage/combinat/ordered_tree.py b/src/sage/combinat/ordered_tree.py index cefb02569e0..c3b5398162b 100644 --- a/src/sage/combinat/ordered_tree.py +++ b/src/sage/combinat/ordered_tree.py @@ -443,7 +443,7 @@ def to_undirected_graph(self): relabel = True roots = [self] g.add_vertex(name=self.label()) - while len(roots) != 0: + while roots: node = roots.pop() for child in node: g.add_vertex(name=child.label()) @@ -489,7 +489,7 @@ def to_poset(self, root_to_leaf=False): relations = [] elements = [self.label()] roots = [self] - while len(roots) != 0: + while roots: node = roots.pop() for child in node: elements.append(child.label()) diff --git a/src/sage/combinat/parking_functions.py b/src/sage/combinat/parking_functions.py index 7c26143acb9..0c54166db75 100644 --- a/src/sage/combinat/parking_functions.py +++ b/src/sage/combinat/parking_functions.py @@ -36,17 +36,17 @@ REFERENCES: -.. [Beck] M. Beck, Stanford Math Circle - Parking Functions, October 2010, +.. [Beck] \M. Beck, Stanford Math Circle - Parking Functions, October 2010, http://math.stanford.edu/circle/parkingBeck.pdf .. [Hag08] The `q,t` -- Catalan Numbers and the Space of Diagonal Harmonics: With an Appendix on the Combinatorics of Macdonald Polynomials, James Haglund, University of Pennsylvania, Philadelphia -- AMS, 2008, 167 pp. -.. [Shin] H. Shin, Forests and Parking Functions, slides from talk September 24, 2008, +.. [Shin] \H. Shin, Forests and Parking Functions, slides from talk September 24, 2008, http://www.emis.de/journals/SLC/wpapers/s61vortrag/shin.pdf -.. [GXZ] A. M. Garsia, G. Xin, M. Zabrocki, A three shuffle case of the +.. [GXZ] \A. M. Garsia, G. Xin, M. Zabrocki, A three shuffle case of the compositional parking function conjecture, :arxiv:`1208.5796v1` AUTHORS: diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index a92c09d4d22..c6f745da145 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -76,8 +76,8 @@ sage: Partitions(4).first() [4] -Using the method ``.next()``, we can calculate the 'next' partition. -When we are at the last partition, ``None`` will be returned:: +Using the method ``.next(p)``, we can calculate the 'next' partition +after `p`. When we are at the last partition, ``None`` will be returned:: sage: Partitions(4).next([4]) [3, 1] @@ -582,7 +582,7 @@ def __classcall_private__(cls, mu=None, **keyword): raise ValueError('incorrect syntax for Partition()') def __setstate__(self, state): - """ + r""" In order to maintain backwards compatibility and be able to unpickle a old pickle from ``Partition_class`` we have to override the default ``__setstate__``. @@ -3093,7 +3093,7 @@ def centralizer_size(self, t=0, q=0): \prod_i m_i! i^{m_i}. - Including the optional parameters `t` and `q` gives the `q - t` analog + Including the optional parameters `t` and `q` gives the `q,t` analog, which is the former product times .. MATH:: @@ -3115,10 +3115,11 @@ def centralizer_size(self, t=0, q=0): sage: Partition([]).centralizer_size(q=2, t=4) 1 """ - p = self - a = p.to_exp() - size = prod([(i+1)**a[i]*factorial(a[i]) for i in range(len(a))]) - size *= prod( [ (1-q**j)/(1-t**j) for j in p ] ) + size = prod(i ** mi * factorial(mi) + for i, mi in self.to_exp_dict().iteritems()) + if t or q: + size *= prod((ZZ.one() - q ** j) / (ZZ.one() - t ** j) + for j in self) return size @@ -6818,7 +6819,7 @@ def __iter__(self): sage: P = Partitions(regular=3) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [2, 1, 1]] """ n = 0 @@ -6896,7 +6897,7 @@ def __iter__(self): sage: P = Partitions(regular=3, max_length=2) sage: it = P.__iter__() - sage: [it.next() for x in range(10)] + sage: [next(it) for x in range(10)] [[], [1], [2], [1, 1], [3], [2, 1], [4], [3, 1], [2, 2], [5]] """ n = 0 diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 1ffb4817092..7ecae3cfd89 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -85,9 +85,9 @@ class of modules for the algebras, which are generalisations of the Specht REFERENCES: -.. [DJM99] R. Dipper, G. James and A. Mathas "The cyclotomic q-Schur algebra", Math. Z, +.. [DJM99] \R. Dipper, G. James and A. Mathas "The cyclotomic q-Schur algebra", Math. Z, 229 (1999), 385-416. -.. [BK09] J. Brundan and A. Kleshchev "Graded decomposition numbers for cyclotomic Hecke algebras", +.. [BK09] \J. Brundan and A. Kleshchev "Graded decomposition numbers for cyclotomic Hecke algebras", Adv. Math., 222 (2009), 1883-1942" AUTHORS: @@ -1178,7 +1178,7 @@ def top_garnir_tableau(self,e,cell): REFERENCE: - .. [KMR] A. Kleshchev, A. Mathas, and A. Ram, *Universal Specht + .. [KMR] \A. Kleshchev, A. Mathas, and A. Ram, *Universal Specht modules for cyclotomic Hecke algebras*, Proc. London Math. Soc. (2012) 105 (6): 1245-1289. :arxiv:`1102.3519v1` @@ -2102,7 +2102,7 @@ def cardinality(self): return ZZ(gp.eval('polcoeff((1/eta(x+O(x^%s)))^%s, %s, x)'%(self.size()+1,self.level(), self.size()))) def __setstate__(self, state): - """ + r""" In order to maintain backwards compatibility and be able to unpickle a old pickle from PartitionTuples_nk we have to override the default ``__setstate__``. diff --git a/src/sage/combinat/perfect_matching.py b/src/sage/combinat/perfect_matching.py index 00c8ff308b0..0e58a61bfdb 100644 --- a/src/sage/combinat/perfect_matching.py +++ b/src/sage/combinat/perfect_matching.py @@ -44,13 +44,14 @@ .. [CM] Benoit Collins, Sho Matsumoto, On some properties of orthogonal Weingarten functions, :arxiv:`0903.5143`. """ - #***************************************************************************** # Copyright (C) 2010 Valentin Feray # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division from sage.structure.unique_representation import UniqueRepresentation @@ -834,7 +835,8 @@ def to_non_crossing_set_partition(self): raise ValueError("matching must be non-crossing") else: perm = self.to_permutation() - perm2 = Permutation([(perm[2*i])/2 for i in range(len(perm)/2)]) + perm2 = Permutation([perm[2 * i] // 2 + for i in range(len(perm) // 2)]) return SetPartition(perm2.cycle_tuples()) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index ecdca194fa7..4d6374d5e4a 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -966,7 +966,6 @@ def to_cycles(self, singletons=True): sage: all(from_cycles(size, p.to_cycles()) == p for p in sample) True - Note: there is an alternative implementation called ``_to_cycle_set`` which could be slightly (10%) faster for some input (typically for permutations of size in the range [100, 10000]). You can run the @@ -995,17 +994,17 @@ def to_cycles(self, singletons=True): l = self[:] - #Go through until we've considered every number between 1 and len(l) + # Go through until we've considered every number between 1 and len(l) for i in range(len(l)): - if l[i] == False: + if not l[i]: continue - cycleFirst = i+1 - cycle = [ cycleFirst ] + cycleFirst = i + 1 + cycle = [cycleFirst] l[i], next = False, l[i] while next != cycleFirst: cycle.append( next ) l[next - 1], next = False, l[next - 1] - #Add the cycle to the list of cycles + # Add the cycle to the list of cycles if singletons or len(cycle) > 1: cycles.append(tuple(cycle)) return cycles @@ -1865,6 +1864,24 @@ def length(self): """ return self.number_of_inversions() + def absolute_length(self): + """ + Return the absolute length of ``self`` + + The absolute length is the length of the shortest expression + of the element as a product of reflections. + + For permutations in the symmetric groups, the absolute + length is the size minus the number of its disjoint + cycles. + + EXAMPLES:: + + sage: Permutation([4,2,3,1]).absolute_length() + 1 + """ + return self.size() - len(self.cycle_type()) + @combinatorial_map(order=2,name='inverse') def inverse(self): r""" @@ -2912,7 +2929,7 @@ def descent_polynomial(self): REFERENCES: - .. [GarStan1984] A. M. Garsia, Dennis Stanton. + .. [GarStan1984] \A. M. Garsia, Dennis Stanton. *Group actions on Stanley-Reisner rings and invariants of permutation groups*. Adv. in Math. **51** (1984), 107-201. http://www.sciencedirect.com/science/article/pii/0001870884900057 @@ -4618,7 +4635,7 @@ def retract_okounkov_vershik(self, m): REFERENCES: - .. [OkounkovVershik2] A. M. Vershik, A. Yu. Okounkov. + .. [OkounkovVershik2] \A. M. Vershik, A. Yu. Okounkov. *A New Approach to the Representation Theory of the Symmetric Groups. 2*. http://uk.arxiv.org/abs/math/0503040v3. @@ -4710,7 +4727,7 @@ def hyperoctahedral_double_coset_type(self): REFERENCES: - .. [Mcd] I. G. Macdonald. Symmetric functions and Hall + .. [Mcd] \I. G. Macdonald. Symmetric functions and Hall polynomials. Oxford University Press, second edition, 1995. """ from sage.combinat.perfect_matching import PerfectMatchings @@ -5972,7 +5989,7 @@ def __init__(self, n): sage: TestSuite(P).run() sage: P.global_options.reset() """ - cat = FiniteWeylGroups() & FinitePermutationGroups() + cat = FiniteWeylGroups().Irreducible() & FinitePermutationGroups() StandardPermutations_n_abstract.__init__(self, n, category=cat) def _repr_(self): @@ -6083,10 +6100,20 @@ def unrank(self, r): else: return from_rank(self.n, r) - def rank(self, p): + def rank(self, p=None): """ + Return the rank of ``self`` or ``p`` depending on input. + + If a permutation ``p`` is given, return the rank of ``p`` + in ``self``. Otherwise return the dimension of the + underlying vector space spanned by the (simple) roots. + EXAMPLES:: + sage: P = Permutations(5) + sage: P.rank() + 4 + sage: SP3 = Permutations(3) sage: map(SP3.rank, SP3) [0, 1, 2, 3, 4, 5] @@ -6094,6 +6121,8 @@ def rank(self, p): sage: map(SP0.rank, SP0) [0] """ + if p is None: + return self.n - 1 if p in self: return Permutation(p).rank() raise ValueError("x not in self") @@ -6122,6 +6151,35 @@ def cardinality(self): """ return factorial(self.n) + def degrees(self): + """ + Return the degrees of ``self``. + + These are the degrees of the fundamental invariants of the + ring of polynomial invariants. + + EXAMPLES:: + + sage: Permutations(3).degrees() + (2, 3) + sage: Permutations(7).degrees() + (2, 3, 4, 5, 6, 7) + """ + return tuple(Integer(i) for i in range(2, self.n+1)) + + def codegrees(self): + """ + Return the codegrees of ``self``. + + EXAMPLES:: + + sage: Permutations(3).codegrees() + (0, 1) + sage: Permutations(7).codegrees() + (0, 1, 2, 3, 4, 5) + """ + return tuple(Integer(i) for i in range(self.n-1)) + def element_in_conjugacy_classes(self, nu): r""" Return a permutation with cycle type ``nu``. @@ -7511,10 +7569,17 @@ def permutohedron_lequal(p1, p2, side="right"): ############ # Patterns # ############ +from sage.combinat.words.finite_word import evaluation_dict -def to_standard(p): +def to_standard(p, cmp=None): r""" - Return a standard permutation corresponding to the list ``p``. + Return a standard permutation corresponding to the iterable ``p``. + + INPUT: + + - ``p`` -- an iterable + - ``cmp`` -- (optional) a comparison function for the two elements + ``x`` and ``y`` of ``p`` (return an integer according to the outcome) EXAMPLES:: @@ -7525,6 +7590,10 @@ def to_standard(p): [1, 2, 3] sage: permutation.to_standard([]) [] + sage: permutation.to_standard([1,2,3], cmp=lambda x,y: int(-1)*cmp(x,y)) + [3, 2, 1] + sage: permutation.to_standard([5,8,2,5], cmp=lambda x,y: int(-1)*cmp(x,y)) + [2, 1, 4, 3] TESTS: @@ -7535,22 +7604,38 @@ def to_standard(p): [1, 2, 3] sage: a [1, 2, 4] - """ - if not p: - return Permutations()([]) - s = [0]*len(p) - c = p[:] - biggest = max(p) + 1 - i = 1 - for _ in range(len(c)): - smallest = min(c) - smallest_index = c.index(smallest) - s[smallest_index] = i - i += 1 - c[smallest_index] = biggest - - return Permutations()(s) + We check against the naive method:: + + sage: def std(p): + ....: s = [0]*len(p) + ....: c = p[:] + ....: biggest = max(p) + 1 + ....: i = 1 + ....: for _ in range(len(c)): + ....: smallest = min(c) + ....: smallest_index = c.index(smallest) + ....: s[smallest_index] = i + ....: i += 1 + ....: c[smallest_index] = biggest + ....: return Permutations()(s) + sage: p = list(Words(100, 1000).random_element()) + sage: std(p) == permutation.to_standard(p) + True + + """ + ev_dict = evaluation_dict(p) + ordered_alphabet = sorted(ev_dict, cmp=cmp) + offset = 0 + for k in ordered_alphabet: + temp = ev_dict[k] + ev_dict[k] = offset + offset += temp + result = [] + for l in p: + ev_dict[l] += 1 + result.append(ev_dict[l]) + return Permutations(len(result))(result) ########################################################## diff --git a/src/sage/combinat/permutation_cython.pyx b/src/sage/combinat/permutation_cython.pyx index 539e15d8e5e..e36d48c488b 100644 --- a/src/sage/combinat/permutation_cython.pyx +++ b/src/sage/combinat/permutation_cython.pyx @@ -28,7 +28,7 @@ speed, we provide a class that wraps our struct. # # Copyright 2010, Tom Boothby -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from cpython.list cimport * ########################################################## @@ -160,7 +160,7 @@ def permutation_iterator_transposition_list(int n): N *= m m -= 1 - c = sage_malloc(2*n*sizeof(int)) + c = sig_malloc(2*n*sizeof(int)) if c is NULL: raise MemoryError("Failed to allocate memory in " "permutation_iterator_transposition_list") @@ -169,14 +169,14 @@ def permutation_iterator_transposition_list(int n): try: T = PyList_New(N-1) except Exception: - sage_free(c) + sig_free(c) raise MemoryError("Failed to allocate memory in " "permutation_iterator_transposition_list") reset_swap(n,c,o) for m in range(N-1): PyList_SET_ITEM(T, m, next_swap(n,c,o)) - sage_free(c) + sig_free(c) return T diff --git a/src/sage/combinat/posets/hasse_diagram.py b/src/sage/combinat/posets/hasse_diagram.py index 10d68ebf8d3..b27b134d636 100644 --- a/src/sage/combinat/posets/hasse_diagram.py +++ b/src/sage/combinat/posets/hasse_diagram.py @@ -5,29 +5,24 @@ {INDEX_OF_FUNCTIONS} """ + #***************************************************************************** -# Copyright (C) 2008 Peter Jipsen , -# Franco Saliola -# -# 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: +# Copyright (C) 2008 Peter Jipsen +# Copyright (C) 2008 Franco Saliola # +# 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 copy import copy from sage.graphs.digraph import DiGraph from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ -from sage.misc.misc import uniq from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecated_function_alias class HasseDiagram(DiGraph): """ @@ -707,7 +702,6 @@ def cardinality(self): """ return self.order() - 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 @@ -743,7 +737,6 @@ def moebius_function(self,i,j): # dumb algorithm return self._moebius_function_values[(i,j)] mobius_function = deprecated_function_alias(19855, moebius_function) - 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 @@ -789,7 +782,6 @@ def moebius_function_matrix(self): mobius_function_matrix = deprecated_function_alias(19855, moebius_function_matrix) # 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 @@ -1295,6 +1287,52 @@ def is_distributive_lattice(self): # still a dumb algorithm... if mt[x][jn[y][z]]!=jn[mt[x][y]][mt[x][z]]: return False return True + def vertical_decomposition(self, return_list=False): + """ + Return vertical decomposition of the lattice. + + This is the backend function for vertical decomposition + functions of lattices. + + The property of being vertically decomposable is defined for lattices. + This is not checked, and the function works with any bounded poset. + + INPUT: + + - ``return_list``, a boolean. If ``False`` (the default), return + ``True`` if the lattice is vertically decomposable and ``False`` + otherwise. If ``True``, return list of decomposition elements. + + EXAMPLES:: + + sage: H = Posets.BooleanLattice(4)._hasse_diagram + sage: H.vertical_decomposition() + False + sage: P = Poset( ([1,2,3,6,12,18,36], attrcall("divides")) ) + sage: P._hasse_diagram.vertical_decomposition() + True + sage: P._hasse_diagram.vertical_decomposition(return_list=True) + [3] + """ + n = self.cardinality() + if n < 3: + if return_list: + return [] + else: + return False + result = [] # Never take the bottom element to list. + e = 0 + m = 0 + for i in range(n-1): + for j in self.outgoing_edge_iterator(i): + m = max(m, j[1]) + if m == i+1: + if not return_list: + return m < n-1 + result.append(m) + result.pop() # Remove the top element. + return result + def is_complemented_lattice(self): r""" Return ``True`` if ``self`` is the Hasse diagram of a @@ -1703,6 +1741,4 @@ def frattini_sublattice(self): all(e in ms for ms in max_sublats)] from sage.misc.rest_index_of_methods import gen_rest_table_index -import sys __doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(HasseDiagram)) - diff --git a/src/sage/combinat/posets/incidence_algebras.py b/src/sage/combinat/posets/incidence_algebras.py index d1f4e0a4b5b..a8428df24aa 100644 --- a/src/sage/combinat/posets/incidence_algebras.py +++ b/src/sage/combinat/posets/incidence_algebras.py @@ -2,26 +2,24 @@ r""" Incidence Algebras """ + #***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw # -# 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/ #***************************************************************************** + from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.categories.algebras import Algebras from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.matrix_space import MatrixSpace +from sage.misc.superseded import deprecated_function_alias from copy import copy @@ -226,7 +224,6 @@ def zeta(self): """ return self.sum(self.basis()) - from sage.misc.superseded import deprecated_function_alias @cached_method def moebius(self): """ @@ -570,7 +567,6 @@ def zeta(self): """ return self.sum(self.basis()) - from sage.misc.superseded import deprecated_function_alias @cached_method def moebius(self): """ diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index 284a5e0c065..a9ab370582a 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -43,13 +43,16 @@ :meth:`~FiniteLatticePoset.is_lower_semimodular` | Return ``True`` if the lattice is lower semimodular. :meth:`~FiniteLatticePoset.is_upper_semimodular` | Return ``True`` if the lattice is upper semimodular. :meth:`~FiniteLatticePoset.is_atomic` | Return ``True`` if every element of the lattice can be written as a join of atoms. + :meth:`~FiniteLatticePoset.is_coatomic` | Return ``True`` if every element of the lattice can be written as a meet of coatoms. :meth:`~FiniteLatticePoset.is_geometric` | Return ``True`` if the lattice is atomic and upper semimodular. :meth:`~FiniteLatticePoset.is_complemented` | Return ``True`` if every element of the lattice has at least one complement. + :meth:`~FiniteLatticePoset.is_relatively_complemented` | Return ``True`` if every interval of the lattice is complemented. :meth:`~FiniteLatticePoset.is_pseudocomplemented` | Return ``True`` if every element of the lattice has a pseudocomplement. :meth:`~FiniteLatticePoset.is_supersolvable` | Return ``True`` if the lattice is supersolvable. :meth:`~FiniteLatticePoset.is_planar` | Return ``True`` if the lattice has an upward planar drawing. :meth:`~FiniteLatticePoset.is_dismantlable` | Return ``True`` if the lattice is dismantlable. :meth:`~FiniteLatticePoset.breadth` | Return the breadth of the lattice. + :meth:`~FiniteLatticePoset.is_vertically_decomposable` | Return ``True`` if the lattice is vertically decomposable. **Elements and sublattices** @@ -58,12 +61,16 @@ :widths: 30, 70 :delim: | + :meth:`~FiniteLatticePoset.atoms` | Return the list of elements covering the bottom element. + :meth:`~FiniteLatticePoset.coatoms` | Return the list of elements covered by the top element. + :meth:`~FiniteLatticePoset.double_irreducibles` | Return the list of double irreducible elements. :meth:`~FiniteLatticePoset.complements` | Return the list of complements of an element, or the dictionary of complements for all elements. - :meth:`~FiniteLatticePoset.pseudocomplement` | Return the pseudocomplement of an element. + :meth:`~FiniteMeetSemilattice.pseudocomplement` | Return the pseudocomplement of an element. :meth:`~FiniteLatticePoset.is_modular_element` | Return ``True`` if given element is modular in the lattice. :meth:`~FiniteLatticePoset.sublattice` | Return sublattice generated by list of elements. :meth:`~FiniteLatticePoset.maximal_sublattices` | Return maximal sublattices of the lattice. :meth:`~FiniteLatticePoset.frattini_sublattice` | Return the intersection of maximal sublattices of the lattice. + :meth:`~FiniteLatticePoset.vertical_decomposition` | Return the vertical decomposition of the lattice. **Miscellaneous** @@ -99,7 +106,7 @@ #################################################################################### -def MeetSemilattice(data, *args, **options): +def MeetSemilattice(data=None, *args, **options): r""" Construct a meet semi-lattice from various forms of input data. @@ -217,6 +224,13 @@ def meet(self, x, y=None): r""" Return the meet of given elements in the lattice. + INPUT: + + - ``x, y`` -- two elements of the (semi)lattice OR + - ``x`` -- a list or tuple of elements + + .. SEEALSO:: :meth:`sage.combinat.posets.lattices.FiniteJoinSemilattice.join()`. + EXAMPLES:: sage: D = Posets.DiamondPoset(5) @@ -238,14 +252,11 @@ def meet(self, x, y=None): sage: B4.meet([]) 15 - Test that this method also works for facade lattices:: + For non-facade lattices operator ``*`` works for meet:: - sage: L = LatticePoset([[1,2],[3],[3]], facade = True) - sage: L.meet(2, 3) - 2 - sage: L.meet(1, 2) + sage: L = Posets.PentagonPoset(facade=False) + sage: L(1)*L(2) 0 - """ if y is not None: # Handle basic case fast i, j = map(self._element_to_vertex, (x,y)) @@ -317,7 +328,7 @@ def pseudocomplement(self, element): #################################################################################### -def JoinSemilattice(data, *args, **options): +def JoinSemilattice(data=None, *args, **options): r""" Construct a join semi-lattice from various forms of input data. @@ -436,9 +447,10 @@ def join(self, x, y=None): INPUT: - - ``x, y`` - two elements of the (semi)lattice OR + - ``x, y`` -- two elements of the (semi)lattice OR + - ``x`` -- a list or tuple of elements - - ``x`` - a list or tuple of elements + .. SEEALSO:: :meth:`sage.combinat.posets.lattices.FiniteMeetSemilattice.meet()`. EXAMPLES:: @@ -461,14 +473,11 @@ def join(self, x, y=None): sage: B4.join([]) 0 - Test that this method also works for facade lattices:: - - sage: L = LatticePoset([[1,2],[3],[3]], facade = True) - sage: L.join(1, 0) - 1 - sage: L.join(1, 2) - 3 + For non-facade lattices operator ``+`` works for join:: + sage: L = Posets.PentagonPoset(facade=False) + sage: L(1)+L(2) + 4 """ if y is not None: # Handle basic case fast i, j = map(self._element_to_vertex, (x,y)) @@ -481,7 +490,7 @@ def join(self, x, y=None): #################################################################################### -def LatticePoset(data, *args, **options): +def LatticePoset(data=None, *args, **options): r""" Construct a lattice from various forms of input data. @@ -582,6 +591,94 @@ def _repr_(self): s += " with distinguished linear extension" return s + def atoms(self): + """ + Return the atoms of this lattice. + + An *atom* of a lattice is an element covering the bottom element. + + .. SEEALSO:: + + :meth:`coatoms` + + EXAMPLES:: + + sage: L = Posets.DivisorLattice(60) + sage: sorted(L.atoms()) + [2, 3, 5] + + TESTS:: + + sage: LatticePoset().atoms() + [] + sage: LatticePoset({0: []}).atoms() + [] + """ + if self.cardinality() == 0: + return [] + return self.upper_covers(self.bottom()) + + def coatoms(self): + """ + Return the co-atoms of this lattice. + + A *co-atom* of a lattice is an element covered by the top element. + + .. SEEALSO:: + + :meth:`atoms` + + EXAMPLES:: + + sage: L = Posets.DivisorLattice(60) + sage: sorted(L.coatoms()) + [12, 20, 30] + + TESTS:: + + sage: LatticePoset().coatoms() + [] + sage: LatticePoset({0: []}).coatoms() + [] + """ + if self.cardinality() == 0: + return [] + return self.lower_covers(self.top()) + + def double_irreducibles(self): + """ + Return the list of double irreducible elements of this lattice. + + A *double irreducible* element of a lattice is an element + covering and covered by exactly one element. In other words + it is neither a meet nor a join of any elements. + + .. SEEALSO:: + + :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.meet_irreducibles`, + :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.join_irreducibles` + + EXAMPLES:: + + sage: L = Posets.DivisorLattice(12) + sage: sorted(L.double_irreducibles()) + [3, 4] + + sage: L = Posets.BooleanLattice(3) + sage: L.double_irreducibles() + [] + + TESTS:: + + sage: LatticePoset().double_irreducibles() + [] + sage: Posets.ChainPoset(2).double_irreducibles() + [] + """ + H = self._hasse_diagram + return [self._vertex_to_element(e) for e in H + if H.in_degree(e) == 1 and H.out_degree(e) == 1] + def is_distributive(self): r""" Return ``True`` if the lattice is distributive, and ``False`` @@ -625,6 +722,81 @@ def is_complemented(self): """ return self._hasse_diagram.is_complemented_lattice() + def is_relatively_complemented(self): + """ + Return ``True`` if the lattice is relatively complemented, and + ``False`` otherwise. + + A lattice is relatively complemented if every interval of it + is a complemented lattice. + + EXAMPLES:: + + sage: L = LatticePoset({1: [2, 3, 4, 8], 2: [5, 6], 3: [5, 7], + ....: 4: [6, 7], 5: [9], 6: [9], 7: [9], 8: [9]}) + sage: L.is_relatively_complemented() + True + + sage: L = Posets.PentagonPoset() + sage: L.is_relatively_complemented() + False + + Relatively complemented lattice must be both atomic and coatomic. + Implication to other direction does not hold:: + + sage: L = LatticePoset({0: [1, 2, 3, 4, 5], 1: [6, 7], 2: [6, 8], + ....: 3: [7, 8, 9], 4: [9, 11], 5: [9, 10], + ....: 6: [10, 11], 7: [12], 8: [12], 9: [12], + ....: 10: [12], 11: [12]}) + sage: L.is_atomic() and L.is_coatomic() + True + sage: L.is_relatively_complemented() + False + + TESTS:: + + sage: [Posets.ChainPoset(i).is_relatively_complemented() for + ....: i in range(5)] + [True, True, True, False, False] + + Usually a lattice that is not relatively complemented contains elements + `l`, `m`, and `u` such that `r(l) + 1 = r(m) = r(u) - 1`, where `r` is + the rank function and `m` is the only element in the interval `[l, u]`. + We construct an example where this does not hold:: + + sage: B3 = Posets.BooleanLattice(3) + sage: B5 = Posets.BooleanLattice(5) + sage: B3 = B3.subposet([e for e in B3 if e not in [0, 7]]) + sage: B5 = B5.subposet([e for e in B5 if e not in [0, 31]]) + sage: B3 = B3.hasse_diagram() + sage: B5 = B5.relabel(lambda x: x+10).hasse_diagram() + sage: G = B3.union(B5) + sage: G.add_edge(B3.sources()[0], B5.neighbors_in(B5.sinks()[0])[0]) + sage: L = LatticePoset(Poset(G).with_bounds()) + sage: L.is_relatively_complemented() + False + """ + from sage.misc.flatten import flatten + from collections import Counter + + # Work directly with Hasse diagram + H = self._hasse_diagram + n = H.order() + if n < 3: + return True + + # Quick check: the lattice must be atomic and coatomic. + if H.out_degree(0) != H.in_degree().count(1): + return False + if H.in_degree(n-1) != H.out_degree().count(1): + return False + + for e1 in range(n-1): + C = Counter(flatten([H.neighbors_out(e2) for e2 in H.neighbors_out(e1)])) + if any(c == 1 and len(H.closed_interval(e1, e3)) == 3 for e3, c in C.iteritems()): + return False + return True + def breadth(self, certificate=False): r""" Return the breadth of the lattice. @@ -633,6 +805,10 @@ def breadth(self, certificate=False): any join of elements `x_1, x_2, \ldots, x_{n+1}` is join of a proper subset of `x_i`. + This can be also characterized by sublattices: a lattice + of breadth at least `n` contains a sublattice isomorphic to the + Boolean lattice of `2^n` elements. + INPUT: - ``certificate`` -- (boolean; default: ``False``) -- whether to @@ -651,12 +827,6 @@ def breadth(self, certificate=False): sage: B3.breadth(certificate=True) [1, 2, 4] - Smallest example of a lattice with breadth 4:: - - sage: L = LatticePoset(DiGraph('O]???w?K_@S?E_??Q?@_?D??I??W?B??@??C??O?@???')) - sage: L.breadth() - 4 - ALGORITHM: For a lattice to have breadth at least `n`, it must have an @@ -734,7 +904,7 @@ def complements(self, element=None): INPUT: - - ``element`` - an element of the lattice 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. @@ -832,20 +1002,25 @@ def is_pseudocomplemented(self): def is_atomic(self): r""" - Returns ``True`` if ``self`` is an atomic lattice and ``False`` otherwise. + Return ``True`` if the lattice is atomic, and ``False`` otherwise. A lattice is atomic if every element can be written as a join of atoms. EXAMPLES:: - sage: L = LatticePoset({0:[1,2,3],1:[4],2:[4],3:[4]}) + sage: L = LatticePoset({1: [2, 3, 4], 2: [5], 3:[5], 4:[6], 5:[6]}) sage: L.is_atomic() True - sage: L = LatticePoset({0:[1,2],1:[3],2:[3],3:[4]}) + sage: L = LatticePoset({0: [1, 2], 1: [3], 2: [3], 3:[4]}) sage: L.is_atomic() False + TESTS:: + + sage: LatticePoset({}).is_atomic() + True + NOTES: See [Sta97]_, Section 3.3 for a discussion of atomic lattices. @@ -856,17 +1031,45 @@ def is_atomic(self): Enumerative Combinatorics, Vol. 1. Cambridge University Press, 1997 + .. SEEALSO:: + + :meth:`~FiniteLatticePoset.is_coatomic` """ - bottom_element = self.bottom() - for x in self: - if x == bottom_element: - continue - lcovers = self.lower_covers(x) - if bottom_element in lcovers: - continue - if len(lcovers)<=1: - return False - return True + return (self.cardinality() == 0 or + self._hasse_diagram.out_degree(0) == + self._hasse_diagram.in_degree().count(1)) + + def is_coatomic(self): + r""" + Return ``True`` if the lattice is coatomic, and ``False`` otherwise. + + A lattice is coatomic if every element can be written as a meet + of coatoms; i.e. if the dual of the lattice is atomic. + + EXAMPLES:: + + sage: L = Posets.BooleanLattice(3) + sage: L.is_coatomic() + True + + sage: L = LatticePoset({1: [2], 2: [3, 4], 3: [5], 4:[5]}) + sage: L.is_coatomic() + False + + TESTS:: + + sage: LatticePoset({}).is_coatomic() + True + + .. SEEALSO:: + + :meth:`~FiniteLatticePoset.is_atomic` + """ + n = self.cardinality() + if n == 0: + return True + return (self._hasse_diagram.in_degree(n-1) == + self._hasse_diagram.out_degree().count(1)) def is_geometric(self): """ @@ -966,11 +1169,11 @@ def is_planar(self): REFERENCES: - .. [GW14] G. Gratzer and F. Wehrung, + .. [GW14] \G. Gratzer and F. Wehrung, Lattice Theory: Special Topics and Applications Vol. 1, Springer, 2014. - .. [Platt76] C. R. Platt, + .. [Platt76] \C. R. Platt, Planar lattices and planar graphs, Journal of Combinatorial Theory Series B, Vol 21, no. 1 (1976): 30-39. @@ -1087,7 +1290,9 @@ def is_upper_semimodular(self): 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`. + .. SEEALSO:: + + :meth:`is_modular` and :meth:`is_lower_semimodular`. See :wikipedia:`Semimodular_lattice` @@ -1129,7 +1334,9 @@ def is_lower_semimodular(self): 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`. + .. SEEALSO:: + + :meth:`is_modular` and :meth:`is_upper_semimodular`. See :wikipedia:`Semimodular_lattice` @@ -1193,12 +1400,20 @@ def is_supersolvable(self): ....: 9: [11], 10: [11]}) sage: L.is_supersolvable() False + + TESTS:: + + sage: LatticePoset({}).is_supersolvable() + True """ from sage.misc.cachefunc import cached_function if not self.is_ranked(): return False + if self.cardinality() == 0: + return True + H = self._hasse_diagram height = self.height() n = H.order() @@ -1225,6 +1440,102 @@ def is_modular_elt(a): next_.append(H.neighbor_in_iterator(cur)) return True + def vertical_decomposition(self, elements_only=False): + r""" + Return sublattices from the vertical decomposition of the lattice. + + Let `d_1, \ldots, d_n` be elements (excluding the top and bottom + elements) comparable to every element of the lattice. Let `b` + be the bottom element and `t` be the top element. This function + returns either a list `d_1, \ldots, d_n`, or the list of + intervals `[b, d_1], [d_1, d_2], \ldots, [d_{n-1}, d_n], [d_n, + t]` as lattices. + + Informally said, this returns the lattice split into parts at + every single-element "cutting point". + + INPUT: + + - ``elements_only`` - if ``True``, return the list of decomposing + elements as defined above; if ``False`` (the default), + return the list of sublattices so that the lattice is a + vertical composition of them. + + .. SEEALSO:: + + :meth:`is_vertically_decomposable` + + EXAMPLES: + + Number 6 is divided by 1, 2, and 3, and it divides 12, 18 and 36:: + + sage: L = LatticePoset( ([1, 2, 3, 6, 12, 18, 36], + ....: attrcall("divides")) ) + sage: parts = L.vertical_decomposition() + sage: [lat.list() for lat in parts] + [[1, 2, 3, 6], [6, 12, 18, 36]] + sage: L.vertical_decomposition(elements_only=True) + [6] + + TESTS:: + + sage: [Posets.ChainPoset(i).vertical_decomposition(elements_only=True) + ....: for i in range(5)] + [[], [], [], [1], [1, 2]] + """ + if self.cardinality() <= 2: + if not elements_only: + return [self] + else: + return [] + if elements_only: + return [self[e] for e in + self._hasse_diagram.vertical_decomposition(return_list=True)] + elms = ( [0] + + self._hasse_diagram.vertical_decomposition(return_list=True) + + [self.cardinality()-1] ) + n = len(elms) + result = [] + for i in range(n-1): + result.append(LatticePoset( + self.subposet([self[e] for e in range(elms[i], elms[i+1]+1)]))) + return result + + def is_vertically_decomposable(self): + r""" + Return ``True`` if the lattice is vertically decomposable, and + ``False`` otherwise. + + A lattice is vertically decomposable if it has an element that + is comparable to all elements and is neither the bottom nor + the top element. + + Informally said, a lattice is vertically decomposable if it + can be seen as two lattices "glued" by unifying the top + element of first lattice to the bottom element of second one. + + .. SEEALSO:: + + :meth:`vertical_decomposition` + + EXAMPLES:: + + sage: L = LatticePoset( ([1, 2, 3, 6, 12, 18, 36], + ....: attrcall("divides")) ) + sage: L.is_vertically_decomposable() + True + sage: Posets.TamariLattice(4).is_vertically_decomposable() + False + + TESTS:: + + sage: [Posets.ChainPoset(i).is_vertically_decomposable() for i in + ....: range(5)] + [False, False, False, True, True] + """ + # TODO: Make better example when compose_vertically() is done. + return self._hasse_diagram.vertical_decomposition() + def sublattice(self, elms): r""" Return the smallest sublattice containing elements on the given list. @@ -1242,12 +1553,6 @@ def sublattice(self, elms): sage: L = Posets.BooleanLattice(3) sage: L.sublattice([3,5,6,7]) Finite lattice containing 8 elements - - .. NOTE:: - - This is very unoptimal algorithm. Better one is described on - "Computing the sublattice of a lattice generated by a set of - elements" by K. Bertet and M. Morvan. Feel free to implement it. """ gens_remaining = set(elms) current_set = set() diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 0e16a16e90c..7be90e1f9bd 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -22,19 +22,26 @@ :meth:`~Posets.BooleanLattice` | Return the Boolean lattice on `2^n` elements. :meth:`~Posets.ChainPoset` | Return a chain on `n` elements. :meth:`~Posets.DiamondPoset` | Return the lattice of rank two on `n` elements. + :meth:`~Posets.DivisorLattice` | Return the divisor lattice of an integer. :meth:`~Posets.IntegerCompositions` | Return the poset of integer compositions of `n`. :meth:`~Posets.IntegerPartitions` | Return the poset of integer partitions of ``n``. + :meth:`~Posets.IntegerPartitionsDominanceOrder` | Return the poset of integer partitions on the integer `n` ordered by dominance. :meth:`~Posets.PentagonPoset` | Return the Pentagon poset. - :meth:`~Posets.RandomPoset` | Return a random poset on `n` vertices according to a probability `p`. + :meth:`~Posets.RandomPoset` | Return a random poset on `n` elements. :meth:`~Posets.RestrictedIntegerPartitions` | Return the poset of integer partitions of `n`, ordered by restricted refinement. + :meth:`~Posets.SetPartitions` | Return the poset of set partitions of the set `\{1,\dots,n\}`. :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.SymmetricGroupAbsoluteOrderPoset` | The poset of permutations with respect to absolute order. :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. :meth:`~Posets.TamariLattice` | Return the Tamari lattice. :meth:`~Posets.TetrahedralPoset` | Return the Tetrahedral poset with `n-1` layers based on the input colors. + :meth:`~Posets.YoungDiagramPoset` | Return the poset of cells in the Young diagram of a partition. + :meth:`~Posets.YoungsLattice` | Return Young's Lattice up to rank `n`. + :meth:`~Posets.YoungsLatticePrincipalOrderIdeal` | Return the principal order ideal of the partition `lam` in Young's Lattice. Constructions ------------- @@ -59,7 +66,8 @@ import sage.categories.posets from sage.combinat.permutation import Permutations, Permutation from sage.combinat.posets.posets import Poset, FinitePosets_n -from sage.combinat.posets.lattices import LatticePoset +from sage.combinat.posets.lattices import (LatticePoset, MeetSemilattice, + JoinSemilattice) from sage.graphs.digraph import DiGraph from sage.rings.integer import Integer @@ -154,12 +162,13 @@ def ChainPoset(n): Finite lattice containing 6 elements sage: C.linear_extension() [0, 1, 2, 3, 4, 5] - sage: for i in range(5): - ... for j in range(5): - ... if C.covers(C(i),C(j)) and j != i+1: - ... print "TEST FAILED" - TESTS: + TESTS:: + + sage: for i in range(5): + ....: for j in range(5): + ....: if C.covers(C(i),C(j)) and j != i+1: + ....: print "TEST FAILED" Check that :trac:`8422` is solved:: @@ -192,10 +201,13 @@ def AntichainPoset(n): sage: A = Posets.AntichainPoset(6); A Finite poset containing 6 elements + + TESTS:: + sage: for i in range(5): - ... for j in range(5): - ... if A.covers(A(i),A(j)): - ... print "TEST FAILED" + ....: for j in range(5): + ....: if A.covers(A(i),A(j)): + ....: print "TEST FAILED" TESTS: @@ -228,9 +240,9 @@ def PentagonPoset(facade = None): INPUT: - ``facade`` (boolean) -- whether to make the returned poset a - facade poset (see :mod:`sage.categories.facade_sets`). The + 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). + the :func:`~sage.combinat.posets.posets.Poset` constructor EXAMPLES:: @@ -263,12 +275,12 @@ def DiamondPoset(n, facade = None): INPUT: - - ``n`` - number of vertices, an integer at least 3. + - ``n`` -- number of elements, an integer at least 3 - ``facade`` (boolean) -- whether to make the returned poset a - facade poset (see :mod:`sage.categories.facade_sets`). The + 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). + the :func:`~sage.combinat.posets.posets.Poset` constructor EXAMPLES:: @@ -286,6 +298,47 @@ def DiamondPoset(n, facade = None): c[n-1] = [] return LatticePoset(c, facade = facade) + @staticmethod + def DivisorLattice(n, facade=None): + """ + Return the divisor lattice of an integer. + + Elements of the lattice are divisors of `n` and + `x < y` in the lattice if `x` divides `y`. + + INPUT: + + - ``n`` -- an integer + - ``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 + + EXAMPLES:: + + sage: P = Posets.DivisorLattice(12) + sage: sorted(P.cover_relations()) + [[1, 2], [1, 3], [2, 4], [2, 6], [3, 6], [4, 12], [6, 12]] + + sage: P = Posets.DivisorLattice(10, facade=False) + sage: P(2) < P(5) + False + + TESTS:: + + sage: Posets.DivisorLattice(1) + Finite lattice containing 1 elements + """ + from sage.arith.misc import divisors + try: + n = Integer(n) + except TypeError: + raise TypeError("number of elements must be an integer, not {0}".format(n)) + if n <= 0: + raise ValueError("n must be a positive integer") + return LatticePoset( (divisors(n), lambda x, y: y % x == 0), + facade=facade, linear_extension=True) + @staticmethod def IntegerCompositions(n): """ @@ -386,33 +439,68 @@ def lower_covers(partition): return Poset(H.reverse()) @staticmethod - def RandomPoset(n,p): + def IntegerPartitionsDominanceOrder(n): r""" - Generate a random poset on ``n`` vertices according to a + Return the poset of integer partitions on the integer `n` + ordered by dominance. + + That is, if `p=(p_1,\ldots,p_i)` and `q=(q_1,\ldots,q_j)` are + integer partitions of `n`, then `p` is greater than `q` if and + only if `p_1+\cdots+p_k > q_1+\cdots+q_k` for all `k`. + + INPUT: + + - ``n`` -- a positive integer + + EXAMPLES:: + + sage: P = Posets.IntegerPartitionsDominanceOrder(6); P + Finite lattice containing 11 elements + sage: P.cover_relations() + [[[1, 1, 1, 1, 1, 1], [2, 1, 1, 1, 1]], + [[2, 1, 1, 1, 1], [2, 2, 1, 1]], + [[2, 2, 1, 1], [2, 2, 2]], + [[2, 2, 1, 1], [3, 1, 1, 1]], + [[2, 2, 2], [3, 2, 1]], + [[3, 1, 1, 1], [3, 2, 1]], + [[3, 2, 1], [3, 3]], + [[3, 2, 1], [4, 1, 1]], + [[3, 3], [4, 2]], + [[4, 1, 1], [4, 2]], + [[4, 2], [5, 1]], + [[5, 1], [6]]] + """ + from sage.rings.semirings.non_negative_integer_semiring import NN + if n not in NN: + raise ValueError('n must be an integer') + from sage.combinat.partition import Partitions, Partition + return LatticePoset((Partitions(n), Partition.dominates)).dual() + + @staticmethod + def RandomPoset(n, p): + r""" + Generate a random poset on ``n`` elements according to a probability ``p``. INPUT: - - ``n`` - number of vertices, a non-negative integer + - ``n`` - number of elements, a non-negative integer - ``p`` - a probability, a real number between 0 and 1 (inclusive) OUTPUT: - A poset on ``n`` vertices. The construction decides to make an - ordered pair of vertices comparable in the poset with probability - ``p``, however a pair is not made comparable if it would violate - the defining properties of a poset, such as transitivity. - - So in practice, once the probability exceeds a small number the - generated posets may be very similar to a chain. So to create - interesting examples, keep the probability small, perhaps on the - order of `1/n`. + A poset on `n` elements. The probability `p` roughly measures + width/height of the output: `p=0` always generates an antichain, + `p=1` will return a chain. To create interesting examples, + keep the probability small, perhaps on the order of `1/n`. EXAMPLES:: - sage: Posets.RandomPoset(17,.15) - Finite poset containing 17 elements + sage: set_random_seed(0) # Results are reproducible + sage: P = Posets.RandomPoset(5, 0.3) + sage: P.cover_relations() + [[5, 4], [4, 2], [1, 2]] TESTS:: @@ -435,8 +523,12 @@ def RandomPoset(n,p): Traceback (most recent call last): ... ValueError: probability must be between 0 and 1, not -0.5 + + sage: Posets.RandomPoset(0, 0.5) + Finite poset containing 0 elements """ from sage.misc.prandom import random + try: n = Integer(n) except TypeError: @@ -450,15 +542,36 @@ def RandomPoset(n,p): if p < 0 or p> 1: raise ValueError("probability must be between 0 and 1, not {0}".format(p)) - D = DiGraph(loops=False,multiedges=False) + D = DiGraph(loops=False, multiedges=False) D.add_vertices(range(n)) for i in range(n): - for j in range(n): + for j in range(i+1, n): if random() < p: - D.add_edge(i,j) - if not D.is_directed_acyclic(): - D.delete_edge(i,j) - return Poset(D,cover_relations=False) + D.add_edge(i, j) + D.relabel(list(Permutations(n).random_element())) + return Poset(D, cover_relations=False) + + @staticmethod + def SetPartitions(n): + r""" + Return the lattice of set partitions of the set `\{1,\ldots,n\}` + ordered by refinement. + + INPUT: + + - ``n`` -- a positive integer + + EXAMPLES:: + + sage: Posets.SetPartitions(4) + Finite lattice containing 15 elements + """ + from sage.rings.semirings.non_negative_integer_semiring import NN + if n not in NN: + raise ValueError('n must be an integer') + from sage.combinat.set_partition import SetPartitions + S = SetPartitions(n) + return LatticePoset((S, S.is_less_than)) @staticmethod def SSTPoset(s,f=None): @@ -546,10 +659,10 @@ def StandardExample(n, facade=None): REFERENCES: - .. [Rosen] K. Rosen *Handbook of Discrete and Combinatorial + .. [Rosen] \K. Rosen *Handbook of Discrete and Combinatorial Mathematics* (1999), Chapman and Hall. - .. [Garg] V. Garg *Introduction to Lattice Theory with Computer + .. [Garg] \V. Garg *Introduction to Lattice Theory with Computer Science Applications* (2015), Wiley. TESTS:: @@ -723,7 +836,7 @@ def TetrahedralPoset(n, *colors, **labels): REFERENCES: - .. [Striker2011] J. Striker. *A unifying poset perpective on + .. [Striker2011] \J. Striker. *A unifying poset perpective on alternating sign matrices, plane partitions, Catalan objects, tournaments, and tableaux*, Advances in Applied Mathematics 46 (2011), no. 4, 583-609. :arXiv:`1408.5391` @@ -775,5 +888,178 @@ def TetrahedralPoset(n, *colors, **labels): import sage.combinat.tamari_lattices TamariLattice = staticmethod(sage.combinat.tamari_lattices.TamariLattice) + @staticmethod + def CoxeterGroupAbsoluteOrderPoset(W, use_reduced_words=True): + r""" + Return the poset of elements of a Coxeter group with respect + to absolute order. + + INPUT: + + - ``W`` -- a Coxeter group + - ``use_reduced_words`` -- boolean (default: ``True``); if + ``True``, then the elements are labeled by their lexicographically + minimal reduced word + + EXAMPLES:: + + sage: W = CoxeterGroup(['B', 3]) + sage: Posets.CoxeterGroupAbsoluteOrderPoset(W) + Finite poset containing 48 elements + + sage: W = WeylGroup(['B', 2], prefix='s') + sage: Posets.CoxeterGroupAbsoluteOrderPoset(W, False) + Finite poset containing 8 elements + """ + if use_reduced_words: + element_labels = {s: tuple(s.reduced_word()) for s in W} + return Poset({s: s.absolute_covers() for s in W}, element_labels) + return Poset({s: s.absolute_covers() for s in W}) + + @staticmethod + def SymmetricGroupAbsoluteOrderPoset(n, labels="permutations"): + r""" + Return the poset of permutations with respect to absolute order. + + INPUT: + + - ``n`` -- a positive integer + + - ``label`` -- (default: ``'permutations'``) a label for the elements + of the poset returned by the function; the options are + + * ``'permutations'`` - labels the elements are given by their + one-line notation + * ``'reduced_words'`` - labels the elements by the + lexicographically minimal reduced word + * ``'cycles'`` - labels the elements by their expression + as a product of cycles + + EXAMPLES:: + + sage: Posets.SymmetricGroupAbsoluteOrderPoset(4) + Finite poset containing 24 elements + sage: Posets.SymmetricGroupAbsoluteOrderPoset(3, labels="cycles") + Finite poset containing 6 elements + sage: Posets.SymmetricGroupAbsoluteOrderPoset(3, labels="reduced_words") + Finite poset containing 6 elements + """ + from sage.groups.perm_gps.permgroup_named import SymmetricGroup + W = SymmetricGroup(n) + if labels == "permutations": + element_labels = {s: s.tuple() for s in W} + if labels == "reduced_words": + element_labels = {s: tuple(s.reduced_word()) for s in W} + if labels == "cycles": + element_labels = {s: "".join(x for x in s.cycle_string() if x != ',') + for s in W} + + return Poset({s: s.absolute_covers() for s in W}, element_labels) + + @staticmethod + def YoungDiagramPoset(lam): + """ + Return the poset of cells in the Young diagram of a partition. + + INPUT: + + - ``lam`` -- a partition + + EXAMPLES:: + + sage: P = Posets.YoungDiagramPoset(Partition([2,2])); P + Finite meet-semilattice containing 4 elements + sage: P.cover_relations() + [[(0, 0), (0, 1)], [(0, 0), (1, 0)], [(0, 1), (1, 1)], [(1, 0), + (1, 1)]] + """ + def cell_leq(a, b): + """ + Nested function that returns `True` if the cell `a` is + to the left or above + the cell `b` in the (English) Young diagram. + """ + return ((a[0] == b[0] - 1 and a[1] == b[1]) + or (a[1] == b[1] - 1 and a[0] == b[0])) + return MeetSemilattice((lam.cells(), cell_leq), cover_relations=True) + + @staticmethod + def YoungsLattice(n): + """ + Return Young's Lattice up to rank `n`. + + In other words, the poset of partitions + of size less than or equal to `n` ordered by inclusion. + + INPUT: + + - ``n`` -- a positive integer + + EXAMPLES:: + + sage: P = Posets.YoungsLattice(3); P + Finite meet-semilattice containing 7 elements + sage: P.cover_relations() + [[[], [1]], + [[1], [1, 1]], + [[1], [2]], + [[1, 1], [1, 1, 1]], + [[1, 1], [2, 1]], + [[2], [2, 1]], + [[2], [3]]] + """ + from sage.combinat.partition import Partitions, Partition + from sage.misc.flatten import flatten + partitions = flatten([list(Partitions(i)) for i in range(n + 1)]) + return JoinSemilattice((partitions, Partition.contains)).dual() + + @staticmethod + def YoungsLatticePrincipalOrderIdeal(lam): + """ + Return the principal order ideal of the + partition `lam` in Young's Lattice. + + INPUT: + + - ``lam`` -- a partition + + EXAMPLES:: + + sage: P = Posets.YoungsLatticePrincipalOrderIdeal(Partition([2,2])) + sage: P + Finite lattice containing 6 elements + sage: P.cover_relations() + [[[], [1]], + [[1], [1, 1]], + [[1], [2]], + [[1, 1], [2, 1]], + [[2], [2, 1]], + [[2, 1], [2, 2]]] + """ + from sage.misc.flatten import flatten + from sage.combinat.partition import Partition + + def lower_covers(l): + """ + Nested function returning those partitions obtained + from the partition `l` by removing + a single cell. + """ + return [l.remove_cell(c[0], c[1]) for c in l.removable_cells()] + + def contained_partitions(l): + """ + Nested function returning those partitions contained in + the partition `l` + """ + if l == Partition([]): + return l + return flatten([l, [contained_partitions(m) + for m in lower_covers(l)]]) + + ideal = list(set(contained_partitions(lam))) + H = DiGraph(dict([[p, lower_covers(p)] for p in ideal])) + return LatticePoset(H.reverse()) + posets = Posets diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index f4c5bd96eb3..faad0cc5b1b 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -71,6 +71,7 @@ :meth:`~FinitePoset.is_graded` | Return ``True`` if all maximal chains of the poset has same length. :meth:`~FinitePoset.is_ranked` | Return ``True`` if the poset has a rank function. :meth:`~FinitePoset.is_rank_symmetric` | Return ``True`` if the poset is rank symmetric. + :meth:`~FinitePoset.is_series_parallel` | Return ``True`` if the poset can be built by ordinal sums and disjoint unions. :meth:`~FinitePoset.is_eulerian` | Return ``True`` if the poset is Eulerian. :meth:`~FinitePoset.is_incomparable_chain_free` | Return ``True`` if the poset is (m+n)-free. :meth:`~FinitePoset.is_slender` | Return ``True`` if the poset is slender. @@ -227,19 +228,15 @@ Classes and functions --------------------- """ + #***************************************************************************** -# Copyright (C) 2008 Peter Jipsen , -# Franco Saliola -# -# 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: +# Copyright (C) 2008 Peter Jipsen +# Copyright (C) 2008 Franco Saliola # +# 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/ #***************************************************************************** @@ -1917,7 +1914,7 @@ def relations_iterator(self, strict=False): sage: it = P.relations_iterator() sage: type(it) - sage: it.next(), it.next() + sage: next(it), next(it) ([1, 1], [1, 2]) sage: P = posets.PentagonPoset() @@ -2553,9 +2550,40 @@ def is_connected(self): """ return self._hasse_diagram.is_connected() + def is_series_parallel(self): + """ + Return ``True`` if the poset is series-parallel, and ``False`` + otherwise. + + A poset is *series-parallel* if it can be built up from one-element + posets using the operations of disjoint union and ordinal + sum. This is also called *N-free* property: every poset that is not + series-parallel contains a subposet isomorphic to the 4-element + N-shaped poset where `a > c, d` and `b > d`. + + See :wikipedia:`Series-parallel partial order`. + + EXAMPLES:: + + sage: VA = Poset({1: [2, 3], 4: [5], 6: [5]}) + sage: VA.is_series_parallel() + True + sage: big_N = Poset({1: [2, 4], 2: [3], 4:[7], 5:[6], 6:[7]}) + sage: big_N.is_series_parallel() + False + + TESTS:: + + sage: Poset().is_series_parallel() + True + """ + # TODO: Add series-parallel decomposition later. + N = Poset({0: [2, 3], 1: [3]}) + return not self.has_isomorphic_subposet(N) + def is_EL_labelling(self, f, return_raising_chains=False): r""" - Returns ``True`` if ``f`` is an EL labelling of ``self``. + Return ``True`` if ``f`` is an EL labelling of ``self``. A labelling `f` of the edges of the Hasse diagram of a poset is called an EL labelling (edge lexicographic labelling) if @@ -3021,7 +3049,7 @@ def lower_covers_iterator(self, x): sage: l0 = P.lower_covers_iterator(3) sage: type(l0) - sage: l0.next() + sage: next(l0) 2 """ for e in self._hasse_diagram.neighbor_in_iterator(self._element_to_vertex(x)): @@ -3057,7 +3085,6 @@ def cardinality(self): """ return Integer(self._hasse_diagram.order()) - from sage.misc.superseded import deprecated_function_alias def moebius_function(self,x,y): r""" Returns the value of the Möbius function of the poset on the @@ -3095,7 +3122,6 @@ def moebius_function(self,x,y): return self._hasse_diagram.moebius_function(i,j) mobius_function = deprecated_function_alias(19855, moebius_function) - 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 Möbius @@ -3499,7 +3525,7 @@ def antichains_iterator(self): sage: it = Posets.PentagonPoset().antichains_iterator(); it - sage: it.next(), it.next() + sage: next(it), next(it) ([], [4]) .. SEEALSO:: :meth:`antichains` @@ -3805,6 +3831,13 @@ def product(self, other): sage: Q.is_isomorphic(Posets.BooleanLattice(4)) True + One can also simply use `*`:: + + sage: P = Posets.ChainPoset(2) + sage: Q = Posets.ChainPoset(3) + sage: P*Q + Finite lattice containing 6 elements + TESTS:: sage: Poset({0:[1]}).product(Poset()) # Product with empty poset @@ -3832,6 +3865,8 @@ def product(self, other): constructor = Poset return constructor(self.hasse_diagram().cartesian_product(other.hasse_diagram())) + _mul_ = product + def disjoint_union(self, other, labels='pairs'): """ Return a poset isomorphic to disjoint union (also called direct diff --git a/src/sage/combinat/q_analogues.py b/src/sage/combinat/q_analogues.py index 69d66ab3f2f..cd2ad115b08 100644 --- a/src/sage/combinat/q_analogues.py +++ b/src/sage/combinat/q_analogues.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- r""" -q-Analogues +`q`-Analogues """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -16,6 +16,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division from sage.misc.cachefunc import cached_function from sage.misc.all import prod @@ -278,8 +280,8 @@ def q_binomial(n, k, q=None, algorithm='auto'): is_polynomial = isinstance(q, Polynomial) R = parent(q) - zero = R(0) - one = R(1) + zero = R.zero() + one = R.one() if not(0 <= k and k <= n): return zero @@ -290,7 +292,7 @@ def q_binomial(n, k, q=None, algorithm='auto'): if algorithm == 'auto': from sage.symbolic.ring import SR if is_polynomial: - if n <= 70 or k <= n/4: + if n <= 70 or k <= n // 4: algorithm = 'naive' else: algorithm = 'cyclo_polynomial' @@ -325,11 +327,12 @@ def q_binomial(n, k, q=None, algorithm='auto'): from sage.rings.polynomial.cyclotomic import cyclotomic_value return prod(cyclotomic_value(d,q) for d in range(2,n+1) - if (n/d).floor() != (k/d).floor() + ((n-k)/d).floor()) + if (n//d) != (k//d) + ((n-k)//d)) elif algorithm == 'cyclo_polynomial': return prod(R.cyclotomic_polynomial(d) for d in range(2,n+1) - if (n/d).floor() != (k/d).floor() + ((n-k)/d).floor()) + if (n//d) != (k//d) + ((n-k)//d)) + def gaussian_binomial(n, k, q=None, algorithm='auto'): r""" @@ -658,7 +661,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 Möbius 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/ribbon_tableau.py b/src/sage/combinat/ribbon_tableau.py index 8610f09689e..b90757933db 100644 --- a/src/sage/combinat/ribbon_tableau.py +++ b/src/sage/combinat/ribbon_tableau.py @@ -15,6 +15,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -29,6 +31,7 @@ import permutation import functools + class RibbonTableau(SkewTableau): r""" A ribbon tableau. @@ -106,20 +109,20 @@ def length(self): sage: RibbonTableau([[1,0],[2,0]]).length() 2 """ - if self.to_expr() == [[],[]]: + if self.to_expr() == [[], []]: return 0 tableau = self.to_expr()[1] l = 0 t = 0 for k in range(len(tableau)): - t += len( [ x for x in tableau[k] if x is not None and x > -1 ] ) - l += len( [ x for x in tableau[k] if x is not None and x > 0 ] ) + t += len([ x for x in tableau[k] if x is not None and x > -1]) + l += len([ x for x in tableau[k] if x is not None and x > 0]) if l == 0: return t else: - return t/l + return t // l def to_word(self): """ diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 137b1cac183..deab2d46083 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -324,7 +324,6 @@ def multiplicity(self): return Integer(1) mult = Integer(1) - CM = self.parent()._classical_ct.cartan_matrix() I = self.parent()._classical_ct.index_set() for a,m in self.up_root: p = self.weight[a] @@ -848,7 +847,7 @@ def _children_iter(self, node): L = [range(val + 1) for val in node.up_root.to_vector()] it = itertools.product(*L) - it.next() # First element is the zero element + next(it) # First element is the zero element for root in it: # Convert the list to an honest root in the root space converted_root = RS.sum_of_terms([[I[i], val] for i, val in enumerate(root)]) diff --git a/src/sage/combinat/rigged_configurations/rc_crystal.py b/src/sage/combinat/rigged_configurations/rc_crystal.py index 1c31feb3a0b..839b7bb4ba2 100644 --- a/src/sage/combinat/rigged_configurations/rc_crystal.py +++ b/src/sage/combinat/rigged_configurations/rc_crystal.py @@ -246,8 +246,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): """ vac_num = self._wt[self.index_set()[a]] - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(i) + for b in range(self._cartan_matrix.ncols()): + vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) return vac_num @@ -331,10 +331,11 @@ def _calc_vacancy_number(self, partitions, a, i, **options): vac_num = self._wt[ia] gamma = self._folded_ct.scaling_factors() - for b, value in enumerate(self._cartan_matrix.row(a)): + g = gamma[ia] + for b in range(self._cartan_matrix.ncols()): ib = I[b] - q = partitions[b].get_num_cells_to_column(gamma[ia]*i, gamma[ib]) - vac_num -= value * q / gamma[ib] + q = partitions[b].get_num_cells_to_column(g*i, gamma[ib]) + vac_num -= self._cartan_matrix[a,b] * q / gamma[ib] return vac_num diff --git a/src/sage/combinat/rigged_configurations/rc_infinity.py b/src/sage/combinat/rigged_configurations/rc_infinity.py index e9cf187af27..7c57cb9e746 100644 --- a/src/sage/combinat/rigged_configurations/rc_infinity.py +++ b/src/sage/combinat/rigged_configurations/rc_infinity.py @@ -240,8 +240,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): """ vac_num = 0 - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(i) + for b in range(self._cartan_matrix.ncols()): + vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) return vac_num @@ -334,10 +334,11 @@ def _calc_vacancy_number(self, partitions, a, i): vac_num = 0 gamma = self._folded_ct.scaling_factors() - for b, value in enumerate(self._cartan_matrix.row(a)): + g = gamma[ia] + for b in range(self._cartan_matrix.ncols()): ib = I[b] - q = partitions[b].get_num_cells_to_column(gamma[ia]*i, gamma[ib]) - vac_num -= value * q / gamma[ib] + q = partitions[b].get_num_cells_to_column(g*i, gamma[ib]) + vac_num -= self._cartan_matrix[a,b] * q / gamma[ib] return vac_num @@ -397,7 +398,7 @@ def to_virtual(self, rc): partitions[k] = [row_len*gamma[a] for row_len in rp._list] riggings[k] = [rig_val*gamma[a] for rig_val in rp.rigging] return self.virtual.element_class(self.virtual, partition_list=partitions, - rigging_list=riggings) + rigging_list=riggings) def from_virtual(self, vrc): """ diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index bbfe85b94a7..a40d7f79f2f 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -202,14 +202,14 @@ def __init__(self, parent, rigged_partitions=[], **options): nu.append(RiggedPartition()) else: if len(data) != n: # otherwise n should be equal to the number of tableaux - raise ValueError("Incorrect number of partitions") + raise ValueError("incorrect number of partitions") nu = [] if "rigging_list" in options: rigging_data = options["rigging_list"] if len(rigging_data) != n: - raise ValueError("Incorrect number of riggings") + raise ValueError("incorrect number of riggings") for i in range(n): nu.append(RiggedPartition(tuple(data[i]), \ @@ -586,7 +586,7 @@ def e(self, a): new_partitions.append(RiggedPartition(new_list, new_rigging, new_vac_nums)) - ret_RC = self.__class__(self.parent(), new_partitions) + ret_RC = self.__class__(self.parent(), new_partitions, use_vacancy_numbers=True) nu = ret_RC.nu() if k != 1 and not set_vac_num: # If we did not remove a row nor found another row of length k-1 # Update that row's vacancy number @@ -618,7 +618,7 @@ def _generate_partition_e(self, a, b, k): """ # Check to make sure we will do something - if not self.parent()._cartan_matrix[a][b]: + if not self.parent()._cartan_matrix[a,b]: return self[b] new_list = self[b][:] @@ -626,7 +626,7 @@ def _generate_partition_e(self, a, b, k): new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = self.parent()._cartan_matrix[a][b] + value = self.parent()._cartan_matrix[a,b] for i in range(len(new_vac_nums)): if new_list[i] < k: break @@ -634,7 +634,7 @@ def _generate_partition_e(self, a, b, k): new_vac_nums[i] += value new_rigging[i] += value - return(RiggedPartition(new_list, new_rigging, new_vac_nums)) + return RiggedPartition(new_list, new_rigging, new_vac_nums) def f(self, a): r""" @@ -735,7 +735,7 @@ def f(self, a): # Note that we do not need to sort the rigging since if there was a # smaller rigging in a larger row, then `k` would be larger. - return self.__class__(self.parent(), new_partitions) + return self.__class__(self.parent(), new_partitions, use_vacancy_numbers=True) def _generate_partition_f(self, a, b, k): r""" @@ -761,7 +761,7 @@ def _generate_partition_f(self, a, b, k): """ # Check to make sure we will do something - if not self.parent()._cartan_matrix[a][b]: + if not self.parent()._cartan_matrix[a,b]: return self[b] new_list = self[b][:] @@ -769,7 +769,7 @@ def _generate_partition_f(self, a, b, k): new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = self.parent()._cartan_matrix[a][b] + value = self.parent()._cartan_matrix[a,b] for i in range(len(new_vac_nums)): if new_list[i] <= k: break @@ -777,7 +777,7 @@ def _generate_partition_f(self, a, b, k): new_vac_nums[i] -= value new_rigging[i] -= value - return(RiggedPartition(new_list, new_rigging, new_vac_nums)) + return RiggedPartition(new_list, new_rigging, new_vac_nums) def epsilon(self, a): r""" diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index ffef31f723b..f2b6f677691 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -213,7 +213,7 @@ class RiggedConfigurations(UniqueRepresentation, Parent): REFERENCES: - .. [HKOTT2002] G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. + .. [HKOTT2002] \G. Hatayama, A. Kuniba, M. Okado, T. Takagi, Z. Tsuboi. Paths, Crystals and Fermionic Formulae. Prog. Math. Phys. **23** (2002) Pages 205-272. @@ -796,8 +796,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == self._rc_index[a]: vac_num += min(dim[1], i) - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(i) + for b in range(self._cartan_matrix.ncols()): + vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) return vac_num @@ -1158,8 +1158,10 @@ def _calc_vacancy_number(self, partitions, a, i, **options): vac_num += min(dim[1], i) gamma = self._folded_ct.scaling_factors() - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(gamma[a+1]*i, gamma[b+1]) // gamma[b+1] + for b in range(self._cartan_matrix.ncols()): + vac_num -= (self._cartan_matrix[a,b] + * partitions[b].get_num_cells_to_column(gamma[a+1]*i, gamma[b+1]) + // gamma[b+1]) return vac_num @@ -1337,18 +1339,15 @@ def to_virtual(self, rc): gamma = self._folded_ct.scaling_factors() sigma = self._folded_ct.folding_orbit() n = self._folded_ct._folding.classical().rank() - partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n # +/- 1 for indexing - for a in range(len(rc)): + partitions = [None] * n + for a,rp in enumerate(rc): + g = gamma[a+1] for i in sigma[a+1]: - partitions[i-1] = [row_len*gamma[a+1] for row_len in rc[a]._list] - riggings[i-1] = [rig_val*gamma[a+1] for rig_val in rc[a].rigging] - vac_nums[i-1] = [vac_num*gamma[a+1] for vac_num in rc[a].vacancy_numbers] - return self.virtual.element_class(self.virtual, partition_list=partitions, - rigging_list=riggings, - vacancy_numbers_list=vac_nums) + partitions[i-1] = RiggedPartition([row_len*g for row_len in rp._list], + [rig_val*g for rig_val in rp.rigging], + [vac_num*g for vac_num in rp.vacancy_numbers]) + return self.virtual.element_class(self.virtual, partitions, use_vacancy_numbers=True) def from_virtual(self, vrc): """ @@ -1376,16 +1375,14 @@ def from_virtual(self, vrc): sigma = self._folded_ct.folding_orbit() n = self._cartan_type.classical().rank() partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n # +/- 1 for indexing for a in range(n): - index = sigma[a+1][0]-1 - partitions[a] = [row_len//gamma[a+1] for row_len in vrc[index]._list] - riggings[a] = [rig_val//gamma[a+1] for rig_val in vrc[index].rigging] - vac_nums[a] = [vac_val//gamma[a+1] for vac_val in vrc[index].vacancy_numbers] - return self.element_class(self, partition_list=partitions, - rigging_list=riggings, vacancy_numbers_list=vac_nums) + rp = vrc[sigma[a+1][0] - 1] + g = gamma[a+1] + partitions[a] = RiggedPartition([row_len//g for row_len in rp._list], + [rig_val//g for rig_val in rp.rigging], + [vac_val//g for vac_val in rp.vacancy_numbers]) + return self.element_class(self, partitions, use_vacancy_numbers=True) def _test_virtual_vacancy_numbers(self, **options): """ @@ -1506,8 +1503,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): vac_num += min(dim[1], i) gamma = self._folded_ct.scaling_factors() - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(i) // gamma[b+1] + for b in range(self._cartan_matrix.ncols()): + vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) // gamma[b+1] return vac_num @@ -1543,17 +1540,14 @@ def to_virtual(self, rc): sigma = self._folded_ct.folding_orbit() n = self._folded_ct._folding.classical().rank() partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n - # +/- 1 for indexing - for a in range(len(rc)): + for a,rp in enumerate(rc): + g = gamma[a+1] for i in sigma[a+1]: - partitions[i-1] = [row_len for row_len in rc[a]._list] - riggings[i-1] = [rig_val*gamma[a+1] for rig_val in rc[a].rigging] - vac_nums[i-1] = [vac_num*gamma[a+1] for vac_num in rc[a].vacancy_numbers] - return self.virtual.element_class(self.virtual, partition_list=partitions, - rigging_list=riggings, - vacancy_numbers_list=vac_nums) + partitions[i-1] = RiggedPartition([row_len for row_len in rp._list], + [rig_val*g for rig_val in rp.rigging], + [vac_num*g for vac_num in rp.vacancy_numbers]) + return self.virtual.element_class(self.virtual, partitions, use_vacancy_numbers=True) + def from_virtual(self, vrc): """ @@ -1582,16 +1576,15 @@ def from_virtual(self, vrc): sigma = self._folded_ct.folding_orbit() n = self._cartan_type.classical().rank() partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n # +/- 1 for indexing for a in range(n): - index = sigma[a+1][0]-1 - partitions[index] = [row_len for row_len in vrc[index]._list] - riggings[index] = [rig_val//gamma[a+1] for rig_val in vrc[index].rigging] - vac_nums[a] = [vac_val//gamma[a+1] for vac_val in vrc[index].vacancy_numbers] - return self.element_class(self, partition_list=partitions, - rigging_list=riggings, vacancy_numbers_list=vac_nums) + rp = vrc[sigma[a+1][0] - 1] + g = gamma[a+1] + partitions[a] = RiggedPartition([row_len for row_len in rp._list], + [rig_val//g for rig_val in rp.rigging], + [vac_val//g for vac_val in rp.vacancy_numbers]) + return self.element_class(self, partitions, use_vacancy_numbers=True) + class RCTypeA2Dual(RCTypeA2Even): r""" @@ -1658,8 +1651,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): if dim[0] == self._rc_index[a]: vac_num += min(dim[1], i) - for b, value in enumerate(self._cartan_matrix.row(a)): - vac_num -= value * partitions[b].get_num_cells_to_column(i) / 2 + for b in range(self._cartan_matrix.ncols()): + vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) / 2 return vac_num @@ -1868,17 +1861,12 @@ def to_virtual(self, rc): sigma = self._folded_ct.folding_orbit() n = self._folded_ct._folding.classical().rank() partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n - # +/- 1 for indexing - for a in range(len(rc)): + for a,rp in enumerate(rc): + g = gammatilde[a+1] for i in sigma[a+1]: - partitions[i-1] = [row_len for row_len in rc[a]._list] - riggings[i-1] = [rig_val*gammatilde[a+1] for rig_val in rc[a].rigging] - vac_nums[i-1] = [vac_num for vac_num in rc[a].vacancy_numbers] - return self.virtual.element_class(self.virtual, partition_list=partitions, - rigging_list=riggings, - vacancy_numbers_list=vac_nums) + partitions[i-1] = RiggedPartition([row_len for row_len in rp._list], + [rig_val*g for rig_val in rp.rigging]) + return self.virtual.element_class(self.virtual, partitions) def from_virtual(self, vrc): """ @@ -1908,15 +1896,13 @@ def from_virtual(self, vrc): sigma = self._folded_ct.folding_orbit() n = self._cartan_type.classical().rank() partitions = [None] * n - riggings = [None] * n - vac_nums = [None] * n # +/- 1 for indexing for a in range(n): - index = sigma[a+1][0]-1 - partitions[index] = [row_len for row_len in vrc[index]._list] - riggings[index] = [rig_val/gammatilde[a+1] for rig_val in vrc[index].rigging] - vac_nums[a] = [vac_val for vac_val in vrc[index].vacancy_numbers] - return self.element_class(self, partition_list=partitions, - rigging_list=riggings, vacancy_numbers_list=vac_nums) + rp = vrc[sigma[a+1][0] - 1] + g = gammatilde[a+1] + partitions[a] = RiggedPartition([row_len for row_len in rp._list], + [rig_val/g for rig_val in rp.rigging]) + return self.element_class(self, partitions) Element = KRRCTypeA2DualElement + diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index 6a9be3b1b0c..d04920d008b 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -61,6 +61,16 @@ The categories :class:`CoxeterGroups` and :class:`WeylGroups` +Finite reflection groups +------------------------ + +- :ref:`sage.combinat.root_system.reflection_group_complex` +- :ref:`sage.combinat.root_system.reflection_group_real` + +.. SEEALSO:: + + The category :class:`~sage.categories.complex_reflection_groups.ComplexReflectionGroups` + Representation theory --------------------- diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index a4a4ecc0c33..3e68f6a6957 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -10,6 +10,7 @@ from coxeter_type import CoxeterType from root_system import RootSystem, WeylDim from weyl_group import WeylGroup, WeylGroupElement +lazy_import('sage.combinat.root_system.reflection_group_real', 'ReflectionGroup') lazy_import('sage.combinat.root_system.extended_affine_weyl_group', 'ExtendedAffineWeylGroup') from coxeter_group import CoxeterGroup from weyl_characters import WeylCharacterRing, WeightRing diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index 049f277e421..bb10fb25dd2 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -2115,7 +2115,7 @@ def c(self): REFERENCES: - .. [FSS07] G. Fourier, A. Schilling, and M. Shimozono, + .. [FSS07] \G. Fourier, A. Schilling, and M. Shimozono, Demazure structure inside Kirillov-Reshetikhin crystals, J. Algebra, Vol. 309, (2007), p. 386-404 http://arxiv.org/abs/math/0605451 @@ -2282,7 +2282,7 @@ def translation_factors(self): REFERENCES: - .. [HST09] F. Hivert, A. Schilling, and N. M. Thiery, + .. [HST09] \F. Hivert, A. Schilling, and N. M. Thiery, Hecke group algebras as quotients of affine Hecke algebras at level 0, JCT A, Vol. 116, (2009) p. 844-863 http://arxiv.org/abs/0804.3781 diff --git a/src/sage/combinat/root_system/coxeter_group.py b/src/sage/combinat/root_system/coxeter_group.py index b1d4aa5b2a6..2f109c97fdf 100644 --- a/src/sage/combinat/root_system/coxeter_group.py +++ b/src/sage/combinat/root_system/coxeter_group.py @@ -10,9 +10,6 @@ #***************************************************************************** from sage.misc.cachefunc import cached_function, cached_method -from sage.categories.category import Category -from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups -from sage.categories.finite_permutation_groups import FinitePermutationGroups from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.combinat.root_system.weyl_group import WeylGroup from sage.structure.unique_representation import UniqueRepresentation @@ -75,11 +72,13 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No We now use the ``implementation`` option:: - sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - chevie - sage: W # optional - chevie + sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - gap3 + sage: W # optional - gap3 Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)] - sage: W.category() # optional - chevie - Join of Category of finite permutation groups and Category of finite coxeter groups + sage: W.category() # optional - gap3 + Join of Category of finite permutation groups + and Category of finite coxeter groups + and Category of well generated finite irreducible complex reflection groups sage: W = CoxeterGroup(["A",2], implementation="matrix") sage: W @@ -104,6 +103,9 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No ... NotImplementedError: Coxeter group of type ['A', 4, 1] as permutation group not implemented + sage: W = CoxeterGroup(["A",4], implementation="chevie"); W # optional - gap3 + Irreducible real reflection group of rank 4 and type A4 + We use the different options for the "reflection" implementation:: sage: W = CoxeterGroup(["H",3], implementation="reflection", base_ring=RR) @@ -122,7 +124,7 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No sage: W = groups.misc.CoxeterGroup(["H",3]) """ - if implementation not in ["permutation", "matrix", "coxeter3", "reflection", None]: + if implementation not in ["permutation", "matrix", "coxeter3", "reflection", "chevie", None]: raise ValueError("invalid type implementation") try: @@ -149,6 +151,9 @@ def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=No if cartan_type.is_crystallographic(): return WeylGroup(cartan_type) return CoxeterMatrixGroup(cartan_type, base_ring, index_set) + elif implementation == "chevie": + from sage.combinat.root_system.reflection_group_real import ReflectionGroup + return ReflectionGroup(data, index_set=index_set) raise NotImplementedError("Coxeter group of type {} as {} group not implemented".format(cartan_type, implementation)) @@ -181,9 +186,9 @@ def __classcall__(cls, cartan_type): TESTS:: sage: from sage.combinat.root_system.coxeter_group import CoxeterGroupAsPermutationGroup - sage: W1 = CoxeterGroupAsPermutationGroup(CartanType(["H",3])) # optional - chevie - sage: W2 = CoxeterGroupAsPermutationGroup(["H",3]) # optional - chevie - sage: W1 is W2 # optional - chevie + sage: W1 = CoxeterGroupAsPermutationGroup(CartanType(["H",3])) # optional - gap3 + sage: W2 = CoxeterGroupAsPermutationGroup(["H",3]) # optional - gap3 + sage: W1 is W2 # optional - gap3 True """ cartan_type = CartanType(cartan_type) @@ -198,11 +203,11 @@ def __init__(self, cartan_type): TESTS:: sage: from sage.combinat.root_system.coxeter_group import CoxeterGroupAsPermutationGroup - sage: W = CoxeterGroupAsPermutationGroup(CartanType(["H",3])) # optional - chevie - sage: TestSuite(W).run() # optional - chevie + sage: W = CoxeterGroupAsPermutationGroup(CartanType(["H",3])) # optional - gap3 + sage: TestSuite(W).run() # optional - gap3 """ - assert cartan_type.is_finite() - assert cartan_type.is_irreducible() + if not (cartan_type.is_finite() and cartan_type.is_irreducible()): + raise ValueError("must be a finite irreducible type") self._semi_simple_rank = cartan_type.n from sage.interfaces.gap3 import gap3 gap3._start() @@ -212,8 +217,11 @@ def __init__(self, cartan_type): N = self._gap_group.__getattr__("N").sage() generators = [str(x) for x in self._gap_group.generators] self._is_positive_root = [None] + [ True ] * N + [False]*N - PermutationGroup_generic.__init__(self, gens = generators, - category = Category.join([FinitePermutationGroups(), FiniteCoxeterGroups()])) + from sage.categories.finite_permutation_groups import FinitePermutationGroups + from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups + PermutationGroup_generic.__init__(self, gens=generators, + category=(FinitePermutationGroups(), + FiniteCoxeterGroups().Irreducible())) def _element_class(self): """ @@ -222,8 +230,9 @@ def _element_class(self): TESTS:: - sage: W = CoxeterGroup(["H",3]) # optional - chevie - sage: W._element_class() is W.element_class # optional - chevie + sage: from sage.combinat.root_system.coxeter_group import CoxeterGroupAsPermutationGroup + sage: W = CoxeterGroupAsPermutationGroup("H3") # optional - gap3 + sage: W._element_class() is W.element_class # optional - gap3 True """ return self.element_class @@ -234,8 +243,8 @@ def index_set(self): EXAMPLES:: - sage: W = CoxeterGroup(["H",3], implementation = "permutation") # optional - chevie - sage: W.index_set() # optional - chevie + sage: W = CoxeterGroup(["H",3], implementation = "permutation") # optional - gap3 + sage: W.index_set() # optional - gap3 [1, 2, 3] """ @@ -246,29 +255,53 @@ def reflection(self, i): """ Returns the `i`-th reflection of ``self``. - For `i` in `1,\dots,n`, this gives the `i`-th simple + For `i` in `1, \ldots, n`, this gives the `i`-th simple reflection of ``self``. EXAMPLES:: - sage: W = CoxeterGroup(["H",3], implementation = "permutation") # optional - chevie - sage: W.simple_reflection(1) # optional - chevie + sage: W = CoxeterGroup(["H",3], implementation="permutation") # optional - gap3 + sage: W.simple_reflection(1) # optional - gap3 (1,16)(2,5)(4,7)(6,9)(8,10)(11,13)(12,14)(17,20)(19,22)(21,24)(23,25)(26,28)(27,29) - sage: W.simple_reflection(2) # optional - chevie + sage: W.simple_reflection(2) # optional - gap3 (1,4)(2,17)(3,6)(5,7)(9,11)(10,12)(14,15)(16,19)(18,21)(20,22)(24,26)(25,27)(29,30) - sage: W.simple_reflection(3) # optional - chevie + sage: W.simple_reflection(3) # optional - gap3 (2,6)(3,18)(4,8)(5,9)(7,10)(11,12)(13,14)(17,21)(19,23)(20,24)(22,25)(26,27)(28,29) - sage: W.reflection(4) # optional - chevie + sage: W.reflection(4) # optional - gap3 (1,5)(2,22)(3,11)(4,19)(7,17)(8,12)(9,13)(10,15)(16,20)(18,26)(23,27)(24,28)(25,30) - sage: W.reflection(5) # optional - chevie + sage: W.reflection(5) # optional - gap3 (1,22)(2,4)(3,9)(5,20)(6,13)(7,16)(8,14)(12,15)(17,19)(18,24)(21,28)(23,29)(27,30) - sage: W.reflection(6) # optional - chevie + sage: W.reflection(6) # optional - gap3 (1,8)(2,18)(3,17)(5,12)(6,21)(7,11)(9,10)(13,15)(16,23)(20,27)(22,26)(24,25)(28,30) """ return self(str(self._gap_group.Reflection(i))) simple_reflection = reflection + @cached_method + def degrees(self): + r""" + Return the degrees of ``self`` ordered within each irreducible + component of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.coxeter_group import CoxeterGroupAsPermutationGroup + sage: W = CoxeterGroupAsPermutationGroup("A3") # optional - gap3 + sage: W.degrees() # optional - gap3 + (2, 3, 4) + sage: W = CoxeterGroupAsPermutationGroup("H3") # optional - gap3 + sage: W.degrees() # optional - gap3 + (2, 6, 10) + """ + if self.is_irreducible(): + try: + return tuple(sorted(self._gap_group.degrees.sage())) + except AttributeError: + return tuple(sorted(self._gap_group.ReflectionDegrees().sage())) + else: + return sum([comp.degrees() for comp in self.irreducible_components()],tuple()) + class Element(PermutationGroupElement): def has_descent(self, i, side = 'right', positive=False): @@ -312,11 +345,11 @@ def has_left_descent(self, i): EXAMPLES:: - sage: W = CoxeterGroup(["A",3], implementation = "permutation") # optional - chevie - sage: s = W.simple_reflections() # optional - chevie - sage: (s[1]*s[2]).has_left_descent(1) # optional - chevie + sage: W = CoxeterGroup(["A",3], implementation = "permutation") # optional - gap3 + sage: s = W.simple_reflections() # optional - gap3 + sage: (s[1]*s[2]).has_left_descent(1) # optional - gap3 True - sage: (s[1]*s[2]).has_left_descent(2) # optional - chevie + sage: (s[1]*s[2]).has_left_descent(2) # optional - gap3 False """ return not self.parent()._is_positive_root[self(i)] @@ -328,8 +361,9 @@ def __cmp__(self, other): EXAMPLES:: - sage: W = CoxeterGroup(["B",3], implementation = "permutation") # optional - chevie - sage: cmp(W.an_element(), W.one()) # optional - chevie + sage: W = CoxeterGroup(["B",3], implementation = "permutation") # optional - gap3 + sage: cmp(W.an_element(), W.one()) # optional - gap3 1 """ return super(CoxeterGroupAsPermutationGroup.Element, self).__cmp__(other) + diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index 174821ecc45..eb08ce1fb43 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -678,7 +678,7 @@ def __iter__(self): EXAMPLES:: sage: CM = CoxeterMatrix([[1,8],[8,1]]) - sage: CM.__iter__().next() + sage: next(CM.__iter__()) (1, 8) """ return iter(self._matrix) @@ -764,7 +764,7 @@ def _matrix_(self, R=None): sage: matrix(CM) [ 1 -3] [-3 1] - sage: matrix(CM,RR) + sage: matrix(RR, CM) [ 1.00000000000000 -3.00000000000000] [-3.00000000000000 1.00000000000000] """ @@ -1052,6 +1052,22 @@ def recognize_coxeter_type_from_matrix(coxeter_matrix, index_set): ....: recognized_type = recognize_coxeter_type_from_matrix(relabeled_matrix, relabelling_perm) ....: if C.is_finite() or C.is_affine(): ....: assert recognized_type == C.coxeter_type() + + We check the rank 2 cases (:trac:`20419`):: + + sage: for i in range(2, 10): + ....: M = matrix([[1,i],[i,1]]) + ....: CoxeterMatrix(M).coxeter_type() + Coxeter type of A1xA1 relabelled by {1: 2} + Coxeter type of ['A', 2] + Coxeter type of ['B', 2] + Coxeter type of ['I', 5] + Coxeter type of ['G', 2] + Coxeter type of ['I', 7] + Coxeter type of ['I', 8] + Coxeter type of ['I', 9] + sage: CoxeterMatrix(matrix([[1,-1],[-1,1]]), index_set=[0,1]).coxeter_type() + Coxeter type of ['A', 1, 1] """ # First, we build the Coxeter graph of the group without the edge labels n = ZZ(coxeter_matrix.nrows()) @@ -1070,8 +1086,10 @@ def recognize_coxeter_type_from_matrix(coxeter_matrix, index_set): if r == 2: # Type B2, G2, or I_2(p) e = S.edge_labels()[0] if e == 3: # Can't be 2 because it is connected - ct = CoxeterType(['B',2]) + ct = CoxeterType(['A',2]) elif e == 4: + ct = CoxeterType(['B',2]) + elif e == 6: ct = CoxeterType(['G',2]) elif e > 0 and e < float('inf'): # Remaining non-affine types ct = CoxeterType(['I',e]) diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index aa5bfbd67ec..6810e44854c 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -77,7 +77,7 @@ class HeckeAlgebraRepresentation(WithEqualityById, SageObject): REFERENCES: - .. [HST2008] F. Hivert, A. Schilling, N. Thiery, + .. [HST2008] \F. Hivert, A. Schilling, N. Thiery, Hecke group algebras as quotients of affine Hecke algebras at level 0, Journal of Combinatorial Theory, Series A 116 (2009) 844-863 ( arXiv:0804.3781 [math.RT] ) """ diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index 0101351d9b2..827318a9c89 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -342,15 +342,15 @@ class NonSymmetricMacdonaldPolynomials(CherednikOperatorsEigenvectors): REFERENCES: - .. [Haiman ICM] M. Haiman, Cherednik algebras, Macdonald polynomials and combinatorics, + .. [HaimanICM] \M. Haiman, Cherednik algebras, Macdonald polynomials and combinatorics, Proceedings of the International Congress of Mathematicians, Madrid 2006, Vol. III, 843-872. - .. [HHL06] J. Haglund, M. Haiman and N. Loehr, + .. [HHL06] \J. Haglund, M. Haiman and N. Loehr, A combinatorial formula for nonsymmetric Macdonald polynomials, Amer. J. Math. 130, No. 2 (2008), 359-383. - .. [LNSSS12] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, + .. [LNSSS12] \C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, A uniform model for Kirillov-Reshetikhin crystals I: Lifting the parabolic quantum Bruhat graph, preprint arXiv.1211.2042 [math.QA] diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index bfffa519a74..d9047e8b36e 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -67,12 +67,12 @@ class PieriFactors(UniqueRepresentation, Parent): REFERENCES: - .. [FoSta1994] S. Fomin, R. Stanley. Schubert polynomials and the nilCoxeter algebra. Advances in Math., 1994. - .. [BH1994] S. Billey, M. Haiman. Schubert polynomials for the classical groups. J. Amer. Math. Soc., 1994. - .. [TKLam1996] T.K. Lam. B and D analogues of stable Schubert polynomials and related insertion algorithms. PhD Thesis, MIT, 1996. - .. [Lam2008] T. Lam. Schubert polynomials for the affine Grassmannian. J. Amer. Math. Soc., 2008. - .. [LSS2009] T. Lam, A. Schilling, M. Shimozono. Schubert polynomials for the affine Grassmannian of the symplectic group. Mathematische Zeitschrift 264(4) (2010) 765-811 (arXiv:0710.2720 [math.CO]) - .. [Pon2010] S. Pon. Types B and D affine Stanley symmetric functions, unpublished PhD Thesis, UC Davis, 2010. + .. [FoSta1994] \S. Fomin, R. Stanley. Schubert polynomials and the nilCoxeter algebra. Advances in Math., 1994. + .. [BH1994] \S. Billey, M. Haiman. Schubert polynomials for the classical groups. J. Amer. Math. Soc., 1994. + .. [TKLam1996] \T.K. Lam. B and D analogues of stable Schubert polynomials and related insertion algorithms. PhD Thesis, MIT, 1996. + .. [Lam2008] \T. Lam. Schubert polynomials for the affine Grassmannian. J. Amer. Math. Soc., 2008. + .. [LSS2009] \T. Lam, A. Schilling, M. Shimozono. Schubert polynomials for the affine Grassmannian of the symplectic group. Mathematische Zeitschrift 264(4) (2010) 765-811 (arXiv:0710.2720 [math.CO]) + .. [Pon2010] \S. Pon. Types B and D affine Stanley symmetric functions, unpublished PhD Thesis, UC Davis, 2010. """ def _repr_(self): diff --git a/src/sage/combinat/root_system/reflection_group_c.pyx b/src/sage/combinat/root_system/reflection_group_c.pyx new file mode 100644 index 00000000000..c5d693dbee8 --- /dev/null +++ b/src/sage/combinat/root_system/reflection_group_c.pyx @@ -0,0 +1,410 @@ +r""" +This contains a few time-critial auxillary cython functions for +finite complex or real reflection groups. +""" +#***************************************************************************** +# Copyright (C) 2011-2016 Christian Stump +# 2016 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.groups.perm_gps.permgroup_element cimport PermutationGroupElement +from collections import deque + +cdef class Iterator(object): + """ + Iterator class for reflection groups. + """ + cdef int n + cdef int N # number of refections/positive roots + cdef tuple S + cdef str algorithm + cdef bint tracking_words + cdef list noncom + + cdef list noncom_letters(self): + """ + Return a list ``L`` of lists such that ... + + WARNING:: This is not used as it slows down the computation. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(["B", 4]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: TestSuite(I).run(skip="_test_pickling") # optional - gap3 + """ + cdef tuple S = self.S + cdef int n = len(S) + cdef list noncom = [] + cdef list noncom_i + for i in range(n): + si = S[i] + noncom_i = [] + for j in range(i+1,n): + sj = S[j] + if si._mul_(sj) == sj._mul_(si): + pass + else: + noncom_i.append(j) + noncom.append(noncom_i) + noncom.append(range(n)) + return noncom + + def __init__(self, W, int N, str algorithm="depth", bint tracking_words=True): + """ + Initalize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(["B", 4]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: TestSuite(I).run(skip="_test_pickling") # optional - gap3 + """ + self.S = tuple(W.simple_reflections()) + self.n = len(W._index_set) + self.N = N + self.tracking_words = tracking_words + + # "breadth" is 1.5x slower than "depth" since it uses + # a deque with popleft instead of a list with pop + if algorithm not in ["depth","breadth"]: + raise ValueError('the algorithm (="%s") must be either "depth" or "breadth"') + self.algorithm = algorithm + +# self.noncom = self.noncom_letters() + + cdef list succ(self, PermutationGroupElement u, int first): + cdef PermutationGroupElement u1, si + cdef int i + cdef list successors = [] + cdef tuple S = self.S + cdef int N = self.N +# cdef list nc = self.noncom[first] + + for i in range(first): + si = (S[i]) + if self.test(u, si, i): + successors.append((si._mul_(u), i)) + for i in range(first+1,self.n): +# for i in nc: + if u.perm[i] < N: + si = (S[i]) + if self.test(u, si, i): + successors.append((si._mul_(u), i)) + return successors + + cdef list succ_words(self, PermutationGroupElement u, list word, int first): + cdef PermutationGroupElement u1, si + cdef int i, j + cdef list successors = [] + cdef list word_new + cdef tuple S = self.S + cdef int N = self.N + + for i in range(first): + si = (S[i]) + if self.test(u, si, i): + u1 = (si._mul_(u)) + # try to use word+[i] and the reversed + word_new = [i] + word + u1._reduced_word = word_new + successors.append((u1, word_new, i)) + for i in range(first+1, self.n): + if u.perm[i] < self.N: + si = (S[i]) + if self.test(u, si, i): + u1 = (si._mul_(u)) + word_new = [i] + word + u1._reduced_word = word_new + successors.append((u1, word_new, i)) + return successors + + cdef bint test(self, PermutationGroupElement u, PermutationGroupElement si, int i): + cdef int j, sij + cdef int N = self.N + cdef int* siperm = si.perm + cdef int* uperm = u.perm + + for j in range(i): + sij = siperm[j] + if uperm[sij] >= N: + return False + return True + + def __iter__(self): + """ + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(["B", 4]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: len(list(I)) == W.cardinality() # optional - gap3 + True + """ + # the breadth search iterator is ~2x slower as it + # uses a deque with popleft + if self.algorithm == "depth": + if self.tracking_words: + return self.iter_words_depth() + else: + return self.iter_depth() + elif self.algorithm == "breadth": + if self.tracking_words: + return self.iter_words_breadth() + else: + return self.iter_breadth() + + def iter_depth(self): + """ + Iterate over ``self`` using depth-first-search. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(['B', 2]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: list(I.iter_depth()) # optional - gap3 + [(), + (1,3)(2,6)(5,7), + (1,5)(2,4)(6,8), + (1,3,5,7)(2,8,6,4), + (1,7)(3,5)(4,8), + (1,7,5,3)(2,4,6,8), + (2,8)(3,7)(4,6), + (1,5)(2,6)(3,7)(4,8)] + """ + cdef tuple node + cdef list cur = [(self.S[0].parent().one(), -1)] + cdef PermutationGroupElement u + cdef int first + cdef list L = [] + + while True: + if not cur: + if not L: + return + cur = L.pop() + continue + + u, first = cur.pop() + yield u + L.append(self.succ(u, first)) + + def iter_words_depth(self): + """ + Iterate over ``self`` using depth-first-search and setting + the reduced word. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(['B', 2]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: for w in I.iter_words_depth(): w._reduced_word # optional - gap3 + [] + [1] + [0] + [1, 0] + [0, 1, 0] + [0, 1] + [1, 0, 1] + [0, 1, 0, 1] + """ + cdef tuple node + cdef list cur, word + + cdef PermutationGroupElement u + cdef int first + cdef list L = [] + + one = self.S[0].parent().one() + one._reduced_word = [] + cur = [(one, list(), -1)] + + while True: + if not cur: + if not L: + return + cur = L.pop() + continue + + u, word, first = cur.pop() + yield u + L.append(self.succ_words(u, word, first)) + + def iter_breadth(self): + """ + Iterate over ``self`` using breadth-first-search. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(['B', 2]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: list(I.iter_breadth()) # optional - gap3 + [(), + (1,3)(2,6)(5,7), + (1,5)(2,4)(6,8), + (1,7,5,3)(2,4,6,8), + (1,3,5,7)(2,8,6,4), + (2,8)(3,7)(4,6), + (1,7)(3,5)(4,8), + (1,5)(2,6)(3,7)(4,8)] + """ + cdef tuple node + cdef list cur = [(self.S[0].parent().one(), -1)] + cdef PermutationGroupElement u + cdef int first + L = deque() + + while True: + if not cur: + if not L: + return + cur = L.popleft() + continue + + u, first = cur.pop() + yield u + L.append(self.succ(u, first)) + + def iter_words_breadth(self): + """ + Iterate over ``self`` using breadth-first-search and setting + the reduced word. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import Iterator + sage: W = ReflectionGroup(['B', 2]) # optional - gap3 + sage: I = Iterator(W, W.number_of_reflections()) # optional - gap3 + sage: for w in I.iter_words_breadth(): w._reduced_word # optional - gap3 + [] + [1] + [0] + [0, 1] + [1, 0] + [1, 0, 1] + [0, 1, 0] + [0, 1, 0, 1] + """ + cdef tuple node + cdef list cur, word + cdef PermutationGroupElement u + cdef int first + L = deque() + + one = self.S[0].parent().one() + one._reduced_word = [] + cur = [(one, list(), -1)] + + while True: + if not cur: + if not L: + return + cur = L.popleft() + continue + + u, word, first = cur.pop() + yield u + L.append(self.succ_words(u, word, first)) + +def iterator_tracking_words(W): + r""" + Return an iterator through the elements of ``self`` together + with the words in the simple generators. + + The iterator is a breadth first search through the graph of the + elements of the group with generators. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import iterator_tracking_words + sage: W = ReflectionGroup(4) # optional - gap3 + sage: for w in iterator_tracking_words(W): w # optional - gap3 + ((), []) + ((1,3,9)(2,4,7)(5,10,18)(6,11,16)(8,12,19)(13,15,20)(14,17,21)(22,23,24), [0]) + ((1,5,13)(2,6,10)(3,7,14)(4,8,15)(9,16,22)(11,12,17)(18,19,23)(20,21,24), [1]) + ((1,9,3)(2,7,4)(5,18,10)(6,16,11)(8,19,12)(13,20,15)(14,21,17)(22,24,23), [0, 0]) + ((1,7,6,12,23,20)(2,8,17,24,9,5)(3,16,10,19,15,21)(4,14,11,22,18,13), [0, 1]) + ((1,10,4,12,21,22)(2,11,19,24,13,3)(5,15,7,17,16,23)(6,18,8,20,14,9), [1, 0]) + ((1,13,5)(2,10,6)(3,14,7)(4,15,8)(9,22,16)(11,17,12)(18,23,19)(20,24,21), [1, 1]) + ((1,16,12,15)(2,14,24,18)(3,5,19,17)(4,6,22,20)(7,8,23,9)(10,13,21,11), [0, 0, 1]) + ((1,2,12,24)(3,6,19,20)(4,17,22,5)(7,11,23,13)(8,21,9,10)(14,16,18,15), [0, 1, 0]) + ((1,14,12,18)(2,15,24,16)(3,22,19,4)(5,6,17,20)(7,10,23,21)(8,11,9,13), [0, 1, 1]) + ((1,18,12,14)(2,16,24,15)(3,4,19,22)(5,20,17,6)(7,21,23,10)(8,13,9,11), [1, 0, 0]) + ((1,15,12,16)(2,18,24,14)(3,17,19,5)(4,20,22,6)(7,9,23,8)(10,11,21,13), [1, 1, 0]) + ((1,6,23)(2,17,9)(3,10,15)(4,11,18)(5,8,24)(7,12,20)(13,14,22)(16,19,21), [0, 0, 1, 0]) + ((1,22,21,12,4,10)(2,3,13,24,19,11)(5,23,16,17,7,15)(6,9,14,20,8,18), [0, 0, 1, 1]) + ((1,4,21)(2,19,13)(3,11,24)(5,7,16)(6,8,14)(9,18,20)(10,12,22)(15,17,23), [0, 1, 0, 0]) + ((1,17,13,12,5,11)(2,20,10,24,6,21)(3,23,14,19,7,18)(4,9,15,22,8,16), [0, 1, 1, 0]) + ((1,19,9,12,3,8)(2,22,7,24,4,23)(5,21,18,17,10,14)(6,13,16,20,11,15), [1, 0, 0, 1]) + ((1,20,23,12,6,7)(2,5,9,24,17,8)(3,21,15,19,10,16)(4,13,18,22,11,14), [1, 1, 0, 0]) + ((1,11,5,12,13,17)(2,21,6,24,10,20)(3,18,7,19,14,23)(4,16,8,22,15,9), [0, 0, 1, 0, 0]) + ((1,23,6)(2,9,17)(3,15,10)(4,18,11)(5,24,8)(7,20,12)(13,22,14)(16,21,19), [0, 0, 1, 1, 0]) + ((1,8,3,12,9,19)(2,23,4,24,7,22)(5,14,10,17,18,21)(6,15,11,20,16,13), [0, 1, 0, 0, 1]) + ((1,21,4)(2,13,19)(3,24,11)(5,16,7)(6,14,8)(9,20,18)(10,22,12)(15,23,17), [0, 1, 1, 0, 0]) + ((1,12)(2,24)(3,19)(4,22)(5,17)(6,20)(7,23)(8,9)(10,21)(11,13)(14,18)(15,16), [0, 0, 1, 0, 0, 1]) + ((1,24,12,2)(3,20,19,6)(4,5,22,17)(7,13,23,11)(8,10,9,21)(14,15,18,16), [0, 0, 1, 1, 0, 0]) + """ + cdef tuple I = tuple(W.simple_reflections()) + cdef list index_list = range(len(I)) + + cdef list level_set_cur = [(W.one(), [])] + cdef set level_set_old = set([ W.one() ]) + cdef list word + cdef PermutationGroupElement x, y + + while level_set_cur: + level_set_new = [] + for x, word in level_set_cur: + yield x, word + for i in index_list: + y = x._mul_(I[i]) + if y not in level_set_old: + level_set_old.add(y) + level_set_new.append((y, word+[i])) + level_set_cur = level_set_new + +cdef bint has_descent(PermutationGroupElement w, int i, int N): + return w.perm[i] >= N + +cdef int first_descent(PermutationGroupElement w, int n, int N): + cdef int i + for i in range(n): + if has_descent(w,i,N): + return i + return -1 + +cpdef list reduced_word_c(W,w): + r""" + Computes a reduced word for the element `w` in the + reflection group `W` in the positions ``range(n)``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_c import reduced_word_c + sage: W = ReflectionGroup(['B',2]) # optional - gap3 + sage: [ reduced_word_c(W,w) for w in W ] # optional - gap3 + [[], [1], [0], [0, 1], [1, 0], [1, 0, 1], [0, 1, 0], [0, 1, 0, 1]] + """ + cdef tuple S = tuple(W.simple_reflections()) + cdef int n = len(S) + cdef int N = W._number_of_reflections + cdef int fdes = 0 + cdef list word = [] + + while fdes != -1: + fdes = first_descent(w,n,N) + si = S[fdes] + w = si._mul_(w) + word.append(fdes) + return word[:-1] diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py new file mode 100644 index 00000000000..775a6304520 --- /dev/null +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -0,0 +1,2604 @@ +r""" +Finite complex reflection groups +---------------------------------- + +Let `V` be a finite-dimensional complex vector space. A reflection of +`V` is an operator `r \in \operatorname{GL}(V)` that has finite order +and fixes pointwise a hyperplane in `V`. + +For more definitions and classification types of finite complex +reflection groups, see :wikipedia:`Complex_reflection_group`. + +The point of entry to work with reflection groups is :func:`~sage.combinat.root_system.reflection_group_real.ReflectionGroup` +which can be used with finite Cartan-Killing types:: + + sage: ReflectionGroup(['A',2]) # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: ReflectionGroup(['F',4]) # optional - gap3 + Irreducible real reflection group of rank 4 and type F4 + sage: ReflectionGroup(['H',3]) # optional - gap3 + Irreducible real reflection group of rank 3 and type H3 + +or with Shephard-Todd types:: + + sage: ReflectionGroup((1,1,3)) # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: ReflectionGroup((2,1,3)) # optional - gap3 + Irreducible real reflection group of rank 3 and type B3 + sage: ReflectionGroup((3,1,3)) # optional - gap3 + Irreducible complex reflection group of rank 3 and type G(3,1,3) + sage: ReflectionGroup((4,2,3)) # optional - gap3 + Irreducible complex reflection group of rank 3 and type G(4,2,3) + sage: ReflectionGroup(4) # optional - gap3 + Irreducible complex reflection group of rank 2 and type ST4 + sage: ReflectionGroup(31) # optional - gap3 + Irreducible complex reflection group of rank 4 and type ST31 + +Also reducible types are allowed using concatenation:: + + sage: ReflectionGroup(['A',3],(4,2,3)) # optional - gap3 + Reducible complex reflection group of rank 6 and type A3 x G(4,2,3) + +Some special cases also occur, among them are:: + + sage: W = ReflectionGroup((2,2,2)); W # optional - gap3 + Reducible real reflection group of rank 2 and type A1 x A1 + sage: W = ReflectionGroup((2,2,3)); W # optional - gap3 + Irreducible real reflection group of rank 3 and type A3 + +.. WARNING:: Uses the GAP3 package *Chevie* which is available as an + experimental package (installed by ``sage -i gap3``) or to + download by hand from `Jean Michel's website `_. + +A guided tour +------------- + +We start with the example type `B_2`:: + + sage: W = ReflectionGroup(['B',2]); W # optional - gap3 + Irreducible real reflection group of rank 2 and type B2 + +Most importantly, observe that the group elements are usually represented +by permutations of the roots:: + + sage: for w in W: print w # optional - gap3 + () + (1,3)(2,6)(5,7) + (1,5)(2,4)(6,8) + (1,7,5,3)(2,4,6,8) + (1,3,5,7)(2,8,6,4) + (2,8)(3,7)(4,6) + (1,7)(3,5)(4,8) + (1,5)(2,6)(3,7)(4,8) + +This has the drawback that one can hardly see anything. Usually, one +would look at elements with either of the following methods:: + + sage: for w in W: w.reduced_word() # optional - gap3 + [] + [2] + [1] + [1, 2] + [2, 1] + [2, 1, 2] + [1, 2, 1] + [1, 2, 1, 2] + + sage: for w in W: w.reduced_word_in_reflections() # optional - gap3 + [] + [2] + [1] + [1, 2] + [1, 4] + [3] + [4] + [1, 3] + + sage: for w in W: w.reduced_word(); w.to_matrix(); print("") # optional - gap3 + [] + [1 0] + [0 1] + + [2] + [ 1 1] + [ 0 -1] + + [1] + [-1 0] + [ 2 1] + + [1, 2] + [-1 -1] + [ 2 1] + + [2, 1] + [ 1 1] + [-2 -1] + + [2, 1, 2] + [ 1 0] + [-2 -1] + + [1, 2, 1] + [-1 -1] + [ 0 1] + + [1, 2, 1, 2] + [-1 0] + [ 0 -1] + + +The standard references for actions of complex reflection groups have +the matrices acting on the right, so:: + + sage: W.simple_reflection(1).to_matrix() # optional - gap3 + [-1 0] + [ 2 1] + +sends the simple root `\alpha_0`, or ``(1,0)`` in vector notation, to +its negative, while sending `\alpha_1` to `2\alpha_0+\alpha_1`. + +.. TODO:: + + - properly provide root systems for real reflection groups + - element class should be unique to be able to work with large groups + without creating elements multiple times + - ``is_shephard_group``, ``is_generalized_coxeter_group`` + - exponents and coexponents + - coinvariant ring: + + * fake degrees from Torsten Hoge + * operation of linear characters on all characters + * harmonic polynomials + + - linear forms for hyperplanes + - field of definition + - intersection lattice and characteristic polynomial:: + + X = [ alpha(t) for t in W.distinguished_reflections() ] + X = Matrix(CF,X).transpose() + Y = Matroid(X) + + - linear characters + - permutation pi on irreducibles + - hyperplane orbits (76.13 in Gap Manual) + - improve invariant_form with a code similar to the one in + ``reflection_group_real.py`` + - add a method ``reflection_to_root`` or + ``distinguished_reflection_to_positive_root`` + - diagrams in ASCII-art (76.15) + - standard (BMR) presentations + - character table directly from Chevie + - ``GenericOrder`` (76.20), ``TorusOrder`` (76.21) + - correct fundamental invariants for `G_34`, check the others + - copy hardcoded data (degrees, invariants, braid relations...) into sage + - add other hardcoded data from the tables in chevie (location is + SAGEDIR/local/gap3/gap-jm5-2015-02-01/gap3/pkg/chevie/tbl): + basic derivations, discriminant, ... + - transfer code for ``reduced_word_in_reflections`` into Gap4 or Sage + - list of reduced words for an element + - list of reduced words in reflections for an element + - Hurwitz action? + - :meth:`is_crystallographic` should be hardcoded + +AUTHORS: + +- Christian Stump (2015): initial version +""" +#***************************************************************************** +# Copyright (C) 2011-2016 Christian Stump +# +# 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, cached_in_parent_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.flatten import flatten +from sage.misc.misc_c import prod +from sage.categories.category import Category +from sage.categories.permutation_groups import PermutationGroups +from sage.categories.complex_reflection_groups import ComplexReflectionGroups +from sage.categories.coxeter_groups import CoxeterGroups +from sage.groups.perm_gps.permgroup_element import PermutationGroupElement +from sage.sets.family import Family +from sage.structure.unique_representation import UniqueRepresentation +from sage.groups.perm_gps.permgroup import PermutationGroup_generic +from sage.rings.all import ZZ, QQ +from sage.matrix.all import Matrix, identity_matrix +from sage.matrix.matrix import is_Matrix +from sage.interfaces.gap3 import gap3 +from sage.rings.universal_cyclotomic_field import E +from sage.arith.misc import lcm +from sage.modules.free_module_element import vector +from sage.combinat.root_system.cartan_matrix import CartanMatrix + +from sage.misc.sage_eval import sage_eval + +class ComplexReflectionGroup(UniqueRepresentation, PermutationGroup_generic): + """ + A complex reflection group given as a permutation group. + + .. SEEALSO:: + + :func:`ReflectionGroup` + """ + def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflection_index_set=None): + r""" + TESTS:: + + sage: from sage.categories.complex_reflection_groups import ComplexReflectionGroups + sage: W = ComplexReflectionGroups().example() # optional - gap3 + sage: TestSuite(W).run() # optional - gap3 + """ + W_components = [] + reflection_type = [] + for W_type in W_types: + if W_type == (1,1,1): + raise ValueError("the one element group is not considered a reflection group") + elif W_type in ZZ: + call_str = 'ComplexReflectionGroup(%s)'%W_type + elif isinstance(W_type,CartanMatrix): + call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._M._gap_())) + elif is_Matrix(W_type): + call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._gap_())) + elif W_type in ZZ or ( isinstance(W_type, tuple) and len(W_type) == 3 ): + call_str = 'ComplexReflectionGroup%s'%str(W_type) + else: + if W_type[0] == "I": + call_str = 'CoxeterGroup("I",2,%s)'%W_type[1] + else: + call_str = 'CoxeterGroup("%s",%s)'%W_type + + W_components.append(gap3(call_str)) + X = list(W_components[-1].ReflectionType()) + if len(X) > 1: + raise ValueError("input data %s is invalid"%W_type) + X = X[0] + type_dict = dict() + type_dict["series"] = X.series.sage() + type_dict["rank"] = X.rank.sage() + type_dict["indices"] = X.indices.sage() + if hasattr(X.ST,"sage"): + type_dict["ST"] = X.ST.sage() + elif hasattr(X.p,"sage") and hasattr(X.q,"sage"): + type_dict["ST"] = ( X.p.sage(), X.q.sage(), X.rank.sage() ) + elif hasattr(X.bond,"sage"): + type_dict["bond"] = X.bond.sage() + if type_dict["series"] == "B" and (X.cartanType.sage() == 1 or X.indices.sage() == [2,1]): + type_dict["series"] = "C" + reflection_type.append( type_dict ) + + self._type = reflection_type + self._gap_group = prod(W_components) + generators = [str(x) for x in self._gap_group.generators] + self._index_set = index_set + self._hyperplane_index_set = hyperplane_index_set + self._reflection_index_set = reflection_index_set + + self._conjugacy_classes = {} + self._conjugacy_classes_representatives = None + self._reflection_representation = None + + self._rank = self._gap_group.rank.sage() + if len(generators) == self._rank: + category = ComplexReflectionGroups().Finite().WellGenerated() + if all(str(W_comp).find('CoxeterGroup') >= 0 for W_comp in W_components): + category = Category.join([category,CoxeterGroups()]) + else: + category = ComplexReflectionGroups().Finite() + if len(self._type) == 1: + category = category.Irreducible() + + category = Category.join([category,PermutationGroups()]).Finite() + + PermutationGroup_generic.__init__(self, gens=generators, + canonicalize=False, + category=category) + + l_set = range(1, len(self.gens())+1) + if self._index_set is None: + self._index_set = tuple(l_set) + else: + if len(self._index_set) != len(l_set): + raise ValueError("the given index set (= %s) does not have the right size"%self._index_set.values()) + self._index_set_inverse = {i: ii for ii,i in enumerate(self._index_set)} + Nstar_set = range(1,self.number_of_reflection_hyperplanes()+1) + if self._hyperplane_index_set is None: + self._hyperplane_index_set = tuple(Nstar_set) + else: + if len(self._hyperplane_index_set) != len(Nstar_set): + raise ValueError("the given hyperplane index set (= %s) does not have the right size"%self._index_set.values()) + self._hyperplane_index_set_inverse = {i: ii for ii,i in enumerate(self._hyperplane_index_set)} + + # storing the number of reflections for later use in descents + self._number_of_reflections = self.number_of_reflections() + + N_set = range(1, self._number_of_reflections+1) + if self._reflection_index_set is None: + self._reflection_index_set = tuple(N_set) + else: + if len(self._reflection_index_set) != len(N_set): + raise ValueError("the given reflection index set (= %s) does not have the right size"%self._index_set.values()) + self._reflection_index_set_inverse = {i: ii for ii,i in enumerate(self._reflection_index_set)} + + def _irrcomp_repr_(self,W_type): + r""" + Return the string representation of an irreducible component + of ``self``. + + TESTS:: + + sage: W = ReflectionGroup(25,[4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 + Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) + sage: for W_type in W._type: print(W._irrcomp_repr_(W_type)) # optional - gap3 + ST25 + G(4,1,4) + A3 + I2(5) + """ + type_str = '' + if "ST" in W_type: + if W_type["ST"] in ZZ: + type_str += "ST" + str(W_type["ST"]) + else: + type_str += 'G' + str(W_type["ST"]).replace(' ','') + else: + type_str += str(W_type["series"]) + if W_type["series"] == "I": + type_str += '2(' + str(W_type["bond"]) + ')' + else: + type_str += str(W_type["rank"]) + return type_str + + def _repr_(self): + r""" + Return the string representation of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(25, [4,1,4],[1,1,4],[5,5,2]); W # optional - gap3 + Reducible complex reflection group of rank 12 and type ST25 x G(4,1,4) x A3 x I2(5) + """ + type_str = '' + for W_type in self._type: + type_str += self._irrcomp_repr_(W_type) + type_str += ' x ' + type_str = type_str[:-3] + return 'Reducible complex reflection group of rank %s and type %s'%(self._rank,type_str) + + def __iter__(self): + r""" + Return an iterator going through all elements in ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: w # optional - gap3 + () + (1,3)(2,5)(4,6) + (1,4)(2,3)(5,6) + (1,6,2)(3,5,4) + (1,2,6)(3,4,5) + (1,5)(2,4)(3,6) + """ + from sage.combinat.root_system.reflection_group_c import iterator_tracking_words + for w,word in iterator_tracking_words(self): + w._reduced_word = word + yield w + + @cached_method + def index_set(self): + r""" + Return the index set of the simple reflections of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.index_set() # optional - gap3 + (1, 2, 3) + sage: W = ReflectionGroup((1,1,4), index_set=[1,3,'asdf']) # optional - gap3 + sage: W.index_set() # optional - gap3 + (1, 3, 'asdf') + sage: W = ReflectionGroup((1,1,4), index_set=('a', 'b', 'c')) # optional - gap3 + sage: W.index_set() # optional - gap3 + ('a', 'b', 'c') + """ + return self._index_set + + def simple_reflection(self, i): + r""" + Return the ``i``-th simple reflection of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.simple_reflection(1) # optional - gap3 + (1,4)(2,3)(5,6) + sage: W.simple_reflections() # optional - gap3 + Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6)} + """ + return self.gens()[self._index_set_inverse[i]] + + def series(self): + r""" + Return the series of the classification type to which ``self`` + belongs. + + For real reflection groups, these are the Cartan-Killing + classification types "A","B","C","D","E","F","G","H","I", and + for complx non-real reflection groups these are the + Shephard-Todd classification type "ST". + + EXAMPLES:: + + sage: ReflectionGroup((1,1,3)).series() # optional - gap3 + ['A'] + sage: ReflectionGroup((3,1,3)).series() # optional - gap3 + ['ST'] + """ + return [self._type[i]['series'] for i in range(len(self._type))] + + @cached_method + def hyperplane_index_set(self): + r""" + Return the index set of the hyperplanes of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + (1, 2, 3, 4, 5, 6) + sage: W = ReflectionGroup((1,1,4), hyperplane_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + (1, 3, 'asdf', 7, 9, 11) + sage: W = ReflectionGroup((1,1,4),hyperplane_index_set=('a','b','c','d','e','f')) # optional - gap3 + sage: W.hyperplane_index_set() # optional - gap3 + ('a', 'b', 'c', 'd', 'e', 'f') + """ + return self._hyperplane_index_set + + @cached_method + def distinguished_reflections(self): + r""" + Return a finite family containing the distinguished reflections + of ``self`` indexed by :meth:`hyperplane_index_set`. + + These are the reflections in ``self`` acting on the complement + of the fixed hyperplane `H` as `\operatorname{exp}(2 \pi i / n)`, + where `n` is the order of the reflection subgroup fixing `H`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.distinguished_reflections() # optional - gap3 + Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)} + + sage: W = ReflectionGroup((1,1,3),hyperplane_index_set=['a','b','c']) # optional - gap3 + sage: W.distinguished_reflections() # optional - gap3 + Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)} + + sage: W = ReflectionGroup((3,1,1)) # optional - gap3 + sage: W.distinguished_reflections() # optional - gap3 + Finite family {1: (1,2,3)} + + sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 + sage: W.distinguished_reflections() # optional - gap3 + Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), + 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), + 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), + 5: (1,7)(2,6)(5,8), + 6: (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26), + 7: (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29), + 8: (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28)} + """ + # makes sure that the simple reflections come first + gens = self.gens() + R = [t for t in gens] + # Then import all distinguished reflections from gap, + # the Set is used as every such appears multiple times. + for r in self._gap_group.Reflections(): + t = self(str(r)) + if t not in R: + R.append(t) + return Family(self._hyperplane_index_set, + lambda i: R[self._hyperplane_index_set_inverse[i]]) + + def distinguished_reflection(self, i): + r""" + Return the ``i``-th distinguished reflection of ``self``. + + These are the reflections in ``self`` acting on the complement + of the fixed hyperplane `H` as `\operatorname{exp}(2 \pi i / n)`, + where `n` is the order of the reflection subgroup fixing `H`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.distinguished_reflection(1) # optional - gap3 + (1,4)(2,3)(5,6) + sage: W.distinguished_reflection(2) # optional - gap3 + (1,3)(2,5)(4,6) + sage: W.distinguished_reflection(3) # optional - gap3 + (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((3,1,1),hyperplane_index_set=['a']) # optional - gap3 + sage: W.distinguished_reflection('a') # optional - gap3 + (1,2,3) + + sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 + sage: for i in range(W.number_of_reflection_hyperplanes()): # optional - gap3 + ....: W.distinguished_reflection(i+1) # optional - gap3 + (1,6)(2,5)(7,8) + (1,5)(2,7)(6,8) + (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30) + (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30) + (1,7)(2,6)(5,8) + (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26) + (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29) + (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28) + """ + return self.distinguished_reflections()[i] + + @cached_method + def reflection_hyperplanes(self, as_linear_functionals=False): + r""" + Return the list of all reflection hyperplanes of ``self``, + either as a codimension 1 space, or as its linear functional. + + INPUT: + + - ``as_linear_functionals`` -- (default:``False``) flag whether + to return the hyperplane or its linear functional in the basis + dual to the given root basis + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for H in W.reflection_hyperplanes(): H # optional - gap3 + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 2] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 1/2] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 -1] + + sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 + (1, -1/2) + (1, -2) + (1, 1) + + + sage: W = ReflectionGroup((2,1,2)) # optional - gap3 + sage: for H in W.reflection_hyperplanes(): H # optional - gap3 + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 1] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 1/2] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [0 1] + + sage: for H in W.reflection_hyperplanes(as_linear_functionals=True): H # optional - gap3 + (1, -1) + (1, -2) + (0, 1) + (1, 0) + """ + Hs = [] + for r in self.distinguished_reflections(): + mat = (r.to_matrix().transpose() - identity_matrix(self.rank())) + if as_linear_functionals: + Hs.append( mat.row_space().gen() ) + else: + Hs.append( mat.right_kernel() ) + return Family(self._hyperplane_index_set, + lambda i: Hs[self._hyperplane_index_set_inverse[i]]) + + def reflection_hyperplane(self, i, as_linear_functional=False): + r""" + Return the ``i``-th reflection hyperplane of ``self``. + + The ``i``-th reflection hyperplane corresponds to the ``i`` + distinguished reflection. + + INPUT: + + - ``i`` -- an index in the index set + - ``as_linear_functionals`` -- (default:``False``) flag whether + to return the hyperplane or its linear functional in the basis + dual to the given root basis + + EXAMPLES:: + + sage: W = ReflectionGroup((2,1,2)) # optional - gap3 + sage: W.reflection_hyperplane(3) # optional - gap3 + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + + One can ask for the result as a linear form:: + + sage: W.reflection_hyperplane(3, True) # optional - gap3 + (0, 1) + """ + return self.reflection_hyperplanes(as_linear_functionals=as_linear_functional)[i] + + @cached_method + def reflection_index_set(self): + r""" + Return the index set of the reflections of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + (1, 2, 3, 4, 5, 6) + sage: W = ReflectionGroup((1,1,4), reflection_index_set=[1,3,'asdf',7,9,11]) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + (1, 3, 'asdf', 7, 9, 11) + sage: W = ReflectionGroup((1,1,4), reflection_index_set=('a','b','c','d','e','f')) # optional - gap3 + sage: W.reflection_index_set() # optional - gap3 + ('a', 'b', 'c', 'd', 'e', 'f') + """ + return self._reflection_index_set + + @cached_method + def reflections(self): + r""" + Return a finite family containing the reflections of ``self``, + indexed by :meth:`self.reflection_index_set`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.reflections() # optional - gap3 + Finite family {1: (1,4)(2,3)(5,6), 2: (1,3)(2,5)(4,6), 3: (1,5)(2,4)(3,6)} + + sage: W = ReflectionGroup((1,1,3),reflection_index_set=['a','b','c']) # optional - gap3 + sage: W.reflections() # optional - gap3 + Finite family {'a': (1,4)(2,3)(5,6), 'c': (1,5)(2,4)(3,6), 'b': (1,3)(2,5)(4,6)} + + sage: W = ReflectionGroup((3,1,1)) # optional - gap3 + sage: W.reflections() # optional - gap3 + Finite family {1: (1,2,3), 2: (1,3,2)} + + sage: W = ReflectionGroup((1,1,3),(3,1,2)) # optional - gap3 + sage: W.reflections() # optional - gap3 + Finite family {1: (1,6)(2,5)(7,8), 2: (1,5)(2,7)(6,8), + 3: (3,9,15)(4,10,16)(12,17,23)(14,18,24)(20,25,29)(21,22,26)(27,28,30), + 4: (3,11)(4,12)(9,13)(10,14)(15,19)(16,20)(17,21)(18,22)(23,27)(24,28)(25,26)(29,30), + 5: (1,7)(2,6)(5,8), + 6: (3,19)(4,25)(9,11)(10,17)(12,28)(13,15)(14,30)(16,18)(20,27)(21,29)(22,23)(24,26), + 7: (4,21,27)(10,22,28)(11,13,19)(12,14,20)(16,26,30)(17,18,25)(23,24,29), + 8: (3,13)(4,24)(9,19)(10,29)(11,15)(12,26)(14,21)(16,23)(17,30)(18,27)(20,22)(25,28), + 9: (3,15,9)(4,16,10)(12,23,17)(14,24,18)(20,29,25)(21,26,22)(27,30,28), + 10: (4,27,21)(10,28,22)(11,19,13)(12,20,14)(16,30,26)(17,25,18)(23,29,24)} + """ + T = self.distinguished_reflections().values() + for i in range(self.number_of_reflection_hyperplanes()): + for j in range(2, T[i].order()): + T.append(T[i]**j) + return Family(self._reflection_index_set, + lambda i: T[self._reflection_index_set_inverse[i]]) + + def reflection(self,i): + r""" + Return the ``i``-th reflection of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.reflection(1) # optional - gap3 + (1,4)(2,3)(5,6) + sage: W.reflection(2) # optional - gap3 + (1,3)(2,5)(4,6) + sage: W.reflection(3) # optional - gap3 + (1,5)(2,4)(3,6) + + sage: W = ReflectionGroup((3,1,1),reflection_index_set=['a','b']) # optional - gap3 + sage: W.reflection('a') # optional - gap3 + (1,2,3) + sage: W.reflection('b') # optional - gap3 + (1,3,2) + """ + return self.reflections()[i] + + def reflection_character(self): + r""" + Return the reflection characters of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.reflection_character() # optional - gap3 + [2, 0, -1] + """ + return self._gap_group.ReflectionCharacter().sage() + + def is_crystallographic(self): + r""" + Return ``True`` if self is crystallographic. + + This is, if the field of definition is the rational field. + + .. TODO:: + + Make this more robust and do not use the matrix + representation of the simple reflections. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: W.is_crystallographic() # optional - gap3 + True + + sage: W = ReflectionGroup((2,1,3)); W # optional - gap3 + Irreducible real reflection group of rank 3 and type B3 + sage: W.is_crystallographic() # optional - gap3 + True + + sage: W = ReflectionGroup(23); W # optional - gap3 + Irreducible real reflection group of rank 3 and type H3 + sage: W.is_crystallographic() # optional - gap3 + False + + sage: W = ReflectionGroup((3,1,3)); W # optional - gap3 + Irreducible complex reflection group of rank 3 and type G(3,1,3) + sage: W.is_crystallographic() # optional - gap3 + False + + sage: W = ReflectionGroup((4,2,2)); W # optional - gap3 + Irreducible complex reflection group of rank 2 and type G(4,2,2) + sage: W.is_crystallographic() # optional - gap3 + False + """ + return self.is_real() and all(t.to_matrix().base_ring() is QQ for t in self.simple_reflections()) + + def _element_class(self): + r""" + A temporary workaround for compatibility with Sage's + permutation groups. + + TESTS:: + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: W._element_class() is W.element_class # optional - gap3 + True + """ + return self.element_class + + def number_of_irreducible_components(self): + r""" + Return the number of irreducible components of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.number_of_irreducible_components() # optional - gap3 + 1 + + sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 + sage: W.number_of_irreducible_components() # optional - gap3 + 2 + """ + return len(self._type) + + def irreducible_components(self): + r""" + Return a list containing the irreducible components of ``self`` + as finite reflection groups. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.irreducible_components() # optional - gap3 + [Irreducible real reflection group of rank 2 and type A2] + + sage: W = ReflectionGroup((1,1,3),(2,1,3)) # optional - gap3 + sage: W.irreducible_components() # optional - gap3 + [Irreducible real reflection group of rank 2 and type A2, + Irreducible real reflection group of rank 3 and type B3] + """ + from sage.combinat.root_system.reflection_group_real import ReflectionGroup + irr_comps = [] + for W_type in self._type: + if W_type["series"] in ["A","B","D","E","F","G","H","I"]: + W_str = (W_type["series"],W_type["rank"]) + elif "ST" in W_type: + W_str = W_type["ST"] + irr_comps.append(ReflectionGroup(W_str)) + return irr_comps + + @cached_method + def conjugacy_classes_representatives(self): + r""" + Return the shortest representatives of the conjugacy classes of + ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + [[], [1], [1, 2]] + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + [[], [1], [1, 3], [1, 2], [1, 3, 2]] + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + [[], [1], [1, 1], [2, 1, 2, 1], [2, 1, 2, 1, 1], + [2, 1, 1, 2, 1, 1], [2], [1, 2], [1, 1, 2]] + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: [w.reduced_word() for w in W.conjugacy_classes_representatives()] # optional - gap3 + [[], + [1], + [1, 2], + [1, 3], + [2, 3], + [1, 2, 3], + [1, 2, 1, 2], + [1, 2, 1, 2, 3], + [1, 2, 1, 2, 3, 2, 1, 2, 3], + [1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3]] + """ + S = str(gap3('List(ConjugacyClasses(%s),Representative)'%self._gap_group._name)) + exec('_conjugacy_classes_representatives=' + _gap_return(S)) + return _conjugacy_classes_representatives + + def conjugacy_classes(self): + r""" + Return the conjugacy classes of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for C in W.conjugacy_classes(): sorted(C) # optional - gap3 + [()] + [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] + [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + True + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + True + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: sum(len(C) for C in W.conjugacy_classes()) == W.cardinality() # optional - gap3 + True + """ + return Family(self.conjugacy_classes_representatives(), + lambda w: w.conjugacy_class()) + + def rank(self): + r""" + Return the rank of ``self``. + + This is the dimension of the underlying vector space. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.rank() # optional - gap3 + 2 + sage: W = ReflectionGroup((2,1,3)) # optional - gap3 + sage: W.rank() # optional - gap3 + 3 + sage: W = ReflectionGroup((4,1,3)) # optional - gap3 + sage: W.rank() # optional - gap3 + 3 + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.rank() # optional - gap3 + 3 + """ + return self._rank + + @cached_method + def degrees(self): + r""" + Return the degrees of ``self`` ordered within each irreducible + component of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.degrees() # optional - gap3 + (2, 3, 4) + + sage: W = ReflectionGroup((2,1,4)) # optional - gap3 + sage: W.degrees() # optional - gap3 + (2, 4, 6, 8) + + sage: W = ReflectionGroup((4,1,4)) # optional - gap3 + sage: W.degrees() # optional - gap3 + (4, 8, 12, 16) + + sage: W = ReflectionGroup((4,2,4)) # optional - gap3 + sage: W.degrees() # optional - gap3 + (4, 8, 8, 12) + + sage: W = ReflectionGroup((4,4,4)) # optional - gap3 + sage: W.degrees() # optional - gap3 + (4, 4, 8, 12) + + Examples of reducible types:: + + sage: W = ReflectionGroup((1,1,4), (3,1,2)); W # optional - gap3 + Reducible complex reflection group of rank 5 and type A3 x G(3,1,2) + sage: W.degrees() # optional - gap3 + (2, 3, 4, 3, 6) + + sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 + sage: W.degrees() # optional - gap3 + (2, 3, 4, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 2, 6, 10) + """ + if self.is_irreducible(): + try: + return tuple(sorted(self._gap_group.degrees.sage())) + except AttributeError: + return tuple(sorted(self._gap_group.ReflectionDegrees().sage())) + else: + return sum([comp.degrees() for comp in self.irreducible_components()],tuple()) + + @cached_method + def codegrees(self): + r""" + Return the codegrees of ``self`` ordered within each irreducible + component of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,4)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (2, 1, 0) + + sage: W = ReflectionGroup((2,1,4)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (6, 4, 2, 0) + + sage: W = ReflectionGroup((4,1,4)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (12, 8, 4, 0) + + sage: W = ReflectionGroup((4,2,4)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (12, 8, 4, 0) + + sage: W = ReflectionGroup((4,4,4)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (8, 8, 4, 0) + + sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 + sage: W.codegrees() # optional - gap3 + (2, 1, 0, 3, 0) + + sage: W = ReflectionGroup((1,1,4), (6,1,12), 23) # optional - gap3 # fails in GAP3 + sage: W.codegrees() # optional - gap3 + (2, 1, 0, 66, 60, 54, 48, 42, 36, 30, 24, 18, 12, 6, 0, 8, 4, 0) + """ + if self.is_irreducible(): + if self.is_well_generated(): + h = self.coxeter_number() + return tuple([h-d for d in self.degrees()]) + else: + return tuple(sorted(self._gap_group.ReflectionCoDegrees().sage(), reverse=True)) + else: + return sum([comp.codegrees() for comp in self.irreducible_components()],tuple()) + + @cached_method + def reflection_eigenvalues_family(self): + r""" + Return the reflection eigenvalues of ``self`` as a finite family + indexed by the class representatives of ``self``. + + OUTPUT: + + - list with entries `k/n` representing the eigenvalue `\zeta_n^k`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.reflection_eigenvalues_family() # optional - gap3 + Finite family {(): [0, 0], (1,4)(2,3)(5,6): [1/2, 0], (1,6,2)(3,5,4): [1/3, 2/3]} + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 + sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 + ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) # optional - gap3 + () [0, 0] + (1,3,9)(2,4,10)(6,11,17)(8,12,18)(14,19,23)(15,16,20)(21,22,24) [1/3, 0] + (1,3,9)(2,16,24)(4,20,21)(5,7,13)(6,12,23)(8,19,17)(10,15,22)(11,18,14) [1/3, 1/3] + (1,5)(2,6)(3,7)(4,8)(9,13)(10,14)(11,15)(12,16)(17,21)(18,22)(19,20)(23,24) [1/2, 0] + (1,7,3,13,9,5)(2,8,16,19,24,17)(4,14,20,11,21,18)(6,15,12,22,23,10) [1/6, 2/3] + (1,9,3)(2,10,4)(6,17,11)(8,18,12)(14,23,19)(15,20,16)(21,24,22) [2/3, 0] + (1,9,3)(2,20,22)(4,15,24)(5,7,13)(6,18,19)(8,23,11)(10,16,21)(12,14,17) [1/3, 2/3] + (1,9,3)(2,24,16)(4,21,20)(5,13,7)(6,23,12)(8,17,19)(10,22,15)(11,14,18) [2/3, 2/3] + (1,13,9,7,3,5)(2,14,24,18,16,11)(4,6,21,23,20,12)(8,22,17,15,19,10) [1/3, 5/6] + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: reflection_eigenvalues = W.reflection_eigenvalues_family() # optional - gap3 + sage: for elt in sorted(reflection_eigenvalues.keys()): # optional - gap3 + ....: print('%s %s'%(elt, reflection_eigenvalues[elt])) # optional - gap3 + () [0, 0, 0] + (1,8,4)(2,21,3)(5,10,11)(6,18,17)(7,9,12)(13,14,15)(16,23,19)(20,25,26)(22,24,27)(28,29,30) [1/3, 2/3, 0] + (1,16)(2,5)(4,7)(6,9)(8,10)(11,13)(12,14)(17,20)(19,22)(21,24)(23,25)(26,28)(27,29) [1/2, 0, 0] + (1,16)(2,9)(3,18)(4,10)(5,6)(7,8)(11,14)(12,13)(17,24)(19,25)(20,21)(22,23)(26,29)(27,28) [1/2, 1/2, 0] + (1,16)(2,17)(3,18)(4,19)(5,20)(6,21)(7,22)(8,23)(9,24)(10,25)(11,26)(12,27)(13,28)(14,29)(15,30) [1/2, 1/2, 1/2] + (1,19,20,2,7)(3,6,11,13,9)(4,5,17,22,16)(8,12,15,14,10)(18,21,26,28,24)(23,27,30,29,25) [1/5, 4/5, 0] + (1,20,7,19,2)(3,11,9,6,13)(4,17,16,5,22)(8,15,10,12,14)(18,26,24,21,28)(23,30,25,27,29) [2/5, 3/5, 0] + (1,23,26,29,22,16,8,11,14,7)(2,10,4,9,18,17,25,19,24,3)(5,21,27,30,28,20,6,12,15,13) [1/10, 1/2, 9/10] + (1,24,17,16,9,2)(3,12,13,18,27,28)(4,21,29,19,6,14)(5,25,26,20,10,11)(7,23,30,22,8,15) [1/6, 1/2, 5/6] + (1,29,8,7,26,16,14,23,22,11)(2,9,25,3,4,17,24,10,18,19)(5,30,6,13,27,20,15,21,28,12) [3/10, 1/2, 7/10] + """ + class_representatives = self.conjugacy_classes_representatives() + Ev_list = self._gap_group.ReflectionEigenvalues().sage() + return Family(class_representatives, + lambda w: Ev_list[class_representatives.index(w)]) + + @cached_method + def reflection_eigenvalues(self, w, is_class_representative=False): + r""" + Return the reflection eigenvalue of ``w`` in ``self``. + + INPUT: + + - ``is_class_representative`` -- boolean (default ``True``) whether to + compute instead on the conjugacy class representative. + + .. SEEALSO:: :meth:`reflection_eigenvalues_family` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print('%s %s'%(w.reduced_word(), W.reflection_eigenvalues(w))) # optional - gap3 + [] [0, 0] + [2] [1/2, 0] + [1] [1/2, 0] + [1, 2] [1/3, 2/3] + [2, 1] [1/3, 2/3] + [1, 2, 1] [1/2, 0] + """ + if not is_class_representative: + w = w.conjugacy_class_representative() + return self.reflection_eigenvalues_family()[w] + + @cached_method + def simple_roots(self): + r""" + Return the simple roots of ``self``. + + These are the roots corresponding to the simple reflections. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.simple_roots() # optional - gap3 + Finite family {1: (1, 0), 2: (0, 1)} + + sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 + sage: W.simple_roots() # optional - gap3 + Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, 0, 1)} + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.simple_roots() # optional - gap3 + Finite family {1: (1, 0), 2: (-1, 1)} + + sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 + sage: W.simple_roots() # optional - gap3 + Finite family {1: (1, 0, 0, 0, 0), 2: (0, 1, 0, 0, 0), 3: (0, 0, 1, 0, 0), 4: (0, 0, 0, 1, 0), 5: (0, 0, 0, -1, 1)} + """ + from sage.sets.family import Family + return Family({ind:self.roots()[i] for i,ind in enumerate(self._index_set)}) + + def simple_root(self, i): + r""" + Return the simple root with index ``i``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.simple_root(1) # optional - gap3 + (1, 0, 0) + sage: W.simple_root(2) # optional - gap3 + (0, 1, 0) + sage: W.simple_root(3) # optional - gap3 + (0, 0, 1) + + TESTS:: + + sage: W.simple_root(0) # optional - gap3 + Traceback (most recent call last): + ... + KeyError: 0 + """ + return self.simple_roots()[i] + + @cached_method + def simple_coroots(self): + r""" + Return the simple coroots of ``self``. + + These are the coroots corresponding to the simple reflections. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.simple_coroots() # optional - gap3 + Finite family {1: (2, -1), 2: (-1, 2)} + + sage: W = ReflectionGroup((1,1,4), (2,1,2)) # optional - gap3 + sage: W.simple_coroots() # optional - gap3 + Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, 2, -2), 5: (0, 0, 0, -1, 2)} + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.simple_coroots() # optional - gap3 + Finite family {1: (-2*E(3) - E(3)^2, 0), 2: (-1, 1)} + + sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 + sage: W.simple_coroots() # optional - gap3 + Finite family {1: (2, -1, 0, 0, 0), 2: (-1, 2, -1, 0, 0), 3: (0, -1, 2, 0, 0), 4: (0, 0, 0, -2*E(3) - E(3)^2, 0), 5: (0, 0, 0, -1, 1)} + """ + from sage.sets.family import Family + coroots = self._gap_group.simpleCoroots.sage() + for i,coroot in enumerate(coroots): + coroot = vector(coroot) + coroot.set_immutable() + coroots[i] = coroot + return Family({ind:coroots[i] for i,ind in enumerate(self.index_set())}) + + def simple_coroot(self, i): + r""" + Return the simple root with index ``i``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.simple_coroot(1) # optional - gap3 + (2, -1, 0) + """ + return self.simple_coroots()[i] + + @cached_method + def independent_roots(self): + r""" + Return a collection of simple roots generating the underlying + vector space of ``self``. + + For well-generated groups, these are all simple roots. + Otherwise, a linearly independent subset of the simple roots is + chosen. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.independent_roots() # optional - gap3 + Finite family {1: (1, 0), 2: (0, 1)} + + sage: W = ReflectionGroup((4,2,3)) # optional - gap3 + sage: W.simple_roots() # optional - gap3 + Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 3: (-1, 1, 0), 4: (0, -1, 1)} + sage: W.independent_roots() # optional - gap3 + Finite family {1: (1, 0, 0), 2: (-E(4), 1, 0), 4: (0, -1, 1)} + """ + Delta = self.simple_roots() + if self.is_well_generated(): + return Delta + + from sage.sets.family import Family + basis = dict() + for i,ind in enumerate(self._index_set): + vec = Delta[ind] + if Matrix(basis.values()+[vec]).rank() == len(basis) + 1: + basis[ind] = vec + return Family(basis) + + @cached_method + def base_change_matrix(self): + r""" + Return the base change from the standard basis of the vector + space of ``self`` to the basis given by the independent roots of + ``self``. + + .. TODO:: + + For non-well-generated groups there is a conflict with + construction of the matrix for an element. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.base_change_matrix() # optional - gap3 + [1 0] + [0 1] + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: W.base_change_matrix() # optional - gap3 + [1 0 0] + [0 1 0] + [0 0 1] + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.base_change_matrix() # optional - gap3 + [1 0] + [1 1] + + sage: W = ReflectionGroup((4,2,2)) # optional - gap3 + sage: W.base_change_matrix() # optional - gap3 + [ 1 0] + [E(4) 1] + """ + return Matrix( list(self.independent_roots()) ).inverse() + + @cached_method + def roots(self): + r""" + Return all roots corresponding to all reflections of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.roots() # optional - gap3 + [(1, 0), (0, 1), (1, 1), (-1, 0), (0, -1), (-1, -1)] + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.roots() # optional - gap3 + [(1, 0), (-1, 1), (E(3), 0), (-E(3), 1), (0, 1), (1, -1), + (0, E(3)), (1, -E(3)), (E(3)^2, 0), (-E(3)^2, 1), + (E(3), -1), (E(3), -E(3)), (0, E(3)^2), (1, -E(3)^2), + (-1, E(3)), (-E(3), E(3)), (E(3)^2, -1), (E(3)^2, -E(3)), + (E(3), -E(3)^2), (-E(3)^2, E(3)), (-1, E(3)^2), + (-E(3), E(3)^2), (E(3)^2, -E(3)^2), (-E(3)^2, E(3)^2)] + + sage: W = ReflectionGroup((4,2,2)) # optional - gap3 + sage: W.roots() # optional - gap3 + [(1, 0), (-E(4), 1), (-1, 1), (-1, 0), (E(4), 1), (1, 1), + (0, -E(4)), (E(4), -1), (E(4), E(4)), (0, E(4)), + (E(4), -E(4)), (0, 1), (1, -E(4)), (1, -1), (0, -1), + (1, E(4)), (-E(4), 0), (-1, E(4)), (E(4), 0), (-E(4), E(4)), + (-E(4), -1), (-E(4), -E(4)), (-1, -E(4)), (-1, -1)] + + sage: W = ReflectionGroup((1,1,4), (3,1,2)) # optional - gap3 + sage: W.roots() # optional - gap3 + [(1, 0, 0, 0, 0), (0, 1, 0, 0, 0), (0, 0, 1, 0, 0), + (0, 0, 0, 1, 0), (0, 0, 0, -1, 1), (1, 1, 0, 0, 0), + (0, 1, 1, 0, 0), (1, 1, 1, 0, 0), (-1, 0, 0, 0, 0), + (0, -1, 0, 0, 0), (0, 0, -1, 0, 0), (-1, -1, 0, 0, 0), + (0, -1, -1, 0, 0), (-1, -1, -1, 0, 0), (0, 0, 0, E(3), 0), + (0, 0, 0, -E(3), 1), (0, 0, 0, 0, 1), (0, 0, 0, 1, -1), + (0, 0, 0, 0, E(3)), (0, 0, 0, 1, -E(3)), (0, 0, 0, E(3)^2, 0), + (0, 0, 0, -E(3)^2, 1), (0, 0, 0, E(3), -1), (0, 0, 0, E(3), -E(3)), + (0, 0, 0, 0, E(3)^2), (0, 0, 0, 1, -E(3)^2), (0, 0, 0, -1, E(3)), + (0, 0, 0, -E(3), E(3)), (0, 0, 0, E(3)^2, -1), + (0, 0, 0, E(3)^2, -E(3)), (0, 0, 0, E(3), -E(3)^2), + (0, 0, 0, -E(3)^2, E(3)), (0, 0, 0, -1, E(3)^2), + (0, 0, 0, -E(3), E(3)^2), (0, 0, 0, E(3)^2, -E(3)^2), + (0, 0, 0, -E(3)^2, E(3)^2)] + """ + roots = [vector(sage_eval(str(root).replace("^","**"))) + for root in self._gap_group.roots] + for v in roots: + v.set_immutable() + return roots + + @cached_method + def braid_relations(self): + r""" + Return the braid relations of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.braid_relations() # optional - gap3 + [[[2, 1, 2], [1, 2, 1]]] + + sage: W = ReflectionGroup((2,1,3)) # optional - gap3 + sage: W.braid_relations() # optional - gap3 + [[[2, 1, 2, 1], [1, 2, 1, 2]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]] + + sage: W = ReflectionGroup((2,2,3)) # optional - gap3 + sage: W.braid_relations() # optional - gap3 + [[[2, 1, 2], [1, 2, 1]], [[3, 1], [1, 3]], [[3, 2, 3], [2, 3, 2]]] + """ + return self._gap_group.BraidRelations().sage() + + def fundamental_invariants(self): + r""" + Return the fundamental invariants of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: W.fundamental_invariants() # optional - gap3 + (-2*x0^2 + 2*x0*x1 - 2*x1^2, 6*x0^2*x1 - 6*x0*x1^2) + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W.fundamental_invariants() # optional - gap3 + (x0^3 + x1^3, x0^3*x1^3) + """ + import re + from sage.rings.polynomial.all import PolynomialRing + + if not self.is_irreducible(): + return sum([W.fundamental_invariants() for W in self.irreducible_components() ],tuple()) + + I = [ str(p) for p in gap3('List(Invariants(%s),x->ApplyFunc(x,List([0..%s],i->Mvp(SPrint("x",i)))))'%(self._gap_group._name,self.rank()-1)) ] + P = PolynomialRing(QQ,['x%s'%i for i in range(0,self.rank())]) + x = P.gens() + for i in range(len(I)): + I[i] = I[i].replace('^','**') + I[i] = re.compile('E(\d\d*)').sub(r'E(\1)', I[i]) + I[i] = re.compile('(\d)E\(').sub(r'\1*E(', I[i]) + for j in range(len(x)): + I[i] = I[i].replace('x%s'%j,'*x[%s]'%j) + I[i] = I[i].replace("+*","+").replace("-*","-").replace("ER(5)","*(E(5)-E(5)**2-E(5)**3+E(5)**4)").lstrip("*") + # sage_eval is used since eval kills the rational entries! + I = [ sage_eval(p, locals={'x':x}) for p in I ] + return tuple(sorted(I,lambda f,g: cmp(f.degree(),g.degree()))) + + def cartan_matrix(self): + r""" + Return the Cartan matrix associated with ``self``. + + If ``self`` is crystallographic, the returned Cartan matrix is + an instance of :class:`CartanMatrix`, and a normal matrix + otherwise. + + Let `s_1, \ldots, s_n` be a set of reflections which generate + ``self`` with associated simple roots `s_1,\ldots,s_n` and + simple coroots `s^\vee_i`. Then the Cartan matrix `C = (c_{ij})` + is given by `s^\vee_i(s_j)`. The Cartan matrix completely + determines the reflection representation if the `s_i` are + linearly independent. + + EXAMPLES:: + + sage: ReflectionGroup(['A',4]).cartan_matrix() # optional - gap3 + [ 2 -1 0 0] + [-1 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -1 2] + + sage: ReflectionGroup(['H',4]).cartan_matrix() # optional - gap3 + [ 2 E(5)^2 + E(5)^3 0 0] + [E(5)^2 + E(5)^3 2 -1 0] + [ 0 -1 2 -1] + [ 0 0 -1 2] + + sage: ReflectionGroup(4).cartan_matrix() # optional - gap3 + [-2*E(3) - E(3)^2 E(3)^2] + [ -E(3)^2 -2*E(3) - E(3)^2] + + sage: ReflectionGroup((4,2,2)).cartan_matrix() # optional - gap3 + [ 2 -2*E(4) -2] + [ E(4) 2 1 - E(4)] + [ -1 1 + E(4) 2] + """ + # an alternative implementation is + # Matrix(tuple(W.simple_coroots()))*Matrix(tuple(W.simple_roots())).transpose() + # this should be implemented once we get the simple roots in an easy way + if self.is_crystallographic(): + from sage.combinat.root_system.cartan_matrix import CartanMatrix as CartanMat + else: + from sage.matrix.all import Matrix as CartanMat + return CartanMat(self._gap_group.CartanMat().sage()) + + def invariant_form(self, brute_force=False): + r""" + Return the form that is invariant under the action of ``self``. + + This is unique only up to a global scalar on the irreducible + components. + + INPUT: + + - ``brute_force`` -- if ``True``, the computation is done by + applying the Reynolds operator; this is, the invariant form + of `e_i` and `e_j` is computed as the sum + `\langle w(e_i), w(e_j)\rangle`, where + `\langle \cdot, \cdot\rangle` is the standard scalar product + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: F = W.invariant_form(); F # optional - gap3 + [ 1 -1/2 0] + [-1/2 1 -1/2] + [ 0 -1/2 1] + + To check that this is indeed the invariant form, see:: + + sage: S = W.simple_reflections() # optional - gap3 + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 + True + + sage: W = ReflectionGroup(['B',3]) # optional - gap3 + sage: F = W.invariant_form(); F # optional - gap3 + [ 1 -1 0] + [-1 2 -1] + [ 0 -1 2] + sage: w = W.an_element().to_matrix() # optional - gap3 + sage: w * F * w.transpose().conjugate() == F # optional - gap3 + True + + sage: S = W.simple_reflections() # optional - gap3 + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose() for i in W.index_set() ) # optional - gap3 + True + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: F = W.invariant_form(); F # optional - gap3 + [1 0] + [0 1] + + sage: S = W.simple_reflections() # optional - gap3 + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + True + + It also worked for badly generated groups:: + + sage: W = ReflectionGroup(7) # optional - gap3 + sage: W.is_well_generated() # optional - gap3 + False + + sage: F = W.invariant_form(); F # optional - gap3 + [1 0] + [0 1] + sage: S = W.simple_reflections() # optional - gap3 + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + True + + And also for reducible types:: + + sage: W = ReflectionGroup(['B',3],(4,2,3),4,7); W # optional - gap3 + Reducible complex reflection group of rank 10 and type B3 x G(4,2,3) x ST4 x ST7 + sage: F = W.invariant_form(); S = W.simple_reflections() # optional - gap3 + sage: all( F == S[i].matrix()*F*S[i].matrix().transpose().conjugate() for i in W.index_set() ) # optional - gap3 + True + + TESTS:: + + sage: tests = [['A',3],['B',3],['F',4],(4,2,2),4,7] # optional - gap3 + sage: for ty in tests: # optional - gap3 + ....: W = ReflectionGroup(ty) # optional - gap3 + ....: A = W.invariant_form() # optional - gap3 + ....: B = W.invariant_form(brute_force=True) # optional - gap3 + ....: print ty, A == B/B[0,0] # optional - gap3 + ['A', 3] True + ['B', 3] True + ['F', 4] True + (4, 2, 2) True + 4 True + 7 True + """ + if brute_force: + return self._invariant_form_brute_force() + + n = self.rank() + from sage.matrix.constructor import zero_matrix + + if self.is_crystallographic(): + ring = QQ + else: + from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField + ring = UniversalCyclotomicField() + + form = zero_matrix(ring, n, n) + + C = self.cartan_matrix() + if not self.is_well_generated(): + indep_inds = sorted(self._index_set_inverse[key] for key in self.independent_roots().keys()) + C = C.matrix_from_rows_and_columns(indep_inds,indep_inds) + + for j in range(n): + for i in range(j): + if C[j,i] != 0: + form[j,j] = form[i,i] * \ + ( C[i,j] * C[j,j].conjugate() ) / \ + ( C[j,i].conjugate() * C[i,i] ) + if form[j,j] == 0: + form[j,j] = ring.one() + for j in range(n): + for i in range(j): + form[j, i] = C[i, j] * form[i, i] / C[i,i] + form[i, j] = form[j, i].conjugate() + + B = self.base_change_matrix() + form = B*form*B.conjugate().transpose() + form /= form[0,0] + form.set_immutable() + return form + + def _invariant_form_brute_force(self): + r""" + Return the form that is invariant under the action of ``self``. + + This brute force algorithm is only kept for possible testing. + + EXAMPLES:: + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: W._invariant_form_brute_force() # optional - gap3 + [1 0] + [0 1] + """ + from sage.misc.cachefunc import cached_function + + Phi = self.roots() + + base_change = self.base_change_matrix() + Delta = tuple(self.independent_roots()) + basis_is_Delta = base_change.is_one() + if not basis_is_Delta: + Delta = [beta * base_change for beta in Delta] + + S = self.simple_reflections() + n = self.rank() + + def act_on_root(w,beta): + if basis_is_Delta: + return w.act_on_root(beta) + else: + return beta * w.to_matrix() + + @cached_function + def invariant_value(i,j): + if i > j: + return invariant_value(j,i).conjugate() + val = sum((act_on_root(w,Delta[i])) * (act_on_root(w,Delta[j])).conjugate() for w in self) + if val in QQ: + val = QQ(val) + return val + + coeffs = [] + for i in self.index_set(): + coeff = 1-E(S[i].order()) + if coeff in QQ: + coeff = QQ(coeff) + coeffs.append(coeff) + + return Matrix([ [ invariant_value(i,j)/self.cardinality() for j in range(n) ] for i in range(n) ]) + + def set_reflection_representation(self,refl_repr=None): + r""" + Set the reflection representation of ``self``. + + INPUT: + + - ``refl_repr`` -- a dictionary representing the matrices of the + generators of ``self`` with keys given by the index set, or + ``None`` to reset to the default reflection representation + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 + [1 0] + [0 1] + ----- + [ 1 1] + [ 0 -1] + ----- + [-1 0] + [ 1 1] + ----- + [-1 -1] + [ 1 0] + ----- + [ 0 1] + [-1 -1] + ----- + [ 0 -1] + [-1 0] + ----- + + sage: W.set_reflection_representation({1: matrix([[0,1,0],[1,0,0],[0,0,1]]), 2: matrix([[1,0,0],[0,0,1],[0,1,0]])}) # optional - gap3 + sage: for w in W: w.to_matrix(); print("-----") # optional - gap3 + [1 0 0] + [0 1 0] + [0 0 1] + ----- + [1 0 0] + [0 0 1] + [0 1 0] + ----- + [0 1 0] + [1 0 0] + [0 0 1] + ----- + [0 0 1] + [1 0 0] + [0 1 0] + ----- + [0 1 0] + [0 0 1] + [1 0 0] + ----- + [0 0 1] + [0 1 0] + [1 0 0] + ----- + sage: W.set_reflection_representation() # optional - gap3 + """ + if refl_repr is None or set(refl_repr.keys()) == set(self.index_set()): + self._reflection_representation = refl_repr + else: + raise ValueError("the reflection representation must be defined for the complete index set") + + class Element(PermutationGroupElement): + + #@cached_in_parent_method + def conjugacy_class_representative(self): + r""" + Return a representative of the conjugacy class of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print('%s %s'%(w.reduced_word(), w.conjugacy_class_representative().reduced_word())) # optional - gap3 + [] [] + [2] [1] + [1] [1] + [1, 2] [1, 2] + [2, 1] [1, 2] + [1, 2, 1] [1] + """ + W = self.parent() + for w in W._conjugacy_classes.keys(): + if self in W._conjugacy_classes[w]: + return w + return W.conjugacy_classes_representatives()[ gap3("PositionClass(%s,%s)"%(W._gap_group._name,self)).sage()-1 ] + + def conjugacy_class(self): + r""" + Return the conjugacy class of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: sorted(w.conjugacy_class()) # optional - gap3 + [()] + [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] + [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] + [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] + [(1,2,6)(3,4,5), (1,6,2)(3,5,4)] + [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6), (1,5)(2,4)(3,6)] + """ + W = self.parent() + if self not in W.conjugacy_classes_representatives(): + self = self.conjugacy_class_representative() + if self in W._conjugacy_classes.keys(): + return W._conjugacy_classes[self] + gens = W.simple_reflections() + count = 0 + orbit = [self] + orbit_set = set(orbit) + while count < len(orbit): + w = orbit[count] + count += 1 + for s in gens: + w_new = s*w*s**-1 + if w_new not in orbit_set: + orbit.append(w_new) + orbit_set.add(w_new) + orbit_set = frozenset(orbit_set) + W._conjugacy_classes[self] = orbit_set + return orbit_set + + def reduced_word(self): + r""" + Return a word in the simple reflections to obtain ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((5,1,1), index_set=['a'], hyperplane_index_set=['x'], reflection_index_set=['A','B','C','D']) # optional - gap3 + sage: [w.reduced_word() for w in W] # optional - gap3 + [[], ['a'], ['a', 'a'], ['a', 'a', 'a'], ['a', 'a', 'a', 'a']] + + .. SEEALSO:: :meth:`reduced_word_in_reflections` + """ + I = self.parent()._index_set + return [I[i] for i in self._reduced_word] + + @lazy_attribute + def _reduced_word(self): + r""" + Computes a reduced word and stores it into ``self._reduced_word``. + + TESTS:: + + sage: W = ReflectionGroup((5,1,1)) # optional - gap3 + sage: w = W.an_element() # optional - gap3 + sage: w._reduced_word # optional - gap3 + [0] + """ + W = self.parent() + gens = [W.simple_reflection(j) for j in W._index_set] + return _gap_factorization(self, gens) + + #@cached_in_parent_method + def reduced_word_in_reflections(self): + r""" + Return a word in the reflections to obtain ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((5,1,1), index_set=['a'], reflection_index_set=['A','B','C','D']) # optional - gap3 + sage: [w.reduced_word_in_reflections() for w in W] # optional - gap3 + [[], ['A'], ['B'], ['C'], ['D']] + + .. SEEALSO:: :meth:`reduced_word` + """ + if self.is_one(): + return [] + + W = self.parent() + gens = [W.reflection(j) for j in W._reflection_index_set] + word = _gap_factorization(self, gens) + return [self.parent()._reflection_index_set[i] for i in word] + + def length(self): + r""" + Return the length of ``self`` in generating reflections. + + This is the minimal numbers of generating reflections needed + to obtain ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(4) # optional - gap3 + sage: for w in W: print w.reduced_word(), w.length() # optional - gap3 + [] 0 + [1] 1 + [2] 1 + [1, 1] 2 + [1, 2] 2 + [2, 1] 2 + [2, 2] 2 + [1, 1, 2] 3 + [1, 2, 1] 3 + [1, 2, 2] 3 + [2, 1, 1] 3 + [2, 2, 1] 3 + [1, 1, 2, 1] 4 + [1, 1, 2, 2] 4 + [1, 2, 1, 1] 4 + [1, 2, 2, 1] 4 + [2, 1, 1, 2] 4 + [2, 2, 1, 1] 4 + [1, 1, 2, 1, 1] 5 + [1, 1, 2, 2, 1] 5 + [1, 2, 1, 1, 2] 5 + [1, 2, 2, 1, 1] 5 + [1, 1, 2, 1, 1, 2] 6 + [1, 1, 2, 2, 1, 1] 6 + """ + return len(self.reduced_word()) + + #@cached_in_parent_method + def reflection_length(self, in_unitary_group=False): + r""" + Return the reflection length of ``self``. + + This is the minimal numbers of reflections needed to obtain + ``self``. + + INPUT: + + - ``in_unitary_group`` -- (default: ``False``) if ``True``, + the reflection length is computed in the unitary group + which is the dimension of the move space of ``self`` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 1, 2, 2] + + sage: W = ReflectionGroup((2,1,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 1, 1, 2, 2, 2] + + sage: W = ReflectionGroup((2,2,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 2] + + sage: W = ReflectionGroup((3,1,2)) # optional - gap3 + sage: sorted([t.reflection_length() for t in W]) # optional - gap3 + [0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] + """ + W = self.parent() + if self in W.conjugacy_classes_representatives(): + if in_unitary_group or W.is_real(): + return W.rank() - self.reflection_eigenvalues(is_class_representative=True).count(0) + else: + return len(self.reduced_word_in_reflections()) + else: + w = self.conjugacy_class_representative() + # the following assert a possible implementation bug and + # is hopefully never needed + assert w in self.parent().conjugacy_classes_representatives() + return w.reflection_length(in_unitary_group=in_unitary_group) + + #@cached_in_parent_method + def to_matrix(self): + r""" + Return ``self`` as a matrix acting on the underlying vector + space. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print(w.reduced_word()) # optional - gap3 + ....: print(w.to_matrix()) # optional - gap3 + [] + [1 0] + [0 1] + [2] + [ 1 1] + [ 0 -1] + [1] + [-1 0] + [ 1 1] + [1, 2] + [-1 -1] + [ 1 0] + [2, 1] + [ 0 1] + [-1 -1] + [1, 2, 1] + [ 0 -1] + [-1 0] + """ + W = self.parent() + if W._reflection_representation is None: + Delta = W.independent_roots() + Phi = W.roots() + M = Matrix([Phi[self(Phi.index(alpha)+1)-1] for alpha in Delta]) + return W.base_change_matrix() * M + else: + refl_repr = W._reflection_representation + id_mat = identity_matrix(QQ,refl_repr[W.index_set()[0]].nrows()) + return prod([refl_repr[i] for i in self.reduced_word()], id_mat) + + matrix = to_matrix + + def to_permutation_of_roots(self): + r""" + Return ``self`` as a permutation of the roots with indexing + starting at `1`. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: perm = w.to_permutation_of_roots(); print perm, perm==w # optional - gap3 + () True + (1,3)(2,5)(4,6) True + (1,4)(2,3)(5,6) True + (1,6,2)(3,5,4) True + (1,2,6)(3,4,5) True + (1,5)(2,4)(3,6) True + """ + return PermutationGroupElement(self) + + #@cached_in_parent_method + def fix_space(self): + r""" + Return the fix space of ``self``. + + This is the sub vector space of the underlying vector space + on which ``self`` acts trivially. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: w.reduced_word() # optional - gap3 + ....: w.fix_space() # optional - gap3 + [] + Vector space of degree 2 and dimension 2 over Rational Field + Basis matrix: + [1 0] + [0 1] + [2] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [1 0] + [1] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [0 1] + [1, 2] + Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + [2, 1] + Vector space of degree 2 and dimension 0 over Rational Field + Basis matrix: + [] + [1, 2, 1] + Vector space of degree 2 and dimension 1 over Rational Field + Basis matrix: + [ 1 -1] + + sage: W = ReflectionGroup(23) # optional - gap3 + sage: W.an_element().fix_space() # optional - gap3 + Vector space of degree 3 and dimension 2 over Universal Cyclotomic Field + Basis matrix: + [0 1 0] + [0 0 1] + """ + I = identity_matrix(QQ, self.parent().rank()) + return (self.to_matrix() - I).right_kernel() + + #@cached_in_parent_method + def reflection_eigenvalues(self, is_class_representative=False): + r""" + Return the reflection eigenvalues of ``self``. + + INPUT: + + - ``is_class_representative`` -- (default: ``False``) whether + to first replace ``self`` by the representative of its + conjugacy class + + EXAMPLES:: + + sage: W = ReflectionGroup(4) # optional - gap3 + sage: for w in W: w.reflection_eigenvalues() # optional - gap3 + [0, 0] + [1/3, 0] + [1/3, 0] + [2/3, 0] + [1/6, 1/2] + [1/6, 1/2] + [2/3, 0] + [1/4, 3/4] + [1/4, 3/4] + [1/4, 3/4] + [1/4, 3/4] + [1/4, 3/4] + [1/3, 0] + [1/2, 5/6] + [1/3, 0] + [1/2, 5/6] + [1/2, 5/6] + [1/2, 5/6] + [1/6, 1/2] + [2/3, 0] + [1/6, 1/2] + [2/3, 0] + [1/2, 1/2] + [1/4, 3/4] + """ + return self.parent().reflection_eigenvalues(self, is_class_representative=is_class_representative) + + #@cached_in_parent_method + def galois_conjugates(self): + r""" + Return all Galois conjugates of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(4) # optional - gap3 + sage: for w in W: print(w.galois_conjugates()) # optional - gap3 + [[1 0] + [0 1]] + [[ 1 0] + [ 0 E(3)], [ 1 0] + [ 0 E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2]] + [[ 1 0] + [ 0 E(3)^2], [ 1 0] + [ 0 E(3)]] + [[ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2]] + [[ -1 0] + [ 0 -E(3)], [ -1 0] + [ 0 -E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) + 4/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [ 4/3*E(3) + 2/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2]] + [[ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2], + [-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 1/3*E(3) + 2/3*E(3)^2]] + [[-1/3*E(3) + 1/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2]] + [[ -1 0] + [ 0 -E(3)^2], [ -1 0] + [ 0 -E(3)]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [-4/3*E(3) - 2/3*E(3)^2 -2/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) - 4/3*E(3)^2 -1/3*E(3) - 2/3*E(3)^2]] + [[-1 0] + [ 0 -1]] + [[-1/3*E(3) + 1/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2] + [ 2/3*E(3) - 2/3*E(3)^2 1/3*E(3) - 1/3*E(3)^2], + [ 1/3*E(3) - 1/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2] + [-2/3*E(3) + 2/3*E(3)^2 -1/3*E(3) + 1/3*E(3)^2]] + """ + rk = self.parent().rank() + M = self.to_matrix().list() + m = lcm([x.conductor() if hasattr(x,"conductor") else 1 for x in M]) + M_gals = [x.galois_conjugates(m) if hasattr(x,"galois_conjugates") else [x] for x in M] + conjugates = [] + for i in range(len(M_gals[0])): + conjugates.append(Matrix(rk, [X[i] for X in M_gals])) + return conjugates + + def __cmp__(self, other): + r""" + Compare ``self`` with ``other``. + + Without this comparison method, the initialization of this + permutation group fails :-(. + + TESTS:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: a,b = W.gens() # optional - gap3 + sage: a.__cmp__(b) # optional - gap3 + 1 + sage: b.__cmp__(a) # optional - gap3 + -1 + """ + return super(ComplexReflectionGroup.Element, self).__cmp__(other) + +class IrreducibleComplexReflectionGroup(ComplexReflectionGroup): + + def _repr_(self): + r""" + Return the string representation of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)); W # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: W = ReflectionGroup((3,1,4)); W # optional - gap3 + Irreducible complex reflection group of rank 4 and type G(3,1,4) + """ + type_str = self._irrcomp_repr_(self._type[0]) + return 'Irreducible complex reflection group of rank %s and type %s'%(self._rank,type_str) + + def elements_below_coxeter_element(self, c=None): + r""" + Return all elements in ``self`` in the interval `[1,c]` in the + absolute order of ``self``. + + This order is defined by + + .. MATH:: + + \omega \leq_R \tau \Leftrightarrow \ell_R(\omega) + + \ell_R(\omega^{-1} \tau) = \ell_R(\tau), + + where `\ell_R` denotes the reflection length. + + .. NOTE:: + + ``self`` is assumed to be well-generated. + + INPUT: + + - ``c`` -- (default: ``None``) if an element ``c`` is given, it + is used as the maximal element in the interval; if a list is + given, the union of the various maximal elements is computed + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + + sage: sorted( w.reduced_word() for w in W.elements_below_coxeter_element() ) # optional - gap3 + [[], [1], [1, 2], [1, 2, 1], [2]] + + sage: sorted( w.reduced_word() for w in W.elements_below_coxeter_element(W.from_reduced_word([2,1])) ) # optional - gap3 + [[], [1], [1, 2, 1], [2], [2, 1]] + + sage: sorted( w.reduced_word() for w in W.elements_below_coxeter_element(W.from_reduced_word([2])) ) # optional - gap3 + [[], [2]] + """ + if c in self: + cs = [c] + elif c is None: + cs = [self.coxeter_element()] + else: + cs = list(c) + l = cs[0].reflection_length(in_unitary_group=True) + f = lambda pi: any(pi.reflection_length(in_unitary_group=True) + + (c*pi**-1).reflection_length(in_unitary_group=True) == l + for c in cs) + # first computing the conjugacy classes only needed if the interaction with gap3 is slow due to a bug + #self.conjugacy_classes() + return filter(f, self) + + # TODO: lift to ComplexReflectionGroups.Finite + # this method can be defined for well-generated, finite, + # irreducible complex reflection group. The current + # implementation uses this particular connection to chevie. + # TODO: have a cached and an uncached version + @cached_method + def noncrossing_partition_lattice(self, c=None, L=None, in_unitary_group=False): + r""" + Return the interval `[1,c]` in the absolute order of + ``self`` as a finite lattice. + + .. SEEALSO:: :meth:`elements_below_coxeter_element` + + INPUT: + + - ``c`` -- (default: ``None``) if an element ``c`` in ``self`` is + given, it is used as the maximal element in the interval + + - ``L`` -- (default: ``None``) if a subset ``L`` (must be hashable!) + of ``self`` is given, it is used as the underlying set (only + cover relations are checked) + + - ``in_unitary_group`` -- (default: ``False``) if ``False``, the + relation is given by `\sigma \leq \tau` if + `l_R(\sigma) + l_R(\sigma^{-1}\tau) = l_R(\tau)`; + if ``True``, the relation is given by `\sigma \leq \tau` if + `\dim(\mathrm{Fix}(\sigma)) + \dim(\mathrm{Fix}(\sigma^{-1}\tau)) + = \dim(\mathrm{Fix}(\tau))` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + + sage: sorted( w.reduced_word() for w in W.noncrossing_partition_lattice() ) # optional - gap3 + [[], [1], [1, 2], [1, 2, 1], [2]] + + sage: sorted( w.reduced_word() for w in W.noncrossing_partition_lattice(W.from_reduced_word([2,1])) ) # optional - gap3 + [[], [1], [1, 2, 1], [2], [2, 1]] + + sage: sorted( w.reduced_word() for w in W.noncrossing_partition_lattice(W.from_reduced_word([2])) ) # optional - gap3 + [[], [2]] + """ + from sage.combinat.posets.all import Poset, LatticePoset + if c is None: + c = self.coxeter_element() + + smart_covers = not in_unitary_group + + if self.is_real(): + smart_covers = in_unitary_group = True + + R = self.reflections() + if L is None: + L = self.elements_below_coxeter_element(c=c) + if c.is_coxeter_element(): + smart_covers = in_unitary_group = True + rels = [] + ref_lens = {w: w.reflection_length(in_unitary_group=in_unitary_group) + for w in L} + if smart_covers: + for pi in L: + for t in R: + tau = pi*t + if tau in L and ref_lens[pi] + 1 == ref_lens[tau]: + rels.append((pi,tau)) + else: + rels = [(pi,tau) for pi in L for tau in L + if ref_lens[pi] + ref_lens[pi.inverse()*tau] == ref_lens[tau]] + P = Poset((L,rels), cover_relations=smart_covers, facade=True) + if P.is_lattice(): + return LatticePoset(P) + else: + return P + + # TODO: lift to ComplexReflectionGroups.Finite + # this method can be defined for well-generated, finite, + # irreducible complex reflection group. The current + # implementation uses this particular connection to chevie. + def generalized_noncrossing_partitions(self, m, c=None, positive=False): + r""" + Return the set of all chains of length ``m`` in the noncrossing + partition lattice of ``self``, see + :meth:`noncrossing_partition_lattice`. + + .. NOTE:: + + ``self`` is assumed to be well-generated + + INPUT: + + - ``c`` -- (default: ``None``) if an element ``c`` in ``self`` + is given, it is used as the maximal element in the interval + + - ``positive`` -- (default: ``False``) if ``True``, only those + generalized noncrossing partitions of full support are returned + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + + sage: sorted([w.reduced_word() for w in chain] # optional - gap3 + ....: for chain in W.generalized_noncrossing_partitions(2)) # optional - gap3 + [[[], [], [1, 2]], + [[], [1], [2]], + [[], [1, 2], []], + [[], [1, 2, 1], [1]], + [[], [2], [1, 2, 1]], + [[1], [], [2]], + [[1], [2], []], + [[1, 2], [], []], + [[1, 2, 1], [], [1]], + [[1, 2, 1], [1], []], + [[2], [], [1, 2, 1]], + [[2], [1, 2, 1], []]] + + sage: sorted([w.reduced_word() for w in chain] # optional - gap3 + ....: for chain in W.generalized_noncrossing_partitions(2, positive=True)) # optional - gap3 + [[[], [1, 2], []], + [[], [1, 2, 1], [1]], + [[1], [2], []], + [[1, 2], [], []], + [[1, 2, 1], [], [1]], + [[1, 2, 1], [1], []], + [[2], [1, 2, 1], []]] + """ + from sage.combinat.combination import Combinations + NC = self.noncrossing_partition_lattice(c=c) + one = self.one() + if c is None: + c = self.coxeter_element() + chains = NC.chains() + NCm = set() + iter = chains.breadth_first_search_iterator() + chain = next(iter) + chain = next(iter) + while len(chain) <= m: + chain.append( c ) + for i in range(len(chain)-1, 0, -1): + chain[i] = chain[i-1]**-1 * chain[i] + k = m + 1 - len(chain) + for positions in Combinations(range(m+1),k): + ncm = [] + for l in range(m+1): + if l in positions: + ncm.append(one) + else: + l_prime = l - len([i for i in positions if i <= l]) + ncm.append(chain[l_prime]) + if not positive or prod(ncm[:-1]).has_full_support(): + NCm.add(tuple(ncm)) + try: + chain = next(iter) + except StopIteration: + chain = range(m + 1) + return NCm + + # TODO: lift to ComplexReflectionGroups.Finite + # this method can be defined for well-generated, finite, + # irreducible complex reflection group. The current + # implementation uses this particular connection to chevie. + # TODO: have a cached and an uncached version + def absolute_poset(self, in_unitary_group=False): + r""" + Return the poset induced by the absolute order of ``self`` as a + finite lattice. + + INPUT: + + - ``in_unitary_group`` -- (default: ``False``) if ``False``, the + relation is given by ``\sigma \leq \tau`` if + `l_R(\sigma) + l_R(\sigma^{-1}\tau) = l_R(\tau)` + If ``True``, the relation is given by `\sigma \leq \tau` if + `\dim(\mathrm{Fix}(\sigma)) + \dim(\mathrm{Fix}(\sigma^{-1}\tau)) + = \dim(\mathrm{Fix}(\tau))`. + + .. SEEALSO:: :meth:`noncrossing_partition_lattice` + + EXAMPLES:: + + sage: P = ReflectionGroup((1,1,3)).absolute_poset(); P # optional - gap3 + Finite poset containing 6 elements + + sage: sorted(w.reduced_word() for w in P) # optional - gap3 + [[], [1], [1, 2], [1, 2, 1], [2], [2, 1]] + + sage: W = ReflectionGroup(4); W # optional - gap3 + Irreducible complex reflection group of rank 2 and type ST4 + sage: W.absolute_poset() # optional - gap3 + Finite poset containing 24 elements + """ + return self.noncrossing_partition_lattice(L=self) + + class Element(ComplexReflectionGroup.Element): + + # TODO: lift to ComplexReflectionGroups.Finite + # this method can be defined for well-generated, finite, + # irreducible complex reflection group. The current + # implementation uses this particular connection to chevie. + #@cached_in_parent_method + def is_coxeter_element(self, which_primitive=1, is_class_representative=False): + r""" + Return ``True`` if ``self`` is a Coxeter element. + + INPUT: + + - ``which_primitive`` -- (default:``1``) for which power of + the first primitive ``h``-th root of unity to look as a + reflection eigenvalue for a regular element + + - ``is_class_representative`` -- boolean (default ``True``) whether + to compute instead on the conjugacy class representative + + .. SEEALSO:: + + :meth:`~IrreducibleComplexReflectionGroup.coxeter_element` + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print('%s %s'%(w.reduced_word(), w.is_coxeter_element())) # optional - gap3 + [] False + [2] False + [1] False + [1, 2] True + [2, 1] True + [1, 2, 1] False + """ + if not self.parent().is_irreducible() or not self.parent().is_well_generated(): + raise ValueError("this method is available for elements in irreducible, well-generated complex reflection groups") + h = self.parent().coxeter_number() + # to check regularity for a Coxeter number h, we get that an eigenvector is regular for free + return any(QQ(ev).denom() == h and QQ(ev).numer() == which_primitive + for ev in self.reflection_eigenvalues(is_class_representative=is_class_representative)) + + #@cached_in_parent_method + def is_h_regular(self, is_class_representative=False): + r""" + Return whether ``self`` is regular. + + This is if ``self`` has an eigenvector with eigenvalue `h` + and which does not lie in any reflection hyperplane. + Here, `h` denotes the Coxeter number. + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print('%s %s'%(w.reduced_word(), w.is_h_regular())) # optional - gap3 + [] False + [2] False + [1] False + [1, 2] True + [2, 1] True + [1, 2, 1] False + """ + if not self.parent().is_irreducible() or not self.parent().is_well_generated(): + raise ValueError("This method is available for elements in irreducible, well-generated complex reflection groups") + h = self.parent().coxeter_number() + # to check regularity for a Coxeter number h, we get that an eigenvector is regular for free + return any(QQ(ev).denom() == h + for ev in self.reflection_eigenvalues(is_class_representative=is_class_representative)) + + #@cached_in_parent_method + def is_regular(self, h, is_class_representative=False): + r""" + Return whether ``self`` is regular. + + This is, if ``self`` has an eigenvector with eigenvalue + ``h`` and which does not lie in any reflection hyperplane. + + - ``is_class_representative`` -- boolean (default ``True``) whether + to compute instead on the conjugacy class representative + + EXAMPLES:: + + sage: W = ReflectionGroup((1,1,3)); h = W.coxeter_number() # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print w.reduced_word(), w.is_regular(h) # optional - gap3 + [] False + [2] False + [1] False + [1, 2] True + [2, 1] True + [1, 2, 1] False + + sage: W = ReflectionGroup(23); h = W.coxeter_number() # optional - gap3 + sage: for w in W: # optional - gap3 + ....: if w.is_regular(h): # optional - gap3 + ....: w.reduced_word() # optional - gap3 + [1, 2, 3] + [2, 1, 3] + [1, 3, 2] + [3, 2, 1] + [2, 1, 2, 3, 2] + [2, 3, 2, 1, 2] + [1, 2, 1, 2, 3, 2, 1] + [1, 2, 3, 2, 1, 2, 1] + [1, 2, 1, 2, 3, 2, 1, 2, 3] + [2, 1, 2, 1, 3, 2, 1, 2, 3] + [2, 1, 2, 3, 2, 1, 2, 1, 3] + [1, 2, 3, 2, 1, 2, 1, 3, 2] + [3, 2, 1, 2, 1, 3, 2, 1, 2] + [1, 2, 1, 2, 1, 3, 2, 1, 2] + [2, 3, 2, 1, 2, 1, 3, 2, 1] + [2, 1, 2, 1, 3, 2, 1, 2, 1] + [2, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] + [1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] + [1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 3] + [1, 2, 1, 2, 3, 2, 1, 2, 1, 3, 2] + [1, 2, 3, 2, 1, 2, 1, 3, 2, 1, 2] + [2, 1, 2, 3, 2, 1, 2, 1, 3, 2, 1] + [2, 1, 2, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] + [1, 2, 1, 3, 2, 1, 2, 1, 3, 2, 1, 2, 3] + """ + evs = self.reflection_eigenvalues(is_class_representative=is_class_representative) + I = identity_matrix(self.parent().rank()) + for ev in evs: + ev = QQ(ev) + if h == ev.denom(): + M = self.to_matrix() - E(ev.denom(),ev.numer()) * I + V = M.right_kernel() + if all(not V.is_subspace(H) for H in self.parent().reflection_hyperplanes()): + return True + return False + +def _gap_factorization(w, gens): + r""" + Return a factorization of ``w`` using the generators ``gens``. + + .. WARNING:: + + This is only available through GAP3 and Chevie. + + EXAMPLES:: + + sage: from sage.combinat.root_system.reflection_group_complex import _gap_factorization + sage: W = ReflectionGroup((1,1,3)) # optional - gap3 + sage: gens = [ W.simple_reflection(i) for i in W.index_set() ] # optional - gap3 + sage: for w in W: _gap_factorization(w,gens) # optional - gap3 + [] + [1] + [0] + [0, 1] + [1, 0] + [0, 1, 0] + """ + gap3.execute('W := GroupWithGenerators(%s)'%str(gens)) + gap3.execute(_gap_factorization_code) + fac = gap3('MinimalWord(W,%s)'%str(w)).sage() + return [i-1 for i in fac] + +_gap_factorization_code = """ +# MinimalWord(G,w) +# given a permutation group G find some expression of minimal length in the +# generators of G and their inverses of the element w (an inverse is +# representated by a negative index). +# To speed up later calls to the same function the fields G.base, G.words, +# G.nbwordslength are kept. +MinimalWord:=function(G,w) + local decode,i,p,g,h,n,bag,nbe,nbf,new,gens,inds; + # to save space elements of G are represented as image of the base, and + # words are represented as: index of previous elt, last generator applied; + if not IsBound(G.base) then + StabChain(G);g:=G; G.base:=[]; + while IsBound(g.orbit) do Add(G.base,g.orbit[1]); g:=g.stabilizer; od; + fi; + w:=OnTuples(G.base,w); + if not IsBound(G.words) then + G.words:=[G.base]; G.lastmult:=[[0,0]]; + G.nbwordslength:=[1]; + fi; + gens:=ShallowCopy(G.generators);inds:=[1..Length(gens)]; + # for g in G.generators do + # if g<>g^-1 then Add(gens,g^-1);Add(inds,-Position(gens,g));fi; + # od; + bag:=Set(G.words); + nbe:=0;nbf:=0; + decode:=function(i)local w;w:=[]; + while i<>1 do Add(w,G.lastmult[i][2]); i:=G.lastmult[i][1];od; + return Reversed(w); + end; + while true do + if w in bag then return decode(Position(G.words,w));fi; + new:=Length(G.words); + for g in [1..Length(gens)] do + for h in [1+Sum(G.nbwordslength{[1..Length(G.nbwordslength)-1]})..new] do + n:=OnTuples(G.words[h],gens[g]); + if n in bag then + nbe:=nbe+1;# if nbe mod 500=1 then Print(".\c");fi; + else + nbf:=nbf+1;# if nbf mod 500=1 then Print("*\c");fi; + Add(G.words,n);Add(G.lastmult,[h,inds[g]]);AddSet(bag,n); + fi; + od; + od; + Add(G.nbwordslength,Length(G.words)-new); + Print("\n",G.nbwordslength[Length(G.nbwordslength)]," elements of length ", + Length(G.nbwordslength)-1); + od; +end;""" + +def _gap_return(S, coerce_obj='self'): + r""" + Return the string ``S`` after a few modifications are done. + + This is a stupid internal function to take GAP output as a string, + replace a few things, to then turn it into a Sage object. + + TESTS:: + + sage: from sage.combinat.root_system.reflection_group_complex import _gap_return + sage: _gap_return("[ (), (1,4)(2,3)(5,6), (1,6,2)(3,5,4) ]") # optional - gap3 + "[self('()',check=False),self('(1,4)(2,3)(5,6)',check=False),self('(1,6,2)(3,5,4)',check=False)]" + """ + S = S.replace(' ','').replace('\n','') + S = S.replace(',(','\',check=False),%s(\'('%coerce_obj).replace('[','[%s(\''%coerce_obj).replace(']','\',check=False)]') + return S + diff --git a/src/sage/combinat/root_system/reflection_group_real.py b/src/sage/combinat/root_system/reflection_group_real.py new file mode 100644 index 00000000000..f0c09efd63d --- /dev/null +++ b/src/sage/combinat/root_system/reflection_group_real.py @@ -0,0 +1,947 @@ +r""" +Finite real reflection groups +------------------------------- + +Let `V` be a finite-dimensional real vector space. A reflection of +`V` is an operator `r \in \operatorname{GL}(V)` that has order `2` +and fixes pointwise a hyperplane in `V`. +In the present implementation, finite real reflection groups are +tied with a root system. + +Finite real reflection groups with root systems have been classified +according to finite Cartan-Killing types. +For more definitions and classification types of finite complex +reflection groups, see :wikipedia:`Complex_reflection_group`. + +The point of entry to work with reflection groups is :func:`~sage.combinat.root_system.reflection_group_real.ReflectionGroup` +which can be used with finite Cartan-Killing types:: + + sage: ReflectionGroup(['A',2]) # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + sage: ReflectionGroup(['F',4]) # optional - gap3 + Irreducible real reflection group of rank 4 and type F4 + sage: ReflectionGroup(['H',3]) # optional - gap3 + Irreducible real reflection group of rank 3 and type H3 + +AUTHORS: + +- Christian Stump (initial version 2011--2015) + +.. WARNING:: + + Uses the GAP3 package *Chevie* which is available as an + experimental package (installed by ``sage -i gap3``) or to + download by hand from `Jean Michel's website + `_. + +.. TODO:: + + - Implement descents, left/right descents, ``has_descent``, + ``first_descent`` directly in this class, since the generic + implementation is much slower. +""" +#***************************************************************************** +# Copyright (C) 2011-2016 Christian Stump +# +# 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, cached_in_parent_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.combinat.root_system.cartan_type import CartanType, CartanType_abstract +from sage.rings.all import ZZ, QQ +from sage.matrix.matrix import is_Matrix +from sage.interfaces.gap3 import gap3 +from sage.combinat.root_system.reflection_group_complex import ComplexReflectionGroup, IrreducibleComplexReflectionGroup +from sage.categories.coxeter_groups import CoxeterGroups +from sage.combinat.root_system.cartan_matrix import CartanMatrix +from sage.combinat.root_system.coxeter_group import is_chevie_available +from sage.misc.sage_eval import sage_eval +from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField +from sage.combinat.root_system.reflection_group_c import reduced_word_c + +def ReflectionGroup(*args,**kwds): + r""" + Construct a finite (complex or real) reflection group as a Sage + permutation group by fetching the permutation representation of the + generators from chevie's database. + + INPUT: + + can be one or multiple of the following: + + - a triple `(r, p, n)` with `p` divides `r`, which denotes the group + `G(r, p, n)` + + - an integer between `4` and `37`, which denotes an exceptional + irreducible complex reflection group + + - a finite Cartan-Killing type + + EXAMPLES: + + Finite reflection groups can be constructed from + + Cartan-Killing classification types:: + + sage: W = ReflectionGroup(['A',3]); W # optional - gap3 + Irreducible real reflection group of rank 3 and type A3 + + sage: W = ReflectionGroup(['H',4]); W # optional - gap3 + Irreducible real reflection group of rank 4 and type H4 + + sage: W = ReflectionGroup(['I',5]); W # optional - gap3 + Irreducible real reflection group of rank 2 and type I2(5) + + the complex infinite family `G(r,p,n)` with `p` divides `r`:: + + sage: W = ReflectionGroup((1,1,4)); W # optional - gap3 + Irreducible real reflection group of rank 3 and type A3 + + sage: W = ReflectionGroup((2,1,3)); W # optional - gap3 + Irreducible real reflection group of rank 3 and type B3 + + Chevalley-Shepard-Todd exceptional classification types:: + + sage: W = ReflectionGroup(23); W # optional - gap3 + Irreducible real reflection group of rank 3 and type H3 + + Cartan types and matrices:: + + sage: ReflectionGroup(CartanType(['A',2])) # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + + sage: ReflectionGroup(CartanType((['A',2],['A',2]))) # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x A2 + + sage: C = CartanMatrix(['A',2]) # optional - gap3 + sage: ReflectionGroup(C) # optional - gap3 + Irreducible real reflection group of rank 2 and type A2 + + multiples of the above:: + + sage: W = ReflectionGroup(['A',2],['B',2]); W # optional - gap3 + Reducible real reflection group of rank 4 and type A2 x B2 + + sage: W = ReflectionGroup(['A',2],4); W # optional - gap3 + Reducible complex reflection group of rank 4 and type A2 x ST4 + + sage: W = ReflectionGroup((4,2,2),4); W # optional - gap3 + Reducible complex reflection group of rank 4 and type G(4,2,2) x ST4 + """ + if not is_chevie_available(): + raise ImportError("the GAP3 package 'chevie' is needed to work with (complex) reflection groups") + gap3.load_package("chevie") + + error_msg = "the input data (%s) is not valid for reflection groups" + + W_types = [] + is_complex = False + for arg in args: + # preparsing + if isinstance(arg, list): + X = tuple(arg) + else: + X = arg + + # precheck for valid input data + if not (isinstance(X, (CartanType_abstract,tuple)) or (X in ZZ and 4 <= X <= 37)): + raise ValueError(error_msg%X) + + # transforming two reducible types and an irreducible type + if isinstance(X, CartanType_abstract): + if not X.is_finite(): + raise ValueError(error_msg%X) + if hasattr(X,"cartan_type"): + X = X.cartan_type() + if X.is_irreducible(): + W_types.extend([(X.letter, X.n)]) + else: + W_types.extend([(x.letter, x.n) for x in X.component_types()]) + + elif X == (2,2,2) or X == ('I',2): + W_types.extend([('A',1), ('A',1)]) + + elif X == (2,2,3): + W_types.extend([('A', 3)]) + + else: + W_types.append(X) + + # converting the real types given as complex types + # and then checking for real vs complex + for i,W_type in enumerate(W_types): + if W_type in ZZ: + if W_type == 23: + W_types[i] = ('H', 3) + elif W_type == 28: + W_types[i] = ('F', 4) + elif W_type == 30: + W_types[i] = ('H', 4) + elif W_type == 35: + W_types[i] = ('E', 6) + elif W_type == 36: + W_types[i] = ('E', 7) + elif W_type == 37: + W_types[i] = ('E', 8) + if isinstance(W_type,tuple) and len(W_type) == 3: + if W_type[0] == W_type[1] == 1: + W_types[i] = ('A', W_type[2]-1) + elif W_type[0] == 2 and W_type[1] == 1: + W_types[i] = ('B', W_type[2]) + elif W_type[0] == W_type[1] == 2: + W_types[i] = ('D', W_type[2]) + elif W_type[0] == W_type[1] and W_type[2] == 2: + W_types[i] = ('I', W_type[0]) + + W_type = W_types[i] + # check for real vs complex + if W_type in ZZ or (isinstance(W_type, tuple) and len(W_type) == 3): + is_complex = True + + for index_set_kwd in ['index_set', 'hyperplane_index_set', 'reflection_index_set']: + index_set = kwds.get(index_set_kwd, None) + if index_set is not None: + if isinstance(index_set, (list, tuple)): + kwds[index_set_kwd] = tuple(index_set) + else: + raise ValueError('the keyword %s must be a list or tuple'%index_set_kwd) + + if len(W_types) == 1: + if is_complex is True: + cls = IrreducibleComplexReflectionGroup + else: + cls = IrreducibleRealReflectionGroup + else: + if is_complex is True: + cls = ComplexReflectionGroup + else: + cls = RealReflectionGroup + return cls(tuple(W_types), + index_set=kwds.get('index_set', None), + hyperplane_index_set=kwds.get('hyperplane_index_set', None), + reflection_index_set=kwds.get('reflection_index_set', None)) + +class RealReflectionGroup(ComplexReflectionGroup): + """ + A real reflection group given as a permutation group. + + .. SEEALSO:: + + :func:`ReflectionGroup` + """ + def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflection_index_set=None): + r""" + Initialize ``self``. + + TESTS:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: TestSuite(W).run() # optional - gap3 + """ + W_types = tuple([tuple(W_type) if isinstance(W_type, (list,tuple)) else W_type + for W_type in W_types]) + cartan_types = [] + for W_type in W_types: + W_type = CartanType(W_type) + if not W_type.is_finite() or not W_type.is_irreducible(): + raise ValueError("the given Cartan type of a component is not irreducible and finite") + cartan_types.append( W_type ) + if len(W_types) == 1: + cls = IrreducibleComplexReflectionGroup + else: + cls = ComplexReflectionGroup + cls.__init__(self, W_types, index_set = index_set, + hyperplane_index_set = hyperplane_index_set, + reflection_index_set = reflection_index_set) + + def _repr_(self): + r""" + Return the string representation of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3],['B',2],['I',5],['I',6]) # optional - gap3 + sage: W._repr_() # optional - gap3 + 'Reducible real reflection group of rank 9 and type A3 x B2 x I2(5) x G2' + """ + type_str = '' + for W_type in self._type: + type_str += self._irrcomp_repr_(W_type) + type_str += ' x ' + type_str = type_str[:-3] + return 'Reducible real reflection group of rank %s and type %s'%(self._rank,type_str) + + def iteration(self, algorithm="breadth", tracking_words=True): + r""" + Return an iterator going through all elements in ``self``. + + INPUT: + + - ``algorithm`` (default: ``'breadth'``) -- must be one of + the following: + + * ``'breadth'`` - iterate over in a linear extension of the + weak order + * ``'depth'`` - iterate by a depth-first-search + + - ``tracking_words`` (default: ``True``) -- whether or not to keep + track of the reduced words and store them in ``_reduced_word`` + + .. NOTE:: + + The fastest iteration is the depth first algorithm without + tracking words. In particular, ``'depth'`` is ~1.5x faster. + + EXAMPLES:: + + sage: W = ReflectionGroup(["B",2]) # optional - gap3 + + sage: for w in W.iteration("breadth",True): # optional - gap3 + ....: print("%s %s"%(w, w._reduced_word)) # optional - gap3 + () [] + (1,3)(2,6)(5,7) [1] + (1,5)(2,4)(6,8) [0] + (1,7,5,3)(2,4,6,8) [0, 1] + (1,3,5,7)(2,8,6,4) [1, 0] + (2,8)(3,7)(4,6) [1, 0, 1] + (1,7)(3,5)(4,8) [0, 1, 0] + (1,5)(2,6)(3,7)(4,8) [0, 1, 0, 1] + + sage: for w in W.iteration("depth", False): w # optional - gap3 + () + (1,3)(2,6)(5,7) + (1,5)(2,4)(6,8) + (1,3,5,7)(2,8,6,4) + (1,7)(3,5)(4,8) + (1,7,5,3)(2,4,6,8) + (2,8)(3,7)(4,6) + (1,5)(2,6)(3,7)(4,8) + """ + from sage.combinat.root_system.reflection_group_c import Iterator + return iter(Iterator(self, N=self._number_of_reflections, + algorithm=algorithm, tracking_words=tracking_words)) + + def __iter__(self): + r""" + Return an iterator going through all elements in ``self``. + + For options and faster iteration see :meth:`iteration`. + + EXAMPLES:: + + sage: W = ReflectionGroup(["B",2]) # optional - gap3 + + sage: for w in W: print("%s %s"%(w, w._reduced_word)) # optional - gap3 + () [] + (1,3)(2,6)(5,7) [1] + (1,5)(2,4)(6,8) [0] + (1,7,5,3)(2,4,6,8) [0, 1] + (1,3,5,7)(2,8,6,4) [1, 0] + (2,8)(3,7)(4,6) [1, 0, 1] + (1,7)(3,5)(4,8) [0, 1, 0] + (1,5)(2,6)(3,7)(4,8) [0, 1, 0, 1] + """ + return self.iteration(algorithm="breadth", tracking_words=True) + + @cached_method + def bipartite_index_set(self): + r""" + Return the bipartite index set of a real reflection group. + + EXAMPLES:: + + sage: W = ReflectionGroup(["A",5]) # optional - gap3 + sage: W.bipartite_index_set() # optional - gap3 + [[1, 3, 5], [2, 4]] + + sage: W = ReflectionGroup(["A",5],index_set=['a','b','c','d','e']) # optional - gap3 + sage: W.bipartite_index_set() # optional - gap3 + [['a', 'c', 'e'], ['b', 'd']] + """ + L, R = self._gap_group.BipartiteDecomposition().sage() + inv = self._index_set_inverse + L = [i for i in self._index_set if inv[i] + 1 in L] + R = [i for i in self._index_set if inv[i] + 1 in R] + return [L, R] + + def cartan_type(self): + r""" + Return the Cartan type of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.cartan_type() # optional - gap3 + ['A', 3] + + sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 + sage: W.cartan_type() # optional - gap3 + A3xB2 + """ + if len(self._type) == 1: + ct = self._type[0] + return CartanType([ct['series'], ct['rank']]) + else: + return CartanType([W.cartan_type() for W in self.irreducible_components()]) + + def simple_root(self, i): + r""" + Return the simple root with index ``i``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.simple_root(1) # optional - gap3 + (1, 0, 0) + """ + return self.simple_roots()[i] + + def positive_roots(self): + r""" + Return the positive roots of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 + sage: W.positive_roots() # optional - gap3 + [(1, 0, 0, 0, 0), + (0, 1, 0, 0, 0), + (0, 0, 1, 0, 0), + (0, 0, 0, 1, 0), + (0, 0, 0, 0, 1), + (1, 1, 0, 0, 0), + (0, 1, 1, 0, 0), + (0, 0, 0, 1, 1), + (1, 1, 1, 0, 0), + (0, 0, 0, 2, 1)] + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.positive_roots() # optional - gap3 + [(1, 0, 0), (0, 1, 0), (0, 0, 1), (1, 1, 0), (0, 1, 1), (1, 1, 1)] + """ + return self.roots()[:self._number_of_reflections] + + def almost_positive_roots(self): + r""" + Return the almost positive roots of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 + sage: W.almost_positive_roots() # optional - gap3 + [(-1, 0, 0, 0, 0), + (0, -1, 0, 0, 0), + (0, 0, -1, 0, 0), + (0, 0, 0, -1, 0), + (0, 0, 0, 0, -1), + (1, 0, 0, 0, 0), + (0, 1, 0, 0, 0), + (0, 0, 1, 0, 0), + (0, 0, 0, 1, 0), + (0, 0, 0, 0, 1), + (1, 1, 0, 0, 0), + (0, 1, 1, 0, 0), + (0, 0, 0, 1, 1), + (1, 1, 1, 0, 0), + (0, 0, 0, 2, 1)] + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.almost_positive_roots() # optional - gap3 + [(-1, 0, 0), + (0, -1, 0), + (0, 0, -1), + (1, 0, 0), + (0, 1, 0), + (0, 0, 1), + (1, 1, 0), + (0, 1, 1), + (1, 1, 1)] + """ + return [-beta for beta in self.simple_roots()] + self.positive_roots() + + def root_to_reflection(self, root): + r""" + Return the reflection along the given ``root``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for beta in W.roots(): W.root_to_reflection(beta) # optional - gap3 + (1,4)(2,3)(5,6) + (1,3)(2,5)(4,6) + (1,5)(2,4)(3,6) + (1,4)(2,3)(5,6) + (1,3)(2,5)(4,6) + (1,5)(2,4)(3,6) + """ + Phi = self.roots() + R = self.reflections() + i = Phi.index(root) + 1 + j = Phi.index(-root) + 1 + for r in R: + if r(i) == j: + return r + raise AssertionError("there is a bug in root_to_reflection") + + def reflection_to_positive_root(self, r): + r""" + Return the positive root orthogonal to the given reflection. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for r in W.reflections(): print W.reflection_to_positive_root(r) # optional - gap3 + (1, 0) + (0, 1) + (1, 1) + """ + Phi = self.roots() + N = len(Phi) / 2 + for i in range(1, N+1): + if r(i) == i + N: + return Phi[i-1] + raise AssertionError("there is a bug in reflection_to_positive_root") + + @cached_method + def fundamental_weights(self): + r""" + Return the fundamental weights of ``self`` in terms of the simple roots. + + The fundamental weights are defined by + `s_j(\omega_i) = \omega_i - \delta_{i=j}\alpha_j` + for the simple reflection `s_j` with corresponding simple + roots `\alpha_j`. + + In other words, the transpose Cartan matrix sends the weight + basis to the root basis. Observe again that the action here is + defined as a right action, see the example below. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3], ['B',2]) # optional - gap3 + sage: W.fundamental_weights() # optional - gap3 + Finite family {1: (3/4, 1/2, 1/4, 0, 0), 2: (1/2, 1, 1/2, 0, 0), 3: (1/4, 1/2, 3/4, 0, 0), 4: (0, 0, 0, 1, 1/2), 5: (0, 0, 0, 1, 1)} + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.fundamental_weights() # optional - gap3 + Finite family {1: (3/4, 1/2, 1/4), 2: (1/2, 1, 1/2), 3: (1/4, 1/2, 3/4)} + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: S = W.simple_reflections() # optional - gap3 + sage: N = W.fundamental_weights() # optional - gap3 + sage: for i in W.index_set(): # optional - gap3 + ....: for j in W.index_set(): # optional - gap3 + ....: print i, j, N[i], N[i]*S[j].to_matrix() # optional - gap3 + 1 1 (3/4, 1/2, 1/4) (-1/4, 1/2, 1/4) + 1 2 (3/4, 1/2, 1/4) (3/4, 1/2, 1/4) + 1 3 (3/4, 1/2, 1/4) (3/4, 1/2, 1/4) + 2 1 (1/2, 1, 1/2) (1/2, 1, 1/2) + 2 2 (1/2, 1, 1/2) (1/2, 0, 1/2) + 2 3 (1/2, 1, 1/2) (1/2, 1, 1/2) + 3 1 (1/4, 1/2, 3/4) (1/4, 1/2, 3/4) + 3 2 (1/4, 1/2, 3/4) (1/4, 1/2, 3/4) + 3 3 (1/4, 1/2, 3/4) (1/4, 1/2, -1/4) + """ + from sage.sets.family import Family + m = self.cartan_matrix().transpose().inverse() + Delta = tuple(self.simple_roots()) + zero = Delta[0].parent().zero() + weights = [sum([m[i,j] * sj for j,sj in enumerate(Delta)], zero) + for i in range(len(Delta))] + for weight in weights: + weight.set_immutable() + return Family({ind:weights[i] for i,ind in enumerate(self._index_set)}) + + def fundamental_weight(self, i): + r""" + Return the fundamental weight with index ``i``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: [ W.fundamental_weight(i) for i in W.index_set() ] # optional - gap3 + [(3/4, 1/2, 1/4), (1/2, 1, 1/2), (1/4, 1/2, 3/4)] + """ + return self.fundamental_weights()[i] + + @cached_method + def coxeter_matrix(self): + """ + Return the Coxeter matrix associated to ``self``. + + EXAMPLES:: + + sage: G = ReflectionGroup(['A',3]) # optional - gap3 + sage: G.coxeter_matrix() # optional - gap3 + [1 3 2] + [3 1 3] + [2 3 1] + """ + return self.cartan_type().coxeter_matrix() + + def permutahedron(self, point=None): + r""" + Return the permutahedron of ``self``. + + This is the convex hull of the point ``point`` in the weight + basis under the action of ``self`` on the underlying vector + space `V`. + + INPUT: + + - ``point`` -- optional, a point given by its coordinates in + the weight basis (default is `(1, 1, 1, \ldots)`) + + .. NOTE:: + + The result is expressed in the root basis coordinates. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.permutahedron() # optional - gap3 + A 3-dimensional polyhedron in QQ^3 defined as the convex hull + of 24 vertices + + sage: W = ReflectionGroup(['A',3],['B',2]) # optional - gap3 + sage: W.permutahedron() # optional - gap3 + A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 192 vertices + + TESTS:: + + sage: W = ReflectionGroup(['A',3]) # optional - gap3 + sage: W.permutahedron([3,5,8]) # optional - gap3 + A 3-dimensional polyhedron in QQ^3 defined as the convex hull + of 24 vertices + """ + n = self.rank() + weights = self.fundamental_weights() + if point is None: + point = [1] * n + v = sum(point[i] * wt for i, wt in enumerate(weights)) + from sage.geometry.polyhedron.constructor import Polyhedron + return Polyhedron(vertices=[v*w.to_matrix() for w in self]) + + @cached_method + def right_coset_representatives(self, J): + r""" + Return the right coset representatives of ``self`` for the + parabolic subgroup generated by the simple reflections in ``J``. + + EXAMPLES:: + + sage: W = ReflectionGroup(["A",3]) # optional - gap3 + sage: for J in Subsets([1,2,3]): W.right_coset_representatives(J) # optional - gap3 + [(), (2,5)(3,9)(4,6)(8,11)(10,12), (1,4)(2,8)(3,5)(7,10)(9,11), + (1,7)(2,4)(5,6)(8,10)(11,12), (1,2,10)(3,6,5)(4,7,8)(9,12,11), + (1,4,6)(2,3,11)(5,8,9)(7,10,12), (1,6,4)(2,11,3)(5,9,8)(7,12,10), + (1,7)(2,6)(3,9)(4,5)(8,12)(10,11), + (1,10,2)(3,5,6)(4,8,7)(9,11,12), (1,2,3,12)(4,5,10,11)(6,7,8,9), + (1,5,9,10)(2,12,8,6)(3,4,7,11), (1,6)(2,9)(3,8)(5,11)(7,12), + (1,8)(2,7)(3,6)(4,10)(9,12), (1,10,9,5)(2,6,8,12)(3,11,7,4), + (1,12,3,2)(4,11,10,5)(6,9,8,7), (1,3)(2,12)(4,10)(5,11)(6,8)(7,9), + (1,5,12)(2,9,4)(3,10,8)(6,7,11), (1,8,11)(2,5,7)(3,12,4)(6,10,9), + (1,11,8)(2,7,5)(3,4,12)(6,9,10), (1,12,5)(2,4,9)(3,8,10)(6,11,7), + (1,3,7,9)(2,11,6,10)(4,8,5,12), (1,9,7,3)(2,10,6,11)(4,12,5,8), + (1,11)(3,10)(4,9)(5,7)(6,12), (1,9)(2,8)(3,7)(4,11)(5,10)(6,12)] + [(), (2,5)(3,9)(4,6)(8,11)(10,12), (1,4)(2,8)(3,5)(7,10)(9,11), + (1,2,10)(3,6,5)(4,7,8)(9,12,11), (1,4,6)(2,3,11)(5,8,9)(7,10,12), + (1,6,4)(2,11,3)(5,9,8)(7,12,10), (1,2,3,12)(4,5,10,11)(6,7,8,9), + (1,5,9,10)(2,12,8,6)(3,4,7,11), (1,6)(2,9)(3,8)(5,11)(7,12), + (1,3)(2,12)(4,10)(5,11)(6,8)(7,9), + (1,5,12)(2,9,4)(3,10,8)(6,7,11), (1,3,7,9)(2,11,6,10)(4,8,5,12)] + [(), (2,5)(3,9)(4,6)(8,11)(10,12), (1,7)(2,4)(5,6)(8,10)(11,12), + (1,4,6)(2,3,11)(5,8,9)(7,10,12), + (1,7)(2,6)(3,9)(4,5)(8,12)(10,11), + (1,10,2)(3,5,6)(4,8,7)(9,11,12), (1,2,3,12)(4,5,10,11)(6,7,8,9), + (1,10,9,5)(2,6,8,12)(3,11,7,4), (1,12,3,2)(4,11,10,5)(6,9,8,7), + (1,8,11)(2,5,7)(3,12,4)(6,10,9), (1,12,5)(2,4,9)(3,8,10)(6,11,7), + (1,11)(3,10)(4,9)(5,7)(6,12)] + [(), (1,4)(2,8)(3,5)(7,10)(9,11), (1,7)(2,4)(5,6)(8,10)(11,12), + (1,2,10)(3,6,5)(4,7,8)(9,12,11), (1,6,4)(2,11,3)(5,9,8)(7,12,10), + (1,10,2)(3,5,6)(4,8,7)(9,11,12), (1,5,9,10)(2,12,8,6)(3,4,7,11), + (1,8)(2,7)(3,6)(4,10)(9,12), (1,12,3,2)(4,11,10,5)(6,9,8,7), + (1,3)(2,12)(4,10)(5,11)(6,8)(7,9), + (1,11,8)(2,7,5)(3,4,12)(6,9,10), (1,9,7,3)(2,10,6,11)(4,12,5,8)] + [(), (2,5)(3,9)(4,6)(8,11)(10,12), (1,4,6)(2,3,11)(5,8,9)(7,10,12), + (1,2,3,12)(4,5,10,11)(6,7,8,9)] + [(), (1,4)(2,8)(3,5)(7,10)(9,11), (1,2,10)(3,6,5)(4,7,8)(9,12,11), + (1,6,4)(2,11,3)(5,9,8)(7,12,10), (1,5,9,10)(2,12,8,6)(3,4,7,11), + (1,3)(2,12)(4,10)(5,11)(6,8)(7,9)] + [(), (1,7)(2,4)(5,6)(8,10)(11,12), (1,10,2)(3,5,6)(4,8,7)(9,11,12), + (1,12,3,2)(4,11,10,5)(6,9,8,7)] + [()] + """ + from sage.combinat.root_system.reflection_group_complex import _gap_return + J_inv = [self._index_set_inverse[j] + 1 for j in J] + S = str(gap3('ReducedRightCosetRepresentatives(%s,ReflectionSubgroup(%s,%s))' % (self._gap_group._name, self._gap_group._name, J_inv))) + return sage_eval(_gap_return(S), locals={'self': self}) + + class Element(ComplexReflectionGroup.Element): + + @lazy_attribute + def _reduced_word(self): + r""" + Computes a reduced word and stores it into ``self._reduced_word``. + The words are in ``range(n)`` and not in the index set. + + TESTS:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: [w._reduced_word for w in W] # optional - gap3 + [[], [1], [0], [0, 1], [1, 0], [0, 1, 0]] + """ + return reduced_word_c(self.parent(),self) + + def reduced_word_in_reflections(self): + r""" + Return a word in the reflections to obtain ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2], index_set=['a','b'], reflection_index_set=['A','B','C']) # optional - gap3 + sage: [(w.reduced_word(), w.reduced_word_in_reflections()) for w in W] # optional - gap3 + [([], []), + (['b'], ['B']), + (['a'], ['A']), + (['a', 'b'], ['A', 'B']), + (['b', 'a'], ['A', 'C']), + (['a', 'b', 'a'], ['C'])] + + .. SEEALSO:: :meth:`reduced_word` + """ + if self.is_one(): + return [] + + W = self.parent() + r = self.reflection_length() + R = W.reflections() + I = W.reflection_index_set() + word = [] + while r > 0: + for i in I: + w = R[i]._mul_(self) + if w.reflection_length() < r: + word += [i] + r -= 1 + self = w + break + return word + + def length(self): + r""" + Return the length of ``self`` in generating reflections. + + This is the minimal numbers of generating reflections needed + to obtain ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print("%s %s"%(w.reduced_word(), w.length())) # optional - gap3 + [] 0 + [2] 1 + [1] 1 + [1, 2] 2 + [2, 1] 2 + [1, 2, 1] 3 + """ + return len(self._reduced_word) + + def has_left_descent(self, i): + r""" + Return whether ``i`` is a left descent of ``self``. + + This is done by testing whether ``i`` is mapped by ``self`` + to a negative root. + + EXAMPLES:: + + sage: W = ReflectionGroup(["A",3]) # optional - gap3 + sage: s = W.simple_reflections() # optional - gap3 + sage: (s[1]*s[2]).has_left_descent(1) # optional - gap3 + True + sage: (s[1]*s[2]).has_left_descent(2) # optional - gap3 + False + """ + W = self.parent() + return self(W._index_set_inverse[i]+1) > W._number_of_reflections + + def has_descent(self, i, side='left', positive=False): + r""" + Return whether ``i`` is a descent (or ascent) of ``self``. + + This is done by testing whether ``i`` is mapped by ``self`` + to a negative root. + + INPUT: + + - ``i`` -- an index of a simple reflection + - ``side`` (default: ``'right'``) -- ``'left'`` or ``'right'`` + - ``positive`` (default: ``False``) -- a boolean + + EXAMPLES:: + + sage: W = ReflectionGroup(["A",3]) # optional - gap3 + sage: s = W.simple_reflections() # optional - gap3 + sage: (s[1]*s[2]).has_descent(1) # optional - gap3 + True + sage: (s[1]*s[2]).has_descent(2) # optional - gap3 + False + """ + if not isinstance(positive, bool): + raise TypeError("%s is not a boolean"%(bool)) + + if i not in self.parent().index_set(): + raise ValueError("the given index %s is not in the index set"%i) + + negative = not positive + + if side == 'left': + return self.has_left_descent(i) is negative + elif side == 'right': + return self.has_right_descent(i) is negative + else: + raise ValueError("the method 'has_descent' needs the input 'side' to be either 'left' or 'right'") + + def act_on_root(self, root, side="right"): + r""" + Return the root obtained by applying ``self`` on ``root``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print("%s %s"%(w.reduced_word(), # optional - gap3 + ....: [w.act_on_root(beta) for beta in W.positive_roots()])) # optional - gap3 + [] [(1, 0), (0, 1), (1, 1)] + [2] [(1, 1), (0, -1), (1, 0)] + [1] [(-1, 0), (1, 1), (0, 1)] + [1, 2] [(-1, -1), (1, 0), (0, -1)] + [2, 1] [(0, 1), (-1, -1), (-1, 0)] + [1, 2, 1] [(0, -1), (-1, 0), (-1, -1)] + + sage: elt = W.from_reduced_word([1,2]) # optional - gap3 + sage: [ elt.act_on_root(beta, side="left") for beta in W.positive_roots() ] # optional - gap3 + [(0, 1), (-1, -1), (-1, 0)] + """ + Phi = self.parent().roots() + if side == "left": + w = ~self + elif side == "right": + w = self + else: + raise ValueError("the action on roots must be on the left or on the right") + + return Phi[w(Phi.index(root)+1) - 1] + + def inversion_set(self, side="right"): + r""" + Return the inversion set of ``self``. + + This is the set `\{\beta \in \Phi^+ : s(\beta) \in \Phi^-\}`, + where `s` is ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: print("%s %s"%(w.reduced_word(), w.inversion_set())) # optional - gap3 + [] [] + [2] [(0, 1)] + [1] [(1, 0)] + [1, 2] [(1, 0), (1, 1)] + [2, 1] [(0, 1), (1, 1)] + [1, 2, 1] [(0, 1), (1, 0), (1, 1)] + + sage: W.from_reduced_word([1,2]).inversion_set(side="left") # optional - gap3 + [(0, 1), (1, 1)] + """ + Phi_plus = set(self.parent().positive_roots()) + if side == "left": + w = ~self + elif side == "right": + w = self + else: + raise ValueError("the action on roots must be on the left or on the right") + + return [root for root in Phi_plus if w.act_on_root(root) not in Phi_plus] + + @cached_in_parent_method + def right_coset_representatives(self): + r""" + Return the right coset representatives of ``self``. + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: rcr = w.right_coset_representatives() # optional - gap3 + ....: print("%s %s"%(w.reduced_word(), # optional - gap3 + ....: [v.reduced_word() for v in rcr])) # optional - gap3 + [] [[], [2], [1], [2, 1], [1, 2], [1, 2, 1]] + [2] [[], [2], [1]] + [1] [[], [1], [1, 2]] + [1, 2] [[]] + [2, 1] [[]] + [1, 2, 1] [[], [2], [2, 1]] + """ + from sage.combinat.root_system.reflection_group_complex import _gap_return + W = self.parent() + T = W.reflections() + T_fix = [i + 1 for i in T.keys() + if self.fix_space().is_subspace(T[i].fix_space())] + S = str(gap3('ReducedRightCosetRepresentatives(%s,ReflectionSubgroup(%s,%s))' % (W._gap_group._name, W._gap_group._name, T_fix))) + return sage_eval(_gap_return(S, coerce_obj='W'), + locals={'self': self, 'W': W}) + + def left_coset_representatives(self): + r""" + Return the left coset representatives of ``self``. + + .. SEEALSO:: :meth:`right_coset_representatives` + + EXAMPLES:: + + sage: W = ReflectionGroup(['A',2]) # optional - gap3 + sage: for w in W: # optional - gap3 + ....: lcr = w.left_coset_representatives() # optional - gap3 + ....: print("%s %s"%(w.reduced_word(), # optional - gap3 + ....: [v.reduced_word() for v in lcr])) # optional - gap3 + [] [[], [2], [1], [1, 2], [2, 1], [1, 2, 1]] + [2] [[], [2], [1]] + [1] [[], [1], [2, 1]] + [1, 2] [[]] + [2, 1] [[]] + [1, 2, 1] [[], [2], [1, 2]] + """ + return [ (~w) for w in self.right_coset_representatives() ] + +class IrreducibleRealReflectionGroup(RealReflectionGroup, IrreducibleComplexReflectionGroup): + + def _repr_(self): + r""" + Return the string representation of ``self``. + + EXAMPLES:: + + sage: for i in [2..7]: ReflectionGroup(["I", i]) # optional - gap3 + Reducible real reflection group of rank 2 and type A1 x A1 + Irreducible real reflection group of rank 2 and type A2 + Irreducible real reflection group of rank 2 and type C2 + Irreducible real reflection group of rank 2 and type I2(5) + Irreducible real reflection group of rank 2 and type G2 + Irreducible real reflection group of rank 2 and type I2(7) + """ + type_str = self._irrcomp_repr_(self._type[0]) + return 'Irreducible real reflection group of rank %s and type %s'%(self._rank,type_str) + + class Element(RealReflectionGroup.Element, IrreducibleComplexReflectionGroup.Element): + pass + diff --git a/src/sage/combinat/root_system/root_lattice_realization_algebras.py b/src/sage/combinat/root_system/root_lattice_realization_algebras.py index 691b914d70d..021f91058a3 100644 --- a/src/sage/combinat/root_system/root_lattice_realization_algebras.py +++ b/src/sage/combinat/root_system/root_lattice_realization_algebras.py @@ -225,7 +225,7 @@ def demazure_operators(self): REFERENCES: - .. [Kumar1987] S. Kumar, Demazure character formula in arbitrary Kac-Moody setting, + .. [Kumar1987] \S. Kumar, Demazure character formula in arbitrary Kac-Moody setting, Invent. Math. 89 (1987), no. 2, 395-423. EXAMPLES: @@ -425,11 +425,11 @@ def demazure_lusztig_operators(self, q1, q2, convention="antidominant"): REFERENCES: - .. [Lusztig1985] G. Lusztig, + .. [Lusztig1985] \G. Lusztig, *Equivariant K-theory and representations of Hecke algebras*, Proc. Amer. Math. Soc. 94 (1985), no. 2, 337-342. - .. [Cherednik1995] I. Cherednik, + .. [Cherednik1995] \I. Cherednik, *Nonsymmetric Macdonald polynomials*. IMRN 10, 483-515 (1995). EXAMPLES:: @@ -752,7 +752,7 @@ def T0_check_on_basis(self, q1, q2, convention="antidominant"): REFERENCES: - .. [Haiman06] M. Haiman, Cherednik algebras, Macdonald polynomials and combinatorics, ICM 2006. + .. [Haiman06] \M. Haiman, Cherednik algebras, Macdonald polynomials and combinatorics, ICM 2006. .. WARNING:: diff --git a/src/sage/combinat/root_system/type_folded.py b/src/sage/combinat/root_system/type_folded.py index f4847610ab7..3b758c06b5b 100644 --- a/src/sage/combinat/root_system/type_folded.py +++ b/src/sage/combinat/root_system/type_folded.py @@ -150,7 +150,7 @@ class CartanTypeFolded(UniqueRepresentation, SageObject): - :wikipedia:`Dynkin_diagram#Folding` - .. [OSShimo03] M. Okado, A. Schilling, M. Shimozono. + .. [OSShimo03] \M. Okado, A. Schilling, M. Shimozono. "Virtual crystals and fermionic formulas for type `D_{n+1}^{(2)}`, `A_{2n}^{(2)}`, and `C_n^{(1)}`". Representation Theory. **7** (2003). 101-163. :doi:`10.1.1.192.2095`, :arxiv:`0810.5067`. diff --git a/src/sage/combinat/root_system/type_reducible.py b/src/sage/combinat/root_system/type_reducible.py index 5b059b12fa2..74c5a262bcb 100644 --- a/src/sage/combinat/root_system/type_reducible.py +++ b/src/sage/combinat/root_system/type_reducible.py @@ -176,9 +176,19 @@ def __cmp__(self, other): False sage: ct1 == ct3 False + + TESTS: + + Check that :trac:`20418` is fixed:: + + sage: ct = CartanType(["A2", "B2"]) + sage: ct == (1, 2, 1) + False """ if isinstance(other, CartanType_simple): return 1 + if other.__class__ != self.__class__: + return cmp(self.__class__, other.__class__) return cmp(self._types, other._types) def component_types(self): @@ -409,8 +419,35 @@ def is_affine(self): """ return False + @cached_method + def coxeter_diagram(self): + """ + Return the Coxeter diagram for ``self``. + + EXAMPLES:: + + sage: cd = CartanType("A2xB2xF4").coxeter_diagram() + sage: cd + Graph on 8 vertices + sage: cd.edges() + [(1, 2, 3), (3, 4, 4), (5, 6, 3), (6, 7, 4), (7, 8, 3)] + sage: CartanType("F4xA2").coxeter_diagram().edges() + [(1, 2, 3), (2, 3, 4), (3, 4, 3), (5, 6, 3)] + sage: cd = CartanType("A1xH3").coxeter_diagram(); cd + Graph on 4 vertices + sage: cd.edges() + [(2, 3, 3), (3, 4, 5)] + """ + from sage.graphs.graph import Graph + relabelling = self._index_relabelling + g = Graph(multiedges=False) + g.add_vertices(self.index_set()) + for i,t in enumerate(self._types): + for [e1, e2, l] in t.coxeter_diagram().edges(): + g.add_edge(relabelling[i,e1], relabelling[i,e2], label=l) + return g class AmbientSpace(ambient_space.AmbientSpace): """ diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index 3b3c9d3449a..b61ed9a27a9 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -359,17 +359,26 @@ def reflections(self): sage: [r+refdict[r].action(r) for r in refdict.keys()] [(0, 0), (0, 0), (0, 0), (0, 0)] + sage: W = WeylGroup(['A',2,1], prefix="s") + sage: W.reflections() + Lazy family (real root to reflection(i))_{i in + Positive real roots of type ['A', 2, 1]} + + TESTS:: + + sage: CM = CartanMatrix([[2,-6],[-1,2]]) + sage: W = WeylGroup(CM, prefix='s') + sage: W.reflections() + Traceback (most recent call last): + ... + NotImplementedError: only implemented for finite and affine Cartan types """ - ret = {} - try: - for alp in self.domain().positive_roots(): - m = Matrix([self.domain().reflection(alp)(x).to_vector() - for x in self.domain().basis()]) - r = self(m) - ret[alp] = r - return Family(ret) - except Exception: - raise NotImplementedError("reflections are only implemented for finite Weyl groups") + prr = self.domain().positive_real_roots() + def to_elt(alp): + ref = self.domain().reflection(alp) + m = Matrix([ref(x).to_vector() for x in self.domain().basis()]) + return self(m.transpose()) + return Family(prr, to_elt, name="real root to reflection") def _repr_(self): """ @@ -711,11 +720,10 @@ def __init__(self, parent, g, check=False): sage: TestSuite(s1).run() """ MatrixGroupElement_gap.__init__(self, parent, g, check=check) - self.__matrix = self.matrix() self._parent = parent def __hash__(self): - return hash(self.__matrix) + return hash(self.matrix()) def domain(self): """ @@ -758,7 +766,7 @@ def _repr_(self): return ret + "%s%d"%(self._parent._prefix, redword[-1]) def _latex_(self): - """ + r""" Return the latex representation of ``self``. EXAMPLES:: @@ -803,7 +811,7 @@ def __eq__(self, other): """ return self.__class__ == other.__class__ and \ self._parent == other._parent and \ - self.__matrix == other.__matrix + self.matrix() == other.matrix() def _cmp_(self, other): """ @@ -850,7 +858,7 @@ def action(self, v): """ if v not in self.domain(): raise ValueError("{} is not in the domain".format(v)) - return self.domain().from_vector(self.__matrix*v.to_vector()) + return self.domain().from_vector(self.matrix()*v.to_vector()) ########################################################################## @@ -859,12 +867,14 @@ def action(self, v): def has_descent(self, i, positive=False, side = "right"): """ - Tests if self has a descent at position `i`, that is if self is + Test if ``self`` has a descent at position ``i``. + + An element `w` has a descent in position `i` if `w` is on the strict negative side of the `i^{th}` simple reflection hyperplane. - If positive is True, tests if it is on the strict positive - side instead. + If ``positive`` is ``True``, tests if it is on the strict + positive side instead. EXAMPLES:: @@ -921,33 +931,54 @@ def has_descent(self, i, positive=False, side = "right"): self = ~self if use_rho: - s = self.action(L.rho() ).scalar(L.alphacheck()[i]) >= 0 + s = self.action(L.rho()).scalar(L.alphacheck()[i]) >= 0 else: s = self.action(L.alpha()[i]).is_positive_root() return s is positive - def has_left_descent(self,i): + def has_left_descent(self, i): """ - Tests if self has a left descent at position `i`. + Test if ``self`` has a left descent at position ``i``. EXAMPLES:: sage: W = WeylGroup(['A',3]) sage: s = W.simple_reflections() - sage: [W.one().has_descent(i) for i in W.domain().index_set()] + sage: [W.one().has_left_descent(i) for i in W.domain().index_set()] [False, False, False] - sage: [s[1].has_descent(i) for i in W.domain().index_set()] + sage: [s[1].has_left_descent(i) for i in W.domain().index_set()] [True, False, False] - sage: [s[2].has_descent(i) for i in W.domain().index_set()] + sage: [s[2].has_left_descent(i) for i in W.domain().index_set()] [False, True, False] - sage: [s[3].has_descent(i) for i in W.domain().index_set()] + sage: [s[3].has_left_descent(i) for i in W.domain().index_set()] + [False, False, True] + sage: [(s[3]*s[2]).has_left_descent(i) for i in W.domain().index_set()] [False, False, True] - sage: [s[3].has_descent(i, True) for i in W.domain().index_set()] - [True, True, False] """ return self.has_descent(i, side = "left") + def has_right_descent(self, i): + """ + Test if ``self`` has a right descent at position ``i``. + + EXAMPLES:: + + sage: W = WeylGroup(['A',3]) + sage: s = W.simple_reflections() + sage: [W.one().has_right_descent(i) for i in W.domain().index_set()] + [False, False, False] + sage: [s[1].has_right_descent(i) for i in W.domain().index_set()] + [True, False, False] + sage: [s[2].has_right_descent(i) for i in W.domain().index_set()] + [False, True, False] + sage: [s[3].has_right_descent(i) for i in W.domain().index_set()] + [False, False, True] + sage: [(s[3]*s[2]).has_right_descent(i) for i in W.domain().index_set()] + [False, True, False] + """ + return self.has_descent(i, side="right") + def apply_simple_reflection(self, i, side = "right"): s = self.parent().simple_reflections() if side == "right": diff --git a/src/sage/combinat/rsk.py b/src/sage/combinat/rsk.py index c708fe60921..161f44c4708 100644 --- a/src/sage/combinat/rsk.py +++ b/src/sage/combinat/rsk.py @@ -77,7 +77,7 @@ Advances in Mathematics 63 (1987), pp. 42-99. http://www.sciencedirect.com/science/article/pii/0001870887900636 -.. [BKSTY06] A. Buch, A. Kresch, M. Shimozono, H. Tamvakis, and A. Yong. +.. [BKSTY06] \A. Buch, A. Kresch, M. Shimozono, H. Tamvakis, and A. Yong. *Stable Grothendieck polynomials and* `K`-*theoretic factor sequences*. Math. Ann. **340** Issue 2, (2008), pp. 359--382. :arxiv:`math/0601514v1`. @@ -555,6 +555,16 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): Traceback (most recent call last): ... ValueError: p(=[[1, 2, 3]]) and q(=[[1, 2]]) must have the same shape + + Check that :trac:`20430` is fixed:: + + sage: RSK([1,1,1,1,1,1,1,2,2,2,3], [1,1,1,1,1,1,3,2,2,2,1]) + [[[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]], + [[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]] + sage: t = SemistandardTableau([[1, 1, 1, 1, 1, 1, 1, 2, 2], [2], [3]]) + sage: RSK_inverse(t, t, 'array') + [[1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3], + [1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 1]] """ if insertion == 'hecke': return hecke_insertion_reverse(p, q, output) @@ -577,7 +587,8 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): use_EG = (insertion == 'EG') - for i in reversed(d.values()): # Delete last entry from i-th row of p_copy + for key in sorted(d, reverse=True): # Delete last entry from i-th row of p_copy + i = d[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y_pos = bisect_left(row,x) - 1 @@ -626,8 +637,9 @@ def RSK_inverse(p, q, output='array', insertion='RSK'): #d is now a double family such that for every integers k and j, #the value d[k][j] is the row i such that the (i, j)-th cell of #q is filled with k. - for value, row_dict in reversed(d.items()): - for i in reversed(row_dict.values()): + for value, row_dict in sorted(d.items(), reverse=True, key=lambda x: x[0]): + for key in sorted(row_dict, reverse=True): + i = row_dict[key] x = p_copy[i].pop() # Always the right-most entry for row in reversed(p_copy[:i]): y = bisect_left(row,x) - 1 diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index 0ead546b110..d7c2cae7a6b 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -812,7 +812,7 @@ def ordered_set_partition_action(self, s): REFERENCES: - .. [LM2011] A. Lauve, M. Mastnak. *The primitives and antipode in + .. [LM2011] \A. Lauve, M. Mastnak. *The primitives and antipode in the Hopf algebra of symmetric functions in noncommuting variables*. Advances in Applied Mathematics. **47** (2011). 536-544. :arxiv:`1006.0367v3` :doi:`10.1016/j.aam.2011.01.002`. diff --git a/src/sage/combinat/sf/character.py b/src/sage/combinat/sf/character.py index 921319ea3e1..0f3cb7f57e8 100644 --- a/src/sage/combinat/sf/character.py +++ b/src/sage/combinat/sf/character.py @@ -8,7 +8,7 @@ REFERENCES: -.. [OZ2015] R. Orellana, M. Zabrocki, *Symmetric group characters +.. [OZ2015] \R. Orellana, M. Zabrocki, *Symmetric group characters as symmetric functions*, :arxiv:`1510.00438`. """ diff --git a/src/sage/combinat/sf/hall_littlewood.py b/src/sage/combinat/sf/hall_littlewood.py index 0bf5458e4fa..32097ab803b 100644 --- a/src/sage/combinat/sf/hall_littlewood.py +++ b/src/sage/combinat/sf/hall_littlewood.py @@ -5,7 +5,7 @@ REFERENCES: -.. [Mac1995] I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., +.. [Mac1995] \I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., The Clarendon Press, Oxford University Press, New York, 1995, With contributions by A. Zelevinsky, Oxford Science Publications. """ diff --git a/src/sage/combinat/sf/jack.py b/src/sage/combinat/sf/jack.py index 389797a3de6..6412febccc6 100644 --- a/src/sage/combinat/sf/jack.py +++ b/src/sage/combinat/sf/jack.py @@ -8,11 +8,11 @@ REFERENCES: -.. [Jack1970] H. Jack, +.. [Jack1970] \H. Jack, *A class of symmetric functions with a parameter*, Proc. R. Soc. Edinburgh (A), 69, 1-18. -.. [Ma1995] I. G. Macdonald, +.. [Ma1995] \I. G. Macdonald, *Symmetric functions and Hall polynomials*, second ed., The Clarendon Press, Oxford University Press, New York, 1995, With contributions @@ -989,7 +989,7 @@ def scalar_jack_basis(self, part1, part2 = None): REFRENCES: - .. [Mc1995] I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., + .. [Mc1995] \I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., The Clarendon Press, Oxford University Press, New York, 1995, With contributions by A. Zelevinsky, Oxford Science Publications. @@ -1399,14 +1399,6 @@ def scalar_zonal(self, x): P = self.parent()._P return P(self).scalar_jack(P(x),2) -############# -# Cache # -############# -#from sage.misc.cache import Cache -#cache_p = Cache(JackPolynomials_p) -#cache_j = Cache(JackPolynomials_j) -#cache_q = Cache(JackPolynomials_q) -#cache_z = Cache(SymmetricFunctionAlgebra_zonal) # Backward compatibility for unpickling from sage.structure.sage_object import register_unpickle_override diff --git a/src/sage/combinat/sf/k_dual.py b/src/sage/combinat/sf/k_dual.py index 245f9196fe3..93649340e9c 100644 --- a/src/sage/combinat/sf/k_dual.py +++ b/src/sage/combinat/sf/k_dual.py @@ -95,7 +95,7 @@ def __init__(self, Sym, k, t='t'): sage: Q = Sym.kBoundedQuotient(3) Traceback (most recent call last): ... - TypeError: unable to convert t to a rational + TypeError: unable to convert 't' to a rational sage: Sym = SymmetricFunctions(QQ['t'].fraction_field()) sage: Q = Sym.kBoundedQuotient(3) sage: km = Q.km() @@ -1242,7 +1242,7 @@ class DualkSchurFunctions(KBoundedQuotientBasis): REFERENCES: - .. [LLMSSZ] T. Lam, L. Lapointe, J. Morse, A. Schilling, M. Shimozono, M. Zabrocki, + .. [LLMSSZ] \T. Lam, L. Lapointe, J. Morse, A. Schilling, M. Shimozono, M. Zabrocki, k-Schur functions and affine Schubert calculus. """ @@ -1364,7 +1364,7 @@ class AffineSchurFunctions(KBoundedQuotientBasis): REFERENCES: - .. [Lam2006] T. Lam, Schubert polynomials for the affine Grassmannian, J. Amer. + .. [Lam2006] \T. Lam, Schubert polynomials for the affine Grassmannian, J. Amer. Math. Soc., 21 (2008), 259-281. """ diff --git a/src/sage/combinat/sf/macdonald.py b/src/sage/combinat/sf/macdonald.py index adbf9b2b830..251b6b5ecbe 100644 --- a/src/sage/combinat/sf/macdonald.py +++ b/src/sage/combinat/sf/macdonald.py @@ -14,22 +14,22 @@ REFERENCES: -.. [Macdonald1995] I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., +.. [Macdonald1995] \I. G. Macdonald, Symmetric functions and Hall polynomials, second ed., The Clarendon Press, Oxford University Press, New York, 1995, With contributions by A. Zelevinsky, Oxford Science Publications. -.. [GH1993] A. Garsia, M. Haiman, A graded representation module for Macdonald's +.. [GH1993] \A. Garsia, M. Haiman, A graded representation module for Macdonald's polynomials, Proc. Nat. Acad. U.S.A. no. 90, 3607--3610. -.. [BGHT1999] F. Bergeron, A. M. Garsia, M. Haiman, and G. Tesler, Identities and +.. [BGHT1999] \F. Bergeron, A. M. Garsia, M. Haiman, and G. Tesler, Identities and positivity conjectures for some remarkable operators in the theory of symmetric functions, Methods Appl. Anal. 6 (1999), no. 3, 363--420. -.. [LLM1998] L. Lapointe, A. Lascoux, J. Morse, Determinantal Expressions for +.. [LLM1998] \L. Lapointe, A. Lascoux, J. Morse, Determinantal Expressions for Macdonald Polynomials, IRMN no. 18 (1998). :arXiv:`math/9808050`. -.. [BH2013] F. Bergeron, M. Haiman, Tableaux Formulas for Macdonald Polynomials, +.. [BH2013] \F. Bergeron, M. Haiman, Tableaux Formulas for Macdonald Polynomials, Special edition in honor of Christophe Reutenauer 60 birthday, International Journal of Algebra and Computation, Volume 23, Issue 4, (2013), pp. 833-852. """ diff --git a/src/sage/combinat/sf/new_kschur.py b/src/sage/combinat/sf/new_kschur.py index 0a05851c765..b83771ce27f 100644 --- a/src/sage/combinat/sf/new_kschur.py +++ b/src/sage/combinat/sf/new_kschur.py @@ -209,10 +209,10 @@ def K_kschur(self): REFERENCES: - .. [Morse11] J. Morse, Combinatorics of the K-theory of affine Grassmannians, + .. [Morse11] \J. Morse, Combinatorics of the K-theory of affine Grassmannians, Adv. in Math., Volume 229, Issue 5, pp. 2950--2984. - .. [LamSchillingShimozono10] T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian, + .. [LamSchillingShimozono10] \T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian, Compositio Math. 146 (2010), 811-852. @@ -663,7 +663,7 @@ def hl_creation_operator(self, nu, t = None): REFERENCES: - .. [SZ.2001] M. Shimozono, M. Zabrocki, + .. [SZ.2001] \M. Shimozono, M. Zabrocki, Hall-Littlewood vertex operators and generalized Kostka polynomials. Adv. Math. 158 (2001), no. 1, 66-85. @@ -1243,10 +1243,10 @@ class K_kSchur(CombinatorialFreeModule): REFERENCES: - .. [Morse2011] J. Morse, Combinatorics of the K-theory of affine Grassmannians, + .. [Morse2011] \J. Morse, Combinatorics of the K-theory of affine Grassmannians, Adv. in Math., Volume 229, Issue 5, pp. 2950--2984. - .. [LamSchillingShimozono2010] T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian, + .. [LamSchillingShimozono2010] \T. Lam, A. Schilling, M.Shimozono, K-theory Schubert calculus of the affine Grassmannian, Compositio Math. 146 (2010), 811-852. """ diff --git a/src/sage/combinat/sf/sf.py b/src/sage/combinat/sf/sf.py index 17c7de792b8..932119314cc 100644 --- a/src/sage/combinat/sf/sf.py +++ b/src/sage/combinat/sf/sf.py @@ -1535,7 +1535,7 @@ def kBoundedQuotient(self, k, t='t'): sage: KQ = Sym.kBoundedQuotient(3); KQ Traceback (most recent call last): ... - TypeError: unable to convert t to a rational + TypeError: unable to convert 't' to a rational sage: KQ = Sym.kBoundedQuotient(3,t=1); KQ 3-Bounded Quotient of Symmetric Functions over Rational Field with t=1 sage: Sym = SymmetricFunctions(QQ['t'].fraction_field()) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 5a2d6b15c14..ed4ade05e6c 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -5234,7 +5234,7 @@ def hl_creation_operator(self, nu, t = None): REFERENCES: - .. [SZ2001] M. Shimozono, M. Zabrocki, + .. [SZ2001] \M. Shimozono, M. Zabrocki, Hall-Littlewood vertex operators and generalized Kostka polynomials. Adv. Math. 158 (2001), no. 1, 66-85. diff --git a/src/sage/combinat/sf/symplectic.py b/src/sage/combinat/sf/symplectic.py index 2fcd5c09d12..2e4195428dc 100644 --- a/src/sage/combinat/sf/symplectic.py +++ b/src/sage/combinat/sf/symplectic.py @@ -59,7 +59,7 @@ class SymmetricFunctionAlgebra_symplectic(sfa.SymmetricFunctionAlgebra_generic): *Symmetric functions and representations of quantum affine algebras*. :arXiv:`math/0011161v1` - .. [KoikeTerada1987] K. Koike, I. Terada, *Young-diagrammatic methods for + .. [KoikeTerada1987] \K. Koike, I. Terada, *Young-diagrammatic methods for the representation theory of the classical groups of type Bn, Cn, Dn*. J. Algebra 107 (1987), no. 2, 466-511. diff --git a/src/sage/combinat/shard_order.py b/src/sage/combinat/shard_order.py index ebe7121db8f..e2844458c2c 100644 --- a/src/sage/combinat/shard_order.py +++ b/src/sage/combinat/shard_order.py @@ -19,13 +19,13 @@ REFERENCES: -.. [Banc2011] E. E. Bancroft, *Shard Intersections and Cambrian Congruence +.. [Banc2011] \E. E. Bancroft, *Shard Intersections and Cambrian Congruence Classes in Type A.*, Ph.D. Thesis, North Carolina State University. 2011. -.. [Pete2013] T. Kyle Petersen, *On the shard intersection order of +.. [Pete2013] \T. Kyle Petersen, *On the shard intersection order of a Coxeter group*, SIAM J. Discrete Math. 27 (2013), no. 4, 1880-1912. -.. [Read2011] N. Reading, *Noncrossing partitions and the shard intersection +.. [Read2011] \N. Reading, *Noncrossing partitions and the shard intersection order*, J. Algebraic Combin., 33 (2011), 483-530. """ from sage.combinat.posets.posets import Poset diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index 2402b8e97e6..a348f6b41be 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -28,7 +28,7 @@ REFERENCES: -.. [NS] T. Nakanishi, S. Stella, Wonder of sine-Gordon Y-systems, +.. [NS] \T. Nakanishi, S. Stella, Wonder of sine-Gordon Y-systems, to appear in Trans. Amer. Math. Soc., :arxiv:`1212.6853` """ #***************************************************************************** diff --git a/src/sage/combinat/species/generating_series.py b/src/sage/combinat/species/generating_series.py index b2a16703adc..7d6ec6ac73c 100644 --- a/src/sage/combinat/species/generating_series.py +++ b/src/sage/combinat/species/generating_series.py @@ -60,7 +60,7 @@ REFERENCES: -.. [BLL] F. Bergeron, G. Labelle, and P. Leroux. +.. [BLL] \F. Bergeron, G. Labelle, and P. Leroux. "Combinatorial species and tree-like structures". Encyclopedia of Mathematics and its Applications, vol. 67, Cambridge Univ. Press. 1998. .. [BLL-Intro] Francois Bergeron, Gilbert Labelle, and Pierre Leroux. @@ -752,7 +752,7 @@ def arithmetic_product(self, g, check_input = True): REFERENCES: - .. [MM] M. Maia and M. Mendez. "On the arithmetic product of combinatorial species". + .. [MM] \M. Maia and M. Mendez. "On the arithmetic product of combinatorial species". Discrete Mathematics, vol. 308, issue 23, 2008, pp. 5407-5427. :arXiv:`math/0503436v2`. @@ -1264,7 +1264,7 @@ def _exp_gen(R = RationalField()): sage: from sage.combinat.species.generating_series import _exp_gen sage: g = _exp_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [p[], p[1], 1/2*p[1, 1] + 1/2*p[2], 1/6*p[1, 1, 1] + 1/2*p[2, 1] + 1/3*p[3]] """ return (_exp_term(i, R) for i in _integers_from(0)) @@ -1323,7 +1323,7 @@ def _cl_gen (R = RationalField()): sage: from sage.combinat.species.generating_series import _cl_gen sage: g = _cl_gen() - sage: [g.next() for i in range(4)] + sage: [next(g) for i in range(4)] [0, p[1], -1/2*p[1, 1] - 1/2*p[2], 1/3*p[1, 1, 1] - 1/3*p[3]] """ return (_cl_term(i, R) for i in _integers_from(0)) @@ -1355,7 +1355,7 @@ def LogarithmCycleIndexSeries(R = RationalField()): REFERENCES: - .. [Labelle] G. Labelle. "New combinatorial computational methods arising from pseudo-singletons." DMTCS Proceedings 1, 2008. + .. [Labelle] \G. Labelle. "New combinatorial computational methods arising from pseudo-singletons." DMTCS Proceedings 1, 2008. """ CIS = CycleIndexSeriesRing(R) return CIS(_cl_gen(R)) diff --git a/src/sage/combinat/subword_complex.py b/src/sage/combinat/subword_complex.py index d664c1f289d..a9cd3d105db 100644 --- a/src/sage/combinat/subword_complex.py +++ b/src/sage/combinat/subword_complex.py @@ -28,6 +28,9 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division + from copy import copy from sage.misc.cachefunc import cached_method from sage.structure.element import Element @@ -38,6 +41,7 @@ from sage.geometry.cone import Cone from sage.combinat.subword_complex_c import _flip_c, _construct_facets_c + class SubwordComplexFacet(Simplex, Element): r""" A facet of a subword complex. @@ -341,7 +345,7 @@ def upper_root_configuration(self): conf = self._root_configuration_indices() W = self.parent().group() Phi = W.roots() - N = len(Phi) / 2 + N = len(Phi) // 2 return [Phi[i - N] for i in conf if i >= N] # weights diff --git a/src/sage/combinat/tableau.py b/src/sage/combinat/tableau.py index 3cc12c796e8..14ca5ea9cbe 100644 --- a/src/sage/combinat/tableau.py +++ b/src/sage/combinat/tableau.py @@ -1488,7 +1488,7 @@ def bender_knuth_involution(self, k, rows=None, check=True): REFERENCES: - .. [BerKilGGI] A. N. Kirillov, A. D. Berenstein, + .. [BerKilGGI] \A. N. Kirillov, A. D. Berenstein, *Groups generated by involutions, Gelfand--Tsetlin patterns, and combinatorics of Young tableaux*, Algebra i Analiz, 1995, Volume 7, Issue 1, pp. 92--152. @@ -1939,7 +1939,7 @@ def k_weight(self, k): REFERENCES: - .. [Ive2012] S. Iveson, + .. [Ive2012] \S. Iveson, *Tableaux on `k + 1`-cores, reduced words for affine permutations, and `k`-Schur expansions*, Operators on `k`-tableaux and the `k`-Littlewood-Richardson @@ -2620,10 +2620,10 @@ def _slide_down(self, c, n): right_neighbor = new_st[spotl][spotc + 1] if go_right is None or upper_neighbor > right_neighbor: go_right = True - if go_right == True: + if go_right is True: new_st[spotl][spotc] = right_neighbor spotc += 1 - elif go_right == False: + elif go_right is False: new_st[spotl][spotc] = upper_neighbor spotl += 1 else: @@ -3327,7 +3327,7 @@ def promotion_operator(self, i): REFERENCES: - .. [LLM01] L. Lapointe, A. Lascoux, J. Morse. + .. [LLM01] \L. Lapointe, A. Lascoux, J. Morse. *Tableau atoms and a new Macdonald positivity conjecture*. :arxiv:`math/0008073v2`. @@ -3553,12 +3553,12 @@ def is_key_tableau(self): REFERENCES: - .. [LS90] A. Lascoux, M.-P. Schutzenberger. + .. [LS90] \A. Lascoux, M.-P. Schutzenberger. Keys and standard bases, invariant theory and tableaux. IMA Volumes in Math and its Applications (D. Stanton, ED.). Southend on Sea, UK, 19 (1990). 125-144. - .. [Willis10] M. Willis. A direct way to find the right key of + .. [Willis10] \M. Willis. A direct way to find the right key of a semistandard Young tableau. :arxiv:`1110.6184v1`. EXAMPLES:: @@ -3748,7 +3748,7 @@ def seg(self): REFERENCES: - .. [S14] B. Salisbury. + .. [S14] \B. Salisbury. The flush statistic on semistandard Young tableaux. :arXiv:`1401.1185` @@ -5747,7 +5747,7 @@ def random_element(self): REFERENCES: - .. [Krat99] C. Krattenthaler, + .. [Krat99] \C. Krattenthaler, *Another Involution Principle-Free Bijective Proof of Stanley's Hook Content Formula*, Journal of Combinatorial Theory, Series A vol 88 Issue 1 (1999), 66-92, http://www.sciencedirect.com/science/article/pii/0012365X9290368P diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 6e1af7a1cc9..9ce7035efac 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -189,10 +189,10 @@ REFERENCES: -.. [DJM] R. Dipper, G. James and A. Mathas "The cyclotomic q-Schur algebra", +.. [DJM] \R. Dipper, G. James and A. Mathas "The cyclotomic q-Schur algebra", Math. Z, 229 (1999), 385-416. -.. [BK] J. Brundan and A. Kleshchev "Graded decomposition numbers for cyclotomic Hecke algebras", +.. [BK] \J. Brundan and A. Kleshchev "Graded decomposition numbers for cyclotomic Hecke algebras", Adv. Math., 222 (2009), 1883-1942" """ diff --git a/src/sage/combinat/tamari_lattices.py b/src/sage/combinat/tamari_lattices.py index 2aad873edff..2dfba4b6b6b 100644 --- a/src/sage/combinat/tamari_lattices.py +++ b/src/sage/combinat/tamari_lattices.py @@ -207,7 +207,7 @@ def GeneralizedTamariLattice(a, b, m=1): REFERENCES: - .. [BMFPR] M. Bousquet-Melou, E. Fusy, L.-F. Preville Ratelle. + .. [BMFPR] \M. Bousquet-Melou, E. Fusy, L.-F. Preville Ratelle. *The number of intervals in the m-Tamari lattices*. :arxiv:`1106.1498` """ if not(gcd(a, b) == 1 and a >= b): diff --git a/src/sage/combinat/tiling.py b/src/sage/combinat/tiling.py index 142776ac162..6d771a7e94b 100644 --- a/src/sage/combinat/tiling.py +++ b/src/sage/combinat/tiling.py @@ -69,9 +69,9 @@ 15 sage: it = T.solve() sage: next(it) - [Polyomino: [(0,)], Color: gray, Polyomino: [(1,), (2,)], Color: gray, Polyomino: [(3,), (4,), (5,)], Color: gray] + [Polyomino: [(0)], Color: gray, Polyomino: [(1), (2)], Color: gray, Polyomino: [(3), (4), (5)], Color: gray] sage: next(it) - [Polyomino: [(0,)], Color: gray, Polyomino: [(1,), (2,), (3,)], Color: gray, Polyomino: [(4,), (5,)], Color: gray] + [Polyomino: [(0)], Color: gray, Polyomino: [(1), (2), (3)], Color: gray, Polyomino: [(4), (5)], Color: gray] sage: T.number_of_solutions() 6 @@ -218,6 +218,9 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division + import itertools from sage.structure.sage_object import SageObject from sage.modules.free_module_element import vector @@ -225,6 +228,7 @@ from sage.misc.cachefunc import cached_method, cached_function from sage.misc.superseded import deprecated_function_alias + ####################################### # n-cube isometry group transformations ####################################### @@ -447,13 +451,25 @@ def __init__(self, coords, color='gray'): sage: Polyomino([(0,0), (1,0), (2,0)]) Polyomino: [(0, 0), (1, 0), (2, 0)], Color: gray """ - assert isinstance(color, str) + from sage.modules.free_module import FreeModule + from sage.rings.integer_ring import ZZ + + if not isinstance(color, str): + raise TypeError("color = ({!r}) must be a string".format(color)) self._color = color - self._blocs = frozenset(tuple(c) for c in coords) - assert len(self._blocs) != 0, "Polyomino must be non empty" - dimension_set = set(len(a) for a in self._blocs) - assert len(dimension_set) <= 1, "coord must be all of the same dimension" - self._dimension = dimension_set.pop() + + if not isinstance(coords, (tuple,list)): + coords = list(coords) + if not coords: + raise ValueError("Polyomino must be non empty") + self._dimension = ZZ(len(coords[0])) + self._free_module = FreeModule(ZZ, self._dimension) + + self._blocs = coords + self._blocs = map(self._free_module, self._blocs) + for b in self._blocs: + b.set_immutable() + self._blocs = tuple(sorted(set(self._blocs))) def __repr__(self): r""" @@ -503,7 +519,7 @@ def __eq__(self, other): sage: p == r False """ - return self._blocs == other._blocs + return isinstance(other, Polyomino) and self._blocs == other._blocs def __ne__(self, other): r""" @@ -528,7 +544,7 @@ def __ne__(self, other): sage: p != r True """ - return self._blocs != other._blocs + return not self._blocs == other._blocs def __iter__(self): r""" @@ -538,7 +554,7 @@ def __iter__(self): sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') sage: it = iter(p) sage: next(it) - (1, 1, 0) + (0, 0, 0) """ return iter(self._blocs) @@ -589,11 +605,8 @@ def __sub__(self, v): sage: p - (2,2,2) Polyomino: [(-2, -2, -2), (-1, -2, -2), (-1, -1, -2), (-1, -1, -1), (-1, 0, -2)], Color: deeppink """ - if not len(v) == self._dimension: - raise ValueError("Dimension of input vector must match the " - "dimension of the polyomino") - v = vector(v) - return Polyomino([vector(p)-v for p in self], color=self._color) + v = self._free_module(v) + return Polyomino([p-v for p in self], color=self._color) def __add__(self, v): r""" @@ -614,11 +627,8 @@ def __add__(self, v): sage: p + (2,2,2) Polyomino: [(2, 2, 2), (3, 2, 2), (3, 3, 2), (3, 3, 3), (3, 4, 2)], Color: deeppink """ - if not len(v) == self._dimension: - raise ValueError("Dimension of input vector must match " - "the dimension of the polyomino") - v = vector(v) - return Polyomino([vector(p)+v for p in self], color=self._color) + v = self._free_module(v) + return Polyomino([p+v for p in self], color=self._color) def __rmul__(self, m): r""" @@ -655,7 +665,7 @@ def __rmul__(self, m): if not m.nrows() == m.ncols() == self._dimension: raise ValueError("Dimension of input matrix must match the " "dimension of the polyomino") - return Polyomino([m * vector(p) for p in self], color=self._color) + return Polyomino([m * p for p in self], color=self._color) def bounding_box(self): r""" @@ -913,7 +923,6 @@ def neighbor_edges(self): [(1, 1), (1, 2)] """ for P, Q in itertools.combinations(self, 2): - P, Q = vector(P), vector(Q) s = sorted(map(abs, Q-P)) firsts = s[:-1] last = s[-1] @@ -943,7 +952,7 @@ def center(self): sage: p.center() (3/4, 3/4) """ - return sum(vector(t) for t in self) / len(self) + return sum(self) / len(self) def boundary(self): r""" @@ -1143,6 +1152,7 @@ def __init__(self, pieces, box, rotation=True, Reusing pieces allowed: False """ self._pieces = pieces + self._free_module = self._pieces[0]._free_module self._box = box self._rotation = rotation self._reflection = reflection @@ -1241,7 +1251,10 @@ def space(self): sage: list(T.space()) [(0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 0, 3), (0, 0, 4), (0, 0, 5)] """ - return xmrange(self._box, tuple) + for v in xmrange(self._box, tuple): + v = self._free_module(v) + v.set_immutable() + yield v @cached_method def coord_to_int_dict(self): @@ -1350,9 +1363,9 @@ def rows_for_piece(self, i, mod_box_isometries=False): sage: T.rows_for_piece(0) [[0, 3], [0, 4], [0, 5], [0, 6], [0, 7], [0, 8]] sage: T.rows_for_piece(1) - [[1, 3, 4], [1, 4, 5], [1, 5, 6], [1, 6, 7], [1, 8, 7]] + [[1, 3, 4], [1, 4, 5], [1, 5, 6], [1, 6, 7], [1, 7, 8]] sage: T.rows_for_piece(2) - [[2, 3, 4, 5], [2, 4, 5, 6], [2, 5, 6, 7], [2, 8, 6, 7]] + [[2, 3, 4, 5], [2, 4, 5, 6], [2, 5, 6, 7], [2, 6, 7, 8]] Less rows when using ``mod_box_isometries=True``:: @@ -1360,18 +1373,18 @@ def rows_for_piece(self, i, mod_box_isometries=False): sage: b = Polyomino([(0,0,0), (1,0,0), (0,1,0)]) sage: T = TilingSolver([a,b], box=(2,1,3)) sage: T.rows_for_piece(0) - [[0, 5, 3, 6], - [0, 7, 4, 6], + [[0, 3, 5, 6], + [0, 4, 6, 7], + [0, 2, 5, 6], + [0, 3, 6, 7], [0, 2, 3, 6], - [0, 7, 3, 4], - [0, 5, 2, 3], - [0, 3, 4, 6], - [0, 5, 2, 6], - [0, 7, 3, 6]] + [0, 3, 4, 7], + [0, 2, 3, 5], + [0, 3, 4, 6]] sage: T.rows_for_piece(0, mod_box_isometries=True) - [[0, 2, 3, 6], [0, 7, 3, 4]] + [[0, 2, 5, 6], [0, 3, 6, 7]] sage: T.rows_for_piece(1, mod_box_isometries=True) - [[1, 2, 3, 6], [1, 7, 3, 4]] + [[1, 2, 5, 6], [1, 3, 6, 7]] """ p = self._pieces[i] if self._rotation: @@ -1420,11 +1433,11 @@ def rows(self): [1, 4, 5] [1, 5, 6] [1, 6, 7] - [1, 8, 7] + [1, 7, 8] [2, 3, 4, 5] [2, 4, 5, 6] [2, 5, 6, 7] - [2, 8, 6, 7] + [2, 6, 7, 8] """ rows = [] for i in range(len(self._pieces)): @@ -1453,25 +1466,25 @@ def _rows_mod_box_isometries(self, i): sage: p = Polyomino([(0,0,0), (1,0,0), (1,1,0), (1,0,1), (2,0,1)], color='red') sage: T = TilingSolver([p], box=(3,4,2)) sage: T._rows_mod_box_isometries(0) - [[0, 4, 3, 11, 2, 5], - [0, 7, 5, 6, 13, 4], - [0, 11, 12, 19, 13, 10], - [0, 14, 13, 15, 12, 21], - [0, 9, 12, 1, 18, 10], - [0, 11, 3, 20, 12, 14], - [0, 22, 5, 13, 16, 14], - [0, 9, 12, 20, 2, 10], - [0, 22, 11, 12, 4, 14], - [0, 14, 13, 16, 24, 6], - [0, 4, 3, 1, 13, 11], - [0, 5, 6, 15, 13, 3], - [0, 9, 11, 12, 19, 21], - [0, 14, 13, 21, 23, 11]] + [[0, 2, 10, 11, 12, 20], + [0, 4, 12, 13, 14, 22], + [0, 6, 14, 15, 16, 24], + [0, 1, 9, 10, 11, 18], + [0, 3, 11, 12, 13, 20], + [0, 5, 13, 14, 15, 22], + [0, 4, 6, 10, 11, 12], + [0, 6, 8, 12, 13, 14], + [0, 12, 14, 18, 19, 20], + [0, 14, 16, 20, 21, 22], + [0, 4, 9, 11, 12, 14], + [0, 6, 11, 13, 14, 16], + [0, 12, 17, 19, 20, 22], + [0, 14, 19, 21, 22, 24]] We test that there are four times less rows for that polyomino:: - sage: len(T.rows()) / len(T._rows_mod_box_isometries(0)) - 4 + sage: len(T.rows()) == 4 * len(T._rows_mod_box_isometries(0)) + True Now, a real use case. A solution of the game Quantumino is a tiling of a 5x8x2 box. Since a 5x8x2 box has four orientation preserving @@ -1569,7 +1582,7 @@ def row_to_polyomino(self, row_number): :: sage: T.row_to_polyomino(7) - Polyomino: [(0, 0, 1), (1, 0, 1), (1, 0, 2)], Color: blue + Polyomino: [(0, 0, 1), (0, 0, 2), (1, 0, 1)], Color: blue :: @@ -1665,25 +1678,25 @@ def _dlx_common_prefix_solutions_iterator(self): sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow') sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True) sage: for a in T._dlx_common_prefix_solutions_iterator(): a - [64, 83, 149, 44, 179, 62, 35, 162, 132, 101] - [64, 83, 149, 44, 179] - [64, 83, 149, 44, 179, 154, 35, 162, 132, 175] - [64, 83, 149] - [64, 83, 149, 97, 39, 162, 35, 62, 48, 106] - [64] - [64, 157, 149, 136, 179, 62, 35, 162, 132, 101] - [64, 157, 149, 136, 179] - [64, 157, 149, 136, 179, 154, 35, 162, 132, 175] + [0, 65, 177, 164, 87, 44, 109, 70, 160, 129] + [0, 65, 177, 164, 87] + [0, 65, 177, 164, 87, 182, 109, 70, 160, 83] + [0] + [0, 111, 177, 26, 87, 44, 109, 70, 160, 129] + [0, 111, 177, 26, 87] + [0, 111, 177, 26, 87, 182, 109, 70, 160, 83] + [0, 111, 177] + [0, 111, 177, 125, 21, 70, 109, 44, 30, 134] [] - [82, 119, 58, 97, 38, 87, 8, 63, 48, 107] - [82, 119, 58, 97, 38] - [82, 119, 58, 97, 38, 161, 8, 63, 140, 107] - [82, 119] - [82, 119, 150, 136, 180, 63, 8, 161, 131, 175] - [82, 119, 150] - [82, 119, 150, 171, 38, 87, 8, 63, 48, 107] - [82, 119, 150, 171, 38] - [82, 119, 150, 171, 38, 161, 8, 63, 140, 107] + [110, 147, 40, 125, 20, 69, 54, 45, 168, 135] + [110, 147, 40, 125, 20] + [110, 147, 40, 125, 20, 115, 54, 45, 30, 135] + [110, 147] + [110, 147, 178, 79, 20, 69, 54, 45, 168, 135] + [110, 147, 178, 79, 20] + [110, 147, 178, 79, 20, 115, 54, 45, 30, 135] + [110, 147, 178] + [110, 147, 178, 164, 88, 45, 54, 69, 159, 83] """ it = self._dlx_solutions_iterator() B = next(it) @@ -1730,16 +1743,16 @@ def _dlx_incremental_solutions_iterator(self): sage: y = Polyomino([(0,0),(1,0),(2,0),(3,0),(2,1)], color='yellow') sage: T = TilingSolver([y], box=(5,10), reusable=True, reflection=True) sage: for a in T._dlx_solutions_iterator(): a - [64, 83, 149, 44, 179, 62, 35, 162, 132, 101] - [64, 83, 149, 44, 179, 154, 35, 162, 132, 175] - [64, 83, 149, 97, 39, 162, 35, 62, 48, 106] - [64, 157, 149, 136, 179, 62, 35, 162, 132, 101] - [64, 157, 149, 136, 179, 154, 35, 162, 132, 175] - [82, 119, 58, 97, 38, 87, 8, 63, 48, 107] - [82, 119, 58, 97, 38, 161, 8, 63, 140, 107] - [82, 119, 150, 136, 180, 63, 8, 161, 131, 175] - [82, 119, 150, 171, 38, 87, 8, 63, 48, 107] - [82, 119, 150, 171, 38, 161, 8, 63, 140, 107] + [0, 65, 177, 164, 87, 44, 109, 70, 160, 129] + [0, 65, 177, 164, 87, 182, 109, 70, 160, 83] + [0, 111, 177, 26, 87, 44, 109, 70, 160, 129] + [0, 111, 177, 26, 87, 182, 109, 70, 160, 83] + [0, 111, 177, 125, 21, 70, 109, 44, 30, 134] + [110, 147, 40, 125, 20, 69, 54, 45, 168, 135] + [110, 147, 40, 125, 20, 115, 54, 45, 30, 135] + [110, 147, 178, 79, 20, 69, 54, 45, 168, 135] + [110, 147, 178, 79, 20, 115, 54, 45, 30, 135] + [110, 147, 178, 164, 88, 45, 54, 69, 159, 83] sage: len(list(T._dlx_incremental_solutions_iterator())) 123 """ diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index cd79c7622ba..88c6134eb4b 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -254,7 +254,7 @@ The founding idea of algebraic combinatorics, introduced by Euler in a letter to Goldbach of 1751 to treat a similar problem , is to -manipuate all the numbers `c_n` simultaneously, by encoding them +manipulate all the numbers `c_n` simultaneously, by encoding them as coefficients in a formal power series, called the *generating function* of the `c_n`’s: @@ -531,7 +531,7 @@ .. math:: c_{n+1}=\frac{(4n-2)}{n+1}c_n After fixing the correct initial conditions, it becomes possible to -calculate the coefficents of `C(z)` recursively:: +calculate the coefficients of `C(z)` recursively:: sage: def C(n): return 1 if n <= 1 else (4*n-6)/n * C(n-1) sage: [ C(i) for i in range(10) ] @@ -1116,7 +1116,7 @@ sage: next(counter_examples) 23 -.. topic:: Exercice +.. topic:: Exercise What do the following commands do? @@ -1133,7 +1133,7 @@ Which of the last two is more economical in terms of time? In terms of memory? By how much? -.. topic:: Exercice +.. topic:: Exercise Try each of the following commands, and explain its result. If possible, hide the result first and try to guess it before @@ -1342,7 +1342,7 @@ Counting the words one by one is clearly not an efficient method in this case, since the formula `n^\ell` is also available; note, -though, that this is not the stupidest possible approach — it does, at +though, that this is not the stupidest possible approach - it does, at least, avoid constructing the entire list in memory. We now consider Dyck words, which are well-parenthesized words in the @@ -1786,7 +1786,7 @@ In general, testing if two labelled graphs are isomorphic is expensive. However, the number of graphs, even unlabelled, grows very rapidly. Nonetheless, it is possible to list unlabelled graphs very efficiently -considering their number. For example, the program Nauty can list the +considering their number. For example, the program ``Nauty`` can list the `12005168` simple graphs with `10` vertices in `20` seconds. diff --git a/src/sage/combinat/words/finite_word.py b/src/sage/combinat/words/finite_word.py index d5172fafd44..a7cc0ef21fb 100644 --- a/src/sage/combinat/words/finite_word.py +++ b/src/sage/combinat/words/finite_word.py @@ -2963,21 +2963,21 @@ def defect(self, f=None): REFERENCES: - .. [BBGL08] A. Blondin Massé, S. Brlek, A. Garon, and S. Labbé, + .. [BBGL08] \A. Blondin Massé, S. Brlek, A. Garon, and S. Labbé, Combinatorial properties of f -palindromes in the Thue-Morse sequence. Pure Math. Appl., 19(2-3):39--52, 2008. - .. [BHNR04] S. Brlek, S. Hamel, M. Nivat, C. Reutenauer, On the + .. [BHNR04] \S. Brlek, S. Hamel, M. Nivat, C. Reutenauer, On the Palindromic Complexity of Infinite Words, in J. Berstel, J. Karhumaki, D. Perrin, Eds, Combinatorics on Words with Applications, International Journal of Foundation of Computer Science, Vol. 15, No. 2 (2004) 293--306. - .. [DJP01] X. Droubay, J. Justin, G. Pirillo, Episturmian words and some + .. [DJP01] \X. Droubay, J. Justin, G. Pirillo, Episturmian words and some constructions of de Luca and Rauzy, Theoret. Comput. Sci. 255, (2001), no. 1--2, 539--553. - .. [Sta11] Š. Starosta, On Theta-palindromic Richness, Theoret. Comp. + .. [Sta11] \Š. Starosta, On Theta-palindromic Richness, Theoret. Comp. Sci. 412 (2011) 1111--1121 """ g_w = 0 @@ -4621,10 +4621,7 @@ def evaluation_dict(self): sage: f['3'] == 1 True """ - d = {} - for a in self: - d[a] = d.get(a,0) + 1 - return d + return evaluation_dict(self) def evaluation_sparse(self): r""" @@ -4915,20 +4912,8 @@ def standard_permutation(self): sage: Word(p.inverse().action(w)) word: bbbaaa """ - ev_dict = self.evaluation_dict() - ordered_alphabet = sorted(ev_dict, cmp=self.parent().cmp_letters) - offset = 0 - temp = 0 - for k in ordered_alphabet: - temp = ev_dict[k] - ev_dict[k] = offset - offset += temp - result = [] - for l in self: - ev_dict[l] += 1 - result.append(ev_dict[l]) - from sage.combinat.permutation import Permutation - return Permutation(result) + from sage.combinat.permutation import to_standard + return to_standard(self, cmp=self.parent().cmp_letters) def _s(self, i): r""" @@ -5710,12 +5695,12 @@ def is_sturmian_factor(self): REFERENCES: - .. [Arn2002] P. Arnoux, Sturmian sequences, in Substitutions in Dynamics, + .. [Arn2002] \P. Arnoux, Sturmian sequences, in Substitutions in Dynamics, N. Pytheas Fogg (Ed.), Arithmetics, and Combinatorics (Lecture Notes in Mathematics, Vol. 1794), 2002. - .. [Ser1985] C. Series. The geometry of Markoff numbers. The Mathematical + .. [Ser1985] \C. Series. The geometry of Markoff numbers. The Mathematical Intelligencer, 7(3):20–29, 1985. - .. [SU2009] J. Smillie and C. Ulcigrai. Symbolic coding for linear + .. [SU2009] \J. Smillie and C. Ulcigrai. Symbolic coding for linear trajectories in the regular octagon, :arxiv:`0905.0871`, 2009. AUTHOR: @@ -5775,7 +5760,7 @@ def is_tangent(self): REFERENCES: - .. [Mon2010] T. Monteil, The asymptotic language of smooth curves, talk + .. [Mon2010] \T. Monteil, The asymptotic language of smooth curves, talk at LaCIM2010. AUTHOR: @@ -6883,7 +6868,7 @@ def is_christoffel(self): some recent results). In S. Bozapalidis and G. Rahonis, editors, CAI 2007,volume 4728 of Lecture Notes in Computer Science, pages 23-47. Springer-Verlag, 2007. - .. [2] J. Berstel, A. Lauve, C. R., F. Saliola, Combinatorics on + .. [2] \J. Berstel, A. Lauve, C. R., F. Saliola, Combinatorics on words: Christoffel words and repetitions in words, CRM Monograph Series, 27. American Mathematical Society, Providence, RI, 2009. xii+147 pp. ISBN: 978-0-8218-4480-9 @@ -6967,3 +6952,35 @@ def __repr__(self): """ return '(%s)' % ', '.join(w.string_rep() for w in self) +####################################################################### + +def evaluation_dict(w): + r""" + Return a dictionary keyed by the letters occurring in ``w`` with + values the number of occurrences of the letter. + + INPUT: + + - ``w`` -- a word + + TESTS:: + + sage: from sage.combinat.words.finite_word import evaluation_dict + sage: evaluation_dict([2,1,4,2,3,4,2]) + {1: 1, 2: 3, 3: 1, 4: 2} + sage: evaluation_dict('badbcdb') + {'a': 1, 'b': 3, 'c': 1, 'd': 2} + sage: evaluation_dict([]) + {} + + :: + + sage: evaluation_dict('1213121') # keys appear in random order + {'1': 4, '2': 2, '3': 1} + + """ + d = defaultdict(int) + for a in w: + d[a] += 1 + return dict(d) + diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index ea40dbaf4b5..e6068e0be35 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -630,7 +630,7 @@ def _process_letter(self, letter): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1),letter) - while end_state == False: + while not end_state: # adjoin a new state rr and create a transition from r to rr rr = len(self._transition_function) self._transition_function[rr] = {} @@ -819,7 +819,7 @@ def to_digraph(self, word_labels=False): INPUT: - - ``word_labels`` - boolean (defaut: ``False``) if ``False``, labels + - ``word_labels`` - boolean (default: ``False``) if ``False``, labels the edges by pairs `(i, j)`; if ``True``, labels the edges by ``word[i:j]``. @@ -852,14 +852,14 @@ def plot(self, word_labels=False, layout='tree', tree_root=0, INPUT: - - ``word_labels`` - boolean (defaut: ``False``) if ``False``, labels + - ``word_labels`` - boolean (default: ``False``) if ``False``, labels the edges by pairs `(i, j)`; if ``True``, labels the edges by ``word[i:j]``. - - ``layout`` - (defaut: ``'tree'``) - - ``tree_root`` - (defaut: 0) - - ``tree_orientation`` - (defaut: ``'up'``) - - ``vertex_colors`` - (defaut: ``None``) - - ``edge_labels`` - (defaut: ``True``) + - ``layout`` - (default: ``'tree'``) + - ``tree_root`` - (default: 0) + - ``tree_orientation`` - (default: ``'up'``) + - ``vertex_colors`` - (default: ``None``) + - ``edge_labels`` - (default: ``True``) EXAMPLES:: @@ -1096,7 +1096,7 @@ def to_explicit_suffix_tree(self): (s,(k,i)) = self._active_state old_r = 0 (end_state, r) = self._test_and_split(s,(k,i-1), end_of_string) - while end_state == False: + while not end_state: (s, k) = self._canonize(self._suffix_link[s], (k,i-1)) (end_state, r) = self._test_and_split(s, (k,i-1), end_of_string) # remove the end of string symbol from the word diff --git a/src/sage/combinat/words/word_char.pyx b/src/sage/combinat/words/word_char.pyx index a0a253eda38..7260f0b3436 100644 --- a/src/sage/combinat/words/word_char.pyx +++ b/src/sage/combinat/words/word_char.pyx @@ -12,7 +12,7 @@ Fast word datatype using an array of unsigned char. #***************************************************************************** include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "sage/data_structures/bitset.pxi" cimport cython @@ -103,7 +103,7 @@ cdef class WordDatatype_char(WordDatatype): """ cdef size_t i self._length = len(data) - self._data = sage_malloc(self._length * sizeof(unsigned char)) + self._data = sig_malloc(self._length * sizeof(unsigned char)) if self._data == NULL: raise MemoryError @@ -114,13 +114,13 @@ cdef class WordDatatype_char(WordDatatype): r""" Deallocate memory only if self uses it own memory. - Note that ``sage_free`` will not deallocate memory if self is the + Note that ``sig_free`` will not deallocate memory if self is the master of another word. """ # it is strictly forbidden here to access _master here! (it will be set # to None most of the time) if self._is_slice == 0: - sage_free(self._data) + sig_free(self._data) def __nonzero__(self): r""" @@ -373,7 +373,7 @@ cdef class WordDatatype_char(WordDatatype): return self._new_c(NULL, 0, None) if step == 1: return self._new_c(self._data+start, stop-start, self) - data = sage_malloc(slicelength * sizeof(unsigned char)) + data = sig_malloc(slicelength * sizeof(unsigned char)) j = 0 for k in range(start,stop,step): data[j] = self._data[k] @@ -427,7 +427,7 @@ cdef class WordDatatype_char(WordDatatype): cdef _concatenate(self, WordDatatype_char other): cdef unsigned char * data - data = sage_malloc((self._length + other._length) * sizeof(unsigned char)) + data = sig_malloc((self._length + other._length) * sizeof(unsigned char)) if data == NULL: raise MemoryError @@ -539,7 +539,7 @@ cdef class WordDatatype_char(WordDatatype): if w._length > SIZE_T_MAX / (i+1): raise OverflowError("the length of the result is too large") cdef size_t new_length = w._length * i + rest - cdef unsigned char * data = sage_malloc(new_length * sizeof(unsigned char)) + cdef unsigned char * data = sig_malloc(new_length * sizeof(unsigned char)) if data == NULL: raise MemoryError diff --git a/src/sage/combinat/words/word_generators.py b/src/sage/combinat/words/word_generators.py index 34e27aaf7a2..9868abbcea2 100644 --- a/src/sage/combinat/words/word_generators.py +++ b/src/sage/combinat/words/word_generators.py @@ -18,18 +18,18 @@ REFERENCES: -.. [AC03] B. Adamczewski, J. Cassaigne, On the transcendence of real +.. [AC03] \B. Adamczewski, J. Cassaigne, On the transcendence of real numbers with a regular expansion, J. Number Theory 103 (2003) 27--37. -.. [BmBGL07] A. Blondin-Masse, S. Brlek, A. Glen, and S. Labbe. On the +.. [BmBGL07] \A. Blondin-Masse, S. Brlek, A. Glen, and S. Labbe. On the critical exponent of generalized Thue-Morse words. *Discrete Math. Theor. Comput. Sci.* 9 (1):293--304, 2007. -.. [BmBGL09] A. Blondin-Masse, S. Brlek, A. Garon, and S. Labbe. Christoffel +.. [BmBGL09] \A. Blondin-Masse, S. Brlek, A. Garon, and S. Labbe. Christoffel and Fibonacci Tiles, DGCI 2009, Montreal, to appear in LNCS. -.. [Loth02] M. Lothaire, Algebraic Combinatorics On Words, vol. 90 of +.. [Loth02] \M. Lothaire, Algebraic Combinatorics On Words, vol. 90 of Encyclopedia of Mathematics and its Applications, Cambridge University Press, U.K., 2002. @@ -1257,10 +1257,10 @@ def StandardEpisturmianWord(self, directive_word): REFERENCES: - .. [JP02] J. Justin, G. Pirillo, Episturmian words and episturmian + .. [JP02] \J. Justin, G. Pirillo, Episturmian words and episturmian morphisms, Theoret. Comput. Sci. 276 (2002) 281--313. - .. [GJ07] A. Glen, J. Justin, Episturmian words: a survey, Preprint, + .. [GJ07] \A. Glen, J. Justin, Episturmian words: a survey, Preprint, 2007, arXiv:0801.1655. """ if not isinstance(directive_word, Word_class): @@ -1335,7 +1335,7 @@ def MinimalSmoothPrefix(self, n): REFERENCES: - .. [BMP07] S. Brlek, G. Melançon, G. Paquin, Properties of the extremal + .. [BMP07] \S. Brlek, G. Melançon, G. Paquin, Properties of the extremal infinite smooth words, Discrete Math. Theor. Comput. Sci. 9 (2007) 33--49. """ diff --git a/src/sage/crypto/block_cipher/miniaes.py b/src/sage/crypto/block_cipher/miniaes.py index a40545d2e6e..05319154ee4 100644 --- a/src/sage/crypto/block_cipher/miniaes.py +++ b/src/sage/crypto/block_cipher/miniaes.py @@ -131,7 +131,7 @@ class MiniAES(SageObject): REFERENCES: - .. [P02] R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a + .. [P02] \R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002. """ diff --git a/src/sage/crypto/block_cipher/sdes.py b/src/sage/crypto/block_cipher/sdes.py index c45a21784ea..8197c552f12 100644 --- a/src/sage/crypto/block_cipher/sdes.py +++ b/src/sage/crypto/block_cipher/sdes.py @@ -76,10 +76,10 @@ class SimplifiedDES(SageObject): REFERENCES: - .. [Pha02] R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a + .. [Pha02] \R. C.-W. Phan. Mini advanced encryption standard (mini-AES): a testbed for cryptanalysis students. Cryptologia, 26(4):283--306, 2002. - .. [Sch96] E. Schaefer. A simplified data encryption algorithm. + .. [Sch96] \E. Schaefer. A simplified data encryption algorithm. Cryptologia, 20(1):77--84, 1996. """ diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index bf856190868..0b37907d368 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -688,14 +688,14 @@ cdef class BooleanFunction(SageObject): if self._walsh_hadamard_transform is None: n = self._truth_table.size - temp = sage_malloc(sizeof(long)*n) + temp = sig_malloc(sizeof(long)*n) for 0<= i < n: temp[i] = (bitset_in(self._truth_table,i)<<1)-1 walsh_hadamard(temp, self._nvariables) self._walsh_hadamard_transform = tuple( [temp[i] for i in xrange(n)] ) - sage_free(temp) + sig_free(temp) return self._walsh_hadamard_transform @@ -862,7 +862,7 @@ cdef class BooleanFunction(SageObject): if self._autocorrelation is None: n = self._truth_table.size - temp = sage_malloc(sizeof(long)*n) + temp = sig_malloc(sizeof(long)*n) W = self.walsh_hadamard_transform() for 0<= i < n: @@ -870,7 +870,7 @@ cdef class BooleanFunction(SageObject): walsh_hadamard(temp, self._nvariables) self._autocorrelation = tuple( [temp[i]>>self._nvariables for i in xrange(n)] ) - sage_free(temp) + sig_free(temp) return self._autocorrelation diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index 885359b561c..8ea6bbcb3f9 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -48,7 +48,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, - ``n`` -- Determinant size, primal:`det(L) = q^n`, dual:`det(L) = q^{m-n}`. For ideal lattices this is also the degree of the quotient polynomial. - ``m`` -- Lattice dimension, `L \subseteq Z^m`. - - ``q`` -- Coefficent size, `q-Z^m \subseteq L`. + - ``q`` -- Coefficient size, `q-Z^m \subseteq L`. - ``seed`` -- Randomness seed. - ``quotient`` -- For the type ideal, this determines the quotient polynomial. Ignored for all other types. diff --git a/src/sage/crypto/mq/sbox.py b/src/sage/crypto/mq/sbox.py index d95d0bcd2be..edfbabc5fef 100644 --- a/src/sage/crypto/mq/sbox.py +++ b/src/sage/crypto/mq/sbox.py @@ -2,6 +2,7 @@ S-Boxes and Their Algebraic Representations """ +from sage.misc.cachefunc import cached_method from sage.combinat.integer_vector import IntegerVectors from sage.matrix.constructor import Matrix from sage.misc.misc_c import prod as mul @@ -13,6 +14,7 @@ from sage.rings.integer import Integer from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.sage_object import SageObject +from sage.crypto.boolean_function import BooleanFunction class SBox(SageObject): r""" @@ -24,7 +26,7 @@ class SBox(SageObject): cryptanalysis [Heys02]_. This module implements an S-box class which allows an algebraic - treatment. + treatment and determine various cryptographic properties. EXAMPLE: @@ -60,11 +62,11 @@ class SBox(SageObject): REFERENCES: - .. [Heys02] H. Heys *A Tutorial on Linear and Differential + .. [Heys02] \H. Heys *A Tutorial on Linear and Differential Cryptanalysis* ; 2002' available at http://www.engr.mun.ca/~howard/PAPERS/ldc_tutorial.pdf - .. [PRESENT07] A. Bogdanov, L. Knudsen, G. Leander, C. Paar, + .. [PRESENT07] \A. Bogdanov, L. Knudsen, G. Leander, C. Paar, A. Poschmann, M. Robshaw, Y. Seurin, C. Vikkelsoe *PRESENT: An Ultra-Lightweight Block Cipher*; in Proceedings of CHES 2007; LNCS 7427; pp. 450-466; Springer Verlag 2007; available at @@ -134,6 +136,8 @@ def __init__(self, *args, **kwargs): self._F = GF(2) self._big_endian = kwargs.get("big_endian",True) + self.differential_uniformity = self.maximal_difference_probability_absolute + def _repr_(self): """ EXAMPLE:: @@ -426,6 +430,9 @@ def maximal_difference_probability_absolute(self): highest probability in absolute terms, i.e. how often it occurs in total. + Equivalently, this is equal to the differential uniformity + of this S-Box. + EXAMPLE:: sage: S = mq.SBox(7,6,0,4,2,5,1,3) @@ -454,6 +461,7 @@ def maximal_difference_probability(self): """ return self.maximal_difference_probability_absolute()/(2.0**self.n) + @cached_method def linear_approximation_matrix(self): """ Return linear approximation matrix ``A`` for this S-box. @@ -492,30 +500,23 @@ def linear_approximation_matrix(self): True True """ - try: - return self._linear_approximation_matrix - except AttributeError: - pass - m = self.m n = self.n nrows = 1<> self.m)) >> 1) + ret.append((j, i, c)) + return ret + + def max_degree(self): + """ + Return the maximal algebraic degree of all its component functions. + + EXAMPLES:: + + sage: S = mq.SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2]) + sage: S.max_degree() + 3 + """ + n = self.n + ret = 0 + + for i in xrange(n): + deg_Si = self.component_function(1< ret: + ret = deg_Si + return ret + + def min_degree(self): + """ + Return the minimal algebraic degree of all its component functions. + + EXAMPLES:: + + sage: S = mq.SBox([12,5,6,11,9,0,10,13,3,14,15,8,4,7,1,2]) + sage: S.min_degree() + 2 + """ + n = self.n + ret = self.m + + for b in xrange(1, 1<>1))) + + def fixed_points(self): + """ + Return a list of all fixed points of this S-Box. + + EXAMPLES:: + + sage: S = mq.SBox([0,1,3,6,7,4,5,2]) + sage: S.fixed_points() + [0, 1] + """ + m = self.m + return [i for i in xrange(1<sage_malloc(n_rows * sizeof(bitset_t)) + m.rows = sig_malloc(n_rows * sizeof(bitset_t)) if m.rows == NULL: raise MemoryError @@ -43,7 +43,7 @@ cdef inline binary_matrix_free(binary_matrix_t m): for i in range(m.n_rows): bitset_free(m.rows[i]) - sage_free(m.rows) + sig_free(m.rows) cdef inline binary_matrix_fill(binary_matrix_t m, bint bit): r""" diff --git a/src/sage/data_structures/bitset.pxi b/src/sage/data_structures/bitset.pxi index 1209f9555ac..4012fa7687a 100644 --- a/src/sage/data_structures/bitset.pxi +++ b/src/sage/data_structures/bitset.pxi @@ -25,7 +25,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from libc.string cimport strlen from sage.libs.gmp.mpn cimport * from sage.data_structures.bitset cimport * @@ -95,7 +95,7 @@ cdef inline bint bitset_realloc(bitset_t bits, mp_bitcnt_t size) except -1: raise ValueError("bitset capacity must be greater than 0") bits.limbs = (size - 1) / (8 * sizeof(mp_limb_t)) + 1 - tmp = sage_realloc(bits.bits, bits.limbs * sizeof(mp_limb_t)) + tmp = sig_realloc(bits.bits, bits.limbs * sizeof(mp_limb_t)) if tmp != NULL: bits.bits = tmp else: @@ -114,7 +114,7 @@ cdef inline void bitset_free(bitset_t bits): """ Deallocate the memory in bits. """ - sage_free(bits.bits) + sig_free(bits.bits) cdef inline void bitset_clear(bitset_t bits): """ @@ -731,7 +731,7 @@ cdef char* bitset_chars(char* s, bitset_t bits, char zero=c'0', char one=c'1'): """ cdef long i if s == NULL: - s = sage_malloc(bits.size + 1) + s = sig_malloc(bits.size + 1) for i from 0 <= i < bits.size: s[i] = one if bitset_in(bits, i) else zero s[bits.size] = 0 @@ -755,7 +755,7 @@ cdef bitset_string(bitset_t bits): cdef char* s = bitset_chars(NULL, bits) cdef object py_s py_s = s - sage_free(s) + sig_free(s) return py_s cdef list bitset_list(bitset_t bits): diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index dfb789ab7a5..929d77cd60c 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -248,6 +248,7 @@ def increasing_tree_shape(elt, compare=min): FINDSTAT_STATISTIC_COLLECTION = 'StatisticCollection' FINDSTAT_STATISTIC_DATA = 'StatisticData' FINDSTAT_STATISTIC_GENERATING_FUNCTION = 'StatisticGeneratingFunction' +FINDSTAT_STATISTIC_NAME = 'StatisticTitle' FINDSTAT_STATISTIC_DESCRIPTION = 'StatisticDescription' FINDSTAT_STATISTIC_REFERENCES = 'StatisticReferences' FINDSTAT_STATISTIC_CODE = 'StatisticCode' @@ -526,7 +527,7 @@ def query_by_dict(query, collection=None): # we expect a dictionary from objects or strings to # integers l = query.iteritems() - (key, value) = l.next() + (key, value) = next(l) (collection, to_str) = get_collection(collection, key) @@ -956,6 +957,7 @@ def _find_by_id(self): raise self._description = self._raw[FINDSTAT_STATISTIC_DESCRIPTION].encode("utf-8") + self._name = self._raw[FINDSTAT_STATISTIC_NAME].encode("utf-8") self._references = self._raw[FINDSTAT_STATISTIC_REFERENCES].encode("utf-8") self._collection = FindStatCollection(self._raw[FINDSTAT_STATISTIC_COLLECTION]) self._code = self._raw[FINDSTAT_STATISTIC_CODE] @@ -1518,14 +1520,14 @@ def set_description(self, value): sage: s = findstat([(d, randint(1,1000)) for d in DyckWords(4)]); s # optional -- internet a new statistic on Cc0005: Dyck paths - sage: s.set_description("Random values on Dyck paths.\r\nNot for submssion.") # optional -- internet + sage: s.set_description("Random values on Dyck paths.\r\nNot for submission.") # optional -- internet sage: s # optional -- internet a new statistic on Cc0005: Dyck paths sage: s.name() # optional -- internet 'Random values on Dyck paths.' sage: print s.description() # optional -- internet Random values on Dyck paths. - Not for submssion. + Not for submission. """ self._raise_error_modifying_statistic_with_perfect_match() @@ -1547,7 +1549,11 @@ def name(self): sage: findstat(1).name() # optional -- internet,random u'The number of ways to write a permutation as a minimal length product of simple transpositions.' """ - return self._description.partition(FINDSTAT_SEPARATOR_NAME)[0] + # this needs to be decided how to do properly + if hasattr(self,"_name"): + return self._name + else: + return self._description.partition(FINDSTAT_SEPARATOR_NAME)[0] def references(self): r""" @@ -2159,21 +2165,34 @@ def _repr_(self): """ return "%s: %s" %(self.id_str(), self._name_plural) - def name(self): + def name(self, style="singular"): r""" Return the name of the FindStat collection. + INPUT: + + - a string -- (default:"singular") can be + "singular", or "plural". + OUTPUT: - The name of the FindStat collection, in singular. + The name of the FindStat collection, in singular or in plural. EXAMPLES:: sage: from sage.databases.findstat import FindStatCollection sage: FindStatCollection("Binary trees").name() # optional -- internet u'Binary tree' + + sage: FindStatCollection("Binary trees").name(style="plural") # optional -- internet + u'Binary trees' """ - return self._name + if style == "singular": + return self._name + elif style == "plural": + return self._name_plural + else: + raise ValueError("Argument 'style' (=%s) must be 'singular' or 'plural'."%style) class FindStatCollections(Parent, UniqueRepresentation): r""" @@ -2456,7 +2475,7 @@ def _element_constructor_(self, entry): # check whether entry is iterable (it's not a string!) try: - obj = iter(entry).next() + obj = next(iter(entry)) for (id, c) in self._findstat_collections.iteritems(): if isinstance(obj, c[3]): return self.element_class(self, id, c, entry) diff --git a/src/sage/databases/oeis.py b/src/sage/databases/oeis.py index ff882867bc8..af68c3fc2c8 100644 --- a/src/sage/databases/oeis.py +++ b/src/sage/databases/oeis.py @@ -15,6 +15,8 @@ - Vincent Delecroix (2014): modifies continued fractions because of :trac:`14567` +- Moritz Firsching (2016): modifies handling of dead sequence, see :trac:`17330` + EXAMPLES:: sage: oeis @@ -644,6 +646,12 @@ def __init__(self, entry): sage: sfibo = oeis('A039834') # optional -- internet + Handle dead sequences: see :trac:`17330` :: + + sage: oeis(17) # optional -- internet + .. RuntimeWarning: This sequence is dead "A000017: Erroneous version of A032522." + A000017: Erroneous version of A032522. + sage: s = oeis._imaginary_sequence() """ self._raw = entry @@ -651,6 +659,11 @@ def __init__(self, entry): self._fields = defaultdict(list) for line in entry.splitlines(): self._fields[line[1]].append(line[11:]) + if 'dead' in self.keywords(): + ("This sequence is dead: \""+self.name()+"\"") + from warnings import warn + warn('This sequence is dead "'+self.id()+": "+self.name()+'"', RuntimeWarning) + def id(self, format='A'): r""" @@ -837,11 +850,11 @@ def keywords(self): EXAMPLES:: - 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 = oeis(53) ; f # optional -- internet + A000053: Local stops on New York City Broadway line (IRT #1) subway. sage: f.keywords() # optional -- internet - ('core', 'nonn', 'nice', 'easy', 'hear') + ('nonn', 'fini', 'full') TESTS:: @@ -1083,6 +1096,14 @@ def first_terms(self, number=None, absolute_value=False): sage: f.first_terms()[:10] # optional -- internet (0, 1, 1, 2, 3, 5, 8, 13, 21, 34) + Handle dead sequences: see :trac:`17330` :: + + sage: oeis(17).first_terms(12) # optional -- internet + oeis(17).first_terms(12) + .. RuntimeWarning: This sequence is dead "A000017: Erroneous version of A032522." + warn('This sequence is dead "'+self.id()+": "+self.name()+'"', RuntimeWarning) + (1, 0, 0, 2, 2, 4, 8, 4, 16, 12, 48, 80) + TESTS:: sage: s = oeis._imaginary_sequence() @@ -1107,7 +1128,7 @@ def first_terms(self, number=None, absolute_value=False): sage: s(42) 1 """ - if absolute_value or ('nonn' in self.keywords()): + if absolute_value or ('nonn' in self.keywords()) or ('dead' in self.keywords()): fields = ['S', 'T', 'U'] elif ('sign' in self.keywords()): fields = ['V', 'W', 'X'] diff --git a/src/sage/dev/test/data/trac_8703-trees-fh.patch b/src/sage/dev/test/data/trac_8703-trees-fh.patch index 93e35c91925..735e91dd1cc 100644 --- a/src/sage/dev/test/data/trac_8703-trees-fh.patch +++ b/src/sage/dev/test/data/trac_8703-trees-fh.patch @@ -1516,7 +1516,7 @@ new file mode 100644 + + REFERENCES: + -+ .. [DG] S. Dulucq and O, Guibert. Mots de piles, tableaux ++ .. [DG] \S. Dulucq and O, Guibert. Mots de piles, tableaux + standards et permutations de Baxter, proceedings of + Formal Power Series and Algebraic Combinatorics, 1994. + """ diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 6a49e680b8c..d644ca0b3ac 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -31,6 +31,7 @@ from forker import DocTestDispatcher from reporting import DocTestReporter from util import NestedName, Timer, count_noun, dict_difference +from external import external_software, available_software nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') optionaltag_regex = re.compile(r'^\w+$') @@ -78,7 +79,7 @@ def __init__(self, **kwds): self.sagenb = False self.long = False self.warn_long = None - self.optional = set(["sage"]) + self.optional = set(['sage']) self.randorder = None self.global_iterations = 1 # sage-runtests default is 0 self.file_iterations = 1 # sage-runtests default is 0 @@ -596,7 +597,7 @@ def expand_files_into_sources(self): sage: DC = DocTestController(DD, [dirname]) sage: DC.expand_files_into_sources() sage: len(DC.sources) - 10 + 11 sage: DC.sources[0].options.optional True @@ -697,6 +698,7 @@ def sort_sources(self): sage.doctest.parsing sage.doctest.forker sage.doctest.fixtures + sage.doctest.external sage.doctest.control sage.doctest.all sage.doctest @@ -1032,12 +1034,18 @@ def run(self): pass self.log("Using --optional=" + self._optional_tags_string()) + if self.options.optional is True or 'external' in self.options.optional: + self.log("External software to be detected: " + ','.join(external_software)) self.add_files() self.expand_files_into_sources() self.filter_sources() self.sort_sources() self.run_doctests() + + if self.options.optional is True or 'external' in self.options.optional: + self.log("External software detected for doctesting: " + + ','.join(available_software.seen())) return self.reporter.error_status def run_doctests(module, options=None): diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py new file mode 100644 index 00000000000..e0d7bfdcd08 --- /dev/null +++ b/src/sage/doctest/external.py @@ -0,0 +1,349 @@ +""" +Detecting external software + +This module makes up a list of external software that Sage interfaces. Availability +of each software is tested only when necessary. This is mainly used for the doctests +which require certain external software installed on the system. + +AUTHORS: + +- Kwankyu Lee (2016-03-09) -- initial version, based on code by Robert Bradshaw and Nathann Cohen +""" + +#***************************************************************************** +# Copyright (C) 2016 KWANKYU LEE +# +# 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 multiprocessing import Array + +# Functions in this module whose name is of the form 'has_xxx' tests if the +# software xxx is available to Sage. +prefix = 'has_' + +def has_internet(): + """ + Test if Internet is available. + + Failure of connecting to the site "http://www.sagemath.org" within a second + is regarded as internet being not available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_internet + sage: has_internet() # random + True + """ + from six.moves import urllib + try: + urllib.request.urlopen("http://www.sagemath.org",timeout=1) + return True + except urllib.error.URLError: + return False + +def has_latex(): + """ + Test if Latex is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_latex + sage: has_latex() # random + True + """ + from sage.misc.latex import _run_latex_, _latex_file_ + from sage.misc.temporary_file import tmp_filename + try: + f = tmp_filename(ext='.tex') + O = open(f, 'w') + O.write(_latex_file_('2+3')) + O.close() + _run_latex_(f) + return True + except Exception: + return False + +def has_magma(): + """ + Test if Magma is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_magma + sage: has_magma() # random + True + """ + from sage.interfaces.magma import magma + try: + magma('2+3') + return True + except Exception: + return False + +def has_matlab(): + """ + Test if Matlab is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_matlab + sage: has_matlab() # random + True + """ + from sage.interfaces.matlab import matlab + try: + matlab('2+3') + return True + except Exception: + return False + +def has_mathematica(): + """ + Test if Mathematica is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_mathematica + sage: has_mathematica() # random + True + """ + from sage.interfaces.mathematica import mathematica + try: + mathematica('2+3') + return True + except Exception: + return False + +def has_maple(): + """ + Test if Maple is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_maple + sage: has_maple() # random + True + """ + from sage.interfaces.maple import maple + try: + maple('2+3') + return True + except Exception: + return False + +def has_macaulay2(): + """ + Test if Macaulay2 is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_macaulay2 + sage: has_macaulay2() # random + True + """ + from sage.interfaces.macaulay2 import macaulay2 + try: + macaulay2('2+3') + return True + except Exception: + return False + +def has_octave(): + """ + Test if Octave is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_octave + sage: has_octave() # random + True + """ + from sage.interfaces.octave import octave + try: + octave('2+3') + return True + except Exception: + return False + +def has_scilab(): + """ + Test if Scilab is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_scilab + sage: has_scilab() # random + True + """ + from sage.interfaces.scilab import scilab + try: + scilab('2+3') + return True + except Exception: + return False + +def has_cplex(): + """ + Test if CPLEX is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_cplex + sage: has_cplex() # random + True + """ + from sage.numerical.mip import MixedIntegerLinearProgram + try: + MixedIntegerLinearProgram(solver='cplex') + return True + except Exception: + return False + +def has_gurobi(): + """ + Test if Gurobi is available. + + EXAMPLES:: + + sage: from sage.doctest.external import has_gurobi + sage: has_gurobi() # random + True + """ + from sage.numerical.mip import MixedIntegerLinearProgram + try: + MixedIntegerLinearProgram(solver='gurobi') + return True + except Exception: + return False + +def external_software(): + """ + Return the alphabetical list of external software supported by this module. + + EXAMPLES:: + + sage: from sage.doctest.external import external_software + sage: sorted(external_software) == external_software + True + """ + supported = list() + for func in globals(): + if func.startswith(prefix): + supported.append(func[len(prefix):]) + return sorted(supported) + +external_software = external_software() + +def _lookup(software): + """ + Test if the software is available on the system. + + EXAMPLES:: + + sage: sage.doctest.external._lookup('internet') # random + True + """ + if software in external_software: + func = globals().get(prefix + software) + return func() + else: + return False + +class AvailableSoftware(object): + """ + This class keeps the set of available software whose availability is detected lazily + from the list of external software. + + EXAMPLES:: + + sage: from sage.doctest.external import external_software, available_software + sage: external_software + ['cplex', + 'gurobi', + 'internet', + 'latex', + 'macaulay2', + 'magma', + 'maple', + 'mathematica', + 'matlab', + 'octave', + 'scilab'] + sage: 'internet' in available_software # random + True + sage: available_software.issuperset(set(['internet','latex'])) # random + True + """ + def __init__(self): + """ + Initialization. + + EXAMPLES:: + sage: from sage.doctest.external import AvailableSoftware + sage: S = AvailableSoftware() + sage: S.seen() # random + [] + """ + # For multiprocessing of doctests, the data self._seen should be + # shared among subprocesses. Thus we use Array class from the + # multiprocessing module. + self._seen = Array('i', len(external_software)) # initialized to zeroes + + def __contains__(self, item): + """ + Return ``True`` if ``item`` is available on the system. + + EXAMPLES:: + sage: from sage.doctest.external import available_software + sage: 'internet' in available_software # random + True + """ + try: + idx = external_software.index(item) + except Exception: + return False + if not self._seen[idx]: + if _lookup(item): + self._seen[idx] = 1 # available + else: + self._seen[idx] = -1 # not available + if self._seen[idx] == 1: + return True + elif self._seen[idx] == -1: + return False + else: + raise AssertionError("Invalid value for self.seen") + + def issuperset(self, other): + """ + Return ``True`` if ``other`` is a subset of ``self``. + + EXAMPLES:: + + sage: from sage.doctest.external import available_software + sage: available_software.issuperset(set(['internet','latex','magma'])) # random + True + """ + for item in other: + if item not in self: + return False + return True + + def seen(self): + """ + Return the list of detected external software. + + EXAMPLES:: + + sage: from sage.doctest.external import available_software + sage: available_software.seen() # random + ['internet', 'latex', 'magma'] + """ + return [external_software[i] for i in range(len(external_software)) if self._seen[i] > 0] + +available_software = AvailableSoftware() diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index bd40131bb44..e9ca3238dad 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -30,6 +30,8 @@ from sage.repl.preparse import preparse, strip_string_literals from functools import reduce +from external import available_software + float_regex = re.compile('\s*([+-]?\s*((\d*\.?\d+)|(\d+\.?))([eE][+-]?\d+)?)') optional_regex = re.compile(r'(long time|not implemented|not tested|known bug)|([^ a-z]\s*optional\s*[:-]*((\s|\w)*))') find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) @@ -563,8 +565,12 @@ def parse(self, string, *args): optional_tags.remove('long time') else: continue - if not (self.optional_tags is True or optional_tags.issubset(self.optional_tags)): - continue + if not self.optional_tags is True: + extra = optional_tags - self.optional_tags # set difference + if len(extra) > 0: + if not('external' in self.optional_tags + and available_software.issuperset(extra)): + continue elif self.optional_only: self.optionals['sage'] += 1 continue diff --git a/src/sage/dynamics/flat_surfaces/strata.py b/src/sage/dynamics/flat_surfaces/strata.py index 9e0b3a63688..aefb6d1a70a 100644 --- a/src/sage/dynamics/flat_surfaces/strata.py +++ b/src/sage/dynamics/flat_surfaces/strata.py @@ -33,15 +33,15 @@ REFERENCES: -.. [KonZor03] M. Kontsevich, A. Zorich "Connected components of the moduli space +.. [KonZor03] \M. Kontsevich, A. Zorich "Connected components of the moduli space of Abelian differentials with prescripebd singularities" Invent. math. 153, 631-678 (2003) -.. [Lan08] E. Lanneau "Connected components of the strata of the moduli spaces +.. [Lan08] \E. Lanneau "Connected components of the strata of the moduli spaces of quadratic differentials", Annales sci. de l'ENS, serie 4, fascicule 1, 41, 1-56 (2008) -.. [Zor08] A. Zorich "Explicit Jenkins-Strebel representatives of all strata of +.. [Zor08] \A. Zorich "Explicit Jenkins-Strebel representatives of all strata of Abelian and quadratic differentials", Journal of Modern Dynamics, vol. 2, no 1, 139-185 (2008) (http://www.math.psu.edu/jmd) @@ -1443,10 +1443,10 @@ def representative(self, reduced=True, alphabet=None): INPUT: - - ``reduced`` - boolean (defaut: ``True``): whether you obtain + - ``reduced`` - boolean (default: ``True``): whether you obtain a reduced or labelled permutation - - ``alphabet`` - alphabet or ``None`` (defaut: ``None``): + - ``alphabet`` - alphabet or ``None`` (default: ``None``): whether you want to specify an alphabet for your representative diff --git a/src/sage/dynamics/interval_exchanges/constructors.py b/src/sage/dynamics/interval_exchanges/constructors.py index 59c05927c41..afd7c9747b8 100644 --- a/src/sage/dynamics/interval_exchanges/constructors.py +++ b/src/sage/dynamics/interval_exchanges/constructors.py @@ -81,7 +81,7 @@ sage: r_red.cardinality() 4 -By defaut, Rauzy diagram are generated by induction on the right. You can use +By default, Rauzy diagrams are generated by induction on the right. You can use several options to enlarge (or restrict) the diagram (try help(iet.RauzyDiagram) for more precisions):: @@ -432,7 +432,7 @@ def GeneralizedPermutation(*args,**kargs): - ``intervals`` - strings, list, tuples - - ``reduced`` - boolean (defaut: False) specifies reduction. False means + - ``reduced`` - boolean (default: False) specifies reduction. False means labelled permutation and True means reduced permutation. - ``flips`` - iterable (default: None) the letters which correspond to @@ -893,7 +893,7 @@ def IntervalExchangeTransformation(permutation=None, lengths=None): sage: iet.IntervalExchangeTransformation(('a b c','c b a'),[0.1,'rho',2]) Traceback (most recent call last): ... - TypeError: unable to convert x (='rho') into a real number + TypeError: unable to convert 'rho' to a float sage: iet.IntervalExchangeTransformation(('a b c','c b a'),[0.1,-2,2]) Traceback (most recent call last): ... @@ -926,7 +926,7 @@ def IntervalExchangeTransformation(permutation=None, lengths=None): try: y = float(x) except ValueError: - raise TypeError("unable to convert x (='%s') into a real number" % (str(x))) + raise TypeError("unable to convert {!r} to a float".format(x)) if y <= 0: raise ValueError("lengths must be positive") diff --git a/src/sage/dynamics/interval_exchanges/iet.py b/src/sage/dynamics/interval_exchanges/iet.py index a1e2c5003da..79e8d56c01b 100644 --- a/src/sage/dynamics/interval_exchanges/iet.py +++ b/src/sage/dynamics/interval_exchanges/iet.py @@ -91,7 +91,7 @@ class IntervalExchangeTransformation(SageObject): sage: iet.IntervalExchangeTransformation(('a b','b a'),['e','f']) Traceback (most recent call last): ... - TypeError: unable to convert x (='e') into a real number + TypeError: unable to convert 'e' to a float The value for the lengths must be positive:: @@ -203,7 +203,7 @@ def normalize(self, total=1): sage: s = t.normalize('bla') Traceback (most recent call last): ... - TypeError: unable to convert total (='bla') into a real number + TypeError: unable to convert 'bla' to a float sage: s = t.normalize(-691) Traceback (most recent call last): ... @@ -212,8 +212,7 @@ def normalize(self, total=1): try: float(total) except ValueError: - raise TypeError("unable to convert total (='%s') into a real number" - % (str(total))) + raise TypeError("unable to convert {!r} to a float".format(total)) if total <= 0: raise ValueError("the total length must be positive") @@ -823,9 +822,9 @@ def plot_two_intervals(self, - ``position`` - a 2-uple of the position - - ``horizontal_alignment`` - left (defaut), center or right + - ``horizontal_alignment`` - left (default), center or right - - ``labels`` - boolean (defaut: True) + - ``labels`` - boolean (default: True) - ``fontsize`` - the size of the label diff --git a/src/sage/dynamics/interval_exchanges/template.py b/src/sage/dynamics/interval_exchanges/template.py index 9314d99b03e..e0440dd3c98 100644 --- a/src/sage/dynamics/interval_exchanges/template.py +++ b/src/sage/dynamics/interval_exchanges/template.py @@ -672,7 +672,7 @@ def has_rauzy_move(self, winner='top', side=None): - ``winner`` - 'top' or 'bottom' corresponding to the interval - - ``side`` - 'left' or 'right' (defaut) + - ``side`` - 'left' or 'right' (default) OUTPUT: @@ -732,7 +732,7 @@ def rauzy_move(self, winner, side='right', iteration=1): - ``winner`` - 'top' or 'bottom' interval - - ``side`` - 'right' or 'left' (defaut: 'right') corresponding + - ``side`` - 'right' or 'left' (default: 'right') corresponding to the side on which the Rauzy move must be performed. - ``iteration`` - a non negative integer @@ -2564,7 +2564,7 @@ def right_composition(self, function, composition = None) : - ``function`` - function must be of the form (indice,type) -> element. Moreover function(None,None) must be an identity element for initialization. - - ``composition`` - the composition function for the function. * if None (defaut None) + - ``composition`` - the composition function for the function. * if None (default None) TEST:: diff --git a/src/sage/env.py b/src/sage/env.py index 79443bb7465..f4497d1e76a 100644 --- a/src/sage/env.py +++ b/src/sage/env.py @@ -165,7 +165,7 @@ def sage_include_directories(use_sources=False): sage: sage.env.sage_include_directories() ['.../include', '.../include/python...', - '.../python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '.../python.../site-packages', '.../python.../site-packages/sage/ext'] """ diff --git a/src/sage/ext/fast_eval.pyx b/src/sage/ext/fast_eval.pyx index 4780db35a83..8aa5d9de082 100644 --- a/src/sage/ext/fast_eval.pyx +++ b/src/sage/ext/fast_eval.pyx @@ -90,7 +90,7 @@ AUTHORS: from sage.ext.fast_callable import fast_callable, Wrapper -include "stdsage.pxi" +include "cysignals/memory.pxi" cimport cython from cpython.ref cimport Py_INCREF @@ -290,7 +290,7 @@ def _unpickle_FastDoubleFunc(nargs, max_height, op_list): self.nops = len(op_list) self.nargs = nargs self.max_height = max_height - self.ops = sage_malloc(sizeof(fast_double_op) * self.nops) + self.ops = sig_malloc(sizeof(fast_double_op) * self.nops) self.allocate_stack() cfunc_addresses = reverse_map(cfunc_names) op_enums = reverse_map(op_names) @@ -492,7 +492,7 @@ cdef class FastDoubleFunc: self.nargs = param+1 self.nops = 1 self.max_height = 1 - self.ops = sage_malloc(sizeof(fast_double_op)) + self.ops = sig_malloc(sizeof(fast_double_op)) self.ops[0].type = LOAD_ARG self.ops[0].params.n = param @@ -500,7 +500,7 @@ cdef class FastDoubleFunc: self.nargs = 0 self.nops = 1 self.max_height = 1 - self.ops = sage_malloc(sizeof(fast_double_op)) + self.ops = sig_malloc(sizeof(fast_double_op)) self.ops[0].type = PUSH_CONST self.ops[0].params.c = param @@ -520,7 +520,7 @@ cdef class FastDoubleFunc: self.py_funcs += arg.py_funcs self.nargs = max(self.nargs, arg.nargs) self.max_height = max(self.max_height, arg.max_height+i) - self.ops = sage_malloc(sizeof(fast_double_op) * self.nops) + self.ops = sig_malloc(sizeof(fast_double_op) * self.nops) if self.ops == NULL: raise MemoryError i = 0 @@ -536,20 +536,20 @@ cdef class FastDoubleFunc: self.allocate_stack() cdef int allocate_stack(FastDoubleFunc self) except -1: - self.argv = sage_malloc(sizeof(double) * self.nargs) + self.argv = sig_malloc(sizeof(double) * self.nargs) if self.argv == NULL: raise MemoryError - self.stack = sage_malloc(sizeof(double) * self.max_height) + self.stack = sig_malloc(sizeof(double) * self.max_height) if self.stack == NULL: raise MemoryError def __dealloc__(self): if self.ops: - sage_free(self.ops) + sig_free(self.ops) if self.stack: - sage_free(self.stack) + sig_free(self.stack) if self.argv: - sage_free(self.argv) + sig_free(self.argv) def __reduce__(self): """ @@ -1227,7 +1227,7 @@ cdef class FastDoubleFunc: feval.max_height = self.max_height if type == DUP: feval.max_height += 1 - feval.ops = sage_malloc(sizeof(fast_double_op) * feval.nops) + feval.ops = sig_malloc(sizeof(fast_double_op) * feval.nops) memcpy(feval.ops, self.ops, sizeof(fast_double_op) * self.nops) feval.ops[feval.nops - 1].type = type feval.py_funcs = self.py_funcs @@ -1287,7 +1287,7 @@ cdef FastDoubleFunc binop(_left, _right, char type): feval.nargs = max(left.nargs, right.nargs) feval.nops = left.nops + right.nops + 1 feval.max_height = max(left.max_height, right.max_height+1) - feval.ops = sage_malloc(sizeof(fast_double_op) * feval.nops) + feval.ops = sig_malloc(sizeof(fast_double_op) * feval.nops) memcpy(feval.ops, left.ops, sizeof(fast_double_op) * left.nops) memcpy(feval.ops + left.nops, right.ops, sizeof(fast_double_op) * right.nops) feval.ops[feval.nops - 1].type = type diff --git a/src/sage/ext/interactive_constructors_c.pyx b/src/sage/ext/interactive_constructors_c.pyx index c14d66529af..9bc5b4cf745 100644 --- a/src/sage/ext/interactive_constructors_c.pyx +++ b/src/sage/ext/interactive_constructors_c.pyx @@ -3,6 +3,9 @@ Constructors that automatically inject variables into the global module scope """ import sage.rings.all +import sage.misc.superseded +sage.misc.superseded.deprecation(20442, + '''The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead.''') _verbose=True _inject_mode_off = False @@ -26,6 +29,8 @@ def inject_on(verbose=True): EXAMPLES:: sage: inject_on(verbose=True) + doctest:...: DeprecationWarning: The inject_on() functionality is deprecated, use the syntax "R. = PolynomialRing(QQ)" instead. + See http://trac.sagemath.org/20442 for details. Redefining: FiniteField Frac FractionField FreeMonoid GF LaurentSeriesRing NumberField PolynomialRing quo quotient sage: GF(9,'b') Defining b diff --git a/src/sage/ext/memory.pxd b/src/sage/ext/memory.pxd deleted file mode 100644 index bb772e06119..00000000000 --- a/src/sage/ext/memory.pxd +++ /dev/null @@ -1,133 +0,0 @@ -""" -Low-level memory allocation functions -""" - -#***************************************************************************** -# Copyright (C) 2011-2015 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/ -#***************************************************************************** - - -cimport cython -from libc.stdlib cimport malloc, calloc, realloc, free -from cysignals.signals cimport sig_block, sig_unblock - -cdef extern from *: - int unlikely(int) nogil # Defined by Cython - - -cdef inline void* sage_malloc "sage_malloc"(size_t n) nogil: - sig_block() - cdef void* ret = malloc(n) - sig_unblock() - return ret - - -cdef inline void* sage_realloc "sage_realloc"(void* ptr, size_t size) nogil: - sig_block() - cdef void* ret = realloc(ptr, size) - sig_unblock() - return ret - - -cdef inline void* sage_calloc "sage_calloc"(size_t nmemb, size_t size) nogil: - sig_block() - cdef void* ret = calloc(nmemb, size) - sig_unblock() - return ret - - -cdef inline void sage_free "sage_free"(void* ptr) nogil: - sig_block() - free(ptr) - sig_unblock() - - -@cython.cdivision(True) -cdef inline size_t mul_overflowcheck(size_t a, size_t b) nogil: - """ - Return a*b, checking for overflow. Assume that a > 0. - If overflow occurs, return (-1). - We assume that malloc(-1) always fails. - """ - # If a and b both less than MUL_NO_OVERFLOW, no overflow can occur - cdef size_t MUL_NO_OVERFLOW = ((1) << (4*sizeof(size_t))) - if a >= MUL_NO_OVERFLOW or b >= MUL_NO_OVERFLOW: - if unlikely(b > (-1) // a): - return (-1) - return a*b - - -cdef inline void* check_allocarray(size_t nmemb, size_t size) except? NULL: - """ - Allocate memory for ``nmemb`` elements of size ``size``. - """ - if nmemb == 0: - return NULL - cdef size_t n = mul_overflowcheck(nmemb, size) - cdef void* ret = sage_malloc(n) - if unlikely(ret == NULL): - raise MemoryError("failed to allocate %s * %s bytes" % (nmemb, size)) - return ret - - -cdef inline void* check_reallocarray(void* ptr, size_t nmemb, size_t size) except? NULL: - """ - Re-allocate memory at ``ptr`` to hold ``nmemb`` elements of size - ``size``. If ``ptr`` equals ``NULL``, this behaves as - ``check_allocarray``. - - When ``nmemb`` equals 0, then free the memory at ``ptr``. - """ - if nmemb == 0: - sage_free(ptr) - return NULL - cdef size_t n = mul_overflowcheck(nmemb, size) - cdef void* ret = sage_realloc(ptr, n) - if unlikely(ret == NULL): - raise MemoryError("failed to allocate %s * %s bytes" % (nmemb, size)) - return ret - - -cdef inline void* check_malloc(size_t n) except? NULL: - """ - Allocate ``n`` bytes of memory. - """ - if n == 0: - return NULL - cdef void* ret = sage_malloc(n) - if unlikely(ret == NULL): - raise MemoryError("failed to allocate %s bytes" % n) - return ret - - -cdef inline void* check_realloc(void* ptr, size_t n) except? NULL: - """ - Re-allocate memory at ``ptr`` to hold ``n`` bytes. - If ``ptr`` equals ``NULL``, this behaves as ``check_malloc``. - """ - if n == 0: - sage_free(ptr) - return NULL - cdef void* ret = sage_realloc(ptr, n) - if unlikely(ret == NULL): - raise MemoryError("failed to allocate %s bytes" % n) - return ret - - -cdef inline void* check_calloc(size_t nmemb, size_t size) except? NULL: - """ - Allocate memory for ``nmemb`` elements of size ``size``. The - resulting memory is zeroed. - """ - if nmemb == 0: - return NULL - cdef void* ret = sage_calloc(nmemb, size) - if unlikely(ret == NULL): - raise MemoryError("failed to allocate %s * %s bytes" % (nmemb, size)) - return ret diff --git a/src/sage/ext/memory.pyx b/src/sage/ext/memory.pyx index ebf025708ec..fe8278f9d51 100644 --- a/src/sage/ext/memory.pyx +++ b/src/sage/ext/memory.pyx @@ -32,11 +32,12 @@ AUTHORS: #***************************************************************************** from sage.libs.gmp.misc cimport mp_set_memory_functions -include "cysignals/signals.pxi" +include "cysignals/memory.pxi" cdef extern from "Python.h": # Declare as returning void without except value void PyErr_Format(object exception, char *format, ...) + int unlikely(int) nogil # Defined by Cython cdef void alloc_error(size_t size) nogil: @@ -54,7 +55,7 @@ cdef void* sage_sig_malloc(size_t size) nogil: Out-of-memory errors are handled using the ``sig_error`` mechanism. """ - cdef void* p = sage_malloc(size) + cdef void* p = sig_malloc(size) if unlikely(p == NULL): alloc_error(size) return p @@ -66,7 +67,7 @@ cdef void* sage_sig_realloc(void *ptr, size_t old_size, size_t new_size) nogil: Out-of-memory errors are handled using the ``sig_error`` mechanism. """ - cdef void* p = sage_realloc(ptr, new_size) + cdef void* p = sig_realloc(ptr, new_size) if unlikely(p == NULL): alloc_error(new_size) return p @@ -76,7 +77,7 @@ cdef void sage_sig_free(void *ptr, size_t size) nogil: """ ``free()`` function for the MPIR/GMP library. """ - sage_free(ptr) + sig_free(ptr) def init_memory_functions(): diff --git a/src/sage/ext/memory_allocator.pyx b/src/sage/ext/memory_allocator.pyx index f0a2a222ce4..ba1a5a7e243 100644 --- a/src/sage/ext/memory_allocator.pyx +++ b/src/sage/ext/memory_allocator.pyx @@ -1,5 +1,9 @@ -from sage.ext.memory cimport * include "cysignals/signals.pxi" +include "cysignals/memory.pxi" + +cdef extern from *: + int unlikely(int) nogil # Defined by Cython + cdef class MemoryAllocator: r""" @@ -104,6 +108,6 @@ cdef class MemoryAllocator: """ cdef size_t i for i in range(self.n): - sage_free(self.pointers[i]) + sig_free(self.pointers[i]) if self.pointers != self.static_pointers: - sage_free(self.pointers) + sig_free(self.pointers) diff --git a/src/sage/ext/mod_int.h b/src/sage/ext/mod_int.h index 2d372077c45..bb18adf3c11 100644 --- a/src/sage/ext/mod_int.h +++ b/src/sage/ext/mod_int.h @@ -14,13 +14,15 @@ * Hence, we use signed 64-bit ints. This gives us fast conversion * to/from Python on 64-bit Linux and OSX, and a large number of * available (but not quite as fast) primes on 64-bit Windows and all - * 32-bit platforms (see Trac ##10281) + * 32-bit platforms (see Trac #10281) */ -#define mod_int int_fast64_t + +typedef int64_t mod_int; /* The largest value we can do arithmetic on without risking overflowing. * That is, you can multiply two MOD_INT_MAX in a mod_int. */ -#define MOD_INT_OVERFLOW ((((mod_int)1) << (sizeof(mod_int)*8 - 1)) - 1) -#define MOD_INT_MAX ((mod_int)sqrt(MOD_INT_OVERFLOW) - 1) +#define MOD_INT_OVERFLOW_UNSIGNED ((((uint64_t)1) << (sizeof(mod_int)*8 - 1)) - 1) +#define MOD_INT_OVERFLOW ((mod_int)MOD_INT_OVERFLOW_UNSIGNED) +#define MOD_INT_MAX ((mod_int)sqrt(MOD_INT_OVERFLOW_UNSIGNED) - 1) diff --git a/src/sage/ext/mod_int.pxd b/src/sage/ext/mod_int.pxd index ebddd81c768..763fa69f068 100644 --- a/src/sage/ext/mod_int.pxd +++ b/src/sage/ext/mod_int.pxd @@ -9,8 +9,10 @@ The `mod_int` Data Type # Copyright (C) 2013 Volker Braun # Copyright (C) 2013 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/ #***************************************************************************** diff --git a/src/sage/ext/stdsage.pxi b/src/sage/ext/stdsage.pxi index 7ca73b38250..d6a55253a3d 100644 --- a/src/sage/ext/stdsage.pxi +++ b/src/sage/ext/stdsage.pxi @@ -10,11 +10,15 @@ Standard C helper code for Cython modules # http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" +include "cysignals/memory.pxi" -from sage.ext.stdsage cimport PY_NEW, HAS_DICTIONARY -from sage.ext.memory cimport ( - sage_free, sage_realloc, sage_malloc, sage_calloc, +from cysignals.memory cimport sig_malloc as sage_malloc +from cysignals.memory cimport sig_realloc as sage_realloc +from cysignals.memory cimport sig_calloc as sage_calloc +from cysignals.memory cimport sig_free as sage_free +from cysignals.memory cimport ( check_allocarray, check_reallocarray, check_malloc, check_realloc, check_calloc) + +from sage.ext.stdsage cimport PY_NEW, HAS_DICTIONARY from sage.ext.memory import init_memory_functions diff --git a/src/sage/finance/stock.py b/src/sage/finance/stock.py index c5dd4adee0d..062a202c9e9 100644 --- a/src/sage/finance/stock.py +++ b/src/sage/finance/stock.py @@ -243,7 +243,7 @@ def current_price_data(self): yahoo = deprecated_function_alias(18355,current_price_data) - def history(self,startdate='Jan+1,+1900',enddate=date.today().strftime("%b+%d,+%Y"), histperiod='daily'): + def history(self, startdate='Jan+1,+1900', enddate=None, histperiod='daily'): """ Return an immutable sequence of historical price data for this stock, obtained from Google. OHLC data is stored @@ -322,7 +322,6 @@ def history(self,startdate='Jan+1,+1900',enddate=date.today().strftime("%b+%d,+% 9-Jan-07 29.90 30.05 29.60 29.90 103338 ] - Here, we create a stock by cid, and get historical data. Note that when using historical, if a cid is specified, it will take precedence over the stock's symbol. So, if @@ -337,8 +336,10 @@ def history(self,startdate='Jan+1,+1900',enddate=date.today().strftime("%b+%d,+% 11-Jun-99 0.00 1.73 1.65 1.66 46261600, 14-Jun-99 0.00 1.67 1.61 1.62 39270000 ] - """ + if enddate is None: + enddate = date.today().strftime("%b+%d,+%Y") + cid = self.cid symbol = self.symbol @@ -571,7 +572,7 @@ def _load_from_csv(self, R): hist_data = Sequence(hist_data,cr=True,universe=lambda x:x, immutable=True) return hist_data - def _get_data(self, exchange='', startdate='Jan+1,+1900', enddate=date.today().strftime("%b+%d,+%Y"), histperiod='daily'): + def _get_data(self, exchange, startdate, enddate, histperiod='daily'): """ This function is used internally. diff --git a/src/sage/finance/time_series.pyx b/src/sage/finance/time_series.pyx index 919f27a4b93..3536a06aaec 100644 --- a/src/sage/finance/time_series.pyx +++ b/src/sage/finance/time_series.pyx @@ -46,7 +46,7 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from cpython.string cimport * from libc.math cimport exp, floor, log, pow, sqrt from libc.string cimport memcpy @@ -145,7 +145,7 @@ cdef class TimeSeries: np = cnumpy.PyArray_GETCONTIGUOUS(np) np_data = cnumpy.PyArray_DATA(np) self._length = np.shape[0] - self._values = sage_malloc(sizeof(double) * self._length) + self._values = sig_malloc(sizeof(double) * self._length) if self._values == NULL: raise MemoryError @@ -155,7 +155,7 @@ cdef class TimeSeries: values = [float(x) for x in values] self._length = len(values) - self._values = sage_malloc(sizeof(double) * self._length) + self._values = sig_malloc(sizeof(double) * self._length) if self._values == NULL: raise MemoryError if not initialize: return @@ -235,7 +235,7 @@ cdef class TimeSeries: sage: del v """ if self._values: - sage_free(self._values) + sig_free(self._values) def vector(self): """ @@ -703,12 +703,12 @@ cdef class TimeSeries: if len(right) == 0: return cdef TimeSeries T = right - cdef double* z = sage_malloc(sizeof(double)*(self._length + T._length)) + cdef double* z = sig_malloc(sizeof(double)*(self._length + T._length)) if z == NULL: raise MemoryError memcpy(z, self._values, sizeof(double)*self._length) memcpy(z + self._length, T._values, sizeof(double)*T._length) - sage_free(self._values) + sig_free(self._values) self._values = z self._length = self._length + T._length @@ -1944,7 +1944,7 @@ cdef class TimeSeries: return counts, v cdef Py_ssize_t i - cdef Py_ssize_t* cnts = sage_malloc(sizeof(Py_ssize_t)*bins) + cdef Py_ssize_t* cnts = sig_malloc(sizeof(Py_ssize_t)*bins) if cnts == NULL: raise MemoryError for i from 0 <= i < bins: @@ -1961,7 +1961,7 @@ cdef class TimeSeries: counts = [cnts[i]*b for i in range(bins)] else: counts = [cnts[i] for i in range(bins)] - sage_free(cnts) + sig_free(cnts) return counts, v @@ -2558,7 +2558,7 @@ cdef new_time_series(Py_ssize_t length): raise ValueError, "length must be nonnegative" cdef TimeSeries t = TimeSeries.__new__(TimeSeries) t._length = length - t._values = sage_malloc(sizeof(double)*length) + t._values = sig_malloc(sizeof(double)*length) return t def unpickle_time_series_v1(v, Py_ssize_t n): diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index fb0bdc97d60..34362b5bcc5 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -1,4 +1,7 @@ -from piecewise import piecewise, Piecewise +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.functions.piecewise_old', 'Piecewise') # deprecated +lazy_import('sage.functions.piecewise', 'piecewise') from trig import ( sin, cos, sec, csc, cot, tan, asin, acos, atan, @@ -21,13 +24,14 @@ arg, real_part, real, imag_part, imag, imaginary, conjugate) -from log import (exp, log, ln, polylog, dilog, lambert_w) +from log import (exp, exp_polar, log, ln, polylog, dilog, lambert_w) from transcendental import (zeta, zetaderiv, zeta_symmetric, hurwitz_zeta, dickman_rho, stieltjes) -from sage.functions.bessel import (bessel_I, bessel_J, bessel_K, bessel_Y, Bessel) +from sage.functions.bessel import (bessel_I, bessel_J, bessel_K, bessel_Y, + Bessel, struve_H, struve_L) from special import (hypergeometric_U, spherical_bessel_J, spherical_bessel_Y, diff --git a/src/sage/functions/bessel.py b/src/sage/functions/bessel.py index 991eb775bb1..de1ae02d85a 100644 --- a/src/sage/functions/bessel.py +++ b/src/sage/functions/bessel.py @@ -7,18 +7,20 @@ The main objects which are exported from this module are: - * ``bessel_J`` -- The Bessel J function - * ``bessel_Y`` -- The Bessel Y function - * ``bessel_I`` -- The Bessel I function - * ``bessel_K`` -- The Bessel K function - * ``Bessel`` -- A factory function for producing Bessel functions of + * :meth:`bessel_J(n, x) ` -- The Bessel J function + * :meth:`bessel_Y(n, x) ` -- The Bessel Y function + * :meth:`bessel_I(n, x) ` -- The Bessel I function + * :meth:`bessel_K(n, x) ` -- The Bessel K function + * :meth:`Bessel(...) ` -- A factory function for producing Bessel functions of various kinds and orders + * :meth:`struve_H(nu, z) ` -- The Struve function + * :meth:`struve_L(nu, z) ` -- The modified Struve function - Bessel functions, first defined by the Swiss mathematician Daniel Bernoulli and named after Friedrich Bessel, are canonical solutions y(x) of Bessel's differential equation: - .. math:: + .. MATH:: x^2 \frac{d^2 y}{dx^2} + x \frac{dy}{dx} + \left(x^2 - \nu^2\right)y = 0, @@ -30,7 +32,7 @@ Function of the First Kind. This function also arises as a special case of the hypergeometric function `{}_0F_1`: - .. math:: + .. MATH:: J_\nu(x) = \frac{x^n}{2^\nu \Gamma(\nu + 1)} {}_0F_1(\nu + 1, -\frac{x^2}{4}). @@ -39,7 +41,7 @@ singular at `x=0`) is denoted by `Y_\nu` and is called the Bessel Function of the Second Kind: - .. math:: + .. MATH:: Y_\nu(x) = \frac{ J_\nu(x) \cos(\pi \nu) - J_{-\nu}(x)}{\sin(\pi \nu)}. @@ -48,14 +50,14 @@ Functions. The Bessel I Function, or the Modified Bessel Function of the First Kind, is defined by: - .. math:: + .. MATH:: I_\nu(x) = i^{-\nu} J_\nu(ix). The Bessel K Function, or the Modified Bessel Function of the Second Kind, is defined by: - .. math:: + .. MATH:: K_\nu(x) = \frac{\pi}{2} \cdot \frac{I_{-\nu}(x) - I_n(x)}{\sin(\pi \nu)}. @@ -66,7 +68,7 @@ - It follows from Bessel's differential equation that the derivative of `J_n(x)` with respect to `x` is: - .. math:: + .. MATH:: \frac{d}{dx} J_n(x) = \frac{1}{x^n} \left(x^n J_{n-1}(x) - n x^{n-1} J_n(z) \right) @@ -76,11 +78,11 @@ `H_\nu^{(1)}(x)` and `H_\nu^{(2)}(x)`, defined by: - .. math:: + .. MATH:: H_\nu^{(1)}(x) = J_\nu(x) + i Y_\nu(x) - .. math:: + .. MATH:: H_\nu^{(2)}(x) = J_\nu(x) - i Y_\nu(x) @@ -151,18 +153,20 @@ - Some of the documentation here has been adapted from David Joyner's original documentation of Sage's special functions module (2006). -REFERENCES: - - - Abramowitz and Stegun: Handbook of Mathematical Functions, - http://www.math.sfu.ca/~cbm/aands/ - - - http://en.wikipedia.org/wiki/Bessel_function - - - mpmath Library `Bessel Functions`_ -.. _`mpmath Library`: http://code.google.com/p/mpmath/ -.. _`Bessel Functions`: http://mpmath.googlecode.com/svn/trunk/doc/build/functions/bessel.html +REFERENCES: +.. [AS-Bessel] \F. W. J. Olver: 9. Bessel Functions of Integer Order, in Abramowitz and Stegun: Handbook of Mathematical Functions + http://people.math.sfu.ca/~cbm/aands/page_355.htm +.. [AS-Struve] \M. Abramowitz: 12. Struve Functions and Related Functions, in Abramowitz and Stegun: Handbook of Mathematical Functions + http://people.math.sfu.ca/~cbm/aands/page_495.htm +.. [DLMF-Bessel] \F. W. J. Olver and L. C. Maximon: 10. Bessel Functions, in NIST Digital Library of Mathematical Functions + http://dlmf.nist.gov/10 +.. [DLMF-Struve] \R. B. Paris: 11. Struve and Related Functions, in NIST Digital Library of Mathematical Functions + http://dlmf.nist.gov/11 +.. _`mpmath Library`: https://github.com/fredrik-johansson/mpmath +.. [WP-Bessel] :wikipedia:`Bessel_function` +.. [WP-Struve] :wikipedia:`Struve_function` """ #***************************************************************************** @@ -205,7 +209,7 @@ class Function_Bessel_J(BuiltinFunction): The Bessel J Function, denoted by bessel_J(`\nu`, x) or `J_\nu(x)`. As a Taylor series about `x=0` it is equal to: - .. math:: + .. MATH:: J_\nu(x) = \sum_{k=0}^\infty \frac{(-1)^k}{k! \Gamma(k+\nu+1)} \left(\frac{x}{2}\right)^{2k+\nu} @@ -218,14 +222,14 @@ class Function_Bessel_J(BuiltinFunction): For integer orders `\nu = n` there is an integral representation: - .. math:: + .. MATH:: J_n(x) = \frac{1}{\pi} \int_0^\pi \cos(n t - x \sin(t)) \; dt This function also arises as a special case of the hypergeometric function `{}_0F_1`: - .. math:: + .. MATH:: J_\nu(x) = \frac{x^n}{2^\nu \Gamma(\nu + 1)} {}_0F_1\left(\nu + 1, -\frac{x^2}{4}\right). @@ -282,11 +286,19 @@ class Function_Bessel_J(BuiltinFunction): Numerical evaluation is handled by the mpmath library. Symbolics are handled by a combination of Maxima and Sage (Ginac/Pynac). - + Check whether the return value is real whenever the argument is real (:trac:`10251`):: sage: bessel_J(5, 1.5) in RR True + + REFERENCES: + + - [AS-Bessel]_ + + - [DLMF-Bessel]_ + + - [AS-Bessel]_ """ def __init__(self): """ @@ -359,7 +371,7 @@ def _derivative_(self, n, x, diff_param): def _print_latex_(self, n, z): """ - Custom _print_latex_ method. + Custom ``_print_latex_`` method. EXAMPLES:: @@ -382,14 +394,14 @@ class Function_Bessel_Y(BuiltinFunction): DEFINITION: - .. math:: + .. MATH:: Y_n(z) = \frac{J_\nu(z) \cos(\nu z) - J_{-\nu}(z)}{\sin(\nu z)} Its derivative with respect to `z` is: - .. math:: + .. MATH:: \frac{d}{dz} Y_n(z) = \frac{1}{z^n} \left(z^n Y_{n-1}(z) - n z^{n-1} Y_n(z) \right) @@ -459,6 +471,14 @@ class Function_Bessel_Y(BuiltinFunction): -0.78121282130028871654715000004796482054990639071644460784383 sage: parent(r) Real Field with 200 bits of precision + + REFERENCES: + + - [AS-Bessel]_ + + - [DLMF-Bessel]_ + + - [WP-Bessel]_ """ def __init__(self): """ @@ -530,7 +550,7 @@ def _derivative_(self, n, x, diff_param): def _print_latex_(self, n, z): """ - Custom _print_latex_ method. + Custom ``_print_latex_`` method. EXAMPLES:: @@ -548,7 +568,7 @@ class Function_Bessel_I(BuiltinFunction): DEFINITION: - .. math:: + .. MATH:: I_\nu(x) = i^{-\nu} J_\nu(ix) @@ -624,6 +644,14 @@ class Function_Bessel_I(BuiltinFunction): sage: bessel_I(5, 1.5) in RR True + + REFERENCES: + + - [AS-Bessel]_ + + - [DLMF-Bessel]_ + + - [WP-Bessel]_ """ def __init__(self): """ @@ -692,7 +720,7 @@ def _derivative_(self, n, x, diff_param): def _print_latex_(self, n, z): """ - Custom _print_latex_ method. + Custom ``_print_latex_`` method. EXAMPLES:: @@ -710,7 +738,7 @@ class Function_Bessel_K(BuiltinFunction): DEFINITION: - .. math:: + .. MATH:: K_\nu(x) = \frac{\pi}{2} \frac{I_{-\nu}(x)-I_\nu(x)}{\sin(\nu \pi)} @@ -797,6 +825,14 @@ class Function_Bessel_K(BuiltinFunction): sage: bessel_K(5, 1.5) in RR True + + REFERENCES: + + - [AS-Bessel]_ + + - [DLMF-Bessel]_ + + - [WP-Bessel]_ """ def __init__(self): """ @@ -861,7 +897,7 @@ def _derivative_(self, n, x, diff_param): def _print_latex_(self, n, z): """ - Custom _print_latex_ method. + Custom ``_print_latex_`` method. EXAMPLES:: @@ -1043,3 +1079,233 @@ def Bessel(*args, **kwds): else: return _f + +class Function_Struve_H(BuiltinFunction): + r""" + The Struve functions, solutions to the non-homogeneous Bessel differential equation: + + .. MATH:: + + x^2\frac{d^2y}{dx^2}+x\frac{dy}{dx}+(x^2-\alpha^2)y=\frac{4\bigl(\frac{x}{2}\bigr)^{\alpha+1}}{\sqrt\pi\Gamma(\alpha+\tfrac12)}, + + .. MATH:: + + \mathrm{H}_\alpha(x) = y(x) + + EXAMPLES:: + + sage: struve_H(-1/2,x) + sqrt(2)*sqrt(1/(pi*x))*sin(x) + sage: struve_H(2,x) + struve_H(2, x) + sage: struve_H(1/2,pi).n() + 0.900316316157106 + + REFERENCES: + + - [AS-Struve]_ + + - [DLMF-Struve]_ + + - [WP-Struve]_ + """ + def __init__(self): + r""" + EXAMPLES:: + + sage: n = var('n') + sage: maxima("struve_h(n,x);").sage() + struve_H(n, x) + sage: struve_H(7/5,1)._maxima_() + struve_h(7/5,1) + sage: loads(dumps(struve_H(n,x))) + struve_H(n, x) + """ + BuiltinFunction.__init__(self, 'struve_H', nargs=2, + conversions=dict(maple='StruveH', + mathematica='StruveH', + maxima='struve_h', + sympy='struveh')) + + def _eval_(self, a, z): + """ + EXAMPLES:: + + sage: struve_H(0,0) + 0 + sage: struve_H(pi,0) + 0 + sage: struve_H(-1/2,x) + sqrt(2)*sqrt(1/(pi*x))*sin(x) + sage: struve_H(1/2,-1) + -sqrt(2)*sqrt(-1/pi)*(cos(1) - 1) + sage: struve_H(1/2,pi) + 2*sqrt(2)/pi + sage: struve_H(2,x) + struve_H(2, x) + sage: struve_H(-3/2,x) + -bessel_J(3/2, x) + """ + from sage.symbolic.ring import SR + if z.is_zero() \ + and (SR(a).is_numeric() or SR(a).is_constant()) \ + and a.real() >= -1: + return ZZ(0) + if a == -Integer(1)/2: + from sage.functions.trig import sin + return sqrt(2/(pi*z)) * sin(z) + if a == Integer(1)/2: + from sage.functions.trig import cos + return sqrt(2/(pi*z)) * (1-cos(z)) + if a < 0 and not SR(a).is_integer() and SR(2*a).is_integer(): + from sage.rings.rational_field import QQ + n = (a*(-2) - 1)/2 + return Integer(-1)**n * bessel_J(n+QQ(1)/2, z) + + def _evalf_(self, a, z, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: struve_H(1/2,pi).n() + 0.900316316157106 + sage: struve_H(1/2,pi).n(200) + 0.9003163161571060695551991910... + """ + import mpmath + return mpmath_utils.call(mpmath.struveh, a, z, parent=parent) + + def _derivative_(self, a, z, diff_param=None): + """ + EXAMPLES:: + + sage: diff(struve_H(3/2,x),x) + -1/2*sqrt(2)*sqrt(1/(pi*x))*(cos(x) - 1) + 1/16*sqrt(2)*x^(3/2)/sqrt(pi) - 1/2*struve_H(5/2, x) + """ + if diff_param == 0: + raise ValueError("cannot differentiate struve_H in the first parameter") + + from sage.functions.other import sqrt, gamma + return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_H(a+1,z)+struve_H(a-1,z))/2 + + def _print_latex_(self, a, z): + """ + EXAMPLES:: + + sage: latex(struve_H(2,x)) + H_{{2}}({x}) + """ + return r"H_{{%s}}({%s})" % (a, z) + +struve_H = Function_Struve_H() + +class Function_Struve_L(BuiltinFunction): + r""" + The modified Struve functions. + + .. MATH:: + + \mathrm{L}_\alpha(x) = -i\cdot e^{-\frac{i\alpha\pi}{2}}\cdot\mathrm{H}_\alpha(ix) + + EXAMPLES:: + + sage: struve_L(2,x) + struve_L(2, x) + sage: struve_L(1/2,pi).n() + 4.76805417696286 + sage: diff(struve_L(1,x),x) + 1/3*x/pi - 1/2*struve_L(2, x) + 1/2*struve_L(0, x) + + REFERENCES: + + - [AS-Struve]_ + + - [DLMF-Struve]_ + + - [WP-Struve]_ + """ + def __init__(self): + r""" + EXAMPLES:: + + sage: n = var('n') + sage: maxima("struve_l(n,x);").sage() + struve_L(n, x) + sage: struve_L(7/5,1)._maxima_() + struve_l(7/5,1) + sage: loads(dumps(struve_L(n,x))) + struve_L(n, x) + """ + BuiltinFunction.__init__(self, 'struve_L', nargs=2, + conversions=dict(maple='StruveL', + mathematica='StruveL', + maxima='struve_l', + sympy='struvel')) + + def _eval_(self, a, z): + """ + EXAMPLES:: + + sage: struve_L(-2,0) + struve_L(-2, 0) + sage: struve_L(-1,0) + 0 + sage: struve_L(pi,0) + 0 + sage: struve_L(-1/2,x) + sqrt(2)*sqrt(1/(pi*x))*sinh(x) + sage: struve_L(1/2,1) + sqrt(2)*(cosh(1) - 1)/sqrt(pi) + sage: struve_L(2,x) + struve_L(2, x) + sage: struve_L(-3/2,x) + -bessel_I(3/2, x) + """ + from sage.symbolic.ring import SR + if z.is_zero() \ + and (SR(a).is_numeric() or SR(a).is_constant()) \ + and a.real() >= -1: + return ZZ(0) + if a == -Integer(1)/2: + from sage.functions.hyperbolic import sinh + return sqrt(2/(pi*z)) * sinh(z) + if a == Integer(1)/2: + from sage.functions.hyperbolic import cosh + return sqrt(2/(pi*z)) * (cosh(z)-1) + if a < 0 and not SR(a).is_integer() and SR(2*a).is_integer(): + from sage.rings.rational_field import QQ + n = (a*(-2) - 1)/2 + return Integer(-1)**n * bessel_I(n+QQ(1)/2, z) + + def _evalf_(self, a, z, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: struve_L(1/2,pi).n() + 4.76805417696286 + sage: struve_L(1/2,pi).n(200) + 4.768054176962864289162484345... + """ + import mpmath + return mpmath_utils.call(mpmath.struvel, a, z, parent=parent) + + def _derivative_(self, a, z, diff_param=None): + """ + EXAMPLES:: + + sage: diff(struve_L(1,x),x) + 1/3*x/pi - 1/2*struve_L(2, x) + 1/2*struve_L(0, x) + """ + if diff_param == 0: + raise ValueError("cannot differentiate struve_L in the first parameter") + + from sage.functions.other import sqrt, gamma + return (z**a/(sqrt(pi)*2**a*gamma(a+Integer(3)/Integer(2)))-struve_L(a+1,z)+struve_L(a-1,z))/2 + + def _print_latex_(self, a, z): + """ + sage: latex(struve_L(2,x)) + L_{{2}}({x}) + """ + return r"L_{{%s}}({%s})" % (a, z) + +struve_L = Function_Struve_L() diff --git a/src/sage/functions/hyperbolic.py b/src/sage/functions/hyperbolic.py index 575e4e2fae2..6589a7a444e 100644 --- a/src/sage/functions/hyperbolic.py +++ b/src/sage/functions/hyperbolic.py @@ -189,13 +189,26 @@ def __init__(self): sage: latex(tanh(x)) \tanh\left(x\right) + + Check that real/imaginary parts are correct (:trac:`20098`):: + + sage: tanh(1+2*I).n() + 1.16673625724092 - 0.243458201185725*I + sage: tanh(1+2*I).real().n() + 1.16673625724092 + sage: tanh(1+2*I).imag().n() + -0.243458201185725 + sage: tanh(x).real() + sinh(2*real_part(x))/(cos(2*imag_part(x)) + cosh(2*real_part(x))) + sage: tanh(x).imag() + sin(2*imag_part(x))/(cos(2*imag_part(x)) + cosh(2*real_part(x))) """ GinacFunction.__init__(self, "tanh", latex_name=r"\tanh") tanh = Function_tanh() -class Function_coth(HyperbolicFunction): +class Function_coth(GinacFunction): def __init__(self): r""" The hyperbolic cotangent function. @@ -204,45 +217,33 @@ def __init__(self): sage: coth(pi) coth(pi) - sage: coth(3.1415) - 1.00374256795520 - sage: float(coth(pi)) - 1.0037418731973213 - sage: RR(coth(pi)) - 1.00374187319732 - - sage: latex(coth(x)) - \coth\left(x\right) - """ - HyperbolicFunction.__init__(self, "coth", latex_name=r"\coth", - evalf_float=lambda x: 1/math.tanh(x)) - - def _eval_(self, x): - """ - EXAMPLES:: - sage: coth(0) - +Infinity + Infinity sage: coth(pi*I) - +Infinity + Infinity sage: coth(pi*I/2) 0 sage: coth(7*pi*I/2) 0 sage: coth(8*pi*I/2) - +Infinity + Infinity sage: coth(7.*pi*I/2) - coth(3.50000000000000*I*pi) + -I*cot(3.50000000000000*pi) + sage: coth(3.1415) + 1.00374256795520 + sage: float(coth(pi)) + 1.0037418731973213 + sage: RR(coth(pi)) + 1.00374187319732 + + sage: bool(diff(coth(x), x) == diff(1/tanh(x), x)) + True + sage: diff(coth(x), x) + -1/sinh(x)^2 + sage: latex(coth(x)) + \operatorname{coth}\left(x\right) """ - if x.is_zero(): - return Infinity - if isinstance(x, Expression): - y = 2 * x / pi / I - if y.is_integer(): - if ZZ(y) % 2 == 1: - return 0 - else: - return Infinity + GinacFunction.__init__(self, "coth", latex_name=r"\operatorname{coth}") def _eval_numpy_(self, x): """ @@ -255,22 +256,10 @@ def _eval_numpy_(self, x): """ return 1 / tanh(x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: bool(diff(coth(x), x) == diff(1/tanh(x), x)) - True - sage: diff(coth(x), x) - -csch(x)^2 - """ - x = args[0] - return -csch(x)**2 - coth = Function_coth() -class Function_sech(HyperbolicFunction): +class Function_sech(GinacFunction): def __init__(self): r""" The hyperbolic secant function. @@ -285,39 +274,27 @@ def __init__(self): 0.0862667383340544... sage: RR(sech(pi)) 0.0862667383340544 - - sage: latex(sech(x)) - {\rm sech}\left(x\right) - """ - HyperbolicFunction.__init__(self, "sech", latex_name=r"{\rm sech}", - evalf_float=lambda x: 1/math.cosh(x)) - - def _eval_(self, x): - """ - EXAMPLES:: - sage: sech(0) 1 sage: sech(pi*I) -1 sage: sech(pi*I/2) - +Infinity + Infinity sage: sech(7*pi*I/2) - +Infinity + Infinity sage: sech(8*pi*I/2) 1 sage: sech(8.*pi*I/2) - sech(4.00000000000000*I*pi) + sec(4.00000000000000*pi) + + sage: bool(diff(sech(x), x) == diff(1/cosh(x), x)) + True + sage: diff(sech(x), x) + -sech(x)*tanh(x) + sage: latex(sech(x)) + \operatorname{sech}\left(x\right) """ - if x.is_zero(): - return 1 - if isinstance(x, Expression): - y = 2 * x / pi / I - if y.is_integer(): - if ZZ(y) % 2 == 1: - return Infinity - else: - return ZZ(-1) ** ZZ(y / 2) + GinacFunction.__init__(self, "sech", latex_name=r"\operatorname{sech}",) def _eval_numpy_(self, x): """ @@ -330,22 +307,10 @@ def _eval_numpy_(self, x): """ return 1 / cosh(x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: bool(diff(sech(x), x) == diff(1/cosh(x), x)) - True - sage: diff(sech(x), x) - -sech(x)*tanh(x) - """ - x = args[0] - return -sech(x)*tanh(x) - sech = Function_sech() -class Function_csch(HyperbolicFunction): +class Function_csch(GinacFunction): def __init__(self): r""" The hyperbolic cosecant function. @@ -360,37 +325,25 @@ def __init__(self): 0.0865895375300469... sage: RR(csch(pi)) 0.0865895375300470 - - sage: latex(csch(x)) - {\rm csch}\left(x\right) - """ - HyperbolicFunction.__init__(self, "csch", latex_name=r"{\rm csch}", - evalf_float=lambda x: 1/math.sinh(x)) - - def _eval_(self, x): - """ - EXAMPLES:: - sage: csch(0) - +Infinity + Infinity sage: csch(pi*I) - +Infinity + Infinity sage: csch(pi*I/2) -I sage: csch(7*pi*I/2) I sage: csch(7.*pi*I/2) - csch(3.50000000000000*I*pi) + -I*csc(3.50000000000000*pi) + + sage: bool(diff(csch(x), x) == diff(1/sinh(x), x)) + True + sage: diff(csch(x), x) + -coth(x)*csch(x) + sage: latex(csch(x)) + {\rm csch}\left(x\right) """ - if x.is_zero(): - return Infinity - if isinstance(x, Expression): - y = 2 * x / pi / I - if y.is_integer(): - if ZZ(y) % 2 == 1: - return ZZ(-1) ** ZZ((y + 1) / 2) * I - else: - return Infinity + GinacFunction.__init__(self, "csch", latex_name=r"{\rm csch}") def _eval_numpy_(self, x): """ @@ -403,18 +356,6 @@ def _eval_numpy_(self, x): """ return 1 / sinh(x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: bool(diff(csch(x), x) == diff(1/sinh(x), x)) - True - sage: diff(csch(x), x) - -coth(x)*csch(x) - """ - x = args[0] - return -csch(x)*coth(x) - csch = Function_csch() @@ -617,7 +558,7 @@ def __init__(self): arctanh = atanh = Function_arctanh() -class Function_arccoth(HyperbolicFunction): +class Function_arccoth(GinacFunction): def __init__(self): r""" The inverse of the hyperbolic cotangent function. @@ -633,6 +574,11 @@ def __init__(self): sage: arccoth(2).n(200) 0.54930614433405484569762261846126285232374527891137472586735 + sage: bool(diff(acoth(x), x) == diff(atanh(x), x)) + True + sage: diff(acoth(x), x) + -1/(x^2 - 1) + Using first the `.n(53)` method is slightly more precise than converting directly to a ``float``:: @@ -646,12 +592,11 @@ def __init__(self): TESTS:: sage: latex(arccoth(x)) - {\rm arccoth}\left(x\right) + \operatorname{arccoth}\left(x\right) """ - HyperbolicFunction.__init__(self, "arccoth", - latex_name=r"{\rm arccoth}", - conversions=dict(maxima='acoth', sympy='acoth'), - evalf_float=lambda x: atanh(float(1/x))) + GinacFunction.__init__(self, "arccoth", + latex_name=r"\operatorname{arccoth}", + conversions=dict(maxima='acoth', sympy='acoth')) def _eval_numpy_(self, x): """ @@ -664,22 +609,10 @@ def _eval_numpy_(self, x): """ return arctanh(1.0 / x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: bool(diff(acoth(x), x) == diff(atanh(x), x)) - True - sage: diff(acoth(x), x) - -1/(x^2 - 1) - """ - x = args[0] - return -1/(x**2 - 1) - arccoth = acoth = Function_arccoth() -class Function_arcsech(HyperbolicFunction): +class Function_arcsech(GinacFunction): def __init__(self): r""" The inverse of the hyperbolic secant function. @@ -697,12 +630,13 @@ def __init__(self): sage: float(arcsech(1/2)) 1.3169578969248168 + sage: diff(asech(x), x) + -1/(sqrt(-x^2 + 1)*x) sage: latex(arcsech(x)) - {\rm arcsech}\left(x\right) + \operatorname{arcsech}\left(x\right) """ - HyperbolicFunction.__init__(self, "arcsech", - latex_name=r"{\rm arcsech}", - evalf_float=lambda x: acosh(float(1/x)), + GinacFunction.__init__(self, "arcsech", + latex_name=r"\operatorname{arcsech}", conversions=dict(maxima='asech')) def _eval_numpy_(self, x): @@ -717,20 +651,10 @@ def _eval_numpy_(self, x): """ return arccosh(1.0 / x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: diff(asech(x), x) - -1/((x + 1)*x*sqrt(-(x - 1)/(x + 1))) - """ - x = args[0] - return -1/(x * (x+1) * ( (1-x)/(1+x) ).sqrt()) - arcsech = asech = Function_arcsech() -class Function_arccsch(HyperbolicFunction): +class Function_arccsch(GinacFunction): def __init__(self): r""" The inverse of the hyperbolic cosecant function. @@ -748,12 +672,13 @@ def __init__(self): sage: float(arccsch(1)) 0.881373587019543 + sage: diff(acsch(x), x) + -1/(sqrt(x^2 + 1)*x) sage: latex(arccsch(x)) - {\rm arccsch}\left(x\right) + \operatorname{arccsch}\left(x\right) """ - HyperbolicFunction.__init__(self, "arccsch", - latex_name=r"{\rm arccsch}", - evalf_float=lambda x: arcsinh(float(1/x)), + GinacFunction.__init__(self, "arccsch", + latex_name=r"\operatorname{arccsch}", conversions=dict(maxima='acsch')) def _eval_numpy_(self, x): @@ -768,14 +693,4 @@ def _eval_numpy_(self, x): """ return arcsinh(1.0 / x) - def _derivative_(self, *args, **kwds): - """ - EXAMPLES:: - - sage: diff(acsch(x), x) - -1/(x^2*sqrt(1/x^2 + 1)) - """ - x = args[0] - return -1/(x**2 * (1 + x**(-2)).sqrt()) - arccsch = acsch = Function_arccsch() diff --git a/src/sage/functions/jacobi.py b/src/sage/functions/jacobi.py index 1448c8976b7..eb51b7e8f64 100644 --- a/src/sage/functions/jacobi.py +++ b/src/sage/functions/jacobi.py @@ -127,7 +127,7 @@ - :wikipedia:`Jacobi's_elliptic_functions` -.. [KhaSuk04] A. Khare and U. Sukhatme. "Cyclic Identities Involving +.. [KhaSuk04] \A. Khare and U. Sukhatme. "Cyclic Identities Involving Jacobi Elliptic Functions". :arxiv:`math-ph/0201004` AUTHORS: diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index f3146eb35d2..08dd0531ebc 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -5,9 +5,12 @@ - Yoora Yi Tenen (2012-11-16): Add documentation for :meth:`log()` (:trac:`12113`) +- Tomas Kalvoda (2015-04-01): Add :meth:`exp_polar()` (:trac:`18085`) + """ from sage.symbolic.function import GinacFunction, BuiltinFunction from sage.symbolic.constants import e as const_e +from sage.symbolic.constants import pi as const_pi from sage.libs.mpmath import utils as mpmath_utils from sage.structure.all import parent as s_parent @@ -788,3 +791,107 @@ def _print_latex_(self, n, z): return r"\operatorname{W_{%s}}({%s})" % (n, z._latex_()) lambert_w = Function_lambert_w() + +class Function_exp_polar(BuiltinFunction): + def __init__(self): + r""" + Representation of a complex number in a polar form. + + INPUT: + + - ``z`` - a complex number `z = a + ib`. + + OUTPUT: + + A complex number with modulus `\exp(a)` and argument `b`. + + If `-\pi < b \leq \pi` then `\operatorname{exp\_polar}(z)=\exp(z)`. + For other values of `b` the function is left unevaluated. + + EXAMPLES: + + The following expressions are evaluated using the exponential + function:: + + sage: exp_polar(pi*I/2) + I + sage: x = var('x', domain='real') + sage: exp_polar(-1/2*I*pi + x) + e^(-1/2*I*pi + x) + + The function is left unevaluated when the imaginary part of the + input `z` does not satisfy `-\pi < \Im(z) \leq \pi`:: + + sage: exp_polar(2*pi*I) + exp_polar(2*I*pi) + sage: exp_polar(-4*pi*I) + exp_polar(-4*I*pi) + + This fixes :trac:`18085`:: + + sage: integrate(1/sqrt(1+x^3),x,algorithm='sympy') + 1/3*x*hypergeometric((1/3, 1/2), (4/3,), -x^3)*gamma(1/3)/gamma(4/3) + + SEEALSO: + + `Examples in Sympy documentation `_, + `Sympy source code of exp_polar `_ + + REFERENCES: + + :wikipedia:`Complex_number#Polar_form` + """ + BuiltinFunction.__init__(self, "exp_polar", + latex_name=r"\operatorname{exp\_polar}", + conversions=dict(sympy='exp_polar')) + + def _evalf_(self, z, parent=None, algorithm=None): + r""" + EXAMPLES: + + If the imaginary part of `z` obeys `-\pi < z \leq \pi`, then + `\operatorname{exp\_polar}(z)` is evaluated as `\exp(z)`:: + + sage: exp_polar(1.0 + 2.0*I) + -1.13120438375681 + 2.47172667200482*I + + If the imaginary part of `z` is outside of that interval the + expression is left unevaluated:: + + sage: exp_polar(-5.0 + 8.0*I) + exp_polar(-5.00000000000000 + 8.00000000000000*I) + + An attempt to numerically evaluate such an expression raises an error:: + + sage: exp_polar(-5.0 + 8.0*I).n() + Traceback (most recent call last): + ... + TypeError: cannot evaluate symbolic expression numerically + + """ + from sage.functions.other import imag + + if (not isinstance(z, Expression) + and bool(-const_pi < imag(z) <= const_pi)): + return exp(z) + else: + return exp_polar(z) + + def _eval_(self, z): + """ + EXAMPLES:: + + sage: exp_polar(3*I*pi) + exp_polar(3*I*pi) + sage: x = var('x', domain='real') + sage: exp_polar(4*I*pi + x) + exp_polar(4*I*pi + x) + + """ + if (isinstance(z, Expression) + and bool(-const_pi < z.imag_part() <= const_pi)): + return exp(z) + else: + return None + +exp_polar = Function_exp_polar() diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index c6e3a4734cc..09e3612b3a6 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -845,7 +845,7 @@ def __init__(self): EXAMPLES: Numerical evaluation happens when appropriate, to the - appropriate accuracy (see #10072):: + appropriate accuracy (see :trac:`10072`):: sage: log_gamma(6) log(120) @@ -860,7 +860,7 @@ def __init__(self): sage: log_gamma(-3.1) 0.400311696703985 - Symbolic input works (see #10075):: + Symbolic input works (see :trac:`10075`):: sage: log_gamma(3*x) log_gamma(3*x) @@ -872,7 +872,7 @@ def __init__(self): To get evaluation of input for which gamma is negative and the ceiling is even, we must explicitly make the input complex. This is - a known issue, see #12521:: + a known issue, see :trac:`12521`:: sage: log_gamma(-2.1) NaN @@ -1119,16 +1119,7 @@ def gamma(a, *args, **kwds): ... TypeError: cannot coerce arguments: no canonical coercion... - We make an exception for elements of AA or QQbar, which cannot be - coerced into symbolic expressions to allow this usage:: - - sage: t = QQbar(sqrt(2)) + sqrt(3); t - 3.146264369941973? - sage: t.parent() - Algebraic Field - - Symbolic functions convert the arguments to symbolic expressions if they - are in QQbar or AA:: + TESTS:: sage: gamma(QQbar(I)) -0.154949828301811 - 0.498015668118356*I @@ -1563,7 +1554,7 @@ def __init__(self): sage: loads(dumps(binomial(n,k))) binomial(n, k) """ - GinacFunction.__init__(self, "binomial", nargs=2, + GinacFunction.__init__(self, "binomial", nargs=2, preserved_arg=1, conversions=dict(maxima='binomial', mathematica='Binomial', sympy='binomial')) diff --git a/src/sage/functions/piecewise.py b/src/sage/functions/piecewise.py index d417ab40077..3dc8e514163 100644 --- a/src/sage/functions/piecewise.py +++ b/src/sage/functions/piecewise.py @@ -1,23 +1,23 @@ r""" Piecewise-defined Functions -Sage implements a very simple class of piecewise-defined functions. -Functions may be any type of symbolic expression. Infinite -intervals are not supported. The endpoints of each interval must -line up. +This module implement piecewise functions in a single variable. See +:mod:`sage.sets.real_set` for more information about how to construct +subsets of the real line for the domains. -TODO: +EXAMPLES:: -- Implement max/min location and values, + sage: f = piecewise([((0,1), x^3), ([-1,0], -x^2)]); f + piecewise(x|-->x^3 on (0, 1), x|-->-x^2 on [-1, 0]; x) + sage: 2*f + 2*piecewise(x|-->x^3 on (0, 1), x|-->-x^2 on [-1, 0]; x) + sage: f(x=1/2) + 1/8 + sage: plot(f) # not tested -- Need: parent object - ring of piecewise functions +TODO: -- This class should derive from an element-type class, and should - define ``_add_``, ``_mul_``, etc. That will automatically take care - of left multiplication and proper coercion. The coercion mentioned - below for scalar mult on right is bad, since it only allows ints and - rationals. The right way is to use an element class and only define - ``_mul_``, and have a parent, so anything gets coerced properly. +- Implement max/min location and values, AUTHORS: @@ -44,1679 +44,1129 @@ - Paul Butler (2009-01): added indefinite integration and default_variable +- Volker Braun (2013): Complete rewrite + +- Ralf Stephan (2015): Rewrite of convolution() and other calculus + functions; many doctest adaptations + TESTS:: - sage: R. = QQ[] - sage: f = Piecewise([[(0,1),1*x^0]]) - sage: 2*f - Piecewise defined function with 1 parts, [[(0, 1), 2]] + sage: fast_callable(f, vars=[x])(0.5) + 0.125000000000... """ #***************************************************************************** # Copyright (C) 2006 William Stein # 2006 David Joyner +# 2013 Volker Braun # -# 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: -# +# Distributed under the terms of the GNU General Public License (GPL), +# version 2 or any later version. The full text of the GPL is available at: # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.misc.sage_eval import sage_eval -from sage.rings.all import QQ, RR, Integer, Rational, infinity -from sage.calculus.functional import derivative -from sage.symbolic.expression import is_Expression -from sage.symbolic.assumptions import assume, forget - -from sage.calculus.calculus import SR, maxima - -def piecewise(list_of_pairs, var=None): - """ - Returns a piecewise function from a list of (interval, function) - pairs. - - ``list_of_pairs`` is a list of pairs (I, fcn), where - fcn is a Sage function (such as a polynomial over RR, or functions - using the lambda notation), and I is an interval such as I = (1,3). - Two consecutive intervals must share a common endpoint. - - If the optional ``var`` is specified, then any symbolic expressions - in the list will be converted to symbolic functions using - ``fcn.function(var)``. (This says which variable is considered to - be "piecewise".) - - We assume that these definitions are consistent (ie, no checking is - done). - - EXAMPLES:: - - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) - sage: f(1) - -1 - sage: f(3) - 2 - sage: f = Piecewise([[(0,1),x], [(1,2),x^2]], x); f - Piecewise defined function with 2 parts, [[(0, 1), x |--> x], [(1, 2), x |--> x^2]] - sage: f(0.9) - 0.900000000000000 - sage: f(1.1) - 1.21000000000000 - """ - return PiecewisePolynomial(list_of_pairs, var=var) - -Piecewise = piecewise - -class PiecewisePolynomial: - """ - Returns a piecewise function from a list of (interval, function) - pairs. - - EXAMPLES:: - - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) - sage: f(1) - -1 - sage: f(3) - 2 - """ - def __init__(self, list_of_pairs, var=None): - r""" - ``list_of_pairs`` is a list of pairs (I, fcn), where - fcn is a Sage function (such as a polynomial over RR, or functions - using the lambda notation), and I is an interval such as I = (1,3). - Two consecutive intervals must share a common endpoint. - - If the optional ``var`` is specified, then any symbolic - expressions in the list will be converted to symbolic - functions using ``fcn.function(var)``. (This says which - variable is considered to be "piecewise".) - - We assume that these definitions are consistent (ie, no checking is - done). - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.list() - [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] - sage: f.length() - 2 - """ - self._length = len(list_of_pairs) - self._intervals = [x[0] for x in list_of_pairs] - functions = [x[1] for x in list_of_pairs] - if var is not None: - for i in range(len(functions)): - if is_Expression(functions[i]): - functions[i] = functions[i].function(var) - self._functions = functions - # We regenerate self._list in case self._functions was modified - # above. This also protects us in case somebody mutates a list - # after they use it as an argument to piecewise(). - self._list = [[self._intervals[i], self._functions[i]] for i in range(self._length)] - - def list(self): - """ - Returns the pieces of this function as a list of functions. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.list() - [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] - """ - return self._list - - def length(self): - """ - Returns the number of pieces of this function. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.length() - 2 - """ - return self._length - - def __repr__(self): - """ - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]); f - Piecewise defined function with 2 parts, [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] - """ - return 'Piecewise defined function with %s parts, %s'%( - self.length(),self.list()) - - def _latex_(self): - r""" - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: latex(f) - \begin{cases} - x \ {\mapsto}\ 1 &\text{on $(0, 1)$}\cr - x \ {\mapsto}\ -x + 1 &\text{on $(1, 2)$}\cr - \end{cases} - - :: - - sage: f(x) = sin(x*pi/2) - sage: g(x) = 1-(x-1)^2 - sage: h(x) = -x - sage: P = Piecewise([[(0,1), f], [(1,3),g], [(3,5), h]]) - sage: latex(P) - \begin{cases} - x \ {\mapsto}\ \sin\left(\frac{1}{2} \, \pi x\right) &\text{on $(0, 1)$}\cr - x \ {\mapsto}\ -{\left(x - 1\right)}^{2} + 1 &\text{on $(1, 3)$}\cr - x \ {\mapsto}\ -x &\text{on $(3, 5)$}\cr - \end{cases} - """ - from sage.misc.latex import latex - tex = ['\\begin{cases}\n'] - for (left, right), f in self.list(): - tex.append('%s &\\text{on $(%s, %s)$}\\cr\n' % (latex(f), left, right)) - tex.append(r'\end{cases}') - return ''.join(tex) - - def intervals(self): - """ - A piecewise non-polynomial example. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = exp(x) - sage: f4(x) = sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f.intervals() - [(0, 1), (1, 2), (2, 3), (3, 10)] - """ - return self._intervals - - def domain(self): - """ - Returns the domain of the function. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = exp(x) - sage: f4(x) = sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f.domain() - (0, 10) - """ - endpoints = sum(self.intervals(), ()) - return (min(endpoints), max(endpoints)) - - def functions(self): - """ - Returns the list of functions (the "pieces"). - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = exp(x) - sage: f4(x) = sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f.functions() - [x |--> 1, x |--> -x + 1, x |--> e^x, x |--> sin(2*x)] - """ - return self._functions - - def extend_by_zero_to(self,xmin=-1000,xmax=1000): - """ - This function simply returns the piecewise defined function which - is extended by 0 so it is defined on all of (xmin,xmax). This is - needed to add two piecewise functions in a reasonable way. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1 - x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.extend_by_zero_to(-1, 3) - Piecewise defined function with 4 parts, [[(-1, 0), 0], [(0, 1), x |--> 1], [(1, 2), x |--> -x + 1], [(2, 3), 0]] - """ - zero = QQ['x'](0) - list_of_pairs = self.list() - a, b = self.domain() - if xmin < a: - list_of_pairs = [[(xmin, a), zero]] + list_of_pairs - if xmax > b: - list_of_pairs = list_of_pairs + [[(b, xmax), zero]] - return Piecewise(list_of_pairs) - - def unextend(self): - """ - This removes any parts in the front or back of the function which - is zero (the inverse to extend_by_zero_to). - - EXAMPLES:: - - sage: R. = QQ[] - sage: f = Piecewise([[(-3,-1),1+2+x],[(-1,1),1-x^2]]) - sage: e = f.extend_by_zero_to(-10,10); e - Piecewise defined function with 4 parts, [[(-10, -3), 0], [(-3, -1), x + 3], [(-1, 1), -x^2 + 1], [(1, 10), 0]] - sage: d = e.unextend(); d - Piecewise defined function with 2 parts, [[(-3, -1), x + 3], [(-1, 1), -x^2 + 1]] - sage: d==f - True - """ - list_of_pairs = self.list() - funcs = self.functions() - if funcs[0] == 0: - list_of_pairs = list_of_pairs[1:] - if funcs[-1] == 0: - list_of_pairs = list_of_pairs[:-1] - return Piecewise(list_of_pairs) - - def _riemann_sum_helper(self, N, func, initial=0): - """ - A helper function for computing Riemann sums. - - INPUT: - - - - ``N`` - the number of subdivisions - - - ``func`` - a function to apply to the endpoints of - each subdivision - - - ``initial`` - the starting value - - - EXAMPLES:: - - sage: f1(x) = x^2 ## example 1 - sage: f2(x) = 5-x^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f._riemann_sum_helper(6, lambda x0, x1: (x1-x0)*f(x1)) - 19/6 - """ - a,b = self.domain() - rsum = initial - h = (b-a)/N - for i in range(N): - x0 = a+i*h - x1 = a+(i+1)*h - rsum += func(x0, x1) - return rsum - - def riemann_sum_integral_approximation(self,N,mode=None): - """ - Returns the piecewise line function defined by the Riemann sums in - numerical integration based on a subdivision into N subintervals. - - Set mode="midpoint" for the height of the rectangles to be - determined by the midpoint of the subinterval; set mode="right" for - the height of the rectangles to be determined by the right-hand - endpoint of the subinterval; the default is mode="left" (the height - of the rectangles to be determined by the left-hand endpoint of - the subinterval). - - EXAMPLES:: - - sage: f1(x) = x^2 ## example 1 - sage: f2(x) = 5-x^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.riemann_sum_integral_approximation(6) - 17/6 - sage: f.riemann_sum_integral_approximation(6,mode="right") - 19/6 - sage: f.riemann_sum_integral_approximation(6,mode="midpoint") - 3 - sage: f.integral(definite=True) - 3 - """ - if mode is None: - return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x0)) - elif mode == "right": - return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x1)) - elif mode == "midpoint": - return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self((x0+x1)/2)) - else: - raise ValueError("invalid mode") - - def riemann_sum(self,N,mode=None): - """ - Returns the piecewise line function defined by the Riemann sums in - numerical integration based on a subdivision into N subintervals. - Set mode="midpoint" for the height of the rectangles to be - determined by the midpoint of the subinterval; set mode="right" for - the height of the rectangles to be determined by the right-hand - endpoint of the subinterval; the default is mode="left" (the height - of the rectangles to be determined by the left-hand endpoint of - the subinterval). - - EXAMPLES:: - - sage: f1(x) = x^2 - sage: f2(x) = 5-x^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.riemann_sum(6,mode="midpoint") - Piecewise defined function with 6 parts, [[(0, 1/3), 1/36], [(1/3, 2/3), 1/4], [(2/3, 1), 25/36], [(1, 4/3), 131/36], [(4/3, 5/3), 11/4], [(5/3, 2), 59/36]] - - :: - - sage: f = Piecewise([[(-1,1),(1-x^2).function(x)]]) - sage: rsf = f.riemann_sum(7) - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()]) - sage: P + Q + L - Graphics object consisting of 15 graphics primitives - - :: - - sage: f = Piecewise([[(-1,1),(1/2+x-x^3)]], x) ## example 3 - sage: rsf = f.riemann_sum(8) - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()]) - sage: P + Q + L - Graphics object consisting of 17 graphics primitives - """ - if mode is None: - rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x0))]], - initial=[]) - elif mode == "right": - rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x1))]], - initial=[]) - elif mode == "midpoint": - rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self((x0+x1)/2))]], - initial=[]) - else: - raise ValueError("invalid mode") - return Piecewise(rsum) - - def trapezoid(self,N): - """ - Returns the piecewise line function defined by the trapezoid rule - for numerical integration based on a subdivision into N - subintervals. - - EXAMPLES:: - - sage: R. = QQ[] - sage: f1 = x^2 - sage: f2 = 5-x^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.trapezoid(4) - Piecewise defined function with 4 parts, [[(0, 1/2), 1/2*x], [(1/2, 1), 9/2*x - 2], [(1, 3/2), 1/2*x + 2], [(3/2, 2), -7/2*x + 8]] - - :: - - sage: R. = QQ[] - sage: f = Piecewise([[(-1,1),1-x^2]]) - sage: tf = f.trapezoid(4) - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()]) - sage: P+Q+L - Graphics object consisting of 9 graphics primitives - - :: - - sage: R. = QQ[] - sage: f = Piecewise([[(-1,1),1/2+x-x^3]]) ## example 3 - sage: tf = f.trapezoid(6) - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()]) - sage: P+Q+L - Graphics object consisting of 13 graphics primitives - - TESTS: - - Use variables other than x (:trac:`13836`):: - - sage: R. = QQ[] - sage: f1 = y^2 - sage: f2 = 5-y^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.trapezoid(4) - Piecewise defined function with 4 parts, [[(0, 1/2), 1/2*y], [(1/2, 1), 9/2*y - 2], [(1, 3/2), 1/2*y + 2], [(3/2, 2), -7/2*y + 8]] - - """ - x = QQ[self.default_variable()].gen() - def f(x0, x1): - f0, f1 = self(x0), self(x1) - return [[(x0,x1),f0+(f1-f0)*(x1-x0)**(-1)*(x-x0)]] - rsum = self._riemann_sum_helper(N, f, initial=[]) - return Piecewise(rsum) - - def trapezoid_integral_approximation(self,N): - """ - Returns the approximation given by the trapezoid rule for numerical - integration based on a subdivision into N subintervals. - - EXAMPLES:: - - sage: f1(x) = x^2 ## example 1 - sage: f2(x) = 1-(1-x)^2 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: tf = f.trapezoid(6) - sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: ta = f.trapezoid_integral_approximation(6) - sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25)) - sage: a = f.integral(definite=True) - sage: tt = text('area under curve = %s'%a, (1.5, -0.5)) - sage: P + Q + t + tt - Graphics object consisting of 10 graphics primitives - - :: - - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) ## example 2 - sage: tf = f.trapezoid(4) - sage: ta = f.trapezoid_integral_approximation(4) - sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) - sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25)) - sage: a = f.integral(definite=True) - sage: tt = text('area under curve = %s'%a, (1.5, -0.5)) - sage: P+Q+t+tt - Graphics object consisting of 8 graphics primitives - """ - def f(x0, x1): - f0, f1 = self(x0), self(x1) - return ((f1+f0)/2)*(x1-x0) - return self._riemann_sum_helper(N, f) - - def critical_points(self): - """ - Return the critical points of this piecewise function. - - .. warning:: - - Uses maxima, which prints the warning to use results with - caution. Only works for piecewise functions whose parts are - polynomials with real critical not occurring on the - interval endpoints. - - EXAMPLES:: - - sage: R. = QQ[] - sage: f1 = x^0 - sage: f2 = 10*x - x^2 - sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x - sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) - sage: expected = [5, 12, 13, 14] - sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) - True - - TESTS: - - Use variables other than x (:trac:`13836`):: - - sage: R. = QQ[] - sage: f1 = y^0 - sage: f2 = 10*y - y^2 - sage: f3 = 3*y^4 - 156*y^3 + 3036*y^2 - 26208*y - sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) - sage: expected = [5, 12, 13, 14] - sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) - True - """ - from sage.calculus.calculus import maxima - x = QQ[self.default_variable()].gen() - crit_pts = [] - for (a,b), f in self.list(): - for root in maxima.allroots(SR(f).diff(x)==0): - root = float(root.rhs()) - if a < root < b: - crit_pts.append(root) - return crit_pts - - def base_ring(self): - """ - Returns the base ring of the function pieces. This - is useful when this class is extended. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = x^2-5 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) - sage: base_ring(f) - Symbolic Ring - - :: - - sage: R. = QQ[] - sage: f1 = x^0 - sage: f2 = 10*x - x^2 - sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x - sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) - sage: f.base_ring() - Rational Field - """ - return (self.functions()[0]).base_ring() - - def end_points(self): - """ - Returns a list of all interval endpoints for this function. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = x^2-5 - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) - sage: f.end_points() - [0, 1, 2, 3] - """ - intervals = self.intervals() - return [ intervals[0][0] ] + [b for a,b in intervals] - - def __call__(self,x0): - """ - Evaluates self at x0. Returns the average value of the jump if x0 - is an interior endpoint of one of the intervals of self and the - usual value otherwise. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = exp(x) - sage: f4(x) = sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f(0.5) - 1 - sage: f(5/2) - e^(5/2) - sage: f(5/2).n() - 12.1824939607035 - sage: f(1) - 1/2 - """ - #x0 = QQ(x0) ## does not allow for evaluation at pi - n = self.length() - endpts = self.end_points() - for i in range(1,n): - if x0 == endpts[i]: - return (self.functions()[i-1](x0) + self.functions()[i](x0))/2 - if x0 == endpts[0]: - return self.functions()[0](x0) - if x0 == endpts[n]: - return self.functions()[n-1](x0) - for i in range(n): - if endpts[i] < x0 < endpts[i+1]: - return self.functions()[i](x0) - raise ValueError("Value not defined outside of domain.") - - def which_function(self,x0): - """ - Returns the function piece used to evaluate self at x0. - - EXAMPLES:: - - sage: f1(z) = z - sage: f2(x) = 1-x - sage: f3(y) = exp(y) - sage: f4(t) = sin(2*t) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f.which_function(3/2) - x |--> -x + 1 - """ - for (a,b), f in self.list(): - if a <= x0 <= b: - return f - raise ValueError("Function not defined outside of domain.") - - def default_variable(self): - r""" - Return the default variable. The default variable is defined as the - first variable in the first piece that has a variable. If no pieces have - a variable (each piece is a constant value), `x` is returned. - - The result is cached. - - AUTHOR: Paul Butler - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 5*x - sage: p = Piecewise([[(0,1),f1],[(1,4),f2]]) - sage: p.default_variable() - x - - sage: f1 = 3*var('y') - sage: p = Piecewise([[(0,1),4],[(1,4),f1]]) - sage: p.default_variable() - y - - """ - try: - return self.__default_variable - except AttributeError: - pass - for _, fun in self._list: - try: - fun = SR(fun) - if fun.variables(): - v = fun.variables()[0] - self.__default_variable = v - return v - except TypeError: - # pass if fun is lambda function - pass - # default to x - v = SR.var('x') - self.__default_value = v - return v - - def integral(self, x=None, a=None, b=None, definite=False): - r""" - By default, returns the indefinite integral of the function. - If definite=True is given, returns the definite integral. - - AUTHOR: - - - Paul Butler - - EXAMPLES:: - - sage: f1(x) = 1-x - sage: f = Piecewise([[(0,1),1],[(1,2),f1]]) - sage: f.integral(definite=True) - 1/2 - - :: - - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.integral(definite=True) - 1/2*pi - - sage: f1(x) = 2 - sage: f2(x) = 3 - x - sage: f = Piecewise([[(-2, 0), f1], [(0, 3), f2]]) - sage: f.integral() - Piecewise defined function with 2 parts, [[(-2, 0), x |--> 2*x + 4], [(0, 3), x |--> -1/2*x^2 + 3*x + 4]] - - sage: f1(y) = -1 - sage: f2(y) = y + 3 - sage: f3(y) = -y - 1 - sage: f4(y) = y^2 - 1 - sage: f5(y) = 3 - sage: f = Piecewise([[(-4,-3),f1],[(-3,-2),f2],[(-2,0),f3],[(0,2),f4],[(2,3),f5]]) - sage: F = f.integral(y) - sage: F - Piecewise defined function with 5 parts, [[(-4, -3), y |--> -y - 4], [(-3, -2), y |--> 1/2*y^2 + 3*y + 7/2], [(-2, 0), y |--> -1/2*y^2 - y - 1/2], [(0, 2), y |--> 1/3*y^3 - y - 1/2], [(2, 3), y |--> 3*y - 35/6]] - - Ensure results are consistent with FTC:: - - sage: F(-3) - F(-4) - -1 - sage: F(-1) - F(-3) - 1 - sage: F(2) - F(0) - 2/3 - sage: f.integral(y, 0, 2) - 2/3 - sage: F(3) - F(-4) - 19/6 - sage: f.integral(y, -4, 3) - 19/6 - sage: f.integral(definite=True) - 19/6 - - :: - - sage: f1(y) = (y+3)^2 - sage: f2(y) = y+3 - sage: f3(y) = 3 - sage: f = Piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]]) - sage: f.integral() - Piecewise defined function with 3 parts, [[(-Infinity, -3), y |--> 1/3*y^3 + 3*y^2 + 9*y + 9], [(-3, 0), y |--> 1/2*y^2 + 3*y + 9/2], [(0, +Infinity), y |--> 3*y + 9/2]] - - :: - - sage: f1(x) = e^(-abs(x)) - sage: f = Piecewise([[(-infinity, infinity), f1]]) - sage: f.integral(definite=True) - 2 - sage: f.integral() - Piecewise defined function with 1 parts, [[(-Infinity, +Infinity), x |--> -1/2*((sgn(x) - 1)*e^(2*x) - 2*e^x*sgn(x) + sgn(x) + 1)*e^(-x) - 1]] - - :: - - sage: f = Piecewise([((0, 5), cos(x))]) - sage: f.integral() - Piecewise defined function with 1 parts, [[(0, 5), x |--> sin(x)]] - - - TESTS: - - Verify that piecewise integrals of zero work (trac #10841):: - - sage: f0(x) = 0 - sage: f = Piecewise([[(0,1),f0]]) - sage: f.integral(x,0,1) - 0 - sage: f = Piecewise([[(0,1), 0]]) - sage: f.integral(x,0,1) - 0 - sage: f = Piecewise([[(0,1), SR(0)]]) - sage: f.integral(x,0,1) - 0 - - """ - if a is not None and b is not None: - F = self.integral(x) - return F(b) - F(a) - - if a is not None or b is not None: - raise TypeError('only one endpoint given') - - area = 0 # cumulative definite integral of parts to the left of the current interval - integrand_pieces = sorted(self.list()) - new_pieces = [] - - if x is None: - x = self.default_variable() - - # The integral is computed by iterating over the pieces in order. - # The definite integral for each piece is calculated and accumulated in `area`. - # Thus at any time, `area` represents the definite integral of all the pieces - # encountered so far. The indefinite integral of each piece is also calculated, - # and the `area` before each piece is added to the piece. - # - # If a definite integral is requested, `area` is returned. - # Otherwise, a piecewise function is constructed from the indefinite integrals - # and returned. - # - # An exception is made if integral is called on a piecewise function - # that starts at -infinity. In this case, we do not try to calculate the - # definite integral of the first piece, and the value of `area` remains 0 - # after the first piece. - - for (start, end), fun in integrand_pieces: - fun = SR(fun) - if start == -infinity and not definite: - fun_integrated = fun.integral(x, end, x) - else: - try: - assume(start < x) - except ValueError: # Assumption is redundant - pass - fun_integrated = fun.integral(x, start, x) + area - forget(start < x) - if definite or end != infinity: - area += fun.integral(x, start, end) - new_pieces.append([(start, end), SR(fun_integrated).function(x)]) - - if definite: - return SR(area) - else: - return Piecewise(new_pieces) - - def convolution(self, other): - """ - Returns the convolution function, - `f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly - supported `f,g`. - - EXAMPLES:: - - sage: x = PolynomialRing(QQ,'x').gen() - sage: f = Piecewise([[(0,1),1*x^0]]) ## example 0 - sage: g = f.convolution(f) - sage: h = f.convolution(g) - sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1)); - sage: # Type show(P+Q+R) to view - sage: f = Piecewise([[(0,1),1*x^0],[(1,2),2*x^0],[(2,3),1*x^0]]) ## example 1 - sage: g = f.convolution(f) - sage: h = f.convolution(g) - sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1)); - sage: # Type show(P+Q+R) to view - sage: f = Piecewise([[(-1,1),1]]) ## example 2 - sage: g = Piecewise([[(0,3),x]]) - sage: f.convolution(g) - Piecewise defined function with 3 parts, [[(-1, 1), 0], [(1, 2), -3/2*x], [(2, 4), -3/2*x]] - sage: g = Piecewise([[(0,3),1*x^0],[(3,4),2*x^0]]) - sage: f.convolution(g) - Piecewise defined function with 5 parts, [[(-1, 1), x + 1], [(1, 2), 3], [(2, 3), x], [(3, 4), -x + 8], [(4, 5), -2*x + 10]] - """ - f = self - g = other - M = min(min(f.end_points()),min(g.end_points())) - N = max(max(f.end_points()),max(g.end_points())) - R2 = PolynomialRing(QQ,2,names=["tt","uu"]) - tt,uu = R2.gens() - conv = 0 - f0 = f.functions()[0] - g0 = g.functions()[0] - R1 = f0.parent() - xx = R1.gen() - var = repr(xx) - if len(f.intervals())==1 and len(g.intervals())==1: - f = f.unextend() - g = g.unextend() - a1 = f.intervals()[0][0] - a2 = f.intervals()[0][1] - b1 = g.intervals()[0][0] - b2 = g.intervals()[0][1] - i1 = repr(f0).replace(var,repr(uu)) - i2 = repr(g0).replace(var,"("+repr(tt-uu)+")") - cmd1 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, tt-b1) ## if a1+b1 < tt < a2+b1 - cmd2 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, tt-b1) ## if a1+b2 < tt < a2+b1 - cmd3 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, a2) ## if a1+b2 < tt < a2+b2 - cmd4 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, a2) ## if a2+b1 < tt < a1+b2 - conv1 = maxima.eval(cmd1) - conv2 = maxima.eval(cmd2) - conv3 = maxima.eval(cmd3) - conv4 = maxima.eval(cmd4) - # this is a very, very, very ugly hack - x = PolynomialRing(QQ,'x').gen() - fg1 = sage_eval(conv1.replace("tt",var), {'x':x}) ## should be = R2(conv1) - fg2 = sage_eval(conv2.replace("tt",var), {'x':x}) ## should be = R2(conv2) - fg3 = sage_eval(conv3.replace("tt",var), {'x':x}) ## should be = R2(conv3) - fg4 = sage_eval(conv4.replace("tt",var), {'x':x}) ## should be = R2(conv4) - if a1-b11 or len(g.intervals())>1: - z = Piecewise([[(-3*abs(N-M),3*abs(N-M)),0*xx**0]]) - ff = f.functions() - gg = g.functions() - intvlsf = f.intervals() - intvlsg = g.intervals() - for i in range(len(ff)): - for j in range(len(gg)): - f0 = Piecewise([[intvlsf[i],ff[i]]]) - g0 = Piecewise([[intvlsg[j],gg[j]]]) - h = g0.convolution(f0) - z = z + h - return z.unextend() - - def derivative(self): - r""" - Returns the derivative (as computed by maxima) - Piecewise(I,`(d/dx)(self|_I)`), as I runs over the - intervals belonging to self. self must be piecewise polynomial. - - EXAMPLES:: - - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: f.derivative() - Piecewise defined function with 2 parts, [[(0, 1), x |--> 0], [(1, 2), x |--> -1]] - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.derivative() - Piecewise defined function with 2 parts, [[(0, 1/2*pi), x |--> 0], [(1/2*pi, pi), x |--> 0]] - - :: - - sage: f = Piecewise([[(0,1), (x * 2)]], x) - sage: f.derivative() - Piecewise defined function with 1 parts, [[(0, 1), x |--> 2]] - """ - x = self.default_variable() - dlist = [[(a, b), derivative(f(x), x).function(x)] for (a,b),f in self.list()] - return Piecewise(dlist) - - def tangent_line(self, pt): - """ - Computes the linear function defining the tangent line of the - piecewise function self. +from sage.symbolic.function import BuiltinFunction +from sage.sets.real_set import RealSet, InternalRealInterval +from sage.symbolic.ring import SR +from sage.rings.rational_field import QQ +from sage.rings.infinity import minus_infinity, infinity - EXAMPLES:: +class PiecewiseFunction(BuiltinFunction): - sage: f1(x) = x^2 - sage: f2(x) = 5-x^3+x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) - sage: tf = f.tangent_line(0.9) ## tangent line at x=0.9 - sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) - sage: Q = tf.plot(rgbcolor=(0.7,0.2,0.2), plot_points=40) - sage: P + Q - Graphics object consisting of 4 graphics primitives + def __init__(self): """ - pt = QQ(pt) - R = QQ[self.default_variable()] - x = R.gen() - der = self.derivative() - tanline = (x-pt)*der(pt)+self(pt) - dlist = [[(a, b), tanline] for (a,b),f in self.list()] - return Piecewise(dlist) - - def plot(self, *args, **kwds): - """ - Returns the plot of self. - - Keyword arguments are passed onto the plot command for each piece - of the function. E.g., the plot_points keyword affects each - segment of the plot. + Piecewise function EXAMPLES:: - sage: f1(x) = 1 - sage: f2(x) = 1-x - sage: f3(x) = exp(x) - sage: f4(x) = sin(2*x) - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: P = f.plot(rgbcolor=(0.7,0.1,0), plot_points=40) - sage: P - Graphics object consisting of 4 graphics primitives - - Remember: to view this, type show(P) or P.save("path/myplot.png") - and then open it in a graphics viewer such as GIMP. - - TESTS: - - We should not add each piece to the legend individually, since - this creates duplicates (:trac:`12651`). This tests that only - one of the graphics objects in the plot has a non-``None`` - ``legend_label``:: - - sage: f1 = sin(x) - sage: f2 = cos(x) - sage: f = piecewise([[(-1,0), f1],[(0,1), f2]]) - sage: p = f.plot(legend_label='$f(x)$') - sage: lines = [ - ... line - ... for line in p._objects - ... if line.options()['legend_label'] is not None ] - sage: len(lines) - 1 + sage: var('x, y') + (x, y) + sage: f = piecewise([((0,1), x^2*y), ([-1,0], -x*y^2)], var=x); f + piecewise(x|-->x^2*y on (0, 1), x|-->-x*y^2 on [-1, 0]; x) + sage: f(1/2) + 1/4*y + sage: f(-1/2) + 1/2*y^2 """ - from sage.plot.all import plot, Graphics + BuiltinFunction.__init__(self, "piecewise", + latex_name="piecewise", + conversions=dict(), nargs=2) - g = Graphics() - - for i, ((a,b), f) in enumerate(self.list()): - # If it's the first piece, pass all arguments. Otherwise, - # filter out 'legend_label' so that we don't add each - # piece to the legend separately (trac #12651). - if i != 0 and 'legend_label' in kwds: - del kwds['legend_label'] - - g += plot(f, a, b, *args, **kwds) - - return g - - def fourier_series_cosine_coefficient(self,n,L): + def __call__(self, function_pieces, **kwds): r""" - Returns the n-th Fourier series coefficient of - `\cos(n\pi x/L)`, `a_n`. + Piecewise functions INPUT: + + - ``function_pieces`` -- a list of pairs consisting of a + domain and a symbolic function. - - - ``self`` - the function f(x), defined over -L x L - - - ``n`` - an integer n=0 - - - ``L`` - (the period)/2 - + - ``var=x`` -- a symbolic variable or ``None`` (default). The + real variable in which the function is piecewise in. OUTPUT: - `a_n = \frac{1}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` + A piecewise-defined function. A ``ValueError`` will be raised + if the domains of the pieces are not pairwise disjoint. + EXAMPLES:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_cosine_coefficient(2,1) - pi^(-2) - sage: f(x) = x^2 - sage: f = Piecewise([[(-pi,pi),f]]) - sage: f.fourier_series_cosine_coefficient(2,pi) - 1 - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_cosine_coefficient(5,pi) - -3/5/pi - """ - from sage.all import cos, pi - x = SR.var('x') - result = sum([(f(x)*cos(pi*x*n/L)/L).integrate(x, a, b) - for (a,b), f in self.list()]) - if is_Expression(result): - return result.simplify_trig() - return result - - def fourier_series_sine_coefficient(self,n,L): - r""" - Returns the n-th Fourier series coefficient of - `\sin(n\pi x/L)`, `b_n`. - - INPUT: - - - - ``self`` - the function f(x), defined over -L x L - - - ``n`` - an integer n0 - - - ``L`` - (the period)/2 - - + + sage: my_abs = piecewise([((-1, 0), -x), ([0, 1], x)], var=x); my_abs + piecewise(x|-->-x on (-1, 0), x|-->x on [0, 1]; x) + sage: [ my_abs(i/5) for i in range(-4, 5)] + [4/5, 3/5, 2/5, 1/5, 0, 1/5, 2/5, 3/5, 4/5] + + TESTS:: + + sage: piecewise([([-1, 0], -x), ([0, 1], x)], var=x) + Traceback (most recent call last): + ... + ValueError: domains must be pairwise disjoint + + sage: step = piecewise([((-1, 0), -1), ([0, 0], 0), ((0, 1), 1)], var=x); step + piecewise(x|-->-1 on (-1, 0), x|-->0 on {0}, x|-->1 on (0, 1); x) + sage: step(-1/2), step(0), step(1/2) + (-1, 0, 1) + """ + #print 'pf_call', function_pieces, kwds + from types import * + var = kwds.pop('var', None) + parameters = [] + domain_list = [] + for piece in function_pieces: + domain, function = piece + if not isinstance(domain, RealSet): + domain = RealSet(domain) + if domain.is_empty(): + continue + if isinstance(function, FunctionType): + if var is None: + var = SR.var('x') + if function.func_code.co_argcount == 0: + function = function() + else: + function = function(var) + function = SR(function) + if var is None and len(function.variables()) > 0: + var = function.variables()[0] + parameters.append((domain, function)) + domain_list.append(domain) + if not RealSet.are_pairwise_disjoint(*domain_list): + raise ValueError('domains must be pairwise disjoint') + if var is None: + var = self.default_variable() + parameters = SR._force_pyobject(tuple(parameters), recursive=False) + return BuiltinFunction.__call__(self, parameters, var, **kwds) + + def _print_(self, parameters, variable): + """ + Return a string representation + OUTPUT: - `b_n = \frac{1}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx` - - EXAMPLES:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_sine_coefficient(2,1) # L=1, n=2 - 0 - """ - from sage.all import sin, pi - x = SR.var('x') - result = sum([(f(x)*sin(pi*x*n/L)/L).integrate(x, a, b) - for (a,b), f in self.list()]) - if is_Expression(result): - return result.simplify_trig() - return result - - def _fourier_series_helper(self, N, L, scale_function): - r""" - A helper function for the construction of Fourier series. The - argument scale_function is a function which takes in n, - representing the `n^{th}` coefficient, and return an - expression to scale the sine and cosine coefficients by. + + String. EXAMPLES:: - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f._fourier_series_helper(3, 1, lambda n: 1) - cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 - """ - from sage.all import pi, sin, cos, srange - x = self.default_variable() - a0 = self.fourier_series_cosine_coefficient(0,L) - result = a0/2 + sum([(self.fourier_series_cosine_coefficient(n,L)*cos(n*pi*x/L) + - self.fourier_series_sine_coefficient(n,L)*sin(n*pi*x/L))* - scale_function(n) - for n in srange(1,N)]) - return result.expand() - - - def fourier_series_partial_sum(self,N,L): - r""" - Returns the partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - as a string. - - EXAMPLE:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_partial_sum(3,1) - cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_partial_sum(3,pi) - -3*cos(x)/pi - 3*sin(2*x)/pi + 3*sin(x)/pi - 1/4 - """ - return self._fourier_series_helper(N, L, lambda n: 1) - - def fourier_series_partial_sum_cesaro(self,N,L): - r""" - Returns the Cesaro partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - - as a string. This is a "smoother" partial sum - the Gibbs - phenomenon is mollified. - - EXAMPLE:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_partial_sum_cesaro(3,1) - 1/3*cos(2*pi*x)/pi^2 - 8/3*cos(pi*x)/pi^2 + 1/3 - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_partial_sum_cesaro(3,pi) - -2*cos(x)/pi - sin(2*x)/pi + 2*sin(x)/pi - 1/4 - """ - return self._fourier_series_helper(N, L, lambda n: 1-n/N) - - def fourier_series_partial_sum_hann(self,N,L): - r""" - Returns the Hann-filtered partial sum (named after von Hann, not - Hamming) - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - as a string, where `H_N(x) = (1+\cos(\pi x/N))/2`. This is - a "smoother" partial sum - the Gibbs phenomenon is mollified. - - EXAMPLE:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_partial_sum_hann(3,1) - 1/4*cos(2*pi*x)/pi^2 - 3*cos(pi*x)/pi^2 + 1/3 - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_partial_sum_hann(3,pi) - -9/4*cos(x)/pi - 3/4*sin(2*x)/pi + 9/4*sin(x)/pi - 1/4 - """ - from sage.all import cos, pi - return self._fourier_series_helper(N, L, lambda n: (1+cos(pi*n/N))/2) - - def fourier_series_partial_sum_filtered(self,N,L,F): - r""" - Returns the "filtered" partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - as a string, where `F = [F_1,F_2, ..., F_{N}]` is a list - of length `N` consisting of real numbers. This can be used - to plot FS solutions to the heat and wave PDEs. - - EXAMPLE:: - - sage: f(x) = x^2 - sage: f = Piecewise([[(-1,1),f]]) - sage: f.fourier_series_partial_sum_filtered(3,1,[1,1,1]) - cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: f.fourier_series_partial_sum_filtered(3,pi,[1,1,1]) - -3*cos(x)/pi - 3*sin(2*x)/pi + 3*sin(x)/pi - 1/4 - """ - return self._fourier_series_helper(N, L, lambda n: F[n]) - - def plot_fourier_series_partial_sum(self,N,L,xmin,xmax, **kwds): - r""" - Plots the partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - over xmin x xmin. - - EXAMPLE:: - - sage: f1(x) = -2 - sage: f2(x) = 1 - sage: f3(x) = -1 - sage: f4(x) = 2 - sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) - sage: P = f.plot_fourier_series_partial_sum(3,pi,-5,5) # long time - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: P = f.plot_fourier_series_partial_sum(15,pi,-5,5) # long time - - Remember, to view this type show(P) or P.save("path/myplot.png") - and then open it in a graphics viewer such as GIMP. + sage: p = piecewise([((-2, 0), -x), ([0, 4], x)], var=x) + sage: str(p) # indirect doctest + 'piecewise(x|-->-x on (-2, 0), x|-->x on [0, 4]; x)' """ - from sage.plot.all import plot - return plot(self.fourier_series_partial_sum(N,L), xmin, xmax, **kwds) - - def plot_fourier_series_partial_sum_cesaro(self,N,L,xmin,xmax, **kwds): - r""" - Plots the partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - - over xmin x xmin. This is a "smoother" partial sum - the Gibbs - phenomenon is mollified. + s = 'piecewise(' + args = [] + for domain, func in parameters: + args.append('{0}|-->{1} on {2}'.format(str(variable), str(func), str(domain))) + s += ', '.join(args) + '; {0})'.format(str(variable)) + return s - EXAMPLE:: - - sage: f1(x) = -2 - sage: f2(x) = 1 - sage: f3(x) = -1 - sage: f4(x) = 2 - sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) - sage: P = f.plot_fourier_series_partial_sum_cesaro(3,pi,-5,5) # long time - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: P = f.plot_fourier_series_partial_sum_cesaro(15,pi,-5,5) # long time - - Remember, to view this type show(P) or P.save("path/myplot.png") - and then open it in a graphics viewer such as GIMP. + def _subs_(self, subs_map, options, parameters, x): """ - from sage.plot.all import plot - return plot(self.fourier_series_partial_sum_cesaro(N,L), xmin, xmax, **kwds) - - def plot_fourier_series_partial_sum_hann(self,N,L,xmin,xmax, **kwds): - r""" - Plots the partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + Callback from Pynac `subs()` + EXAMPLES: - over xmin x xmin, where H_N(x) = (0.5)+(0.5)\*cos(x\*pi/N) is the - N-th Hann filter. + If the substitution changes the piecewise variable, it must + evaluate to a number so that we know which component we are + on:: - EXAMPLE:: - - sage: f1(x) = -2 - sage: f2(x) = 1 - sage: f3(x) = -1 - sage: f4(x) = 2 - sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) - sage: P = f.plot_fourier_series_partial_sum_hann(3,pi,-5,5) # long time - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) - sage: P = f.plot_fourier_series_partial_sum_hann(15,pi,-5,5) # long time - - Remember, to view this type show(P) or P.save("path/myplot.png") - and then open it in a graphics viewer such as GIMP. - """ - from sage.plot.all import plot - return plot(self.fourier_series_partial_sum_hann(N,L), xmin, xmax, **kwds) - - def plot_fourier_series_partial_sum_filtered(self,N,L,F,xmin,xmax, **kwds): - r""" - Plots the partial sum - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], - - - over xmin x xmin, where `F = [F_1,F_2, ..., F_{N}]` is a - list of length `N` consisting of real numbers. This can be - used to plot FS solutions to the heat and wave PDEs. - - EXAMPLE:: - - sage: f1(x) = -2 - sage: f2(x) = 1 - sage: f3(x) = -1 - sage: f4(x) = 2 - sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) - sage: P = f.plot_fourier_series_partial_sum_filtered(3,pi,[1]*3,-5,5) # long time - sage: f1(x) = -1 - sage: f2(x) = 2 - sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f1],[(pi/2,pi),f2]]) - sage: P = f.plot_fourier_series_partial_sum_filtered(15,pi,[1]*15,-5,5) # long time - - Remember, to view this type show(P) or P.save("path/myplot.png") - and then open it in a graphics viewer such as GIMP. - """ - from sage.plot.all import plot - return plot(self.fourier_series_partial_sum_filtered(N,L,F), xmin, xmax, **kwds) - - def fourier_series_value(self,x,L): - r""" - Returns the value of the Fourier series coefficient of self at - `x`, - - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^\infty [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], \ \ \ -L-x^y on (-2, 0), x|-->x - y on [0, 2]; x) + sage: p.subs(y=sin(y)) + piecewise(x|-->-x^sin(y) on (-2, 0), x|-->x - sin(y) on [0, 2]; x) + """ + point = subs_map.apply_to(x, 0) + if point == x: + # substitution only in auxiliary variables + new_params = [] + for domain, func in parameters: + new_params.append((domain, subs_map.apply_to(func, 0))) + return piecewise(new_params, var=x) + if ((point.is_numeric() or point.is_constant()) + and (point.is_real())): + if hasattr(point, 'pyobject'): + # unwrap any numeric values + point = point.pyobject() else: - return self(xnew) - - def cosine_series_coefficient(self,n,L): - r""" - Returns the n-th cosine series coefficient of - `\cos(n\pi x/L)`, `a_n`. - - INPUT: - - - - ``self`` - the function f(x), defined over 0 x L (no - checking is done to insure this) - - - ``n`` - an integer n=0 + raise ValueError('substituting the piecewise variable must result in real number') - - ``L`` - (the period)/2 - - - OUTPUT: - `a_n = \frac{2}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` such - that - - .. math:: - - f(x) \sim \frac{a_0}{2} + \sum_{n=1}^\infty a_n\cos(\frac{n\pi x}{L}),\ \ 00) - result = sum([(SR(f)*exp(-s*x)).integral(x,a,b) - for (a,b),f in self.list()]) - forget(s>0) - return result - - def _make_compatible(self, other): - """ - Returns self and other extended to be defined on the same domain as - well as a refinement of their intervals. This is used for adding - and multiplying piecewise functions. + Boolean EXAMPLES:: - sage: R. = QQ[] - sage: f1 = Piecewise([[(0, 2), x]]) - sage: f2 = Piecewise([[(1, 3), x^2]]) - sage: f1._make_compatible(f2) - (Piecewise defined function with 2 parts, [[(0, 2), x], [(2, 3), 0]], - Piecewise defined function with 2 parts, [[(0, 1), 0], [(1, 3), x^2]], - [(0, 1), (1, 2), (2, 3)]) - """ - a1, b1 = self.domain() - a2, b2 = other.domain() - a = min(a1, a2) - b = max(b1, b2) - F = self.extend_by_zero_to(a,b) - G = other.extend_by_zero_to(a,b) - endpts = sorted(set(F.end_points()).union(set(G.end_points()))) - return F, G, zip(endpts, endpts[1:]) - - def __add__(self,other): - """ - Returns the piecewise defined function which is the sum of self and - other. Does not require both domains be the same. - - EXAMPLES:: - - sage: x = PolynomialRing(QQ,'x').gen() - sage: f1 = x^0 - sage: f2 = 1-x - sage: f3 = 2*x - sage: f4 = 10-x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: g1 = x-2 - sage: g2 = x-5 - sage: g = Piecewise([[(0,5),g1],[(5,10),g2]]) - sage: h = f+g - sage: h - Piecewise defined function with 5 parts, [[(0, 1), x - 1], [(1, 2), -1], [(2, 3), 3*x - 2], [(3, 5), 8], [(5, 10), 5]] - - Note that in this case the functions must be defined using - polynomial expressions *not* using the lambda notation. + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: piecewise.in_operands(f) + True + sage: piecewise.in_operands(1+sin(f)) + True + sage: piecewise.in_operands(1+sin(0*f)) + False """ - F, G, intervals = self._make_compatible(other) - fcn = [] - for a,b in intervals: - fcn.append([(a,b), F.which_function(b)+G.which_function(b)]) - return Piecewise(fcn) + def is_piecewise(ex): + result = ex.operator() is piecewise + for op in ex.operands(): + result = result or is_piecewise(op) + return result + return is_piecewise(ex) - def __mul__(self,other): - r""" - Returns the piecewise defined function which is the product of one - piecewise function (self) with another one (other). - EXAMPLES:: - - sage: x = PolynomialRing(QQ,'x').gen() - sage: f1 = x^0 - sage: f2 = 1-x - sage: f3 = 2*x - sage: f4 = 10-x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: g1 = x-2 - sage: g2 = x-5 - sage: g = Piecewise([[(0,5),g1],[(5,10),g2]]) - sage: h = f*g - sage: h - Piecewise defined function with 5 parts, [[(0, 1), x - 2], [(1, 2), -x^2 + 3*x - 2], [(2, 3), 2*x^2 - 4*x], [(3, 5), -x^2 + 12*x - 20], [(5, 10), -x^2 + 15*x - 50]] - sage: g*(11/2) - Piecewise defined function with 2 parts, [[(0, 5), 11/2*x - 11], [(5, 10), 11/2*x - 55/2]] - - Note that in this method the functions must be defined using - polynomial expressions *not* using the lambda notation. + @staticmethod + def simplify(ex): """ - ## needed for scalar multiplication - if isinstance(other,Rational) or isinstance(other,Integer): - return Piecewise([[(a,b), other*f] for (a,b),f in self.list()]) - else: - F, G, intervals = self._make_compatible(other) - fcn = [] - for a,b in intervals: - fcn.append([(a,b),F.which_function(b)*G.which_function(b)]) - return Piecewise(fcn) + Combine piecewise operands into single piecewise function - __rmul__ = __mul__ + OUTPUT: - def __eq__(self,other): - """ - Implements Boolean == operator. + A piecewise function whose operands are not piecewiese if + possible, that is, as long as the piecewise variable is the same. EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]) + sage: piecewise.simplify(f) + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + + class EvaluationMethods: + + def expression_at(cls, self, parameters, variable, point): + """ + Return the expression defining the piecewise function at + ``value`` + + INPUT: + + - ``point`` -- a real number. + + OUTPUT: + + The symbolic expression defining the function value at the + given ``point``. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f.expression_at(0) + sin(x) + sage: f.expression_at(1) + cos(x) + sage: f.expression_at(2) + Traceback (most recent call last): + ... + ValueError: point is not in the domain + """ + for domain, func in parameters: + if domain.contains(point): + return func + raise ValueError('point is not in the domain') + + which_function = expression_at + + def domains(cls, self, parameters, variable): + """ + Return the individual domains + + See also :meth:`~expressions`. + + OUTPUT: + + The collection of domains of the component functions as a + tuple of :class:`~sage.sets.real_set.RealSet`. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f.domains() + ({0}, (0, 2)) + """ + return tuple(dom for dom, fun in parameters) + + def domain(cls, self, parameters, variable): + """ + Return the domain + + OUTPUT: + + The union of the domains of the individual pieces as a + :class:`~sage.sets.real_set.RealSet`. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f.domain() + [0, 2) + """ + intervals = [] + for domain, func in parameters: + intervals += list(domain) + return RealSet(*intervals) + + def __len__(cls, self, parameters, variable): + """ + Return the number of "pieces" + + OUTPUT: + + Integer. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: len(f) + 2 + """ + return len(parameters) + + def expressions(cls, self, parameters, variable): + """ + Return the individual domains + + See also :meth:`~domains`. + + OUTPUT: + + The collection of expressions of the component functions. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f.expressions() + (sin(x), cos(x)) + """ + return tuple(fun for dom, fun in parameters) + + def items(cls, self, parameters, variable): + """ + Iterate over the pieces of the piecewise function + + .. NOTE:: + + You should probably use :meth:`pieces` instead, which + offers a nicer interface. + + OUTPUT: + + This method iterates over pieces of the piecewise + function, each represented by a pair. The first element is + the support, and the second the function over that + support. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]) + sage: for support, function in f.items(): + ....: print('support is {0}, function is {1}'.format(support, function)) + support is {0}, function is sin(x) + support is (0, 2), function is cos(x) + """ + for pair in parameters: + yield pair + + def __call__(cls, self, parameters, variable, value=None, **kwds): + """ + Call the piecewise function + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f(0) + 0 + sage: f(1) + cos(1) + sage: f(2) + Traceback (most recent call last): + ... + ValueError: point 2 is not in the domain + """ + self = piecewise(parameters, var=variable) + substitution = dict() + for k, v in kwds.items(): + substitution[SR.var(k)] = v + if value is not None: + substitution[variable] = value + return self.subs(substitution) + + def _fast_float_(cls, self, *args): + """ + Do not support the old ``fast_float`` + + OUTPUT: + + This method raises ``NotImplementedError`` so that + plotting uses the newer `fast_callable` implementation. + + EXAMPLES:: + + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]) + sage: f._fast_float_() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def _fast_callable_(cls, self, parameters, variable, etb): + """ + Override the ``fast_callable`` + + OUTPUT: + + A :class:`~sage.ext.fast_callable.ExpressionCall` + representing the piecewise function in the expression + tree. + + EXAMPLES:: + + sage: p = piecewise([((-1, 0), -x), ([0, 1], x)], var=x) + sage: from sage.ext.fast_callable import ExpressionTreeBuilder + sage: etb = ExpressionTreeBuilder(vars=['x']) + sage: p._fast_callable_(etb) + {piecewise(x|-->-x on (-1, 0), x|-->x on [0, 1]; x)}(v_0) + """ + # print 'ev_fast_cal', parameters, variable, etb + self = piecewise(parameters, var=variable) + return etb.call(self, variable) + + def restriction(cls, self, parameters, variable, restricted_domain): + """ + Restrict the domain + + INPUT: + + - ``restricted_domain`` -- a + :class:`~sage.sets.real_set.RealSet` or something that + defines one. + + OUTPUT: + + A new piecewise function obtained by restricting the domain. + + EXAMPLES:: + + sage: f = piecewise([((-oo, oo), x)]); f + piecewise(x|-->x on (-oo, +oo); x) + sage: f.restriction([[-1,1], [3,3]]) + piecewise(x|-->x on [-1, 1] + {3}; x) + """ + restricted_domain = RealSet(*restricted_domain) + new_param = [] + for domain, func in parameters: + domain = domain.intersection(restricted_domain) + new_param.append((domain, func)) + return piecewise(new_param, var=variable) + + def extension(cls, self, parameters, variable, extension, extension_domain=None): + """ + Extend the function + + INPUT: + + - ``extension`` -- a symbolic expression + + - ``extension_domain`` -- a + :class:`~sage.sets.real_set.RealSet` or ``None`` + (default). The domain of the extension. By default, the + entire complement of the current domain. + + EXAMPLES:: + + sage: f = piecewise([((-1,1), x)]); f + piecewise(x|-->x on (-1, 1); x) + sage: f(3) + Traceback (most recent call last): + ... + ValueError: point 3 is not in the domain + + sage: g = f.extension(0); g + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + sage: g(3) + 0 + + sage: h = f.extension(1, RealSet.unbounded_above_closed(1)); h + piecewise(x|-->x on (-1, 1), x|-->1 on [1, +oo); x) + sage: h(3) + 1 + """ + self = piecewise(parameters, var=variable) + if extension_domain is None: + extension_domain = self.domain().complement() + ext = ((extension_domain, SR(extension)),) + return piecewise(parameters + ext, var=variable) + + def unextend_zero(cls, self, parameters, variable): + """ + Remove zero pieces. + + EXAMPLES:: + + sage: f = piecewise([((-1,1), x)]); f + piecewise(x|-->x on (-1, 1); x) + sage: g = f.extension(0); g + piecewise(x|-->x on (-1, 1), x|-->0 on (-oo, -1] + [1, +oo); x) + sage: g(3) + 0 + sage: h = g.unextend_zero() + sage: bool(h == f) + True + """ + result = [(domain, func) for domain,func in parameters if func<>0] + return piecewise(result, var=variable) + + def pieces(cls, self, parameters, variable): + """ + Return the "pieces". + + OUTPUT: + + A tuple of piecewise functions, each having only a single + expression. + + EXAMPLES:: + + sage: p = piecewise([((-1, 0), -x), ([0, 1], x)], var=x) + sage: p.pieces() + (piecewise(x|-->-x on (-1, 0); x), + piecewise(x|-->x on [0, 1]; x)) + """ + result = [] + for domain, func in parameters: + result.append(piecewise([(domain, func)], var=variable)) + return tuple(result) + + def end_points(cls, self, parameters, variable): + """ + Return a list of all interval endpoints for this function. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = x^2-5 + sage: f = piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) + sage: f.end_points() + [0, 1, 2, 3] + sage: f = piecewise([([0,0], sin(x)), ((0,2), cos(x))]); f + piecewise(x|-->sin(x) on {0}, x|-->cos(x) on (0, 2); x) + sage: f.end_points() + [0, 2] + """ + s = set() + for domain, func in parameters: + for interval in domain: + s.add(interval.lower()) + s.add(interval.upper()) + s.discard(minus_infinity) + s.discard(infinity) + return sorted(s) + + def piecewise_add(cls, self, parameters, variable, other): + """ + Return a new piecewise function with domain the union + of the original domains and functions summed. Undefined + intervals in the union domain get function value `0`. + + EXAMPLES:: + + sage: f = piecewise([([0,1], 1), ((2,3), x)]) + sage: g = piecewise([((1/2, 2), x)]) + sage: f.piecewise_add(g).unextend_zero() + piecewise(x|-->1 on (0, 1/2], x|-->x + 1 on (1/2, 1], x|-->x on (1, 2) + (2, 3); x) + """ + points = ([minus_infinity] + + sorted(set(self.end_points() + other.end_points())) + + [infinity]) + domain = [] + funcs = [] + contains_lower = False + contains_upper = False + for i in range(len(points)-1): + try: + contains_lower = (self.domain().contains(points[i]) or + other.domain().contains(points[i])) and not contains_upper + contains_upper = (self.domain().contains(points[i+1]) or + other.domain().contains(points[i+1])) + if contains_lower: + if contains_upper: + rs = RealSet.closed(points[i],points[i+1]) + else: + rs = RealSet.closed_open(points[i],points[i+1]) + else: + if contains_upper: + rs = RealSet.open_closed(points[i],points[i+1]) + else: + rs = RealSet.open(points[i],points[i+1]) + point = (points[i+1] + points[i])/2 + except ValueError: + if points[i] == minus_infinity and points[i+1] == infinity: + rs = RealSet.open(minus_infinity, infinity) + point = 0 + elif points[i] == minus_infinity: + if contains_lower: + rs = RealSet.unbounded_below_closed(points[i+1]) + else: + rs = RealSet.unbounded_below_open(points[i+1]) + point = points[i+1]-1 + elif points[i+1] == infinity: + if contains_upper: + rs = RealSet.unbounded_above_closed(points[i]) + else: + rs = RealSet.unbounded_above_open(points[i]) + point = points[i]+1 + else: + raise + try: + ex1 = self.expression_at(point) + except ValueError: + ex1 = 0 + try: + ex2 = other.expression_at(point) + except ValueError: + ex2 = 0 + ex = ex1 + ex2 + if i>0 and funcs[-1] == ex: + # extend the previous domain + rs += domain[-1] + domain[-1] = rs + else: + domain += rs + funcs.append(ex) + return piecewise(zip(domain, funcs)) + + def integral(cls, self, parameters, variable, x=None, a=None, b=None, definite=False): + r""" + By default, return the indefinite integral of the function. + If definite=True is given, returns the definite integral. + + AUTHOR: + + - Paul Butler + + EXAMPLES:: + + sage: f1(x) = 1-x + sage: f = piecewise([((0,1),1), ((1,2),f1)]) + sage: f.integral(definite=True) + 1/2 + + :: + + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = piecewise([((0,pi/2),f1), ((pi/2,pi),f2)]) + sage: f.integral(definite=True) + 1/2*pi + + sage: f1(x) = 2 + sage: f2(x) = 3 - x + sage: f = piecewise([[(-2, 0), f1], [(0, 3), f2]]) + sage: f.integral() + piecewise(x|-->2*x + 4 on (-2, 0), x|-->-1/2*x^2 + 3*x + 4 on (0, 3); x) + + sage: f1(y) = -1 + sage: f2(y) = y + 3 + sage: f3(y) = -y - 1 + sage: f4(y) = y^2 - 1 + sage: f5(y) = 3 + sage: f = piecewise([[[-4,-3],f1],[(-3,-2),f2],[[-2,0],f3],[(0,2),f4],[[2,3],f5]]) + sage: F = f.integral(y) + sage: F + piecewise(y|-->-y - 4 on [-4, -3], y|-->1/2*y^2 + 3*y + 7/2 on (-3, -2), y|-->-1/2*y^2 - y - 1/2 on [-2, 0], y|-->1/3*y^3 - y - 1/2 on (0, 2), y|-->3*y - 35/6 on [2, 3]; y) + + Ensure results are consistent with FTC:: + + sage: F(-3) - F(-4) + -1 + sage: F(-1) - F(-3) + 1 + sage: F(2) - F(0) + 2/3 + sage: f.integral(y, 0, 2) + 2/3 + sage: F(3) - F(-4) + 19/6 + sage: f.integral(y, -4, 3) + 19/6 + sage: f.integral(definite=True) + 19/6 + + :: + + sage: f1(y) = (y+3)^2 + sage: f2(y) = y+3 + sage: f3(y) = 3 + sage: f = piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]]) + sage: f.integral() + piecewise(y|-->1/3*y^3 + 3*y^2 + 9*y + 9 on (-oo, -3), y|-->1/2*y^2 + 3*y + 9/2 on (-3, 0), y|-->3*y + 9/2 on (0, +oo); y) + + :: + + sage: f1(x) = e^(-abs(x)) + sage: f = piecewise([[(-infinity, infinity), f1]]) + sage: f.integral(definite=True) + 2 + sage: f.integral() + piecewise(x|-->-1/2*((sgn(x) - 1)*e^(2*x) - 2*e^x*sgn(x) + sgn(x) + 1)*e^(-x) - 1 on (-oo, +oo); x) + + :: + + sage: f = piecewise([((0, 5), cos(x))]) + sage: f.integral() + piecewise(x|-->sin(x) on (0, 5); x) + + + TESTS: + + Verify that piecewise integrals of zero work (trac #10841):: + + sage: f0(x) = 0 + sage: f = piecewise([[[0,1],f0]]) + sage: f.integral(x,0,1) + 0 + sage: f = piecewise([[[0,1], 0]]) + sage: f.integral(x,0,1) + 0 + sage: f = piecewise([[[0,1], SR(0)]]) + sage: f.integral(x,0,1) + 0 + + """ + if a != None and b != None: + F = self.integral(x) + return F(b) - F(a) + + if a != None or b != None: + raise TypeError('only one endpoint given') + + area = 0 + new_pieces = [] + + if x == None: + x = self.default_variable() + + # The integral is computed by iterating over the pieces in order. + # The definite integral for each piece is calculated and accumulated in `area`. + # The indefinite integral of each piece is also calculated, + # and the `area` before each piece is added to the piece. + # + # If a definite integral is requested, `area` is returned. + # Otherwise, a piecewise function is constructed from the indefinite integrals + # and returned. + # + # An exception is made if integral is called on a piecewise function + # that starts at -infinity. In this case, we do not try to calculate the + # definite integral of the first piece, and the value of `area` remains 0 + # after the first piece. + + from sage.symbolic.assumptions import assume, forget + for domain, fun in parameters: + for interval in domain: + start = interval.lower() + end = interval.upper() + if start == -infinity and not definite: + fun_integrated = fun.integral(x, end, x) + else: + try: + assume(start < x) + except ValueError: # Assumption is redundant + pass + fun_integrated = fun.integral(x, start, x) + area + forget(start < x) + if definite or end != infinity: + area += fun.integral(x, start, end) + new_pieces.append([interval, SR(fun_integrated).function(x)]) + + if definite: + return SR(area) + else: + return piecewise(new_pieces) + + def critical_points(cls, self, parameters, variable): + """ + Return the critical points of this piecewise function. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^0 + sage: f2 = 10*x - x^2 + sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x + sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: expected = [5, 12, 13, 14] + sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) + True + + TESTS: + + Use variables other than x (:trac:`13836`):: + + sage: R. = QQ[] + sage: f1 = y^0 + sage: f2 = 10*y - y^2 + sage: f3 = 3*y^4 - 156*y^3 + 3036*y^2 - 26208*y + sage: f = piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: expected = [5, 12, 13, 14] + sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) + True + """ + from sage.calculus.calculus import maxima + x = QQ[self.default_variable()].gen() + crit_pts = [] + for domain, f in parameters: + for interval in domain: + a = interval.lower() + b = interval.upper() + for root in maxima.allroots(SR(f).diff(x)==0): + root = float(root.rhs()) + if a < root < b: + crit_pts.append(root) + return crit_pts + + def convolution(cls, self, parameters, variable, other): + """ + Return the convolution function, + `f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly + supported `f,g`. + + EXAMPLES:: + + sage: x = PolynomialRing(QQ,'x').gen() + sage: f = piecewise([[[0,1],1]]) ## example 0 + sage: g = f.convolution(f); g + piecewise(x|-->x on (0, 1], x|-->-x + 2 on (1, 2]; x) + sage: h = f.convolution(g); h + piecewise(x|-->1/2*x^2 on (0, 1], x|-->-x^2 + 3*x - 3/2 on (1, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) + sage: f = piecewise([[(0,1),1],[(1,2),2],[(2,3),1]]) ## example 1 + sage: g = f.convolution(f) + sage: h = f.convolution(g); h + piecewise(x|-->1/2*x^2 on (0, 1], x|-->2*x^2 - 3*x + 3/2 on (1, 3], x|-->-2*x^2 + 21*x - 69/2 on (3, 4], x|-->-5*x^2 + 45*x - 165/2 on (4, 5], x|-->-2*x^2 + 15*x - 15/2 on (5, 6], x|-->2*x^2 - 33*x + 273/2 on (6, 8], x|-->1/2*x^2 - 9*x + 81/2 on (8, 9]; x) + sage: f = piecewise([[(-1,1),1]]) ## example 2 + sage: g = piecewise([[(0,3),x]]) + sage: f.convolution(g) + piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 1], x|-->2*x on (1, 2], x|-->-1/2*x^2 + x + 4 on (2, 4]; x) + sage: g = piecewise([[(0,3),1],[(3,4),2]]) + sage: f.convolution(g) + piecewise(x|-->x + 1 on (-1, 1], x|-->2 on (1, 2], x|-->x on (2, 3], x|-->-x + 6 on (3, 4], x|-->-2*x + 10 on (4, 5]; x) + + Check that the bugs raised in :trac:`12123` are fixed:: + + sage: f = piecewise([[(-2, 2), 2]]) + sage: g = piecewise([[(0, 2), 3/4]]) + sage: f.convolution(g) + piecewise(x|-->3/2*x + 3 on (-2, 0], x|-->3 on (0, 2], x|-->-3/2*x + 6 on (2, 4]; x) + sage: f = piecewise([[(-1, 1), 1]]) + sage: g = piecewise([[(0, 1), x], [(1, 2), -x + 2]]) + sage: f.convolution(g) + piecewise(x|-->1/2*x^2 + x + 1/2 on (-1, 0], x|-->-1/2*x^2 + x + 1/2 on (0, 2], x|-->1/2*x^2 - 3*x + 9/2 on (2, 3]; x) + """ + from sage.symbolic.integration.integral import definite_integral + f = self + g = other + if len(f.end_points())*len(g.end_points()) == 0: + raise ValueError('one of the piecewise functions is nowhere defined') + M = min(min(f.end_points()),min(g.end_points())) + N = max(max(f.end_points()),max(g.end_points())) + tt = SR.var('tt') + uu = SR.var('uu') + conv = 0 + fd,f0 = parameters[0] + gd,g0 = next(other.items()) + if len(f)==1 and len(g)==1: + f = f.unextend_zero() + g = g.unextend_zero() + a1 = fd[0].lower() + a2 = fd[0].upper() + b1 = gd[0].lower() + b2 = gd[0].upper() + i1 = f0.subs({variable: uu}) + i2 = g0.subs({variable: tt-uu}) + fg1 = definite_integral(i1*i2, uu, a1, tt-b1).subs(tt = variable) + fg2 = definite_integral(i1*i2, uu, tt-b2, tt-b1).subs(tt = variable) + fg3 = definite_integral(i1*i2, uu, tt-b2, a2).subs(tt = variable) + fg4 = definite_integral(i1*i2, uu, a1, a2).subs(tt = variable) + if a1-b11 or len(g)>1: + z = piecewise([[(0,0),0]]) + for fpiece in f.pieces(): + for gpiece in g.pieces(): + h = gpiece.convolution(fpiece) + z = z.piecewise_add(h) + return z.unextend_zero() + + def trapezoid(cls, self, parameters, variable, N): + """ + Return the piecewise line function defined by the trapezoid rule + for numerical integration based on a subdivision of each domain + interval into N subintervals. + + EXAMPLES:: + + sage: f = piecewise([[[0,1], x^2], [RealSet.open_closed(1,2), 5-x^2]]) + sage: f.trapezoid(2) + piecewise(x|-->1/2*x on (0, 1/2), x|-->3/2*x - 1/2 on (1/2, 1), x|-->7/2*x - 5/2 on (1, 3/2), x|-->-7/2*x + 8 on (3/2, 2); x) + sage: f = piecewise([[[-1,1], 1-x^2]]) + sage: f.trapezoid(4).integral(definite=True) + 5/4 + sage: f = piecewise([[[-1,1], 1/2+x-x^3]]) ## example 3 + sage: f.trapezoid(6).integral(definite=True) + 1 + + TESTS: + + Use variables or rings other than x (:trac:`13836`):: + + sage: R. = QQ[] + sage: f1 = y^2 + sage: f2 = 5-y^2 + sage: f = piecewise([[[0,1],f1], [RealSet.open_closed(1,2),f2]]) + sage: f.trapezoid(2) + piecewise(y|-->1/2*y on (0, 1/2), y|-->3/2*y - 1/2 on (1/2, 1), y|-->7/2*y - 5/2 on (1, 3/2), y|-->-7/2*y + 8 on (3/2, 2); y) + """ + x = QQ[self.default_variable()].gen() + def func(x0, x1): + f0, f1 = self(x0), self(x1) + return [[(x0,x1),f0+(f1-f0)*(x1-x0)**(-1)*(x-x0)]] + rsum = [] + for domain, f in parameters: + for interval in domain: + a = interval.lower() + b = interval.upper() + h = (b-a)/N + for i in range(N): + x0 = a+i*h + x1 = a+(i+1)*h + rsum += func(x0, x1) + return piecewise(rsum) + + def laplace(cls, self, parameters, variable, x='x', s='t'): + r""" + Returns the Laplace transform of self with respect to the variable + var. + + INPUT: + + - ``x`` - variable of self + + - ``s`` - variable of Laplace transform. + + We assume that a piecewise function is 0 outside of its domain and + that the left-most endpoint of the domain is 0. + + EXAMPLES:: + + sage: x, s, w = var('x, s, w') + sage: f = piecewise([[(0,1),1],[[1,2], 1-x]]) + sage: f.laplace(x, s) + -e^(-s)/s + (s + 1)*e^(-2*s)/s^2 + 1/s - e^(-s)/s^2 + sage: f.laplace(x, w) + -e^(-w)/w + (w + 1)*e^(-2*w)/w^2 + 1/w - e^(-w)/w^2 + + :: + + sage: y, t = var('y, t') + sage: f = piecewise([[[1,2], 1-y]]) + sage: f.laplace(y, t) + (t + 1)*e^(-2*t)/t^2 - e^(-t)/t^2 + + :: + + sage: s = var('s') + sage: t = var('t') + sage: f1(t) = -t + sage: f2(t) = 2 + sage: f = piecewise([[[0,1],f1],[(1,infinity),f2]]) + sage: f.laplace(t,s) + (s + 1)*e^(-s)/s^2 + 2*e^(-s)/s - 1/s^2 + """ + from sage.all import assume, exp, forget + x = SR.var(x) + s = SR.var(s) + assume(s>0) + result = 0 + for domain, f in parameters: + for interval in domain: + a = interval.lower() + b = interval.upper() + result += (SR(f)*exp(-s*x)).integral(x,a,b) + forget(s>0) + return result + + def fourier_series_cosine_coefficient(cls, self, parameters, variable, n, L): + r""" + Returns the n-th Fourier series coefficient of + `\cos(n\pi x/L)`, `a_n`. + + INPUT: + + + - ``self`` - the function f(x), defined over -L x L + + - ``n`` - an integer n=0 + + - ``L`` - (the period)/2 + + + OUTPUT: + `a_n = \frac{1}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` + + EXAMPLES:: + + sage: f(x) = x^2 + sage: f = piecewise([[(-1,1),f]]) + sage: f.fourier_series_cosine_coefficient(2,1) + pi^(-2) + sage: f(x) = x^2 + sage: f = piecewise([[(-pi,pi),f]]) + sage: f.fourier_series_cosine_coefficient(2,pi) + 1 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_cosine_coefficient(5,pi) + -3/5/pi + """ + from sage.all import cos, pi + x = SR.var('x') + result = 0 + for domain, f in parameters: + for interval in domain: + a = interval.lower() + b = interval.upper() + result += (f*cos(pi*x*n/L)/L).integrate(x, a, b) + return SR(result).simplify_trig() + + def fourier_series_sine_coefficient(cls, self, parameters, variable, n, L): + r""" + Returns the n-th Fourier series coefficient of + `\sin(n\pi x/L)`, `b_n`. + + INPUT: + + + - ``self`` - the function f(x), defined over -L x L + + - ``n`` - an integer n0 + + - ``L`` - (the period)/2 + + + OUTPUT: + `b_n = \frac{1}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx` + + EXAMPLES:: + + sage: f(x) = x^2 + sage: f = piecewise([[(-1,1),f]]) + sage: f.fourier_series_sine_coefficient(2,1) # L=1, n=2 + 0 + """ + from sage.all import sin, pi + x = SR.var('x') + result = 0 + for domain, f in parameters: + for interval in domain: + a = interval.lower() + b = interval.upper() + result += (f*sin(pi*x*n/L)/L).integrate(x, a, b) + return SR(result).simplify_trig() + + def fourier_series_partial_sum(cls, self, parameters, variable, N, L): + r""" + Returns the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + as a string. + + EXAMPLE:: + + sage: f(x) = x^2 + sage: f = piecewise([[(-1,1),f]]) + sage: f.fourier_series_partial_sum(3,1) + cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_partial_sum(3,pi) + -3*cos(x)/pi - 3*sin(2*x)/pi + 3*sin(x)/pi - 1/4 + """ + from sage.all import pi, sin, cos, srange + x = self.default_variable() + a0 = self.fourier_series_cosine_coefficient(0,L) + result = a0/2 + sum([(self.fourier_series_cosine_coefficient(n,L)*cos(n*pi*x/L) + + self.fourier_series_sine_coefficient(n,L)*sin(n*pi*x/L)) + for n in srange(1,N)]) + return SR(result).expand() - sage: f1 = x^0 - sage: f2 = 1-x - sage: f3 = 2*x - sage: f4 = 10-x - sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: g = Piecewise([[(0,1),1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) - sage: f==g - True - """ - return self.list()==other.list() +piecewise = PiecewiseFunction() diff --git a/src/sage/functions/piecewise_old.py b/src/sage/functions/piecewise_old.py new file mode 100644 index 00000000000..166b2a32b87 --- /dev/null +++ b/src/sage/functions/piecewise_old.py @@ -0,0 +1,1853 @@ +r""" +Piecewise-defined Functions + +Sage implements a very simple class of piecewise-defined functions. +Functions may be any type of symbolic expression. Infinite +intervals are not supported. The endpoints of each interval must +line up. + +TODO: + +- Implement max/min location and values, + +- Need: parent object - ring of piecewise functions + +- This class should derive from an element-type class, and should + define ``_add_``, ``_mul_``, etc. That will automatically take care + of left multiplication and proper coercion. The coercion mentioned + below for scalar mult on right is bad, since it only allows ints and + rationals. The right way is to use an element class and only define + ``_mul_``, and have a parent, so anything gets coerced properly. + +AUTHORS: + +- David Joyner (2006-04): initial version + +- David Joyner (2006-09): added __eq__, extend_by_zero_to, unextend, + convolution, trapezoid, trapezoid_integral_approximation, + riemann_sum, riemann_sum_integral_approximation, tangent_line fixed + bugs in __mul__, __add__ + +- David Joyner (2007-03): adding Hann filter for FS, added general FS + filter methods for computing and plotting, added options to plotting + of FS (eg, specifying rgb values are now allowed). Fixed bug in + documentation reported by Pablo De Napoli. + +- David Joyner (2007-09): bug fixes due to behaviour of + SymbolicArithmetic + +- David Joyner (2008-04): fixed docstring bugs reported by J Morrow; added + support for Laplace transform of functions with infinite support. + +- David Joyner (2008-07): fixed a left multiplication bug reported by + C. Boncelet (by defining __rmul__ = __mul__). + +- Paul Butler (2009-01): added indefinite integration and default_variable + +TESTS:: + + sage: R. = QQ[] + sage: f = Piecewise([[(0,1),1*x^0]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: 2*f + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 1 parts, [[(0, 1), 2]] +""" + +#***************************************************************************** +# Copyright (C) 2006 William Stein +# 2006 David Joyner +# +# 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/ +#***************************************************************************** + +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.misc.sage_eval import sage_eval +from sage.rings.all import QQ, RR, Integer, Rational, infinity +from sage.calculus.functional import derivative +from sage.symbolic.expression import is_Expression +from sage.symbolic.assumptions import assume, forget + +from sage.calculus.calculus import SR, maxima +from sage.calculus.all import var + +def Piecewise(list_of_pairs, var=None): + """ + Deprecated spelling of :func:`sage.functions.piecewise`. + + Return a piecewise function from a list of (interval, function) + pairs. + + ``list_of_pairs`` is a list of pairs (I, fcn), where + fcn is a Sage function (such as a polynomial over RR, or functions + using the lambda notation), and I is an interval such as I = (1,3). + Two consecutive intervals must share a common endpoint. + + If the optional ``var`` is specified, then any symbolic expressions + in the list will be converted to symbolic functions using + ``fcn.function(var)``. (This says which variable is considered to + be "piecewise".) + + We assume that these definitions are consistent (ie, no checking is + done). + + EXAMPLES:: + + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f(1) + -1 + sage: f(3) + 2 + sage: f = Piecewise([[(0,1),x], [(1,2),x^2]], x); f + Piecewise defined function with 2 parts, [[(0, 1), x |--> x], [(1, 2), x |--> x^2]] + sage: f(0.9) + 0.900000000000000 + sage: f(1.1) + 1.21000000000000 + """ + from sage.misc.superseded import deprecation + deprecation(14801, 'use lower-case piecewise instead') + return PiecewisePolynomial(list_of_pairs, var=var) + +class PiecewisePolynomial: + """ + Returns a piecewise function from a list of (interval, function) + pairs. + + EXAMPLES:: + + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f(1) + -1 + sage: f(3) + 2 + """ + def __init__(self, list_of_pairs, var=None): + r""" + ``list_of_pairs`` is a list of pairs (I, fcn), where + fcn is a Sage function (such as a polynomial over RR, or functions + using the lambda notation), and I is an interval such as I = (1,3). + Two consecutive intervals must share a common endpoint. + + If the optional ``var`` is specified, then any symbolic + expressions in the list will be converted to symbolic + functions using ``fcn.function(var)``. (This says which + variable is considered to be "piecewise".) + + We assume that these definitions are consistent (ie, no checking is + done). + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.list() + [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] + sage: f.length() + 2 + """ + self._length = len(list_of_pairs) + self._intervals = [x[0] for x in list_of_pairs] + functions = [x[1] for x in list_of_pairs] + if var is not None: + for i in range(len(functions)): + if is_Expression(functions[i]): + functions[i] = functions[i].function(var) + self._functions = functions + # We regenerate self._list in case self._functions was modified + # above. This also protects us in case somebody mutates a list + # after they use it as an argument to piecewise(). + self._list = [[self._intervals[i], self._functions[i]] for i in range(self._length)] + + def list(self): + """ + Returns the pieces of this function as a list of functions. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.list() + [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] + """ + return self._list + + def length(self): + """ + Returns the number of pieces of this function. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.length() + 2 + """ + return self._length + + def __repr__(self): + """ + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]); f + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 2 parts, [[(0, 1), x |--> 1], [(1, 2), x |--> -x + 1]] + """ + return 'Piecewise defined function with %s parts, %s'%( + self.length(),self.list()) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: latex(f) + \begin{cases} + x \ {\mapsto}\ 1 &\text{on $(0, 1)$}\cr + x \ {\mapsto}\ -x + 1 &\text{on $(1, 2)$}\cr + \end{cases} + + :: + + sage: f(x) = sin(x*pi/2) + sage: g(x) = 1-(x-1)^2 + sage: h(x) = -x + sage: P = Piecewise([[(0,1), f], [(1,3),g], [(3,5), h]]) + sage: latex(P) + \begin{cases} + x \ {\mapsto}\ \sin\left(\frac{1}{2} \, \pi x\right) &\text{on $(0, 1)$}\cr + x \ {\mapsto}\ -{\left(x - 1\right)}^{2} + 1 &\text{on $(1, 3)$}\cr + x \ {\mapsto}\ -x &\text{on $(3, 5)$}\cr + \end{cases} + """ + from sage.misc.latex import latex + tex = ['\\begin{cases}\n'] + for (left, right), f in self.list(): + tex.append('%s &\\text{on $(%s, %s)$}\\cr\n' % (latex(f), left, right)) + tex.append(r'\end{cases}') + return ''.join(tex) + + def intervals(self): + """ + A piecewise non-polynomial example. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = exp(x) + sage: f4(x) = sin(2*x) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.intervals() + [(0, 1), (1, 2), (2, 3), (3, 10)] + """ + return self._intervals + + def domain(self): + """ + Returns the domain of the function. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = exp(x) + sage: f4(x) = sin(2*x) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.domain() + (0, 10) + """ + endpoints = sum(self.intervals(), ()) + return (min(endpoints), max(endpoints)) + + def functions(self): + """ + Returns the list of functions (the "pieces"). + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = exp(x) + sage: f4(x) = sin(2*x) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.functions() + [x |--> 1, x |--> -x + 1, x |--> e^x, x |--> sin(2*x)] + """ + return self._functions + + def extend_by_zero_to(self,xmin=-1000,xmax=1000): + """ + This function simply returns the piecewise defined function which + is extended by 0 so it is defined on all of (xmin,xmax). This is + needed to add two piecewise functions in a reasonable way. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1 - x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.extend_by_zero_to(-1, 3) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 4 parts, [[(-1, 0), 0], [(0, 1), x |--> 1], [(1, 2), x |--> -x + 1], [(2, 3), 0]] + """ + zero = QQ['x'](0) + list_of_pairs = self.list() + a, b = self.domain() + if xmin < a: + list_of_pairs = [[(xmin, a), zero]] + list_of_pairs + if xmax > b: + list_of_pairs = list_of_pairs + [[(b, xmax), zero]] + return Piecewise(list_of_pairs) + + def unextend(self): + """ + This removes any parts in the front or back of the function which + is zero (the inverse to extend_by_zero_to). + + EXAMPLES:: + + sage: R. = QQ[] + sage: f = Piecewise([[(-3,-1),1+2+x],[(-1,1),1-x^2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: e = f.extend_by_zero_to(-10,10); e + Piecewise defined function with 4 parts, [[(-10, -3), 0], [(-3, -1), x + 3], [(-1, 1), -x^2 + 1], [(1, 10), 0]] + sage: d = e.unextend(); d + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 2 parts, [[(-3, -1), x + 3], [(-1, 1), -x^2 + 1]] + sage: d==f + True + """ + list_of_pairs = self.list() + funcs = self.functions() + if funcs[0] == 0: + list_of_pairs = list_of_pairs[1:] + if funcs[-1] == 0: + list_of_pairs = list_of_pairs[:-1] + return Piecewise(list_of_pairs) + + def _riemann_sum_helper(self, N, func, initial=0): + """ + A helper function for computing Riemann sums. + + INPUT: + + + - ``N`` - the number of subdivisions + + - ``func`` - a function to apply to the endpoints of + each subdivision + + - ``initial`` - the starting value + + + EXAMPLES:: + + sage: f1(x) = x^2 ## example 1 + sage: f2(x) = 5-x^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f._riemann_sum_helper(6, lambda x0, x1: (x1-x0)*f(x1)) + 19/6 + """ + a,b = self.domain() + rsum = initial + h = (b-a)/N + for i in range(N): + x0 = a+i*h + x1 = a+(i+1)*h + rsum += func(x0, x1) + return rsum + + def riemann_sum_integral_approximation(self,N,mode=None): + """ + Returns the piecewise line function defined by the Riemann sums in + numerical integration based on a subdivision into N subintervals. + + Set mode="midpoint" for the height of the rectangles to be + determined by the midpoint of the subinterval; set mode="right" for + the height of the rectangles to be determined by the right-hand + endpoint of the subinterval; the default is mode="left" (the height + of the rectangles to be determined by the left-hand endpoint of + the subinterval). + + EXAMPLES:: + + sage: f1(x) = x^2 ## example 1 + sage: f2(x) = 5-x^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.riemann_sum_integral_approximation(6) + 17/6 + sage: f.riemann_sum_integral_approximation(6,mode="right") + 19/6 + sage: f.riemann_sum_integral_approximation(6,mode="midpoint") + 3 + sage: f.integral(definite=True) + 3 + """ + if mode is None: + return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x0)) + elif mode == "right": + return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self(x1)) + elif mode == "midpoint": + return self._riemann_sum_helper(N, lambda x0, x1: (x1-x0)*self((x0+x1)/2)) + else: + raise ValueError, "invalid mode" + + def riemann_sum(self,N,mode=None): + """ + Returns the piecewise line function defined by the Riemann sums in + numerical integration based on a subdivision into N subintervals. + Set mode="midpoint" for the height of the rectangles to be + determined by the midpoint of the subinterval; set mode="right" for + the height of the rectangles to be determined by the right-hand + endpoint of the subinterval; the default is mode="left" (the height + of the rectangles to be determined by the left-hand endpoint of + the subinterval). + + EXAMPLES:: + + sage: f1(x) = x^2 + sage: f2(x) = 5-x^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.riemann_sum(6,mode="midpoint") + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 6 parts, [[(0, 1/3), 1/36], [(1/3, 2/3), 1/4], [(2/3, 1), 25/36], [(1, 4/3), 131/36], [(4/3, 5/3), 11/4], [(5/3, 2), 59/36]] + + :: + + sage: f = Piecewise([[(-1,1),(1-x^2).function(x)]]) + sage: rsf = f.riemann_sum(7) + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()]) + sage: P + Q + L + Graphics object consisting of 15 graphics primitives + + :: + + sage: f = Piecewise([[(-1,1),(1/2+x-x^3)]], x) ## example 3 + sage: rsf = f.riemann_sum(8) + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: Q = rsf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: L = add([line([[a,0],[a,f(x=a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in rsf.list()]) + sage: P + Q + L + Graphics object consisting of 17 graphics primitives + """ + if mode is None: + rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x0))]], + initial=[]) + elif mode == "right": + rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self(x1))]], + initial=[]) + elif mode == "midpoint": + rsum = self._riemann_sum_helper(N, lambda x0,x1: [[(x0,x1),SR(self((x0+x1)/2))]], + initial=[]) + else: + raise ValueError, "invalid mode" + return Piecewise(rsum) + + def trapezoid(self,N): + """ + Returns the piecewise line function defined by the trapezoid rule + for numerical integration based on a subdivision into N + subintervals. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^2 + sage: f2 = 5-x^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.trapezoid(4) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 4 parts, [[(0, 1/2), 1/2*x], [(1/2, 1), 9/2*x - 2], [(1, 3/2), 1/2*x + 2], [(3/2, 2), -7/2*x + 8]] + + :: + + sage: R. = QQ[] + sage: f = Piecewise([[(-1,1),1-x^2]]) + sage: tf = f.trapezoid(4) + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()]) + sage: P+Q+L + Graphics object consisting of 9 graphics primitives + + :: + + sage: R. = QQ[] + sage: f = Piecewise([[(-1,1),1/2+x-x^3]]) ## example 3 + sage: tf = f.trapezoid(6) + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: L = add([line([[a,0],[a,f(a)]],rgbcolor=(0.7,0.6,0.6)) for (a,b),f in tf.list()]) + sage: P+Q+L + Graphics object consisting of 13 graphics primitives + + TESTS: + + Use variables other than x (:trac:`13836`):: + + sage: R. = QQ[] + sage: f1 = y^2 + sage: f2 = 5-y^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + sage: f.trapezoid(4) + Piecewise defined function with 4 parts, [[(0, 1/2), 1/2*y], [(1/2, 1), 9/2*y - 2], [(1, 3/2), 1/2*y + 2], [(3/2, 2), -7/2*y + 8]] + + """ + x = QQ[self.default_variable()].gen() + def f(x0, x1): + f0, f1 = self(x0), self(x1) + return [[(x0,x1),f0+(f1-f0)*(x1-x0)**(-1)*(x-x0)]] + rsum = self._riemann_sum_helper(N, f, initial=[]) + return Piecewise(rsum) + + def trapezoid_integral_approximation(self,N): + """ + Returns the approximation given by the trapezoid rule for numerical + integration based on a subdivision into N subintervals. + + EXAMPLES:: + + sage: f1(x) = x^2 ## example 1 + sage: f2(x) = 1-(1-x)^2 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: tf = f.trapezoid(6) + sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: ta = f.trapezoid_integral_approximation(6) + sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25)) + sage: a = f.integral(definite=True) + sage: tt = text('area under curve = %s'%a, (1.5, -0.5)) + sage: P + Q + t + tt + Graphics object consisting of 10 graphics primitives + + :: + + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) ## example 2 + sage: tf = f.trapezoid(4) + sage: ta = f.trapezoid_integral_approximation(4) + sage: Q = tf.plot(rgbcolor=(0.7,0.6,0.6), plot_points=40) + sage: t = text('trapezoid approximation = %s'%ta, (1.5, 0.25)) + sage: a = f.integral(definite=True) + sage: tt = text('area under curve = %s'%a, (1.5, -0.5)) + sage: P+Q+t+tt + Graphics object consisting of 8 graphics primitives + """ + def f(x0, x1): + f0, f1 = self(x0), self(x1) + return ((f1+f0)/2)*(x1-x0) + return self._riemann_sum_helper(N, f) + + def critical_points(self): + """ + Return the critical points of this piecewise function. + + .. warning:: + + Uses maxima, which prints the warning to use results with + caution. Only works for piecewise functions whose parts are + polynomials with real critical not occurring on the + interval endpoints. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = x^0 + sage: f2 = 10*x - x^2 + sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x + sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: expected = [5, 12, 13, 14] + sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) + True + + TESTS: + + Use variables other than x (:trac:`13836`):: + + sage: R. = QQ[] + sage: f1 = y^0 + sage: f2 = 10*y - y^2 + sage: f3 = 3*y^4 - 156*y^3 + 3036*y^2 - 26208*y + sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: expected = [5, 12, 13, 14] + sage: all(abs(e-a) < 0.001 for e,a in zip(expected, f.critical_points())) + True + """ + from sage.calculus.calculus import maxima + x = QQ[self.default_variable()].gen() + crit_pts = [] + for (a,b), f in self.list(): + for root in maxima.allroots(SR(f).diff(x)==0): + root = float(root.rhs()) + if a < root < b: + crit_pts.append(root) + return crit_pts + + def base_ring(self): + """ + Returns the base ring of the function pieces. This + is useful when this class is extended. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = x^2-5 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: base_ring(f) + Symbolic Ring + + :: + + sage: R. = QQ[] + sage: f1 = x^0 + sage: f2 = 10*x - x^2 + sage: f3 = 3*x^4 - 156*x^3 + 3036*x^2 - 26208*x + sage: f = Piecewise([[(0,3),f1],[(3,10),f2],[(10,20),f3]]) + sage: f.base_ring() + Rational Field + """ + return (self.functions()[0]).base_ring() + + def end_points(self): + """ + Returns a list of all interval endpoints for this function. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = x^2-5 + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.end_points() + [0, 1, 2, 3] + """ + intervals = self.intervals() + return [ intervals[0][0] ] + [b for a,b in intervals] + + def __call__(self,x0): + """ + Evaluates self at x0. Returns the average value of the jump if x0 + is an interior endpoint of one of the intervals of self and the + usual value otherwise. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = exp(x) + sage: f4(x) = sin(2*x) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f(0.5) + 1 + sage: f(5/2) + e^(5/2) + sage: f(5/2).n() + 12.1824939607035 + sage: f(1) + 1/2 + """ + #x0 = QQ(x0) ## does not allow for evaluation at pi + n = self.length() + endpts = self.end_points() + for i in range(1,n): + if x0 == endpts[i]: + return (self.functions()[i-1](x0) + self.functions()[i](x0))/2 + if x0 == endpts[0]: + return self.functions()[0](x0) + if x0 == endpts[n]: + return self.functions()[n-1](x0) + for i in range(n): + if endpts[i] < x0 < endpts[i+1]: + return self.functions()[i](x0) + raise ValueError,"Value not defined outside of domain." + + def which_function(self,x0): + """ + Returns the function piece used to evaluate self at x0. + + EXAMPLES:: + + sage: f1(z) = z + sage: f2(x) = 1-x + sage: f3(y) = exp(y) + sage: f4(t) = sin(2*t) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.which_function(3/2) + x |--> -x + 1 + """ + for (a,b), f in self.list(): + if a <= x0 <= b: + return f + raise ValueError,"Function not defined outside of domain." + + def default_variable(self): + r""" + Return the default variable. The default variable is defined as the + first variable in the first piece that has a variable. If no pieces have + a variable (each piece is a constant value), `x` is returned. + + The result is cached. + + AUTHOR: Paul Butler + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 5*x + sage: p = Piecewise([[(0,1),f1],[(1,4),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: p.default_variable() + x + + sage: f1 = 3*var('y') + sage: p = Piecewise([[(0,1),4],[(1,4),f1]]) + sage: p.default_variable() + y + + """ + try: + return self.__default_variable + except AttributeError: + pass + for _, fun in self._list: + try: + fun = SR(fun) + if fun.variables(): + v = fun.variables()[0] + self.__default_variable = v + return v + except TypeError: + # pass if fun is lambda function + pass + # default to x + v = var('x') + self.__default_value = v + return v + + def integral(self, x=None, a=None, b=None, definite=False): + r""" + By default, returns the indefinite integral of the function. + If definite=True is given, returns the definite integral. + + AUTHOR: + + - Paul Butler + + EXAMPLES:: + + sage: f1(x) = 1-x + sage: f = Piecewise([[(0,1),1],[(1,2),f1]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.integral(definite=True) + 1/2 + + :: + + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.integral(definite=True) + 1/2*pi + + sage: f1(x) = 2 + sage: f2(x) = 3 - x + sage: f = Piecewise([[(-2, 0), f1], [(0, 3), f2]]) + sage: f.integral() + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 2 parts, [[(-2, 0), x |--> 2*x + 4], [(0, 3), x |--> -1/2*x^2 + 3*x + 4]] + + sage: f1(y) = -1 + sage: f2(y) = y + 3 + sage: f3(y) = -y - 1 + sage: f4(y) = y^2 - 1 + sage: f5(y) = 3 + sage: f = Piecewise([[(-4,-3),f1],[(-3,-2),f2],[(-2,0),f3],[(0,2),f4],[(2,3),f5]]) + sage: F = f.integral(y) + sage: F + Piecewise defined function with 5 parts, [[(-4, -3), y |--> -y - 4], [(-3, -2), y |--> 1/2*y^2 + 3*y + 7/2], [(-2, 0), y |--> -1/2*y^2 - y - 1/2], [(0, 2), y |--> 1/3*y^3 - y - 1/2], [(2, 3), y |--> 3*y - 35/6]] + + Ensure results are consistent with FTC:: + + sage: F(-3) - F(-4) + -1 + sage: F(-1) - F(-3) + 1 + sage: F(2) - F(0) + 2/3 + sage: f.integral(y, 0, 2) + 2/3 + sage: F(3) - F(-4) + 19/6 + sage: f.integral(y, -4, 3) + 19/6 + sage: f.integral(definite=True) + 19/6 + + :: + + sage: f1(y) = (y+3)^2 + sage: f2(y) = y+3 + sage: f3(y) = 3 + sage: f = Piecewise([[(-infinity, -3), f1], [(-3, 0), f2], [(0, infinity), f3]]) + sage: f.integral() + Piecewise defined function with 3 parts, [[(-Infinity, -3), y |--> 1/3*y^3 + 3*y^2 + 9*y + 9], [(-3, 0), y |--> 1/2*y^2 + 3*y + 9/2], [(0, +Infinity), y |--> 3*y + 9/2]] + + :: + + sage: f1(x) = e^(-abs(x)) + sage: f = Piecewise([[(-infinity, infinity), f1]]) + sage: f.integral(definite=True) + 2 + sage: f.integral() + Piecewise defined function with 1 parts, [[(-Infinity, +Infinity), x |--> -1/2*((sgn(x) - 1)*e^(2*x) - 2*e^x*sgn(x) + sgn(x) + 1)*e^(-x) - 1]] + + :: + + sage: f = Piecewise([((0, 5), cos(x))]) + sage: f.integral() + Piecewise defined function with 1 parts, [[(0, 5), x |--> sin(x)]] + + + TESTS: + + Verify that piecewise integrals of zero work (trac #10841):: + + sage: f0(x) = 0 + sage: f = Piecewise([[(0,1),f0]]) + sage: f.integral(x,0,1) + 0 + sage: f = Piecewise([[(0,1), 0]]) + sage: f.integral(x,0,1) + 0 + sage: f = Piecewise([[(0,1), SR(0)]]) + sage: f.integral(x,0,1) + 0 + + """ + if a != None and b != None: + F = self.integral(x) + return F(b) - F(a) + + if a != None or b != None: + raise TypeError, 'only one endpoint given' + + area = 0 # cumulative definite integral of parts to the left of the current interval + integrand_pieces = self.list() + integrand_pieces.sort() + new_pieces = [] + + if x == None: + x = self.default_variable() + + # The integral is computed by iterating over the pieces in order. + # The definite integral for each piece is calculated and accumulated in `area`. + # Thus at any time, `area` represents the definite integral of all the pieces + # encountered so far. The indefinite integral of each piece is also calculated, + # and the `area` before each piece is added to the piece. + # + # If a definite integral is requested, `area` is returned. + # Otherwise, a piecewise function is constructed from the indefinite integrals + # and returned. + # + # An exception is made if integral is called on a piecewise function + # that starts at -infinity. In this case, we do not try to calculate the + # definite integral of the first piece, and the value of `area` remains 0 + # after the first piece. + + for (start, end), fun in integrand_pieces: + fun = SR(fun) + if start == -infinity and not definite: + fun_integrated = fun.integral(x, end, x) + else: + try: + assume(start < x) + except ValueError: # Assumption is redundant + pass + fun_integrated = fun.integral(x, start, x) + area + forget(start < x) + if definite or end != infinity: + area += fun.integral(x, start, end) + new_pieces.append([(start, end), SR(fun_integrated).function(x)]) + + if definite: + return SR(area) + else: + return Piecewise(new_pieces) + + def convolution(self, other): + """ + Returns the convolution function, + `f*g(t)=\int_{-\infty}^\infty f(u)g(t-u)du`, for compactly + supported `f,g`. + + EXAMPLES:: + + sage: x = PolynomialRing(QQ,'x').gen() + sage: f = Piecewise([[(0,1),1*x^0]]) ## example 0 + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: g = f.convolution(f) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: h = f.convolution(g) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1)); + sage: # Type show(P+Q+R) to view + sage: f = Piecewise([[(0,1),1*x^0],[(1,2),2*x^0],[(2,3),1*x^0]]) ## example 1 + sage: g = f.convolution(f) + sage: h = f.convolution(g) + sage: P = f.plot(); Q = g.plot(rgbcolor=(1,1,0)); R = h.plot(rgbcolor=(0,1,1)); + sage: # Type show(P+Q+R) to view + sage: f = Piecewise([[(-1,1),1]]) ## example 2 + sage: g = Piecewise([[(0,3),x]]) + sage: f.convolution(g) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 3 parts, [[(-1, 1), 0], [(1, 2), -3/2*x], [(2, 4), -3/2*x]] + sage: g = Piecewise([[(0,3),1*x^0],[(3,4),2*x^0]]) + sage: f.convolution(g) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 5 parts, [[(-1, 1), x + 1], [(1, 2), 3], [(2, 3), x], [(3, 4), -x + 8], [(4, 5), -2*x + 10]] + """ + f = self + g = other + M = min(min(f.end_points()),min(g.end_points())) + N = max(max(f.end_points()),max(g.end_points())) + R2 = PolynomialRing(QQ,2,names=["tt","uu"]) + tt,uu = R2.gens() + conv = 0 + f0 = f.functions()[0] + g0 = g.functions()[0] + R1 = f0.parent() + xx = R1.gen() + var = repr(xx) + if len(f.intervals())==1 and len(g.intervals())==1: + f = f.unextend() + g = g.unextend() + a1 = f.intervals()[0][0] + a2 = f.intervals()[0][1] + b1 = g.intervals()[0][0] + b2 = g.intervals()[0][1] + i1 = repr(f0).replace(var,repr(uu)) + i2 = repr(g0).replace(var,"("+repr(tt-uu)+")") + cmd1 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, tt-b1) ## if a1+b1 < tt < a2+b1 + cmd2 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, tt-b1) ## if a1+b2 < tt < a2+b1 + cmd3 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, tt-b2, a2) ## if a1+b2 < tt < a2+b2 + cmd4 = "integrate((%s)*(%s),%s,%s,%s)"%(i1,i2, uu, a1, a2) ## if a2+b1 < tt < a1+b2 + conv1 = maxima.eval(cmd1) + conv2 = maxima.eval(cmd2) + conv3 = maxima.eval(cmd3) + conv4 = maxima.eval(cmd4) + # this is a very, very, very ugly hack + x = PolynomialRing(QQ,'x').gen() + fg1 = sage_eval(conv1.replace("tt",var), {'x':x}) ## should be = R2(conv1) + fg2 = sage_eval(conv2.replace("tt",var), {'x':x}) ## should be = R2(conv2) + fg3 = sage_eval(conv3.replace("tt",var), {'x':x}) ## should be = R2(conv3) + fg4 = sage_eval(conv4.replace("tt",var), {'x':x}) ## should be = R2(conv4) + if a1-b11 or len(g.intervals())>1: + z = Piecewise([[(-3*abs(N-M),3*abs(N-M)),0*xx**0]]) + ff = f.functions() + gg = g.functions() + intvlsf = f.intervals() + intvlsg = g.intervals() + for i in range(len(ff)): + for j in range(len(gg)): + f0 = Piecewise([[intvlsf[i],ff[i]]]) + g0 = Piecewise([[intvlsg[j],gg[j]]]) + h = g0.convolution(f0) + z = z + h + return z.unextend() + + def derivative(self): + r""" + Returns the derivative (as computed by maxima) + Piecewise(I,`(d/dx)(self|_I)`), as I runs over the + intervals belonging to self. self must be piecewise polynomial. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.derivative() + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + Piecewise defined function with 2 parts, [[(0, 1), x |--> 0], [(1, 2), x |--> -1]] + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(0,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.derivative() + Piecewise defined function with 2 parts, [[(0, 1/2*pi), x |--> 0], [(1/2*pi, pi), x |--> 0]] + + :: + + sage: f = Piecewise([[(0,1), (x * 2)]], x) + sage: f.derivative() + Piecewise defined function with 1 parts, [[(0, 1), x |--> 2]] + """ + x = self.default_variable() + dlist = [[(a, b), derivative(f(x), x).function(x)] for (a,b),f in self.list()] + return Piecewise(dlist) + + def tangent_line(self, pt): + """ + Computes the linear function defining the tangent line of the + piecewise function self. + + EXAMPLES:: + + sage: f1(x) = x^2 + sage: f2(x) = 5-x^3+x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: tf = f.tangent_line(0.9) ## tangent line at x=0.9 + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot(rgbcolor=(0.7,0.1,0.5), plot_points=40) + sage: Q = tf.plot(rgbcolor=(0.7,0.2,0.2), plot_points=40) + sage: P + Q + Graphics object consisting of 4 graphics primitives + """ + pt = QQ(pt) + R = QQ[self.default_variable()] + x = R.gen() + der = self.derivative() + tanline = (x-pt)*der(pt)+self(pt) + dlist = [[(a, b), tanline] for (a,b),f in self.list()] + return Piecewise(dlist) + + def plot(self, *args, **kwds): + """ + Returns the plot of self. + + Keyword arguments are passed onto the plot command for each piece + of the function. E.g., the plot_points keyword affects each + segment of the plot. + + EXAMPLES:: + + sage: f1(x) = 1 + sage: f2(x) = 1-x + sage: f3(x) = exp(x) + sage: f4(x) = sin(2*x) + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot(rgbcolor=(0.7,0.1,0), plot_points=40) + sage: P + Graphics object consisting of 4 graphics primitives + + Remember: to view this, type show(P) or P.save("path/myplot.png") + and then open it in a graphics viewer such as GIMP. + + TESTS: + + We should not add each piece to the legend individually, since + this creates duplicates (:trac:`12651`). This tests that only + one of the graphics objects in the plot has a non-``None`` + ``legend_label``:: + + sage: f1 = sin(x) + sage: f2 = cos(x) + sage: f = piecewise([[(-1,0), f1],[(0,1), f2]]) + sage: p = f.plot(legend_label='$f(x)$') + sage: lines = [ + ... line + ... for line in p._objects + ... if line.options()['legend_label'] is not None ] + sage: len(lines) + 1 + """ + from sage.plot.all import plot, Graphics + + g = Graphics() + + for i, ((a,b), f) in enumerate(self.list()): + # If it's the first piece, pass all arguments. Otherwise, + # filter out 'legend_label' so that we don't add each + # piece to the legend separately (trac #12651). + if i != 0 and 'legend_label' in kwds: + del kwds['legend_label'] + + g += plot(f, a, b, *args, **kwds) + + return g + + def fourier_series_cosine_coefficient(self,n,L): + r""" + Returns the n-th Fourier series coefficient of + `\cos(n\pi x/L)`, `a_n`. + + INPUT: + + + - ``self`` - the function f(x), defined over -L x L + + - ``n`` - an integer n=0 + + - ``L`` - (the period)/2 + + + OUTPUT: + `a_n = \frac{1}{L}\int_{-L}^L f(x)\cos(n\pi x/L)dx` + + EXAMPLES:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_cosine_coefficient(2,1) + pi^(-2) + sage: f(x) = x^2 + sage: f = Piecewise([[(-pi,pi),f]]) + sage: f.fourier_series_cosine_coefficient(2,pi) + 1 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_cosine_coefficient(5,pi) + -3/5/pi + """ + from sage.all import cos, pi + x = var('x') + result = sum([(f(x)*cos(pi*x*n/L)/L).integrate(x, a, b) + for (a,b), f in self.list()]) + if is_Expression(result): + return result.simplify_trig() + return result + + def fourier_series_sine_coefficient(self,n,L): + r""" + Returns the n-th Fourier series coefficient of + `\sin(n\pi x/L)`, `b_n`. + + INPUT: + + + - ``self`` - the function f(x), defined over -L x L + + - ``n`` - an integer n0 + + - ``L`` - (the period)/2 + + + OUTPUT: + `b_n = \frac{1}{L}\int_{-L}^L f(x)\sin(n\pi x/L)dx` + + EXAMPLES:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_sine_coefficient(2,1) # L=1, n=2 + 0 + """ + from sage.all import sin, pi + x = var('x') + result = sum([(f(x)*sin(pi*x*n/L)/L).integrate(x, a, b) + for (a,b), f in self.list()]) + if is_Expression(result): + return result.simplify_trig() + return result + + def _fourier_series_helper(self, N, L, scale_function): + r""" + A helper function for the construction of Fourier series. The + argument scale_function is a function which takes in n, + representing the `n^{th}` coefficient, and return an + expression to scale the sine and cosine coefficients by. + + EXAMPLES:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f._fourier_series_helper(3, 1, lambda n: 1) + cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 + """ + from sage.all import pi, sin, cos, srange + x = self.default_variable() + a0 = self.fourier_series_cosine_coefficient(0,L) + result = a0/2 + sum([(self.fourier_series_cosine_coefficient(n,L)*cos(n*pi*x/L) + + self.fourier_series_sine_coefficient(n,L)*sin(n*pi*x/L))* + scale_function(n) + for n in srange(1,N)]) + return result.expand() + + + def fourier_series_partial_sum(self,N,L): + r""" + Returns the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + as a string. + + EXAMPLE:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_partial_sum(3,1) + cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_partial_sum(3,pi) + -3*cos(x)/pi - 3*sin(2*x)/pi + 3*sin(x)/pi - 1/4 + """ + return self._fourier_series_helper(N, L, lambda n: 1) + + def fourier_series_partial_sum_cesaro(self,N,L): + r""" + Returns the Cesaro partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + + as a string. This is a "smoother" partial sum - the Gibbs + phenomenon is mollified. + + EXAMPLE:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_partial_sum_cesaro(3,1) + 1/3*cos(2*pi*x)/pi^2 - 8/3*cos(pi*x)/pi^2 + 1/3 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_partial_sum_cesaro(3,pi) + -2*cos(x)/pi - sin(2*x)/pi + 2*sin(x)/pi - 1/4 + """ + return self._fourier_series_helper(N, L, lambda n: 1-n/N) + + def fourier_series_partial_sum_hann(self,N,L): + r""" + Returns the Hann-filtered partial sum (named after von Hann, not + Hamming) + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + as a string, where `H_N(x) = (1+\cos(\pi x/N))/2`. This is + a "smoother" partial sum - the Gibbs phenomenon is mollified. + + EXAMPLE:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_partial_sum_hann(3,1) + 1/4*cos(2*pi*x)/pi^2 - 3*cos(pi*x)/pi^2 + 1/3 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_partial_sum_hann(3,pi) + -9/4*cos(x)/pi - 3/4*sin(2*x)/pi + 9/4*sin(x)/pi - 1/4 + """ + from sage.all import cos, pi + return self._fourier_series_helper(N, L, lambda n: (1+cos(pi*n/N))/2) + + def fourier_series_partial_sum_filtered(self,N,L,F): + r""" + Returns the "filtered" partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + as a string, where `F = [F_1,F_2, ..., F_{N}]` is a list + of length `N` consisting of real numbers. This can be used + to plot FS solutions to the heat and wave PDEs. + + EXAMPLE:: + + sage: f(x) = x^2 + sage: f = Piecewise([[(-1,1),f]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f.fourier_series_partial_sum_filtered(3,1,[1,1,1]) + cos(2*pi*x)/pi^2 - 4*cos(pi*x)/pi^2 + 1/3 + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: f.fourier_series_partial_sum_filtered(3,pi,[1,1,1]) + -3*cos(x)/pi - 3*sin(2*x)/pi + 3*sin(x)/pi - 1/4 + """ + return self._fourier_series_helper(N, L, lambda n: F[n]) + + def plot_fourier_series_partial_sum(self,N,L,xmin,xmax, **kwds): + r""" + Plots the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + sum_{n=1}^N [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + over xmin x xmin. + + EXAMPLE:: + + sage: f1(x) = -2 + sage: f2(x) = 1 + sage: f3(x) = -1 + sage: f4(x) = 2 + sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot_fourier_series_partial_sum(3,pi,-5,5) # long time + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: P = f.plot_fourier_series_partial_sum(15,pi,-5,5) # long time + + Remember, to view this type show(P) or P.save("path/myplot.png") + and then open it in a graphics viewer such as GIMP. + """ + from sage.plot.all import plot + return plot(self.fourier_series_partial_sum(N,L), xmin, xmax, **kwds) + + def plot_fourier_series_partial_sum_cesaro(self,N,L,xmin,xmax, **kwds): + r""" + Plots the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N (1-n/N)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + + over xmin x xmin. This is a "smoother" partial sum - the Gibbs + phenomenon is mollified. + + EXAMPLE:: + + sage: f1(x) = -2 + sage: f2(x) = 1 + sage: f3(x) = -1 + sage: f4(x) = 2 + sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot_fourier_series_partial_sum_cesaro(3,pi,-5,5) # long time + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: P = f.plot_fourier_series_partial_sum_cesaro(15,pi,-5,5) # long time + + Remember, to view this type show(P) or P.save("path/myplot.png") + and then open it in a graphics viewer such as GIMP. + """ + from sage.plot.all import plot + return plot(self.fourier_series_partial_sum_cesaro(N,L), xmin, xmax, **kwds) + + def plot_fourier_series_partial_sum_hann(self,N,L,xmin,xmax, **kwds): + r""" + Plots the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N H_N(n)*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + + over xmin x xmin, where H_N(x) = (0.5)+(0.5)\*cos(x\*pi/N) is the + N-th Hann filter. + + EXAMPLE:: + + sage: f1(x) = -2 + sage: f2(x) = 1 + sage: f3(x) = -1 + sage: f4(x) = 2 + sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot_fourier_series_partial_sum_hann(3,pi,-5,5) # long time + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,pi/2),f1],[(pi/2,pi),f2]]) + sage: P = f.plot_fourier_series_partial_sum_hann(15,pi,-5,5) # long time + + Remember, to view this type show(P) or P.save("path/myplot.png") + and then open it in a graphics viewer such as GIMP. + """ + from sage.plot.all import plot + return plot(self.fourier_series_partial_sum_hann(N,L), xmin, xmax, **kwds) + + def plot_fourier_series_partial_sum_filtered(self,N,L,F,xmin,xmax, **kwds): + r""" + Plots the partial sum + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^N F_n*[a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], + + + over xmin x xmin, where `F = [F_1,F_2, ..., F_{N}]` is a + list of length `N` consisting of real numbers. This can be + used to plot FS solutions to the heat and wave PDEs. + + EXAMPLE:: + + sage: f1(x) = -2 + sage: f2(x) = 1 + sage: f3(x) = -1 + sage: f4(x) = 2 + sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f3],[(pi/2,pi),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: P = f.plot_fourier_series_partial_sum_filtered(3,pi,[1]*3,-5,5) # long time + sage: f1(x) = -1 + sage: f2(x) = 2 + sage: f = Piecewise([[(-pi,-pi/2),f1],[(-pi/2,0),f2],[(0,pi/2),f1],[(pi/2,pi),f2]]) + sage: P = f.plot_fourier_series_partial_sum_filtered(15,pi,[1]*15,-5,5) # long time + + Remember, to view this type show(P) or P.save("path/myplot.png") + and then open it in a graphics viewer such as GIMP. + """ + from sage.plot.all import plot + return plot(self.fourier_series_partial_sum_filtered(N,L,F), xmin, xmax, **kwds) + + def fourier_series_value(self,x,L): + r""" + Returns the value of the Fourier series coefficient of self at + `x`, + + + .. math:: + + f(x) \sim \frac{a_0}{2} + \sum_{n=1}^\infty [a_n\cos(\frac{n\pi x}{L}) + b_n\sin(\frac{n\pi x}{L})], \ \ \ -L0) + result = sum([(SR(f)*exp(-s*x)).integral(x,a,b) + for (a,b),f in self.list()]) + forget(s>0) + return result + + def _make_compatible(self, other): + """ + Returns self and other extended to be defined on the same domain as + well as a refinement of their intervals. This is used for adding + and multiplying piecewise functions. + + EXAMPLES:: + + sage: R. = QQ[] + sage: f1 = Piecewise([[(0, 2), x]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: f2 = Piecewise([[(1, 3), x^2]]) + sage: f1._make_compatible(f2) + (Piecewise defined function with 2 parts, [[(0, 2), x], [(2, 3), 0]], + Piecewise defined function with 2 parts, [[(0, 1), 0], [(1, 3), x^2]], + [(0, 1), (1, 2), (2, 3)]) + """ + a1, b1 = self.domain() + a2, b2 = other.domain() + a = min(a1, a2) + b = max(b1, b2) + F = self.extend_by_zero_to(a,b) + G = other.extend_by_zero_to(a,b) + endpts = list(set(F.end_points()).union(set(G.end_points()))) + endpts.sort() + return F, G, zip(endpts, endpts[1:]) + + def __add__(self,other): + """ + Returns the piecewise defined function which is the sum of self and + other. Does not require both domains be the same. + + EXAMPLES:: + + sage: x = PolynomialRing(QQ,'x').gen() + sage: f1 = x^0 + sage: f2 = 1-x + sage: f3 = 2*x + sage: f4 = 10-x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: g1 = x-2 + sage: g2 = x-5 + sage: g = Piecewise([[(0,5),g1],[(5,10),g2]]) + sage: h = f+g + sage: h + Piecewise defined function with 5 parts, [[(0, 1), x - 1], [(1, 2), -1], [(2, 3), 3*x - 2], [(3, 5), 8], [(5, 10), 5]] + + Note that in this case the functions must be defined using + polynomial expressions *not* using the lambda notation. + """ + F, G, intervals = self._make_compatible(other) + fcn = [] + for a,b in intervals: + fcn.append([(a,b), F.which_function(b)+G.which_function(b)]) + return Piecewise(fcn) + + def __mul__(self,other): + r""" + Returns the piecewise defined function which is the product of one + piecewise function (self) with another one (other). + + EXAMPLES:: + + sage: x = PolynomialRing(QQ,'x').gen() + sage: f1 = x^0 + sage: f2 = 1-x + sage: f3 = 2*x + sage: f4 = 10-x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: g1 = x-2 + sage: g2 = x-5 + sage: g = Piecewise([[(0,5),g1],[(5,10),g2]]) + sage: h = f*g + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: h + Piecewise defined function with 5 parts, [[(0, 1), x - 2], [(1, 2), -x^2 + 3*x - 2], [(2, 3), 2*x^2 - 4*x], [(3, 5), -x^2 + 12*x - 20], [(5, 10), -x^2 + 15*x - 50]] + sage: g*(11/2) + Piecewise defined function with 2 parts, [[(0, 5), 11/2*x - 11], [(5, 10), 11/2*x - 55/2]] + + Note that in this method the functions must be defined using + polynomial expressions *not* using the lambda notation. + """ + ## needed for scalar multiplication + if isinstance(other,Rational) or isinstance(other,Integer): + return Piecewise([[(a,b), other*f] for (a,b),f in self.list()]) + else: + F, G, intervals = self._make_compatible(other) + fcn = [] + for a,b in intervals: + fcn.append([(a,b),F.which_function(b)*G.which_function(b)]) + return Piecewise(fcn) + + __rmul__ = __mul__ + + def __eq__(self,other): + """ + Implements Boolean == operator. + + EXAMPLES:: + + sage: f1 = x^0 + sage: f2 = 1-x + sage: f3 = 2*x + sage: f4 = 10-x + sage: f = Piecewise([[(0,1),f1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + doctest:...: DeprecationWarning: use lower-case piecewise instead + See http://trac.sagemath.org/14801 for details. + sage: g = Piecewise([[(0,1),1],[(1,2),f2],[(2,3),f3],[(3,10),f4]]) + sage: f==g + True + """ + return self.list()==other.list() diff --git a/src/sage/functions/prime_pi.pyx b/src/sage/functions/prime_pi.pyx index 761db3b5ed0..9d7635cff1b 100644 --- a/src/sage/functions/prime_pi.pyx +++ b/src/sage/functions/prime_pi.pyx @@ -28,7 +28,7 @@ EXAMPLES:: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from sage.libs.pari.paridecl cimport * @@ -108,7 +108,7 @@ cdef class PrimePi(BuiltinFunction): sage: prime_pi(500509, 50051) 41581 - The following test is to verify that ticket #4670 has been essentially + The following test is to verify that :trac:`4670` has been essentially resolved:: sage: prime_pi(10^10) @@ -126,7 +126,7 @@ cdef class PrimePi(BuiltinFunction): REFERENCES: - .. [RAO2011] R.A. Ohana. On Prime Counting in Abelian Number Fields. + .. [RAO2011] \R.A. Ohana. On Prime Counting in Abelian Number Fields. http://wstein.org/home/ohanar/papers/abelian_prime_counting/main.pdf. AUTHOR: @@ -144,13 +144,13 @@ cdef class PrimePi(BuiltinFunction): def __dealloc__(self): if self.__smallPi != NULL: - sage_free(self.__smallPi) - sage_free(self.__tabS) + sig_free(self.__smallPi) + sig_free(self.__tabS) cdef void _init_tables(self): pari.init_primes(0xffffu) self.__pariPrimePtr = diffptr - self.__smallPi = sage_malloc( + self.__smallPi = sig_malloc( 0x10000u * sizeof(uint_fast16_t)) cdef uint32_t p=0u, i=0u, k=0u while i < 0xfff1u: # 0xfff1 is the last prime up to 0xffff @@ -163,7 +163,7 @@ cdef class PrimePi(BuiltinFunction): self.__smallPi[i] = k i += 1u - self.__tabS = sage_malloc(2310*sizeof(int_fast8_t)) + self.__tabS = sig_malloc(2310*sizeof(int_fast8_t)) for i in range(2310u): self.__tabS[i] = ((i+1u)/2u - (i+3u)/6u - (i+5u)/10u + (i+15u)/30u - (i+7u)/14u + (i+21u)/42u + (i+35u)/70u - (i+105u)/210u @@ -305,7 +305,7 @@ cdef class PrimePi(BuiltinFunction): cdef void _clean_cache(self): if self.__numPrimes: - sage_free(self.__primes) + sig_free(self.__primes) self.__numPrimes = 0u self.__maxSieve = 0u @@ -319,10 +319,10 @@ cdef class PrimePi(BuiltinFunction): self.__pariPrimePtr = diffptr newNumPrimes = self._pi(b, 0ull) if self.__numPrimes: - prime = sage_realloc(self.__primes, + prime = sig_realloc(self.__primes, newNumPrimes * sizeof(uint32_t)) else: - prime = sage_malloc(newNumPrimes*sizeof(uint32_t)) + prime = sig_malloc(newNumPrimes*sizeof(uint32_t)) if not sig_on_no_except(): self.__numPrimes = newNumPrimes self._clean_cache() @@ -543,7 +543,7 @@ cpdef Integer legendre_phi(x, a): # Deal with the general case if (prime_pi).__smallPi == NULL: (prime_pi)._init_tables() - cdef uint32_t z = pari.nth_prime(a) + cdef uint32_t z = pari.prime(a) if z >= y: return Integer(1) (prime_pi)._init_primes(z) if not sig_on_no_except(): diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index 829b090cb6c..1c22be8c589 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -337,7 +337,7 @@ def _evalf_(self, *args, **kwds): sage: f._evalf_(1, I, parent=RR) Traceback (most recent call last): ... - TypeError: Unable to convert x (='0.848379570759176-0.0742924734216079*I') to real number. + TypeError: unable to convert '0.848379570759176-0.0742924734216079*I' to a real number """ parent = kwds['parent'] # The result from maxima is a machine double, which corresponds @@ -363,10 +363,7 @@ def _eval_(self, *args): sage: f._eval_(1,1) tanh(1) - - Here arccoth doesn't have 1 in its domain, so we just hold the expression: - - sage: elliptic_e(arccoth(1), x^2*e) + sage: elliptic_e(arccoth(1, hold=True), x^2*e) elliptic_e(arccoth(1), x^2*e) Since Maxima works only with double precision, numerical diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 7f5f6ec8fad..e3212da2715 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -225,6 +225,8 @@ def __init__(self): sage: cot(complex(1,1)) # rel tol 1e-15 (0.21762156185440273-0.8680141428959249j) + sage: cot(1.+I) + 0.217621561854403 - 0.868014142895925*I """ GinacFunction.__init__(self, "cot", latex_name=r"\cot") @@ -425,6 +427,8 @@ def __init__(self): sage: arcsin(x).operator() arcsin + sage: asin(complex(1,1)) + (0.6662394324925152+1.0612750619050357j) """ GinacFunction.__init__(self, 'arcsin', latex_name=r"\arcsin", conversions=dict(maxima='asin', sympy='asin')) @@ -480,6 +484,8 @@ def __init__(self): sage: arccos(x).operator() arccos + sage: acos(complex(1,1)) + (0.9045568943023814-1.0612750619050357j) """ GinacFunction.__init__(self, 'arccos', latex_name=r"\arccos", conversions=dict(maxima='acos', sympy='acos')) @@ -537,6 +543,8 @@ def __init__(self): sage: arctan(x).operator() arctan + sage: atan(complex(1,1)) + (1.0172219678978514+0.4023594781085251j) Check that :trac:`19918` is fixed:: @@ -589,6 +597,8 @@ def __init__(self): sage: arccot(complex(1,1)) # rel tol 1e-15 (0.5535743588970452-0.4023594781085251j) + sage: arccot(1.+I) + 0.553574358897045 - 0.402359478108525*I """ GinacFunction.__init__(self, "arccot", latex_name=r'{\rm arccot}', diff --git a/src/sage/functions/wigner.py b/src/sage/functions/wigner.py index a53540323ae..bbb94bcd447 100644 --- a/src/sage/functions/wigner.py +++ b/src/sage/functions/wigner.py @@ -11,7 +11,7 @@ REFERENCES: -.. [Rasch03] J. Rasch and A. C. H. Yu, 'Efficient Storage Scheme for +.. [Rasch03] \J. Rasch and A. C. H. Yu, 'Efficient Storage Scheme for Pre-calculated Wigner 3j, 6j and Gaunt Coefficients', SIAM J. Sci. Comput. Volume 25, Issue 4, pp. 1416-1428 (2003) diff --git a/src/sage/game_theory/all.py b/src/sage/game_theory/all.py index 0e56b0da187..4658885c3ab 100644 --- a/src/sage/game_theory/all.py +++ b/src/sage/game_theory/all.py @@ -4,4 +4,3 @@ lazy_import('sage.game_theory.cooperative_game', 'CooperativeGame') lazy_import('sage.game_theory.normal_form_game', 'NormalFormGame') lazy_import('sage.game_theory.matching_game', 'MatchingGame') - diff --git a/src/sage/game_theory/catalog.py b/src/sage/game_theory/catalog.py index 573b34f7d84..0a1df1cc07c 100644 --- a/src/sage/game_theory/catalog.py +++ b/src/sage/game_theory/catalog.py @@ -3,4 +3,3 @@ """ import catalog_normal_form_games as normal_form_games - diff --git a/src/sage/game_theory/catalog_normal_form_games.py b/src/sage/game_theory/catalog_normal_form_games.py index dd0e91ebd55..5d0ac7f3fbe 100644 --- a/src/sage/game_theory/catalog_normal_form_games.py +++ b/src/sage/game_theory/catalog_normal_form_games.py @@ -228,7 +228,7 @@ def CoordinationGame(A=10, a=5, B=0, b=0, C=0, c=0, D=5, d=10): TypeError: the input values for a Coordination game must be of the form A > B, D > C, a > c and d > b """ - if not (A > B and D > C and a > c and d > b): + if not (A > B and D > C and a > c and d > b): raise TypeError("the input values for a Coordination game must be of the form A > B, D > C, a > c and d > b") from sage.matrix.constructor import matrix A = matrix([[A, C], [B, D]]) @@ -414,7 +414,7 @@ def AntiCoordinationGame(A=3, a=3, B=5, b=1, C=1, c=5, D=0, d=0): ... TypeError: the input values for an Anti coordination game must be of the form A < B, D < C, a < c and d < b """ - if not (A < B and D < C and a < c and d < b): + if not (A < B and D < C and a < c and d < b): raise TypeError("the input values for an Anti coordination game must be of the form A < B, D < C, a < c and d < b") from sage.matrix.constructor import matrix A = matrix([[A, C], [B, D]]) @@ -506,9 +506,10 @@ def HawkDove(v=2, c=3): ... TypeError: the input values for a Hawk Dove game must be of the form c > v """ - if not (c>v): + if not (c > v): raise TypeError("the input values for a Hawk Dove game must be of the form c > v") - g = AntiCoordinationGame(A=v/2-c, a=v/2-c, B=0, b=v, C=v, c=0, D=v/2, d=v/2) + g = AntiCoordinationGame(A=v/2-c, a=v/2-c, B=0, b=v, + C=v, c=0, D=v/2, d=v/2) g.rename('Hawk-Dove - ' + repr(g)) return g @@ -656,6 +657,7 @@ def RPS(): g.rename('Rock-Paper-Scissors - ' + repr(g)) return g + def RPSLS(): r""" Return a Rock-Paper-Scissors-Lizard-Spock game. @@ -716,7 +718,7 @@ def RPSLS(): from sage.matrix.constructor import matrix A = matrix([[0, -1, 1, 1, -1], [1, 0, -1, -1, 1], - [-1, 1, 0, 1 , -1], + [-1, 1, 0, 1, -1], [-1, 1, -1, 0, 1], [1, -1, 1, -1, 0]]) g = NormalFormGame([A]) @@ -814,6 +816,7 @@ def Chicken(A=0, a=0, B=1, b=-1, C=-1, c=1, D=-10, d=-10): g.rename('Chicken - ' + repr(g)) return g + def TravellersDilemma(max_value=10): r""" Return a Travellers dilemma game. @@ -929,7 +932,7 @@ def TravellersDilemma(max_value=10): from sage.matrix.constructor import matrix from sage.functions.generalized import sign A = matrix([[min(i, j) + 2 * sign(j - i) for j in range(max_value, 1, -1)] - for i in range(max_value, 1, -1)]) + for i in range(max_value, 1, -1)]) g = NormalFormGame([A, A.transpose()]) g.rename('Travellers dilemma - ' + repr(g)) return g diff --git a/src/sage/game_theory/cooperative_game.py b/src/sage/game_theory/cooperative_game.py index 9e6f7296e4b..ec3c4cf6fef 100644 --- a/src/sage/game_theory/cooperative_game.py +++ b/src/sage/game_theory/cooperative_game.py @@ -404,7 +404,7 @@ def shapley_value(self): for player in self.player_list: weighted_contribution = 0 for coalition in powerset(self.player_list): - if coalition: # If non-empty + if coalition: # If non-empty k = Integer(len(coalition)) weight = 1 / (n.binomial(k) * k) t = tuple(p for p in coalition if p != player) @@ -617,7 +617,7 @@ def _latex_(self): cf = self.ch_f output = "v(c) = \\begin{cases}\n" for key in sorted(cf.keys(), key=lambda key: len(key)): - if not key: # == () + if not key: # == () coalition = "\\emptyset" else: coalition = "\\{" + ", ".join(str(player) for player in key) + "\\}" @@ -860,4 +860,3 @@ def is_symmetric(self, payoff_vector): if all(results) and payoff_vector[c1[0]] != payoff_vector[c2[0]]: return False return True - diff --git a/src/sage/game_theory/gambit_docs.py b/src/sage/game_theory/gambit_docs.py index a8f059dd340..42271efbf3c 100644 --- a/src/sage/game_theory/gambit_docs.py +++ b/src/sage/game_theory/gambit_docs.py @@ -107,7 +107,7 @@ Out[14]: [] Note that the above examples only show how to build and find equilibria for -two player strategic form games. Gambit supports mulitple player games as well +two player strategic form games. Gambit supports multiple player games as well as extensive form games: for more details see http://www.gambit-project.org/. If one really wants to use gambit directly in Sage (without using the diff --git a/src/sage/game_theory/matching_game.py b/src/sage/game_theory/matching_game.py index c755060559a..fe162458e2f 100644 --- a/src/sage/game_theory/matching_game.py +++ b/src/sage/game_theory/matching_game.py @@ -24,6 +24,7 @@ from copy import deepcopy from sage.graphs.bipartite_graph import BipartiteGraph + class MatchingGame(SageObject): r""" A matching game. @@ -397,8 +398,8 @@ def _repr_(self): sage: M A matching game with 2 suitors and 2 reviewers """ - return 'A matching game with {} suitors and {} reviewers'.format( - len(self._suitors), len(self._reviewers)) + txt = 'A matching game with {} suitors and {} reviewers' + return txt.format(len(self._suitors), len(self._reviewers)) def _latex_(self): r""" @@ -424,10 +425,10 @@ def _latex_(self): """ output = "\\text{Suitors:}\n\\begin{aligned}" for suitor in self._suitors: - output += "\n\\\\ %s & \\to %s"%(suitor, suitor.pref) + output += "\n\\\\ %s & \\to %s" % (suitor, suitor.pref) output += "\n\\end{aligned}\n\\text{Reviewers:}\n\\begin{aligned}" for reviewer in self._reviewers: - output += "\n\\\\ %s & \\to %s"%(reviewer, reviewer.pref) + output += "\n\\\\ %s & \\to %s" % (reviewer, reviewer.pref) return output + "\n\\end{aligned}" def __eq__(self, other): @@ -493,9 +494,9 @@ def __eq__(self, other): and set(self._suitors) == set(other._suitors) and set(self._reviewers) == set(other._reviewers) and all(r1.pref == r2.pref for r1, r2 in - zip(set(self._reviewers), set(other._reviewers))) + zip(set(self._reviewers), set(other._reviewers))) and all(s1.pref == s2.pref for s1, s2 in - zip(set(self._suitors), set(other._suitors)))) + zip(set(self._suitors), set(other._suitors)))) def __hash__(self): """ @@ -950,8 +951,8 @@ def solve(self, invert=False): self._sol_dict[r] = [r.partner] if invert: - return {key:self._sol_dict[key][0] for key in self._reviewers} - return {key:self._sol_dict[key][0] for key in self._suitors} + return {key: self._sol_dict[key][0] for key in self._reviewers} + return {key: self._sol_dict[key][0] for key in self._suitors} class Player(object): diff --git a/src/sage/game_theory/named_games.py b/src/sage/game_theory/named_games.py deleted file mode 100644 index 659d37fb0c5..00000000000 --- a/src/sage/game_theory/named_games.py +++ /dev/null @@ -1,20 +0,0 @@ -r""" -Named Normal Form Games. - -A module containing several Normal Form Games that are standard in -literature. A list of all Normal Form Games in this database is available via tab -completion. Type ``NormalFormGame.`` (include the last dot) and hit -the ``tab`` key to see which Normal Form Games are available. -""" -from sage.game_theory.catalog import AntiCoordinationGame -from sage.game_theory.catalog import BattleOfTheSexes -from sage.game_theory.catalog import Chicken -from sage.game_theory.catalog import CoordinationGame -from sage.game_theory.catalog import HawkDove -from sage.game_theory.catalog import MatchingPennies -from sage.game_theory.catalog import Pigs -from sage.game_theory.catalog import PrisonersDilemma -from sage.game_theory.catalog import RPS -from sage.game_theory.catalog import StagHunt -from sage.game_theory.catalog import RPSLS -from sage.game_theory.catalog import TravellersDilemma diff --git a/src/sage/game_theory/normal_form_game.py b/src/sage/game_theory/normal_form_game.py index 57bd6e912e1..b917d2af24b 100644 --- a/src/sage/game_theory/normal_form_game.py +++ b/src/sage/game_theory/normal_form_game.py @@ -620,6 +620,7 @@ except ImportError: Game = None + class NormalFormGame(SageObject, MutableMapping): r""" An object representing a Normal Form Game. Primarily used to compute the @@ -1078,7 +1079,7 @@ def _generate_utilities(self, replacement): self.utilities = {} for profile in product(*strategy_sizes): if profile not in self.utilities.keys(): - self.utilities[profile] = [False]*len(self.players) + self.utilities[profile] = [False] * len(self.players) def add_strategy(self, player): r""" @@ -1420,9 +1421,9 @@ def _solve_LCP(self, maximization): scalar *= -1 for strategy_profile in self.utilities: g[strategy_profile][0] = int(scalar * - self.utilities[strategy_profile][0]) + self.utilities[strategy_profile][0]) g[strategy_profile][1] = int(scalar * - self.utilities[strategy_profile][1]) + self.utilities[strategy_profile][1]) output = ExternalLCPSolver().solve(g) nasheq = Parser(output).format_gambit(g) return sorted(nasheq) @@ -1660,7 +1661,7 @@ def _solve_indifference(self, support1, support2, M): sage: g._solve_indifference((0,), (0,), -A.transpose()) (1) """ - linearsystem = matrix(QQ, len(support2)+1, M.nrows()) + linearsystem = matrix(QQ, len(support2) + 1, M.nrows()) # Build linear system for player 1 for strategy1 in support1: @@ -1742,23 +1743,27 @@ def _is_NE(self, a, b, p1_support, p2_support, M1, M2): False """ # Check that supports are obeyed - if not (all([a[i] > 0 for i in p1_support]) and - all([b[j] > 0 for j in p2_support]) and - all([a[i] == 0 for i in range(len(a)) if i not in p1_support]) and - all([b[j] == 0 for j in range(len(b)) if j not in p2_support])): + if not(all([a[i] > 0 for i in p1_support]) and + all([b[j] > 0 for j in p2_support]) and + all([a[i] == 0 for i in range(len(a)) + if i not in p1_support]) and + all([b[j] == 0 for j in range(len(b)) + if j not in p2_support])): return False # Check that have pair of best responses - p1_payoffs = [sum(v * row[i] for i, v in enumerate(b)) for row - in M1.rows()] - p2_payoffs = [sum(v * col[j] for j, v in enumerate(a)) for col - in M2.columns()] + p1_payoffs = [sum(v * row[i] for i, v in enumerate(b)) + for row in M1.rows()] + p2_payoffs = [sum(v * col[j] for j, v in enumerate(a)) + for col in M2.columns()] #if p1_payoffs.index(max(p1_payoffs)) not in p1_support: - if not any(i in p1_support for i, x in enumerate(p1_payoffs) if x == max(p1_payoffs)): + if not any(i in p1_support for i, x in enumerate(p1_payoffs) + if x == max(p1_payoffs)): return False - if not any(i in p2_support for i, x in enumerate(p2_payoffs) if x == max(p2_payoffs)): + if not any(i in p2_support for i, x in enumerate(p2_payoffs) + if x == max(p2_payoffs)): return False return True @@ -1997,12 +2002,12 @@ def is_degenerate(self, certificate=False): *Enumeration of Nash equilibria for two-player games.* http://www.maths.lse.ac.uk/personal/stengel/ETissue/ARSvS.pdf (2010) - .. [AH2002] R. J. Aumann and S. Hart, Elsevier, eds. + .. [AH2002] \R. J. Aumann and S. Hart, Elsevier, eds. *Computing equilibria for two-person games* http://www.maths.lse.ac.uk/personal/stengel/TEXTE/nashsurvey.pdf (2002) - .. [CK2015] J. Campbell and V. Knight. + .. [CK2015] \J. Campbell and V. Knight. *On testing degeneracy of bi-matrix games* http://vknight.org/unpeudemath/code/2015/06/25/on_testing_degeneracy_of_games/ (2015) @@ -2019,11 +2024,11 @@ def is_degenerate(self, certificate=False): M1, M2 = self.payoff_matrices() potential_supports = [[tuple(support) for support in powerset(range(player.num_strategies))] - for player in self.players] + for player in self.players] # filter out all supports that are pure or empty - potential_supports = [[i for i in k if len(i) > 1] for k in - potential_supports] + potential_supports = [[i for i in k if len(i) > 1] + for k in potential_supports] potential_support_pairs = [pair for pair in product(*potential_supports) if diff --git a/src/sage/game_theory/parser.py b/src/sage/game_theory/parser.py index e15ab3b901e..23e70185b76 100644 --- a/src/sage/game_theory/parser.py +++ b/src/sage/game_theory/parser.py @@ -192,7 +192,7 @@ def format_lrs(self): equilibria = [] from sage.misc.sage_eval import sage_eval from itertools import groupby - for collection in [list(x[1]) for x in groupby(self.raw_string[7:], lambda x: x=='\n')]: + for collection in [list(x[1]) for x in groupby(self.raw_string[7:], lambda x: x == '\n')]: if collection[0].startswith('2'): s1 = tuple([sage_eval(k) for k in collection[-1].split()][1:-1]) for s2 in collection[:-1]: @@ -201,7 +201,6 @@ def format_lrs(self): return equilibria - def format_gambit(self, gambit_game): """ Parses the output of gambit so as to return vectors diff --git a/src/sage/games/hexad.py b/src/sage/games/hexad.py index 4abe37b662c..28d7c1603a5 100644 --- a/src/sage/games/hexad.py +++ b/src/sage/games/hexad.py @@ -1,16 +1,18 @@ r""" Hexads in S(5,6,12) -This module completes a 5-element subset of a 12-set X -into a hexad in a Steiner system S(5,6,12) using Curtis and -Conway's "kitten method". The labeling is either the +This module completes a 5-element subset of a 12-set `X` +into a hexad in a Steiner system `S(5, 6, 12)` using Curtis +and Conway's "kitten method". The labeling is either the "modulo 11" labeling or the "shuffle" labeling. The main functions implemented in this file are -blackjack_move and find_hexad. Enter "blackjack_move?" +:meth:`Minimog.blackjack_move` and :meth:`Minimog.find_hexad`. + +Enter ``blackjack_move?`` for help to play blackjack (i.e., the rules of the game), or -"find_hexad?" for help finding hexads of S(5,6,12) in the -shuffle labeling. +``find_hexad?`` for help finding hexads of `S(5, 6, 12)` in +the shuffle labeling. This picture is the kitten in the "shuffle" labeling:: @@ -40,9 +42,10 @@ | 4 | 1 | 8 | 11 | +-----+-----+-----+-----+ -which is specified by the global variable "minimog_shuffle". -See the docstrings for find_hexad and blackjack_move for -further details and examples. +which is specified by the global variable ``minimog_shuffle``. + +See the docstrings for :meth:`Minimog.find_hexad` and +:meth:`Minimog.blackjack_move` for further details and examples. AUTHOR: @@ -50,18 +53,19 @@ REFERENCES: -.. [Cur84] R. Curtis, The Steiner system `S(5,6,12)`, the Mathieu - group `M_{12}`, and the kitten, in *Computational group theory*, +.. [Cur84] \R. Curtis, The Steiner system `S(5,6,12)`, the Mathieu + group `M_{12}`, and the kitten. *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. -.. [Con84] J. Conway, Hexacode and tetracode - MINIMOG and MOG, - in *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. +.. [Con84] \J. Conway, Hexacode and tetracode - MINIMOG and MOG. + *Computational group theory*, ed. M. Atkinson, Academic Press, 1984. -.. [ConSlo86] J. Conway and N. Sloane, *Lexicographic codes: error-correcting - codes from game theory*, IEEE Trans. Infor. Theory 32 (1986) 337-348. +.. [ConSlo86] \J. Conway and N. Sloane. *Lexicographic codes: error-correcting + codes from game theory*, IEEE Trans. Infor. Theory **32** (1986) 337-348. -.. [KahRyb01] J. Kahane and A. Ryba, The hexad game, *Electronic Journal of - Combinatorics*, 8 (2001) http://www.combinatorics.org/Volume_8/Abstracts/v8i2r11.html +.. [KahRyb01] \J. Kahane and A. Ryba. *The hexad game*, + Electronic Journal of Combinatorics, **8** (2001) + http://www.combinatorics.org/Volume_8/Abstracts/v8i2r11.html Some details are also online at: http://www.permutationpuzzles.org/hexad/ """ @@ -74,21 +78,17 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.infinity import Infinity +from sage.rings.infinity import infinity from sage.matrix.matrix_space import MatrixSpace -from sage.rings.rational_field import RationalField -QQ = RationalField() -infinity = Infinity -from sage.rings.finite_rings.finite_field_constructor import FiniteField -GF = FiniteField +from sage.matrix.constructor import matrix +from sage.rings.finite_rings.finite_field_constructor import GF from sage.calculus.calculus import SR -#SR = SymbolicRing() def view_list(L): """ - This provides a printout of the lines, crosses and squares of the MINIMOG, - as in Curtis' paper. + This provides a printout of the lines, crosses and squares + of the MINIMOG, as in Curtis' paper [Cur84]_. EXAMPLES:: @@ -110,18 +110,13 @@ def view_list(L): [1 1 0] [1 1 0] """ - MS = MatrixSpace(QQ,3,3) - box = [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)] - A = MS([[0 for i in range(3)] for i in range(3)]) - for x in box: - if x in L: - A[x] = 1 - return A + return matrix(GF(2), 3, 3, lambda x, y: 1 if (x,y) in L else 0) + def picture_set(A, L): """ - This is needed in the find_hexad function below. + This is needed in the :meth:`Minimog.find_hexad` function below. EXAMPLES:: @@ -135,10 +130,10 @@ def picture_set(A, L): return set([A[x] for x in L]) -class Minimog(): - """ - This implements the Conway/Curtis minimog idea for describing the Steiner - triple system S(5,6,12). +class Minimog(object): + r""" + This implements the Conway/Curtis minimog idea for describing + the Steiner triple system `S(5, 6, 12)`. EXAMPLES:: @@ -150,7 +145,6 @@ class Minimog(): [ 0 3 +Infinity 2] [ 5 9 8 10] [ 4 1 6 7] - """ def __init__(self, type="shuffle"): self.type = type @@ -162,7 +156,7 @@ def __init__(self, type="shuffle"): elif type == "modulo11": self.minimog = minimog_modulo11 else: - raise ValueError("That Minimog type is not implemented.") + raise ValueError("that Minimog type is not implemented") # This initializes the variables in the game. MS34 = MatrixSpace(SR,3,4) A = self.minimog @@ -174,7 +168,7 @@ def __init__(self, type="shuffle"): self.picture21 = MS33([[A[(2,2)],A[(1,3)],A[(0,1)]],[A[(0,3)],A[(2,3)],A[(2,0)]],[A[(1,0)],A[(1,1)],A[(1,2)]]]) ####### self.picture21 is the "picture at 0" - self.line = [set([]) for i in range(12)] + self.line = list(range(12)) self.line[0] = set([(0,0),(0,1),(0,2)]) self.line[1] = set([(1,0),(1,1),(1,2)]) self.line[2] = set([(2,0),(2,1),(2,2)]) @@ -188,7 +182,7 @@ def __init__(self, type="shuffle"): self.line[10] = set([(0,0),(1,2),(2,1)]) self.line[11] = set([(1,0),(0,1),(2,2)]) - self.cross = [set([]) for i in range(18)] + self.cross = list(range(18)) self.cross[0] = set([(0,0),(0,1),(0,2),(1,0),(2,0)]) self.cross[1] = set([(0,0),(0,1),(0,2),(1,2),(2,2)]) self.cross[2] = set([(0,0),(1,0),(2,0),(2,1),(2,2)]) @@ -212,32 +206,40 @@ def __init__(self, type="shuffle"): for i in range(18): self.square[i] = self.box - self.cross[i] - MS34_GF3 = MatrixSpace(GF(3),3,4) - self.col1 = MS34_GF3([[1,0,0,0],[1,0,0,0],[1,0,0,0]]) - self.col2 = MS34_GF3([[0,1,0,0],[0,1,0,0],[0,1,0,0]]) - self.col3 = MS34_GF3([[0,0,1,0],[0,0,1,0],[0,0,1,0]]) - self.col4 = MS34_GF3([[0,0,0,1],[0,0,0,1],[0,0,0,1]]) - - self.tet1 = MS34_GF3([[1,1,1,1],[0,0,0,0],[0,0,0,0]]) - self.tet2 = MS34_GF3([[1,0,0,0],[0,1,1,1],[0,0,0,0]]) - self.tet3 = MS34_GF3([[1,0,0,0],[0,0,0,0],[0,1,1,1]]) - self.tet4 = MS34_GF3([[0,1,0,0],[1,0,1,0],[0,0,0,1]]) - self.tet5 = MS34_GF3([[0,0,0,1],[1,1,0,0],[0,0,1,0]]) - self.tet6 = MS34_GF3([[0,0,1,0],[1,0,0,1],[0,1,0,0]]) - self.tet7 = MS34_GF3([[0,1,0,0],[0,0,0,1],[1,0,1,0]]) - self.tet8 = MS34_GF3([[0,0,1,0],[0,1,0,0],[1,0,0,1]]) - self.tet9 = MS34_GF3([[0,0,0,1],[0,0,1,0],[1,1,0,0]]) - self.col = [self.col1, self.col2, self.col3, self.col4] - self.tet = [self.tet1, self.tet2, self.tet3, self.tet4, - self.tet5, self.tet6, self.tet7, self.tet8, self.tet9] - # return picture00,picture02,picture21,line,cross,square,col,tet + MS34_GF3 = MatrixSpace(GF(2), 3, 4) + cols = {} + cols[1] = MS34_GF3([[1,0,0,0],[1,0,0,0],[1,0,0,0]]) + cols[2] = MS34_GF3([[0,1,0,0],[0,1,0,0],[0,1,0,0]]) + cols[3] = MS34_GF3([[0,0,1,0],[0,0,1,0],[0,0,1,0]]) + cols[4] = MS34_GF3([[0,0,0,1],[0,0,0,1],[0,0,0,1]]) + self.col = cols + + tets = {} + tets[1] = MS34_GF3([[1,1,1,1],[0,0,0,0],[0,0,0,0]]) + tets[2] = MS34_GF3([[1,0,0,0],[0,1,1,1],[0,0,0,0]]) + tets[3] = MS34_GF3([[1,0,0,0],[0,0,0,0],[0,1,1,1]]) + tets[4] = MS34_GF3([[0,1,0,0],[1,0,1,0],[0,0,0,1]]) + tets[5] = MS34_GF3([[0,0,0,1],[1,1,0,0],[0,0,1,0]]) + tets[6] = MS34_GF3([[0,0,1,0],[1,0,0,1],[0,1,0,0]]) + tets[7] = MS34_GF3([[0,1,0,0],[0,0,0,1],[1,0,1,0]]) + tets[8] = MS34_GF3([[0,0,1,0],[0,1,0,0],[1,0,0,1]]) + tets[9] = MS34_GF3([[0,0,0,1],[0,0,1,0],[1,1,0,0]]) + self.tet = tets def __repr__(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: M = Minimog(type="modulo11") + sage: M + Minimog of type modulo11 + """ return "Minimog of type %s" % self.type def __str__(self): """ - EXAMPLES:: sage: M = Minimog(type="modulo11") @@ -252,7 +254,7 @@ def __str__(self): self.minimog) def _latex_(self): - """ + r""" Prints latex code. EXAMPLES:: @@ -303,33 +305,35 @@ def print_kitten(self): """ MINIMOG = self.minimog - Kitten1 = [' ',' ',' ',' ',' ',' ',' ',' ',' ',str(MINIMOG[0][2]),' ',' ',' ',' ',' '] - Kitten2 = [' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '] - Kitten3 = [' ',' ',' ',' ',' ',' ',' ',' ',' ',str(MINIMOG[2][2]),' ',' ',' ',' ',' '] - Kitten4 = [' ',' ',' ',' ',' ',' ',' ',str(MINIMOG[0][3]),' ',' ',str(MINIMOG[1][3]),' ',' ',' ',' '] - Kitten5 = [' ',' ',' ',' ',' ',' ',str(MINIMOG[1][0]),' ',' ',str(MINIMOG[2][3]),' ',' ',str(MINIMOG[0][1]),' ',' ',' '] - Kitten6 = [' ',' ',' ',' ',' ',str(MINIMOG[2][2]),' ',' ',str(MINIMOG[1][1]),' ',' ',str(MINIMOG[2][0]),' ',' ',str(MINIMOG[2][2]),' ',' '] - Kitten7 = [' ',' ',' ',str(MINIMOG[0][3]),' ',' ',str(MINIMOG[1][3]),' ',' ',str(MINIMOG[1][2]),' ',' ',str(MINIMOG[0][3]),' ',' ',str(MINIMOG[1][3]),' '] - Kitten8 = [' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '] - Kitten9 = [str(MINIMOG[0][0]),' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',str(MINIMOG[2][1])] - Kitten = [Kitten1, Kitten2, Kitten3, Kitten4, Kitten5, Kitten6, Kitten7, Kitten8, Kitten9] - kitten = "\n".join("".join(u) for u in Kitten) - print kitten + kitten = ' {}'.format(MINIMOG[0][2]) + kitten += '\n ' + kitten += '\n {}'.format(MINIMOG[2][2]) + kitten += '\n {} {}'.format(MINIMOG[0][3], MINIMOG[1][3]) + kitten += '\n {} {} {}'.format(MINIMOG[1][0], MINIMOG[2][3], MINIMOG[0][1]) + kitten += '\n {0} {1} {2} {0}'.format(MINIMOG[2][2], MINIMOG[1][1], MINIMOG[2][0]) + kitten += '\n {0} {1} {2} {0} {1}'.format(MINIMOG[0][3], MINIMOG[1][3], MINIMOG[1][2]) + kitten += '\n \n' + kitten += '{} {}'.format(MINIMOG[0][0], MINIMOG[2][1]) + print(kitten) def find_hexad0(self, pts): """ + Find a hexad of type 0. + INPUT: - - pts -- a set of 2 distinct elements of MINIMOG, but not including the - "points at infinity" + - ``pts`` -- a set of 2 distinct elements of MINIMOG, but not + including the "points at infinity" OUTPUT: - - hexad containing pts and of type 0 (the 3 points "at infinity" union a line) + hexad containing ``pts`` and of type 0 (the 3 points "at + infinity" union a line) - NOTE: + .. NOTE:: - 3 points "at infinity" = {MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]} + The 3 points "at infinity" are + ``{MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]}``. EXAMPLES:: @@ -359,16 +363,21 @@ def find_hexad0(self, pts): def find_hexad1(self, pts): """ + Find a hexad of type 1. + INPUT: - pts -- a set pts of 5 distinct elements of MINIMOG + - ``pts`` -- a set of 5 distinct elements of MINIMOG OUTPUT: - hexad containing pts and of type 1 (union of 2 parallel lines -- *no* - points "at infinity") + hexad containing ``pts`` and of type 1 (union of 2 parallel + lines -- *no* points "at infinity") + + .. NOTE:: - NOTE: 3 points "at infinity" = {MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]} + The 3 points "at infinity" are + ``{MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]}``. EXAMPLES:: @@ -399,12 +408,14 @@ def find_hexad1(self, pts): return [],[] def find_hexad2(self, pts, x0): - """ + r""" + Find a hexad of type 2. + INPUT: - - pts -- a list S of 4 elements of MINIMOG, not including any "points - at infinity" - - x0 -- in {MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]} + - ``pts`` -- a list S of 4 elements of MINIMOG, not including + any "points at infinity" + - ``x0`` -- in ``{MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]}`` OUTPUT: @@ -418,11 +429,11 @@ def find_hexad2(self, pts, x0): ([], []) The above output indicates that there is no hexad of type 2 - containing {2,3,4,5}. However, there is one containing {2,3,4,8}:: + containing `\{2,3,4,5\}`. However, there is one containing + `\{2,3,4,8\}`:: sage: M.find_hexad2([2,3,4,8],0) ([0, 2, 3, 4, 8, 9], ['cross 12', 'picture 0']) - """ MINIMOG = self.minimog L = set(pts) @@ -444,11 +455,14 @@ def find_hexad2(self, pts, x0): def find_hexad3(self, pts, x0, x1): r""" + Find a hexad of type 3. + INPUT: - - pts -- a list of 3 elements of MINIMOG, not including any "points at - infinity" - - x0,x1 -- in {MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]} + - ``pts`` -- a list of 3 elements of MINIMOG, not including any + "points at infinity" + - ``x0``, ``x1`` -- in ``{MINIMOG[0][2], MINIMOG[2][1], + MINIMOG[0][0]}`` OUTPUT: @@ -483,26 +497,35 @@ def find_hexad3(self, pts, x0, x1): def find_hexad(self, pts): r""" + Find a hexad of some type. + INPUT: - pts -- a list S of 5 elements of MINIMOG + - ``pts`` -- a list S of 5 elements of MINIMOG OUTPUT: hexad containing `S \cup \{x0\}` of some type - NOTE: 3 "points at infinity" = {MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]} + .. NOTE:: + + The 3 "points at infinity" are + ``{MINIMOG[0][2], MINIMOG[2][1], MINIMOG[0][0]}``. - Theorem (Curtis, Conway): Each hexads is of exactly one of the following types: + Theorem ([Cur84]_, [Con84]_): Each hexads is of exactly one + of the following types: 0. {3 "points at infinity"} union {any line}, - 1. the union of any two (distinct) parallel lines in the same picture, - 2. one "point at infinity" union a cross in the corresponding picture, - 3. two "points at infinity" union a square in the picture corresponding - to the omitted point at infinity. + 1. the union of any two (distinct) parallel lines in the same + picture, + 2. one "point at infinity" union a cross in the corresponding + picture, or + 3. two "points at infinity" union a square in the picture + corresponding to the omitted point at infinity. - More precisely, there are 132 such hexads (12 of type 0, 12 of type 1, 54 of type 2, - and 54 of type 3). They form a Steiner system of type `(5,6,12)`. + More precisely, there are 132 such hexads (12 of type 0, + 12 of type 1, 54 of type 2, and 54 of type 3). + They form a Steiner system of type `(5,6,12)`. EXAMPLES:: @@ -529,14 +552,16 @@ def find_hexad(self, pts): MINIMOG = self.minimog L = set(pts) LL = L.copy() + pts_at_infty = set([MINIMOG[0][2],MINIMOG[2][1],MINIMOG[0][0]]) # recall & means intersection - L2 = LL & set([MINIMOG[0][2],MINIMOG[2][1],MINIMOG[0][0]]) + L2 = LL & pts_at_infty if len(L2) == 3: # must be type 0 (line + pts at infty) - H, WHAT = self.find_hexad0(LL - set([MINIMOG[0][2],MINIMOG[2][1],MINIMOG[0][0]])) + H, WHAT = self.find_hexad0(LL - pts_at_infty) return H, WHAT if len(L2) == 2: # type 0 or 3 if (MINIMOG[0][2] in LL and MINIMOG[2][1] in LL): - H, WHAT = self.find_hexad3(LL - set([MINIMOG[0][2],MINIMOG[2][1]]),MINIMOG[0][2],MINIMOG[2][1]) + H, WHAT = self.find_hexad3(LL - set([MINIMOG[0][2],MINIMOG[2][1]]), + MINIMOG[0][2], MINIMOG[2][1]) if H != []: # must be type 3 return list(H), WHAT if H == []: # could be type 0 @@ -544,7 +569,8 @@ def find_hexad(self, pts): if H != []: # must be type 0 return list(H), WHAT if (MINIMOG[2][1] in LL and MINIMOG[0][0] in LL): - H, WHAT = self.find_hexad3(LL - set([MINIMOG[2][1],MINIMOG[0][0]]),MINIMOG[2][1],MINIMOG[0][0]) + H, WHAT = self.find_hexad3(LL - set([MINIMOG[2][1],MINIMOG[0][0]]), + MINIMOG[2][1], MINIMOG[0][0]) if H != []: # must be type 3 return list(H), WHAT if H == []: # could be type 0 @@ -552,7 +578,8 @@ def find_hexad(self, pts): if H != []: # must be type 0 return list(H), WHAT if (MINIMOG[0][2] in LL and MINIMOG[0][0] in LL): - H, WHAT = self.find_hexad3(LL - set([MINIMOG[0][2],MINIMOG[0][0]]),MINIMOG[0][2],MINIMOG[0][0]) + H, WHAT = self.find_hexad3(LL - set([MINIMOG[0][2],MINIMOG[0][0]]), + MINIMOG[0][2], MINIMOG[0][0]) if H != []: # must be type 3 return list(H), WHAT if H == []: # could be type 0 @@ -560,7 +587,7 @@ def find_hexad(self, pts): if H != []: # must be type 0 return list(H), WHAT if len(L2) == 1: - H, WHAT = self.find_hexad2(LL - L2,list(L2)[0]) + H, WHAT = self.find_hexad2(LL - L2, list(L2)[0]) if H == []: # not a cross in picture at infinity if list(L2)[0] == MINIMOG[2][1]: L1 = LL - L2 @@ -591,9 +618,9 @@ def find_hexad(self, pts): return list(H), WHAT return list(H), WHAT # a cross in a pic at infty - if len(L2) == 0: # L is either a union of 2 lines or a cross + if not L2: # L is either a union of 2 lines or a cross for i in LL: - for j in [MINIMOG[0][2],MINIMOG[2][1],MINIMOG[0][0]]: + for j in pts_at_infty: H, WHAT = self.find_hexad2(LL - set([i]),j) if (H != [] and i in H): return list(H), WHAT # L is in a cross @@ -601,32 +628,43 @@ def find_hexad(self, pts): return H, WHAT def blackjack_move(self, L0): - """ - L is a list of cards of length 6, taken from {0,1,...,11}. + r""" + Perform a blackjack move. - MATHEMATICAL BLACKJACK + INPUT: + + - ``L0`` -- a list of cards of length 6, taken + from `\{0, 1, ..., 11\}` - Mathematical blackjack is played with 12 cards, labeled `0,...,11` - (for example: king, ace, `2`, `3`, ..., `10`, jack, where the king is - `0` and the jack is `11`). Divide the 12 cards into two piles of `6` (to be - fair, this should be done randomly). Each of the `6` cards of one of these - piles are to be placed face up on the table. The remaining cards are in a - stack which is shared and visible to both players. If the sum of the cards - face up on the table is less than 21 then no legal move is possible so you must - shuffle the cards and deal a new game. (Conway calls such a game `*={0|0}`, - where `0={|}`; in this game the first player automatically wins.) + .. RUBRIC:: MATHEMATICAL BLACKJACK + + Mathematical blackjack is played with 12 cards, labeled `0, ..., 11` + (for example: king, ace, `2`, `3`, ..., `10`, jack, where the + king is `0` and the jack is `11`). Divide the 12 cards into two + piles of `6` (to be fair, this should be done randomly). Each of + the `6` cards of one of these piles are to be placed face up on + the table. The remaining cards are in a stack which is shared + and visible to both players. If the sum of the cards face up on + the table is less than 21 then no legal move is possible so you + must shuffle the cards and deal a new game. (Conway calls such + a game `*={0|0}`, where `0={|}`; in this game the first player + automatically wins.) * Players alternate moves. - * A move consists of exchanging a card on the table with a lower card from the other pile. - * The player whose move makes the sum of the cards on the table under 21 loses. + * A move consists of exchanging a card on the table with a + lower card from the other pile. + * The player whose move makes the sum of the cards on the table + under 21 loses. - The winning strategy (given below) for this game is due to Conway and Ryba. - There is a Steiner system `S(5,6,12)` of hexads in the set `\{0,1,...,11\}`. - This Steiner system is associated to the MINIMOG of in the "shuffle numbering" - rather than the "modulo `11` labeling". + The winning strategy (given below) for this game is due to + Conway and Ryba. There is a Steiner system `S(5,6,12)` of hexads + in the set `\{0, 1, ..., 11\}`. This Steiner system is associated + to the MINIMOG of in the "shuffle numbering" rather than the + "modulo `11` labeling". - Proposition (Ryba, Conway) For this Steiner system, the winning strategy is to choose a - move which is a hexad from this system. + **Proposition** ([KahRyb01]_) For this Steiner system, the + winning strategy is to choose a move which is a hexad from + this system. EXAMPLES:: @@ -672,11 +710,12 @@ def blackjack_move(self, L0): for x in L: h, WHAT = self.find_hexad(L - set([x])) if list(L0) == list(h): - print " This is a hexad. \n There is no winning move, so make a random legal move." + print(" This is a hexad. \n There is no winning move, so make a random legal move.") return L0 y = list(set(h) - (L - set([x])))[0] #print x,y,h if y < x: return str(x) + ' --> ' + str(y) + ". The total went from " + str(total) + " to " + str(total - x + y) + "." - print "This is a hexad. \n There is no winning move, so make a random legal move." + print("This is a hexad. \n There is no winning move, so make a random legal move.") return L0 + diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 0a4c04df04a..c42c5890ec7 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -83,22 +83,23 @@ pentamino:: sage: for p in s: p # long time (<1s) - Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink - Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink - Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green - Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green - Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red - Polyomino: [(1, 0, 1), (2, 0, 1), (2, 1, 0), (2, 1, 1), (3, 1, 1)], Color: red - Polyomino: [(2, 0, 0), (3, 0, 0), (3, 0, 1), (3, 1, 0), (4, 0, 0)], Color: gray - Polyomino: [(3, 2, 0), (4, 0, 1), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: purple - Polyomino: [(3, 2, 1), (3, 3, 0), (3, 3, 1), (4, 2, 1), (4, 3, 1)], Color: yellow - Polyomino: [(3, 4, 1), (3, 5, 1), (4, 3, 0), (4, 4, 0), (4, 4, 1)], Color: blue - Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0)], Color: midnightblue + Polyomino: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 2, 0), (1, 1, 0)], Color: deeppink + Polyomino: [(0, 1, 1), (1, 1, 1), (1, 2, 1), (2, 1, 0), (2, 1, 1)], Color: deeppink + Polyomino: [(1, 0, 0), (1, 0, 1), (2, 0, 1), (3, 0, 1), (3, 1, 1)], Color: orange + Polyomino: [(2, 0, 0), (3, 0, 0), (4, 0, 0), (4, 0, 1), (4, 1, 1)], Color: green + Polyomino: [(0, 2, 1), (0, 3, 1), (0, 4, 1), (1, 4, 0), (1, 4, 1)], Color: green + Polyomino: [(0, 3, 0), (0, 4, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1)], Color: red + Polyomino: [(2, 2, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0)], Color: red + Polyomino: [(2, 2, 1), (2, 3, 0), (2, 3, 1), (2, 4, 0), (3, 4, 0)], Color: purple + Polyomino: [(3, 3, 0), (4, 2, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1)], Color: yellow + Polyomino: [(3, 3, 1), (3, 4, 1), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: blue + Polyomino: [(2, 4, 1), (2, 5, 0), (2, 5, 1), (3, 5, 0), (3, 6, 0)], Color: blue + Polyomino: [(3, 5, 1), (4, 5, 1), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: purple + Polyomino: [(2, 7, 1), (3, 6, 1), (3, 7, 0), (3, 7, 1), (4, 7, 1)], Color: gray Polyomino: [(0, 6, 0), (0, 7, 0), (0, 7, 1), (1, 7, 0), (2, 7, 0)], Color: darkblue - Polyomino: [(1, 7, 1), (2, 6, 0), (2, 6, 1), (2, 7, 1), (3, 6, 0)], Color: blue - Polyomino: [(1, 5, 1), (1, 6, 0), (1, 6, 1), (2, 5, 0), (2, 5, 1)], Color: yellow - Polyomino: [(3, 6, 1), (3, 7, 0), (3, 7, 1), (4, 5, 1), (4, 6, 1)], Color: purple - Polyomino: [(3, 5, 0), (4, 5, 0), (4, 6, 0), (4, 7, 0), (4, 7, 1)], Color: orange + Polyomino: [(1, 5, 1), (1, 6, 1), (1, 7, 1), (2, 6, 0), (2, 6, 1)], Color: midnightblue + Polyomino: [(0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0), (1, 6, 0)], Color: yellow + To get all the solutions, use the iterator returned by the ``solve`` method. Note that finding the first solution is the most time consuming @@ -231,8 +232,8 @@ def show_pentaminos(box=(5,8,2)): """ G = Graphics() for i,p in enumerate(pentaminos): - x = 3.5 * (i%4) - y = 3.5 * (i/4) + x = 4 * (i%4) + y = 4 * (i/4) q = p + (x, y, 0) G += q.show3d() G += text3d(str(i), (x,y,2)) @@ -372,7 +373,7 @@ def show3d(self, size=0.85): G = Graphics() for p in self: G += p.show3d(size=size) - aside_pento = self._aside.canonical() + (2.5*size/0.75,-4*size/0.75,0) + aside_pento = self._aside.canonical() + (2,-4,0) G += aside_pento.show3d(size=size) # the box to fill @@ -504,22 +505,22 @@ def solve(self, partial=None): The explicit solution:: sage: for p in s: p # long time (fast) - Polyomino: [(0, 0, 0), (1, 0, 0), (1, 1, 0), (1, 1, 1), (1, 2, 0)], Color: deeppink - Polyomino: [(0, 0, 1), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)], Color: deeppink - Polyomino: [(0, 2, 0), (0, 3, 0), (0, 4, 0), (1, 4, 0), (1, 4, 1)], Color: green - Polyomino: [(0, 3, 1), (1, 3, 1), (2, 2, 0), (2, 2, 1), (2, 3, 1)], Color: green - Polyomino: [(1, 3, 0), (2, 3, 0), (2, 4, 0), (2, 4, 1), (3, 4, 0)], Color: red - Polyomino: [(1, 0, 1), (2, 0, 0), (2, 0, 1), (2, 1, 0), (3, 0, 1)], Color: midnightblue - Polyomino: [(0, 4, 1), (0, 5, 0), (0, 5, 1), (0, 6, 0), (1, 5, 0)], Color: red - Polyomino: [(2, 1, 1), (3, 0, 0), (3, 1, 0), (3, 1, 1), (4, 0, 0)], Color: blue - Polyomino: [(3, 2, 0), (4, 0, 1), (4, 1, 0), (4, 1, 1), (4, 2, 0)], Color: purple - Polyomino: [(3, 2, 1), (3, 3, 0), (4, 2, 1), (4, 3, 0), (4, 3, 1)], Color: yellow - Polyomino: [(3, 3, 1), (3, 4, 1), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: blue - Polyomino: [(0, 6, 1), (0, 7, 0), (0, 7, 1), (1, 5, 1), (1, 6, 1)], Color: purple - Polyomino: [(1, 6, 0), (1, 7, 0), (1, 7, 1), (2, 7, 0), (3, 7, 0)], Color: darkblue - Polyomino: [(2, 5, 0), (2, 6, 0), (3, 6, 0), (4, 6, 0), (4, 6, 1)], Color: orange - Polyomino: [(2, 5, 1), (3, 5, 0), (3, 5, 1), (3, 6, 1), (4, 5, 1)], Color: gray - Polyomino: [(2, 6, 1), (2, 7, 1), (3, 7, 1), (4, 7, 0), (4, 7, 1)], Color: orange + Polyomino: [(0, 0, 0), (0, 0, 1), (0, 1, 0), (0, 2, 0), (1, 1, 0)], Color: deeppink + Polyomino: [(0, 1, 1), (1, 1, 1), (1, 2, 1), (2, 1, 0), (2, 1, 1)], Color: deeppink + Polyomino: [(1, 0, 0), (1, 0, 1), (2, 0, 1), (3, 0, 1), (3, 1, 1)], Color: orange + Polyomino: [(2, 0, 0), (3, 0, 0), (4, 0, 0), (4, 0, 1), (4, 1, 1)], Color: green + Polyomino: [(0, 2, 1), (0, 3, 1), (0, 4, 1), (1, 4, 0), (1, 4, 1)], Color: green + Polyomino: [(0, 3, 0), (0, 4, 0), (1, 2, 0), (1, 3, 0), (1, 3, 1)], Color: red + Polyomino: [(2, 2, 0), (3, 1, 0), (3, 2, 0), (3, 2, 1), (4, 1, 0)], Color: red + Polyomino: [(2, 2, 1), (2, 3, 0), (2, 3, 1), (2, 4, 1), (3, 3, 0)], Color: midnightblue + Polyomino: [(3, 3, 1), (3, 4, 1), (4, 2, 0), (4, 2, 1), (4, 3, 1)], Color: purple + Polyomino: [(3, 4, 0), (4, 3, 0), (4, 4, 0), (4, 4, 1), (4, 5, 0)], Color: gray + Polyomino: [(1, 5, 1), (1, 6, 1), (2, 4, 0), (2, 5, 0), (2, 5, 1)], Color: blue + Polyomino: [(0, 5, 0), (0, 5, 1), (0, 6, 1), (1, 5, 0), (1, 6, 0)], Color: yellow + Polyomino: [(0, 6, 0), (0, 7, 0), (0, 7, 1), (1, 7, 0), (2, 7, 0)], Color: darkblue + Polyomino: [(1, 7, 1), (2, 6, 0), (2, 6, 1), (2, 7, 1), (3, 6, 0)], Color: blue + Polyomino: [(3, 7, 0), (4, 5, 1), (4, 6, 0), (4, 6, 1), (4, 7, 0)], Color: purple + Polyomino: [(3, 5, 0), (3, 5, 1), (3, 6, 1), (3, 7, 1), (4, 7, 1)], Color: orange Enumerate the solutions:: diff --git a/src/sage/geometry/cone.py b/src/sage/geometry/cone.py index db59d20e9ce..e9a5493a7a3 100644 --- a/src/sage/geometry/cone.py +++ b/src/sage/geometry/cone.py @@ -2294,7 +2294,7 @@ def face_lattice(self): C1 and C2 are equal, but not identical. We currently want them to have non identical face lattices, even if the faces - themselves are equal (see #10998):: + themselves are equal (see :trac:`10998`):: sage: C1.face_lattice() is C2.face_lattice() False @@ -4482,7 +4482,7 @@ def lineality(self): REFERENCES: - .. [Rockafellar] R.T. Rockafellar. Convex Analysis. Princeton + .. [Rockafellar] \R.T. Rockafellar. Convex Analysis. Princeton University Press, Princeton, 1970. EXAMPLES: @@ -4564,7 +4564,7 @@ def discrete_complementarity_set(self): REFERENCES: - .. [Orlitzky] M. Orlitzky. The Lyapunov rank of an improper cone. + .. [Orlitzky] \M. Orlitzky. The Lyapunov rank of an improper cone. http://www.optimization-online.org/DB_HTML/2015/10/5135.html EXAMPLES: @@ -4655,7 +4655,7 @@ def lyapunov_like_basis(self): M. Orlitzky. The Lyapunov rank of an improper cone. http://www.optimization-online.org/DB_HTML/2015/10/5135.html - .. [Rudolf] G. Rudolf, N. Noyan, D. Papp, and F. Alizadeh. + .. [Rudolf] \G. Rudolf, N. Noyan, D. Papp, and F. Alizadeh. Bilinear optimality constraints for the cone of positive polynomials. Mathematical Programming, Series B, 129 (2011) 5-31. @@ -4824,7 +4824,7 @@ def lyapunov_rank(self): REFERENCES: - .. [Gowda-Tao] M.S. Gowda and J. Tao. On the bilinearity rank of + .. [Gowda-Tao] \M.S. Gowda and J. Tao. On the bilinearity rank of a proper cone and Lyapunov-like transformations. Mathematical Programming, 147 (2014) 155-170. @@ -5240,7 +5240,7 @@ def random_cone(lattice=None, min_ambient_dim=0, max_ambient_dim=None, cone, ``False`` for a non-strictly-convex cone, or ``None`` if you don't care. - * ``solid`` (defalt: random) -- Whether or not to make the returned + * ``solid`` (default: random) -- Whether or not to make the returned cone solid. Specify ``True`` for a solid cone, ``False`` for a non-solid cone, or ``None`` if you don't care. diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index ed2eb245ed3..87f374eb589 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -137,7 +137,7 @@ def bigraphical(self, G, A=None, K=QQ, names=None): REFERENCES: - .. [BigraphicalArrangements] S. Hopkins, D. Perkinson. + .. [BigraphicalArrangements] \S. Hopkins, D. Perkinson. "Bigraphical Arrangements". :arxiv:`1212.4398` """ @@ -399,7 +399,7 @@ def Ish(self, n, K=QQ, names=None): REFERENCES: - .. [AR] D. Armstrong, B. Rhoades + .. [AR] \D. Armstrong, B. Rhoades "The Shi arrangement and the Ish arrangement" :arxiv:`1009.1655` """ diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index e8ea034ace8..d8755fadca5 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -737,9 +737,10 @@ def _element_constructor_(self, arg, sort_slopes=True, last_slope=Infinity): raise TypeError("argument must be a list of coordinates or a list of (rational) slopes") x += 1 y += slope - vertices.append((x,y)) + vertices.append((x, y)) else: vertices = [(x, y) for (x, y) in arg if y is not Infinity] + if len(vertices) == 0: polyhedron = Polyhedron(base_ring=self.base_ring(), ambient_dim=2) else: diff --git a/src/sage/geometry/polyhedra.py b/src/sage/geometry/polyhedra.py deleted file mode 100644 index d353e4564a9..00000000000 --- a/src/sage/geometry/polyhedra.py +++ /dev/null @@ -1,3 +0,0 @@ -from sage.misc.superseded import deprecation -deprecation(11634, 'The module sage.geometry.polyhedra has been removed, use sage.geometry.polyhedron instead.') -from polyhedron.all import * diff --git a/src/sage/geometry/polyhedron/base.py b/src/sage/geometry/polyhedron/base.py index e5eed84d004..442d26d4af5 100644 --- a/src/sage/geometry/polyhedron/base.py +++ b/src/sage/geometry/polyhedron/base.py @@ -1032,17 +1032,27 @@ def n_lines(self): """ return len(self.lines()) - def to_linear_program(self, solver=None): + def to_linear_program(self, solver=None, return_variable=False, base_ring=None): r""" - Return the polyhedron as a :class:`MixedIntegerLinearProgram`. + Return a linear optimization problem over the polyhedron in the form of + a :class:`MixedIntegerLinearProgram`. INPUT: - - ``solver`` -- select a solver (data structure). See the documentation + - ``solver`` -- select a solver (MIP backend). See the documentation of for :class:`MixedIntegerLinearProgram`. Set to ``None`` by default. + - ``return_variable`` -- (default: ``False``) If ``True``, return a tuple + ``(p, x)``, where ``p`` is the :class:`MixedIntegerLinearProgram` object + and ``x`` is the vector-valued MIP variable in this problem, indexed + from 0. If ``False``, only return ``p``. + + - ``base_ring`` -- select a field over which the linear program should be + set up. Use ``RDF`` to request a fast inexact (floating point) solver + even if ``self`` is exact. + Note that the :class:`MixedIntegerLinearProgram` object will have the - null function as an objective. + null function as an objective to be maximized. .. SEEALSO:: @@ -1050,10 +1060,56 @@ def to_linear_program(self, solver=None): polyhedron associated with a :class:`MixedIntegerLinearProgram` object. - EXAMPLE:: + EXAMPLES: + + Exact rational linear program:: - sage: polytopes.cube().to_linear_program() + sage: p = polytopes.cube() + sage: p.to_linear_program() Mixed Integer Program ( maximization, 3 variables, 6 constraints ) + sage: lp, x = p.to_linear_program(return_variable=True) + sage: lp.set_objective(2*x[0] + 1*x[1] + 39*x[2]) + sage: lp.solve() + 42 + sage: lp.get_values(x[0], x[1], x[2]) + [1, 1, 1] + + Floating-point linear program:: + + sage: lp, x = p.to_linear_program(return_variable=True, base_ring=RDF) + sage: lp.set_objective(2*x[0] + 1*x[1] + 39*x[2]) + sage: lp.solve() + 42.0 + + Irrational algebraic linear program over an embedded number field:: + + sage: p=polytopes.icosahedron() + sage: lp, x = p.to_linear_program(return_variable=True) + sage: lp.set_objective(x[0] + x[1] + x[2]) + sage: lp.solve() + 1/4*sqrt5 + 3/4 + + Same example with floating point:: + + sage: lp, x = p.to_linear_program(return_variable=True, base_ring=RDF) + sage: lp.set_objective(x[0] + x[1] + x[2]) + sage: lp.solve() # tol 1e-5 + 1.3090169943749475 + + Same example with a specific floating point solver:: + + sage: lp, x = p.to_linear_program(return_variable=True, solver='GLPK') + sage: lp.set_objective(x[0] + x[1] + x[2]) + sage: lp.solve() # tol 1e-8 + 1.3090169943749475 + + Irrational algebraic linear program over `AA`:: + + sage: p=polytopes.icosahedron(base_ring=AA) + sage: lp, x = p.to_linear_program(return_variable=True) + sage: lp.set_objective(x[0] + x[1] + x[2]) + sage: lp.solve() + 1.309016994374948? TESTS:: @@ -1065,17 +1121,13 @@ def to_linear_program(self, solver=None): sage: p.to_linear_program(solver='PPL') Traceback (most recent call last): ... - NotImplementedError: Cannot use PPL on exact irrational data. + TypeError: The PPL backend only supports rational data. """ - from sage.rings.rational_field import QQ - R = self.base_ring() - if (solver is not None and - solver.lower() == 'ppl' and - R.is_exact() and (not R == QQ)): - raise NotImplementedError('Cannot use PPL on exact irrational data.') - + if base_ring is None: + base_ring = self.base_ring() + base_ring = base_ring.fraction_field() from sage.numerical.mip import MixedIntegerLinearProgram - p = MixedIntegerLinearProgram(solver=solver) + p = MixedIntegerLinearProgram(solver=solver, base_ring=base_ring) x = p.new_variable(real=True, nonnegative=False) for ineqn in self.inequalities_list(): @@ -1086,7 +1138,10 @@ def to_linear_program(self, solver=None): b = -eqn.pop(0) p.add_constraint(p.sum([x[i]*eqn[i] for i in range(len(eqn))]) == -b) - return p + if return_variable: + return p, x + else: + return p def Hrepresentation(self, index=None): """ @@ -3780,7 +3835,7 @@ def contains(self, point): sage: ray.contains(['hello', 'kitty']) # no common ring for coordinates False - The empty polyhedron needs extra care, see trac #10238:: + The empty polyhedron needs extra care, see :trac:`10238`:: sage: empty = Polyhedron(); empty The empty polyhedron in ZZ^0 @@ -3846,7 +3901,7 @@ def interior_contains(self, point): sage: P.interior_contains( [0,0] ) False - The empty polyhedron needs extra care, see trac #10238:: + The empty polyhedron needs extra care, see :trac:`10238`:: sage: empty = Polyhedron(); empty The empty polyhedron in ZZ^0 @@ -3896,7 +3951,7 @@ def relative_interior_contains(self, point): sage: P.relative_interior_contains( (1,0) ) False - The empty polyhedron needs extra care, see trac #10238:: + The empty polyhedron needs extra care, see :trac:`10238`:: sage: empty = Polyhedron(); empty The empty polyhedron in ZZ^0 @@ -4681,7 +4736,7 @@ def affine_hull(self): gens.append(l.vector()) # Pick subset of coordinates to coordinatize the affine span - pivots = matrix(gens, base_ring=self.base_ring()).pivots() + pivots = matrix(gens).pivots() def pivot(indexed): return [indexed[i] for i in pivots] diff --git a/src/sage/geometry/polyhedron/representation.py b/src/sage/geometry/polyhedron/representation.py index 3249ceb5184..2ba07f2ab75 100644 --- a/src/sage/geometry/polyhedron/representation.py +++ b/src/sage/geometry/polyhedron/representation.py @@ -2,14 +2,16 @@ H(yperplane) and V(ertex) representation objects for polyhedra """ -######################################################################## +#***************************************************************************** # Copyright (C) 2008 Marshall Hampton # Copyright (C) 2011 Volker Braun # -# Distributed under the terms of the GNU General Public License (GPL) -# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ -######################################################################## +#***************************************************************************** from sage.structure.sage_object import SageObject @@ -1067,7 +1069,6 @@ def type(self): """ return self.VERTEX - def is_vertex(self): """ Tests if this object is a vertex. By construction it always is. @@ -1098,6 +1099,28 @@ def _repr_(self): """ return 'A vertex at ' + repr(self.vector()); + def homogeneous_vector(self, base_ring=None): + """ + Return homogeneous coordinates for this vertex. + + Since a vertex is given by an affine point, this is the vector + with a 1 appended. + + INPUT: + + - ``base_ring`` -- the base ring of the vector. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(2,0)], rays=[(1,0)], lines=[(3,2)]) + sage: P.vertices()[0].homogeneous_vector() + (2, 0, 1) + sage: P.vertices()[0].homogeneous_vector(RDF) + (2.0, 0.0, 1.0) + """ + v = list(self._vector) + [1] + return vector(base_ring or self._base_ring, v) + def evaluated_on(self, Hobj): r""" Returns `A\vec{x}+b` @@ -1167,7 +1190,6 @@ def type(self): """ return self.RAY - def is_ray(self): """ Tests if this object is a ray. Always True by construction. @@ -1194,6 +1216,28 @@ def _repr_(self): """ return 'A ray in the direction ' + repr(self.vector()); + def homogeneous_vector(self, base_ring=None): + """ + Return homogeneous coordinates for this ray. + + Since a ray is given by a direction, this is the vector with a + 0 appended. + + INPUT: + + - ``base_ring`` -- the base ring of the vector. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(2,0)], rays=[(1,0)], lines=[(3,2)]) + sage: P.rays()[0].homogeneous_vector() + (1, 0, 0) + sage: P.rays()[0].homogeneous_vector(RDF) + (1.0, 0.0, 0.0) + """ + v = list(self._vector) + [0] + return vector(base_ring or self._base_ring, v) + def evaluated_on(self, Hobj): r""" Returns `A\vec{r}` @@ -1270,6 +1314,28 @@ def _repr_(self): """ return 'A line in the direction ' + repr(self.vector()); + def homogeneous_vector(self, base_ring=None): + """ + Return homogeneous coordinates for this line. + + Since a line is given by a direction, this is the vector with a + 0 appended. + + INPUT: + + - ``base_ring`` -- the base ring of the vector. + + EXAMPLES:: + + sage: P = Polyhedron(vertices=[(2,0)], rays=[(1,0)], lines=[(3,2)]) + sage: P.lines()[0].homogeneous_vector() + (3, 2, 0) + sage: P.lines()[0].homogeneous_vector(RDF) + (3.0, 2.0, 0.0) + """ + v = list(self._vector) + [0] + return vector(base_ring or self._base_ring, v) + def evaluated_on(self, Hobj): r""" Returns `A\vec{\ell}` @@ -1283,5 +1349,3 @@ def evaluated_on(self, Hobj): 0 """ return Hobj.A() * self.vector() - - diff --git a/src/sage/graphs/asteroidal_triples.pyx b/src/sage/graphs/asteroidal_triples.pyx index aebcb9155cf..c15986baf80 100644 --- a/src/sage/graphs/asteroidal_triples.pyx +++ b/src/sage/graphs/asteroidal_triples.pyx @@ -48,11 +48,11 @@ over all triples. References ---------- -.. [Koh04] E. Kohler. *Recognizing graphs without asteroidal triples*. Journal of +.. [Koh04] \E. Kohler. *Recognizing graphs without asteroidal triples*. Journal of Discrete Algorithms 2(4):439-452, Dec. 2004 http://dx.doi.org/10.1016/j.jda.2004.04.005 -.. [LB62] C. G. Lekkerkerker, J. Ch. Boland. *Representation of a finite graph +.. [LB62] \C. G. Lekkerkerker, J. Ch. Boland. *Representation of a finite graph by a set of intervals on the real line*. Fundamenta Mathematicae, 51:45-64, 1962. diff --git a/src/sage/graphs/base/boost_graph.pyx b/src/sage/graphs/base/boost_graph.pyx index 894e542952e..2973d2b417f 100644 --- a/src/sage/graphs/base/boost_graph.pyx +++ b/src/sage/graphs/base/boost_graph.pyx @@ -841,9 +841,9 @@ cpdef shortest_paths(g, start, weight_function=None, algorithm=None): pred = {} if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. @@ -954,9 +954,9 @@ cpdef johnson_shortest_paths(g, weight_function = None): raise ValueError("The graph contains a negative cycle.") if weight_function is not None: - correct_type = type(weight_function(g.edge_iterator().next())) + correct_type = type(weight_function(next(g.edge_iterator()))) elif g.weighted(): - correct_type = type(g.edge_iterator().next()[2]) + correct_type = type(next(g.edge_iterator())[2]) else: correct_type = int # Needed for rational curves. diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index 50deaa6532d..dee5c4a3dea 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -410,7 +410,7 @@ cdef class CGraph: if self.out_degrees[v] > size: size = self.out_degrees[v] if size > 0: - neighbors = sage_malloc(size * sizeof(int)) + neighbors = sig_malloc(size * sizeof(int)) if not neighbors: raise RuntimeError("Failure allocating memory.") # delete each arc incident with v @@ -420,7 +420,7 @@ cdef class CGraph: num_nbrs = self.out_neighbors_unsafe(v, neighbors, size) for i in range(num_nbrs): self.del_arc_unsafe(v, neighbors[i]) - sage_free(neighbors) + sig_free(neighbors) self.num_verts -= 1 bitset_remove(self.active_vertices, v) @@ -1043,6 +1043,8 @@ cdef class CGraph: sage: from sage.graphs.base.sparse_graph import SparseGraph sage: SparseGraph(7)._in_degree(3) + doctest:...: DeprecationWarning: _in_degree is deprecated + See http://trac.sagemath.org/20253 for details. 0 TEST:: @@ -1051,6 +1053,8 @@ cdef class CGraph: sage: g._backend.degree(5, False) 3 """ + from sage.misc.superseded import deprecation + deprecation(20253, "_in_degree is deprecated") if not self.has_vertex(v): raise LookupError("Vertex ({0}) is not a vertex of the graph.".format(v)) return self.in_degrees[v] @@ -1071,8 +1075,12 @@ cdef class CGraph: sage: from sage.graphs.base.sparse_graph import SparseGraph sage: SparseGraph(7)._out_degree(3) + doctest:...: DeprecationWarning: _out_degree is deprecated + See http://trac.sagemath.org/20253 for details. 0 """ + from sage.misc.superseded import deprecation + deprecation(20253, "_out_degree is deprecated") if not self.has_vertex(v): raise LookupError("Vertex ({0}) is not a vertex of the graph.".format(v)) return self.out_degrees[v] @@ -1093,8 +1101,12 @@ cdef class CGraph: sage: from sage.graphs.base.sparse_graph import SparseGraph sage: SparseGraph(7)._num_verts() + doctest:...: DeprecationWarning: _num_verts is deprecated + See http://trac.sagemath.org/20253 for details. 7 """ + from sage.misc.superseded import deprecation + deprecation(20253, "_num_verts is deprecated") return self.num_verts def _num_arcs(self): @@ -1113,8 +1125,12 @@ cdef class CGraph: sage: from sage.graphs.base.sparse_graph import SparseGraph sage: SparseGraph(7)._num_arcs() + doctest:...: DeprecationWarning: _num_arcs is deprecated + See http://trac.sagemath.org/20253 for details. 0 """ + from sage.misc.superseded import deprecation + deprecation(20253, "_num_arcs is deprecated") return self.num_arcs cdef class CGraphBackend(GenericGraphBackend): @@ -1435,14 +1451,14 @@ cdef class CGraphBackend(GenericGraphBackend): """ cdef int v_int = self.get_vertex(v) if directed: - return self._cg._in_degree(v_int) + self._cg._out_degree(v_int) + return self._cg.in_degrees[v_int] + self._cg.out_degrees[v_int] d = 0 if self._loops and self.has_edge(v, v, None): if self._multiple_edges: d += len(self.get_edge_label(v, v)) else: d += 1 - return self._cg._out_degree(v_int) + d + return self._cg.out_degrees[v_int] + d def out_degree(self, v): r""" @@ -1461,7 +1477,7 @@ cdef class CGraphBackend(GenericGraphBackend): """ cdef int v_int = self.get_vertex(v) if self._directed: - return self._cg._out_degree(v_int) + return self._cg.out_degrees[v_int] d = 0 if self._loops and self.has_edge(v, v, None): if self._multiple_edges: @@ -1469,7 +1485,7 @@ cdef class CGraphBackend(GenericGraphBackend): else: d += 1 - return self._cg._out_degree(v_int) + d + return self._cg.out_degrees[v_int] + d def in_degree(self, v): r""" @@ -1491,7 +1507,7 @@ cdef class CGraphBackend(GenericGraphBackend): cdef int v_int = self.get_vertex(v) - return self._cg_rev._out_degree(v_int) + return self._cg_rev.out_degrees[v_int] def add_vertex(self, name): """ @@ -1911,7 +1927,7 @@ cdef class CGraphBackend(GenericGraphBackend): TESTS: - Ensure that ticket #8395 is fixed. :: + Ensure that :trac:`8395` is fixed. :: sage: G = Graph({1:[1]}); G Looped graph on 1 vertex @@ -1971,9 +1987,9 @@ cdef class CGraphBackend(GenericGraphBackend): 1 """ if directed: - return self._cg._num_arcs() + return self._cg.num_arcs else: - i = self._cg._num_arcs() + i = self._cg.num_arcs k = 0 if self.loops(None): if self.multiple_edges(None): diff --git a/src/sage/graphs/base/dense_graph.pyx b/src/sage/graphs/base/dense_graph.pyx index 29bf399164d..4f46955498a 100644 --- a/src/sage/graphs/base/dense_graph.pyx +++ b/src/sage/graphs/base/dense_graph.pyx @@ -19,21 +19,19 @@ This example initializes a dense graph with room for twenty vertices, the first ten of which are in the graph. In general, the first ``nverts`` are "active." For example, see that 9 is already in the graph:: - sage: D._num_verts() - 10 + sage: D.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] sage: D.add_vertex(9) 9 - sage: D._num_verts() - 10 + sage: D.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] But 10 is not, until we add it:: - sage: D._num_verts() - 10 sage: D.add_vertex(10) 10 - sage: D._num_verts() - 11 + sage: D.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] You can begin working right away as follows:: @@ -56,10 +54,6 @@ You can begin working right away as follows:: sage: D.del_vertex(7) sage: D.has_arc(7,3) False - sage: D._num_verts() - 10 - sage: D._num_arcs() - 2 Dense graphs do not support multiple or labeled edges. @@ -177,14 +171,14 @@ cdef class DenseGraph(CGraph): # self.num_longs = "ceil(total_verts/radix)" self.num_longs = total_verts / radix + (0 != (total_verts & radix_mod_mask)) - self.edges = sage_calloc(total_verts * self.num_longs, sizeof(unsigned long)) - self.in_degrees = sage_calloc(total_verts, sizeof(int)) - self.out_degrees = sage_calloc(total_verts, sizeof(int)) + self.edges = sig_calloc(total_verts * self.num_longs, sizeof(unsigned long)) + self.in_degrees = sig_calloc(total_verts, sizeof(int)) + self.out_degrees = sig_calloc(total_verts, sizeof(int)) if not self.edges or not self.in_degrees or not self.out_degrees: - sage_free(self.edges) - sage_free(self.in_degrees) - sage_free(self.out_degrees) + sig_free(self.edges) + sig_free(self.in_degrees) + sig_free(self.out_degrees) raise MemoryError bitset_init(self.active_vertices, total_verts) @@ -201,9 +195,9 @@ cdef class DenseGraph(CGraph): """ New and dealloc are both tested at class level. """ - sage_free(self.edges) - sage_free(self.in_degrees) - sage_free(self.out_degrees) + sig_free(self.edges) + sig_free(self.in_degrees) + sig_free(self.out_degrees) bitset_free(self.active_vertices) cpdef realloc(self, int total_verts): @@ -275,15 +269,15 @@ cdef class DenseGraph(CGraph): min_longs = self.num_longs # Resize of self.edges - cdef unsigned long *new_edges = sage_calloc(total_verts * self.num_longs, sizeof(unsigned long)) + cdef unsigned long *new_edges = sig_calloc(total_verts * self.num_longs, sizeof(unsigned long)) for i from 0 <= i < min_verts: memcpy(new_edges+i*self.num_longs, self.edges+i*old_longs, min_longs*sizeof(unsigned long)) - sage_free(self.edges) + sig_free(self.edges) self.edges = new_edges - self.in_degrees = sage_realloc(self.in_degrees , total_verts * sizeof(int)) - self.out_degrees = sage_realloc(self.out_degrees, total_verts * sizeof(int)) + self.in_degrees = sig_realloc(self.in_degrees , total_verts * sizeof(int)) + self.out_degrees = sig_realloc(self.out_degrees, total_verts * sizeof(int)) for i in range(self.active_vertices.size, total_verts): self.in_degrees[i] = 0 @@ -518,12 +512,12 @@ cdef class DenseGraph(CGraph): if self.out_degrees[u] == 0: return [] cdef int size = self.out_degrees[u] - cdef int *neighbors = sage_malloc(size * sizeof(int)) + cdef int *neighbors = sig_malloc(size * sizeof(int)) if not neighbors: raise MemoryError num_nbrs = self.out_neighbors_unsafe(u, neighbors, size) output = [neighbors[i] for i from 0 <= i < num_nbrs] - sage_free(neighbors) + sig_free(neighbors) return output cdef int in_neighbors_unsafe(self, int v, int *neighbors, int size) except -2: @@ -577,12 +571,12 @@ cdef class DenseGraph(CGraph): if self.in_degrees[v] == 0: return [] cdef int size = self.in_degrees[v] - cdef int *neighbors = sage_malloc(size * sizeof(int)) + cdef int *neighbors = sig_malloc(size * sizeof(int)) if not neighbors: raise MemoryError num_nbrs = self.in_neighbors_unsafe(v, neighbors, size) output = [neighbors[i] for i from 0 <= i < num_nbrs] - sage_free(neighbors) + sig_free(neighbors) return output ############################## @@ -609,17 +603,17 @@ def _test_adjacency_sequence_out(): cdef DenseGraph g = DenseGraph(n, verts=randg.vertices(), arcs=randg.edges(labels=False)) - assert g._num_verts() == randg.order(), ( - "Graph order mismatch: %s vs. %s" % (g._num_verts(), randg.order())) - assert g._num_arcs() == randg.size(), ( - "Graph size mismatch: %s vs. %s" % (g._num_arcs(), randg.size())) + assert g.num_verts == randg.order(), ( + "Graph order mismatch: %s vs. %s" % (g.num_verts, randg.order())) + assert g.num_arcs == randg.size(), ( + "Graph size mismatch: %s vs. %s" % (g.num_arcs, randg.size())) M = randg.adjacency_matrix() - cdef int *V = sage_malloc(n * sizeof(int)) + cdef int *V = sig_malloc(n * sizeof(int)) cdef int i = 0 for v in randg.vertex_iterator(): V[i] = v i += 1 - cdef int *seq = sage_malloc(n * sizeof(int)) + cdef int *seq = sig_malloc(n * sizeof(int)) for 0 <= i < randint(50, 101): u = randint(low, n - 1) g.adjacency_sequence_out(n, V, u, seq) @@ -627,11 +621,11 @@ def _test_adjacency_sequence_out(): try: assert A == list(M[u]) except AssertionError: - sage_free(V) - sage_free(seq) + sig_free(V) + sig_free(seq) raise AssertionError("Graph adjacency mismatch") - sage_free(seq) - sage_free(V) + sig_free(seq) + sig_free(V) ########################################### # Dense Graph Backend diff --git a/src/sage/graphs/base/sparse_graph.pyx b/src/sage/graphs/base/sparse_graph.pyx index 5fb96fe7859..53763646694 100644 --- a/src/sage/graphs/base/sparse_graph.pyx +++ b/src/sage/graphs/base/sparse_graph.pyx @@ -19,21 +19,19 @@ This example initializes a sparse graph with room for twenty vertices, the first ten of which are in the graph. In general, the first ``nverts`` are "active." For example, see that 9 is already in the graph:: - sage: S._num_verts() - 10 + sage: S.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] sage: S.add_vertex(9) 9 - sage: S._num_verts() - 10 + sage: S.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] But 10 is not, until we add it:: - sage: S._num_verts() - 10 sage: S.add_vertex(10) 10 - sage: S._num_verts() - 11 + sage: S.verts() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] You can begin working with unlabeled arcs right away as follows:: @@ -58,10 +56,6 @@ You can begin working with unlabeled arcs right away as follows:: Traceback (most recent call last): ... LookupError: Vertex (7) is not a vertex of the graph. - sage: S._num_verts() - 10 - sage: S._num_arcs() - 2 Sparse graphs support multiple edges and labeled edges, but requires that the labels be positive integers (the case label = 0 is treated as no label). @@ -282,15 +276,15 @@ cdef class SparseGraph(CGraph): # Allocating memory self.vertices = \ - sage_malloc(nverts * self.hash_length * sizeof(SparseGraphBTNode *)) - self.in_degrees = sage_malloc(nverts * sizeof(int)) - self.out_degrees = sage_malloc(nverts * sizeof(int)) + sig_malloc(nverts * self.hash_length * sizeof(SparseGraphBTNode *)) + self.in_degrees = sig_malloc(nverts * sizeof(int)) + self.out_degrees = sig_malloc(nverts * sizeof(int)) # Checking the memory was actually allocated if not self.vertices or not self.in_degrees or not self.out_degrees: - if self.vertices: sage_free(self.vertices) - if self.in_degrees: sage_free(self.in_degrees) - if self.out_degrees: sage_free(self.out_degrees) + if self.vertices: sig_free(self.vertices) + if self.in_degrees: sig_free(self.in_degrees) + if self.out_degrees: sig_free(self.out_degrees) raise RuntimeError("Failure allocating memory.") # Initializing variables: @@ -338,15 +332,15 @@ cdef class SparseGraph(CGraph): label_temp = temp[0].labels while label_temp != NULL: temp[0].labels = label_temp.next - sage_free(label_temp) + sig_free(label_temp) label_temp = temp[0].labels - sage_free(temp[0]) + sig_free(temp[0]) temp[0] = NULL temp = &(self.vertices[i]) - sage_free(self.vertices) - sage_free(self.in_degrees) - sage_free(self.out_degrees) + sig_free(self.vertices) + sig_free(self.in_degrees) + sig_free(self.out_degrees) bitset_free(self.active_vertices) cpdef realloc(self, int total): @@ -403,9 +397,9 @@ cdef class SparseGraph(CGraph): return -1 bitset_free(bits) - self.vertices = sage_realloc(self.vertices, total * self.hash_length * sizeof(SparseGraphBTNode *)) - self.in_degrees = sage_realloc(self.in_degrees, total * sizeof(int)) - self.out_degrees = sage_realloc(self.out_degrees, total * sizeof(int)) + self.vertices = sig_realloc(self.vertices, total * self.hash_length * sizeof(SparseGraphBTNode *)) + self.in_degrees = sig_realloc(self.in_degrees, total * sizeof(int)) + self.out_degrees = sig_realloc(self.out_degrees, total * sizeof(int)) cdef int new_vertices = total - self.active_vertices.size @@ -450,7 +444,7 @@ cdef class SparseGraph(CGraph): ins_pt[0].number += 1 break if ins_pt[0] == NULL: - ins_pt[0] = sage_malloc(sizeof(SparseGraphBTNode)) + ins_pt[0] = sig_malloc(sizeof(SparseGraphBTNode)) if not ins_pt[0]: raise RuntimeError("Failure allocating memory.") ins_pt[0].vertex = v @@ -581,7 +575,7 @@ cdef class SparseGraph(CGraph): while labels != NULL: i = labels.number parent[0].labels = parent[0].labels.next - sage_free(labels) + sig_free(labels) labels = parent[0].labels self.in_degrees[v] -= i self.out_degrees[u] -= i @@ -594,14 +588,14 @@ cdef class SparseGraph(CGraph): if parent[0].left == NULL: temp = parent[0] parent[0] = parent[0].right - sage_free(temp) + sig_free(temp) return 0 # If there is no right child elif parent[0].right == NULL: temp = parent[0] parent[0] = parent[0].left - sage_free(temp) + sig_free(temp) return 0 # Both children @@ -632,7 +626,7 @@ cdef class SparseGraph(CGraph): parent[0] = left_child[0] left_child[0] = left_child[0].left parent[0].left = temp.left - sage_free(temp) + sig_free(temp) return 0 else: right_child[0].left = parent[0].left @@ -640,7 +634,7 @@ cdef class SparseGraph(CGraph): parent[0] = right_child[0] right_child[0] = right_child[0].right parent[0].right = temp.right - sage_free(temp) + sig_free(temp) return 0 cpdef del_all_arcs(self, int u, int v): @@ -706,7 +700,7 @@ cdef class SparseGraph(CGraph): neighbors[i] = pointers[0][i].vertex n_neighbors = -1 - sage_free(pointers[0]) + sig_free(pointers[0]) return n_neighbors cdef int out_neighbors_BTNode_unsafe(self, int u, SparseGraphBTNode *** p_pointers): @@ -735,7 +729,7 @@ cdef class SparseGraph(CGraph): if degree == 0: p_pointers[0] = NULL return 0 - cdef SparseGraphBTNode **pointers = sage_malloc(degree * sizeof(SparseGraphBTNode *)) + cdef SparseGraphBTNode **pointers = sig_malloc(degree * sizeof(SparseGraphBTNode *)) p_pointers[0] = pointers if pointers == NULL: raise RuntimeError("Failure allocating memory.") @@ -783,12 +777,12 @@ cdef class SparseGraph(CGraph): if self.out_degrees[u] == 0: return [] cdef int size = self.out_degrees[u] - cdef int *neighbors = sage_malloc(size * sizeof(int)) + cdef int *neighbors = sig_malloc(size * sizeof(int)) if not neighbors: raise RuntimeError("Failure allocating memory.") num_nbrs = self.out_neighbors_unsafe(u, neighbors, size) output = [neighbors[i] for i from 0 <= i < num_nbrs] - sage_free(neighbors) + sig_free(neighbors) return output cpdef int out_degree(self, int u): @@ -857,7 +851,7 @@ cdef class SparseGraph(CGraph): label = label.next if pointers[0] != NULL: - sage_free(pointers[0]) + sig_free(pointers[0]) return l @@ -918,12 +912,12 @@ cdef class SparseGraph(CGraph): if self.in_degrees[v] == 0: return [] cdef int size = self.in_degrees[v] - cdef int *neighbors = sage_malloc(size * sizeof(int)) + cdef int *neighbors = sig_malloc(size * sizeof(int)) if not neighbors: raise RuntimeError("Failure allocating memory.") num_nbrs = self.in_neighbors_unsafe(v, neighbors, size) output = [neighbors[i] for i from 0 <= i < num_nbrs] - sage_free(neighbors) + sig_free(neighbors) return output cpdef int in_degree(self, int u): @@ -977,7 +971,7 @@ cdef class SparseGraph(CGraph): else: break if ins_pt[0] == NULL: - ins_pt[0] = sage_malloc(sizeof(SparseGraphBTNode)) + ins_pt[0] = sig_malloc(sizeof(SparseGraphBTNode)) if not ins_pt[0]: raise RuntimeError("Failure allocating memory.") ins_pt[0].number = 0 @@ -990,9 +984,9 @@ cdef class SparseGraph(CGraph): while label_ptr != NULL and label_ptr.label != l: label_ptr = label_ptr.next if label_ptr == NULL: - label_ptr = sage_malloc(sizeof(SparseGraphLLNode)) + label_ptr = sig_malloc(sizeof(SparseGraphLLNode)) if not label_ptr: - sage_free(ins_pt[0]) + sig_free(ins_pt[0]) raise RuntimeError("Failure allocating memory.") label_ptr.label = l label_ptr.number = 1 @@ -1181,15 +1175,15 @@ cdef class SparseGraph(CGraph): size = self.in_degrees[v] else: size = self.out_degrees[u] - arc_labels = sage_malloc(size * sizeof(int)) + arc_labels = sig_malloc(size * sizeof(int)) if not arc_labels: raise RuntimeError("Failure allocating memory.") num_arcs = self.all_arcs_unsafe(u, v, arc_labels, size) if num_arcs == -1: - sage_free(arc_labels) + sig_free(arc_labels) raise RuntimeError("There was an error: there seem to be more arcs than self.in_degrees or self.out_degrees indicate.") output = [arc_labels[i] for i from 0 <= i < num_arcs] - sage_free(arc_labels) + sig_free(arc_labels) return output cdef int del_arc_label_unsafe(self, int u, int v, int l): @@ -1239,7 +1233,7 @@ cdef class SparseGraph(CGraph): label.number -= 1 else: labels[0] = labels[0].next - sage_free(label) + sig_free(label) if labels == &(parent[0].labels) and labels[0] == NULL and parent[0].number == 0: # here we need to delete an "empty" binary tree node self.del_arc_unsafe(u, v) @@ -1370,17 +1364,17 @@ def _test_adjacency_sequence_out(): cdef SparseGraph g = SparseGraph(n, verts=randg.vertices(), arcs=E) - assert g._num_verts() == randg.order(), ( - "Graph order mismatch: %s vs. %s" % (g._num_verts(), randg.order())) - assert g._num_arcs() == randg.size(), ( - "Graph size mismatch: %s vs. %s" % (g._num_arcs(), randg.size())) + assert g.num_verts == randg.order(), ( + "Graph order mismatch: %s vs. %s" % (g.num_verts, randg.order())) + assert g.num_arcs == randg.size(), ( + "Graph size mismatch: %s vs. %s" % (g.num_arcs, randg.size())) M = randg.adjacency_matrix() - cdef int *V = sage_malloc(n * sizeof(int)) + cdef int *V = sig_malloc(n * sizeof(int)) cdef int i = 0 for v in randg.vertex_iterator(): V[i] = v i += 1 - cdef int *seq = sage_malloc(n * sizeof(int)) + cdef int *seq = sig_malloc(n * sizeof(int)) for 0 <= i < randint(50, 101): u = randint(low, n - 1) g.adjacency_sequence_out(n, V, u, seq) @@ -1388,11 +1382,11 @@ def _test_adjacency_sequence_out(): try: assert A == list(M[u]) except AssertionError: - sage_free(V) - sage_free(seq) + sig_free(V) + sig_free(seq) raise AssertionError("Graph adjacency mismatch") - sage_free(seq) - sage_free(V) + sig_free(seq) + sig_free(V) ########################################### # Sparse Graph Backend diff --git a/src/sage/graphs/base/static_dense_graph.pxd b/src/sage/graphs/base/static_dense_graph.pxd index d5f43764fdd..acad753d261 100644 --- a/src/sage/graphs/base/static_dense_graph.pxd +++ b/src/sage/graphs/base/static_dense_graph.pxd @@ -1,5 +1,4 @@ from sage.data_structures.binary_matrix cimport binary_matrix_t from libc.stdint cimport uint32_t, uint64_t -from sage.ext.memory cimport check_calloc cdef dict dense_graph_init(binary_matrix_t m, g, translation = ?) diff --git a/src/sage/graphs/base/static_dense_graph.pyx b/src/sage/graphs/base/static_dense_graph.pyx index 6b04dc7845c..297bc0e3a6f 100644 --- a/src/sage/graphs/base/static_dense_graph.pyx +++ b/src/sage/graphs/base/static_dense_graph.pyx @@ -275,6 +275,6 @@ def triangles_count(G): bitset_free(b_tmp) binary_matrix_free(g) - sage_free(count) + sig_free(count) return ans diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index 580755c0645..53bc7db4eab 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -44,7 +44,7 @@ from c_graph cimport CGraphBackend from sage.data_structures.bitset cimport FrozenBitset from libc.stdint cimport uint32_t include 'sage/data_structures/bitset.pxi' -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" cdef class StaticSparseCGraph(CGraph): """ @@ -65,6 +65,13 @@ cdef class StaticSparseCGraph(CGraph): sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph sage: g = StaticSparseCGraph(graphs.PetersenGraph()) + + Check that the digraph methods are working (see :trac:`20253`):: + + sage: G = DiGraph([(0,1),(1,0)]) + sage: G2 = G.copy(immutable=True) + sage: G2.is_strongly_connected() + True """ cdef int i, j, tmp has_labels = any(l is not None for _,_,l in G.edge_iterator()) @@ -92,9 +99,13 @@ cdef class StaticSparseCGraph(CGraph): break # Defining the meaningless set of 'active' vertices. Because of CGraph. + # As well as num_verts and num_edges bitset_init(self.active_vertices, self.g.n+1) bitset_set_first_n(self.active_vertices, self.g.n) + self.num_verts = self.g.n + self.num_arcs = self.g.m + def __dealloc__(self): r""" Freeing the memory @@ -106,7 +117,7 @@ cdef class StaticSparseCGraph(CGraph): """ bitset_free(self.active_vertices) free_short_digraph(self.g) - sage_free(self.number_of_loops) + sig_free(self.number_of_loops) if self.g_rev != NULL: free_short_digraph(self.g_rev) diff --git a/src/sage/graphs/base/static_sparse_graph.pxd b/src/sage/graphs/base/static_sparse_graph.pxd index 9cbd8631abf..35c224f9f42 100644 --- a/src/sage/graphs/base/static_sparse_graph.pxd +++ b/src/sage/graphs/base/static_sparse_graph.pxd @@ -1,7 +1,7 @@ from cpython cimport PyObject from libc.stdint cimport uint32_t, uint64_t from sage.data_structures.bitset cimport * -from sage.ext.memory cimport check_calloc + ctypedef unsigned short ushort ctypedef unsigned int uint diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index 834ceb09b11..2bac48a2bc8 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -188,9 +188,17 @@ from sage.graphs.base.c_graph cimport CGraph from static_sparse_backend cimport StaticSparseCGraph from static_sparse_backend cimport StaticSparseBackend from sage.ext.memory_allocator cimport MemoryAllocator -from sage.ext.memory cimport check_allocarray +include "cysignals/memory.pxi" from libcpp.vector cimport vector +cdef extern from "fenv.h": + int FE_TONEAREST + int FE_UPWARD + int FE_DOWNWARD + int FE_TOWARDZERO + int fegetround () + int fesetround (int) + cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1: r""" Initializes ``short_digraph g`` from a Sage (Di)Graph. @@ -228,11 +236,11 @@ cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1 for i, v in enumerate(vertices): v_to_id[v] = i - g.edges = sage_malloc(n_edges*sizeof(uint32_t)) + g.edges = sig_malloc(n_edges*sizeof(uint32_t)) if g.edges == NULL: raise ValueError("Problem while allocating memory (edges)") - g.neighbors = sage_malloc((1+g.n)*sizeof(uint32_t *)) + g.neighbors = sig_malloc((1+g.n)*sizeof(uint32_t *)) if g.neighbors == NULL: raise ValueError("Problem while allocating memory (neighbors)") @@ -305,11 +313,11 @@ cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1: dst.edge_labels = NULL cdef list edge_labels - dst.edges = sage_malloc(n_edges(src)*sizeof(uint32_t)) + dst.edges = sig_malloc(n_edges(src)*sizeof(uint32_t)) if dst.edges == NULL: raise ValueError("Problem while allocating memory (edges)") - dst.neighbors = sage_malloc((src.n+1)*sizeof(uint32_t *)) + dst.neighbors = sig_malloc((src.n+1)*sizeof(uint32_t *)) if dst.neighbors == NULL: raise ValueError("Problem while allocating memory (neighbors)") @@ -333,7 +341,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: # vector. With this information, we can initialize dst.neighbors to its # correct value. The content of dst.edges is not touched at this level. - cdef int * in_degree = sage_malloc(src.n*sizeof(int)) + cdef int * in_degree = sig_malloc(src.n*sizeof(int)) if in_degree == NULL: raise ValueError("Problem while allocating memory (in_degree)") @@ -347,7 +355,7 @@ cdef int init_reverse(short_digraph dst, short_digraph src) except -1: dst.neighbors[0] = dst.edges for i in range(1, src.n+1): dst.neighbors[i] = dst.neighbors[i-1] + in_degree[i-1] - sage_free(in_degree) + sig_free(in_degree) #### 2/3 # @@ -404,7 +412,7 @@ cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except # We will be doing a Depth-First Search. We allocate the stack we need for # that, and put "src" on top of it. - cdef int * stack = sage_malloc(g.n*sizeof(int)) + cdef int * stack = sig_malloc(g.n*sizeof(int)) if stack == NULL: raise ValueError("Problem while allocating memory (stack)") @@ -437,7 +445,7 @@ cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except v += 1 - sage_free(stack) + sig_free(stack) cdef int tarjan_strongly_connected_components_C(short_digraph g, int *scc): r""" @@ -773,10 +781,10 @@ cdef strongly_connected_component_containing_vertex(short_digraph g, short_digra cdef void free_short_digraph(short_digraph g): if g.edges != NULL: - sage_free(g.edges) + sig_free(g.edges) if g.neighbors != NULL: - sage_free(g.neighbors) + sig_free(g.neighbors) if g.edge_labels != NULL: cpython.Py_XDECREF(g.edge_labels) @@ -838,5 +846,209 @@ def triangles_count(G): ans = {w:Integer(count[i]/2) for i,w in enumerate(G.vertices())} - sage_free(count) + sig_free(count) return ans + +def spectral_radius(G, prec=1e-10): + r""" + Return an interval of floating point number that encloses the spectral + radius of this graph + + The input graph ``G`` must be *strongly connected*. + + INPUT: + + - ``prec`` -- (default ``1e-10``) an upper bound for the relative precision + of the interval + + The algorithm is iterative and uses an inequality valid for non-negative + matrices. Namely, if `A` is a non-negative square matrix with + Perron-Frobenius eigenvalue `\lambda` then the following inequality is valid + for any vector `x` + + .. MATH:: + + \min_i \frac{(Ax)_i}{x_i} \leq \lambda \leq \max_i \frac{(Ax)_i}{x_i} + + .. NOTE:: + + The speed of convergence of the algorithm is governed by the spectral + gap (the distance to the second largest modulus of other eigenvalues). + If this gap is small, then this function might not be appropriate. + + The algorithm is not smart and not parallel! It uses basic interval + arithmetic and native floating point arithmetic. + + EXAMPLES:: + + sage: from sage.graphs.base.static_sparse_graph import spectral_radius + + sage: G = DiGraph([(0,0),(0,1),(1,0)], loops=True) + sage: phi = (RR(1) + RR(5).sqrt() ) / 2 + sage: phi # abs tol 1e-14 + 1.618033988749895 + sage: e_min, e_max = spectral_radius(G, 1e-14) + sage: e_min, e_max # abs tol 1e-14 + (1.618033988749894, 1.618033988749896) + sage: (e_max - e_min) # abs tol 1e-14 + 1e-14 + sage: e_min < phi < e_max + True + + This function also works for graphs:: + + sage: G = Graph([(0,1),(0,2),(1,2),(1,3),(2,4),(3,4)]) + sage: e_min, e_max = spectral_radius(G, 1e-14) + sage: e = max(G.adjacency_matrix().charpoly().roots(AA, multiplicities=False)) + sage: e_min < e < e_max + True + + sage: G.spectral_radius() # abs tol 1e-9 + (2.48119430408, 2.4811943041) + + A larger example:: + + sage: G = DiGraph() + sage: G.add_edges((i,i+1) for i in range(200)) + sage: G.add_edge(200,0) + sage: G.add_edge(1,0) + sage: e_min, e_max = spectral_radius(G, 0.00001) + sage: p = G.adjacency_matrix(sparse=True).charpoly() + sage: p + x^201 - x^199 - 1 + sage: r = p.roots(AA, multiplicities=False)[0] + sage: e_min < r < e_max + True + + A much larger example:: + + sage: G = DiGraph(100000) + sage: r = range(100000) + sage: while not G.is_strongly_connected(): + ....: shuffle(r) + ....: G.add_edges(enumerate(r)) + sage: spectral_radius(G, 1e-10) # random + (1.9997956006500042, 1.9998043797692782) + + The algorithm takes care of multiple edges:: + + sage: G = DiGraph(2,loops=True,multiedges=True) + sage: G.add_edges([(0,0),(0,0),(0,1),(1,0)]) + sage: spectral_radius(G, 1e-14) # abs tol 1e-14 + (2.414213562373094, 2.414213562373095) + sage: max(G.adjacency_matrix().eigenvalues(AA)) + 2.414213562373095? + + TESTS:: + + sage: spectral_radius(G, 1e-20) + Traceback (most recent call last): + ... + ValueError: precision (=1.00000000000000e-20) is too small + + sage: for _ in range(100): + ....: G = digraphs.RandomDirectedGNM(10,35) + ....: if not G.is_strongly_connected(): + ....: continue + ....: e = max(G.adjacency_matrix().charpoly().roots(AA,multiplicities=False)) + ....: e_min, e_max = G.spectral_radius(1e-13) + ....: assert e_min < e < e_max + """ + if G.is_directed(): + if not G.is_strongly_connected(): + raise ValueError("G must be strongly connected") + elif not G.is_connected(): + raise ValueError("G must be connected") + + cdef double c_prec = prec + if 1+c_prec/2 == 1: + raise ValueError("precision (={!r}) is too small".format(prec)) + + # make a copy of G if needed to obtain a static sparse graph + # NOTE: the following potentially copies the labels of the graph which is + # comptely useless for the computation! + cdef short_digraph g + G = G.copy(immutable=True) + g[0] = ( ( G._backend)._cg).g[0] + + cdef long n = g.n + cdef long m = g.m + cdef uint32_t ** neighbors = g.neighbors + + cdef double * v1 = sig_malloc(n * sizeof(double)) + cdef double * v2 = sig_malloc(n * sizeof(double)) + cdef double * v3 + if v1 == NULL or v2 == NULL: + sig_free(v1) + sig_free(v2) + raise MemoryError + + cdef size_t i + cdef uint32_t *p + cdef double e_min, e_max + cdef double s + + for i in range(n): + v1[i] = 1 + s = n + + cdef int old_rounding = fegetround() + e_max = m + e_min = 0 + try: + sig_on() + while (e_max - e_min) > e_max * c_prec: + # renormalize + s = n/s + for i in range(n): + v1[i] *= s + + # computing e_max (with upward rounding) + e_max = 0 + fesetround(FE_UPWARD) + p = neighbors[0] + for i in range(n): + v2[i] = 0 + while p < neighbors[i+1]: + v2[i] += v1[p[0]] + p += 1 + e = v2[i] / v1[i] + if e > e_max: + e_max = e + + # computing e_min (with downward rounding) + e_min = m + fesetround(FE_DOWNWARD) + p = neighbors[0] + for i in range(n): + v2[i] = 0 + while p < neighbors[i+1]: + v2[i] += v1[p[0]] + p += 1 + s += v2[i] + e = v2[i] / v1[i] + if e < e_min: + e_min = e + + # computing the next vector (with nearest rounding) + fesetround(FE_TONEAREST) + s = 0 + p = neighbors[0] + for i in range(n): + v2[i] = 0 + while p < neighbors[i+1]: + v2[i] += v1[p[0]] + p += 1 + s += v2[i] + v3 = v1; v1 = v2; v2 = v3 + + sig_off() + finally: + # be sure that the rounding is back to default + fesetround(old_rounding) + + # and that the memory is freed + sig_free(v1) + sig_free(v2) + + return (e_min, e_max) diff --git a/src/sage/graphs/bliss.pyx b/src/sage/graphs/bliss.pyx index 685530d754f..5a5dfc38590 100644 --- a/src/sage/graphs/bliss.pyx +++ b/src/sage/graphs/bliss.pyx @@ -28,7 +28,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' from cpython cimport PyObject from libc.limits cimport LONG_MAX diff --git a/src/sage/graphs/centrality.pyx b/src/sage/graphs/centrality.pyx index 9e5a714bf86..0ba5f59ab5d 100644 --- a/src/sage/graphs/centrality.pyx +++ b/src/sage/graphs/centrality.pyx @@ -22,7 +22,7 @@ from libc.string cimport memset from libc.stdint cimport uint32_t from sage.libs.gmp.mpq cimport * from sage.rings.rational cimport Rational -from sage.ext.memory cimport check_malloc, check_calloc +include "cysignals/memory.pxi" from sage.ext.memory_allocator cimport MemoryAllocator ctypedef fused numerical_type: @@ -306,11 +306,11 @@ cdef dict centrality_betweenness_C(G, numerical_type _, normalize=True): free_short_digraph(bfs_dag) bitset_free(seen) bitset_free(next_layer) - sage_free(queue) - sage_free(n_paths_from_source) - sage_free(degrees) - sage_free(betweenness_source) - sage_free(betweenness) + sig_free(queue) + sig_free(n_paths_from_source) + sig_free(degrees) + sig_free(betweenness_source) + sig_free(betweenness) if not G.is_directed(): betweenness_list = [x/2 for x in betweenness_list] diff --git a/src/sage/graphs/chrompoly.pyx b/src/sage/graphs/chrompoly.pyx index 03728d51f8d..759b3024706 100644 --- a/src/sage/graphs/chrompoly.pyx +++ b/src/sage/graphs/chrompoly.pyx @@ -23,7 +23,6 @@ from sage.ext.memory_allocator cimport MemoryAllocator from sage.misc.all import prod include "cysignals/signals.pxi" include 'sage/ext/cdefs.pxi' -include 'sage/ext/stdsage.pxi' def chromatic_polynomial(G, return_tree_basis = False): """ diff --git a/src/sage/graphs/cliquer.pyx b/src/sage/graphs/cliquer.pyx index cb4c36d2264..1953e15f181 100644 --- a/src/sage/graphs/cliquer.pyx +++ b/src/sage/graphs/cliquer.pyx @@ -36,7 +36,7 @@ Methods include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" cdef extern from "sage/graphs/cliquer/cl.c": @@ -88,7 +88,7 @@ def max_clique(graph): for i in range(size): b.append(list[i]) - sage_free(list) + sig_free(list) graph_free(g) return list_composition(b,d_inv) @@ -166,7 +166,7 @@ def all_max_clique(graph): b.append(list_composition(c,d_inv)) c=[] - sage_free(list) + sig_free(list) graph_free(g) return sorted(b) diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index 526cb09c21c..26d9c869cde 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -211,7 +211,7 @@ Methods #***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from copy import copy @@ -744,7 +744,7 @@ def is_transitive(g, certificate = False): for i in range(n): if ((c_distances[i] != -1) and (c_distances[i] > 1)): - sage_free(distances) + sig_free(distances) if certificate: return int_to_vertex[j], int_to_vertex[i] @@ -753,5 +753,5 @@ def is_transitive(g, certificate = False): c_distances += n - sage_free(distances) + sig_free(distances) return True diff --git a/src/sage/graphs/convexity_properties.pyx b/src/sage/graphs/convexity_properties.pyx index 9634ef1e617..fbc267f993c 100644 --- a/src/sage/graphs/convexity_properties.pyx +++ b/src/sage/graphs/convexity_properties.pyx @@ -172,7 +172,7 @@ cdef class ConvexityProperties: # _cache_hull_pairs[u*n + v] is a bitset whose 1 bits are the vertices located on a shortest path from vertex u to v # # Note that u < v - self._cache_hull_pairs = sage_malloc(((n*(n-1))>>1)*sizeof(bitset_t)) + self._cache_hull_pairs = sig_malloc(((n*(n-1))>>1)*sizeof(bitset_t)) cdef bitset_t * p_bitset = self._cache_hull_pairs # Filling the cache @@ -227,7 +227,7 @@ cdef class ConvexityProperties: bitset_free(p_bitset[0]) p_bitset = p_bitset + 1 - sage_free(self._cache_hull_pairs) + sig_free(self._cache_hull_pairs) cdef list _vertices_to_integers(self, vertices): r""" @@ -423,7 +423,7 @@ cdef class ConvexityProperties: REFERENCE: - .. [CHZ02] F. Harary, E. Loukakis, C. Tsouros + .. [CHZ02] \F. Harary, E. Loukakis, C. Tsouros The geodetic number of a graph Mathematical and computer modelling vol. 17 n11 pp.89--95, 1993 diff --git a/src/sage/graphs/digraph_generators.py b/src/sage/graphs/digraph_generators.py index 5764d31679d..51aa0e841ec 100644 --- a/src/sage/graphs/digraph_generators.py +++ b/src/sage/graphs/digraph_generators.py @@ -755,11 +755,11 @@ def GeneralizedDeBruijn(self, n, d): REFERENCES: - .. [RPK80] S. M. Reddy, D. K. Pradhan, and J. Kuhl. Directed graphs with + .. [RPK80] \S. M. Reddy, D. K. Pradhan, and J. Kuhl. Directed graphs with minimal diameter and maximal connectivity, School Eng., Oakland Univ., Rochester MI, Tech. Rep., July 1980. - .. [RPK83] S. Reddy, P. Raghavan, and J. Kuhl. A Class of Graphs for + .. [RPK83] \S. Reddy, P. Raghavan, and J. Kuhl. A Class of Graphs for Processor Interconnection. *IEEE International Conference on Parallel Processing*, pages 154-157, Los Alamitos, Ca., USA, August 1983. """ @@ -828,7 +828,7 @@ def ImaseItoh(self, n, d): REFERENCE: - .. [II83] M. Imase and M. Itoh. A design for directed graphs with + .. [II83] \M. Imase and M. Itoh. A design for directed graphs with minimum diameter, *IEEE Trans. Comput.*, vol. C-32, pp. 782-784, 1983. """ if n < 2: @@ -932,7 +932,7 @@ def Kautz(self, k, D, vertices = 'strings'): REFERENCE: - .. [Kautz68] W. H. Kautz. Bounds on directed (d, k) graphs. Theory of + .. [Kautz68] \W. H. Kautz. Bounds on directed (d, k) graphs. Theory of cellular logic networks and machines, AFCRL-68-0668, SRI Project 7258, Final Rep., pp. 20-28, 1968. """ @@ -1062,10 +1062,10 @@ def RandomDirectedGNP(self, n, p, loops = False, seed = None): REFERENCES: - .. [1] P. Erdos and A. Renyi, On Random Graphs, Publ. Math. 6, 290 + .. [1] \P. Erdos and A. Renyi, On Random Graphs, Publ. Math. 6, 290 (1959). - .. [2] E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959). + .. [2] \E. N. Gilbert, Random Graphs, Ann. Math. Stat., 30, 1141 (1959). PLOTTING: When plotting, this graph will use the default spring-layout diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index dd1174ba719..c0422f1619f 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -110,28 +110,28 @@ AUTHOR: REFERENCE: -.. [KRG96b] S. Klavzar, A. Rajapakse, and I. Gutman. The Szeged and the +.. [KRG96b] \S. Klavzar, A. Rajapakse, and I. Gutman. The Szeged and the Wiener index of graphs. *Applied Mathematics Letters*, 9(5):45--49, 1996. -.. [GYLL93c] I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent +.. [GYLL93c] \I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent results in the theory of the Wiener number. *Indian Journal of Chemistry*, 32A:651--661, 1993. -.. [CGH+13] P. Crescenzi, R. Grossi, M. Habib, L. Lanzi, A. Marino. On computing +.. [CGH+13] \P. Crescenzi, R. Grossi, M. Habib, L. Lanzi, A. Marino. On computing the diameter of real-world undirected graphs. *Theor. Comput. Sci.* 514: 84-95 (2013) http://dx.doi.org/10.1016/j.tcs.2012.09.018 -.. [CGI+10] P. Crescenzi, R. Grossi, C. Imbrenda, L. Lanzi, and A. Marino. +.. [CGI+10] \P. Crescenzi, R. Grossi, C. Imbrenda, L. Lanzi, and A. Marino. Finding the Diameter in Real-World Graphs: Experimentally Turning a Lower Bound into an Upper Bound. Proceedings of *18th Annual European Symposium on Algorithms*. Lecture Notes in Computer Science, vol. 6346, 302-313. Springer (2010). -.. [MLH08] C. Magnien, M. Latapy, and M. Habib. Fast computation of empirically +.. [MLH08] \C. Magnien, M. Latapy, and M. Habib. Fast computation of empirically tight bounds for the diameter of massive graphs. *ACM Journal of Experimental Algorithms* 13 (2008) http://dx.doi.org/10.1145/1412228.1455266 -.. [TK13] F. W. Takes and W. A. Kosters. Computing the eccentricity distribution +.. [TK13] \F. W. Takes and W. A. Kosters. Computing the eccentricity distribution of large graphs. *Algorithms* 6:100-118 (2013) http://dx.doi.org/10.3390/a6010100 @@ -312,16 +312,16 @@ cdef unsigned short * c_shortest_path_all_pairs(G) except NULL: """ cdef unsigned int n = G.order() - cdef unsigned short * distances = sage_malloc(n*n*sizeof(unsigned short)) + cdef unsigned short * distances = sig_malloc(n*n*sizeof(unsigned short)) if distances == NULL: raise MemoryError() - cdef unsigned short * predecessors = sage_malloc(n*n*sizeof(unsigned short)) + cdef unsigned short * predecessors = sig_malloc(n*n*sizeof(unsigned short)) if predecessors == NULL: - sage_free(distances) + sig_free(distances) raise MemoryError() all_pairs_shortest_path_BFS(G, predecessors, distances, NULL) - sage_free(distances) + sig_free(distances) return predecessors @@ -387,7 +387,7 @@ def shortest_path_all_pairs(G): c_predecessors += n - sage_free(predecessors) + sig_free(predecessors) return d ############# @@ -404,7 +404,7 @@ cdef unsigned short * c_distances_all_pairs(G): """ cdef unsigned int n = G.order() - cdef unsigned short * distances = sage_malloc(n*n*sizeof(unsigned short)) + cdef unsigned short * distances = sig_malloc(n*n*sizeof(unsigned short)) if distances == NULL: raise MemoryError() all_pairs_shortest_path_BFS(G, NULL, distances, NULL) @@ -462,7 +462,7 @@ def distances_all_pairs(G): d[int_to_vertex[j]] = d_tmp c_distances += n - sage_free(distances) + sig_free(distances) return d def is_distance_regular(G, parameters = False): @@ -551,7 +551,7 @@ def is_distance_regular(G, parameters = False): try: binary_matrix_init(b_distance_matrix,n*(diameter+2),n) except MemoryError: - sage_free(distance_matrix) + sig_free(distance_matrix) bitset_free(b_tmp) raise @@ -589,12 +589,12 @@ def is_distance_regular(G, parameters = False): ci[d] = c elif bi[d] != b or ci[d] != c: - sage_free(distance_matrix) + sig_free(distance_matrix) binary_matrix_free(b_distance_matrix) bitset_free(b_tmp) return False - sage_free(distance_matrix) + sig_free(distance_matrix) binary_matrix_free(b_distance_matrix) bitset_free(b_tmp) @@ -662,13 +662,13 @@ def distances_and_predecessors_all_pairs(G): if n == 0: return {}, {} - cdef unsigned short * distances = sage_malloc(n*n*sizeof(unsigned short)) + cdef unsigned short * distances = sig_malloc(n*n*sizeof(unsigned short)) if distances == NULL: raise MemoryError() cdef unsigned short * c_distances = distances - cdef unsigned short * predecessor = sage_malloc(n*n*sizeof(unsigned short)) + cdef unsigned short * predecessor = sig_malloc(n*n*sizeof(unsigned short)) if predecessor == NULL: - sage_free(distances) + sig_free(distances) raise MemoryError() cdef unsigned short * c_predecessor = predecessor @@ -701,8 +701,8 @@ def distances_and_predecessors_all_pairs(G): c_distances += n c_predecessor += n - sage_free(distances) - sage_free(predecessor) + sig_free(distances) + sig_free(predecessor) return d_distance, d_predecessor @@ -719,7 +719,7 @@ cdef uint32_t * c_eccentricity(G) except NULL: """ cdef unsigned int n = G.order() - cdef uint32_t * ecc = sage_calloc(n, sizeof(uint32_t)) + cdef uint32_t * ecc = sig_calloc(n, sizeof(uint32_t)) if ecc == NULL: raise MemoryError() all_pairs_shortest_path_BFS(G, NULL, NULL, ecc) @@ -752,12 +752,12 @@ cdef uint32_t * c_eccentricity_bounding(G) except NULL: # allocated some data structures cdef bitset_t seen bitset_init(seen, n) - cdef uint32_t * distances = sage_malloc(3 * n * sizeof(uint32_t)) - cdef uint32_t * LB = sage_calloc(n, sizeof(uint32_t)) + cdef uint32_t * distances = sig_malloc(3 * n * sizeof(uint32_t)) + cdef uint32_t * LB = sig_calloc(n, sizeof(uint32_t)) if distances==NULL or LB==NULL: bitset_free(seen) - sage_free(LB) - sage_free(distances) + sig_free(LB) + sig_free(distances) free_short_digraph(sd) raise MemoryError() cdef uint32_t * waiting_list = distances + n @@ -799,7 +799,7 @@ cdef uint32_t * c_eccentricity_bounding(G) except NULL: sig_off() - sage_free(distances) + sig_free(distances) bitset_free(seen) free_short_digraph(sd) @@ -888,7 +888,7 @@ def eccentricity(G, algorithm="standard"): from sage.rings.integer import Integer cdef list l_ecc = [Integer(ecc[i]) if ecc[i]!=UINT32_MAX else +Infinity for i in range(n)] - sage_free(ecc) + sig_free(ecc) return l_ecc @@ -1097,7 +1097,7 @@ cdef tuple diameter_lower_bound_multi_sweep(uint32_t n, # Allocate some arrays and a bitset cdef bitset_t seen bitset_init(seen, n) - cdef uint32_t * distances = sage_malloc(3 * n * sizeof(uint32_t)) + cdef uint32_t * distances = sig_malloc(3 * n * sizeof(uint32_t)) if distances==NULL: bitset_free(seen) raise MemoryError() @@ -1110,7 +1110,7 @@ cdef tuple diameter_lower_bound_multi_sweep(uint32_t n, # infinite. tmp = diameter_lower_bound_2sweep(n, p_vertices, source, distances, predecessors, waiting_list, seen) if tmp==UINT32_MAX: - sage_free(distances) + sig_free(distances) bitset_free(seen) return (UINT32_MAX, 0, 0, 0) @@ -1135,7 +1135,7 @@ cdef tuple diameter_lower_bound_multi_sweep(uint32_t n, # We perform a new 2sweep call from m tmp = diameter_lower_bound_2sweep(n, p_vertices, m, distances, predecessors, waiting_list, seen) - sage_free(distances) + sig_free(distances) bitset_free(seen) return (LB, s, m, d) @@ -1180,7 +1180,7 @@ cdef uint32_t diameter_iFUB(uint32_t n, # We allocate some arrays and a bitset cdef bitset_t seen bitset_init(seen, n) - cdef uint32_t * distances = sage_malloc(4 * n * sizeof(uint32_t)) + cdef uint32_t * distances = sig_malloc(4 * n * sizeof(uint32_t)) if distances==NULL: bitset_free(seen) raise MemoryError() @@ -1226,7 +1226,7 @@ cdef uint32_t diameter_iFUB(uint32_t n, LB = tmp - sage_free(distances) + sig_free(distances) bitset_free(seen) # We finally return the computed diameter @@ -1374,7 +1374,7 @@ def diameter(G, algorithm='iFUB', source=None): if algorithm=='2sweep': # We need to allocate arrays and bitset bitset_init(seen, n) - tab = sage_malloc(2* n * sizeof(uint32_t)) + tab = sig_malloc(2* n * sizeof(uint32_t)) if tab == NULL: free_short_digraph(sd) bitset_free(seen) @@ -1383,7 +1383,7 @@ def diameter(G, algorithm='iFUB', source=None): LB = diameter_lower_bound_2sweep(n, sd.neighbors, isource, tab, NULL, tab+n, seen) bitset_free(seen) - sage_free(tab) + sig_free(tab) elif algorithm=='multi-sweep': LB = diameter_lower_bound_multi_sweep(n, sd.neighbors, isource)[0] @@ -1442,7 +1442,7 @@ def wiener_index(G): cdef uint64_t s = 0 for 0 <= i < NN: s += distances[i] - sage_free(distances) + sig_free(distances) return Integer(s)/2 ########################## @@ -1527,7 +1527,7 @@ def distances_distribution(G): for 0 <= i < NN: count[ distances[i] ] = count.get(distances[i],0) + 1 - sage_free(distances) + sig_free(distances) # We normalize the distribution for j in count: diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 53c0b81ea3c..e80f2a57048 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -688,7 +688,7 @@ def NonisotropicUnitaryPolarGraph(m, q): REFERENCE: - .. [Hu75] X. L. Hubaut. + .. [Hu75] \X. L. Hubaut. Strongly regular graphs. Disc. Math. 13(1975), pp 357--381. http://dx.doi.org/10.1016/0012-365X(75)90057-6 @@ -809,7 +809,7 @@ def SymplecticDualPolarGraph(m, q): REFERENCE: - .. [Co81] A. M. Cohen, + .. [Co81] \A. M. Cohen, `A synopsis of known distance-regular graphs with large diameters `_, Stichting Mathematisch Centrum, 1981. @@ -833,7 +833,7 @@ def TaylorTwographDescendantSRG(q, clique_partition=None): obtained as a two-graph descendant of the :func:`Taylor's two-graph ` `T`. This graph admits a partition into cliques of size `q`, which are useful in - :func:`TaylorTwographSRG `, + :func:`~sage.graphs.graph_generators.GraphGenerators.TaylorTwographSRG`, a strongly regular graph on `q^3+1` vertices in the Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques. The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital @@ -919,7 +919,7 @@ def TaylorTwographSRG(q): .. SEEALSO:: - * :func:`TaylorTwographDescendantSRG ` + * :meth:`~sage.graphs.graph_generators.GraphGenerators.TaylorTwographDescendantSRG` EXAMPLES:: @@ -972,7 +972,7 @@ def AhrensSzekeresGeneralizedQuadrangleGraph(q, dual=False): .. [GQwiki] `Generalized quadrangle `__ - .. [PT09] S. Payne, J. A. Thas. + .. [PT09] \S. Payne, J. A. Thas. Finite generalized quadrangles. European Mathematical Society, 2nd edition, 2009. @@ -1278,7 +1278,7 @@ def CossidentePenttilaGraph(q): REFERENCES: - .. [CP05] A.Cossidente and T.Penttila + .. [CP05] \A.Cossidente and T.Penttila Hemisystems on the Hermitian surface Journal of London Math. Soc. 72(2005), 731-741 """ diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index e79053ec2d2..0874d2fe437 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -697,7 +697,7 @@ def GoethalsSeidelGraph(k,r): in Theorem 2.4 of [GS70]_. It relies on a :func:`(v,k)-BIBD ` with `r` blocks and a - :func:`~sage.combinat.matrices.hadamard_matrix.hadamard_matrix>` of order + :func:`~sage.combinat.matrices.hadamard_matrix.hadamard_matrix` of order `r+1`. The result is a :func:`sage.graphs.strongly_regular_db.strongly_regular_graph` on `v(r+1)` vertices with degree `k=(n+r-1)/2`. @@ -1599,11 +1599,12 @@ def PasechnikGraph(n): """ Pasechnik strongly regular graph on `(4n-1)^2` vertices - A strongly regular graph with parameters of the orthogonal array graph - :func:`OrthogonalArrayBlockGraph - `, also - known as pseudo Latin squares graph `L_{2n-1}(4n-1)`, constructed from a - skew Hadamard matrix of order `4n` following [Pa92]_. + A strongly regular graph with parameters of the orthogonal array + graph + :func:`~sage.graphs.graph_generators.GraphGenerators.OrthogonalArrayBlockGraph`, + also known as pseudo Latin squares graph `L_{2n-1}(4n-1)`, + constructed from a skew Hadamard matrix of order `4n` following + [Pa92]_. EXAMPLES:: @@ -1611,6 +1612,7 @@ def PasechnikGraph(n): (225, 98, 43, 42) sage: graphs.PasechnikGraph(9).is_strongly_regular(parameters=True) # long time (1225, 578, 273, 272) + """ from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix from sage.matrix.constructor import identity_matrix, matrix @@ -1627,7 +1629,7 @@ def SquaredSkewHadamardMatrixGraph(n): A strongly regular graph with parameters of the orthogonal array graph :func:`OrthogonalArrayBlockGraph - `, also + `, also known as pseudo Latin squares graph `L_{2n}(4n-1)`, constructed from a skew Hadamard matrix of order `4n`, due to Goethals and Seidel, see [BvL84]_. @@ -1658,7 +1660,7 @@ def SwitchedSquaredSkewHadamardMatrixGraph(n): A strongly regular graph in the :meth:`Seidel switching ` class of the disjoint union of a 1-vertex graph and the one produced by :func:`Pseudo-L_{2n}(4n-1) - ` + ` In this case, the other possible parameter set of a strongly regular graph in the Seidel switching class of the latter graph (see [BH12]_) coincides with the set @@ -2548,11 +2550,11 @@ def MathonPseudocyclicStronglyRegularGraph(t, G=None, L=None): REFERENCES: - .. [Mat78] R. A. Mathon, + .. [Mat78] \R. A. Mathon, Symmetric conference matrices of order `pq^2 + 1`, Canad. J. Math. 30 (1978) 321-331 - .. [ST78] J. J. Seidel and D. E. Taylor, + .. [ST78] \J. J. Seidel and D. E. Taylor, Two-graphs, a second survey. Algebraic methods in graph theory, Vol. I, II (Szeged, 1978), pp. 689–711, Colloq. Math. Soc. János Bolyai, 25, diff --git a/src/sage/graphs/generators/random.py b/src/sage/graphs/generators/random.py index 456191c2f4b..4c2ad500ab6 100644 --- a/src/sage/graphs/generators/random.py +++ b/src/sage/graphs/generators/random.py @@ -49,13 +49,13 @@ def RandomGNP(n, p, seed=None, fast=True, algorithm='Sage'): REFERENCES: - .. [ErdRen1959] P. Erdos and A. Renyi. On Random Graphs, Publ. + .. [ErdRen1959] \P. Erdos and A. Renyi. On Random Graphs, Publ. Math. 6, 290 (1959). - .. [Gilbert1959] E. N. Gilbert. Random Graphs, Ann. Math. Stat., + .. [Gilbert1959] \E. N. Gilbert. Random Graphs, Ann. Math. Stat., 30, 1141 (1959). - .. [BatBra2005] V. Batagelj and U. Brandes. Efficient generation of + .. [BatBra2005] \V. Batagelj and U. Brandes. Efficient generation of large random networks. Phys. Rev. E, 71, 036113, 2005. PLOTTING: When plotting, this graph will use the default spring-layout @@ -540,7 +540,7 @@ def RandomTree(n): ALGORITHM: - The algoritm works by generating an `(n-2)`-long + The algorithm works by generating an `(n-2)`-long random sequence of numbers chosen independently and uniformly from `\{0,1,\ldots,n-1\}` and then applies an inverse Prufer transformation. @@ -995,7 +995,7 @@ def RandomTriangulation(n, set_position=False): .. SEEALSO:: :meth:`~sage.graphs.graph_generators.GraphGenerators.triangulations`, - :meth:`~sage.homology.examples.RandomTwoSphere`. + :func:`~sage.homology.examples.RandomTwoSphere`. EXAMPLES:: diff --git a/src/sage/graphs/generators/smallgraphs.py b/src/sage/graphs/generators/smallgraphs.py index d38bdec0537..a2ef3280041 100644 --- a/src/sage/graphs/generators/smallgraphs.py +++ b/src/sage/graphs/generators/smallgraphs.py @@ -341,7 +341,7 @@ def WellsGraph(): REFERENCES: - .. [BCN89] A. E. Brouwer, A. M. Cohen, A. Neumaier, + .. [BCN89] \A. E. Brouwer, A. M. Cohen, A. Neumaier, Distance-Regular Graphs, Springer, 1989. """ @@ -1948,13 +1948,13 @@ def DejterGraph(): 4 """ from sage.graphs.generators.families import CubeGraph - from sage.coding.code_constructions import HammingCode + from sage.coding.hamming_code import HammingCode from sage.rings.finite_rings.finite_field_constructor import FiniteField from string import join g = CubeGraph(7) g.delete_vertices([join(map(str,x),"") - for x in HammingCode(3, FiniteField(2))]) + for x in HammingCode(FiniteField(2), 3)]) g.name("Dejter Graph") return g @@ -4947,7 +4947,7 @@ def JankoKharaghaniTonchevGraph(): REFERENCES: - .. [JKT01] Z.Janko, H.Kharaghani, V.D.Tonchev + .. [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 diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 7b02799f911..252984f86b2 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -1637,11 +1637,13 @@ def to_dictionary(self, edge_labels=False, multiple_edges=False): return d def adjacency_matrix(self, sparse=None, vertices=None): - """ + r""" Returns the adjacency matrix of the (di)graph. - The matrix returned is over the integers. If a different ring is - desired, use either :meth:`sage.matrix.matrix0.Matrix.change_ring` method or :func:`matrix` + The matrix returned is over the integers. If a different ring + is desired, use either + :meth:`sage.matrix.matrix0.Matrix.change_ring` method or + :class:`matrix ` function. INPUT: @@ -5040,7 +5042,7 @@ def blocks_and_cut_vertices(self): REFERENCE: - .. [Tarjan72] R.E. Tarjan. Depth-First Search and Linear Graph + .. [Tarjan72] \R.E. Tarjan. Depth-First Search and Linear Graph Algorithms. SIAM J. Comput. 1(2): 146-160 (1972). """ if not self: # empty graph @@ -5187,9 +5189,9 @@ def blocks_and_cuts_tree(self): REFERENCES: - .. [HarPri] F. Harary and G. Prins. The block-cutpoint-tree of + .. [HarPri] \F. Harary and G. Prins. The block-cutpoint-tree of a graph. Publ. Math. Debrecen 13 1966 103-107. - .. [Gallai] T. Gallai, Elementare Relationen bezueglich der + .. [Gallai] \T. Gallai, Elementare Relationen bezueglich der Glieder und trennenden Punkte von Graphen, Magyar Tud. Akad. Mat. Kutato Int. Kozl. 9 (1964) 235-236 """ @@ -9459,6 +9461,23 @@ def vertex_boundary(self, vertices1, vertices2=None): sage: D = DiGraph({0:[1,2], 3:[0]}) sage: D.vertex_boundary([0]) [1, 2] + + + TESTS: + + When vertices2 is None, then vertices2 is the complement of vertices 1. + Corrected in ticket :trac:`20479`:: + + sage: P = graphs.PathGraph(3) + sage: P.vertex_boundary([0,1]) + [2] + sage: P.vertex_boundary([0,1], set(P.vertices()).difference([0,1])) + [2] + sage: Q = DiGraph(P) + sage: Q.vertex_boundary([0,1]) + [2] + sage: Q.vertex_boundary([0,1], set(Q.vertices()).difference([0,1])) + [2] """ vertices1 = [v for v in vertices1 if v in self] output = set() @@ -9468,7 +9487,9 @@ def vertex_boundary(self, vertices1, vertices2=None): else: for v in vertices1: output.update(self.neighbor_iterator(v)) - if vertices2 is not None: + if vertices2 is None: + output.difference_update(vertices1) + else: output.intersection_update(vertices2) return list(output) @@ -15671,7 +15692,9 @@ def shortest_path_all_pairs(self, by_weight=False, algorithm=None, def wiener_index(self, by_weight=False, algorithm=None, weight_function=None, check_weight=True): r""" - Returns the Wiener index of the graph. + Return the Wiener index of the graph. + + The graph is expected to have no cycles of negative weight. The Wiener index of a graph `G` is `W(G) = \frac 1 2 \sum_{u,v\in G} d(u,v)` @@ -15688,28 +15711,32 @@ def wiener_index(self, by_weight=False, algorithm=None, - ``by_weight`` (boolean) - if ``True``, the edges in the graph are weighted; if ``False``, all edges have weight 1. - - ``algorithm`` (string) - one of the following algorithms: + - ``algorithm`` (string) - the algorithm to use: - - ``'BFS'`` - the computation is done through a BFS centered on each - vertex successively. Works only if ``by_weight==False``. + - For ``by_weight==False`` only: - - ``'Floyd-Warshall-Cython'`` - the Cython implementation of - the Floyd-Warshall algorithm. Works only if ``by_weight==False``. + - ``'BFS'`` - the computation is done through a BFS centered on + each vertex successively. - - ``'Floyd-Warshall-Python'`` - the Python implementation of - the Floyd-Warshall algorithm. Works also with weighted graphs, even - with negative weights (but no negative cycle is allowed). + - ``'Floyd-Warshall-Cython'`` - the Cython implementation of + the Floyd-Warshall algorithm. Usually slower than ``'BFS'``. - - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in - NetworkX. It works with weighted graphs, but no negative weight is - allowed. + - For graphs without negative weights: - - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in Boost - (works only with positive weights). + - ``'Dijkstra_Boost'``: the Dijkstra algorithm, implemented in + Boost. - - ``'Johnson_Boost'``: the Johnson algorithm, implemented in - Boost (works also with negative weights, if there is no negative - cycle). + - ``'Dijkstra_NetworkX'``: the Dijkstra algorithm, implemented in + NetworkX. Usually slower than ``'Dijkstra_Boost'``. + + - For graphs with negative weights: + + - ``'Johnson_Boost'``: the Johnson algorithm, implemented in + Boost. + + - ``'Floyd-Warshall-Python'`` - the Python implementation of + the Floyd-Warshall algorithm. Usually slower than + ``'Johnson_Boost'``. - ``None`` (default): Sage chooses the best algorithm: ``'BFS'`` for unweighted graphs, ``'Dijkstra_Boost'`` if all weights are @@ -15842,7 +15869,7 @@ def average_distance(self, by_weight=False, algorithm=None, REFERENCE: - .. [GYLL93] I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent + .. [GYLL93] \I. Gutman, Y.-N. Yeh, S.-L. Lee, and Y.-L. Luo. Some recent results in the theory of the Wiener number. *Indian Journal of Chemistry*, 32A:651--661, 1993. @@ -19114,6 +19141,11 @@ def spectrum(self, laplacian=False): A list of the eigenvalues, including multiplicities, sorted with the largest eigenvalue first. + .. SEEALSO:: + + The method :meth:`spectral_radius` returns floating point + approximation of the maximum eigenvalue. + EXAMPLES:: sage: P = graphs.PetersenGraph() @@ -20986,6 +21018,9 @@ def is_cayley(self, return_group = False, mapping = False, from sage.graphs.base.boost_graph import dominator_tree GenericGraph.dominator_tree = types.MethodType(dominator_tree, None, GenericGraph) +from sage.graphs.base.static_sparse_graph import spectral_radius +GenericGraph.spectral_radius = types.MethodType(spectral_radius, None, GenericGraph) + # From Python modules import sage.graphs.line_graph GenericGraph.line_graph = sage.graphs.line_graph.line_graph @@ -21089,8 +21124,8 @@ def graph_isom_equivalent_non_edge_labeled_graph(g, partition=None, standard_lab - ``g`` -- Graph or DiGraph - ``partition`` -- (default:None) if given, the partition of the vertices is as well relabeled - ``standard_label`` -- (default:None) the standard label is not considered to be changed - - ``return_relabeling`` -- (defaut:False) if True, a dictionary containing the relabeling is returned - - ``return_edge_labels`` -- (defaut:False) if True, the different edge_labels are returned (useful if inplace is True) + - ``return_relabeling`` -- (default: False) if True, a dictionary containing the relabeling is returned + - ``return_edge_labels`` -- (default: False) if True, the different edge_labels are returned (useful if inplace is True) - ``inplace`` -- (default:False) if True, g is modified, otherwise the result is returned. Note that attributes of g are *not* copied for speed issues, only edges and vertices. OUTPUT: diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index b4b6726b3c8..05bf35d13ae 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -19,7 +19,7 @@ AUTHORS: include "cysignals/signals.pxi" include 'sage/ext/cdefs.pxi' -from sage.ext.memory cimport check_allocarray, sage_malloc, sage_free +include "cysignals/memory.pxi" include "sage/data_structures/binary_matrix.pxi" # import from Python standard library @@ -146,7 +146,7 @@ def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True try: elist = check_allocarray(2 * len(G.edges()) + 2, sizeof(int)) except MemoryError: - sage_free(pos) + sig_free(pos) raise cdef int cur_edge = 0 @@ -172,8 +172,8 @@ def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True try: cen = check_allocarray(dim, sizeof(double)) except MemoryError: - sage_free(elist) - sage_free(pos) + sig_free(elist) + sig_free(pos) raise for x from 0 <= x < dim: cen[x] = 0 for i from 0 <= i < n: @@ -191,15 +191,15 @@ def spring_layout_fast(G, iterations=50, int dim=2, vpos=None, bint rescale=True for i from 0 <= i < n: for x from 0 <= x < dim: pos[i*dim + x] /= r - sage_free(cen) + sig_free(cen) # put the data back into a position dictionary vpos = {} for i from 0 <= i < n: vpos[vlist[i]] = [pos[i*dim+x] for x from 0 <= x < dim] - sage_free(pos) - sage_free(elist) + sig_free(pos) + sig_free(elist) return vpos @@ -309,7 +309,7 @@ cdef run_spring(int iterations, int dim, double* pos, int* edges, int n, bint he sig_off() - sage_free(disp) + sig_free(disp) def int_to_binary_string(n): """ @@ -333,7 +333,7 @@ def int_to_binary_string(n): mpz_set_ui(i,n) cdef char* s=mpz_get_str(NULL, 2, i) t=str(s) - sage_free(s) + sig_free(s) mpz_clear(i) return t @@ -396,7 +396,7 @@ def small_integer_to_graph6(n): def length_and_string_from_graph6(s): r""" - Returns a pair `(length,graph6_string)` from a graph6 string of unknown length. + Returns a pair ``(length,graph6_string)`` from a graph6 string of unknown length. This helper function is the inverse of `N` from [McK]_. @@ -688,17 +688,17 @@ cdef class SubgraphSearch: # A vertex is said to be busy if it is already part of the partial copy # of H in G. - self.busy = sage_malloc(self.ng * sizeof(int)) - self.tmp_array = sage_malloc(self.ng * sizeof(int)) - self.stack = sage_malloc(self.nh * sizeof(int)) - self.vertices = sage_malloc(self.nh * sizeof(int)) - self.line_h_out = sage_malloc(self.nh * sizeof(int *)) - self.line_h_in = sage_malloc(self.nh * sizeof(int *)) if self.directed else NULL + self.busy = sig_malloc(self.ng * sizeof(int)) + self.tmp_array = sig_malloc(self.ng * sizeof(int)) + self.stack = sig_malloc(self.nh * sizeof(int)) + self.vertices = sig_malloc(self.nh * sizeof(int)) + self.line_h_out = sig_malloc(self.nh * sizeof(int *)) + self.line_h_in = sig_malloc(self.nh * sizeof(int *)) if self.directed else NULL if self.line_h_out is not NULL: - self.line_h_out[0] = sage_malloc(self.nh*self.nh*sizeof(int)) + self.line_h_out[0] = sig_malloc(self.nh*self.nh*sizeof(int)) if self.line_h_in is not NULL: - self.line_h_in[0] = sage_malloc(self.nh*self.nh*sizeof(int)) + self.line_h_in[0] = sig_malloc(self.nh*self.nh*sizeof(int)) if (self.tmp_array == NULL or self.busy == NULL or @@ -836,16 +836,16 @@ cdef class SubgraphSearch: Freeing the allocated memory. """ if self.line_h_in is not NULL: - sage_free(self.line_h_in[0]) + sig_free(self.line_h_in[0]) if self.line_h_out is not NULL: - sage_free(self.line_h_out[0]) + sig_free(self.line_h_out[0]) # Free the memory - sage_free(self.busy) - sage_free(self.stack) - sage_free(self.vertices) - sage_free(self.line_h_out) - sage_free(self.line_h_in) + sig_free(self.busy) + sig_free(self.stack) + sig_free(self.vertices) + sig_free(self.line_h_out) + sig_free(self.line_h_in) cdef inline bint vectors_equal(int n, int *a, int *b): r""" @@ -930,8 +930,8 @@ def _test_vectors_equal_inferior(): assert vectors_inferior(n, u, v) assert vectors_inferior(n, v, u) except AssertionError: - sage_free(u) - sage_free(v) + sig_free(u) + sig_free(v) raise AssertionError("Vectors u and v should be equal.") # Different vectors: u != v because we have u_j > v_j for some j. Thus, # u_i = v_i for 0 <= i < j and u_j > v_j. For j < k < n - 2, we could have: @@ -960,8 +960,8 @@ def _test_vectors_equal_inferior(): assert v[n - 1] > u[n - 1] assert not vectors_inferior(n, u, v) except AssertionError: - sage_free(u) - sage_free(v) + sig_free(u) + sig_free(v) raise AssertionError("".join([ "Vectors u and v should not be equal. ", "u should not be inferior to v, and vice versa."])) @@ -991,8 +991,8 @@ def _test_vectors_equal_inferior(): assert u[j] < v[j] assert not vectors_inferior(n, u, v) except AssertionError: - sage_free(u) - sage_free(v) + sig_free(u) + sig_free(v) raise AssertionError("".join([ "Vectors u and v should not be equal. ", "u should not be inferior to v, and vice versa."])) @@ -1005,8 +1005,8 @@ def _test_vectors_equal_inferior(): assert not vectors_equal(n, u, v) assert not vectors_equal(n, v, u) except AssertionError: - sage_free(u) - sage_free(v) + sig_free(u) + sig_free(v) raise AssertionError("Vectors u and v should not be equal.") # u is inferior to v, but v is not inferior to u for 0 <= i < n: @@ -1023,8 +1023,8 @@ def _test_vectors_equal_inferior(): raise AssertionError( "u should be inferior to v, but v is not inferior to u.") finally: - sage_free(u) - sage_free(v) + sig_free(u) + sig_free(v) cpdef tuple find_hamiltonian( G, long max_iter=100000, long reset_bound=30000, long backtrack_bound=1000, find_path=False ): r""" @@ -1282,10 +1282,10 @@ cpdef tuple find_hamiltonian( G, long max_iter=100000, long reset_bound=30000, l if bigcount*reset_bound > max_iter: verts=G.vertices() output=[ verts[ longest_path[i] ] for i from 0<= i < longest ] - sage_free( member ) - sage_free( path ) - sage_free( longest_path ) - sage_free( temp_path ) + sig_free( member ) + sig_free( path ) + sig_free( longest_path ) + sig_free( temp_path ) return (False, output) # # # # Output test @@ -1315,10 +1315,10 @@ cpdef tuple find_hamiltonian( G, long max_iter=100000, long reset_bound=30000, l raise RuntimeError( 'Vertex %d appears twice in the cycle.'%(u) ) verts=G.vertices() output=[ verts[path[i]] for i from 0<= i < length ] - sage_free( member ) - sage_free( path ) - sage_free( longest_path ) - sage_free( temp_path ) + sig_free( member ) + sig_free( path ) + sig_free( longest_path ) + sig_free( temp_path ) return (True,output) diff --git a/src/sage/graphs/genus.pyx b/src/sage/graphs/genus.pyx index 5e54d9c9027..20aa5c6571d 100644 --- a/src/sage/graphs/genus.pyx +++ b/src/sage/graphs/genus.pyx @@ -47,7 +47,7 @@ from sage.graphs.base.dense_graph cimport DenseGraph from sage.graphs.graph import Graph -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" @@ -124,17 +124,17 @@ cdef class simple_connected_genus_backtracker: cdef int i if self.vertex_darts != NULL: - sage_free(self.vertex_darts[0]) - sage_free(self.vertex_darts) + sig_free(self.vertex_darts[0]) + sig_free(self.vertex_darts) if self.swappers != NULL: - sage_free(self.swappers[0]) - sage_free(self.swappers) + sig_free(self.swappers[0]) + sig_free(self.swappers) - sage_free(self.face_map) - sage_free(self.visited) - sage_free(self.face_freeze) - sage_free(self.degree) + sig_free(self.face_map) + sig_free(self.visited) + sig_free(self.face_freeze) + sig_free(self.degree) cdef int got_memory(self): """ @@ -192,20 +192,20 @@ cdef class simple_connected_genus_backtracker: if self.num_verts <= 1: return - self.face_map = sage_malloc(self.num_darts * sizeof(int)) - self.vertex_darts = sage_malloc(self.num_verts * sizeof(int *)) - self.swappers = sage_malloc(self.num_verts * sizeof(int *)) - self.degree = sage_malloc(self.num_verts * sizeof(int)) - self.visited = sage_malloc(self.num_darts * sizeof(int)) - self.face_freeze = sage_malloc(self.num_darts * sizeof(int)) + self.face_map = sig_malloc(self.num_darts * sizeof(int)) + self.vertex_darts = sig_malloc(self.num_verts * sizeof(int *)) + self.swappers = sig_malloc(self.num_verts * sizeof(int *)) + self.degree = sig_malloc(self.num_verts * sizeof(int)) + self.visited = sig_malloc(self.num_darts * sizeof(int)) + self.face_freeze = sig_malloc(self.num_darts * sizeof(int)) if self.got_memory() == 0: # dealloc is NULL-safe and frees everything that did get alloc'd raise MemoryError, "Error allocating memory for graph genus a" - w = sage_malloc((self.num_verts + self.num_darts) * sizeof(int)) + w = sig_malloc((self.num_verts + self.num_darts) * sizeof(int)) self.vertex_darts[0] = w - s = sage_malloc( 2 * (self.num_darts - self.num_verts) * sizeof(int)) + s = sig_malloc( 2 * (self.num_darts - self.num_verts) * sizeof(int)) self.swappers[0] = s if w == NULL or s == NULL: diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 379ef6d35c8..d87ee63654c 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1498,7 +1498,7 @@ def spanning_trees(self): REFERENCES: .. [RT75] Read, R. C. and Tarjan, R. E. - Bounds on Backtrack Algoritms for Listing Cycles, Paths, and Spanning Trees + Bounds on Backtrack Algorithms for Listing Cycles, Paths, and Spanning Trees Networks, Volume 5 (1975), numer 3, pages 237-252. """ @@ -1908,7 +1908,7 @@ def is_even_hole_free(self, certificate = False): REFERENCE: - .. [ABCHRS08] L. Addario-Berry, M. Chudnovsky, F. Havet, B. Reed, P. Seymour + .. [ABCHRS08] \L. Addario-Berry, M. Chudnovsky, F. Havet, B. Reed, P. Seymour Bisimplicial vertices in even-hole-free graphs Journal of Combinatorial Theory, Series B vol 98, n.6 pp 1119-1164, 2008 @@ -1990,7 +1990,7 @@ def is_odd_hole_free(self, certificate = False): REFERENCES: - .. [CRST06] M. Chudnovsky, G. Cornuejols, X. Liu, P. Seymour, K. Vuskovic + .. [CRST06] \M. Chudnovsky, G. Cornuejols, X. Liu, P. Seymour, K. Vuskovic Recognizing berge graphs Combinatorica vol 25, n 2, pages 143--186 2005 @@ -2249,7 +2249,7 @@ def is_split(self): REFERENCES: - .. [GraphClasses] A. Brandstadt, VB Le and JP Spinrad + .. [GraphClasses] \A. Brandstadt, VB Le and JP Spinrad Graph classes: a survey SIAM Monographs on Discrete Mathematics and Applications}, 1999 @@ -2633,7 +2633,7 @@ def is_perfect(self, certificate = False): REFERENCES: - .. [SPGT] M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. + .. [SPGT] \M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. The strong perfect graph theorem Annals of Mathematics vol 164, number 1, pages 51--230 @@ -3675,7 +3675,7 @@ def chromatic_symmetric_function(self, R=None): REFERENCES: - .. [Stanley95] R. P. Stanley, *A symmetric function generalization + .. [Stanley95] \R. P. Stanley, *A symmetric function generalization of the chromatic polynomial of a graph*, Adv. Math., ***111*** no.1 (1995), 166-194. """ @@ -3906,7 +3906,7 @@ def matching(self, value_only=False, algorithm="Edmonds", use_edge_labels=True, return sum(weight(self.edge_label(u, v)) for u, v in d.iteritems()) * 0.5 else: - return Integer(len(d)/2) + return Integer(len(d) // 2) else: return [(u, v, self.edge_label(u, v)) for u, v in d.iteritems() if u < v] @@ -4345,7 +4345,7 @@ def independent_set_of_representatives(self, family, solver=None, verbose=0): REFERENCE: - .. [AhaBerZiv07] R. Aharoni and E. Berger and R. Ziv + .. [AhaBerZiv07] \R. Aharoni and E. Berger and R. Ziv Independent systems of representatives in weighted graphs Combinatorica vol 27, num 3, p253--267 2007 @@ -4829,20 +4829,23 @@ def seidel_adjacency_matrix(self, vertices=None): r""" Returns the Seidel adjacency matrix of ``self``. - Returns `J-I-2A`, for `A` the (ordinary) - :meth:`adjacency matrix ` of ``self``, - `I` the identity matrix, and `J` the all-1 matrix. + Returns `J-I-2A`, for `A` the (ordinary) :meth:`adjacency + matrix + ` of + ``self``, `I` the identity matrix, and `J` the all-1 matrix. It is closely related to :meth:`twograph`. The matrix returned is over the integers. If a different ring is desired, use either :meth:`sage.matrix.matrix0.Matrix.change_ring` - method or :func:`matrix` function. + method or :class:`matrix ` function. INPUT: - - ``vertices`` (list) -- the ordering of the vertices defining how they - should appear in the matrix. By default, the ordering given by - :meth:`GenericGraph.vertices` is used. + - ``vertices`` (list) -- the ordering of the vertices defining + how they should appear in the matrix. By default, the + ordering given by + :meth:`~sage.graphs.generic_graph.GenericGraph.vertices` is + used. EXAMPLES:: @@ -5792,7 +5795,7 @@ def vertex_cover(self, algorithm = "Cliquer", value_only = False, REFERENCE: - .. [ACFLSS04] F. N. Abu-Khzam, R. L. Collins, M. R. Fellows, M. A. + .. [ACFLSS04] \F. N. Abu-Khzam, R. L. Collins, M. R. Fellows, M. A. Langston, W. H. Suters, and C. T. Symons: Kernelization Algorithm for the Vertex Cover Problem: Theory and Experiments. *SIAM ALENEX/ANALCO* 2004: 62-69. @@ -6407,7 +6410,7 @@ def modular_decomposition(self): International Journal of Foundations of Computer Science vol. 10 n2 pp.147--170, 1999 - .. [CapHabMont02] C. Capelle, M. Habib et F. de Montgolfier + .. [CapHabMont02] \C. Capelle, M. Habib et F. de Montgolfier Graph decomposition and Factorising Permutations Discrete Mathematics and Theoretical Computer Sciences, vol 5 no. 1 , 2002. @@ -6469,7 +6472,7 @@ def is_prime(self): return D[0] == "Prime" and len(D[1]) == self.order() @rename_keyword(deprecation=19550, method='algorithm') - def _gomory_hu_tree(self, vertices, algorithm="FF"): + def _gomory_hu_tree(self, vertices, algorithm=None): r""" Return a Gomory-Hu tree associated to self. @@ -6486,15 +6489,9 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): fakes one introduced during the computations. This variable is useful for the algorithm and for recursion purposes. - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problem is solved using - Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. EXAMPLE: @@ -6566,7 +6563,7 @@ def _gomory_hu_tree(self, vertices, algorithm="FF"): @doc_index("Connectivity, orientations, trees") @rename_keyword(deprecation=19550, method='algorithm') - def gomory_hu_tree(self, algorithm="FF"): + def gomory_hu_tree(self, algorithm=None): r""" Returns a Gomory-Hu tree of self. @@ -6584,15 +6581,9 @@ def gomory_hu_tree(self, algorithm="FF"): INPUT: - - ``algorithm`` -- There are currently two different - implementations of this method : - - * If ``algorithm = "FF"`` (default), a Python - implementation of the Ford-Fulkerson algorithm is - used. - - * If ``algorithm = "LP"``, the flow problems are solved - using Linear Programming. + - ``algorithm`` -- select the algorithm used by the :meth:`edge_cut` + method. Refer to its documentation for allowed values and default + behaviour. OUTPUT: diff --git a/src/sage/graphs/graph_decompositions/cutwidth.pyx b/src/sage/graphs/graph_decompositions/cutwidth.pyx index 93f0361996f..60e85620b44 100644 --- a/src/sage/graphs/graph_decompositions/cutwidth.pyx +++ b/src/sage/graphs/graph_decompositions/cutwidth.pyx @@ -166,13 +166,12 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' include "cysignals/signals.pxi" include 'sage/ext/cdefs.pxi' from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, popcount32 from sage.graphs.graph_decompositions.vertex_separation import is_valid_ordering from libc.stdint cimport uint8_t -from sage.ext.memory cimport check_allocarray +include "cysignals/memory.pxi" from sage.rings.integer_ring import ZZ @@ -493,7 +492,7 @@ def cutwidth_dyn(G, lower_bound=0): return k, [g.int_to_vertices[i] for i in order] sig_off() finally: - sage_free(neighborhoods) + sig_free(neighborhoods) order = find_order(g, neighborhoods, k) return k, [g.int_to_vertices[i] for i in order] diff --git a/src/sage/graphs/graph_decompositions/fast_digraph.pyx b/src/sage/graphs/graph_decompositions/fast_digraph.pyx index 453d733e224..490f4c254d3 100644 --- a/src/sage/graphs/graph_decompositions/fast_digraph.pyx +++ b/src/sage/graphs/graph_decompositions/fast_digraph.pyx @@ -13,7 +13,7 @@ In the following code, sets are represented as integers, where the ith bit is set if element i belongs to the set. """ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include 'sage/ext/cdefs.pxi' from libc.stdint cimport uint8_t @@ -33,7 +33,7 @@ cdef class FastDigraph: self.n = D.order() self.graph = NULL - self.graph = sage_malloc(self.n*sizeof(int)) + self.graph = sig_malloc(self.n*sizeof(int)) memset(self.graph, 0, self.n * sizeof(int)) @@ -60,7 +60,7 @@ cdef class FastDigraph: tmp |= 1 << vertices_to_int[v] self.graph[vertices_to_int[u]] = tmp - self.degree = sage_malloc(self.n*sizeof(int)) + self.degree = sig_malloc(self.n*sizeof(int)) for i in range(self.n): self.degree[i] = popcount32(self.graph[i]) @@ -69,8 +69,8 @@ cdef class FastDigraph: Destructor. """ if self.graph != NULL: - sage_free(self.graph) - sage_free(self.degree) + sig_free(self.graph) + sig_free(self.degree) def print_adjacency_matrix(self): r""" diff --git a/src/sage/graphs/graph_decompositions/rankwidth.pyx b/src/sage/graphs/graph_decompositions/rankwidth.pyx index 183607fbf44..b8be4c1672e 100644 --- a/src/sage/graphs/graph_decompositions/rankwidth.pyx +++ b/src/sage/graphs/graph_decompositions/rankwidth.pyx @@ -123,7 +123,7 @@ Methods #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from libc.string cimport memset @@ -313,7 +313,7 @@ def mkgraph(int num_vertices): from sage.graphs.graph import Graph g = Graph() - cdef subset_t * tab = sage_malloc(sizeof(subset_t) * (2*num_vertices -1)) + cdef subset_t * tab = sig_malloc(sizeof(subset_t) * (2*num_vertices -1)) tab[0] = 0x7ffffffful >> (31 - num_vertices) cdef int beg = 0 @@ -335,7 +335,7 @@ def mkgraph(int num_vertices): tab[end] = cslots[s] end += 1 - sage_free(tab) + sig_free(tab) return g cdef bitset_to_vertex_set(subset_t s): diff --git a/src/sage/graphs/graph_decompositions/tdlib.pyx b/src/sage/graphs/graph_decompositions/tdlib.pyx index b0224f579db..9f1f03bcf1d 100644 --- a/src/sage/graphs/graph_decompositions/tdlib.pyx +++ b/src/sage/graphs/graph_decompositions/tdlib.pyx @@ -48,16 +48,16 @@ AUTHOR: Lukas Larisch (10-25-2015): Initial version REFERENCE: -.. [ST93] P. D. Seymour and Robin Thomas, +.. [ST93] \P. D. Seymour and Robin Thomas, Graph searching and a min-max theorem for tree-width, J. Comb. Theory Ser. B 58, 1 (May 1993), 22-33. -.. [AP86] S. Arnborg, A. Proskurowski, +.. [AP86] \S. Arnborg, A. Proskurowski, Characterization and Recognition of Partial 3-Trees, SIAM Journal of Alg. and Discrete Methods, Vol. 7, pp. 305-314, 1986 -.. [Bodlaender93] H. L. Bodlaender, +.. [Bodlaender93] \H. L. Bodlaender, A Tourist Guide through Treewidth, Acta Cybern. 1993 Methods @@ -70,7 +70,6 @@ from sage.sets.set import Set from sage.graphs.graph import Graph include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' cdef extern from "tdlib/sage_tdlib.cpp": int sage_exact_decomposition(vector[unsigned int] &V_G, vector[unsigned int] &E_G, vector[vector[int]] &V_T, vector[unsigned int] &E_T, int lb) diff --git a/src/sage/graphs/graph_decompositions/vertex_separation.pyx b/src/sage/graphs/graph_decompositions/vertex_separation.pyx index 27cd62c1083..00599aab76e 100644 --- a/src/sage/graphs/graph_decompositions/vertex_separation.pyx +++ b/src/sage/graphs/graph_decompositions/vertex_separation.pyx @@ -96,7 +96,7 @@ knows that having `S` in a sequence means a total cost of at least `c'(S) + 5`. For this reason, for each set `S` we store the value of `c'(S)`, and replace it by `\max (c'(S), \min_{\text{next}})` (where `\min_{\text{next}}` is the minimum of the costs of the out-neighbors of `S`) once the costs of these -out-neighbors have been evaluated by the algrithm. +out-neighbors have been evaluated by the algorithm. .. NOTE:: @@ -269,7 +269,7 @@ Methods ------- """ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" include 'sage/ext/cdefs.pxi' from sage.graphs.graph_decompositions.fast_digraph cimport FastDigraph, compute_out_neighborhood_cardinality, popcount32 @@ -347,7 +347,7 @@ def lower_bound(G): cdef int n = FD.n # minimums[i] is means to store the value of c'_{i+1} - minimums = sage_malloc(sizeof(uint8_t)* n) + minimums = sig_malloc(sizeof(uint8_t)* n) cdef unsigned int i # They are initialized to n @@ -370,7 +370,7 @@ def lower_bound(G): cdef int min = minimums[0] - sage_free(minimums) + sig_free(minimums) return min @@ -738,7 +738,7 @@ def vertex_separation_exp(G, verbose = False): sig_on() cdef unsigned int mem = 1 << g.n - cdef uint8_t * neighborhoods = sage_malloc(mem) + cdef uint8_t * neighborhoods = sig_malloc(mem) if neighborhoods == NULL: sig_off() @@ -760,7 +760,7 @@ def vertex_separation_exp(G, verbose = False): cdef list order = find_order(g, neighborhoods, k) - sage_free(neighborhoods) + sig_free(neighborhoods) sig_off() return k, list( g.int_to_vertices[i] for i in order ) @@ -874,7 +874,7 @@ def is_valid_ordering(G, L): OUTPUT: Returns ``True`` if `L` is a valid vertex ordering for `G`, and ``False`` - oterwise. + otherwise. EXAMPLE: @@ -1384,11 +1384,11 @@ def vertex_separation_BAB(G, cdef binary_matrix_t bm_pool binary_matrix_init(bm_pool, 3*n+2, n) - cdef int * prefix = sage_malloc(n * sizeof(int)) - cdef int * positions = sage_malloc(n * sizeof(int)) + cdef int * prefix = sig_malloc(n * sizeof(int)) + cdef int * positions = sig_malloc(n * sizeof(int)) if prefix==NULL or positions==NULL: - sage_free(prefix) - sage_free(positions) + sig_free(prefix) + sig_free(positions) binary_matrix_free(H) binary_matrix_free(bm_pool) raise MemoryError("Unable to allocate data strutures.") @@ -1430,8 +1430,8 @@ def vertex_separation_BAB(G, finally: if verbose: print 'Stored prefixes: {}'.format(len(prefix_storage)) - sage_free(prefix) - sage_free(positions) + sig_free(prefix) + sig_free(positions) binary_matrix_free(H) binary_matrix_free(bm_pool) diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 4e17261b504..5919ab398ae 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -1189,7 +1189,7 @@ def fullerenes(self, order, ipr=False): REFERENCE: - .. [buckygen] G. Brinkmann, J. Goedgebeur and B.D. McKay, Generation of Fullerenes, + .. [buckygen] \G. Brinkmann, J. Goedgebeur and B.D. McKay, Generation of Fullerenes, Journal of Chemical Information and Modeling, 52(11):2910-2918, 2012. """ from sage.misc.package import is_package_installed @@ -1274,7 +1274,7 @@ def fusenes(self, hexagon_count, benzenoids=False): REFERENCE: - .. [benzene] G. Brinkmann, G. Caporossi and P. Hansen, A Constructive Enumeration of Fusenes and Benzenoids, + .. [benzene] \G. Brinkmann, G. Caporossi and P. Hansen, A Constructive Enumeration of Fusenes and Benzenoids, Journal of Algorithms, 45:155-166, 2002. """ from sage.misc.package import is_package_installed @@ -1427,7 +1427,7 @@ def planar_graphs(self, order, minimum_degree=None, REFERENCE: - .. [plantri] G. Brinkmann and B.D. McKay, Fast generation of planar graphs, + .. [plantri] \G. Brinkmann and B.D. McKay, Fast generation of planar graphs, MATCH-Communications in Mathematical and in Computer Chemistry, 58(2):323-357, 2007. """ from sage.misc.package import is_package_installed diff --git a/src/sage/graphs/graph_generators_pyx.pyx b/src/sage/graphs/graph_generators_pyx.pyx index 9feb6301b30..3e73d05b121 100644 --- a/src/sage/graphs/graph_generators_pyx.pyx +++ b/src/sage/graphs/graph_generators_pyx.pyx @@ -37,9 +37,9 @@ def RandomGNP(n, p, directed = False, loops = False): REFERENCES: - .. [1] P. Erdos and A. Renyi. On Random Graphs, Publ. Math. 6, 290 (1959). + .. [1] \P. Erdos and A. Renyi. On Random Graphs, Publ. Math. 6, 290 (1959). - .. [2] E. N. Gilbert. Random Graphs, Ann. Math. Stat., 30, 1141 (1959). + .. [2] \E. N. Gilbert. Random Graphs, Ann. Math. Stat., 30, 1141 (1959). EXAMPLE:: diff --git a/src/sage/graphs/graph_plot.py b/src/sage/graphs/graph_plot.py index 8648d072360..20f88cea924 100644 --- a/src/sage/graphs/graph_plot.py +++ b/src/sage/graphs/graph_plot.py @@ -9,6 +9,10 @@ sage: P = G.plot() sage: P.show() # long time +.. PLOT: + + sphinx_plot(graphs.WheelGraph(15)) + If you create a graph in Sage using the ``Graph`` command, then plot that graph, the positioning of nodes is determined using the spring-layout algorithm. For the special graph constructors, which you get using ``graphs.[tab]``, the @@ -17,9 +21,22 @@ sage: petersen_spring = Graph(':I`ES@obGkqegW~') sage: petersen_spring.show() # long time + +.. PLOT:: + + petersen_spring = Graph(':I`ES@obGkqegW~') + sphinx_plot(petersen_spring) + +:: + sage: petersen_database = graphs.PetersenGraph() sage: petersen_database.show() # long time +.. PLOT:: + + petersen_database = graphs.PetersenGraph() + sphinx_plot(petersen_database) + For all the constructors in this database (except the octahedral, dodecahedral, random and empty graphs), the position dictionary is filled in, instead of using the spring-layout algorithm. @@ -292,6 +309,15 @@ def set_pos(self): sage: t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) Graphics object consisting of 14 graphics primitives + .. PLOT:: + + g = Graph({0:[1,2], 2:[3], 4:[0,1]}) + g.graphplot(save_pos=True, layout='circular') # indirect doctest + T = list(graphs.trees(7)) + t = T[3] + P = t.plot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}) + sphinx_plot(P) + TESTS: Make sure that vertex locations are floats. Not being floats @@ -324,14 +350,37 @@ def set_vertices(self, **vertex_options): sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') + ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + ... edge_style='dashed') sage: GP.set_vertices(talk=True) sage: GP.plot() Graphics object consisting of 22 graphics primitives sage: GP.set_vertices(vertex_colors='pink', vertex_shape='^') sage: GP.plot() Graphics object consisting of 22 graphics primitives + + .. PLOT:: + + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), + (0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + edge_style='dashed') + GP.set_vertices(talk=True) + sphinx_plot(GP) + + .. PLOT:: + + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'),(0,1,'e'),(0,1,'f'), + (0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + edge_style='dashed') + GP.set_vertices(talk=True) + GP.set_vertices(vertex_colors='pink', vertex_shape='^') + sphinx_plot(GP) + """ # Handle base vertex options voptions = {} @@ -377,11 +426,15 @@ def set_vertices(self, **vertex_options): voptions['facecolor'] = vertex_colors if self._arcdigraph: self._plot_components['vertices'] = [circle(center, - self._vertex_radius, fill=True, facecolor=vertex_colors, edgecolor='black', clip=False) - for center in self._pos.values()] + self._vertex_radius, + fill=True, + facecolor=vertex_colors, + edgecolor='black', + clip=False) + for center in self._pos.values()] else: - self._plot_components['vertices'] = scatter_plot( - self._pos.values(), clip=False, **voptions) + self._plot_components['vertices'] = scatter_plot(self._pos.values(), + clip=False, **voptions) else: # Color list must be ordered: pos = [] @@ -403,11 +456,16 @@ def set_vertices(self, **vertex_options): if self._arcdigraph: self._plot_components['vertices'] = [circle(pos[i], - self._vertex_radius, fill=True, facecolor=colors[i], edgecolor='black', clip=False) - for i in range(len(pos))] + self._vertex_radius, + fill=True, + facecolor=colors[i], + edgecolor='black', + clip=False) + for i in range(len(pos))] else: self._plot_components['vertices'] = scatter_plot(pos, - facecolor=colors, clip=False, **voptions) + facecolor=colors, + clip=False, **voptions) if self._options['vertex_labels']: self._plot_components['vertex_labels'] = [] @@ -428,26 +486,78 @@ def set_edges(self, **edge_options): sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), - ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') + ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + ... edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 22 graphics primitives + + .. PLOT:: + + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), + (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_edges(edge_style='solid') + sphinx_plot(GP) + + :: + sage: GP.set_edges(edge_color='black') sage: GP.plot() Graphics object consisting of 22 graphics primitives + .. PLOT:: + + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), + (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = g.graphplot(vertex_size=100, edge_labels=True, + color_by_label=True, edge_style='dashed') + GP.set_edges(edge_style='solid') + GP.set_edges(edge_color='black') + sphinx_plot(GP) + + :: + sage: d = DiGraph({}, loops=True, multiedges=True, sparse=True) sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + ... edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() Graphics object consisting of 24 graphics primitives + + .. PLOT:: + + d = DiGraph({}, loops=True, multiedges=True, sparse=True) + d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), + (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + edge_style='dashed') + GP.set_edges(edge_style='solid') + sphinx_plot(GP) + + :: + sage: GP.set_edges(edge_color='black') sage: GP.plot() Graphics object consisting of 24 graphics primitives + .. PLOT:: + + d = DiGraph({}, loops=True, multiedges=True, sparse=True) + d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), + (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + edge_style='dashed') + GP.set_edges(edge_style='solid') + GP.set_edges(edge_color='black') + sphinx_plot(GP) + TESTS:: sage: G = Graph("Fooba") @@ -566,9 +676,12 @@ def set_edges(self, **edge_options): distance = float(max_dist)/len(local_labels) curr_loop_size = loop_size for i in range(len(local_labels)): - self._plot_components['edges'].append(circle((self._pos[a][0],self._pos[a][1]-curr_loop_size), curr_loop_size, rgbcolor=local_labels[i][1], **eoptions)) + self._plot_components['edges'].append(circle((self._pos[a][0], + self._pos[a][1]-curr_loop_size), curr_loop_size, + rgbcolor=local_labels[i][1], **eoptions)) if labels: - self._plot_components['edge_labels'].append(text(local_labels[i][0], (self._pos[a][0], self._pos[a][1]-2*curr_loop_size))) + self._plot_components['edge_labels'].append(text(local_labels[i][0], + (self._pos[a][0], self._pos[a][1]-2*curr_loop_size))) curr_loop_size += distance/4 elif len(edges_to_draw[(a,b)]) > 1: # Multi-edge @@ -611,19 +724,33 @@ def set_edges(self, **edge_options): for i in range(len(local_labels)//2): k = (i+1.0)*distance if self._arcdigraph: - odd_start = self._polar_hack_for_multidigraph(p1, [odd_x(k),odd_y(k)], self._vertex_radius)[0] - odd_end = self._polar_hack_for_multidigraph([odd_x(k),odd_y(k)], p2, self._vertex_radius)[1] - even_start = self._polar_hack_for_multidigraph(p1, [even_x(k),even_y(k)], self._vertex_radius)[0] - even_end = self._polar_hack_for_multidigraph([even_x(k),even_y(k)], p2, self._vertex_radius)[1] - self._plot_components['edges'].append(arrow(path=[[odd_start,[odd_x(k),odd_y(k)],odd_end]], head=local_labels[2*i][2], zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(arrow(path=[[even_start,[even_x(k),even_y(k)],even_end]], head=local_labels[2*i+1][2], zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) + odd_start = self._polar_hack_for_multidigraph(p1, + [odd_x(k),odd_y(k)], self._vertex_radius)[0] + odd_end = self._polar_hack_for_multidigraph([odd_x(k),odd_y(k)], + p2, self._vertex_radius)[1] + even_start = self._polar_hack_for_multidigraph(p1, + [even_x(k),even_y(k)], self._vertex_radius)[0] + even_end = self._polar_hack_for_multidigraph([even_x(k),even_y(k)], + p2, self._vertex_radius)[1] + self._plot_components['edges'].append(arrow(path=[[odd_start, + [odd_x(k),odd_y(k)],odd_end]], head=local_labels[2*i][2], + zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) + self._plot_components['edges'].append(arrow(path=[[even_start, + [even_x(k),even_y(k)],even_end]], head=local_labels[2*i+1][2], + zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) else: - self._plot_components['edges'].append(bezier_path([[p1,[odd_x(k),odd_y(k)],p2]],zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) - self._plot_components['edges'].append(bezier_path([[p1,[even_x(k),even_y(k)],p2]],zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) + self._plot_components['edges'].append(bezier_path([[p1, + [odd_x(k),odd_y(k)],p2]],zorder=1, + rgbcolor=local_labels[2*i][1], **eoptions)) + self._plot_components['edges'].append(bezier_path([[p1, + [even_x(k),even_y(k)],p2]],zorder=1, + rgbcolor=local_labels[2*i+1][1], **eoptions)) if labels: j = k/2.0 - self._plot_components['edge_labels'].append(text(local_labels[2*i][0],[odd_x(j),odd_y(j)])) - self._plot_components['edge_labels'].append(text(local_labels[2*i+1][0],[even_x(j),even_y(j)])) + self._plot_components['edge_labels'].append(text(local_labels[2*i][0], + [odd_x(j),odd_y(j)])) + self._plot_components['edge_labels'].append(text(local_labels[2*i+1][0], + [even_x(j),even_y(j)])) if len(local_labels)%2 == 1: edges_to_draw[(a,b)] = [local_labels[-1]] # draw line for last odd @@ -631,15 +758,23 @@ def set_edges(self, **edge_options): for (a,b) in edges_to_draw: if self._arcdigraph: C,D = self._polar_hack_for_multidigraph(self._pos[a], self._pos[b], self._vertex_radius) - self._plot_components['edges'].append(arrow(C,D, rgbcolor=edges_to_draw[(a,b)][0][1], head=edges_to_draw[(a,b)][0][2], **eoptions)) + self._plot_components['edges'].append(arrow(C,D, + rgbcolor=edges_to_draw[(a,b)][0][1], head=edges_to_draw[(a,b)][0][2], + **eoptions)) if labels: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]),[(C[0]+D[0])/2., (C[1]+D[1])/2.])) + self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]), + [(C[0]+D[0])/2., (C[1]+D[1])/2.])) elif dir: - self._plot_components['edges'].append(arrow(self._pos[a],self._pos[b], rgbcolor=edges_to_draw[(a,b)][0][1], arrowshorten=self._arrowshorten, head=edges_to_draw[(a,b)][0][2], **eoptions)) + self._plot_components['edges'].append(arrow(self._pos[a],self._pos[b], + rgbcolor=edges_to_draw[(a,b)][0][1], arrowshorten=self._arrowshorten, + head=edges_to_draw[(a,b)][0][2], **eoptions)) else: - self._plot_components['edges'].append(line([self._pos[a],self._pos[b]], rgbcolor=edges_to_draw[(a,b)][0][1], **eoptions)) + self._plot_components['edges'].append(line([self._pos[a],self._pos[b]], + rgbcolor=edges_to_draw[(a,b)][0][1], **eoptions)) if labels and not self._arcdigraph: - self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]),[(self._pos[a][0]+self._pos[b][0])/2., (self._pos[a][1]+self._pos[b][1])/2.])) + self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]), + [(self._pos[a][0]+self._pos[b][0])/2., + (self._pos[a][1]+self._pos[b][1])/2.])) def _polar_hack_for_multidigraph(self, A, B, VR): """ @@ -652,7 +787,8 @@ def _polar_hack_for_multidigraph(self, A, B, VR): sage: d = DiGraph({}, loops=True, multiedges=True, sparse=True) sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') + sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, + ... edge_style='dashed') sage: GP._polar_hack_for_multidigraph((0,1),(1,1),.1) ([0.10..., 1.00...], [0.90..., 1.00...]) @@ -678,7 +814,8 @@ def _polar_hack_for_multidigraph(self, A, B, VR): theta = atan(D[1]/D[0]) + pi elif D[1] > 0: theta = pi/2 - return ([VR*cos(theta)+A[0], VR*sin(theta)+A[1]], [(R-VR)*cos(theta)+A[0], (R-VR)*sin(theta)+A[1]]) + return ([VR*cos(theta)+A[0], VR*sin(theta)+A[1]], [(R-VR)*cos(theta)+A[0], + (R-VR)*sin(theta)+A[1]]) def show(self, **kwds): """ @@ -702,6 +839,13 @@ def show(self, **kwds): sage: C = graphs.CubeGraph(8) sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) sage: P.show() + + .. PLOT:: + + C = graphs.CubeGraph(8) + P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sphinx_plot(P) + """ # Setting the default values if needed for k,value in DEFAULT_SHOW_OPTIONS.iteritems(): @@ -729,7 +873,8 @@ def plot(self, **kwds): sage: from math import sin, cos, pi sage: P = graphs.PetersenGraph() - sage: d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], '#0000FF':[4,9]} + sage: d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], + ... '#0000FF':[4,9]} sage: pos_dict = {} sage: for i in range(5): ... x = float(cos(pi/2 + ((2*pi)/5)*i)) @@ -744,24 +889,72 @@ def plot(self, **kwds): sage: pl = P.graphplot(pos=pos_dict, vertex_colors=d) sage: pl.show() + .. PLOT:: + + from math import sin, cos, pi + P = graphs.PetersenGraph() + d = {'#FF0000':[0,5], '#FF9900':[1,6], '#FFFF00':[2,7], '#00FF00':[3,8], + '#0000FF':[4,9]} + pos_dict = {} + for i in range(5): + x = float(cos(pi/2 + ((2*pi)/5)*i)) + y = float(sin(pi/2 + ((2*pi)/5)*i)) + pos_dict[i] = [x,y] + + for i in range(10)[5:]: + x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) + y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) + pos_dict[i] = [x,y] + + pl = P.graphplot(pos=pos_dict, vertex_colors=d) + sphinx_plot(pl) + Here are some more common graphs with typical options:: sage: C = graphs.CubeGraph(8) sage: P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) sage: P.show() + .. PLOT:: + + C = graphs.CubeGraph(8) + P = C.graphplot(vertex_labels=False, vertex_size=0, graph_border=True) + sphinx_plot(P) + + :: + sage: G = graphs.HeawoodGraph().copy(sparse=True) sage: for u,v,l in G.edges(): ... G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') sage: G.graphplot(edge_labels=True).show() + .. PLOT:: + + G = graphs.HeawoodGraph().copy(sparse=True) + for u,v,l in G.edges(): + G.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sphinx_plot(G.graphplot(edge_labels=True)) + The options for plotting also work with directed graphs:: - sage: D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], 10: [11], 11: [12, 18], 12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], 17: [18], 18: [19], 19: []}) + sage: D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], + ... 4: [17, 5], 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], + ... 10: [11], 11: [12, 18], 12: [16, 13], 13: [14], 14: [15], 15: [16], + ... 16: [17], 17: [18], 18: [19], 19: []}) sage: for u,v,l in D.edges(): ... D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') sage: D.graphplot(edge_labels=True, layout='circular').show() + .. PLOT:: + + D = DiGraph( { 0: [1, 10, 19], 1: [8, 2], 2: [3, 6], 3: [19, 4], 4: [17, 5], + 5: [6, 15], 6: [7], 7: [8, 14], 8: [9], 9: [10, 13], 10: [11], + 11: [12, 18],12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], + 17: [18], 18: [19], 19: []}) + for u,v,l in D.edges(): + D.set_edge_label(u,v,'(' + str(u) + ',' + str(v) + ')') + sphinx_plot(D.graphplot(edge_labels=True, layout='circular')) + This example shows off the coloring of edges:: sage: from sage.plot.colors import rainbow @@ -776,6 +969,20 @@ def plot(self, **kwds): ... edge_colors[R[i]].append((u,v,l)) sage: C.graphplot(vertex_labels=False, vertex_size=0, edge_colors=edge_colors).show() + .. PLOT:: + + from sage.plot.colors import rainbow + C = graphs.CubeGraph(5) + R = rainbow(5) + edge_colors = {} + for i in range(5): + edge_colors[R[i]] = [] + for u,v,l in C.edges(): + for i in range(5): + if u[i] != v[i]: + edge_colors[R[i]].append((u,v,l)) + sphinx_plot(C.graphplot(vertex_labels=False, vertex_size=0, + edge_colors=edge_colors)) With the ``partition`` option, we can separate out same-color groups of vertices:: @@ -784,6 +991,12 @@ def plot(self, **kwds): sage: Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] sage: D.show(partition=Pi) + .. PLOT:: + + D = graphs.DodecahedralGraph() + Pi = [[6,5,15,14,7],[16,13,8,2,4],[12,17,9,3,1],[0,19,18,10,11]] + sphinx_plot(D.plot(partition=Pi)) + Loops are also plotted correctly:: sage: G = graphs.PetersenGraph() @@ -791,19 +1004,43 @@ def plot(self, **kwds): sage: G.add_edge(0,0) sage: G.show() + .. PLOT:: + + G = graphs.PetersenGraph() + G.allow_loops(True) + G.add_edge(0,0) + sphinx_plot(G) + :: sage: D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) sage: D.show() sage: D.show(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + .. PLOT:: + + D = DiGraph({0:[0,1], 1:[2], 2:[3]}, loops=True) + P = D.plot(edge_colors={(0,1,0):[(0,1,None),(1,2,None)],(0,0,0):[(2,3,None)]}) + sphinx_plot(P) + More options:: - sage: pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], 3:[0.6, -0.8], 4:[0.8, 0.3]} + sage: pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], + ... 3:[0.6, -0.8], 4:[0.8, 0.3]} sage: g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) sage: g.graphplot(pos=pos, layout='spring', iterations=0).plot() Graphics object consisting of 11 graphics primitives + .. PLOT: + + pos = {0:[0.0, 1.5], 1:[-0.8, 0.3], 2:[-0.6, -0.8], + 3:[0.6, -0.8], 4:[0.8, 0.3]} + g = Graph({0:[1], 1:[2], 2:[3], 3:[4], 4:[0]}) + P = g.graphplot(pos=pos, layout='spring', iterations=0).plot() + sphinx_plot(P) + + :: + sage: G = Graph() sage: P = G.graphplot().plot() sage: P.axes() @@ -820,12 +1057,27 @@ def plot(self, **kwds): sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() Graphics object consisting of 14 graphics primitives + .. PLOT: + + T = list(graphs.trees(7)) + t = T[3] + sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + :: sage: T = list(graphs.trees(7)) sage: t = T[3] sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}).plot() Graphics object consisting of 14 graphics primitives + + .. PLOT:: + + T = list(graphs.trees(7)) + t = T[3] + sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]})) + + :: + sage: t.set_edge_label(0,1,-7) sage: t.set_edge_label(0,5,3) sage: t.set_edge_label(0,5,99) @@ -836,22 +1088,53 @@ def plot(self, **kwds): sage: t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True).plot() Graphics object consisting of 20 graphics primitives + .. PLOT:: + + T = list(graphs.trees(7)) + t = T[3] + t.set_edge_label(0,1,-7) + t.set_edge_label(0,5,3) + t.set_edge_label(0,5,99) + t.set_edge_label(1,2,1000) + t.set_edge_label(3,2,'spam') + t.set_edge_label(2,6,3/2) + t.set_edge_label(0,4,66) + sphinx_plot(t.graphplot(heights={0:[0], 1:[4,5,1], 2:[2], 3:[3,6]}, edge_labels=True)) + :: sage: T = list(graphs.trees(7)) sage: t = T[3] sage: t.graphplot(layout='tree').show() + .. PLOT: + + T = list(graphs.trees(7)) + t = T[3] + sphinx_plot(t.graphplot(layout='tree')) + The tree layout is also useful:: sage: t = DiGraph('JCC???@A??GO??CO??GO??') sage: t.graphplot(layout='tree', tree_root=0, tree_orientation="up").show() + .. PLOT:: + + t = DiGraph('JCC???@A??GO??CO??GO??') + sphinx_plot(t.graphplot(layout='tree', tree_root=0, tree_orientation="up")) + More examples:: sage: D = DiGraph({0:[1,2,3], 2:[1,4], 3:[0]}) sage: D.graphplot().show() + .. PLOT:: + + D = DiGraph({0:[1,2,3], 2:[1,4], 3:[0]}) + sphinx_plot(D.graphplot()) + + :: + sage: D = DiGraph(multiedges=True, sparse=True) sage: for i in range(5): ... D.add_edge((i,i+1,'a')) @@ -859,12 +1142,29 @@ def plot(self, **kwds): sage: D.graphplot(edge_labels=True,edge_colors=D._color_by_label()).plot() Graphics object consisting of 34 graphics primitives + .. PLOT:: + + D = DiGraph(multiedges=True, sparse=True) + for i in range(5): + D.add_edge((i,i+1,'a')) + D.add_edge((i,i-1,'b')) + sphinx_plot(D.graphplot(edge_labels=True,edge_colors=D._color_by_label())) + + :: + sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed').plot() Graphics object consisting of 22 graphics primitives + .. PLOT:: + + g = Graph({}, loops=True, multiedges=True, sparse=True) + g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), + (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) + sphinx_plot(g.graphplot(edge_labels=True, color_by_label=True, edge_style='dashed')) + The ``edge_style`` option may be provided in the short format too:: sage: g.graphplot(edge_labels=True, color_by_label=True, edge_style='--').plot() @@ -918,8 +1218,11 @@ def plot(self, **kwds): ymax = G.ymax() dx = (xmax-xmin)/10.0 dy = (ymax-ymin)/10.0 - border = (line([( xmin - dx, ymin - dy), ( xmin - dx, ymax + dy ), ( xmax + dx, ymax + dy ), ( xmax + dx, ymin - dy ), ( xmin - dx, ymin - dy )], thickness=1.3)) - border.axes_range(xmin = (xmin - dx), xmax = (xmax + dx), ymin = (ymin - dy), ymax = (ymax + dy)) + border = (line([( xmin - dx, ymin - dy), ( xmin - dx, ymax + dy ), + ( xmax + dx, ymax + dy ), ( xmax + dx, ymin - dy ), ( xmin - dx, ymin - dy )], + thickness=1.3)) + border.axes_range(xmin = (xmin - dx), xmax = (xmax + dx), ymin = (ymin - dy), + ymax = (ymax + dy)) G += border G.set_aspect_ratio(1) G.axes(False) diff --git a/src/sage/graphs/hyperbolicity.pyx b/src/sage/graphs/hyperbolicity.pyx index 2dfa7bd0a8f..7c28d149cdb 100644 --- a/src/sage/graphs/hyperbolicity.pyx +++ b/src/sage/graphs/hyperbolicity.pyx @@ -127,23 +127,23 @@ At Python level : REFERENCES: -.. [BCCM15] M. Borassi, D. Coudert, P. Crescenzi, and A. Marino. +.. [BCCM15] \M. Borassi, D. Coudert, P. Crescenzi, and A. Marino. On Computing the Hyperbolicity of Real-World Graphs. Proceedings of the 23rd European Symposium on Algorithms (ESA 2015) -.. [CCL15] N. Cohen, D. Coudert, and A. Lancin. On computing the Gromov +.. [CCL15] \N. Cohen, D. Coudert, and A. Lancin. On computing the Gromov hyperbolicity. ACM Journal of Experimental Algorithmics, 20(1.6):1-18, 2015. [``_] or [``_]. -.. [FIV12] H. Fournier, A. Ismail, and A. Vigneron. Computing the Gromov +.. [FIV12] \H. Fournier, A. Ismail, and A. Vigneron. Computing the Gromov hyperbolicity of a discrete metric space. ArXiv, Tech. Rep. arXiv:1210.3323, Oct. 2012. [``_]. -.. [Gromov87] M. Gromov. Hyperbolic groups. Essays in Group Theory, 8:75--263, +.. [Gromov87] \M. Gromov. Hyperbolic groups. Essays in Group Theory, 8:75--263, 1987. -.. [Soto11] M. A. Soto Gomez. 2011. Quelques proprietes topologiques des +.. [Soto11] \M. A. Soto Gomez. 2011. Quelques proprietes topologiques des graphes et applications a internet et aux reseaux. Ph.D. Dissertation. Univ. Paris Diderot (Paris 7). @@ -179,14 +179,13 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.functions.other import floor from sage.data_structures.bitset import Bitset -from sage.ext.memory cimport check_allocarray, check_calloc +include "cysignals/memory.pxi" from sage.ext.memory_allocator cimport MemoryAllocator from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph from sage.graphs.base.static_sparse_graph cimport free_short_digraph from libc.stdint cimport uint16_t, uint32_t, uint64_t include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" include "sage/data_structures/bitset.pxi" @@ -567,10 +566,10 @@ cdef inline pair** sort_pairs(uint32_t N, pairs_of_length[0] == NULL or cpt_pairs == NULL): if pairs_of_length != NULL: - sage_free(pairs_of_length[0]) - sage_free(nb_pairs_of_length) - sage_free(pairs_of_length) - sage_free(cpt_pairs) + sig_free(pairs_of_length[0]) + sig_free(nb_pairs_of_length) + sig_free(pairs_of_length) + sig_free(cpt_pairs) raise MemoryError # ==> Defines pairs_of_length[d] for all d @@ -596,7 +595,7 @@ cdef inline pair** sort_pairs(uint32_t N, pairs_of_length[ k ][ cpt_pairs[ k ] ].t = j cpt_pairs[ k ] += 1 - sage_free(cpt_pairs) + sig_free(cpt_pairs) return pairs_of_length @@ -841,8 +840,8 @@ cdef tuple hyperbolicity_BCCM(int N, h_UB = h # We now free the memory - sage_free(pairs_of_length[0]) - sage_free(pairs_of_length) + sig_free(pairs_of_length[0]) + sig_free(pairs_of_length) if verbose: print "Visited 4-tuples:", nq @@ -1060,9 +1059,9 @@ cdef tuple hyperbolicity_CCL(int N, break # We now free the memory - sage_free(nb_pairs_of_length) - sage_free(pairs_of_length[0]) - sage_free(pairs_of_length) + sig_free(nb_pairs_of_length) + sig_free(pairs_of_length[0]) + sig_free(pairs_of_length) # Last, we return the computed value and the certificate if len(certificate) == 0: @@ -1407,10 +1406,10 @@ def hyperbolicity(G, _far_apart_pairs_ = check_allocarray(N * N, sizeof(unsigned short)) far_apart_pairs = check_allocarray(N, sizeof(unsigned short *)) if _distances_ == NULL or _far_apart_pairs_ == NULL or far_apart_pairs == NULL: - sage_free(_distances_) - sage_free(distances) - sage_free(_far_apart_pairs_) - sage_free(far_apart_pairs) + sig_free(_distances_) + sig_free(distances) + sig_free(_far_apart_pairs_) + sig_free(far_apart_pairs) raise MemoryError("Unable to allocate array '_distances_' or '_far_apart_pairs_'.") distances_and_far_apart_pairs(G, _distances_, _far_apart_pairs_) @@ -1477,10 +1476,10 @@ def hyperbolicity(G, # We now release the memory - sage_free(distances) - sage_free(_distances_) - sage_free(_far_apart_pairs_) - sage_free(far_apart_pairs) + sig_free(distances) + sig_free(_distances_) + sig_free(_far_apart_pairs_) + sig_free(far_apart_pairs) # Map the certificate 'certif' with the corresponding vertices in the graph V = G.vertices() @@ -1544,7 +1543,7 @@ cdef dict __hyperbolicity_distribution__(int N, unsigned short ** distances): Nchoose4 = binomial(N,4) cdef dict hdict = {ZZ(i)/2: (ZZ(hdistr[i])/Nchoose4) for 0 <= i <= N if hdistr[i] > 0} - sage_free(hdistr) + sig_free(hdistr) return hdict @@ -1613,7 +1612,7 @@ cdef dict __hyperbolicity_sampling__(int N, unsigned short ** distances, uint64_ # We prepare the dictionary of hyperbolicity distribution from sampling cdef dict hdict = dict( [ (ZZ(i)/2, ZZ(hdistr[i])/ZZ(sampling_size)) for 0 <= i <= N if hdistr[i] > 0 ] ) - sage_free(hdistr) + sig_free(hdistr) return hdict @@ -1710,7 +1709,7 @@ def hyperbolicity_distribution(G, algorithm='sampling', sampling_size=10**6): _distances_ = c_distances_all_pairs(H) distances = check_allocarray(N, sizeof(unsigned short *)) if distances == NULL: - sage_free(_distances_) + sig_free(_distances_) raise MemoryError for 0 <= i < N: @@ -1724,7 +1723,7 @@ def hyperbolicity_distribution(G, algorithm='sampling', sampling_size=10**6): raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm)) # We release memory - sage_free(distances) - sage_free(_distances_) + sig_free(distances) + sig_free(_distances_) return hdict diff --git a/src/sage/graphs/isgci.py b/src/sage/graphs/isgci.py index 5251262e9cc..f94f7dfe9d3 100644 --- a/src/sage/graphs/isgci.py +++ b/src/sage/graphs/isgci.py @@ -589,7 +589,7 @@ def __contains__(self, g): excluded = self.forbidden_subgraphs() if excluded is None: - raise NotImplementedError("No recognition agorithm is available"+ + raise NotImplementedError("No recognition algorithm is available "+ "for this class.") for gg in excluded: diff --git a/src/sage/graphs/matchpoly.pyx b/src/sage/graphs/matchpoly.pyx index 1b2fc6a5d8d..9c7ea5e202d 100644 --- a/src/sage/graphs/matchpoly.pyx +++ b/src/sage/graphs/matchpoly.pyx @@ -37,7 +37,7 @@ from sage.rings.integer cimport Integer from sage.misc.all import prod include "cysignals/signals.pxi" include 'sage/ext/cdefs.pxi' -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_poly cimport * @@ -257,13 +257,13 @@ def matching_polynomial(G, complement=True, name=None): # delete_and_add will need the rest of the memory. fmpz_poly_init(pol) # sets to zero - edges_mem = sage_malloc(2 * nedges * nedges * sizeof(int)) - edges = sage_malloc(2 * nedges * sizeof(int *)) + edges_mem = sig_malloc(2 * nedges * nedges * sizeof(int)) + edges = sig_malloc(2 * nedges * sizeof(int *)) if edges_mem is NULL or edges is NULL: if edges_mem is not NULL: - sage_free(edges_mem) + sig_free(edges_mem) if edges is not NULL: - sage_free(edges) + sig_free(edges) raise MemoryError("Error allocating memory for matchpoly.") for i from 0 <= i < 2 * nedges: @@ -294,8 +294,8 @@ def matching_polynomial(G, complement=True, name=None): coeffs_ZZ.append(c_ZZ * (-1)**((nverts - i) / 2)) f = x.parent()(coeffs_ZZ) - sage_free(edges_mem) - sage_free(edges) + sig_free(edges_mem) + sig_free(edges) fmpz_poly_clear(pol) if name is not None: return f.change_variable_name(name) diff --git a/src/sage/graphs/mcqd.pyx b/src/sage/graphs/mcqd.pyx index c5932870302..6764fa40560 100644 --- a/src/sage/graphs/mcqd.pyx +++ b/src/sage/graphs/mcqd.pyx @@ -1,4 +1,3 @@ -include 'sage/ext/stdsage.pxi' from sage.ext.memory_allocator cimport MemoryAllocator def mcqd(G): diff --git a/src/sage/graphs/modular_decomposition.pyx b/src/sage/graphs/modular_decomposition.pyx index fe37f2c331f..78170f8debb 100644 --- a/src/sage/graphs/modular_decomposition.pyx +++ b/src/sage/graphs/modular_decomposition.pyx @@ -2,7 +2,7 @@ r""" Modular decomposition """ -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from libc.string cimport memset @@ -102,7 +102,7 @@ cpdef modular_decomposition(g): International Journal of Foundations of Computer Science vol. 10 n2 pp.147--170, 1999 - .. [CapHabMont02b] C. Capelle, M. Habib et F. de Montgolfier + .. [CapHabMont02b] \C. Capelle, M. Habib et F. de Montgolfier Graph decomposition and Factorising Permutations Discrete Mathematics and Theoretical Computer Sciences, vol 5 no. 1 , 2002. """ @@ -120,7 +120,7 @@ cpdef modular_decomposition(g): label_id[label] = id G.n = g.order() - G.G = sage_malloc(G.n*sizeof(c_adj *)) + G.G = sig_malloc(G.n*sizeof(c_adj *)) memset( G.G, 0, G.n*sizeof(c_adj *)) @@ -130,12 +130,12 @@ cpdef modular_decomposition(g): i = label_id[u] j = label_id[v] - a= sage_malloc(sizeof(c_adj)) + a= sig_malloc(sizeof(c_adj)) a.s = j a.suiv = G.G[i] G.G[i] = a - a= sage_malloc(sizeof(c_adj)) + a= sig_malloc(sizeof(c_adj)) a.s = i a.suiv = G.G[j] G.G[j] = a diff --git a/src/sage/graphs/pq_trees.py b/src/sage/graphs/pq_trees.py index d2fa99bcd89..7e250d17199 100644 --- a/src/sage/graphs/pq_trees.py +++ b/src/sage/graphs/pq_trees.py @@ -95,7 +95,7 @@ REFERENCES: -.. [Haj] M. Hajiaghayi +.. [Haj] \M. Hajiaghayi http://www-math.mit.edu/~hajiagha/pp11.ps Authors: diff --git a/src/sage/graphs/schnyder.py b/src/sage/graphs/schnyder.py index 6af206e9581..b570a9ad7ff 100644 --- a/src/sage/graphs/schnyder.py +++ b/src/sage/graphs/schnyder.py @@ -525,8 +525,8 @@ def _compute_coordinates(g, x): class TreeNode(): """ - A class to represent each node in the trees used by :func:`_realizer` and - :func:`_compute_coordinates` when finding a planar geometric embedding in + A class to represent each node in the trees used by ``_realizer`` and + ``_compute_coordinates`` when finding a planar geometric embedding in the grid. Each tree node is doubly linked to its parent and children. diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 6ccaf14b0be..e53ea62f971 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -23,11 +23,11 @@ including minimum spanning trees. REFERENCES: -.. [Aldous90] D. Aldous, 'The random walk construction of +.. [Aldous90] \D. Aldous, 'The random walk construction of uniform spanning trees', SIAM J Discrete Math 3 (1990), 450-465. -.. [Broder89] A. Broder, 'Generating random spanning trees', +.. [Broder89] \A. Broder, 'Generating random spanning trees', Proceedings of the 30th IEEE Symposium on Foundations of Computer Science, 1989, pp. 442-447. :doi:`10.1109/SFCS.1989.63516`, _ diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 2c1a24436cd..ad7e2e86498 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -20,7 +20,7 @@ by Sage. Help us if you know any. REFERENCES: -.. [BvL84] A. Brouwer, J van Lint, +.. [BvL84] \A. Brouwer, J van Lint, Strongly regular graphs and partial geometries, Enumeration and design, (Waterloo, Ont., 1982) (1984): 85-122. @@ -184,7 +184,7 @@ def is_orthogonal_array_block_graph(int v,int k,int l,int mu): REFERENCE: - .. [Pa92] D. V. Pasechnik, + .. [Pa92] \D. V. Pasechnik, Skew-symmetric association schemes with two classes and strongly regular graphs of type `L_{2n-1}(4n- 1)`, Acta Applicandaie Math. 29(1992), 129-138 @@ -966,7 +966,7 @@ def is_polhill(int v,int k,int l,int mu): REFERENCE: - .. [Polhill09] J. Polhill, + .. [Polhill09] \J. Polhill, Negative Latin square type partial difference sets and amorphic association schemes with Galois rings, Journal of Combinatorial Designs 17, no. 3 (2009): 266-282. @@ -1806,7 +1806,7 @@ def SRG_100_44_18_20(): REFERENCES: - .. [JK03] L. K. Jørgensen, M. Klin, M., + .. [JK03] \L. K. Jørgensen, M. Klin, M., Switching of edges in strongly regular graphs. I. A family of partial difference sets on 100 vertices, Electronic Journal of Combinatorics 10(1), 2003. @@ -1857,12 +1857,12 @@ def SRG_105_32_4_12(): REFERENCES: - .. [GS70] J.-M. Goethals and J. J. Seidel, + .. [GS70] \J.-M. Goethals and J. J. Seidel, Strongly regular graphs derived from combinatorial designs, Can. J. Math. 22 (1970) 597-614. http://dx.doi.org/10.4153/CJM-1970-067-9 - .. [Co06] K. Coolsaet, + .. [Co06] \K. Coolsaet, The uniqueness of the strongly regular graph srg(105,32,4,12), Bull. Belg. Math. Soc. 12(2006), 707-718. http://projecteuclid.org/euclid.bbms/1136902608 @@ -1948,7 +1948,7 @@ def SRG_176_49_12_14(): REFERENCE: - .. [BrouwerPolarities82] A. Brouwer, + .. [BrouwerPolarities82] \A. Brouwer, Polarities of G. Higman's symmetric design and a strongly regular graph on 176 vertices, Aequationes mathematicae 25, no. 1 (1982): 77-82. """ @@ -2013,12 +2013,12 @@ def SRG_210_99_48_45(): REFERENCES: - .. [KPRWZ10] M. H. Klin, C. Pech, S. Reichard, A. Woldar, M. Zvi-Av, + .. [KPRWZ10] \M. H. Klin, C. Pech, S. Reichard, A. Woldar, M. Zvi-Av, Examples of computer experimentation in algebraic combinatorics, ARS MATHEMATICA CONTEMPORANEA 3 (2010) 237–258 http://amc-journal.eu/index.php/amc/article/viewFile/119/118 - .. [COCO] I. A. Faradjev and M. H. Klin, + .. [COCO] \I. A. Faradjev and M. H. Klin, Computer package for computations with coherent configurations, Proc. ISSAC-91, ACM Press, Bonn, 1991, pages 219–223; code, by I.A.Faradjev (with contributions by A.E.Brouwer, D.V.Pasechnik) @@ -2069,7 +2069,7 @@ def SRG_243_110_37_60(): REFERENCE: - .. [GS75] J.M. Goethals, and J. J. Seidel, + .. [GS75] \J.M. Goethals, and J. J. Seidel, The regular two-graph on 276 vertices, Discrete Mathematics 12, no. 2 (1975): 143-158. http://dx.doi.org/10.1016/0012-365X(75)90029-1 @@ -2116,7 +2116,7 @@ def SRG_196_91_42_42(): REFERENCE: - .. [IS06] Y.J. Ionin, S. Shrikhande, + .. [IS06] \Y.J. Ionin, S. Shrikhande, Combinatorics of symmetric designs. Cambridge University Press, 2006. """ @@ -2179,7 +2179,7 @@ def SRG_276_140_58_84(): REFERENCE: - .. [HT96] W. H. Haemers and V. D. Tonchev, + .. [HT96] \W. H. Haemers and V. D. Tonchev, Spreads in strongly regular graphs, Designs, Codes and Cryptography 8 (1996) 145-157. """ @@ -2249,7 +2249,7 @@ def SRG_280_117_44_52(): REFERENCE: - .. [MR85] R. Mathon and A. Rosa, + .. [MR85] \R. Mathon and A. Rosa, A new strongly regular graph, Journal of Combinatorial Theory, Series A 38, no. 1 (1985): 84-86. http://dx.doi.org/10.1016/0097-3165(85)90025-1 @@ -2299,7 +2299,7 @@ def strongly_regular_from_two_weight_code(L): REFERENCES: - .. [vLintSchrijver81] J. H. van Lint, and A. Schrijver (1981), + .. [vLintSchrijver81] \J. H. van Lint, and A. Schrijver (1981), Construction of strongly regular graphs, two-weight codes and partial geometries by finite fields, Combinatorica, 1(1), 63-73. @@ -2409,7 +2409,7 @@ def strongly_regular_from_two_intersection_set(M): REFERENCES: - .. [CDB13] I. Cardinali and B. De Bruyn, + .. [CDB13] \I. Cardinali and B. De Bruyn, Spin-embeddings, two-intersection sets and two-weight codes, Ars Comb. 109 (2013): 309-319. https://biblio.ugent.be/publication/4241842/file/4241845.pdf @@ -2590,7 +2590,7 @@ def SRG_1288_792_476_504(): REFERENCE: - .. [BvE92] A. Brouwer and C. Van Eijl, + .. [BvE92] \A. Brouwer and C. Van Eijl, On the p-Rank of the Adjacency Matrices of Strongly Regular Graphs Journal of Algebraic Combinatorics (1992), vol.1, n.4, pp329-346, http://dx.doi.org/10.1023/A%3A1022438616684 @@ -3020,7 +3020,7 @@ def _build_small_srg_database(): REFERENCES: - .. [CK86] R. Calderbank, W.M. Kantor, + .. [CK86] \R. Calderbank, W.M. Kantor, The geometry of two-weight codes, Bull. London Math. Soc. 18(1986) 97-122 """ diff --git a/src/sage/graphs/trees.pyx b/src/sage/graphs/trees.pyx index 037aa0b31bb..b3c014de3ad 100644 --- a/src/sage/graphs/trees.pyx +++ b/src/sage/graphs/trees.pyx @@ -18,7 +18,7 @@ REFERENCES: cdef extern from "limits.h": cdef int INT_MAX -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" # from networkx import MultiGraph @@ -85,10 +85,10 @@ cdef class TreeIterator: sage: t = None # indirect doctest """ if self.l != NULL: - sage_free(self.l) + sig_free(self.l) self.l = NULL if self.current_level_sequence != NULL: - sage_free(self.current_level_sequence) + sig_free(self.current_level_sequence) self.current_level_sequence = NULL def __str__(self): @@ -146,8 +146,8 @@ cdef class TreeIterator: self.first_time = 0 self.q = 0 else: - self.l = sage_malloc(self.vertices * sizeof(int)) - self.current_level_sequence = sage_malloc(self.vertices * sizeof(int)) + self.l = sig_malloc(self.vertices * sizeof(int)) + self.current_level_sequence = sig_malloc(self.vertices * sizeof(int)) if self.l == NULL or self.current_level_sequence == NULL: raise MemoryError diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index 419c73fbf8e..e77fde51780 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -93,7 +93,7 @@ class AffineGroup(UniqueRepresentation, Group): space. If an integer is given, it must be a prime power and the corresponding finite field is constructed. - * ``var`` -- (Defalut: ``'a'``) Keyword argument to specify the finite + * ``var`` -- (default: ``'a'``) Keyword argument to specify the finite field generator name in the case where ``ring`` is a prime power. EXAMPLES:: diff --git a/src/sage/groups/affine_gps/euclidean_group.py b/src/sage/groups/affine_gps/euclidean_group.py index 15f1ad8f442..7ad5bda8412 100644 --- a/src/sage/groups/affine_gps/euclidean_group.py +++ b/src/sage/groups/affine_gps/euclidean_group.py @@ -92,7 +92,7 @@ class EuclideanGroup(AffineGroup): space. If an integer is given, it must be a prime power and the corresponding finite field is constructed. - * ``var`` -- (Defalut: ``'a'``) Keyword argument to specify the finite + * ``var`` -- (default: ``'a'``) Keyword argument to specify the finite field generator name in the case where ``ring`` is a prime power. EXAMPLES:: diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 8e2a058b0de..b9074c61238 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -31,18 +31,20 @@ """ #***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) +# Copyright (C) 2013 Volker Braun # +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from sage.matrix.matrix import is_Matrix from sage.misc.cachefunc import cached_method -from sage.groups.matrix_gps.group_element import MatrixGroupElement_base +from sage.structure.element import MultiplicativeGroupElement -class AffineGroupElement(MatrixGroupElement_base): +class AffineGroupElement(MultiplicativeGroupElement): """ An affine group element. @@ -84,6 +86,20 @@ class AffineGroupElement(MatrixGroupElement_base): sage: G(2) [2 0] [0] x |-> [0 2] x + [0] + + Conversion from a matrix and a matrix group element:: + + sage: M = Matrix(4, 4, [0, 0, -1, 1, 0, -1, 0, 1, -1, 0, 0, 1, 0, 0, 0, 1]) + sage: A = AffineGroup(3, ZZ) + sage: A(M) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] + sage: G = MatrixGroup([M]) + sage: A(G.0) + [ 0 0 -1] [1] + x |-> [ 0 -1 0] x + [1] + [-1 0 0] [1] """ def __init__(self, parent, A, b=0, convert=True, check=True): r""" @@ -95,10 +111,14 @@ def __init__(self, parent, A, b=0, convert=True, check=True): sage: g = G.random_element() sage: TestSuite(g).run() """ + try: + A = A.matrix() + except AttributeError: + pass if is_Matrix(A) and A.nrows() == A.ncols() == parent.degree()+1: g = A - A = g.submatrix(0,0,2,2) d = parent.degree() + A = g.submatrix(0, 0, d, d) b = [ g[i,d] for i in range(d) ] convert = True if convert: @@ -416,3 +436,26 @@ def __cmp__(self, other): return c return cmp(self._b, other._b) + def list(self): + """ + Return list representation of ``self``. + + EXAMPLES:: + + sage: F = AffineGroup(3, QQ) + sage: g = F([1,2,3,4,5,6,7,8,0], [10,11,12]) + sage: g + [1 2 3] [10] + x |-> [4 5 6] x + [11] + [7 8 0] [12] + sage: g.matrix() + [ 1 2 3|10] + [ 4 5 6|11] + [ 7 8 0|12] + [--------+--] + [ 0 0 0| 1] + sage: g.list() + [[1, 2, 3, 10], [4, 5, 6, 11], [7, 8, 0, 12], [0, 0, 0, 1]] + """ + return [r.list() for r in self.matrix().rows()] + diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 2727917bd86..5dce46996b2 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -735,9 +735,9 @@ def tropical_coordinates(self): REFERENCES: - .. [Dynnikov07] I. Dynnikov and B. Wiest, On the complexity of braids, + .. [Dynnikov07] \I. Dynnikov and B. Wiest, On the complexity of braids, J. Europ. Math. Soc. 9 (2007) - .. [Dehornoy] P. Dehornoy, Le probleme d'isotopie des tresses, in + .. [Dehornoy] \P. Dehornoy, Le probleme d'isotopie des tresses, in lecons de mathematiques d'aujourd'hui vol. 4 """ coord = [0, 1] * self.strands() diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 92a4393af15..580a1c34d49 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -1067,7 +1067,7 @@ def direct_product(self, H, reduced=False, new_names=True): REFERENCES: - .. [JohnsonPG90] D.L. Johnson. *Presentations of Groups*. + .. [JohnsonPG90] \D.L. Johnson. *Presentations of Groups*. Cambridge University Press. (1990). """ from sage.groups.free_group import FreeGroup, _lexi_gen @@ -1168,9 +1168,7 @@ def semidirect_product(self, H, hom, check=True, reduced=False): You can attempt to reduce the presentation of the output group:: sage: D = C2.semidirect_product(C8, hom); D - Finitely presented group < a, b, c, d | - a^2, b^-1*a^-1*b*a*d^-1*c^-1, c^-1*a^-1*c*a*d^-1, d^-1*a^-1*d*a, - b^2*c^-1, c^-1*b^-1*c*b, d^-1*b^-1*d*b, c^2*d^-1, d^-1*c^-1*d*c, d^2 > + Finitely presented group < a, b | a^2, b^8, a^-1*b*a*b > sage: D = C2.semidirect_product(C8, hom, reduced=True); D Finitely presented group < a, b | a^2, (a*b)^2, b^8 > @@ -1178,10 +1176,9 @@ def semidirect_product(self, H, hom, check=True, reduced=False): sage: C4 = groups.presentation.Cyclic(4) sage: hom = (C3.gens(), [(C4.gens(), C4.gens())]) sage: C3.semidirect_product(C4, hom) - Finitely presented group < a, b, c | - a^3, b^-1*a^-1*b*a, c^-1*a^-1*c*a, b^2*c^-1, c^-1*b^-1*c*b, c^2 > + Finitely presented group < a, b | a^3, b^4, a^-1*b*a*b^-1 > sage: D = C3.semidirect_product(C4, hom, reduced=True); D - Finitely presented group < a, b | a^3, b^4, b^-1*a^-1*b*a > + Finitely presented group < a, b | a^3, b^4, a^-1*b*a*b^-1 > sage: D.as_permutation_group().is_cyclic() True @@ -1193,9 +1190,7 @@ def semidirect_product(self, H, hom, check=True, reduced=False): sage: C12 = groups.presentation.Cyclic(12) sage: hom = (C5.gens(), [(C12.gens(), C12.gens())]) sage: sp = C5.semidirect_product(C12, hom, check=False); sp - Finitely presented group < a, b, c, d | - a^5, b^-1*a^-1*b*a, c^-1*a^-1*c*a, d^-1*a^-1*d*a, b^2*d^-1, - c^-1*b^-1*c*b, d^-1*b^-1*d*b, c^3, d^-1*c^-1*d*c, d^2 > + Finitely presented group < a, b | a^5, b^12, a^-1*b*a*b^-1 > sage: sp.as_permutation_group().is_cyclic(), sp.order() (True, 60) diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index 5a23083c58c..49ba708eae2 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -89,7 +89,7 @@ cdef class Group(Parent): sage: G._repr_option('element_is_atomic') False - Check for #8119:: + Check for :trac:`8119`:: sage: G = SymmetricGroup(2) sage: h = hash(G) diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index f36bf4b1a30..cab18742bf9 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -57,14 +57,20 @@ - Miscellaneous Groups (``groups.misc.``) - - :func:`groups.misc.AdditiveAbelian ` - - :class:`groups.misc.AdditiveCyclic ` - - :func:`groups.misc.Braid ` - - :func:`groups.misc.CoxeterGroup ` - - :func:`groups.misc.Free ` - - :class:`groups.misc.RightAngledArtin ` - - :func:`groups.misc.SemimonomialTransformation ` - - :func:`groups.misc.WeylGroup ` + - Coxeter, reflection and related groups + + - :func:`groups.misc.Braid ` + - :func:`groups.misc.CoxeterGroup ` + - :func:`groups.misc.ReflectionGroup ` + - :class:`groups.misc.RightAngledArtin ` + - :func:`groups.misc.WeylGroup ` + + - other miscellanous groups + + - :func:`groups.misc.AdditiveAbelian ` + - :class:`groups.misc.AdditiveCyclic ` + - :func:`groups.misc.Free ` + - :func:`groups.misc.SemimonomialTransformation ` """ diff --git a/src/sage/groups/indexed_free_group.py b/src/sage/groups/indexed_free_group.py index af26bd8dcc3..8ab0fe3a408 100644 --- a/src/sage/groups/indexed_free_group.py +++ b/src/sage/groups/indexed_free_group.py @@ -348,7 +348,7 @@ def _element_constructor_(self, x=None): sage: G(-5) Traceback (most recent call last): ... - ValueError: unable to convert -5, use gen() instead + TypeError: unable to convert -5, use gen() instead """ if isinstance(x, (list, tuple, dict)): x = dict(x) diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index 8a06581cac1..a4fd17655e7 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -14,176 +14,6 @@ from sage.libs.all import libgap from sage.misc.cachefunc import cached_method - -class GroupElementMixinLibGAP(object): - - @cached_method - def order(self): - """ - Return the order of this group element, which is the smallest - positive integer `n` such that `g^n = 1`, or - +Infinity if no such integer exists. - - EXAMPLES:: - - sage: k = GF(7); - sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G - Matrix group over Finite Field of size 7 with 2 generators ( - [1 1] [1 0] - [0 1], [0 2] - ) - sage: G.order() - 21 - sage: G.gen(0).order(), G.gen(1).order() - (7, 3) - - sage: k = QQ; - sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G - Matrix group over Rational Field with 2 generators ( - [1 1] [1 0] - [0 1], [0 2] - ) - sage: G.order() - +Infinity - sage: G.gen(0).order(), G.gen(1).order() - (+Infinity, +Infinity) - - sage: gl = GL(2, ZZ); gl - General Linear Group of degree 2 over Integer Ring - sage: g = gl.gen(2); g - [1 1] - [0 1] - sage: g.order() - +Infinity - """ - order = self.gap().Order() - if order.IsInt(): - return order.sage() - else: - assert order.IsInfinity() - from sage.rings.all import Infinity - return Infinity - - def word_problem(self, gens=None): - r""" - Solve the word problem. - - This method writes the group element as a product of the - elements of the list ``gens``, or the standard generators of - the parent of self if ``gens`` is None. - - INPUT: - - - ``gens`` -- a list/tuple/iterable of elements (or objects - that can be converted to group elements), or ``None`` - (default). By default, the generators of the parent group - are used. - - OUTPUT: - - A factorization object that contains information about the - order of factors and the exponents. A ``ValueError`` is raised - if the group element cannot be written as a word in ``gens``. - - ALGORITHM: - - Use GAP, which has optimized algorithms for solving the word - problem (the GAP functions ``EpimorphismFromFreeGroup`` and - ``PreImagesRepresentative``). - - EXAMPLE:: - - sage: G = GL(2,5); G - General Linear Group of degree 2 over Finite Field of size 5 - sage: G.gens() - ( - [2 0] [4 1] - [0 1], [4 0] - ) - sage: G(1).word_problem([G.gen(0)]) - 1 - sage: type(_) - - - sage: g = G([0,4,1,4]) - sage: g.word_problem() - ([4 1] - [4 0])^-1 - - Next we construct a more complicated element of the group from the - generators:: - - sage: s,t = G.0, G.1 - sage: a = (s * t * s); b = a.word_problem(); b - ([2 0] - [0 1]) * - ([4 1] - [4 0]) * - ([2 0] - [0 1]) - sage: flatten(b) - [ - [2 0] [4 1] [2 0] - [0 1], 1, [4 0], 1, [0 1], 1 - ] - sage: b.prod() == a - True - - We solve the word problem using some different generators:: - - sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) - sage: a.word_problem([s,t,u]) - ([2 0] - [0 1])^-1 * - ([1 1] - [0 1])^-1 * - ([0 4] - [1 0]) * - ([2 0] - [0 1])^-1 - - We try some elements that don't actually generate the group:: - - sage: a.word_problem([t,u]) - Traceback (most recent call last): - ... - ValueError: word problem has no solution - - AUTHORS: - - - David Joyner and William Stein - - David Loeffler (2010): fixed some bugs - - Volker Braun (2013): LibGAP - """ - from sage.libs.gap.libgap import libgap - G = self.parent() - if gens: - gen = lambda i:gens[i] - H = libgap.Group([G(x).gap() for x in gens]) - else: - gen = G.gen - H = G.gap() - hom = H.EpimorphismFromFreeGroup() - preimg = hom.PreImagesRepresentative(self.gap()) - - if preimg.is_bool(): - assert preimg == libgap.eval('fail') - raise ValueError('word problem has no solution') - - result = [] - n = preimg.NumberSyllables().sage() - exponent_syllable = libgap.eval('ExponentSyllable') - generator_syllable = libgap.eval('GeneratorSyllable') - for i in range(n): - exponent = exponent_syllable(preimg, i+1).sage() - generator = gen(generator_syllable(preimg, i+1).sage() - 1) - result.append( (generator, exponent) ) - from sage.structure.factorization import Factorization - result = Factorization(result) - result._set_cr(True) - return result - - class GroupMixinLibGAP(object): @cached_method @@ -407,6 +237,38 @@ def center(self): center = [G.One()] return self.subgroup(center) + def intersection(self, other): + """ + Return the intersection of two groups (if it makes sense) as a + subgroup of the first group. + + EXAMPLES:: + + sage: A = Matrix([(0, 1/2, 0), (2, 0, 0), (0, 0, 1)]) + sage: B = Matrix([(0, 1/2, 0), (-2, -1, 2), (0, 0, 1)]) + sage: G = MatrixGroup([A,B]) + sage: len(G) # isomorphic to S_3 + 6 + sage: G.intersection(GL(3,ZZ)) + Matrix group over Rational Field with 1 generators ( + [ 1 0 0] + [-2 -1 2] + [ 0 0 1] + ) + sage: GL(3,ZZ).intersection(G) + Matrix group over Integer Ring with 1 generators ( + [ 1 0 0] + [-2 -1 2] + [ 0 0 1] + ) + sage: G.intersection(SL(3,ZZ)) + Matrix group over Rational Field with 0 generators () + """ + G = self.gap() + H = other.gap() + C = G.Intersection(H) + return self.subgroup(C.GeneratorsOfGroup()) + @cached_method def irreducible_characters(self): """ @@ -510,7 +372,7 @@ def list(self): sage: all(g in G for g in G.list()) True - An example over a ring (see trac 5241):: + An example over a ring (see :trac:`5241`):: sage: M1 = matrix(ZZ,2,[[-1,0],[0,1]]) sage: M2 = matrix(ZZ,2,[[1,0],[0,-1]]) @@ -530,7 +392,7 @@ def list(self): [ 0 1], [ 0 -1], [ 0 -1] ) - An example over a field (see trac 10515):: + An example over a field (see :trac:`10515`):: sage: gens = [matrix(QQ,2,[1,0,0,1])] sage: MatrixGroup(gens).list() @@ -539,7 +401,7 @@ def list(self): [0 1] ) - Another example over a ring (see trac 9437):: + Another example over a ring (see :trac:`9437`):: sage: len(SL(2, Zmod(4)).list()) 48 diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index c47b62659e6..935d18dd6cd 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -238,10 +238,15 @@ class ParentLibGAP(SageObject): [ f1^-1*f4^-1*f1*f4, f1^-1*f5^-1*f1*f5, f1^-1*f6^-1*f1*f6, f2^-1*f4^-1*f2*f4, f2^-1*f5^-1*f2*f5, f2^-1*f6^-1*f2*f6, f3^-1*f4^-1*f3*f4, f3^-1*f5^-1*f3*f5, f3^-1*f6^-1*f3*f6 ] + + We can also convert directly to libgap:: + + sage: libgap(GL(2, ZZ)) + GL(2,Integers) """ return self._libgap - _gap_ = gap + _libgap_ = _gap_ = gap @cached_method def _gap_gens(self): diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py index b406cdbc19b..bce4233f78d 100644 --- a/src/sage/groups/matrix_gps/coxeter_group.py +++ b/src/sage/groups/matrix_gps/coxeter_group.py @@ -32,6 +32,7 @@ from sage.rings.all import ZZ from sage.rings.infinity import infinity from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField +from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecated_function_alias from sage.misc.cachefunc import cached_method @@ -265,6 +266,8 @@ def __init__(self, coxeter_matrix, base_ring, index_set): for j in range(n)}, coerce=True, copy=True) for i in range(n)] + # Make the generators dense matrices for consistancy and speed + gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is @@ -273,168 +276,21 @@ def __init__(self, coxeter_matrix, base_ring, index_set): category = category.Finite() else: category = category.Infinite() + self._index_set_inverse = {i: ii for ii,i in enumerate(self._matrix.index_set())} FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category) - def _finite_recognition(self): - """ - Return ``True`` if and only if the type is finite. - - This is an auxiliary function used during the initialisation. - - EXAMPLES: - - Some infinite ones:: - - sage: F = CoxeterGroups().Finite() - sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) - sage: W in F # indirect doctest - False - sage: W = CoxeterGroup([[1,-1,-1],[-1,1,-1],[-1,-1,1]]) - sage: W in F # indirect doctest - False - - Some finite ones:: - - sage: CoxeterGroup(['D',4], base_ring=QQ) in F # indirect doctest - True - sage: CoxeterGroup(['H',4]) in F # indirect doctest - True - """ - # First, we build the Coxeter graph of the group, without the - # edge labels. - coxeter_matrix = self._matrix - n = ZZ(coxeter_matrix.nrows()) - G = Graph([(i, j) for i in range(n) for j in range(i) - if coxeter_matrix[i, j] not in [1, 2]]) - # Coxeter graphs of finite Coxeter groups are forests - if not(G.is_forest()): - return False - comps = G.connected_components() - finite = True - # The group is finite if and only if for every connected - # component ``comp`` of its Coxeter graph, the submatrix of - # the Coxeter matrix corresponding to ``comp`` is one of the - # type-A,B,D,E,F,H,I matrices (up to permutation). So we - # shall check this condition on every ``comp``. - for comp in comps: - l = len(comp) - if l == 1: - # Any `1 \times 1` Coxeter matrix gives a finite group. - continue # A1 - elif l == 2: - # A dihedral group is finite iff there is no `\infty` - # in its Coxeter matrix. - c0, c1 = comp - if coxeter_matrix[c0, c1] > 0: - continue # I2 - return False - elif l == 3: - # The `3`-node case. The finite groups to check for - # here are `A_3`, `B_3` and `H_3`. - c0, c1, c2 = comp - s = sorted([coxeter_matrix[c0, c1], - coxeter_matrix[c0, c2], - coxeter_matrix[c1, c2]]) - if s[1] == 3 and s[2] in [3, 4, 5]: - continue # A3, B3, H3 - return False - elif l == 4: - # The `4`-node case. The finite groups to check for - # here are `A_4`, `B_4`, `D_4`, `F_4` and `H_4`. - c0, c1, c2, c3 = comp - u = [coxeter_matrix[c0, c1], - coxeter_matrix[c0, c2], - coxeter_matrix[c0, c3], - coxeter_matrix[c1, c2], - coxeter_matrix[c1, c3], - coxeter_matrix[c2, c3]] - s = sorted(u) - # ``s`` is the list of all off-diagonal entries of - # the ``comp``-submatrix of the Coxeter matrix, - # sorted in increasing order. - if s[3:5] == [3, 3]: - if s[5] == 3: - continue # A4, D4 - if s[5] in [4, 5]: - u0 = u[0] + u[1] + u[2] - u1 = u[0] + u[3] + u[4] - u2 = u[1] + u[3] + u[5] - u3 = u[2] + u[4] + u[5] - ss = sorted([u0, u1, u2, u3]) - if ss in [[7, 7, 9, 9], [7, 8, 8, 9], - [7, 8, 9, 10]]: - continue # F4, B4, H4 - return False - else: - # The case of `l \geq 5` nodes. The finite - # groups to check for here are `A_l`, `B_l`, `D_l`, - # and `E_l` (for `l = 6, 7, 8`). - - # Checking that the Coxeter matrix of the subgroup - # corresponding to the vertices ``comp`` has all its - # off-diagonal entries equal to 2, 3 or at most once 4 - found_a_4 = False - for j in range(l): - for i in range(j): - coxeter_entry = coxeter_matrix[comp[i], comp[j]] - if coxeter_entry in [2, 3]: - continue - if coxeter_entry == 4 and not found_a_4: - found_a_4 = True - continue - return False - - G0 = G.subgraph(comp) - if found_a_4: - # The case when a `4` has been found in the - # Coxeter matrix. This needs only to be checked - # against `B_l`. We use the observation that - # the group is `B_l` if and only if the Coxeter - # graph is an `l`-path (i.e., has diameter - # `l - 1`) and the `4` corresponds to one of - # its two outermost edges. - diameter = G0.diameter() - if diameter != l - 1: - return False - - ecc = sorted(((u, v) for (v, u) in G0.eccentricity(with_labels=True).items())) - left_end = ecc[-1][1] - right_end = ecc[-2][1] - left_almost_end = G0.neigbors(left_end)[0] - right_almost_end = G0.neigbors(right_end)[0] - if (coxeter_matrix[left_end, left_almost_end] == 4 - or coxeter_matrix[right_end, right_almost_end] == 4): - continue # Bl - return False - - # Now, all off-diagonal entries of the Coxeter matrix - # are 2's and 3's. We need to check our group against - # `A_l`, `D_l` and `E_l`. Knowing that the Coxeter - # graph is a tree, we can use its vertex - # eccentricities to check this. - ecc = sorted(G0.eccentricity()) - if ecc[-1] == l - 1: - continue # Al - if ecc[-3] == l - 2: - continue # Dl - if l <= 8 and ecc[-2] == l - 2 and ecc[-5] == l - 3: - continue # El - return False - - return True - def _repr_(self): """ Return a string representation of ``self``. EXAMPLES:: - sage: CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) + sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2] - [3 1 3] - [2 3 1] + [3 1 4] + [2 4 1] """ rep = "Finite " if self.is_finite() else "" rep += "Coxeter group over {} with Coxeter matrix:\n{}".format(self.base_ring(), self._matrix) @@ -561,6 +417,7 @@ def is_finite(self): # the category of ``self``. return "Finite" in self.category().axioms() + @cached_method def order(self): """ Return the order of ``self``. @@ -577,7 +434,10 @@ def order(self): +Infinity """ if self.is_finite(): - return self._cardinality_from_iterator() + try: + return ZZ(len(self._list)) + except AttributeError: + return self._cardinality_from_iterator() return infinity def canonical_representation(self): @@ -617,9 +477,7 @@ def simple_reflection(self, i): [ 0 1 0] [ 0 1 -1] """ - if not i in self.index_set(): - raise ValueError("%s is not in the index set %s" % (i, self.index_set())) - return self.gen(self.index_set().index(i)) + return self.gen(self._index_set_inverse[i]) @cached_method def _positive_roots_reflections(self): @@ -767,7 +625,7 @@ def simple_root_index(self, i): [0, 2, 5] """ roots = self.roots() - rt = roots[0].parent().gen(self.index_set().index(i)) + rt = roots[0].parent().gen(self._index_set_inverse[i]) return roots.index(rt) def fundamental_weights(self): @@ -792,6 +650,85 @@ class Element(MatrixGroupElement_generic): """ A Coxeter group element. """ + def first_descent(self, side = 'right', index_set=None, positive=False): + """ + Return the first left (resp. right) descent of ``self``, as + ane element of ``index_set``, or ``None`` if there is none. + + See :meth:`descents` for a description of the options. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], implementation="reflection") + sage: a,b,c = W.gens() + sage: elt = b*a*c + sage: elt.first_descent() + 1 + sage: elt.first_descent(side='left') + 2 + """ + M = self.matrix() + if side != 'right': + M = ~M + I = self.parent().index_set() + n = len(I) + zero = M.base_ring().zero() + if index_set is None: + index_set = range(n) + else: + I_inv = self.parent()._index_set_inverse + index_set = [I_inv[i] for i in index_set] + if positive: + for i in index_set: + if not _matrix_test_right_descent(M, i, n, zero): + return I[i] + else: + for i in index_set: + if _matrix_test_right_descent(M, i, n, zero): + return I[i] + return None + + def descents(self, side='right', index_set=None, positive=False): + """ + Return the descents of ``self``, as a list of elements of the + ``index_set``. + + INPUT: + + - ``index_set`` -- (default: all of them) a subset (as a list + or iterable) of the nodes of the Dynkin diagram + - ``side`` -- (default: ``'right'``) ``'left'`` or ``'right'`` + - ``positive`` -- (default: ``False``) boolean + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], implementation="reflection") + sage: a,b,c = W.gens() + sage: elt = b*a*c + sage: elt.descents() + [1, 3] + sage: elt.descents(positive=True) + [2] + sage: elt.descents(index_set=[1,2]) + [1] + sage: elt.descents(side='left') + [2] + """ + M = self.matrix() + if side != 'right': + M = ~M + I = self.parent().index_set() + n = len(I) + zero = M.base_ring().zero() + if index_set is None: + index_set = range(n) + else: + I_inv = self.parent()._index_set_inverse + index_set = [I_inv[i] for i in index_set] + if positive: + return [I[i] for i in index_set if not _matrix_test_right_descent(M, i, n, zero)] + return [I[i] for i in index_set if _matrix_test_right_descent(M, i, n, zero)] + def has_right_descent(self, i): r""" Return whether ``i`` is a right descent of ``self``. @@ -815,12 +752,14 @@ def has_right_descent(self, i): sage: W = CoxeterGroup(['A',3], implementation="reflection") sage: a,b,c = W.gens() sage: elt = b*a*c - sage: map(lambda i: elt.has_right_descent(i), [1, 2, 3]) + sage: [elt.has_right_descent(i) for i in [1, 2, 3]] [True, False, True] """ - i = self.parent().index_set().index(i) - col = self.matrix().column(i) - return all(x <= 0 for x in col) + i = self.parent()._index_set_inverse[i] + n = len(self.parent().index_set()) + M = self.matrix() + zero = M.base_ring().zero() + return _matrix_test_right_descent(M, i, n, zero) def canonical_matrix(self): r""" @@ -857,3 +796,45 @@ def action_on_root_indices(self, i): rt = self * roots[i] return roots.index(rt) + +def _matrix_test_right_descent(M, i, n, zero): + """ + Test if the matrix ``M`` has a right ``i``-descent. + + INPUT: + + - ``M`` -- the matrix + - ``i`` -- the index + - ``n`` -- the size of the matrix + - ``zero`` -- the zero element in the base ring of ``M`` + + .. NOTE:: + + This is a helper function for :class:`CoxeterMatrixGroup.Element` + and optimized for speed. Specifically, it is called often and + there is no need to recompute ``n`` (and ``zero``) each time this + function is called. + + .. TODO:: + + Cythonize this function. + + EXAMPLES:: + + sage: from sage.groups.matrix_gps.coxeter_group import _matrix_test_right_descent + sage: W = CoxeterGroup(['A',3], implementation="reflection") + sage: a,b,c = W.gens() + sage: elt = b*a*c + sage: zero = W.base_ring().zero() + sage: [_matrix_test_right_descent(elt.matrix(), i, 3, zero) + ....: for i in range(3)] + [True, False, True] + """ + for j in xrange(n): + c = M[j, i] + if c < zero: + return True + elif c > zero: + return False + raise AssertionError('a zero column, so there must be a bug') + diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index 305b57052bc..4bd01565a13 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -774,7 +774,7 @@ def invariant_generators(self): ReyName = 't'+singular._next_var_name() singular.eval('matrix %s[%d][%d]'%(ReyName,self.cardinality(),n)) for i in range(1,self.cardinality()+1): - M = Matrix(elements[i-1],F) + M = Matrix(F, elements[i-1]) D = [{} for foobar in range(self.degree())] for x,y in M.dict().items(): D[x[0]][x[1]] = y diff --git a/src/sage/groups/matrix_gps/group_element.pxd b/src/sage/groups/matrix_gps/group_element.pxd new file mode 100644 index 00000000000..82355a1f7db --- /dev/null +++ b/src/sage/groups/matrix_gps/group_element.pxd @@ -0,0 +1,15 @@ +from sage.structure.element cimport MultiplicativeGroupElement, Element, MonoidElement, Matrix +from sage.groups.libgap_wrapper cimport ElementLibGAP + +cpdef is_MatrixGroupElement(x) + +cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): + cdef public Matrix _matrix + + cpdef _act_on_(self, x, bint self_on_left) + cpdef list list(self) + +cdef class MatrixGroupElement_gap(ElementLibGAP): + cpdef _act_on_(self, x, bint self_on_left) + cpdef list list(self) + diff --git a/src/sage/groups/matrix_gps/group_element.py b/src/sage/groups/matrix_gps/group_element.py deleted file mode 100644 index a5d1a860781..00000000000 --- a/src/sage/groups/matrix_gps/group_element.py +++ /dev/null @@ -1,440 +0,0 @@ -""" -Matrix Group Elements - -EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens); G - Matrix group over Finite Field of size 3 with 2 generators ( - [1 0] [1 1] - [0 1], [0 1] - ) - sage: g = G([[1,1],[0,1]]) - sage: h = G([[1,2],[0,1]]) - sage: g*h - [1 0] - [0 1] - -You cannot add two matrices, since this is not a group operation. -You can coerce matrices back to the matrix space and add them -there:: - - sage: g + h - Traceback (most recent call last): - ... - TypeError: unsupported operand type(s) for +: - 'FinitelyGeneratedMatrixGroup_gap_with_category.element_class' and - 'FinitelyGeneratedMatrixGroup_gap_with_category.element_class' - - sage: g.matrix() + h.matrix() - [2 0] - [0 2] - -Similarly, you cannot multiply group elements by scalars but you can -do it with the underlying matrices:: - - sage: 2*g - Traceback (most recent call last): - ... - TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and 'Matrix group over Finite Field of size 3 with 2 generators ( - [1 0] [1 1] - [0 1], [0 1] - )' - -AUTHORS: - -- David Joyner (2006-05): initial version David Joyner - -- David Joyner (2006-05): various modifications to address William - Stein's TODO's. - -- William Stein (2006-12-09): many revisions. - -- Volker Braun (2013-1) port to new Parent, libGAP. -""" - -#***************************************************************************** -# Copyright (C) 2006 David Joyner and William Stein -# Copyright (C) 2013 Volker Braun -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from sage.interfaces.gap import gap -from sage.structure.element import MultiplicativeGroupElement -from sage.matrix.matrix import Matrix, is_Matrix -from sage.structure.factorization import Factorization -from sage.structure.element import have_same_parent -from sage.libs.gap.element import GapElement, GapElement_List -from sage.misc.cachefunc import cached_method -from sage.groups.libgap_wrapper import ElementLibGAP -from sage.groups.libgap_mixin import GroupElementMixinLibGAP - - -def is_MatrixGroupElement(x): - """ - Test whether ``x`` is a matrix group element - - INPUT: - - - ``x`` -- anything. - - OUTPUT: - - Boolean. - - EXAMPLES:: - - sage: from sage.groups.matrix_gps.group_element import is_MatrixGroupElement - sage: is_MatrixGroupElement('helloooo') - False - - sage: G = GL(2,3) - sage: is_MatrixGroupElement(G.an_element()) - True - """ - return isinstance(x, MatrixGroupElement_base) - - -class MatrixGroupElement_base(MultiplicativeGroupElement): - """ - Base class for elements of matrix groups. - - You should use one of the two subclasses: - - * :class:`MatrixGroupElement_sage` implements the group - multiplication using Sage matrices. - - * :class:`MatrixGroupElement_gap` implements the group - multiplication using libGAP matrices. - - The base class only assumes that derived classes implement - :meth:`matrix`. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.random_element() - sage: type(g) - - """ - def __hash__(self): - r""" - TESTS:: - - sage: MS = MatrixSpace(GF(3), 2) - sage: G = MatrixGroup([MS([1,1,0,1]), MS([1,0,1,1])]) - sage: g = G.an_element() - sage: hash(g) - 0 - """ - return hash(self.matrix()) - - def _repr_(self): - r""" - Return string representation of this matrix. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G([[1, 1], [0, 1]]) - sage: g # indirect doctest - [1 1] - [0 1] - sage: g._repr_() - '[1 1]\n[0 1]' - """ - return str(self.matrix()) - - def _latex_(self): - r""" - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G([[1, 1], [0, 1]]) - sage: print g._latex_() - \left(\begin{array}{rr} - 1 & 1 \\ - 0 & 1 - \end{array}\right) - - Type ``view(g._latex_())`` to see the object in an - xdvi window (assuming you have latex and xdvi installed). - """ - return self.matrix()._latex_() - - def _act_on_(self, x, self_on_left): - """ - EXAMPLES:: - - sage: G = GL(4,7) - sage: G.0 * vector([1,2,3,4]) - (3, 2, 3, 4) - sage: v = vector(GF(7), [3,2,1,-1]) - sage: g = G.1 - sage: v * g == v * g.matrix() # indirect doctest - True - """ - if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): - try: - if self_on_left: - return self.matrix() * x - else: - return x * self.matrix() - except TypeError: - return None - - def _cmp_(self, other): - """ - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2) - sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] - sage: G = MatrixGroup(gens) - sage: g = G([1,1, 0,1]) - sage: h = G([1,1, 0,1]) - sage: g == h - True - sage: g == G.one() - False - """ - return cmp(self.matrix(), other.matrix()) - - __cmp__ = _cmp_ - - def list(self): - """ - Return list representation of this matrix. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.0 - sage: g - [1 0] - [0 1] - sage: g.list() - [[1, 0], [0, 1]] - """ - return [list(_) for _ in self.matrix().rows()] - - -################################################################### -# -# Matrix groups elements implemented with Sage matrices -# -################################################################### - -class MatrixGroupElement_generic(MatrixGroupElement_base): - - def __init__(self, parent, M, check=True, convert=True): - r""" - Element of a matrix group over a generic ring. - - The group elements are implemented as Sage matrices. - - INPUT: - - - ``M`` -- a matrix. - - - ``parent`` -- the parent. - - - ``check`` -- bool (default: ``True``). If true does some - type checking. - - - ``convert`` -- bool (default: ``True``). If true convert - ``M`` to the right matrix space. - - TESTS:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: g = G.random_element() - sage: TestSuite(g).run() - """ - if convert: - M = parent.matrix_space()(M) - if check: - if not is_Matrix(M): - raise TypeError('M must be a matrix') - if M.parent() is not parent.matrix_space(): - raise TypeError('M must be a in the matrix space of the group') - parent._check_matrix(M) - super(MatrixGroupElement_generic, self).__init__(parent) - if M.is_immutable(): - self._matrix = M - else: - self._matrix = M.__copy__() - self._matrix.set_immutable() - - def matrix(self): - """ - Obtain the usual matrix (as an element of a matrix space) - associated to this matrix group element. - - One reason to compute the associated matrix is that matrices - support a huge range of functionality. - - EXAMPLES:: - - sage: k = GF(7); G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]) - sage: g = G.0 - sage: g.matrix() - [1 1] - [0 1] - sage: parent(g.matrix()) - Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7 - - Matrices have extra functionality that matrix group elements - do not have:: - - sage: g.matrix().charpoly('t') - t^2 + 5*t + 1 - """ - return self._matrix - - def _mul_(self,other): - """ - Return the product of self and other, which must have identical - parents. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2) - sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] - sage: G = MatrixGroup(gens) - sage: g = G([1,1, 0,1]) - sage: h = G([1,1, 0,1]) - sage: g*h # indirect doctest - [1 2] - [0 1] - """ - parent = self.parent() - return parent.element_class(parent, self._matrix * other._matrix, - check=False, convert=False) - - def __invert__(self): - """ - Return the inverse group element - - OUTPUT: - - A matrix group element. - - EXAMPLES:: - - sage: G = GL(2,3) - sage: g = G([1,2,1,0]); g - [1 2] - [1 0] - sage: g.__invert__() - [0 1] - [2 1] - sage: g * (~g) - [1 0] - [0 1] - """ - parent = self.parent() - return parent.element_class(parent, ~self._matrix, check=False, convert=False) - - inverse = __invert__ - -################################################################### -# -# Matrix group elements implemented in GAP -# -################################################################### - -class MatrixGroupElement_gap(GroupElementMixinLibGAP, MatrixGroupElement_base, ElementLibGAP): - - def __init__(self, parent, M, check=True, convert=True): - r""" - Element of a matrix group over a generic ring. - - The group elements are implemented as Sage matrices. - - INPUT: - - - ``M`` -- a matrix. - - - ``parent`` -- the parent. - - - ``check`` -- bool (default: ``True``). If true does some - type checking. - - - ``convert`` -- bool (default: ``True``). If true convert - ``M`` to the right matrix space. - - TESTS:: - - sage: MS = MatrixSpace(GF(3),2,2) - sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) - sage: G.gen(0) - [1 0] - [0 1] - sage: g = G.random_element() - sage: TestSuite(g).run() - """ - if isinstance(M, GapElement): - ElementLibGAP.__init__(self, parent, M) - return - if convert: - M = parent.matrix_space()(M) - from sage.libs.gap.libgap import libgap - M_gap = libgap(M) - if check: - if not is_Matrix(M): - raise TypeError('M must be a matrix') - if M.parent() is not parent.matrix_space(): - raise TypeError('M must be a in the matrix space of the group') - parent._check_matrix(M, M_gap) - ElementLibGAP.__init__(self, parent, M_gap) - - def __reduce__(self): - """ - Implement pickling. - - TESTS:: - - sage: MS = MatrixSpace(GF(3), 2, 2) - sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) - sage: loads(G.gen(0).dumps()) - [1 0] - [0 1] - """ - return (self.parent(), (self.matrix(),)) - - @cached_method - def matrix(self): - """ - Obtain the usual matrix (as an element of a matrix space) - associated to this matrix group element. - - EXAMPLES:: - - sage: F = GF(3); MS = MatrixSpace(F,2,2) - sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] - sage: G = MatrixGroup(gens) - sage: G.gen(0).matrix() - [1 0] - [0 1] - sage: _.parent() - Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3 - """ - g = self.gap() - m = g.matrix(self.base_ring()) - m.set_immutable() - return m - diff --git a/src/sage/groups/matrix_gps/group_element.pyx b/src/sage/groups/matrix_gps/group_element.pyx new file mode 100644 index 00000000000..28f4a7fad09 --- /dev/null +++ b/src/sage/groups/matrix_gps/group_element.pyx @@ -0,0 +1,781 @@ +""" +Matrix Group Elements + +EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens); G + Matrix group over Finite Field of size 3 with 2 generators ( + [1 0] [1 1] + [0 1], [0 1] + ) + sage: g = G([[1,1],[0,1]]) + sage: h = G([[1,2],[0,1]]) + sage: g*h + [1 0] + [0 1] + +You cannot add two matrices, since this is not a group operation. +You can coerce matrices back to the matrix space and add them +there:: + + sage: g + h + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for +: + 'sage.groups.matrix_gps.group_element.MatrixGroupElement_gap' and + 'sage.groups.matrix_gps.group_element.MatrixGroupElement_gap' + + sage: g.matrix() + h.matrix() + [2 0] + [0 2] + +Similarly, you cannot multiply group elements by scalars but you can +do it with the underlying matrices:: + + sage: 2*g + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for '*': 'Integer Ring' and 'Matrix group over Finite Field of size 3 with 2 generators ( + [1 0] [1 1] + [0 1], [0 1] + )' + +AUTHORS: + +- David Joyner (2006-05): initial version David Joyner + +- David Joyner (2006-05): various modifications to address William + Stein's TODO's. + +- William Stein (2006-12-09): many revisions. + +- Volker Braun (2013-1) port to new Parent, libGAP. + +- Travis Scrimshaw (2016-01): reworks class hierarchy in order + to cythonize +""" + +#***************************************************************************** +# Copyright (C) 2006 David Joyner and William Stein +# Copyright (C) 2013 Volker Braun +# Copyright (C) 2016 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.element cimport MultiplicativeGroupElement, Element, MonoidElement, Matrix +from sage.structure.parent cimport Parent +from sage.libs.gap.element cimport GapElement, GapElement_List +from sage.groups.libgap_wrapper cimport ElementLibGAP + +from sage.matrix.matrix import is_Matrix +from sage.structure.factorization import Factorization +from sage.misc.cachefunc import cached_method +from sage.rings.all import ZZ + + +cpdef is_MatrixGroupElement(x): + """ + Test whether ``x`` is a matrix group element + + INPUT: + + - ``x`` -- anything. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.groups.matrix_gps.group_element import is_MatrixGroupElement + sage: is_MatrixGroupElement('helloooo') + False + + sage: G = GL(2,3) + sage: is_MatrixGroupElement(G.an_element()) + True + """ + return isinstance(x, (MatrixGroupElement_generic, MatrixGroupElement_gap)) + +################################################################### +# +# Matrix group elements implemented in Sage +# +################################################################### + +cdef class MatrixGroupElement_generic(MultiplicativeGroupElement): + """ + Element of a matrix group over a generic ring. + + The group elements are implemented as Sage matrices. + + INPUT: + + - ``M`` -- a matrix + + - ``parent`` -- the parent + + - ``check`` -- bool (default: ``True``); if ``True``, then + does some type checking + + - ``convert`` -- bool (default: ``True``); if ``True``, then + convert ``M`` to the right matrix space + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: g + [ 0 0 -1] + [ 1 0 -1] + [ 0 1 -1] + """ + def __init__(self, parent, M, check=True, convert=True): + r""" + Initialize ``self``. + + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: TestSuite(g).run() + """ + if convert: + M = parent.matrix_space()(M) + if check: + if not is_Matrix(M): + raise TypeError('M must be a matrix') + if M.parent() is not parent.matrix_space(): + raise TypeError('M must be a in the matrix space of the group') + parent._check_matrix(M) + super(MatrixGroupElement_generic, self).__init__(parent) + if M.is_immutable(): + self._matrix = M + else: + self._matrix = M.__copy__() + self._matrix.set_immutable() + + def __hash__(self): + r""" + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: hash(g) + -2 + """ + return hash(self._matrix) + + def __reduce__(self): + """ + Implement pickling. + + TESTS:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: loads(g.dumps()) == g + True + """ + return (_unpickle_generic_element, (self.parent(), self._matrix,)) + + def _repr_(self): + """ + Return string representation of this matrix. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: W.an_element() + [ 0 0 -1] + [ 1 0 -1] + [ 0 1 -1] + """ + return str(self._matrix) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: latex(g) + \left(\begin{array}{rrr} + 0 & 0 & -1 \\ + 1 & 0 & -1 \\ + 0 & 1 & -1 + \end{array}\right) + """ + return self._matrix._latex_() + + cpdef _act_on_(self, x, bint self_on_left): + """ + EXAMPLES:: + + sage: W = CoxeterGroup(['A',4], base_ring=ZZ) + sage: g = W.gen(0) + sage: g * vector([1,1,1,1]) + (0, 1, 1, 1) + sage: v = vector([3,2,1,-1]) + sage: g = W.gen(1) + sage: v * g == v * g.matrix() # indirect doctest + True + """ + if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): + try: + if self_on_left: + return self._matrix * x + else: + return x * self._matrix + except TypeError: + return None + + cpdef int _cmp_(self, Element other) except -2: + """ + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: TestSuite(g).run() + sage: h = W.gen(0) * W.gen(1) * W.gen(2) + sage: g == h + True + sage: a = W.gen(0) + sage: a == g + False + sage: a != g + True + """ + cdef MatrixGroupElement_generic x = self + cdef MatrixGroupElement_generic y = other + return cmp(x._matrix, y._matrix) + + cpdef list list(self): + """ + Return list representation of this matrix. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: g + [-1 1 0] + [ 0 1 0] + [ 0 0 1] + sage: g.list() + [[-1, 1, 0], [0, 1, 0], [0, 0, 1]] + """ + return [r.list() for r in self._matrix.rows()] + + def matrix(self): + """ + Obtain the usual matrix (as an element of a matrix space) + associated to this matrix group element. + + One reason to compute the associated matrix is that matrices + support a huge range of functionality. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: g.matrix() + [-1 1 0] + [ 0 1 0] + [ 0 0 1] + sage: parent(g.matrix()) + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + + Matrices have extra functionality that matrix group elements + do not have:: + + sage: g.matrix().charpoly('t') + t^3 - t^2 - t + 1 + """ + return self._matrix + + cpdef MonoidElement _mul_(self, MonoidElement other): + """ + Return the product of ``self`` and`` other``, which must + have identical parents. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.gen(0) + sage: h = W.an_element() + sage: g * h + [ 1 0 0] + [ 1 0 -1] + [ 0 1 -1] + """ + cdef Parent parent = self.parent() + cdef MatrixGroupElement_generic y = other + cdef Matrix M = self._matrix * y._matrix + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) + + def is_one(self): + """ + Return whether ``self`` is the identity of the group. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3]) + sage: g = W.gen(0) + sage: g.is_one() + False + + sage: W.an_element().is_one() + False + sage: W.one().is_one() + True + """ + return self._matrix.is_one() + + def __invert__(self): + """ + Return the inverse group element + + OUTPUT: + + A matrix group element. + + EXAMPLES:: + + sage: W = CoxeterGroup(['A',3], base_ring=ZZ) + sage: g = W.an_element() + sage: ~g + [-1 1 0] + [-1 0 1] + [-1 0 0] + sage: g * ~g == W.one() + True + sage: ~g * g == W.one() + True + + sage: W = CoxeterGroup(['B',3]) + sage: W.base_ring() + Universal Cyclotomic Field + sage: g = W.an_element() + sage: ~g + [ -1 1 0] + [ -1 0 E(8) - E(8)^3] + [-E(8) + E(8)^3 0 1] + """ + cdef Parent parent = self.parent() + cdef Matrix M = self._matrix + # We have a special method for dense matrices over ZZ + if M.base_ring() is ZZ and M.is_dense(): + M = M._invert_unit() + else: + M = ~M + if M.base_ring() is not parent.base_ring(): + M = M.change_ring(parent.base_ring()) + # Make it immutable so the constructor doesn't make a copy + M.set_immutable() + return parent.element_class(parent, M, check=False, convert=False) + + inverse = __invert__ + +################################################################### +# +# Matrix group elements implemented in GAP +# +################################################################### + +cdef class MatrixGroupElement_gap(ElementLibGAP): + """ + Element of a matrix group over a generic ring. + + The group elements are implemented as wrappers around libGAP matrices. + + INPUT: + + - ``M`` -- a matrix + + - ``parent`` -- the parent + + - ``check`` -- bool (default: ``True``); if ``True`` does some + type checking + + - ``convert`` -- bool (default: ``True``); if ``True`` convert + ``M`` to the right matrix space + """ + def __init__(self, parent, M, check=True, convert=True): + r""" + Initialize ``self``. + + TESTS:: + + sage: MS = MatrixSpace(GF(3),2,2) + sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) + sage: G.gen(0) + [1 0] + [0 1] + sage: g = G.random_element() + sage: TestSuite(g).run() + """ + if isinstance(M, GapElement): + ElementLibGAP.__init__(self, parent, M) + return + if convert: + M = parent.matrix_space()(M) + from sage.libs.gap.libgap import libgap + M_gap = libgap(M) + if check: + if not is_Matrix(M): + raise TypeError('M must be a matrix') + if M.parent() is not parent.matrix_space(): + raise TypeError('M must be a in the matrix space of the group') + parent._check_matrix(M, M_gap) + ElementLibGAP.__init__(self, parent, M_gap) + + def __reduce__(self): + """ + Implement pickling. + + TESTS:: + + sage: MS = MatrixSpace(GF(3), 2, 2) + sage: G = MatrixGroup(MS([[1,0],[0,1]]), MS([[1,1],[0,1]])) + sage: loads(G.gen(0).dumps()) + [1 0] + [0 1] + """ + return (self.parent(), (self.matrix(),)) + + def __hash__(self): + r""" + TESTS:: + + sage: MS = MatrixSpace(GF(3), 2) + sage: G = MatrixGroup([MS([1,1,0,1]), MS([1,0,1,1])]) + sage: g = G.an_element() + sage: hash(g) + 0 + """ + return hash(self.matrix()) + + def _repr_(self): + r""" + Return string representation of this matrix. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G([[1, 1], [0, 1]]) + sage: g # indirect doctest + [1 1] + [0 1] + sage: g._repr_() + '[1 1]\n[0 1]' + """ + return str(self.matrix()) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G([[1, 1], [0, 1]]) + sage: print g._latex_() + \left(\begin{array}{rr} + 1 & 1 \\ + 0 & 1 + \end{array}\right) + + Type ``view(g._latex_())`` to see the object in an + xdvi window (assuming you have latex and xdvi installed). + """ + return self.matrix()._latex_() + + cpdef _act_on_(self, x, bint self_on_left): + """ + EXAMPLES:: + + sage: G = GL(4,7) + sage: G.0 * vector([1,2,3,4]) + (3, 2, 3, 4) + sage: v = vector(GF(7), [3,2,1,-1]) + sage: g = G.1 + sage: v * g == v * g.matrix() # indirect doctest + True + """ + if not is_MatrixGroupElement(x) and x not in self.parent().base_ring(): + try: + if self_on_left: + return self.matrix() * x + else: + return x * self.matrix() + except TypeError: + return None + + cpdef int _cmp_(self, Element other) except -2: + """ + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2) + sage: gens = [MS([1,0, 0,1]), MS([1,1, 0,1])] + sage: G = MatrixGroup(gens) + sage: g = G([1,1, 0,1]) + sage: h = G([1,1, 0,1]) + sage: g == h + True + sage: g == G.one() + False + """ + return cmp(self.matrix(), other.matrix()) + + @cached_method + def matrix(self): + """ + Obtain the usual matrix (as an element of a matrix space) + associated to this matrix group element. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: m = G.gen(0).matrix(); m + [1 0] + [0 1] + sage: m.parent() + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 3 + + sage: k = GF(7); G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]) + sage: g = G.0 + sage: g.matrix() + [1 1] + [0 1] + sage: parent(g.matrix()) + Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 7 + + Matrices have extra functionality that matrix group elements + do not have:: + + sage: g.matrix().charpoly('t') + t^2 + 5*t + 1 + """ + # We do a slightly specialized version of sage.libs.gap.element.GapElement.matrix() + # in order to use our current matrix space directly and avoid + # some overhead safety checks. + entries = self.gap().Flat() + MS = self.parent().matrix_space() + ring = MS.base_ring() + m = MS([x.sage(ring=ring) for x in entries]) + m.set_immutable() + return m + + cpdef list list(self): + """ + Return list representation of this matrix. + + EXAMPLES:: + + sage: F = GF(3); MS = MatrixSpace(F,2,2) + sage: gens = [MS([[1,0],[0,1]]),MS([[1,1],[0,1]])] + sage: G = MatrixGroup(gens) + sage: g = G.0 + sage: g + [1 0] + [0 1] + sage: g.list() + [[1, 0], [0, 1]] + """ + return [r.list() for r in self.matrix().rows()] + + @cached_method + def order(self): + """ + Return the order of this group element, which is the smallest + positive integer `n` such that `g^n = 1`, or + +Infinity if no such integer exists. + + EXAMPLES:: + + sage: k = GF(7); + sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G + Matrix group over Finite Field of size 7 with 2 generators ( + [1 1] [1 0] + [0 1], [0 2] + ) + sage: G.order() + 21 + sage: G.gen(0).order(), G.gen(1).order() + (7, 3) + + sage: k = QQ; + sage: G = MatrixGroup([matrix(k,2,[1,1,0,1]), matrix(k,2,[1,0,0,2])]); G + Matrix group over Rational Field with 2 generators ( + [1 1] [1 0] + [0 1], [0 2] + ) + sage: G.order() + +Infinity + sage: G.gen(0).order(), G.gen(1).order() + (+Infinity, +Infinity) + + sage: gl = GL(2, ZZ); gl + General Linear Group of degree 2 over Integer Ring + sage: g = gl.gen(2); g + [1 1] + [0 1] + sage: g.order() + +Infinity + """ + order = self.gap().Order() + if order.IsInt(): + return order.sage() + else: + assert order.IsInfinity() + from sage.rings.all import Infinity + return Infinity + + def word_problem(self, gens=None): + r""" + Solve the word problem. + + This method writes the group element as a product of the + elements of the list ``gens``, or the standard generators of + the parent of self if ``gens`` is None. + + INPUT: + + - ``gens`` -- a list/tuple/iterable of elements (or objects + that can be converted to group elements), or ``None`` + (default). By default, the generators of the parent group + are used. + + OUTPUT: + + A factorization object that contains information about the + order of factors and the exponents. A ``ValueError`` is raised + if the group element cannot be written as a word in ``gens``. + + ALGORITHM: + + Use GAP, which has optimized algorithms for solving the word + problem (the GAP functions ``EpimorphismFromFreeGroup`` and + ``PreImagesRepresentative``). + + EXAMPLE:: + + sage: G = GL(2,5); G + General Linear Group of degree 2 over Finite Field of size 5 + sage: G.gens() + ( + [2 0] [4 1] + [0 1], [4 0] + ) + sage: G(1).word_problem([G.gen(0)]) + 1 + sage: type(_) + + + sage: g = G([0,4,1,4]) + sage: g.word_problem() + ([4 1] + [4 0])^-1 + + Next we construct a more complicated element of the group from the + generators:: + + sage: s,t = G.0, G.1 + sage: a = (s * t * s); b = a.word_problem(); b + ([2 0] + [0 1]) * + ([4 1] + [4 0]) * + ([2 0] + [0 1]) + sage: flatten(b) + [ + [2 0] [4 1] [2 0] + [0 1], 1, [4 0], 1, [0 1], 1 + ] + sage: b.prod() == a + True + + We solve the word problem using some different generators:: + + sage: s = G([2,0,0,1]); t = G([1,1,0,1]); u = G([0,-1,1,0]) + sage: a.word_problem([s,t,u]) + ([2 0] + [0 1])^-1 * + ([1 1] + [0 1])^-1 * + ([0 4] + [1 0]) * + ([2 0] + [0 1])^-1 + + We try some elements that don't actually generate the group:: + + sage: a.word_problem([t,u]) + Traceback (most recent call last): + ... + ValueError: word problem has no solution + + AUTHORS: + + - David Joyner and William Stein + - David Loeffler (2010): fixed some bugs + - Volker Braun (2013): LibGAP + """ + from sage.libs.gap.libgap import libgap + G = self.parent() + if gens: + gen = lambda i:gens[i] + H = libgap.Group([G(x).gap() for x in gens]) + else: + gen = G.gen + H = G.gap() + hom = H.EpimorphismFromFreeGroup() + preimg = hom.PreImagesRepresentative(self.gap()) + + if preimg.is_bool(): + assert preimg == libgap.eval('fail') + raise ValueError('word problem has no solution') + + result = [] + n = preimg.NumberSyllables().sage() + exponent_syllable = libgap.eval('ExponentSyllable') + generator_syllable = libgap.eval('GeneratorSyllable') + for i in range(n): + exponent = exponent_syllable(preimg, i+1).sage() + generator = gen(generator_syllable(preimg, i+1).sage() - 1) + result.append( (generator, exponent) ) + result = Factorization(result) + result._set_cr(True) + return result + +def _unpickle_generic_element(G, mat): + """ + Unpickle the element in ``G`` given by ``mat``. + + EXAMPLES:: + + sage: m1 = matrix(SR, [[1,2],[3,4]]) + sage: m2 = matrix(SR, [[1,3],[-1,0]]) + sage: G = MatrixGroup(m1, m2) + sage: m = G.an_element() + sage: from sage.groups.matrix_gps.group_element import _unpickle_generic_element + sage: _unpickle_generic_element(G, m.matrix()) == m + True + """ + return G.element_class(G, mat, False, False) + diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index 698a58790d6..ce7bac7973d 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -584,19 +584,19 @@ def __iter__(self): sage: i = iter(GL(6,5)) sage: [ next(i) for j in range(8) ] [ - [1 0 0 0 0 0] [4 0 0 0 0 1] [0 4 0 0 0 0] [0 4 0 0 0 0] - [0 1 0 0 0 0] [4 0 0 0 0 0] [0 0 4 0 0 0] [0 0 4 0 0 0] - [0 0 1 0 0 0] [0 4 0 0 0 0] [0 0 0 4 0 0] [0 0 0 4 0 0] - [0 0 0 1 0 0] [0 0 4 0 0 0] [0 0 0 0 4 0] [0 0 0 0 4 0] - [0 0 0 0 1 0] [0 0 0 4 0 0] [0 0 0 0 0 4] [0 0 0 0 0 4] - [0 0 0 0 0 1], [0 0 0 0 4 0], [1 4 0 0 0 0], [2 4 0 0 0 0], + [1 0 0 0 0 0] [2 0 0 0 0 0] [3 0 0 0 0 0] [3 2 0 0 0 0] + [0 1 0 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] + [0 0 1 0 0 0] [0 0 1 0 0 0] [0 0 1 0 0 0] [0 0 1 0 0 0] + [0 0 0 1 0 0] [0 0 0 1 0 0] [0 0 0 1 0 0] [0 0 0 1 0 0] + [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 0 1 0] + [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1], - [3 0 0 0 0 1] [4 0 0 1 3 3] [0 0 0 2 0 0] [1 0 0 0 4 4] - [3 0 0 0 0 0] [4 0 0 0 3 3] [0 0 0 0 4 0] [1 0 0 0 0 4] - [0 4 0 0 0 0] [3 0 0 0 0 1] [2 2 0 0 0 2] [1 0 0 0 0 0] - [0 0 4 0 0 0] [3 0 0 0 0 0] [1 4 0 0 0 0] [0 1 0 0 0 0] - [0 0 0 4 0 0] [0 4 0 0 0 0] [0 2 4 0 0 0] [0 0 1 0 0 0] - [4 0 0 0 2 3], [2 0 3 4 4 4], [0 0 1 4 0 0], [0 0 0 1 0 0] + [2 1 0 0 0 0] [3 3 0 2 3 0] [2 4 0 1 4 0] [1 2 4 1 0 3] + [0 1 0 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] [0 1 0 0 0 0] + [0 0 1 0 0 0] [0 0 1 0 0 0] [0 0 1 0 0 0] [0 0 1 0 0 0] + [0 0 0 1 0 0] [0 0 0 1 0 0] [0 0 0 1 0 0] [0 0 0 1 0 0] + [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 0 1 0] [0 0 0 0 1 0] + [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1], [0 0 0 0 0 1] ] This is the direct computation in GAP, which will just run @@ -658,7 +658,7 @@ def list(self): sage: all(g in G for g in G.list()) True - An example over a ring (see trac 5241):: + An example over a ring (see :trac:`5241`):: sage: M1 = matrix(ZZ,2,[[-1,0],[0,1]]) sage: M2 = matrix(ZZ,2,[[1,0],[0,-1]]) @@ -678,7 +678,7 @@ def list(self): [ 0 1], [ 0 -1], [ 0 -1] ) - An example over a field (see trac 10515):: + An example over a field (see :trac:`10515`):: sage: gens = [matrix(QQ,2,[1,0,0,1])] sage: MatrixGroup(gens).list() @@ -687,7 +687,7 @@ def list(self): [0 1] ) - Another example over a ring (see trac 9437):: + Another example over a ring (see :trac:`9437`):: sage: len(SL(2, Zmod(4)).list()) 48 diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 307336fd382..34428cc125a 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -21,4 +21,4 @@ from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.root_system.weyl_group import WeylGroup from sage.groups.raag import RightAngledArtinGroup as RightAngledArtin - +from sage.combinat.root_system.reflection_group_real import ReflectionGroup diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx index 50f834a8a3d..6d5797e154f 100644 --- a/src/sage/groups/old.pyx +++ b/src/sage/groups/old.pyx @@ -47,7 +47,7 @@ cdef class Group(sage.structure.parent_gens.ParentWithGens): ... AssertionError: Category of commutative additive groups is not a subcategory of Category of groups - Check for #8119:: + Check for :trac:`8119`:: sage: G = SymmetricGroup(2) sage: h = hash(G) diff --git a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx index ef44b65801e..8066bb5bfce 100644 --- a/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx +++ b/src/sage/groups/perm_gps/partn_ref/automorphism_group_canonical_label.pyx @@ -260,10 +260,10 @@ def coset_rep(list perm=[0,1,2,3,4,5], list gens=[[1,2,3,4,5,0]]): cdef Integer I = Integer(0) cdef PartitionStack *part part = PS_new(n, 1) - cdef int *c_perm = sage_malloc(n * sizeof(int)) + cdef int *c_perm = sig_malloc(n * sizeof(int)) cdef StabilizerChain *group = SC_new(n, 1) if part is NULL or c_perm is NULL or group is NULL: - sage_free(c_perm) + sig_free(c_perm) PS_dealloc(part) SC_dealloc(group) raise MemoryError @@ -281,7 +281,7 @@ def coset_rep(list perm=[0,1,2,3,4,5], list gens=[[1,2,3,4,5,0]]): PS_dealloc(part) SC_dealloc(group) deallocate_agcl_output(output) - sage_free(c_perm) + sig_free(c_perm) return label cdef aut_gp_and_can_lab *allocate_agcl_output(int n): @@ -290,12 +290,12 @@ cdef aut_gp_and_can_lab *allocate_agcl_output(int n): be input to the get_aut_gp_and_can_lab function, and the output will be stored to it. """ - cdef aut_gp_and_can_lab *output = sage_malloc(sizeof(aut_gp_and_can_lab)) + cdef aut_gp_and_can_lab *output = sig_malloc(sizeof(aut_gp_and_can_lab)) if output is NULL: return NULL output.group = SC_new(n) - output.relabeling = sage_malloc(n*sizeof(int)) - output.generators = sage_malloc(2*n*n*sizeof(int)) + output.relabeling = sig_malloc(n*sizeof(int)) + output.generators = sig_malloc(2*n*n*sizeof(int)) output.size_of_generator_array = 2*n*n if output.group is NULL or \ output.relabeling is NULL or \ @@ -310,9 +310,9 @@ cdef void deallocate_agcl_output(aut_gp_and_can_lab *output): """ if output is not NULL: SC_dealloc(output.group) - sage_free(output.relabeling) - sage_free(output.generators) - sage_free(output) + sig_free(output.relabeling) + sig_free(output.generators) + sig_free(output) cdef agcl_work_space *allocate_agcl_work_space(int n): r""" @@ -323,19 +323,19 @@ cdef agcl_work_space *allocate_agcl_work_space(int n): cdef int *int_array cdef agcl_work_space *work_space - work_space = sage_malloc(sizeof(agcl_work_space)) + work_space = sig_malloc(sizeof(agcl_work_space)) if work_space is NULL: return NULL work_space.degree = n - int_array = sage_malloc((n*n + # for perm_stack + int_array = sig_malloc((n*n + # for perm_stack n + # for label_indicators 7*n # for int_array )*sizeof(int)) work_space.group1 = SC_new(n) work_space.group2 = SC_new(n) work_space.label_ps = PS_new(n,0) - work_space.bitset_array = sage_malloc((n + 2*len_of_fp_and_mcr + 1)*sizeof(bitset_t)) + work_space.bitset_array = sig_malloc((n + 2*len_of_fp_and_mcr + 1)*sizeof(bitset_t)) work_space.orbits_of_subgroup = OP_new(n) work_space.orbits_of_permutation = OP_new(n) work_space.first_ps = PS_new(n,0) @@ -376,15 +376,15 @@ cdef void deallocate_agcl_work_space(agcl_work_space *work_space): if work_space.bitset_array is not NULL: for i from 0 <= i < n + 2*len_of_fp_and_mcr + 1: bitset_free(work_space.bitset_array[i]) - sage_free(work_space.perm_stack) + sig_free(work_space.perm_stack) SC_dealloc(work_space.group1) SC_dealloc(work_space.group2) PS_dealloc(work_space.label_ps) - sage_free(work_space.bitset_array) + sig_free(work_space.bitset_array) OP_dealloc(work_space.orbits_of_subgroup) OP_dealloc(work_space.orbits_of_permutation) PS_dealloc(work_space.first_ps) - sage_free(work_space) + sig_free(work_space) cdef aut_gp_and_can_lab *get_aut_gp_and_can_lab(void *S, PartitionStack *partition, int n, @@ -836,7 +836,7 @@ cdef aut_gp_and_can_lab *get_aut_gp_and_can_lab(void *S, if n*output.num_gens == output.size_of_generator_array: # must double its size output.size_of_generator_array *= 2 - output.generators = sage_realloc( output.generators, output.size_of_generator_array * sizeof(int) ) + output.generators = sig_realloc( output.generators, output.size_of_generator_array * sizeof(int) ) if output.generators is NULL: mem_err = True continue # main loop diff --git a/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx b/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx index 54f9b3eda60..223a7bf47e7 100644 --- a/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx +++ b/src/sage/groups/perm_gps/partn_ref/canonical_augmentation.pyx @@ -282,20 +282,20 @@ cdef canonical_generator_data *allocate_cgd(int max_depth, int degree): r""" Allocate the data part of the canonical generation iterator struct. """ - cdef canonical_generator_data *cgd = sage_malloc(sizeof(canonical_generator_data)) + cdef canonical_generator_data *cgd = sig_malloc(sizeof(canonical_generator_data)) cdef PartitionStack *part if cgd is NULL: - sage_free(cgd) + sig_free(cgd) return NULL - cgd.object_stack = sage_malloc(max_depth * sizeof(void *)) - cgd.degree_stack = sage_malloc(max_depth * sizeof(int)) - cgd.iterator_stack = sage_malloc(max_depth * sizeof(iterator)) - cgd.aut_gp_stack = sage_malloc(max_depth * sizeof(aut_gp_and_can_lab *)) - cgd.agcl_work_spaces = sage_malloc(max_depth * sizeof(agcl_work_space *)) - cgd.dc_work_spaces = sage_malloc(max_depth * sizeof(dc_work_space *)) - cgd.ps_stack = sage_malloc(max_depth * sizeof(PartitionStack *)) - cgd.aug_stack = sage_malloc(max_depth * sizeof(void *)) - cgd.parent_stack = sage_malloc(max_depth * sizeof(void *)) + cgd.object_stack = sig_malloc(max_depth * sizeof(void *)) + cgd.degree_stack = sig_malloc(max_depth * sizeof(int)) + cgd.iterator_stack = sig_malloc(max_depth * sizeof(iterator)) + cgd.aut_gp_stack = sig_malloc(max_depth * sizeof(aut_gp_and_can_lab *)) + cgd.agcl_work_spaces = sig_malloc(max_depth * sizeof(agcl_work_space *)) + cgd.dc_work_spaces = sig_malloc(max_depth * sizeof(dc_work_space *)) + cgd.ps_stack = sig_malloc(max_depth * sizeof(PartitionStack *)) + cgd.aug_stack = sig_malloc(max_depth * sizeof(void *)) + cgd.parent_stack = sig_malloc(max_depth * sizeof(void *)) part = PS_new(degree, 1) cdef agcl_work_space *agclws = allocate_agcl_work_space(degree) cdef aut_gp_and_can_lab *output = allocate_agcl_output(degree) @@ -304,16 +304,16 @@ cdef canonical_generator_data *allocate_cgd(int max_depth, int degree): cgd.agcl_work_spaces is NULL or cgd.dc_work_spaces is NULL or \ cgd.ps_stack is NULL or cgd.aug_stack is NULL or \ cgd.parent_stack is NULL or agclws is NULL or output is NULL: - sage_free(cgd.object_stack) - sage_free(cgd.degree_stack) - sage_free(cgd.iterator_stack) - sage_free(cgd.aut_gp_stack) - sage_free(cgd.agcl_work_spaces) - sage_free(cgd.dc_work_spaces) - sage_free(cgd.ps_stack) - sage_free(cgd.aug_stack) - sage_free(cgd.parent_stack) - sage_free(cgd) + sig_free(cgd.object_stack) + sig_free(cgd.degree_stack) + sig_free(cgd.iterator_stack) + sig_free(cgd.aut_gp_stack) + sig_free(cgd.agcl_work_spaces) + sig_free(cgd.dc_work_spaces) + sig_free(cgd.ps_stack) + sig_free(cgd.aug_stack) + sig_free(cgd.parent_stack) + sig_free(cgd) PS_dealloc(part) deallocate_agcl_work_space(agclws) deallocate_agcl_output(output) @@ -363,16 +363,16 @@ cdef void deallocate_cgd(canonical_generator_data *cgd): cgd.free_aug(cgd.aug_stack[i]) if cgd.iterator_stack[i].data is not NULL: cgd.free_iter_data(cgd.iterator_stack[i].data) - sage_free(cgd.object_stack) - sage_free(cgd.degree_stack) - sage_free(cgd.iterator_stack) - sage_free(cgd.aut_gp_stack) - sage_free(cgd.agcl_work_spaces) - sage_free(cgd.dc_work_spaces) - sage_free(cgd.ps_stack) - sage_free(cgd.aug_stack) - sage_free(cgd.parent_stack) - sage_free(cgd) + sig_free(cgd.object_stack) + sig_free(cgd.degree_stack) + sig_free(cgd.iterator_stack) + sig_free(cgd.aut_gp_stack) + sig_free(cgd.agcl_work_spaces) + sig_free(cgd.dc_work_spaces) + sig_free(cgd.ps_stack) + sig_free(cgd.aug_stack) + sig_free(cgd.parent_stack) + sig_free(cgd) cdef iterator *setup_canonical_generator(int degree, bint (*all_children_are_equivalent)(PartitionStack *PS, void *S), @@ -477,10 +477,10 @@ cdef iterator *setup_canonical_generator(int degree, cdef iterator *canonical_generator cdef canonical_generator_data *cgd if cangen_prealloc is NULL: - canonical_generator = sage_malloc(sizeof(iterator)) + canonical_generator = sig_malloc(sizeof(iterator)) cgd = allocate_cgd(max_depth, degree) if canonical_generator is NULL or cgd is NULL: - sage_free(canonical_generator) + sig_free(canonical_generator) deallocate_cgd(cgd) raise MemoryError cgd.dealloc = 1 diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures_pyx.pxi b/src/sage/groups/perm_gps/partn_ref/data_structures_pyx.pxi index 8638dab5acb..1ca27bfcfbf 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures_pyx.pxi +++ b/src/sage/groups/perm_gps/partn_ref/data_structures_pyx.pxi @@ -45,11 +45,11 @@ cdef inline OrbitPartition *OP_new(int n): """ cdef int i cdef OrbitPartition *OP = \ - sage_malloc(sizeof(OrbitPartition)) - cdef int *int_array = sage_malloc( 4*n * sizeof(int) ) + sig_malloc(sizeof(OrbitPartition)) + cdef int *int_array = sig_malloc( 4*n * sizeof(int) ) if OP is NULL or int_array is NULL: - sage_free(OP) - sage_free(int_array) + sig_free(OP) + sig_free(int_array) return NULL OP.degree = n OP.num_cells = n @@ -105,8 +105,8 @@ cdef inline OP_clear(OrbitPartition *OP): cdef inline int OP_dealloc(OrbitPartition *OP): if OP is not NULL: - sage_free(OP.parent) - sage_free(OP) + sig_free(OP.parent) + sig_free(OP) cdef inline int OP_find(OrbitPartition *OP, int n): """ @@ -234,7 +234,7 @@ def OP_represent(int n, merges, perm): (OP.size[i], OP.mcr[i], OP.rank[i]) print s print "Allocating array to test merge_perm." - cdef int *gamma = sage_malloc( n * sizeof(int) ) + cdef int *gamma = sig_malloc( n * sizeof(int) ) if gamma is NULL: print "Allocation failed!" OP_dealloc(OP) @@ -254,7 +254,7 @@ def OP_represent(int n, merges, perm): (OP.size[i], OP.mcr[i], OP.rank[i]) print s print "Deallocating OrbitPartition." - sage_free(gamma) + sig_free(gamma) OP_dealloc(OP) print "Done." @@ -267,11 +267,11 @@ cdef inline PartitionStack *PS_new(int n, bint unit_partition): """ cdef int i cdef PartitionStack *PS = \ - sage_malloc(sizeof(PartitionStack)) - cdef int *int_array = sage_malloc( 2*n * sizeof(int) ) + sig_malloc(sizeof(PartitionStack)) + cdef int *int_array = sig_malloc( 2*n * sizeof(int) ) if PS is NULL or int_array is NULL: - sage_free(PS) - sage_free(int_array) + sig_free(PS) + sig_free(int_array) return NULL PS.entries = int_array PS.levels = int_array + n @@ -301,11 +301,11 @@ cdef inline PartitionStack *PS_copy(PartitionStack *PS): cdef int i, n = PS.degree cdef PartitionStack *PS2 = \ - sage_malloc(sizeof(PartitionStack)) - cdef int *int_array = sage_malloc( 2*n * sizeof(int) ) + sig_malloc(sizeof(PartitionStack)) + cdef int *int_array = sig_malloc( 2*n * sizeof(int) ) if PS2 is NULL or int_array is NULL: - sage_free(PS2) - sage_free(int_array) + sig_free(PS2) + sig_free(int_array) return NULL PS2.entries = int_array PS2.levels = int_array + n @@ -347,8 +347,8 @@ cdef PartitionStack *PS_from_list(list L): cdef inline PS_dealloc(PartitionStack *PS): if PS is not NULL: - sage_free(PS.entries) - sage_free(PS) + sig_free(PS.entries) + sig_free(PS) cdef PS_print(PartitionStack *PS): """ @@ -534,14 +534,14 @@ cdef int PS_all_new_cells(PartitionStack *PS, bitset_t** nonsingletons_ptr): for i from beg <= i <= end: bitset_set(scratch, PS.entries[i]) count +=1 - nonsingletons = sage_realloc(nonsingletons, count * sizeof(bitset_t)) + nonsingletons = sig_realloc(nonsingletons, count * sizeof(bitset_t)) if nonsingletons is NULL: raise MemoryError, "Memory error in PS_all_new_cells" bitset_init(nonsingletons[count-1], n) bitset_copy(nonsingletons[count-1], scratch) else: if beg==0: - nonsingletons = sage_realloc(nonsingletons, sizeof(bitset_t)) + nonsingletons = sig_realloc(nonsingletons, sizeof(bitset_t)) if nonsingletons is NULL: raise MemoryError, "Memory error in PS_all_new_cells" bitset_init(nonsingletons[0], n) @@ -733,10 +733,10 @@ def PS_represent(partition, splits): print PS_split_point(PS, s) PS_print(PS) print "Getting permutation from PS2->PS:" - gamma = sage_malloc(n * sizeof(int)) + gamma = sig_malloc(n * sizeof(int)) PS_get_perm_from(PS, PS2, gamma) print [gamma[i] for i from 0 <= i < n] - sage_free(gamma) + sig_free(gamma) print "Finding first smallest:" bitset_init(b, n) i = PS_first_smallest(PS, b) @@ -767,7 +767,7 @@ cdef StabilizerChain *SC_new(int n, bint init_gens=True): """ cdef int i cdef StabilizerChain *SC = \ - sage_calloc(1, sizeof(StabilizerChain)) + sig_calloc(1, sizeof(StabilizerChain)) cdef int *array1 cdef int *array2 cdef int *array3 @@ -777,12 +777,12 @@ cdef StabilizerChain *SC_new(int n, bint init_gens=True): SC.degree = n SC.base_size = 0 if n == 0: - # All internal pointers have been initialized to NULL by sage_calloc + # All internal pointers have been initialized to NULL by sig_calloc return SC # first level allocations - cdef int *int_array = sage_malloc( (3*n*n + 6*n + 1) * sizeof(int) ) - cdef int **int_ptrs = sage_calloc( 5*n, sizeof(int *) ) + cdef int *int_array = sig_malloc( (3*n*n + 6*n + 1) * sizeof(int) ) + cdef int **int_ptrs = sig_calloc( 5*n, sizeof(int *) ) SC.OP_scratch = OP_new(n) # bitset_init without the MemoryError: cdef long limbs = (default_num_bits - 1)/(8*sizeof(unsigned long)) + 1 @@ -790,15 +790,15 @@ cdef StabilizerChain *SC_new(int n, bint init_gens=True): SC.gen_is_id.size = default_num_bits SC.gen_used.limbs = limbs SC.gen_is_id.limbs = limbs - SC.gen_used.bits = sage_malloc(limbs * sizeof(mp_limb_t)) - SC.gen_is_id.bits = sage_malloc(limbs * sizeof(mp_limb_t)) + SC.gen_used.bits = sig_malloc(limbs * sizeof(mp_limb_t)) + SC.gen_is_id.bits = sig_malloc(limbs * sizeof(mp_limb_t)) # check for allocation failures if int_array is NULL or int_ptrs is NULL or \ SC.gen_used.bits is NULL or SC.gen_is_id.bits is NULL or \ SC.OP_scratch is NULL: - sage_free(int_array) - sage_free(int_ptrs) + sig_free(int_array) + sig_free(int_ptrs) SC_dealloc(SC) return NULL @@ -826,8 +826,8 @@ cdef StabilizerChain *SC_new(int n, bint init_gens=True): if init_gens: for i from 0 <= i < n: SC.array_size[i] = default_num_gens - SC.generators[i] = sage_malloc( default_num_gens*n * sizeof(int) ) - SC.gen_inverses[i] = sage_malloc( default_num_gens*n * sizeof(int) ) + SC.generators[i] = sig_malloc( default_num_gens*n * sizeof(int) ) + SC.gen_inverses[i] = sig_malloc( default_num_gens*n * sizeof(int) ) if SC.generators[i] is NULL or SC.gen_inverses[i] is NULL: SC_dealloc(SC) return NULL @@ -849,8 +849,8 @@ cdef StabilizerChain *SC_symmetric_group(int n): SC.array_size[i] = n-i-1 SC.array_size[n-1] = default_num_gens for i from 0 <= i < n: - SC.generators[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) - SC.gen_inverses[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) + SC.generators[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) + SC.gen_inverses[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) if SC.generators[i] is NULL or SC.gen_inverses[i] is NULL: SC_dealloc(SC) return NULL @@ -891,8 +891,8 @@ cdef StabilizerChain *SC_alternating_group(int n): SC.array_size[n-2] = default_num_gens SC.array_size[n-1] = default_num_gens for i from 0 <= i < n: - SC.generators[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) - SC.gen_inverses[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) + SC.generators[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) + SC.gen_inverses[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) if SC.generators[i] is NULL or SC.gen_inverses[i] is NULL: SC_dealloc(SC) return NULL @@ -928,11 +928,11 @@ cdef inline int SC_realloc_gens(StabilizerChain *SC, int level, int size): cdef int *temp cdef int n = SC.degree - temp = sage_realloc( SC.generators[level], n * size * sizeof(int) ) + temp = sig_realloc( SC.generators[level], n * size * sizeof(int) ) if temp is NULL: return 1 SC.generators[level] = temp - temp = sage_realloc( SC.gen_inverses[level], n * size * sizeof(int) ) + temp = sig_realloc( SC.gen_inverses[level], n * size * sizeof(int) ) if temp is NULL: return 1 SC.gen_inverses[level] = temp @@ -953,12 +953,12 @@ cdef int SC_realloc_bitsets(StabilizerChain *SC, long size): new_size *= 2 cdef unsigned long limbs_old = SC.gen_used.limbs cdef long limbs = (new_size - 1)/(8*sizeof(unsigned long)) + 1 - cdef mp_limb_t *tmp = sage_realloc(SC.gen_used.bits, limbs * sizeof(mp_limb_t)) + cdef mp_limb_t *tmp = sig_realloc(SC.gen_used.bits, limbs * sizeof(mp_limb_t)) if tmp is not NULL: SC.gen_used.bits = tmp else: return 1 - tmp = sage_realloc(SC.gen_is_id.bits, limbs * sizeof(mp_limb_t)) + tmp = sig_realloc(SC.gen_is_id.bits, limbs * sizeof(mp_limb_t)) if tmp is not NULL: SC.gen_is_id.bits = tmp else: @@ -979,14 +979,14 @@ cdef inline SC_dealloc(StabilizerChain *SC): n = SC.degree if SC.generators is not NULL: for i from 0 <= i < n: - sage_free(SC.generators[i]) - sage_free(SC.gen_inverses[i]) - sage_free(SC.generators) # frees int_ptrs - sage_free(SC.orbit_sizes) # frees int_array - sage_free(SC.gen_used.bits) - sage_free(SC.gen_is_id.bits) + sig_free(SC.generators[i]) + sig_free(SC.gen_inverses[i]) + sig_free(SC.generators) # frees int_ptrs + sig_free(SC.orbit_sizes) # frees int_array + sig_free(SC.gen_used.bits) + sig_free(SC.gen_is_id.bits) OP_dealloc(SC.OP_scratch) - sage_free(SC) + sig_free(SC) cdef StabilizerChain *SC_copy(StabilizerChain *SC, int level): """ @@ -1000,15 +1000,15 @@ cdef StabilizerChain *SC_copy(StabilizerChain *SC, int level): return NULL level = min(level, SC.base_size) for i from 0 <= i < level: - SCC.generators[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) - SCC.gen_inverses[i] = sage_malloc( SC.array_size[i]*n * sizeof(int) ) + SCC.generators[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) + SCC.gen_inverses[i] = sig_malloc( SC.array_size[i]*n * sizeof(int) ) if SCC.generators[i] is NULL or SCC.gen_inverses[i] is NULL: SC_dealloc(SCC) return NULL SCC.array_size[i] = SC.array_size[i] for i from level <= i < n: - SCC.generators[i] = sage_malloc( default_num_gens*n * sizeof(int) ) - SCC.gen_inverses[i] = sage_malloc( default_num_gens*n * sizeof(int) ) + SCC.generators[i] = sig_malloc( default_num_gens*n * sizeof(int) ) + SCC.gen_inverses[i] = sig_malloc( default_num_gens*n * sizeof(int) ) if SCC.generators[i] is NULL or SCC.gen_inverses[i] is NULL: SC_dealloc(SCC) return NULL @@ -1526,11 +1526,11 @@ cdef bint SC_is_giant(int n, int num_perms, int *perms, float p, bitset_t suppor cdef int i, j, num_steps, m = 1, support_root cdef unsigned long q cdef int *gen - cdef int *perm = sage_malloc(n*sizeof(int)) + cdef int *perm = sig_malloc(n*sizeof(int)) cdef OrbitPartition *OP = OP_new(n) if OP is NULL or perm is NULL: OP_dealloc(OP) - sage_free(perm) + sig_free(perm) return False # giants are transitive @@ -1549,7 +1549,7 @@ cdef bint SC_is_giant(int n, int num_perms, int *perms, float p, bitset_t suppor support_root = i if m == 1: OP_dealloc(OP) - sage_free(perm) + sig_free(perm) return False bitset_zero(support) for i from 0 <= i < n: @@ -1572,12 +1572,12 @@ cdef bint SC_is_giant(int n, int num_perms, int *perms, float p, bitset_t suppor q = OP.size[i] if m < 2*q and q < m-2: if n_is_prime(q): - sage_free(perm) + sig_free(perm) OP_dealloc(OP) return True SC_mult_perms(perm, perm, perms + n*(rand()%num_perms), n) OP_dealloc(OP) - sage_free(perm) + sig_free(perm) return False def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, bint test_contains): @@ -1739,16 +1739,16 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, if limit_complain: print 'TOO BIG' return SC = SC_new(n) - cdef int *perm = sage_malloc(n * (len(L)+3) * sizeof(int)) + cdef int *perm = sig_malloc(n * (len(L)+3) * sizeof(int)) try: bitset_init(giant_support, n) except MemoryError: - sage_free(perm) + sig_free(perm) SC_dealloc(SC) raise MemoryError if perm is NULL or SC is NULL: bitset_free(giant_support) - sage_free(perm) + sig_free(perm) SC_dealloc(SC) raise MemoryError cdef int *perm2 = perm + n @@ -1758,7 +1758,7 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, perm[i] = Lperm[i] if SC_insert(SC, 0, perm, 1): bitset_free(giant_support) - sage_free(perm) + sig_free(perm) SC_dealloc(SC) raise MemoryError SCC = SC_copy(SC, n) @@ -1768,7 +1768,7 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, SC_nb = SC_new_base(SC, perm, n) if SCC is NULL or SCCC is NULL or SC_nb is NULL: bitset_free(giant_support) - sage_free(perm) + sig_free(perm) SC_dealloc(SC) SC_dealloc(SCC) SC_dealloc(SCCC) @@ -1972,7 +1972,7 @@ def SC_test_list_perms(list L, int n, int limit, bint gap, bint limit_complain, raise finally: bitset_free(giant_support) - sage_free(perm) + sig_free(perm) SC_dealloc(SC) SC_dealloc(SCC) SC_dealloc(SCCC) diff --git a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx index e8f6cbffdac..2bd1d9ac6a5 100644 --- a/src/sage/groups/perm_gps/partn_ref/double_coset.pyx +++ b/src/sage/groups/perm_gps/partn_ref/double_coset.pyx @@ -170,14 +170,14 @@ def coset_eq(list perm1=[0,1,2,3,4,5], list perm2=[1,2,3,4,5,0], list gens=[[1,2 cdef int i, n = len(perm1) assert all(len(g) == n for g in gens+[perm2]) cdef PartitionStack *part = PS_new(n, 1) - cdef int *c_perm = sage_malloc(n * sizeof(int)) + cdef int *c_perm = sig_malloc(n * sizeof(int)) cdef StabilizerChain *group = SC_new(n, 1) - cdef int *isomorphism = sage_malloc(n * sizeof(int)) + cdef int *isomorphism = sig_malloc(n * sizeof(int)) if part is NULL or c_perm is NULL or group is NULL or isomorphism is NULL: - sage_free(c_perm) + sig_free(c_perm) PS_dealloc(part) SC_dealloc(group) - sage_free(isomorphism) + sig_free(isomorphism) raise MemoryError for g in gens: for i from 0 <= i < n: @@ -186,14 +186,14 @@ def coset_eq(list perm1=[0,1,2,3,4,5], list perm2=[1,2,3,4,5,0], list gens=[[1,2 for i from 0 <= i < n: c_perm[i] = i cdef bint isomorphic = double_coset( perm1, perm2, part, c_perm, n, &all_children_are_equivalent_trivial, &refine_and_return_invariant_trivial, &compare_perms, group, NULL, isomorphism) - sage_free(c_perm) + sig_free(c_perm) PS_dealloc(part) SC_dealloc(group) if isomorphic: x = [isomorphism[i] for i from 0 <= i < n] else: x = False - sage_free(isomorphism) + sig_free(isomorphism) return x cdef dc_work_space *allocate_dc_work_space(int n): @@ -205,19 +205,19 @@ cdef dc_work_space *allocate_dc_work_space(int n): cdef int *int_array cdef dc_work_space *work_space - work_space = sage_malloc(sizeof(dc_work_space)) + work_space = sig_malloc(sizeof(dc_work_space)) if work_space is NULL: return NULL work_space.degree = n - int_array = sage_malloc((n*n + # for perm_stack + int_array = sig_malloc((n*n + # for perm_stack 5*n # for int_array )*sizeof(int)) work_space.group1 = SC_new(n) work_space.group2 = SC_new(n) work_space.current_ps = PS_new(n,0) work_space.first_ps = PS_new(n,0) - work_space.bitset_array = sage_calloc((n + 2*len_of_fp_and_mcr + 1), sizeof(bitset_t)) + work_space.bitset_array = sig_calloc((n + 2*len_of_fp_and_mcr + 1), sizeof(bitset_t)) work_space.orbits_of_subgroup = OP_new(n) work_space.perm_stack = NULL @@ -228,7 +228,7 @@ cdef dc_work_space *allocate_dc_work_space(int n): work_space.first_ps is NULL or \ work_space.bitset_array is NULL or \ work_space.orbits_of_subgroup is NULL: - sage_free(int_array) + sig_free(int_array) deallocate_dc_work_space(work_space) return NULL @@ -256,14 +256,14 @@ cdef void deallocate_dc_work_space(dc_work_space *work_space): if work_space.bitset_array is not NULL: for i from 0 <= i < n + 2*len_of_fp_and_mcr + 1: bitset_free(work_space.bitset_array[i]) - sage_free(work_space.perm_stack) + sig_free(work_space.perm_stack) SC_dealloc(work_space.group1) SC_dealloc(work_space.group2) PS_dealloc(work_space.current_ps) PS_dealloc(work_space.first_ps) - sage_free(work_space.bitset_array) + sig_free(work_space.bitset_array) OP_dealloc(work_space.orbits_of_subgroup) - sage_free(work_space) + sig_free(work_space) cdef int double_coset(void *S1, void *S2, PartitionStack *partition1, int *ordering2, int n, bint (*all_children_are_equivalent)(PartitionStack *PS, void *S), 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 cd2a4724dd0..407f59f8778 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_binary.pyx @@ -38,22 +38,22 @@ cdef class LinearBinaryCodeStruct(BinaryCodeStruct): # By the time the dimension gets this big, the computation is infeasible anyway... self.nwords = 1< sage_malloc(self.dimension * sizeof(bitset_s)) - self.scratch_bitsets = sage_malloc((2*self.dimension+2) * sizeof(bitset_s)) - self.alpha_is_wd = sage_malloc(sizeof(bitset_s)) + self.basis = sig_malloc(self.dimension * sizeof(bitset_s)) + self.scratch_bitsets = sig_malloc((2*self.dimension+2) * sizeof(bitset_s)) + self.alpha_is_wd = sig_malloc(sizeof(bitset_s)) self.word_ps = PS_new(self.nwords, 1) - self.alpha = sage_malloc((self.nwords+self.degree) * sizeof(int)) - self.scratch = sage_malloc((3*self.nwords+3*self.degree+2) * sizeof(int)) + self.alpha = sig_malloc((self.nwords+self.degree) * sizeof(int)) + self.scratch = sig_malloc((3*self.nwords+3*self.degree+2) * sizeof(int)) if self.basis is NULL or self.scratch_bitsets is NULL \ or self.alpha_is_wd is NULL or self.word_ps is NULL \ or self.alpha is NULL or self.scratch is NULL: - sage_free(self.basis) - sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd) + sig_free(self.basis) + sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd) PS_dealloc(self.word_ps) - sage_free(self.alpha) - sage_free(self.scratch) + sig_free(self.alpha) + sig_free(self.scratch) raise MemoryError cdef bint memerr = 0 @@ -83,9 +83,9 @@ cdef class LinearBinaryCodeStruct(BinaryCodeStruct): bitset_free(&self.basis[j]) memerr = 1 if memerr: - sage_free(self.basis); sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps) - sage_free(self.alpha); sage_free(self.scratch) + sig_free(self.basis); sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd); PS_dealloc(self.word_ps) + sig_free(self.alpha); sig_free(self.scratch) raise MemoryError else: bitset_zero(self.alpha_is_wd) @@ -307,12 +307,12 @@ cdef class LinearBinaryCodeStruct(BinaryCodeStruct): cdef int *ordering cdef PartitionStack *part part = PS_new(n, 1) - ordering = sage_malloc(self.degree * sizeof(int)) - output = sage_malloc(self.degree * sizeof(int)) + ordering = sig_malloc(self.degree * sizeof(int)) + output = sig_malloc(self.degree * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < n: ordering[i] = i @@ -322,12 +322,12 @@ cdef class LinearBinaryCodeStruct(BinaryCodeStruct): cdef bint isomorphic = double_coset( self, other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_linear_codes, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) if isomorphic: output_py = [output[i] for i from 0 <= i < n] else: output_py = False - sage_free(output) + sig_free(output) return output_py def __dealloc__(self): @@ -337,9 +337,9 @@ cdef class LinearBinaryCodeStruct(BinaryCodeStruct): bitset_free(&self.scratch_bitsets[j]) for j from 0 <= j < self.dimension: bitset_free(&self.basis[j]) - sage_free(self.basis); sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps) - sage_free(self.alpha); sage_free(self.scratch) + sig_free(self.basis); sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd); PS_dealloc(self.word_ps) + sig_free(self.alpha); sig_free(self.scratch) if self.output is not NULL: deallocate_agcl_output(self.output) @@ -365,21 +365,21 @@ cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct): else: raise NotImplementedError - self.words = sage_malloc(self.nwords * sizeof(bitset_s)) - self.scratch_bitsets = sage_malloc((4*self.nwords+1) * sizeof(bitset_s)) - self.alpha_is_wd = sage_malloc(sizeof(bitset_s)) + self.words = sig_malloc(self.nwords * sizeof(bitset_s)) + self.scratch_bitsets = sig_malloc((4*self.nwords+1) * sizeof(bitset_s)) + self.alpha_is_wd = sig_malloc(sizeof(bitset_s)) self.word_ps = PS_new(self.nwords, 1) - self.alpha = sage_malloc((self.nwords+self.degree) * sizeof(int)) - self.scratch = sage_malloc((3*self.nwords+3*self.degree+2) * sizeof(int)) + self.alpha = sig_malloc((self.nwords+self.degree) * sizeof(int)) + self.scratch = sig_malloc((3*self.nwords+3*self.degree+2) * sizeof(int)) if self.words is NULL or self.scratch_bitsets is NULL \ or self.alpha_is_wd is NULL or self.word_ps is NULL \ or self.alpha is NULL or self.scratch is NULL: - sage_free(self.words) - sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd) + sig_free(self.words) + sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd) PS_dealloc(self.word_ps) - sage_free(self.alpha) - sage_free(self.scratch) + sig_free(self.alpha) + sig_free(self.scratch) raise MemoryError cdef bint memerr = 0 @@ -417,9 +417,9 @@ cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct): bitset_free(&self.words[j]) memerr = 1 if memerr: - sage_free(self.words); sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps) - sage_free(self.alpha); sage_free(self.scratch) + sig_free(self.words); sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd); PS_dealloc(self.word_ps) + sig_free(self.alpha); sig_free(self.scratch) raise MemoryError else: bitset_zero(self.alpha_is_wd) @@ -440,9 +440,9 @@ cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct): bitset_free(&self.scratch_bitsets[j]) for j from 0 <= j < self.nwords: bitset_free(&self.words[j]) - sage_free(self.words); sage_free(self.scratch_bitsets) - sage_free(self.alpha_is_wd); PS_dealloc(self.word_ps) - sage_free(self.alpha); sage_free(self.scratch) + sig_free(self.words); sig_free(self.scratch_bitsets) + sig_free(self.alpha_is_wd); PS_dealloc(self.word_ps) + sig_free(self.alpha); sig_free(self.scratch) if self.output is not NULL: deallocate_agcl_output(self.output) @@ -568,12 +568,12 @@ cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct): cdef int *ordering cdef PartitionStack *part part = PS_new(n, 1) - ordering = sage_malloc(n * sizeof(int)) - output = sage_malloc(n * sizeof(int)) + ordering = sig_malloc(n * sizeof(int)) + output = sig_malloc(n * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < n: ordering[i] = i @@ -583,12 +583,12 @@ cdef class NonlinearBinaryCodeStruct(BinaryCodeStruct): cdef bint isomorphic = double_coset( self, other, part, ordering, n, &all_children_are_equivalent, &refine_by_bip_degree, &compare_nonlinear_codes, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) if isomorphic: output_py = [output[i] for i from 0 <= i < n] else: output_py = False - sage_free(output) + sig_free(output) return output_py cdef int ith_word_nonlinear(BinaryCodeStruct self, int i, bitset_s *word): diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx index e0420762b6e..5896b7bbed7 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_graphs.pyx @@ -132,36 +132,36 @@ def isomorphic(G1, G2, partn, ordering2, dig, use_indicator_function, sparse=Fal return {} part = PS_from_list(partition) - ordering = sage_malloc(n * sizeof(int)) - output = sage_malloc(n * sizeof(int)) + ordering = sig_malloc(n * sizeof(int)) + output = sig_malloc(n * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < n: ordering[i] = to2[ordering2[i]] - GS1.scratch = sage_malloc((3*n+1) * sizeof(int)) - GS2.scratch = sage_malloc((3*n+1) * sizeof(int)) + GS1.scratch = sig_malloc((3*n+1) * sizeof(int)) + GS2.scratch = sig_malloc((3*n+1) * sizeof(int)) if GS1.scratch is NULL or GS2.scratch is NULL: - sage_free(GS1.scratch) - sage_free(GS2.scratch) + sig_free(GS1.scratch) + sig_free(GS2.scratch) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) raise MemoryError cdef bint isomorphic = double_coset(GS1, GS2, part, ordering, n, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) - sage_free(GS1.scratch) - sage_free(GS2.scratch) + sig_free(ordering) + sig_free(GS1.scratch) + sig_free(GS2.scratch) if isomorphic: output_py = dict([[frm1[i], frm2[output[i]]] for i from 0 <= i < n]) else: output_py = False - sage_free(output) + sig_free(output) return output_py def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certify=False, @@ -426,16 +426,16 @@ def search_tree(G_in, partition, lab=True, dig=False, dict_rep=False, certify=Fa else: return tuple(return_tuple) - GS.scratch = sage_malloc( (3*G.num_verts + 1) * sizeof(int) ) + GS.scratch = sig_malloc( (3*G.num_verts + 1) * sizeof(int) ) part = PS_from_list(partition) if GS.scratch is NULL or part is NULL: PS_dealloc(part) - sage_free(GS.scratch) + sig_free(GS.scratch) raise MemoryError lab_new = lab or certify output = get_aut_gp_and_can_lab(GS, part, G.num_verts, &all_children_are_equivalent, &refine_by_degree, &compare_graphs, lab, NULL, NULL, NULL) - sage_free( GS.scratch ) + sig_free( GS.scratch ) # prepare output list_of_gens = [] for i from 0 <= i < output.num_gens: @@ -944,7 +944,7 @@ def coarsest_equitable_refinement(CGraph G, list partition, bint directed): cdef GraphStruct GS = GraphStruct() GS.G = G - GS.scratch = sage_malloc((3*n+1) * sizeof(int)) + GS.scratch = sig_malloc((3*n+1) * sizeof(int)) if GS.scratch is NULL: PS_dealloc(nu) raise MemoryError @@ -953,10 +953,10 @@ def coarsest_equitable_refinement(CGraph G, list partition, bint directed): # set up cells to refine by cdef int num_cells = len(partition) - cdef int *alpha = sage_malloc(n * sizeof(int)) + cdef int *alpha = sig_malloc(n * sizeof(int)) if alpha is NULL: PS_dealloc(nu) - sage_free(GS.scratch) + sig_free(GS.scratch) raise MemoryError j = 0 for i from 0 <= i < num_cells: @@ -975,8 +975,8 @@ def coarsest_equitable_refinement(CGraph G, list partition, bint directed): cell = [] PS_dealloc(nu) - sage_free(GS.scratch) - sage_free(alpha) + sig_free(GS.scratch) + sig_free(alpha) return eq_part @@ -999,7 +999,7 @@ def get_orbits(list gens, int n): return [[i] for i from 0 <= i < n] cdef OrbitPartition *OP = OP_new(n) - cdef int *perm_ints = sage_malloc(n * sizeof(int)) + cdef int *perm_ints = sig_malloc(n * sizeof(int)) if perm_ints is NULL: OP_dealloc(OP) raise MemoryError @@ -1018,7 +1018,7 @@ def get_orbits(list gens, int n): orbit_dict[j] = [i] OP_dealloc(OP) - sage_free(perm_ints) + sig_free(perm_ints) return orbit_dict.values() @@ -1074,15 +1074,15 @@ cdef void *allocate_degd(int degree): r""" Allocate the data part of the iterator over edges to add to the graph. """ - cdef dg_edge_gen_data *degd = sage_malloc(sizeof(dg_edge_gen_data)) + cdef dg_edge_gen_data *degd = sig_malloc(sizeof(dg_edge_gen_data)) cdef iterator *edge_iterator = allocate_subset_gen(degree, 2) if degd is NULL or edge_iterator is NULL: - sage_free(degd) + sig_free(degd) free_subset_gen(edge_iterator) return NULL edge_iterator = setup_set_gen(edge_iterator, degree, 2) if edge_iterator is NULL: - sage_free(degd) + sig_free(degd) return NULL degd.edge_iterator = edge_iterator return degd @@ -1093,7 +1093,7 @@ cdef void deallocate_degd(void *data): """ cdef dg_edge_gen_data *degd = data free_subset_gen(degd.edge_iterator) - sage_free(degd) + sig_free(degd) cdef int gen_children_dg_edge(void *S, aut_gp_and_can_lab *group, iterator *it): r""" @@ -1153,7 +1153,7 @@ cdef void *allocate_dg_edge(int n, bint loops): try: GS = GraphStruct() G = DenseGraph(n) - scratch = sage_malloc((3*n+1) * sizeof(int)) + scratch = sig_malloc((3*n+1) * sizeof(int)) if scratch is NULL: raise MemoryError except MemoryError: @@ -1172,7 +1172,7 @@ cdef void free_dg_edge(void *child): Deallocates an object for this augmentation scheme. """ cdef GraphStruct GS = child - sage_free(GS.scratch) + sig_free(GS.scratch) Py_DECREF(GS.G) Py_DECREF(GS) @@ -1210,10 +1210,10 @@ cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops): r""" Allocates the iterator for generating graphs. """ - cdef iterator *dg_edge_gen = sage_malloc(sizeof(iterator)) + cdef iterator *dg_edge_gen = sig_malloc(sizeof(iterator)) cdef canonical_generator_data *cgd = allocate_cgd(depth, degree) if dg_edge_gen is NULL or cgd is NULL: - sage_free(dg_edge_gen) + sig_free(dg_edge_gen) deallocate_cgd(cgd) return NULL cdef int i, j @@ -1229,7 +1229,7 @@ cdef iterator *allocate_dg_edge_gen(int degree, int depth, bint loops): deallocate_degd(cgd.iterator_stack[j].data) free_dg_edge(cgd.object_stack[j]) free_dg_edge(cgd.parent_stack[j]) - sage_free(dg_edge_gen) + sig_free(dg_edge_gen) deallocate_cgd(cgd) return NULL dg_edge_gen.data = cgd @@ -1242,7 +1242,7 @@ cdef void free_dg_edge_gen(iterator *dg_edge_gen): """ cdef canonical_generator_data *cgd = dg_edge_gen.data deallocate_cgd(cgd) - sage_free(dg_edge_gen) + sig_free(dg_edge_gen) def generate_dense_graphs_edge_addition(int n, bint loops, G = None, depth = None, bint construct = False, @@ -1421,7 +1421,7 @@ cdef void *allocate_dg_vert(int n, int depth): G = DenseGraph(0, extra_vertices=depth) bitset_set_first_n(G.active_vertices, n) G.num_verts = n - scratch = sage_malloc((3*depth+1) * sizeof(int)) + scratch = sig_malloc((3*depth+1) * sizeof(int)) if scratch is NULL: raise MemoryError except MemoryError: @@ -1440,7 +1440,7 @@ cdef void free_dg_vert(void *child): Deallocates an object for this augmentation scheme. """ cdef GraphStruct GS = child - sage_free(GS.scratch) + sig_free(GS.scratch) Py_DECREF(GS.G) Py_DECREF(GS) @@ -1470,11 +1470,11 @@ cdef iterator *allocate_dg_vert_gen(int degree, int depth): r""" Allocates the iterator for generating graphs. """ - cdef iterator *dg_vert_gen = sage_malloc(sizeof(iterator)) + cdef iterator *dg_vert_gen = sig_malloc(sizeof(iterator)) cdef canonical_generator_data *cgd = allocate_cgd(depth, degree) cdef canonical_generator_data *cgd2 if dg_vert_gen is NULL or cgd is NULL: - sage_free(dg_vert_gen) + sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL cdef int i, j @@ -1486,7 +1486,7 @@ cdef iterator *allocate_dg_vert_gen(int degree, int depth): for j from 0 <= j <= i: free_dg_vert(cgd.object_stack[j]) free_dg_vert(cgd.parent_stack[j]) - sage_free(dg_vert_gen) + sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL for i from 0 <= i < depth-1: @@ -1500,7 +1500,7 @@ cdef iterator *allocate_dg_vert_gen(int degree, int depth): for j from 0 <= j < i: cgd2 = cgd.iterator_stack[j].data deallocate_cgd(cgd2) - sage_free(dg_vert_gen) + sig_free(dg_vert_gen) deallocate_cgd(cgd) return NULL dg_vert_gen.data = cgd @@ -1513,7 +1513,7 @@ cdef void free_dg_vert_gen(iterator *dg_vert_gen): """ cdef canonical_generator_data *cgd = dg_vert_gen.data deallocate_cgd(cgd) - sage_free(dg_vert_gen) + sig_free(dg_vert_gen) cdef void free_cgd_2(void *data): r""" diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_lists.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_lists.pyx index 464de83b784..2dd68f1ea50 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_lists.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_lists.pyx @@ -34,12 +34,12 @@ def is_isomorphic(self, other): cdef int *output cdef int *ordering part = PS_new(n, 1) - ordering = sage_malloc((len(self)) * sizeof(int)) - output = sage_malloc((len(self)) * sizeof(int)) + ordering = sig_malloc((len(self)) * sizeof(int)) + output = sig_malloc((len(self)) * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < (len(self)): ordering[i] = i @@ -47,12 +47,12 @@ def is_isomorphic(self, other): cdef bint isomorphic = double_coset( self, other, part, ordering, (len(self)), &all_list_children_are_equivalent, &refine_list, &compare_lists, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) if isomorphic: output_py = [output[i] for i from 0 <= i < (len(self))] else: output_py = False - sage_free(output) + sig_free(output) return output_py cdef bint all_list_children_are_equivalent(PartitionStack *PS, void *S): 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 296150b403e..737d113d827 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_matrices.pyx @@ -48,10 +48,10 @@ cdef class MatrixStruct: self.nsymbols = len(self.symbols) self.symbol_structs = [] - num_rows = sage_malloc(self.nsymbols * sizeof(int)) + num_rows = sig_malloc(self.nsymbols * sizeof(int)) self.temp_col_ps = PS_new(self.degree, 1) if num_rows is NULL or self.temp_col_ps is NULL: - sage_free(num_rows) + sig_free(num_rows) PS_dealloc(self.temp_col_ps) raise MemoryError @@ -78,7 +78,7 @@ cdef class MatrixStruct: bitset_set( &S_temp.words[num_rows[j]], i) if row_list.count(s) == 1 or row_list.index(s) == self.degree - i - 1: num_rows[j] += 1 - sage_free(num_rows) + sig_free(num_rows) self.output = NULL def __dealloc__(self): @@ -233,12 +233,12 @@ cdef class MatrixStruct: S_temp = other.symbol_structs[i] S_temp.first_time = 1 part = PS_new(n, 1) - ordering = sage_malloc(self.degree * sizeof(int)) - output = sage_malloc(self.degree * sizeof(int)) + ordering = sig_malloc(self.degree * sizeof(int)) + output = sig_malloc(self.degree * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < self.degree: ordering[i] = i @@ -246,12 +246,12 @@ cdef class MatrixStruct: cdef bint isomorphic = double_coset( self, other, part, ordering, self.degree, &all_matrix_children_are_equivalent, &refine_matrix, &compare_matrices, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) if isomorphic: output_py = [output[i] for i from 0 <= i < self.degree] else: output_py = False - sage_free(output) + sig_free(output) return output_py cdef int refine_matrix(PartitionStack *PS, void *S, int *cells_to_refine_by, int ctrb_len): diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx index 5c88c7cc8fc..58f46916e76 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_python.pyx @@ -545,12 +545,12 @@ def double_coset_python(S1, S2, partition1, ordering2, n, obj_wrapper2 = PythonObjectWrapper(S2, all_children_are_equivalent, refine_and_return_invariant, compare_structures, n) cdef PartitionStack *part = PS_from_list(partition1) - cdef int *ordering = sage_malloc(n * sizeof(int)) - cdef int *output = sage_malloc(n * sizeof(int)) + cdef int *ordering = sig_malloc(n * sizeof(int)) + cdef int *output = sig_malloc(n * sizeof(int)) if part is NULL or ordering is NULL or output is NULL: PS_dealloc(part) - sage_free(ordering) - sage_free(output) + sig_free(ordering) + sig_free(output) raise MemoryError for i from 0 <= i < n: ordering[i] = ordering2[i] @@ -562,12 +562,12 @@ def double_coset_python(S1, S2, partition1, ordering2, n, &compare_structures_python, NULL, NULL, output) PS_dealloc(part) - sage_free(ordering) + sig_free(ordering) if isomorphic: output_py = [output[i] for i from 0 <= i < n] else: output_py = False - sage_free(output) + sig_free(output) return output_py diff --git a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx index d364b826334..f75089713d1 100644 --- a/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx +++ b/src/sage/groups/perm_gps/partn_ref/refinement_sets.pyx @@ -137,32 +137,32 @@ def set_stab_py(generators, sett, relab=False): cdef int i, j, n = len(generators[0]), n_gens = len(generators) cdef StabilizerChain *supergroup = SC_new(n) cdef aut_gp_and_can_lab *stabilizer - cdef int *gens = sage_malloc(n*n_gens * sizeof(int)) - cdef subset *subset_sett = sage_malloc(sizeof(subset)) + cdef int *gens = sig_malloc(n*n_gens * sizeof(int)) + cdef subset *subset_sett = sig_malloc(sizeof(subset)) if gens is NULL or supergroup is NULL or subset_sett is NULL: SC_dealloc(supergroup) - sage_free(gens) - sage_free(subset_sett) + sig_free(gens) + sig_free(subset_sett) raise MemoryError bitset_init(&subset_sett.bits, n) - subset_sett.scratch = sage_malloc((3*n+1) * sizeof(int)) + subset_sett.scratch = sig_malloc((3*n+1) * sizeof(int)) for i from 0 <= i < len(generators): for j from 0 <= j < n: gens[n*i + j] = generators[i][j] if SC_insert(supergroup, 0, gens, n_gens): SC_dealloc(supergroup) - sage_free(gens) - sage_free(subset_sett) + sig_free(gens) + sig_free(subset_sett) raise MemoryError - sage_free(gens) + sig_free(gens) bitset_clear(&subset_sett.bits) for i in sett: bitset_add(&subset_sett.bits, i) stabilizer = set_stab(supergroup, subset_sett, relab) SC_dealloc(supergroup) bitset_free(&subset_sett.bits) - sage_free(subset_sett.scratch) - sage_free(subset_sett) + sig_free(subset_sett.scratch) + sig_free(subset_sett) if stabilizer is NULL: raise MemoryError stab_gens = [] @@ -385,20 +385,20 @@ def sets_isom_py(generators, set1, set2): return False cdef int i, j, n = len(generators[0]), n_gens = len(generators) cdef StabilizerChain *supergroup = SC_new(n) - cdef int *gens = sage_malloc(n*n_gens * sizeof(int)) - cdef int *isom = sage_malloc(n * sizeof(int)) - cdef subset *subset_sett1 = sage_malloc(sizeof(subset)) - cdef subset *subset_sett2 = sage_malloc(sizeof(subset)) + cdef int *gens = sig_malloc(n*n_gens * sizeof(int)) + cdef int *isom = sig_malloc(n * sizeof(int)) + cdef subset *subset_sett1 = sig_malloc(sizeof(subset)) + cdef subset *subset_sett2 = sig_malloc(sizeof(subset)) bitset_init(&subset_sett1.bits, n) bitset_init(&subset_sett2.bits, n) - subset_sett1.scratch = sage_malloc((3*n+1) * sizeof(int)) - subset_sett2.scratch = sage_malloc((3*n+1) * sizeof(int)) + subset_sett1.scratch = sig_malloc((3*n+1) * sizeof(int)) + subset_sett2.scratch = sig_malloc((3*n+1) * sizeof(int)) for i from 0 <= i < len(generators): for j from 0 <= j < n: gens[n*i + j] = generators[i][j] if SC_insert(supergroup, 0, gens, n_gens): raise MemoryError - sage_free(gens) + sig_free(gens) bitset_clear(&subset_sett1.bits) bitset_clear(&subset_sett2.bits) for i in set1: @@ -409,15 +409,15 @@ def sets_isom_py(generators, set1, set2): SC_dealloc(supergroup) bitset_free(&subset_sett1.bits) bitset_free(&subset_sett2.bits) - sage_free(subset_sett1.scratch) - sage_free(subset_sett2.scratch) - sage_free(subset_sett1) - sage_free(subset_sett2) + sig_free(subset_sett1.scratch) + sig_free(subset_sett2.scratch) + sig_free(subset_sett1) + sig_free(subset_sett2) if isomorphic: output_py = [isom[i] for i from 0 <= i < n] else: output_py = False - sage_free(isom) + sig_free(isom) return output_py cdef int sets_isom(StabilizerChain *supergroup, subset *set1, subset *set2, int *isom) except -1: @@ -483,17 +483,17 @@ cdef void *allocate_subset(int n): r""" Allocates a subset struct of degree n. """ - cdef subset *set1 = sage_malloc(sizeof(subset)) - cdef int *scratch = sage_malloc((3*n+1) * sizeof(int)) + cdef subset *set1 = sig_malloc(sizeof(subset)) + cdef int *scratch = sig_malloc((3*n+1) * sizeof(int)) if set1 is NULL or scratch is NULL: - sage_free(set1) - sage_free(scratch) + sig_free(set1) + sig_free(scratch) return NULL try: bitset_init(&set1.bits, n) except MemoryError: - sage_free(set1) - sage_free(scratch) + sig_free(set1) + sig_free(scratch) return NULL set1.scratch = scratch return set1 @@ -504,16 +504,16 @@ cdef void free_subset(void *child): """ cdef subset *set1 = child if set1 is not NULL: - sage_free(set1.scratch) + sig_free(set1.scratch) bitset_free(&set1.bits) - sage_free(set1) + sig_free(set1) cdef void *allocate_sgd(int degree): r""" Allocates the data part of an iterator which generates augmentations, i.e., elements to add to the set. """ - cdef subset_generator_data *sgd = sage_malloc(sizeof(subset_generator_data)) + cdef subset_generator_data *sgd = sig_malloc(sizeof(subset_generator_data)) sgd.orbits = OP_new(degree) if sgd is NULL or sgd.orbits is NULL: deallocate_sgd(sgd) @@ -527,7 +527,7 @@ cdef void deallocate_sgd(void *data): cdef subset_generator_data *sgd = data if sgd is not NULL: OP_dealloc(sgd.orbits) - sage_free(sgd) + sig_free(sgd) cdef void *subset_generator_next(void *data, int *degree, bint *mem_err): r""" @@ -618,10 +618,10 @@ cdef iterator *allocate_subset_gen(int degree, int max_size): r""" Allocates the generator of subsets. """ - cdef iterator *subset_gen = sage_malloc(sizeof(iterator)) + cdef iterator *subset_gen = sig_malloc(sizeof(iterator)) if subset_gen is not NULL: if allocate_subset_gen_2(degree, max_size, subset_gen): - sage_free(subset_gen) + sig_free(subset_gen) subset_gen = NULL return subset_gen @@ -658,7 +658,7 @@ cdef void free_subset_gen(iterator *subset_gen): if subset_gen is NULL: return cdef canonical_generator_data *cgd = subset_gen.data deallocate_cgd(cgd) - sage_free(subset_gen) + sig_free(subset_gen) cdef iterator *setup_set_gen(iterator *subset_gen, int degree, int max_size): r""" @@ -810,19 +810,19 @@ def sets_modulo_perm_group(list generators, int max_size, bint indicate_mem_err cdef subset *thing cdef StabilizerChain *group = SC_new(n) - cdef int *gens = sage_malloc(n*n_gens * sizeof(int)) + cdef int *gens = sig_malloc(n*n_gens * sizeof(int)) if group is NULL or gens is NULL: SC_dealloc(group) - sage_free(gens) + sig_free(gens) raise MemoryError for i from 0 <= i < len(generators): for j from 0 <= j < n: gens[n*i + j] = generators[i][j] if SC_insert(group, 0, gens, n_gens): SC_dealloc(group) - sage_free(gens) + sig_free(gens) raise MemoryError - sage_free(gens) + sig_free(gens) cdef iterator *subset_gen = allocate_subset_gen(n, max_size) if subset_gen is NULL: diff --git a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx index b63870d8421..c2e25b680c6 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx @@ -272,7 +272,7 @@ cdef class _BestValStore: """ self.default_data_length = n self.storage_length = 0 - self.values = sage_malloc(0) + self.values = sig_malloc(0) if self.values is NULL: raise MemoryError('allocating _BestValStore') @@ -280,7 +280,7 @@ cdef class _BestValStore: """ Dealloc. """ - sage_free(self.values) + sig_free(self.values) cdef long * get_row(self, int i): r""" @@ -291,7 +291,7 @@ cdef class _BestValStore: with the all zero vector. """ if i >= self.storage_length: - self.values = < long *> sage_realloc(self.values, (i + 1)* self.default_data_length * sizeof(long)) + self.values = < long *> sig_realloc(self.values, (i + 1)* self.default_data_length * sizeof(long)) if self.values is NULL: raise MemoryError('resizing _BestValStore') self.storage_length = i + 1 @@ -336,12 +336,12 @@ cdef class LabelledBranching: self.n = n self.group = libgap.eval("Group(())") self.ClosureGroup = libgap.eval("ClosureGroup") - self.father = < int *> sage_malloc(n * sizeof(int)) + self.father = < int *> sig_malloc(n * sizeof(int)) if self.father is NULL: raise MemoryError('allocating LabelledBranching') - self.act_perm = < int *> sage_malloc(n * sizeof(int)) + self.act_perm = < int *> sig_malloc(n * sizeof(int)) if self.act_perm is NULL: - sage_free(self.father) + sig_free(self.father) raise MemoryError('allocating LabelledBranching') cdef int i for i from 0 <= i < self.n: @@ -351,8 +351,8 @@ cdef class LabelledBranching: """ Dealloc. """ - sage_free(self.father) - sage_free(self.act_perm) + sig_free(self.father) + sig_free(self.act_perm) cpdef add_gen(self, GapElement_Permutation gen): r""" @@ -472,7 +472,7 @@ cdef class PartitionRefinement_generic: self._is_candidate_initialized = False self._known_automorphisms = LabelledBranching(n) - self._refine_vals_scratch = sage_malloc(n * sizeof(long)) + self._refine_vals_scratch = sig_malloc(n * sizeof(long)) if self._refine_vals_scratch is NULL: raise MemoryError('allocating PartitionRefinement_generic') @@ -487,7 +487,7 @@ cdef class PartitionRefinement_generic: Dealloc. """ PS_dealloc(self._part) - sage_free(self._refine_vals_scratch) + sig_free(self._refine_vals_scratch) ##################################################################### # The following functions have to be implemented by derived classes @@ -627,7 +627,7 @@ cdef class PartitionRefinement_generic: self._fixed_not_minimized = PS_singletons(self._part) self._inner_min_unminimized(&inner_group_changed) if self._part is NULL: - sage_free(self._refine_vals_scratch) + sig_free(self._refine_vals_scratch) raise MemoryError('initializing the partition stack') cdef void _start_Sn_backtrack(self): diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index e3079c99dd3..268eb4f6dfa 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -602,7 +602,7 @@ def _element_class(self): EXAMPLE:: - sage: SymmetricGroup(17)._element_class() + sage: AlternatingGroup(17)._element_class() """ return PermutationGroupElement @@ -1548,18 +1548,23 @@ def _order(self): EXAMPLES:: - sage: SymmetricGroup(10).stabilizer(4)._order() + sage: G = SymmetricGroup(10).subgroup([(i, 10) for i in range(1, 10) if i != 4]) + sage: G._order() 362880 - sage: SymmetricGroup(10).stabilizer(4).stabilizer(5)._order() - 40320 - sage: SymmetricGroup(200).stabilizer(100)._order() == factorial(199) # this should be very fast - True TESTS:: sage: [SymmetricGroup(n).stabilizer(1)._gap_().Size() for n in [4..10]] [6, 24, 120, 720, 5040, 40320, 362880] - sage: [SymmetricGroup(n).stabilizer(1)._order() for n in [4..10]] + sage: special_gens = [ + ....: [(3,4), (2,4)], + ....: [(4,5), (3,5), (2,5)], + ....: [(5,6), (4,6), (3,6), (2,6)], + ....: [(6,7), (5,7), (4,7), (3,7), (2,7)], + ....: [(7,8), (6,8), (5,8), (4,8), (3,8), (2,8)], + ....: [(8,9), (7,9), (6,9), (5,9), (4,9), (3,9), (2,9)], + ....: [(9,10), (8,10), (7,10), (6,10), (5,10), (4,10), (3,10), (2,10)]] + sage: [SymmetricGroup(n).subgroup(gen)._order() for gen in special_gens] [6, 24, 120, 720, 5040, 40320, 362880] """ gens = self.gens() @@ -2210,7 +2215,7 @@ def semidirect_product(self, N, mapping, check=True): REFERENCES: - .. [THOMAS-WOODS] A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) + .. [THOMAS-WOODS] \A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) AUTHOR: diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index b1358a7d144..167a45fce22 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -460,9 +460,9 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): self.n = max(parent.degree(), 1) if self.perm is NULL or self.perm is self.perm_buf: - self.perm = sage_malloc(sizeof(int) * self.n) + self.perm = sig_malloc(sizeof(int) * self.n) else: - self.perm = sage_realloc(self.perm, sizeof(int) * self.n) + self.perm = sig_realloc(self.perm, sizeof(int) * self.n) cdef int i, vn = len(v) @@ -481,7 +481,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): def __dealloc__(self): if self.perm is not NULL and self.perm is not self.perm_buf: - sage_free(self.perm) + sig_free(self.perm) def __reduce__(self): """ @@ -508,7 +508,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): if other.n <= sizeof(other.perm_buf) / sizeof(int): other.perm = other.perm_buf else: - other.perm = sage_malloc(sizeof(int) * other.n) + other.perm = sig_malloc(sizeof(int) * other.n) return other def _gap_(self, gap=None): @@ -1059,7 +1059,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): cdef long long order_c = 1 cdef int cycle_len cdef int i, k - cdef bint* seen = sage_malloc(sizeof(bint) * self.n) + cdef bint* seen = sig_malloc(sizeof(bint) * self.n) for i from 0 <= i < self.n: seen[i] = 0 for i from 0 <= i < self.n: if seen[i] or self.perm[i] == i: @@ -1076,7 +1076,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): order_c = (order_c * cycle_len) / arith.c_gcd_longlong(order_c, cycle_len) if order_c > LONG_LONG_MAX / (self.n - i): order = Integer(order_c) - sage_free(seen) + sig_free(seen) return Integer(order_c) if order is None else order def inverse(self): @@ -1133,7 +1133,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): """ cdef int cycle_len_sum = 0 cdef int i, k - cdef bint* seen = sage_malloc(sizeof(bint) * self.n) + cdef bint* seen = sig_malloc(sizeof(bint) * self.n) for i from 0 <= i < self.n: seen[i] = 0 for i from 0 <= i < self.n: if seen[i] or self.perm[i] == i: @@ -1143,7 +1143,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): seen[k] = 1 k = self.perm[k] cycle_len_sum += 1 - sage_free(seen) + sig_free(seen) return 1 - 2*(cycle_len_sum % 2) # == (-1)^cycle_len @@ -1208,7 +1208,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): L = [] cdef PermutationGroupElement cycle cdef int i, j, k, next_k - cdef bint* seen = sage_malloc(sizeof(bint) * self.n) + cdef bint* seen = sig_malloc(sizeof(bint) * self.n) for i from 0 <= i < self.n: seen[i] = 0 for i from 0 <= i < self.n: if seen[i] or self.perm[i] == i: @@ -1221,7 +1221,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): next_k = cycle.perm[k] = self.perm[k] k = next_k PyList_Append(L, cycle) - sage_free(seen) + sig_free(seen) return L def cycle_tuples(self, singletons=False): @@ -1259,7 +1259,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): from_gap = self._parent._domain_from_gap L = [] cdef int i, k - cdef bint* seen = sage_malloc(sizeof(bint) * self.n) + cdef bint* seen = sig_malloc(sizeof(bint) * self.n) for i from 0 <= i < self.n: seen[i] = 0 for i from 0 <= i < self.n: if seen[i]: @@ -1279,7 +1279,7 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): seen[k] = 1 k = self.perm[k] PyList_Append(L, tuple(cycle)) - sage_free(seen) + sig_free(seen) return L def cycle_string(self, singletons=False): @@ -1439,6 +1439,31 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): print " ",l5 return l1,l2 +cdef class SymmetricGroupElement(PermutationGroupElement): + """ + An element of the symmetric group. + """ + def absolute_length(self): + """ + Return the absolute length of ``self``. + + The absolute length is the size minus the number of its disjoint + cycles. Alternatively, it is the length of the shortest + expression of the element as a product of reflections. + + .. SEEALSO:: + + :meth:`absolute_le` + + EXAMPLES:: + + sage: S = SymmetricGroup(3) + sage: [x.absolute_length() for x in S] + [0, 1, 2, 2, 1, 1] + """ + from sage.combinat.permutation import Permutation + return Permutation(self).absolute_length() + cdef bint is_valid_permutation(int* perm, int n): """ This is used in the __init__ method. diff --git a/src/sage/groups/perm_gps/permgroup_named.py b/src/sage/groups/perm_gps/permgroup_named.py index 64d06572f0c..25ce8ce78ca 100644 --- a/src/sage/groups/perm_gps/permgroup_named.py +++ b/src/sage/groups/perm_gps/permgroup_named.py @@ -91,7 +91,7 @@ from sage.misc.functional import is_even from sage.misc.cachefunc import cached_method, weak_cached_function from sage.groups.perm_gps.permgroup import PermutationGroup_generic -from sage.groups.perm_gps.permgroup_element import PermutationGroupElement +from sage.groups.perm_gps.permgroup_element import SymmetricGroupElement from sage.structure.unique_representation import CachedRepresentation from sage.structure.parent import Parent from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets @@ -275,7 +275,7 @@ def __init__(self, domain=None): gens = [tuple(self._domain)] if len(self._domain) > 2: gens.append(tuple(self._domain[:2])) - self._gens = [PermutationGroupElement(g, self, check=False) + self._gens = [self._element_class()(g, self, check=False) for g in gens] def _gap_init_(self, gap=None): @@ -351,6 +351,19 @@ def cartan_type(self): from sage.combinat.root_system.cartan_type import CartanType return CartanType(['A', max(self.degree() - 1,0)]) + def coxeter_matrix(self): + r""" + Return the Coxeter matrix of ``self``. + + EXAMPLES:: + + sage: A = SymmetricGroup([2,3,7,'a']); A.coxeter_matrix() + [1 3 2] + [3 1 3] + [2 3 1] + """ + return self.cartan_type().coxeter_matrix() + def simple_reflection(self, i): r""" For `i` in the index set of ``self``, this returns the @@ -368,6 +381,20 @@ def simple_reflection(self, i): """ return self([(i, self._domain[self._domain.index(i)+1])], check=False) + def reflections(self): + """ + Return the list of all reflections in ``self``. + + EXAMPLES:: + + sage: A = SymmetricGroup(3) + sage: A.reflections() + [(1,2), (1,3), (2,3)] + """ + from itertools import combinations + dom = self._domain + return [self([(i, j)], check=False) for i, j in combinations(dom, 2)] + def young_subgroup(self, comp): """ Return the Young subgroup associated with the composition ``comp``. @@ -591,6 +618,17 @@ def algebra(self, base_ring, category=None): else: return super(SymmetricGroup, self).algebra(base_ring) + def _element_class(self): + r""" + Return the class to be used for creating elements of this group. + + EXAMPLE:: + + sage: SymmetricGroup(17)._element_class() + + """ + return SymmetricGroupElement + class AlternatingGroup(PermutationGroup_symalt): def __init__(self, domain=None): """ @@ -1263,7 +1301,7 @@ class GeneralDihedralGroup(PermutationGroup_generic): REFERENCES: - .. [1] A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) + .. [1] \A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) AUTHOR: diff --git a/src/sage/gsl/fft.pyx b/src/sage/gsl/fft.pyx index a4ab8e7e08e..3cdc4e8c04b 100644 --- a/src/sage/gsl/fft.pyx +++ b/src/sage/gsl/fft.pyx @@ -20,7 +20,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" import sage.plot.all import sage.libs.pari.all @@ -106,7 +106,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): """ self.n = n self.stride = stride - self.data = sage_malloc(sizeof(double)*(2*n)) + self.data = sig_malloc(sizeof(double)*(2*n)) cdef int i for i from 0 <= i < 2*n: self.data[i] = 0 @@ -121,7 +121,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): sage: del a """ - sage_free(self.data) + sig_free(self.data) def __len__(self): """ @@ -162,8 +162,7 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): sage: a[1] = I Traceback (most recent call last): ... - TypeError: Unable to convert 1.0*I to float; use abs() or real_part() as desired - + TypeError: unable to convert 1.0*I to float; use abs() or real_part() as desired """ # just set real for now if i < 0 or i >= self.n: diff --git a/src/sage/gsl/gsl_array.pyx b/src/sage/gsl/gsl_array.pyx index 42f175cc238..852f88bd669 100644 --- a/src/sage/gsl/gsl_array.pyx +++ b/src/sage/gsl/gsl_array.pyx @@ -2,7 +2,7 @@ GSL arrays """ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" cdef class GSLDoubleArray: r""" @@ -19,7 +19,7 @@ cdef class GSLDoubleArray: self.n = n self.stride = stride - self.data = sage_malloc(sizeof(double)*n) + self.data = sig_malloc(sizeof(double)*n) if data is not None: for i from 0 <= i < n: self.data[i] = data[i] @@ -28,7 +28,7 @@ cdef class GSLDoubleArray: self.data[i] = 0 def __dealloc__(self): - sage_free(self.data) + sig_free(self.data) def __len__(self): return self.n diff --git a/src/sage/gsl/integration.pyx b/src/sage/gsl/integration.pyx index 94eb6ad73b1..9753d98bace 100644 --- a/src/sage/gsl/integration.pyx +++ b/src/sage/gsl/integration.pyx @@ -9,7 +9,7 @@ AUTHORS: - Robert Bradshaw (2008-08): fast float integration -- Jeroen Demeyer (2011-11-23): Trac #12047: return 0 when the +- Jeroen Demeyer (2011-11-23): :trac:`12047`: return 0 when the integration interval is a point; reformat documentation and add to the reference manual. """ @@ -187,7 +187,7 @@ def numerical_integral(func, a, b=None, If the interval of integration is a point, then the result is always zero (this makes sense within the Lebesgue theory of - integration), see Trac ticket #12047:: + integration), see :trac:`12047`:: sage: numerical_integral(log, 0, 0) (0.0, 0.0) @@ -206,7 +206,7 @@ def numerical_integral(func, a, b=None, TESTS: Make sure that constant Expressions, not merely uncallable arguments, - can be integrated (trac #10088), at least if we can coerce them + can be integrated (:trac:`10088`), at least if we can coerce them to float:: sage: f, g = x, x-1 diff --git a/src/sage/gsl/interpolation.pyx b/src/sage/gsl/interpolation.pyx index c7d6832c710..95ab6be6737 100644 --- a/src/sage/gsl/interpolation.pyx +++ b/src/sage/gsl/interpolation.pyx @@ -2,7 +2,7 @@ r""" Real Interpolation using GSL """ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" @@ -246,20 +246,20 @@ cdef class Spline: cdef start_interp(self): if self.started: - sage_free(self.x) - sage_free(self.y) + sig_free(self.x) + sig_free(self.y) return v = list(self.v) v.sort() n = len(v) if n < 3: raise RuntimeError("must have at least 3 points in order to interpolate") - self.x = sage_malloc(n*sizeof(double)) + self.x = sig_malloc(n*sizeof(double)) if self.x == 0: raise MemoryError - self.y = sage_malloc(n*sizeof(double)) + self.y = sig_malloc(n*sizeof(double)) if self.y == 0: - sage_free(self.x) + sig_free(self.x) raise MemoryError cdef int i @@ -275,8 +275,8 @@ cdef class Spline: cdef stop_interp(self): if not self.started: return - sage_free(self.x) - sage_free(self.y) + sig_free(self.x) + sig_free(self.y) gsl_spline_free (self.spline) gsl_interp_accel_free (self.acc) self.started = 0 diff --git a/src/sage/gsl/ode.pyx b/src/sage/gsl/ode.pyx index c77bdb71f84..ff3f2cb42a3 100644 --- a/src/sage/gsl/ode.pyx +++ b/src/sage/gsl/ode.pyx @@ -21,7 +21,7 @@ AUTHORS: include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from sage.libs.gsl.all cimport * import sage.gsl.interpolation @@ -446,7 +446,7 @@ class ode_solver(object): cdef double * scale_abs_array scale_abs_array=NULL - y= sage_malloc(sizeof(double)*(dim)) + y= sig_malloc(sizeof(double)*(dim)) if y==NULL: raise MemoryError,"error allocating memory" result=[] @@ -485,7 +485,7 @@ class ode_solver(object): cdef gsl_odeiv_step * s s = gsl_odeiv_step_alloc (T, dim) if s==NULL: - sage_free(y) + sig_free(y) raise MemoryError, "error setting up solver" @@ -498,7 +498,7 @@ class ode_solver(object): c = gsl_odeiv_control_standard_new(self.error_abs,self.error_rel,self.a,self.a_dydt) elif hasattr(self.scale_abs,'__len__'): if len(self.scale_abs)==dim: - scale_abs_array = sage_malloc(dim*sizeof(double)) + scale_abs_array = sig_malloc(dim*sizeof(double)) for i from 0 <=isage_malloc(self.dimension*(sizeof(double))) + self.vec = sig_malloc(self.dimension*(sizeof(double))) def set_seed(self, seed): """ @@ -282,7 +282,7 @@ cdef class SphericalDistribution(ProbabilityDistribution): def __dealloc__(self): if self.r != NULL: gsl_rng_free(self.r) - sage_free(self.vec) + sig_free(self.vec) def get_random_element(self): """ @@ -592,7 +592,7 @@ cdef class RealDistribution(ProbabilityDistribution): if self.r != NULL: gsl_rng_free(self.r) if self.parameters != NULL: - sage_free(self.parameters) + sig_free(self.parameters) def __str__(self): """ @@ -660,7 +660,7 @@ cdef class RealDistribution(ProbabilityDistribution): sage: T.set_distribution('pareto', [0, 1]) """ if self.parameters != NULL: - sage_free(self.parameters) + sig_free(self.parameters) if name == 'uniform': self.distribution_type = uniform @@ -669,7 +669,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(x) except Exception: raise TypeError("Uniform distribution requires parameters coercible to float") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = parameters[0] self.parameters[1] = parameters[1] elif name == 'gaussian': @@ -677,7 +677,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(parameters) except Exception: raise TypeError("gaussian distribution requires parameter sigma coercible to float") - self.parameters = sage_malloc(sizeof(double)) + self.parameters = sig_malloc(sizeof(double)) self.parameters[0] = float(parameters) self.distribution_type = gaussian elif name == 'pareto': @@ -687,7 +687,7 @@ cdef class RealDistribution(ProbabilityDistribution): map(float, parameters) except Exception: raise TypeError("parameters must be coercible to float") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = pareto @@ -697,7 +697,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(parameters) except Exception: raise TypeError("rayleigh distribution requires parameter sigma coercible to float") - self.parameters = sage_malloc(sizeof(double)) + self.parameters = sig_malloc(sizeof(double)) self.parameters[0] = float(parameters) self.distribution_type = rayleigh elif name == 'lognormal': @@ -708,7 +708,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(x) except Exception: raise TypeError("Lognormal distribution requires real parameters") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = lognormal @@ -717,7 +717,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(parameters) except Exception: raise TypeError("parameter to t distribution must be coercible to float") - self.parameters = sage_malloc(sizeof(double)) + self.parameters = sig_malloc(sizeof(double)) self.parameters[0] = float(parameters) self.distribution_type = t elif name == 'F': @@ -727,7 +727,7 @@ cdef class RealDistribution(ProbabilityDistribution): map(float, parameters) except Exception: raise TypeError("F-distribution requires real parameters") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = F @@ -736,7 +736,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(parameters) except Exception: raise TypeError("parameters to t distribution must be coercible to float") - self.parameters = sage_malloc(sizeof(double)) + self.parameters = sig_malloc(sizeof(double)) self.parameters[0] = float(parameters) self.distribution_type = chisquared elif name == 'exppow': @@ -747,7 +747,7 @@ cdef class RealDistribution(ProbabilityDistribution): float(x) except Exception: raise TypeError("exponential power distribution requires real parameters") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = exppow @@ -758,7 +758,7 @@ cdef class RealDistribution(ProbabilityDistribution): map(float, parameters) except Exception: raise TypeError("weibull distribution requires real parameters") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = weibull @@ -769,7 +769,7 @@ cdef class RealDistribution(ProbabilityDistribution): map(float, parameters) except Exception: raise TypeError("beta distribution requires real parameters") - self.parameters = sage_malloc(sizeof(double)*2) + self.parameters = sig_malloc(sizeof(double)*2) self.parameters[0] = float(parameters[0]) self.parameters[1] = float(parameters[1]) self.distribution_type = beta @@ -1043,7 +1043,7 @@ cdef class GeneralDiscreteDistribution(ProbabilityDistribution): n = len(P) cdef double *P_vec - P_vec = sage_malloc(n*(sizeof(double))) + P_vec = sig_malloc(n*(sizeof(double))) cdef int i for i in range(n): @@ -1054,7 +1054,7 @@ cdef class GeneralDiscreteDistribution(ProbabilityDistribution): self.dist = gsl_ran_discrete_preproc(n, P_vec) - sage_free(P_vec) + sig_free(P_vec) def set_seed(self, seed): """ diff --git a/src/sage/homology/chain_homotopy.py b/src/sage/homology/chain_homotopy.py index 5585410d77a..faa1f83d5e3 100644 --- a/src/sage/homology/chain_homotopy.py +++ b/src/sage/homology/chain_homotopy.py @@ -33,16 +33,16 @@ REFERENCES: -.. [M-AR] H. Molina-Abril and P. Réal, *Homology computation using spanning +.. [M-AR] \H. Molina-Abril and P. Réal, *Homology computation using spanning trees* in Progress in Pattern Recognition, Image Analysis, Computer Vision, and Applications, Lecture Notes in Computer Science, volume 5856, pp 272-278, Springer, Berlin (2009). -.. [PR] P. Pilarczyk and P. Réal, *Computation of cubical homology, +.. [PR] \P. Pilarczyk and P. Réal, *Computation of cubical homology, cohomology, and (co)homological operations via chain contraction*, Adv. Comput. Math. 41 (2015), pp 253--275. -.. [RM-A] P. Réal and H. Molina-Abril, *Cell AT-models for digital +.. [RM-A] \P. Réal and H. Molina-Abril, *Cell AT-models for digital volumes* in Torsello, Escolano, Brun (eds.), Graph-Based Representations in Pattern Recognition, Lecture Notes in Computer Science, volume 5534, pp. 314-3232, Springer, Berlin (2009). diff --git a/src/sage/homology/delta_complex.py b/src/sage/homology/delta_complex.py index 0aed7de1eed..e5643a5f832 100644 --- a/src/sage/homology/delta_complex.py +++ b/src/sage/homology/delta_complex.py @@ -52,7 +52,7 @@ .. [Hat] Allen Hatcher, "Algebraic Topology", Cambridge University Press (2002). -.. [EZ] S. Eilenberg and J. Zilber, "Semi-Simplicial Complexes and Singular +.. [EZ] \S. Eilenberg and J. Zilber, "Semi-Simplicial Complexes and Singular Homology", Ann. Math. (2) 51 (1950), 499-513. """ diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py index e86220a1003..a406a15abde 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/homology/examples.py @@ -400,7 +400,7 @@ def KleinBottle(): REFERENCES: - .. [Ce1994] D. P. Cervone, "Vertex-minimal simplicial immersions of the Klein + .. [Ce1994] \D. P. Cervone, "Vertex-minimal simplicial immersions of the Klein bottle in three-space", Geom. Ded. 50 (1994) 117-141, http://www.math.union.edu/~dpvc/papers/1993-03.kb/vmkb.pdf. """ @@ -521,7 +521,7 @@ def ComplexProjectivePlane(): REFERENCES: - .. [KB1983] W. Kühnel and T. F. Banchoff, "The 9-vertex complex + .. [KB1983] \W. Kühnel and T. F. Banchoff, "The 9-vertex complex projective plane", Math. Intelligencer 5 (1983), no. 3, 11-22. @@ -731,7 +731,7 @@ def RealProjectiveSpace(n): .. [Da2007] Basudeb Datta, "Minimal triangulations of manifolds", J. Indian Inst. Sci. 87 (2007), no. 4, 429-449. - .. [Ku1987] W. Kühnel, "Minimal triangulations of Kummer varieties", + .. [Ku1987] \W. Kühnel, "Minimal triangulations of Kummer varieties", Abh. Math. Sem. Univ. Hamburg 57 (1987), 7-20. .. [Lu2005] Frank H. Lutz, "Triangulated Manifolds with Few Vertices: @@ -877,11 +877,11 @@ def K3Surface(): REFERENCES: - .. [CK2001] M. Casella and W. Kühnel, "A triangulated K3 surface + .. [CK2001] \M. Casella and W. Kühnel, "A triangulated K3 surface with the minimum number of vertices", Topology 40 (2001), 753–772. - .. [SK2011] J. Spreer and W. Kühnel, "Combinatorial properties + .. [SK2011] \J. Spreer and W. Kühnel, "Combinatorial properties of the K3 surface: Simplicial blowups and slicings", Experimental Mathematics, Volume 20, Issue 2, 2011. @@ -1375,7 +1375,7 @@ def SumComplex(n, A): REFERENCES: - .. [LMR2010] N. Linial, R. Meshulam and M. Rosenthal, "Sum + .. [LMR2010] \N. Linial, R. Meshulam and M. Rosenthal, "Sum complexes -- a new family of hypertrees", Discrete & Computational Geometry, 2010, Volume 44, Number 3, Pages 622-636 diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py new file mode 100644 index 00000000000..db40f62cc32 --- /dev/null +++ b/src/sage/homology/hochschild_complex.py @@ -0,0 +1,457 @@ +""" +Hochschild Complexes +""" + +######################################################################## +# Copyright (C) 2014 Travis Scrimshaw +# +# 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.misc.cachefunc import cached_method +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.category_object import CategoryObject +from sage.categories.category_types import ChainComplexes +from sage.categories.tensor import tensor +from sage.combinat.free_module import CombinatorialFreeModule +from sage.homology.chain_complex import ChainComplex + +class HochschildComplex(UniqueRepresentation, CategoryObject): + r""" + The Hochschild complex. + + Let `A` be an algebra over a commutative ring `R` such + that `A` a projective `R`-module, and `M` an `A`-bimodule. + The *Hochschild complex* is the chain complex given by + + .. MATH:: + + C_n(A, M) := M \otimes A^{\otimes n} + + with the boundary operators given as follows. For fixed `n`, define + the face maps + + .. MATH:: + + f_{n,i}(m \otimes a_1 \otimes \cdots \otimes a_n) = \begin{cases} + m a_1 \otimes \cdots \otimes a_n & \text{if } i = 0, \\ + a_n m \otimes a_1 \otimes \cdots \otimes a_{n-1} + & \text{if } i = n, \\ + m \otimes a_1 \otimes \cdots \otimes a_i a_{i+1} \otimes + \cdots \otimes a_n & \text{otherwise.} + \end{cases} + + We define the boundary operators as + + .. MATH:: + + d_n = \sum_{i=0}^n (-1)^i f_{n,i}. + + The *Hochschild homology* of `A` is the homology of this complex. + Alternatively, the Hochschild homology can be described by + `HH_n(A, M) = \operatorname{Tor}_n^{A^e}(A, M)`, where + `A^e = A \otimes A^o` (`A^o` is the opposite algebra of `A`) + is the enveloping algebra of `A`. + + *Hochschild cohomology* is the homology of the dual complex and + can be described by `HH^n(A, M) = \operatorname{Ext}^n_{A^e}(A, M)`. + + Another perspective on Hochschild homology is that `f_{n,i}` + make the family `C_n(A, M)` a simplicial object in the + category of `R`-modules, and the degeneracy maps are + + .. MATH:: + + s_i(a_0 \otimes \cdots \otimes a_n) = + a_0 \otimes \cdots \otimes a_i \otimes 1 \otimes a_{i+1} + \otimes \cdots \otimes a_n + + The Hochschild homology can also be constructed as the homology + of this simplicial module. + + REFERENCES: + + - :wikipedia:`Hochschild_homology` + - https://ncatlab.org/nlab/show/Hochschild+cohomology + + .. [Redondo] Maria Julia Redondo. + *Hochschild cohomology: some methods for computations*. + http://inmabb.criba.edu.ar/gente/mredondo/crasp.pdf + """ + def __init__(self, A, M): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + + We skip the category test because the category containment + tests assumes ``H`` is an instance of :class:`Parent`:: + + sage: TestSuite(H).run(skip="_test_category") + sage: H.category() == ChainComplexes(QQ) + True + sage: H in ChainComplexes(QQ) # known bug + True + """ + self._A = A + self._M = M + CategoryObject.__init__(self, base=A.base_ring(), + category=ChainComplexes(A.base_ring())) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: T.rename("Trivial representation of SGA") + sage: SGA.hochschild_complex(T) + Hochschild complex of Symmetric group algebra of order 3 over Rational Field + with coefficients in Trivial representation of SGA + sage: T.rename() # reset the name + """ + return "Hochschild complex of {} with coefficients in {}".format(self._A, self._M) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: latex(H) + C_{\bullet}\left(..., ...\right) + """ + from sage.misc.latex import latex + return "C_{{\\bullet}}\\left({}, {}\\right)".format(latex(self._A), latex(self._M)) + + def algebra(self): + """ + Return the defining algebra of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.algebra() + Symmetric group algebra of order 3 over Rational Field + """ + return self._A + + def coefficients(self): + """ + Return the coefficients of ``self``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.coefficients() + Trivial representation of Standard permutations of 3 over Rational Field + """ + return self._M + + def free_module(self, d): + """ + Return the free module in degree ``d``. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.free_module(0) + Trivial representation of Standard permutations of 3 over Rational Field + sage: H.free_module(1) + Trivial representation of Standard permutations of 3 over Rational Field + # Symmetric group algebra of order 3 over Rational Field + sage: H.free_module(2) + Trivial representation of Standard permutations of 3 over Rational Field + # Symmetric group algebra of order 3 over Rational Field + # Symmetric group algebra of order 3 over Rational Field + """ + if d < 0: + raise ValueError("only defined for non-negative degree") + return tensor([self._M] + [self._A]*d) + + @cached_method + def trivial_module(self): + """ + Return the trivial module of ``self``. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H.trivial_module() + Free module generated by {} over Rational Field + """ + return CombinatorialFreeModule(self._A.base_ring(), []) + + def boundary(self, d): + """ + Return the boundary operator in degree ``d``. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: d1 = H.boundary(1) + sage: z = d1.domain().an_element(); z + 2*1 # 1 + 2*1 # x + 3*1 # y + sage: d1(z) + 0 + sage: d1.matrix() + [ 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 2 0 0 -2 0 0 0 0 0 0] + + sage: s = SymmetricFunctions(QQ).s() + sage: H = s.hochschild_complex(s) + sage: d1 = H.boundary(1) + sage: x = d1.domain().an_element(); x + 2*s[] # s[] + 2*s[] # s[1] + 3*s[] # s[2] + sage: d1(x) + 0 + sage: y = tensor([s.an_element(), s.an_element()]) + sage: d1(y) + 0 + sage: z = tensor([s[2,1] + s[3], s.an_element()]) + sage: d1(z) + 0 + + TESTS:: + + sage: def test_complex(H, n): + ....: phi = H.boundary(n) + ....: psi = H.boundary(n+1) + ....: comp = phi * psi + ....: zero = H.free_module(n-1).zero() + ....: return all(comp(b) == zero for b in H.free_module(n+1).basis()) + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: H = SGA.hochschild_complex(SGA) + sage: test_complex(H, 1) + True + sage: test_complex(H, 2) + True + sage: test_complex(H, 3) # long time + True + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: test_complex(H, 1) + True + sage: test_complex(H, 2) + True + sage: test_complex(H, 3) + True + """ + R = self._A.base_ring() + one = R.one() + if d == 0: + t = self.trivial_module() + zero = t.zero() + return self.free_module(0).module_morphism(lambda x: zero, codomain=t) + Fd = self.free_module(d-1) + Fd1 = self.free_module(d) + mone = -one + def on_basis(k): + p = self._M.monomial(k[0]) * self._A.monomial(k[1]) + ret = Fd._from_dict({(m,) + k[2:]: c for m,c in p}, remove_zeros=False) + for i in range(1, d): + p = self._A.monomial(k[i]) * self._A.monomial(k[i+1]) + ret += mone**i * Fd._from_dict({k[:i] + (m,) + k[i+2:]: c + for m,c in p}, remove_zeros=False) + p = self._A.monomial(k[-1]) * self._M.monomial(k[0]) + ret += mone**d * Fd._from_dict({(m,) + k[1:-1]: c for m,c in p}, + remove_zeros=False) + return ret + return Fd1.module_morphism(on_basis, codomain=Fd) + + def coboundary(self, d): + """ + Return the coboundary morphism of degree ``d``. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: del1 = H.coboundary(1) + sage: z = del1.domain().an_element(); z + 2 + 2*x + 3*y + sage: del1(z) + 0 + sage: del1.matrix() + [ 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 2] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 0 -2] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 0 0] + [ 0 0 0 0] + + TESTS:: + + sage: def test_complex(H, n): + ....: phi = H.coboundary(n) + ....: psi = H.coboundary(n+1) + ....: comp = psi * phi + ....: zero = H.free_module(n+1).zero() + ....: return all(comp(b) == zero for b in H.free_module(n-1).basis()) + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: H = SGA.hochschild_complex(SGA) + sage: test_complex(H, 1) + True + sage: test_complex(H, 2) + True + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: test_complex(H, 1) + True + sage: test_complex(H, 2) + True + sage: test_complex(H, 3) + True + """ + if self._A.category() is not self._A.category().FiniteDimensional(): + raise NotImplementedError("the algebra must be finite dimensional") + bdry = self.boundary(d) + dom = bdry.domain() + cod = bdry.codomain() + return cod.module_morphism(matrix=bdry.matrix().transpose(), codomain=dom) + + def homology(self, d): + """ + Return the ``d``-th homology group. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H.homology(0) + Vector space of dimension 3 over Rational Field + sage: H.homology(1) + Vector space of dimension 4 over Rational Field + sage: H.homology(2) + Vector space of dimension 6 over Rational Field + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.homology(0) + Vector space of dimension 1 over Rational Field + sage: H.homology(1) + Vector space of dimension 0 over Rational Field + sage: H.homology(2) + Vector space of dimension 0 over Rational Field + + When working over general rings (except `\ZZ`) and we can + construct a unitriangular basis for the image quotient, + we fallback to a slower implementation using (combinatorial) + free modules:: + + sage: R. = QQ[] + sage: SGA = SymmetricGroupAlgebra(R, 2) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.homology(1) + Free module generated by {} over Multivariate Polynomial Ring in x, y over Rational Field + """ + if self._A.category() is not self._A.category().FiniteDimensional(): + raise NotImplementedError("the algebra must be finite dimensional") + + maps = {d: self.boundary(d).matrix(), d+1: self.boundary(d+1).matrix()} + C = ChainComplex(maps, degree_of_differential=-1) + try: + return C.homology(d) + except NotImplementedError: + pass + # Fallback if we are not working over a field or \ZZ + bdry = self.boundary(d) + bdry1 = self.boundary(d+1) + ker = bdry.kernel() + im_retract = ker.submodule([ker.retract(b) for b in bdry1.image_basis()], + unitriangular=True) + return ker.quotient_module(im_retract) + + def cohomology(self, d): + """ + Return the ``d``-th cohomology group. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H.cohomology(0) + Vector space of dimension 3 over Rational Field + sage: H.cohomology(1) + Vector space of dimension 4 over Rational Field + sage: H.cohomology(2) + Vector space of dimension 6 over Rational Field + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.cohomology(0) + Vector space of dimension 1 over Rational Field + sage: H.cohomology(1) + Vector space of dimension 0 over Rational Field + sage: H.cohomology(2) + Vector space of dimension 0 over Rational Field + + When working over general rings (except `\ZZ`) and we can + construct a unitriangular basis for the image quotient, + we fallback to a slower implementation using (combinatorial) + free modules:: + + sage: R. = QQ[] + sage: SGA = SymmetricGroupAlgebra(R, 2) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H.cohomology(1) + Free module generated by {} over Multivariate Polynomial Ring in x, y over Rational Field + """ + if self._A.category() is not self._A.category().FiniteDimensional(): + raise NotImplementedError("the algebra must be finite dimensional") + + maps = {d+1: self.coboundary(d+1).matrix(), d: self.coboundary(d).matrix()} + C = ChainComplex(maps, degree_of_differential=1) + try: + return C.homology(d+1) + except NotImplementedError: + pass + # Fallback if we are not working over a field or \ZZ + cb = self.coboundary(d) + cb1 = self.coboundary(d+1) + ker = cb1.kernel() + im_retract = ker.submodule([ker.retract(b) for b in cb.image_basis()], + unitriangular=True) + return ker.quotient_module(im_retract) + diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index 00f87597bcd..3835e136cad 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -7,11 +7,11 @@ REFERENCES: -.. [G-DR03] R. González-Díaz and P. Réal, *Computation of cohomology +.. [G-DR03] \R. González-Díaz and P. Réal, *Computation of cohomology operations on finite simplicial complexes* in Homology, Homotopy and Applications 5 (2003), 83-93. -.. [G-DR99] R. González-Díaz and P. Réal, *A combinatorial method for +.. [G-DR99] \R. González-Díaz and P. Réal, *A combinatorial method for computing Steenrod squares* in J. Pure Appl. Algebra 139 (1999), 89-108. AUTHORS: @@ -144,7 +144,7 @@ class HomologyVectorSpaceWithBasis(CombinatorialFreeModule): sage: b.cup_product(b) h^{2,0} """ - def __init__(self, base_ring, cell_complex, cohomology=False, cat=None): + def __init__(self, base_ring, cell_complex, cohomology=False, category=None): """ Initialize ``self``. @@ -170,7 +170,7 @@ def __init__(self, base_ring, cell_complex, cohomology=False, cat=None): # We only need the rank of M in each degree, and since # we're working over a field, we don't need to dualize M # if working with cohomology. - cat = Modules(base_ring).WithBasis().Graded().or_subcategory(cat) + category = Modules(base_ring).WithBasis().Graded().FiniteDimensional().or_subcategory(category) self._contraction = phi self._complex = cell_complex self._cohomology = cohomology @@ -178,7 +178,7 @@ def __init__(self, base_ring, cell_complex, cohomology=False, cat=None): for deg in range(cell_complex.dimension()+1)} indices = [(deg, i) for deg in self._graded_indices for i in self._graded_indices[deg]] - CombinatorialFreeModule.__init__(self, base_ring, indices, category=cat) + CombinatorialFreeModule.__init__(self, base_ring, indices, category=category) def basis(self, d=None): """ @@ -435,7 +435,7 @@ def __init__(self, base_ring, cell_complex): sage: H = RP2.cohomology_ring(GF(5)) sage: TestSuite(H).run() """ - cat = Algebras(base_ring).WithBasis().Graded() + cat = Algebras(base_ring).WithBasis().Graded().FiniteDimensional() HomologyVectorSpaceWithBasis.__init__(self, base_ring, cell_complex, True, cat) def _repr_(self): diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index d1b179334d6..7b98d85a53d 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -3277,11 +3277,11 @@ def _cubical_(self): REFERENCES: - .. [BP2000] V. M. Bukhshtaber and T. E. Panov, "Moment-angle complexes + .. [BP2000] \V. M. Bukhshtaber and T. E. Panov, "Moment-angle complexes and combinatorics of simplicial manifolds," *Uspekhi Mat. Nauk* 55 (2000), 171--172. - .. [SS1992] M. A. Shtan'ko and and M. I. Shtogrin, "Embedding cubic + .. [SS1992] \M. A. Shtan'ko and and M. I. Shtogrin, "Embedding cubic manifolds and complexes into a cubic lattice", *Uspekhi Mat. Nauk* 47 (1992), 219-220. diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index 01913fdcee2..ada93e30d28 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -190,9 +190,9 @@ def _make_cmd(self, B1, B2, kwds): def _run_ecm(self, cmd, n): """ Run ECM and return output as string. - + INPUT: - + - ``cmd`` -- list of strings. The command. - ``n`` -- integer suitable for ECM. No argument checking is @@ -213,7 +213,7 @@ def _run_ecm(self, cmd, n): if err != '': raise ValueError(err) return out - + def __call__(self, n): """ Call syntax. @@ -221,7 +221,7 @@ def __call__(self, n): INPUT: - ``n`` -- integer. - + OUTPUT: String. The ECM output. @@ -249,7 +249,7 @@ def interact(self): print "Press control-C to exit." os.system(self._cmd) - # Recommended settings from + # Recommended settings from # http://www.mersennewiki.org/index.php/Elliptic_Curve_Method _recommended_B1_list = {15: 2000, 20: 11000, @@ -262,7 +262,7 @@ def interact(self): 55: 110000000, 60: 260000000, 65: 850000000, - 70: 2900000000,} + 70: 2900000000} def _B1_table_value(self, factor_digits, min=15, max=70): """ @@ -283,12 +283,12 @@ def _B1_table_value(self, factor_digits, min=15, max=70): sage: ecm._B1_table_value(33) 35 """ - if factor_digits < min: + if factor_digits < min: factor_digits = min - if factor_digits > max: + if factor_digits > max: raise ValueError('too many digits') step = 5 - return ((factor_digits+step-1) // step) * step + return ((factor_digits + step - 1) // step) * step def recommended_B1(self, factor_digits): r""" @@ -330,7 +330,7 @@ def _parse_output(self, n, out): - ``n`` -- integer. The ECM input number. - ``out`` -- string. The stdout from the ECM invocation. - + OUTPUT: List of pairs ``(integer, bool)`` consisting of factors of the @@ -370,7 +370,7 @@ def _parse_output(self, n, out): sage: from sage.interfaces.ecm import TEST_ECM_OUTPUT_3, TEST_ECM_OUTPUT_4 sage: n3 = 66955751844124594814248420514215108438425124740949701470891 sage: ecm._parse_output(n3, TEST_ECM_OUTPUT_3) - [(197002597249, True), + [(197002597249, True), (339872432034468861533158743041639097889948066859, False)] sage: ecm._parse_output(n3, TEST_ECM_OUTPUT_4) [(265748496095531068869578877937, False), @@ -385,8 +385,8 @@ def _parse_output(self, n, out): m = self._parse_status_re.match(line) if m is not None: group = m.groups() - self._last_params = {'B1' : group[0], 'B2' : group[1], - 'poly' : group[2], 'sigma' : group[3]} + self._last_params = {'B1': group[0], 'B2': group[1], + 'poly': group[2], 'sigma': group[3]} continue m = self._found_input_re.match(line) if m is not None: @@ -404,7 +404,7 @@ def _parse_output(self, n, out): primality = m.group('primality') assert primality in ['Probable prime', 'Composite'] result += [(ZZ(cofactor), primality == 'Probable prime')] - #assert len(result) == 2 + # assert len(result) == 2 return result raise ValueError('failed to parse ECM output') @@ -417,7 +417,7 @@ def one_curve(self, n, factor_digits=None, B1=2000, algorithm="ECM", **kwds): curves to factor `n`. INPUT: - + - ``n`` -- a positive integer - ``factor_digits`` -- integer. Decimal digits estimate of the @@ -490,8 +490,8 @@ def _find_factor(self, n, factor_digits, B1, **kwds): sage: f = ECM() sage: n = 508021860739623467191080372196682785441177798407961 - sage: f._find_factor(n, None, 2000) - [(79792266297612017, True), + sage: sorted(f._find_factor(n, None, 2000)) + [(79792266297612017, True), (6366805760909027985741435139224233, True)] """ n = self._validate(n) @@ -513,8 +513,8 @@ def find_factor(self, n, factor_digits=None, B1=2000, **kwds): `n`. INPUT: - - - ``n`` -- a positive integer, + + - ``n`` -- a positive integer, - ``factor_digits`` -- integer or ``None`` (default). Decimal digits estimate of the wanted factor. @@ -530,7 +530,7 @@ def find_factor(self, n, factor_digits=None, B1=2000, **kwds): the factor, this is the best algorithm to find a factor. - .. NOTE: + .. NOTE:: ECM is not a good primality test. Not finding a factorization is only weak evidence for `n` being @@ -544,7 +544,7 @@ def find_factor(self, n, factor_digits=None, B1=2000, **kwds): sage: f.find_factor(n) [79792266297612017, 6366805760909027985741435139224233] - Note that the input number can't have more than 4095 digits:: + Note that the input number cannot have more than 4095 digits:: sage: f=2^2^14+1 sage: ecm.find_factor(f) @@ -562,7 +562,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): Combines GMP-ECM with a primality test, see :meth:`~sage.rings.integer.Integer.is_prime`. The primality test is provable or probabilistic depending on the `proof` - flag. + flag. Moreover, for small `n` PARI is used directly. @@ -582,7 +582,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): - ``B1`` -- initial lower bound, defaults to 2000 (15 digit factors). Used if ``factor_digits`` is not specified. - + - ``proof`` -- boolean (default: ``False``). Whether to prove that the factors are prime. @@ -596,7 +596,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): .. NOTE:: Trial division should typically be performed, but this is - not implemented (yet) in this method. + not implemented (yet) in this method. If you suspect that n is the product of two similarly-sized primes, other methods (such as a quadratic @@ -621,7 +621,7 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): probable_prime_factors = [] # output prime factors while len(factors) > 0: n = factors.pop() - + # Step 0: Primality test if n.is_prime(proof=proof): probable_prime_factors.append(n) @@ -629,10 +629,10 @@ def factor(self, n, factor_digits=None, B1=2000, proof=False, **kwds): # Step 1: Use PARI directly for small primes if n.ndigits() < 15: - for p,e in n.factor(algorithm='pari'): - probable_prime_factors.extend([p]*e) + for p, e in n.factor(algorithm='pari'): + probable_prime_factors.extend([p] * e) continue - + # Step 2: Deal with small factors efficiently # Step 2+1/3: Determine if N is a perfect power if n.is_perfect_power(): @@ -661,7 +661,7 @@ def get_last_params(self): will return the parameters that yielded the factorization. OUTPUT: - + A dictionary containing the parameters for the most recent factorization. @@ -679,7 +679,7 @@ def time(self, n, factor_digits, verbose=False): Print a runtime estimate. BUGS: - + This method should really return something and not just print stuff on the screen. @@ -876,8 +876,3 @@ def _validate(self, n): Found composite factor of 30 digits: 265748496095531068869578877937 Probable prime cofactor 251951573867253012259144010843 has 30 digits """ - - - - - diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 756f7017777..36d380b8368 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -130,35 +130,27 @@ def __init__(self, name, prompt, command=None, server=None, terminal_echo=True): Interface.__init__(self, name) + + # Read environment variables + env_name = 'SAGE_%s_{}'%self.name().upper() + import os + if server is None: + server = os.getenv(env_name.format('SERVER')) + if server_tmpdir is None: + server_tmpdir = os.getenv(env_name.format('TMPDIR')) + if command is None: + command = os.getenv(env_name.format('COMMAND')) + if script_subdirectory is None: + script_subdirectory = os.getenv(env_name.format('SCRIPT_SUBDIRECTORY')) self.__is_remote = False self.__remote_cleaner = remote_cleaner - if command is None: - command = name - if server is not None: - if ulimit: - command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) - else: - command = "sage-native-execute ssh -t %s '%s'"%(server, command) - self.__is_remote = True -# eval_using_file_cutoff = 0 # don't allow this! - if verbose_start: - print "Using remote server" - print command - self._server = server - if server_tmpdir is None: - # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder - print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server - self.__remote_tmpdir = "/tmp/" - else: - self.__remote_tmpdir = server_tmpdir - else: - self._server = None - self.__do_cleaner = do_cleaner + self._expect = None self._eval_using_file_cutoff = eval_using_file_cutoff - self.__command = command + self.__verbose_start = verbose_start + self.set_server_and_command(server, command, server_tmpdir, ulimit) + self.__do_cleaner = do_cleaner self._prompt = prompt self._restart_on_ctrlc = restart_on_ctrlc - self.__verbose_start = verbose_start if path is not None: self.__path = os.path.abspath(path) elif script_subdirectory is None: @@ -169,7 +161,6 @@ def __init__(self, name, prompt, command=None, server=None, raise EnvironmentError("path %r does not exist" % self.__path) self.__initialized = False self.__seq = -1 - self._expect = None self._session_number = 0 self.__init_code = init_code @@ -185,6 +176,70 @@ def __init__(self, name, prompt, command=None, server=None, self._available_vars = [] self._terminal_echo = terminal_echo + def set_server_and_command(self, server=None, command=None, server_tmpdir=None, ulimit=None): + """ + Changes the server and the command to use for this interface. + This raises a Runtime error if the interface is already started. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote', command = 'mymagma') # indirect doctest + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() + 'remote' + sage: magma.command() + "sage-native-execute ssh -t remote 'mymagma'" + """ + if self._expect: + raise RuntimeError("interface has already started") + if command is None: + command = self.name() + self._server = server + if server is not None: + if ulimit: + command = "sage-native-execute ssh -t %s 'ulimit %s; %s'"%(server, ulimit, command) + else: + command = "sage-native-execute ssh -t %s '%s'"%(server, command) + self.__is_remote = True + self._eval_using_file_cutoff = 0 # don't allow this! + if self.__verbose_start: + print "Using remote server" + print command + if server_tmpdir is None: + # TO DO: Why default to /tmp/? Might be better to use the expect process itself to get a tmp folder + print "No remote temporary directory (option server_tmpdir) specified, using /tmp/ on "+server + self.__remote_tmpdir = "/tmp/" + else: + self.__remote_tmpdir = server_tmpdir + else: + self.__is_remote = False + self.__command = command + + def server(self): + """ + Returns the server used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(server = 'remote') + No remote temporary directory (option server_tmpdir) specified, using /tmp/ on remote + sage: magma.server() # indirect doctest + 'remote' + """ + return self._server + + def command(self): + """ + Returns the command used in this interface. + + EXAMPLES:: + + sage: magma.set_server_and_command(command = 'magma-2.19') + sage: magma.command() # indirect doctest + 'magma-2.19' + """ + return self.__command + def _get(self, wait=0.1, alternate_prompt=None): if self._expect is None: self._start() @@ -506,7 +561,7 @@ def quit(self, verbose=False, timeout=None): sage: a = maxima('y') sage: maxima.quit(verbose=True) - Exiting Maxima with PID ... running .../local/bin/maxima ... + Exiting Maxima with PID ... running .../bin/maxima ... sage: a._check_valid() Traceback (most recent call last): ... @@ -1051,8 +1106,8 @@ def _expect_expr(self, expr=None, timeout=None): '...abc;\r\n[1] ' We test interrupting ``_expect_expr`` using the GP interface, - see #6661. Unfortunately, this test doesn't work reliably using - Singular, see #9163 and the follow-up #10476. + see :trac:`6661`. Unfortunately, this test doesn't work reliably using + Singular, see :trac:`9163` and the follow-up :trac:`10476`. The ``gp.eval('0')`` in this test makes sure that ``gp`` is running, so a timeout of 1 second should be sufficient. :: @@ -1423,13 +1478,13 @@ def __enter__(self): return self.interface def __exit__(self, typ, value, tb): - """ + r""" EXAMPLE:: sage: from sage.interfaces.expect import StdOutContext sage: with StdOutContext(gap): ....: gap('1+1') - $sage... + \$sage... """ if self.silent: return diff --git a/src/sage/interfaces/frobby.py b/src/sage/interfaces/frobby.py index a1f419171c5..1ff820b70de 100644 --- a/src/sage/interfaces/frobby.py +++ b/src/sage/interfaces/frobby.py @@ -260,7 +260,7 @@ def irreducible_decomposition(self, monomial_ideal): We now try the special case of the zero ideal in different rings. We should also try PolynomialRing(QQ, names=[]), but it has a bug - which makes that impossible (see trac ticket 3028). :: + which makes that impossible (see :trac:`3028`). :: sage: rings = [ZZ['x'], CC['x,y']] # optional - frobby sage: allOK = True # optional - frobby diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 1f446aa1eb2..198fe8457ca 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -716,7 +716,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if sage: gap.eval('quit;') '' sage: a = gap(3) - ** Gap crashed or quit executing '$sage...:=3;;' ** + ** Gap crashed or quit executing '\$sage...:=3;;' ** Restarting Gap and trying again sage: a 3 @@ -858,7 +858,7 @@ def version(self): EXAMPLES:: sage: print gap.version() - 4.7... + 4.8... """ return self.eval('VERSION')[1:-1] @@ -1162,25 +1162,29 @@ def __reduce__(self): return reduce_load_GAP, tuple([]) def _next_var_name(self): - """ + r""" Returns the next unused variable name. + Note that names starting with dollar signs are valid GAP + identifiers, but need to be escaped with a backslash starting + with GAP-4.8. + EXAMPLES:: sage: g = Gap() sage: g._next_var_name() - '$sage1' + '\\$sage1' sage: g(2)^2 4 sage: g._next_var_name() - '$sage...' + '\\$sage...' """ if len(self._available_vars) != 0: v = self._available_vars[0] del self._available_vars[0] return v self.__seq += 1 - return '$sage%s'%self.__seq + return r'\$sage%s'%self.__seq def _start(self): """ @@ -1324,7 +1328,7 @@ def help(self, s, pager=True): else: tmp_to_use = self._local_tmpfile() self.eval('SetGAPDocTextTheme("none")') - self.eval('$SAGE.tempfile := "%s";'%tmp_to_use) + self.eval(r'\$SAGE.tempfile := "%s";'%tmp_to_use) line = Expect.eval(self, "? %s"%s) Expect.eval(self, "? 1") match = re.search("Page from (\d+)", line) @@ -1384,7 +1388,7 @@ def _pre_interact(self): sage: gap._pre_interact() sage: gap._post_interact() """ - self._eval_line("$SAGE.StartInteract();") + self._eval_line(r'\$SAGE.StartInteract();') def _post_interact(self): """ @@ -1393,7 +1397,7 @@ def _post_interact(self): sage: gap._pre_interact() sage: gap._post_interact() """ - self._eval_line("$SAGE.StopInteract();") + self._eval_line(r'\$SAGE.StopInteract();') def _eval_line_using_file(self, line): i = line.find(':=') @@ -1641,7 +1645,7 @@ def _tab_completion(self): """ from sage.misc.misc import uniq P = self.parent() - v = P.eval('$SAGE.OperationsAdmittingFirstArgument(%s)'%self.name()) + v = P.eval(r'\$SAGE.OperationsAdmittingFirstArgument(%s)'%self.name()) v = v.replace('Tester(','').replace('Setter(','').replace(')','').replace('\n', '') v = v.split(',') v = [ oper.split('"')[1] for oper in v ] diff --git a/src/sage/interfaces/gap3.py b/src/sage/interfaces/gap3.py index 587ca766feb..ec58f35afb6 100644 --- a/src/sage/interfaces/gap3.py +++ b/src/sage/interfaces/gap3.py @@ -6,34 +6,28 @@ AUTHORS: - Franco Saliola (February 2010) +- Christian Stump (March 2016) .. WARNING:: - GAP3 is not distrubuted with Sage. You need to install it - separately; see the section `Obtaining GAP3`_. + The experimental package for GAP3 is Jean Michel's pre-packaged GAP3, + which is a minimal GAP3 distribution containing packages that have + no equivalent in GAP4, see :trac:20107 and also + + https://webusers.imj-prg.fr/~jean.michel/gap3/ Obtaining GAP3 -------------- -The GAP3 interface will only work if GAP3 is installed on your computer. -Here are some ways to obtain GAP3: - -- There is an optional Sage package providing GAP3 pre-packaged - with several GAP3 packages: - - http://trac.sagemath.org/sage_trac/ticket/8906 +Instead of installing the experimental GAP3 package, one can as well install +by hand either of the following two versions of GAP3: - Frank Luebeck maintains a GAP3 Linux executable, optimized for i686 and statically linked for jobs of 2 GByte or more: http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3 -- Jean Michel maintains a version of GAP3 pre-packaged with - CHEVIE and VKCURVE. It can be obtained here: - - http://people.math.jussieu.fr/~jmichel/chevie/chevie.html - -- Finally, you can download GAP3 from the GAP website below. Since GAP3 +- or you can download GAP3 from the GAP website below. Since GAP3 is no longer supported, it may not be easy to install this version. http://www.gap-system.org/Gap3/Download3/download.html @@ -43,7 +37,7 @@ .. WARNING:: - There is a bug in the pexpect module (see trac ticket #8471) that + There is a bug in the pexpect module (see :trac:`8471`) that prevents the following from working correctly. For now, just make sure that ``gap3`` is in your ``PATH``. @@ -171,15 +165,14 @@ ``RequirePackage`` method (note that one can instead use the ``load_package`` method):: - sage: gap3.RequirePackage('"chevie"') #optional - gap3 chevie - W... to the CHEVIE package, ... + sage: gap3.RequirePackage('"chevie"') #optional - gap3 Examples -------- Load a GAP3 package:: - sage: gap3.load_package("chevie") #optional - gap3 chevie + sage: gap3.load_package("chevie") #optional - gap3 sage: gap3.version() # random #optional - gap3 'lib: v3r4p4 1997/04/18, src: v3r4p0 1994/07/10, sys: usg gcc ansi' @@ -568,34 +561,27 @@ def _install_hints(self): Your attempt to start GAP3 failed, either because you do not have have GAP3 installed, or because it is not configured correctly. - - If you do not have GAP3 installed, then you must download ... + - If you do not have GAP3 installed, then you must either... sage: print gap3._install_hints() Your attempt to start GAP3 failed, either because you do not have have GAP3 installed, or because it is not configured correctly. - - If you do not have GAP3 installed, then you must download ... + - If you do not have GAP3 installed, then you must either... """ return r""" Your attempt to start GAP3 failed, either because you do not have have GAP3 installed, or because it is not configured correctly. - - If you do not have GAP3 installed, then you must download and - install it yourself because it is not distrubuted with Sage. - Here are some ways to obtain GAP3: - - - There is an optional Sage package providing GAP3 pre-packaged - with several GAP3 packages: - http://trac.sagemath.org/sage_trac/ticket/8906 + - If you do not have GAP3 installed, then you must either install + the optional package, see :trac:20107, or you download and + install it yourself. + Here are two other ways to obtain GAP3: - Frank Luebeck maintains a GAP3 Linux executable, optimized for i686 and statically linked for jobs of 2 GByte or more: http://www.math.rwth-aachen.de/~Frank.Luebeck/gap/GAP3 - - Jean Michel maintains a version of GAP3 pre-packaged with - CHEVIE and VKCURVE. It can be obtained here: - http://people.math.jussieu.fr/~jmichel/chevie/chevie.html - - Finally, you can download GAP3 from the GAP website below. Since GAP3 is no longer an officially supported distribution of GAP, it may not be easy to install this version. diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index ea918672888..1a305c1a90c 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -985,7 +985,7 @@ def _matrix_(self, R): matrix[[0,1-y,1-y^2,1-y^3],[x-1,x-y,x-y^2,x-y^3],[x^2-1,x^2-y,x^2-y^2,x^2-y^3],[x^3-1,x^3-y,x^3-y^2,x^3-y^3]] sage: M.eigenvals() # random; optional - giac 0,0,(x^3+x^2+x-y^3-y^2-y+sqrt(x^6+2*x^5+3*x^4-14*x^3*y^3+2*x^3*y^2+2*x^3*y+6*x^3+2*x^2*y^3-14*x^2*y^2+2*x^2*y+5*x^2+2*x*y^3+2*x*y^2-14*x*y+4*x+y^6+2*y^5+3*y^4+6*y^3+5*y^2+4*y-12))/2,(x^3+x^2+x-y^3-y^2-y-sqrt(x^6+2*x^5+3*x^4-14*x^3*y^3+2*x^3*y^2+2*x^3*y+6*x^3+2*x^2*y^3-14*x^2*y^2+2*x^2*y+5*x^2+2*x*y^3+2*x*y^2-14*x*y+4*x+y^6+2*y^5+3*y^4+6*y^3+5*y^2+4*y-12))/2 - sage: Z=matrix(M,R);Z # optional - giac + sage: Z=matrix(R,M);Z # optional - giac [ 0 -y + 1 -y^2 + 1 -y^3 + 1] [ x - 1 x - y -y^2 + x -y^3 + x] [ x^2 - 1 x^2 - y x^2 - y^2 -y^3 + x^2] @@ -1061,9 +1061,7 @@ def integral(self, var='x', min=None, max=None): :: sage: f = giac('exp(x^2)').integral('x',0,1) ; f # optional - giac - integra... - sage: f.evalf(10) # optional - giac - 1.462651746 + 1.46265174... sage: x,y=giac('x'),giac('y');integrate(cos(x+y),'x=0..pi').simplify() # optional - giac -2*sin(y) """ diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index ebdd76e53cc..da041f83582 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -230,7 +230,7 @@ EXTCODE_DIR = None -def extcode_dir(): +def extcode_dir(iface = None): """ Return directory that contains all the Magma extcode. This is put in a writable directory owned by the user, since when attached, @@ -243,10 +243,25 @@ def extcode_dir(): """ global EXTCODE_DIR if not EXTCODE_DIR: - import shutil - tmp = sage.misc.temporary_file.tmp_dir() - shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') - EXTCODE_DIR = "%s/data/"%tmp + if iface is None or iface._server is None: + import shutil + tmp = sage.misc.temporary_file.tmp_dir() + shutil.copytree('%s/magma/'%SAGE_EXTCODE, tmp + '/data') + EXTCODE_DIR = "%s/data/"%tmp + else: + import os + tmp = iface._remote_tmpdir() + command = 'scp -q -r "%s/magma/" "%s:%s/data" 1>&2 2>/dev/null'%(SAGE_EXTCODE,iface._server,tmp) + try: + ans = os.system(command) + EXTCODE_DIR = "%s/data/"%tmp + if ans != 0: + raise IOError + except (OSError,IOError): + out_str = 'Tried to copy the file structure in "%s/magma/" to "%s:%s/data" and failed (possibly because scp is not installed in the system).\nFor the remote Magma to work you should populate the remote directory by some other method, or install scp in the system and retry.'%(SAGE_EXTCODE, iface._server, tmp) + from warnings import warn, resetwarnings + resetwarnings() + warn(out_str) return EXTCODE_DIR @@ -267,6 +282,9 @@ class Magma(ExtraTabCompletion, Expect): ``magma_free`` command instead, which uses the free demo web interface to Magma. + If you have ssh access to a remote installation of Magma, you can + also set the ``server`` parameter to use it. + EXAMPLES: You must use nvals = 0 to call a function that doesn't return @@ -280,9 +298,9 @@ class Magma(ExtraTabCompletion, Expect): '1.1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000' sage: magma.SetDefaultRealFieldPrecision(30, nvals=0) # optional - magma """ - def __init__(self, maxread=None, script_subdirectory=None, + def __init__(self, script_subdirectory=None, logfile=None, server=None, server_tmpdir=None, - user_config=False, seed=None): + user_config=False, seed=None, command=None): """ INPUT: @@ -293,24 +311,35 @@ def __init__(self, maxread=None, script_subdirectory=None, - ``server`` - address of remote server + - ``server_tmpdir`` - temporary directory to use in remote server + - ``user_config`` - if True, then local user configuration files will be read by Magma. If False (the default), then Magma is started with the -n option which suppresses user configuration files. + - ``seed`` - Seed to use in the random number generator. + + - ``command`` - (Default: 'magma') The command to execute to start Magma. EXAMPLES:: sage: Magma(logfile=tmp_filename()) Magma """ - # If the -b argument is given to Magma, the opening banner and all other - # introductory messages are suppressed. The final "total time" message is - # also suppressed. - #command = 'sage-native-execute magma -b ' - command = 'sage-native-execute magma' + if command is None: + import os + command = os.getenv('SAGE_MAGMA_COMMAND') or 'magma' + if not user_config: command += ' -n' + + # Obtain the parameters from the environment, to allow the magma = Magma() phrase + # to work with non-default parameters. + if seed is None: + import os + seed = os.getenv('SAGE_MAGMA_SEED') + Expect.__init__(self, name = "magma", prompt = ">>SAGE>>", @@ -567,7 +596,7 @@ def _start(self): self.expect().expect(PROMPT) self.expect().expect(PROMPT) self.expect().expect(PROMPT) - self.attach_spec(extcode_dir() + '/spec') + self.attach_spec(extcode_dir(self) + '/spec') # set random seed self.set_seed(self._seed) @@ -1380,7 +1409,8 @@ def version(self): sage: magma.version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - return magma_version() + t = tuple([int(n) for n in self.eval('GetVersion()').split()]) + return t, 'V%s.%s-%s'%t def help(self, s): """ @@ -2757,8 +2787,9 @@ def magma_version(): sage: magma_version() # random, optional - magma ((2, 14, 9), 'V2.14-9') """ - t = tuple([int(n) for n in magma.eval('GetVersion()').split()]) - return t, 'V%s.%s-%s'%t + from sage.misc.superseded import deprecation + deprecation(20388, 'This function has been deprecated. Use magma.version() instead.') + return magma.version() class MagmaGBLogPrettyPrinter: """ diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index c523450bbea..1181c2c576d 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -31,7 +31,7 @@ sage: mobj2.parent() # optional - mathematica Mathematica sage: sobj = mobj2.sage(); sobj # optional - mathematica - (x - 1)*(x + 1) + (x + 1)*(x - 1) sage: sobj.parent() # optional - mathematica Symbolic Ring @@ -232,11 +232,7 @@ sage: F[4] # optional - mathematica {541, 1} -We can also load the ECM package and factoring using it:: - - sage: _ = mathematica.eval("<
    \frac{\sin (x \cos (y))}{\sqrt{1-x^2}}
    + """ from sage.repl.rich_output import get_display_manager dm = get_display_manager() - dm.display_immediately(self, ImageSize=ImageSize) - + dm.display_immediately(self, ImageSize=ImageSize) + def str(self): return str(self) @@ -941,20 +936,55 @@ def __cmp__(self, other): return -1 # everything is supposed to be comparable in Python, so we define # the comparison thus when no comparable in interfaced system. - def N(self, *args): - """ + def N(self, precision=None): + r""" + Numerical approximation by calling Mathematica's `N[]` + + Calling Mathematica's `N[]` function, with optional precision in decimal digits. + Unlike Sage's `n()`, `N()` can be applied to symbolic Mathematica objects. + + A workaround for :trac:`18888` backtick issue, stripped away by `get()`, + is included. + + .. note:: + + The base class way up the hierarchy defines an `N` (modeled + after Mathematica's) which overwrites the Mathematica one, + and doesn't work at all. We restore it here. + EXAMPLES:: - sage: mathematica('Pi').N(10) # optional -- mathematica - 3.1415926536 - sage: mathematica('Pi').N(50) # optional -- mathematica - 3.14159265358979323846264338327950288419716939937511 + sage: mathematica('Pi/2').N(10) # optional -- mathematica + 1.570796327 + sage: mathematica('Pi').N(50) # optional -- mathematica + 3.1415926535897932384626433832795028841971693993751 + sage: mathematica('Pi*x^2-1/2').N() # optional -- mathematica + 2 + -0.5 + 3.14159 x """ - # The base class way up the hierarchy defines an "N" (modeled - # after Mathematica's!) which overwrites the Mathematica one, - # and doesn't work at all. We restore it here. - return self.parent().N(self, *args) + P = self.parent() + if precision is None: + return P.eval('N[%s]'%self.name()) + return P.eval('N[%s,%s]'%(self.name(),precision)) + + def n(self, *args, **kwargs): + r""" + Numerical approximation by converting to Sage object first + + Convert the object into a Sage object and return its numerical + approximation. See documentation of the function + :func:`sage.misc.functional.n` for details. + EXAMPLES:: + + sage: mathematica('Pi').n(10) # optional -- mathematica + 3.1 + sage: mathematica('Pi').n() # optional -- mathematica + 3.14159265358979 + sage: mathematica('Pi').n(digits=10) # optional -- mathematica + 3.141592654 + """ + return self._sage_().n(*args, **kwargs) class MathematicaFunction(ExpectFunction): def _sage_doc_(self): diff --git a/src/sage/interfaces/mwrank.py b/src/sage/interfaces/mwrank.py index 2bb30463164..6cf76f2691c 100644 --- a/src/sage/interfaces/mwrank.py +++ b/src/sage/interfaces/mwrank.py @@ -241,7 +241,7 @@ def __call__(self, cmd): TESTS: - Invalid input raises an ValueError (see #10108); this includes + Invalid input raises an ValueError (see :trac:`10108`); this includes syntactically valid input which defines a singular curve:: sage: mwrank(10) diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index b8b26de2d9e..3270dffd057 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -316,7 +316,7 @@ def clear(self, var): Clear the variable named var. Note that the exact format of the NameError for a cleared variable - is slightly platform dependent, see trac #10539. + is slightly platform dependent, see :trac:`10539`. EXAMPLES:: @@ -346,7 +346,7 @@ def console(self): sage: sage0.console() #not tested ---------------------------------------------------------------------- - | SageMath Version ..., Release Date: ... | + | SageMath version ..., Release Date: ... | | Type notebook() for the GUI, and license() for information. | ---------------------------------------------------------------------- ... @@ -358,7 +358,7 @@ def version(self): EXAMPLES:: sage: sage0.version() - 'SageMath Version ..., Release Date: ...' + 'SageMath version ..., Release Date: ...' sage: sage0.version() == version() True """ @@ -525,7 +525,7 @@ def sage0_console(): sage: sage0_console() #not tested ---------------------------------------------------------------------- - | SageMath Version ..., Release Date: ... | + | SageMath version ..., Release Date: ... | | Type notebook() for the GUI, and license() for information. | ---------------------------------------------------------------------- ... diff --git a/src/sage/interfaces/tachyon.py b/src/sage/interfaces/tachyon.py index 0286eda3942..af68c9132cc 100644 --- a/src/sage/interfaces/tachyon.py +++ b/src/sage/interfaces/tachyon.py @@ -19,7 +19,6 @@ from sage.misc.pager import pager from sage.misc.temporary_file import tmp_filename -from sage.misc.sagedoc import format from sage.structure.sage_object import SageObject @@ -778,6 +777,7 @@ def help(self, use_pager=True): parameters so that you can position the image map on the object properly. """ + from sage.misc.sagedoc import format f = format(s) f = f.replace('{ ','').replace('}','').replace('{','') if use_pager == True: diff --git a/src/sage/interfaces/tides.py b/src/sage/interfaces/tides.py index 47530018716..c00d9f34980 100644 --- a/src/sage/interfaces/tides.py +++ b/src/sage/interfaces/tides.py @@ -33,7 +33,7 @@ REFERENCES: -.. [ALG924] A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM Transactions on Mathematical Software*, *39* (1), 1-28. +.. [ALG924] \A. Abad, R. Barrio, F. Blesa, M. Rodriguez. Algorithm 924. *ACM Transactions on Mathematical Software*, *39* (1), 1-28. .. [TI] A. Abad, R. Barrio, F. Blesa, M. Rodriguez. diff --git a/src/sage/knots/__init__.py b/src/sage/knots/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/knots/all.py b/src/sage/knots/all.py new file mode 100644 index 00000000000..d4047819d7b --- /dev/null +++ b/src/sage/knots/all.py @@ -0,0 +1,3 @@ +from sage.knots.knot import Knot +from sage.knots.link import Link + diff --git a/src/sage/knots/knot.py b/src/sage/knots/knot.py new file mode 100644 index 00000000000..d9698ff7e17 --- /dev/null +++ b/src/sage/knots/knot.py @@ -0,0 +1,228 @@ +r""" +Knots + +AUTHORS: + +- Miguel Angel Marco Buzunariz +- Amit Jamadagni +""" + +#***************************************************************************** +# Copyright (C) 2014 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.knots.link import Link +from sage.rings.finite_rings.integer_mod import Mod + +class Knot(Link): + """ + A knot. + + A knot is defined as embedding of the circle `\mathbb{S}^1` in the + 3-dimensional sphere `\mathbb{S}^3`, considered up to ambient isotopy. + They represent the physical idea of a knotted rope, but with the + particularity that the rope is closed. That is, the ends of the rope + are joined. + + .. SEEALSO:: + + :class:`Link` + + INPUT: + + - ``data`` -- see :class:`Link` for the allowable inputs + - ``check`` -- optional, default ``True``. If ``True``, make sure + that the data define a knot, not a link + + EXAMPLES: + + We construct the knot `8_{14}` and compute some invariants:: + + sage: B = BraidGroup(4) + sage: K = Knot(B([1,1,1,2,-1,2,-3,2,-3])) + + .. PLOT:: + :width: 300 px + + B = BraidGroup(4) + K = Knot(B([1,1,1,2,-1,2,-3,2,-3])) + sphinx_plot(K.plot()) + + :: + + sage: K.alexander_polynomial() + -2*t^-2 + 8*t^-1 - 11 + 8*t - 2*t^2 + sage: K.jones_polynomial() + t^7 - 3*t^6 + 4*t^5 - 5*t^4 + 6*t^3 - 5*t^2 + 4*t + 1/t - 2 + sage: K.determinant() + 31 + sage: K.signature() + -2 + + REFERENCES: + + - :wikipedia:`Knot_(mathematics)` + + .. TODO:: + + - Implement the connect sum of two knots. + - Make a class Knots for the monoid of all knots and have this be an + element in that monoid. + """ + def __init__(self, data, check=True): + """ + Initialize ``self``. + + TESTS:: + + sage: B = BraidGroup(8) + sage: K = Knot(B([-1, -1, -1, 2, 1, -2, 3, -2, 3])) + sage: TestSuite(K).run() + sage: K = Knot(B([1, -2, 1, -2])) + sage: TestSuite(K).run() + sage: K = Knot([[1, 1, 2, 2]]) + sage: TestSuite(K).run() + + The following is not a knot: it has two components. :: + + sage: Knot([[[1, 2], [-2, -1]], [1, -1]]) + Traceback (most recent call last): + ... + ValueError: the input has more than 1 connected component + + sage: Knot([[[1, 2], [-2, -1]], [1, -1]], check=False) + Knot represented by 2 crossings + """ + Link.__init__(self, data) + if check: + if self.number_of_components() != 1: + raise ValueError("the input has more than 1 connected component") + + def __repr__(self): + """ + Return a string representation. + + EXAMPLES:: + + sage: B = BraidGroup(8) + sage: K = Knot(B([1, 2, 1, 2])) + sage: K + Knot represented by 4 crossings + sage: K = Knot([[1, 7, 2, 6], [7, 3, 8, 2], [3, 11, 4, 10], [11, 5, 12, 4], [14, 5, 1, 6], [13, 9, 14, 8], [12, 9, 13, 10]]) + sage: K + Knot represented by 7 crossings + """ + pd_len = len(self.pd_code()) + return 'Knot represented by {} crossings'.format(pd_len) + + def dt_code(self): + """ + Return the DT code of ``self``. + + ALGORITHM: + + The DT code is generated by the following way: + + Start moving along the knot, as we encounter the crossings we + start numbering them, so every crossing has two numbers assigned to + it once we have traced the entire knot. Now we take the even number + associated with every crossing. + + The following sign convention is to be followed: + + Take the even number with a negative sign if it is an overcrossing + that we are encountering. + + OUTPUT: DT code representation of the knot + + EXAMPLES:: + + sage: K = Knot([[1,5,2,4],[5,3,6,2],[3,1,4,6]]) + sage: K.dt_code() + [4, 6, 2] + sage: B = BraidGroup(4) + sage: K = Knot(B([1, 2, 1, 2])) + sage: K.dt_code() + [4, -6, 8, -2] + sage: K = Knot([[[1, -2, 3, -4, 5, -1, 2, -3, 4, -5]], [1, 1, 1, 1, 1]]) + sage: K.dt_code() + [6, 8, 10, 2, 4] + """ + b = self.braid().Tietze() + N = len(b) + label = [0 for i in range(2 * N)] + string = 1 + next_label = 1 + type1 = 0 + crossing = 0 + while next_label <= 2 * N: + string_found = False + for i in range(crossing, N): + if abs(b[i]) == string or abs(b[i]) == string - 1: + string_found = True + crossing = i + break + if not string_found: + for i in range(0, crossing): + if abs(b[i]) == string or abs(b[i]) == string - 1: + string_found = True + crossing = i + break + assert label[2 * crossing + next_label % 2] != 1, "invalid knot" + + label[2 * crossing + next_label % 2] = next_label + next_label = next_label + 1 + if type1 == 0: + if b[crossing] < 0: + type1 = 1 + else: + type1 = -1 + else: + type1 = -1 * type1 + if ((abs(b[crossing]) == string and b[crossing] * type1 > 0) + or (abs(b[crossing]) != string and b[crossing] * type1 < 0)): + if next_label % 2 == 1: + label[2 * crossing] = label[2 * crossing] * -1 + if abs(b[crossing]) == string: + string = string + 1 + else: + string = string - 1 + crossing = crossing + 1 + code = [0 for i in range(N)] + for i in range(N): + for j in range(N): + if label[2 * j + 1] == 2 * i + 1: + code[i] = label[2 * j] + break + return code + + def arf_invariant(self): + """ + Return the Arf invariant. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: K = Knot(B([-1, 2, 1, 2])) + sage: K.arf_invariant() + 0 + sage: B = BraidGroup(8) + sage: K = Knot(B([-2, 3, 1, 2, 1, 4])) + sage: K.arf_invariant() + 0 + sage: K = Knot(B([1, 2, 1, 2])) + sage: K.arf_invariant() + 1 + """ + a = self.alexander_polynomial() + if Mod(a(-1), 8) == 1 or Mod(a(-1), 8) == 7: + return 0 + + return 1 + diff --git a/src/sage/knots/link.py b/src/sage/knots/link.py new file mode 100644 index 00000000000..4b174510856 --- /dev/null +++ b/src/sage/knots/link.py @@ -0,0 +1,2298 @@ +r""" +Links + +A knot is defined as embedding of the circle `\mathbb{S}^1` in the +3-dimensional sphere `\mathbb{S}^3`, considered up to ambient isotopy. +They represent the physical idea of a knotted rope, but with the +particularity that the rope is closed. That is, the ends of the +rope are joined. + +A link is an embedding of one or more copies of `\mathbb{S}^1` in +`\mathbb{S}^3`, considered up to ambient isotopy. That is, a link +represents the idea of one or more tied ropes. Every knot is a link, +but not every link is a knot. + +Generically, the projection of a link on `\RR^2` is a curve with +crossings. The crossings are represented to show which strand goes +over the other. This curve is called a planar diagram of the link. +If we remove the crossings, the resulting connected components are +segments. These segments are called the edges of the diagram. + +REFERENCES: + +- :wikipedia:`Knot_(mathematics)` + +.. [Collins13] Julia Collins. *An algorithm for computing the Seifert + matrix of a link from a braid representation*. (2013). + http://www.maths.ed.ac.uk/~jcollins/SeifertMatrix/SeifertMatrix.pdf + +.. [KnotAtlas] The Knot atlas. http://katlas.org/wiki/Main_Page + +.. SEEALSO:: + + There are also tables of link and knot invariants at + http://www.indiana.edu/~knotinfo/ + and http://www.indiana.edu/~linkinfo/. + +AUTHORS: + +- Miguel Angel Marco Buzunariz +- Amit Jamadagni +""" + +#***************************************************************************** +# Copyright (C) 2014 Miguel Angel Marco Buzunariz +# Amit Jamadagni +# +# 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 +from sage.rings.integer_ring import ZZ +from sage.graphs.digraph import DiGraph +from sage.graphs.graph import Graph +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing +from sage.symbolic.ring import SR +from sage.rings.integer import Integer +from sage.numerical.mip import MixedIntegerLinearProgram +from sage.functions.generalized import sign +from sage.misc.flatten import flatten +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.cachefunc import cached_method +from copy import deepcopy, copy + + +class Link(object): + r""" + A link. + + A link is an embedding of one or more copies of `\mathbb{S}^1` in + `\mathbb{S}^3`, considered up to ambient isotopy. That is, a link + represents the idea of one or more tied ropes. Every knot is a link, + but not every link is a knot. + + A link can be created by using one of the conventions mentioned below: + + Braid: + + - The closure of a braid is a link:: + + sage: B = BraidGroup(8) + sage: L = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 3])) + sage: L + Link with 1 component represented by 9 crossings + sage: L = Link(B([1, 2, 1, -2, -1])) + sage: L + Link with 2 components represented by 5 crossings + + .. NOTE:: + + The strands of the braid that have no crossings at all + are removed. + + - Oriented Gauss Code: + + Label the crossings from `1` to `n` (where `n` is the number of + crossings) and start moving along the link. Trace every component of + the link, by starting at a particular point on one component of the + link and writing down each of the crossings that you encounter until + returning to the starting point. The crossings are written with sign + depending on whether we cross them as over or undercrossing. Each + component is then represented as a list whose elements are the + crossing numbers. A second list of `+1` and `-1`'s keeps track of + the orientation of each crossing:: + + sage: L = Link([[[-1, 2, 3, -4, 5, -6, 7, 8, -2, -5, 6, 1, -8, -3, 4, -7]], + ....: [-1, -1, -1, -1, 1, 1, -1, 1]]) + sage: L + Link with 1 component represented by 8 crossings + + For links there may be more than one component and the input is + as follows:: + + sage: L = Link([[[-1, 2], [-3, 4], [1, 3, -4, -2]], [-1, -1, 1, 1]]) + sage: L + Link with 3 components represented by 4 crossings + + - Planar Diagram (PD) Code: + + The diagram of the link is formed by segments that are adjacent to + the crossings. Label each one of this segments with a positive number, + and for each crossing, write down the four incident segments. The + order of these segments is clockwise, starting with the incoming + undercrossing. + + There is no particular distinction between knots and links for + this input. + + EXAMPLES: + + One of the representations of the trefoil knot:: + + sage: L = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]]) + sage: L + Link with 1 component represented by 3 crossings + + .. PLOT:: + :width: 300 px + + L = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]]) + sphinx_plot(L.plot()) + + One of the representations of the Hopf link:: + + sage: L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sage: L + Link with 2 components represented by 2 crossings + + .. PLOT:: + :width: 300 px + + L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sphinx_plot(L.plot()) + + We can construct links from from the braid group:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, -1, -1, -2, 1, -2, 3, -2])) + sage: L + Link with 2 components represented by 8 crossings + + .. PLOT:: + :width: 300 px + + B = BraidGroup(4) + L = Link(B([-1, -1, -1, -2, 1, -2, 3, -2])) + sphinx_plot(L.plot()) + + :: + + sage: L = Link(B([1, 2, 1, 3])) + sage: L + Link with 2 components represented by 4 crossings + + .. PLOT:: + :width: 300 px + + B = BraidGroup(4) + L = Link(B([1, 2, 1, 3])) + sphinx_plot(L.plot()) + + We construct the "monster" unknot using a planar code, and + then construct the oriented Gauss code and braid representation:: + + sage: L = Link([[3,1,2,4], [8,9,1,7], [5,6,7,3], [4,18,6,5], + ....: [17,19,8,18], [9,10,11,14], [10,12,13,11], + ....: [12,19,15,13], [20,16,14,15], [16,20,17,2]]) + sage: L.oriented_gauss_code() + [[[1, -4, 3, -1, 10, -9, 6, -7, 8, 5, 4, -3, 2, -6, 7, -8, 9, -10, -5, -2]], + [1, -1, 1, 1, 1, -1, -1, -1, -1, -1]] + sage: L.braid() + s0*s1^-1*s2^-1*s3^-1*s2*s1^-1*s0^-1*s1*s2^2*s1^-1*s3*s2*s1^-3 + + .. PLOT:: + :width: 300 px + + L = Link([[3,1,2,4],[8,9,1,7],[5,6,7,3],[4,18,6,5], + [17,19,8,18],[9,10,11,14],[10,12,13,11], + [12,19,15,13],[20,16,14,15],[16,20,17,2]]) + sphinx_plot(L.plot()) + + We construct the Ochiai unknot by using an oriented Gauss code:: + + sage: L = Link([[[1,-2,-3,-8,-12,13,-14,15,-7,-1,2,-4,10,11,-13,12, + ....: -11,-16,4,3,-5,6,-9,7,-15,14,16,-10,8,9,-6,5]], + ....: [-1,-1,1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1]]) + sage: L.pd_code() + [[10, 2, 11, 1], [2, 12, 3, 11], [3, 20, 4, 21], [12, 19, 13, 20], + [21, 32, 22, 1], [31, 22, 32, 23], [9, 25, 10, 24], [4, 29, 5, 30], + [23, 30, 24, 31], [28, 14, 29, 13], [17, 14, 18, 15], [5, 17, 6, 16], + [15, 7, 16, 6], [7, 27, 8, 26], [25, 9, 26, 8], [18, 28, 19, 27]] + + .. PLOT:: + :width: 300 px + + L = Link([[[1,-2,-3,-8,-12,13,-14,15,-7,-1,2,-4,10,11,-13,12, + -11,-16,4,3,-5,6,-9,7,-15,14,16,-10,8,9,-6,5]], + [-1,-1,1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1]]) + sphinx_plot(L.plot()) + + We construct the knot `7_1` and compute some invariants:: + + sage: B = BraidGroup(2) + sage: L = Link(B([1]*7)) + + .. PLOT:: + :width: 300 px + + B = BraidGroup(2) + L = Link(B([1]*7)) + sphinx_plot(L.plot()) + + :: + + sage: L.alexander_polynomial() + t^-3 - t^-2 + t^-1 - 1 + t - t^2 + t^3 + sage: L.jones_polynomial() + -t^10 + t^9 - t^8 + t^7 - t^6 + t^5 + t^3 + sage: L.determinant() + 7 + sage: L.signature() + -6 + + The links here have removed components in which no strand is used:: + + sage: B = BraidGroup(8) + sage: b = B([1]) + sage: L = Link(b) + sage: b.components_in_closure() + 7 + sage: L.number_of_components() + 1 + sage: L.braid().components_in_closure() + 1 + sage: L.braid().parent() + Braid group on 2 strands + + .. WARNING:: + + Equality of knots is done by comparing the corresponding braids, + which may give false negatives. + + .. NOTE:: + + The behavior of removing unused strands from an element of a + braid group may change without notice in the future. Do not + rely on this feature. + + .. TODO:: + + Implement methods to creating new links from previously created links. + """ + def __init__(self, data): + """ + Initialize ``self``. + + TESTS:: + + sage: B = BraidGroup(8) + sage: L = Link(B([-1, -1, -1, -2,1, -2, 3, -2])) + sage: TestSuite(L).run() + sage: L = Link(B([1, 2, 1])) + sage: TestSuite(L).run() + sage: L = Link([[1, 1, 2, 2]]) + sage: TestSuite(L).run() + + sage: L = Link(B.one()) + sage: L = Link([]) + sage: L = Link([[], []]) + + sage: Link([[[-1, 2, -1, 2]], [1, 1, 1, 1]]) + Traceback (most recent call last): + ... + ValueError: invalid input: data is not a valid oriented Gauss code + + sage: Link([[[-1, 2, 3, 4]]]) + Traceback (most recent call last): + ... + ValueError: invalid PD code: crossings must be represented by four segments + + sage: L = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 3]]) + Traceback (most recent call last): + ... + ValueError: invalid PD code: each segment must appear twice + + sage: L = Link(5) + Traceback (most recent call last): + ... + ValueError: invalid input: data must be either a list or a braid + """ + if isinstance(data, list): + if len(data) != 2 or not all(isinstance(i, list) for i in data[0]): + for i in data: + if len(i) != 4: + raise ValueError("invalid PD code: crossings must be represented by four segments") + else: + flat = flatten(data) + if any(flat.count(i) != 2 for i in set(flat)): + raise ValueError("invalid PD code: each segment must appear twice") + self._pd_code = data + self._oriented_gauss_code = None + self._braid = None + + else: + flat = flatten(data[0]) + if flat: + a, b = max(flat), min(flat) + if 2 * len(data[1]) != len(flat) or set(range(b, a + 1)) - set([0]) != set(flat): + raise ValueError("invalid input: data is not a valid oriented Gauss code") + self._oriented_gauss_code = data + self._pd_code = None + self._braid = None + + else: + from sage.groups.braid import Braid, BraidGroup + if isinstance(data, Braid): + # Remove all unused strands + support = sorted(set(abs(x) for x in data.Tietze())) + i = 0 + cur = 1 + while i < len(support): + if support[i] == cur: + cur += 1 + i += 1 + elif support[i] == cur + 1: + support.insert(i, cur+1) + cur += 2 + i += 2 + else: + cur = support[i] + i += 1 + d = {} + for i,s in enumerate(support): + d[s] = i+1 + d[-s] = -i-1 + if not support: + B = BraidGroup(2) + else: + B = BraidGroup(len(support)+1) + self._braid = B([d[x] for x in data.Tietze()]) + self._oriented_gauss_code = None + self._pd_code = None + + else: + raise ValueError("invalid input: data must be either a list or a braid") + + def __repr__(self): + """ + Return a string representation. + + OUTPUT: string representation + + EXAMPLES:: + + sage: B = BraidGroup(8) + sage: L = Link(B([1, 2, 1, 2])) + sage: L + Link with 1 component represented by 4 crossings + sage: L = Link([[[-1, 2], [-3, 4], [1, 3, -4, -2]], [-1, -1, 1, 1]]) + sage: L + Link with 3 components represented by 4 crossings + """ + number_of_components = self.number_of_components() + if number_of_components > 1: + plural = 's' + else: + plural = '' + pd_len = len(self.pd_code()) + return 'Link with {} component{} represented by {} crossings'.format(number_of_components, plural, pd_len) + + def __eq__(self, other): + """ + Check equality. + + TESTS:: + + sage: B = BraidGroup(8) + sage: L1 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 5, 4])) + sage: L2 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 5, 4])) + sage: L1 == L2 + True + sage: L3 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2])) + sage: L1 == L3 + False + """ + if not isinstance(other, self.__class__): + return False + if self._pd_code is not None: + if self.pd_code() == other.pd_code(): + return True + if self._oriented_gauss_code is not None: + if self.oriented_gauss_code() == other.oriented_gauss_code(): + return True + return self.braid() == other.braid() + + def __ne__(self, other): + """ + Check inequality. + + TESTS:: + + sage: B = BraidGroup(8) + sage: L1 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 5, 4])) + sage: L2 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2, 5, 4])) + sage: L1 != L2 + False + sage: L3 = Link(B([-1, -1, -1, -2, 1, -2, 3, -2])) + sage: L1 != L3 + True + """ + return not self.__eq__(other) + + def braid(self): + """ + Return a braid representation of ``self``. + + OUTPUT: an element in the braid group + + EXAMPLES:: + + sage: L = Link([[2, 3, 1, 4], [4, 1, 3, 2]]) + sage: L.braid() + s^2 + sage: L = Link([[[-1, 2, -3, 1, -2, 3]], [-1, -1, -1]]) + sage: L.braid() + s^-3 + sage: L = Link([[1,8,2,7], [8,4,9,5], [3,9,4,10], [10,1,7,6], [5,3,6,2]]) + sage: L.braid() + (s0*s1^-1)^2*s1^-1 + + TESTS:: + + sage: L = Link([]) + sage: L.braid() + 1 + sage: L = Link([[], []]) + sage: L.braid() + 1 + """ + if self._braid is not None: + return self._braid + + from sage.groups.braid import BraidGroup + comp = self._isolated_components() + if len(comp) > 1: + L1 = Link(comp[0]) + L2 = Link(flatten(comp[1:], max_level=1)) + b1 = L1.braid() + b2 = L2.braid() + n1 = b1.parent().strands() + n2 = b2.parent().strands() + t1 = list(b1.Tietze()) + t2 = [sign(x)*(abs(x) + n1) for x in b2.Tietze()] + B = BraidGroup(n1 + n2) + self._braid = B(t1 + t2) + return self._braid + + # look for possible Vogel moves, perform them and call recursively to the modified link + pd_code = self.pd_code() + if not pd_code: + B = BraidGroup(2) + self._braid = B.one() + return self._braid + seifert_circles = self.seifert_circles() + newedge = max(flatten(pd_code)) + 1 + for region in self.regions(): + n = len(region) + for i in range(n-1): + a = region[i] + seifcirca = [x for x in seifert_circles if abs(a) in x] + for j in range(i+1,n): + b = region[j] + seifcircb = [x for x in seifert_circles if abs(b) in x] + if seifcirca != seifcircb and sign(a) == sign(b): + tails, heads = self._directions_of_edges() + + newPD = deepcopy(pd_code) + if sign(a) == 1: + C1 = newPD[newPD.index(heads[a])] + C1[C1.index(a)] = newedge + 1 + C2 = newPD[newPD.index(tails[b])] + C2[C2.index(b)] = newedge + 2 + newPD.append([newedge + 3, a, b, newedge]) + newPD.append([newedge + 2, newedge + 1, newedge + 3, newedge]) + self._braid = Link(newPD).braid() + return self._braid + else: + C1 = newPD[newPD.index(heads[-a])] + C1[C1.index(-a)] = newedge + 1 + C2 = newPD[newPD.index(tails[-b])] + C2[C2.index(-b)] = newedge + 2 + newPD.append([newedge + 2, newedge, newedge + 3, newedge + 1]) + newPD.append([newedge + 3, newedge, -b , -a]) + self._braid = Link(newPD).braid() + return self._braid + + # We are in the case where no Vogel moves are necessary. + G = DiGraph() + G.add_vertices([tuple(c) for c in seifert_circles]) + for i,c in enumerate(pd_code): + if self.orientation()[i] == 1: + a = [x for x in seifert_circles if c[1] in x][0] + b = [x for x in seifert_circles if c[0] in x][0] + else: + a = [x for x in seifert_circles if c[0] in x][0] + b = [x for x in seifert_circles if c[3] in x][0] + G.add_edge(tuple(a), tuple(b)) + + # Get a simple path from a source to a sink in the digraph + it = G.all_paths_iterator(starting_vertices=G.sources(), ending_vertices=G.sinks(), simple=True) + ordered_cycles = next(it) + + B = BraidGroup(len(ordered_cycles)) + available_crossings = copy(pd_code) + oc_set = set(ordered_cycles[0]) + for i,x in enumerate(pd_code): + if any(elt in oc_set for elt in x): + crossing = x + crossing_index = i + break + available_crossings.remove(crossing) + status = [None for i in ordered_cycles] + orientation = self.orientation() + if orientation[crossing_index] == 1: + b = B([1]) + status[0] = crossing[2] + status[1] = crossing[3] + else: + b = B([-1]) + status[0] = crossing[1] + status[1] = crossing[2] + counter = 0 + while available_crossings: + possibles = [x for x in available_crossings if status[counter] in x] + if len(status) < counter + 2 or status[counter + 1] is not None: + possibles = [x for x in possibles if status[counter + 1] in x] + if possibles: + added = possibles[0] + if orientation[pd_code.index(added)] == 1: + b *= B([counter + 1]) + status[counter] = added[2] + status[counter + 1] = added[3] + else: + b *= B([-counter - 1]) + status[counter] = added[1] + status[counter + 1] = added[2] + if counter > 0: + counter -= 1 + available_crossings.remove(added) + else: + counter += 1 + self._braid = b + return b + + def _directions_of_edges(self): + r""" + Return the directions of the edges given by the PD code of ``self``. + + OUTPUT: + + A tuple of two dictionaries. The first one assigns + each edge of the PD code to the crossing where it starts. + The second dictionary assigns it to where it ends. + + EXAMPLES:: + + sage: L = Link([[1, 3, 2, 4], [2, 3, 1, 4]]) + sage: L._directions_of_edges() + ({1: [2, 3, 1, 4], 2: [1, 3, 2, 4], 3: [1, 3, 2, 4], 4: [2, 3, 1, 4]}, + {1: [1, 3, 2, 4], 2: [2, 3, 1, 4], 3: [2, 3, 1, 4], 4: [1, 3, 2, 4]}) + + :: + + sage: L = Link([[1,5,2,4], [5,3,6,2], [3,1,4,6]]) + sage: L._directions_of_edges() + ({1: [3, 1, 4, 6], + 2: [1, 5, 2, 4], + 3: [5, 3, 6, 2], + 4: [3, 1, 4, 6], + 5: [1, 5, 2, 4], + 6: [5, 3, 6, 2]}, + {1: [1, 5, 2, 4], + 2: [5, 3, 6, 2], + 3: [3, 1, 4, 6], + 4: [1, 5, 2, 4], + 5: [5, 3, 6, 2], + 6: [3, 1, 4, 6]}) + + :: + + sage: L = Link([[1,2,3,3], [2,4,5,5], [4,1,7,7]]) + sage: L._directions_of_edges() + ({1: [4, 1, 7, 7], + 2: [1, 2, 3, 3], + 3: [1, 2, 3, 3], + 4: [2, 4, 5, 5], + 5: [2, 4, 5, 5], + 7: [4, 1, 7, 7]}, + {1: [1, 2, 3, 3], + 2: [2, 4, 5, 5], + 3: [1, 2, 3, 3], + 4: [4, 1, 7, 7], + 5: [2, 4, 5, 5], + 7: [4, 1, 7, 7]}) + """ + tails = {} + heads = {} + pd_code = self.pd_code() + for C in pd_code: + tails[C[2]] = C + a = C[2] + D = C + while not a in heads: + next_crossing = [x for x in pd_code if a in x and x != D] + if not next_crossing: + heads[a] = D + tails[a] = D + if D[0] == a: + a = D[2] + elif D[1] == a: + a = D[3] + else: + a = D[1] + else: + heads[a] = next_crossing[0] + tails[a] = D + D = next_crossing[0] + a = D[(D.index(a)+2) % 4] + + unassigned = set(flatten(pd_code)).difference(set(tails.keys())) + while unassigned: + a = unassigned.pop() + for x in pd_code: + if a in x: + D = x + break + while not a in heads: + tails[a] = D + for x in pd_code: + if a in x and x != D: + next_crossing = x + break + heads[a] = next_crossing + D = next_crossing + a = D[(D.index(a)+2) % 4] + if a in unassigned: + unassigned.remove(a) + return tails, heads + + def oriented_gauss_code(self): + """ + Return the oriented Gauss code of ``self``. + + The oriented Gauss code has two parts: + + a. the Gauss code + + b. the orientation of each crossing + + The following orientation was taken into consideration for + construction of knots: + + From the outgoing of the overcrossing if we move in the clockwise + direction to reach the outgoing of the undercrossing then we label + that crossing as `-1`. + + From the outgoing of the overcrossing if we move in the anticlockwise + direction to reach the outgoing of the undercrossing then we label + that crossing as `+1`. + + One more consideration we take in while constructing the orientation + is the order of the orientation is same as the ordering of the + crossings in the Gauss code. + + .. NOTE:: + + Convention: under is denoted by `-1`, and over by `+1` in the + crossing info. + + EXAMPLES:: + + sage: L = Link([[1, 11, 2, 10], [6, 2, 7, 3], [3, 12, 4, 9], [9, 5, 10, 6], [8, 1, 5, 4], [11, 8, 12, 7]]) + sage: L.oriented_gauss_code() + [[[-1, 2, -3, 5], [4, -2, 6, -5], [-4, 1, -6, 3]], [-1, 1, 1, 1, -1, -1]] + sage: L = Link([[1, 4, 2, 3], [6, 1, 3, 2], [7, 4, 8, 5], [5, 8, 6, 7]]) + sage: L.oriented_gauss_code() + [[[-1, 2], [-3, 4], [1, 3, -4, -2]], [-1, -1, 1, 1]] + sage: B = BraidGroup(8) + sage: b = B([1, 1, 1, 1, 1]) + sage: L = Link(b) + sage: L.oriented_gauss_code() + [[[1, -2, 3, -4, 5, -1, 2, -3, 4, -5]], [1, 1, 1, 1, 1]] + + TESTS:: + + sage: L = Link([]) + sage: L.oriented_gauss_code() + [[], []] + sage: L = Link(BraidGroup(2).one()) + sage: L.oriented_gauss_code() + [[], []] + """ + if self._oriented_gauss_code is not None: + return self._oriented_gauss_code + + pd = self.pd_code() + orient = self.orientation() + crossing_info = {} + for i, j in enumerate(pd): + if orient[i] == -1: + crossing_info[(j[0], -1, i + 1)] = j[2] + crossing_info[(j[3], 1, i + 1)] = j[1] + elif orient[i] == 1: + crossing_info[(j[0], -1, i + 1)] = j[2] + crossing_info[(j[1], 1, i + 1)] = j[3] + edges = {} + cross_number = {} + for i, j in crossing_info.items(): + edges[i[0]] = [j] + if i[1] == 1: + cross_number[i[0]] = i[2] + elif i[1] == -1: + cross_number[i[0]] = -i[2] + edges_graph = DiGraph(edges) + d = edges_graph.all_simple_cycles() + code = [] + for i in d: + l = [] + for j in i: + l.append(cross_number[j]) + del l[-1] + code.append(l) + oriented_code = [code, orient] + self._oriented_gauss_code = oriented_code + return self._oriented_gauss_code + + def pd_code(self): + """ + Return the planar diagram code of ``self``. + + The planar diagram is returned in the following format. + + We construct the crossing by starting with the entering component + of the undercrossing, move in the clockwise direction and then + generate the list. If the crossing is given by `[a, b, c, d]`, + then we interpret this information as: + + 1. `a` is the entering component of the undercrossing; + 2. `b, d` are the components of the overcrossing; + 3. `c` is the leaving component of the undercrossing. + + EXAMPLES:: + + sage: L = Link([[[1, -2, 3, -4, 2, -1, 4, -3]], [1, 1, -1, -1]]) + sage: L.pd_code() + [[6, 1, 7, 2], [2, 5, 3, 6], [8, 4, 1, 3], [4, 8, 5, 7]] + sage: B = BraidGroup(2) + sage: b = B([1, 1, 1, 1, 1]) + sage: L = Link(b) + sage: L.pd_code() + [[2, 1, 3, 4], [4, 3, 5, 6], [6, 5, 7, 8], [8, 7, 9, 10], [10, 9, 1, 2]] + sage: L = Link([[[2, -1], [1, -2]], [1, 1]]) + sage: L.pd_code() + [[2, 3, 1, 4], [4, 1, 3, 2]] + sage: L = Link([[1, 2, 3, 3], [2, 4, 5, 5], [4, 1, 7, 7]]) + sage: L.pd_code() + [[1, 2, 3, 3], [2, 4, 5, 5], [4, 1, 7, 7]] + + TESTS:: + + sage: L = Link([[], []]) + sage: L.pd_code() + [] + sage: L = Link(BraidGroup(2).one()) + sage: L.pd_code() + [] + """ + if self._pd_code is not None: + return self._pd_code + + if self._oriented_gauss_code is not None: + oriented_gauss_code = self._oriented_gauss_code + d_dic = {} + if len(oriented_gauss_code[0]) > 1: + d = flatten(oriented_gauss_code[0]) + for i, j in enumerate(d): + d_dic[j] = [i + 1, i + 2] + # here we collect the final component in each Gauss code + last_component = [i[-1] for i in oriented_gauss_code[0]] + first_component = [i[0] for i in oriented_gauss_code[0]] + # here we correct the last_component + for i, j in zip(last_component, first_component): + d_dic[i][1] = d_dic[j][0] + crossing_dic = {} + for i,x in enumerate(oriented_gauss_code[1]): + if x == -1: + crossing_dic[i + 1] = [d_dic[-(i + 1)][0], d_dic[i + 1][1], + d_dic[-(i + 1)][1], d_dic[i + 1][0]] + elif x == 1: + crossing_dic[i + 1] = [d_dic[-(i + 1)][0], d_dic[i + 1][0], + d_dic[-(i + 1)][1], d_dic[i + 1][1]] + elif len(oriented_gauss_code[0]) == 1: + for i, j in enumerate(oriented_gauss_code[0][0]): + d_dic[j] = [i + 1, i + 2] + d_dic[oriented_gauss_code[0][0][-1]][1] = 1 + crossing_dic = {} + for i, x in enumerate(oriented_gauss_code[1]): + if x == -1: + crossing_dic[i + 1] = [d_dic[-(i + 1)][0], d_dic[i + 1][1], + d_dic[-(i + 1)][1], d_dic[i + 1][0]] + elif x == 1: + crossing_dic[i + 1] = [d_dic[-(i + 1)][0], d_dic[i + 1][0], + d_dic[-(i + 1)][1], d_dic[i + 1][1]] + else: + crossing_dic = {} + + pd = crossing_dic.values() + self._pd_code = pd + return self._pd_code + + if self._braid is not None: + strings = range(1, self._braid.strands() + 1) + b = list(self._braid.Tietze()) + pd = [] + strings_max = strings[-1] + for i in b: + if i > 0: + pd.append( + [strings[i], strings[i - 1], strings_max + 1, strings_max + 2]) + else: + pd.append( + [strings[abs(i) - 1], strings_max + 1, strings_max + 2, strings[abs(i)]]) + strings[abs(i) - 1] = strings_max + 1 + strings[abs(i)] = strings_max + 2 + strings_max = strings_max + 2 + for i in pd: + for j in range(4): + if i[j] in strings: + i[j] = strings.index(i[j]) + 1 + self._pd_code = pd + return pd + + raise AssertionError("invalid state") + + def gauss_code(self): + """ + Return the Gauss code of ``self``. + + The Gauss code is generated by the following procedure: + + a. Number the crossings from `1` to `n`. + b. Select a point on the knot and start moving along the component. + c. At each crossing, take the number of the crossing, along with + sign, which is `-` if it is an undercrossing and `+` if it is a + overcrossing. + + EXAMPLES:: + + sage: L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sage: L.gauss_code() + [[-1, 2], [1, -2]] + sage: B = BraidGroup(8) + sage: L = Link(B([1, -2, 1, -2, -2])) + sage: L.gauss_code() + [[-1, 3, -4, 5], [1, -2, 4, -5, 2, -3]] + sage: L = Link([[[-1, 2], [-3, 4], [1, 3, -4, -2]], [-1, -1, 1, 1]]) + sage: L.gauss_code() + [[-1, 2], [-3, 4], [1, 3, -4, -2]] + """ + return self.oriented_gauss_code()[0] + + def dowker_notation(self): + """ + Return the Dowker notation of ``self``. + + Similar to the PD code we number the components, so every crossing + is represented by four numbers. We focus on the incoming entities + of the under and the overcrossing. It is the pair of incoming + undercrossing and the incoming overcrossing. This information at + every crossing gives the Dowker notation. + + OUTPUT: + + A list containing the pair of incoming under cross and the incoming + over cross. + + EXAMPLES:: + + sage: L = Link([[[-1, 2, -3, 4, 5, 1, -2, 6, 7, 3, -4, -7, -6,-5]], [-1, -1, -1, -1, 1, -1, 1]]) + sage: L.dowker_notation() + [(1, 6), (7, 2), (3, 10), (11, 4), (14, 5), (13, 8), (12, 9)] + sage: B = BraidGroup(4) + sage: L = Link(B([1, 2, 1, 2])) + sage: L.dowker_notation() + [(2, 1), (3, 5), (6, 4), (7, 9)] + sage: L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sage: L.dowker_notation() + [(1, 3), (4, 2)] + """ + pd = self.pd_code() + orient = self.orientation() + dn = [(i[0], i[3]) if orient[j] == -1 else (i[0], i[1]) + for j, i in enumerate(pd)] + return dn + + def _braid_word_components(self): + """ + Return the disjoint braid components, if any, else return the braid + of ``self``. + + For example consider the braid ``[-1, 3, 1, 3]`` this can be viewed + as a braid with components as ``[-1, 1]`` and ``[3, 3]``. There is no + common crossing to these two (in sense there is a crossing between + strand `1` and `2`, crossing between `3` and `4` but no crossing + between strand `2` and `3`, so these can be viewed as independent + components in the braid). + + OUTPUT: list containing the components + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L._braid_word_components() + ([-1, 1], [3, 3]) + sage: B = BraidGroup(8) + sage: L = Link(B([-1, 3, 1, 5, 1, 7, 1, 6])) + sage: L._braid_word_components() + ([-1, 1, 1, 1], [3], [5, 7, 6]) + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L._braid_word_components() + ([-2, 1, 1], [4, 4], [6]) + """ + ml = list(self.braid().Tietze()) + if not ml: + return tuple() + + l = set(abs(k) for k in ml) + missing1 = set(range(min(l), max(l) + 1)) - l + if not missing1: + return (ml,) + + missing = sorted(missing1) + x = [[] for i in range(len(missing) + 1)] + for i,a in enumerate(missing): + for j, mlj in enumerate(ml): + if mlj != 0 and abs(mlj) < a: + x[i].append(mlj) + ml[j] = 0 + elif mlj != 0 and abs(mlj) > missing[-1]: + x[-1].append(mlj) + ml[j] = 0 + return tuple([a for a in x if a]) + + def _braid_word_components_vector(self): + """ + The list from the :meth:`_braid_word_components` is flattened to + give out the vector form. + + OUTPUT: list containing braid word components + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L._braid_word_components_vector() + [-1, 1, 3, 3] + sage: B = BraidGroup(8) + sage: L = Link(B([-1, 3, 1, 5, 1, 7, 1, 6])) + sage: L._braid_word_components_vector() + [-1, 1, 1, 1, 3, 5, 7, 6] + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L._braid_word_components_vector() + [-2, 1, 1, 4, 4, 6] + """ + return flatten(self._braid_word_components()) + + def _homology_generators(self): + """ + The set of generators for the first homology group of the connected + Seifert surface of the given link. + + This method uses the :meth:`_braid_word_components_vector` to generate + the homology generators. The position of the repeated element w.r.t. + the braid word component vector list is compiled into a list. + + This is based on Lemma 3.1 in [Collins13]_. + + OUTPUT: + + A list of integers `i \in \{1, 2, \ldots, n-1\}` corresponding + to the simple generators `s_i` that gives a homology generator or + `0` if the position does not represent a generator. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L._homology_generators() + [1, 0, 3] + sage: B = BraidGroup(8) + sage: L = Link(B([-1, 3, 1, 5, 1, 7, 1, 6])) + sage: L._homology_generators() + [1, 2, 3, 0, 0, 0, 0] + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L._homology_generators() + [0, 2, 0, 4, 0] + """ + x = self._braid_word_components_vector() + hom_gen = [] + for j in range(len(x) - 1): + a = abs(x[j]) + for i in range(j + 1, len(x)): + if a == abs(x[i]): + hom_gen.append(i) + break + else: + hom_gen.append(0) + return hom_gen + + @cached_method + def seifert_matrix(self): + """ + Return the Seifert matrix associated with ``self``. + + ALGORITHM: + + This is the algorithm presented in Section 3.3 of [Collins13]_. + + OUTPUT: + + The intersection matrix of a (not necessarily minimal) Seifert surface. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L.seifert_matrix() + [ 0 0] + [ 0 -1] + sage: B = BraidGroup(8) + sage: L = Link(B([-1, 3, 1, 5, 1, 7, 1, 6])) + sage: L.seifert_matrix() + [ 0 0 0] + [ 1 -1 0] + [ 0 1 -1] + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L.seifert_matrix() + [-1 0] + [ 0 -1] + """ + x = self._braid_word_components_vector() + h = self._homology_generators() + hl = len(h) + A = matrix(ZZ, hl, hl) + indices = [i for i,hi in enumerate(h) if hi != 0] + for i in indices: + hi = h[i] + for j in range(i, hl): + if i == j: + A[i, j] = cmp(0, x[i] + x[hi]) + elif hi > h[j]: + A[i, j] = 0 + A[j, i] = 0 + elif hi < j: + A[i, j] = 0 + A[j, i] = 0 + elif hi == j: + if x[j] > 0: + A[i, j] = 0 + A[j, i] = 1 + else: + A[i, j] = -1 + A[j, i] = 0 + elif abs(abs(x[i]) - abs(x[j])) > 1: + A[i, j] = 0 + elif abs(x[i]) - abs(x[j]) == 1: + A[i, j] = 0 + A[j, i] = -1 + elif abs(x[j]) - abs(x[i]) == 1: + A[i, j] = 1 + A[j, i] = 0 + else: # for debugging + A[i, j] = 2 + A[j, i] = 2 + A = A.matrix_from_rows_and_columns(indices, indices) + A.set_immutable() + return A + + @cached_method + def number_of_components(self): + """ + Return the number of connected components of ``self``. + + OUTPUT: number of connected components + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L.number_of_components() + 4 + sage: B = BraidGroup(8) + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L.number_of_components() + 5 + sage: L = Link(B([1, 2, 1, 2])) + sage: L.number_of_components() + 1 + sage: L = Link(B.one()) + sage: L.number_of_components() + 1 + """ + G = Graph() + pd = self.pd_code() + if not pd: + return ZZ.one() + G.add_vertices(set(flatten(pd))) + for c in pd: + G.add_edge(c[0], c[2]) + G.add_edge(c[1], c[3]) + return G.connected_components_number() + + def is_knot(self): + """ + Return ``True`` if ``self`` is a knot. + + Every knot is a link but the converse is not true. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([1, 3, 1, -3])) + sage: L.is_knot() + False + sage: B = BraidGroup(8) + sage: L = Link(B([1, 2, 3, 4, 5, 6])) + sage: L.is_knot() + True + """ + return self.number_of_components() == 1 + + def genus(self): + """ + Return the genus of ``self``. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L.genus() + 0 + sage: L = Link(B([1,3])) + sage: L.genus() + 0 + sage: B = BraidGroup(8) + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L.genus() + 0 + sage: L = Link(B([1, 2, 1, 2])) + sage: L.genus() + 1 + """ + b = self.braid().Tietze() + if not b: + return ZZ.zero() + + B = self.braid().parent() + x = self._braid_word_components() + q = [] + genus = 0 + s_tmp = [] + for xi in x: + tmp = [] + b1 = min(abs(k) for k in xi) + for xij in xi: + if xij > 0: + xij = xij - b1 + 1 + else: + xij = xij + b1 - 1 + tmp.append(xij) + s_tmp.append(B(tmp)) + s = [] + for i in s_tmp: + b = i.Tietze() + s.append(list(b)) + t = [Link(B(si)).number_of_components() for si in s] + for i, j in enumerate(s): + if not j: + s[i].append(-2) + for i in s: + q2 = max(abs(k) + 1 for k in i) + q.append(q2) + g = [((2 - t[i]) + len(x[i]) - q[i]) / 2 for i in range(len(x))] + for i in range(len(g)): + genus = genus + g[i] + return Integer(genus) + + def signature(self): + """ + Return the signature of ``self``. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L.signature() + -1 + sage: B = BraidGroup(8) + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L.signature() + -2 + sage: L = Link(B([1, 2, 1, 2])) + sage: L.signature() + -2 + """ + m = 2 * (self.seifert_matrix() + self.seifert_matrix().transpose()) + e = m.eigenvalues() + tot = ZZ.zero() + s = [] + for i, j in enumerate(e): + s.append(cmp(j, 0)) + tot = tot + s[i] + return tot + + def alexander_polynomial(self, var='t'): + """ + Return the Alexander polynomial of ``self``. + + INPUT: + + - ``var`` -- (default: ``'t'``) the variable in the polynomial + + EXAMPLES: + + We begin by computing the Alexander polynomial for the + figure-eight knot:: + + sage: B = BraidGroup(3) + sage: L = Link(B([1, -2, 1, -2])) + sage: L.alexander_polynomial() + -t^-1 + 3 - t + + The "monster" unknot:: + + sage: L = Link([[3,1,2,4],[8,9,1,7],[5,6,7,3],[4,18,6,5], + ....: [17,19,8,18],[9,10,11,14],[10,12,13,11], + ....: [12,19,15,13],[20,16,14,15],[16,20,17,2]]) + sage: L.alexander_polynomial() + 1 + + Some additional examples:: + + sage: B = BraidGroup(2) + sage: L = Link(B([1])) + sage: L.alexander_polynomial() + 1 + sage: L = Link(B.one()) + sage: L.alexander_polynomial() + 1 + sage: B = BraidGroup(3) + sage: L = Link(B([1, 2, 1, 2])) + sage: L.alexander_polynomial() + t^-1 - 1 + t + + When the Seifert surface is disconnected, the Alexander + polynomial is defined to be `0`:: + + sage: B = BraidGroup(4) + sage: L = Link(B([1,3])) + sage: L.alexander_polynomial() + 0 + + TESTS:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 3, 1, 3])) + sage: L.alexander_polynomial() + 0 + sage: L = Link(B([1,3,1,1,3,3])) + sage: L.alexander_polynomial() + 0 + sage: B = BraidGroup(8) + sage: L = Link(B([-2, 4, 1, 6, 1, 4])) + sage: L.alexander_polynomial() + 0 + """ + R = LaurentPolynomialRing(ZZ, var) + # The Alexander polynomial of disjoint links are defined to be 0 + if len(self._braid_word_components()) > 1: + return R.zero() + t = R.gen() + seifert_matrix = self.seifert_matrix() + f = (seifert_matrix - t * seifert_matrix.transpose()).determinant() + if f != 0: + exp = f.exponents() + return t ** ((-max(exp) - min(exp)) / 2) * f + return f + + def determinant(self): + """ + Return the determinant of ``self``. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, 2, 1, 2])) + sage: L.determinant() + 1 + sage: B = BraidGroup(8) + sage: L = Link(B([2, 4, 2, 3, 1, 2])) + sage: L.determinant() + 3 + sage: L = Link(B([1]*16 + [2,1,2,1,2,2,2,2,2,2,2,1,2,1,2,-1,2,-2])) + sage: L.determinant() + 65 + + TESTS:: + + sage: Link(B([1, 2, 1, -2, -1])).determinant() + Traceback (most recent call last): + ... + NotImplementedError: determinant implemented only for knots + """ + if self.is_knot(): + a = self.alexander_polynomial() + return Integer(abs(a(-1))) + + raise NotImplementedError("determinant implemented only for knots") + + def is_alternating(self): + """ + Return ``True`` if the given knot diagram is alternating else + returns ``False``. + + Alternating diagram implies every overcross is followed by an + undercross or the vice-versa. + + We look at the Gauss code if the sign is alternating, ``True`` + is returned else the knot is not alternating ``False`` is returned. + + EXAMPLES:: + + sage: B = BraidGroup(4) + sage: L = Link(B([-1, -1, -1, -1])) + sage: L.is_alternating() + False + sage: L = Link(B([1, -2, -1, 2])) + sage: L.is_alternating() + False + sage: L = Link(B([-1, 3, 1, 3, 2])) + sage: L.is_alternating() + False + sage: L = Link(B([1]*16 + [2,1,2,1,2,2,2,2,2,2,2,1,2,1,2,-1,2,-2])) + sage: L.is_alternating() + False + sage: L = Link(B([-1,2,-1,2])) + sage: L.is_alternating() + True + """ + if not self.is_knot(): + return False + x = self.gauss_code() + s = [cmp(i, 0) for i in x[0]] + return (s == [(-1) ** (i + 1) for i in range(len(x[0]))] + or s == [(-1) ** i for i in range(len(x[0]))]) + + def orientation(self): + r""" + Return the orientation of the crossings of the link diagram + of ``self``. + + EXAMPLES:: + + sage: L = Link([[1, 4, 5, 2], [3, 5, 6, 7], [4, 8, 9, 6], [7, 9, 10, 11], [8, 1, 13, 10], [11, 13, 2, 3]]) + sage: L.orientation() + [-1, 1, -1, 1, -1, 1] + sage: L = Link([[1, 7, 2, 6], [7, 3, 8, 2], [3, 11, 4, 10], [11, 5, 12, 4], [14, 5, 1, 6], [13, 9, 14, 8], [12, 9, 13, 10]]) + sage: L.orientation() + [-1, -1, -1, -1, 1, -1, 1] + sage: L = Link([[1, 2, 3, 3], [2, 4, 5, 5], [4, 1, 7, 7]]) + sage: L.orientation() + [-1, -1, -1] + """ + directions = self._directions_of_edges()[0] + orientation = [] + for C in self.pd_code(): + if C[0] == C[1] or C[2] == C[3]: + orientation.append(-1) + elif C[1] == C[2] or C[0] == C[3]: + orientation.append(1) + elif directions[C[1]] == C: + orientation.append(-1) + else: + orientation.append(1) + return orientation + + def seifert_circles(self): + """ + Return the Seifert circles from the link diagram of ``self``. + + Seifert circles are the circles obtained by smoothing all crossings + respecting the orientation of the segments. + + Each Seifert circle is represented as a list of the segments + that form it. + + EXAMPLES:: + + sage: L = Link([[[1, -2, 3, -4, 2, -1, 4, -3]], [1, 1, -1, -1]]) + sage: L.seifert_circles() + [[1, 7, 5, 3], [2, 6], [4, 8]] + sage: L = Link([[[-1, 2, 3, -4, 5, -6, 7, 8, -2, -5, 6, 1, -8, -3, 4, -7]], [-1, -1, -1, -1, 1, 1, -1, 1]]) + sage: L.seifert_circles() + [[1, 13, 9, 3, 15, 5, 11, 7], [2, 10, 6, 12], [4, 16, 8, 14]] + sage: L = Link([[[-1, 2, -3, 4, 5, 1, -2, 6, 7, 3, -4, -7, -6,-5]], [-1, -1, -1, -1, 1, -1, 1]]) + sage: L.seifert_circles() + [[1, 7, 3, 11, 5], [2, 8, 14, 6], [4, 12, 10], [9, 13]] + sage: L = Link([[1, 7, 2, 6], [7, 3, 8, 2], [3, 11, 4, 10], [11, 5, 12, 4], [14, 5, 1, 6], [13, 9, 14, 8], [12, 9, 13, 10]]) + sage: L.seifert_circles() + [[1, 7, 3, 11, 5], [2, 8, 14, 6], [4, 12, 10], [9, 13]] + sage: L = Link([[[-1, 2, -3, 5], [4, -2, 6, -5], [-4, 1, -6, 3]], [-1, 1, 1, 1, -1, -1]]) + sage: L.seifert_circles() + [[1, 11, 8], [2, 7, 12, 4, 5, 10], [3, 9, 6]] + sage: B = BraidGroup(2) + sage: L = Link(B([1, 1, 1])) + sage: L.seifert_circles() + [[1, 3, 5], [2, 4, 6]] + """ + available_segments = set(flatten(self.pd_code())) + result = [] + tails, heads = self._directions_of_edges() + while available_segments: + a = available_segments.pop() + if heads[a] == tails[a]: + result.append([a]) + else: + C = heads[a] + par = [] + while not a in par: + par.append(a) + if tails[C[(C.index(a) + 1) % 4]] == C: + a = C[(C.index(a) + 1) % 4] + else: + a = C[(C.index(a) - 1) % 4] + if a in available_segments: + available_segments.remove(a) + C = heads[a] + result.append(par) + return result + + def regions(self): + """ + Return the regions from the link diagram of ``self``. + + Regions are obtained always turning left at each crossing. + + Then the regions are represented as a list with the segments that form + its boundary, with a sign depending on the orientation of the segment + as part of the boundary. + + EXAMPLES:: + + sage: L = Link([[[-1, +2, -3, 4, +5, +1, -2, +6, +7, 3, -4, -7, -6,-5]],[-1, -1, -1, -1, 1, -1, 1]]) + sage: L.regions() + [[1, 7, 3, 11, 5], [2, -7], [4, -11], [6, -1], [8, -13, 10, -3], [9, 13], [12, -9, 14, -5], [-14, -8, -2, -6], [-12, -4, -10]] + sage: L = Link([[[1, -2, 3, -4, 2, -1, 4, -3]],[1, 1, -1, -1]]) + sage: L.regions() + [[1, 7, -4], [2, -5, -7], [3, -8, 5], [4, 8], [6, -1, -3], [-2, -6]] + sage: L = Link([[[-1, +2, 3, -4, 5, -6, 7, 8, -2, -5, +6, +1, -8, -3, 4, -7]],[-1, -1, -1, -1, 1, 1, -1, 1]]) + sage: L.regions() + [[1, 13, -8], [2, -9, -13], [3, -14, 9], [4, 16, 8, 14], [5, 11, 7, -16], [6, -11], [10, -5, -15, -3], [12, -1, -7], [15, -4], [-12, -6, -10, -2]] + sage: B = BraidGroup(2) + sage: L = Link(B([-1, -1, -1])) + sage: L.regions() + [[1, 3, 5], [2, -1], [4, -3], [6, -5], [-2, -6, -4]] + sage: L = Link([[[1, -2, 3, -4], [-1, 5, -3, 2, -5, 4]], [-1, 1, 1, -1, -1]]) + sage: L.regions() + [[1, -5], [2, -8, 4, 5], [3, 8], [6, -9, -2], [7, -3, 9], [10, -4, -7], [-10, -6, -1]] + sage: L = Link([[1, 2, 3, 3], [2, 5, 4, 4], [5, 7, 6, 6], [7, 1, 8, 8]]) + sage: L.regions() + [[-3], [-4], [-6], [-8], [1, 2, 5, 7], [-2, 3, -1, 8, -7, 6, -5, 4]] + + .. NOTE:: + + The link diagram is assumed to have only one completely isolated + component. This is because otherwise some regions would have + disconnected boundary. + + TESTS:: + + sage: B = BraidGroup(6) + sage: L = Link(B([1, 3, 5])) + sage: L.regions() + Traceback (most recent call last): + ... + NotImplementedError: can only have one isolated component + """ + if len(self._isolated_components()) != 1: + raise NotImplementedError("can only have one isolated component") + pd = self.pd_code() + tails, heads = self._directions_of_edges() + available_edges = set(flatten(pd)) + if len(pd) == 1: + if pd[0][0] == pd[0][1]: + return [[-pd[0][2]], [pd[0][0]], [pd[0][2], -pd[0][0]]] + else: + return [[pd[0][2]], [-pd[0][0]], [-pd[0][2], pd[0][0]]] + + loops = [i for i in available_edges if heads[i] == tails[i]] + available_edges = available_edges.union({-i for i in available_edges}) + regions = [] + + for edge in loops: + cros = heads[edge] + if cros[1] == edge: + regions.append([edge]) + else: + regions.append([-edge]) + available_edges.remove(edge) + available_edges.remove(-edge) + + while available_edges: + edge = available_edges.pop() + region = [] + while not edge in region: + region.append(edge) + if edge > 0 : + cros = heads[edge] + ind = cros.index(edge) + else: + cros = tails[-edge] + ind = cros.index(-edge) + next_edge = cros[(ind + 1) % 4] + if [next_edge] in regions: + region.append(-next_edge) + next_edge = cros[(ind - 1) % 4] + elif [-next_edge] in regions: + region.append(next_edge) + next_edge = cros[(ind - 1) % 4] + if tails[next_edge] == cros: + edge = next_edge + else: + edge = -next_edge + if edge in available_edges: + available_edges.remove(edge) + regions.append(region) + return regions + + def writhe(self): + """ + Return the writhe of ``self``. + + EXAMPLES:: + + sage: L = Link([[[1, -2, 3, -4, 2, -1, 4, -3]],[1, 1, -1, -1]]) + sage: L.writhe() + 0 + sage: L = Link([[[-1, 2, -3, 4, 5, 1, -2, 6, 7, 3, -4, -7, -6,-5]], + ....: [-1, -1, -1, -1, 1, -1, 1]]) + sage: L.writhe() + -3 + sage: L = Link([[[-1, 2, 3, -4, 5, -6, 7, 8, -2, -5, 6, 1, -8, -3, 4, -7]], + ....: [-1, -1, -1, -1, 1, 1, -1, 1]]) + sage: L.writhe() + -2 + """ + x = self.oriented_gauss_code() + pos = x[1].count(1) + neg = (-1) * x[1].count(-1) + return pos + neg + + def jones_polynomial(self, variab=None, skein_normalization=False, algorithm='jonesrep'): + r""" + Return the Jones polynomial of ``self``. + + The normalization is so that the unknot has Jones polynomial `1`. + If ``skein_normalization`` is ``True``, the variable of the result + is replaced by a itself to the power of `4`, so that the result + agrees with the conventions of [Lic]_ (which in particular differs + slightly from the conventions used otherwise in this class), had + one used the conventional Kauffman bracket variable notation directly. + + If ``variab`` is ``None`` return a polynomial in the variable `A` + or `t`, depending on the value ``skein_normalization``. In + particular, if ``skein_normalization`` is ``False``, return the + result in terms of the variable `t`, also used in [Lic]_. + + ALGORITHM: + + The calculation goes through one of two possible algorithms, + depending on the value of ``algorithm``. Possible values are + ``'jonesrep'`` which uses the Jones representation of a braid + representation of ``self`` to compute the polynomial of the + trace closure of the braid, and ``statesum`` which recursively + computes the Kauffman bracket of ``self``. Depending on how the + link is given, there might be significant time gains in using + one over the other. When the trace closure of the braid is + ``self``, the algorithms give the same result. + + INPUT: + + - ``variab`` -- variable (default: ``None``); the variable in the + resulting polynomial; if unspecified, use either a default variable + in `\ZZ[A,A^{-1}]` or the variable `t` in the symbolic ring + + - ``skein_normalization`` -- boolean (default: ``False``); determines + the variable of the resulting polynomial + + - ``algorithm`` -- string (default: ``'jonesrep'``); algorithm to use + and can be one of the following: + + * ``'jonesrep'`` - use the Jones representation of the braid + representation + * ``'statesum'`` - recursively computes the Kauffman bracket + + OUTPUT: + + If ``skein_normalization`` if ``False``, this returns an element + in the symbolic ring as the Jones polynomial of the link might + have fractional powers when the link is not a knot. Otherwise the + result is a Laurant polynomial in ``variab``. + + EXAMPLES: + + The unknot:: + + sage: B = BraidGroup(9) + sage: b = B([1, 2, 3, 4, 5, 6, 7, 8]) + sage: Link(b).jones_polynomial() + 1 + + The "monster" unknot:: + + sage: L = Link([[3,1,2,4],[8,9,1,7],[5,6,7,3],[4,18,6,5], + ....: [17,19,8,18],[9,10,11,14],[10,12,13,11], + ....: [12,19,15,13],[20,16,14,15],[16,20,17,2]]) + sage: L.jones_polynomial() + 1 + + The Ochiai unknot:: + + sage: L = Link([[[1,-2,-3,-8,-12,13,-14,15,-7,-1,2,-4,10,11,-13,12, + ....: -11,-16,4,3,-5,6,-9,7,-15,14,16,-10,8,9,-6,5]], + ....: [-1,-1,1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1]]) + sage: L.jones_polynomial() # long time + 1 + + Two unlinked unknots:: + + sage: B = BraidGroup(4) + sage: b = B([1, 3]) + sage: Link(b).jones_polynomial() + -sqrt(t) - 1/sqrt(t) + + The Hopf link:: + + sage: B = BraidGroup(2) + sage: b = B([-1,-1]) + sage: Link(b).jones_polynomial() + -1/sqrt(t) - 1/t^(5/2) + + Different representations of the trefoil and one of its mirror:: + + sage: B = BraidGroup(2) + sage: b = B([-1, -1, -1]) + sage: Link(b).jones_polynomial(skein_normalization=True) + -A^-16 + A^-12 + A^-4 + sage: Link(b).jones_polynomial() + 1/t + 1/t^3 - 1/t^4 + sage: B = BraidGroup(3) + sage: b = B([-1, -2, -1, -2]) + sage: Link(b).jones_polynomial(skein_normalization=True) + -A^-16 + A^-12 + A^-4 + sage: R. = LaurentPolynomialRing(GF(2)) + sage: Link(b).jones_polynomial(skein_normalization=True, variab=x) + x^-16 + x^-12 + x^-4 + sage: B = BraidGroup(3) + sage: b = B([1, 2, 1, 2]) + sage: Link(b).jones_polynomial(skein_normalization=True) + A^4 + A^12 - A^16 + + `K11n42` (the mirror of the "Kinoshita-Terasaka" knot) and `K11n34` + (the mirror of the "Conway" knot) in [KnotAtlas]_:: + + sage: B = BraidGroup(4) + sage: K11n42 = Link(B([1, -2, 3, -2, 3, -2, -2, -1, 2, -3, -3, 2, 2])) + sage: K11n34 = Link(B([1, 1, 2, -3, 2, -3, 1, -2, -2, -3, -3])) + sage: cmp(K11n42.jones_polynomial(), K11n34.jones_polynomial()) + 0 + + The two algorithms for computation give the same result when the + trace closure of the braid representation is the link itself:: + + sage: L = Link([[[-1, 2, -3, 4, 5, 1, -2, 6, 7, 3, -4, -7, -6, -5]], + ....: [-1, -1, -1, -1, 1, -1, 1]]) + sage: jonesrep = L.jones_polynomial(algorithm='jonesrep') + sage: statesum = L.jones_polynomial(algorithm='statesum') + sage: cmp(jonesrep, statesum) + 0 + + When we have thrown away unknots so that the trace closure of the + braid is not necessarily the link itself, this is only true up to a + power of the Jones polynomial of the unknot:: + + sage: B = BraidGroup(3) + sage: b = B([1]) + sage: L = Link(b) + sage: b.components_in_closure() + 2 + sage: L.number_of_components() + 1 + sage: b.jones_polynomial() + -sqrt(t) - 1/sqrt(t) + sage: L.jones_polynomial(algorithm='statesum') + 1 + + TESTS:: + + sage: L = Link([]) + sage: L.jones_polynomial(algorithm='statesum') + 1 + + sage: L.jones_polynomial(algorithm='other') + Traceback (most recent call last): + ... + ValueError: bad value of algorithm + """ + if algorithm == 'statesum': + poly = self._bracket() + t = poly.parent().gens()[0] + writhe = self.writhe() + jones = (poly * (-t)**(-3 * writhe)) + # Switch to the variable A to have the result agree with the output + # of the jonesrep algorithm + A = LaurentPolynomialRing(ZZ, 'A').gen() + jones = jones(A**-1) + + if skein_normalization: + if variab is None: + return jones + else: + return jones(variab) + else: + if variab is None: + variab = 't' + # We force the result to be in the symbolic ring because of the expand + return jones(SR(variab)**(ZZ(1)/ZZ(4))).expand() + elif algorithm == 'jonesrep': + return self.braid().jones_polynomial(variab, skein_normalization) + + raise ValueError("bad value of algorithm") + + @cached_method + def _bracket(self): + r""" + Return the Kaufmann bracket polynomial of the diagram of ``self``. + + Note that this is not an invariant of the link, but of the diagram. + In particular, it is not invariant under Reidemeister I moves. + + EXAMPLES:: + + sage: L = Link([[[-1, 2, 3, -4, 5, -6, 7, 8, -2, -5, 6, 1, -8, -3, 4, -7]], + ....: [-1, -1, -1, -1, 1, 1, -1, 1]]) + sage: L._bracket() + -t^-10 + 2*t^-6 - t^-2 + 2*t^2 - t^6 + t^10 - t^14 + sage: L = Link([[2, 1, 3, 4], [4, 3, 1, 2]]) + sage: L._bracket() + -t^-4 - t^4 + """ + t = LaurentPolynomialRing(ZZ, 't').gen() + pd_code = self.pd_code() + if not pd_code: + return t.parent().one() + if len(pd_code) == 1: + if pd_code[0][0] == pd_code[0][1]: + return -t**(-3) + else: + return -t**3 + + cross = pd_code[0] + rest = deepcopy(pd_code[1:]) + [a, b, c, d] = cross + if a == b and c == d and len(rest) > 0: + return (~t + t**(-5)) * Link(rest)._bracket() + elif a == d and c == b and len(rest) > 0: + return (t + t**5) * Link(rest)._bracket() + elif a == b: + for cross in rest: + if d in cross: + cross[cross.index(d)] = c + return -t**(-3) * Link(rest)._bracket() + elif a == d: + for cross in rest: + if c in cross: + cross[cross.index(c)] = b + return -t**3 * Link(rest)._bracket() + elif c == b: + for cross in rest: + if d in cross: + cross[cross.index(d)] = a + return -t**3 * Link(rest)._bracket() + elif c == d: + for cross in rest: + if b in cross: + cross[cross.index(b)] = a + return -t**(-3) * Link(rest)._bracket() + else: + rest_2 = deepcopy(rest) + for cross in rest: + if d in cross: + cross[cross.index(d)] = a + if c in cross: + cross[cross.index(c)] = b + for cross in rest_2: + if d in cross: + cross[cross.index(d)] = c + if b in cross: + cross[cross.index(b)] = a + return t * Link(rest)._bracket() + ~t * Link(rest_2)._bracket() + + def _isolated_components(self): + r""" + Return the PD codes of the isolated components of ``self``. + + Isolated components are links corresponding to subdiagrams that + do not have any common crossing. + + EXAMPLES:: + + sage: L = Link([[1, 1, 2, 2], [3, 3, 4, 4]]) + sage: L._isolated_components() + [[[1, 1, 2, 2]], [[3, 3, 4, 4]]] + """ + G = Graph() + for c in self.pd_code(): + G.add_vertex(tuple(c)) + for i in range(G.num_verts()-1): + for j in range(i, G.num_verts()): + if len(set(G.vertices()[i]).intersection(G.vertices()[j])) > 0: + G.add_edge(G.vertices()[i], G.vertices()[j]) + return [[list(i) for i in j] for j in G.connected_components()] + + def plot(self, gap=0.1, component_gap=0.5, solver=None, **kwargs): + r""" + Plot ``self``. + + INPUT: + + - ``gap`` -- (default: 0.1) the size of the blank gap left for + the crossings + + - ``component_gap`` -- (default: 0.5) the gap between isolated + components + + - ``solver`` -- the linear solver to use, see + :class:`~sage.numerical.mip.MixedIntegerLinearProgram`. + + The usual keywords for plots can be used here too. + + EXAMPLES: + + We construct the simplest version of the unknot:: + + sage: L = Link([[2, 1, 1, 2]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + B = BraidGroup(2) + L = Link([[2, 1, 1, 2]]) + sphinx_plot(L.plot()) + + We construct a more interesting example of the unknot:: + + sage: L = Link([[2, 1, 4, 5], [3, 5, 6, 7], [4, 1, 9, 6], [9, 2, 3, 7]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[2,1,4,5], [3,5,6,7], [4,1,9,6], [9,2,3,7]]) + sphinx_plot(L.plot()) + + The "monster" unknot:: + + sage: L = Link([[3,1,2,4],[8,9,1,7],[5,6,7,3],[4,18,6,5], + ....: [17,19,8,18],[9,10,11,14],[10,12,13,11], + ....: [12,19,15,13],[20,16,14,15],[16,20,17,2]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[3,1,2,4],[8,9,1,7],[5,6,7,3],[4,18,6,5], + [17,19,8,18],[9,10,11,14],[10,12,13,11], + [12,19,15,13],[20,16,14,15],[16,20,17,2]]) + sphinx_plot(L.plot()) + + The Ochiai unknot:: + + sage: L = Link([[[1,-2,-3,-8,-12,13,-14,15,-7,-1,2,-4,10,11,-13,12, + ....: -11,-16,4,3,-5,6,-9,7,-15,14,16,-10,8,9,-6,5]], + ....: [-1,-1,1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[[1,-2,-3,-8,-12,13,-14,15,-7,-1,2,-4,10,11,-13,12, + -11,-16,4,3,-5,6,-9,7,-15,14,16,-10,8,9,-6,5]], + [-1,-1,1,1,1,1,-1,1,1,-1,1,-1,-1,-1,-1,-1]]) + sphinx_plot(L.plot()) + + One of the representations of the trefoil knot:: + + sage: L = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]]) + sage: L.plot() + Graphics object consisting of 14 graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[1, 5, 2, 4], [5, 3, 6, 2], [3, 1, 4, 6]]) + sphinx_plot(L.plot()) + + The figure-eight knot:: + + sage: L = Link([[2, 1, 4, 5], [5, 6, 7, 3], [6, 4, 1, 9], [9, 2, 3, 7]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[2,1,4,5], [5,6,7,3], [6,4,1,9], [9,2,3,7]]) + sphinx_plot(L.plot()) + + The knot `K11n121` in [KnotAtlas]_:: + + sage: L = Link([[4,2,5,1], [10,3,11,4], [5,16,6,17], [7,12,8,13], + ....: [18,9,19,10], [2,11,3,12], [13,20,14,21], [15,6,16,7], + ....: [22,18,1,17], [8,19,9,20], [21,14,22,15]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[4,2,5,1], [10,3,11,4], [5,16,6,17], [7,12,8,13], + [18,9,19,10], [2,11,3,12], [13,20,14,21], [15,6,16,7], + [22,18,1,17], [8,19,9,20], [21,14,22,15]]) + sphinx_plot(L.plot()) + + One of the representations of the Hopf link:: + + sage: L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[1, 4, 2, 3], [4, 1, 3, 2]]) + sphinx_plot(L.plot()) + + Plotting links with multiple isolated components:: + + sage: L = Link([[[-1, 2, -3, 1, -2, 3], [4, -5, 6, -4, 5, -6]], [1, 1, 1, 1, 1, 1]]) + sage: L.plot() + Graphics object consisting of ... graphics primitives + + .. PLOT:: + :width: 300 px + + L = Link([[[-1,2,-3,1,-2,3], [4,-5,6,-4,5,-6]], [1,1,1,1,1,1]]) + sphinx_plot(L.plot()) + + TESTS: + + Check that :trac:`20315` is fixed:: + + sage: L = Link([[2,1,4,5], [5,6,7,3], [6,4,1,9], [9,2,3,7]]) + sage: L.plot(solver='GLPK') + Graphics object consisting of ... graphics primitives + sage: L.plot(solver='Coin') # optional - cbc + Graphics object consisting of ... graphics primitives + sage: L.plot(solver='CPLEX') # optional - CPLEX + Graphics object consisting of ... graphics primitives + sage: L.plot(solver='Gurobi') # optional - Gurobi + Graphics object consisting of ... graphics primitives + """ + comp = self._isolated_components() + # Handle isolated components individually + if len(comp) > 1: + L1 = Link(comp[0]) + L2 = Link(flatten(comp[1:], max_level=1)) + P1 = L1.plot(gap, **kwargs) + P2 = L2.plot(gap, **kwargs) + xtra = P1.get_minmax_data()['xmax'] + component_gap - P2.get_minmax_data()['xmin'] + for P in P2: + if hasattr(P, 'path'): + for p in P.path[0]: + p[0] += xtra + for p in P.vertices: + p[0] += xtra + else: + P.xdata = [p + xtra for p in P.xdata] + return P1 + P2 + + if not 'color' in kwargs: + kwargs['color'] = 'blue' + if not 'axes' in kwargs: + kwargs['axes'] = False + if not 'aspect_ratio' in kwargs: + kwargs['aspect_ratio'] = 1 + + from sage.plot.line import line + from sage.plot.bezier_path import bezier_path + from sage.plot.circle import circle + + # Special case for the unknot + if not self.pd_code(): + return circle((0,0), ZZ(1)/ZZ(2), **kwargs) + + # The idea is the same followed in spherogram, but using MLP instead of + # network flows. + # We start by computing a way to bend the edges left or right + # such that the resulting regions are in fact closed regions + # with straight angles, and using the minimal number of bends. + regions = sorted(self.regions(), key=len) + regions = regions[:-1] + edges = list(set(flatten(self.pd_code()))) + edges.sort() + MLP = MixedIntegerLinearProgram(maximization=False, solver=solver) + # v will be the list of variables in the MLP problem. There will be + # two variables for each edge: number of right bendings and number of + # left bendings (at the end, since we are minimizing the total, only one + # of each will be nonzero + v = MLP.new_variable(nonnegative=True, integer=True) + # one condition for each region + for i in range(len(regions)): + cond = 0 + r = regions[i] + for e in r: + if e > 0: + cond = cond + v[2*edges.index(e)] - v[2*edges.index(e) + 1] + else: + cond = cond - v[2*edges.index(-e)] + v[2*edges.index(-e) + 1] + MLP.add_constraint(cond == 4 - len(r)) + MLP.set_objective(MLP.sum(v.values())) + MLP.solve() + # we store the result in a vector s packing right bends as negative left ones + s = range(len(edges)) + values = MLP.get_values(v) + for i in range(len(edges)): + s[i] = int(values[2*i] - values[2*i + 1]) + # segments represents the different parts of the previos edges after bending + segments = {e: [(e,i) for i in range(abs(s[edges.index(e)])+1)] for e in edges} + pieces = {tuple(i): [i] for j in segments.values() for i in j} + nregions = [] + for r in regions: + nregion = [] + for e in r: + if e > 0: + rev = segments[e][:-1] + sig = sign(s[edges.index(e)]) + nregion += [[a, sig] for a in rev] + nregion.append([segments[e][-1], 1]) + else: + rev = segments[-e][1:] + rev.reverse() + sig = sign(s[edges.index(-e)]) + nregion+=[[a, -sig] for a in rev] + nregion.append([segments[-e][0], 1]) + nregions.append(nregion) + N = max(segments.keys()) + 1 + segments = [i for j in segments.values() for i in j] + badregions = [nr for nr in nregions if any(-1 == x[1] for x in nr)] + while len(badregions) > 0: + badregion = badregions[0] + badturns = [] + a = 0 + while badregion[a][1] != -1: + a += 1 + c = -1 + b = a + while c != 2: + if b == len(badregion)-1: + b = 0 + else: + b += 1 + c += badregion[b][1] + otherregion = [nr for nr in nregions + if any(badregion[b][0] == x[0] for x in nr)] + if len(otherregion) == 1: + otherregion = None + elif otherregion[0] == badregion: + otherregion = otherregion[1] + else: + otherregion = otherregion[0] + N1 = N + N = N + 2 + N2 = N1 + 1 + segments.append(N1) + segments.append(N2) + if type(badregion[b][0]) in (int, Integer): + segmenttoadd = [x for x in pieces.keys() + if badregion[b][0] in pieces[x]] + if len(segmenttoadd) > 0: + pieces[segmenttoadd[0]].append(N2) + else: + pieces[tuple(badregion[b][0])].append(N2) + + if a < b: + r1 = badregion[:a] + [[badregion[a][0],0], [N1,1]] + badregion[b:] + r2 = badregion[a+1:b] + [[N2,1],[N1,1]] + else: + r1 = badregion[b:a] + [[badregion[a][0],0], [N1,1]] + r2 = badregion[:b] + [[N2,1],[N1,1]] + badregion[a+1:] + + if otherregion: + c = [x for x in otherregion if badregion[b][0] == x[0]] + c = otherregion.index(c[0]) + otherregion.insert(c+1, [N2,otherregion[c][1]]) + otherregion[c][1] = 0 + nregions.remove(badregion) + nregions.append(r1) + nregions.append(r2) + badregions = [nr for nr in nregions if any(x[1] == -1 for x in nr)] + MLP = MixedIntegerLinearProgram(maximization=False, solver=solver) + v = MLP.new_variable(nonnegative=True, integer=True) + for e in segments: + MLP.set_min(v[e], 1) + for r in nregions: + horp = [] + horm = [] + verp = [] + verm = [] + direction = 0 + for se in r: + if direction % 4 == 0: + horp.append(v[se[0]]) + elif direction == 1: + verp.append(v[se[0]]) + elif direction == 2: + horm.append(v[se[0]]) + elif direction == 3: + verm.append(v[se[0]]) + if se[1] == 1: + direction += 1 + MLP.add_constraint(MLP.sum(horp) - MLP.sum(horm) == 0) + MLP.add_constraint(MLP.sum(verp) - MLP.sum(verm) == 0) + MLP.set_objective(MLP.sum(v.values())) + solved = MLP.solve() + v = MLP.get_values(v) + lengths = {piece: sum(v[a] for a in pieces[piece]) for piece in pieces} + image = line([], **kwargs) + crossings = {tuple(self.pd_code()[0]): (0,0,0)} + availables = self.pd_code()[1:] + used_edges = [] + horizontal_eq = 0 + vertical_eq = 0 + ims = line([], **kwargs) + while len(used_edges) < len(edges): + i = 0 + j = 0 + while crossings.keys()[i][j] in used_edges: + if j < 3: + j += 1 + else: + j = 0 + i+=1 + c = crossings.keys()[i] + e = c[j] + used_edges.append(e) + direction = (crossings[c][2] - c.index(e)) % 4 + orien = self.orientation()[self.pd_code().index(list(c))] + if s[edges.index(e)] < 0: + turn = -1 + else: + turn = 1 + lengthse = [lengths[(e,i)] for i in range(abs(s[edges.index(e)])+1)] + if c.index(e) == 0 or (c.index(e) == 1 and orien == 1) or (c.index(e) == 3 and orien == -1): + turn = -turn + lengthse.reverse() + tailshort = (c.index(e) % 2 == 0) + x0 = crossings[c][0] + y0 = crossings[c][1] + im = [] + for l in lengthse: + if direction == 0: + x1 = x0 + l + y1 = y0 + elif direction == 1: + x1 = x0 + y1 = y0 + l + elif direction == 2: + x1 = x0 - l + y1 = y0 + elif direction == 3: + x1 = x0 + y1 = y0 -l + im.append(([[x0,y0],[x1,y1]], l, direction)) + direction = (direction + turn) % 4 + x0 = x1 + y0 = y1 + direction = (direction - turn) % 4 + c2 = [ee for ee in availables if e in ee] + if len(c2) == 1: + availables.remove(c2[0]) + crossings[tuple(c2[0])] = (x1, y1, (direction + c2[0].index(e) + 2) % 4) + c2 = [ee for ee in self.pd_code() if e in ee and ee != list(c)] + if not c2: + headshort = not tailshort + else: + headshort = (c2[0].index(e) % 2 == 0) + a = deepcopy(im[0][0]) + b = deepcopy(im[-1][0]) + if tailshort: + im[0][0][0][0] += cmp(a[1][0], im[0][0][0][0]) * gap + im[0][0][0][1] += cmp(a[1][1], im[0][0][0][1]) * gap + if headshort: + im[-1][0][1][0] -= cmp(b[1][0], im[-1][0][0][0]) * gap + im[-1][0][1][1] -= cmp(b[1][1], im[-1][0][0][1]) * gap + l = line([], **kwargs) + c = 0 + p = im[0][0][0] + if len(im) == 4 and max([x[1] for x in im]) == 1: + l = bezier_path([[im[0][0][0], im[0][0][1], im[-1][0][0], im[-1][0][1]]], **kwargs) + p = im[-1][0][1] + else: + while c < len(im)-1: + if im[c][1] > 1: + (a, b) = im[c][0] + if b[0] > a[0]: + e = [b[0] - 1, b[1]] + elif b[0] < a[0]: + e = [b[0] + 1, b[1]] + elif b[1] > a[1]: + e = [b[0], b[1] - 1] + elif b[1] < a[1]: + e = [b[0] , b[1] + 1] + l += line((p, e), **kwargs) + p = e + if im[c+1][1] == 1 and c < len(im) - 2: + xr = round(im[c+2][0][1][0]) + yr = round(im[c+2][0][1][1]) + xp = xr - im[c+2][0][1][0] + yp = yr - im[c+2][0][1][1] + q = [p[0] + im[c+1][0][1][0] - im[c+1][0][0][0] - xp, + p[1] + im[c+1][0][1][1] - im[c+1][0][0][1] - yp] + l += bezier_path([[p, im[c+1][0][0], im[c+1][0][1], q]], **kwargs) + c += 2 + p = q + else: + if im[c+1][1] == 1: + q = im[c+1][0][1] + else: + q = [im[c+1][0][0][0] + sign(im[c+1][0][1][0] - im[c+1][0][0][0]), + im[c+1][0][0][1] + sign(im[c+1][0][1][1] - im[c+1][0][0][1])] + l += bezier_path([[p, im[c+1][0][0], q]], **kwargs) + p = q + c += 1 + l += line([p, im[-1][0][1]], **kwargs) + image += l + ims += sum(line(a[0], **kwargs) for a in im) + return image + diff --git a/src/sage/lfunctions/dokchitser.py b/src/sage/lfunctions/dokchitser.py index 13aeed486f7..5394de900e2 100644 --- a/src/sage/lfunctions/dokchitser.py +++ b/src/sage/lfunctions/dokchitser.py @@ -316,7 +316,8 @@ def init_coeffs(self, v, cutoff=1, sage: sum(a[n]/float(n)^14 for n in range(1,1000)) 0.9985830631627459 - Illustrate that one can give a list of complex numbers for v (see trac 10937):: + Illustrate that one can give a list of complex numbers for v + (see :trac:`10937`):: sage: L2 = Dokchitser(conductor=1, gammaV=[0,1], weight=12, eps=1) sage: L2.init_coeffs(list(delta_qexp(1000))[1:]) @@ -326,7 +327,7 @@ def init_coeffs(self, v, cutoff=1, TESTS: Verify that setting the `w` parameter does not raise an error - (see trac 10937). Note that the meaning of `w` does not seem to + (see :trac:`10937`). Note that the meaning of `w` does not seem to be documented anywhere in Dokchitser's package yet, so there is no claim that the example below is meaningful! :: diff --git a/src/sage/lfunctions/zero_sums.pyx b/src/sage/lfunctions/zero_sums.pyx index 0735ca01423..2810e885a04 100644 --- a/src/sage/lfunctions/zero_sums.pyx +++ b/src/sage/lfunctions/zero_sums.pyx @@ -1732,7 +1732,7 @@ cdef class LFunctionZeroSum_EllipticCurve(LFunctionZeroSum_abstract): REFERENCES: - .. [Bob-13] J.W. Bober. Conditionally bounding analytic ranks of elliptic curves. + .. [Bob-13] \J.W. Bober. Conditionally bounding analytic ranks of elliptic curves. ANTS 10. http://msp.org/obs/2013/1-1/obs-v1-n1-p07-s.pdf """ diff --git a/src/sage/libs/arb/acb_mat.pxd b/src/sage/libs/arb/acb_mat.pxd new file mode 100644 index 00000000000..bc6da5cc18f --- /dev/null +++ b/src/sage/libs/arb/acb_mat.pxd @@ -0,0 +1,24 @@ +from sage.libs.arb.types cimport acb_t, acb_mat_t + +cdef extern from "acb_mat.h": + unsigned int acb_mat_nrows(acb_mat_t mat) + unsigned int acb_mat_ncols(acb_mat_t mat) + acb_t acb_mat_entry(acb_mat_t mat, unsigned long i, unsigned long j) + + void acb_mat_init(acb_mat_t mat, long r, long c) + void acb_mat_clear(acb_mat_t mat) + + void acb_mat_set(acb_mat_t dest, const acb_mat_t src) + + void acb_mat_zero(acb_mat_t mat) + void acb_mat_one(acb_mat_t mat) + + void acb_mat_add(acb_mat_t res, const acb_mat_t mat1, + const acb_mat_t mat2, long prec) + void acb_mat_sub(acb_mat_t res, const acb_mat_t mat1, + const acb_mat_t mat2, long prec); + void acb_mat_mul(acb_mat_t res, const acb_mat_t mat1, + const acb_mat_t mat2, long prec); + + void acb_mat_scalar_addmul_acb(acb_mat_t B, const acb_mat_t A, + const acb_t c, long prec) diff --git a/src/sage/libs/arb/types.pxd b/src/sage/libs/arb/types.pxd index d1a79eb4bab..2e30dc9cdfc 100644 --- a/src/sage/libs/arb/types.pxd +++ b/src/sage/libs/arb/types.pxd @@ -33,3 +33,7 @@ cdef extern from "acb.h": ctypedef acb_struct * acb_ptr ctypedef const acb_struct * acb_srcptr +cdef extern from "acb_mat.h": + ctypedef struct acb_mat_struct: + pass + ctypedef acb_mat_struct[1] acb_mat_t diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index 73d40a73b9d..374f2887664 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -354,9 +354,9 @@ def parabolic_kazhdan_lusztig_polynomial(self, u, v, J, constant_term_one=True): REFERENCES: - .. [Deodhar1987] V.V. Deodhar, On some geometric aspects of Bruhat orderings II. The parabolic analogue of Kazhdan-Lusztig polynomials, J. Alg. 111 (1987) 483-506. + .. [Deodhar1987] \V.V. Deodhar, On some geometric aspects of Bruhat orderings II. The parabolic analogue of Kazhdan-Lusztig polynomials, J. Alg. 111 (1987) 483-506. - .. [LeclercThibon1998] B. Leclerc, J.-Y. Thibon, Littlewood-Richardson coefficients and Kazhdan-Lusztig polynomials, http://front.math.ucdavis.edu/9809.5122 + .. [LeclercThibon1998] \B. Leclerc, J.-Y. Thibon, Littlewood-Richardson coefficients and Kazhdan-Lusztig polynomials, http://front.math.ucdavis.edu/9809.5122 EXAMPLES:: diff --git a/src/sage/libs/eclib/mwrank.pyx b/src/sage/libs/eclib/mwrank.pyx index 7f6e36a10d1..6d9d39dbee0 100644 --- a/src/sage/libs/eclib/mwrank.pyx +++ b/src/sage/libs/eclib/mwrank.pyx @@ -25,7 +25,7 @@ import sys from sage.libs.eclib cimport bigint, Curvedata, mw, two_descent include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" cdef extern from "wrap.cpp": ### misc functions ### @@ -72,7 +72,7 @@ cdef object string_sigoff(char* s): sig_off() # Makes a python string and deletes what is pointed to by s. t = str(s) - sage_free(s) + sig_free(s) return t # set the default diff --git a/src/sage/libs/eclib/newforms.pyx b/src/sage/libs/eclib/newforms.pyx index 647c7a2a461..02aaaa4709a 100644 --- a/src/sage/libs/eclib/newforms.pyx +++ b/src/sage/libs/eclib/newforms.pyx @@ -162,7 +162,7 @@ cdef class ECModularSymbol: sage: M("garbage") Traceback (most recent call last): ... - TypeError: Unable to convert garbage to a Cusp + TypeError: unable to convert 'garbage' to a cusp sage: M(7/5) 3 """ diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx index a1108625aa7..bbb2a7a2a9f 100644 --- a/src/sage/libs/fes.pyx +++ b/src/sage/libs/fes.pyx @@ -75,7 +75,7 @@ cdef extern from "fes_interface.h": include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.rings.integer import Integer from sage.rings.infinity import Infinity @@ -190,11 +190,11 @@ def exhaustive_search(eqs, max_sols=Infinity, verbose=False): # ------- initialize a data-structure to communicate the equations to the library - cdef int ***coeffs = sage_calloc(len(eqs), sizeof(int **)) + cdef int ***coeffs = sig_calloc(len(eqs), sizeof(int **)) for e,f in enumerate(eqs): - coeffs[e] = sage_calloc(degree+1, sizeof(int *)) + coeffs[e] = sig_calloc(degree+1, sizeof(int *)) for d in range(degree+1): - coeffs[e][d] = sage_calloc(binomial(n,d), sizeof(int)) + coeffs[e][d] = sig_calloc(binomial(n,d), sizeof(int)) for m in f: # we enumerate the monomials of f d = m.degree() @@ -229,9 +229,9 @@ def exhaustive_search(eqs, max_sols=Infinity, verbose=False): if coeffs[e] != NULL: for d in range(degree+1): if coeffs[e][d] != NULL: - sage_free(coeffs[e][d]) - sage_free(coeffs[e]) - sage_free(coeffs) + sig_free(coeffs[e][d]) + sig_free(coeffs[e]) + sig_free(coeffs) # ------ convert (packed) solutions to suitable format dict_sols = [] diff --git a/src/sage/libs/flint/fmpz_mat.pxd b/src/sage/libs/flint/fmpz_mat.pxd index 4b7ce9bc33c..b233517e931 100644 --- a/src/sage/libs/flint/fmpz_mat.pxd +++ b/src/sage/libs/flint/fmpz_mat.pxd @@ -21,7 +21,8 @@ cdef extern from "flint/fmpz_mat.h": void fmpz_mat_add(fmpz_mat_t C, const fmpz_mat_t A, const fmpz_mat_t B) void fmpz_mat_sub(fmpz_mat_t C, const fmpz_mat_t A, const fmpz_mat_t B) void fmpz_mat_pow(fmpz_mat_t C, const fmpz_mat_t A, unsigned long n) - int fmpz_mat_is_zero(const fmpz_mat_t mat) + bint fmpz_mat_is_zero(const fmpz_mat_t mat) + bint fmpz_mat_is_one(const fmpz_mat_t mat) void fmpz_mat_charpoly(fmpz_poly_t cp, const fmpz_mat_t mat) void fmpz_mat_det(fmpz_t det, const fmpz_mat_t A) int fmpz_mat_inv(fmpz_mat_t Ainv, fmpz_t den, const fmpz_mat_t A) diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index b23ac779b4a..c0a62be0990 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -26,6 +26,7 @@ from cpython.sequence cimport * from sage.structure.sage_object cimport SageObject from sage.rings.integer cimport Integer +from sage.libs.flint.fmpz_poly cimport * cdef class Fmpz_poly(SageObject): @@ -118,7 +119,7 @@ cdef class Fmpz_poly(SageObject): """ cdef char* ss = fmpz_poly_get_str(self.poly) cdef object s = ss - sage_free(ss) + sig_free(ss) return s def degree(self): diff --git a/src/sage/libs/flint/nmod_poly_linkage.pxi b/src/sage/libs/flint/nmod_poly_linkage.pxi index d9e65f7970f..e06856a3260 100644 --- a/src/sage/libs/flint/nmod_poly_linkage.pxi +++ b/src/sage/libs/flint/nmod_poly_linkage.pxi @@ -20,18 +20,18 @@ AUTHOR: from sage.libs.flint.nmod_poly cimport * from sage.libs.flint.ulong_extras cimport * -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" cdef inline celement *celement_new(unsigned long n): - cdef celement *g = sage_malloc(sizeof(nmod_poly_t)) + cdef celement *g = sig_malloc(sizeof(nmod_poly_t)) nmod_poly_init(g, n) return g cdef inline int celement_delete(nmod_poly_t e, unsigned long n): nmod_poly_clear(e) - sage_free(e) + sig_free(e) cdef inline int celement_construct(nmod_poly_t e, unsigned long n): """ diff --git a/src/sage/libs/flint/padic.pxd b/src/sage/libs/flint/padic.pxd index e1a8a9b614f..ce827746638 100644 --- a/src/sage/libs/flint/padic.pxd +++ b/src/sage/libs/flint/padic.pxd @@ -17,7 +17,7 @@ cdef extern from "flint/padic.h": long padic_val(padic_t) # Context ****************************************************************** - void padic_ctx_init(padic_ctx_t ctx, fmpz_t p, long N, padic_print_mode mode) + void padic_ctx_init(padic_ctx_t ctx, fmpz_t p, long min, long max, padic_print_mode mode) void padic_ctx_clear(padic_ctx_t ctx) int _padic_ctx_pow_ui(fmpz_t rop, unsigned long e, padic_ctx_t ctx) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index 80e5601b71e..a7b7946acab 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1097,10 +1097,11 @@ cdef class GapElement(RingElement): m = len(entries) // n if len(entries) % n != 0: raise ValueError('not a rectangular list of lists') - from sage.matrix.constructor import matrix + from sage.matrix.matrix_space import MatrixSpace if ring is None: ring = entries.DefaultRing().sage() - return matrix(ring, n, m, [ x.sage(ring=ring) for x in entries ]) + MS = MatrixSpace(ring, n, m) + return MS([x.sage(ring=ring) for x in entries]) _matrix_ = matrix diff --git a/src/sage/libs/gap/gap_includes.pxd b/src/sage/libs/gap/gap_includes.pxd index bd945c7872a..fa23ba5b41a 100644 --- a/src/sage/libs/gap/gap_includes.pxd +++ b/src/sage/libs/gap/gap_includes.pxd @@ -131,7 +131,7 @@ cdef extern from "gap/objects.h": cdef int libGAP_LAST_TESTING_TNUM cdef extern from "gap/read.h": - void* libGAP_ReadEvalCommand(libGAP_Obj context) + void* libGAP_ReadEvalCommand(libGAP_Obj context, libGAP_UInt *dualSemicolon) void* libGAP_ReadEvalFile() void* libGAP_ReadEvalResult bint libGAP_READ_ERROR() diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index 52a75be9216..d62b8e9ed34 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -280,7 +280,7 @@ cdef libGAP_Obj gap_eval(str gap_string) except? NULL: libgap_start_interaction(cmd) try: sig_on() - status = libGAP_ReadEvalCommand(libGAP_BottomLVars) + status = libGAP_ReadEvalCommand(libGAP_BottomLVars, NULL) if status != libGAP_STATUS_END: libgap_call_error_handler() sig_off() @@ -464,7 +464,7 @@ def command(command_string): libgap_start_interaction(cmd) try: sig_on() - status = libGAP_ReadEvalCommand(libGAP_BottomLVars) + status = libGAP_ReadEvalCommand(libGAP_BottomLVars, NULL) if status != libGAP_STATUS_END: libgap_call_error_handler() sig_off() diff --git a/src/sage/libs/giac.py b/src/sage/libs/giac.py index bb394b4a8d5..0baf3d6aafd 100644 --- a/src/sage/libs/giac.py +++ b/src/sage/libs/giac.py @@ -166,6 +166,8 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** sage: P = PolynomialRing(GF(previous_prime(2**31)), 6, 'x') # optional - giacpy sage: I = sage.rings.ideal.Cyclic(P) # optional - giacpy sage: B=gb_giac(I.gens());B # optional - giacpy + + // Groebner basis computation time ... Polynomial Sequence with 45 Polynomials in 6 Variables sage: B.is_groebner() # optional - giacpy True @@ -181,6 +183,8 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** If successfull, error probability is less than 1e-16 ... sage: sage.structure.proof.all.polynomial(True) # optional - giacpy sage: B2 = gb_giac(I.gens()) # optional - giacpy, long time (4s) + + // Groebner basis computation time... sage: B1==B2 # optional - giacpy, long time True sage: B1.is_groebner() # optional - giacpy, long time (20s) @@ -231,6 +235,8 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** sage: libgiac.purge('x2'),libgiac.purge('x4') # optional - giacpy (22, whywouldyoudothis) sage: gb_giac(I) # optional - giacpy, long time (3s) + + // Groebner basis computation time... Polynomial Sequence with 74 Polynomials in 8 Variables sage: I=ideal(P(0),P(0)) # optional - giacpy @@ -249,7 +255,7 @@ def groebner_basis(gens, proba_epsilon=None, threads=None, prot=False, *args, ** gens = gens.gens() # get the ring from gens - P = iter(gens).next().parent() + P = next(iter(gens)).parent() K = P.base_ring() p = K.characteristic() diff --git a/src/sage/libs/glpk/env.pxd b/src/sage/libs/glpk/env.pxd index 1cc0cd92e46..80d5153d3c9 100644 --- a/src/sage/libs/glpk/env.pxd +++ b/src/sage/libs/glpk/env.pxd @@ -8,5 +8,6 @@ cdef extern from "glpk.h": int glp_open_tee(const char *fname) int glp_close_tee() void glp_error_hook(void (*func)(void *info), void *info) + int glp_at_error() void glp_mem_limit(int limit) void glp_mem_usage(int *count, int *cpeak, size_t *total, size_t *tpeak) diff --git a/src/sage/libs/glpk/error.pyx b/src/sage/libs/glpk/error.pyx new file mode 100644 index 00000000000..084341bb130 --- /dev/null +++ b/src/sage/libs/glpk/error.pyx @@ -0,0 +1,88 @@ +""" +Error handler for the GLPK library +""" + +#***************************************************************************** +# Copyright (C) 2015 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/ +#***************************************************************************** + +include "cysignals/signals.pxi" +from .env cimport * +from cpython.exc cimport PyErr_SetObject +from sage.numerical.mip import MIPSolverException + +class GLPKError(MIPSolverException): + """ + An error raised by the GLPK library. + + EXAMPLES:: + + sage: from sage.libs.glpk.error import GLPKError + sage: raise GLPKError("trouble!") + Traceback (most recent call last): + ... + GLPKError: trouble! + """ + pass + + +# Global error message string +cdef error_message = "" + + +cdef int sage_glpk_term_hook(void *info, const char *s) with gil: + """ + A hook to intercept all output written by GLPK. + """ + global error_message + if glp_at_error(): + # Save error message and skip normal printing + error_message += s + return 1 + else: + # Normal non-error output: the return value 0 means that GLPK + # will write the output as usual. + return 0 + + +cdef void sage_glpk_error_hook(void *info) with gil: + """ + A hook to intercept GLPK errors. + """ + global error_message + PyErr_SetObject(GLPKError, error_message.strip()) + error_message = "" + sig_error() + + +def setup_glpk_error_handler(): + r""" + Setup the GLPK error handler. Called automatically when this module + is imported at Sage startup. + + TESTS:: + + sage: cython(''' + ....: #clib glpk z gmp + ....: from sage.libs.glpk.env cimport glp_term_out + ....: include "cysignals/signals.pxi" + ....: + ....: sig_on() + ....: glp_term_out(12345) # invalid value + ....: sig_off() + ....: ''') + Traceback (most recent call last): + ... + GLPKError: glp_term_out: flag = 12345; invalid parameter + Error detected in file env/stdout.c at line ... + """ + glp_term_hook(sage_glpk_term_hook, NULL) + glp_error_hook(sage_glpk_error_hook, NULL) + +setup_glpk_error_handler() diff --git a/src/sage/libs/linkages/padics/API.pxi b/src/sage/libs/linkages/padics/API.pxi index 5cd0e9d7ee5..7555bc45b56 100644 --- a/src/sage/libs/linkages/padics/API.pxi +++ b/src/sage/libs/linkages/padics/API.pxi @@ -53,8 +53,6 @@ file gives the function signatures. The gluing file should ctypedef celement as appropriate. -Each linkage file should include stdsage.pxi - .. NOTE:: This particular file is not included anywhere. It just defines @@ -366,7 +364,7 @@ cdef inline int csetzero(celement out, PowComputer_class prime_pow) except -1: """ pass -cdef inline bint cisone(celement out, PowComputer_class prime_pow) except -1: +cdef inline bint cisone(celement a, PowComputer_class prime_pow) except -1: """ Returns whether this element is equal to 1. @@ -381,7 +379,7 @@ cdef inline bint cisone(celement out, PowComputer_class prime_pow) except -1: """ pass -cdef inline bint ciszero(celement out, PowComputer_class prime_pow) except -1: +cdef inline bint ciszero(celement a, PowComputer_class prime_pow) except -1: """ Returns whether this element is equal to 0. diff --git a/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi new file mode 100644 index 00000000000..dd39bcd7ada --- /dev/null +++ b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi @@ -0,0 +1,779 @@ +""" +This linkage file implements the API for unramified extensions of the padics +using FLINT's fmpz_poly_t. + +AUTHORS: + +- David Roe, Julian Rueth (2013-03-21) -- initial version + +""" +#***************************************************************************** +# Copyright (C) 2013 David Roe +# Julian Rueth +# +# 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/ +#***************************************************************************** + +include "sage/ext/stdsage.pxi" +include "cysignals/signals.pxi" +from cpython.list cimport PyList_Check, PyList_New, PyList_Append + +from sage.rings.padics.common_conversion cimport cconv_mpz_t_out_shared, cconv_mpz_t_shared, cconv_mpq_t_out_shared, cconv_mpq_t_shared, cconv_shared + +from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational +from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +from sage.rings.finite_rings.integer_mod cimport IntegerMod_abstract +from sage.rings.finite_rings.integer_mod_ring import Zmod + +from sage.libs.flint.fmpz cimport * +from sage.libs.flint.fmpz_poly cimport * + +cdef inline int cconstruct(celement value, PowComputer_ prime_pow) except -1: + """ + Construct a new element. + + INPUT: + + - ``unit`` -- an ``celement`` to be initialized. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_init(value) + +cdef inline int cdestruct(celement value, PowComputer_ prime_pow) except -1: + """ + Deallocate an element. + + INPUT: + + - ``unit`` -- an ``celement`` to be cleared. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_clear(value) + +cdef inline int ccmp(celement a, celement b, long prec, bint reduce_a, bint reduce_b, PowComputer_ prime_pow) except -2: + """ + Comparison of two elements. + + INPUT: + + - ``a`` -- an ``celement``. + - ``b`` -- an ``celement``. + - ``prec`` -- a long, the precision of the comparison. + - ``reduce_a`` -- a bint, whether ``a`` needs to be reduced. + - ``reduce_b`` -- a bint, whether ``b`` needs to be reduced. + - ``prime_pow`` -- the PowComputer for the ring. + + OUPUT: + + - If neither ``a`` nor ``b`` needs to be reduced, returns + -1 (if `a < b`), 0 (if `a == b`) or 1 (if `a > b`) + + - If at least one needs to be reduced, returns + 0 (if ``a == b mod p^prec``) or 1 (otherwise) + """ + csub(prime_pow.poly_ccmp, a, b, prec, prime_pow) + creduce(prime_pow.poly_ccmp, prime_pow.poly_ccmp, prec, prime_pow) + + if reduce_a or reduce_b: + return not ciszero(prime_pow.poly_ccmp, prime_pow) + + if prec == 0: + return 0 + + if ciszero(prime_pow.poly_ccmp, prime_pow): return 0 + + cdef long da = fmpz_poly_degree(a) + cdef long db = fmpz_poly_degree(b) + if da < db: return -1 + elif da > db: return 1 + + cdef long cmp + cdef long i + for i in range(da+1): + fmpz_poly_get_coeff_fmpz(prime_pow.fmpz_ccmp, prime_pow.poly_ccmp, i) + cmp = fmpz_cmp_si(prime_pow.fmpz_ccmp, 0) + if cmp < 0: return -1 + elif cmp > 0: return 1 + assert False + +cdef inline int cneg(celement out, celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Negation + + Note that no reduction is performed. + + INPUT: + + - ``out`` -- an ``celement`` to store the negation. + - ``a`` -- an ``celement`` to be negated. + - ``prec`` -- a long, the precision: ignored. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_neg(out, a) + +cdef inline int cadd(celement out, celement a, celement b, long prec, PowComputer_ prime_pow) except -1: + """ + Addition + + Note that no reduction is performed. + + INPUT: + + - ``out`` -- an ``celement`` to store the sum. + - ``a`` -- an ``celement``, the first summand. + - ``b`` -- an ``celement``, the second summand. + - ``prec`` -- a long, the precision: ignored. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_add(out, a, b) + +cdef inline bint creduce(celement out, celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Reduce modulo a power of the maximal ideal. + + INPUT: + + - ``out`` -- an ``celement`` to store the reduction. + - ``a`` -- the element to be reduced. + - ``prec`` -- a long, the precision to reduce modulo. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - returns True if the reduction is zero; False otherwise. + """ + if prec == 0: + csetzero(out, prime_pow) + return True + sig_on() + fmpz_poly_rem(out, a, prime_pow.get_modulus(prec)[0]) + fmpz_poly_scalar_mod_fmpz(out, out, prime_pow.pow_fmpz_t_tmp(prec)[0]) + sig_off() + return ciszero(out, prime_pow) + +cdef inline bint creduce_small(celement out, celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Reduce modulo a power of the maximal ideal. + + This function assumes that at most one addition/subtraction has + happened on reduced inputs. For integral inputs this translates + to the assumption that `-p^prec < a < 2p^prec`. + + INPUT: + + - ``out`` -- an ``celement`` to store the reduction. + - ``a`` -- the element to be reduced. + - ``prec`` -- a long, the precision to reduce modulo. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - returns True if the reduction is zero; False otherwise. + """ + return creduce(out, a, prec, prime_pow) + +cdef inline long cremove(celement out, celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Extract the maximum power of the uniformizer dividing this element. + + INPUT: + + - ``out`` -- an ``celement`` to store the unit. + - ``a`` -- the element whose valuation and unit are desired. + - ``prec`` -- a long, used if `a = 0`. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - if `a = 0`, returns prec (the value of ``out`` is undefined). + Otherwise, returns the number of times `p` divides `a`. + """ + if ciszero(a, prime_pow): + return prec + cdef long ret = cvaluation(a, prec, prime_pow) + if ret: + sig_on() + fmpz_poly_scalar_divexact_fmpz(out, a, (prime_pow).pow_fmpz_t_tmp(ret)[0]) + sig_off() + else: + fmpz_poly_set(out, a) + return ret + +cdef inline long cvaluation(celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Returns the maximum power of the uniformizer dividing this + element. + + This function differs from :meth:`cremove` in that the unit is + discarded. + + INPUT: + + - ``a`` -- the element whose valuation is desired. + - ``prec`` -- a long, used if `a = 0`. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - if `a = 0`, returns prec. Otherwise, returns the number of + times p divides a. + """ + if ciszero(a, prime_pow): + return prec + cdef long ret = maxordp + cdef long val + cdef long i + for i in range(fmpz_poly_length(a)): + fmpz_poly_get_coeff_fmpz(prime_pow.fmpz_cval, a, i) + if fmpz_is_zero(prime_pow.fmpz_cval): + continue + val = fmpz_remove(prime_pow.fmpz_cval, prime_pow.fmpz_cval, prime_pow.fprime) + if val < ret: ret = val + return ret + +cdef inline bint cisunit(celement a, PowComputer_ prime_pow) except -1: + """ + Returns whether this element has valuation zero. + + INPUT: + + - ``a`` -- the element to test. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - returns True if `a` has valuation 0, and False otherwise. + """ + fmpz_poly_scalar_mod_fmpz(prime_pow.poly_cisunit, a, prime_pow.fprime) + return not ciszero(prime_pow.poly_cisunit, prime_pow) + +cdef inline int cshift(celement out, celement a, long n, long prec, PowComputer_ prime_pow, bint reduce_afterward) except -1: + """ + Mulitplies by a power of the uniformizer. + + INPUT: + + - ``out`` -- an ``celement`` to store the result. If `n >= 0` + then out will be set to `a * p^n`. + If `n < 0`, out will be set to `a // p^-n`. + - ``a`` -- the element to shift. + - ``n`` -- long, the amount to shift by. + - ``prec`` -- long, a precision modulo which to reduce. + - ``prime_pow`` -- the PowComputer for the ring. + - ``reduce_afterward`` -- whether to reduce afterward. + """ + if n > 0: + fmpz_poly_scalar_mul_fmpz(out, a, prime_pow.pow_fmpz_t_tmp(n)[0]) + elif n < 0: + sig_on() + fmpz_poly_scalar_fdiv_fmpz(out, a, prime_pow.pow_fmpz_t_tmp(-n)[0]) + sig_off() + else: + fmpz_poly_set(out, a) + if reduce_afterward: + creduce(out, out, prec, prime_pow) + +cdef inline int cshift_notrunc(celement out, celement a, long n, long prec, PowComputer_ prime_pow) except -1: + """ + Mulitplies by a power of the uniformizer, assuming that the + valuation of a is at least -n. + + INPUT: + + - ``out`` -- an ``celement`` to store the result. If `n >= 0` + then out will be set to `a * p^n`. + If `n < 0`, out will be set to `a // p^-n`. + - ``a`` -- the element to shift. Assumes that the valuation of a + is at least -n. + - ``n`` -- long, the amount to shift by. + - ``prec`` -- long, a precision modulo which to reduce. + - ``prime_pow`` -- the PowComputer for the ring. + """ + if n > 0: + fmpz_poly_scalar_mul_fmpz(out, a, prime_pow.pow_fmpz_t_tmp(n)[0]) + elif n < 0: + sig_on() + fmpz_poly_scalar_divexact_fmpz(out, a, prime_pow.pow_fmpz_t_tmp(-n)[0]) + sig_off() + else: + fmpz_poly_set(out, a) + +cdef inline int csub(celement out, celement a, celement b, long prec, PowComputer_ prime_pow) except -1: + """ + Subtraction. + + Note that no reduction is performed. + + INPUT: + + - ``out`` -- an ``celement`` to store the difference. + - ``a`` -- an ``celement``, the first input. + - ``b`` -- an ``celement``, the second input. + - ``prec`` -- a long, the precision: ignored. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_sub(out, a, b) + +cdef inline int cinvert(celement out, celement a, long prec, PowComputer_ prime_pow) except -1: + """ + Inversion + + The result will be reduced modulo p^prec. + + INPUT: + + - ``out`` -- an ``celement`` to store the inverse. + - ``a`` -- an ``celement``, the element to be inverted. + - ``prec`` -- a long, the precision. + - ``prime_pow`` -- the PowComputer for the ring. + """ + sig_on() + try: + fmpz_poly_set(prime_pow.poly_cinv, prime_pow.get_modulus(prec)[0]) + fmpz_poly_primitive_part(prime_pow.poly_cinv, prime_pow.poly_cinv) + + fmpz_poly_content(prime_pow.fmpz_cinv, a) + fmpz_poly_scalar_divexact_fmpz(out, a, prime_pow.fmpz_cinv) + + fmpz_poly_xgcd(prime_pow.fmpz_cinv2, out, prime_pow.poly_cinv2, out, prime_pow.poly_cinv) + if fmpz_is_zero(prime_pow.fmpz_cinv2): raise ValueError("polynomials are not coprime") + + fmpz_mul(prime_pow.fmpz_cinv2, prime_pow.fmpz_cinv, prime_pow.fmpz_cinv2) + if not fmpz_invmod(prime_pow.fmpz_cinv2, prime_pow.fmpz_cinv2, prime_pow.pow_fmpz_t_tmp(prec)[0]): raise ValueError("content or xgcd is not a unit") + fmpz_poly_scalar_mul_fmpz(out, out, prime_pow.fmpz_cinv2) + + creduce(out, out, prec, prime_pow) + finally: + sig_off() + +cdef inline int cmul(celement out, celement a, celement b, long prec, PowComputer_ prime_pow) except -1: + """ + Multiplication. + + Note that no reduction is performed. + + INPUT: + + - ``out`` -- an ``celement`` to store the product. + - ``a`` -- an ``celement``, the first input. + - ``b`` -- an ``celement``, the second input. + - ``prec`` -- a long, the precision: ignored. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_mul(out, a, b) + +cdef inline int cdivunit(celement out, celement a, celement b, long prec, PowComputer_ prime_pow) except -1: + """ + Division. + + The inversion is perfomed modulo p^prec. Note that no reduction + is performed after the product. + + INPUT: + + - ``out`` -- an ``celement`` to store the quotient. + - ``a`` -- an ``celement``, the first input. + - ``b`` -- an ``celement``, the second input. + - ``prec`` -- a long, the precision. + - ``prime_pow`` -- the PowComputer for the ring. + """ + cinvert(out, b, prec, prime_pow) + cmul(out, a, out, prec, prime_pow) + +cdef inline int csetone(celement out, PowComputer_ prime_pow) except -1: + """ + Sets to 1. + + INPUT: + + - ``out`` -- the ``celement`` in which to store 1. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_set_ui(out, 1) + +cdef inline int csetzero(celement out, PowComputer_ prime_pow) except -1: + """ + Sets to 0. + + INPUT: + + - ``out`` -- the ``celement`` in which to store 0. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_set_ui(out, 0) + +cdef inline bint cisone(celement a, PowComputer_ prime_pow) except -1: + """ + Returns whether this element is equal to 1. + + INPUT: + + - ``a`` -- the element to test. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - returns True if `a = 1`, and False otherwise. + """ + return fmpz_poly_is_one(a) + +cdef inline bint ciszero(celement a, PowComputer_ prime_pow) except -1: + """ + Returns whether this element is equal to 0. + + INPUT: + + - ``a`` -- the element to test. + - ``prime_pow`` -- the PowComputer for the ring. + + OUTPUT: + + - returns True if `a = 0`, and False otherwise. + """ + return fmpz_poly_is_zero(a) + +cdef inline int cpow(celement out, celement a, mpz_t n, long prec, PowComputer_ prime_pow) except -1: + """ + Exponentiation. + + INPUT: + + - ``out`` -- the ``celement`` in which to store the result. + - ``a`` -- the base. + - ``n`` -- an ``mpz_t``, the exponent. + - ``prec`` -- a long, the working absolute precision. + - ``prime_pow`` -- the PowComputer for the ring. + """ + if mpz_sgn(n) < 0: + raise NotImplementedError("negative exponent") + elif mpz_sgn(n) == 0: + csetone(out, prime_pow) + elif mpz_even_p(n): + mpz_divexact_ui(prime_pow.mpz_cpow, n, 2) + cpow(out, a, prime_pow.mpz_cpow, prec, prime_pow) + fmpz_poly_sqr(out, out) + else: + mpz_sub_ui(prime_pow.mpz_cpow, n, 1) + cpow(out, a, prime_pow.mpz_cpow, prec, prime_pow) + fmpz_poly_mul(out, out, a) + + creduce(out, out, prec, prime_pow) + +cdef inline int ccopy(celement out, celement a, PowComputer_ prime_pow) except -1: + """ + Copying. + + INPUT: + + - ``out`` -- the ``celement`` to store the result. + - ``a`` -- the element to copy. + - ``prime_pow`` -- the PowComputer for the ring. + """ + fmpz_poly_set(out, a) + +cdef inline cpickle(celement a, PowComputer_ prime_pow): + """ + Serialization into objects that Sage knows how to pickle. + + INPUT: + + - ``a`` the element to pickle. + - ``prime_pow`` the PowComputer for the ring. + + OUTPUT: + + - a serializable object storing ``a``. + """ + return fmpz_poly_get_str(a).decode("UTF-8") + +cdef inline int cunpickle(celement out, x, PowComputer_ prime_pow) except -1: + """ + Reconstruction from the output of meth:`cpickle`. + + INPUT: + + - ``out`` -- the ``celement`` in which to store the result. + - ``x`` -- the result of `meth`:cpickle. + - ``prime_pow`` -- the PowComputer for the ring. + """ + byte_string = x.encode("UTF-8") + cdef char* c_str = byte_string + fmpz_poly_set_str(out, c_str) + +cdef inline long chash(celement a, long ordp, long prec, PowComputer_ prime_pow) except -1: + """ + Hashing. + + INPUT: + + - ``a`` -- an ``celement`` storing the underlying element to hash. + - ``ordp`` -- a long storing the valuation. + - ``prec`` -- a long storing the precision. + - ``prime_pow`` -- a PowComputer for the ring. + """ + if ciszero(a, prime_pow): + return 0 + + cdef Integer h = PY_NEW(Integer) + fmpz_poly_get_coeff_mpz(h.value, a, 0) + return hash(h) + +cdef clist(celement a, long prec, bint pos, PowComputer_ prime_pow): + """ + Returns a list of digits in the series expansion. + + This function is used in printing, and expresses ``a`` as a series + in the standard uniformizer ``p``. + + INPUT: + + - ``a`` -- an ``celement`` giving the underlying `p`-adic element. + - ``prec`` -- a precision giving the number of digits desired. + - ``pos`` -- if True then representatives in 0..(p-1) are used; + otherwise the range (-p/2..p/2) is used. + - ``prime_pow`` -- a PowComputer for the ring. + + OUTPUT: + + A list of `p`-adic digits `[a_0, a_1, \ldots]` so that `a = a_0 + a_1*p + + \cdots` modulo `p^{prec}`. The digits are represented as lists themselves. + The returned list might omit trailing zeros and therefore contain less than + ``prec`` elements. + """ + + ret = [] + cdef Integer digit, zero = Integer(0) + cdef long i,j + for i in range(fmpz_poly_length(a)): + fmpz_poly_get_coeff_fmpz(prime_pow.fmpz_clist, a, i) + j = 0 + while j < prec: + if fmpz_is_zero(prime_pow.fmpz_clist): break + fmpz_fdiv_qr(prime_pow.fmpz_clist, prime_pow.fmpz_clist2, prime_pow.fmpz_clist, prime_pow.fprime) + if not fmpz_is_zero(prime_pow.fmpz_clist2): + if not pos and fmpz_cmp(prime_pow.fmpz_clist2, prime_pow.half_prime) > 0: + fmpz_sub(prime_pow.fmpz_clist2, prime_pow.fmpz_clist2, prime_pow.fprime) + fmpz_add_ui(prime_pow.fmpz_clist, prime_pow.fmpz_clist, 1) + while len(ret) <= j: ret.append([]) + while len(ret[j]) <= i: ret[j].append(zero) + digit = PY_NEW(Integer) + fmpz_get_mpz(digit.value, prime_pow.fmpz_clist2) + ret[j][i] =digit + j += 1 + return ret + +# The element is filled in for zero in the output of clist if necessary. +_list_zero = [] + +cdef int cteichmuller(celement out, celement value, long prec, PowComputer_ prime_pow) except -1: + """ + Teichmuller lifting. + + INPUT: + + - ``out`` -- an ``celement`` which is set to a `q-1` root of unity + congruent to `value` mod `\pi`; or 0 if `a \equiv 0 + \pmod{\pi}`. + - ``value`` -- an ``celement``, the element mod `\pi` to lift. + - ``prec`` -- a long, the precision to which to lift. + - ``prime_pow`` -- the Powcomputer of the ring. + + ALGORITHM: + + We use Hensel lifting to solve the equation `f(T)=T^q-T`. Instead of + dividing by the derivative of `f`, we divide by `( q - 1 )` whose first + digits coincide with `f'`. This does probably not yield quadratic + convergence but taking inverses would be much more expansive than what is + done here. + + """ + fmpz_poly_set(out, value) + + if prec == 0: + return 0 + + # fmpz_ctm = 1 / (1 - q) (mod p^prec) + fmpz_set_ui(prime_pow.fmpz_ctm, 1) + fmpz_sub(prime_pow.fmpz_ctm, prime_pow.fmpz_ctm, prime_pow.q) + fmpz_invmod(prime_pow.fmpz_ctm, prime_pow.fmpz_ctm, prime_pow.pow_fmpz_t_tmp(prec)[0]) + while True: + # poly_ctm = out + fmpz_ctm*(out^q - out) + fmpz_get_mpz(prime_pow.mpz_ctm, prime_pow.q) + cpow(prime_pow.poly_ctm, out, prime_pow.mpz_ctm, prec, prime_pow) + csub(prime_pow.poly_ctm, prime_pow.poly_ctm, out, prec, prime_pow) + fmpz_poly_scalar_mul_fmpz(prime_pow.poly_ctm, prime_pow.poly_ctm, prime_pow.fmpz_ctm) + cadd(prime_pow.poly_ctm, prime_pow.poly_ctm, out, prec, prime_pow) + creduce(prime_pow.poly_ctm, prime_pow.poly_ctm, prec, prime_pow) + # break if out == poly_ctm + if ccmp(prime_pow.poly_ctm, out, prec, False, False, prime_pow) == 0: + return 0 + # out = poly_ctm + fmpz_poly_set(out, prime_pow.poly_ctm) + +cdef int cconv(celement out, x, long prec, long valshift, PowComputer_ prime_pow) except -2: + """ + Conversion from other Sage types. + + INPUT: + + - ``out`` -- an ``celement`` to store the output. + + - ``x`` -- a Sage element that can be converted to a `p`-adic element. + + - ``prec`` -- a long, giving the precision desired: absolute if + `valshift = 0`, relative if `valshift != 0`. + + - ``valshift`` -- the power of the uniformizer to divide by before + storing the result in ``out``. + + - ``prime_pow`` -- a PowComputer for the ring. + """ + cdef long i + cdef long degree + + if PyList_Check(x): + for i in range(len(x)): + cconv(prime_pow.poly_cconv, x[i], prec, valshift, prime_pow) + degree = fmpz_poly_degree(prime_pow.poly_cconv) + if degree == -1: continue + elif degree == 0: + fmpz_poly_get_coeff_fmpz(prime_pow.fmpz_cconv, prime_pow.poly_cconv, 0) + fmpz_poly_set_coeff_fmpz(out, i, prime_pow.fmpz_cconv) + else: + raise ValueError + creduce(out, out, prec, prime_pow) + else: + cconv_shared(prime_pow.mpz_cconv, x, prec, valshift, prime_pow) + fmpz_poly_set_mpz(out, prime_pow.mpz_cconv) + +cdef inline long cconv_mpq_t(celement out, mpq_t x, long prec, bint absolute, PowComputer_ prime_pow) except? -10000: + """ + A fast pathway for conversion of rationals that doesn't require + precomputation of the valuation. + + INPUT: + + - ``out`` -- an ``celement`` to store the output. + - ``x`` -- an ``mpq_t`` giving the integer to be converted. + - ``prec`` -- a long, giving the precision desired: absolute or + relative depending on the ``absolute`` input. + - ``absolute`` -- if False then extracts the valuation and returns + it, storing the unit in ``out``; if True then + just reduces ``x`` modulo the precision. + - ``prime_pow`` -- a PowComputer for the ring. + + OUTPUT: + + - If ``absolute`` is False then returns the valuation that was + extracted (``maxordp`` when `x = 0`). + """ + cdef long val + val = cconv_mpq_t_shared(prime_pow.mpz_cconv, x, prec, absolute, prime_pow) + fmpz_poly_set_mpz(out, prime_pow.mpz_cconv) + return val + +cdef inline int cconv_mpq_t_out(mpq_t out, celement x, long valshift, long prec, PowComputer_ prime_pow) except -1: + """ + Converts the underlying `p`-adic element into a rational + + - ``out`` -- gives a rational approximating the input. Currently uses rational reconstruction but + may change in the future to use a more naive method + - ``x`` -- an ``celement`` giving the underlying `p`-adic element + - ``valshift`` -- a long giving the power of `p` to shift `x` by + -` ``prec`` -- a long, the precision of ``x``, used in rational reconstruction + - ``prime_pow`` -- a PowComputer for the ring + """ + cdef long degree = fmpz_poly_degree(x) + if degree > 0: + raise ValueError + elif degree == -1: + mpz_set_ui(prime_pow.mpz_cconv, 0) + else: + fmpz_poly_get_coeff_mpz(prime_pow.mpz_cconv, x, 0) + + cconv_mpq_t_out_shared(out, prime_pow.mpz_cconv, valshift, prec, prime_pow) + +cdef inline long cconv_mpz_t(celement out, mpz_t x, long prec, bint absolute, PowComputer_ prime_pow) except -2: + """ + A fast pathway for conversion of integers that doesn't require + precomputation of the valuation. + + INPUT: + + - ``out`` -- an ``celement`` to store the output. + - ``x`` -- an ``mpz_t`` giving the integer to be converted. + - ``prec`` -- a long, giving the precision desired: absolute or + relative depending on the ``absolute`` input. + - ``absolute`` -- if False then extracts the valuation and returns + it, storing the unit in ``out``; if True then + just reduces ``x`` modulo the precision. + - ``prime_pow`` -- a PowComputer for the ring. + + OUTPUT: + + - If ``absolute`` is False then returns the valuation that was + extracted (``maxordp`` when `x = 0`). + """ + cdef long val + val = cconv_mpz_t_shared(prime_pow.mpz_cconv, x, prec, absolute, prime_pow) + fmpz_poly_set_mpz(out, prime_pow.mpz_cconv) + return val + +cdef inline int cconv_mpz_t_out(mpz_t out, celement x, long valshift, long prec, PowComputer_ prime_pow) except -1: + """ + Converts the underlying `p`-adic element into an integer if + possible. + + - ``out`` -- stores the resulting integer as an integer between 0 + and `p^{prec + valshift}`. + - ``x`` -- an ``celement`` giving the underlying `p`-adic element. + - ``valshift`` -- a long giving the power of `p` to shift `x` by. + -` ``prec`` -- a long, the precision of ``x``: currently not used. + - ``prime_pow`` -- a PowComputer for the ring. + """ + cdef long degree = fmpz_poly_degree(x) + if degree > 0: + raise ValueError + elif degree == -1: + mpz_set_ui(prime_pow.mpz_cconv, 0) + else: + fmpz_poly_get_coeff_mpz(prime_pow.mpz_cconv, x, 0) + + cconv_mpz_t_out_shared(out, prime_pow.mpz_cconv, valshift, prec, prime_pow) + + +## Extra functions ## + +cdef cmatrix_mod_pn(celement a, long aprec, long valshift, PowComputer_ prime_pow): + """ + Returns the matrix of right multiplication by the element on + the power basis `1, x, x^2, \ldots, x^{d-1}` for this + extension field. Thus the *rows* of this matrix give the + images of each of the `x^i`. The entries of the matrices are + IntegerMod elements, defined modulo ``p^(self.absprec() / e)``. + """ + cdef Py_ssize_t i, j, d, deg = prime_pow.deg + cdef int fail + R = Zmod(prime_pow.pow_Integer(aprec)) + cdef IntegerMod_abstract zero = R(0) + cdef IntegerMod_abstract item + L = [] + cshift(prime_pow.poly_matmod, a, valshift, aprec, prime_pow, True) + for i in range(deg): + L.append([]) + d = fmpz_poly_degree(prime_pow.poly_matmod) + for j in range(d+1): + item = zero._new_c_from_long(0) + fmpz_poly_get_coeff_mpz(prime_pow.mpz_matmod, prime_pow.poly_matmod, j) + item.set_from_mpz(prime_pow.mpz_matmod) + L[-1].append(item) + for j in range(d+1,deg): + L[-1].append(zero) + fmpz_poly_shift_left(prime_pow.poly_matmod, prime_pow.poly_matmod, 1) + creduce(prime_pow.poly_matmod, prime_pow.poly_matmod, aprec, prime_pow) + from sage.matrix.all import matrix + return matrix(R, deg, deg, L) diff --git a/src/sage/libs/linkages/padics/mpz.pxi b/src/sage/libs/linkages/padics/mpz.pxi index c3fb2272051..804f7a50fba 100644 --- a/src/sage/libs/linkages/padics/mpz.pxi +++ b/src/sage/libs/linkages/padics/mpz.pxi @@ -26,12 +26,13 @@ from sage.libs.gmp.rational_reconstruction cimport mpq_rational_reconstruction from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.padics.padic_generic_element cimport pAdicGenericElement +from sage.rings.padics.common_conversion cimport cconv_mpz_t_out_shared, cconv_mpz_t_shared, cconv_mpq_t_out_shared, cconv_mpq_t_shared, cconv_shared import sage.rings.finite_rings.integer_mod cdef Integer holder = PY_NEW(Integer) cdef Integer holder2 = PY_NEW(Integer) -cdef inline int cconstruct(mpz_t value, PowComputer_class prime_pow) except -1: +cdef inline int cconstruct(mpz_t value, PowComputer_ prime_pow) except -1: """ Construct a new element. @@ -42,7 +43,7 @@ cdef inline int cconstruct(mpz_t value, PowComputer_class prime_pow) except -1: """ mpz_init(value) -cdef inline int cdestruct(mpz_t value, PowComputer_class prime_pow) except -1: +cdef inline int cdestruct(mpz_t value, PowComputer_ prime_pow) except -1: """ Deallocate an element. @@ -53,7 +54,7 @@ cdef inline int cdestruct(mpz_t value, PowComputer_class prime_pow) except -1: """ mpz_clear(value) -cdef inline int ccmp(mpz_t a, mpz_t b, long prec, bint reduce_a, bint reduce_b, PowComputer_class prime_pow) except -2: +cdef inline int ccmp(mpz_t a, mpz_t b, long prec, bint reduce_a, bint reduce_b, PowComputer_ prime_pow) except -2: """ Comparison of two elements. @@ -86,7 +87,7 @@ cdef inline int ccmp(mpz_t a, mpz_t b, long prec, bint reduce_a, bint reduce_b, return -1 return 0 -cdef inline int cneg(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cneg(mpz_t out, mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Negation. @@ -101,7 +102,7 @@ cdef inline int cneg(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) """ mpz_neg(out, a) -cdef inline int cadd(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cadd(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_ prime_pow) except -1: """ Addition. @@ -117,7 +118,7 @@ cdef inline int cadd(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class p """ mpz_add(out, a, b) -cdef inline bint creduce(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline bint creduce(mpz_t out, mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Reduce modulo a power of the maximal ideal. @@ -135,7 +136,7 @@ cdef inline bint creduce(mpz_t out, mpz_t a, long prec, PowComputer_class prime_ mpz_mod(out, a, prime_pow.pow_mpz_t_tmp(prec)) return mpz_sgn(out) == 0 -cdef inline bint creduce_small(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline bint creduce_small(mpz_t out, mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Reduce modulo a power of the maximal ideal. @@ -161,7 +162,7 @@ cdef inline bint creduce_small(mpz_t out, mpz_t a, long prec, PowComputer_class mpz_set(out, a) return mpz_sgn(out) == 0 -cdef inline long cremove(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline long cremove(mpz_t out, mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Extract the maximum power of the uniformizer dividing this element. @@ -183,7 +184,7 @@ cdef inline long cremove(mpz_t out, mpz_t a, long prec, PowComputer_class prime_ return prec return mpz_remove(out, a, prime_pow.prime.value) -cdef inline long cvaluation(mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline long cvaluation(mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Returns the maximum power of the uniformizer dividing this element. @@ -206,7 +207,7 @@ cdef inline long cvaluation(mpz_t a, long prec, PowComputer_class prime_pow) exc return prec return mpz_remove(holder.value, a, prime_pow.prime.value) -cdef inline bint cisunit(mpz_t a, PowComputer_class prime_pow) except -1: +cdef inline bint cisunit(mpz_t a, PowComputer_ prime_pow) except -1: """ Returns whether this element has valuation zero. @@ -221,7 +222,7 @@ cdef inline bint cisunit(mpz_t a, PowComputer_class prime_pow) except -1: """ return mpz_divisible_p(a, prime_pow.prime.value) == 0 -cdef inline int cshift(mpz_t out, mpz_t a, long n, long prec, PowComputer_class prime_pow, bint reduce_afterward) except -1: +cdef inline int cshift(mpz_t out, mpz_t a, long n, long prec, PowComputer_ prime_pow, bint reduce_afterward) except -1: """ Multiplies by a power of the uniformizer. @@ -247,7 +248,7 @@ cdef inline int cshift(mpz_t out, mpz_t a, long n, long prec, PowComputer_class if reduce_afterward: creduce(out, out, prec, prime_pow) -cdef inline int cshift_notrunc(mpz_t out, mpz_t a, long n, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cshift_notrunc(mpz_t out, mpz_t a, long n, long prec, PowComputer_ prime_pow) except -1: """ Multiplies by a power of the uniformizer, assuming that the valuation of a is at least -n. @@ -272,7 +273,7 @@ cdef inline int cshift_notrunc(mpz_t out, mpz_t a, long n, long prec, PowCompute else: mpz_set(out, a) -cdef inline int csub(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class prime_pow) except -1: +cdef inline int csub(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_ prime_pow) except -1: """ Subtraction. @@ -288,7 +289,7 @@ cdef inline int csub(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class p """ mpz_sub(out, a, b) -cdef inline int cinvert(mpz_t out, mpz_t a, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cinvert(mpz_t out, mpz_t a, long prec, PowComputer_ prime_pow) except -1: """ Inversion. @@ -306,7 +307,7 @@ cdef inline int cinvert(mpz_t out, mpz_t a, long prec, PowComputer_class prime_p if not success: raise ZeroDivisionError -cdef inline int cmul(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cmul(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_ prime_pow) except -1: """ Multiplication. @@ -322,7 +323,7 @@ cdef inline int cmul(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class p """ mpz_mul(out, a, b) -cdef inline int cdivunit(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cdivunit(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_ prime_pow) except -1: """ Division. @@ -343,7 +344,7 @@ cdef inline int cdivunit(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_cla raise ZeroDivisionError mpz_mul(out, a, out) -cdef inline int csetone(mpz_t out, PowComputer_class prime_pow) except -1: +cdef inline int csetone(mpz_t out, PowComputer_ prime_pow) except -1: """ Sets to 1. @@ -354,7 +355,7 @@ cdef inline int csetone(mpz_t out, PowComputer_class prime_pow) except -1: """ mpz_set_ui(out, 1) -cdef inline int csetzero(mpz_t out, PowComputer_class prime_pow) except -1: +cdef inline int csetzero(mpz_t out, PowComputer_ prime_pow) except -1: """ Sets to 0. @@ -365,7 +366,7 @@ cdef inline int csetzero(mpz_t out, PowComputer_class prime_pow) except -1: """ mpz_set_ui(out, 0) -cdef inline bint cisone(mpz_t out, PowComputer_class prime_pow) except -1: +cdef inline bint cisone(mpz_t out, PowComputer_ prime_pow) except -1: """ Returns whether this element is equal to 1. @@ -380,7 +381,7 @@ cdef inline bint cisone(mpz_t out, PowComputer_class prime_pow) except -1: """ return mpz_cmp_ui(out, 1) == 0 -cdef inline bint ciszero(mpz_t out, PowComputer_class prime_pow) except -1: +cdef inline bint ciszero(mpz_t out, PowComputer_ prime_pow) except -1: """ Returns whether this element is equal to 0. @@ -395,7 +396,7 @@ cdef inline bint ciszero(mpz_t out, PowComputer_class prime_pow) except -1: """ return mpz_cmp_ui(out, 0) == 0 -cdef inline int cpow(mpz_t out, mpz_t a, mpz_t n, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cpow(mpz_t out, mpz_t a, mpz_t n, long prec, PowComputer_ prime_pow) except -1: """ Exponentiation. @@ -409,7 +410,7 @@ cdef inline int cpow(mpz_t out, mpz_t a, mpz_t n, long prec, PowComputer_class p """ mpz_powm(out, a, n, prime_pow.pow_mpz_t_tmp(prec)) -cdef inline int ccopy(mpz_t out, mpz_t a, PowComputer_class prime_pow) except -1: +cdef inline int ccopy(mpz_t out, mpz_t a, PowComputer_ prime_pow) except -1: """ Copying. @@ -421,7 +422,7 @@ cdef inline int ccopy(mpz_t out, mpz_t a, PowComputer_class prime_pow) except -1 """ mpz_set(out, a) -cdef inline cpickle(mpz_t a, PowComputer_class prime_pow): +cdef inline cpickle(mpz_t a, PowComputer_ prime_pow): """ Serialization into objects that Sage knows how to pickle. @@ -438,7 +439,7 @@ cdef inline cpickle(mpz_t a, PowComputer_class prime_pow): mpz_set(pic.value, a) return pic -cdef inline int cunpickle(mpz_t out, x, PowComputer_class prime_pow) except -1: +cdef inline int cunpickle(mpz_t out, x, PowComputer_ prime_pow) except -1: """ Reconstruction from the output of meth:`cpickle`. @@ -450,7 +451,7 @@ cdef inline int cunpickle(mpz_t out, x, PowComputer_class prime_pow) except -1: """ mpz_set(out, (x).value) -cdef inline long chash(mpz_t a, long ordp, long prec, PowComputer_class prime_pow) except -1: +cdef inline long chash(mpz_t a, long ordp, long prec, PowComputer_ prime_pow) except -1: """ Hashing. @@ -478,7 +479,7 @@ cdef inline long chash(mpz_t a, long ordp, long prec, PowComputer_class prime_po return -2 return n -cdef clist(mpz_t a, long prec, bint pos, PowComputer_class prime_pow): +cdef clist(mpz_t a, long prec, bint pos, PowComputer_ prime_pow): """ Returns a list of digits in the series expansion. @@ -538,7 +539,7 @@ cdef clist(mpz_t a, long prec, bint pos, PowComputer_class prime_pow): # It could be [] for some other linkages. _list_zero = Integer(0) -cdef int cteichmuller(mpz_t out, mpz_t value, long prec, PowComputer_class prime_pow) except -1: +cdef int cteichmuller(mpz_t out, mpz_t value, long prec, PowComputer_ prime_pow) except -1: """ Teichmuller lifting. @@ -582,7 +583,7 @@ cdef int cteichmuller(mpz_t out, mpz_t value, long prec, PowComputer_class prime mpz_add(holder2.value, holder2.value, out) mpz_mod(holder2.value, holder2.value, prime_pow.pow_mpz_t_tmp(prec)) -cdef int cconv(mpz_t out, x, long prec, long valshift, PowComputer_class prime_pow) except -2: +cdef int cconv(mpz_t out, x, long prec, long valshift, PowComputer_ prime_pow) except -2: """ Conversion from other Sage types. @@ -600,45 +601,19 @@ cdef int cconv(mpz_t out, x, long prec, long valshift, PowComputer_class prime_p - ``prime_pow`` -- a PowComputer for the ring. """ - if isinstance(x, pari_gen): - x = x.sage() - if isinstance(x, pAdicGenericElement) or sage.rings.finite_rings.integer_mod.is_IntegerMod(x): - x = x.lift() - if isinstance(x, Integer): - if valshift > 0: - mpz_divexact(out, (x).value, prime_pow.pow_mpz_t_tmp(valshift)) - mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) - elif valshift < 0: - raise RuntimeError("Integer should not have negative valuation") - else: - mpz_mod(out, (x).value, prime_pow.pow_mpz_t_tmp(prec)) - elif isinstance(x, Rational): - if valshift == 0: - mpz_invert(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(prec)) - mpz_mul(out, out, mpq_numref((x).value)) - elif valshift < 0: - mpz_divexact(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(-valshift)) - mpz_invert(out, out, prime_pow.pow_mpz_t_tmp(prec)) - mpz_mul(out, out, mpq_numref((x).value)) - else: - mpz_invert(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(prec)) - mpz_divexact(holder.value, mpq_numref((x).value), prime_pow.pow_mpz_t_tmp(valshift)) - mpz_mul(out, out, holder.value) - mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) - else: - raise NotImplementedError("No conversion defined") + return cconv_shared(out, x, prec, valshift, prime_pow) -cdef inline long cconv_mpz_t(mpz_t out, mpz_t x, long prec, bint absolute, PowComputer_class prime_pow) except -2: +cdef inline long cconv_mpq_t(mpz_t out, mpq_t x, long prec, bint absolute, PowComputer_ prime_pow) except? -10000: """ - A fast pathway for conversion of integers that doesn't require + A fast pathway for conversion of rationals that doesn't require precomputation of the valuation. INPUT: - ``out`` -- an ``mpz_t`` to store the output. - - ``x`` -- an ``mpz_t`` giving the integer to be converted. + - ``x`` -- an ``mpq_t`` giving the integer to be converted. - ``prec`` -- a long, giving the precision desired: absolute or - relative depending on the ``absolute`` input. + relative depending on the ``absolute`` input. - ``absolute`` -- if False then extracts the valuation and returns it, storing the unit in ``out``; if True then just reduces ``x`` modulo the precision. @@ -649,47 +624,32 @@ cdef inline long cconv_mpz_t(mpz_t out, mpz_t x, long prec, bint absolute, PowCo - If ``absolute`` is False then returns the valuation that was extracted (``maxordp`` when `x = 0`). """ - cdef long val - if absolute: - mpz_mod(out, x, prime_pow.pow_mpz_t_tmp(prec)) - elif mpz_sgn(x) == 0: - mpz_set_ui(out, 0) - return maxordp - else: - val = mpz_remove(out, x, prime_pow.prime.value) - mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) - return val + return cconv_mpq_t_shared(out, x, prec, absolute, prime_pow) -cdef inline int cconv_mpz_t_out(mpz_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1: +cdef inline int cconv_mpq_t_out(mpq_t out, mpz_t x, long valshift, long prec, PowComputer_ prime_pow) except -1: """ - Converts the underlying `p`-adic element into an integer if - possible. + Converts the underlying `p`-adic element into a rational - - ``out`` -- stores the resulting integer as an integer between 0 - and `p^{prec + valshift}`. - - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element. - - ``valshift`` -- a long giving the power of `p` to shift `x` by. - -` ``prec`` -- a long, the precision of ``x``: currently not used. - - ``prime_pow`` -- a PowComputer for the ring. + - ``out`` -- gives a rational approximating the input. Currently uses rational reconstruction but + may change in the future to use a more naive method + - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element + - ``valshift`` -- a long giving the power of `p` to shift `x` by + -` ``prec`` -- a long, the precision of ``x``, used in rational reconstruction + - ``prime_pow`` -- a PowComputer for the ring """ - if valshift == 0: - mpz_set(out, x) - elif valshift < 0: - raise ValueError("negative valuation") - else: - mpz_mul(out, x, prime_pow.pow_mpz_t_tmp(valshift)) + return cconv_mpq_t_out_shared(out, x, valshift, prec, prime_pow) -cdef inline long cconv_mpq_t(mpz_t out, mpq_t x, long prec, bint absolute, PowComputer_class prime_pow) except? -10000: +cdef inline long cconv_mpz_t(mpz_t out, mpz_t x, long prec, bint absolute, PowComputer_ prime_pow) except -2: """ - A fast pathway for conversion of rationals that doesn't require + A fast pathway for conversion of integers that doesn't require precomputation of the valuation. INPUT: - ``out`` -- an ``mpz_t`` to store the output. - - ``x`` -- an ``mpq_t`` giving the integer to be converted. + - ``x`` -- an ``mpz_t`` giving the integer to be converted. - ``prec`` -- a long, giving the precision desired: absolute or - relative depending on the ``absolute`` input. + relative depending on the ``absolute`` input. - ``absolute`` -- if False then extracts the valuation and returns it, storing the unit in ``out``; if True then just reduces ``x`` modulo the precision. @@ -700,48 +660,18 @@ cdef inline long cconv_mpq_t(mpz_t out, mpq_t x, long prec, bint absolute, PowCo - If ``absolute`` is False then returns the valuation that was extracted (``maxordp`` when `x = 0`). """ - cdef long numval, denval - cdef bint success - if prec <= 0: - raise ValueError - if absolute: - success = mpz_invert(out, mpq_denref(x), prime_pow.pow_mpz_t_tmp(prec)) - if not success: - raise ValueError("p divides denominator") - mpz_mul(out, out, mpq_numref(x)) - mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) - elif mpq_sgn(x) == 0: - mpz_set_ui(out, 0) - return maxordp - else: - denval = mpz_remove(out, mpq_denref(x), prime_pow.prime.value) - mpz_invert(out, out, prime_pow.pow_mpz_t_tmp(prec)) - if denval == 0: - numval = mpz_remove(holder.value, mpq_numref(x), prime_pow.prime.value) - mpz_mul(out, out, holder.value) - else: - numval = 0 - mpz_mul(out, out, mpq_numref(x)) - mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) - return numval - denval - -cdef inline int cconv_mpq_t_out(mpq_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1: - """ - Converts the underlying `p`-adic element into a rational + return cconv_mpz_t_shared(out, x, prec, absolute, prime_pow) - - ``out`` -- gives a rational approximating the input. Currently uses rational reconstruction but - may change in the future to use a more naive method - - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element - - ``valshift`` -- a long giving the power of `p` to shift `x` by - -` ``prec`` -- a long, the precision of ``x``, used in rational reconstruction - - ``prime_pow`` -- a PowComputer for the ring +cdef inline int cconv_mpz_t_out(mpz_t out, mpz_t x, long valshift, long prec, PowComputer_ prime_pow) except -1: """ - mpq_rational_reconstruction(out, x, prime_pow.pow_mpz_t_tmp(prec)) + Converts the underlying `p`-adic element into an integer if + possible. - # if valshift is nonzero then we starte with x as a p-adic unit, - # so there will be no powers of p in the numerator or denominator - # and the following operations yield reduced rationals. - if valshift > 0: - mpz_mul(mpq_numref(out), mpq_numref(out), prime_pow.pow_mpz_t_tmp(valshift)) - elif valshift < 0: - mpz_mul(mpq_denref(out), mpq_denref(out), prime_pow.pow_mpz_t_tmp(-valshift)) + - ``out`` -- stores the resulting integer as an integer between 0 + and `p^{prec + valshift}`. + - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element. + - ``valshift`` -- a long giving the power of `p` to shift `x` by. + -` ``prec`` -- a long, the precision of ``x``: currently not used. + - ``prime_pow`` -- a PowComputer for the ring. + """ + return cconv_mpz_t_out_shared(out, x, valshift, prec, prime_pow) diff --git a/src/sage/libs/linkages/padics/unram_shared.pxi b/src/sage/libs/linkages/padics/unram_shared.pxi new file mode 100644 index 00000000000..0e184cb8223 --- /dev/null +++ b/src/sage/libs/linkages/padics/unram_shared.pxi @@ -0,0 +1,202 @@ +def frobenius_unram(self, arithmetic=True): + """ + Returns the image of this element under the Frobenius automorphism + applied to its parent. + + INPUT: + + - ``self`` -- an element of an unramified extension. + - ``arithmetic`` -- whether to apply the arithmetic Frobenius (acting + by raising to the `p`-th power on the residue field). If ``False`` is + provided, the image of geometric Frobenius (raising to the `(1/p)`-th + power on the residue field) will be returned instead. + + EXAMPLES:: + + sage: R.
    = Zq(5^4,3) + sage: a.frobenius() + (a^3 + a^2 + 3*a) + (3*a + 1)*5 + (2*a^3 + 2*a^2 + 2*a)*5^2 + O(5^3) + sage: f = R.defining_polynomial() + sage: f(a) + O(5^3) + sage: f(a.frobenius()) + O(5^3) + sage: for i in range(4): a = a.frobenius() + sage: a + a + O(5^3) + + sage: K. = Qq(7^3,4) + sage: b = (a+1)/7 + sage: c = b.frobenius(); c + (3*a^2 + 5*a + 1)*7^-1 + (6*a^2 + 6*a + 6) + (4*a^2 + 3*a + 4)*7 + (6*a^2 + a + 6)*7^2 + O(7^3) + sage: c.frobenius().frobenius() + (a + 1)*7^-1 + O(7^3) + + An error will be raised if the parent of self is a ramified extension:: + + sage: K. = Qp(5).extension(x^2 - 5) + sage: a.frobenius() + Traceback (most recent call last): + ... + NotImplementedError: Frobenius automorphism only implemented for unramified extensions + """ + R = self.parent() + if self.is_zero(): return self + L = self.teichmuller_list() + ppow = R.uniformizer_pow(self.valuation()) + if arithmetic: + exp = R.prime() + else: + exp = R.prime()**(R.degree()-1) + ans = ppow * L[0]**exp + for m in range(1,len(L)): + ppow = ppow << 1 + ans += ppow * L[m]**exp + return ans + +def norm_unram(self, base = None): + """ + Return the absolute or relative norm of this element. + + .. WARNING:: + + This is not the `p`-adic absolute value. This is a + field theoretic norm down to a ground ring. If you want the + `p`-adic absolute value, use the ``abs()`` function instead. + + INPUT:: + + ``base`` -- a subfield of the parent `L` of this element. + The norm is the relative norm from ``L`` to ``base``. + Defaults to the absolute norm down to `\mathbb{Q}_p` or `\mathbb{Z}_p`. + + EXAMPLES:: + + sage: R = ZpCR(5,5) + sage: S. = R[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: ((1+2*w)^5).norm() + 1 + 5^2 + O(5^5) + sage: ((1+2*w)).norm()^5 + 1 + 5^2 + O(5^5) + + TESTS:: + + sage: R = ZpCA(5,5) + sage: S. = ZZ[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: ((1+2*w)^5).norm() + 1 + 5^2 + O(5^5) + sage: ((1+2*w)).norm()^5 + 1 + 5^2 + O(5^5) + sage: R = ZpFM(5,5) + sage: S. = ZZ[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: ((1+2*w)^5).norm() + 1 + 5^2 + O(5^5) + sage: ((1+2*w)).norm()^5 + 1 + 5^2 + O(5^5) + + TESTS: + + Check that #11586 has been resolved:: + + sage: R. = QQ[] + sage: f = x^2 + 3*x + 1 + sage: M. = Qp(7).extension(f) + sage: M(7).norm() + 7^2 + O(7^22) + sage: b = 7*a + 35 + sage: b.norm() + 4*7^2 + 7^3 + O(7^22) + sage: b*b.frobenius() + 4*7^2 + 7^3 + O(7^22) + """ + if base is not None: + if base is self.parent(): + return self + else: + raise NotImplementedError + if self._is_exact_zero(): + return self.parent().ground_ring()(0) + elif self._is_inexact_zero(): + return self.ground_ring(0, self.valuation()) + if self.valuation() == 0: + return self.parent().ground_ring()(self.matrix_mod_pn().det()) + else: + if self.parent().e() == 1: + norm_of_uniformizer = self.parent().ground_ring().uniformizer_pow(self.parent().degree()) + else: + norm_of_uniformizer = (-1)**self.parent().degree() * self.parent().defining_polynomial()[0] + return self.parent().ground_ring()(self.unit_part().matrix_mod_pn().det()) * norm_of_uniformizer**self.valuation() + +def trace_unram(self, base = None): + """ + Return the absolute or relative trace of this element. + + If ``base`` is given then ``base`` must be a subfield of the + parent `L` of ``self``, in which case the norm is the relative + norm from `L` to ``base``. + + In all other cases, the norm is the absolute norm down to + `\mathbb{Q}_p` or `\mathbb{Z}_p`. + + EXAMPLES:: + + sage: R = ZpCR(5,5) + sage: S. = R[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: a = (2+3*w)^7 + sage: b = (6+w^3)^5 + sage: a.trace() + 3*5 + 2*5^2 + 3*5^3 + 2*5^4 + O(5^5) + sage: a.trace() + b.trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + sage: (a+b).trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + + TESTS:: + + sage: R = ZpCA(5,5) + sage: S. = ZZ[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: a = (2+3*w)^7 + sage: b = (6+w^3)^5 + sage: a.trace() + 3*5 + 2*5^2 + 3*5^3 + 2*5^4 + O(5^5) + sage: a.trace() + b.trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + sage: (a+b).trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + sage: R = ZpFM(5,5) + sage: S. = R[] + sage: f = x^5 + 75*x^3 - 15*x^2 +125*x - 5 + sage: W. = R.ext(f) + sage: a = (2+3*w)^7 + sage: b = (6+w^3)^5 + sage: a.trace() + 3*5 + 2*5^2 + 3*5^3 + 2*5^4 + O(5^5) + sage: a.trace() + b.trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + sage: (a+b).trace() + 4*5 + 5^2 + 5^3 + 2*5^4 + O(5^5) + """ + if base is not None: + if base is self.parent(): + return self + else: + raise NotImplementedError + if self._is_exact_zero(): + return self.parent().ground_ring()(0) + elif self._is_inexact_zero(): + return self.ground_ring(0, self.precision_absolute()) + if self.valuation() >= 0: + return self.parent().ground_ring()(self.matrix_mod_pn().trace()) + else: + shift = -self.valuation() + return self.parent().ground_ring()((self * self.parent().prime() ** shift).matrix_mod_pn().trace()) / self.parent().prime()**shift diff --git a/src/sage/libs/ntl/misc.pxi b/src/sage/libs/ntl/misc.pxi index 62ca91939d5..ea9a0280c80 100644 --- a/src/sage/libs/ntl/misc.pxi +++ b/src/sage/libs/ntl/misc.pxi @@ -1,7 +1,7 @@ # distutils: depends = NTL/ZZ.h include "cysignals/signals.pxi" -from sage.ext.memory cimport sage_free +include "cysignals/memory.pxi" # Unset the signal handler and create a string from the buffer, # then free the memory in the buffer. @@ -17,7 +17,7 @@ cdef object string(char* s): sig_off() # Makes a python string and deletes what is pointed to by s. t = str(s) - sage_free(s) + sig_free(s) return t cdef object string_delete(char* s): diff --git a/src/sage/libs/ntl/ntl_ZZX.pyx b/src/sage/libs/ntl/ntl_ZZX.pyx index 2114eeab566..ed0f02d6181 100644 --- a/src/sage/libs/ntl/ntl_ZZX.pyx +++ b/src/sage/libs/ntl/ntl_ZZX.pyx @@ -16,7 +16,7 @@ from __future__ import division include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "decl.pxi" include 'misc.pxi' @@ -1126,8 +1126,8 @@ cdef class ntl_ZZX(object): F = [] for i from 0 <= i < n: F.append((make_ZZX(v[i]), e[i])) - sage_free(v) - sage_free(e) + sig_free(v) + sig_free(e) return F diff --git a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx index ce9043636cf..22ceb5cbc33 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pEX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pEX.pyx @@ -24,7 +24,6 @@ AUTHORS: from __future__ import division include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" include 'misc.pxi' include 'decl.pxi' diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index c4f1c1870c9..e4112beb0a5 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -162,7 +162,7 @@ cdef class ntl_ZZ_pX(object): self.c.restore_c() #cdef char* s = ZZ_pX_repr(&self.x) #t = str(s) - #sage_free(s) + #sig_free(s) return ZZ_pX_to_PyString(&self.x) #return t @@ -951,8 +951,8 @@ cdef class ntl_ZZ_pX(object): #F.append((make_ZZ_pX(v[i], self.c), e[i])) for i from 0 <= i < n: del v[i] - sage_free(v) - sage_free(e) + sig_free(v) + sig_free(e) return F def linear_roots(self): @@ -997,7 +997,7 @@ cdef class ntl_ZZ_pX(object): #F.append(make_ZZ_p(v[i], self.c)) for i from 0 <= i < n: del v[i] - sage_free(v) + sig_free(v) return F def reverse(self, hi=None): diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index bdb63f08bcb..04ade9ca986 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -25,7 +25,6 @@ AUTHORS: from __future__ import division include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" include "sage/ext/cdefs.pxi" include 'misc.pxi' include 'decl.pxi' diff --git a/src/sage/libs/pari/closure.pyx b/src/sage/libs/pari/closure.pyx index 212aa537876..99d4b303e38 100644 --- a/src/sage/libs/pari/closure.pyx +++ b/src/sage/libs/pari/closure.pyx @@ -35,7 +35,6 @@ from cpython.ref cimport Py_INCREF include "cysignals/signals.pxi" from .paridecl cimport * -include "sage/libs/pari/pari_err.pxi" from pari_instance cimport pari_instance from gen cimport objtogen @@ -164,7 +163,7 @@ cpdef gen objtoclosure(f): ... PariError: call_python: forbidden multiplication t_VEC (1 elts) * t_VEC (1 elts) """ - pari_catch_sig_on() + sig_on() # Convert f to a t_INT containing the address of f cdef GEN f_int = utoi(f) # Create a t_CLOSURE which calls call_python() with py_func equal to f diff --git a/src/sage/libs/pari/decl.pxi b/src/sage/libs/pari/decl.pxi index 461716248b2..6e6227f8da1 100644 --- a/src/sage/libs/pari/decl.pxi +++ b/src/sage/libs/pari/decl.pxi @@ -1 +1,4 @@ +from sage.misc.superseded import deprecation +deprecation(20205, '''pari/decl.pxi is deprecated, use "from sage.libs.pari.paridecl cimport *" instead''') + from sage.libs.pari.paridecl cimport * diff --git a/src/sage/libs/pari/gen.pyx b/src/sage/libs/pari/gen.pyx index 94c36ec97c2..15b06d52d83 100644 --- a/src/sage/libs/pari/gen.pyx +++ b/src/sage/libs/pari/gen.pyx @@ -32,6 +32,19 @@ AUTHORS: - Jeroen Demeyer (2015-03-17): automatically generate methods from ``pari.desc`` (:trac:`17631` and :trac:`17860`) +- Kiran Kedlaya (2016-03-23): implement infinity type + +TESTS: + +Before :trac:`15654`, this used to take a very long time. +Now it takes much less than a second:: + + sage: pari.allocatemem(200000) + 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() + 2*3 + 3^4 + 2*3^6 + 3^7 + 2*3^8 + 2*3^9 + O(3^10) """ #***************************************************************************** @@ -63,15 +76,11 @@ from sage.misc.superseded import deprecation, deprecated_function_alias from .paridecl cimport * from .paripriv cimport * -include 'pari_err.pxi' -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" cimport cython -cdef extern from "misc.h": - int factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff) - from sage.libs.gmp.mpz cimport * from sage.libs.gmp.pylong cimport mpz_set_pylong from sage.libs.pari.closure cimport objtoclosure @@ -95,8 +104,7 @@ cdef class gen(gen_auto): raise RuntimeError("PARI objects cannot be instantiated directly; use pari(x) to convert x to PARI") def __dealloc__(self): - if self.b: - sage_free( self.b) + sig_free(self.b) def __repr__(self): """ @@ -114,13 +122,13 @@ cdef class gen(gen_auto): "hello" """ cdef char *c - pari_catch_sig_on() + sig_on() # Use sig_block(), which is needed because GENtostr() uses # malloc(), which is dangerous inside sig_on() sig_block() c = GENtostr(self.g) sig_unblock() - pari_catch_sig_off() + sig_off() s = str(c) pari_free(c) @@ -159,17 +167,11 @@ cdef class gen(gen_auto): """ cdef long h - pari_catch_sig_on() + sig_on() h = hash_GEN(self.g) - pari_catch_sig_off() + sig_off() return h - def _testclass(self): - import test - T = test.testclass() - T._init(self) - return T - def list(self): """ Convert self to a list of PARI gens. @@ -239,19 +241,19 @@ cdef class gen(gen_auto): return (objtogen, (s,)) cpdef ModuleElement _add_(self, ModuleElement right): - pari_catch_sig_on() + sig_on() return P.new_gen(gadd(self.g, (right).g)) cpdef ModuleElement _sub_(self, ModuleElement right): - pari_catch_sig_on() + sig_on() return P.new_gen(gsub(self.g, ( right).g)) cpdef RingElement _mul_(self, RingElement right): - pari_catch_sig_on() + sig_on() return P.new_gen(gmul(self.g, (right).g)) cpdef RingElement _div_(self, RingElement right): - pari_catch_sig_on() + sig_on() return P.new_gen(gdiv(self.g, (right).g)) def _add_one(gen self): @@ -269,7 +271,7 @@ cdef class gen(gen_auto): sage: n._add_one() x^3 + 1 """ - pari_catch_sig_on() + sig_on() return P.new_gen(gaddsg(1, self.g)) def __mod__(self, other): @@ -289,7 +291,7 @@ cdef class gen(gen_auto): """ cdef gen selfgen = objtogen(self) cdef gen othergen = objtogen(other) - pari_catch_sig_on() + sig_on() return P.new_gen(gmod(selfgen.g, othergen.g)) def __pow__(self, n, m): @@ -314,11 +316,11 @@ cdef class gen(gen_auto): cdef gen t1 = objtogen(n) if m is not None: t0 = t0.Mod(m) - pari_catch_sig_on() + sig_on() return P.new_gen(gpow(t0.g, t1.g, prec_bits_to_words(0))) def __neg__(gen self): - pari_catch_sig_on() + sig_on() return P.new_gen(gneg(self.g)) def __rshift__(self, long n): @@ -340,7 +342,7 @@ cdef class gen(gen_auto): 8 """ cdef gen t0 = objtogen(self) - pari_catch_sig_on() + sig_on() return P.new_gen(gshift(t0.g, -n)) def __lshift__(self, long n): @@ -361,11 +363,11 @@ cdef class gen(gen_auto): 132 """ cdef gen t0 = objtogen(self) - pari_catch_sig_on() + sig_on() return P.new_gen(gshift(t0.g, n)) def __invert__(gen self): - pari_catch_sig_on() + sig_on() return P.new_gen(ginv(self.g)) def getattr(gen self, attr): @@ -391,7 +393,7 @@ cdef class gen(gen_auto): """ cdef str s = "_." + attr cdef char *t = PyString_AsString(s) - pari_catch_sig_on() + sig_on() return P.new_gen(closure_callgen1(strtofunction(t), self.g)) def mod(self): @@ -411,7 +413,7 @@ cdef class gen(gen_auto): """ if typ(self.g) != t_INTMOD and typ(self.g) != t_POLMOD: raise TypeError("Not an INTMOD or POLMOD in mod()") - pari_catch_sig_on() + sig_on() # The hardcoded 1 below refers to the position in the internal # representation of a INTMOD or POLDMOD where the modulus is # stored. @@ -462,7 +464,7 @@ cdef class gen(gen_auto): PariError: incorrect type in pol (t_VEC) """ - pari_catch_sig_on() + sig_on() return P.new_gen(member_pol(self.g)) def nf_get_diff(self): @@ -480,7 +482,7 @@ cdef class gen(gen_auto): sage: pari(K).nf_get_diff() [12, 0, 0, 0; 0, 12, 8, 0; 0, 0, 4, 0; 0, 0, 0, 4] """ - pari_catch_sig_on() + sig_on() return P.new_gen(member_diff(self.g)) def nf_get_sign(self): @@ -508,11 +510,11 @@ cdef class gen(gen_auto): cdef long r1 cdef long r2 cdef GEN sign - pari_catch_sig_on() + sig_on() sign = member_sign(self.g) r1 = itos(gel(sign, 1)) r2 = itos(gel(sign, 2)) - pari_catch_sig_off() + sig_off() return [r1, r2] def nf_get_zk(self): @@ -531,7 +533,7 @@ cdef class gen(gen_auto): sage: pari(K).nf_get_zk() [1, y, y^3 - 4*y, y^2 - 2] """ - pari_catch_sig_on() + sig_on() return P.new_gen(member_zk(self.g)) def bnf_get_no(self): @@ -544,7 +546,7 @@ cdef class gen(gen_auto): sage: K.pari_bnf().bnf_get_no() 8 """ - pari_catch_sig_on() + sig_on() return P.new_gen(bnf_get_no(self.g)) def bnf_get_cyc(self): @@ -560,7 +562,7 @@ cdef class gen(gen_auto): sage: K.pari_bnf().bnf_get_cyc() [4, 2] """ - pari_catch_sig_on() + sig_on() return P.new_gen(bnf_get_cyc(self.g)) def bnf_get_gen(self): @@ -578,7 +580,7 @@ cdef class gen(gen_auto): sage: map(lambda J: K.ideal(J), G) [Fractional ideal (3, a + 2), Fractional ideal (2, a + 1)] """ - pari_catch_sig_on() + sig_on() return P.new_gen(bnf_get_gen(self.g)) def bnf_get_reg(self): @@ -593,9 +595,13 @@ cdef class gen(gen_auto): sage: K.pari_bnf().bnf_get_reg() 2.66089858019037... """ - pari_catch_sig_on() + sig_on() return P.new_gen(bnf_get_reg(self.g)) + def bnfunit(self): + sig_on() + return P.new_gen(bnf_get_fu(self.g)) + def pr_get_p(self): """ Returns the prime of `\ZZ` lying below this prime ideal. @@ -611,7 +617,7 @@ cdef class gen(gen_auto): sage: F[0,0].pr_get_p() 5 """ - pari_catch_sig_on() + sig_on() return P.new_gen(pr_get_p(self.g)) def pr_get_e(self): @@ -632,9 +638,9 @@ cdef class gen(gen_auto): 1 """ cdef long e - pari_catch_sig_on() + sig_on() e = pr_get_e(self.g) - pari_catch_sig_off() + sig_off() return e def pr_get_f(self): @@ -655,9 +661,9 @@ cdef class gen(gen_auto): 1 """ cdef long f - pari_catch_sig_on() + sig_on() f = pr_get_f(self.g) - pari_catch_sig_off() + sig_off() return f def pr_get_gen(self): @@ -681,7 +687,7 @@ cdef class gen(gen_auto): [-2, 1]~ i - 2 """ - pari_catch_sig_on() + sig_on() return P.new_gen(pr_get_gen(self.g)) def bid_get_cyc(self): @@ -699,7 +705,7 @@ cdef class gen(gen_auto): sage: J.bid_get_cyc() [4, 2] """ - pari_catch_sig_on() + sig_on() return P.new_gen(bid_get_cyc(self.g)) def bid_get_gen(self): @@ -726,7 +732,7 @@ cdef class gen(gen_auto): ... PariError: missing bid generators. Use idealstar(,,2) """ - pari_catch_sig_on() + sig_on() return P.new_gen(bid_get_gen(self.g)) def __getitem__(gen self, n): @@ -1029,7 +1035,7 @@ cdef class gen(gen_auto): cdef long l cdef Py_ssize_t ii, jj, step - pari_catch_sig_on() + sig_on() try: if isinstance(n, tuple): if typ(self.g) != t_MAT: @@ -1087,16 +1093,11 @@ cdef class gen(gen_auto): (self.g)[i+1] = (x.g) return finally: - pari_catch_sig_off() + sig_off() def __len__(gen self): return glength(self.g) - - ########################################### - # comparisons - ########################################### - cpdef _richcmp_(left, Element right, int op): """ Compare ``left`` and ``right`` using ``op``. @@ -1160,14 +1161,14 @@ cdef class gen(gen_auto): cdef bint r cdef GEN x = (left).g cdef GEN y = (right).g - pari_catch_sig_on() + sig_on() if op == 2: # == r = (gequal(x, y) != 0) elif op == 3: # != r = (gequal(x, y) == 0) else: r = rich_to_bool(op, gcmp(x, y)) - pari_catch_sig_off() + sig_off() return r cpdef int _cmp_(left, Element right) except -2: @@ -1231,25 +1232,28 @@ cdef class gen(gen_auto): """ cdef int r - pari_catch_sig_on() + sig_on() r = cmp_universal(left.g, (right).g) - pari_catch_sig_off() + sig_off() return r def __copy__(gen self): - pari_catch_sig_on() + sig_on() return P.new_gen(gcopy(self.g)) - ########################################### - # Conversion --> Python - # Try to convert to a meaningful python object - # in various ways - ########################################### - def list_str(gen self): """ Return str that might correctly evaluate to a Python-list. + + TESTS:: + + sage: pari.primes(5).list_str() + doctest:...: DeprecationWarning: the method list_str() is deprecated + See http://trac.sagemath.org/20219 for details. + [2, 3, 5, 7, 11] """ + deprecation(20219, "the method list_str() is deprecated") + s = str(self) if s[:4] == "Mat(": s = "[" + s[4:-1] + "]" @@ -1297,7 +1301,7 @@ cdef class gen(gen_auto): return "0" lx = lgefint(x)-2 # number of words size = lx*2*sizeof(long) - s = sage_malloc(size+2) # 1 char for sign, 1 char for '\0' + s = sig_malloc(size+2) # 1 char for sign, 1 char for '\0' sp = s + size+1 sp[0] = 0 xp = int_LSW(x) @@ -1315,7 +1319,7 @@ cdef class gen(gen_auto): sp = sp-1 sp[0] = c'-' k = sp - sage_free(s) + sig_free(s) return k def __int__(gen self): @@ -1367,19 +1371,15 @@ cdef class gen(gen_auto): sage: type(w[0]) """ - cdef long n, m + cdef long n if typ(self.g) != t_VECSMALL: raise TypeError("Object (=%s) must be of type t_VECSMALL." % self) - V = [] - m = glength(self.g) - for n from 0 <= n < m: - V.append(self.g[n+1]) - return V + return [self.g[n+1] for n in range(glength(self.g))] def python_list(gen self): """ Return a Python list of the PARI gens. This object must be of type - t_VEC. + t_VEC or t_COL. INPUT: None @@ -1391,7 +1391,7 @@ cdef class gen(gen_auto): EXAMPLES:: - sage: v=pari([1,2,3,10,102,10]) + sage: v = pari([1,2,3,10,102,10]) sage: w = v.python_list() sage: w [1, 2, 3, 10, 102, 10] @@ -1399,17 +1399,16 @@ cdef class gen(gen_auto): sage: pari("[1,2,3]").python_list() [1, 2, 3] + + sage: pari("[1,2,3]~").python_list() + [1, 2, 3] """ - cdef long n, m + cdef long n cdef gen t - if typ(self.g) != t_VEC: - raise TypeError("Object (=%s) must be of type t_VEC." % self) - m = glength(self.g) - V = [] - for n from 0 <= n < m: - V.append(self[n]) - return V + if typ(self.g) != t_VEC and typ(self.g) != t_COL: + raise TypeError("Object (=%s) must be of type t_VEC or t_COL." % self) + return [self[n] for n in range(glength(self.g))] def python(self, locals=None): """ @@ -1546,6 +1545,13 @@ cdef class gen(gen_auto): 11^-10 + 5*11^-7 + 11^-6 + O(11^-5) sage: pari(K(11^-5)).sage() 11^-5 + O(11^0) + + Conversion of infinities:: + + sage: pari('oo').sage() + +Infinity + sage: pari('-oo').sage() + -Infinity """ return gentoobj(self, locals) @@ -1583,9 +1589,9 @@ cdef class gen(gen_auto): Return Python float. """ cdef double d - pari_catch_sig_on() + sig_on() d = gtodouble(self.g) - pari_catch_sig_off() + sig_off() return d def __complex__(self): @@ -1612,10 +1618,10 @@ cdef class gen(gen_auto): PariError: incorrect type in greal/gimag (t_INTMOD) """ cdef double re, im - pari_catch_sig_on() + sig_on() re = gtodouble(greal(self.g)) im = gtodouble(gimag(self.g)) - pari_catch_sig_off() + sig_off() return complex(re, im) def __nonzero__(self): @@ -1634,11 +1640,6 @@ cdef class gen(gen_auto): """ return not gequal0(self.g) - - ########################################### - # Comparisons (from PARI) - ########################################### - def gequal(gen a, b): r""" Check whether `a` and `b` are equal using PARI's ``gequal``. @@ -1668,9 +1669,9 @@ cdef class gen(gen_auto): False """ cdef gen t0 = objtogen(b) - pari_catch_sig_on() + sig_on() cdef int ret = gequal(a.g, t0.g) - pari_catch_sig_off() + sig_off() return ret != 0 def gequal0(gen a): @@ -1690,9 +1691,9 @@ cdef class gen(gen_auto): sage: pari(GF(3^20,'t')(0)).gequal0() True """ - pari_catch_sig_on() + sig_on() cdef int ret = gequal0(a.g) - pari_catch_sig_off() + sig_off() return ret != 0 def gequal_long(gen a, long b): @@ -1717,15 +1718,11 @@ cdef class gen(gen_auto): sage: c.gequal_long(-3) False """ - pari_catch_sig_on() + sig_on() cdef int ret = gequalsg(b, a.g) - pari_catch_sig_off() + sig_off() return ret != 0 - - ########################################### - # arith1.c - ########################################### def isprime(gen self, long flag=0): """ isprime(x, flag=0): Returns True if x is a PROVEN prime number, and @@ -1763,186 +1760,15 @@ cdef class gen(gen_auto): (True, [2, 3, 1; 3, 5, 1; 7, 3, 1; 11, 3, 1; 31, 2, 1; 151, 3, 1; 331, 3, 1]) """ cdef GEN x - pari_catch_sig_on() + sig_on() x = gisprime(self.g, flag) if typ(x) != t_INT: # case flag=1 with prime input: x is the certificate return True, P.new_gen(x) else: - pari_catch_sig_off() + sig_off() return signe(x) != 0 - def qfbhclassno(gen n): - r""" - Computes the Hurwitz-Kronecker class number of `n`. - - INPUT: - - - `n` (gen) -- a non-negative integer - - OUTPUT: - - 0 if `n<0`, otherwise the Hurwitz-Kronecker class number of - `n`. This is `0` if `n\equiv1,2\mod4`, `-1/12` when `n=0`, - and otherwise it is the number of classes of positive definite - binary quadratic forms with discriminant `-n`, each weighted - by the number of its automorphisms. - - .. note:: - - If `n` is large (more than `5*10^5`), the result is - conditional upon GRH. - - EXAMPLES: - - The Hurwitz class number is 0 if n is congruent to 1 or 2 modulo 4:: - - sage: pari(10009).qfbhclassno() - 0 - sage: pari(2).qfbhclassno() - 0 - - It is -1/12 for n=0:: - - sage: pari(0).qfbhclassno() - -1/12 - - Otherwise it is the number of classes of positive definite - binary quadratic forms with discriminant `-n`, weighted by - `1/m` where `m` is the number of automorphisms of the form:: - - sage: pari(4).qfbhclassno() - 1/2 - sage: pari(3).qfbhclassno() - 1/3 - sage: pari(23).qfbhclassno() - 3 - - """ - pari_catch_sig_on() - return P.new_gen(hclassno(n.g)) - - def qfbclassno(gen d, long flag=0): - r""" - Computes the class number of the quadratic order of discriminant `d`. - - INPUT: - - - `d` (gen) -- a quadratic discriminant, which is an integer - congruent to `0` or `1`\mod4`, not a square. - - - ``flag`` (long int) -- if 0 (default), uses Euler product - and the functional equation for `d>0` or Shanks's method for - `d<0`; if 1, uses Euler products and the functional equation - in both cases. - - OUTPUT: - - The class number of the quadratic order with discriminant `d`. - - .. warning:: - - Using Euler products and the functional equation is - reliable but has complexity `O(|d|^{1/2})`. Using Shanks's - method for `d<0` is `O(|d|^{1/4})` but this function may give - incorrect results when the class group has many cyclic - factors, because implementing Shanks's method in full - generality slows it down immensely. It is therefore - strongly recommended to double-check results using either - the version with ``flag`` = 1 or the function - ``quadclassunit``. The result is unconditionally correct - for `-d < 2e10`. - - EXAMPLES:: - - sage: pari(-4).qfbclassno() - 1 - sage: pari(-23).qfbclassno() - 3 - sage: pari(-104).qfbclassno() - 6 - - sage: pari(109).qfbclassno() - 1 - sage: pari(10001).qfbclassno() - 16 - sage: pari(10001).qfbclassno(flag=1) - 16 - - TESTS: - - The input must be congruent to `0` or `1\mod4` and not a square:: - - sage: pari(3).qfbclassno() - Traceback (most recent call last): - ... - PariError: domain error in classno2: disc % 4 > 1 - sage: pari(4).qfbclassno() - Traceback (most recent call last): - ... - PariError: domain error in classno2: issquare(disc) = 1 - """ - pari_catch_sig_on() - return P.new_gen(qfbclassno0(d.g, flag)) - - def quadclassunit(gen d, long precision=0): - r""" - Returns the class group of a quadratic order of discriminant `d`. - - INPUT: - - - `d` (gen) -- a quadratic discriminant, which is an integer - congruent to `0` or `1`\mod4`, not a square. - - OUTPUT: - - (h,cyc,gen,reg) where: - - - h is the class number - - cyc is the class group structure (list of invariants) - - gen is the class group generators (list of quadratic forms) - - reg is the regulator - - ALGORITHM: - - Buchmann-McCurley's sub-exponential algorithm - - EXAMPLES:: - - sage: pari(-4).quadclassunit() - [1, [], [], 1] - sage: pari(-23).quadclassunit() - [3, [3], [Qfb(2, 1, 3)], 1] - sage: pari(-104).quadclassunit() - [6, [6], [Qfb(5, -4, 6)], 1] - - sage: pari(109).quadclassunit() - [1, [], [], 5.56453508676047] - sage: pari(10001).quadclassunit() # random generators - [16, [16], [Qfb(10, 99, -5, 0.E-38)], 5.29834236561059] - sage: pari(10001).quadclassunit()[0] - 16 - sage: pari(10001).quadclassunit()[1] - [16] - sage: pari(10001).quadclassunit()[3] - 5.29834236561059 - - TESTS: - - The input must be congruent to `0` or `1\mod4` and not a square:: - - sage: pari(3).quadclassunit() - Traceback (most recent call last): - ... - PariError: domain error in Buchquad: disc % 4 > 1 - sage: pari(4).quadclassunit() - Traceback (most recent call last): - ... - PariError: domain error in Buchquad: issquare(disc) = 1 - """ - pari_catch_sig_on() - return P.new_gen(quadclassunit0(d.g, 0, NULL, prec_bits_to_words(precision))) - def ispseudoprime(gen self, long flag=0): """ ispseudoprime(x, flag=0): Returns True if x is a pseudo-prime @@ -1977,9 +1803,9 @@ cdef class gen(gen_auto): sage: n.ispseudoprime(2) False """ - pari_catch_sig_on() + sig_on() cdef long t = ispseudoprime(self.g, flag) - pari_catch_sig_off() + sig_off() return t != 0 def ispower(gen self, k=None): @@ -2019,19 +1845,19 @@ cdef class gen(gen_auto): cdef gen t0 if k is None: - pari_catch_sig_on() + sig_on() n = gisanypower(self.g, &x) if n == 0: - pari_catch_sig_off() + sig_off() return 1, self else: return n, P.new_gen(x) else: t0 = objtogen(k) - pari_catch_sig_on() + sig_on() n = ispower(self.g, t0.g, &x) if n == 0: - pari_catch_sig_off() + sig_off() return False, None else: return k, P.new_gen(x) @@ -2072,10 +1898,10 @@ cdef class gen(gen_auto): cdef GEN x cdef long n - pari_catch_sig_on() + sig_on() n = isprimepower(self.g, &x) if n == 0: - pari_catch_sig_off() + sig_off() return 0, self else: return n, P.new_gen(x) @@ -2109,68 +1935,38 @@ cdef class gen(gen_auto): cdef GEN x cdef long n - pari_catch_sig_on() + sig_on() n = ispseudoprimepower(self.g, &x) if n == 0: - pari_catch_sig_off() + sig_off() return 0, self else: return n, P.new_gen(x) - ########################################### - # 1: Standard monadic or dyadic OPERATORS - ########################################### - def sign(gen x): + def vecmax(x): """ - Return the sign of x, where x is of type integer, real or - fraction. + Return the maximum of the elements of the vector/matrix `x`. EXAMPLES:: - sage: pari(pi).sign() - 1 - sage: pari(0).sign() - 0 - sage: pari(-1/2).sign() - -1 - - PARI throws an error if you attempt to take the sign of a - complex number:: - - sage: pari(I).sign() - Traceback (most recent call last): - ... - PariError: incorrect type in gsigne (t_COMPLEX) - + sage: pari([1, -5/3, 8.0]).vecmax() + 8.00000000000000 """ - pari_catch_sig_on() - r = gsigne(x.g) - pari_catch_sig_off() - return r + sig_on() + return P.new_gen(vecmax(x.g)) - def vecmax(gen x): - """ - vecmax(x): Return the maximum of the elements of the vector/matrix - x. + def vecmin(x): """ - pari_catch_sig_on() - return P.new_gen(vecmax(x.g)) + Return the minimum of the elements of the vector/matrix `x`. + EXAMPLES:: - def vecmin(gen x): - """ - vecmin(x): Return the maximum of the elements of the vector/matrix - x. + sage: pari([1, -5/3, 8.0]).vecmin() + -5/3 """ - pari_catch_sig_on() + sig_on() return P.new_gen(vecmin(x.g)) - - - ########################################### - # 2: CONVERSIONS and similar elementary functions - ########################################### - def Col(gen x, long n = 0): """ Transform the object `x` into a column vector with minimal size `|n|`. @@ -2215,7 +2011,7 @@ cdef class gen(gen_auto): See also :meth:`Vec` (create a row vector) for more examples and :meth:`Colrev` (create a column in reversed order). """ - pari_catch_sig_on() + sig_on() return P.new_gen(_Vec_append(gtocol(x.g), gen_0, n)) def Colrev(gen x, long n = 0): @@ -2256,7 +2052,7 @@ cdef class gen(gen_auto): sage: pari([1,2,3,4]).Colrev(-6) [4, 3, 2, 1, 0, 0]~ """ - pari_catch_sig_on() + sig_on() # Create a non-reversed column vector cdef GEN v = _Vec_append(gtocol(x.g), gen_0, n) # Reverse it in-place @@ -2271,300 +2067,6 @@ cdef class gen(gen_auto): R -= 1 return P.new_gen(v) - def List(gen x): - """ - List(x): transforms the PARI vector or list x into a list. - - EXAMPLES:: - - sage: v = pari([1,2,3]) - sage: v - [1, 2, 3] - sage: v.type() - 't_VEC' - sage: w = v.List() - sage: w - List([1, 2, 3]) - sage: w.type() - 't_LIST' - """ - pari_catch_sig_on() - return P.new_gen(gtolist(x.g)) - - def Mat(gen x): - """ - Mat(x): Returns the matrix defined by x. - - - If x is already a matrix, a copy of x is created and returned. - - - If x is not a vector or a matrix, this function returns a 1x1 - matrix. - - - If x is a row (resp. column) vector, this functions returns - a 1-row (resp. 1-column) matrix, *unless* all elements are - column (resp. row) vectors of the same length, in which case - the vectors are concatenated sideways and the associated big - matrix is returned. - - INPUT: - - - - ``x`` - gen - - - OUTPUT: - - - - ``gen`` - a PARI matrix - - - EXAMPLES:: - - sage: x = pari(5) - sage: x.type() - 't_INT' - sage: y = x.Mat() - sage: y - Mat(5) - sage: y.type() - 't_MAT' - sage: x = pari('[1,2;3,4]') - sage: x.type() - 't_MAT' - sage: x = pari('[1,2,3,4]') - sage: x.type() - 't_VEC' - sage: y = x.Mat() - sage: y - Mat([1, 2, 3, 4]) - sage: y.type() - 't_MAT' - - :: - - sage: v = pari('[1,2;3,4]').Vec(); v - [[1, 3]~, [2, 4]~] - sage: v.Mat() - [1, 2; 3, 4] - sage: v = pari('[1,2;3,4]').Col(); v - [[1, 2], [3, 4]]~ - sage: v.Mat() - [1, 2; 3, 4] - """ - pari_catch_sig_on() - return P.new_gen(gtomat(x.g)) - - def Mod(gen x, y): - """ - Mod(x, y): Returns the object x modulo y, denoted Mod(x, y). - - The input y must be a an integer or a polynomial: - - - If y is an INTEGER, x must also be an integer, a rational - number, or a p-adic number compatible with the modulus y. - - - If y is a POLYNOMIAL, x must be a scalar (which is not a - polmod), a polynomial, a rational function, or a power - series. - - .. warning:: - - This function is not the same as ``x % y`` which is an - integer or a polynomial. - - INPUT: - - - - ``x`` - gen - - - ``y`` - integer or polynomial - - - OUTPUT: - - - - ``gen`` - intmod or polmod - - - EXAMPLES:: - - sage: z = pari(3) - sage: x = z.Mod(pari(7)) - sage: x - Mod(3, 7) - sage: x^2 - Mod(2, 7) - sage: x^100 - Mod(4, 7) - sage: x.type() - 't_INTMOD' - - :: - - sage: f = pari("x^2 + x + 1") - sage: g = pari("x") - sage: a = g.Mod(f) - sage: a - Mod(x, x^2 + x + 1) - sage: a*a - Mod(-x - 1, x^2 + x + 1) - sage: a.type() - 't_POLMOD' - """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(gmodulo(x.g, t0.g)) - - def Pol(self, v=-1): - """ - Pol(x, v): convert x into a polynomial with main variable v and - return the result. - - - If x is a scalar, returns a constant polynomial. - - - If x is a power series, the effect is identical to - ``truncate``, i.e. it chops off the `O(X^k)`. - - - If x is a vector, this function creates the polynomial whose - coefficients are given in x, with x[0] being the leading - coefficient (which can be zero). - - .. warning:: - - This is *not* a substitution function. It will not - transform an object containing variables of higher priority - than v:: - - sage: pari('x+y').Pol('y') - Traceback (most recent call last): - ... - PariError: incorrect priority in gtopoly: variable x < y - - INPUT: - - - - ``x`` - gen - - - ``v`` - (optional) which variable, defaults to 'x' - - - OUTPUT: - - - - ``gen`` - a polynomial - - - EXAMPLES:: - - sage: v = pari("[1,2,3,4]") - sage: f = v.Pol() - sage: f - x^3 + 2*x^2 + 3*x + 4 - sage: f*f - x^6 + 4*x^5 + 10*x^4 + 20*x^3 + 25*x^2 + 24*x + 16 - - :: - - sage: v = pari("[1,2;3,4]") - sage: v.Pol() - [1, 3]~*x + [2, 4]~ - """ - pari_catch_sig_on() - return P.new_gen(gtopoly(self.g, P.get_var(v))) - - def Polrev(self, v=-1): - """ - Polrev(x, v): Convert x into a polynomial with main variable v and - return the result. This is the reverse of Pol if x is a vector, - otherwise it is identical to Pol. By "reverse" we mean that the - coefficients are reversed. - - INPUT: - - - ``x`` - gen - - OUTPUT: - - - ``gen`` - a polynomial - - EXAMPLES:: - - sage: v = pari("[1,2,3,4]") - sage: f = v.Polrev() - sage: f - 4*x^3 + 3*x^2 + 2*x + 1 - sage: v.Pol() - x^3 + 2*x^2 + 3*x + 4 - sage: v.Polrev('y') - 4*y^3 + 3*y^2 + 2*y + 1 - - Note that Polrev does *not* reverse the coefficients of a - polynomial! :: - - sage: f - 4*x^3 + 3*x^2 + 2*x + 1 - sage: f.Polrev() - 4*x^3 + 3*x^2 + 2*x + 1 - sage: v = pari("[1,2;3,4]") - sage: v.Polrev() - [2, 4]~*x + [1, 3]~ - """ - pari_catch_sig_on() - return P.new_gen(gtopolyrev(self.g, P.get_var(v))) - - def Qfb(gen a, b, c, D=0, unsigned long precision=0): - """ - Qfb(a,b,c,D=0.): Returns the binary quadratic form - - .. math:: - - ax^2 + bxy + cy^2. - - - The optional D is 0 by default and initializes Shank's distance if - `b^2 - 4ac > 0`. The discriminant of the quadratic form must not - be a perfect square. - - .. note:: - - Negative definite forms are not implemented, so use their - positive definite counterparts instead. (I.e., if f is a - negative definite quadratic form, then -f is positive - definite.) - - INPUT: - - - - ``a`` - gen - - - ``b`` - gen - - - ``c`` - gen - - - ``D`` - gen (optional, defaults to 0) - - - OUTPUT: - - - - ``gen`` - binary quadratic form - - - EXAMPLES:: - - sage: pari(3).Qfb(7, 1) - Qfb(3, 7, 1, 0.E-19) - sage: pari(3).Qfb(7, 2) # discriminant is 25 - Traceback (most recent call last): - ... - PariError: domain error in Qfb: issquare(disc) = 1 - """ - cdef gen t0 = objtogen(b) - cdef gen t1 = objtogen(c) - cdef gen t2 = objtogen(D) - pari_catch_sig_on() - return P.new_gen(Qfb0(a.g, t0.g, t1.g, t2.g, prec_bits_to_words(precision))) - def Ser(gen f, v=-1, long precision=-1): """ Return a power series or Laurent series in the variable `v` @@ -2627,7 +2129,7 @@ cdef class gen(gen_auto): """ if precision < 0: precision = P.get_series_precision() - pari_catch_sig_on() + sig_on() cdef long vn = P.get_var(v) if typ(f.g) == t_VEC: # The precision flag is ignored for vectors, so we first @@ -2636,44 +2138,7 @@ cdef class gen(gen_auto): else: return P.new_gen(gtoser(f.g, vn, precision)) - def Set(gen x): - """ - Set(x): convert x into a set, i.e. a row vector of strings in - increasing lexicographic order. - - INPUT: - - - - ``x`` - gen - - - OUTPUT: - - - - ``gen`` - a vector of strings in increasing - lexicographic order. - - - EXAMPLES:: - - sage: pari([1,5,2]).Set() - [1, 2, 5] - sage: pari([]).Set() # the empty set - [] - sage: pari([1,1,-1,-1,3,3]).Set() - [-1, 1, 3] - sage: pari(1).Set() - [1] - sage: pari('1/(x*y)').Set() - [1/(y*x)] - sage: pari('["bc","ab","bc"]').Set() - ["ab", "bc"] - """ - pari_catch_sig_on() - return P.new_gen(gtoset(x.g)) - - - def Str(self): + def Str(self): """ Str(self): Return the print representation of self as a PARI object. @@ -2706,7 +2171,7 @@ cdef class gen(gen_auto): 't_STR' """ cdef char* c - pari_catch_sig_on() + sig_on() # Use sig_block(), which is needed because GENtostr() uses # malloc(), which is dangerous inside sig_on() sig_block() @@ -2716,42 +2181,6 @@ cdef class gen(gen_auto): pari_free(c) return v - - def Strchr(gen x): - """ - Strchr(x): converts x to a string, translating each integer into a - character (in ASCII). - - .. note:: - - :meth:`.Vecsmall` is (essentially) the inverse to :meth:`.Strchr`. - - INPUT: - - - - ``x`` - PARI vector of integers - - - OUTPUT: - - - - ``gen`` - a PARI string - - - EXAMPLES:: - - sage: pari([65,66,123]).Strchr() - "AB{" - sage: pari('"Sage"').Vecsmall() # pari('"Sage"') --> PARI t_STR - Vecsmall([83, 97, 103, 101]) - sage: _.Strchr() - "Sage" - sage: pari([83, 97, 103, 101]).Strchr() - "Sage" - """ - pari_catch_sig_on() - return P.new_gen(Strchr(x.g)) - def Strexpand(gen x): """ Concatenate the entries of the vector `x` into a single string, @@ -2782,7 +2211,7 @@ cdef class gen(gen_auto): """ if typ(x.g) != t_VEC: x = P.vector(1, [x]) - pari_catch_sig_on() + sig_on() return P.new_gen(Strexpand(x.g)) def Strtex(gen x): @@ -2813,11 +2242,10 @@ cdef class gen(gen_auto): """ if typ(x.g) != t_VEC: x = P.vector(1, [x]) - pari_catch_sig_on() + sig_on() return P.new_gen(Strtex(x.g)) - def printtex(gen x): - return x.Strtex() + printtex = deprecated_function_alias(20219, Strtex) def Vec(gen x, long n = 0): """ @@ -2874,7 +2302,7 @@ cdef class gen(gen_auto): See also :meth:`Col` (create a column vector) and :meth:`Vecrev` (create a vector in reversed order). """ - pari_catch_sig_on() + sig_on() return P.new_gen(_Vec_append(gtovec(x.g), gen_0, n)) def Vecrev(gen x, long n = 0): @@ -2921,7 +2349,7 @@ cdef class gen(gen_auto): sage: pari([1,2,3,4]).Vecrev(-6) [4, 3, 2, 1, 0, 0] """ - pari_catch_sig_on() + sig_on() return P.new_gen(_Vec_append(gtovecrev(x.g), gen_0, -n)) def Vecsmall(gen x, long n = 0): @@ -2961,197 +2389,9 @@ cdef class gen(gen_auto): sage: pari([1,2,3]).Vecsmall(-6) Vecsmall([0, 0, 0, 1, 2, 3]) """ - pari_catch_sig_on() + sig_on() return P.new_gen(_Vec_append(gtovecsmall(x.g), 0, n)) - def binary(gen x): - """ - Return the vector formed by the binary digits of abs(x). - - INPUT: - - - ``x`` -- gen of type ``t_INT`` - - OUTPUT: - - - ``gen`` -- gen of type ``t_VEC`` - - EXAMPLES:: - - sage: pari(0).binary() - [] - sage: pari(-5).binary() - [1, 0, 1] - sage: pari(5).binary() - [1, 0, 1] - sage: pari(2005).binary() - [1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1] - - :: - - sage: pari('"2"').binary() - Traceback (most recent call last): - ... - PariError: incorrect type in binary (t_STR) - """ - pari_catch_sig_on() - return P.new_gen(binaire(x.g)) - - def bitand(gen x, y): - """ - bitand(x,y): Bitwise and of two integers x and y. Negative numbers - behave as if modulo some large power of 2. - - INPUT: - - - - ``x`` - gen (of type t_INT) - - - ``y`` - coercible to gen (of type t_INT) - - - OUTPUT: - - - - ``gen`` - of type type t_INT - - - EXAMPLES:: - - sage: pari(8).bitand(4) - 0 - sage: pari(8).bitand(8) - 8 - sage: pari(6).binary() - [1, 1, 0] - sage: pari(7).binary() - [1, 1, 1] - sage: pari(6).bitand(7) - 6 - sage: pari(19).bitand(-1) - 19 - sage: pari(-1).bitand(-1) - -1 - """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(gbitand(x.g, t0.g)) - - - def bitneg(gen x, long n=-1): - r""" - bitneg(x,n=-1): Bitwise negation of the integer x truncated to n - bits. n=-1 (the default) represents an infinite sequence of the bit - 1. Negative numbers behave as if modulo some large power of 2. - - With n=-1, this function returns -n-1. With n = 0, it returns a - number a such that `a\cong -n-1 \pmod{2^n}`. - - INPUT: - - - - ``x`` - gen (t_INT) - - - ``n`` - long, default = -1 - - - OUTPUT: - - - - ``gen`` - t_INT - - - EXAMPLES:: - - sage: pari(10).bitneg() - -11 - sage: pari(1).bitneg() - -2 - sage: pari(-2).bitneg() - 1 - sage: pari(-1).bitneg() - 0 - sage: pari(569).bitneg() - -570 - sage: pari(569).bitneg(10) - 454 - sage: 454 % 2^10 - 454 - sage: -570 % 2^10 - 454 - """ - pari_catch_sig_on() - return P.new_gen(gbitneg(x.g,n)) - - - def bitnegimply(gen x, y): - """ - bitnegimply(x,y): Bitwise negated imply of two integers x and y, in - other words, x BITAND BITNEG(y). Negative numbers behave as if - modulo big power of 2. - - INPUT: - - - - ``x`` - gen (of type t_INT) - - - ``y`` - coercible to gen (of type t_INT) - - - OUTPUT: - - - - ``gen`` - of type type t_INT - - - EXAMPLES:: - - sage: pari(14).bitnegimply(0) - 14 - sage: pari(8).bitnegimply(8) - 0 - sage: pari(8+4).bitnegimply(8) - 4 - """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(gbitnegimply(x.g, t0.g)) - - - def bitor(gen x, y): - """ - bitor(x,y): Bitwise or of two integers x and y. Negative numbers - behave as if modulo big power of 2. - - INPUT: - - - - ``x`` - gen (of type t_INT) - - - ``y`` - coercible to gen (of type t_INT) - - - OUTPUT: - - - - ``gen`` - of type type t_INT - - - EXAMPLES:: - - sage: pari(14).bitor(0) - 14 - sage: pari(8).bitor(4) - 12 - sage: pari(12).bitor(1) - 13 - sage: pari(13).bitor(1) - 13 - """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(gbitor(x.g, t0.g)) - def bittest(gen x, long n): """ @@ -3189,5867 +2429,1715 @@ cdef class gen(gen_auto): sage: [pari(-3).bittest(n) for n in range(10)] [True, False, True, True, True, True, True, True, True, True] """ - pari_catch_sig_on() + sig_on() cdef long b = bittest(x.g, n) - pari_catch_sig_off() + sig_off() return b != 0 - def bitxor(gen x, y): + lift_centered = gen_auto.centerlift + + def padicprime(gen x): """ - bitxor(x,y): Bitwise exclusive or of two integers x and y. Negative - numbers behave as if modulo big power of 2. + The uniformizer of the p-adic ring this element lies in, as a t_INT. INPUT: + - ``x`` - gen, of type t_PADIC - - ``x`` - gen (of type t_INT) + OUTPUT: - - ``y`` - coercible to gen (of type t_INT) + - ``p`` - gen, of type t_INT + EXAMPLES:: - OUTPUT: + sage: K = Qp(11,5) + sage: x = K(11^-10 + 5*11^-7 + 11^-6) + sage: y = pari(x) + sage: y.padicprime() + 11 + sage: y.padicprime().type() + 't_INT' + """ + sig_on() + return P.new_gen(gel(x.g, 2)) + + def precision(gen x, long n=-1): + """ + Change the precision of `x` to be `n`, where `n` is an integer. + If `n` is omitted, output the real precision of `x`. + INPUT: - - ``gen`` - of type type t_INT + - ``x`` - gen + - ``n`` - (optional) int - EXAMPLES:: + OUTPUT: gen + """ + if n <= 0: + return precision(x.g) + sig_on() + return P.new_gen(precision0(x.g, n)) - sage: pari(6).bitxor(4) - 2 - sage: pari(0).bitxor(4) - 4 - sage: pari(6).bitxor(0) - 6 + def round(gen x, estimate=False): """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(gbitxor(x.g, t0.g)) + round(x,estimate=False): If x is a real number, returns x rounded + to the nearest integer (rounding up). If the optional argument + estimate is True, also returns the binary exponent e of the + difference between the original and the rounded value (the + "fractional part") (this is the integer ceiling of log_2(error)). + When x is a general PARI object, this function returns the result + of rounding every coefficient at every level of PARI object. Note + that this is different than what the truncate function does (see + the example below). - def ceil(gen x): - """ - For real x: return the smallest integer = x. For rational - functions: the quotient of numerator by denominator. For lists: - apply componentwise. + One use of round is to get exact results after a long approximate + computation, when theory tells you that the coefficients must be + integers. INPUT: - ``x`` - gen + - ``estimate`` - (optional) bool, False by default + OUTPUT: + - if estimate is False, return a single gen. - - ``gen`` - depends on type of x - + - if estimate is True, return rounded version of x and error + estimate in bits, both as gens. EXAMPLES:: - sage: pari(1.4).ceil() + sage: pari('1.5').round() 2 - sage: pari(-1.4).ceil() - -1 - sage: pari(3/4).ceil() - 1 - sage: pari(x).ceil() - x - sage: pari((x^2+x+1)/x).ceil() - x + 1 - - This may be unexpected: but it is correct, treating the argument as - a rational function in RR(x). - - :: - - sage: pari(x^2+5*x+2.5).ceil() - x^2 + 5*x + 2.50000000000000 + sage: pari('1.5').round(True) + (2, -1) + sage: pari('1.5 + 2.1*I').round() + 2 + 2*I + sage: pari('1.0001').round(True) + (1, -14) + sage: pari('(2.4*x^2 - 1.7)/x').round() + (2*x^2 - 2)/x + sage: pari('(2.4*x^2 - 1.7)/x').truncate() + 2.40000000000000*x """ - pari_catch_sig_on() - return P.new_gen(gceil(x.g)) + cdef int n + cdef long e + cdef gen y + sig_on() + if not estimate: + return P.new_gen(ground(x.g)) + y = P.new_gen(grndtoi(x.g, &e)) + return y, e - def centerlift(gen x, v=-1): + def sizeword(gen x): """ - Centered lift of x. This function returns exactly the same thing as lift, - except if x is an integer mod. + Return the total number of machine words occupied by the + complete tree of the object x. A machine word is 32 or + 64 bits, depending on the computer. INPUT: - - ``x`` -- gen - - - ``v`` -- var (default: x) - - OUTPUT: + - ``x`` - gen - - `r` -- gen. If `x` is an integer mod `n`, return the unique element `r` congruent - to `x` mod `n` such that `-n/2 < r \\leq n/2`. + OUTPUT: int (a Python int) EXAMPLES:: - sage: x = pari(-2).Mod(5) - sage: x.centerlift() - -2 - sage: x.lift() + sage: pari('0').sizeword() + 2 + sage: pari('1').sizeword() 3 - sage: f = pari('x-1').Mod('x^2 + 1') - sage: f.centerlift() - x - 1 - sage: f.lift() - x - 1 - sage: f = pari('x-y').Mod('x^2+1') - sage: f - Mod(x - y, x^2 + 1) - sage: f.centerlift('x') - x - y - sage: f.centerlift('y') - Mod(x - y, x^2 + 1) - - For compatibility with other classes in Sage, there is an alias - ``lift_centered``:: - - sage: pari("Mod(3,5)").lift_centered() - -2 + sage: pari('1000000').sizeword() + 3 + sage: pari('10^100').sizeword() + 13 # 32-bit + 8 # 64-bit + sage: pari(RDF(1.0)).sizeword() + 4 # 32-bit + 3 # 64-bit + sage: pari('x').sizeword() + 9 + sage: pari('x^20').sizeword() + 66 + sage: pari('[x, I]').sizeword() + 20 """ - pari_catch_sig_on() - return P.new_gen(centerlift0(x.g, P.get_var(v))) - - lift_centered = centerlift + return gsizeword(x.g) - def component(gen x, long n): + def sizebyte(gen x): """ - component(x, long n): Return n'th component of the internal - representation of x. This function is 1-based instead of 0-based. - - .. note:: - - For vectors or matrices, it is simpler to use x[n-1]. For - list objects such as is output by nfinit, it is easier to - use member functions. + Return the total number of bytes occupied by the complete tree + of the object x. Note that this number depends on whether the + computer is 32-bit or 64-bit. INPUT: - - ``x`` - gen - - ``n`` - C long (coercible to) - - - OUTPUT: gen - - EXAMPLES:: - - sage: pari([0,1,2,3,4]).component(1) - 0 - sage: pari([0,1,2,3,4]).component(2) - 1 - sage: pari([0,1,2,3,4]).component(4) - 3 - sage: pari('x^3 + 2').component(1) - 2 - sage: pari('x^3 + 2').component(2) - 0 - sage: pari('x^3 + 2').component(4) - 1 + OUTPUT: int (a Python int) - :: + EXAMPLE:: - sage: pari('x').component(0) - Traceback (most recent call last): - ... - PariError: non-existent component: index < 1 + sage: pari('1').sizebyte() + 12 # 32-bit + 24 # 64-bit """ - pari_catch_sig_on() - return P.new_gen(compo(x.g, n)) + return gsizebyte(x.g) - def conj(gen x): + def truncate(gen x, estimate=False): """ - conj(x): Return the algebraic conjugate of x. - - INPUT: - - - - ``x`` - gen + truncate(x,estimate=False): Return the truncation of x. If estimate + is True, also return the number of error bits. + When x is in the real numbers, this means that the part after the + decimal point is chopped away, e is the binary exponent of the + difference between the original and truncated value (the + "fractional part"). If x is a rational function, the result is the + integer part (Euclidean quotient of numerator by denominator) and + if requested the error estimate is 0. - OUTPUT: gen + When truncate is applied to a power series (in X), it transforms it + into a polynomial or a rational function with denominator a power + of X, by chopping away the `O(X^k)`. Similarly, when + applied to a p-adic number, it transforms it into an integer or a + rational number by chopping away the `O(p^k)`. - EXAMPLES:: + INPUT: - sage: pari('x+1').conj() - x + 1 - sage: pari('x+I').conj() - x - I - sage: pari('1/(2*x+3*I)').conj() - 1/(2*x - 3*I) - sage: pari([1,2,'2-I','Mod(x,x^2+1)', 'Mod(x,x^2-2)']).conj() - [1, 2, 2 + I, Mod(-x, x^2 + 1), Mod(-x, x^2 - 2)] - sage: pari('Mod(x,x^2-2)').conj() - Mod(-x, x^2 - 2) - sage: pari('Mod(x,x^3-3)').conj() - Traceback (most recent call last): - ... - PariError: incorrect type in gconj (t_POLMOD) - """ - pari_catch_sig_on() - return P.new_gen(gconj(x.g)) - def conjvec(gen x, unsigned long precision=0): - """ - conjvec(x): Returns the vector of all conjugates of the algebraic - number x. An algebraic number is a polynomial over Q modulo an - irreducible polynomial. + - ``x`` - gen - INPUT: + - ``estimate`` - (optional) bool, which is False by + default - - ``x`` - gen + OUTPUT: + - if estimate is False, return a single gen. - OUTPUT: gen + - if estimate is True, return rounded version of x and error + estimate in bits, both as gens. EXAMPLES:: - sage: pari('Mod(1+x,x^2-2)').conjvec() - [-0.414213562373095, 2.41421356237310]~ - sage: pari('Mod(x,x^3-3)').conjvec() - [1.44224957030741, -0.721124785153704 - 1.24902476648341*I, -0.721124785153704 + 1.24902476648341*I]~ - sage: pari('Mod(1+x,x^2-2)').conjvec(precision=192)[0].sage() - -0.414213562373095048801688724209698078569671875376948073177 + sage: pari('(x^2+1)/x').round() + (x^2 + 1)/x + sage: pari('(x^2+1)/x').truncate() + x + sage: pari('1.043').truncate() + 1 + sage: pari('1.043').truncate(True) + (1, -5) + sage: pari('1.6').truncate() + 1 + sage: pari('1.6').round() + 2 + sage: pari('1/3 + 2 + 3^2 + O(3^3)').truncate() + 34/3 + sage: pari('sin(x+O(x^10))').truncate() + 1/362880*x^9 - 1/5040*x^7 + 1/120*x^5 - 1/6*x^3 + x + sage: pari('sin(x+O(x^10))').round() # each coefficient has abs < 1 + x + O(x^10) """ - pari_catch_sig_on() - return P.new_gen(conjvec(x.g, prec_bits_to_words(precision))) + cdef long e + cdef gen y + sig_on() + if not estimate: + return P.new_gen(gtrunc(x.g)) + y = P.new_gen(gcvtoi(x.g, &e)) + return y, e - def denominator(gen x): + def _valp(gen x): """ - denominator(x): Return the denominator of x. When x is a vector, - this is the least common multiple of the denominators of the - components of x. + Return the valuation of x where x is a p-adic number (t_PADIC) + or a Laurent series (t_SER). If x is a different type, this + will give a bogus number. + + EXAMPLES:: - what about poly? INPUT: + sage: pari('1/x^2 + O(x^10)')._valp() + -2 + sage: pari('O(x^10)')._valp() + 10 + sage: pari('(1145234796 + O(3^10))/771966234')._valp() + -2 + sage: pari('O(2^10)')._valp() + 10 + sage: pari('x')._valp() # random + -35184372088832 + """ + # This is a simple macro, so we don't need sig_on() + return valp(x.g) + def bernfrac(x): + r""" + The Bernoulli number `B_x`, where `B_0 = 1`, + `B_1 = -1/2`, `B_2 = 1/6,\ldots,` expressed as a + rational number. The argument `x` should be of type + integer. - - ``x`` - gen + EXAMPLES:: + sage: pari(18).bernfrac() + 43867/798 + sage: [pari(n).bernfrac() for n in range(10)] + [1, -1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0] + """ + return P.bernfrac(x) - OUTPUT: gen + def bernreal(x, unsigned long precision=0): + r""" + The Bernoulli number `B_x`, as for the function bernfrac, + but `B_x` is returned as a real number (with the current + precision). EXAMPLES:: - sage: pari('5/9').denominator() - 9 - sage: pari('(x+1)/(x-2)').denominator() - x - 2 - sage: pari('2/3 + 5/8*x + 7/3*x^2 + 1/5*y').denominator() - 1 - sage: pari('2/3*x').denominator() - 1 - sage: pari('[2/3, 5/8, 7/3, 1/5]').denominator() - 120 + sage: pari(18).bernreal() + 54.9711779448622 + sage: pari(18).bernreal(precision=192).sage() + 54.9711779448621553884711779448621553884711779448621553885 """ - pari_catch_sig_on() - return P.new_gen(denom(x.g)) + return P.bernreal(x, precision) - def floor(gen x): + def besselk(gen nu, x, flag=None, unsigned long precision=0): """ - For real x: return the largest integer = x. For rational functions: - the quotient of numerator by denominator. For lists: apply - componentwise. + nu.besselk(x): K-Bessel function (modified Bessel function + of the second kind) of index nu, which can be complex, and argument + x. - INPUT: + If `nu` or `x` is an exact argument, it is first + converted to a real or complex number using the optional parameter + precision (in bits). If the arguments are inexact (e.g. real), the + smallest of their precisions is used in the computation, and the + parameter precision is ignored. + INPUT: - - ``x`` - gen + - ``nu`` - a complex number - OUTPUT: gen + - ``x`` - real number (positive or negative) EXAMPLES:: - sage: pari(5/9).floor() - 0 - sage: pari(11/9).floor() - 1 - sage: pari(1.17).floor() - 1 - sage: pari([1.5,2.3,4.99]).floor() - [1, 2, 4] - sage: pari([[1.1,2.2],[3.3,4.4]]).floor() - [[1, 2], [3, 4]] - sage: pari(x).floor() - x - sage: pari((x^2+x+1)/x).floor() - x + 1 - sage: pari(x^2+5*x+2.5).floor() - x^2 + 5*x + 2.50000000000000 + sage: C. = ComplexField() + sage: pari(2+i).besselk(3) + 0.0455907718407551 + 0.0289192946582081*I :: - sage: pari('"hello world"').floor() - Traceback (most recent call last): - ... - PariError: incorrect type in gfloor (t_STR) - """ - pari_catch_sig_on() - return P.new_gen(gfloor(x.g)) - - def frac(gen x): - """ - frac(x): Return the fractional part of x, which is x - floor(x). - - INPUT: - - - - ``x`` - gen - - - OUTPUT: gen + sage: pari(2+i).besselk(-3) + -4.34870874986752 - 5.38744882697109*I - EXAMPLES:: + :: - sage: pari(1.75).frac() - 0.750000000000000 - sage: pari(sqrt(2)).frac() - 0.414213562373095 - sage: pari('sqrt(-2)').frac() - Traceback (most recent call last): - ... - PariError: incorrect type in gfloor (t_COMPLEX) + sage: pari(2+i).besselk(300) + 3.74224603319728 E-132 + 2.49071062641525 E-134*I + sage: pari(2+i).besselk(300, flag=1) + doctest:...: DeprecationWarning: The flag argument to besselk() is deprecated and not used anymore + See http://trac.sagemath.org/20219 for details. + 3.74224603319728 E-132 + 2.49071062641525 E-134*I """ - pari_catch_sig_on() - return P.new_gen(gfrac(x.g)) + if flag is not None: + deprecation(20219, 'The flag argument to besselk() is deprecated and not used anymore') + cdef gen t0 = objtogen(x) + sig_on() + return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) - def imag(gen x): - """ - imag(x): Return the imaginary part of x. This function also works - component-wise. + def eint1(gen x, long n=0, unsigned long precision=0): + r""" + x.eint1(n): exponential integral E1(x): - INPUT: + .. math:: + \int_{x}^{\infty} \frac{e^{-t}}{t} dt - - ``x`` - gen + If n is present, output the vector [eint1(x), eint1(2\*x), ..., + eint1(n\*x)]. This is faster than repeatedly calling eint1(i\*x). - OUTPUT: gen + If `x` is an exact argument, it is first converted to a + real or complex number using the optional parameter precision (in + bits). If `x` is inexact (e.g. real), its own precision is + used in the computation, and the parameter precision is ignored. - EXAMPLES:: + REFERENCE: - sage: pari('1+2*I').imag() - 2 - sage: pari(sqrt(-2)).imag() - 1.41421356237310 - sage: pari('x+I').imag() - 1 - sage: pari('x+2*I').imag() - 2 - sage: pari('(1+I)*x^2+2*I').imag() - x^2 + 2 - sage: pari('[1,2,3] + [4*I,5,6]').imag() - [4, 0, 0] - """ - pari_catch_sig_on() - return P.new_gen(gimag(x.g)) + - See page 262, Prop 5.6.12, of Cohen's book "A Course in + Computational Algebraic Number Theory". - def lift(gen x, v=-1): + EXAMPLES: """ - lift(x,v): Returns the lift of an element of Z/nZ to Z or R[x]/(P) - to R[x] for a type R if v is omitted. If v is given, lift only - polymods with main variable v. If v does not occur in x, lift only - intmods. - - INPUT: - + sig_on() + if n <= 0: + return P.new_gen(eint1(x.g, prec_bits_to_words(precision))) + else: + return P.new_gen(veceint1(x.g, stoi(n), prec_bits_to_words(precision))) - - ``x`` - gen + log_gamma = gen_auto.lngamma - - ``v`` - (optional) variable + def polylog(gen x, long m, long flag=0, unsigned long precision=0): + """ + x.polylog(m,flag=0): m-th polylogarithm of x. flag is optional, and + can be 0: default, 1: D_m -modified m-th polylog of x, 2: + D_m-modified m-th polylog of x, 3: P_m-modified m-th polylog of + x. + If `x` is an exact argument, it is first converted to a + real or complex number using the optional parameter precision (in + bits). If `x` is inexact (e.g. real), its own precision is + used in the computation, and the parameter precision is ignored. - OUTPUT: gen + TODO: Add more explanation, copied from the PARI manual. EXAMPLES:: - sage: x = pari("x") - sage: a = x.Mod('x^3 + 17*x + 3') - sage: a - Mod(x, x^3 + 17*x + 3) - sage: b = a^4; b - Mod(-17*x^2 - 3*x, x^3 + 17*x + 3) - sage: b.lift() - -17*x^2 - 3*x - - ??? more examples + sage: pari(10).polylog(3) + 5.64181141475134 - 8.32820207698027*I + sage: pari(10).polylog(3,0) + 5.64181141475134 - 8.32820207698027*I + sage: pari(10).polylog(3,1) + 0.523778453502411 + sage: pari(10).polylog(3,2) + -0.400459056163451 """ - pari_catch_sig_on() - if v == -1: - return P.new_gen(lift(x.g)) - return P.new_gen(lift0(x.g, P.get_var(v))) + sig_on() + return P.new_gen(polylog0(m, x.g, flag, prec_bits_to_words(precision))) - def numbpart(gen x): - """ - numbpart(x): returns the number of partitions of x. + def sqrtn(gen x, n, unsigned long precision=0): + r""" + x.sqrtn(n): return the principal branch of the n-th root of x, + i.e., the one such that + `\arg(\sqrt(x)) \in ]-\pi/n, \pi/n]`. Also returns a second + argument which is a suitable root of unity allowing one to recover + all the other roots. If it was not possible to find such a number, + then this second return value is 0. If the argument is present and + no square root exists, return 0 instead of raising an error. - EXAMPLES:: + If `x` is an exact argument, it is first converted to a + real or complex number using the optional parameter precision (in + bits). If `x` is inexact (e.g. real), its own precision is + used in the computation, and the parameter precision is ignored. - sage: pari(20).numbpart() - 627 - sage: pari(100).numbpart() - 190569292 - """ - pari_catch_sig_on() - return P.new_gen(numbpart(x.g)) + .. note:: - def padicprec(gen x, p): - """ - padicprec(x,p): Return the absolute p-adic precision of the object - x. + intmods (modulo a prime) and `p`-adic numbers are + allowed as arguments. INPUT: - ``x`` - gen + - ``n`` - integer - OUTPUT: int - EXAMPLES:: + OUTPUT: - sage: K = Qp(11,5) - sage: x = K(11^-10 + 5*11^-7 + 11^-6) - sage: y = pari(x) - sage: y.padicprec(11) - -5 - sage: y.padicprec(17) - Traceback (most recent call last): - ... - PariError: inconsistent moduli in padicprec: 11 != 17 - This works for polynomials too:: + - ``gen`` - principal n-th root of x - sage: R. = PolynomialRing(Zp(3)) - sage: pol = R([O(3^4), O(3^6), O(3^5)]) - sage: pari(pol).padicprec(3) - 4 - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - cdef long prec = padicprec(x.g, t0.g) - pari_catch_sig_off() - return prec - - def padicprime(gen x): - """ - The uniformizer of the p-adic ring this element lies in, as a t_INT. - - INPUT: - - - ``x`` - gen, of type t_PADIC - - OUTPUT: + - ``gen`` - root of unity z that gives the other + roots - - ``p`` - gen, of type t_INT EXAMPLES:: - sage: K = Qp(11,5) - sage: x = K(11^-10 + 5*11^-7 + 11^-6) - sage: y = pari(x) - sage: y.padicprime() - 11 - sage: y.padicprime().type() - 't_INT' - """ - pari_catch_sig_on() - return P.new_gen(gel(x.g, 2)) - - def precision(gen x, long n=-1): + sage: s, z = pari(2).sqrtn(5) + sage: z + 0.309016994374947 + 0.951056516295154*I + sage: s + 1.14869835499704 + sage: s^5 + 2.00000000000000 + sage: z^5 + 1.00000000000000 - 2.710505431 E-20*I # 32-bit + 1.00000000000000 - 2.71050543121376 E-20*I # 64-bit + sage: (s*z)^5 + 2.00000000000000 + 0.E-19*I """ - precision(x,n): Change the precision of x to be n, where n is a - C-integer). If n is omitted, output the real precision of x. + cdef GEN zetan + cdef gen t0 = objtogen(n) + sig_on() + ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) + return ans, P.new_gen(zetan) - INPUT: + phi = deprecated_function_alias(20219, gen_auto.eulerphi) + def ffprimroot(self): + r""" + Return a primitive root of the multiplicative group of the + definition field of the given finite field element. - - ``x`` - gen + INPUT: - - ``n`` - (optional) int + - ``self`` -- a PARI finite field element (``FFELT``) + OUTPUT: - OUTPUT: nothing or gen if n is omitted + - A generator of the multiplicative group of the finite field + generated by ``self``. - EXAMPLES: - """ - if n <= -1: - return precision(x.g) - pari_catch_sig_on() - return P.new_gen(precision0(x.g, n)) + EXAMPLES:: - def round(gen x, estimate=False): + sage: x = polygen(GF(3)) + sage: k. = GF(9, modulus=x^2+1) + sage: b = pari(a).ffprimroot() + sage: b # random + a + 1 + sage: b.fforder() + 8 """ - round(x,estimate=False): If x is a real number, returns x rounded - to the nearest integer (rounding up). If the optional argument - estimate is True, also returns the binary exponent e of the - difference between the original and the rounded value (the - "fractional part") (this is the integer ceiling of log_2(error)). - - When x is a general PARI object, this function returns the result - of rounding every coefficient at every level of PARI object. Note - that this is different than what the truncate function does (see - the example below). - - One use of round is to get exact results after a long approximate - computation, when theory tells you that the coefficients must be - integers. - - INPUT: + sig_on() + return P.new_gen(ffprimroot(self.g, NULL)) + def fibonacci(self): + r""" + Return the Fibonacci number of index x. - - ``x`` - gen + EXAMPLES:: - - ``estimate`` - (optional) bool, False by default + sage: pari(18).fibonacci() + 2584 + sage: [pari(n).fibonacci() for n in range(10)] + [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] + """ + return P.fibonacci(self) + def issquare(gen x, find_root=False): + """ + issquare(x,n): ``True`` if x is a square, ``False`` if not. If + ``find_root`` is given, also returns the exact square root. + """ + cdef GEN G + cdef long t + cdef gen g + sig_on() + if find_root: + t = itos(gissquareall(x.g, &G)) + if t: + return True, P.new_gen(G) + else: + P.clear_stack() + return False, None + else: + t = itos(gissquare(x.g)) + sig_off() + return t != 0 - OUTPUT: + def issquarefree(gen self): + """ + EXAMPLES:: - - if estimate is False, return a single gen. + sage: pari(10).issquarefree() + True + sage: pari(20).issquarefree() + False + """ + sig_on() + cdef long t = issquarefree(self.g) + sig_off() + return t != 0 - - if estimate is True, return rounded version of x and error - estimate in bits, both as gens. + def sumdiv(gen n): + """ + Return the sum of the divisors of `n`. EXAMPLES:: - sage: pari('1.5').round() - 2 - sage: pari('1.5').round(True) - (2, -1) - sage: pari('1.5 + 2.1*I').round() - 2 + 2*I - sage: pari('1.0001').round(True) - (1, -14) - sage: pari('(2.4*x^2 - 1.7)/x').round() - (2*x^2 - 2)/x - sage: pari('(2.4*x^2 - 1.7)/x').truncate() - 2.40000000000000*x + sage: pari(10).sumdiv() + 18 """ - cdef int n - cdef long e - cdef gen y - pari_catch_sig_on() - if not estimate: - return P.new_gen(ground(x.g)) - y = P.new_gen(grndtoi(x.g, &e)) - return y, e + sig_on() + return P.new_gen(sumdiv(n.g)) - def simplify(gen x): + def sumdivk(gen n, long k): """ - simplify(x): Simplify the object x as much as possible, and return - the result. - - A complex or quadratic number whose imaginary part is an exact 0 - (i.e., not an approximate one such as O(3) or 0.E-28) is converted - to its real part, and a a polynomial of degree 0 is converted to - its constant term. Simplification occurs recursively. - - This function is useful before using arithmetic functions, which - expect integer arguments: + Return the sum of the k-th powers of the divisors of n. EXAMPLES:: - sage: y = pari('y') - sage: x = pari('9') + y - y - sage: x - 9 - sage: x.type() - 't_POL' - sage: x.factor() - matrix(0,2) - sage: pari('9').factor() - Mat([3, 2]) - sage: x.simplify() - 9 - sage: x.simplify().factor() - Mat([3, 2]) - sage: x = pari('1.5 + 0*I') - sage: x.type() - 't_REAL' - sage: x.simplify() - 1.50000000000000 - sage: y = x.simplify() - sage: y.type() - 't_REAL' - """ - pari_catch_sig_on() - return P.new_gen(simplify(x.g)) + sage: pari(10).sumdivk(2) + 130 + """ + sig_on() + return P.new_gen(sumdivk(n.g, k)) - def sizeword(gen x): + def Zn_issquare(gen self, n): """ - Return the total number of machine words occupied by the - complete tree of the object x. A machine word is 32 or - 64 bits, depending on the computer. + Return ``True`` if ``self`` is a square modulo `n`, ``False`` + if not. INPUT: - - ``x`` - gen + - ``self`` -- integer - OUTPUT: int (a Python int) + - ``n`` -- integer or factorisation matrix EXAMPLES:: - sage: pari('0').sizeword() - 2 - sage: pari('1').sizeword() - 3 - sage: pari('1000000').sizeword() - 3 - sage: pari('10^100').sizeword() - 13 # 32-bit - 8 # 64-bit - sage: pari(RDF(1.0)).sizeword() - 4 # 32-bit - 3 # 64-bit - sage: pari('x').sizeword() - 9 - sage: pari('x^20').sizeword() - 66 - sage: pari('[x, I]').sizeword() - 20 + sage: pari(3).Zn_issquare(4) + False + sage: pari(4).Zn_issquare(30.factor()) + True + """ - return gsizeword(x.g) + cdef gen t0 = objtogen(n) + sig_on() + cdef long t = Zn_issquare(self.g, t0.g) + sig_off() + return t != 0 - def sizebyte(gen x): + def Zn_sqrt(gen self, n): """ - Return the total number of bytes occupied by the complete tree - of the object x. Note that this number depends on whether the - computer is 32-bit or 64-bit. + Return a square root of ``self`` modulo `n`, if such a square + root exists; otherwise, raise a ``ValueError``. INPUT: - - ``x`` - gen + - ``self`` -- integer - OUTPUT: int (a Python int) + - ``n`` -- integer or factorisation matrix - EXAMPLE:: + EXAMPLES:: - sage: pari('1').sizebyte() - 12 # 32-bit - 24 # 64-bit - """ - return gsizebyte(x.g) + sage: pari(3).Zn_sqrt(4) + Traceback (most recent call last): + ... + ValueError: 3 is not a square modulo 4 + sage: pari(4).Zn_sqrt(30.factor()) + 22 - def truncate(gen x, estimate=False): """ - truncate(x,estimate=False): Return the truncation of x. If estimate - is True, also return the number of error bits. - - When x is in the real numbers, this means that the part after the - decimal point is chopped away, e is the binary exponent of the - difference between the original and truncated value (the - "fractional part"). If x is a rational function, the result is the - integer part (Euclidean quotient of numerator by denominator) and - if requested the error estimate is 0. + cdef gen t0 = objtogen(n) + cdef GEN s + sig_on() + s = Zn_sqrt(self.g, t0.g) + if s == NULL: + sig_off() + raise ValueError("%s is not a square modulo %s" % (self, n)) + return P.new_gen(s) - When truncate is applied to a power series (in X), it transforms it - into a polynomial or a rational function with denominator a power - of X, by chopping away the `O(X^k)`. Similarly, when - applied to a p-adic number, it transforms it into an integer or a - rational number by chopping away the `O(p^k)`. + def ellinit(self, long flag=-1, unsigned long precision=0): + """ + Return the PARI elliptic curve object with Weierstrass coefficients + given by self, a list with 5 elements. INPUT: - - ``x`` - gen - - - ``estimate`` - (optional) bool, which is False by - default + - ``self`` -- a list of 5 coefficients + - ``flag`` -- ignored (for backwards compatibility) - OUTPUT: + - ``precision (optional, default: 0)`` - the real + precision to be used in the computation of the components of the + PARI (s)ell structure; if 0, use the default 64 bits. - - if estimate is False, return a single gen. + .. note:: - - if estimate is True, return rounded version of x and error - estimate in bits, both as gens. + The parameter ``precision`` in ``ellinit`` controls not + only the real precision of the resulting (s)ell structure, + but in some cases also the precision of most subsequent + computations with this elliptic curve (if those rely on + the precomputations done by ``ellinit``). You should + therefore set the precision from the start to the value + you require. - EXAMPLES:: + OUTPUT: - sage: pari('(x^2+1)/x').round() - (x^2 + 1)/x - sage: pari('(x^2+1)/x').truncate() - x - sage: pari('1.043').truncate() - 1 - sage: pari('1.043').truncate(True) - (1, -5) - sage: pari('1.6').truncate() - 1 - sage: pari('1.6').round() - 2 - sage: pari('1/3 + 2 + 3^2 + O(3^3)').truncate() - 34/3 - sage: pari('sin(x+O(x^10))').truncate() - 1/362880*x^9 - 1/5040*x^7 + 1/120*x^5 - 1/6*x^3 + x - sage: pari('sin(x+O(x^10))').round() # each coefficient has abs < 1 - x + O(x^10) - """ - cdef long e - cdef gen y - pari_catch_sig_on() - if not estimate: - return P.new_gen(gtrunc(x.g)) - y = P.new_gen(gcvtoi(x.g, &e)) - return y, e + - ``gen`` -- a PARI ell structure. - def valuation(gen x, p): - """ - valuation(x,p): Return the valuation of x with respect to p. + EXAMPLES: - The valuation is the highest exponent of p dividing x. + An elliptic curve with integer coefficients:: - - If p is an integer, x must be an integer, an intmod whose - modulus is divisible by p, a rational number, a p-adic - number, or a polynomial or power series in which case the - valuation is the minimum of the valuations of the - coefficients. + sage: e = pari([0,1,0,1,0]).ellinit(); e + [0, 1, 0, 1, 0, 4, 2, 0, -1, -32, 224, -48, 2048/3, Vecsmall([1]), [Vecsmall([64, -1])], [0, 0, 0, 0, 0, 0, 0, 0]] - - If p is a polynomial, x must be a polynomial or a rational - function. If p is a monomial then x may also be a power - series. + The coefficients can be any ring elements that convert to PARI:: - - If x is a vector, complex or quadratic number, then the - valuation is the minimum of the component valuations. + sage: pari([0,1/2,0,-3/4,0]).ellinit() + [0, 1/2, 0, -3/4, 0, 2, -3/2, 0, -9/16, 40, -116, 117/4, 256000/117, Vecsmall([1]), [Vecsmall([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]] + sage: pari([0,0.5,0,-0.75,0]).ellinit() + [0, 0.500000000000000, 0, -0.750000000000000, 0, 2.00000000000000, -1.50000000000000, 0, -0.562500000000000, 40.0000000000000, -116.000000000000, 29.2500000000000, 2188.03418803419, Vecsmall([0]), [Vecsmall([64, 1])], [0, 0, 0, 0]] + sage: pari([0,I,0,1,0]).ellinit() + [0, I, 0, 1, 0, 4*I, 2, 0, -1, -64, 352*I, -80, 16384/5, Vecsmall([0]), [Vecsmall([64, 0])], [0, 0, 0, 0]] + sage: pari([0,x,0,2*x,1]).ellinit() + [0, x, 0, 2*x, 1, 4*x, 4*x, 4, -4*x^2 + 4*x, 16*x^2 - 96*x, -64*x^3 + 576*x^2 - 864, 64*x^4 - 576*x^3 + 576*x^2 - 432, (256*x^6 - 4608*x^5 + 27648*x^4 - 55296*x^3)/(4*x^4 - 36*x^3 + 36*x^2 - 27), Vecsmall([0]), [Vecsmall([64, 0])], [0, 0, 0, 0]] + """ + if flag != -1: + from sage.misc.superseded import deprecation + deprecation(16997, 'The flag argument to ellinit() is deprecated and not used anymore') + sig_on() + return P.new_gen(ellinit(self.g, NULL, prec_bits_to_words(precision))) - - If x = 0, the result is `2^31-1` on 32-bit machines or - `2^63-1` on 64-bit machines if x is an exact - object. If x is a p-adic number or power series, the result - is the exponent of the zero. + def ellan(self, long n, python_ints=False): + """ + Return the first `n` Fourier coefficients of the modular + form attached to this elliptic curve. See ellak for more details. INPUT: - - ``x`` - gen + - ``n`` - a long integer - - ``p`` - coercible to gen + - ``python_ints`` - bool (default is False); if True, + return a list of Python ints instead of a PARI gen wrapper. - OUTPUT: + EXAMPLES:: + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.ellan(3) + [1, -2, -1] + sage: e.ellan(20) + [1, -2, -1, 2, 1, 2, -2, 0, -2, -2, 1, -2, 4, 4, -1, -4, -2, 4, 0, 2] + sage: e.ellan(-1) + [] + sage: v = e.ellan(10, python_ints=True); v + [1, -2, -1, 2, 1, 2, -2, 0, -2, -2] + sage: type(v) + + sage: type(v[0]) + + """ + sig_on() + cdef GEN g = anell(self.g, n) + if python_ints: + v = [gtolong(gel(g, i+1)) for i in range(glength(g))] + P.clear_stack() + return v + else: + return P.new_gen(g) - - ``gen`` - integer + def ellaplist(self, long n, python_ints=False): + r""" + e.ellaplist(n): Returns a PARI list of all the prime-indexed + coefficients `a_p` (up to n) of the `L`-function + of the elliptic curve `e`, i.e. the Fourier coefficients of + the newform attached to `e`. + INPUT: - EXAMPLES:: + - ``self`` -- an elliptic curve - sage: pari(9).valuation(3) - 2 - sage: pari(9).valuation(9) - 1 - sage: x = pari(9).Mod(27); x.valuation(3) - 2 - sage: pari('5/3').valuation(3) - -1 - sage: pari('9 + 3*x + 15*x^2').valuation(3) - 1 - sage: pari([9,3,15]).valuation(3) - 1 - sage: pari('9 + 3*x + 15*x^2 + O(x^5)').valuation(3) - 1 + - ``n`` -- a long integer - :: + - ``python_ints`` -- bool (default is False); if True, + return a list of Python ints instead of a PARI gen wrapper. - sage: pari('x^2*(x+1)^3').valuation(pari('x+1')) - 3 - sage: pari('x + O(x^5)').valuation('x') - 1 - sage: pari('2*x^2 + O(x^5)').valuation('x') - 2 + .. WARNING:: - :: + The curve e must be a medium or long vector of the type given by + ellinit. For this function to work for every n and not just those + prime to the conductor, e must be a minimal Weierstrass equation. + If this is not the case, use the function ellminimalmodel first + before using ellaplist (or you will get INCORRECT RESULTS!) - sage: pari(0).valuation(3) - 2147483647 # 32-bit - 9223372036854775807 # 64-bit - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - v = gvaluation(x.g, t0.g) - pari_catch_sig_off() - return v + EXAMPLES:: - def _valp(gen x): - """ - Return the valuation of x where x is a p-adic number (t_PADIC) - or a Laurent series (t_SER). If x is a different type, this - will give a bogus number. + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: v = e.ellaplist(10); v + [-2, -1, 1, -2] + sage: type(v) + + sage: v.type() + 't_VEC' + sage: e.ellan(10) + [1, -2, -1, 2, 1, 2, -2, 0, -2, -2] + sage: v = e.ellaplist(10, python_ints=True); v + [-2, -1, 1, -2] + sage: type(v) + + sage: type(v[0]) + - EXAMPLES:: + TESTS:: - sage: pari('1/x^2 + O(x^10)')._valp() - -2 - sage: pari('O(x^10)')._valp() - 10 - sage: pari('(1145234796 + O(3^10))/771966234')._valp() - -2 - sage: pari('O(2^10)')._valp() - 10 - sage: pari('x')._valp() # random - -35184372088832 + sage: v = e.ellaplist(1) + sage: print v, type(v) + [] + sage: v = e.ellaplist(1, python_ints=True) + sage: print v, type(v) + [] """ - # This is a simple macro, so we don't need pari_catch_sig_on() - return valp(x.g) + if python_ints: + return [int(x) for x in self.ellaplist(n)] - def variable(gen x): - """ - variable(x): Return the main variable of the object x, or p if x is - a p-adic number. + if n < 2: + sig_on() + return P.new_gen(zerovec(0)) + + # 1. Make a table of primes up to n. + P.init_primes(n+1) + cdef gen t0 = objtogen(n) + sig_on() + cdef GEN g = primes(gtolong(primepi(t0.g))) + + # 2. Replace each prime in the table by ellap of it. + cdef long i + for i from 0 <= i < glength(g): + set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) + return P.new_gen(g) - This function raises a TypeError exception on scalars, i.e., on - objects with no variable associated to them. + def ellheight(self, a, b=None, long flag=-1, unsigned long precision=0): + """ + Canonical height of point ``a`` on elliptic curve ``self``, + resp. the value of the associated bilinear form at ``(a,b)``. INPUT: + - ``self``-- an elliptic curve over `\QQ`. - - ``x`` - gen + - ``a`` -- rational point on ``self``. + - ``b`` -- (optional) rational point on ``self``. - OUTPUT: gen + - ``precision (optional)`` -- the precision of the + result, in bits. EXAMPLES:: - sage: pari('x^2 + x -2').variable() - x - sage: pari('1+2^3 + O(2^5)').variable() - 2 - sage: pari('x+y0').variable() - x - sage: pari('y0+z0').variable() - y0 - """ - pari_catch_sig_on() - return P.new_gen(gpolvar(x.g)) - + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: e.ellheight([1,0]) + 0.476711659343740 + sage: e.ellheight([1,0], precision=128).sage() + 0.47671165934373953737948605888465305945902294218 # 32-bit + 0.476711659343739537379486058884653059459022942211150879336 # 64-bit - ########################################### - # 3: TRANSCENDENTAL functions - # AUTHORS: Pyrex Code, docs -- Justin Walker (justin@mac.com) - # Examples, docs -- William Stein - ########################################### + Computing the bilinear form:: - def abs(gen x, unsigned long precision=0): + sage: e.ellheight([1, 0], [-1, 1]) + 0.418188984498861 """ - Returns the absolute value of x (its modulus, if x is complex). - Rational functions are not allowed. Contrary to most transcendental - functions, an exact argument is not converted to a real number - before applying abs and an exact result is returned if possible. - - EXAMPLES:: + if flag != -1: + from sage.misc.superseded import deprecation + deprecation(16997, 'The flag argument to ellheight() is deprecated and not used anymore') + cdef gen t0 = objtogen(a) + cdef gen t1 + if b is None: + sig_on() + return P.new_gen(ellheight(self.g, t0.g, prec_bits_to_words(precision))) + else: + t1 = objtogen(b) + sig_on() + return P.new_gen(ellheight0(self.g, t0.g, t1.g, prec_bits_to_words(precision))) - sage: x = pari("-27.1") - sage: x.abs() - 27.1000000000000 - sage: pari('1 + I').abs(precision=128).sage() - 1.4142135623730950488016887242096980786 + def ellisoncurve(self, x): + """ + e.ellisoncurve(x): return True if the point x is on the elliptic + curve e, False otherwise. - If x is a polynomial, returns -x if the leading coefficient is real - and negative else returns x. For a power series, the constant - coefficient is considered instead. + If the point or the curve have inexact coefficients, an attempt is + made to take this into account. EXAMPLES:: - sage: pari('x-1.2*x^2').abs() - 1.20000000000000*x^2 - x - sage: pari('-2 + t + O(t^2)').abs() - 2 - t + O(t^2) + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: e.ellisoncurve([1,0]) + True + sage: e.ellisoncurve([1,1]) + False + sage: e.ellisoncurve([1,0.00000000000000001]) + False + sage: e.ellisoncurve([1,0.000000000000000001]) + True + sage: e.ellisoncurve([0]) + True """ - pari_catch_sig_on() - return P.new_gen(gabs(x.g, prec_bits_to_words(precision))) + cdef gen t0 = objtogen(x) + sig_on() + cdef int t = oncurve(self.g, t0.g) + sig_off() + return t != 0 - def acos(gen x, unsigned long precision=0): - r""" - The principal branch of `\cos^{-1}(x)`, so that - `\RR e(\mathrm{acos}(x))` belongs to `[0,Pi]`. If `x` - is real and `|x| > 1`, then `\mathrm{acos}(x)` is complex. + def ellminimalmodel(self): + """ + ellminimalmodel(e): return the standard minimal integral model of + the rational elliptic curve e and the corresponding change of + variables. INPUT: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - EXAMPLES:: + - ``e`` - gen (that defines an elliptic curve) - sage: pari(0.5).acos() - 1.04719755119660 - sage: pari(1/2).acos() - 1.04719755119660 - sage: pari(1.1).acos() - 0.443568254385115*I - sage: C. = ComplexField() - sage: pari(1.1+i).acos() - 0.849343054245252 - 1.09770986682533*I - """ - pari_catch_sig_on() - return P.new_gen(gacos(x.g, prec_bits_to_words(precision))) - def acosh(gen x, unsigned long precision=0): - r""" - The principal branch of `\cosh^{-1}(x)`, so that - `\Im(\mathrm{acosh}(x))` belongs to `[0,Pi]`. If - `x` is real and `x < 1`, then - `\mathrm{acosh}(x)` is complex. + OUTPUT: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - EXAMPLES:: + - ``gen`` - minimal model - sage: pari(2).acosh() - 1.31695789692482 - sage: pari(0).acosh() - 1.57079632679490*I - sage: C. = ComplexField() - sage: pari(i).acosh() - 0.881373587019543 + 1.57079632679490*I - """ - pari_catch_sig_on() - return P.new_gen(gacosh(x.g, prec_bits_to_words(precision))) + - ``gen`` - change of coordinates - def agm(gen x, y, unsigned long precision=0): - r""" - The arithmetic-geometric mean of x and y. In the case of complex or - negative numbers, the principal square root is always chosen. - p-adic or power series arguments are also allowed. Note that a - p-adic AGM exists only if x/y is congruent to 1 modulo p (modulo 16 - for p=2). x and y cannot both be vectors or matrices. - - If any of `x` or `y` is an exact argument, it is - first converted to a real or complex number using the optional - parameter precision (in bits). If the arguments are inexact (e.g. - real), the smallest of their two precisions is used in the - computation, and the parameter precision is ignored. EXAMPLES:: - sage: pari(2).agm(2) - 2.00000000000000 - sage: pari(0).agm(1) - 0 - sage: pari(1).agm(2) - 1.45679103104691 - sage: C. = ComplexField() - sage: pari(1+i).agm(-3) - -0.964731722290876 + 1.15700282952632*I + sage: e = pari([1,2,3,4,5]).ellinit() + sage: F, ch = e.ellminimalmodel() + sage: F[:5] + [1, -1, 0, 4, 3] + sage: ch + [1, -1, 0, -1] + sage: e.ellchangecurve(ch)[:5] + [1, -1, 0, 4, 3] """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(agm(x.g, t0.g, prec_bits_to_words(precision))) - - def arg(gen x, unsigned long precision=0): - r""" - arg(x): argument of x,such that `-\pi < \arg(x) \leq \pi`. + cdef GEN x, y + cdef gen model, change + cdef pari_sp t + sig_on() + x = ellminimalmodel(self.g, &y) + change = P.new_gen_noclear(y) + model = P.new_gen(x) + return model, change - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + def elltors(self, flag=None): + """ + Return information about the torsion subgroup of the given + elliptic curve. - EXAMPLES:: + INPUT: - sage: C. = ComplexField() - sage: pari(2+i).arg() - 0.463647609000806 - """ - pari_catch_sig_on() - return P.new_gen(garg(x.g, prec_bits_to_words(precision))) + - ``e`` - elliptic curve over `\QQ` - def asin(gen x, unsigned long precision=0): - r""" - The principal branch of `\sin^{-1}(x)`, so that - `\RR e(\mathrm{asin}(x))` belongs to `[-\pi/2,\pi/2]`. If - `x` is real and `|x| > 1` then `\mathrm{asin}(x)` - is complex. + OUTPUT: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - EXAMPLES:: + - ``gen`` - the order of the torsion subgroup, a.k.a. + the number of points of finite order - sage: pari(pari(0.5).sin()).asin() - 0.500000000000000 - sage: pari(2).asin() - 1.57079632679490 - 1.31695789692482*I - """ - pari_catch_sig_on() - return P.new_gen(gasin(x.g, prec_bits_to_words(precision))) + - ``gen`` - vector giving the structure of the torsion + subgroup as a product of cyclic groups, sorted in non-increasing + order - def asinh(gen x, unsigned long precision=0): - r""" - The principal branch of `\sinh^{-1}(x)`, so that - `\Im(\mathrm{asinh}(x))` belongs to `[-\pi/2,\pi/2]`. + - ``gen`` - vector giving points on e generating these + cyclic groups - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. EXAMPLES:: - sage: pari(2).asinh() - 1.44363547517881 - sage: C. = ComplexField() - sage: pari(2+i).asinh() - 1.52857091948100 + 0.427078586392476*I + sage: e = pari([1,0,1,-19,26]).ellinit() + sage: e.elltors() + [12, [6, 2], [[1, 2], [3, -2]]] """ - pari_catch_sig_on() - return P.new_gen(gasinh(x.g, prec_bits_to_words(precision))) - - def atan(gen x, unsigned long precision=0): - r""" - The principal branch of `\tan^{-1}(x)`, so that - `\RR e(\mathrm{atan}(x))` belongs to `]-\pi/2, \pi/2[`. + if flag is not None: + deprecation(20219, 'The flag argument to elltors() is deprecated and not used anymore') + sig_on() + return P.new_gen(elltors(self.g)) - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + def omega(self, unsigned long precision=0): + """ + Return the basis for the period lattice of this elliptic curve. EXAMPLES:: - sage: pari(1).atan() - 0.785398163397448 - sage: C. = ComplexField() - sage: pari(1.5+i).atan() - 1.10714871779409 + 0.255412811882995*I + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.omega() + [1.26920930427955, 0.634604652139777 - 1.45881661693850*I] """ - pari_catch_sig_on() - return P.new_gen(gatan(x.g, prec_bits_to_words(precision))) - - def atanh(gen x, unsigned long precision=0): - r""" - The principal branch of `\tanh^{-1}(x)`, so that - `\Im(\mathrm{atanh}(x))` belongs to `]-\pi/2,\pi/2]`. If - `x` is real and `|x| > 1` then `\mathrm{atanh}(x)` - is complex. + sig_on() + return P.new_gen(ellR_omega(self.g, prec_bits_to_words(precision))) - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + def disc(self): + """ + Return the discriminant of this object. EXAMPLES:: - sage: pari(0).atanh() - 0.E-19 - sage: pari(2).atanh() - 0.549306144334055 - 1.57079632679490*I + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.disc() + -161051 + sage: _.factor() + [-1, 1; 11, 5] """ - pari_catch_sig_on() - return P.new_gen(gatanh(x.g, prec_bits_to_words(precision))) + sig_on() + return P.new_gen(member_disc(self.g)) - def bernfrac(gen x): - r""" - The Bernoulli number `B_x`, where `B_0 = 1`, - `B_1 = -1/2`, `B_2 = 1/6,\ldots,` expressed as a - rational number. The argument `x` should be of type - integer. + def j(self): + """ + Return the j-invariant of this object. EXAMPLES:: - sage: pari(18).bernfrac() - 43867/798 - sage: [pari(n).bernfrac() for n in range(10)] - [1, -1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0] + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.j() + -122023936/161051 + sage: _.factor() + [-1, 1; 2, 12; 11, -5; 31, 3] """ - pari_catch_sig_on() - return P.new_gen(bernfrac(x)) + sig_on() + return P.new_gen(member_j(self.g)) - def bernreal(gen x, unsigned long precision=0): - r""" - The Bernoulli number `B_x`, as for the function bernfrac, - but `B_x` is returned as a real number (with the current - precision). + def _eltabstorel(self, x): + """ + Return the relative number field element corresponding to `x`. - EXAMPLES:: + The result is a ``t_POLMOD`` with ``t_POLMOD`` coefficients. - sage: pari(18).bernreal() - 54.9711779448622 - sage: pari(18).bernreal(precision=192).sage() - 54.9711779448621553884711779448621553884711779448621553885 - """ - pari_catch_sig_on() - return P.new_gen(bernreal(x, prec_bits_to_words(precision))) + .. WARNING:: - def besselh1(gen nu, x, unsigned long precision=0): - r""" - The `H^1`-Bessel function of index `\nu` and - argument `x`. + This is a low-level version of :meth:`rnfeltabstorel` that + only needs the output of :meth:`_nf_rnfeq`, not a full + PARI ``rnf`` structure. This method may raise errors or + return undefined results if called with invalid arguments. - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + TESTS:: - EXAMPLES:: + sage: K = pari('y^2 + 1').nfinit() + sage: rnfeq = K._nf_rnfeq(x^2 + 2) + sage: f_abs = rnfeq[0]; f_abs + x^4 + 6*x^2 + 1 + sage: x_rel = rnfeq._eltabstorel(x); x_rel + Mod(x + Mod(-y, y^2 + 1), x^2 + 2) + sage: f_abs(x_rel) + Mod(0, x^2 + 2) - sage: pari(2).besselh1(3) - 0.486091260585891 - 0.160400393484924*I """ cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(hbessel1(nu.g, t0.g, prec_bits_to_words(precision))) - - def besselh2(gen nu, x, unsigned long precision=0): - r""" - The `H^2`-Bessel function of index `\nu` and - argument `x`. + sig_on() + return P.new_gen(eltabstorel(self.g, t0.g)) - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + def _eltabstorel_lift(self, x): + """ + Return the relative number field element corresponding to `x`. - EXAMPLES:: + The result is a ``t_POL`` with ``t_POLMOD`` coefficients. - sage: pari(2).besselh2(3) - 0.486091260585891 + 0.160400393484924*I - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(hbessel2(nu.g, t0.g, prec_bits_to_words(precision))) + .. WARNING:: - def besselj(gen nu, x, unsigned long precision=0): - r""" - Bessel J function (Bessel function of the first kind), with index - `\nu` and argument `x`. If `x` converts to - a power series, the initial factor - `(x/2)^{\nu}/\Gamma(\nu+1)` is omitted (since it cannot be - represented in PARI when `\nu` is not integral). + This is a low-level version of :meth:`rnfeltabstorel` that + only needs the output of :meth:`_nf_rnfeq`, not a full + PARI ``rnf`` structure. This method may raise errors or + return undefined results if called with invalid arguments. - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + TESTS:: - EXAMPLES:: + sage: K = pari('y^2 + 1').nfinit() + sage: rnfeq = K._nf_rnfeq(x^2 + 2) + sage: rnfeq._eltabstorel_lift(x) + x + Mod(-y, y^2 + 1) - sage: pari(2).besselj(3) - 0.486091260585891 """ cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(jbessel(nu.g, t0.g, prec_bits_to_words(precision))) + sig_on() + return P.new_gen(eltabstorel_lift(self.g, t0.g)) - def besseljh(gen nu, x, unsigned long precision=0): + def _eltreltoabs(self, x): """ - J-Bessel function of half integral index (Spherical Bessel - function of the first kind). More precisely, besseljh(n,x) computes - `J_{n+1/2}(x)` where n must an integer, and x is any - complex value. In the current implementation (PARI, version - 2.2.11), this function is not very accurate when `x` is - small. - - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + Return the absolute number field element corresponding to `x`. - EXAMPLES:: + The result is a ``t_POL``. - sage: pari(2).besseljh(3) - 0.412710032209716 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(jbesselh(nu.g, t0.g, prec_bits_to_words(precision))) + .. WARNING:: - def besseli(gen nu, x, unsigned long precision=0): - r""" - Bessel I function (Bessel function of the second kind), with index - `\nu` and argument `x`. If `x` converts to - a power series, the initial factor - `(x/2)^{\nu}/\Gamma(\nu+1)` is omitted (since it cannot be - represented in PARI when `\nu` is not integral). + This is a low-level version of :meth:`rnfeltreltoabs` that + only needs the output of :meth:`_nf_rnfeq`, not a full + PARI ``rnf`` structure. This method may raise errors or + return undefined results if called with invalid arguments. - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + TESTS:: - EXAMPLES:: + sage: K = pari('y^2 + 1').nfinit() + sage: rnfeq = K._nf_rnfeq(x^2 + 2) + sage: rnfeq._eltreltoabs(x) + 1/2*x^3 + 7/2*x + sage: rnfeq._eltreltoabs('y') + 1/2*x^3 + 5/2*x - sage: pari(2).besseli(3) - 2.24521244092995 - sage: C. = ComplexField() - sage: pari(2).besseli(3+i) - 1.12539407613913 + 2.08313822670661*I """ cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(ibessel(nu.g, t0.g, prec_bits_to_words(precision))) + sig_on() + return P.new_gen(eltreltoabs(self.g, t0.g)) - def besselk(gen nu, x, long flag=0, unsigned long precision=0): + def galoissubfields(self, long flag=0, v=-1): """ - nu.besselk(x, flag=0): K-Bessel function (modified Bessel function - of the second kind) of index nu, which can be complex, and argument - x. + List all subfields of the Galois group ``self``. - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. + This wraps the `galoissubfields`_ function from PARI. + + This method is essentially the same as applying + :meth:`galoisfixedfield` to each group returned by + :meth:`galoissubgroups`. INPUT: + - ``self`` -- A Galois group as generated by :meth:`galoisinit`. - - ``nu`` - a complex number + - ``flag`` -- Has the same meaning as in :meth:`galoisfixedfield`. - - ``x`` - real number (positive or negative) + - ``v`` -- Has the same meaning as in :meth:`galoisfixedfield`. - - ``flag`` - default: 0 or 1: use hyperu (hyperu is - much slower for small x, and doesn't work for negative x). + OUTPUT: + A vector of all subfields of this group. Each entry is as + described in the :meth:`galoisfixedfield` method. EXAMPLES:: - sage: C. = ComplexField() - sage: pari(2+i).besselk(3) - 0.0455907718407551 + 0.0289192946582081*I + sage: G = pari(x^6 + 108).galoisinit() + 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')[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 + """ + sig_on() + return P.new_gen(galoissubfields(self.g, flag, P.get_var(v))) - sage: pari(2+i).besselk(-3) - -4.34870874986752 - 5.38744882697109*I + idealintersection = deprecated_function_alias(20219, gen_auto.idealintersect) - :: + def nfeltval(self, x, p): + """ + Return the valuation of the number field element `x` at the prime `p`. - sage: pari(2+i).besselk(300, flag=1) - 3.74224603319728 E-132 + 2.49071062641525 E-134*I + EXAMPLES:: + + sage: nf = pari('x^2 + 1').nfinit() + sage: p = nf.idealprimedec(5)[0] + sage: nf.nfeltval('50 - 25*x', p) + 3 """ cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(kbessel(nu.g, t0.g, prec_bits_to_words(precision))) + cdef gen t1 = objtogen(p) + sig_on() + v = nfval(self.g, t0.g, t1.g) + sig_off() + return v - def besseln(gen nu, x, unsigned long precision=0): - """ - nu.besseln(x): Bessel N function (Spherical Bessel function of the - second kind) of index nu and argument x. + elementval = deprecated_function_alias(20219, nfeltval) - If `nu` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. - - EXAMPLES:: - - sage: C. = ComplexField() - sage: pari(2+i).besseln(3) - -0.280775566958244 - 0.486708533223726*I - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(nbessel(nu.g, t0.g, prec_bits_to_words(precision))) - - def cos(gen x, unsigned long precision=0): + def nfbasis(self, long flag=0, fa=None): """ - The cosine function. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + Integral basis of the field `\QQ[a]`, where ``a`` is a root of + the polynomial x. - EXAMPLES:: + INPUT: - sage: pari(1.5).cos() - 0.0707372016677029 - sage: C. = ComplexField() - sage: pari(1+i).cos() - 0.833730025131149 - 0.988897705762865*I - sage: pari('x+O(x^8)').cos() - 1 - 1/2*x^2 + 1/24*x^4 - 1/720*x^6 + 1/40320*x^8 + O(x^9) - """ - pari_catch_sig_on() - return P.new_gen(gcos(x.g, prec_bits_to_words(precision))) + - ``flag``: if set to 1 and ``fa`` is not given: assume that no + square of a prime > 500000 divides the discriminant of ``x``. - def cosh(gen x, unsigned long precision=0): - """ - The hyperbolic cosine function. + - ``fa``: If present, encodes a subset of primes at which to + check for maximality. This must be one of the three following + things: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + - an integer: check all primes up to ``fa`` using trial + division. - EXAMPLES:: + - a vector: a list of primes to check. - sage: pari(1.5).cosh() - 2.35240961524325 - sage: C. = ComplexField() - sage: pari(1+i).cosh() - 0.833730025131149 + 0.988897705762865*I - sage: pari('x+O(x^8)').cosh() - 1 + 1/2*x^2 + 1/24*x^4 + 1/720*x^6 + O(x^8) - """ - pari_catch_sig_on() - return P.new_gen(gcosh(x.g, prec_bits_to_words(precision))) + - a matrix: a partial factorization of the discriminant + of ``x``. - def cotan(gen x, unsigned long precision=0): - """ - The cotangent of x. + .. NOTE:: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + In earlier versions of Sage, other bits in ``flag`` were + defined but these are now simply ignored. EXAMPLES:: - sage: pari(5).cotan() - -0.295812915532746 - - Computing the cotangent of `\pi` doesn't raise an error, - but instead just returns a very large (positive or negative) - number. + sage: pari('x^3 - 17').nfbasis() + [1, x, 1/3*x^2 - 1/3*x + 1/3] - :: + We test ``flag`` = 1, noting it gives a wrong result when the + discriminant (-4 * `p`^2 * `q` in the example below) has a big square + factor:: - sage: x = RR(pi) - sage: pari(x).cotan() # random - -8.17674825 E15 + sage: p = next_prime(10^10); q = next_prime(p) + sage: x = polygen(QQ); f = x^2 + p^2*q + sage: pari(f).nfbasis(1) # Wrong result + [1, x] + sage: pari(f).nfbasis() # Correct result + [1, 1/10000000019*x] + sage: pari(f).nfbasis(fa=10^6) # Check primes up to 10^6: wrong result + [1, x] + sage: pari(f).nfbasis(fa="[2,2; %s,2]"%p) # Correct result and faster + [1, 1/10000000019*x] + sage: pari(f).nfbasis(fa=[2,p]) # Equivalent with the above + [1, 1/10000000019*x] """ - pari_catch_sig_on() - return P.new_gen(gcotan(x.g, prec_bits_to_words(precision))) - - def dilog(gen x, unsigned long precision=0): - r""" - The principal branch of the dilogarithm of `x`, i.e. the - analytic continuation of the power series - `\log_2(x) = \sum_{n>=1} x^n/n^2`. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + if flag < 0 or flag > 1: + flag = flag & 1 + from sage.misc.superseded import deprecation + deprecation(15767, 'In nfbasis(), flag must be 0 or 1, other bits are deprecated and ignored') - EXAMPLES:: + cdef gen t0 + cdef GEN g0 + if fa is not None: + t0 = objtogen(fa) + g0 = t0.g + elif flag: + g0 = utoi(500000) + else: + g0 = NULL + sig_on() + return P.new_gen(nfbasis(self.g, NULL, g0)) - sage: pari(1).dilog() - 1.64493406684823 - sage: C. = ComplexField() - sage: pari(1+i).dilog() - 0.616850275068085 + 1.46036211675312*I + def nfbasis_d(self, long flag=0, fa=None): """ - pari_catch_sig_on() - return P.new_gen(dilog(x.g, prec_bits_to_words(precision))) - - def eint1(gen x, long n=0, unsigned long precision=0): - r""" - x.eint1(n): exponential integral E1(x): - - .. math:: - - \int_{x}^{\infty} \frac{e^{-t}}{t} dt + Like :meth:`nfbasis`, but return a tuple ``(B, D)`` where `B` + is the integral basis and `D` the discriminant. + EXAMPLES:: - If n is present, output the vector [eint1(x), eint1(2\*x), ..., - eint1(n\*x)]. This is faster than repeatedly calling eint1(i\*x). + sage: F = NumberField(x^3-2,'alpha') + sage: F._pari_()[0].nfbasis_d() + ([1, y, y^2], -108) - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + :: - REFERENCE: + sage: G = NumberField(x^5-11,'beta') + sage: G._pari_()[0].nfbasis_d() + ([1, y, y^2, y^3, y^4], 45753125) - - See page 262, Prop 5.6.12, of Cohen's book "A Course in - Computational Algebraic Number Theory". + :: - EXAMPLES: + sage: pari([-2,0,0,1]).Polrev().nfbasis_d() + ([1, x, x^2], -108) """ - pari_catch_sig_on() - if n <= 0: - return P.new_gen(eint1(x.g, prec_bits_to_words(precision))) + if flag < 0 or flag > 1: + flag = flag & 1 + from sage.misc.superseded import deprecation + deprecation(15767, 'In nfbasis_d(), flag must be 0 or 1, other bits are deprecated and ignored') + + cdef gen t0 + cdef GEN g0 + cdef GEN disc + if fa is not None: + t0 = objtogen(fa) + g0 = t0.g + elif flag & 1: + g0 = utoi(500000) else: - return P.new_gen(veceint1(x.g, stoi(n), prec_bits_to_words(precision))) + g0 = NULL + sig_on() + B = P.new_gen_noclear(nfbasis(self.g, &disc, g0)) + D = P.new_gen(disc) + return B, D - def erfc(gen x, unsigned long precision=0): + def nfbasistoalg_lift(nf, x): r""" - Return the complementary error function: - - .. math:: + Transforms the column vector ``x`` on the integral basis into a + polynomial representing the algebraic number. - (2/\sqrt{\pi}) \int_{x}^{\infty} e^{-t^2} dt. + INPUT: + - ``nf`` -- a number field + - ``x`` -- a column of rational numbers of length equal to the + degree of ``nf`` or a single rational number + OUTPUT: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + - ``nf.nfbasistoalg(x).lift()`` EXAMPLES:: - sage: pari(1).erfc() - 0.157299207050285 + sage: x = polygen(QQ) + sage: K. = NumberField(x^3 - 17) + sage: Kpari = K.pari_nf() + sage: Kpari.getattr('zk') + [1, 1/3*y^2 - 1/3*y + 1/3, y] + sage: Kpari.nfbasistoalg_lift(42) + 42 + sage: Kpari.nfbasistoalg_lift("[3/2, -5, 0]~") + -5/3*y^2 + 5/3*y - 1/6 + sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") + -5/3*y^2 + 5/3*y - 1/6 """ - pari_catch_sig_on() - return P.new_gen(gerfc(x.g, prec_bits_to_words(precision))) - - def eta(gen x, long flag=0, unsigned long precision=0): - r""" - x.eta(flag=0): if flag=0, `\eta` function without the - `q^{1/24}`; otherwise `\eta` of the complex number - `x` in the upper half plane intelligently computed using - `\mathrm{SL}(2,\ZZ)` transformations. - - DETAILS: This functions computes the following. If the input - `x` is a complex number with positive imaginary part, the - result is `\prod_{n=1}^{\infty} (q-1^n)`, where - `q=e^{2 i \pi x}`. If `x` is a power series - (or can be converted to a power series) with positive valuation, - the result is `\prod_{n=1}^{\infty} (1-x^n)`. + cdef gen t0 = objtogen(x) + sig_on() + return P.new_gen(gel(basistoalg(nf.g, t0.g), 2)) - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + def nfdisc(self, long flag=-1, p=None): + """ + nfdisc(x): Return the discriminant of the number field defined over + QQ by x. EXAMPLES:: - sage: C. = ComplexField() - sage: pari(i).eta() - 0.998129069925959 - """ - pari_catch_sig_on() - if flag == 1: - return P.new_gen(trueeta(x.g, prec_bits_to_words(precision))) - return P.new_gen(eta(x.g, prec_bits_to_words(precision))) + sage: F = NumberField(x^3-2,'alpha') + sage: F._pari_()[0].nfdisc() + -108 - def exp(gen self, unsigned long precision=0): - """ - x.exp(): exponential of x. + :: - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + sage: G = NumberField(x^5-11,'beta') + sage: G._pari_()[0].nfdisc() + 45753125 - EXAMPLES:: + :: - sage: pari(0).exp() - 1.00000000000000 - sage: pari(1).exp() - 2.71828182845905 - sage: pari('x+O(x^8)').exp() - 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + 1/5040*x^7 + O(x^8) + sage: f = x^3-2 + sage: f._pari_() + x^3 - 2 + sage: f._pari_().nfdisc() + -108 """ - pari_catch_sig_on() - return P.new_gen(gexp(self.g, prec_bits_to_words(precision))) + if flag != -1 or p is not None: + from sage.misc.superseded import deprecation + deprecation(16997, 'The flag and p arguments to nfdisc() are deprecated and not used anymore') + sig_on() + return P.new_gen(nfdisc(self.g)) - def gamma(gen s, unsigned long precision=0): - """ - s.gamma(precision): Gamma function at s. + def nfgenerator(self): + f = self[0] + x = f.variable() + return x.Mod(f) - If `s` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `s` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + def _nf_rnfeq(self, relpol): + """ + Return data for converting number field elements between + absolute and relative representation. - EXAMPLES:: + .. NOTE:: - sage: pari(2).gamma() - 1.00000000000000 - sage: pari(5).gamma() - 24.0000000000000 - sage: C. = ComplexField() - sage: pari(1+i).gamma() - 0.498015668118356 - 0.154949828301811*I + The output of this method is suitable for the methods + :meth:`_eltabstorel`, :meth:`_eltabstorel_lift` and + :meth:`_eltreltoabs`. TESTS:: - sage: pari(-1).gamma() - Traceback (most recent call last): - ... - PariError: domain error in gamma: argument = non-positive integer - """ - pari_catch_sig_on() - return P.new_gen(ggamma(s.g, prec_bits_to_words(precision))) + sage: K = pari('y^2 + 1').nfinit() + sage: K._nf_rnfeq(x^2 + 2) + [x^4 + 6*x^2 + 1, 1/2*x^3 + 5/2*x, -1, y^2 + 1, x^2 + 2] - def gammah(gen s, unsigned long precision=0): """ - s.gammah(): Gamma function evaluated at the argument x+1/2. - - If `s` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `s` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. + cdef gen t0 = objtogen(relpol) + sig_on() + return P.new_gen(nf_rnfeq(self.g, t0.g)) - EXAMPLES:: + reverse = deprecated_function_alias(20219, gen_auto.polrecip) - sage: pari(2).gammah() - 1.32934038817914 - sage: pari(5).gammah() - 52.3427777845535 - sage: C. = ComplexField() - sage: pari(1+i).gammah() - 0.575315188063452 + 0.0882106775440939*I + def eval(self, *args, **kwds): """ - pari_catch_sig_on() - return P.new_gen(ggammah(s.g, prec_bits_to_words(precision))) + Evaluate ``self`` with the given arguments. - def hyperu(gen a, b, x, unsigned long precision=0): - r""" - a.hyperu(b,x): U-confluent hypergeometric function. + This is currently implemented in 3 cases: + + - univariate polynomials, rational functions, power series and + Laurent series (using a single unnamed argument or keyword + arguments), + - any PARI object supporting the PARI function ``substvec`` + (in particular, multivariate polynomials) using keyword + arguments, + - objects of type ``t_CLOSURE`` (functions in GP bytecode form) + using unnamed arguments. - If `a`, `b`, or `x` is an exact argument, - it is first converted to a real or complex number using the - optional parameter precision (in bits). If the arguments are - inexact (e.g. real), the smallest of their precisions is used in - the computation, and the parameter precision is ignored. + In no case is mixing unnamed and keyword arguments allowed. EXAMPLES:: - sage: pari(1).hyperu(2,3) - 0.333333333333333 - """ - cdef gen t0 = objtogen(b) - cdef gen t1 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(hyperu(a.g, t0.g, t1.g, prec_bits_to_words(precision))) - - def incgam(gen s, x, y=None, unsigned long precision=0): - r""" - s.incgam(x, y, precision): incomplete gamma function. y is optional - and is the precomputed value of gamma(s). - - If `s` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `s` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: C. = ComplexField() - sage: pari(1+i).incgam(3-i) - -0.0458297859919946 + 0.0433696818726677*I - """ - cdef gen t0 = objtogen(x) - cdef gen t1 - if y is None: - pari_catch_sig_on() - return P.new_gen(incgam(s.g, t0.g, prec_bits_to_words(precision))) - else: - t1 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(incgam0(s.g, t0.g, t1.g, prec_bits_to_words(precision))) - - def incgamc(gen s, x, unsigned long precision=0): - r""" - s.incgamc(x): complementary incomplete gamma function. - - The arguments `x` and `s` are complex numbers such - that `s` is not a pole of `\Gamma` and - `|x|/(|s|+1)` is not much larger than `1` - (otherwise, the convergence is very slow). The function returns the - value of the integral - `\int_{0}^{x} e^{-t} t^{s-1} dt.` - - If `s` or `x` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. - - EXAMPLES:: - - sage: pari(1).incgamc(2) - 0.864664716763387 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(incgamc(s.g, t0.g, prec_bits_to_words(precision))) - - def log(gen x, unsigned long precision=0): - r""" - x.log(): natural logarithm of x. - - This function returns the principal branch of the natural logarithm - of `x`, i.e., the branch such that - `\Im(\log(x)) \in ]-\pi, \pi].` The result is - complex (with imaginary part equal to `\pi`) if - `x\in \RR` and `x<0`. In general, the algorithm uses - the formula - - .. math:: - - \log(x) \simeq \frac{\pi}{2{\rm agm}(1,4/s)} - m\log(2), - - - if `s=x 2^m` is large enough. (The result is exact to - `B` bits provided that `s>2^{B/2}`.) At low - accuracies, this function computes `\log` using the series - expansion near `1`. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - Note that `p`-adic arguments can also be given as input, - with the convention that `\log(p)=0`. Hence, in particular, - `\exp(\log(x))/x` is not in general equal to `1` - but instead to a `(p-1)`-st root of unity (or - `\pm 1` if `p=2`) times a power of `p`. - - EXAMPLES:: - - sage: pari(5).log() - 1.60943791243410 - sage: C. = ComplexField() - sage: pari(i).log() - 0.E-19 + 1.57079632679490*I - """ - pari_catch_sig_on() - return P.new_gen(glog(x.g, prec_bits_to_words(precision))) - - def lngamma(gen x, unsigned long precision=0): - r""" - Alias for :meth:`log_gamma`. - - EXAMPLES:: - - sage: pari(100).lngamma() - 359.134205369575 - """ - return x.log_gamma(precision) - - def log_gamma(gen x, unsigned long precision=0): - r""" - Logarithm of the gamma function of x. - - This function returns the principal branch of the logarithm of the - gamma function of `x`. The function - `\log(\Gamma(x))` is analytic on the complex plane with - non-positive integers removed. This function can have much larger - inputs than `\Gamma` itself. - - The `p`-adic analogue of this function is unfortunately not - implemented. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(100).log_gamma() - 359.134205369575 - """ - pari_catch_sig_on() - return P.new_gen(glngamma(x.g, prec_bits_to_words(precision))) - - def polylog(gen x, long m, long flag=0, unsigned long precision=0): - """ - x.polylog(m,flag=0): m-th polylogarithm of x. flag is optional, and - can be 0: default, 1: D_m -modified m-th polylog of x, 2: - D_m-modified m-th polylog of x, 3: P_m-modified m-th polylog of - x. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - TODO: Add more explanation, copied from the PARI manual. - - EXAMPLES:: - - sage: pari(10).polylog(3) - 5.64181141475134 - 8.32820207698027*I - sage: pari(10).polylog(3,0) - 5.64181141475134 - 8.32820207698027*I - sage: pari(10).polylog(3,1) - 0.523778453502411 - sage: pari(10).polylog(3,2) - -0.400459056163451 - """ - pari_catch_sig_on() - return P.new_gen(polylog0(m, x.g, flag, prec_bits_to_words(precision))) - - def psi(gen x, unsigned long precision=0): - r""" - x.psi(): psi-function at x. - - Return the `\psi`-function of `x`, i.e., the - logarithmic derivative `\Gamma'(x)/\Gamma(x)`. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(1).psi() - -0.577215664901533 - """ - pari_catch_sig_on() - return P.new_gen(gpsi(x.g, prec_bits_to_words(precision))) - - def sin(gen x, unsigned long precision=0): - """ - x.sin(): The sine of x. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(1).sin() - 0.841470984807897 - sage: C. = ComplexField() - sage: pari(1+i).sin() - 1.29845758141598 + 0.634963914784736*I - """ - pari_catch_sig_on() - return P.new_gen(gsin(x.g, prec_bits_to_words(precision))) - - def sinh(gen x, unsigned long precision=0): - """ - The hyperbolic sine function. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(0).sinh() - 0.E-19 - sage: C. = ComplexField() - sage: pari(1+i).sinh() - 0.634963914784736 + 1.29845758141598*I - """ - pari_catch_sig_on() - return P.new_gen(gsinh(x.g, prec_bits_to_words(precision))) - - def sqr(gen x): - """ - x.sqr(): square of x. Faster than, and most of the time (but not - always - see the examples) identical to x\*x. - - EXAMPLES:: - - sage: pari(2).sqr() - 4 - - For `2`-adic numbers, x.sqr() may not be identical to x\*x - (squaring a `2`-adic number increases its precision):: - - sage: pari("1+O(2^5)").sqr() - 1 + O(2^6) - sage: pari("1+O(2^5)")*pari("1+O(2^5)") - 1 + O(2^5) - - However:: - - sage: x = pari("1+O(2^5)"); x*x - 1 + O(2^6) - """ - pari_catch_sig_on() - return P.new_gen(gsqr(x.g)) - - - def sqrt(gen x, unsigned long precision=0): - """ - x.sqrt(precision): The square root of x. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(2).sqrt() - 1.41421356237310 - """ - pari_catch_sig_on() - return P.new_gen(gsqrt(x.g, prec_bits_to_words(precision))) - - def sqrtint(gen x): - r""" - Return the integer square root of the integer `x`, rounded - towards zero. - - EXAMPLES:: - - sage: pari(8).sqrtint() - 2 - sage: pari(10^100).sqrtint() - 100000000000000000000000000000000000000000000000000 - """ - pari_catch_sig_on() - return P.new_gen(sqrtint(x.g)) - - def sqrtn(gen x, n, unsigned long precision=0): - r""" - x.sqrtn(n): return the principal branch of the n-th root of x, - i.e., the one such that - `\arg(\sqrt(x)) \in ]-\pi/n, \pi/n]`. Also returns a second - argument which is a suitable root of unity allowing one to recover - all the other roots. If it was not possible to find such a number, - then this second return value is 0. If the argument is present and - no square root exists, return 0 instead of raising an error. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - .. note:: - - intmods (modulo a prime) and `p`-adic numbers are - allowed as arguments. - - INPUT: - - - - ``x`` - gen - - - ``n`` - integer - - - OUTPUT: - - - - ``gen`` - principal n-th root of x - - - ``gen`` - root of unity z that gives the other - roots - - - EXAMPLES:: - - sage: s, z = pari(2).sqrtn(5) - sage: z - 0.309016994374947 + 0.951056516295154*I - sage: s - 1.14869835499704 - sage: s^5 - 2.00000000000000 - sage: z^5 - 1.00000000000000 - 2.710505431 E-20*I # 32-bit - 1.00000000000000 - 2.71050543121376 E-20*I # 64-bit - sage: (s*z)^5 - 2.00000000000000 + 0.E-19*I - """ - # TODO: ??? lots of good examples in the PARI docs ??? - cdef GEN zetan - cdef gen t0 = objtogen(n) - pari_catch_sig_on() - ans = P.new_gen_noclear(gsqrtn(x.g, t0.g, &zetan, prec_bits_to_words(precision))) - return ans, P.new_gen(zetan) - - def tan(gen x, unsigned long precision=0): - """ - x.tan() - tangent of x - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(2).tan() - -2.18503986326152 - sage: C. = ComplexField() - sage: pari(i).tan() - 0.761594155955765*I - """ - pari_catch_sig_on() - return P.new_gen(gtan(x.g, prec_bits_to_words(precision))) - - def tanh(gen x, unsigned long precision=0): - """ - x.tanh() - hyperbolic tangent of x - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(1).tanh() - 0.761594155955765 - sage: C. = ComplexField() - sage: z = pari(i); z - 1.00000000000000*I - sage: result = z.tanh() - sage: result.real() <= 1e-18 - True - sage: result.imag() - 1.55740772465490 - """ - pari_catch_sig_on() - return P.new_gen(gtanh(x.g, prec_bits_to_words(precision))) - - def teichmuller(gen x): - r""" - teichmuller(x): teichmuller character of p-adic number x. - - This is the unique `(p-1)`-st root of unity congruent to - `x/p^{v_p(x)}` modulo `p`. - - EXAMPLES:: - - sage: pari('2+O(7^5)').teichmuller() - 2 + 4*7 + 6*7^2 + 3*7^3 + O(7^5) - """ - pari_catch_sig_on() - return P.new_gen(teich(x.g)) - - def theta(gen q, z, unsigned long precision=0): - """ - q.theta(z): Jacobi sine theta-function. - - If `q` or `z` is an exact argument, it is first - converted to a real or complex number using the optional parameter - precision (in bits). If the arguments are inexact (e.g. real), the - smallest of their precisions is used in the computation, and the - parameter precision is ignored. - - EXAMPLES:: - - sage: pari(0.5).theta(2) - 1.63202590295260 - """ - cdef gen t0 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(theta(q.g, t0.g, prec_bits_to_words(precision))) - - def thetanullk(gen q, long k, unsigned long precision=0): - """ - q.thetanullk(k): return the k-th derivative at z=0 of theta(q,z). - - If `q` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `q` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - EXAMPLES:: - - sage: pari(0.5).thetanullk(1) - 0.548978532560341 - """ - pari_catch_sig_on() - return P.new_gen(thetanullk(q.g, k, prec_bits_to_words(precision))) - - def weber(gen x, long flag=0, unsigned long precision=0): - r""" - x.weber(flag=0): One of Weber's f functions of x. flag is optional, - and can be 0: default, function - f(x)=exp(-i\*Pi/24)\*eta((x+1)/2)/eta(x) such that - `j=(f^{24}-16)^3/f^{24}`, 1: function f1(x)=eta(x/2)/eta(x) - such that `j=(f1^24+16)^3/f2^{24}`, 2: function - f2(x)=sqrt(2)\*eta(2\*x)/eta(x) such that - `j=(f2^{24}+16)^3/f2^{24}`. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - TODO: Add further explanation from PARI manual. - - EXAMPLES:: - - sage: C. = ComplexField() - sage: pari(i).weber() - 1.18920711500272 - sage: pari(i).weber(1) - 1.09050773266526 - sage: pari(i).weber(2) - 1.09050773266526 - """ - pari_catch_sig_on() - return P.new_gen(weber0(x.g, flag, prec_bits_to_words(precision))) - - def zeta(gen s, unsigned long precision=0): - """ - zeta(s): zeta function at s with s a complex or a p-adic number. - - If `s` is a complex number, this is the Riemann zeta - function `\zeta(s)=\sum_{n\geq 1} n^{-s}`, computed either - using the Euler-Maclaurin summation formula (if `s` is not - an integer), or using Bernoulli numbers (if `s` is a - negative integer or an even nonnegative integer), or using modular - forms (if `s` is an odd nonnegative integer). - - If `s` is a `p`-adic number, this is the - Kubota-Leopoldt zeta function, i.e. the unique continuous - `p`-adic function on the `p`-adic integers that - interpolates the values of `(1-p^{-k})\zeta(k)` at negative - integers `k` such that `k\equiv 1\pmod{p-1}` if - `p` is odd, and at odd `k` if `p=2`. - - If `x` is an exact argument, it is first converted to a - real or complex number using the optional parameter precision (in - bits). If `x` is inexact (e.g. real), its own precision is - used in the computation, and the parameter precision is ignored. - - INPUT: - - - - ``s`` - gen (real, complex, or p-adic number) - - - OUTPUT: - - - - ``gen`` - value of zeta at s. - - - EXAMPLES:: - - sage: pari(2).zeta() - 1.64493406684823 - sage: x = RR(pi)^2/6 - sage: pari(x) - 1.64493406684823 - sage: pari(3).zeta() - 1.20205690315959 - sage: pari('1+5*7+2*7^2+O(7^3)').zeta() - 4*7^-2 + 5*7^-1 + O(7^0) - """ - pari_catch_sig_on() - return P.new_gen(gzeta(s.g, prec_bits_to_words(precision))) - - ########################################### - # 4: NUMBER THEORETICAL functions - ########################################### - - def binomial(gen x, long k): - """ - binomial(x, k): return the binomial coefficient "x choose k". - - INPUT: - - - - ``x`` - any PARI object (gen) - - - ``k`` - integer - - - EXAMPLES:: - - sage: pari(6).binomial(2) - 15 - sage: pari('x+1').binomial(3) - 1/6*x^3 - 1/6*x - sage: pari('2+x+O(x^2)').binomial(3) - 1/3*x + O(x^2) - """ - pari_catch_sig_on() - return P.new_gen(binomial(x.g, k)) - - def ffgen(gen T, v=-1): - r""" - Return the generator `g=x \bmod T` of the finite field defined - by the polynomial `T`. - - INPUT: - - - ``T`` -- a gen of type t_POL with coefficients of type t_INTMOD: - a polynomial over a prime finite field - - - ``v`` -- string: a variable name or -1 (optional) - - If `v` is a string, then `g` will be a polynomial in `v`, else the - variable of the polynomial `T` is used. - - EXAMPLES:: - - sage: x = GF(2)['x'].gen() - sage: pari(x^2+x+2).ffgen() - x - sage: pari(x^2+x+1).ffgen('a') - a - """ - pari_catch_sig_on() - return P.new_gen(ffgen(T.g, P.get_var(v))) - - def ffinit(gen p, long n, v=-1): - r""" - Return a monic irreducible polynomial `g` of degree `n` over the - finite field of `p` elements. - - INPUT: - - - ``p`` -- a gen of type t_INT: a prime number - - - ``n`` -- integer: the degree of the polynomial - - - ``v`` -- string: a variable name or -1 (optional) - - If `v \geq 0', then `g` will be a polynomial in `v`, else the - variable `x` is used. - - EXAMPLES:: - - sage: pari(7).ffinit(11) - Mod(1, 7)*x^11 + Mod(1, 7)*x^10 + Mod(4, 7)*x^9 + Mod(5, 7)*x^8 + Mod(1, 7)*x^7 + Mod(1, 7)*x^2 + Mod(1, 7)*x + Mod(6, 7) - sage: pari(2003).ffinit(3) - Mod(1, 2003)*x^3 + Mod(1, 2003)*x^2 + Mod(1993, 2003)*x + Mod(1995, 2003) - """ - pari_catch_sig_on() - return P.new_gen(ffinit(p.g, n, P.get_var(v))) - - def fflog(gen self, g, o=None): - r""" - Return the discrete logarithm of the finite field element - ``self`` in base `g`. - - INPUT: - - - ``self`` -- a PARI finite field element (``FFELT``) in the - multiplicative group generated by `g`. - - - ``g`` -- the base of the logarithm as a PARI finite field - element (``FFELT``). If `o` is ``None``, this must be a - generator of the parent finite field. - - - ``o`` -- either ``None`` (then `g` must a primitive root) - or the order of `g` or a tuple ``(o, o.factor())``. - - OUTPUT: - - - An integer `n` such that ``self = g^n``. - - EXAMPLES:: - - sage: k. = GF(2^12) - sage: g = pari(a).ffprimroot() - sage: (g^1234).fflog(g) - 1234 - sage: pari(k(1)).fflog(g) - 0 - - This element does not generate the full multiplicative group:: - - sage: b = g^5 - sage: ord = b.fforder(); ord - 819 - sage: (b^555).fflog(b, ord) - 555 - sage: (b^555).fflog(b, (ord, ord.factor()) ) - 555 - """ - cdef gen t0 = objtogen(g) - cdef gen t1 - if o is None: - pari_catch_sig_on() - return P.new_gen(fflog(self.g, t0.g, NULL)) - else: - t1 = objtogen(o) - pari_catch_sig_on() - return P.new_gen(fflog(self.g, t0.g, t1.g)) - - def fforder(gen self, o=None): - r""" - Return the multiplicative order of the finite field element - ``self``. - - INPUT: - - - ``self`` -- a PARI finite field element (``FFELT``). - - - ``o`` -- either ``None`` or a multiple of the order of `o` - or a tuple ``(o, o.factor())``. - - OUTPUT: - - - The smallest positive integer `n` such that ``self^n = 1``. - - EXAMPLES:: - - sage: k. = GF(5^80) - sage: g = pari(a).ffprimroot() - sage: g.fforder() - 82718061255302767487140869206996285356581211090087890624 - sage: g.fforder( (5^80-1, factor(5^80-1)) ) - 82718061255302767487140869206996285356581211090087890624 - sage: k(2)._pari_().fforder(o=4) - 4 - """ - cdef gen t0 - if o is None: - pari_catch_sig_on() - return P.new_gen(fforder(self.g, NULL)) - else: - t0 = objtogen(o) - pari_catch_sig_on() - return P.new_gen(fforder(self.g, t0.g)) - - def ffprimroot(gen self): - r""" - Return a primitive root of the multiplicative group of the - definition field of the given finite field element. - - INPUT: - - - ``self`` -- a PARI finite field element (``FFELT``) - - OUTPUT: - - - A generator of the multiplicative group of the finite field - generated by ``self``. - - EXAMPLES:: - - sage: x = polygen(GF(3)) - sage: k. = GF(9, modulus=x^2+1) - sage: b = pari(a).ffprimroot() - sage: b # random - a + 1 - sage: b.fforder() - 8 - """ - pari_catch_sig_on() - return P.new_gen(ffprimroot(self.g, NULL)) - - def fibonacci(gen x): - r""" - Return the Fibonacci number of index x. - - EXAMPLES:: - - sage: pari(18).fibonacci() - 2584 - sage: [pari(n).fibonacci() for n in range(10)] - [0, 1, 1, 2, 3, 5, 8, 13, 21, 34] - """ - pari_catch_sig_on() - return P.new_gen(fibo(long(x))) - - def gcd(gen x, y=None): - """ - Return the greatest common divisor of `x` and `y`. - - If `y` is ``None``, then `x` must be a list or tuple, and the - greatest common divisor of its components is returned. - - EXAMPLES:: - - sage: pari(10).gcd(15) - 5 - sage: pari([5, 'y']).gcd() - 1 - sage: pari(['x', x^2]).gcd() - x - - """ - cdef gen t0 - if y is None: - pari_catch_sig_on() - return P.new_gen(ggcd0(x.g, NULL)) - else: - t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(ggcd0(x.g, t0.g)) - - def issquare(gen x, find_root=False): - """ - issquare(x,n): ``True`` if x is a square, ``False`` if not. If - ``find_root`` is given, also returns the exact square root. - """ - cdef GEN G - cdef long t - cdef gen g - pari_catch_sig_on() - if find_root: - t = itos(gissquareall(x.g, &G)) - if t: - return True, P.new_gen(G) - else: - P.clear_stack() - return False, None - else: - t = itos(gissquare(x.g)) - pari_catch_sig_off() - return t != 0 - - def issquarefree(gen self): - """ - EXAMPLES:: - - sage: pari(10).issquarefree() - True - sage: pari(20).issquarefree() - False - """ - pari_catch_sig_on() - cdef long t = issquarefree(self.g) - pari_catch_sig_off() - return t != 0 - - def lcm(gen x, y=None): - """ - Return the least common multiple of `x` and `y`. - - If `y` is ``None``, then `x` must be a list or tuple, and the - least common multiple of its components is returned. - - EXAMPLES:: - - sage: pari(10).lcm(15) - 30 - sage: pari([5, 'y']).lcm() - 5*y - sage: pari([10, 'x', x^2]).lcm() - 10*x^2 - - """ - cdef gen t0 - if y is None: - pari_catch_sig_on() - return P.new_gen(glcm0(x.g, NULL)) - else: - t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(glcm0(x.g, t0.g)) - - def numdiv(gen n): - """ - Return the number of divisors of the integer n. - - EXAMPLES:: - - sage: pari(10).numdiv() - 4 - """ - pari_catch_sig_on() - return P.new_gen(numdiv(n.g)) - - def phi(gen n): - """ - Return the Euler phi function of n. - - EXAMPLES:: - - sage: pari(10).phi() - 4 - """ - pari_catch_sig_on() - return P.new_gen(eulerphi(n.g)) - - def primepi(gen self): - """ - Return the number of primes less than or equal to self. - - EXAMPLES:: - - sage: pari(7).primepi() - 4 - sage: pari(100).primepi() - 25 - sage: pari(1000).primepi() - 168 - sage: pari(100000).primepi() - 9592 - sage: pari(0).primepi() - 0 - sage: pari(-15).primepi() - 0 - sage: pari(500509).primepi() - 41581 - """ - pari_catch_sig_on() - if self > P._primelimit(): - P.init_primes(self + 10) - if signe(self.g) != 1: - pari_catch_sig_off() - return P.PARI_ZERO - return P.new_gen(primepi(self.g)) - - def sumdiv(gen n): - """ - Return the sum of the divisors of `n`. - - EXAMPLES:: - - sage: pari(10).sumdiv() - 18 - """ - pari_catch_sig_on() - return P.new_gen(sumdiv(n.g)) - - def sumdivk(gen n, long k): - """ - Return the sum of the k-th powers of the divisors of n. - - EXAMPLES:: - - sage: pari(10).sumdivk(2) - 130 - """ - pari_catch_sig_on() - return P.new_gen(sumdivk(n.g, k)) - - def Zn_issquare(gen self, n): - """ - Return ``True`` if ``self`` is a square modulo `n`, ``False`` - if not. - - INPUT: - - - ``self`` -- integer - - - ``n`` -- integer or factorisation matrix - - EXAMPLES:: - - sage: pari(3).Zn_issquare(4) - False - sage: pari(4).Zn_issquare(30.factor()) - True - - """ - cdef gen t0 = objtogen(n) - pari_catch_sig_on() - cdef long t = Zn_issquare(self.g, t0.g) - pari_catch_sig_off() - return t != 0 - - def Zn_sqrt(gen self, n): - """ - Return a square root of ``self`` modulo `n`, if such a square - root exists; otherwise, raise a ``ValueError``. - - INPUT: - - - ``self`` -- integer - - - ``n`` -- integer or factorisation matrix - - EXAMPLES:: - - sage: pari(3).Zn_sqrt(4) - Traceback (most recent call last): - ... - ValueError: 3 is not a square modulo 4 - sage: pari(4).Zn_sqrt(30.factor()) - 22 - - """ - cdef gen t0 = objtogen(n) - cdef GEN s - pari_catch_sig_on() - s = Zn_sqrt(self.g, t0.g) - if s == NULL: - pari_catch_sig_off() - raise ValueError("%s is not a square modulo %s" % (self, n)) - return P.new_gen(s) - - - ################################################## - # 5: Elliptic curve functions - ################################################## - - def ellinit(self, long flag=-1, unsigned long precision=0): - """ - Return the PARI elliptic curve object with Weierstrass coefficients - given by self, a list with 5 elements. - - INPUT: - - - - ``self`` -- a list of 5 coefficients - - - ``flag`` -- ignored (for backwards compatibility) - - - ``precision (optional, default: 0)`` - the real - precision to be used in the computation of the components of the - PARI (s)ell structure; if 0, use the default 64 bits. - - .. note:: - - The parameter ``precision`` in ``ellinit`` controls not - only the real precision of the resulting (s)ell structure, - but in some cases also the precision of most subsequent - computations with this elliptic curve (if those rely on - the precomputations done by ``ellinit``). You should - therefore set the precision from the start to the value - you require. - - OUTPUT: - - - ``gen`` -- a PARI ell structure. - - EXAMPLES: - - An elliptic curve with integer coefficients:: - - sage: e = pari([0,1,0,1,0]).ellinit(); e - [0, 1, 0, 1, 0, 4, 2, 0, -1, -32, 224, -48, 2048/3, Vecsmall([1]), [Vecsmall([64, -1])], [0, 0, 0, 0, 0, 0, 0, 0]] - - The coefficients can be any ring elements that convert to PARI:: - - sage: pari([0,1/2,0,-3/4,0]).ellinit() - [0, 1/2, 0, -3/4, 0, 2, -3/2, 0, -9/16, 40, -116, 117/4, 256000/117, Vecsmall([1]), [Vecsmall([64, 1])], [0, 0, 0, 0, 0, 0, 0, 0]] - sage: pari([0,0.5,0,-0.75,0]).ellinit() - [0, 0.500000000000000, 0, -0.750000000000000, 0, 2.00000000000000, -1.50000000000000, 0, -0.562500000000000, 40.0000000000000, -116.000000000000, 29.2500000000000, 2188.03418803419, Vecsmall([0]), [Vecsmall([64, 1])], [0, 0, 0, 0]] - sage: pari([0,I,0,1,0]).ellinit() - [0, I, 0, 1, 0, 4*I, 2, 0, -1, -64, 352*I, -80, 16384/5, Vecsmall([0]), [Vecsmall([64, 0])], [0, 0, 0, 0]] - sage: pari([0,x,0,2*x,1]).ellinit() - [0, x, 0, 2*x, 1, 4*x, 4*x, 4, -4*x^2 + 4*x, 16*x^2 - 96*x, -64*x^3 + 576*x^2 - 864, 64*x^4 - 576*x^3 + 576*x^2 - 432, (256*x^6 - 4608*x^5 + 27648*x^4 - 55296*x^3)/(4*x^4 - 36*x^3 + 36*x^2 - 27), Vecsmall([0]), [Vecsmall([64, 0])], [0, 0, 0, 0]] - """ - if flag != -1: - from sage.misc.superseded import deprecation - deprecation(16997, 'The flag argument to ellinit() is deprecated and not used anymore') - pari_catch_sig_on() - return P.new_gen(ellinit(self.g, NULL, prec_bits_to_words(precision))) - - def ellglobalred(self): - """ - Return information related to the global minimal model of the - elliptic curve e. - - INPUT: - - - ``e`` -- elliptic curve (returned by ellinit) - - OUTPUT: A vector [N, [u,r,s,t], c, faN, L] with - - - ``N`` - the (arithmetic) conductor of `e` - - - ``[u,r,s,t]`` - a vector giving the coordinate change over - Q from e to its minimal integral model (see also ellminimalmodel) - - - ``c`` - the product of the local Tamagawa numbers of `e`. - - - ``faN`` is the factorization of `N` - - - ``L[i]`` is ``elllocalred(E, faN[i,1])`` - - EXAMPLES:: - - sage: e = pari([0, 5, 2, -1, 1]).ellinit() - sage: e.ellglobalred() - [20144, [1, -2, 0, -1], 1, [2, 4; 1259, 1], [[4, 2, 0, 1], [1, 5, 0, 1]]] - sage: e = pari(EllipticCurve('17a').a_invariants()).ellinit() - sage: e.ellglobalred() - [17, [1, 0, 0, 0], 4, Mat([17, 1]), [[1, 8, 0, 4]]] - """ - pari_catch_sig_on() - return P.new_gen(ellglobalred(self.g)) - - def elladd(self, z0, z1): - """ - e.elladd(z0, z1): return the sum of the points z0 and z1 on this - elliptic curve. - - INPUT: - - - - ``e`` - elliptic curve E - - - ``z0`` - point on E - - - ``z1`` - point on E - - - OUTPUT: point on E - - EXAMPLES: - - First we create an elliptic curve:: - - sage: e = pari([0, 1, 1, -2, 0]).ellinit() - - Next we add two points on the elliptic curve. Notice that the - Python lists are automatically converted to PARI objects so you - don't have to do that explicitly in your code. - - :: - - sage: e.elladd([1,0], [-1,1]) - [-3/4, -15/8] - """ - cdef gen t0 = objtogen(z0) - cdef gen t1 = objtogen(z1) - pari_catch_sig_on() - return P.new_gen(elladd(self.g, t0.g, t1.g)) - - def ellak(self, n): - r""" - e.ellak(n): Returns the coefficient `a_n` of the - `L`-function of the elliptic curve e, i.e. the - `n`-th Fourier coefficient of the weight 2 newform - associated to e (according to Shimura-Taniyama). - - The curve `e` *must* be a medium or long vector of the type - given by ellinit. For this function to work for every n and not - just those prime to the conductor, e must be a minimal Weierstrass - equation. If this is not the case, use the function ellminimalmodel - first before using ellak (or you will get INCORRECT RESULTS!) - - - INPUT: - - - - ``e`` - a PARI elliptic curve. - - - ``n`` - integer. - - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.ellak(6) - 2 - sage: e.ellak(2005) - 2 - sage: e.ellak(-1) - 0 - sage: e.ellak(0) - 0 - """ - cdef gen t0 = objtogen(n) - pari_catch_sig_on() - return P.new_gen(akell(self.g, t0.g)) - - - def ellan(self, long n, python_ints=False): - """ - Return the first `n` Fourier coefficients of the modular - form attached to this elliptic curve. See ellak for more details. - - INPUT: - - - - ``n`` - a long integer - - - ``python_ints`` - bool (default is False); if True, - return a list of Python ints instead of a PARI gen wrapper. - - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.ellan(3) - [1, -2, -1] - sage: e.ellan(20) - [1, -2, -1, 2, 1, 2, -2, 0, -2, -2, 1, -2, 4, 4, -1, -4, -2, 4, 0, 2] - sage: e.ellan(-1) - [] - sage: v = e.ellan(10, python_ints=True); v - [1, -2, -1, 2, 1, 2, -2, 0, -2, -2] - sage: type(v) - - sage: type(v[0]) - - """ - pari_catch_sig_on() - cdef GEN g - if python_ints: - g = anell(self.g, n) - v = [gtolong( g[i+1]) for i in range(glength(g))] - P.clear_stack() - return v - else: - return P.new_gen(anell(self.g, n)) - - def ellanalyticrank(self, unsigned long precision=0): - r""" - Returns a 2-component vector with the order of vanishing at - `s = 1` of the L-function of the elliptic curve and the value - of the first non-zero derivative. - - EXAMPLE:: - - sage: E = EllipticCurve('389a1') - sage: pari(E).ellanalyticrank() - [2, 1.51863300057685] - """ - pari_catch_sig_on() - return P.new_gen(ellanalyticrank(self.g, 0, prec_bits_to_words(precision))) - - def ellap(self, p): - r""" - e.ellap(p): Returns the prime-indexed coefficient `a_p` of the - `L`-function of the elliptic curve `e`, i.e. the `p`-th Fourier - coefficient of the newform attached to e. - - The computation uses the Shanks--Mestre method, or the SEA - algorithm. - - .. WARNING:: - - For this function to work for every n and not just those prime - to the conductor, e must be a minimal Weierstrass equation. - If this is not the case, use the function ellminimalmodel first - before using ellap (or you will get INCORRECT RESULTS!) - - - INPUT: - - - - ``e`` - a PARI elliptic curve. - - - ``p`` - prime integer - - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.ellap(2) - -2 - sage: e.ellap(2003) - 4 - sage: e.ellak(-1) - 0 - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(ellap(self.g, t0.g)) - - - def ellaplist(self, long n, python_ints=False): - r""" - e.ellaplist(n): Returns a PARI list of all the prime-indexed - coefficients `a_p` (up to n) of the `L`-function - of the elliptic curve `e`, i.e. the Fourier coefficients of - the newform attached to `e`. - - INPUT: - - - ``self`` -- an elliptic curve - - - ``n`` -- a long integer - - - ``python_ints`` -- bool (default is False); if True, - return a list of Python ints instead of a PARI gen wrapper. - - .. WARNING:: - - The curve e must be a medium or long vector of the type given by - ellinit. For this function to work for every n and not just those - prime to the conductor, e must be a minimal Weierstrass equation. - If this is not the case, use the function ellminimalmodel first - before using ellaplist (or you will get INCORRECT RESULTS!) - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: v = e.ellaplist(10); v - [-2, -1, 1, -2] - sage: type(v) - - sage: v.type() - 't_VEC' - sage: e.ellan(10) - [1, -2, -1, 2, 1, 2, -2, 0, -2, -2] - sage: v = e.ellaplist(10, python_ints=True); v - [-2, -1, 1, -2] - sage: type(v) - - sage: type(v[0]) - - - TESTS:: - - sage: v = e.ellaplist(1) - sage: print v, type(v) - [] - sage: v = e.ellaplist(1, python_ints=True) - sage: print v, type(v) - [] - """ - if python_ints: - return [int(x) for x in self.ellaplist(n)] - - if n < 2: - pari_catch_sig_on() - return P.new_gen(zerovec(0)) - - # 1. Make a table of primes up to n. - P.init_primes(n+1) - cdef gen t0 = objtogen(n) - pari_catch_sig_on() - cdef GEN g = primes(gtolong(primepi(t0.g))) - - # 2. Replace each prime in the table by ellap of it. - cdef long i - for i from 0 <= i < glength(g): - set_gel(g, i + 1, ellap(self.g, gel(g, i + 1))) - return P.new_gen(g) - - def ellchangecurve(self, ch): - """ - e.ellchangecurve(ch): return the new model (equation) for the - elliptic curve e given by the change of coordinates ch. - - The change of coordinates is specified by a vector ch=[u,r,s,t]; if - `x'` and `y'` are the new coordinates, then - `x = u^2 x' + r` and `y = u^3 y' + su^2 x' + t`. - - INPUT: - - - ``e`` - elliptic curve - - - ``ch`` - change of coordinates vector with 4 - entries - - EXAMPLES:: - - sage: e = pari([1,2,3,4,5]).ellinit() - sage: e.ellglobalred() - [10351, [1, -1, 0, -1], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]] - sage: f = e.ellchangecurve([1,-1,0,-1]) - sage: f[:5] - [1, -1, 0, 4, 3] - """ - cdef gen t0 = objtogen(ch) - pari_catch_sig_on() - return P.new_gen(ellchangecurve(self.g, t0.g)) - - def elleta(self, unsigned long precision=0): - """ - e.elleta(): return the vector [eta1,eta2] of quasi-periods - associated with the period lattice e.omega() of the elliptic curve - e. - - EXAMPLES:: - - sage: e = pari([0,0,0,-82,0]).ellinit() - sage: e.elleta() - [3.60546360143265, 3.60546360143265*I] - sage: w1, w2 = e.omega() - sage: eta1, eta2 = e.elleta() - sage: w1*eta2 - w2*eta1 - 6.28318530717959*I - """ - pari_catch_sig_on() - return P.new_gen(elleta(self.g, prec_bits_to_words(precision))) - - def ellheight(self, a, b=None, long flag=-1, unsigned long precision=0): - """ - Canonical height of point ``a`` on elliptic curve ``self``, - resp. the value of the associated bilinear form at ``(a,b)``. - - INPUT: - - - ``self``-- an elliptic curve over `\QQ`. - - - ``a`` -- rational point on ``self``. - - - ``b`` -- (optional) rational point on ``self``. - - - ``precision (optional)`` -- the precision of the - result, in bits. - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: e.ellheight([1,0]) - 0.476711659343740 - sage: e.ellheight([1,0], precision=128).sage() - 0.47671165934373953737948605888465305945902294218 # 32-bit - 0.476711659343739537379486058884653059459022942211150879336 # 64-bit - - Computing the bilinear form:: - - sage: e.ellheight([1, 0], [-1, 1]) - 0.418188984498861 - """ - if flag != -1: - from sage.misc.superseded import deprecation - deprecation(16997, 'The flag argument to ellheight() is deprecated and not used anymore') - cdef gen t0 = objtogen(a) - cdef gen t1 - if b is None: - pari_catch_sig_on() - return P.new_gen(ellheight(self.g, t0.g, prec_bits_to_words(precision))) - else: - t1 = objtogen(b) - pari_catch_sig_on() - return P.new_gen(ellheight0(self.g, t0.g, t1.g, prec_bits_to_words(precision))) - - def ellheightmatrix(self, x, unsigned long precision=0): - """ - e.ellheightmatrix(x): return the height matrix for the vector x of - points on the elliptic curve e. - - In other words, it returns the Gram matrix of x with respect to the - height bilinear form on e (see ellbil). - - INPUT: - - - - ``e`` - elliptic curve over `\QQ`, - assumed to be in a standard minimal integral model (as given by - ellminimalmodel) - - - ``x`` - vector of rational points on e - - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit().ellminimalmodel()[0] - sage: e.ellheightmatrix([[1,0], [-1,1]]) - [0.476711659343740, 0.418188984498861; 0.418188984498861, 0.686667083305587] - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(ellheightmatrix(self.g, t0.g, prec_bits_to_words(precision))) - - def ellisoncurve(self, x): - """ - e.ellisoncurve(x): return True if the point x is on the elliptic - curve e, False otherwise. - - If the point or the curve have inexact coefficients, an attempt is - made to take this into account. - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: e.ellisoncurve([1,0]) - True - sage: e.ellisoncurve([1,1]) - False - sage: e.ellisoncurve([1,0.00000000000000001]) - False - sage: e.ellisoncurve([1,0.000000000000000001]) - True - sage: e.ellisoncurve([0]) - True - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - cdef int t = oncurve(self.g, t0.g) - pari_catch_sig_off() - return t != 0 - - def elllocalred(self, p): - r""" - e.elllocalred(p): computes the data of local reduction at the prime - p on the elliptic curve e - - For more details on local reduction and Kodaira types, see IV.8 and - IV.9 in J. Silverman's book "Advanced topics in the arithmetic of - elliptic curves". - - INPUT: - - - - ``e`` - elliptic curve with coefficients in `\ZZ` - - - ``p`` - prime number - - - OUTPUT: - - - - ``gen`` - the exponent of p in the arithmetic - conductor of e - - - ``gen`` - the Kodaira type of e at p, encoded as an - integer: - - - ``1`` - type `I_0`: good reduction, - nonsingular curve of genus 1 - - - ``2`` - type `II`: rational curve with a - cusp - - - ``3`` - type `III`: two nonsingular rational - curves intersecting tangentially at one point - - - ``4`` - type `IV`: three nonsingular - rational curves intersecting at one point - - - ``5`` - type `I_1`: rational curve with a - node - - - ``6 or larger`` - think of it as `4+v`, then - it is type `I_v`: `v` nonsingular rational curves - arranged as a `v`-gon - - - ``-1`` - type `I_0^*`: nonsingular rational - curve of multiplicity two with four nonsingular rational curves of - multiplicity one attached - - - ``-2`` - type `II^*`: nine nonsingular - rational curves in a special configuration - - - ``-3`` - type `III^*`: eight nonsingular - rational curves in a special configuration - - - ``-4`` - type `IV^*`: seven nonsingular - rational curves in a special configuration - - - ``-5 or smaller`` - think of it as `-4-v`, - then it is type `I_v^*`: chain of `v+1` - nonsingular rational curves of multiplicity two, with two - nonsingular rational curves of multiplicity one attached at either - end - - - ``gen`` - a vector with 4 components, giving the - coordinate changes done during the local reduction; if the first - component is 1, then the equation for e was already minimal at p - - - ``gen`` - the local Tamagawa number `c_p` - - - EXAMPLES: - - Type `I_0`:: - - sage: e = pari([0,0,0,0,1]).ellinit() - sage: e.elllocalred(7) - [0, 1, [1, 0, 0, 0], 1] - - Type `II`:: - - sage: e = pari(EllipticCurve('27a3').a_invariants()).ellinit() - sage: e.elllocalred(3) - [3, 2, [1, -1, 0, 1], 1] - - Type `III`:: - - sage: e = pari(EllipticCurve('24a4').a_invariants()).ellinit() - sage: e.elllocalred(2) - [3, 3, [1, 1, 0, 1], 2] - - Type `IV`:: - - sage: e = pari(EllipticCurve('20a2').a_invariants()).ellinit() - sage: e.elllocalred(2) - [2, 4, [1, 1, 0, 1], 3] - - Type `I_1`:: - - sage: e = pari(EllipticCurve('11a2').a_invariants()).ellinit() - sage: e.elllocalred(11) - [1, 5, [1, 0, 0, 0], 1] - - Type `I_2`:: - - sage: e = pari(EllipticCurve('14a4').a_invariants()).ellinit() - sage: e.elllocalred(2) - [1, 6, [1, 0, 0, 0], 2] - - Type `I_6`:: - - sage: e = pari(EllipticCurve('14a1').a_invariants()).ellinit() - sage: e.elllocalred(2) - [1, 10, [1, 0, 0, 0], 2] - - Type `I_0^*`:: - - sage: e = pari(EllipticCurve('32a3').a_invariants()).ellinit() - sage: e.elllocalred(2) - [5, -1, [1, 1, 1, 0], 1] - - Type `II^*`:: - - sage: e = pari(EllipticCurve('24a5').a_invariants()).ellinit() - sage: e.elllocalred(2) - [3, -2, [1, 2, 1, 4], 1] - - Type `III^*`:: - - sage: e = pari(EllipticCurve('24a2').a_invariants()).ellinit() - sage: e.elllocalred(2) - [3, -3, [1, 2, 1, 4], 2] - - Type `IV^*`:: - - sage: e = pari(EllipticCurve('20a1').a_invariants()).ellinit() - sage: e.elllocalred(2) - [2, -4, [1, 0, 1, 2], 3] - - Type `I_1^*`:: - - sage: e = pari(EllipticCurve('24a1').a_invariants()).ellinit() - sage: e.elllocalred(2) - [3, -5, [1, 0, 1, 2], 4] - - Type `I_6^*`:: - - sage: e = pari(EllipticCurve('90c2').a_invariants()).ellinit() - sage: e.elllocalred(3) - [2, -10, [1, 96, 1, 316], 4] - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(elllocalred(self.g, t0.g)) - - def elllseries(self, s, A=1, unsigned long precision=0): - """ - e.elllseries(s, A=1): return the value of the `L`-series of - the elliptic curve e at the complex number s. - - This uses an `O(N^{1/2})` algorithm in the conductor N of - e, so it is impractical for large conductors (say greater than - `10^{12}`). - - INPUT: - - - - ``e`` - elliptic curve defined over `\QQ` - - - ``s`` - complex number - - - ``A (optional)`` - cutoff point for the integral, - which must be chosen close to 1 for best speed. - - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: e.elllseries(2.1) - 0.402838047956645 - sage: e.elllseries(1, precision=128) - 6.21952537507477 E-39 - sage: e.elllseries(1, precision=256) - 2.95993347819786 E-77 - sage: e.elllseries(-2) - 0 - sage: e.elllseries(2.1, A=1.1) - 0.402838047956645 - """ - cdef gen t0 = objtogen(s) - cdef gen t1 = objtogen(A) - pari_catch_sig_on() - return P.new_gen(elllseries(self.g, t0.g, t1.g, prec_bits_to_words(precision))) - - def ellminimalmodel(self): - """ - ellminimalmodel(e): return the standard minimal integral model of - the rational elliptic curve e and the corresponding change of - variables. INPUT: - - - - ``e`` - gen (that defines an elliptic curve) - - - OUTPUT: - - - - ``gen`` - minimal model - - - ``gen`` - change of coordinates - - - EXAMPLES:: - - sage: e = pari([1,2,3,4,5]).ellinit() - sage: F, ch = e.ellminimalmodel() - sage: F[:5] - [1, -1, 0, 4, 3] - sage: ch - [1, -1, 0, -1] - sage: e.ellchangecurve(ch)[:5] - [1, -1, 0, 4, 3] - """ - cdef GEN x, y - cdef gen model, change - cdef pari_sp t - pari_catch_sig_on() - x = ellminimalmodel(self.g, &y) - change = P.new_gen_noclear(y) - model = P.new_gen(x) - return model, change - - def ellorder(self, x): - """ - e.ellorder(x): return the order of the point x on the elliptic - curve e (return 0 if x is not a torsion point) - - INPUT: - - - - ``e`` - elliptic curve defined over `\QQ` - - - ``x`` - point on e - - - EXAMPLES:: - - sage: e = pari(EllipticCurve('65a1').a_invariants()).ellinit() - - A point of order two:: - - sage: e.ellorder([0,0]) - 2 - - And a point of infinite order:: - - sage: e.ellorder([1,0]) - 0 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(orderell(self.g, t0.g)) - - def ellordinate(self, x, unsigned long precision=0): - """ - e.ellordinate(x): return the `y`-coordinates of the points - on the elliptic curve e having x as `x`-coordinate. - - INPUT: - - - - ``e`` - elliptic curve - - - ``x`` - x-coordinate (can be a complex or p-adic - number, or a more complicated object like a power series) - - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: e.ellordinate(0) - [0, -1] - sage: e.ellordinate(I) - [0.582203589721741 - 1.38606082464177*I, -1.58220358972174 + 1.38606082464177*I] - sage: e.ellordinate(I, precision=128)[0].sage() - 0.58220358972174117723338947874993600727 - 1.3860608246417697185311834209833653345*I - sage: e.ellordinate(1+3*5^1+O(5^3)) - [4*5 + 5^2 + O(5^3), 4 + 3*5^2 + O(5^3)] - sage: e.ellordinate('z+2*z^2+O(z^4)') - [-2*z - 7*z^2 - 23*z^3 + O(z^4), -1 + 2*z + 7*z^2 + 23*z^3 + O(z^4)] - - The field in which PARI looks for the point depends on the - input field:: - - sage: e.ellordinate(5) - [] - sage: e.ellordinate(5.0) - [11.3427192823270, -12.3427192823270] - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(ellordinate(self.g, t0.g, prec_bits_to_words(precision))) - - def ellpointtoz(self, pt, unsigned long precision=0): - """ - e.ellpointtoz(pt): return the complex number (in the fundamental - parallelogram) corresponding to the point ``pt`` on the elliptic curve - e, under the complex uniformization of e given by the Weierstrass - p-function. - - The complex number z returned by this function lies in the - parallelogram formed by the real and complex periods of e, as given - by e.omega(). - - EXAMPLES:: - - sage: e = pari([0,0,0,1,0]).ellinit() - sage: e.ellpointtoz([0,0]) - 1.85407467730137 - - The point at infinity is sent to the complex number 0:: - - sage: e.ellpointtoz([0]) - 0 - """ - cdef gen t0 = objtogen(pt) - pari_catch_sig_on() - return P.new_gen(zell(self.g, t0.g, prec_bits_to_words(precision))) - - def ellmul(self, z, n): - """ - Return `n` times the point `z` on the elliptic curve `e`. - - INPUT: - - - ``e`` - elliptic curve - - - ``z`` - point on `e` - - - ``n`` - integer, or a complex quadratic integer of complex - multiplication for `e`. Complex multiplication currently - only works if `e` is defined over `Q`. - - EXAMPLES: We consider a curve with CM by `Z[i]`:: - - sage: e = pari([0,0,0,3,0]).ellinit() - sage: p = [1,2] # Point of infinite order - - Multiplication by two:: - - sage: e.ellmul([0,0], 2) - [0] - sage: e.ellmul(p, 2) - [1/4, -7/8] - - Complex multiplication:: - - sage: q = e.ellmul(p, 1+I); q - [-2*I, 1 + I] - sage: e.ellmul(q, 1-I) - [1/4, -7/8] - - TESTS:: - - sage: for D in [-7, -8, -11, -12, -16, -19, -27, -28]: # long time (1s) - ....: hcpol = hilbert_class_polynomial(D) - ....: j = hcpol.roots(multiplicities=False)[0] - ....: t = (1728-j)/(27*j) - ....: E = EllipticCurve([4*t,16*t^2]) - ....: P = E.point([0, 4*t]) - ....: assert(E.j_invariant() == j) - ....: # - ....: # Compute some CM number and its minimal polynomial - ....: # - ....: cm = pari('cm = (3*quadgen(%s)+2)'%D) - ....: cm_minpoly = pari('minpoly(cm)') - ....: # - ....: # Evaluate cm_minpoly(cm)(P), which should be zero - ....: # - ....: e = pari(E) # Convert E to PARI - ....: P2 = e.ellmul(P, cm_minpoly[2]*cm + cm_minpoly[1]) - ....: P0 = e.elladd(e.ellmul(P, cm_minpoly[0]), e.ellmul(P2, cm)) - ....: assert(P0 == E(0)) - """ - cdef gen t0 = objtogen(z) - cdef gen t1 = objtogen(n) - pari_catch_sig_on() - return P.new_gen(ellmul(self.g, t0.g, t1.g)) - - def ellrootno(self, p=None): - """ - Return the root number for the L-function of the elliptic curve - E/Q at a prime p (including 0, for the infinite place); return - the global root number if p is omitted. - - INPUT: - - - ``e`` - elliptic curve over `\QQ` - - - ``p`` - a prime number or ``None``. - - OUTPUT: 1 or -1 - - EXAMPLES: Here is a curve of rank 3:: - - sage: e = pari([0,0,0,-82,0]).ellinit() - sage: e.ellrootno() - -1 - sage: e.ellrootno(2) - 1 - sage: e.ellrootno(1009) - 1 - """ - cdef gen t0 - cdef GEN g0 - if p is None: - g0 = NULL - elif p == 1: - from sage.misc.superseded import deprecation - deprecation(15767, 'The argument p=1 in ellrootno() is deprecated, use p=None instead') - g0 = NULL - else: - t0 = objtogen(p) - g0 = t0.g - pari_catch_sig_on() - rootno = ellrootno(self.g, g0) - pari_catch_sig_off() - return rootno - - def ellsigma(self, z, long flag=0, unsigned long precision=0): - """ - e.ellsigma(z, flag=0): return the value at the complex point z of - the Weierstrass `\sigma` function associated to the - elliptic curve e. - - EXAMPLES:: - - sage: e = pari([0,0,0,1,0]).ellinit() - sage: C. = ComplexField() - sage: e.ellsigma(2+i) - 1.43490215804166 + 1.80307856719256*I - """ - cdef gen t0 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(ellsigma(self.g, t0.g, flag, prec_bits_to_words(precision))) - - def ellsub(self, z0, z1): - """ - e.ellsub(z0, z1): return z0-z1 on this elliptic curve. - - INPUT: - - - - ``e`` - elliptic curve E - - - ``z0`` - point on E - - - ``z1`` - point on E - - - OUTPUT: point on E - - EXAMPLES:: - - sage: e = pari([0, 1, 1, -2, 0]).ellinit() - sage: e.ellsub([1,0], [-1,1]) - [0, 0] - """ - cdef gen t0 = objtogen(z0) - cdef gen t1 = objtogen(z1) - pari_catch_sig_on() - return P.new_gen(ellsub(self.g, t0.g, t1.g)) - - def elltaniyama(self, long n=-1): - if n < 0: - n = P.get_series_precision() - pari_catch_sig_on() - return P.new_gen(elltaniyama(self.g, n)) - - def elltors(self, long flag=0): - """ - e.elltors(flag = 0): return information about the torsion subgroup - of the elliptic curve e - - INPUT: - - - - ``e`` - elliptic curve over `\QQ` - - - ``flag (optional)`` - specify which algorithm to - use: - - - ``0 (default)`` - use Doud's algorithm: bound - torsion by computing the cardinality of e(GF(p)) for small primes - of good reduction, then look for torsion points using Weierstrass - parametrization and Mazur's classification - - - ``1`` - use algorithm given by the Nagell-Lutz - theorem (this is much slower) - - - OUTPUT: - - - - ``gen`` - the order of the torsion subgroup, a.k.a. - the number of points of finite order - - - ``gen`` - vector giving the structure of the torsion - subgroup as a product of cyclic groups, sorted in non-increasing - order - - - ``gen`` - vector giving points on e generating these - cyclic groups - - - EXAMPLES:: - - sage: e = pari([1,0,1,-19,26]).ellinit() - sage: e.elltors() - [12, [6, 2], [[1, 2], [3, -2]]] - """ - pari_catch_sig_on() - return P.new_gen(elltors0(self.g, flag)) - - def ellzeta(self, z, unsigned long precision=0): - """ - e.ellzeta(z): return the value at the complex point z of the - Weierstrass `\zeta` function associated with the elliptic - curve e. - - .. note:: - - This function has infinitely many poles (one of which is at - z=0); attempting to evaluate it too close to one of the - poles will result in a PariError. - - INPUT: - - - - ``e`` - elliptic curve - - - ``z`` - complex number - - - EXAMPLES:: - - sage: e = pari([0,0,0,1,0]).ellinit() - sage: e.ellzeta(1) - 1.06479841295883 - sage: C. = ComplexField() - sage: e.ellzeta(i-1) - -0.350122658523049 - 0.350122658523049*I - """ - cdef gen t0 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(ellzeta(self.g, t0.g, prec_bits_to_words(precision))) - - def ellztopoint(self, z, unsigned long precision=0): - """ - e.ellztopoint(z): return the point on the elliptic curve e - corresponding to the complex number z, under the usual complex - uniformization of e by the Weierstrass p-function. - - INPUT: - - - - ``e`` - elliptic curve - - - ``z`` - complex number - - - OUTPUT point on e - - EXAMPLES:: - - sage: e = pari([0,0,0,1,0]).ellinit() - sage: C. = ComplexField() - sage: e.ellztopoint(1+i) - [0.E-... - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] - - Complex numbers belonging to the period lattice of e are of course - sent to the point at infinity on e:: - - sage: e.ellztopoint(0) - [0] - """ - cdef gen t0 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(pointell(self.g, t0.g, prec_bits_to_words(precision))) - - def omega(self, unsigned long precision=0): - """ - e.omega(): return basis for the period lattice of the elliptic - curve e. - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.omega() - [1.26920930427955, 0.634604652139777 - 1.45881661693850*I] - """ - pari_catch_sig_on() - return P.new_gen(ellR_omega(self.g, prec_bits_to_words(precision))) - - def disc(self): - """ - e.disc(): return the discriminant of the elliptic curve e. - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.disc() - -161051 - sage: _.factor() - [-1, 1; 11, 5] - """ - pari_catch_sig_on() - return P.new_gen(member_disc(self.g)) - - def j(self): - """ - e.j(): return the j-invariant of the elliptic curve e. - - EXAMPLES:: - - sage: e = pari([0, -1, 1, -10, -20]).ellinit() - sage: e.j() - -122023936/161051 - sage: _.factor() - [-1, 1; 2, 12; 11, -5; 31, 3] - """ - pari_catch_sig_on() - return P.new_gen(member_j(self.g)) - - def ellj(self, unsigned long precision=0): - """ - Elliptic `j`-invariant of ``self``. - - EXAMPLES:: - - sage: pari(I).ellj() - 1728.00000000000 - sage: pari(3*I).ellj() - 153553679.396729 - sage: pari('quadgen(-3)').ellj() - 0.E-54 - sage: pari('quadgen(-7)').ellj(precision=256).sage() - -3375.000000000000000000000000000000000000000000000000000000000000000000000000 - sage: pari(-I).ellj() - Traceback (most recent call last): - ... - PariError: domain error in modular function: Im(argument) <= 0 - """ - pari_catch_sig_on() - return P.new_gen(jell(self.g, prec_bits_to_words(precision))) - - - ########################################### - # 6: Functions related to NUMBER FIELDS - ########################################### - def bnfcertify(self): - r""" - ``bnf`` being as output by ``bnfinit``, checks whether the result is - correct, i.e. whether the calculation of the contents of ``self`` - are correct without assuming the Generalized Riemann Hypothesis. - If it is correct, the answer is 1. If not, the program may output - some error message or loop indefinitely. - - For more information about PARI and the Generalized Riemann - Hypothesis, see [PariUsers], page 120. - - REFERENCES: - - .. [PariUsers] User's Guide to PARI/GP, - http://pari.math.u-bordeaux.fr/pub/pari/manuals/2.7.0/users.pdf - """ - pari_catch_sig_on() - n = bnfcertify(self.g) - pari_catch_sig_off() - return n - - def bnfunit(self): - pari_catch_sig_on() - return P.new_gen(bnf_get_fu(self.g)) - - def bnrclassno(self, I): - r""" - Return the order of the ray class group of self modulo ``I``. - - INPUT: - - - ``self``: a pari "BNF" object representing a number field - - ``I``: a pari "BID" object representing an ideal of self - - OUTPUT: integer - - TESTS:: - - sage: K. = QuadraticField(-23) - sage: p = K.primes_above(3)[0] - sage: K.pari_bnf().bnrclassno(p._pari_bid_()) - 3 - """ - cdef gen t0 = objtogen(I) - pari_catch_sig_on() - return P.new_gen(bnrclassno(self.g, t0.g)) - - def _eltabstorel(self, x): - """ - Return the relative number field element corresponding to `x`. - - The result is a ``t_POLMOD`` with ``t_POLMOD`` coefficients. - - .. WARNING:: - - This is a low-level version of :meth:`rnfeltabstorel` that - only needs the output of :meth:`_nf_rnfeq`, not a full - PARI ``rnf`` structure. This method may raise errors or - return undefined results if called with invalid arguments. - - TESTS:: - - sage: K = pari('y^2 + 1').nfinit() - sage: rnfeq = K._nf_rnfeq(x^2 + 2) - sage: f_abs = rnfeq[0]; f_abs - x^4 + 6*x^2 + 1 - sage: x_rel = rnfeq._eltabstorel(x); x_rel - Mod(x + Mod(-y, y^2 + 1), x^2 + 2) - sage: f_abs(x_rel) - Mod(0, x^2 + 2) - - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(eltabstorel(self.g, t0.g)) - - def _eltabstorel_lift(self, x): - """ - Return the relative number field element corresponding to `x`. - - The result is a ``t_POL`` with ``t_POLMOD`` coefficients. - - .. WARNING:: - - This is a low-level version of :meth:`rnfeltabstorel` that - only needs the output of :meth:`_nf_rnfeq`, not a full - PARI ``rnf`` structure. This method may raise errors or - return undefined results if called with invalid arguments. - - TESTS:: - - sage: K = pari('y^2 + 1').nfinit() - sage: rnfeq = K._nf_rnfeq(x^2 + 2) - sage: rnfeq._eltabstorel_lift(x) - x + Mod(-y, y^2 + 1) - - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(eltabstorel_lift(self.g, t0.g)) - - def _eltreltoabs(self, x): - """ - Return the absolute number field element corresponding to `x`. - - The result is a ``t_POL``. - - .. WARNING:: - - This is a low-level version of :meth:`rnfeltreltoabs` that - only needs the output of :meth:`_nf_rnfeq`, not a full - PARI ``rnf`` structure. This method may raise errors or - return undefined results if called with invalid arguments. - - TESTS:: - - sage: K = pari('y^2 + 1').nfinit() - sage: rnfeq = K._nf_rnfeq(x^2 + 2) - sage: rnfeq._eltreltoabs(x) - 1/2*x^3 + 7/2*x - sage: rnfeq._eltreltoabs('y') - 1/2*x^3 + 5/2*x - - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(eltreltoabs(self.g, t0.g)) - - def galoisinit(self, den=None): - """ - Calculate the Galois group of ``self``. - - This wraps the `galoisinit`_ function from PARI. - - INPUT: - - - ``self`` -- A number field or a polynomial. - - - ``den`` -- If set, this must be a multiple of the least - common denominator of the automorphisms, expressed as - polynomials in a root of the defining polynomial. - - OUTPUT: - - An eight-tuple, represented as a GEN object, - with details about the Galois group of the number field. - For details see `the PARI manual `_. - Note that the element indices in Sage and PARI are - 0-based and 1-based, respectively. - - EXAMPLES:: - - sage: P = pari(x^6 + 108) - sage: G = P.galoisinit() - sage: G[0] == P - True - sage: len(G[5]) == prod(G[7]) - True - - .. _galoisinit: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoisinit - """ - cdef gen t0 - if den is None: - pari_catch_sig_on() - return P.new_gen(galoisinit(self.g, NULL)) - else: - t0 = objtogen(den) - pari_catch_sig_on() - return P.new_gen(galoisinit(self.g, t0.g)) - - def galoispermtopol(self, perm): - """ - Return the polynomial defining the Galois automorphism ``perm``. - - This wraps the `galoispermtopol`_ function from PARI. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`. - - - ``perm`` -- A permutation from that group, - or a vector or matrix of such permutations. - - OUTPUT: - - The defining polynomial of the specified automorphism. - - EXAMPLES:: - - sage: G = pari(x^6 + 108).galoisinit() - sage: G.galoispermtopol(G[5]) - [x, 1/12*x^4 - 1/2*x, -1/12*x^4 - 1/2*x, 1/12*x^4 + 1/2*x, -1/12*x^4 + 1/2*x, -x] - sage: G.galoispermtopol(G[5][1]) - 1/12*x^4 - 1/2*x - sage: G.galoispermtopol(G[5][1:4]) - [1/12*x^4 - 1/2*x, -1/12*x^4 - 1/2*x, 1/12*x^4 + 1/2*x] - - .. _galoispermtopol: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoispermtopol - """ - cdef gen t0 = objtogen(perm) - pari_catch_sig_on() - return P.new_gen(galoispermtopol(self.g, t0.g)) - - def galoisfixedfield(self, perm, long flag=0, v=-1): - """ - Compute the fixed field of the Galois group ``self``. - - This wraps the `galoisfixedfield`_ function from PARI. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`. - - - ``perm`` -- An element of a Galois group, a vector of such - elements, or a subgroup generated by :meth:`galoissubgroups`. - - - ``flag`` -- Amount of data to include in output (see below). - - - ``v`` -- Name of the second variable to use (default: ``'y'``). - - OUTPUT: - - This depends on the value of ``flag``: - - - ``flag = 0`` -- A two-element tuple consisting of the defining - polynomial of the fixed field and a description of its roots - modulo the primes used in the group. - - - ``flag = 1`` -- Just the polynomial. - - - ``flag = 2`` -- A third tuple element will describe the - factorization of the original polynomial, using the variable - indicated by ``v`` to stand for a root of the polynomial - from the first tuple element. - - EXAMPLES:: - - sage: G = pari(x^4 + 1).galoisinit() - sage: G.galoisfixedfield(G[5][1], flag=2) - [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[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 - """ - cdef gen t0 = objtogen(perm) - pari_catch_sig_on() - return P.new_gen(galoisfixedfield(self.g, t0.g, flag, P.get_var(v))) - - def galoissubfields(self, long flag=0, v=-1): - """ - List all subfields of the Galois group ``self``. - - This wraps the `galoissubfields`_ function from PARI. - - This method is essentially the same as applying - :meth:`galoisfixedfield` to each group returned by - :meth:`galoissubgroups`. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`. - - - ``flag`` -- Has the same meaning as in :meth:`galoisfixedfield`. - - - ``v`` -- Has the same meaning as in :meth:`galoisfixedfield`. - - OUTPUT: - - A vector of all subfields of this group. Each entry is as - described in the :meth:`galoisfixedfield` method. - - EXAMPLES:: - - sage: G = pari(x^6 + 108).galoisinit() - 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')[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 - """ - pari_catch_sig_on() - return P.new_gen(galoissubfields(self.g, flag, P.get_var(v))) - - def galoissubgroups(self): - """ - List all subgroups of the Galois group ``self``. - - This wraps the `galoissubgroups`_ function from PARI. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`, - or a subgroup thereof as returned by :meth:`galoissubgroups`. - - OUTPUT: - - A vector of all subgroups of this group. - Each subgroup is described as a two-tuple, - with the subgroup generators as first element - and the orders of these generators as second element. - - EXAMPLES:: - - sage: G = pari(x^6 + 108).galoisinit() - sage: L = G.galoissubgroups() - sage: list(L[0][1]) - [3, 2] - - .. _galoissubgroups: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoissubgroups - """ - pari_catch_sig_on() - return P.new_gen(galoissubgroups(self.g)) - - def galoisisabelian(self, long flag=0): - """ - Decide whether ``self`` is an abelian group. - - This wraps the `galoisisabelian`_ function from PARI. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`, - or a subgroup thereof as returned by :meth:`galoissubgroups`. - - - ``flag`` -- Controls the details contained in the returned result. - - OUTPUT: - - This returns 0 if ``self`` is not an abelian group. If it is, - then the output depends on ``flag``: - - - ``flag = 0`` -- The HNF matrix of ``self`` over its generators - is returned. - - - ``flag = 1`` -- The return value is simply 1. - - EXAMPLES:: - - sage: G = pari(x^6 + 108).galoisinit() - sage: G.galoisisabelian() - 0 - sage: H = G.galoissubgroups()[2] - sage: H.galoisisabelian() - Mat(2) - sage: H.galoisisabelian(flag=1) - 1 - - .. _galoisisabelian: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoisisabelian - """ - pari_catch_sig_on() - return P.new_gen(galoisisabelian(self.g, flag)) - - def galoisisnormal(self, subgrp): - """ - Decide whether ``subgrp`` is a normal subgroup of ``self``. - - This wraps the `galoisisnormal`_ function from PARI. - - INPUT: - - - ``self`` -- A Galois group as generated by :meth:`galoisinit`, - or a subgroup thereof as returned by :meth:`galoissubgroups`. - - - ``subgrp`` -- A subgroup of ``self`` as returned by - :meth:`galoissubgroups`. - - OUTPUT: - - One if ``subgrp`` is a subgroup of ``self``, zero otherwise. - - EXAMPLES:: - - sage: G = pari(x^6 + 108).galoisinit() - sage: L = G.galoissubgroups() - sage: G.galoisisnormal(L[0]) - 1 - sage: G.galoisisnormal(L[2]) - 0 - - .. _galoisisnormal: http://pari.math.u-bordeaux.fr/dochtml/html.stable/Functions_related_to_general_number_fields.html#galoisisnormal - """ - cdef gen t0 = objtogen(subgrp) - pari_catch_sig_on() - v = galoisisnormal(self.g, t0.g) - P.clear_stack() - return v - - def idealchinese(self, x, y): - """ - Chinese Remainder Theorem over number fields. - - INPUT: - - - ``x`` -- prime ideal factorization - - ``y`` -- vector of elements - - OUTPUT: - - An element b in the ambient number field ``self`` such that - `v_p(b-y_p) \ge v_p(x)` for all prime ideals `p` dividing `x`, - and `v_p(b) \ge 0` for all other `p`. - - EXAMPLES:: - - sage: F = QuadraticField(5, 'alpha') - sage: nf = F._pari_() - sage: P = F.ideal(F.gen()) - sage: Q = F.ideal(2) - sage: moduli = pari.matrix(2,2,[P.pari_prime(),4,Q.pari_prime(),4]) - sage: residues = pari.vector(2,[0,1]) - sage: b = F(nf.idealchinese(moduli,residues)) - sage: b.valuation(P) >= 4 - True - sage: (b-1).valuation(Q) >= 2 - True - """ - cdef gen tx = objtogen(x) - cdef gen ty = objtogen(y) - pari_catch_sig_on() - return P.new_gen(idealchinese(self.g, tx.g, ty.g)) - - def idealcoprime(self, x, y): - """ - Given two integral ideals x and y of a pari number field self, - return an element a of the field (expressed in the integral - basis of self) such that a*x is an integral ideal coprime to - y. - - EXAMPLES:: - - sage: F = NumberField(x^3-2, 'alpha') - sage: nf = F._pari_() - sage: x = pari('[1, -1, 2]~') - sage: y = pari('[1, -1, 3]~') - sage: nf.idealcoprime(x, y) - [1, 0, 0]~ - - sage: y = pari('[2, -2, 4]~') - sage: nf.idealcoprime(x, y) - [5/43, 9/43, -1/43]~ - """ - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(idealcoprime(self.g, t0.g, t1.g)) - - def idealintersection(self, x, y): - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(idealintersect(self.g, t0.g, t1.g)) - - def ideallist(self, long bound, long flag = 4): - """ - Vector of vectors `L` of all idealstar of all ideals of `norm <= bound`. - - The binary digits of flag mean: - - - 1: give generators; - - 2: add units; - - 4: (default) give only the ideals and not the bid. - - EXAMPLES:: - - sage: R. = PolynomialRing(QQ) - sage: K. = NumberField(x^2 + 1) - sage: L = K.pari_nf().ideallist(100) - - Now we have our list `L`. Entry `L[n-1]` contains all ideals of - norm `n`:: - - sage: L[0] # One ideal of norm 1. - [[1, 0; 0, 1]] - sage: L[64] # 4 ideals of norm 65. - [[65, 8; 0, 1], [65, 47; 0, 1], [65, 18; 0, 1], [65, 57; 0, 1]] - """ - pari_catch_sig_on() - return P.new_gen(ideallist0(self.g, bound, flag)) - - def ideallog(self, x, bid): - """ - Return the discrete logarithm of the unit x in (ring of integers)/bid. - - INPUT: - - - ``self`` - a pari number field - - - ``bid`` - a big ideal structure (corresponding to an ideal I - of self) output by idealstar - - - ``x`` - an element of self with valuation zero at all - primes dividing I - - OUTPUT: - - - the discrete logarithm of x on the generators given in bid[2] - - EXAMPLE:: - - sage: F = NumberField(x^3-2, 'alpha') - sage: nf = F._pari_() - sage: I = pari('[1, -1, 2]~') - sage: bid = nf.idealstar(I) - sage: x = pari('5') - sage: nf.ideallog(x, bid) - [25]~ - """ - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(bid) - pari_catch_sig_on() - return P.new_gen(ideallog(self.g, t0.g, t1.g)) - - def idealprimedec(nf, p): - """ - Prime ideal decomposition of the prime number `p` in the number - field `nf` as a vector of 5 component vectors `[p,a,e,f,b]` - representing the prime ideals `p O_K + a O_K`, `e` ,`f` as usual, - `a` as vector of components on the integral basis, `b` Lenstra's - constant. - - EXAMPLES:: - - sage: K. = QuadraticField(-1) - sage: F = pari(K).idealprimedec(5); F - [[5, [-2, 1]~, 1, 1, [2, -1; 1, 2]], [5, [2, 1]~, 1, 1, [-2, -1; 1, -2]]] - sage: F[0].pr_get_p() - 5 - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(idealprimedec(nf.g, t0.g)) - - def idealstar(self, I, long flag=1): - """ - Return the big ideal (bid) structure of modulus I. - - INPUT: - - - ``self`` - a pari number field - - - ``I`` -- an ideal of self, or a row vector whose first - component is an ideal and whose second component - is a row vector of r_1 0 or 1. - - - ``flag`` - determines the amount of computation and the shape - of the output: - - - ``1`` (default): return a bid structure without - generators - - - ``2``: return a bid structure with generators (slower) - - - ``0`` (deprecated): only outputs units of (ring of integers/I) - as an abelian group, i.e as a 3-component - vector [h,d,g]: h is the order, d is the vector - of SNF cyclic components and g the corresponding - generators. This flag is deprecated: it is in - fact slightly faster to compute a true bid - structure, which contains much more information. - - EXAMPLE:: - - sage: F = NumberField(x^3-2, 'alpha') - sage: nf = F._pari_() - sage: I = pari('[1, -1, 2]~') - sage: nf.idealstar(I) - [[[43, 9, 5; 0, 1, 0; 0, 0, 1], [0]], [42, [42]], Mat([[43, [9, 1, 0]~, 1, 1, [-5, 2, -18; -9, -5, 2; 1, -9, -5]], 1]), [[[[42], [3], [3], [Vecsmall([])], 1]], [[], [], []]], Mat(1)] - """ - cdef gen t0 = objtogen(I) - pari_catch_sig_on() - return P.new_gen(idealstar0(self.g, t0.g, flag)) - - def idealval(self, x, p): - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(p) - pari_catch_sig_on() - v = idealval(self.g, t0.g, t1.g) - pari_catch_sig_off() - return v - - def elementval(self, x, p): - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(p) - pari_catch_sig_on() - v = nfval(self.g, t0.g, t1.g) - pari_catch_sig_off() - return v - - def nfbasis(self, long flag=0, fa=None): - """ - Integral basis of the field `\QQ[a]`, where ``a`` is a root of - the polynomial x. - - INPUT: - - - ``flag``: if set to 1 and ``fa`` is not given: assume that no - square of a prime > 500000 divides the discriminant of ``x``. - - - ``fa``: If present, encodes a subset of primes at which to - check for maximality. This must be one of the three following - things: - - - an integer: check all primes up to ``fa`` using trial - division. - - - a vector: a list of primes to check. - - - a matrix: a partial factorization of the discriminant - of ``x``. - - .. NOTE:: - - In earlier versions of Sage, other bits in ``flag`` were - defined but these are now simply ignored. - - EXAMPLES:: - - sage: pari('x^3 - 17').nfbasis() - [1, x, 1/3*x^2 - 1/3*x + 1/3] - - We test ``flag`` = 1, noting it gives a wrong result when the - discriminant (-4 * `p`^2 * `q` in the example below) has a big square - factor:: - - sage: p = next_prime(10^10); q = next_prime(p) - sage: x = polygen(QQ); f = x^2 + p^2*q - sage: pari(f).nfbasis(1) # Wrong result - [1, x] - sage: pari(f).nfbasis() # Correct result - [1, 1/10000000019*x] - sage: pari(f).nfbasis(fa=10^6) # Check primes up to 10^6: wrong result - [1, x] - sage: pari(f).nfbasis(fa="[2,2; %s,2]"%p) # Correct result and faster - [1, 1/10000000019*x] - sage: pari(f).nfbasis(fa=[2,p]) # Equivalent with the above - [1, 1/10000000019*x] - """ - if flag < 0 or flag > 1: - flag = flag & 1 - from sage.misc.superseded import deprecation - deprecation(15767, 'In nfbasis(), flag must be 0 or 1, other bits are deprecated and ignored') - - cdef gen t0 - cdef GEN g0 - if fa is not None: - t0 = objtogen(fa) - g0 = t0.g - elif flag: - g0 = utoi(500000) - else: - g0 = NULL - pari_catch_sig_on() - return P.new_gen(nfbasis(self.g, NULL, g0)) - - def nfbasis_d(self, long flag=0, fa=None): - """ - Like :meth:`nfbasis`, but return a tuple ``(B, D)`` where `B` - is the integral basis and `D` the discriminant. - - EXAMPLES:: - - sage: F = NumberField(x^3-2,'alpha') - sage: F._pari_()[0].nfbasis_d() - ([1, y, y^2], -108) - - :: - - sage: G = NumberField(x^5-11,'beta') - sage: G._pari_()[0].nfbasis_d() - ([1, y, y^2, y^3, y^4], 45753125) - - :: - - sage: pari([-2,0,0,1]).Polrev().nfbasis_d() - ([1, x, x^2], -108) - """ - if flag < 0 or flag > 1: - flag = flag & 1 - from sage.misc.superseded import deprecation - deprecation(15767, 'In nfbasis_d(), flag must be 0 or 1, other bits are deprecated and ignored') - - cdef gen t0 - cdef GEN g0 - cdef GEN disc - if fa is not None: - t0 = objtogen(fa) - g0 = t0.g - elif flag & 1: - g0 = utoi(500000) - else: - g0 = NULL - pari_catch_sig_on() - B = P.new_gen_noclear(nfbasis(self.g, &disc, g0)) - D = P.new_gen(disc) - return B, D - - def nfbasistoalg(nf, x): - r""" - Transforms the column vector ``x`` on the integral basis into an - algebraic number. - - INPUT: - - - ``nf`` -- a number field - - ``x`` -- a column of rational numbers of length equal to the - degree of ``nf`` or a single rational number - - OUTPUT: - - - A POLMOD representing the element of ``nf`` whose coordinates - are ``x`` in the Z-basis of ``nf``. - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: K. = NumberField(x^3 - 17) - sage: Kpari = K.pari_nf() - sage: Kpari.getattr('zk') - [1, 1/3*y^2 - 1/3*y + 1/3, y] - sage: Kpari.nfbasistoalg(42) - Mod(42, y^3 - 17) - sage: Kpari.nfbasistoalg("[3/2, -5, 0]~") - Mod(-5/3*y^2 + 5/3*y - 1/6, y^3 - 17) - sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") - -5/3*y^2 + 5/3*y - 1/6 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(basistoalg(nf.g, t0.g)) - - def nfbasistoalg_lift(nf, x): - r""" - Transforms the column vector ``x`` on the integral basis into a - polynomial representing the algebraic number. - - INPUT: - - - ``nf`` -- a number field - - ``x`` -- a column of rational numbers of length equal to the - degree of ``nf`` or a single rational number - - OUTPUT: - - - ``nf.nfbasistoalg(x).lift()`` - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: K. = NumberField(x^3 - 17) - sage: Kpari = K.pari_nf() - sage: Kpari.getattr('zk') - [1, 1/3*y^2 - 1/3*y + 1/3, y] - sage: Kpari.nfbasistoalg_lift(42) - 42 - sage: Kpari.nfbasistoalg_lift("[3/2, -5, 0]~") - -5/3*y^2 + 5/3*y - 1/6 - sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") - -5/3*y^2 + 5/3*y - 1/6 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(gel(basistoalg(nf.g, t0.g), 2)) - - def nfdisc(self, long flag=-1, p=None): - """ - nfdisc(x): Return the discriminant of the number field defined over - QQ by x. - - EXAMPLES:: - - sage: F = NumberField(x^3-2,'alpha') - sage: F._pari_()[0].nfdisc() - -108 - - :: - - sage: G = NumberField(x^5-11,'beta') - sage: G._pari_()[0].nfdisc() - 45753125 - - :: - - sage: f = x^3-2 - sage: f._pari_() - x^3 - 2 - sage: f._pari_().nfdisc() - -108 - """ - if flag != -1 or p is not None: - from sage.misc.superseded import deprecation - deprecation(16997, 'The flag and p arguments to nfdisc() are deprecated and not used anymore') - pari_catch_sig_on() - return P.new_gen(nfdisc(self.g)) - - def nfeltdiveuc(self, x, y): - """ - Given `x` and `y` in the number field ``self``, return `q` such - that `x - q y` is "small". - - EXAMPLES:: - - sage: k. = NumberField(x^2 + 5) - sage: x = 10 - sage: y = a + 1 - sage: pari(k).nfeltdiveuc(pari(x), pari(y)) - [2, -2]~ - """ - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(nfdiveuc(self.g, t0.g, t1.g)) - - def nfeltreduce(self, x, I): - """ - Given an ideal I in Hermite normal form and an element x of the pari - number field self, finds an element r in self such that x-r belongs - to the ideal and r is small. - - EXAMPLES:: - - sage: k. = NumberField(x^2 + 5) - sage: I = k.ideal(a) - sage: kp = pari(k) - sage: kp.nfeltreduce(12, I.pari_hnf()) - [2, 0]~ - sage: 12 - k(kp.nfeltreduce(12, I.pari_hnf())) in I - True - """ - cdef gen t0 = objtogen(x) - cdef gen t1 = objtogen(I) - pari_catch_sig_on() - return P.new_gen(nfreduce(self.g, t0.g, t1.g)) - - def nfgenerator(self): - f = self[0] - x = f.variable() - return x.Mod(f) - - def nfhilbert(self, a, b, p=None): - """ - nfhilbert(nf,a,b,{p}): if p is omitted, global Hilbert symbol (a,b) - in nf, that is 1 if X^2-aY^2-bZ^2 has a non-trivial solution (X,Y,Z) - in nf, -1 otherwise. Otherwise compute the local symbol modulo the - prime ideal p. - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: K. = NumberField(x^3 - x + 1) - sage: pari(K).nfhilbert(t, t + 2) - -1 - sage: P = K.ideal(t^2 + t - 2) # Prime ideal above 5 - sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) - -1 - sage: P = K.ideal(t^2 + 3*t - 1) # Prime ideal above 23, ramified - sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) - 1 - """ - cdef gen t0 = objtogen(a) - cdef gen t1 = objtogen(b) - cdef gen t2 - if p: - t2 = objtogen(p) - pari_catch_sig_on() - r = nfhilbert0(self.g, t0.g, t1.g, t2.g) - else: - pari_catch_sig_on() - r = nfhilbert(self.g, t0.g, t1.g) - pari_catch_sig_off() - return r - - def nfhnf(self,x): - """ - nfhnf(nf,x) : given a pseudo-matrix (A, I) or an integral pseudo-matrix (A,I,J), finds a - pseudo-basis in Hermite normal form of the module it generates. - - A pseudo-matrix is a 2-component row vector (A, I) where A is a relative m x n matrix and - I an ideal list of length n. An integral pseudo-matrix is a 3-component row vector (A, I, J). - - .. NOTE:: - - The definition of a pseudo-basis ([Cohen]_): - Let M be a finitely generated, torsion-free R-module, and set V = KM. If `\mathfrak{a}_i` are - fractional ideals of R and `w_i` are elements of V, we say that - `(w_i, \mathfrak{a}_k)_{1 \leq i \leq k}` - is a pseudo-basis of M if - `M = \mathfrak{a}_1 w_1 \oplus \cdots \oplus \mathfrak{a}_k w_k.` - - REFERENCES: - - .. [Cohen] Cohen, "Advanced Topics in Computational Number Theory" - - EXAMPLES:: - - sage: F. = NumberField(x^2-x-1) - sage: Fp = pari(F) - sage: A = matrix(F,[[1,2,a,3],[3,0,a+2,0],[0,0,a,2],[3+a,a,0,1]]) - sage: I = [F.ideal(-2*a+1),F.ideal(7), F.ideal(3),F.ideal(1)] - sage: Fp.nfhnf([pari(A),[pari(P) for P in I]]) - [[1, [-969/5, -1/15]~, [15, -2]~, [-1938, -3]~; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1], [[3997, 1911; 0, 7], [15, 6; 0, 3], 1, 1]] - sage: K. = NumberField(x^3-2) - sage: Kp = pari(K) - sage: A = matrix(K,[[1,0,0,5*b],[1,2*b^2,b,57],[0,2,1,b^2-3],[2,0,0,b]]) - sage: I = [K.ideal(2),K.ideal(3+b^2),K.ideal(1),K.ideal(1)] - sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) - [[1, -225, 72, -31; 0, 1, [0, -1, 0]~, [0, 0, -1/2]~; 0, 0, 1, [0, 0, -1/2]~; 0, 0, 0, 1], [[1116, 756, 612; 0, 18, 0; 0, 0, 18], 2, 1, [2, 0, 0; 0, 1, 0; 0, 0, 1]]] - - An example where the ring of integers of the number field is not a PID:: - - sage: K. = NumberField(x^2+5) - sage: Kp = pari(K) - sage: A = matrix(K,[[1,0,0,5*b],[1,2*b^2,b,57],[0,2,1,b^2-3],[2,0,0,b]]) - sage: I = [K.ideal(2),K.ideal(3+b^2),K.ideal(1),K.ideal(1)] - sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) - [[1, [15, 6]~, [0, -54]~, [113, 72]~; 0, 1, [-4, -1]~, [0, -1]~; 0, 0, 1, 0; 0, 0, 0, 1], [[360, 180; 0, 180], [6, 4; 0, 2], 1, 1]] - sage: A = matrix(K,[[1,0,0,5*b],[1,2*b,b,57],[0,2,1,b-3],[2,0,b,b]]) - sage: I = [K.ideal(2).factor()[0][0],K.ideal(3+b),K.ideal(1),K.ideal(1)] - sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) - [[1, [7605, 4]~, [5610, 5]~, [7913, -6]~; 0, 1, 0, -1; 0, 0, 1, 0; 0, 0, 0, 1], [[19320, 13720; 0, 56], [2, 1; 0, 1], 1, 1]] - - AUTHORS: - - - Aly Deines (2012-09-19) - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(nfhnf(self.g, t0.g)) - - def nfinit(self, long flag=0, unsigned long precision=0): - """ - nfinit(pol, {flag=0}): ``pol`` being a nonconstant irreducible - polynomial, gives a vector containing all the data necessary for PARI - to compute in this number field. - - ``flag`` is optional and can be set to: - - 0: default - - 1: do not compute different - - 2: first use polred to find a simpler polynomial - - 3: outputs a two-element vector [nf,Mod(a,P)], where nf is as in 2 - and Mod(a,P) is a polmod equal to Mod(x,pol) and P=nf.pol - - EXAMPLES:: - - sage: pari('x^3 - 17').nfinit() - [x^3 - 17, [1, 1], -867, 3, [[1, 1.68006914259990, 2.57128159065824; 1, -0.340034571299952 - 2.65083754153991*I, -1.28564079532912 + 2.22679517779329*I], [1, 1.68006914259990, 2.57128159065824; 1, -2.99087211283986, 0.941154382464174; 1, 2.31080297023995, -3.51243597312241], [1, 2, 3; 1, -3, 1; 1, 2, -4], [3, 1, 0; 1, -11, 17; 0, 17, 0], [51, 0, 16; 0, 17, 3; 0, 0, 1], [17, 0, -1; 0, 0, 3; -1, 3, 2], [51, [-17, 6, -1; 0, -18, 3; 1, 0, -16]], [3, 17]], [2.57128159065824, -1.28564079532912 + 2.22679517779329*I], [1, 1/3*x^2 - 1/3*x + 1/3, x], [1, 0, -1; 0, 0, 3; 0, 1, 1], [1, 0, 0, 0, -4, 6, 0, 6, -1; 0, 1, 0, 1, 1, -1, 0, -1, 3; 0, 0, 1, 0, 2, 0, 1, 0, 1]] - - TESTS:: - - sage: pari('x^2 + 10^100 + 1').nfinit() - [...] - sage: pari('1.0').nfinit() - Traceback (most recent call last): - ... - PariError: incorrect type in checknf [please apply nfinit()] (t_REAL) - """ - pari_catch_sig_on() - return P.new_gen(nfinit0(self.g, flag, prec_bits_to_words(precision))) - - def nfisisom(self, other): - """ - nfisisom(x, y): Determine if the number fields defined by x and y - are isomorphic. According to the PARI documentation, this is much - faster if at least one of x or y is a number field. If they are - isomorphic, it returns an embedding for the generators. If not, - returns 0. - - EXAMPLES:: - - sage: F = NumberField(x^3-2,'alpha') - sage: G = NumberField(x^3-2,'beta') - sage: F._pari_().nfisisom(G._pari_()) - [y] - - :: - - sage: GG = NumberField(x^3-4,'gamma') - sage: F._pari_().nfisisom(GG._pari_()) - [1/2*y^2] - - :: - - sage: F._pari_().nfisisom(GG.pari_nf()) - [1/2*y^2] - - :: - - sage: F.pari_nf().nfisisom(GG._pari_()[0]) - [y^2] - - :: - - sage: H = NumberField(x^2-2,'alpha') - sage: F._pari_().nfisisom(H._pari_()) - 0 - - TESTS: - - This method converts its second argument (:trac:`18728`):: - - sage: K. = NumberField(x^2 + x + 1) - sage: L. = NumberField(x^2 + 3) - sage: pari(K).nfisisom(L) - [-1/2*y - 1/2, 1/2*y - 1/2] - - """ - cdef gen t0 = objtogen(other) - pari_catch_sig_on() - return P.new_gen(nfisisom(self.g, t0.g)) - - def nfrootsof1(self): - """ - nf.nfrootsof1() - - number of roots of unity and primitive root of unity in the number - field nf. - - EXAMPLES:: - - sage: nf = pari('x^2 + 1').nfinit() - sage: nf.nfrootsof1() - [4, x] - """ - pari_catch_sig_on() - return P.new_gen(rootsof1(self.g)) - - def nfsubfields(self, long d=0): - """ - Find all subfields of degree d of number field nf (all subfields if - d is null or omitted). Result is a vector of subfields, each being - given by [g,h], where g is an absolute equation and h expresses one - of the roots of g in terms of the root x of the polynomial defining - nf. - - INPUT: - - - - ``self`` - nf number field - - - ``d`` - C long integer - """ - pari_catch_sig_on() - return P.new_gen(nfsubfields(self.g, d)) - - def _nf_rnfeq(self, relpol): - """ - Return data for converting number field elements between - absolute and relative representation. - - .. NOTE:: - - The output of this method is suitable for the methods - :meth:`_eltabstorel`, :meth:`_eltabstorel_lift` and - :meth:`_eltreltoabs`. - - TESTS:: - - sage: K = pari('y^2 + 1').nfinit() - sage: K._nf_rnfeq(x^2 + 2) - [x^4 + 6*x^2 + 1, 1/2*x^3 + 5/2*x, -1, y^2 + 1, x^2 + 2] - - """ - cdef gen t0 = objtogen(relpol) - pari_catch_sig_on() - return P.new_gen(nf_rnfeq(self.g, t0.g)) - - def rnfidealdown(self, x): - r""" - rnfidealdown(rnf,x): finds the intersection of the ideal x with the base field. - - EXAMPLES:: - - sage: x = ZZ['xx1'].0; pari(x) - xx1 - sage: y = ZZ['yy1'].0; pari(y) - yy1 - sage: nf = pari(y^2 - 6*y + 24).nfinit() - sage: rnf = nf.rnfinit(x^2 - pari(y)) - - This is the relative HNF of the inert ideal (2) in rnf:: - - sage: P = pari('[[[1, 0]~, [0, 0]~; [0, 0]~, [1, 0]~], [[2, 0; 0, 2], [2, 0; 0, 1/2]]]') - - And this is the inert ideal (2) in nf: - - sage: rnf.rnfidealdown(P) - 2 - """ - cdef gen t0 = objtogen(x) - pari_catch_sig_on() - return P.new_gen(rnfidealdown(self.g, t0.g)) - - def rnfinit(self, poly): - """ - EXAMPLES: We construct a relative number field. - - :: - - sage: f = pari('y^3+y+1') - sage: K = f.nfinit() - sage: x = pari('x'); y = pari('y') - sage: g = x^5 - x^2 + y - sage: L = K.rnfinit(g) - """ - cdef gen t0 = objtogen(poly) - pari_catch_sig_on() - return P.new_gen(rnfinit(self.g, t0.g)) - - def quadhilbert(self): - r""" - Returns a polynomial over `\QQ` whose roots generate the - Hilbert class field of the quadratic field of discriminant - ``self`` (which must be fundamental). - - EXAMPLES:: - - sage: pari(-23).quadhilbert() - x^3 - x^2 + 1 - sage: pari(145).quadhilbert() - x^4 - 6*x^2 - 5*x - 1 - sage: pari(-12).quadhilbert() # Not fundamental - Traceback (most recent call last): - ... - PariError: domain error in quadray: isfundamental(D) = 0 - """ - pari_catch_sig_on() - # Precision argument is only used for real quadratic extensions - # and will be automatically increased by PARI if needed. - return P.new_gen(quadhilbert(self.g, DEFAULTPREC)) - - - ################################################## - # 7: POLYNOMIALS and power series - ################################################## - def reverse(self): - """ - Return the polynomial obtained by reversing the coefficients of - this polynomial. - """ - return self.Vec().Polrev() - - def content(self): - """ - Greatest common divisor of all the components of ``self``. - - EXAMPLES:: - - sage: R. = PolynomialRing(ZZ) - sage: pari(2*x^2 + 2).content() - 2 - sage: pari("4*x^3 - 2*x/3 + 2/5").content() - 2/15 - """ - pari_catch_sig_on() - return P.new_gen(content(self.g)) - - def eval(self, *args, **kwds): - """ - Evaluate ``self`` with the given arguments. - - This is currently implemented in 3 cases: - - - univariate polynomials, rational functions, power series and - Laurent series (using a single unnamed argument or keyword - arguments), - - any PARI object supporting the PARI function ``substvec`` - (in particular, multivariate polynomials) using keyword - arguments, - - objects of type ``t_CLOSURE`` (functions in GP bytecode form) - using unnamed arguments. - - In no case is mixing unnamed and keyword arguments allowed. - - EXAMPLES:: - - sage: f = pari('x^2 + 1') - sage: f.type() - 't_POL' - sage: f.eval(I) - 0 - sage: f.eval(x=2) - 5 - sage: (1/f).eval(x=1) - 1/2 - - The notation ``f(x)`` is an alternative for ``f.eval(x)``:: - - sage: f(3) == f.eval(3) - True - - Evaluating power series:: - - sage: f = pari('1 + x + x^3 + O(x^7)') - sage: f(2*pari('y')^2) - 1 + 2*y^2 + 8*y^6 + O(y^14) - - Substituting zero is sometimes possible, and trying to do so - in illegal cases can raise various errors:: - - sage: pari('1 + O(x^3)').eval(0) - 1 - sage: pari('1/x').eval(0) - Traceback (most recent call last): - ... - PariError: impossible inverse in gdiv: 0 - sage: pari('1/x + O(x^2)').eval(0) - Traceback (most recent call last): - ... - ZeroDivisionError: substituting 0 in Laurent series with negative valuation - sage: pari('1/x + O(x^2)').eval(pari('O(x^3)')) - Traceback (most recent call last): - ... - PariError: impossible inverse in gdiv: O(x^3) - sage: pari('O(x^0)').eval(0) - Traceback (most recent call last): - ... - PariError: domain error in polcoeff: t_SER = O(x^0) - - Evaluating multivariate polynomials:: - - sage: f = pari('y^2 + x^3') - sage: f(1) # Dangerous, depends on PARI variable ordering - y^2 + 1 - sage: f(x=1) # Safe - y^2 + 1 - sage: f(y=1) - x^3 + 1 - sage: f(1, 2) - Traceback (most recent call last): - ... - TypeError: evaluating PARI t_POL takes exactly 1 argument (2 given) - sage: f(y='x', x='2*y') - x^2 + 8*y^3 - sage: f() - x^3 + y^2 - - It's not an error to substitute variables which do not appear:: - - sage: f.eval(z=37) - x^3 + y^2 - sage: pari(42).eval(t=0) - 42 - - We can define and evaluate closures as follows:: - - sage: T = pari('n -> n + 2') - sage: T.type() - 't_CLOSURE' - sage: T.eval(3) - 5 - - sage: T = pari('() -> 42') - sage: T() - 42 - - sage: pr = pari('s -> print(s)') - sage: pr.eval('"hello world"') - hello world - - sage: f = pari('myfunc(x,y) = x*y') - sage: f.eval(5, 6) - 30 - - Default arguments work, missing arguments are treated as zero - (like in GP):: - - sage: f = pari("(x, y, z=1.0) -> [x, y, z]") - sage: f(1, 2, 3) - [1, 2, 3] - sage: f(1, 2) - [1, 2, 1.00000000000000] - sage: f(1) - [1, 0, 1.00000000000000] - sage: f() - [0, 0, 1.00000000000000] - - Variadic closures are supported as well (:trac:`18623`):: - - sage: f = pari("(v[..])->length(v)") - sage: f('a', f) - 2 - sage: g = pari("(x,y,z[..])->[x,y,z]") - sage: g(), g(1), g(1,2), g(1,2,3), g(1,2,3,4) - ([0, 0, []], [1, 0, []], [1, 2, []], [1, 2, [3]], [1, 2, [3, 4]]) - - Using keyword arguments, we can substitute in more complicated - objects, for example a number field:: - - sage: K. = NumberField(x^2 + 1) - sage: nf = K._pari_() - sage: nf - [y^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-38 + 1.00000000000000*I]), [1, 1.00000000000000; 1, -1.00000000000000], [1, 1; 1, -1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]], []], [0.E-38 + 1.00000000000000*I], [1, y], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] - sage: nf(y='x') - [x^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-38 + 1.00000000000000*I]), [1, 1.00000000000000; 1, -1.00000000000000], [1, 1; 1, -1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]], []], [0.E-38 + 1.00000000000000*I], [1, x], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] - """ - cdef long t = typ(self.g) - cdef gen t0 - cdef GEN result - cdef long arity - cdef long nargs = len(args) - cdef long nkwds = len(kwds) - - # Closure must be evaluated using *args - if t == t_CLOSURE: - if nkwds > 0: - raise TypeError("cannot evaluate a PARI closure using keyword arguments") - if closure_is_variadic(self.g): - arity = closure_arity(self.g) - 1 - args = list(args[:arity]) + [0]*(arity-nargs) + [args[arity:]] - t0 = objtogen(args) - pari_catch_sig_on() - result = closure_callgenvec(self.g, t0.g) - if result == gnil: - P.clear_stack() - return None - return P.new_gen(result) - - # Evaluate univariate polynomials, rational functions and - # series using *args - if nargs > 0: - if nkwds > 0: - raise TypeError("mixing unnamed and keyword arguments not allowed when evaluating a PARI object") - if not (t == t_POL or t == t_RFRAC or t == t_SER): - raise TypeError("cannot evaluate PARI %s using unnamed arguments" % self.type()) - if nargs != 1: - raise TypeError("evaluating PARI %s takes exactly 1 argument (%d given)" - % (self.type(), nargs)) - - t0 = objtogen(args[0]) - pari_catch_sig_on() - if t == t_POL or t == t_RFRAC: - return P.new_gen(poleval(self.g, t0.g)) - else: # t == t_SER - if isexactzero(t0.g): - # Work around the fact that PARI currently doesn't - # support substituting exact 0 in a power series. - # We don't try to imitate this when using keyword - # arguments, and hope this will be fixed in a - # future PARI version. - if valp(self.g) < 0: - pari_catch_sig_off() - raise ZeroDivisionError('substituting 0 in Laurent series with negative valuation') - elif valp(self.g) == 0: - return P.new_gen(polcoeff0(self.g, 0, -1)) - else: - pari_catch_sig_off() - return P.PARI_ZERO - return P.new_gen(gsubst(self.g, varn(self.g), t0.g)) - - # Call substvec() using **kwds - vstr = kwds.keys() # Variables as Python strings - t0 = objtogen(kwds.values()) # Replacements - - pari_catch_sig_on() - cdef GEN v = cgetg(nkwds+1, t_VEC) # Variables as PARI polynomials - cdef long i - for i in range(nkwds): - set_gel(v, i+1, pol_x(P.get_var(vstr[i]))) - return P.new_gen(gsubstvec(self.g, v, t0.g)) - - - def __call__(self, *args, **kwds): - """ - Evaluate ``self`` with the given arguments. - - This has the same effect as :meth:`eval`. - - EXAMPLES:: - - sage: R. = GF(3)[] - sage: f = (x^2 + x + 1)._pari_() - sage: f.type() - 't_POL' - sage: f(2) - Mod(1, 3) - - TESTS:: - - sage: T = pari('n -> 1/n') - sage: T.type() - 't_CLOSURE' - sage: T(0) - Traceback (most recent call last): - ... - PariError: _/_: impossible inverse in gdiv: 0 - sage: pari('() -> 42')(1,2,3) - Traceback (most recent call last): - ... - PariError: too many parameters in user-defined function call - sage: pari('n -> n')(n=2) - Traceback (most recent call last): - ... - TypeError: cannot evaluate a PARI closure using keyword arguments - sage: pari('x + y')(4, y=1) - Traceback (most recent call last): - ... - TypeError: mixing unnamed and keyword arguments not allowed when evaluating a PARI object - sage: pari("12345")(4) - Traceback (most recent call last): - ... - TypeError: cannot evaluate PARI t_INT using unnamed arguments - """ - return self.eval(*args, **kwds) - - def factornf(self, t): - """ - Factorization of the polynomial ``self`` over the number field - defined by the polynomial ``t``. This does not require that `t` - is integral, nor that the discriminant of the number field can be - factored. - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: K. = NumberField(x^2 - 1/8) - sage: pari(x^2 - 2).factornf(K.pari_polynomial("a")) - [x + Mod(-a, a^2 - 2), 1; x + Mod(a, a^2 - 2), 1] - """ - cdef gen t0 = objtogen(t) - pari_catch_sig_on() - return P.new_gen(polfnf(self.g, t0.g)) - - def factorpadic(self, p, long r=20, long flag=-1): - """ - p-adic factorization of the polynomial ``pol`` to precision ``r``. - - EXAMPLES:: - - sage: x = polygen(QQ) - sage: pol = (x^2 - 1)^2 - sage: pari(pol).factorpadic(5) - [(1 + O(5^20))*x + (1 + O(5^20)), 2; (1 + O(5^20))*x + (4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + 4*5^11 + 4*5^12 + 4*5^13 + 4*5^14 + 4*5^15 + 4*5^16 + 4*5^17 + 4*5^18 + 4*5^19 + O(5^20)), 2] - sage: pari(pol).factorpadic(5,3) - [(1 + O(5^3))*x + (1 + O(5^3)), 2; (1 + O(5^3))*x + (4 + 4*5 + 4*5^2 + O(5^3)), 2] - """ - if flag != -1: - from sage.misc.superseded import deprecation - deprecation(16997, 'The flag argument to factorpadic() is deprecated and not used anymore') - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(factorpadic(self.g, t0.g, r)) - - def newtonpoly(self, p): - """ - x.newtonpoly(p): Newton polygon of polynomial x with respect to the - prime p. - - EXAMPLES:: - - sage: x = pari('y^8+6*y^6-27*y^5+1/9*y^2-y+1') - sage: x.newtonpoly(3) - [1, 1, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] - """ - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(newtonpoly(self.g, t0.g)) - - def polcoeff(self, long n, var=-1): - """ - EXAMPLES:: - - sage: f = pari("x^2 + y^3 + x*y") - sage: f - x^2 + y*x + y^3 - sage: f.polcoeff(1) - y - sage: f.polcoeff(3) - 0 - sage: f.polcoeff(3, "y") - 1 - sage: f.polcoeff(1, "y") - x - """ - pari_catch_sig_on() - return P.new_gen(polcoeff0(self.g, n, P.get_var(var))) - - def poldegree(self, var=-1): - """ - f.poldegree(var=x): Return the degree of this polynomial. - """ - pari_catch_sig_on() - n = poldegree(self.g, P.get_var(var)) - pari_catch_sig_off() - return n - - def poldisc(self, var=-1): - """ - Return the discriminant of this polynomial. - - EXAMPLES:: - - sage: pari("x^2 + 1").poldisc() - -4 - - Before :trac:`15654`, this used to take a very long time. - Now it takes much less than a second:: - - sage: pari.allocatemem(200000) - 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() - 2*3 + 3^4 + 2*3^6 + 3^7 + 2*3^8 + 2*3^9 + O(3^10) - """ - pari_catch_sig_on() - return P.new_gen(poldisc0(self.g, P.get_var(var))) - - def nfgaloisconj(self, long flag=0, denom=None, unsigned long precision=0): - r""" - Edited from the pari documentation: - - nfgaloisconj(nf): list of conjugates of a root of the - polynomial x=nf.pol in the same number field. - - Uses a combination of Allombert's algorithm and nfroots. - - EXAMPLES:: - - sage: x = QQ['x'].0; nf = pari(x^2 + 2).nfinit() - sage: nf.nfgaloisconj() - [-x, x]~ - sage: nf = pari(x^3 + 2).nfinit() - sage: nf.nfgaloisconj() - [x]~ - sage: nf = pari(x^4 + 2).nfinit() - sage: nf.nfgaloisconj() - [-x, x]~ - """ - cdef gen t0 - if denom is None: - pari_catch_sig_on() - return P.new_gen(galoisconj0(self.g, flag, NULL, prec_bits_to_words(precision))) - else: - t0 = objtogen(denom) - pari_catch_sig_on() - return P.new_gen(galoisconj0(self.g, flag, t0.g, prec_bits_to_words(precision))) - - def nfroots(self, poly): - r""" - Return the roots of `poly` in the number field self without - multiplicity. - - EXAMPLES:: - - sage: y = QQ['yy'].0; _ = pari(y) # pari has variable ordering rules - sage: x = QQ['zz'].0; nf = pari(x^2 + 2).nfinit() - sage: nf.nfroots(y^2 + 2) - [Mod(-zz, zz^2 + 2), Mod(zz, zz^2 + 2)] - sage: nf = pari(x^3 + 2).nfinit() - sage: nf.nfroots(y^3 + 2) - [Mod(zz, zz^3 + 2)] - sage: nf = pari(x^4 + 2).nfinit() - sage: nf.nfroots(y^4 + 2) - [Mod(-zz, zz^4 + 2), Mod(zz, zz^4 + 2)] - """ - cdef gen t0 = objtogen(poly) - pari_catch_sig_on() - return P.new_gen(nfroots(self.g, t0.g)) - - def polisirreducible(self): - """ - f.polisirreducible(): Returns True if f is an irreducible - non-constant polynomial, or False if f is reducible or constant. - """ - pari_catch_sig_on() - t = isirreducible(self.g) - P.clear_stack() - return t != 0 - - def polroots(self, long flag=-1, unsigned long precision=0): - """ - Complex roots of the given polynomial using Schonhage's method, - as modified by Gourdon. - """ - if flag != -1: - from sage.misc.superseded import deprecation - deprecation(16997, 'The flag argument to polroots() is deprecated and not used anymore') - pari_catch_sig_on() - return P.new_gen(cleanroots(self.g, prec_bits_to_words(precision))) - - def polrootspadicfast(self, p, r=20): - from sage.misc.superseded import deprecation - deprecation(16997, 'polrootspadicfast is deprecated, use polrootspadic or the direct PARI call ZpX_roots instead') - cdef gen t0 = objtogen(p) - pari_catch_sig_on() - return P.new_gen(rootpadic(self.g, t0.g, r)) - - polsturm_full = deprecated_function_alias(18203, gen_auto.polsturm) - - def serreverse(self): - """ - serreverse(f): reversion of the power series f. - - If f(t) is a series in t with valuation 1, find the series g(t) - such that g(f(t)) = t. - - EXAMPLES:: - - sage: f = pari('x+x^2+x^3+O(x^4)'); f - x + x^2 + x^3 + O(x^4) - sage: g = f.serreverse(); g - x - x^2 + x^3 + O(x^4) - sage: f.subst('x',g) - x + O(x^4) - sage: g.subst('x',f) - x + O(x^4) - """ - pari_catch_sig_on() - return P.new_gen(serreverse(self.g)) - - def rnfisnorm(self, T, long flag=0): - cdef gen t0 = objtogen(T) - pari_catch_sig_on() - return P.new_gen(rnfisnorm(t0.g, self.g, flag)) - - ########################################### - # 8: Vectors, matrices, LINEAR ALGEBRA and sets - ########################################### - - def vecextract(self, y, z=None): - r""" - self.vecextract(y,z): extraction of the components of the matrix or - vector x according to y and z. If z is omitted, y designates - columns, otherwise y corresponds to rows and z to columns. y and z - can be vectors (of indices), strings (indicating ranges as - in"1..10") or masks (integers whose binary representation indicates - the indices to extract, from left to right 1, 2, 4, 8, etc.) - - .. note:: - - This function uses the PARI row and column indexing, so the - first row or column is indexed by 1 instead of 0. - """ - cdef gen t0 = objtogen(y) - cdef gen t1 - if z is None: - pari_catch_sig_on() - return P.new_gen(shallowextract(self.g, t0.g)) - else: - t1 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(extract0(self.g, t0.g, t1.g)) - - def ncols(self): - """ - Return the number of columns of self. - - EXAMPLES:: - - sage: pari('matrix(19,8)').ncols() - 8 - """ - cdef long n - pari_catch_sig_on() - n = glength(self.g) - pari_catch_sig_off() - return n - - def nrows(self): - """ - Return the number of rows of self. - - EXAMPLES:: - - sage: pari('matrix(19,8)').nrows() - 19 - """ - cdef long n - pari_catch_sig_on() - # if this matrix has no columns - # then it has no rows. - if self.ncols() == 0: - pari_catch_sig_off() - return 0 - n = glength((self.g[1])) - pari_catch_sig_off() - return n - - def mattranspose(self): - """ - Transpose of the matrix self. - - EXAMPLES:: - - sage: pari('[1,2,3; 4,5,6; 7,8,9]').mattranspose() - [1, 4, 7; 2, 5, 8; 3, 6, 9] - """ - pari_catch_sig_on() - return P.new_gen(gtrans(self.g)).Mat() - - def matadjoint(self): - """ - matadjoint(x): adjoint matrix of x. - - EXAMPLES:: - - sage: pari('[1,2,3; 4,5,6; 7,8,9]').matadjoint() - [-3, 6, -3; 6, -12, 6; -3, 6, -3] - sage: pari('[a,b,c; d,e,f; g,h,i]').matadjoint() - [(i*e - h*f), (-i*b + h*c), (f*b - e*c); (-i*d + g*f), i*a - g*c, -f*a + d*c; (h*d - g*e), -h*a + g*b, e*a - d*b] - """ - pari_catch_sig_on() - return P.new_gen(adj(self.g)).Mat() - - def lllgram(self): - return self.qflllgram(0) - - def lllgramint(self): - return self.qflllgram(1) - - def qfminim(self, b=None, m=None, long flag=0, unsigned long precision=0): - """ - Return vectors with bounded norm for this quadratic form. - - INPUT: - - - ``self`` -- a quadratic form - - - ``b`` -- a bound on vector norm (finds minimal non-zero - vectors if b is ``None``) - - - ``m`` -- maximum number of vectors to return. If ``None`` - (default), return all vectors of norm at most B - - - ``flag`` (optional) -- - - - 0: default; - - 1: return only the first minimal vector found (ignore ``max``); - - 2: as 0 but uses a more robust, slower implementation, - valid for non integral quadratic forms. - - OUTPUT: - - A triple consisting of - - - the number of vectors of norm <= b, - - the actual maximum norm of vectors listed - - a matrix whose columns are vectors with norm less than or - equal to b for the definite quadratic form. Only one of `v` - and `-v` is returned and the zero vector is never returned. - - .. note:: - - If max is specified then only max vectors will be output, - but all vectors withing the given norm bound will be computed. - - EXAMPLES:: - - sage: A = Matrix(3,3,[1,2,3,2,5,5,3,5,11]) - sage: A.is_positive_definite() - True - - The first 5 vectors of norm at most 10:: - - sage: pari(A).qfminim(10, 5).python() - [ - [17 14 15 16 13] - [-4 -3 -3 -3 -2] - 146, 10, [-3 -3 -3 -3 -3] - ] - - All vectors of minimal norm:: - - sage: pari(A).qfminim().python() - [ - [ 5 2 1] - [-1 -1 0] - 6, 1, [-1 0 0] - ] - - - Use flag=2 for non-integral input:: - - sage: pari(A.change_ring(RR)).qfminim(5, m=5, flag=2).python() - [ - [ -5 -10 -2 -7 3] - [ 1 2 1 2 0] - 10, 5.00000000000000000, [ 1 2 0 1 -1] - ] - """ - cdef gen t0, t1 - cdef GEN g0, g1 - if b is None: - g0 = NULL - else: - t0 = objtogen(b) - g0 = t0.g - if m is None: - g1 = NULL - else: - t1 = objtogen(m) - g1 = t1.g - pari_catch_sig_on() - # precision is only used when flag == 2 - return P.new_gen(qfminim0(self.g, g0, g1, flag, prec_bits_to_words(precision))) - - def qfrep(self, B, long flag=0): - """ - Vector of (half) the number of vectors of norms from 1 to `B` - for the integral and definite quadratic form ``self``. - Binary digits of flag mean 1: count vectors of even norm from - 1 to `2B`, 2: return a ``t_VECSMALL`` instead of a ``t_VEC`` - (which is faster). - - EXAMPLES:: - - sage: M = pari("[5,1,1;1,3,1;1,1,1]") - sage: M.qfrep(20) - [1, 1, 2, 2, 2, 4, 4, 3, 3, 4, 2, 4, 6, 0, 4, 6, 4, 5, 6, 4] - sage: M.qfrep(20, flag=1) - [1, 2, 4, 3, 4, 4, 0, 6, 5, 4, 12, 4, 4, 8, 0, 3, 8, 6, 12, 12] - sage: M.qfrep(20, flag=2) - Vecsmall([1, 1, 2, 2, 2, 4, 4, 3, 3, 4, 2, 4, 6, 0, 4, 6, 4, 5, 6, 4]) - """ - # PARI 2.7 always returns a t_VECSMALL, but for backwards - # compatibility, we keep returning a t_VEC (unless flag & 2) - cdef gen t0 = objtogen(B) - cdef GEN r - pari_catch_sig_on() - r = qfrep0(self.g, t0.g, flag & 1) - if (flag & 2) == 0: - r = vecsmall_to_vec(r) - return P.new_gen(r) - - def qfparam(self, sol, long flag=0): - """ - Coefficients of binary quadratic forms that parametrize the - solutions of the ternary quadratic form ``self``, using the - particular solution ``sol``. - - INPUT: - - - ``self`` -- a rational symmetric matrix - - - ``sol`` -- a non-trivial solution to the quadratic form - ``self`` - - OUTPUT: - - A matrix whose rows define polynomials which parametrize all - solutions to the quadratic form ``self`` in the projective - plane. + sage: f = pari('x^2 + 1') + sage: f.type() + 't_POL' + sage: f.eval(I) + 0 + sage: f.eval(x=2) + 5 + sage: (1/f).eval(x=1) + 1/2 - EXAMPLES: + The notation ``f(x)`` is an alternative for ``f.eval(x)``:: - The following can be used to parametrize Pythagorean triples:: - - sage: M = diagonal_matrix([1,1,-1]) - sage: P = M._pari_().qfparam([0,1,-1]); P - [0, -2, 0; 1, 0, -1; -1, 0, -1] - sage: R. = QQ[] - sage: v = P.sage() * vector([x^2, x*y, y^2]); v - (-2*x*y, x^2 - y^2, -x^2 - y^2) - sage: v(x=2, y=1) - (-4, 3, -5) - sage: v(x=3,y=8) - (-48, -55, -73) - sage: 48^2 + 55^2 == 73^2 + sage: f(3) == f.eval(3) True - """ - cdef gen t0 = objtogen(sol) - cdef GEN s = t0.g - - pari_catch_sig_on() - return P.new_gen(qfparam(self.g, s, flag)) - - def qfsolve(self): - """ - Try to solve over `\mathbb{Q}` the quadratic equation - `X^t G X = 0` for a matrix G with rational coefficients. - - INPUT: - - - ``self`` -- a rational symmetric matrix - - OUTPUT: - - If the quadratic form is solvable, return a column or a matrix - with multiple columns spanning an isotropic subspace (there is - no guarantee that the maximal isotropic subspace is returned). - - If the quadratic form is not solvable and the dimension is at - 3, return the local obstruction: a place (`-1` or a prime `p`) - where the form is not locally solvable. For unsolvable forms in - dimension 2, the number -2 is returned. - - EXAMPLES:: - - sage: M = diagonal_matrix([1,2,3,4,-5]) - sage: M._pari_().qfsolve() - [0, 1, -1, 0, -1]~ - sage: M = diagonal_matrix([4,-9]) - sage: M._pari_().qfsolve() - [6, 4]~ - - An example of a real obstruction:: - - sage: M = diagonal_matrix([1,1,1,1,1]) - sage: M._pari_().qfsolve() - -1 - - An example of a `p`-adic obstruction:: - - sage: M = diagonal_matrix([1,1,-3]) - sage: M._pari_().qfsolve() - 3 - - In dimension 2, we get -2 if the form is not solvable:: - - sage: M = diagonal_matrix([1,-42]) - sage: M._pari_().qfsolve() - -2 - - For singular quadratic forms, the kernel is returned:: - sage: M = diagonal_matrix([1,-1,0,0]) - sage: M._pari_().qfsolve().sage() - [0 0] - [0 0] - [1 0] - [0 1] - """ - pari_catch_sig_on() - return P.new_gen(qfsolve(self.g)) - - def matsolve(self, B): - """ - matsolve(B): Solve the linear system Mx=B for an invertible matrix - M + Evaluating power series:: - matsolve(B) uses Gaussian elimination to solve Mx=B, where M is - invertible and B is a column vector. + sage: f = pari('1 + x + x^3 + O(x^7)') + sage: f(2*pari('y')^2) + 1 + 2*y^2 + 8*y^6 + O(y^14) - The corresponding pari library routine is gauss. The gp-interface - name matsolve has been given preference here. + Substituting zero is sometimes possible, and trying to do so + in illegal cases can raise various errors:: - INPUT: + sage: pari('1 + O(x^3)').eval(0) + 1 + sage: pari('1/x').eval(0) + Traceback (most recent call last): + ... + PariError: impossible inverse in gdiv: 0 + sage: pari('1/x + O(x^2)').eval(0) + Traceback (most recent call last): + ... + ZeroDivisionError: substituting 0 in Laurent series with negative valuation + sage: pari('1/x + O(x^2)').eval(pari('O(x^3)')) + Traceback (most recent call last): + ... + PariError: impossible inverse in gdiv: O(x^3) + sage: pari('O(x^0)').eval(0) + Traceback (most recent call last): + ... + PariError: domain error in polcoeff: t_SER = O(x^0) + Evaluating multivariate polynomials:: - - ``B`` - a column vector of the same dimension as the - square matrix self + sage: f = pari('y^2 + x^3') + sage: f(1) # Dangerous, depends on PARI variable ordering + y^2 + 1 + sage: f(x=1) # Safe + y^2 + 1 + sage: f(y=1) + x^3 + 1 + sage: f(1, 2) + Traceback (most recent call last): + ... + TypeError: evaluating PARI t_POL takes exactly 1 argument (2 given) + sage: f(y='x', x='2*y') + x^2 + 8*y^3 + sage: f() + x^3 + y^2 + It's not an error to substitute variables which do not appear:: - EXAMPLES:: + sage: f.eval(z=37) + x^3 + y^2 + sage: pari(42).eval(t=0) + 42 - sage: pari('[1,1;1,-1]').matsolve(pari('[1;0]')) - [1/2; 1/2] - """ - cdef gen t0 = objtogen(B) - pari_catch_sig_on() - return P.new_gen(gauss(self.g, t0.g)) + We can define and evaluate closures as follows:: - def matsolvemod(self, D, B, long flag = 0): - r""" - For column vectors `D=(d_i)` and `B=(b_i)`, find a small integer - solution to the system of linear congruences + sage: T = pari('n -> n + 2') + sage: T.type() + 't_CLOSURE' + sage: T.eval(3) + 5 - .. math:: + sage: T = pari('() -> 42') + sage: T() + 42 - R_ix=b_i\text{ (mod }d_i), + sage: pr = pari('s -> print(s)') + sage: pr.eval('"hello world"') + hello world - where `R_i` is the ith row of ``self``. If `d_i=0`, the equation is - considered over the integers. The entries of ``self``, ``D``, and - ``B`` should all be integers (those of ``D`` should also be - non-negative). + sage: f = pari('myfunc(x,y) = x*y') + sage: f.eval(5, 6) + 30 - If ``flag`` is 1, the output is a two-component row vector whose first - component is a solution and whose second component is a matrix whose - columns form a basis of the solution set of the homogeneous system. + Default arguments work, missing arguments are treated as zero + (like in GP):: - For either value of ``flag``, the output is 0 if there is no solution. + sage: f = pari("(x, y, z=1.0) -> [x, y, z]") + sage: f(1, 2, 3) + [1, 2, 3] + sage: f(1, 2) + [1, 2, 1.00000000000000] + sage: f(1) + [1, 0, 1.00000000000000] + sage: f() + [0, 0, 1.00000000000000] - Note that if ``D`` or ``B`` is an integer, then it will be considered - as a vector all of whose entries are that integer. + Variadic closures are supported as well (:trac:`18623`):: - EXAMPLES:: + sage: f = pari("(v[..])->length(v)") + sage: f('a', f) + 2 + sage: g = pari("(x,y,z[..])->[x,y,z]") + sage: g(), g(1), g(1,2), g(1,2,3), g(1,2,3,4) + ([0, 0, []], [1, 0, []], [1, 2, []], [1, 2, [3]], [1, 2, [3, 4]]) - sage: D = pari('[3,4]~') - sage: B = pari('[1,2]~') - sage: M = pari('[1,2;3,4]') - sage: M.matsolvemod(D, B) - [-2, 0]~ - sage: M.matsolvemod(3, 1) - [-1, 1]~ - sage: M.matsolvemod(pari('[3,0]~'), pari('[1,2]~')) - [6, -4]~ - sage: M2 = pari('[1,10;9,18]') - sage: M2.matsolvemod(3, pari('[2,3]~'), 1) - [[0, -1]~, [-1, -2; 1, -1]] - sage: M2.matsolvemod(9, pari('[2,3]~')) - 0 - sage: M2.matsolvemod(9, pari('[2,45]~'), 1) - [[1, 1]~, [-1, -4; 1, -5]] - """ - cdef gen t0 = objtogen(D) - cdef gen t1 = objtogen(B) - pari_catch_sig_on() - return P.new_gen(matsolvemod0(self.g, t0.g, t1.g, flag)) + Using keyword arguments, we can substitute in more complicated + objects, for example a number field:: - def matker(self, long flag=0): + sage: K. = NumberField(x^2 + 1) + sage: nf = K._pari_() + sage: nf + [y^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-38 + 1.00000000000000*I]), [1, 1.00000000000000; 1, -1.00000000000000], [1, 1; 1, -1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]], []], [0.E-38 + 1.00000000000000*I], [1, y], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] + sage: nf(y='x') + [x^2 + 1, [0, 1], -4, 1, [Mat([1, 0.E-38 + 1.00000000000000*I]), [1, 1.00000000000000; 1, -1.00000000000000], [1, 1; 1, -1], [2, 0; 0, -2], [2, 0; 0, 2], [1, 0; 0, -1], [1, [0, -1; 1, 0]], []], [0.E-38 + 1.00000000000000*I], [1, x], [1, 0; 0, 1], [1, 0, 0, -1; 0, 1, 1, 0]] """ - Return a basis of the kernel of this matrix. - - INPUT: - - - - ``flag`` - optional; may be set to 0: default; - non-zero: x is known to have integral entries. + cdef long t = typ(self.g) + cdef gen t0 + cdef GEN result + cdef long arity + cdef long nargs = len(args) + cdef long nkwds = len(kwds) + # Closure must be evaluated using *args + if t == t_CLOSURE: + if nkwds > 0: + raise TypeError("cannot evaluate a PARI closure using keyword arguments") + if closure_is_variadic(self.g): + arity = closure_arity(self.g) - 1 + args = list(args[:arity]) + [0]*(arity-nargs) + [args[arity:]] + t0 = objtogen(args) + sig_on() + result = closure_callgenvec(self.g, t0.g) + if result == gnil: + P.clear_stack() + return None + return P.new_gen(result) - EXAMPLES:: + # Evaluate univariate polynomials, rational functions and + # series using *args + if nargs > 0: + if nkwds > 0: + raise TypeError("mixing unnamed and keyword arguments not allowed when evaluating a PARI object") + if not (t == t_POL or t == t_RFRAC or t == t_SER): + raise TypeError("cannot evaluate PARI %s using unnamed arguments" % self.type()) + if nargs != 1: + raise TypeError("evaluating PARI %s takes exactly 1 argument (%d given)" + % (self.type(), nargs)) - sage: pari('[1,2,3;4,5,6;7,8,9]').matker() - [1; -2; 1] + t0 = objtogen(args[0]) + sig_on() + if t == t_POL or t == t_RFRAC: + return P.new_gen(poleval(self.g, t0.g)) + else: # t == t_SER + if isexactzero(t0.g): + # Work around the fact that PARI currently doesn't + # support substituting exact 0 in a power series. + # We don't try to imitate this when using keyword + # arguments, and hope this will be fixed in a + # future PARI version. + if valp(self.g) < 0: + sig_off() + raise ZeroDivisionError('substituting 0 in Laurent series with negative valuation') + elif valp(self.g) == 0: + return P.new_gen(polcoeff0(self.g, 0, -1)) + else: + sig_off() + return P.PARI_ZERO + return P.new_gen(gsubst(self.g, varn(self.g), t0.g)) - With algorithm 1, even if the matrix has integer entries the kernel - need not be saturated (which is weird):: + # Call substvec() using **kwds + vstr = kwds.keys() # Variables as Python strings + t0 = objtogen(kwds.values()) # Replacements - sage: pari('[1,2,3;4,5,6;7,8,9]').matker(1) - [3; -6; 3] - sage: pari('matrix(3,3,i,j,i)').matker() - [-1, -1; 1, 0; 0, 1] - sage: pari('[1,2,3;4,5,6;7,8,9]*Mod(1,2)').matker() - [Mod(1, 2); Mod(0, 2); Mod(1, 2)] - """ - pari_catch_sig_on() - return P.new_gen(matker0(self.g, flag)) + sig_on() + cdef GEN v = cgetg(nkwds+1, t_VEC) # Variables as PARI polynomials + cdef long i + for i in range(nkwds): + set_gel(v, i+1, pol_x(P.get_var(vstr[i]))) + return P.new_gen(gsubstvec(self.g, v, t0.g)) - def matkerint(self, long flag=0): + def __call__(self, *args, **kwds): """ - Return the integer kernel of a matrix. + Evaluate ``self`` with the given arguments. - This is the LLL-reduced Z-basis of the kernel of the matrix x with - integral entries. + This has the same effect as :meth:`eval`. EXAMPLES:: - sage: pari('[2,1;2,1]').matker() - [-1/2; 1] - sage: pari('[2,1;2,1]').matkerint() - [1; -2] - sage: pari('[2,1;2,1]').matkerint(1) - doctest:...: DeprecationWarning: The flag argument to matkerint() is deprecated by PARI - See http://trac.sagemath.org/18203 for details. - [1; -2] - """ - if flag: - deprecation(18203, "The flag argument to matkerint() is deprecated by PARI") - pari_catch_sig_on() - return P.new_gen(matkerint0(self.g, flag)) - - def matdet(self, long flag=0): - """ - Return the determinant of this matrix. - - INPUT: - - - - ``flag`` - (optional) flag 0: using Gauss-Bareiss. - 1: use classical Gaussian elimination (slightly better for integer - entries) - + sage: R. = GF(3)[] + sage: f = (x^2 + x + 1)._pari_() + sage: f.type() + 't_POL' + sage: f(2) + Mod(1, 3) - EXAMPLES:: + TESTS:: - sage: pari('[1,2; 3,4]').matdet(0) - -2 - sage: pari('[1,2; 3,4]').matdet(1) - -2 + sage: T = pari('n -> 1/n') + sage: T.type() + 't_CLOSURE' + sage: T(0) + Traceback (most recent call last): + ... + PariError: _/_: impossible inverse in gdiv: 0 + sage: pari('() -> 42')(1,2,3) + Traceback (most recent call last): + ... + PariError: too many parameters in user-defined function call + sage: pari('n -> n')(n=2) + Traceback (most recent call last): + ... + TypeError: cannot evaluate a PARI closure using keyword arguments + sage: pari('x + y')(4, y=1) + Traceback (most recent call last): + ... + TypeError: mixing unnamed and keyword arguments not allowed when evaluating a PARI object + sage: pari("12345")(4) + Traceback (most recent call last): + ... + TypeError: cannot evaluate PARI t_INT using unnamed arguments """ - pari_catch_sig_on() - return P.new_gen(det0(self.g, flag)) + return self.eval(*args, **kwds) - def trace(self): + def factorpadic(self, p, long r=20, long flag=-1): """ - Return the trace of this PARI object. + p-adic factorization of the polynomial ``pol`` to precision ``r``. EXAMPLES:: - sage: pari('[1,2; 3,4]').trace() - 5 + sage: x = polygen(QQ) + sage: pol = (x^2 - 1)^2 + sage: pari(pol).factorpadic(5) + [(1 + O(5^20))*x + (1 + O(5^20)), 2; (1 + O(5^20))*x + (4 + 4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + 4*5^11 + 4*5^12 + 4*5^13 + 4*5^14 + 4*5^15 + 4*5^16 + 4*5^17 + 4*5^18 + 4*5^19 + O(5^20)), 2] + sage: pari(pol).factorpadic(5,3) + [(1 + O(5^3))*x + (1 + O(5^3)), 2; (1 + O(5^3))*x + (4 + 4*5 + 4*5^2 + O(5^3)), 2] """ - pari_catch_sig_on() - return P.new_gen(gtrace(self.g)) + if flag != -1: + from sage.misc.superseded import deprecation + deprecation(16997, 'The flag argument to factorpadic() is deprecated and not used anymore') + cdef gen t0 = objtogen(p) + sig_on() + return P.new_gen(factorpadic(self.g, t0.g, r)) - def mathnf(self, long flag=0): + def poldegree(self, var=-1): """ - A.mathnf(flag=0): (upper triangular) Hermite normal form of A, - basis for the lattice formed by the columns of A. - - INPUT: - - - - ``flag`` - optional, value range from 0 to 4 (0 if - omitted), meaning : 0: naive algorithm - - - ``1: Use Batut's algorithm`` - output 2-component - vector [H,U] such that H is the HNF of A, and U is a unimodular - matrix such that xU=H. 3: Use Batut's algorithm. Output [H,U,P] - where P is a permutation matrix such that P A U = H. 4: As 1, using - a heuristic variant of LLL reduction along the way. - - - EXAMPLES:: - - sage: pari('[1,2,3; 4,5,6; 7,8,9]').mathnf() - [6, 1; 3, 1; 0, 1] + Return the degree of this polynomial. """ - pari_catch_sig_on() - return P.new_gen(mathnf0(self.g, flag)) + sig_on() + n = poldegree(self.g, P.get_var(var)) + sig_off() + return n - def mathnfmod(self, d): + def polisirreducible(self): + """ + f.polisirreducible(): Returns True if f is an irreducible + non-constant polynomial, or False if f is reducible or constant. """ - Returns the Hermite normal form if d is a multiple of the - determinant + sig_on() + t = isirreducible(self.g) + P.clear_stack() + return t != 0 - Beware that PARI's concept of a Hermite normal form is an upper - triangular matrix with the same column space as the input matrix. + def polroots(self, long flag=-1, unsigned long precision=0): + """ + Complex roots of the given polynomial using Schonhage's method, + as modified by Gourdon. + """ + if flag != -1: + from sage.misc.superseded import deprecation + deprecation(16997, 'The flag argument to polroots() is deprecated and not used anymore') + sig_on() + return P.new_gen(cleanroots(self.g, prec_bits_to_words(precision))) - INPUT: + def polrootspadicfast(self, p, r=20): + from sage.misc.superseded import deprecation + deprecation(16997, 'polrootspadicfast is deprecated, use polrootspadic or the direct PARI call ZpX_roots instead') + cdef gen t0 = objtogen(p) + sig_on() + return P.new_gen(rootpadic(self.g, t0.g, r)) + polsturm_full = deprecated_function_alias(18203, gen_auto.polsturm) - - ``d`` - multiple of the determinant of self + def rnfisnorm(self, T, long flag=0): + cdef gen t0 = objtogen(T) + sig_on() + return P.new_gen(rnfisnorm(t0.g, self.g, flag)) + def ncols(self): + """ + Return the number of columns of self. EXAMPLES:: - sage: M=matrix([[1,2,3],[4,5,6],[7,8,11]]) - sage: d=M.det() - sage: pari(M).mathnfmod(d) - [6, 4, 3; 0, 1, 0; 0, 0, 1] - - Note that d really needs to be a multiple of the discriminant, not - just of the exponent of the cokernel:: - - sage: M=matrix([[1,0,0],[0,2,0],[0,0,6]]) - sage: pari(M).mathnfmod(6) - [1, 0, 0; 0, 1, 0; 0, 0, 6] - sage: pari(M).mathnfmod(12) - [1, 0, 0; 0, 2, 0; 0, 0, 6] + sage: pari('matrix(19,8)').ncols() + 8 """ - cdef gen t0 = objtogen(d) - pari_catch_sig_on() - return P.new_gen(hnfmod(self.g, t0.g)) + cdef long n + sig_on() + n = glength(self.g) + sig_off() + return n - def mathnfmodid(self, d): + def nrows(self): """ - Returns the Hermite Normal Form of M concatenated with d\*Identity - - Beware that PARI's concept of a Hermite normal form is a maximal - rank upper triangular matrix with the same column space as the - input matrix. - - INPUT: + Return the number of rows of self. + EXAMPLES:: - - ``d`` - Determines + sage: pari('matrix(19,8)').nrows() + 19 + """ + cdef long n + sig_on() + # if this matrix has no columns + # then it has no rows. + if self.ncols() == 0: + sig_off() + return 0 + n = glength((self.g[1])) + sig_off() + return n + def mattranspose(self): + """ + Transpose of the matrix self. EXAMPLES:: - sage: M=matrix([[1,0,0],[0,2,0],[0,0,6]]) - sage: pari(M).mathnfmodid(6) - [1, 0, 0; 0, 2, 0; 0, 0, 6] + sage: pari('[1,2,3; 4,5,6; 7,8,9]').mattranspose() + [1, 4, 7; 2, 5, 8; 3, 6, 9] - This routine is not completely equivalent to mathnfmod:: + Unlike PARI, this always returns a matrix:: - sage: pari(M).mathnfmod(6) - [1, 0, 0; 0, 1, 0; 0, 0, 6] + sage: pari('[1,2,3]').mattranspose() + [1; 2; 3] + sage: pari('[1,2,3]~').mattranspose() + Mat([1, 2, 3]) """ - cdef gen t0 = objtogen(d) - pari_catch_sig_on() - return P.new_gen(hnfmodid(self.g, t0.g)) + sig_on() + return P.new_gen(gtrans(self.g)).Mat() - def matsnf(self, long flag=0): - """ - x.matsnf(flag=0): Smith normal form (i.e. elementary divisors) of - the matrix x, expressed as a vector d. Binary digits of flag mean - 1: returns [u,v,d] where d=u\*x\*v, otherwise only the diagonal d - is returned, 2: allow polynomial entries, otherwise assume x is - integral, 4: removes all information corresponding to entries equal - to 1 in d. + def lllgram(self): + return self.qflllgram(0) - EXAMPLES:: + def lllgramint(self): + return self.qflllgram(1) - sage: pari('[1,2,3; 4,5,6; 7,8,9]').matsnf() - [0, 3, 1] + def qfrep(self, B, long flag=0): """ - pari_catch_sig_on() - return P.new_gen(matsnf0(self.g, flag)) - - def matfrobenius(self, long flag=0): - r""" - M.matfrobenius(flag=0): Return the Frobenius form of the square - matrix M. If flag is 1, return only the elementary divisors (a list - of polynomials). If flag is 2, return a two-components vector [F,B] - where F is the Frobenius form and B is the basis change so that - `M=B^{-1} F B`. + Vector of (half) the number of vectors of norms from 1 to `B` + for the integral and definite quadratic form ``self``. + Binary digits of flag mean 1: count vectors of even norm from + 1 to `2B`, 2: return a ``t_VECSMALL`` instead of a ``t_VEC`` + (which is faster). EXAMPLES:: - sage: a = pari('[1,2;3,4]') - sage: a.matfrobenius() - [0, 2; 1, 5] - sage: a.matfrobenius(flag=1) - [x^2 - 5*x - 2] - sage: a.matfrobenius(2) - [[0, 2; 1, 5], [1, -1/3; 0, 1/3]] - sage: v = a.matfrobenius(2) - sage: v[0] - [0, 2; 1, 5] - sage: v[1]^(-1)*v[0]*v[1] - [1, 2; 3, 4] + sage: M = pari("[5,1,1;1,3,1;1,1,1]") + sage: M.qfrep(20) + [1, 1, 2, 2, 2, 4, 4, 3, 3, 4, 2, 4, 6, 0, 4, 6, 4, 5, 6, 4] + sage: M.qfrep(20, flag=1) + [1, 2, 4, 3, 4, 4, 0, 6, 5, 4, 12, 4, 4, 8, 0, 3, 8, 6, 12, 12] + sage: M.qfrep(20, flag=2) + Vecsmall([1, 1, 2, 2, 2, 4, 4, 3, 3, 4, 2, 4, 6, 0, 4, 6, 4, 5, 6, 4]) + """ + # PARI 2.7 always returns a t_VECSMALL, but for backwards + # compatibility, we keep returning a t_VEC (unless flag & 2) + cdef gen t0 = objtogen(B) + cdef GEN r + sig_on() + r = qfrep0(self.g, t0.g, flag & 1) + if (flag & 2) == 0: + r = vecsmall_to_vec(r) + return P.new_gen(r) - We let t be the matrix of `T_2` acting on modular symbols - of level 43, which was computed using - ``ModularSymbols(43,sign=1).T(2).matrix()``:: + def matkerint(self, long flag=0): + """ + Return the integer kernel of a matrix. - sage: t = pari('[3, -2, 0, 0; 0, -2, 0, 1; 0, -1, -2, 2; 0, -2, 0, 2]') - sage: t.matfrobenius() - [0, 0, 0, -12; 1, 0, 0, -2; 0, 1, 0, 8; 0, 0, 1, 1] - sage: t.charpoly('x') - x^4 - x^3 - 8*x^2 + 2*x + 12 - sage: t.matfrobenius(1) - [x^4 - x^3 - 8*x^2 + 2*x + 12] + This is the LLL-reduced Z-basis of the kernel of the matrix x with + integral entries. - AUTHORS: + EXAMPLES:: - - Martin Albrect (2006-04-02) + sage: pari('[2,1;2,1]').matker() + [-1/2; 1] + sage: pari('[2,1;2,1]').matkerint() + [1; -2] + sage: pari('[2,1;2,1]').matkerint(1) + doctest:...: DeprecationWarning: The flag argument to matkerint() is deprecated by PARI + See http://trac.sagemath.org/18203 for details. + [1; -2] """ - pari_catch_sig_on() - return P.new_gen(matfrobenius(self.g, flag, 0)) - + if flag: + deprecation(18203, "The flag argument to matkerint() is deprecated by PARI") + sig_on() + return P.new_gen(matkerint0(self.g, flag)) - ########################################### - # polarit2.c - ########################################### - def factor(gen self, limit=-1, bint proof=1): + def factor(self, long limit=-1, proof=None): """ Return the factorization of x. @@ -9057,16 +4145,13 @@ cdef class gen(gen_auto): - ``limit`` -- (default: -1) is optional and can be set whenever x is of (possibly recursive) rational type. If limit is - set return partial factorization, using primes up to limit (up to - primelimit if limit=0). + set, return partial factorization, using primes up to limit. - - ``proof`` -- (default: True) optional. If False (not the default), + - ``proof`` -- optional flag. If ``False`` (not the default), returned factors larger than `2^{64}` may only be pseudoprimes. - - .. note:: - - In the standard PARI/GP interpreter and C-library the - factor command *always* has proof=False, so beware! + If ``True``, always check primality. If not given, use the + global PARI default ``factor_proven`` which is ``True`` by + default in Sage. EXAMPLES:: @@ -9074,6 +4159,8 @@ cdef class gen(gen_auto): [x - 1, 1; x + 1, 1; x^4 - x^3 + x^2 - x + 1, 1; x^4 + x^3 + x^2 + x + 1, 1] sage: pari(2^100-1).factor() [3, 1; 5, 3; 11, 1; 31, 1; 41, 1; 101, 1; 251, 1; 601, 1; 1801, 1; 4051, 1; 8101, 1; 268501, 1] + sage: pari(2^100-1).factor(proof=True) + [3, 1; 5, 3; 11, 1; 31, 1; 41, 1; 101, 1; 251, 1; 601, 1; 1801, 1; 4051, 1; 8101, 1; 268501, 1] sage: pari(2^100-1).factor(proof=False) [3, 1; 5, 3; 11, 1; 31, 1; 41, 1; 101, 1; 251, 1; 601, 1; 1801, 1; 4051, 1; 8101, 1; 268501, 1] @@ -9082,6 +4169,13 @@ cdef class gen(gen_auto): sage: pari(next_prime(10^50)*next_prime(10^60)*next_prime(10^4)).factor(10^5) [10007, 1; 100000000000000000000000000000000000000000000000151000000000700000000000000000000000000000000000000000000001057, 1] + Setting a limit is invalid when factoring polynomials:: + + sage: pari('x^11 + 1').factor(limit=17) + Traceback (most recent call last): + ... + PariError: incorrect type in boundfact (t_POL) + PARI doesn't have an algorithm for factoring multivariate polynomials:: @@ -9089,93 +4183,34 @@ cdef class gen(gen_auto): Traceback (most recent call last): ... PariError: sorry, factor for general polynomials is not yet implemented - """ - cdef int r - cdef GEN t0 - cdef GEN cutoff - if limit == -1 and typ(self.g) == t_INT and proof: - pari_catch_sig_on() - # cutoff for checking true primality: 2^64 according to the - # PARI documentation ??ispseudoprime. - cutoff = mkintn(3, 1, 0, 0) # expansion of 2^64 in base 2^32: (1,0,0) - r = factorint_withproof_sage(&t0, self.g, cutoff) - z = P.new_gen(t0) - if not r: - return z - else: - return _factor_int_when_pari_factor_failed(self, z) - pari_catch_sig_on() - return P.new_gen(factor0(self.g, limit)) - - - ########################################### - # misc (classify when I know where they go) - ########################################### - - def order(self): - pari_catch_sig_on() - return P.new_gen(order(self.g)) - - def znprimroot(self): - r""" - Return a primitive root modulo ``self``, whenever it exists. - - INPUT: - - - ``self`` -- an integer `n` such that `|n|` is equal to 1, 2, - 4, a power of an odd prime, or twice a power of an odd prime - OUTPUT: - - A generator (type ``t_INTMOD``) of `(\ZZ/n\ZZ)^*`. Note that - this group is cyclic if and only if `n` is of the above form. - - EXAMPLES:: + TESTS:: - sage: pari(4).znprimroot() - Mod(3, 4) - sage: pari(10007^3).znprimroot() - Mod(5, 1002101470343) - sage: pari(2*109^10).znprimroot() - Mod(236736367459211723407, 473472734918423446802) + sage: pari(2^1000+1).factor(limit=0) + doctest:...: DeprecationWarning: factor(..., lim=0) is deprecated, use an explicit limit instead + See http://trac.sagemath.org/20205 for details. + [257, 1; 1601, 1; 25601, 1; 76001, 1; 133842787352016..., 1] """ - pari_catch_sig_on() - return P.new_gen(znprimroot(self.g)) - - def znstar(self): - r""" - Return the structure of the group `(\ZZ/n\ZZ)^*`. - - INPUT: - - - ``self`` -- any integer `n` (type ``t_INT``) - - OUTPUT: - - A triple `[\phi(n), [d_1, \ldots, d_k], [x_1, \ldots, x_k]]`, - where - - - `\phi(n)` is the order of `(\ZZ/n\ZZ)^*`; - - - `d_1, \ldots, d_k` are the unique integers greater than 1 - with `d_k \mid d_{k-1} \mid \ldots \mid d_1` such that - `(\ZZ/n\ZZ)^*` is isomorphic to `\prod_{i=1}^k \ZZ/d_i\ZZ`; - - - `x_1, \ldots, x_k` are the images of the standard generators - under some isomorphism from `\prod_{i=1}^k \ZZ/d_i\ZZ` to - `(\ZZ/n\ZZ)^*`. - - EXAMPLES:: + cdef GEN g + if limit == 0: + deprecation(20205, "factor(..., lim=0) is deprecated, use an explicit limit instead") + limit = maxprime() + global factor_proven + cdef int saved_factor_proven = factor_proven + try: + if proof is not None: + factor_proven = 1 if proof else 0 + sig_on() + if limit >= 0: + g = boundfact(self.g, limit) + else: + g = factor(self.g) + return P.new_gen(g) + finally: + factor_proven = saved_factor_proven - sage: pari(0).znstar() - [2, [2], [-1]] - sage: pari(96).znstar() - [32, [8, 2, 2], [Mod(37, 96), Mod(31, 96), Mod(65, 96)]] - sage: pari(-5).znstar() - [4, [4], [Mod(2, 5)]] - """ - pari_catch_sig_on() - return P.new_gen(znstar(self.g)) + multiplicative_order = gen_auto.znorder + order = deprecated_function_alias(20219, multiplicative_order) def __abs__(self): return self.abs() @@ -9197,7 +4232,7 @@ cdef class gen(gen_auto): sage: pari(2^100).nextprime() 1267650600228229401496703205653 """ - pari_catch_sig_on() + sig_on() if add_one: return P.new_gen(nextprime(gaddsg(1, self.g))) return P.new_gen(nextprime(self.g)) @@ -9238,9 +4273,9 @@ cdef class gen(gen_auto): sage: f.subst("x", "I") 0 """ - pari_catch_sig_on() + sig_on() cdef long n = P.get_var(var) - pari_catch_sig_off() + sig_off() if varn(self.g) == n: return self if typ(self.g) != t_POL and typ(self.g) != t_SER: @@ -9250,31 +4285,6 @@ cdef class gen(gen_auto): setvarn(newg.g, n) return newg - def subst(self, var, z): - """ - In ``self``, replace the variable ``var`` by the expression `z`. - - EXAMPLES:: - - sage: x = pari("x"); y = pari("y") - sage: f = pari('x^3 + 17*x + 3') - sage: f.subst(x, y) - y^3 + 17*y + 3 - sage: f.subst(x, "z") - z^3 + 17*z + 3 - sage: f.subst(x, "z")^2 - z^6 + 34*z^4 + 6*z^3 + 289*z^2 + 102*z + 9 - sage: f.subst(x, "x+1") - x^3 + 3*x^2 + 20*x + 21 - sage: f.subst(x, "xyz") - xyz^3 + 17*xyz + 3 - sage: f.subst(x, "xyz")^2 - xyz^6 + 34*xyz^4 + 6*xyz^3 + 289*xyz^2 + 102*xyz + 9 - """ - cdef gen t0 = objtogen(z) - pari_catch_sig_on() - return P.new_gen(gsubst(self.g, P.get_var(var), t0.g)) - def nf_subst(self, z): """ Given a PARI number field ``self``, return the same PARI @@ -9313,7 +4323,7 @@ cdef class gen(gen_auto): [2] """ cdef gen t0 = objtogen(z) - pari_catch_sig_on() + sig_on() return P.new_gen(gsubst(self.g, gvar(self.g), t0.g)) def type(gen self): @@ -9331,6 +4341,8 @@ cdef class gen(gen_auto): 't_INT' sage: pari('x').type() 't_POL' + sage: pari('oo').type() + 't_INFINITY' """ # The following original code leaks memory: # return str(type_name(typ(self.g))) @@ -9370,6 +4382,7 @@ cdef class gen(gen_auto): elif t == t_STR: return 't_STR' elif t == t_VECSMALL: return 't_VECSMALL' elif t == t_CLOSURE: return 't_CLOSURE' + elif t == t_INFINITY: return 't_INFINITY' else: raise TypeError("Unknown PARI type: %s" % t) @@ -9383,80 +4396,11 @@ cdef class gen(gen_auto): cdef gen t0 = objtogen(ya) cdef gen t1 = objtogen(x) cdef GEN dy, g - pari_catch_sig_on() + sig_on() g = polint(self.g, t0.g, t1.g, &dy) dif = P.new_gen_noclear(dy) return P.new_gen(g), dif - def algdep(self, long n): - """ - EXAMPLES:: - - sage: n = pari.set_real_precision(210) - sage: w1 = pari('z1=2-sqrt(26); (z1+I)/(z1-I)') - sage: f = w1.algdep(12); f - 545*x^11 - 297*x^10 - 281*x^9 + 48*x^8 - 168*x^7 + 690*x^6 - 168*x^5 + 48*x^4 - 281*x^3 - 297*x^2 + 545*x - sage: f(w1).abs() < 1.0e-200 - True - sage: f.factor() - [x, 1; x + 1, 2; x^2 + 1, 1; x^2 + x + 1, 1; 545*x^4 - 1932*x^3 + 2790*x^2 - 1932*x + 545, 1] - sage: pari.set_real_precision(n) - 210 - """ - pari_catch_sig_on() - return P.new_gen(algdep(self.g, n)) - - def listinsert(self, obj, long n): - cdef gen t0 = objtogen(obj) - pari_catch_sig_on() - return P.new_gen(listinsert(self.g, t0.g, n)) - - def listput(self, obj, long n): - cdef gen t0 = objtogen(obj) - pari_catch_sig_on() - return P.new_gen(listput(self.g, t0.g, n)) - - def elleisnum(self, long k, long flag=0, unsigned long precision=0): - """ - om.elleisnum(k, flag=0): om=[om1,om2] being a 2-component vector - giving a basis of a lattice L and k an even positive integer, - computes the numerical value of the Eisenstein series of weight k. - When flag is non-zero and k=4 or 6, this gives g2 or g3 with the - correct normalization. - - INPUT: - - - - ``om`` - gen, 2-component vector giving a basis of a - lattice L - - - ``k`` - int (even positive) - - - ``flag`` - int (default 0) - - - OUTPUT: - - - - ``gen`` - numerical value of E_k - - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: om = e.omega() - sage: om - [2.49021256085506, -1.97173770155165*I] - sage: om.elleisnum(2) - 10.0672605281120 - sage: om.elleisnum(4) - 112.000000000000 - sage: om.elleisnum(100) - 2.15314248576078 E50 - """ - pari_catch_sig_on() - return P.new_gen(elleisnum(self.g, k, flag, prec_bits_to_words(precision))) - def ellwp(gen self, z='z', long n=20, long flag=0, unsigned long precision=0): """ Return the value or the series expansion of the Weierstrass @@ -9526,38 +4470,13 @@ cdef class gen(gen_auto): cdef GEN g0 = t0.g # Emulate toser_i() but with given precision - pari_catch_sig_on() + sig_on() if typ(g0) == t_POL: g0 = RgX_to_ser(g0, n+4) elif typ(g0) == t_RFRAC: g0 = rfrac_to_ser(g0, n+4) return P.new_gen(ellwp0(self.g, g0, flag, prec_bits_to_words(precision))) - def ellchangepoint(self, y): - """ - self.ellchangepoint(y): change data on point or vector of points - self on an elliptic curve according to y=[u,r,s,t] - - EXAMPLES:: - - sage: e = pari([0,1,1,-2,0]).ellinit() - sage: x = pari([1,0]) - sage: e.ellisoncurve([1,4]) - False - sage: e.ellisoncurve(x) - True - sage: f = e.ellchangecurve([1,2,3,-1]) - sage: f[:5] # show only first five entries - [6, -2, -1, 17, 8] - sage: x.ellchangepoint([1,2,3,-1]) - [-1, 4] - sage: f.ellisoncurve([-1,4]) - True - """ - cdef gen t0 = objtogen(y) - pari_catch_sig_on() - return P.new_gen(ellchangepoint(self.g, t0.g)) - def debug(gen self, long depth = -1): r""" Show the internal structure of self (like the ``\x`` command in gp). @@ -9573,23 +4492,11 @@ cdef class gen(gen_auto): real = gen_0 imag = [&=0000000004c5ef90] REAL(lg=4):0400000000000004 (+,expo=0):6000000000000000 8000000000000000 0000000000000000 """ - pari_catch_sig_on() + sig_on() dbgGEN(self.g, depth) - pari_catch_sig_off() + sig_off() return - #################################################################### - # Functions deprecated by upstream PARI - # - # NOTE: these should remain in Sage as long as PARI supports them, - # do not just delete these methods! - #################################################################### - - def bezout(x, y): - deprecation(18203, "bezout() is deprecated in PARI, use gcdext() instead (note that the output is in a different order!)") - u, v, g = x.gcdext(y) - return g, u, v - def xgcd(x, y): """ Returns u,v,d such that d=gcd(x,y) and u\*x+v\*y=d. @@ -9605,6 +4512,18 @@ cdef class gen(gen_auto): u, v, g = x.gcdext(y) return g, u, v + #################################################################### + # Functions deprecated by upstream PARI + # + # NOTE: these should remain in Sage as long as PARI supports them, + # do not just delete these methods! + #################################################################### + + def bezout(x, y): + deprecation(18203, "bezout() is deprecated in PARI, use gcdext() instead (note that the output is in a different order!)") + u, v, g = x.gcdext(y) + return g, u, v + def sizedigit(x): """ sizedigit(x): Return a quick estimate for the maximal number of @@ -9665,14 +4584,14 @@ cdef class gen(gen_auto): [1, 1/6, -1/30, 1/42, -1/30, 5/66, -691/2730, 7/6, -3617/510] """ deprecation(15767, 'bernvec() is deprecated, use repeated calls to bernfrac() instead') - pari_catch_sig_on() + sig_on() return P.new_gen(bernvec(x)) bezoutres = deprecated_function_alias(18203, gen_auto.polresultantext) ellbil = deprecated_function_alias(18203, ellheight) - ellpow = deprecated_function_alias(18203, ellmul) + ellpow = deprecated_function_alias(18203, gen_auto.ellmul) def rnfpolred(*args, **kwds): deprecation(18203, "rnfpolred() is deprecated in PARI, port your code to use rnfpolredbest() instead") @@ -9796,29 +4715,29 @@ cpdef gen objtogen(s): # Check basic Python types. Start with strings, which are a very # common case. if isinstance(s, str): - pari_catch_sig_on() + sig_on() g = gp_read_str(PyString_AsString(s)) if g == gnil: P.clear_stack() return None return P.new_gen(g) if isinstance(s, int): - pari_catch_sig_on() + sig_on() return P.new_gen(stoi(PyInt_AS_LONG(s))) if isinstance(s, bool): return P.PARI_ONE if s else P.PARI_ZERO if isinstance(s, long): - pari_catch_sig_on() + sig_on() mpz_init(mpz_int) mpz_set_pylong(mpz_int, s) g = P._new_GEN_from_mpz_t(mpz_int) mpz_clear(mpz_int) return P.new_gen(g) if isinstance(s, float): - pari_catch_sig_on() + sig_on() return P.new_gen(dbltor(PyFloat_AS_DOUBLE(s))) if isinstance(s, complex): - pari_catch_sig_on() + sig_on() g = cgetg(3, t_COMPLEX) set_gel(g, 1, dbltor(PyComplex_RealAsDouble(s))) set_gel(g, 2, dbltor(PyComplex_ImagAsDouble(s))) @@ -9903,7 +4822,12 @@ cpdef gentoobj(gen z, locals={}): p = z.padicprime() K = Qp(Integer(p), precp(g)) return K(z.lift()) - + elif t == t_INFINITY: + if z.sign() == 1: + return sage.rings.infinity.infinity + else: + return -sage.rings.infinity.infinity + # Fallback (e.g. polynomials): use string representation from sage.misc.sage_eval import sage_eval return sage_eval(str(z), locals=locals) @@ -9952,38 +4876,3 @@ cdef GEN _Vec_append(GEN v, GEN a, long n): return w else: return v - - -cdef _factor_int_when_pari_factor_failed(x, failed_factorization): - """ - This is called by factor when PARI's factor tried to factor, got - the failed_factorization, and it turns out that one of the factors - in there is not proved prime. At this point, we don't care too much - about speed (so don't write everything below using the PARI C - library), since the probability this function ever gets called is - infinitesimal. (That said, we of course did test this function by - forcing a fake failure in the code in misc.h.) - """ - P = failed_factorization[0] # 'primes' - E = failed_factorization[1] # exponents - if len(P) == 1 and E[0] == 1: - # Major problem -- factor can't split the integer at all, but it's composite. We're stuffed. - print "BIG WARNING: The number %s wasn't split at all by PARI, but it's definitely composite."%(P[0]) - print "This is probably an infinite loop..." - w = [] - for i in range(len(P)): - p = P[i] - e = E[i] - if not p.isprime(): - # Try to factor further -- assume this works. - F = p.factor(proof=True) - for j in range(len(F[0])): - w.append((F[0][j], F[1][j])) - else: - w.append((p, e)) - m = P.matrix(len(w), 2) - for i in range(len(w)): - m[i,0] = w[i][0] - m[i,1] = w[i][1] - return m - diff --git a/src/sage/libs/pari/misc.h b/src/sage/libs/pari/misc.h deleted file mode 100644 index 3de912089ba..00000000000 --- a/src/sage/libs/pari/misc.h +++ /dev/null @@ -1,43 +0,0 @@ -/***************************************** - PARI misc macros and functions - *****************************************/ -#ifndef SAGE_LIBS_PARI_MISC_H -#define SAGE_LIBS_PARI_MISC_H - -#include - - -int factorint_withproof_sage(GEN* ans, GEN x, GEN cutoff) { - /* - Factors and proves that the prime factors are really prime. - If any aren't an ERROR condition (signal) is raised. - - INPUT: - x -- a t_INT - cutoff -- only check for primality of numbers at least this large. - */ - - GEN F = factorint(x, 0); - *ans = F; - - long i, l; - if (lg(F) == 1) return F; // x = 1 - F = gel(F,1); l = lg(F); - for (i = 1; i < l; i++) { - GEN p = gel(F,i); - if (mpcmp(p, cutoff) > 0 && !isprime(p)) { - char *c, *d; - c = GENtostr(x); - d = GENtostr(p); - fprintf(stderr, "***\nPARI's factor(%s): Found composite pseudoprime %s (very rare and exciting -- PLEASE REPORT!!)\n***\n", - c, d); - fprintf(stderr, "Do not worry, SAGE will further factor the number until each factor is proven prime.\n"); - free(c); - free(d); - return 1; - } - } - return 0; -} - -#endif /* SAGE_LIBS_PARI_MISC_H */ diff --git a/src/sage/libs/pari/pari_err.pxi b/src/sage/libs/pari/pari_err.pxi index 6c83887e125..abe015d4633 100644 --- a/src/sage/libs/pari/pari_err.pxi +++ b/src/sage/libs/pari/pari_err.pxi @@ -1,3 +1,6 @@ +from sage.misc.superseded import deprecation +deprecation(20213, '''the file "pari_err.pxi" is deprecated, use sig_on instead of pari_catch_sig_on''') + from cysignals.signals cimport sig_on as pari_catch_sig_on from cysignals.signals cimport sig_str as pari_catch_sig_str from cysignals.signals cimport sig_off as pari_catch_sig_off diff --git a/src/sage/libs/pari/pari_instance.pxd b/src/sage/libs/pari/pari_instance.pxd index 35f5d73eef5..a7090c6f195 100644 --- a/src/sage/libs/pari/pari_instance.pxd +++ b/src/sage/libs/pari/pari_instance.pxd @@ -16,7 +16,7 @@ cdef class PariInstance_auto(ParentWithBase): @cython.final cdef class PariInstance(PariInstance_auto): cdef long _real_precision - cdef gen PARI_ZERO, PARI_ONE, PARI_TWO + cdef readonly gen PARI_ZERO, PARI_ONE, PARI_TWO cpdef gen zero(self) cpdef gen one(self) cdef inline gen new_gen(self, GEN x) diff --git a/src/sage/libs/pari/pari_instance.pyx b/src/sage/libs/pari/pari_instance.pyx index 459a7963151..77433d37524 100644 --- a/src/sage/libs/pari/pari_instance.pyx +++ b/src/sage/libs/pari/pari_instance.pyx @@ -160,6 +160,27 @@ Sage (:trac:`9636`):: sage: pari('print("test")') test +Check that ``default()`` works properly:: + + sage: pari.default("debug") + 0 + sage: pari.default("debug", 3) + sage: pari(2^67+1).factor() + IFAC: cracking composite + 49191317529892137643 + IFAC: factor 6713103182899 + is prime + IFAC: factor 7327657 + is prime + IFAC: prime 7327657 + appears with exponent = 1 + IFAC: prime 6713103182899 + appears with exponent = 1 + IFAC: found 2 large prime (power) factors. + [3, 1; 7327657, 1; 6713103182899, 1] + sage: pari.default("debug", 0) + sage: pari(2^67+1).factor() + [3, 1; 7327657, 1; 6713103182899, 1] """ #***************************************************************************** @@ -173,7 +194,6 @@ Sage (:trac:`9636`):: from .paridecl cimport * from .paripriv cimport * -include 'pari_err.pxi' include "cysignals/signals.pxi" cdef extern from *: int sig_on_count "cysigs.sig_on_count" @@ -184,7 +204,7 @@ cimport libc.stdlib from libc.stdio cimport * cimport cython -from sage.ext.memory cimport sage_malloc, sage_free +include "cysignals/memory.pxi" from sage.ext.memory import init_memory_functions from sage.structure.parent cimport Parent from sage.libs.gmp.all cimport * @@ -193,11 +213,7 @@ from sage.libs.flint.fmpz_mat cimport * from sage.libs.pari.gen cimport gen, objtogen from sage.libs.pari.handle_error cimport _pari_init_error_handling -from sage.misc.superseded import deprecated_function_alias - -# so Galois groups are represented in a sane way -# See the polgalois section of the PARI users manual. -new_galois_format = 1 +from sage.misc.superseded import deprecation, deprecated_function_alias # real precision in decimal digits: see documentation for # get_real_precision() and set_real_precision(). This variable is used @@ -372,13 +388,6 @@ cdef PariInstance pari_instance, P pari_instance = PariInstance() P = pari_instance # shorthand notation -# PariInstance.__init__ must not create gen objects because their parent is not constructed yet -pari_catch_sig_on() -pari_instance.PARI_ZERO = pari_instance.new_gen_noclear(gen_0) -pari_instance.PARI_ONE = pari_instance.new_gen_noclear(gen_1) -pari_instance.PARI_TWO = pari_instance.new_gen_noclear(gen_2) -pari_catch_sig_off() - # Also a copy of PARI accessible from external pure python code. pari = pari_instance @@ -503,6 +512,23 @@ cdef class PariInstance(PariInstance_auto): # (which is want we want for the PARI library interface). GP_DATA.flags = gpd_TEST + # Ensure that Galois groups are represented in a sane way, + # see the polgalois section of the PARI users manual. + global new_galois_format + new_galois_format = 1 + + # By default, factor() should prove primality of returned + # factors. This not only influences the factor() function, but + # also many functions indirectly using factoring. + global factor_proven + factor_proven = 1 + + # Initialize some constants + sig_on() + self.PARI_ZERO = self.new_gen_noclear(gen_0) + self.PARI_ONE = self.new_gen_noclear(gen_1) + self.PARI_TWO = self.new_gen_noclear(gen_2) + sig_off() def debugstack(self): r""" @@ -579,11 +605,6 @@ cdef class PariInstance(PariInstance_auto): """ return (left)._richcmp(right, op) - def default(self, variable, value=None): - if not value is None: - return self('default(%s, %s)'%(variable, value)) - return self('default(%s)'%variable) - def set_debug_level(self, level): """ Set the debug PARI C library variable. @@ -620,9 +641,9 @@ cdef class PariInstance(PariInstance_auto): """ prev = self._real_precision cdef bytes strn = str(n) - pari_catch_sig_on() + sig_on() sd_realprecision(strn, d_SILENT) - pari_catch_sig_off() + sig_off() self._real_precision = n return prev @@ -653,28 +674,31 @@ cdef class PariInstance(PariInstance_auto): cdef inline void clear_stack(self): """ - Call ``pari_catch_sig_off()``, and clear the entire PARI stack - if we are leaving the outermost ``pari_catch_sig_on() ... - pari_catch_sig_off()`` block. - + Call ``sig_off()``. If we are leaving the outermost + ``sig_on() ... sig_off()`` block, then clear the PARI stack. """ global avma if sig_on_count <= 1: avma = pari_mainstack.top - pari_catch_sig_off() + sig_off() cdef inline gen new_gen(self, GEN x): """ Create a new gen wrapping `x`, then call ``clear_stack()``. + Except if `x` is ``gnil``, then we return ``None`` instead. """ - cdef gen g = self.new_gen_noclear(x) + cdef gen g + if x is gnil: + g = None + else: + g = self.new_gen_noclear(x) self.clear_stack() return g cdef inline gen new_gen_noclear(self, GEN x): """ Create a new gen, but don't free any memory on the stack and don't - call pari_catch_sig_off(). + call sig_off(). """ cdef pari_sp address cdef gen y = gen.__new__(gen) @@ -705,7 +729,7 @@ cdef class PariInstance(PariInstance_auto): sage: a5.__hash__() == b5.__hash__() True """ - pari_catch_sig_on() + sig_on() return self.new_gen(self._new_GEN_from_mpz_t(value)) cdef inline GEN _new_GEN_from_mpz_t(self, mpz_t value): @@ -713,7 +737,7 @@ cdef class PariInstance(PariInstance_auto): Create a new PARI ``t_INT`` from a ``mpz_t``. For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + One should call ``sig_on()`` before and ``sig_off()`` after. """ cdef unsigned long limbs = mpz_size(value) @@ -729,7 +753,7 @@ cdef class PariInstance(PariInstance_auto): Create a new PARI ``t_INT`` from a ``fmpz_t``. For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + One should call ``sig_on()`` before and ``sig_off()`` after. """ if COEFF_IS_MPZ(value[0]): return self._new_GEN_from_mpz_t(COEFF_TO_PTR(value[0])) @@ -737,7 +761,7 @@ cdef class PariInstance(PariInstance_auto): return stoi(value[0]) cdef gen new_gen_from_int(self, int value): - pari_catch_sig_on() + sig_on() return self.new_gen(stoi(value)) cdef gen new_gen_from_mpq_t(self, mpq_t value): @@ -767,7 +791,7 @@ cdef class PariInstance(PariInstance_auto): sage: a5.__hash__() == b5.__hash__() True """ - pari_catch_sig_on() + sig_on() return self.new_gen(self._new_GEN_from_mpq_t(value)) cdef inline GEN _new_GEN_from_mpq_t(self, mpq_t value): @@ -775,7 +799,7 @@ cdef class PariInstance(PariInstance_auto): Create a new PARI ``t_INT`` or ``t_FRAC`` from a ``mpq_t``. For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + One should call ``sig_on()`` before and ``sig_off()`` after. """ cdef GEN num = self._new_GEN_from_mpz_t(mpq_numref(value)) if mpz_cmpabs_ui(mpq_denref(value), 1) == 0: @@ -793,7 +817,7 @@ cdef class PariInstance(PariInstance_auto): cdef GEN z cdef int i - pari_catch_sig_on() + sig_on() z = cgetg(length + 2, t_POL) z[1] = evalvarn(varnum) if length != 0: @@ -809,7 +833,7 @@ cdef class PariInstance(PariInstance_auto): cdef gen new_gen_from_padic(self, long ordp, long relprec, mpz_t prime, mpz_t p_pow, mpz_t unit): cdef GEN z - pari_catch_sig_on() + sig_on() z = cgetg(5, t_PADIC) z[1] = evalprecp(relprec) + evalvalp(ordp) set_gel(z, 2, self._new_GEN_from_mpz_t(prime)) @@ -852,7 +876,7 @@ cdef class PariInstance(PariInstance_auto): # of precision (that's the number of mantissa bits in an IEEE # double). - pari_catch_sig_on() + sig_on() if x == 0: return self.new_gen(real_0_bit(-53)) else: @@ -870,12 +894,12 @@ cdef class PariInstance(PariInstance_auto): """ cdef gen t0 = self(re) cdef gen t1 = self(im) - pari_catch_sig_on() + sig_on() return self.new_gen(mkcomplex(t0.g, t1.g)) cdef GEN deepcopy_to_python_heap(self, GEN x, pari_sp* address): cdef size_t s = gsizebyte(x) - cdef pari_sp tmp_bot = sage_malloc(s) + cdef pari_sp tmp_bot = sig_malloc(s) cdef pari_sp tmp_top = tmp_bot + s address[0] = tmp_bot return gcopy_avma(x, &tmp_top) @@ -943,7 +967,7 @@ cdef class PariInstance(PariInstance_auto): from a ``mpz_t**``. For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + One should call ``sig_on()`` before and ``sig_off()`` after. """ cdef GEN x cdef GEN A = zeromatcopy(nr, nc) @@ -963,7 +987,7 @@ cdef class PariInstance(PariInstance_auto): Normal Form because Sage and PARI use different definitions. For internal use only; this directly uses the PARI stack. - One should call ``pari_catch_sig_on()`` before and ``pari_catch_sig_off()`` after. + One should call ``sig_on()`` before and ``sig_off()`` after. """ cdef GEN x cdef GEN A = zeromatcopy(nc, nr) @@ -981,7 +1005,7 @@ cdef class PariInstance(PariInstance_auto): sage: matrix(ZZ,2,[1..6])._pari_() # indirect doctest [1, 2, 3; 4, 5, 6] """ - pari_catch_sig_on() + sig_on() cdef GEN g if permute_for_hnf: g = self._new_GEN_from_fmpz_mat_t_rotate90(B, nr, nc) @@ -1007,7 +1031,7 @@ cdef class PariInstance(PariInstance_auto): sage: matrix(QQ,2,[1..6])._pari_() # indirect doctest [1, 2, 3; 4, 5, 6] """ - pari_catch_sig_on() + sig_on() cdef GEN g = self._new_GEN_from_mpq_t_matrix(B, nr, nc) return self.new_gen(g) @@ -1107,9 +1131,9 @@ cdef class PariInstance(PariInstance_auto): return -1 cdef long varno if isinstance(v, gen): - pari_catch_sig_on() + sig_on() varno = gvar((v).g) - pari_catch_sig_off() + sig_off() if varno < 0: return -1 else: @@ -1263,9 +1287,9 @@ cdef class PariInstance(PariInstance_auto): sizemax = s elif sizemax < s: raise ValueError("the maximum size ({}) should be at least the stack size ({})".format(s, sizemax)) - pari_catch_sig_on() + sig_on() paristack_setsize(s, sizemax) - pari_catch_sig_off() + sig_off() if not silent: print("PARI stack size set to {} bytes, maximum size set to {}". format(self.stacksize(), self.stacksizemax())) @@ -1295,187 +1319,95 @@ cdef class PariInstance(PariInstance_auto): if M <= maxprime(): return - pari_catch_sig_on() + sig_on() initprimetable(M) - pari_catch_sig_off() - - ############################################## - ## Support for GP Scripts - ############################################## - - def read(self, bytes filename): - r""" - Read a script from the named filename into the interpreter. The - functions defined in the script are then available for use from - Sage/PARI. The result of the last command in ``filename`` is - returned. - - EXAMPLES: - - Create a gp file:: - - sage: import tempfile - sage: gpfile = tempfile.NamedTemporaryFile(mode="w") - sage: gpfile.file.write("mysquare(n) = {\n") - sage: gpfile.file.write(" n^2;\n") - sage: gpfile.file.write("}\n") - sage: gpfile.file.write("polcyclo(5)\n") - sage: gpfile.file.flush() - - Read it in Sage, we get the result of the last line:: - - sage: pari.read(gpfile.name) - x^4 + x^3 + x^2 + x + 1 - - Call the function defined in the gp file:: - - sage: pari('mysquare(12)') - 144 - """ - pari_catch_sig_on() - return self.new_gen(gp_read_file(filename)) - - - ############################################## - - def _primelimit(self): - """ - Return the number of primes already computed by PARI. + sig_off() - EXAMPLES:: - - sage: pari._primelimit() - 499979 - sage: pari.init_primes(600000) - sage: pari._primelimit() - 599999 + def primes(self, n=None, end=None): """ - from sage.rings.all import ZZ - return ZZ(maxprime()) + Return a pari vector containing the first `n` primes, the primes + in the interval `[n, end]`, or the primes up to `end`. - def prime_list(self, long n): - """ - prime_list(n): returns list of the first n primes + INPUT: - To extend the table of primes use pari.init_primes(M). + Either - INPUT: + - ``n`` -- integer + or - - ``n`` - C long + - ``n`` -- list or tuple `[a, b]` defining an interval of primes + or - OUTPUT: + - ``n, end`` -- start and end point of an interval of primes + or - - ``gen`` - PARI list of first n primes + - ``end`` -- end point for the list of primes + OUTPUT: a PARI list of prime numbers EXAMPLES:: - sage: pari.prime_list(0) - [] - sage: pari.prime_list(-1) - [] - sage: pari.prime_list(3) + sage: pari.primes(3) [2, 3, 5] - sage: pari.prime_list(10) + sage: pari.primes(10) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] - sage: pari.prime_list(20) + sage: pari.primes(20) [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71] - sage: len(pari.prime_list(1000)) + sage: len(pari.primes(1000)) 1000 - """ - if n >= 2: - self.nth_prime(n) - pari_catch_sig_on() - return self.new_gen(primes(n)) - - def primes_up_to_n(self, long n): - """ - Return the primes <= n as a pari list. + sage: pari.primes(11,29) + [11, 13, 17, 19, 23, 29] + sage: pari.primes((11,29)) + [11, 13, 17, 19, 23, 29] + sage: pari.primes(end=29) + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + sage: pari.primes(10^30, 10^30 + 100) + [1000000000000000000000000000057, 1000000000000000000000000000099] - EXAMPLES:: + TESTS:: - sage: pari.primes_up_to_n(1) + sage: pari.primes(0) + [] + sage: pari.primes(-1) + [] + sage: pari.primes(end=1) + [] + sage: pari.primes(end=-1) + [] + sage: pari.primes(3,2) [] - sage: pari.primes_up_to_n(20) - [2, 3, 5, 7, 11, 13, 17, 19] - """ - if n <= 1: - return pari([]) - self.init_primes(n+1) - return self.prime_list(pari(n).primepi()) - - def __nth_prime(self, long n): - """ - nth_prime(n): returns the n-th prime, where n is a C-int - """ - if n <= 0: - raise ValueError("nth prime meaningless for non-positive n (=%s)" % n) - cdef GEN g - pari_catch_sig_on() - g = prime(n) - return self.new_gen(g) - - def nth_prime(self, long n): - from sage.libs.pari.all import PariError - try: - return self.__nth_prime(n) - except PariError: - self.init_primes(max(2*maxprime(), 20*n)) - return self.nth_prime(n) - - def euler(self, unsigned long precision=0): - """ - Return Euler's constant to the requested real precision (in bits). - - EXAMPLES:: - - sage: pari.euler() - 0.577215664901533 - sage: pari.euler(precision=100).python() - 0.577215664901532860606512090082... """ - pari_catch_sig_on() - return self.new_gen(mpeuler(prec_bits_to_words(precision))) + cdef gen t0, t1 + if end is None: + t0 = objtogen(n) + sig_on() + return self.new_gen(primes0(t0.g)) + elif n is None: + t0 = self.PARI_TWO # First prime + else: + t0 = objtogen(n) + t1 = objtogen(end) + sig_on() + return self.new_gen(primes_interval(t0.g, t1.g)) - def pi(self, unsigned long precision=0): - """ - Return the value of the constant pi to the requested real precision - (in bits). + def primes_up_to_n(self, n): + deprecation(20216, "pari.primes_up_to_n(n) is deprecated, use pari.primes(end=n) instead") + return self.primes(end=n) - EXAMPLES:: + prime_list = deprecated_function_alias(20216, primes) - sage: pari.pi() - 3.14159265358979 - sage: pari.pi(precision=100).python() - 3.1415926535897932384626433832... - """ - pari_catch_sig_on() - return self.new_gen(mppi(prec_bits_to_words(precision))) + nth_prime = deprecated_function_alias(20216, PariInstance_auto.prime) - def pollegendre(self, long n, v=-1): - """ - pollegendre(n, v=x): Legendre polynomial of degree n (n C-integer), - in variable v. + euler = PariInstance_auto.Euler + pi = PariInstance_auto.Pi - EXAMPLES:: - - sage: pari.pollegendre(7) - 429/16*x^7 - 693/16*x^5 + 315/16*x^3 - 35/16*x - sage: pari.pollegendre(7, 'z') - 429/16*z^7 - 693/16*z^5 + 315/16*z^3 - 35/16*z - sage: pari.pollegendre(0) - 1 - """ - pari_catch_sig_on() - return self.new_gen(pollegendre(n, self.get_var(v))) - - def polchebyshev(self, long n, v=-1): + def polchebyshev(self, long n, v=None): """ - polchebyshev(n, v=x): Chebyshev polynomial of the first kind of degree - n, in variable v. + Chebyshev polynomial of the first kind of degree `n`, + in the variable `v`. EXAMPLES:: @@ -1486,9 +1418,11 @@ cdef class PariInstance(PariInstance_auto): sage: pari.polchebyshev(0) 1 """ - pari_catch_sig_on() + sig_on() return self.new_gen(polchebyshev1(n, self.get_var(v))) + # Deprecated by upstream PARI: do not remove this deprecated alias + # as long as it exists in PARI. poltchebi = deprecated_function_alias(18203, polchebyshev) def factorial(self, long n): @@ -1506,42 +1440,10 @@ cdef class PariInstance(PariInstance_auto): sage: pari.factorial(25) 15511210043330985984000000 """ - pari_catch_sig_on() + sig_on() return self.new_gen(mpfact(n)) - def polcyclo(self, long n, v=-1): - """ - polcyclo(n, v=x): cyclotomic polynomial of degree n, in variable - v. - - EXAMPLES:: - - sage: pari.polcyclo(8) - x^4 + 1 - sage: pari.polcyclo(7, 'z') - z^6 + z^5 + z^4 + z^3 + z^2 + z + 1 - sage: pari.polcyclo(1) - x - 1 - """ - pari_catch_sig_on() - return self.new_gen(polcyclo(n, self.get_var(v))) - - def polcyclo_eval(self, long n, v): - """ - polcyclo_eval(n, v): value of the nth cyclotomic polynomial at value v. - - EXAMPLES:: - - sage: pari.polcyclo_eval(8, 2) - 17 - sage: cyclotomic_polynomial(8)(2) - 17 - """ - cdef gen t0 = self(v) - pari_catch_sig_on() - return self.new_gen(polcyclo_eval(n, t0.g)) - - def polsubcyclo(self, long n, long d, v=-1): + def polsubcyclo(self, long n, long d, v=None): """ polsubcyclo(n, d, v=x): return the pari list of polynomial(s) defining the sub-abelian extensions of degree `d` of the @@ -1560,13 +1462,14 @@ cdef class PariInstance(PariInstance_auto): [] """ cdef gen plist - pari_catch_sig_on() + sig_on() plist = self.new_gen(polsubcyclo(n, d, self.get_var(v))) if typ(plist.g) != t_VEC: return pari.vector(1, [plist]) else: return plist - #return self.new_gen(polsubcyclo(n, d, self.get_var(v))) + + polcyclo_eval = deprecated_function_alias(20217, PariInstance_auto.polcyclo) def setrand(self, seed): """ @@ -1599,26 +1502,9 @@ cdef class PariInstance(PariInstance_auto): PariError: incorrect type in setrand (t_POL) """ cdef gen t0 = self(seed) - pari_catch_sig_on() + sig_on() setrand(t0.g) - pari_catch_sig_off() - - def getrand(self): - """ - Returns PARI's current random number seed. - - OUTPUT: - - GEN of type t_VECSMALL - - EXAMPLES:: - - sage: a = pari.getrand() - sage: a.type() - 't_INT' - """ - pari_catch_sig_on() - return self.new_gen(getrand()) + sig_off() def vector(self, long n, entries=None): """ @@ -1647,7 +1533,7 @@ cdef class PariInstance(PariInstance_auto): cdef gen _empty_vector(self, long n): cdef gen v - pari_catch_sig_on() + sig_on() v = self.new_gen(zerovec(n)) return v @@ -1660,7 +1546,7 @@ cdef class PariInstance(PariInstance_auto): cdef gen A cdef gen x - pari_catch_sig_on() + sig_on() A = self.new_gen(zeromatcopy(m,n)) if entries is not None: if len(entries) != m*n: @@ -1702,9 +1588,36 @@ cdef class PariInstance(PariInstance_auto): deprecation(16997, 'The 2-argument version of genus2red() is deprecated, use genus2red(P) or genus2red([P,Q]) instead') P = [P0, P] cdef gen t0 = objtogen(P) - pari_catch_sig_on() + sig_on() return self.new_gen(genus2red(t0.g, NULL)) + def List(self, x=None): + """ + Create an empty list or convert `x` to a list. + + EXAMPLES:: + + sage: pari.List(range(5)) + List([0, 1, 2, 3, 4]) + sage: L = pari.List() + sage: L + List([]) + sage: L.listput(42, 1) + 42 + sage: L + List([42]) + sage: L.listinsert(24, 1) + 24 + sage: L + List([24, 42]) + """ + if x is None: + sig_on() + return self.new_gen(listcreate()) + cdef gen t0 = objtogen(x) + sig_on() + return self.new_gen(gtolist(t0.g)) + cdef inline void INT_to_mpz(mpz_ptr value, GEN g): """ diff --git a/src/sage/libs/pari/paridecl.pxd b/src/sage/libs/pari/paridecl.pxd index f6461d8be76..eae6ee2a5fd 100644 --- a/src/sage/libs/pari/paridecl.pxd +++ b/src/sage/libs/pari/paridecl.pxd @@ -4034,6 +4034,8 @@ cdef extern from "sage/libs/pari/parisage.h": GEN serchop0(GEN s) GEN sqrtnint(GEN a, long n) GEN teich(GEN x) + GEN teichmullerinit(long p, long n) + GEN teichmuller(GEN x, GEN tab) GEN trans_eval(char *fun, GEN (*f) (GEN, long), GEN x, long prec) ulong upowuu(ulong p, ulong k) ulong usqrtn(ulong a, ulong n) diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py new file mode 100644 index 00000000000..bce508418f3 --- /dev/null +++ b/src/sage/libs/pari/tests.py @@ -0,0 +1,1605 @@ +r""" +Tests for the Sage <-> PARI interface + +Deprecation checks:: + + sage: pari.poltchebi(10) + doctest:...: DeprecationWarning: poltchebi is deprecated. Please use polchebyshev instead. + See http://trac.sagemath.org/18203 for details. + 512*x^10 - 1280*x^8 + 1120*x^6 - 400*x^4 + 50*x^2 - 1 + sage: pari("x^3 + 1").polsturm(-1, 1) + doctest:...: DeprecationWarning: argument 2 of the PARI/GP function polsturm is undocumented and deprecated + 1 + sage: pari.nth_prime(10) + doctest:...: DeprecationWarning: nth_prime is deprecated. Please use prime instead. + See http://trac.sagemath.org/20216 for details. + 29 + sage: pari.prime_list(10) + doctest:...: DeprecationWarning: prime_list is deprecated. Please use primes instead. + See http://trac.sagemath.org/20216 for details. + [2, 3, 5, 7, 11, 13, 17, 19, 23, 29] + sage: pari.primes_up_to_n(20) + doctest:...: DeprecationWarning: pari.primes_up_to_n(n) is deprecated, use pari.primes(end=n) instead + See http://trac.sagemath.org/20216 for details. + [2, 3, 5, 7, 11, 13, 17, 19] + sage: pari.polcyclo_eval(8, 2) + doctest:...: DeprecationWarning: polcyclo_eval is deprecated. Please use polcyclo instead. + See http://trac.sagemath.org/20217 for details. + 17 + sage: pari('x^-2').printtex() + doctest:...: DeprecationWarning: printtex is deprecated. Please use Strtex instead. + See http://trac.sagemath.org/20219 for details. + "\\frac{1}{x^2}" + sage: pari(10).phi() + doctest:...: DeprecationWarning: phi is deprecated. Please use eulerphi instead. + See http://trac.sagemath.org/20219 for details. + 4 + sage: pari("x^3 + 5*x").reverse() + doctest:...: DeprecationWarning: reverse is deprecated. Please use polrecip instead. + See http://trac.sagemath.org/20219 for details. + 5*x^2 + 1 + sage: nf = pari("x^2 + 1").nfinit() + sage: id = nf.idealhnf(2) + sage: nf.idealintersection(id, id) + doctest:...: DeprecationWarning: idealintersection is deprecated. Please use idealintersect instead. + See http://trac.sagemath.org/20219 for details. + [2, 0; 0, 2] + sage: e = pari([1,0,1,-19,26]).ellinit() + sage: e.elltors(flag=1) + doctest:...: DeprecationWarning: The flag argument to elltors() is deprecated and not used anymore + See http://trac.sagemath.org/20219 for details. + [12, [6, 2], [[1, 2], [3, -2]]] + sage: pari("Mod(2,5)").order() + doctest:...: DeprecationWarning: order is deprecated. Please use znorder instead. + See http://trac.sagemath.org/20219 for details. + 4 + +A long list of doctests which used to be part of manually written code +which is now automatically generated: + +Reading a gp file:: + + sage: import tempfile + sage: gpfile = tempfile.NamedTemporaryFile(mode="w") + sage: gpfile.file.write("mysquare(n) = {\n") + sage: gpfile.file.write(" n^2;\n") + sage: gpfile.file.write("}\n") + sage: gpfile.file.write("polcyclo(5)\n") + sage: gpfile.file.flush() + sage: pari.read(gpfile.name) + x^4 + x^3 + x^2 + x + 1 + sage: pari('mysquare(12)') + 144 + +Constants:: + + sage: pari.euler() + 0.577215664901533 + sage: pari.euler(precision=100).python() + 0.577215664901532860606512090082... + sage: pari.pi() + 3.14159265358979 + sage: pari.pi(precision=100).python() + 3.1415926535897932384626433832... + +Polynomial functions:: + + sage: R. = PolynomialRing(ZZ) + sage: pari(2*x^2 + 2).content() + 2 + sage: pari("4*x^3 - 2*x/3 + 2/5").content() + 2/15 + + sage: x = pari('y^8+6*y^6-27*y^5+1/9*y^2-y+1') + sage: x.newtonpoly(3) + [1, 1, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] + + sage: f = pari("x^2 + y^3 + x*y") + sage: f + x^2 + y*x + y^3 + sage: f.polcoeff(1) + y + sage: f.polcoeff(3) + 0 + sage: f.polcoeff(3, "y") + 1 + sage: f.polcoeff(1, "y") + x + + sage: pari("x^2 + 1").poldisc() + -4 + + sage: pari.pollegendre(7) + 429/16*x^7 - 693/16*x^5 + 315/16*x^3 - 35/16*x + sage: pari.pollegendre(7, 'z') + 429/16*z^7 - 693/16*z^5 + 315/16*z^3 - 35/16*z + sage: pari.pollegendre(0) + 1 + + sage: pari.polcyclo(8) + x^4 + 1 + sage: pari.polcyclo(7, 'z') + z^6 + z^5 + z^4 + z^3 + z^2 + z + 1 + sage: pari.polcyclo(1) + x - 1 + +Power series:: + + sage: f = pari('x+x^2+x^3+O(x^4)'); f + x + x^2 + x^3 + O(x^4) + sage: g = f.serreverse(); g + x - x^2 + x^3 + O(x^4) + sage: f.subst('x',g) + x + O(x^4) + sage: g.subst('x',f) + x + O(x^4) + +Random seed:: + + sage: a = pari.getrand() + sage: a.type() + 't_INT' + +Constructors:: + + sage: v = pari([1,2,3]) + sage: v + [1, 2, 3] + sage: v.type() + 't_VEC' + sage: w = v.List() + sage: w + List([1, 2, 3]) + sage: w.type() + 't_LIST' + + sage: x = pari(5) + sage: x.type() + 't_INT' + sage: y = x.Mat() + sage: y + Mat(5) + sage: y.type() + 't_MAT' + sage: x = pari('[1,2;3,4]') + sage: x.type() + 't_MAT' + sage: x = pari('[1,2,3,4]') + sage: x.type() + 't_VEC' + sage: y = x.Mat() + sage: y + Mat([1, 2, 3, 4]) + sage: y.type() + 't_MAT' + + sage: v = pari('[1,2;3,4]').Vec(); v + [[1, 3]~, [2, 4]~] + sage: v.Mat() + [1, 2; 3, 4] + sage: v = pari('[1,2;3,4]').Col(); v + [[1, 2], [3, 4]]~ + sage: v.Mat() + [1, 2; 3, 4] + + sage: z = pari(3) + sage: x = z.Mod(pari(7)) + sage: x + Mod(3, 7) + sage: x^2 + Mod(2, 7) + sage: x^100 + Mod(4, 7) + sage: x.type() + 't_INTMOD' + sage: f = pari("x^2 + x + 1") + sage: g = pari("x") + sage: a = g.Mod(f) + sage: a + Mod(x, x^2 + x + 1) + sage: a*a + Mod(-x - 1, x^2 + x + 1) + sage: a.type() + 't_POLMOD' + + sage: v = pari("[1,2,3,4]") + sage: f = v.Pol() + sage: f + x^3 + 2*x^2 + 3*x + 4 + sage: f*f + x^6 + 4*x^5 + 10*x^4 + 20*x^3 + 25*x^2 + 24*x + 16 + + sage: v = pari("[1,2;3,4]") + sage: v.Pol() + [1, 3]~*x + [2, 4]~ + + sage: v = pari("[1,2,3,4]") + sage: f = v.Polrev() + sage: f + 4*x^3 + 3*x^2 + 2*x + 1 + sage: v.Pol() + x^3 + 2*x^2 + 3*x + 4 + sage: v.Polrev('y') + 4*y^3 + 3*y^2 + 2*y + 1 + + sage: f + 4*x^3 + 3*x^2 + 2*x + 1 + sage: f.Polrev() + 4*x^3 + 3*x^2 + 2*x + 1 + sage: v = pari("[1,2;3,4]") + sage: v.Polrev() + [2, 4]~*x + [1, 3]~ + + sage: pari(3).Qfb(7, 1) + Qfb(3, 7, 1, 0.E-19) + sage: pari(3).Qfb(7, 2) + Traceback (most recent call last): + ... + PariError: domain error in Qfb: issquare(disc) = 1 + + sage: pari([1,5,2]).Set() + [1, 2, 5] + sage: pari([]).Set() + [] + sage: pari([1,1,-1,-1,3,3]).Set() + [-1, 1, 3] + sage: pari(1).Set() + [1] + sage: pari('1/(x*y)').Set() + [1/(y*x)] + sage: pari('["bc","ab","bc"]').Set() + ["ab", "bc"] + + sage: pari([65,66,123]).Strchr() + "AB{" + sage: pari('"Sage"').Vecsmall() + Vecsmall([83, 97, 103, 101]) + sage: _.Strchr() + "Sage" + sage: pari([83, 97, 103, 101]).Strchr() + "Sage" + +Basic functions:: + + sage: pari(0).binary() + [] + sage: pari(-5).binary() + [1, 0, 1] + sage: pari(5).binary() + [1, 0, 1] + sage: pari(2005).binary() + [1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1] + sage: pari('"2"').binary() + Traceback (most recent call last): + ... + PariError: incorrect type in binary (t_STR) + + sage: pari(1.4).ceil() + 2 + sage: pari(-1.4).ceil() + -1 + sage: pari(3/4).ceil() + 1 + sage: x = SR.symbol('x') + sage: pari(x).ceil() + x + sage: pari((x^2+x+1)/x).ceil() + x + 1 + sage: pari(x^2+5*x+2.5).ceil() + x^2 + 5*x + 2.50000000000000 + + sage: x = pari(-2).Mod(5) + sage: x.centerlift() + -2 + sage: x.lift() + 3 + sage: f = pari('x-1').Mod('x^2 + 1') + sage: f.centerlift() + x - 1 + sage: f.lift() + x - 1 + sage: f = pari('x-y').Mod('x^2+1') + sage: f + Mod(x - y, x^2 + 1) + sage: f.centerlift('x') + x - y + sage: f.centerlift('y') + Mod(x - y, x^2 + 1) + sage: pari("Mod(3,5)").lift_centered() + -2 + + sage: pari([0,1,2,3,4]).component(1) + 0 + sage: pari([0,1,2,3,4]).component(2) + 1 + sage: pari([0,1,2,3,4]).component(4) + 3 + sage: pari('x^3 + 2').component(1) + 2 + sage: pari('x^3 + 2').component(2) + 0 + sage: pari('x^3 + 2').component(4) + 1 + sage: pari('x').component(0) + Traceback (most recent call last): + ... + PariError: non-existent component: index < 1 + + sage: pari('x+1').conj() + x + 1 + sage: pari('x+I').conj() + x - I + sage: pari('1/(2*x+3*I)').conj() + 1/(2*x - 3*I) + sage: pari([1,2,'2-I','Mod(x,x^2+1)', 'Mod(x,x^2-2)']).conj() + [1, 2, 2 + I, Mod(-x, x^2 + 1), Mod(-x, x^2 - 2)] + sage: pari('Mod(x,x^2-2)').conj() + Mod(-x, x^2 - 2) + sage: pari('Mod(x,x^3-3)').conj() + Traceback (most recent call last): + ... + PariError: incorrect type in gconj (t_POLMOD) + + sage: pari('Mod(1+x,x^2-2)').conjvec() + [-0.414213562373095, 2.41421356237310]~ + sage: pari('Mod(x,x^3-3)').conjvec() + [1.44224957030741, -0.721124785153704 - 1.24902476648341*I, -0.721124785153704 + 1.24902476648341*I]~ + sage: pari('Mod(1+x,x^2-2)').conjvec(precision=192)[0].sage() + -0.414213562373095048801688724209698078569671875376948073177 + + sage: pari('5/9').denominator() + 9 + sage: pari('(x+1)/(x-2)').denominator() + x - 2 + sage: pari('2/3 + 5/8*x + 7/3*x^2 + 1/5*y').denominator() + 1 + sage: pari('2/3*x').denominator() + 1 + sage: pari('[2/3, 5/8, 7/3, 1/5]').denominator() + 120 + + sage: pari(5/9).floor() + 0 + sage: pari(11/9).floor() + 1 + sage: pari(1.17).floor() + 1 + sage: pari([1.5,2.3,4.99]).floor() + [1, 2, 4] + sage: pari([[1.1,2.2],[3.3,4.4]]).floor() + [[1, 2], [3, 4]] + sage: x = SR.symbol('x') + sage: pari(x).floor() + x + sage: pari((x^2+x+1)/x).floor() + x + 1 + sage: pari(x^2+5*x+2.5).floor() + x^2 + 5*x + 2.50000000000000 + sage: pari('"hello world"').floor() + Traceback (most recent call last): + ... + PariError: incorrect type in gfloor (t_STR) + + sage: pari(1.75).frac() + 0.750000000000000 + sage: pari(sqrt(2)).frac() + 0.414213562373095 + sage: pari('sqrt(-2)').frac() + Traceback (most recent call last): + ... + PariError: incorrect type in gfloor (t_COMPLEX) + + sage: pari('1+2*I').imag() + 2 + sage: pari(sqrt(-2)).imag() + 1.41421356237310 + sage: pari('x+I').imag() + 1 + sage: pari('x+2*I').imag() + 2 + sage: pari('(1+I)*x^2+2*I').imag() + x^2 + 2 + sage: pari('[1,2,3] + [4*I,5,6]').imag() + [4, 0, 0] + + sage: x = pari("x") + sage: a = x.Mod('x^3 + 17*x + 3') + sage: a + Mod(x, x^3 + 17*x + 3) + sage: b = a^4; b + Mod(-17*x^2 - 3*x, x^3 + 17*x + 3) + sage: b.lift() + -17*x^2 - 3*x + + sage: pari(pi).sign() + 1 + sage: pari(0).sign() + 0 + sage: pari(-1/2).sign() + -1 + sage: pari(I).sign() + Traceback (most recent call last): + ... + PariError: incorrect type in gsigne (t_COMPLEX) + + sage: y = pari('y') + sage: x = pari('9') + y - y + sage: x + 9 + sage: x.type() + 't_POL' + sage: x.factor() + matrix(0,2) + sage: pari('9').factor() + Mat([3, 2]) + sage: x.simplify() + 9 + sage: x.simplify().factor() + Mat([3, 2]) + sage: x = pari('1.5 + 0*I') + sage: x.type() + 't_REAL' + sage: x.simplify() + 1.50000000000000 + sage: y = x.simplify() + sage: y.type() + 't_REAL' + + sage: pari(2).sqr() + 4 + sage: pari("1+O(2^5)").sqr() + 1 + O(2^6) + sage: pari("1+O(2^5)")*pari("1+O(2^5)") + 1 + O(2^5) + sage: x = pari("1+O(2^5)"); x*x + 1 + O(2^6) + + sage: x = pari("x"); y = pari("y") + sage: f = pari('x^3 + 17*x + 3') + sage: f.subst(x, y) + y^3 + 17*y + 3 + sage: f.subst(x, "z") + z^3 + 17*z + 3 + sage: f.subst(x, "z")^2 + z^6 + 34*z^4 + 6*z^3 + 289*z^2 + 102*z + 9 + sage: f.subst(x, "x+1") + x^3 + 3*x^2 + 20*x + 21 + sage: f.subst(x, "xyz") + xyz^3 + 17*xyz + 3 + sage: f.subst(x, "xyz")^2 + xyz^6 + 34*xyz^4 + 6*xyz^3 + 289*xyz^2 + 102*xyz + 9 + + sage: pari(9).valuation(3) + 2 + sage: pari(9).valuation(9) + 1 + sage: x = pari(9).Mod(27); x.valuation(3) + 2 + sage: pari('5/3').valuation(3) + -1 + sage: pari('9 + 3*x + 15*x^2').valuation(3) + 1 + sage: pari([9,3,15]).valuation(3) + 1 + sage: pari('9 + 3*x + 15*x^2 + O(x^5)').valuation(3) + 1 + sage: pari('x^2*(x+1)^3').valuation(pari('x+1')) + 3 + sage: pari('x + O(x^5)').valuation('x') + 1 + sage: pari('2*x^2 + O(x^5)').valuation('x') + 2 + sage: pari(0).valuation(3) + +oo + + sage: pari('x^2 + x -2').variable() + x + sage: pari('1+2^3 + O(2^5)').variable() + 2 + sage: pari('x+y0').variable() + x + sage: pari('y0+z0').variable() + y0 + +Bitwise functions:: + + sage: pari(8).bitand(4) + 0 + sage: pari(8).bitand(8) + 8 + sage: pari(6).binary() + [1, 1, 0] + sage: pari(7).binary() + [1, 1, 1] + sage: pari(6).bitand(7) + 6 + sage: pari(19).bitand(-1) + 19 + sage: pari(-1).bitand(-1) + -1 + + sage: pari(10).bitneg() + -11 + sage: pari(1).bitneg() + -2 + sage: pari(-2).bitneg() + 1 + sage: pari(-1).bitneg() + 0 + sage: pari(569).bitneg() + -570 + sage: pari(569).bitneg(10) + 454 + sage: 454 % 2^10 + 454 + sage: -570 % 2^10 + 454 + + sage: pari(14).bitnegimply(0) + 14 + sage: pari(8).bitnegimply(8) + 0 + sage: pari(8+4).bitnegimply(8) + 4 + + sage: pari(14).bitor(0) + 14 + sage: pari(8).bitor(4) + 12 + sage: pari(12).bitor(1) + 13 + sage: pari(13).bitor(1) + 13 + + sage: pari(6).bitxor(4) + 2 + sage: pari(0).bitxor(4) + 4 + sage: pari(6).bitxor(0) + 6 + +Transcendental functions:: + + sage: x = pari("-27.1") + sage: x.abs() + 27.1000000000000 + sage: pari('1 + I').abs(precision=128).sage() + 1.4142135623730950488016887242096980786 + sage: pari('x-1.2*x^2').abs() + 1.20000000000000*x^2 - x + sage: pari('-2 + t + O(t^2)').abs() + 2 - t + O(t^2) + + sage: pari(0.5).acos() + 1.04719755119660 + sage: pari(1/2).acos() + 1.04719755119660 + sage: pari(1.1).acos() + 0.443568254385115*I + sage: C. = ComplexField() + sage: pari(1.1+i).acos() + 0.849343054245252 - 1.09770986682533*I + + sage: pari(2).acosh() + 1.31695789692482 + sage: pari(0).acosh() + 1.57079632679490*I + sage: C. = ComplexField() + sage: pari(i).acosh() + 0.881373587019543 + 1.57079632679490*I + + sage: pari(2).agm(2) + 2.00000000000000 + sage: pari(0).agm(1) + 0 + sage: pari(1).agm(2) + 1.45679103104691 + sage: C. = ComplexField() + sage: pari(1+i).agm(-3) + -0.964731722290876 + 1.15700282952632*I + + sage: C. = ComplexField() + sage: pari(2+i).arg() + 0.463647609000806 + + sage: pari(pari(0.5).sin()).asin() + 0.500000000000000 + sage: pari(2).asin() + 1.57079632679490 - 1.31695789692482*I + + sage: pari(2).asinh() + 1.44363547517881 + sage: C. = ComplexField() + sage: pari(2+i).asinh() + 1.52857091948100 + 0.427078586392476*I + + sage: pari(1).atan() + 0.785398163397448 + sage: C. = ComplexField() + sage: pari(1.5+i).atan() + 1.10714871779409 + 0.255412811882995*I + + sage: pari(0).atanh() + 0.E-19 + sage: pari(2).atanh() + 0.549306144334055 - 1.57079632679490*I + + sage: pari(2).besselh1(3) + 0.486091260585891 - 0.160400393484924*I + sage: pari(2).besselh2(3) + 0.486091260585891 + 0.160400393484924*I + sage: pari(2).besselj(3) + 0.486091260585891 + sage: pari(2).besseljh(3) + 0.412710032209716 + sage: pari(2).besseli(3) + 2.24521244092995 + sage: C. = ComplexField() + sage: pari(2).besseli(3+i) + 1.12539407613913 + 2.08313822670661*I + sage: C. = ComplexField() + sage: pari(2+i).besseln(3) + -0.280775566958244 - 0.486708533223726*I + + sage: pari(1.5).cos() + 0.0707372016677029 + sage: C. = ComplexField() + sage: pari(1+i).cos() + 0.833730025131149 - 0.988897705762865*I + sage: pari('x+O(x^8)').cos() + 1 - 1/2*x^2 + 1/24*x^4 - 1/720*x^6 + 1/40320*x^8 + O(x^9) + + sage: pari(1.5).cosh() + 2.35240961524325 + sage: C. = ComplexField() + sage: pari(1+i).cosh() + 0.833730025131149 + 0.988897705762865*I + sage: pari('x+O(x^8)').cosh() + 1 + 1/2*x^2 + 1/24*x^4 + 1/720*x^6 + O(x^8) + + sage: pari(5).cotan() + -0.295812915532746 + sage: x = RR(pi) + sage: pari(x).cotan() # random + -8.17674825 E15 + + sage: pari(1).dilog() + 1.64493406684823 + sage: C. = ComplexField() + sage: pari(1+i).dilog() + 0.616850275068085 + 1.46036211675312*I + + sage: pari(1).erfc() + 0.157299207050285 + + sage: C. = ComplexField() + sage: pari(i).eta() + 0.998129069925959 + + sage: pari(0).exp() + 1.00000000000000 + sage: pari(1).exp() + 2.71828182845905 + sage: pari('x+O(x^8)').exp() + 1 + x + 1/2*x^2 + 1/6*x^3 + 1/24*x^4 + 1/120*x^5 + 1/720*x^6 + 1/5040*x^7 + O(x^8) + + sage: pari(2).gamma() + 1.00000000000000 + sage: pari(5).gamma() + 24.0000000000000 + sage: C. = ComplexField() + sage: pari(1+i).gamma() + 0.498015668118356 - 0.154949828301811*I + sage: pari(-1).gamma() + Traceback (most recent call last): + ... + PariError: domain error in gamma: argument = non-positive integer + + sage: pari(2).gammah() + 1.32934038817914 + sage: pari(5).gammah() + 52.3427777845535 + sage: C. = ComplexField() + sage: pari(1+i).gammah() + 0.575315188063452 + 0.0882106775440939*I + + sage: pari(1).hyperu(2,3) + 0.333333333333333 + + sage: C. = ComplexField() + sage: pari(1+i).incgam(3-i) + -0.0458297859919946 + 0.0433696818726677*I + sage: pari(1).incgamc(2) + 0.864664716763387 + + sage: pari(5).log() + 1.60943791243410 + sage: C. = ComplexField() + sage: pari(i).log() + 0.E-19 + 1.57079632679490*I + + sage: pari(100).lngamma() + 359.134205369575 + sage: pari(100).log_gamma() + 359.134205369575 + + sage: pari(1).psi() + -0.577215664901533 + + sage: pari(1).sin() + 0.841470984807897 + sage: C. = ComplexField() + sage: pari(1+i).sin() + 1.29845758141598 + 0.634963914784736*I + + sage: pari(0).sinh() + 0.E-19 + sage: C. = ComplexField() + sage: pari(1+i).sinh() + 0.634963914784736 + 1.29845758141598*I + + sage: pari(2).sqrt() + 1.41421356237310 + + sage: pari(8).sqrtint() + 2 + sage: pari(10^100).sqrtint() + 100000000000000000000000000000000000000000000000000 + + sage: pari(2).tan() + -2.18503986326152 + sage: C. = ComplexField() + sage: pari(i).tan() + 0.761594155955765*I + + sage: pari(1).tanh() + 0.761594155955765 + sage: C. = ComplexField() + sage: z = pari(i); z + 1.00000000000000*I + sage: result = z.tanh() + sage: result.real() <= 1e-18 + True + sage: result.imag() + 1.55740772465490 + + sage: pari('2+O(7^5)').teichmuller() + 2 + 4*7 + 6*7^2 + 3*7^3 + O(7^5) + + sage: pari(0.5).theta(2) + 1.63202590295260 + + sage: pari(0.5).thetanullk(1) + 0.548978532560341 + + sage: C. = ComplexField() + sage: pari(i).weber() + 1.18920711500272 + sage: pari(i).weber(1) + 1.09050773266526 + sage: pari(i).weber(2) + 1.09050773266526 + + sage: pari(2).zeta() + 1.64493406684823 + sage: x = RR(pi)^2/6 + sage: pari(x) + 1.64493406684823 + sage: pari(3).zeta() + 1.20205690315959 + sage: pari('1+5*7+2*7^2+O(7^3)').zeta() + 4*7^-2 + 5*7^-1 + O(7^0) + +Linear algebra:: + + sage: pari('[1,2,3; 4,5,6; 7,8,9]').matadjoint() + [-3, 6, -3; 6, -12, 6; -3, 6, -3] + sage: pari('[a,b,c; d,e,f; g,h,i]').matadjoint() + [(i*e - h*f), (-i*b + h*c), (f*b - e*c); (-i*d + g*f), i*a - g*c, -f*a + d*c; (h*d - g*e), -h*a + g*b, e*a - d*b] + + sage: pari('[1,1;1,-1]').matsolve(pari('[1;0]')) + [1/2; 1/2] + + sage: D = pari('[3,4]~') + sage: B = pari('[1,2]~') + sage: M = pari('[1,2;3,4]') + sage: M.matsolvemod(D, B) + [-2, 0]~ + sage: M.matsolvemod(3, 1) + [-1, 1]~ + sage: M.matsolvemod(pari('[3,0]~'), pari('[1,2]~')) + [6, -4]~ + sage: M2 = pari('[1,10;9,18]') + sage: M2.matsolvemod(3, pari('[2,3]~'), 1) + [[0, -1]~, [-1, -2; 1, -1]] + sage: M2.matsolvemod(9, pari('[2,3]~')) + 0 + sage: M2.matsolvemod(9, pari('[2,45]~'), 1) + [[1, 1]~, [-1, -4; 1, -5]] + + sage: pari('[1,2,3;4,5,6;7,8,9]').matker() + [1; -2; 1] + sage: pari('[1,2,3;4,5,6;7,8,9]').matker(1) + [3; -6; 3] + sage: pari('matrix(3,3,i,j,i)').matker() + [-1, -1; 1, 0; 0, 1] + sage: pari('[1,2,3;4,5,6;7,8,9]*Mod(1,2)').matker() + [Mod(1, 2); Mod(0, 2); Mod(1, 2)] + + sage: pari('[1,2; 3,4]').matdet(0) + -2 + sage: pari('[1,2; 3,4]').matdet(1) + -2 + + sage: pari('[1,2; 3,4]').trace() + 5 + + sage: pari('[1,2,3; 4,5,6; 7,8,9]').mathnf() + [6, 1; 3, 1; 0, 1] + + sage: M = matrix([[1,2,3],[4,5,6],[7,8,11]]) + sage: d = M.det() + sage: pari(M).mathnfmod(d) + [6, 4, 3; 0, 1, 0; 0, 0, 1] + sage: M = matrix([[1,0,0],[0,2,0],[0,0,6]]) + sage: pari(M).mathnfmod(6) + [1, 0, 0; 0, 1, 0; 0, 0, 6] + sage: pari(M).mathnfmod(12) + [1, 0, 0; 0, 2, 0; 0, 0, 6] + + sage: M = matrix([[1,0,0],[0,2,0],[0,0,6]]) + sage: pari(M).mathnfmodid(6) + [1, 0, 0; 0, 2, 0; 0, 0, 6] + sage: pari(M).mathnfmod(6) + [1, 0, 0; 0, 1, 0; 0, 0, 6] + + sage: pari('[1,2,3; 4,5,6; 7,8,9]').matsnf() + [0, 3, 1] + + sage: a = pari('[1,2;3,4]') + sage: a.matfrobenius() + [0, 2; 1, 5] + sage: a.matfrobenius(flag=1) + [x^2 - 5*x - 2] + sage: a.matfrobenius(2) + [[0, 2; 1, 5], [1, -1/3; 0, 1/3]] + sage: v = a.matfrobenius(2) + sage: v[0] + [0, 2; 1, 5] + sage: v[1]^(-1)*v[0]*v[1] + [1, 2; 3, 4] + sage: t = pari('[3, -2, 0, 0; 0, -2, 0, 1; 0, -1, -2, 2; 0, -2, 0, 2]') + sage: t.matfrobenius() + [0, 0, 0, -12; 1, 0, 0, -2; 0, 1, 0, 8; 0, 0, 1, 1] + sage: t.charpoly('x') + x^4 - x^3 - 8*x^2 + 2*x + 12 + sage: t.matfrobenius(1) + [x^4 - x^3 - 8*x^2 + 2*x + 12] + +Quadratic forms:: + + sage: A = Matrix(3,3,[1,2,3,2,5,5,3,5,11]) + sage: A.is_positive_definite() + True + sage: pari(A).qfminim(10, 5).python() + [ + [17 14 15 16 13] + [-4 -3 -3 -3 -2] + 146, 10, [-3 -3 -3 -3 -3] + ] + sage: pari(A).qfminim().python() + [ + [ 5 2 1] + [-1 -1 0] + 6, 1, [-1 0 0] + ] + sage: pari(A.change_ring(RR)).qfminim(5, m=5, flag=2).python() + [ + [ -5 -10 -2 -7 3] + [ 1 2 1 2 0] + 10, 5.00000000000000000, [ 1 2 0 1 -1] + ] + + sage: M = diagonal_matrix([1,1,-1]) + sage: P = M._pari_().qfparam([0,1,-1]); P + [0, -2, 0; 1, 0, -1; -1, 0, -1] + sage: R. = QQ[] + sage: v = P.sage() * vector([x^2, x*y, y^2]); v + (-2*x*y, x^2 - y^2, -x^2 - y^2) + sage: v(x=2, y=1) + (-4, 3, -5) + sage: v(x=3,y=8) + (-48, -55, -73) + sage: 48^2 + 55^2 == 73^2 + True + + sage: M = diagonal_matrix([1,2,3,4,-5]) + sage: M._pari_().qfsolve() + [0, 1, -1, 0, -1]~ + sage: M = diagonal_matrix([4,-9]) + sage: M._pari_().qfsolve() + [6, 4]~ + sage: M = diagonal_matrix([1,1,1,1,1]) + sage: M._pari_().qfsolve() + -1 + sage: M = diagonal_matrix([1,1,-3]) + sage: M._pari_().qfsolve() + 3 + sage: M = diagonal_matrix([1,-42]) + sage: M._pari_().qfsolve() + -2 + sage: M = diagonal_matrix([1,-1,0,0]) + sage: M._pari_().qfsolve().sage() + [0 0] + [0 0] + [1 0] + [0 1] + +Number-theoretical functions:: + + sage: n = pari.set_real_precision(210) + sage: w1 = pari('z1=2-sqrt(26); (z1+I)/(z1-I)') + sage: f = w1.algdep(12); f + 545*x^11 - 297*x^10 - 281*x^9 + 48*x^8 - 168*x^7 + 690*x^6 - 168*x^5 + 48*x^4 - 281*x^3 - 297*x^2 + 545*x + sage: f(w1).abs() < 1.0e-200 + True + sage: f.factor() + [x, 1; x + 1, 2; x^2 + 1, 1; x^2 + x + 1, 1; 545*x^4 - 1932*x^3 + 2790*x^2 - 1932*x + 545, 1] + sage: pari.set_real_precision(n) + 210 + + sage: pari(6).binomial(2) + 15 + sage: pari('x+1').binomial(3) + 1/6*x^3 - 1/6*x + sage: pari('2+x+O(x^2)').binomial(3) + 1/3*x + O(x^2) + + sage: pari(10).eulerphi() + 4 + + sage: x = SR.symbol('x') + sage: pari(10).gcd(15) + 5 + sage: pari([5, 'y']).gcd() + 1 + sage: pari([x, x^2]).gcd() + x + sage: pari(10).lcm(15) + 30 + sage: pari([5, 'y']).lcm() + 5*y + sage: pari([10, x, x^2]).lcm() + 10*x^2 + + sage: pari(20).numbpart() + 627 + sage: pari(100).numbpart() + 190569292 + + sage: pari(10).numdiv() + 4 + + sage: pari(7).primepi() + 4 + sage: pari(100).primepi() + 25 + sage: pari(1000).primepi() + 168 + sage: pari(100000).primepi() + 9592 + sage: pari(0).primepi() + 0 + sage: pari(-15).primepi() + 0 + sage: pari(500509).primepi() + 41581 + sage: pari(10^7).primepi() + 664579 + + sage: pari(4).znprimroot() + Mod(3, 4) + sage: pari(10007^3).znprimroot() + Mod(5, 1002101470343) + sage: pari(2*109^10).znprimroot() + Mod(236736367459211723407, 473472734918423446802) + + sage: pari(0).znstar() + [2, [2], [-1]] + sage: pari(96).znstar() + [32, [8, 2, 2], [Mod(37, 96), Mod(31, 96), Mod(65, 96)]] + sage: pari(-5).znstar() + [4, [4], [Mod(2, 5)]] + +Finite fields:: + + sage: x = GF(2)['x'].gen() + sage: pari(x^2+x+2).ffgen() + x + sage: pari(x^2+x+1).ffgen('a') + a + + sage: pari(7).ffinit(11) + Mod(1, 7)*x^11 + Mod(1, 7)*x^10 + Mod(4, 7)*x^9 + Mod(5, 7)*x^8 + Mod(1, 7)*x^7 + Mod(1, 7)*x^2 + Mod(1, 7)*x + Mod(6, 7) + sage: pari(2003).ffinit(3) + Mod(1, 2003)*x^3 + Mod(1, 2003)*x^2 + Mod(1993, 2003)*x + Mod(1995, 2003) + + sage: k. = GF(2^12) + sage: g = pari(a).ffprimroot() + sage: (g^1234).fflog(g) + 1234 + sage: pari(k(1)).fflog(g) + 0 + sage: b = g^5 + sage: ord = b.fforder(); ord + 819 + sage: (b^555).fflog(b, ord) + 555 + sage: (b^555).fflog(b, (ord, ord.factor()) ) + 555 + + sage: k. = GF(5^80) + sage: g = pari(a).ffprimroot() + sage: g.fforder() + 82718061255302767487140869206996285356581211090087890624 + sage: g.fforder( (5^80-1, factor(5^80-1)) ) + 82718061255302767487140869206996285356581211090087890624 + sage: k(2)._pari_().fforder(o=4) + 4 + +p-adic functions:: + + sage: K = Qp(11,5) + sage: x = K(11^-10 + 5*11^-7 + 11^-6) + sage: y = pari(x) + sage: y.padicprec(11) + -5 + sage: y.padicprec(17) + Traceback (most recent call last): + ... + PariError: inconsistent moduli in padicprec: 11 != 17 + sage: R. = PolynomialRing(Zp(3)) + sage: pol = R([O(3^4), O(3^6), O(3^5)]) + sage: pari(pol).padicprec(3) + 4 + +Elliptic curves:: + + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: x = pari([1,0]) + sage: e.ellisoncurve([1,4]) + False + sage: e.ellisoncurve(x) + True + sage: f = e.ellchangecurve([1,2,3,-1]) + sage: f[:5] # show only first five entries + [6, -2, -1, 17, 8] + sage: x.ellchangepoint([1,2,3,-1]) + [-1, 4] + sage: f.ellisoncurve([-1,4]) + True + + sage: e = pari([0, 5, 2, -1, 1]).ellinit() + sage: e.ellglobalred() + [20144, [1, -2, 0, -1], 1, [2, 4; 1259, 1], [[4, 2, 0, 1], [1, 5, 0, 1]]] + sage: e = pari(EllipticCurve('17a').a_invariants()).ellinit() + sage: e.ellglobalred() + [17, [1, 0, 0, 0], 4, Mat([17, 1]), [[1, 8, 0, 4]]] + + sage: e = pari([0, 1, 1, -2, 0]).ellinit() + sage: e.elladd([1,0], [-1,1]) + [-3/4, -15/8] + + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.ellak(6) + 2 + sage: e.ellak(2005) + 2 + sage: e.ellak(-1) + 0 + sage: e.ellak(0) + 0 + + sage: E = EllipticCurve('389a1') + sage: pari(E).ellanalyticrank() + [2, 1.51863300057685] + + sage: e = pari([0, -1, 1, -10, -20]).ellinit() + sage: e.ellap(2) + -2 + sage: e.ellap(2003) + 4 + + sage: e = pari([1,2,3,4,5]).ellinit() + sage: e.ellglobalred() + [10351, [1, -1, 0, -1], 1, [11, 1; 941, 1], [[1, 5, 0, 1], [1, 5, 0, 1]]] + sage: f = e.ellchangecurve([1,-1,0,-1]) + sage: f[:5] + [1, -1, 0, 4, 3] + + sage: e = pari([0,0,0,-82,0]).ellinit() + sage: e.elleta() + [3.60546360143265, 3.60546360143265*I] + sage: w1, w2 = e.omega() + sage: eta1, eta2 = e.elleta() + sage: w1*eta2 - w2*eta1 + 6.28318530717959*I + + sage: e = pari([0,1,1,-2,0]).ellinit().ellminimalmodel()[0] + sage: e.ellheightmatrix([[1,0], [-1,1]]) + [0.476711659343740, 0.418188984498861; 0.418188984498861, 0.686667083305587] + + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: om = e.omega() + sage: om + [2.49021256085506, -1.97173770155165*I] + sage: om.elleisnum(2) + 10.0672605281120 + sage: om.elleisnum(4) + 112.000000000000 + sage: om.elleisnum(100) + 2.15314248576078 E50 + + sage: e = pari([0,0,0,0,1]).ellinit() + sage: e.elllocalred(7) + [0, 1, [1, 0, 0, 0], 1] + sage: e = pari(EllipticCurve('27a3').a_invariants()).ellinit() + sage: e.elllocalred(3) + [3, 2, [1, -1, 0, 1], 1] + sage: e = pari(EllipticCurve('24a4').a_invariants()).ellinit() + sage: e.elllocalred(2) + [3, 3, [1, 1, 0, 1], 2] + sage: e = pari(EllipticCurve('20a2').a_invariants()).ellinit() + sage: e.elllocalred(2) + [2, 4, [1, 1, 0, 1], 3] + sage: e = pari(EllipticCurve('11a2').a_invariants()).ellinit() + sage: e.elllocalred(11) + [1, 5, [1, 0, 0, 0], 1] + sage: e = pari(EllipticCurve('14a4').a_invariants()).ellinit() + sage: e.elllocalred(2) + [1, 6, [1, 0, 0, 0], 2] + sage: e = pari(EllipticCurve('14a1').a_invariants()).ellinit() + sage: e.elllocalred(2) + [1, 10, [1, 0, 0, 0], 2] + sage: e = pari(EllipticCurve('32a3').a_invariants()).ellinit() + sage: e.elllocalred(2) + [5, -1, [1, 1, 1, 0], 1] + sage: e = pari(EllipticCurve('24a5').a_invariants()).ellinit() + sage: e.elllocalred(2) + [3, -2, [1, 2, 1, 4], 1] + sage: e = pari(EllipticCurve('24a2').a_invariants()).ellinit() + sage: e.elllocalred(2) + [3, -3, [1, 2, 1, 4], 2] + sage: e = pari(EllipticCurve('20a1').a_invariants()).ellinit() + sage: e.elllocalred(2) + [2, -4, [1, 0, 1, 2], 3] + sage: e = pari(EllipticCurve('24a1').a_invariants()).ellinit() + sage: e.elllocalred(2) + [3, -5, [1, 0, 1, 2], 4] + sage: e = pari(EllipticCurve('90c2').a_invariants()).ellinit() + sage: e.elllocalred(3) + [2, -10, [1, 96, 1, 316], 4] + + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: e.elllseries(2.1) + 0.402838047956645 + sage: e.elllseries(1, precision=128) + 6.21952537507477 E-39 + sage: e.elllseries(1, precision=256) + 2.95993347819786 E-77 + sage: e.elllseries(-2) + 0 + sage: e.elllseries(2.1, A=1.1) + 0.402838047956645 + + sage: e = pari(EllipticCurve('65a1').a_invariants()).ellinit() + sage: e.ellorder([0,0]) + 2 + sage: e.ellorder([1,0]) + 0 + + sage: e = pari([0,1,1,-2,0]).ellinit() + sage: e.ellordinate(0) + [0, -1] + sage: e.ellordinate(I) + [0.582203589721741 - 1.38606082464177*I, -1.58220358972174 + 1.38606082464177*I] + sage: e.ellordinate(I, precision=128)[0].sage() + 0.58220358972174117723338947874993600727 - 1.3860608246417697185311834209833653345*I + sage: e.ellordinate(1+3*5^1+O(5^3)) + [4*5 + 5^2 + O(5^3), 4 + 3*5^2 + O(5^3)] + sage: e.ellordinate('z+2*z^2+O(z^4)') + [-2*z - 7*z^2 - 23*z^3 + O(z^4), -1 + 2*z + 7*z^2 + 23*z^3 + O(z^4)] + sage: e.ellordinate(5) + [] + sage: e.ellordinate(5.0) + [11.3427192823270, -12.3427192823270] + + sage: e = pari([0,0,0,1,0]).ellinit() + sage: e.ellpointtoz([0,0]) + 1.85407467730137 + sage: e.ellpointtoz([0]) + 0 + + sage: e = pari([0,0,0,3,0]).ellinit() + sage: p = [1,2] # Point of infinite order + sage: e.ellmul([0,0], 2) + [0] + sage: e.ellmul(p, 2) + [1/4, -7/8] + sage: q = e.ellmul(p, 1+I); q + [-2*I, 1 + I] + sage: e.ellmul(q, 1-I) + [1/4, -7/8] + sage: for D in [-7, -8, -11, -12, -16, -19, -27, -28]: # long time (1s) + ....: hcpol = hilbert_class_polynomial(D) + ....: j = hcpol.roots(multiplicities=False)[0] + ....: t = (1728-j)/(27*j) + ....: E = EllipticCurve([4*t,16*t^2]) + ....: P = E.point([0, 4*t]) + ....: assert(E.j_invariant() == j) + ....: # + ....: # Compute some CM number and its minimal polynomial + ....: # + ....: cm = pari('cm = (3*quadgen(%s)+2)'%D) + ....: cm_minpoly = pari('minpoly(cm)') + ....: # + ....: # Evaluate cm_minpoly(cm)(P), which should be zero + ....: # + ....: e = pari(E) # Convert E to PARI + ....: P2 = e.ellmul(P, cm_minpoly[2]*cm + cm_minpoly[1]) + ....: P0 = e.elladd(e.ellmul(P, cm_minpoly[0]), e.ellmul(P2, cm)) + ....: assert(P0 == E(0)) + + sage: e = pari([0,0,0,-82,0]).ellinit() + sage: e.ellrootno() + -1 + sage: e.ellrootno(2) + 1 + sage: e.ellrootno(1009) + 1 + + sage: e = pari([0,0,0,1,0]).ellinit() + sage: C. = ComplexField() + sage: e.ellsigma(2+i) + 1.43490215804166 + 1.80307856719256*I + + sage: e = pari([0, 1, 1, -2, 0]).ellinit() + sage: e.ellsub([1,0], [-1,1]) + [0, 0] + + sage: e = pari([0,0,0,1,0]).ellinit() + sage: e.ellzeta(1) + 1.06479841295883 + sage: C. = ComplexField() + sage: e.ellzeta(i-1) + -0.350122658523049 - 0.350122658523049*I + + sage: e = pari([0,0,0,1,0]).ellinit() + sage: C. = ComplexField() + sage: e.ellztopoint(1+i) + [0.E-... - 1.02152286795670*I, -0.149072813701096 - 0.149072813701096*I] + sage: e.ellztopoint(0) + [0] + + sage: pari(I).ellj() + 1728.00000000000 + sage: pari(3*I).ellj() + 153553679.396729 + sage: pari('quadgen(-3)').ellj() + 0.E-54 + sage: pari('quadgen(-7)').ellj(precision=256).sage() + -3375.000000000000000000000000000000000000000000000000000000000000000000000000 + sage: pari(-I).ellj() + Traceback (most recent call last): + ... + PariError: domain error in modular function: Im(argument) <= 0 + +Quadratic class numbers:: + + sage: pari(10009).qfbhclassno() + 0 + sage: pari(2).qfbhclassno() + 0 + sage: pari(0).qfbhclassno() + -1/12 + sage: pari(4).qfbhclassno() + 1/2 + sage: pari(3).qfbhclassno() + 1/3 + sage: pari(23).qfbhclassno() + 3 + + sage: pari(-4).qfbclassno() + 1 + sage: pari(-23).qfbclassno() + 3 + sage: pari(-104).qfbclassno() + 6 + sage: pari(109).qfbclassno() + 1 + sage: pari(10001).qfbclassno() + 16 + sage: pari(10001).qfbclassno(flag=1) + 16 + sage: pari(3).qfbclassno() + Traceback (most recent call last): + ... + PariError: domain error in classno2: disc % 4 > 1 + sage: pari(4).qfbclassno() + Traceback (most recent call last): + ... + PariError: domain error in classno2: issquare(disc) = 1 + + sage: pari(-4).quadclassunit() + [1, [], [], 1] + sage: pari(-23).quadclassunit() + [3, [3], [Qfb(2, 1, 3)], 1] + sage: pari(-104).quadclassunit() + [6, [6], [Qfb(5, -4, 6)], 1] + sage: pari(109).quadclassunit() + [1, [], [], 5.56453508676047] + sage: pari(10001).quadclassunit() # random generators + [16, [16], [Qfb(10, 99, -5, 0.E-38)], 5.29834236561059] + sage: pari(10001).quadclassunit()[0] + 16 + sage: pari(10001).quadclassunit()[1] + [16] + sage: pari(10001).quadclassunit()[3] + 5.29834236561059 + sage: pari(3).quadclassunit() + Traceback (most recent call last): + ... + PariError: domain error in Buchquad: disc % 4 > 1 + sage: pari(4).quadclassunit() + Traceback (most recent call last): + ... + PariError: domain error in Buchquad: issquare(disc) = 1 + +General number fields:: + + sage: x = polygen(QQ) + sage: K. = NumberField(x^2 - 1/8) + sage: pari(x^2 - 2).factornf(K.pari_polynomial("a")) + [x + Mod(-a, a^2 - 2), 1; x + Mod(a, a^2 - 2), 1] + + sage: K. = QuadraticField(-23) + sage: p = K.primes_above(3)[0] + sage: K.pari_bnf().bnrclassno(p._pari_bid_()) + 3 + + sage: x = SR.symbol('x') + sage: P = pari(x^6 + 108) + sage: G = P.galoisinit() + sage: G[0] == P + True + sage: len(G[5]) == prod(G[7]) + True + + sage: G = pari(x^6 + 108).galoisinit() + sage: G.galoispermtopol(G[5]) + [x, 1/12*x^4 - 1/2*x, -1/12*x^4 - 1/2*x, 1/12*x^4 + 1/2*x, -1/12*x^4 + 1/2*x, -x] + sage: G.galoispermtopol(G[5][1]) + 1/12*x^4 - 1/2*x + sage: G.galoispermtopol(G[5][1:4]) + [1/12*x^4 - 1/2*x, -1/12*x^4 - 1/2*x, 1/12*x^4 + 1/2*x] + + sage: G = pari(x^4 + 1).galoisinit() + sage: G.galoisfixedfield(G[5][1], flag=2) + [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[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]] + + sage: G = pari(x^6 + 108).galoisinit() + sage: L = G.galoissubgroups() + sage: list(L[0][1]) + [3, 2] + + sage: G = pari(x^6 + 108).galoisinit() + sage: G.galoisisabelian() + 0 + sage: H = G.galoissubgroups()[2] + sage: H.galoisisabelian() + Mat(2) + sage: H.galoisisabelian(flag=1) + 1 + + sage: G = pari(x^6 + 108).galoisinit() + sage: L = G.galoissubgroups() + sage: G.galoisisnormal(L[0]) + 1 + sage: G.galoisisnormal(L[2]) + 0 + + sage: F = QuadraticField(5, 'alpha') + sage: nf = F._pari_() + sage: P = F.ideal(F.gen()) + sage: Q = F.ideal(2) + sage: moduli = pari.matrix(2,2,[P.pari_prime(),4,Q.pari_prime(),4]) + sage: residues = pari.vector(2,[0,1]) + sage: b = F(nf.idealchinese(moduli,residues)) + sage: b.valuation(P) >= 4 + True + sage: (b-1).valuation(Q) >= 2 + True + + sage: F = NumberField(x^3-2, 'alpha') + sage: nf = F._pari_() + sage: x = pari('[1, -1, 2]~') + sage: y = pari('[1, -1, 3]~') + sage: nf.idealcoprime(x, y) + [1, 0, 0]~ + + sage: y = pari('[2, -2, 4]~') + sage: nf.idealcoprime(x, y) + [5/43, 9/43, -1/43]~ + + sage: R. = PolynomialRing(QQ) + sage: K. = NumberField(x^2 + 1) + sage: L = K.pari_nf().ideallist(100) + sage: L[0] # One ideal of norm 1. + [[1, 0; 0, 1]] + sage: L[64] # 4 ideals of norm 65. + [[65, 8; 0, 1], [65, 47; 0, 1], [65, 18; 0, 1], [65, 57; 0, 1]] + + sage: F = NumberField(x^3-2, 'alpha') + sage: nf = F._pari_() + sage: I = pari('[1, -1, 2]~') + sage: bid = nf.idealstar(I) + sage: nf.ideallog(5, bid) + [25]~ + + sage: K. = QuadraticField(-1) + sage: F = pari(K).idealprimedec(5); F + [[5, [-2, 1]~, 1, 1, [2, -1; 1, 2]], [5, [2, 1]~, 1, 1, [-2, -1; 1, -2]]] + sage: F[0].pr_get_p() + 5 + + sage: x = polygen(ZZ) + sage: F = NumberField(x^3 - 2, 'alpha') + sage: nf = F._pari_() + sage: I = pari('[1, -1, 2]~') + sage: nf.idealstar(I) + [[[43, 9, 5; 0, 1, 0; 0, 0, 1], [0]], [42, [42]], Mat([[43, [9, 1, 0]~, 1, 1, [-5, 2, -18; -9, -5, 2; 1, -9, -5]], 1]), [[[[42], [3], [3], [Vecsmall([])], 1]], [[], [], []]], Mat(1)] + + sage: x = polygen(QQ) + sage: K. = NumberField(x^3 - 17) + sage: Kpari = K.pari_nf() + sage: Kpari.getattr('zk') + [1, 1/3*y^2 - 1/3*y + 1/3, y] + sage: Kpari.nfbasistoalg(42) + Mod(42, y^3 - 17) + sage: Kpari.nfbasistoalg("[3/2, -5, 0]~") + Mod(-5/3*y^2 + 5/3*y - 1/6, y^3 - 17) + sage: Kpari.getattr('zk') * pari("[3/2, -5, 0]~") + -5/3*y^2 + 5/3*y - 1/6 + + sage: k. = NumberField(x^2 + 5) + sage: x = 10 + sage: y = a + 1 + sage: pari(k).nfeltdiveuc(x, y) + [2, -2]~ + + sage: x = polygen(ZZ) + sage: k. = NumberField(x^2 + 5) + sage: I = k.ideal(a) + sage: kp = pari(k) + sage: kp.nfeltreduce(12, I.pari_hnf()) + [2, 0]~ + sage: 12 - k(kp.nfeltreduce(12, I.pari_hnf())) in I + True + + sage: x = QQ['x'].0; nf = pari(x^2 + 2).nfinit() + sage: nf.nfgaloisconj() + [-x, x]~ + sage: nf = pari(x^3 + 2).nfinit() + sage: nf.nfgaloisconj() + [x]~ + sage: nf = pari(x^4 + 2).nfinit() + sage: nf.nfgaloisconj() + [-x, x]~ + + sage: x = polygen(QQ) + sage: K. = NumberField(x^3 - x + 1) + sage: pari(K).nfhilbert(t, t + 2) + -1 + sage: P = K.ideal(t^2 + t - 2) # Prime ideal above 5 + sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) + -1 + sage: P = K.ideal(t^2 + 3*t - 1) # Prime ideal above 23, ramified + sage: pari(K).nfhilbert(t, t + 2, P.pari_prime()) + 1 + + sage: F. = NumberField(x^2-x-1) + sage: Fp = pari(F) + sage: A = matrix(F,[[1,2,a,3],[3,0,a+2,0],[0,0,a,2],[3+a,a,0,1]]) + sage: I = [F.ideal(-2*a+1),F.ideal(7), F.ideal(3),F.ideal(1)] + sage: Fp.nfhnf([pari(A),[pari(P) for P in I]]) + [[1, [-969/5, -1/15]~, [15, -2]~, [-1938, -3]~; 0, 1, 0, 0; 0, 0, 1, 0; 0, 0, 0, 1], [[3997, 1911; 0, 7], [15, 6; 0, 3], 1, 1]] + sage: K. = NumberField(x^3-2) + sage: Kp = pari(K) + sage: A = matrix(K,[[1,0,0,5*b],[1,2*b^2,b,57],[0,2,1,b^2-3],[2,0,0,b]]) + sage: I = [K.ideal(2),K.ideal(3+b^2),K.ideal(1),K.ideal(1)] + sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) + [[1, -225, 72, -31; 0, 1, [0, -1, 0]~, [0, 0, -1/2]~; 0, 0, 1, [0, 0, -1/2]~; 0, 0, 0, 1], [[1116, 756, 612; 0, 18, 0; 0, 0, 18], 2, 1, [2, 0, 0; 0, 1, 0; 0, 0, 1]]] + sage: K. = NumberField(x^2+5) + sage: Kp = pari(K) + sage: A = matrix(K,[[1,0,0,5*b],[1,2*b^2,b,57],[0,2,1,b^2-3],[2,0,0,b]]) + sage: I = [K.ideal(2),K.ideal(3+b^2),K.ideal(1),K.ideal(1)] + sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) + [[1, [15, 6]~, [0, -54]~, [113, 72]~; 0, 1, [-4, -1]~, [0, -1]~; 0, 0, 1, 0; 0, 0, 0, 1], [[360, 180; 0, 180], [6, 4; 0, 2], 1, 1]] + sage: A = matrix(K,[[1,0,0,5*b],[1,2*b,b,57],[0,2,1,b-3],[2,0,b,b]]) + sage: I = [K.ideal(2).factor()[0][0],K.ideal(3+b),K.ideal(1),K.ideal(1)] + sage: Kp.nfhnf([pari(A),[pari(P) for P in I]]) + [[1, [7605, 4]~, [5610, 5]~, [7913, -6]~; 0, 1, 0, -1; 0, 0, 1, 0; 0, 0, 0, 1], [[19320, 13720; 0, 56], [2, 1; 0, 1], 1, 1]] + + sage: pari('x^3 - 17').nfinit() + [x^3 - 17, [1, 1], -867, 3, [[1, 1.68006914259990, 2.57128159065824; 1, -0.340034571299952 - 2.65083754153991*I, -1.28564079532912 + 2.22679517779329*I], [1, 1.68006914259990, 2.57128159065824; 1, -2.99087211283986, 0.941154382464174; 1, 2.31080297023995, -3.51243597312241], [1, 2, 3; 1, -3, 1; 1, 2, -4], [3, 1, 0; 1, -11, 17; 0, 17, 0], [51, 0, 16; 0, 17, 3; 0, 0, 1], [17, 0, -1; 0, 0, 3; -1, 3, 2], [51, [-17, 6, -1; 0, -18, 3; 1, 0, -16]], [3, 17]], [2.57128159065824, -1.28564079532912 + 2.22679517779329*I], [1, 1/3*x^2 - 1/3*x + 1/3, x], [1, 0, -1; 0, 0, 3; 0, 1, 1], [1, 0, 0, 0, -4, 6, 0, 6, -1; 0, 1, 0, 1, 1, -1, 0, -1, 3; 0, 0, 1, 0, 2, 0, 1, 0, 1]] + sage: pari('x^2 + 10^100 + 1').nfinit() + [...] + sage: pari('1.0').nfinit() + Traceback (most recent call last): + ... + PariError: incorrect type in checknf [please apply nfinit()] (t_REAL) + + sage: F = NumberField(x^3-2,'alpha') + sage: G = NumberField(x^3-2,'beta') + sage: F._pari_().nfisisom(G._pari_()) + [y] + sage: GG = NumberField(x^3-4,'gamma') + sage: F._pari_().nfisisom(GG._pari_()) + [1/2*y^2] + sage: F._pari_().nfisisom(GG.pari_nf()) + [1/2*y^2] + sage: F.pari_nf().nfisisom(GG._pari_()[0]) + [y^2] + sage: H = NumberField(x^2-2,'alpha') + sage: F._pari_().nfisisom(H._pari_()) + 0 + sage: K. = NumberField(x^2 + x + 1) + sage: L. = NumberField(x^2 + 3) + sage: pari(K).nfisisom(L) + [-1/2*y - 1/2, 1/2*y - 1/2] + + sage: y = QQ['yy'].0; _ = pari(y) # pari has variable ordering rules + sage: x = QQ['zz'].0; nf = pari(x^2 + 2).nfinit() + sage: nf.nfroots(y^2 + 2) + [Mod(-zz, zz^2 + 2), Mod(zz, zz^2 + 2)] + sage: nf = pari(x^3 + 2).nfinit() + sage: nf.nfroots(y^3 + 2) + [Mod(zz, zz^3 + 2)] + sage: nf = pari(x^4 + 2).nfinit() + sage: nf.nfroots(y^4 + 2) + [Mod(-zz, zz^4 + 2), Mod(zz, zz^4 + 2)] + + sage: nf = pari('x^2 + 1').nfinit() + sage: nf.nfrootsof1() + [4, x] + + sage: x = ZZ['xx1'].0; pari(x) + xx1 + sage: y = ZZ['yy1'].0; pari(y) + yy1 + sage: nf = pari(y^2 - 6*y + 24).nfinit() + sage: rnf = nf.rnfinit(x^2 - pari(y)) + sage: P = pari('[[[1, 0]~, [0, 0]~; [0, 0]~, [1, 0]~], [[2, 0; 0, 2], [2, 0; 0, 1/2]]]') + sage: rnf.rnfidealdown(P) + 2 + + sage: f = pari('y^3+y+1') + sage: K = f.nfinit() + sage: x = pari('x'); y = pari('y') + sage: g = x^5 - x^2 + y + sage: L = K.rnfinit(g) + + sage: pari(-23).quadhilbert() + x^3 - x^2 + 1 + sage: pari(145).quadhilbert() + x^4 - 6*x^2 - 5*x - 1 + sage: pari(-12).quadhilbert() # Not fundamental + Traceback (most recent call last): + ... + PariError: domain error in quadray: isfundamental(D) = 0 +""" diff --git a/src/sage/libs/ppl.pyx b/src/sage/libs/ppl.pyx index 3eacd023caa..5333a6fcd3d 100644 --- a/src/sage/libs/ppl.pyx +++ b/src/sage/libs/ppl.pyx @@ -200,6 +200,7 @@ cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": ctypedef size_t PPL_dimension_type "Parma_Polyhedra_Library::dimension_type" ctypedef mpz_class PPL_Coefficient "Parma_Polyhedra_Library::Coefficient" cdef cppclass PPL_Variable "Parma_Polyhedra_Library::Variable" + cdef cppclass PPL_Variables_Set "Parma_Polyhedra_Library::Variables_Set" cdef cppclass PPL_Linear_Expression "Parma_Polyhedra_Library::Linear_Expression" cdef cppclass PPL_Generator "Parma_Polyhedra_Library::Generator" cdef cppclass PPL_Generator_System "Parma_Polyhedra_Library::Generator_System" @@ -218,6 +219,16 @@ cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": bint OK() PPL_dimension_type space_dimension() + cdef cppclass PPL_Variables_Set: + PPL_Variables_Set() + PPL_Variables_Set(PPL_Variable v) + PPL_Variables_Set(PPL_Variable v, PPL_Variable w) + PPL_dimension_type space_dimension() + void insert(PPL_Variable v) + size_t size() + void ascii_dump() + bint OK() + cdef cppclass PPL_Linear_Expression: PPL_Linear_Expression() PPL_Linear_Expression(PPL_Linear_Expression &e) @@ -318,7 +329,7 @@ cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": cdef enum PPL_Degenerate_Element: UNIVERSE, EMPTY - cdef enum PPL_Optimization_Mode: + cdef enum PPL_Optimization_Mode "Parma_Polyhedra_Library::Optimization_Mode": MINIMIZATION, MAXIMIZATION cdef enum MIP_Problem_Status: @@ -415,6 +426,7 @@ cdef extern from "ppl.hh" namespace "Parma_Polyhedra_Library": void add_space_dimensions_and_embed(PPL_dimension_type m) except +ValueError void add_constraint(PPL_Constraint &c) except +ValueError void add_constraints(PPL_Constraint_System &cs) except +ValueError + void add_to_integer_space_dimensions(PPL_Variables_Set &i_vars) except +ValueError void set_objective_function(PPL_Linear_Expression &obj) except +ValueError void set_optimization_mode(PPL_Optimization_Mode mode) PPL_Optimization_Mode optimization_mode() @@ -460,6 +472,7 @@ cdef extern from "ppl_shim.hh": ### Forward declarations ########################### cdef class _mutable_or_immutable(SageObject) cdef class Variable(object) +cdef class Variables_Set(object) cdef class Linear_Expression(object) cdef class Generator(object) cdef class Generator_System(_mutable_or_immutable) @@ -672,7 +685,7 @@ cdef class MIP_Problem(_mutable_or_immutable): def __cinit__(self, PPL_dimension_type dim = 0, *args): """ - The Cython constructor. + Constructor TESTS:: @@ -681,26 +694,55 @@ cdef class MIP_Problem(_mutable_or_immutable): A MIP_Problem Maximize: 0 Subject to constraints + + Check that :trac:`19903` is fixed:: + + sage: from sage.libs.ppl import Variable, Constraint_System, MIP_Problem + sage: x = Variable(0) + sage: y = Variable(1) + sage: cs = Constraint_System() + sage: cs.insert(x + y <= 2) + sage: _ = MIP_Problem(2, cs, 0) + sage: _ = MIP_Problem(2, cs, x) + sage: _ = MIP_Problem(2, None, None) + Traceback (most recent call last): + ... + TypeError: Cannot convert NoneType to sage.libs.ppl.Constraint_System + sage: _ = MIP_Problem(2, cs, 'hey') + Traceback (most recent call last): + ... + TypeError: unable to convert 'hey' to an integer + sage: _ = MIP_Problem(2, cs, x, 'middle') + Traceback (most recent call last): + ... + ValueError: unknown mode 'middle' """ - if len(args) == 0: + cdef Constraint_System cs + cdef Linear_Expression obj + cdef PPL_Optimization_Mode mode + + if not args: self.thisptr = new PPL_MIP_Problem(dim) - elif len(args) == 2: - cs = args[0] - obj = args[1] - self.thisptr = new PPL_MIP_Problem(dim, cs.thisptr[0], obj.thisptr[0], MAXIMIZATION) - elif len(args) == 3: - cs = args[0] - obj = args[1] - - mode = str(args[2]) - if mode == 'maximization': - self.thisptr = new PPL_MIP_Problem(dim, cs.thisptr[0], obj.thisptr[0], MAXIMIZATION) - elif mode == 'minimization': - self.thisptr = new PPL_MIP_Problem(dim, cs.thisptr[0], obj.thisptr[0], MINIMIZATION) - else: - raise ValueError('Unknown value: mode='+str(mode)+'.') + + elif 2 <= len(args) <= 3: + cs = args[0] + try: + obj = args[1] + except TypeError: + obj = Linear_Expression(args[1]) + + mode = MAXIMIZATION + if len(args) == 3: + if args[2] == 'maximization': + mode = MAXIMIZATION + elif args[2] == 'minimization': + mode = MINIMIZATION + else: + raise ValueError('unknown mode {!r}'.format(args[2])) + self.thisptr = new PPL_MIP_Problem(dim, cs.thisptr[0], obj.thisptr[0], mode) + else: - raise ValueError('Cannot initialize with '+str(args)+'.') + raise ValueError('cannot initialize from {!r}'.format(args)) def __dealloc__(self): """ @@ -969,6 +1011,34 @@ cdef class MIP_Problem(_mutable_or_immutable): finally: sig_off() + def add_to_integer_space_dimensions(self, Variables_Set i_vars): + """ + Sets the variables whose indexes are in set `i_vars` to be integer space dimensions. + + EXAMPLES:: + + sage: from sage.libs.ppl import Variable, Variables_Set, Constraint_System, MIP_Problem + sage: x = Variable(0) + sage: y = Variable(1) + sage: cs = Constraint_System() + sage: cs.insert( x >= 0) + sage: cs.insert( y >= 0 ) + sage: cs.insert( 3 * x + 5 * y <= 10 ) + sage: m = MIP_Problem(2) + sage: m.set_objective_function(x + y) + sage: m.add_constraints(cs) + sage: i_vars = Variables_Set(x, y) + sage: m.add_to_integer_space_dimensions(i_vars) + sage: m.optimal_value() + 3 + """ + self.assert_mutable("The MIP_Problem is not mutable!"); + sig_on() + try: + self.thisptr.add_to_integer_space_dimensions(i_vars.thisptr[0]) + finally: + sig_off() + def set_objective_function(self, Linear_Expression obj): """ Sets the objective function to obj. @@ -3746,6 +3816,155 @@ cdef class Variable(object): return _make_Constraint_from_richcmp(self, other, op) +#################################################### +### Variables_Set ################################## +#################################################### + +cdef class Variables_Set(object): + r""" + Wrapper for PPL's ``Variables_Set`` class. + + A set of variables' indexes. + + EXAMPLES: + + Build the empty set of variable indexes:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: Variables_Set() + Variables_Set of cardinality 0 + + Build the singleton set of indexes containing the index of the variable:: + + sage: v123 = Variable(123) + sage: Variables_Set(v123) + Variables_Set of cardinality 1 + + Build the set of variables' indexes in the range from one variable to + another variable:: + + sage: v127 = Variable(127) + sage: Variables_Set(v123,v127) + Variables_Set of cardinality 5 + """ + + cdef PPL_Variables_Set *thisptr + + def __cinit__(self, *args): + """ + The Cython constructor. + + See :class:`Variables_Set` for documentation. + + TESTS:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: Variables_Set() + Variables_Set of cardinality 0 + """ + if len(args)==0: + self.thisptr = new PPL_Variables_Set() + elif len(args)==1: + v = args[0] + self.thisptr = new PPL_Variables_Set(v.thisptr[0]) + elif len(args)==2: + v = args[0] + w = args[1] + self.thisptr = new PPL_Variables_Set(v.thisptr[0], w.thisptr[0]) + + def __dealloc__(self): + """ + The Cython destructor + """ + del self.thisptr + + def OK(self): + """ + Checks if all the invariants are satisfied. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: v123 = Variable(123) + sage: S = Variables_Set(v123) + sage: S.OK() + True + """ + return self.thisptr.OK() + + def space_dimension(self): + r""" + Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in the set. + + OUPUT: + + Integer. + + EXAMPLES:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: v123 = Variable(123) + sage: S = Variables_Set(v123) + sage: S.space_dimension() + 124 + """ + return self.thisptr.space_dimension() + + def insert(self, Variable v): + r""" + Inserts the index of variable `v` into the set. + + EXAMPLES:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: S = Variables_Set() + sage: v123 = Variable(123) + sage: S.insert(v123) + sage: S.space_dimension() + 124 + """ + self.thisptr.insert(v.thisptr[0]) + + def ascii_dump(self): + r""" + Write an ASCII dump to stderr. + + EXAMPLES:: + + sage: sage_cmd = 'from sage.libs.ppl import Variable, Variables_Set\n' + sage: sage_cmd += 'v123 = Variable(123)\n' + sage: sage_cmd += 'S = Variables_Set(v123)\n' + sage: sage_cmd += 'S.ascii_dump()\n' + sage: from sage.tests.cmdline import test_executable + sage: (out, err, ret) = test_executable(['sage', '-c', sage_cmd], timeout=100) # long time, indirect doctest + sage: print err # long time + + variables( 1 ) + 123 + """ + self.thisptr.ascii_dump() + + def __repr__(self): + """ + Return a string representation. + + OUTPUT: + + String. + + EXAMPLES:: + + sage: from sage.libs.ppl import Variable, Variables_Set + sage: S = Variables_Set() + sage: S.__repr__() + 'Variables_Set of cardinality 0' + """ + return 'Variables_Set of cardinality {}'.format(self.thisptr.size()) + #################################################### ### Linear_Expression ############################## #################################################### diff --git a/src/sage/libs/ratpoints.pyx b/src/sage/libs/ratpoints.pyx index b1a4e5de8c1..c9182abad3a 100644 --- a/src/sage/libs/ratpoints.pyx +++ b/src/sage/libs/ratpoints.pyx @@ -2,7 +2,7 @@ r""" Hyperelliptic Curve Point Finding, via ratpoints. """ -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" @@ -15,9 +15,9 @@ cdef int process(long x, long z, mpz_t y, void *info0, int *quit): if plist.array_size == plist.num_points: i = plist.array_size plist.array_size *= 2 - plist.xes = sage_realloc(plist.xes, plist.array_size * sizeof(long)) - plist.ys = sage_realloc(plist.ys, plist.array_size * sizeof(mpz_t)) - plist.zs = sage_realloc(plist.zs, plist.array_size * sizeof(long)) + plist.xes = sig_realloc(plist.xes, plist.array_size * sizeof(long)) + plist.ys = sig_realloc(plist.ys, plist.array_size * sizeof(mpz_t)) + plist.zs = sig_realloc(plist.zs, plist.array_size * sizeof(long)) while i < plist.array_size: mpz_init(plist.ys[i]) i += 1 @@ -141,19 +141,19 @@ def ratpoints(list coeffs, long H, verbose=False, long max=0, # Set the soefficient array: coeffs = [Integer(a) for a in coeffs] args.degree = len(coeffs)-1 - args.cof = sage_malloc((args.degree+1) * sizeof(mpz_t)) + args.cof = sig_malloc((args.degree+1) * sizeof(mpz_t)) # Create an array to hold the points found: - plist = sage_malloc(sizeof(point_list)) + plist = sig_malloc(sizeof(point_list)) if max == 0: plist.array_size = 64 else: plist.array_size = max - plist.xes = sage_malloc(plist.array_size * sizeof(long)) - plist.ys = sage_malloc(plist.array_size * sizeof(mpz_t)) + plist.xes = sig_malloc(plist.array_size * sizeof(long)) + plist.ys = sig_malloc(plist.array_size * sizeof(mpz_t)) for i from 0 <= i < plist.array_size: mpz_init(plist.ys[i]) - plist.zs = sage_malloc(plist.array_size * sizeof(long)) + plist.zs = sig_malloc(plist.array_size * sizeof(long)) plist.num_points = 0 plist.max_num_points = max @@ -162,7 +162,7 @@ def ratpoints(list coeffs, long H, verbose=False, long max=0, # Set the intervals to be searched, including any specified: args.num_inter = len(intervals) - args.domain = sage_malloc((args.num_inter + args.degree) * sizeof(ratpoints_interval)) + args.domain = sig_malloc((args.num_inter + args.degree) * sizeof(ratpoints_interval)) for i,I in enumerate(intervals): args.domain[i].low = I[0] args.domain[i].up = I[1] @@ -199,8 +199,8 @@ def ratpoints(list coeffs, long H, verbose=False, long max=0, for i from 0 <= i <= args.degree: mpz_clear(args.cof[i]) - sage_free(args.cof) - sage_free(args.domain) + sig_free(args.cof) + sig_free(args.domain) cdef list L = [] for i from 0 <= i < plist.num_points: @@ -214,10 +214,10 @@ def ratpoints(list coeffs, long H, verbose=False, long max=0, for i from 0 <= i < plist.array_size: mpz_clear(plist.ys[i]) - sage_free(plist.xes) - sage_free(plist.ys) - sage_free(plist.zs) - sage_free(plist) + sig_free(plist.xes) + sig_free(plist.ys) + sig_free(plist.zs) + sig_free(plist) return L @@ -243,7 +243,7 @@ cdef int ratpoints_mpz_exists_only(mpz_t *coeffs, long H, int degree, bint verbo assert degree <= RATPOINTS_MAX_DEGREE args.degree = degree args.cof = coeffs - args.domain = sage_malloc(2*args.degree * sizeof(ratpoints_interval)) + args.domain = sig_malloc(2*args.degree * sizeof(ratpoints_interval)) args.height = H args.num_inter = 0 args.b_low = 1 @@ -258,7 +258,7 @@ cdef int ratpoints_mpz_exists_only(mpz_t *coeffs, long H, int degree, bint verbo sig_on() total = find_points(&args, process_exists_only, (&info_s)) sig_off() - sage_free(args.domain) + sig_free(args.domain) if total == RATPOINTS_NON_SQUAREFREE: raise RuntimeError('Polynomial must be square-free') if total == RATPOINTS_BAD_ARGS: diff --git a/src/sage/libs/singular/decl.pxd b/src/sage/libs/singular/decl.pxd index 37cc222c852..44521b27d73 100644 --- a/src/sage/libs/singular/decl.pxd +++ b/src/sage/libs/singular/decl.pxd @@ -25,12 +25,6 @@ AUTHOR: from sage.libs.gmp.types cimport mpz_t, mpz_ptr -cdef extern from "ccobject.h": - pass - -cdef extern from "stdlib.h": - void delete "delete" (void *ptr) - cdef extern from "factor.h": cdef int libfac_interruptflag @@ -264,7 +258,9 @@ cdef extern from "libsingular.h": # integer vectors - ctypedef struct intvec: + cdef cppclass intvec: + intvec() + intvec(int, int, int) int *(*ivGetVec)() # this is internal actually int (*rows)() int (*cols)() @@ -372,6 +368,7 @@ cdef extern from "libsingular.h": cdef ring *currRing cdef ideal *currQuotient + # omalloc bin for numbers cdef omBin *rnumber_bin @@ -1005,13 +1002,13 @@ cdef extern from "libsingular.h": void setFlag(leftv *A, int F) void resetFlag(leftv *A, int F) -cdef extern from "prCopy.h": +cdef extern from "singular/prCopy.h": poly *prCopyR_NoSort(poly *p, ring *r, ring *dest_r) poly *prCopyR(poly *p, ring *r, ring *dest_r) cdef int LANG_TOP -# Non-commutative functions + # Non-commutative functions ctypedef enum nc_type: nc_error # Something's gone wrong! nc_general # yx=q xy+... @@ -1022,44 +1019,37 @@ cdef extern from "prCopy.h": nc_exterior # -cdef extern from "gring.h": +cdef extern from "singular/gring.h": void ncRingType(ring *, nc_type) nc_type ncRingType_get "ncRingType" (ring *) int nc_CallPlural(matrix* CC, matrix* DD, poly* CN, poly* DN, ring* r) bint nc_SetupQuotient(ring *, ring *, bint) -cdef extern from "sca.h": +cdef extern from "singular/sca.h": void sca_p_ProcsSet(ring *, p_Procs_s *) void scaFirstAltVar(ring *, int) void scaLastAltVar(ring *, int) -cdef extern from "ring.h": +cdef extern from "singular/ring.h": bint rIsPluralRing(ring* r) void rPrint "rWrite"(ring* r) char* rOrderingString "rOrdStr"(ring* r) -# void rDebugPrint(ring* r) void pDebugPrint "p_DebugPrint" (poly*p, ring* r) -cdef extern from "stairc.h": +cdef extern from "singular/stairc.h": # Computes the monomial basis for R[x]/I ideal *scKBase(int deg, ideal *s, ideal *Q) -cdef extern from "lists.h": +cdef extern from "singular/lists.h": ctypedef struct lists "slists": int nr leftv *m void (*Init)(int n) -cdef extern from "kstd1.h": +cdef extern from "singular/kstd1.h": cdef extern int Kstd1_deg # degBound, default 0 cdef extern int Kstd1_mu # multBound, default 0 -cdef extern from "intvec.h": - # for the moment we need only new, as we use the cleanup of sleftv - # to get rid of it again - intvec* intvec_new "New"() - intvec* intvec_new_int3 "new intvec"(int, int, int) - -cdef extern from "syz.h": +cdef extern from "singular/syz.h": ctypedef struct syStrategy "ssyStrategy": short references diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 4291ff4bc75..74ecee3b53f 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -485,7 +485,6 @@ def all_vectors(s): return False return True - cdef class Converter(SageObject): """ A :class:`Converter` interfaces between Sage objects and Singular @@ -878,9 +877,8 @@ cdef class Converter(SageObject): Append ``a`` to the list as intvec. """ s = len(a) - cdef intvec *iv=intvec_new() + cdef intvec *iv = new intvec() iv.resize(s) - #new intvec(s); for i in xrange(s): iv.ivGetVec()[i]=a[i] @@ -908,8 +906,7 @@ cdef class Converter(SageObject): """ cdef int nrows = a.nrows() cdef int ncols = a.ncols() - cdef intvec *iv=intvec_new_int3(nrows, ncols, 0) - #new intvec(s); + cdef intvec *iv = new intvec(nrows, ncols, 0) for i in xrange(nrows): for j in xrange(ncols): @@ -1159,11 +1156,16 @@ cdef class KernelCallHandler(BaseCallHandler): """ return True +# The Sage ring used as a dummy for singular function calls. +cdef object dummy_ring + cdef class SingularFunction(SageObject): """ The base class for Singular functions either from the kernel or from the library. """ + + def __init__(self, name): """ INPUT: @@ -1209,19 +1211,23 @@ cdef class SingularFunction(SageObject): def __call__(self, *args, ring=None, bint interruptible=True, attributes=None): """ Call this function with the provided arguments ``args`` in the - ring ``R``. + ``ring``. INPUT: - - ``args`` - a list of arguments - - ``ring`` - a multivariate polynomial ring - - ``interruptible`` - if ``True`` pressing Ctrl-C during the + - ``args`` -- a list of arguments + - ``ring`` -- a multivariate polynomial ring + - ``interruptible`` -- if ``True`` pressing Ctrl-C during the execution of this function will interrupt the computation (default: ``True``) - - ``attributes`` - a dictionary of optional Singular + - ``attributes`` -- a dictionary of optional Singular attributes assigned to Singular objects (default: ``None``) + If ``ring`` is not specified, it is guessed from the given arguments. + If this is not possible, then a dummy ring, univariate polynomial ring + over ``QQ``, is used. + EXAMPLE:: sage: from sage.libs.singular.function import singular_function @@ -1234,19 +1240,17 @@ cdef class SingularFunction(SageObject): sage: size(2, ring=P) 1 sage: size(2) - Traceback (most recent call last): - ... - ValueError: Could not detect ring. - sage: size(Ideal([a*b + c, a + 1]), ring=P) + 1 + sage: size(Ideal([a*b + c, a + 1])) 2 sage: size(Ideal([a*b + c, a + 1])) 2 - sage: size(1,2, ring=P) + sage: size(1,2) Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'size': Wrong number of arguments - sage: size('foobar', ring=P) + sage: size('foobar') 6 Show the usage of the optional ``attributes`` parameter:: @@ -1302,10 +1306,16 @@ cdef class SingularFunction(SageObject): sage: triangL(G,attributes={G:{'isSB':1}}) [[e + d + c + b + a, ...]] """ + global dummy_ring + if ring is None: ring = self.common_ring(args, ring) - if not (isinstance(ring, MPolynomialRing_libsingular) or \ - isinstance(ring, NCPolynomialRing_plural)): + if ring is None: + if dummy_ring is None: + from sage.all import QQ, PolynomialRing + dummy_ring = PolynomialRing(QQ,"dummy",1) # seems a reasonable default + ring = dummy_ring + if not (isinstance(ring, MPolynomialRing_libsingular) or isinstance(ring, NCPolynomialRing_plural)): raise TypeError("Cannot call Singular function '%s' with ring parameter of type '%s'"%(self._name,type(ring))) return call_function(self, args, ring, interruptible, attributes) @@ -1326,27 +1336,30 @@ function '%s'. This wrapper takes care of converting Sage datatypes to Singular datatypes and vice versa. In addition to whatever parameters the -underlying Singular function accepts when called this function also +underlying Singular function accepts when called, this function also accepts the following keyword parameters: INPUT: -- ``args`` - a list of arguments -- ``ring`` - a multivariate polynomial ring -- ``interruptible`` - if ``True`` pressing Ctrl-C during the - execution of this function will - interrupt the computation (default: ``True``) -- ``attributes`` - a dictionary of optional Singular - attributes assigned to Singular objects (default: ``None``) +- ``args`` -- a list of arguments +- ``ring`` -- a multivariate polynomial ring +- ``interruptible`` -- if ``True`` pressing Ctrl-C during the + execution of this function will interrupt the computation + (default: ``True``) +- ``attributes`` -- a dictionary of optional Singular attributes + assigned to Singular objects (default: ``None``) -EXAMPLE:: +If ``ring`` is not specified, it is guessed from the given arguments. +If this is not possible, then a dummy ring, univariate polynomial ring +over ``QQ``, is used. + +EXAMPLES:: sage: groebner = sage.libs.singular.function_factory.ff.groebner sage: P. = PolynomialRing(QQ) sage: I = P.ideal(x^2-y, y+x) sage: groebner(I) [x + y, y^2 - y] - sage: triangL = sage.libs.singular.function_factory.ff.triang__lib.triangL sage: P. = PolynomialRing(QQ, order='lex') sage: f1 = 1/2*((x1^2 + 2*x1 - 4)*x2^2 + 2*(x1^2 + x1)*x2 + x1^2) @@ -1374,12 +1387,12 @@ The Singular documentation for '%s' is given below. If ``ring`` is not ``None`` this routine checks whether it is the parent/ring of all members of ``args`` instead. - If no common ring was found a ``ValueError`` is raised. + If no common ring was found, None is returned. INPUT: - - ``args`` - a list of Python objects - - ``ring`` - an optional ring to check + - ``args`` -- a list of Python objects + - ``ring`` -- an optional ring to check """ from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense from sage.matrix.matrix_integer_dense import Matrix_integer_dense @@ -1418,8 +1431,6 @@ The Singular documentation for '%s' is given below. ring = ring2 elif ring is not ring2: raise ValueError("Rings do not match up.") - if ring is None: - raise ValueError("Could not detect ring.") return ring def __reduce__(self): @@ -1619,11 +1630,11 @@ def singular_function(name): sage: std(I) [3*y - 8*z - 4, 4*x + 1] sage: size = singular_function("size") - sage: size([2, 3, 3], ring=P) + sage: size([2, 3, 3]) 3 - sage: size("sage", ring=P) + sage: size("sage") 4 - sage: size(["hello", "sage"], ring=P) + sage: size(["hello", "sage"]) 2 sage: factorize = singular_function("factorize") sage: factorize(f) @@ -1633,7 +1644,7 @@ def singular_function(name): We give a wrong number of arguments:: - sage: factorize(ring=P) + sage: factorize() Traceback (most recent call last): ... RuntimeError: Error in Singular function call 'factorize': @@ -1653,13 +1664,13 @@ def singular_function(name): arguments:: sage: singular_list = singular_function("list") - sage: singular_list(2, 3, 6, ring=P) + sage: singular_list(2, 3, 6) [2, 3, 6] - sage: singular_list(ring=P) + sage: singular_list() [] - sage: singular_list(1, ring=P) + sage: singular_list(1) [1] - sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ring=P) + sage: singular_list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] We try to define a non-existing function:: @@ -1674,9 +1685,9 @@ def singular_function(name): sage: from sage.libs.singular.function import lib as singular_lib sage: singular_lib('general.lib') sage: number_e = singular_function('number_e') - sage: number_e(10r,ring=P) + sage: number_e(10r) 67957045707/25000000000 - sage: RR(number_e(10r,ring=P)) + sage: RR(number_e(10r)) 2.71828182828000 :: @@ -1694,7 +1705,7 @@ def singular_function(name): sage: l [0, ['x', 'y', 'z'], [['dp', (1, 1, 1)], ['C', (0,)]], [0]] sage: ring=singular_function("ring") - sage: ring(l, ring=P) + sage: ring(l) sage: matrix = Matrix(P,2,2) sage: matrix.randomize(terms=1) @@ -1705,12 +1716,11 @@ def singular_function(name): sage: coeffs(x*y+y+1,y) [ 1] [x + 1] - sage: F. = GF(3)[] sage: intmat = Matrix(ZZ, 2,2, [100,2,3,4]) - sage: det(intmat, ring=F) + sage: det(intmat) 394 sage: random = singular_function("random") - sage: A = random(10,2,3, ring =F); A.nrows(), max(A.list()) <= 10 + sage: A = random(10,2,3); A.nrows(), max(A.list()) <= 10 (2, True) sage: P. = PolynomialRing(QQ) sage: M=P**3 @@ -1753,7 +1763,7 @@ def singular_function(name): doctest... sage: M [(x + y, x*y)] - sage: syz(M, ring=P) + sage: syz(M) [(0)] sage: mres(I, 0) @@ -1769,16 +1779,14 @@ def singular_function(name): sage: l = ringlist(P) sage: len(l) 6 - sage: ring(l, ring=P) + sage: ring(l) sage: I=twostd(I) sage: l[3]=I - sage: ring(l, ring=P) + sage: ring(l) - """ - cdef SingularFunction fnc try: return SingularKernelFunction(name) except NameError: @@ -1790,7 +1798,7 @@ def lib(name): INPUT: - - ``name`` - a Singular library name + - ``name`` -- a Singular library name EXAMPLE:: diff --git a/src/sage/libs/singular/function_factory.py b/src/sage/libs/singular/function_factory.py index 6d3169a8ff3..ea489b0d1dc 100644 --- a/src/sage/libs/singular/function_factory.py +++ b/src/sage/libs/singular/function_factory.py @@ -1,5 +1,5 @@ """ -libSingular: Function Factory. +libSingular: Function Factory AUTHORS: diff --git a/src/sage/libs/singular/polynomial.pyx b/src/sage/libs/singular/polynomial.pyx index d1bee080292..b40dc078704 100644 --- a/src/sage/libs/singular/polynomial.pyx +++ b/src/sage/libs/singular/polynomial.pyx @@ -445,8 +445,9 @@ cdef object singular_polynomial_latex(poly *p, ring *r, object base, object late sage: latex(10*x^2 + 1/2*y) 10 x^{2} + \frac{1}{2} y - Demonstrate that coefficients over non-atomic representated rings are - properly parenthesized (Trac 11186) + Demonstrate that coefficients over non-atomic representated rings are + properly parenthesized (:trac:`11186`):: + sage: x = var('x') sage: K. = QQ.extension(x^2 + x + 1) sage: P. = K[] diff --git a/src/sage/libs/singular/singular.pyx b/src/sage/libs/singular/singular.pyx index 499c2d7411e..33d7f7ec9c6 100644 --- a/src/sage/libs/singular/singular.pyx +++ b/src/sage/libs/singular/singular.pyx @@ -1,5 +1,5 @@ """ -libSingular conversion routines and initialisation. +libSingular: Conversion Routines and Initialisation AUTHOR: @@ -666,8 +666,7 @@ cdef int overflow_check(long e, ring *_ring) except -1: TESTS: Whether an overflow occurs or not, partially depends - on the number of variables in the ring. See trac ticket - #11856:: + on the number of variables in the ring. See :trac:`11856`:: sage: P. = QQ[] sage: y^2^30 diff --git a/src/sage/libs/singular/standard_options.py b/src/sage/libs/singular/standard_options.py index 89dc3b2e9be..0424d70f3d4 100644 --- a/src/sage/libs/singular/standard_options.py +++ b/src/sage/libs/singular/standard_options.py @@ -111,7 +111,7 @@ def libsingular_gb_standard_options(func): sage: "basis.reduced" in sage_getsource(J.interreduced_basis) True - The following tests against a bug that was fixed in trac ticket #11298:: + The following tests against a bug that was fixed in :trac:`11298`:: sage: from sage.misc.sageinspect import sage_getsourcelines, sage_getargspec sage: P. = QQ[] diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 218c54fba75..60c17e6e0c5 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -267,13 +267,13 @@ REFERENCES: -.. [Lee11] J.M. Lee : *Introduction to Topological Manifolds*, +.. [Lee11] \J.M. Lee : *Introduction to Topological Manifolds*, 2nd ed., Springer (New York) (2011). -.. [Lee13] J.M. Lee : *Introduction to Smooth Manifolds*, +.. [Lee13] \J.M. Lee : *Introduction to Smooth Manifolds*, 2nd ed., Springer (New York) (2013) -.. [KN63] S. Kobayashi & K. Nomizu : *Foundations of Differential Geometry*, +.. [KN63] \S. Kobayashi & K. Nomizu : *Foundations of Differential Geometry*, vol. 1, Interscience Publishers (New York) (1963). -.. [Huybrechts05] D. Huybrechts : *Complex Geometry*, +.. [Huybrechts05] \D. Huybrechts : *Complex Geometry*, Springer (Berlin) (2005). """ @@ -1028,7 +1028,7 @@ def irange(self, start=None): In general, one has always:: - sage: M.irange().next() == M.start_index() + sage: next(M.irange()) == M.start_index() True """ diff --git a/src/sage/matrix/constructor.pyx b/src/sage/matrix/constructor.pyx index 3de802cab29..ad12a5f8882 100644 --- a/src/sage/matrix/constructor.pyx +++ b/src/sage/matrix/constructor.pyx @@ -4,6 +4,7 @@ General matrix Constructor #***************************************************************************** # Copyright (C) 2005 William Stein +# 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 @@ -13,15 +14,14 @@ General matrix Constructor #***************************************************************************** import types -import sage.rings.all as rings +from .matrix_space import MatrixSpace from sage.rings.ring import is_Ring -import sage.matrix.matrix_space as matrix_space -from sage.structure.coerce import py_scalar_parent -from sage.structure.element import is_Vector +from sage.rings.all import ZZ, RDF, CDF +from sage.arith.srange import srange +from sage.structure.coerce cimport is_numpy_type, py_scalar_parent +from sage.structure.element cimport Vector from sage.structure.sequence import Sequence -from sage.rings.real_double import RDF -from sage.rings.complex_double import CDF -from sage.rings.all import ZZ +from sage.misc.long cimport pyobject_to_long class MatrixFactory(object): @@ -60,25 +60,27 @@ class MatrixFactory(object): array to a matrix. The ring, number of rows, and number of columns of the matrix can - be specified by setting the ring, nrows, or ncols parameters or by - passing them as the first arguments to the function in the order - ring, nrows, ncols. The ring defaults to ZZ if it is not specified - or cannot be determined from the entries. If the numbers of rows - and columns are not specified and cannot be determined, then an - empty 0x0 matrix is returned. + be specified by setting the ``ring``, ``nrows``, or ``ncols`` + keyword parameters or by passing them as the first arguments to the + function in specified order. The ring defaults to ``ZZ`` if it is + not specified and cannot be determined from the entries. If the + number of rows and columns are not specified and cannot be + determined, then an empty 0x0 matrix is returned. + INPUT: - - ``ring`` - the base ring for the entries of the + - ``ring`` -- the base ring for the entries of the matrix. - - ``nrows`` - the number of rows in the matrix. + - ``nrows`` -- the number of rows in the matrix. - - ``ncols`` - the number of columns in the matrix. + - ``ncols`` -- the number of columns in the matrix. - - ``sparse`` - create a sparse matrix. This defaults - to True when the entries are given as a dictionary, otherwise - defaults to False. + - ``sparse`` -- create a sparse matrix. This defaults to ``True`` + when the entries are given as a dictionary, otherwise defaults to + ``False``. + - ``entries`` -- see examples below. OUTPUT: @@ -86,21 +88,21 @@ class MatrixFactory(object): EXAMPLES:: - sage: m=matrix(2); m; m.parent() + sage: m = matrix(2); m; m.parent() [0 0] [0 0] Full MatrixSpace of 2 by 2 dense matrices over Integer Ring :: - sage: m=matrix(2,3); m; m.parent() + sage: m = matrix(2,3); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 dense matrices over Integer Ring :: - sage: m=matrix(QQ,[[1,2,3],[4,5,6]]); m; m.parent() + sage: m = matrix(QQ,[[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field @@ -118,47 +120,49 @@ class MatrixFactory(object): :: - sage: matrix(QQ,2,3,lambda x, y: x+y) + sage: matrix(QQ, 2, 3, lambda x, y: x+y) [0 1 2] [1 2 3] - sage: matrix(QQ,3,2,lambda x, y: x+y) - [0 1] - [1 2] - [2 3] + sage: matrix(QQ, 5, 5, lambda x, y: (x+1) / (y+1)) + [ 1 1/2 1/3 1/4 1/5] + [ 2 1 2/3 1/2 2/5] + [ 3 3/2 1 3/4 3/5] + [ 4 2 4/3 1 4/5] + [ 5 5/2 5/3 5/4 1] :: sage: v1=vector((1,2,3)) sage: v2=vector((4,5,6)) - sage: m=matrix([v1,v2]); m; m.parent() + sage: m = matrix([v1,v2]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Integer Ring :: - sage: m=matrix(QQ,2,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,2,[1,2,3,4,5,6]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field :: - sage: m=matrix(QQ,2,3,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,2,3,[1,2,3,4,5,6]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field :: - sage: m=matrix({(0,1): 2, (1,1):2/5}); m; m.parent() + sage: m = matrix({(0,1): 2, (1,1):2/5}); m; m.parent() [ 0 2] [ 0 2/5] Full MatrixSpace of 2 by 2 sparse matrices over Rational Field :: - sage: m=matrix(QQ,2,3,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ,2,3,{(1,1): 2}); m; m.parent() [0 0 0] [0 2 0] Full MatrixSpace of 2 by 3 sparse matrices over Rational Field @@ -166,8 +170,8 @@ class MatrixFactory(object): :: sage: import numpy - sage: n=numpy.array([[1,2],[3,4]],float) - sage: m=matrix(n); m; m.parent() + sage: n = numpy.array([[1,2],[3,4]],float) + sage: m = matrix(n); m; m.parent() [1.0 2.0] [3.0 4.0] Full MatrixSpace of 2 by 2 dense matrices over Real Double Field @@ -175,10 +179,10 @@ class MatrixFactory(object): :: sage: v = vector(ZZ, [1, 10, 100]) - sage: m=matrix(v); m; m.parent() + sage: m = matrix(v); m; m.parent() [ 1 10 100] Full MatrixSpace of 1 by 3 dense matrices over Integer Ring - sage: m=matrix(GF(7), v); m; m.parent() + sage: m = matrix(GF(7), v); m; m.parent() [1 3 2] Full MatrixSpace of 1 by 3 dense matrices over Finite Field of size 7 @@ -213,204 +217,255 @@ class MatrixFactory(object): sage: det(A) -x2*x4*x6 + x1*x5*x6 + x2*x3*x7 - x0*x5*x7 - x1*x3*x8 + x0*x4*x8 - TESTS:: + TESTS: + + There are many ways to create an empty matrix:: - sage: m=matrix(); m; m.parent() + sage: m = matrix(); m; m.parent() + [] + Full MatrixSpace of 0 by 0 dense matrices over Integer Ring + sage: m = matrix(sparse=True); m; m.parent() + [] + Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring + sage: m = matrix(QQ); m; m.parent() + [] + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + sage: m = matrix(ring=QQ); m; m.parent() + [] + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + sage: m = matrix(0); m; m.parent() [] Full MatrixSpace of 0 by 0 dense matrices over Integer Ring - sage: m=matrix(QQ); m; m.parent() + sage: m = matrix(0, 0, ring=QQ); m; m.parent() [] Full MatrixSpace of 0 by 0 dense matrices over Rational Field - sage: m=matrix(QQ,2); m; m.parent() + sage: m = matrix([]); m; m.parent() + [] + Full MatrixSpace of 0 by 0 dense matrices over Integer Ring + sage: m = matrix(QQ, []); m; m.parent() + [] + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + sage: m = matrix(QQ, {}); m; m.parent() + [] + Full MatrixSpace of 0 by 0 sparse matrices over Rational Field + + Only a ring and dimensions:: + + sage: m = matrix(2); m; m.parent() + [0 0] + [0 0] + Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + sage: m = matrix(QQ,2); m; m.parent() [0 0] [0 0] Full MatrixSpace of 2 by 2 dense matrices over Rational Field - sage: m=matrix(QQ,2,3); m; m.parent() + sage: m = matrix(QQ,2,3); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 dense matrices over Rational Field - sage: m=matrix([]); m; m.parent() - [] - Full MatrixSpace of 0 by 0 dense matrices over Integer Ring - sage: m=matrix(QQ,[]); m; m.parent() - [] - Full MatrixSpace of 0 by 0 dense matrices over Rational Field - sage: m=matrix(2,2,1); m; m.parent() + + A ring, dimensions and a scalar:: + + sage: m = matrix(2,2,1); m; m.parent() [1 0] [0 1] Full MatrixSpace of 2 by 2 dense matrices over Integer Ring - sage: m=matrix(QQ,2,2,1); m; m.parent() - [1 0] - [0 1] + sage: m = matrix(QQ,2,2,5); m; m.parent() + [5 0] + [0 5] Full MatrixSpace of 2 by 2 dense matrices over Rational Field - sage: m=matrix(2,3,0); m; m.parent() + + For non-square matrices, only zero works:: + + sage: m = matrix(2,3,0); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - sage: m=matrix(QQ,2,3,0); m; m.parent() + sage: m = matrix(QQ,2,3,0); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 dense matrices over Rational Field - sage: m=matrix([[1,2,3],[4,5,6]]); m; m.parent() + sage: matrix(QQ,2,3,1) + Traceback (most recent call last): + ... + TypeError: identity matrix must be square + sage: matrix(QQ,2,3,5) + Traceback (most recent call last): + ... + TypeError: nonzero scalar matrix must be square + + Matrices specified by a list of entries:: + + sage: m = matrix([[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - sage: m=matrix(QQ,2,[[1,2,3],[4,5,6]]); m; m.parent() + sage: m = matrix(QQ,2,[[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field - sage: m=matrix(QQ,3,[[1,2,3],[4,5,6]]); m; m.parent() + sage: m = matrix(QQ,3,[[1,2,3],[4,5,6]]); m; m.parent() Traceback (most recent call last): ... - ValueError: Number of rows does not match up with specified number. - sage: m=matrix(QQ,2,3,[[1,2,3],[4,5,6]]); m; m.parent() + ValueError: number of rows does not match up with specified number + sage: m = matrix(QQ,2,3,[[1,2,3],[4,5,6]]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Rational Field - sage: m=matrix(QQ,2,4,[[1,2,3],[4,5,6]]); m; m.parent() + sage: m = matrix(QQ,2,4,[[1,2,3],[4,5,6]]); m; m.parent() Traceback (most recent call last): ... - ValueError: Number of columns does not match up with specified number. - sage: m=matrix([(1,2,3),(4,5,6)]); m; m.parent() + ValueError: number of columns does not match up with specified number + sage: m = matrix([(1,2,3),(4,5,6)]); m; m.parent() [1 2 3] [4 5 6] Full MatrixSpace of 2 by 3 dense matrices over Integer Ring - sage: m=matrix([1,2,3,4,5,6]); m; m.parent() + sage: m = matrix([1,2,3,4,5,6]); m; m.parent() [1 2 3 4 5 6] Full MatrixSpace of 1 by 6 dense matrices over Integer Ring - sage: m=matrix((1,2,3,4,5,6)); m; m.parent() + sage: m = matrix((1,2,3,4,5,6)); m; m.parent() [1 2 3 4 5 6] Full MatrixSpace of 1 by 6 dense matrices over Integer Ring - sage: m=matrix(QQ,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,[1,2,3,4,5,6]); m; m.parent() [1 2 3 4 5 6] Full MatrixSpace of 1 by 6 dense matrices over Rational Field - sage: m=matrix(QQ,3,2,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,3,2,[1,2,3,4,5,6]); m; m.parent() [1 2] [3 4] [5 6] Full MatrixSpace of 3 by 2 dense matrices over Rational Field - sage: m=matrix(QQ,2,4,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,2,4,[1,2,3,4,5,6]); m; m.parent() Traceback (most recent call last): ... ValueError: entries has the wrong length - sage: m=matrix(QQ,5,[1,2,3,4,5,6]); m; m.parent() + sage: m = matrix(QQ,5,[1,2,3,4,5,6]); m; m.parent() Traceback (most recent call last): ... TypeError: cannot construct an element of Full MatrixSpace of 5 by 1 dense matrices over Rational Field from [1, 2, 3, 4, 5, 6]! - sage: m=matrix({(1,1): 2}); m; m.parent() + + Matrices specified by a dict of entries:: + + sage: m = matrix({(1,1): 2}); m; m.parent() [0 0] [0 2] Full MatrixSpace of 2 by 2 sparse matrices over Integer Ring - sage: m=matrix(QQ,{(1,1): 2}); m; m.parent() + sage: m = matrix({(1,1): 2}, sparse=False); m; m.parent() + [0 0] + [0 2] + Full MatrixSpace of 2 by 2 dense matrices over Integer Ring + sage: m = matrix(QQ,{(1,1): 2}); m; m.parent() [0 0] [0 2] Full MatrixSpace of 2 by 2 sparse matrices over Rational Field - sage: m=matrix(QQ,3,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ,3,{(1,1): 2}); m; m.parent() [0 0 0] [0 2 0] [0 0 0] Full MatrixSpace of 3 by 3 sparse matrices over Rational Field - sage: m=matrix(QQ,3,4,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ,3,4,{(1,1): 2}); m; m.parent() [0 0 0 0] [0 2 0 0] [0 0 0 0] Full MatrixSpace of 3 by 4 sparse matrices over Rational Field - sage: m=matrix(QQ,2,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ,2,{(1,1): 2}); m; m.parent() [0 0] [0 2] Full MatrixSpace of 2 by 2 sparse matrices over Rational Field - sage: m=matrix(QQ,1,{(1,1): 2}); m; m.parent() + sage: m = matrix(QQ,1,{(1,1): 2}); m; m.parent() Traceback (most recent call last): ... IndexError: invalid entries list - sage: m=matrix({}); m; m.parent() + sage: m = matrix({}); m; m.parent() [] Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring - sage: m=matrix(QQ,{}); m; m.parent() + sage: m = matrix(QQ,{}); m; m.parent() [] Full MatrixSpace of 0 by 0 sparse matrices over Rational Field - sage: m=matrix(QQ,2,{}); m; m.parent() + sage: m = matrix(QQ,2,{}); m; m.parent() [0 0] [0 0] Full MatrixSpace of 2 by 2 sparse matrices over Rational Field - sage: m=matrix(QQ,2,3,{}); m; m.parent() + sage: m = matrix(QQ,2,3,{}); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 sparse matrices over Rational Field - sage: m=matrix(2,{}); m; m.parent() + sage: m = matrix(2,{}); m; m.parent() [0 0] [0 0] Full MatrixSpace of 2 by 2 sparse matrices over Integer Ring - sage: m=matrix(2,3,{}); m; m.parent() + sage: m = matrix(2,3,{}); m; m.parent() [0 0 0] [0 0 0] Full MatrixSpace of 2 by 3 sparse matrices over Integer Ring - sage: m=matrix(0); m; m.parent() - [] - Full MatrixSpace of 0 by 0 dense matrices over Integer Ring - sage: m=matrix(0,2); m; m.parent() + + Matrices with zero rows or columns:: + + sage: m = matrix(0,2); m; m.parent() [] Full MatrixSpace of 0 by 2 dense matrices over Integer Ring - sage: m=matrix(2,0); m; m.parent() + sage: m = matrix(2,0); m; m.parent() [] Full MatrixSpace of 2 by 0 dense matrices over Integer Ring - sage: m=matrix(0,[1]); m; m.parent() + sage: m = matrix(0,[1]); m; m.parent() Traceback (most recent call last): ... ValueError: entries has the wrong length - sage: m=matrix(1,0,[]); m; m.parent() + sage: m = matrix(1,0,[]); m; m.parent() [] Full MatrixSpace of 1 by 0 dense matrices over Integer Ring - sage: m=matrix(0,1,[]); m; m.parent() + sage: m = matrix(0,1,[]); m; m.parent() [] Full MatrixSpace of 0 by 1 dense matrices over Integer Ring - sage: m=matrix(0,[]); m; m.parent() + sage: m = matrix(0,[]); m; m.parent() [] Full MatrixSpace of 0 by 0 dense matrices over Integer Ring - sage: m=matrix(0,{}); m; m.parent() + sage: m = matrix(0,{}); m; m.parent() [] Full MatrixSpace of 0 by 0 sparse matrices over Integer Ring - sage: m=matrix(0,{(1,1):2}); m; m.parent() + sage: m = matrix(0,{(1,1):2}); m; m.parent() Traceback (most recent call last): ... IndexError: invalid entries list - sage: m=matrix(2,0,{(1,1):2}); m; m.parent() + sage: m = matrix(2,0,{(1,1):2}); m; m.parent() Traceback (most recent call last): ... IndexError: invalid entries list + + Check conversion from numpy:: + sage: import numpy - sage: n=numpy.array([[numpy.complex(0,1),numpy.complex(0,2)],[3,4]],complex) - sage: m=matrix(n); m; m.parent() + sage: n = numpy.array([[numpy.complex(0,1),numpy.complex(0,2)],[3,4]],complex) + sage: m = matrix(n); m; m.parent() [1.0*I 2.0*I] [ 3.0 4.0] Full MatrixSpace of 2 by 2 dense matrices over Complex Double Field - sage: n=numpy.array([[1,2],[3,4]],'int32') - sage: m=matrix(n); m; m.parent() + sage: n = numpy.array([[1,2],[3,4]],'int32') + sage: m = matrix(n); m; m.parent() [1 2] [3 4] Full MatrixSpace of 2 by 2 dense matrices over Integer Ring sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'float32') - sage: m=matrix(n); m; m.parent() + sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Real Double Field - sage: n=numpy.array([[1,2,3],[4,5,6],[7,8,9]],'float64') - sage: m=matrix(n); m; m.parent() + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'float64') + sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Real Double Field - sage: n=numpy.array([[1,2,3],[4,5,6],[7,8,9]],'complex64') - sage: m=matrix(n); m; m.parent() + sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'complex64') + sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0] Full MatrixSpace of 3 by 3 dense matrices over Complex Double Field - sage: n=numpy.array([[1,2,3],[4,5,6],[7,8,9]],'complex128') - sage: m=matrix(n); m; m.parent() + sage: n = numpy.matrix([[1,2,3],[4,5,6],[7,8,9]],'complex128') + sage: m = matrix(n); m; m.parent() [1.0 2.0 3.0] [4.0 5.0 6.0] [7.0 8.0 9.0] @@ -426,8 +481,41 @@ class MatrixFactory(object): [3.0 4.0] sage: matrix(numpy.array([[5]])) [5] + sage: matrix(numpy.matrix([[5]])) + [5] + + A ring and a numpy array:: + + sage: n = numpy.array([[1,2,3],[4,5,6],[7,8,9]],'float32') + sage: m = matrix(ZZ, n); m; m.parent() + [1 2 3] + [4 5 6] + [7 8 9] + Full MatrixSpace of 3 by 3 dense matrices over Integer Ring + sage: n = matrix(QQ, 2, 2, [1, 1/2, 1/3, 1/4]).numpy(); n + array([[ 1. , 0.5 ], + [ 0.33333333, 0.25 ]]) + sage: matrix(QQ, n) + [ 1 1/2] + [1/3 1/4] + + The dimensions of a matrix may be given as numpy types:: + + sage: matrix(numpy.int32(2), ncols=numpy.int32(3)) + [0 0 0] + [0 0 0] + + The dimensions of a matrix must have an integral type:: + + sage: matrix(RR, 2.0, 2.0) + Traceback (most recent call last): + ... + TypeError: invalid matrix constructor: type matrix? for help + + More tests:: + sage: v = vector(ZZ, [1, 10, 100]) - sage: m=matrix(ZZ['x'], v); m; m.parent() + sage: m = matrix(ZZ['x'], v); m; m.parent() [ 1 10 100] Full MatrixSpace of 1 by 3 dense matrices over Univariate Polynomial Ring in x over Integer Ring sage: matrix(ZZ, 10, 10, range(100)).parent() @@ -440,7 +528,7 @@ class MatrixFactory(object): [ 1.0 2.0 3.0] [ 2.0 1.0 + 2.0*I 3.0] Full MatrixSpace of 2 by 3 dense matrices over Complex Double Field - sage: m=matrix(3,3,1/2); m; m.parent() + sage: m = matrix(3,3,1/2); m; m.parent() [1/2 0 0] [ 0 1/2 0] [ 0 0 1/2] @@ -448,134 +536,152 @@ class MatrixFactory(object): sage: matrix([[1],[2,3]]) Traceback (most recent call last): ... - ValueError: List of rows is not valid (rows are wrong types or lengths) + ValueError: list of rows is not valid (rows are wrong types or lengths) sage: matrix([[1],2]) Traceback (most recent call last): ... - ValueError: List of rows is not valid (rows are wrong types or lengths) + ValueError: list of rows is not valid (rows are wrong types or lengths) sage: matrix(vector(RR,[1,2,3])).parent() Full MatrixSpace of 1 by 3 dense matrices over Real Field with 53 bits of precision - sage: matrix(ZZ, [[0] for i in range(10^5)]).is_zero() # see #10158 + + Check :trac:`10158`:: + + sage: matrix(ZZ, [[0] for i in range(10^5)]).is_zero() True + Test conversion using a ``_matrix_`` method:: + + sage: A = gap(MatrixSpace(QQ, 2, 2)(range(4))) + sage: matrix(QQ, A) + [0 1] + [2 3] + sage: matrix(A, ring=QQ) + [0 1] + [2 3] + sage: matrix(A, QQ) + doctest:...: DeprecationWarning: when constructing a matrix, the ring must be the first argument + See http://trac.sagemath.org/20015 for details. + [0 1] + [2 3] + + A redundant ``ring`` argument:: + + sage: matrix(ZZ, 3, 3, ring=ZZ) + Traceback (most recent call last): + ... + TypeError: invalid matrix constructor: type matrix? for help + AUTHORS: - - ??: Initial implementation + - William Stein: Initial implementation - Jason Grout (2008-03): almost a complete rewrite, with bits and pieces from the original implementation + + - Jeroen Demeyer (2016-02-05): major clean up, see :trac:`20015` + and :trac:`20016` """ - def __call__(self, *args, **kwds): - args = list(args) - sparse = kwds.get('sparse',False) - # if the first object already knows how to make itself into a - # matrix, try that, defaulting to a matrix over the integers. - if len(args) == 1 and hasattr(args[0], '_matrix_'): + def __call__(self, *Args, ring=None, nrows=None, ncols=None, sparse=None): + cdef list args = list(Args) + + # Check for deprecated (matrixable object, ring) argument + if len(args) == 2 and hasattr(args[0], '_matrix_'): + from sage.misc.superseded import deprecation + deprecation(20015, "when constructing a matrix, the ring must be the first argument") + args = [args[1], args[0]] + + # ring argument + if ring is None and args and is_Ring(args[0]): + ring = args.pop(0) + + # object with _matrix_ method + if args: try: - return args[0]._matrix_(sparse=sparse) - except TypeError: - return args[0]._matrix_() - elif len(args) == 2: - if hasattr(args[0], '_matrix_'): - try: - return args[0]._matrix_(args[1], sparse=sparse) - except TypeError: - return args[0]._matrix_(args[1]) - elif hasattr(args[1], '_matrix_'): - try: - return args[1]._matrix_(args[0], sparse=sparse) - except TypeError: - return args[1]._matrix_(args[0]) - - if len(args) == 0: - # if nothing was passed return the empty matrix over the - # integer ring. - return matrix_space.MatrixSpace(rings.ZZ, 0, 0, sparse=sparse)([]) - - if len(args) >= 1 and is_Ring(args[0]): - # A ring is specified - if kwds.get('ring', args[0]) != args[0]: - raise ValueError("Specified rings are not the same") + makematrix = args[0]._matrix_ + except AttributeError: + pass else: - ring = args[0] - args.pop(0) - else: - ring = kwds.get('ring', None) + if ring is None: + args.pop(0) + else: + args[0] = ring + if sparse is None: + return makematrix(*args) + else: + return makematrix(*args, sparse=sparse) - if len(args) >= 1: - # check to see if the number of rows is specified + # nrows argument + if nrows is None and args: + arg = args[0] try: - import numpy - if isinstance(args[0], numpy.ndarray): - raise TypeError - nrows = int(args[0]) - args.pop(0) - if kwds.get('nrows', nrows) != nrows: - raise ValueError("Number of rows specified in two places and they are not the same") + if is_numpy_type(type(arg)): + import numpy + if isinstance(arg, numpy.ndarray): + raise TypeError + nrows = pyobject_to_long(arg) except TypeError: - nrows = kwds.get('nrows', None) - else: - nrows = kwds.get('nrows', None) + pass + else: + args.pop(0) - if len(args) >= 1: - # check to see if additionally, the number of columns is specified + # ncols argument + if ncols is None and args: + arg = args[0] try: - import numpy - if isinstance(args[0], numpy.ndarray): - raise TypeError - ncols = int(args[0]) - args.pop(0) - if kwds.get('ncols', ncols) != ncols: - raise ValueError("Number of columns specified in two places and they are not the same") + if is_numpy_type(type(arg)): + import numpy + if isinstance(arg, numpy.ndarray): + raise TypeError + ncols = pyobject_to_long(arg) except TypeError: - ncols = kwds.get('ncols', None) - else: - ncols = kwds.get('ncols', None) - + pass + else: + args.pop(0) # Now we've taken care of initial ring, nrows, and ncols arguments. # We've also taken care of the Sage object case. # Now the rest of the arguments are a list of rows, a flat list of # entries, a callable, a dict, a numpy array, or a single value. - if len(args) == 0: + entry_ring = ZZ + if not args: # If no entries are specified, pass back a zero matrix entries = 0 - entry_ring = rings.ZZ elif len(args) == 1: - if isinstance(args[0], (types.FunctionType, types.LambdaType, types.MethodType)): + arg = args[0] + if isinstance(arg, (types.FunctionType, types.LambdaType, types.MethodType)): if ncols is None and nrows is None: - raise ValueError("When passing in a callable, the dimensions of the matrix must be specified") + raise TypeError("when passing in a callable, the dimensions of the matrix must be specified") if ncols is None: ncols = nrows elif nrows is None: nrows = ncols - f = args[0] - args[0] = [[f(i,j) for j in range(ncols)] for i in range(nrows)] + irange = srange(nrows) + jrange = srange(ncols) + arg = [[arg(i, j) for j in jrange] for i in irange] - if isinstance(args[0], (list, tuple)): - if len(args[0]) == 0: + if isinstance(arg, (list, tuple)): + if not arg: # no entries are specified, pass back the zero matrix entries = 0 - entry_ring = rings.ZZ - elif isinstance(args[0][0], (list, tuple)) or is_Vector(args[0][0]): + elif isinstance(arg[0], (list, tuple)) or isinstance(arg[0], Vector): # Ensure we have a list of lists, each inner list having the same number of elements - first_len = len(args[0][0]) - if not all( (isinstance(v, (list, tuple)) or is_Vector(v)) and len(v) == first_len for v in args[0]): - raise ValueError("List of rows is not valid (rows are wrong types or lengths)") + first_len = len(arg[0]) + if not all( (isinstance(v, (list, tuple)) or isinstance(v, Vector)) and len(v) == first_len for v in arg): + raise ValueError("list of rows is not valid (rows are wrong types or lengths)") # We have a list of rows or vectors if nrows is None: - nrows = len(args[0]) - elif nrows != len(args[0]): - raise ValueError("Number of rows does not match up with specified number.") + nrows = len(arg) + elif nrows != len(arg): + raise ValueError("number of rows does not match up with specified number") if ncols is None: - ncols = len(args[0][0]) - elif ncols != len(args[0][0]): - raise ValueError("Number of columns does not match up with specified number.") + ncols = len(arg[0]) + elif ncols != len(arg[0]): + raise ValueError("number of columns does not match up with specified number") entries = [] - for v in args[0]: + for v in arg: entries.extend(v) else: @@ -585,26 +691,26 @@ class MatrixFactory(object): if nrows > 0: if ncols is None: - ncols = len(args[0]) // nrows - elif ncols != len(args[0]) // nrows: + ncols = len(arg) // nrows + elif ncols != len(arg) // nrows: raise ValueError("entries has the wrong length") - elif len(args[0]) > 0: + elif len(arg) > 0: raise ValueError("entries has the wrong length") - entries = args[0] + entries = arg if nrows > 0 and ncols > 0 and ring is None: entries, ring = prepare(entries) - elif isinstance(args[0], dict): - # We have a dictionary - # default to sparse - sparse = kwds.get('sparse', True) - if len(args[0]) == 0: + elif isinstance(arg, dict): + # We have a dictionary: default to sparse + if sparse is None: + sparse = True + if not arg: # no entries are specified, pass back the zero matrix entries = 0 else: - entries, entry_ring = prepare_dict(args[0]) + entries, entry_ring = prepare_dict(arg) if nrows is None: nrows = nrows_from_dict(entries) ncols = ncols_from_dict(entries) @@ -613,68 +719,63 @@ class MatrixFactory(object): # See the construction after the numpy case below. else: - import numpy - if isinstance(args[0], numpy.ndarray): - num_array = args[0] - str_dtype = str(num_array.dtype) - - if not( num_array.flags.c_contiguous is True or num_array.flags.f_contiguous is True): - raise TypeError('numpy matrix must be either c_contiguous or f_contiguous') - if str_dtype.count('float32')==1: - m=matrix(RDF,num_array.shape[0],num_array.shape[1],0) - m._replace_self_with_numpy32(num_array) - - elif str_dtype.count('float64')==1: - m=matrix(RDF,num_array.shape[0],num_array.shape[1],0) - m._replace_self_with_numpy(num_array) - - elif str_dtype.count('complex64')==1: - m=matrix(CDF,num_array.shape[0],num_array.shape[1],0) - m._replace_self_with_numpy32(num_array) - - elif str_dtype.count('complex128')==1: - m=matrix(CDF,num_array.shape[0],num_array.shape[1],0) - m._replace_self_with_numpy(num_array) - - elif str_dtype.count('int') == 1: - m = matrix(ZZ, [list(row) for row in list(num_array)]) - - elif str_dtype.count('object') == 1: - #Get the raw nested list from the numpy array - #and feed it back into matrix - try: - return matrix( [list(row) for row in list(num_array)]) - except TypeError: + if is_numpy_type(type(arg)): + import numpy + if isinstance(arg, numpy.ndarray): + # Convert to a numpy array if it was a matrix. + if type(arg) is not numpy.ndarray: + arg = numpy.array(arg) + + str_dtype = str(arg.dtype) + + if not (arg.flags.c_contiguous is True or arg.flags.f_contiguous is True): + raise TypeError('numpy matrix must be either c_contiguous or f_contiguous') + + if str_dtype.count('float32') == 1: + m = matrix(RDF, arg.shape[0], arg.shape[1], 0) + m._replace_self_with_numpy32(arg) + elif str_dtype.count('float64') == 1: + m = matrix(RDF, arg.shape[0], arg.shape[1], 0) + m._replace_self_with_numpy(arg) + elif str_dtype.count('complex64') == 1: + m = matrix(CDF, arg.shape[0], arg.shape[1], 0) + m._replace_self_with_numpy32(arg) + elif str_dtype.count('complex128') == 1: + m = matrix(CDF, arg.shape[0], arg.shape[1], 0) + m._replace_self_with_numpy(arg) + elif str_dtype.count('int') == 1: + m = matrix(ZZ, [list(row) for row in list(arg)]) + elif str_dtype.count('object') == 1: + # Get the raw nested list from the numpy array + # and feed it back into matrix + m = matrix([list(row) for row in list(arg)]) + else: raise TypeError("cannot convert NumPy matrix to Sage matrix") - else: - raise TypeError("cannot convert NumPy matrix to Sage matrix") - return m + if ring is not None and m.base_ring() is not ring: + m = m.change_ring(ring) + + return m elif nrows is not None and ncols is not None: # assume that we should just pass the thing into the # MatrixSpace constructor and hope for the best # This is not documented, but it is doctested if ring is None: - entry_ring = args[0].parent() - entries = args[0] + entry_ring = arg.parent() + entries = arg else: - raise ValueError("Invalid matrix constructor. Type matrix? for help") - else: - raise ValueError("Invalid matrix constructor. Type matrix? for help") + raise TypeError("invalid matrix constructor: type matrix? for help") + else: # len(args) >= 2 + raise TypeError("invalid matrix constructor: type matrix? for help") + if ring is None: + ring = entry_ring if nrows is None: nrows = 0 if ncols is None: ncols = nrows - - if ring is None: - try: - ring = entry_ring - except NameError: - ring = rings.ZZ - - return matrix_space.MatrixSpace(ring, nrows, ncols, sparse=sparse)(entries) + return MatrixSpace(ring, nrows, ncols, sparse=sparse)(entries) Matrix = matrix = MatrixFactory() @@ -717,7 +818,7 @@ def prepare(w): [1] """ if not w: - return Sequence([], rings.ZZ), rings.ZZ + return Sequence([], ZZ), ZZ entries = Sequence(w) ring = entries.universe() if isinstance(ring,type): @@ -750,7 +851,7 @@ def prepare_dict(w): Z = w.items() X = [x for _, x in Z] entries, ring = prepare(X) - return dict([(Z[i][0],entries[i]) for i in xrange(len(entries))]), ring + return dict([(Z[i][0],entries[i]) for i in range(len(entries))]), ring def nrows_from_dict(d): """ diff --git a/src/sage/matrix/docs.py b/src/sage/matrix/docs.py index d3e17fa3b5c..3e43c67748d 100644 --- a/src/sage/matrix/docs.py +++ b/src/sage/matrix/docs.py @@ -347,6 +347,7 @@ class derived from Matrix). They can be either sparse or dense, and Matrix_CC_dense x Matrix_real_double_dense x Matrix_complex_double_dense + x Matrix_complex_ball_dense The corresponding files in the sage/matrix library code directory are named @@ -364,10 +365,10 @@ class derived from Matrix). They can be either sparse or dense, and For each base field it is *absolutely* essential to completely implement the following functionality for that base ring: - * __cinit__ -- should use sage_malloc from ext/stdsage.pxi (only + * __cinit__ -- should use sig_malloc from ext/stdsage.pxi (only needed if allocate memory) * __init__ -- this signature: 'def __init__(self, parent, entries, copy, coerce)' - * __dealloc__ -- use sage_free (only needed if allocate memory) + * __dealloc__ -- use sig_free (only needed if allocate memory) * set_unsafe(self, size_t i, size_t j, x) -- doesn't do bounds or any other checks; assumes x is in self._base_ring * get_unsafe(self, size_t i, size_t j) -- doesn't do checks * __richcmp__ -- always the same (I don't know why its needed -- bug in PYREX). diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 5758521b307..b7a5d04e270 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -793,7 +793,7 @@ cdef class Matrix(sage.structure.element.Matrix): If we're given lists as arguments, we should throw an appropriate error when those lists do not contain valid - indices (trac #6569):: + indices (:trac:`6569`):: sage: A = matrix(4, range(1,17)) sage: A[[1.5], [1]] @@ -809,7 +809,7 @@ cdef class Matrix(sage.structure.element.Matrix): ... IndexError: row indices must be integers - Before trac #6569 was fixed, sparse/dense matrices behaved + Before :trac:`6569` was fixed, sparse/dense matrices behaved differently due to implementation details. Given invalid indices, they should fail in the same manner. These tests just repeat the previous set with a sparse matrix:: @@ -1766,7 +1766,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: K = (A-e).kernel() sage: P = K.basis_matrix() sage: P.str() - '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.288602340904754?*I -0.4847545623533090? - 1.871890760086142?*I]' + '[ 1.000000000000000? + 0.?e-17*I -2.116651487479748? + 0.0255565807096352?*I -0.2585224251020429? + 0.2886023409047535?*I -0.4847545623533090? - 1.871890760086142?*I]' Use single-row delimiters where appropriate:: @@ -4202,7 +4202,7 @@ cdef class Matrix(sage.structure.element.Matrix): REFERENCES: - .. [MulSto] T. Mulders, A. Storjohann, "On lattice reduction + .. [MulSto] \T. Mulders, A. Storjohann, "On lattice reduction for polynomial matrices", J. Symbolic Comput. 35 (2003), no. 4, 377--401 @@ -4239,7 +4239,7 @@ cdef class Matrix(sage.structure.element.Matrix): OUTPUT: a tuple of Python integers: the position of the first nonzero entry in each row of the echelon form. - This returns a tuple so it is immutable; see #10752. + This returns a tuple so it is immutable; see :trac:`10752`. EXAMPLES:: @@ -5185,7 +5185,7 @@ cdef class Matrix(sage.structure.element.Matrix): sage: parent(~1) Rational Field - A matrix with 0 rows and 0 columns is invertible (see trac #3734):: + A matrix with 0 rows and 0 columns is invertible (see :trac:`3734`):: sage: M = MatrixSpace(RR,0,0)(0); M [] @@ -5222,7 +5222,7 @@ cdef class Matrix(sage.structure.element.Matrix): ... ZeroDivisionError: input matrix must be nonsingular - Check to make sure that trac #2256 is still fixed:: + Check to make sure that :trac:`2256` is still fixed:: sage: M = MatrixSpace(CC, 2)(-1.10220440881763) sage: N = ~M diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index b3fba3e2d2e..56dd9d15127 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -106,19 +106,17 @@ cdef class Matrix(matrix0.Matrix): x_1^3-12*x_1^2-18*x_1 sage: A.characteristic_polynomial() x^3 - 12*x^2 - 18*x - sage: matrix(g,QQ) == A + sage: matrix(QQ, g) == A True - Particularly difficult is the case of matrices over - cyclotomic fields and general number fields. See - tickets #5618 and #8909. - :: + Particularly difficult is the case of matrices over cyclotomic + fields and general number fields. See :trac:`5618` and :trac:`8909`:: sage: K. = CyclotomicField(8) sage: A = MatrixSpace(K,2,2)([0,1+zeta,2*zeta,3]) sage: g = gap(A); g [ [ 0, 1+E(8) ], [ 2*E(8), 3 ] ] - sage: matrix(g,K) == A + sage: matrix(K, g) == A True sage: g.IsMatrix() true @@ -127,9 +125,8 @@ cdef class Matrix(matrix0.Matrix): sage: A = MatrixSpace(L,2,2)([0,1+tau,2*tau,3]) sage: g = gap(A); g [ [ !0, tau+1 ], [ 2*tau, !3 ] ] - sage: matrix(g,L) == A + sage: matrix(L, g) == A True - """ cdef Py_ssize_t i, j v = [] diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index d3aa9b9ba19..7753b90c116 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -42,6 +42,8 @@ from sage.rings.integer import Integer from sage.rings.rational_field import QQ, is_RationalField from sage.rings.real_double import RDF from sage.rings.complex_double import CDF +from sage.rings.real_mpfr import RealField +from sage.rings.complex_field import ComplexField from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.misc.derivative import multi_derivative from copy import copy @@ -988,6 +990,159 @@ cdef class Matrix(matrix1.Matrix): pm = pm + self.matrix_from_rows_and_columns(rows, cols).permanent() return pm + def pseudoinverse(self, *, algorithm=None): + """ + Return the Moore-Penrose pseudoinverse of this matrix. + + INPUT: + + - ``algorithm`` (default: guess) -- one of the following: + + - ``"numpy"`` -- Use numpy's ``linalg.pinv()`` which is + suitable over real or complex fields. + + - ``"exact"`` -- Use a simple algorithm which is not + numerically stable but useful over exact fields. Assume that + no conjugation is needed, that the conjugate transpose is + just the transpose. + + - ``"exactconj"`` -- Like ``exact`` but use the conjugate + transpose. + + OUTPUT: a matrix + + EXAMPLES:: + + sage: M = diagonal_matrix(CDF, [0, I, 1+I]) + sage: M + [ 0.0 0.0 0.0] + [ 0.0 1.0*I 0.0] + [ 0.0 0.0 1.0 + 1.0*I] + sage: M.pseudoinverse() # tol 1e-15 + [ 0.0 0.0 0.0] + [ 0.0 -1.0*I 0.0] + [ 0.0 0.0 0.5 - 0.5*I] + + We check the properties of the pseudoinverse over an exact + field:: + + sage: M = random_matrix(QQ, 6, 3) * random_matrix(QQ, 3, 5) + sage: Mx = M.pseudoinverse() + sage: M * Mx * M == M + True + sage: Mx * M * Mx == Mx + True + sage: (M * Mx).is_symmetric() + True + sage: (Mx * M).is_symmetric() + True + + Beware that the ``exact`` algorithm is not numerically stable, + but the default ``numpy`` algorithm is:: + + sage: M = matrix(RR, 3, 3, [1,2,3,1/3,2/3,3/3,1/5,2/5,3/5]) + sage: M.pseudoinverse() # tol 1e-15 + [0.0620518477661335 0.0206839492553778 0.0124103695532267] + [ 0.124103695532267 0.0413678985107557 0.0248207391064534] + [ 0.186155543298400 0.0620518477661335 0.0372311086596801] + sage: M.pseudoinverse(algorithm="numpy") # tol 1e-15 + [0.0620518477661335 0.0206839492553778 0.0124103695532267] + [ 0.124103695532267 0.0413678985107557 0.0248207391064534] + [ 0.186155543298400 0.0620518477661335 0.0372311086596801] + sage: M.pseudoinverse(algorithm="exact") + [ 0.125000000000000 0.0625000000000000 0.0312500000000000] + [ 0.250000000000000 0.125000000000000 0.0625000000000000] + [ 0.000000000000000 0.000000000000000 0.0625000000000000] + + When multiplying the given matrix with the pseudoinverse, the + result is symmetric for the ``exact`` algorithm or hermitian + for the ``exactconj`` algorithm:: + + sage: M = matrix(QQbar, 2, 2, [1, sqrt(-3), -sqrt(-3), 3]) + sage: M * M.pseudoinverse() + [ 0.2500000000000000? 0.4330127018922193?*I] + [-0.4330127018922193?*I 0.750000000000000?] + sage: M * M.pseudoinverse(algorithm="exactconj") + [ 1/4 0.4330127018922193?*I] + [-0.4330127018922193?*I 3/4] + sage: M * M.pseudoinverse(algorithm="exact") + [ -1/2 0.866025403784439?*I] + [0.866025403784439?*I 3/2] + + For an invertible matrix, the pseudoinverse is just the + inverse:: + + sage: M = matrix([[1,2], [3,4]]) + sage: ~M + [ -2 1] + [ 3/2 -1/2] + sage: M.pseudoinverse() + [ -2 1] + [ 3/2 -1/2] + + Numpy gives a strange answer due to rounding errors:: + + sage: M.pseudoinverse(algorithm="numpy") # random + [-1286742750677287/643371375338643 1000799917193445/1000799917193444] + [ 519646110850445/346430740566963 -300239975158034/600479950316067] + + TESTS:: + + sage: M.pseudoinverse(algorithm="exact") + [ -2 1] + [ 3/2 -1/2] + sage: M.pseudoinverse(algorithm="whatever") + Traceback (most recent call last): + ... + ValueError: unknown algorithm 'whatever', valid values are ('numpy', 'exact', 'exactconj') + sage: M.change_ring(RealField(54)).pseudoinverse() + Traceback (most recent call last): + ... + NotImplementedError: pseudoinverse for real/complex field is only implemented for <= 53 bits of precision + """ + ring = self.base_ring() + if algorithm is None: + # Choose algorithm depending on base ring + is_complex = ComplexField(2).has_coerce_map_from(ring) + if is_complex: + if ring.is_exact(): + is_real = RealField(2).has_coerce_map_from(ring) + algorithm = "exact" if is_real else "exactconj" + else: + if ring.precision() <= 53: + algorithm = "numpy" + else: + raise NotImplementedError("pseudoinverse for real/complex field is only implemented for <= 53 bits of precision") + else: + algorithm = "exact" + else: + algos = ("numpy", "exact", "exactconj") + if algorithm not in algos: + raise ValueError("unknown algorithm {!r}, valid values are {}".format(algorithm, algos)) + + if algorithm == "numpy": + from numpy.linalg import pinv + from sage.matrix.constructor import matrix + ans = pinv(self.numpy()) + return matrix(ring.fraction_field(), ans) + + # We use a simple algorithm taken from + # https://en.wikipedia.org/wiki/Moore%E2%80%93Penrose_pseudoinverse#Rank_decomposition + # Write self as A * B such that A and B both have full rank. + B = self.row_space().basis_matrix() + A = B.solve_left(self) + + if algorithm.endswith("conj"): + At = A.conjugate_transpose() + Bt = B.conjugate_transpose() + else: + At = A.transpose() + Bt = B.transpose() + + # Now the pseudoinverse is B^t (A^t A B B^t)^-1 A^t. + Q = (At * A) * (B * Bt) + return Bt * ~Q * At + def rook_vector(self, algorithm="ButeraPernici", complement=False, use_complement=None): r""" Return the rook vector of this matrix. @@ -1136,10 +1291,10 @@ cdef class Matrix(matrix1.Matrix): REFERENCES: - .. [Riordan] J. Riordan, "An Introduction to Combinatorial Analysis", + .. [Riordan] \J. Riordan, "An Introduction to Combinatorial Analysis", Dover Publ. (1958) - .. [Allenby] R.B.J.T Allenby and A. Slomson, "How to count", CRC Press (2011) + .. [Allenby] \R.B.J.T Allenby and A. Slomson, "How to count", CRC Press (2011) AUTHORS: @@ -1383,7 +1538,7 @@ cdef class Matrix(matrix1.Matrix): - Unknown: No author specified in the file from 2009-06-25 - Sebastian Pancratz (2009-06-25): Use the division-free algorithm for charpoly - - Thierry Monteil (2010-10-05): Bugfix for trac ticket #10063, + - Thierry Monteil (2010-10-05): Bugfix for :trac:`10063`, so that the determinant is computed even for rings for which the is_field method is not implemented. """ @@ -1741,7 +1896,7 @@ cdef class Matrix(matrix1.Matrix): # TypeError with the following kind of message: # absolute value is not defined on matrices. If you want the # L^1-norm use m.norm(1) and if you want the matrix obtained by - # applying the absolute value to the coefficents use + # applying the absolute value to the coefficients use # m.apply_map(abs). from sage.misc.superseded import deprecation deprecation(17443, "abs(matrix) is deprecated. Use matrix.det()" @@ -4264,7 +4419,7 @@ cdef class Matrix(matrix1.Matrix): sage: ker.0 * k (0, 0, 0, 0) - Test that trac ticket #9425 is fixed. + Test that :trac:`9425` is fixed. :: @@ -6852,7 +7007,7 @@ cdef class Matrix(matrix1.Matrix): where `W` is the output matrix. Warning: the default of `transformation` will soon be set to ``False``, - see Ticket #16896. Until then, this function will print a deprecation + see :trac:`16896`. Until then, this function will print a deprecation warning as long as `transformation` was not explicitly set to ``True`` or ``False``. @@ -6934,7 +7089,7 @@ row reduced form. This function will soon be fixed (see Ticket #16742).""" We check that the output is the same for a matrix `M` if its entries are rational functions instead of polynomials. We also check that the type of - the output follows the documentation. See #9063 + the output follows the documentation. See :trac:`9063` :: @@ -6976,7 +7131,7 @@ row reduced form. This function will soon be fixed (see Ticket #16742).""" :: sage: R. = QQ['t'] - sage: M = matrix([[t,t,t],[0,0,t]], ascend=False) + sage: M = matrix([[t,t,t],[0,0,t]]) sage: M.row_reduced_form(transformation=False, old_call=False) [t t t] [0 0 t] @@ -7025,11 +7180,11 @@ row reduced form. This function will soon be fixed (see Ticket #16742).""" REFERENCES: - .. [H] F. Hess, "Computing Riemann-Roch spaces in algebraic function + .. [H] \F. Hess, "Computing Riemann-Roch spaces in algebraic function fields and related topics," J. Symbolic Comput. 33 (2002), no. 4, 425--445. - .. [K] T. Kaliath, "Linear Systems", Prentice-Hall, 1980, 383--386. + .. [K] \T. Kaliath, "Linear Systems", Prentice-Hall, 1980, 383--386. """ from sage.matrix.matrix_misc import row_reduced_form @@ -8124,7 +8279,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: m.is_one() False """ - return self.is_scalar(1) + return self.is_scalar(self.base_ring().one()) def is_scalar(self, a = None): """ @@ -8164,11 +8319,10 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" a = self.get_unsafe(0,0) else: a = self.base_ring()(a) - zero = self.base_ring()(0) - for i from 0 <= i < self._nrows: - for j from 0 <= j < self._ncols: + for i in range(self._nrows): + for j in range(self._ncols): if i != j: - if self.get_unsafe(i,j) != zero: + if not self.get_unsafe(i,j).is_zero(): return False else: if self.get_unsafe(i, i) != a: @@ -8890,23 +9044,23 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" ... [-1, 1, -6, -6, 5]]) sage: Q, R = A.QR() sage: Q - [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.687440062597?] - [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.717294125164660? -0.220962877263?] - [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.180872093737548? 0.1964114464561?] - [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.096630296654307? -0.662888631790?] + [ -0.4588314677411235? -0.1260506983326509? 0.3812120831224489? -0.394573711338418? -0.6874400625964?] + [ -0.4588314677411235? 0.4726901187474409? -0.05198346588033394? 0.7172941251646595? -0.2209628772631?] + [ 0.2294157338705618? 0.6617661662464172? 0.6619227988762521? -0.1808720937375480? 0.1964114464561?] + [ 0.6882472016116853? 0.1890760474989764? -0.2044682991293135? 0.0966302966543065? -0.6628886317894?] [ -0.2294157338705618? 0.5357154679137663? -0.609939332995919? -0.536422031427112? 0.0245514308070?] sage: R [ 4.358898943540674? -0.4588314677411235? 13.07669683062202? 6.194224814505168? 2.982404540317303?] [ 0 1.670171752907625? 0.5987408170800917? -1.292019657909672? 6.207996892883057?] - [ 0 0 5.444401659866974? 5.468660610611130? -0.682716185228386?] - [ 0 0 0 1.027626039419836? -3.61930014968662?] - [ 0 0 0 0 0.02455143080702?] + [ 0 0 5.444401659866974? 5.468660610611130? -0.6827161852283857?] + [ 0 0 0 1.027626039419836? -3.619300149686620?] + [ 0 0 0 0 0.024551430807012?] sage: Q.conjugate_transpose()*Q - [1.000000000000000? 0.?e-18 0.?e-17 0.?e-15 0.?e-12] - [ 0.?e-18 1.000000000000000? 0.?e-16 0.?e-15 0.?e-12] - [ 0.?e-17 0.?e-16 1.000000000000000? 0.?e-15 0.?e-12] - [ 0.?e-15 0.?e-15 0.?e-15 1.000000000000000? 0.?e-12] - [ 0.?e-12 0.?e-12 0.?e-12 0.?e-12 1.000000000000?] + [1.000000000000000? 0.?e-18 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-18 1.000000000000000? 0.?e-17 0.?e-16 0.?e-13] + [ 0.?e-17 0.?e-17 1.000000000000000? 0.?e-16 0.?e-13] + [ 0.?e-16 0.?e-16 0.?e-16 1.000000000000000? 0.?e-13] + [ 0.?e-13 0.?e-13 0.?e-13 0.?e-13 1.0000000000000?] sage: Q*R == A True @@ -8921,24 +9075,24 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: Q, R = A.QR() sage: Q [ -0.7302967433402215? 0.2070566455055649? + 0.5383472783144687?*I 0.2463049809998642? - 0.0764456358723292?*I 0.2381617683194332? - 0.1036596032779695?*I] - [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863032? - 0.1952221495524667?*I 0.701244450214469? - 0.364371165098660?*I] - [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506763? - 0.0825191718705412?*I] + [ 0.0912870929175277? -0.2070566455055649? - 0.3778783780476559?*I 0.3786559533863033? - 0.1952221495524667?*I 0.701244450214469? - 0.3643711650986595?*I] + [ 0.6390096504226938? + 0.0912870929175277?*I 0.1708217325420910? + 0.6677576817554466?*I -0.03411475806452072? + 0.04090198741767143?*I 0.3140171085506764? - 0.0825191718705412?*I] [ 0.1825741858350554? + 0.0912870929175277?*I -0.03623491296347385? + 0.0724698259269477?*I 0.8632284069415110? + 0.06322839976356195?*I -0.4499694867611521? - 0.0116119181208918?*I] sage: R [ 10.95445115010333? 0.?e-18 - 1.917028951268082?*I 5.385938482134133? - 2.190890230020665?*I -0.2738612787525831? - 2.190890230020665?*I] - [ 0 4.829596256417300? + 0.?e-17*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] + [ 0 4.829596256417300? + 0.?e-18*I -0.869637911123373? - 5.864879483945125?*I 0.993871898426712? - 0.3054085521207082?*I] [ 0 0 12.00160760935814? + 0.?e-16*I -0.2709533402297273? + 0.4420629644486323?*I] [ 0 0 0 1.942963944258992? + 0.?e-16*I] sage: Q.conjugate_transpose()*Q [1.000000000000000? + 0.?e-19*I 0.?e-18 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] [ 0.?e-18 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-15*I] + [ 0.?e-17 + 0.?e-17*I 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I 0.?e-16 + 0.?e-16*I] + [ 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I 1.000000000000000? + 0.?e-16*I] sage: Q*R - A [ 0.?e-17 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-15*I] + [ 0.?e-18 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] [0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-16 + 0.?e-16*I 0.?e-15 + 0.?e-16*I] + [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-16 + 0.?e-16*I 0.?e-16 + 0.?e-16*I] A rank-deficient rectangular matrix, with both values of the ``full`` keyword. :: @@ -8991,12 +9145,12 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" [ 7.211102550927979? 3.328201177351375? - 5.269651864139676?*I 7.904477796209515? + 8.45917799243475?*I 4.021576422632911? - 2.634825932069838?*I] [ 0 1.074172311059150? -1.611258466588724? - 9.13046464400277?*I 1.611258466588724? + 0.5370861555295747?*I] sage: Q.conjugate_transpose()*Q - [1.000000000000000? + 0.?e-18*I 0.?e-18 + 0.?e-18*I] - [ 0.?e-17 + 0.?e-17*I 1.000000000000000? + 0.?e-17*I] + [1 0] + [0 1] sage: Q*R-A - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-18 + 0.?e-18*I] - [0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-18*I 0.?e-18 + 0.?e-17*I 0.?e-18 + 0.?e-18*I] - [0.?e-18 + 0.?e-18*I 0.?e-17 + 0.?e-18*I 0.?e-17 + 0.?e-17*I 0.?e-18 + 0.?e-18*I] + [0 0 0 0] + [0 0 0 0] + [0 0 0 0] Results of full decompositions are cached and thus returned immutable. :: @@ -9772,7 +9926,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: jf == ~P*m*P True - We verify that the bug from trac ticket #6942 is fixed:: + We verify that the bug from :trac:`6942` is fixed:: sage: M = Matrix(GF(2),[[1,0,1,0,0,0,1],[1,0,0,1,1,1,0],[1,1,0,1,1,1,1],[1,1,1,0,1,1,1],[1,1,1,0,0,1,0],[1,1,1,0,1,0,0],[1,1,1,1,1,1,0]]) sage: J, T = M.jordan_form(transformation=True) @@ -9794,7 +9948,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: M.rank() 7 - We verify that the bug from trac ticket #6932 is fixed:: + We verify that the bug from :trac:`6932` is fixed:: sage: M=Matrix(1,1,[1]) sage: M.jordan_form(transformation=True) @@ -10341,9 +10495,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" True sage: _, T = A.is_similar(B, transformation=True) sage: T - [ 1.0000000000000? + 0.?e-13*I 0.?e-13 + 0.?e-13*I 0.?e-13 + 0.?e-13*I] - [-0.6666666666667? + 0.?e-13*I 0.16666666666667? + 0.?e-14*I -0.8333333333334? + 0.?e-13*I] - [ 0.6666666666667? + 0.?e-13*I 0.?e-13 + 0.?e-13*I -0.333333333334? + 0.?e-13*I] + [ 1.00000000000000? + 0.?e-14*I 0.?e-14 + 0.?e-14*I 0.?e-14 + 0.?e-14*I] + [-0.66666666666667? + 0.?e-15*I 0.166666666666667? + 0.?e-15*I -0.83333333333334? + 0.?e-14*I] + [ 0.66666666666667? + 0.?e-14*I 0.?e-14 + 0.?e-14*I -0.33333333333333? + 0.?e-14*I] sage: T.change_ring(QQ) [ 1 0 0] [-2/3 1/6 -5/6] @@ -11038,7 +11192,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" A = LL^\ast where `L^\ast` is the conjugate-transpose in the complex case, - and just the transpose in the real case. If the matrix + and just the transpose in the real case. If the matrix fails to be positive definite (perhaps because it is not symmetric or Hermitian), then a ``ValueError`` results. @@ -11085,9 +11239,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" of algebraic numbers. :: sage: A = matrix(ZZ, [[ 78, -30, -37, -2], - ... [-30, 102, 179, -18], - ... [-37, 179, 326, -38], - ... [ -2, -18, -38, 15]]) + ....: [-30, 102, 179, -18], + ....: [-37, 179, 326, -38], + ....: [ -2, -18, -38, 15]]) sage: A.is_symmetric() True sage: L = A.cholesky() @@ -11097,7 +11251,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" [ -4.189425026335004? 17.32383862241232? 2.886751345948129? 0] [-0.2264554068289192? -1.973397116652010? -1.649572197684645? 2.886751345948129?] sage: L.parent() - Full MatrixSpace of 4 by 4 dense matrices over Algebraic Field + Full MatrixSpace of 4 by 4 dense matrices over Algebraic Real Field sage: L*L.transpose() == A True @@ -11133,9 +11287,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" True sage: L = A.cholesky() sage: L - [ 1.414213562373095? 0 0] - [2.828427124746190? - 1.414213562373095?*I 1 0] - [4.242640687119285? + 2.828427124746190?*I -2*I + 2 1.732050807568878?] + [ 1.414213562373095? 0 0] + [2.828427124746190? - 1.414213562373095?*I 1 0] + [4.242640687119285? + 2.828427124746190?*I -2*I + 2 1.732050807568878?] sage: L.parent() Full MatrixSpace of 3 by 3 dense matrices over Algebraic Field sage: (L*L.conjugate_transpose() - A.change_ring(QQbar)).norm() < 10^-10 @@ -11182,8 +11336,8 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: A.cholesky() Traceback (most recent call last): ... - ValueError: unable to check positive definiteness because - Unable to create the fraction field of Ring of integers modulo 6 + ValueError: Could not see Ring of integers modulo 6 as a subring of + the real or complex numbers The base field may not have elements that are comparable to zero. :: @@ -11192,8 +11346,8 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: A.cholesky() Traceback (most recent call last): ... - ValueError: unable to check positive definiteness because - cannot convert computations from Finite Field in a of size 5^4 into real numbers + ValueError: Could not see Finite Field in a of size 5^4 as a subring + of the real or complex numbers The algebraic closure of the fraction field of the base ring may not be implemented. :: @@ -11202,15 +11356,15 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: A.cholesky() Traceback (most recent call last): ... - TypeError: base field needs an algebraic closure with square roots, - not Ring of integers modulo 7 + ValueError: Could not see Ring of integers modulo 7 as a subring of + the real or complex numbers The matrix may not be positive definite. :: sage: C. = QuadraticField(-1) sage: B = matrix(C, [[ 2, 4 - 2*I, 2 + 2*I], - ... [4 + 2*I, 8, 10*I], - ... [2 - 2*I, -10*I, -3]]) + ....: [4 + 2*I, 8, 10*I], + ....: [2 - 2*I, -10*I, -3]]) sage: B.is_positive_definite() False sage: B.cholesky() @@ -11223,9 +11377,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" lack a Cholesky decomposition. :: sage: A = matrix(QQ, [[21, 15, 12, -3], - ... [15, 12, 9, 12], - ... [12, 9, 7, 3], - ... [-3, 12, 3, 8]]) + ....: [15, 12, 9, 12], + ....: [12, 9, 7, 3], + ....: [-3, 12, 3, 8]]) sage: A.is_positive_definite() False sage: [A[:i,:i].determinant() for i in range(1,A.nrows()+1)] @@ -11236,25 +11390,6 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" ValueError: matrix is not positive definite, so cannot compute Cholesky decomposition - In certain cases, the algorithm can find an analogue of the - Cholesky decomposition over finite fields:: - - sage: F. = FiniteField(5^3) - sage: A = matrix(F, [[ 4, 2*a^2 + 3, 4*a + 1], - ... [ 2*a^2 + 3, 2*a + 2, 4*a^2 + 4*a + 4], - ... [ 4*a + 1, 4*a^2 + 4*a + 4, a^2 + 4*a]]) - sage: A.is_symmetric() - True - sage: L = A.cholesky() - sage: L*L.transpose() == A - True - - sage: F = FiniteField(7) - sage: A = matrix(F, [[4, 0], [0, 3]]) - sage: A.cholesky() - [ 2 0] - [ 0 2*z2 + 6] - TESTS: This verifies that :trac:`11274` is resolved. :: @@ -11270,6 +11405,17 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" [ 1.414213562373095? 0] [0.7071067811865475? 0.7071067811865475?] + We check that if the input is a real matrix then the output is real as + well (:trac:`18381`):: + + sage: E = matrix(QQ, [[4, 2], [2, 10/9]]) + sage: E.cholesky().base_ring() + Rational Field + + sage: E = matrix(QQ, [[2, 1], [1, 1]]) + sage: E.cholesky().base_ring() + Algebraic Real Field + AUTHOR: - Rob Beezer (2012-05-27) @@ -11283,41 +11429,39 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" if not self.base_ring().is_exact(): msg = 'base ring of the matrix must be exact, not {0}' raise TypeError(msg.format(self.base_ring())) - try: - posdef = self.is_positive_definite() - except (ValueError, TypeError) as e: - msg = "unable to check positive definiteness because {0}" - raise ValueError(msg.format(e)) - if not posdef: - msg = "matrix is not positive definite, so cannot compute Cholesky decomposition" + if not self.is_positive_definite(): + msg = 'matrix is not positive definite, so cannot compute Cholesky decomposition' raise ValueError(msg) # the successful positive definite check will cache a Hermitian # or symmetric indefinite factorization, as appropriate factors = self.fetch('indefinite_factorization_hermitian') if factors is None: factors = self.fetch('indefinite_factorization_symmetric') + from sage.rings.qqbar import AA as F_ac + else: + from sage.rings.qqbar import QQbar as F_ac + L = factors[0] d = factors[1] F = L.base_ring() # field really splits = [] # square roots of diagonal entries + extend = False for x in d: - try: - sqrt = F(x.sqrt()) - except (TypeError, ValueError): - try: - F = F.algebraic_closure() - except (NotImplementedError, AttributeError): - msg = "base field needs an algebraic closure with square roots, not {0}" - raise TypeError(msg.format(F)) - sqrt = F(x).sqrt() + if not extend and x.is_square(): + sqrt = x.sqrt() + else: + extend = True + sqrt = F_ac(x).sqrt() splits.append(sqrt) # move square root of the diagonal matrix # into the lower triangular matrix # We need a copy, to break immutability # and the field may have changed as well - C = copy(L) - if F != C.base_ring(): - C = C.change_ring(F) + if extend: + C = L.change_ring(F_ac) + else: + C = L.__copy__() + for c in range(C.ncols()): C.rescale_col(c, splits[c]) C.set_immutable() @@ -11688,7 +11832,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" True Partial pivoting is based on the absolute values of entries - of a column. Trac #12208 shows that the return value of the + of a column. :trac:`12208` shows that the return value of the absolute value must be handled carefully. This tests that situation in the case of cylotomic fields. :: @@ -11851,9 +11995,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" A simple symmetric matrix. :: sage: A = matrix(QQ, [[ 4, -2, 4, 2], - ... [-2, 10, -2, -7], - ... [ 4, -2, 8, 4], - ... [ 2, -7, 4, 7]]) + ....: [-2, 10, -2, -7], + ....: [ 4, -2, 8, 4], + ....: [ 2, -7, 4, 7]]) sage: A.is_symmetric() True sage: L, d = A._indefinite_factorization('symmetric') @@ -11872,9 +12016,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: x = var('x') sage: C. = NumberField(x^2 + 1) sage: A = matrix(C, [[ 23, 17*I + 3, 24*I + 25, 21*I], - ... [ -17*I + 3, 38, -69*I + 89, 7*I + 15], - ... [-24*I + 25, 69*I + 89, 976, 24*I + 6], - ... [ -21*I, -7*I + 15, -24*I + 6, 28]]) + ....: [ -17*I + 3, 38, -69*I + 89, 7*I + 15], + ....: [-24*I + 25, 69*I + 89, 976, 24*I + 6], + ....: [ -21*I, -7*I + 15, -24*I + 6, 28]]) sage: A.is_hermitian() True sage: L, d = A._indefinite_factorization('hermitian') @@ -11896,7 +12040,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: A = matrix(QQ, [[4, 6, 1], [6, 9, 5], [1, 5, 2]]) sage: B = A[0:2, 0:2]; B.determinant() 0 - sage: A._indefinite_factorization('symmetric') + sage: A.indefinite_factorization(algorithm='symmetric') Traceback (most recent call last): ... ValueError: 2x2 leading principal submatrix is singular, @@ -11960,9 +12104,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" to the ``algorithm``. :: sage: A = matrix(QQ, [[ 4, -2, 4, 2], - ... [-2, 10, -2, -7], - ... [ 4, -2, 8, 4], - ... [ 2, -7, 4, 7]]) + ....: [-2, 10, -2, -7], + ....: [ 4, -2, 8, 4], + ....: [ 2, -7, 4, 7]]) sage: Ls, ds = A._indefinite_factorization('symmetric') sage: Lh, dh = A._indefinite_factorization('hermitian') sage: Ls.is_immutable(), Lh.is_immutable() @@ -12030,14 +12174,14 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" conjugate = (algorithm == 'hermitian') # we need a copy no matter what, so we # (potentially) change to fraction field at the same time - L = self.change_ring(F) - # The change ring doesn't necessarily return a copy if ``self`` - # is immutable and ``F`` is the same as the base ring - if L is self: + if R is F: L = self.__copy__() + else: + L = self.change_ring(F) + m = L._nrows - zero = F(0) - one = F(1) + zero = F.zero() + one = F.one() d = [] d_inv = [] for i in range(m): @@ -12050,9 +12194,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" for k in range(j): t -= L.get_unsafe(k,i)*L.get_unsafe(j,k) if i == j: - if t == zero: - msg = "{0}x{0} leading principal submatrix is singular, so cannot create indefinite factorization" - raise ValueError(msg.format(i+1)) + if not t: + self.cache(cache_string, (False,i+1)) + return (False, i+1) d.append(t) d_inv.append(one/t) L.set_unsafe(i, i, one) @@ -12064,8 +12208,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" for j in range(i+1, m): L.set_unsafe(i, j, zero) L.set_immutable() - d = tuple(d) - factors = (L, d) + factors = (L, tuple(d)) self.cache(cache_string, factors) return factors @@ -12224,6 +12367,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" """ from sage.modules.free_module_element import vector L, d = self._indefinite_factorization(algorithm, check=check) + if L is False: + msg = "{0}x{0} leading principal submatrix is singular, so cannot create indefinite factorization" + raise ValueError(msg.format(d)) return L, vector(L.base_ring(), d) def is_positive_definite(self): @@ -12273,9 +12419,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" determinants of the leading principal submatrices. :: sage: A = matrix(QQ, [[ 4, -2, 4, 2], - ... [-2, 10, -2, -7], - ... [ 4, -2, 8, 4], - ... [ 2, -7, 4, 7]]) + ....: [-2, 10, -2, -7], + ....: [ 4, -2, 8, 4], + ....: [ 2, -7, 4, 7]]) sage: A.is_positive_definite() True sage: _, d = A.indefinite_factorization(algorithm='symmetric') @@ -12288,10 +12434,10 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" with a vector that makes the quadratic form negative. :: sage: A = matrix(QQ, [[ 3, -6, 9, 6, -9], - ... [-6, 11, -16, -11, 17], - ... [ 9, -16, 28, 16, -40], - ... [ 6, -11, 16, 9, -19], - ... [-9, 17, -40, -19, 68]]) + ....: [-6, 11, -16, -11, 17], + ....: [ 9, -16, 28, 16, -40], + ....: [ 6, -11, 16, 9, -19], + ....: [-9, 17, -40, -19, 68]]) sage: A.is_positive_definite() False sage: _, d = A.indefinite_factorization(algorithm='symmetric') @@ -12308,9 +12454,9 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" The vector ``u`` makes the quadratic form zero. :: sage: A = matrix(QQ, [[21, 15, 12, -2], - ... [15, 12, 9, 6], - ... [12, 9, 7, 3], - ... [-2, 6, 3, 8]]) + ....: [15, 12, 9, 6], + ....: [12, 9, 7, 3], + ....: [-2, 6, 3, 8]]) sage: A.is_positive_definite() False sage: [A[:i,:i].determinant() for i in range(1,A.nrows()+1)] @@ -12321,11 +12467,11 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" An Hermitian matrix that is positive definite. :: - sage: C. = NumberField(x^2 + 1) + sage: C. = NumberField(x^2 + 1, embedding=CC(0,1)) sage: A = matrix(C, [[ 23, 17*I + 3, 24*I + 25, 21*I], - ... [ -17*I + 3, 38, -69*I + 89, 7*I + 15], - ... [-24*I + 25, 69*I + 89, 976, 24*I + 6], - ... [ -21*I, -7*I + 15, -24*I + 6, 28]]) + ....: [ -17*I + 3, 38, -69*I + 89, 7*I + 15], + ....: [-24*I + 25, 69*I + 89, 976, 24*I + 6], + ....: [ -21*I, -7*I + 15, -24*I + 6, 28]]) sage: A.is_positive_definite() True sage: _, d = A.indefinite_factorization(algorithm='hermitian') @@ -12339,8 +12485,8 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: C. = QuadraticField(-1) sage: B = matrix(C, [[ 2, 4 - 2*I, 2 + 2*I], - ... [4 + 2*I, 8, 10*I], - ... [2 - 2*I, -10*I, -3]]) + ....: [4 + 2*I, 8, 10*I], + ....: [2 - 2*I, -10*I, -3]]) sage: B.is_positive_definite() False sage: _, d = B.indefinite_factorization(algorithm='hermitian') @@ -12355,8 +12501,8 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" A positive definite matrix over an algebraically closed field. :: sage: A = matrix(QQbar, [[ 2, 4 + 2*I, 6 - 4*I], - ... [ -2*I + 4, 11, 10 - 12*I], - ... [ 4*I + 6, 10 + 12*I, 37]]) + ....: [ -2*I + 4, 11, 10 - 12*I], + ....: [ 4*I + 6, 10 + 12*I, 37]]) sage: A.is_positive_definite() True sage: [A[:i,:i].determinant() for i in range(1,A.nrows()+1)] @@ -12377,57 +12523,48 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" AttributeError: 'sage.rings.finite_rings.element_givaro.FiniteField_givaroElement' object has no attribute 'conjugate' sage: A = matrix(F, - ... [[ a^2 + 2*a, 4*a^2 + 3*a + 4, 3*a^2 + a, 2*a^2 + 2*a + 1], - ... [4*a^2 + 3*a + 4, 4*a^2 + 2, 3*a, 2*a^2 + 4*a + 2], - ... [ 3*a^2 + a, 3*a, 3*a^2 + 2, 3*a^2 + 2*a + 3], - ... [2*a^2 + 2*a + 1, 2*a^2 + 4*a + 2, 3*a^2 + 2*a + 3, 3*a^2 + 2*a + 4]]) + ....: [[ a^2 + 2*a, 4*a^2 + 3*a + 4, 3*a^2 + a, 2*a^2 + 2*a + 1], + ....: [4*a^2 + 3*a + 4, 4*a^2 + 2, 3*a, 2*a^2 + 4*a + 2], + ....: [ 3*a^2 + a, 3*a, 3*a^2 + 2, 3*a^2 + 2*a + 3], + ....: [2*a^2 + 2*a + 1, 2*a^2 + 4*a + 2, 3*a^2 + 2*a + 3, 3*a^2 + 2*a + 4]]) sage: A.is_positive_definite() Traceback (most recent call last): ... - TypeError: cannot convert computations from - Finite Field in a of size 5^3 into real numbers + ValueError: Could not see Finite Field in a of size 5^3 as a subring + of the real or complex numbers AUTHOR: - Rob Beezer (2012-05-24) """ - from sage.rings.all import RR - # check to see if the Hermitian routine is usable - # otherwise we will assume the symmetric case - imaginary = True - a = self.base_ring().an_element() - try: - a.conjugate() - except AttributeError: - imaginary = False - if imaginary: - if not self.is_hermitian(): - return False - else: + from sage.rings.real_lazy import RLF,CLF + + R = self.base_ring() + if RLF.has_coerce_map_from(R): if not self.is_symmetric(): return False - try: - if imaginary: - _, d = self._indefinite_factorization('hermitian', check=False) - else: - _, d = self._indefinite_factorization('symmetric', check=False) - except ValueError as e: - # a zero singular leading principal submatrix is one - # indicator that the matrix is not positive definite - if str(e).find('leading principal submatrix is singular') != -1: + L, d = self._indefinite_factorization('symmetric', check=False) + real = True + elif CLF.has_coerce_map_from(R): + if not self.is_hermitian(): return False - else: - raise ValueError(e) + L, d = self._indefinite_factorization('hermitian', check=False) + real = False + else: + raise ValueError("Could not see {} as a subring of the " + "real or complex numbers".format(R)) + + if L is False: + return False + # Now have diagonal entries (hopefully real) and so can # test with a generator (which will short-circuit) # positive definite iff all entries of d are positive - try: - posdef = all( RR(x) > 0 for x in d ) - except TypeError: - universe = Sequence(d).universe() - msg = "cannot convert computations from {0} into real numbers" - raise TypeError(msg.format(universe)) - return posdef + zero = R.fraction_field().zero() + if real: + return all(x > zero for x in d) + else: + return all(x.real() > zero for x in d) def hadamard_bound(self): r""" @@ -13226,7 +13363,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" - David Loeffler (2009-06-01) - Moritz Minzlaff (2011-03-17): corrected code for matrices of one row; - this fixed trac 9053 + this fixed :trac:`9053` EXAMPLES:: @@ -13274,7 +13411,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" sage: r * m == s and r.det() == 1 True - We verify that trac 9053 is resolved:: + We verify that :trac:`9053` is resolved:: sage: R. = GF(7)[] sage: A = R^3 @@ -13842,12 +13979,12 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" .. rubric:: Citations - .. [STORJOHANN-THESIS] A. Storjohann, Algorithms + .. [STORJOHANN-THESIS] \A. Storjohann, Algorithms for Matrix Canonical Forms. PhD Thesis. Department of Computer Science, Swiss Federal Institute of Technology -- ETH, 2000. - .. [STORJOHANN-ISACC98] A. Storjohann, An O(n^3) + .. [STORJOHANN-ISACC98] \A. Storjohann, An O(n^3) algorithm for Frobenius normal form. Proceedings of the International Symposium on Symbolic and Algebraic Computation (ISSAC'98), ACM Press, 1998, pp. 101-104. @@ -14239,7 +14376,7 @@ explicitly setting the argument to `True` or `False` will avoid this message.""" .. rubric:: Citations - .. [STORJOHANN-EMAIL] A. Storjohann, Email Communication. 30 May 2011. + .. [STORJOHANN-EMAIL] \A. Storjohann, Email Communication. 30 May 2011. AUTHOR: diff --git a/src/sage/matrix/matrix_complex_ball_dense.pxd b/src/sage/matrix/matrix_complex_ball_dense.pxd new file mode 100644 index 00000000000..9b29ed9e9f9 --- /dev/null +++ b/src/sage/matrix/matrix_complex_ball_dense.pxd @@ -0,0 +1,11 @@ +from sage.libs.arb.types cimport acb_mat_t +cimport matrix_dense +from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense +from sage.structure.parent cimport Parent + +cdef void matrix_to_acb_mat(acb_mat_t target, source) +cdef Matrix_generic_dense acb_mat_to_matrix( + acb_mat_t source, Parent CIF) + +cdef class Matrix_complex_ball_dense(matrix_dense.Matrix_dense): + cdef acb_mat_t value diff --git a/src/sage/matrix/matrix_complex_ball_dense.pyx b/src/sage/matrix/matrix_complex_ball_dense.pyx new file mode 100644 index 00000000000..115eb9c7a1a --- /dev/null +++ b/src/sage/matrix/matrix_complex_ball_dense.pyx @@ -0,0 +1,360 @@ +r""" +Arbitrary precision complex ball matrices using Arb + +AUTHORS: + +- Clemens Heuberger (2014-10-25): Initial version. + +This is a rudimentary binding to the `Arb library +`_; it may be useful to refer to its +documentation for more details. + +TESTS:: + + sage: mat = matrix(CBF, 2, 2, range(4)) + sage: x = polygen(QQ) + sage: pol = x^3 + 2 + sage: pol(mat) + [8.000000000000000 11.00000000000000] + [22.00000000000000 41.00000000000000] +""" +#***************************************************************************** +# Copyright (C) 2014 Clemens Heuberger +# +# 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/ +#***************************************************************************** + +include 'cysignals/signals.pxi' + + +from sage.libs.arb.acb cimport * +from sage.libs.arb.acb_mat cimport * +from sage.matrix.matrix cimport Matrix +from sage.matrix.constructor import matrix +from sage.matrix.matrix_generic_sparse cimport Matrix_generic_sparse +from sage.rings.complex_interval_field import ComplexIntervalField_class, ComplexIntervalField +from sage.rings.complex_interval cimport ComplexIntervalFieldElement +from sage.rings.complex_arb cimport ( + ComplexBall, + ComplexIntervalFieldElement_to_acb, + acb_to_ComplexIntervalFieldElement) +from sage.structure.element cimport Element + + +cdef void matrix_to_acb_mat(acb_mat_t target, source): + """ + Convert a matrix containing :class:`ComplexIntervalFieldElement` to an ``acb_mat_t``. + + INPUT: + + - ``target`` -- an ``acb_mat_t`` + + - ``source`` -- a matrix consisting of :class:`ComplexIntervalFieldElement` + + OUTPUT: + + None. + """ + cdef unsigned long nrows, ncols, r, c, precision + + nrows = acb_mat_nrows(target) + ncols = acb_mat_ncols(target) + + for r in range(nrows): + for c in range(ncols): + ComplexIntervalFieldElement_to_acb(acb_mat_entry(target, r, c), + source[r][c]) + +cdef ComplexIntervalFieldElement _to_CIF(acb_t source, ComplexIntervalFieldElement template): + cdef ComplexIntervalFieldElement result + result = template._new() + acb_to_ComplexIntervalFieldElement( + result, source) + return result + +cdef Matrix_generic_dense acb_mat_to_matrix( + acb_mat_t source, Parent CIF): + """ + Convert an ``acb_mat_t`` to a matrix containing :class:`ComplexIntervalFieldElement`. + + INPUT: + + - ``source`` -- an ``acb_mat_t`` + + - ``precision`` -- a positive integer. + + OUTPUT: + + A :class:`~sage.matrix.matrix_generic_dense.Matrix_generic_dense` + containing :class:`ComplexIntervalFieldElement`. + """ + cdef unsigned long nrows, ncols, r, c + cdef ComplexIntervalFieldElement template + + nrows = acb_mat_nrows(source) + ncols = acb_mat_ncols(source) + template = CIF(0) + + return matrix( + [[_to_CIF(acb_mat_entry(source, r, c), template) + for c in range(ncols)] + for r in range(nrows)]) + + +cdef class Matrix_complex_ball_dense(matrix_dense.Matrix_dense): + """ + Matrix over a complex ball field. Implemented using the + ``acb_mat`` type of the Arb library. + + EXAMPLES:: + + sage: MatrixSpace(CBF, 3)(2) + [2.000000000000000 0 0] + [ 0 2.000000000000000 0] + [ 0 0 2.000000000000000] + sage: matrix(CBF, 1, 3, [1, 2, -3]) + [ 1.000000000000000 2.000000000000000 -3.000000000000000] + """ + ################################################################# + # LEVEL 1 functionality + # * __cinit__ + # * __init__ + # * __dealloc__ + # * set_unsafe(self, size_t i, size_t j, x) + # * get_unsafe(self, size_t i, size_t j) + ################################################################ + + def __cinit__(self, + parent, + entries, + coerce, + copy): + """ + Create and allocate memory for the matrix. + + INPUT: + + - ``parent, entries, coerce, copy`` - as for + ``__init__``. + + EXAMPLES:: + + sage: from sage.matrix.matrix_complex_ball_dense import Matrix_complex_ball_dense + sage: a = Matrix_complex_ball_dense.__new__( # indirect doctest + ....: Matrix_complex_ball_dense, Mat(CBF, 2), 0, 0, 0) + sage: type(a) + + """ + self._parent = parent + self._base_ring = parent.base_ring() + self._nrows = parent.nrows() + self._ncols = parent.ncols() + sig_str("Arb exception") + acb_mat_init(self.value, self._nrows, self._ncols) + sig_off() + + def __dealloc__(self): + """ + Free all the memory allocated for this matrix. + + EXAMPLES:: + + sage: a = Matrix(CBF, 2, [1, 2, 3, 4]) # indirect doctest + sage: del a + """ + acb_mat_clear(self.value) + + + def __init__(self, + parent, + entries, + copy, + coerce): + r""" + Initialize a dense matrix over the complex ball field. + + INPUT: + + - ``parent`` - a matrix space + + - ``entries`` - list - create the matrix with those + entries along the rows. + + - ``other`` - a scalar; entries is coerced to a complex ball + and the diagonal entries of this matrix are set to that + complex ball. + + - ``coerce`` - whether need to coerce entries to the + complex ball field (program may crash if you get this wrong) + + - ``copy`` - ignored (since complex balls are immutable) + + EXAMPLES: + + The ``__init__`` function is called implicitly in each of the + examples below to actually fill in the values of the matrix. + + We create a `2 \times 2` and a `1\times 4` matrix:: + + sage: matrix(CBF, 2, 2, range(4)) + [ 0 1.000000000000000] + [2.000000000000000 3.000000000000000] + sage: Matrix(CBF, 1, 4, range(4)) + [ 0 1.000000000000000 2.000000000000000 3.000000000000000] + + If the number of columns isn't given, it is determined from the + number of elements in the list. :: + + sage: matrix(CBF, 2, range(4)) + [ 0 1.000000000000000] + [2.000000000000000 3.000000000000000] + sage: matrix(CBF, 2, range(6)) + [ 0 1.000000000000000 2.000000000000000] + [3.000000000000000 4.000000000000000 5.000000000000000] + + Another way to make a matrix is to create the space of matrices and + convert lists into it. :: + + sage: A = Mat(CBF, 2); A + Full MatrixSpace of 2 by 2 dense matrices over + Complex ball field with 53 bits precision + sage: A(range(4)) + [ 0 1.000000000000000] + [2.000000000000000 3.000000000000000] + + Actually it is only necessary that the input can be converted to a + list, so the following also works:: + + sage: v = reversed(range(4)); type(v) + + sage: A(v) + [3.000000000000000 2.000000000000000] + [1.000000000000000 0] + + Matrices can have many rows or columns (in fact, on a 64-bit + machine they could have up to `2^{63}-1` rows or columns):: + + sage: v = matrix(CBF, 1, 10^5, range(10^5)) + sage: v.parent() + Full MatrixSpace of 1 by 100000 dense matrices over + Complex ball field with 53 bits precision + + TESTS:: + + sage: MatrixSpace(CBF, 0, 0).one() + [] + sage: Matrix(CBF, 0, 100) + 0 x 100 dense matrix over Complex ball field with 53 bits precision + (use the '.str()' method to see the entries) + sage: Matrix(CBF, 100, 0) + 100 x 0 dense matrix over Complex ball field with 53 bits precision + (use the '.str()' method to see the entries) + """ + cdef Py_ssize_t i, j, k + cdef bint is_list + cdef ComplexBall x + + if entries is None: + x = self._base_ring.zero() + is_list = False + elif isinstance(entries, (int, long, Element)): + try: + x = self._base_ring(entries) + except TypeError: + raise TypeError("unable to convert entry to a complex ball") + is_list = False + else: + entries = list(entries) + is_list = True + + if is_list: + # Create the matrix whose entries are in the given entry list. + if len(entries) != self._nrows * self._ncols: + raise TypeError("entries has the wrong length") + if coerce: + k = 0 + for i in range(self._nrows): + for j in range(self._ncols): + x = self._base_ring(entries[k]) + acb_set(acb_mat_entry(self.value, i, j), + x.value) + k += 1 + else: + k = 0 + for i in range(self._nrows): + for j in range(self._ncols): + acb_set(acb_mat_entry(self.value, i, j), + ( entries[k]).value) + k += 1 + else: + # If x is zero, make the zero matrix and be done. + if acb_is_zero(x.value): + acb_mat_zero(self.value) + return + + # the matrix must be square: + if self._nrows != self._ncols: + raise TypeError("nonzero scalar matrix must be square") + + # Now we set all the diagonal entries to x and all other entries to 0. + acb_mat_zero(self.value) + for i in range(self._nrows): + acb_set(acb_mat_entry(self.value, i, i), x.value) + + cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object x): + """ + Set position ``i``, ``j`` of this matrix to ``x``. + + The object ``x`` must be of type ``ComplexBall``. + + INPUT: + + - ``i`` -- row + + - ``j`` -- column + + - ``x`` -- must be ComplexBall! The value to set self[i,j] to. + + EXAMPLES:: + + sage: a = matrix(CBF, 2, 3, range(6)); a + [ 0 1.000000000000000 2.000000000000000] + [3.000000000000000 4.000000000000000 5.000000000000000] + sage: a[0, 0] = 10 + sage: a + [10.00000000000000 1.000000000000000 2.000000000000000] + [3.000000000000000 4.000000000000000 5.000000000000000] + """ + acb_set(acb_mat_entry(self.value, i, j), ( x).value) + + cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): + """ + Return ``(i, j)`` entry of this matrix as a new ComplexBall. + + .. warning:: + + This is very unsafe; it assumes ``i`` and ``j`` are in the right + range. + + EXAMPLES:: + + sage: a = MatrixSpace(CBF, 3)(range(9)); a + [ 0 1.000000000000000 2.000000000000000] + [3.000000000000000 4.000000000000000 5.000000000000000] + [6.000000000000000 7.000000000000000 8.000000000000000] + sage: a[1, 2] + 5.000000000000000 + sage: a[4, 7] + Traceback (most recent call last): + ... + IndexError: matrix index out of range + sage: a[-1, 0] + 6.000000000000000 + """ + cdef ComplexBall z = ComplexBall.__new__(ComplexBall) + z._parent = self._base_ring + acb_set(z.value, acb_mat_entry(self.value, i, j)) + return z diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index b7a592b419f..a8d5692adde 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -70,7 +70,7 @@ from sage.misc.misc import verbose import math from sage.matrix.matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_modn_dense_double -from sage.ext.multi_modular import MAX_MODULUS as MAX_MODULUS_multi_modular +from sage.arith.multi_modular import MAX_MODULUS as MAX_MODULUS_multi_modular MAX_MODULUS = min(MAX_MODULUS_modn_dense_double, MAX_MODULUS_multi_modular) # parameters for tuning @@ -529,8 +529,8 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): Multiply a dense cyclotomic matrix by a scalar. INPUT: - self -- dense cyclotomic matrix - right --- scalar in the base cyclotomic field + + - ``right`` -- scalar in the base cyclotomic field EXAMPLES:: @@ -548,7 +548,7 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): if right == 1: return self elif right == 0: - return self.parent()(0) + return self.parent().zero() # Create a new matrix object but with the _matrix attribute not initialized: cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, @@ -607,14 +607,14 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): sage: N1*N2 [ 0 -1 -zeta6 zeta6 zeta6 - 1] - Verify that a degenerate case bug reported at trac 5974 is fixed. + Verify that a degenerate case bug reported at :trac:`5974` is fixed. sage: K.=CyclotomicField(6); matrix(K,1,2) * matrix(K,2,[0, 1, 0, -2*zeta6, 0, 0, 1, -2*zeta6 + 1]) [0 0 0 0] TESTS: - This is from trac #8666:: + This is from :trac:`8666`:: sage: K. = CyclotomicField(4) sage: m = matrix(K, [125]) @@ -750,6 +750,7 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): Make a copy of this matrix. EXAMPLES: + We create a cyclotomic matrix.:: sage: W. = CyclotomicField(5) @@ -810,6 +811,7 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): # * Matrix windows -- only if you need strassen for that base # * Other functions (list them here): # * Specialized echelon form + # * tensor product ######################################################################## def set_immutable(self): """ @@ -1831,3 +1833,79 @@ cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense): return (lifted_matrix, pivot_ls) + def tensor_product(self, A, subdivide=True): + r""" + Return the tensor product of two matrices. + + INPUT: + + - ``A`` -- a matrix + - ``subdivide`` -- (default: ``True``) whether or not to return + natural subdivisions with the matrix + + OUTPUT: + + Replace each element of ``self`` by a copy of ``A``, but first + create a scalar multiple of ``A`` by the element it replaces. + So if ``self`` is an `m\times n` matrix and ``A`` is a + `p\times q` matrix, then the tensor product is an `mp\times nq` + matrix. By default, the matrix will be subdivided into + submatrices of size `p\times q`. + + EXAMPLES:: + + sage: C = CyclotomicField(12) + sage: M = matrix.random(C, 3, 3) + sage: N = matrix.random(C, 50, 50) + sage: M.tensor_product(M) == super(type(M), M).tensor_product(M) + True + sage: N = matrix.random(C, 15, 20) + sage: M.tensor_product(N) == super(type(M), M).tensor_product(N) + True + + TESTS:: + + sage: Mp = matrix.random(C, 2,3) + sage: Np = matrix.random(C, 4,5) + sage: subdiv = super(type(Mp),Mp).tensor_product(Np).subdivisions() + sage: Mp.tensor_product(Np).subdivisions() == subdiv + True + """ + if not isinstance(A, Matrix): + raise TypeError('tensor product requires a second matrix, not {0}'.format(A)) + + if A.base_ring() is not self.base_ring(): + return super(Matrix_cyclo_dense, self).tensor_product(A, subdivide) + + cdef Matrix_cyclo_dense M + l = [] + R = self.base_ring() + X = R._generator_matrix() + d = self._degree + MS = MatrixSpace(QQ, d, d) + mlst = self.list() + for c in self._matrix.columns(): + v = c.list() + for n in range(d-1): + c = c * X + v += c.list() + temp = MS(v) + rmul = MS([v[d*i+j] for j in range(d) for i in range(d)]) # We take the transpose + l.append(rmul * A._rational_matrix()) + + nr = self.nrows() + nc = self.ncols() + Anr = A.nrows() + Anc = A.ncols() + P = MatrixSpace(R, nr*Anr, nc*Anc) + M = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, P, + None, None, None) + MS = MatrixSpace(QQ, d, P.nrows()*P.ncols()) + ret = [[l[mr*nc+mc][i,r*Anc+c] for mr in range(nr) for r in range(Anr) + for mc in range(nc) for c in range(Anc)] + for i in range(d)] + M._matrix = MS(ret) + if subdivide: + M.subdivide([Anr*i for i in range(1,nr)], [Anc*i for i in range(1,nc)]) + return M + diff --git a/src/sage/matrix/matrix_dense.pyx b/src/sage/matrix/matrix_dense.pyx index 85e2739a236..9225064d304 100644 --- a/src/sage/matrix/matrix_dense.pyx +++ b/src/sage/matrix/matrix_dense.pyx @@ -10,7 +10,7 @@ TESTS:: cimport matrix -from sage.structure.element cimport Element +from sage.structure.element cimport Element, RingElement import sage.matrix.matrix_space import sage.structure.sequence @@ -295,3 +295,45 @@ cdef class Matrix_dense(matrix.Matrix): image.subdivide(*self.subdivisions()) return image + def _multiply_classical(left, matrix.Matrix right): + """ + Multiply the matrices left and right using the classical `O(n^3)` + algorithm. + + This method will almost always be overridden either by the + implementation in :class:`~sage.matrix.Matrix_generic_dense`) or by + more specialized versions, but having it here makes it possible to + implement specialized dense matrix types with their own data structure + without necessarily implementing ``_multiply_classical``, as described + in :mod:`sage.matrix.docs`. + + TESTS:: + + sage: from sage.matrix.matrix_dense import Matrix_dense + sage: mats = [ + ....: matrix(2, 2, [1, 2, 3, 4]), + ....: matrix(2, 1, [1, 2]), + ....: matrix(3, 2, [1, 2, 3, 4, 5, 6]), + ....: matrix(ZZ, 0, 2), + ....: matrix(ZZ, 2, 0) + ....: ] + sage: all(Matrix_dense._multiply_classical(a, b) == a*b + ....: for a in mats for b in mats if a.ncols() == b.nrows()) + True + sage: Matrix_dense._multiply_classical(matrix(2, 1), matrix(2, 0)) + Traceback (most recent call last): + ... + ArithmeticError: number of columns of left must equal number of rows of right + """ + cdef Py_ssize_t i, j + if left._ncols != right._nrows: + raise ArithmeticError("number of columns of left must equal number of rows of right") + cdef RingElement zero = left.base_ring().zero() + cdef matrix.Matrix res = left.new_matrix(nrows=left._nrows, ncols=right._ncols) + for i in range(left._nrows): + for j in range(right._ncols): + dotp = zero + for k in range(left._ncols): + dotp += left.get_unsafe(i, k) * right.get_unsafe(k, j) + res.set_unsafe(i, j, dotp) + return res diff --git a/src/sage/matrix/matrix_double_dense.pyx b/src/sage/matrix/matrix_double_dense.pyx index 73ab7933714..28c6a095e8f 100644 --- a/src/sage/matrix/matrix_double_dense.pyx +++ b/src/sage/matrix/matrix_double_dense.pyx @@ -664,7 +664,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): an identity matrix can hit the minimum with the right norm. :: sage: A = matrix(RDF, 10, [1/(i+j+1) for i in range(10) for j in range(10)]) - sage: A.condition() # tol 3e-5 + sage: A.condition() # tol 6e-5 16332197709146.014 sage: id = identity_matrix(CDF, 10) sage: id.condition(p=1) @@ -1140,7 +1140,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): [ 8.0 9.0 10.0 11.0] [ 4.0 5.0 6.0 7.0] - Trac 10839 made this routine available for rectangular matrices. :: + :trac:`10839` made this routine available for rectangular matrices. :: sage: A = matrix(RDF, 5, 6, range(30)); A [ 0.0 1.0 2.0 3.0 4.0 5.0] @@ -1183,7 +1183,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): Trivial cases return matrices of the right size and characteristics. :: - sage: A = matrix(RDF, 5, 0, entries=0) + sage: A = matrix(RDF, 5, 0) sage: P, L, U = A.LU() sage: P.parent() Full MatrixSpace of 5 by 5 dense matrices over Real Double Field @@ -1685,7 +1685,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): sage: A.solve_right(vector(RDF,[])) () - The coefficent matrix must be square. :: + The coefficient matrix must be square. :: sage: A = matrix(RDF, 2, 3, range(6)) sage: b = vector(RDF, [1,2,3]) @@ -1788,7 +1788,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): [ 7.6 2.3 1.0] [ 1.0 2.0 -1.0] sage: b = vector(RDF,[1,2,3]) - sage: x = A.solve_left(b); x.zero_at(1e-18) # fix noisy zeroes + sage: x = A.solve_left(b); x.zero_at(1e-17) # fix noisy zeroes (0.666666666..., 0.0, 0.333333333...) sage: x.parent() Vector space of dimension 3 over Real Double Field @@ -1825,7 +1825,7 @@ cdef class Matrix_double_dense(matrix_dense.Matrix_dense): sage: A.solve_left(vector(RDF,[])) () - The coefficent matrix must be square. :: + The coefficient matrix must be square. :: sage: A = matrix(RDF, 2, 3, range(6)) sage: b = vector(RDF, [1,2,3]) diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index fa932a4bf8c..892ca5eb7dc 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -1317,7 +1317,7 @@ cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): sage: A[1:200,1:200] == A.submatrix(1,1,199,199) True - TESTS for handling of default arguments (ticket #18761):: + TESTS for handling of default arguments (:trac:`18761`):: sage: A.submatrix(17,15) == A.submatrix(17,15,183,185) True diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index 7f5c1103316..cbbd472b5d1 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -55,12 +55,11 @@ 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' +include "cysignals/memory.pxi" #################### # diff --git a/src/sage/matrix/matrix_integer_2x2.py b/src/sage/matrix/matrix_integer_2x2.py deleted file mode 100644 index e2c631a3538..00000000000 --- a/src/sage/matrix/matrix_integer_2x2.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Deprecated two by two matrices over the integers. - -See :trac:`17824` for more informations. -""" - -def MatrixSpace_ZZ_2x2(): - """ - Return the space of 2x2 integer matrices. - - See :trac:`17824` for more informations. - - EXAMPLES:: - - sage: from sage.matrix.matrix_integer_2x2 import MatrixSpace_ZZ_2x2 - sage: M = MatrixSpace_ZZ_2x2() - doctest:...: DeprecationWarning: MatrixSpace_ZZ_2x2 is deprecated. - Please use MatrixSpace(ZZ,2) instead See http://trac.sagemath.org/17824 - for details. - sage: M - Full MatrixSpace of 2 by 2 dense matrices over Integer Ring - sage: M is MatrixSpace_ZZ_2x2() - True - """ - from sage.misc.superseded import deprecation - deprecation(17824, 'MatrixSpace_ZZ_2x2 is deprecated. Please use MatrixSpace(ZZ,2) instead') - - from sage.matrix.matrix_space import MatrixSpace - from sage.rings.integer_ring import ZZ - return MatrixSpace(ZZ,2) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 5e8445374d3..e53ed6f0569 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -46,6 +46,7 @@ TESTS:: # Copyright (C) 2006,2007 William Stein # Copyright (C) 2014 Marc Masdeu # Copyright (C) 2014 Jeroen Demeyer +# Copyright (C) 2015,2016 Vincent Delecroix # # 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 @@ -77,17 +78,13 @@ import sage.libs.pari.pari_instance cdef PariInstance pari = sage.libs.pari.pari_instance.pari from sage.libs.pari.paridecl cimport * -include "sage/libs/pari/pari_err.pxi" - ######################################################### include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" -from sage.ext.multi_modular import MultiModularBasis -from sage.ext.multi_modular cimport MultiModularBasis - +from sage.arith.multi_modular cimport MultiModularBasis from sage.rings.integer cimport Integer from sage.rings.rational_field import QQ from sage.rings.real_double import RDF @@ -235,12 +232,12 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse cdef Py_ssize_t i, j, k sig_on() - self._rows = sage_malloc(sizeof(mpz_t*) * self._nrows) + self._rows = sig_malloc(sizeof(mpz_t*) * self._nrows) if not self._rows: raise MemoryError - self._entries = sage_malloc(sizeof(mpz_t) * self._nrows * self._ncols) + self._entries = sig_malloc(sizeof(mpz_t) * self._nrows * self._ncols) if not self._entries: - sage_free(self._rows) + sig_free(self._rows) raise MemoryError k = 0 for i in range(self._nrows): @@ -259,8 +256,8 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse cdef Py_ssize_t k for k in range(self._nrows * self._ncols): mpz_clear(self._entries[k]) - sage_free(self._rows) - sage_free(self._entries) + sig_free(self._rows) + sig_free(self._entries) self._initialized_mpz = False def __hash__(self): @@ -625,7 +622,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse data = '' else: n = self._nrows*self._ncols*10 - s = sage_malloc(n * sizeof(char)) + s = sig_malloc(n * sizeof(char)) t = s len_so_far = 0 @@ -637,9 +634,9 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse if len_so_far + m + 2 >= n: # copy to new string with double the size n = 2*n + m + 1 - tmp = sage_malloc(n * sizeof(char)) + tmp = sig_malloc(n * sizeof(char)) strcpy(tmp, s) - sage_free(s) + sig_free(s) s = tmp t = s + len_so_far #endif @@ -652,7 +649,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse t = t + 1 sig_off() data = str(s)[:-1] - sage_free(s) + sig_free(s) return data def _unpickle(self, data, int version): @@ -757,7 +754,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse def __nonzero__(self): r""" - Tests whether self is the zero matrix. + Tests whether self is not the zero matrix. EXAMPLES:: @@ -783,6 +780,21 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse """ return not fmpz_mat_is_zero(self._matrix) + def is_one(self): + r""" + Tests whether self is the identity matrix. + + EXAMPLES:: + + sage: matrix(2, [1,0,0,1]).is_one() + True + sage: matrix(2, [1,1,0,1]).is_one() + False + sage: matrix(2, 3, [1,0,0,0,1,0]).is_one() + False + """ + return self.is_square() and fmpz_mat_is_one(self._matrix) + def _multiply_linbox(self, Matrix_integer_dense right): """ Multiply matrices over ZZ using linbox. @@ -1521,7 +1533,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse nc = self._ncols cdef mod_int *entry_list - entry_list = sage_malloc(sizeof(mod_int) * n) + entry_list = sig_malloc(sizeof(mod_int) * n) if entry_list == NULL: raise MemoryError("out of memory allocating multi-modular coefficient list") @@ -1537,7 +1549,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse (res[k])._matrix[i][j] = (entry_list[k])%(res[k]).p sig_off() mpz_clear(tmp) - sage_free(entry_list) + sig_free(entry_list) return res def _echelon_in_place_classical(self): @@ -2295,7 +2307,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse [0 2] [0 0] - Empty matrices are handled sensibly (see trac #3068):: + Empty matrices are handled sensibly (see :trac:`3068`):: sage: m = MatrixSpace(ZZ, 2,0)(0); d,u,v = m.smith_form(); u*m*v == d True @@ -2740,11 +2752,11 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse REFERENCES: - .. [SH95] C. P. Schnorr and H. H. Hörner. *Attacking the Chor-Rivest + .. [SH95] \C. P. Schnorr and H. H. Hörner. *Attacking the Chor-Rivest Cryptosystem by Improved Lattice Reduction*. Advances in Cryptology - EUROCRYPT '95. LNCS Volume 921, 1995, pp 1-12. - .. [GL96] G. Golub and C. van Loan. *Matrix Computations*. + .. [GL96] \G. Golub and C. van Loan. *Matrix Computations*. 3rd edition, Johns Hopkins Univ. Press, 1996. """ @@ -3611,7 +3623,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: matrix(ZZ,3,[1..9])._det_pari(1) 0 """ - pari_catch_sig_on() + sig_on() cdef GEN d = det0(pari_GEN(self), flag) # now convert d to a Sage integer e cdef Integer e = PY_NEW(Integer) @@ -3660,7 +3672,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse fmpz_set_mpz(fmpz_mat_entry(M._matrix,i,j), mp_N[k]) mpz_clear(mp_N[k]) k += 1 - sage_free(mp_N) + sig_free(mp_N) verbose("finished computing null space", time) return M @@ -4168,7 +4180,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sig_check() verbose("Initializing mp_N and mp_D") - mp_N = sage_malloc( n * m * sizeof(mpz_t) ) + mp_N = sig_malloc( n * m * sizeof(mpz_t) ) for i from 0 <= i < n * m: mpz_init(mp_N[i]) mpz_init(mp_D) @@ -4193,7 +4205,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse mpz_clear(mp_D) for i from 0 <= i < n*m: mpz_clear(mp_N[i]) - sage_free(mp_N) + sig_free(mp_N) def _solve_flint(self, Matrix_integer_dense B, right=True): """ @@ -4759,7 +4771,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse for j from 0 <= j < self._ncols: res.set_unsafe_si(i,j,res_l[k]) k += 1 - sage_free(res_l) + sig_free(res_l) cdef int* _hnf_modn_impl(Matrix_integer_dense self, unsigned int det, @@ -4778,34 +4790,34 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse cdef int u, v, d # allocate memory for result matrix - res = sage_malloc(sizeof(int)*ncols*nrows) + res = sig_malloc(sizeof(int)*ncols*nrows) if res == NULL: raise MemoryError("out of memory allocating a matrix") - res_rows = sage_malloc(sizeof(int*)*nrows) + res_rows = sig_malloc(sizeof(int*)*nrows) if res_rows == NULL: - sage_free(res) + sig_free(res) raise MemoryError("out of memory allocating a matrix") # allocate memory for temporary matrix - T_ent = sage_malloc(sizeof(int)*ncols*nrows) + T_ent = sig_malloc(sizeof(int)*ncols*nrows) if T_ent == NULL: - sage_free(res) - sage_free(res_rows) + sig_free(res) + sig_free(res_rows) raise MemoryError("out of memory allocating a matrix") - T_rows = sage_malloc(sizeof(int*)*nrows) + T_rows = sig_malloc(sizeof(int*)*nrows) if T_rows == NULL: - sage_free(res) - sage_free(res_rows) - sage_free(T_ent) + sig_free(res) + sig_free(res_rows) + sig_free(T_ent) raise MemoryError("out of memory allocating a matrix") # allocate memory for temporary row vector - B = sage_malloc(sizeof(int)*nrows) + B = sig_malloc(sizeof(int)*nrows) if B == NULL: - sage_free(res) - sage_free(res_rows) - sage_free(T_ent) - sage_free(T_rows) + sig_free(res) + sig_free(res_rows) + sig_free(T_ent) + sig_free(T_rows) raise MemoryError("out of memory allocating a matrix") # initialize the row pointers @@ -4881,10 +4893,10 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse for k from i <= k < ncols: T_rows[i][k] = B[k] - sage_free(B) - sage_free(res_rows) - sage_free(T_ent) - sage_free(T_rows) + sig_free(B) + sig_free(res_rows) + sig_free(T_ent) + sig_free(T_rows) return res @@ -5394,7 +5406,7 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse sage: matrix(ZZ,3,[1..9])._rank_pari() 2 """ - pari_catch_sig_on() + sig_on() cdef long r = rank(pari_GEN(self)) pari.clear_stack() return r @@ -5461,11 +5473,11 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse [Mat(1), [1, 0; 0, 1]] """ cdef GEN A - pari_catch_sig_on() + sig_on() A = pari._new_GEN_from_fmpz_mat_t_rotate90(self._matrix, self._nrows, self._ncols) cdef GEN H = mathnf0(A, flag) B = self.extract_hnf_from_pari_matrix(H, flag, include_zero_rows) - pari.clear_stack() # This calls pari_catch_sig_off() + pari.clear_stack() # This calls sig_off() return B @@ -5524,9 +5536,9 @@ cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse """ cdef gen H = pari.integer_matrix(self._matrix, self._nrows, self._ncols, 1) H = H.mathnf(flag) - pari_catch_sig_on() + sig_on() B = self.extract_hnf_from_pari_matrix(H.g, flag, include_zero_rows) - pari.clear_stack() # This calls pari_catch_sig_off() + pari.clear_stack() # This calls sig_off() return B cdef extract_hnf_from_pari_matrix(self, GEN H, int flag, bint include_zero_rows): @@ -5622,7 +5634,7 @@ cpdef _lift_crt(Matrix_integer_dense M, residues, moduli=None): [-3 0 -1 6] [ 1 -1 0 -2] - sage: from sage.ext.multi_modular import MultiModularBasis + sage: from sage.arith.multi_modular import MultiModularBasis sage: mm = MultiModularBasis([5,7,11]) sage: _lift_crt(Matrix(ZZ, 4, 4), [T1, T2, T3], mm) [ 1 4 -1 0] @@ -5635,7 +5647,7 @@ cpdef _lift_crt(Matrix_integer_dense M, residues, moduli=None): for ``Matrix_modn_dense_double`` to be able to represent the ``residues`` :: - sage: from sage.ext.multi_modular import MAX_MODULUS as MAX_multi_modular + sage: from sage.arith.multi_modular import MAX_MODULUS as MAX_multi_modular sage: from sage.matrix.matrix_modn_dense_double import MAX_MODULUS as MAX_modn_dense_double sage: MAX_MODULUS = min(MAX_multi_modular, MAX_modn_dense_double) sage: p0 = previous_prime(MAX_MODULUS) @@ -5648,7 +5660,7 @@ cpdef _lift_crt(Matrix_integer_dense M, residues, moduli=None): cdef size_t i, j, k cdef Py_ssize_t nr, n - cdef mpz_t *tmp = sage_malloc(sizeof(mpz_t) * M._ncols) + cdef mpz_t *tmp = sig_malloc(sizeof(mpz_t) * M._ncols) n = len(residues) if n == 0: # special case: obviously residues[0] wouldn't make sense here. return M @@ -5670,13 +5682,13 @@ cpdef _lift_crt(Matrix_integer_dense M, residues, moduli=None): raise TypeError("Can only perform CRT on list of matrices mod n.") cdef mod_int **row_list - row_list = sage_malloc(sizeof(mod_int*) * n) + row_list = sig_malloc(sizeof(mod_int*) * n) if row_list == NULL: raise MemoryError("out of memory allocating multi-modular coefficient list") sig_on() for k in range(n): - row_list[k] = sage_malloc(sizeof(mod_int) * nc) + row_list[k] = sig_malloc(sizeof(mod_int) * nc) if row_list[k] == NULL: raise MemoryError("out of memory allocating multi-modular coefficient list") @@ -5691,11 +5703,11 @@ cpdef _lift_crt(Matrix_integer_dense M, residues, moduli=None): M.set_unsafe_mpz(i,j,tmp[j]) for k in range(n): - sage_free(row_list[k]) + sig_free(row_list[k]) for j in range(M._ncols): mpz_clear(tmp[j]) - sage_free(row_list) - sage_free(tmp) + sig_free(row_list) + sig_free(tmp) sig_off() return M diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index 06245809fe4..7e1c4543d85 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -28,7 +28,7 @@ include 'sage/modules/vector_modn_sparse_h.pxi' include 'sage/modules/vector_modn_sparse_c.pxi' from cpython.sequence cimport * -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from sage.libs.gmp.mpz cimport * from sage.rings.integer cimport Integer @@ -59,7 +59,7 @@ cdef class Matrix_integer_sparse(matrix_sparse.Matrix_sparse): # set the parent, nrows, ncols, etc. matrix_sparse.Matrix_sparse.__init__(self, parent) - self._matrix = sage_malloc(parent.nrows()*sizeof(mpz_vector)) + self._matrix = sig_malloc(parent.nrows()*sizeof(mpz_vector)) if self._matrix == NULL: raise MemoryError("error allocating sparse matrix") @@ -74,7 +74,7 @@ cdef class Matrix_integer_sparse(matrix_sparse.Matrix_sparse): if self._initialized: for i from 0 <= i < self._nrows: mpz_vector_clear(&self._matrix[i]) - sage_free(self._matrix) + sig_free(self._matrix) def __init__(self, parent, entries, copy, coerce): """ @@ -383,7 +383,7 @@ cdef class Matrix_integer_sparse(matrix_sparse.Matrix_sparse): TEST: - Check that ticket #9345 is fixed:: + Check that :trac:`9345` is fixed:: sage: A = random_matrix(ZZ, 3, 3, sparse = True) sage: A.rational_reconstruction(0) @@ -600,7 +600,7 @@ cdef class Matrix_integer_sparse(matrix_sparse.Matrix_sparse): [0 2] [0 0] - The examples above show that Trac ticket #10626 has been implemented. + The examples above show that :trac:`10626` has been implemented. .. seealso:: diff --git a/src/sage/matrix/matrix_misc.py b/src/sage/matrix/matrix_misc.py index 41f01b0aee7..6f1477edafe 100644 --- a/src/sage/matrix/matrix_misc.py +++ b/src/sage/matrix/matrix_misc.py @@ -435,7 +435,7 @@ def permanental_minor_polynomial(A, permanent_only=False, var='t', prec=None): REFERENCES: - .. [ButPer] P. Butera and M. Pernici "Sums of permanental minors + .. [ButPer] \P. Butera and M. Pernici "Sums of permanental minors using Grassmann algebra", :arxiv:`1406.5337` """ if permanent_only: diff --git a/src/sage/matrix/matrix_mod2_dense.pyx b/src/sage/matrix/matrix_mod2_dense.pyx index 64639500bb6..45c6e3fd660 100644 --- a/src/sage/matrix/matrix_mod2_dense.pyx +++ b/src/sage/matrix/matrix_mod2_dense.pyx @@ -101,7 +101,7 @@ TODO: #***************************************************************************** include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" cimport matrix_dense from libc.stdio cimport * @@ -183,7 +183,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse [0 1 0] [0 0 1] - See trac #10858:: + See :trac:`10858`:: sage: matrix(GF(2),0,[]) * vector(GF(2),0,[]) () @@ -726,19 +726,19 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse REFERENCES: - .. [AHU] A. Aho, J. Hopcroft, and J. Ullman. 'Chapter 6: + .. [AHU] \A. Aho, J. Hopcroft, and J. Ullman. 'Chapter 6: Matrix Multiplication and Related Operations.' The Design and Analysis of Computer Algorithms. Addison-Wesley, 1974. - .. [ADKF70] V. Arlazarov, E. Dinic, M. Kronrod, and + .. [ADKF70] \V. Arlazarov, E. Dinic, M. Kronrod, and I. Faradzev. 'On Economical Construction of the Transitive Closure of a Directed Graph.' Dokl. Akad. Nauk. SSSR No. 194 (in Russian), English Translation in Soviet Math Dokl. No. 11, 1970. - .. [Bard06] G. Bard. 'Accelerating Cryptanalysis with the + .. [Bard06] \G. Bard. 'Accelerating Cryptanalysis with the Method of Four Russians'. Cryptography E-Print Archive (http://eprint.iacr.org/2006/251.pdf), 2006. @@ -1087,7 +1087,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse REFERENCES: - .. [Bard06] G. Bard. 'Accelerating Cryptanalysis with the Method of + .. [Bard06] \G. Bard. 'Accelerating Cryptanalysis with the Method of Four Russians'. Cryptography E-Print Archive (http://eprint.iacr.org/2006/251.pdf), 2006. """ @@ -1727,7 +1727,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sage: A[1:200,1:200] == A.submatrix(1,1,199,199) True - TESTS for handling of default arguments (ticket #18761):: + TESTS for handling of default arguments (:trac:`18761`):: sage: A.submatrix(17,15) == A.submatrix(17,15,183,185) True @@ -1822,7 +1822,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse data = '' else: n = self._nrows*self._ncols*2 + 2 - s = sage_malloc(n * sizeof(char)) + s = sig_malloc(n * sizeof(char)) k = 0 sig_on() for i in range(self._nrows): @@ -1834,7 +1834,7 @@ cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse sig_off() s[k-1] = 0 data = str(s) - sage_free(s) + sig_free(s) return data def density(self, approx=False): @@ -2068,7 +2068,7 @@ def unpickle_matrix_mod2_dense_v1(r, c, data, size): if r == 0 or c == 0: return A - cdef signed char *buf = sage_malloc(size) + cdef signed char *buf = sig_malloc(size) for i from 0 <= i < size: buf[i] = data[i] @@ -2076,7 +2076,7 @@ def unpickle_matrix_mod2_dense_v1(r, c, data, size): cdef gdImagePtr im = gdImageCreateFromPngPtr(size, buf) sig_off() - sage_free(buf) + sig_free(buf) if gdImageSX(im) != c or gdImageSY(im) != r: raise TypeError("Pickled data dimension doesn't match.") diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index 39dd100e11d..12611abc045 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -91,7 +91,7 @@ include "cysignals/signals.pxi" from libc.stdint cimport uint64_t from cpython.string cimport * -from sage.ext.memory cimport sage_malloc, sage_free +include "cysignals/memory.pxi" from sage.libs.gmp.mpz cimport * from sage.libs.linbox.fflas cimport fflas_trans_enum, fflas_no_trans, fflas_trans, \ fflas_right, vector, list as std_list @@ -175,8 +175,8 @@ cdef inline linbox_echelonize(celement modulus, celement* entries, Py_ssize_t nr cdef Py_ssize_t i, j cdef ModField *F = new ModField(modulus) - cdef size_t* P = sage_malloc(sizeof(size_t)*nrows) - cdef size_t* Q = sage_malloc(sizeof(size_t)*ncols) + cdef size_t* P = sig_malloc(sizeof(size_t)*nrows) + cdef size_t* Q = sig_malloc(sizeof(size_t)*ncols) if nrows*ncols > 1000: sig_on() cdef Py_ssize_t r = Mod_echelon(F[0], nrows, ncols, entries, ncols, P, Q) @@ -192,8 +192,8 @@ cdef inline linbox_echelonize(celement modulus, celement* entries, Py_ssize_t nr cdef list pivots = [int(Q[i]) for i in range(r)] - sage_free(P) - sage_free(Q) + sig_free(P) + sig_free(Q) del F return r, pivots @@ -237,7 +237,7 @@ cdef inline celement *linbox_copy(celement modulus, celement *entries, Py_ssize """ Create a copy of the entries array. """ - cdef celement *entries_copy = sage_malloc(sizeof(celement)*nrows*ncols) + cdef celement *entries_copy = sig_malloc(sizeof(celement)*nrows*ncols) memcpy(entries_copy, entries, sizeof(celement)*nrows*ncols) return entries_copy @@ -252,7 +252,7 @@ cdef inline int linbox_rank(celement modulus, celement* entries, Py_ssize_t nrow if nrows*ncols > 1000: sig_on() r = ModRank(F[0], nrows, ncols, cpy, ncols) if nrows*ncols > 1000: sig_off() - sage_free(cpy) + sig_free(cpy) del F return r @@ -265,7 +265,7 @@ cdef inline celement linbox_det(celement modulus, celement* entries, Py_ssize_t if nrows*ncols > 1000: sig_on() d = ModDet(F[0], nrows, ncols, cpy, ncols) if nrows*ncols > 1000: sig_off() - sage_free(cpy) + sig_free(cpy) del F return d @@ -306,8 +306,8 @@ cdef inline linbox_minpoly(celement modulus, Py_ssize_t nrows, celement* entries cdef Py_ssize_t i cdef ModField *F = new ModField(modulus) cdef vector[ModFieldElement] *minP = new vector[ModFieldElement]() - cdef ModFieldElement *X = sage_malloc(nrows*(nrows+1)*sizeof(ModFieldElement)) - cdef size_t *P = sage_malloc(nrows*sizeof(size_t)) + cdef ModFieldElement *X = sig_malloc(nrows*(nrows+1)*sizeof(ModFieldElement)) + cdef size_t *P = sig_malloc(nrows*sizeof(size_t)) cdef celement *cpy = linbox_copy(modulus, entries, nrows, nrows) @@ -315,14 +315,14 @@ cdef inline linbox_minpoly(celement modulus, Py_ssize_t nrows, celement* entries Mod_MinPoly(F[0], minP[0], nrows, cpy, nrows, X, nrows, P) if nrows*nrows > 1000: sig_off() - sage_free(cpy) + sig_free(cpy) l = [] for i in range(minP.size()): l.append( minP.at(i) ) - sage_free(P) - sage_free(X) + sig_free(P) + sig_free(X) del F return l @@ -341,7 +341,7 @@ cdef inline linbox_charpoly(celement modulus, Py_ssize_t nrows, celement* entrie Mod_CharPoly(F[0], P_list, nrows, cpy, nrows) if nrows*nrows > 1000: sig_off() - sage_free(cpy) + sig_free(cpy) cdef vector[ModFieldElement] tmp l = [] @@ -390,16 +390,16 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): raise OverflowError("p (=%s) must be < %s."%(p, MAX_MODULUS)) sig_on() - self._entries = sage_malloc(sizeof(celement)*self._nrows*self._ncols) + self._entries = sig_malloc(sizeof(celement)*self._nrows*self._ncols) sig_off() if self._entries == NULL: raise MemoryError("Error allocating matrix.") sig_on() - self._matrix = sage_malloc(sizeof(celement*)*self._nrows) + self._matrix = sig_malloc(sizeof(celement*)*self._nrows) sig_off() if self._matrix == NULL: - sage_free(self._entries) + sig_free(self._entries) self._entries = NULL raise MemoryError("Error allocating memory.") @@ -429,8 +429,8 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): """ if self._entries == NULL: return - sage_free(self._entries) - sage_free(self._matrix) + sig_free(self._entries) + sig_free(self._matrix) def __init__(self, parent, entries, copy, coerce): """ @@ -626,7 +626,7 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): else: word_size = sizeof(mod_int) - cdef void *buf = sage_malloc(word_size * self._nrows * self._ncols) + cdef void *buf = sig_malloc(word_size * self._nrows * self._ncols) if not buf: raise MemoryError @@ -649,7 +649,7 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): s = PyString_FromStringAndSize(buf, word_size * self._nrows * self._ncols) finally: - sage_free(buf) + sig_free(buf) sig_off() return (word_size, little_endian, s), 10 @@ -1234,8 +1234,8 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): cdef Py_ssize_t i cdef Vector_modn_dense b = v - cdef celement *_b = sage_malloc(sizeof(celement)*self._nrows) - cdef celement *_c = sage_malloc(sizeof(celement)*self._ncols) + cdef celement *_b = sig_malloc(sizeof(celement)*self._nrows) + cdef celement *_c = sig_malloc(sizeof(celement)*self._ncols) for i in range(self._nrows): _b[i] = b._entries[i] @@ -1244,8 +1244,8 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): for i in range(self._ncols): c._entries[i] = _c[i] - sage_free(_b) - sage_free(_c) + sig_free(_b) + sig_free(_c) return c cdef Vector _matrix_times_vector_(self, Vector v): @@ -1288,8 +1288,8 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): cdef Py_ssize_t i cdef Vector_modn_dense b = v - cdef celement *_b = sage_malloc(sizeof(celement)*self._ncols) - cdef celement *_c = sage_malloc(sizeof(celement)*self._nrows) + cdef celement *_b = sig_malloc(sizeof(celement)*self._ncols) + cdef celement *_c = sig_malloc(sizeof(celement)*self._nrows) for i in range(self._ncols): _b[i] = b._entries[i] @@ -1298,8 +1298,8 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): for i in range(self._nrows): c._entries[i] = _c[i] - sage_free(_b) - sage_free(_c) + sig_free(_b) + sig_free(_c) return c ######################################################################## @@ -2836,7 +2836,7 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): data = '' else: n = self._nrows*self._ncols*(ndigits + 1) + 2 # spaces between each number plus trailing null - s = sage_malloc(n * sizeof(char)) + s = sig_malloc(n * sizeof(char)) t = s sig_on() for i in range(self._nrows * self._ncols): @@ -2844,7 +2844,7 @@ cdef class Matrix_modn_dense_template(matrix_dense.Matrix_dense): sig_off() data = str(s)[:-1] - sage_free(s) + sig_free(s) return data def _list(self): diff --git a/src/sage/matrix/matrix_modn_sparse.pyx b/src/sage/matrix/matrix_modn_sparse.pyx index 045feeca7b0..cfac6f6f041 100644 --- a/src/sage/matrix/matrix_modn_sparse.pyx +++ b/src/sage/matrix/matrix_modn_sparse.pyx @@ -77,7 +77,7 @@ TESTS:: include "sage/ext/cdefs.pxi" include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include 'sage/modules/vector_modn_sparse_c.pxi' from cpython.sequence cimport * @@ -142,7 +142,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): self.p = p - self.rows = sage_malloc(nr*sizeof(c_vector_modint)) + self.rows = sig_malloc(nr*sizeof(c_vector_modint)) if self.rows == NULL: raise MemoryError, "error allocating memory for sparse matrix" @@ -154,7 +154,7 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): cdef int i for i from 0 <= i < self._nrows: clear_c_vector_modint(&self.rows[i]) - sage_free(self.rows) + sig_free(self.rows) def __init__(self, parent, entries, copy, coerce): """ @@ -930,15 +930,15 @@ cdef class Matrix_modn_sparse(matrix_sparse.Matrix_sparse): for i from 0 <= i < self._nrows: L_row = &(L._matrix[i]) A_row = &(self.rows[i]) - sage_free(L_row.entries) - L_row.entries = sage_malloc(sizeof(mpz_t)*A_row.num_nonzero) + sig_free(L_row.entries) + L_row.entries = sig_malloc(sizeof(mpz_t)*A_row.num_nonzero) L_row.num_nonzero = A_row.num_nonzero if L_row.entries == NULL: raise MemoryError, "error allocating space for sparse vector during sparse lift" - sage_free(L_row.positions) - L_row.positions = sage_malloc(sizeof(Py_ssize_t)*A_row.num_nonzero) + sig_free(L_row.positions) + L_row.positions = sig_malloc(sizeof(Py_ssize_t)*A_row.num_nonzero) if L_row.positions == NULL: - sage_free(L_row.entries) + sig_free(L_row.entries) L_row.entries = NULL raise MemoryError, "error allocating space for sparse vector during sparse lift" for j from 0 <= j < A_row.num_nonzero: diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 5b197385b8f..19613e3c5e4 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -91,8 +91,6 @@ import sage.libs.pari.pari_instance cdef PariInstance pari = sage.libs.pari.pari_instance.pari from sage.libs.pari.paridecl cimport * -include "sage/libs/pari/pari_err.pxi" - ######################################################### cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): @@ -138,14 +136,14 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): cdef Py_ssize_t i, k sig_on() - self._entries = sage_malloc(sizeof(mpq_t)*(self._nrows * self._ncols)) + self._entries = sig_malloc(sizeof(mpq_t)*(self._nrows * self._ncols)) if self._entries == NULL: sig_off() raise MemoryError("out of memory allocating a matrix") - self._matrix = sage_malloc(sizeof(mpq_t*) * self._nrows) + self._matrix = sig_malloc(sizeof(mpq_t*) * self._nrows) if self._matrix == NULL: - sage_free(self._entries) + sig_free(self._entries) self._entries = NULL sig_off() raise MemoryError("out of memory allocating a matrix") @@ -166,8 +164,8 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): cdef Py_ssize_t i for i from 0 <= i < self._nrows * self._ncols: mpq_clear(self._entries[i]) - sage_free(self._entries) - sage_free(self._matrix) + sig_free(self._entries) + sig_free(self._matrix) def __init__(self, parent, entries=None, coerce=True, copy=True): @@ -268,7 +266,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): data = '' else: n = self._nrows*self._ncols*10 - s = sage_malloc(n * sizeof(char)) + s = sig_malloc(n * sizeof(char)) t = s len_so_far = 0 @@ -280,9 +278,9 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): if len_so_far + m + 1 >= n: # copy to new string with double the size n = 2*n + m + 1 - tmp = sage_malloc(n * sizeof(char)) + tmp = sig_malloc(n * sizeof(char)) strcpy(tmp, s) - sage_free(s) + sig_free(s) s = tmp t = s + len_so_far #endif @@ -295,7 +293,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): t = t + 1 sig_off() data = str(s)[:-1] - sage_free(s) + sig_free(s) return data cdef _unpickle_version0(self, data): @@ -1518,7 +1516,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): The result is an immutable matrix, so if you want to modify the result then you need to make a copy. This - checks that Trac #10543 is fixed. :: + checks that :trac:`10543` is fixed. :: sage: A = matrix(QQ, 2, range(6)) sage: E = A.echelon_form() @@ -2516,7 +2514,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): """ if self._nrows != self._ncols: raise ValueError("self must be a square matrix") - pari_catch_sig_on() + sig_on() cdef GEN d = det0(pari_GEN(self), flag) # now convert d to a Sage rational cdef Rational e = Rational.__new__(Rational) @@ -2533,7 +2531,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): sage: matrix(QQ,3,[1..9])._rank_pari() 2 """ - pari_catch_sig_on() + sig_on() cdef long r = rank(pari_GEN(self)) pari.clear_stack() return r @@ -2562,7 +2560,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): # pari doesn't work in case of 0 rows or columns # This case is easy, since the answer must be the 0 matrix. return self.matrix_space(self._nrows, right._ncols).zero_matrix().__copy__() - pari_catch_sig_on() + sig_on() cdef GEN M = gmul(pari_GEN(self), pari_GEN(right)) A = new_matrix_from_pari_GEN(self.matrix_space(self._nrows, right._ncols), M) pari.clear_stack() @@ -2586,7 +2584,7 @@ cdef class Matrix_rational_dense(matrix_dense.Matrix_dense): raise ValueError("self must be a square matrix") cdef GEN M, d - pari_catch_sig_on() + sig_on() M = pari_GEN(self) d = ginv(M) diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index 1af06094b70..31241d078c1 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -62,7 +62,7 @@ cdef class Matrix_rational_sparse(matrix_sparse.Matrix_sparse): # set the parent, nrows, ncols, etc. matrix_sparse.Matrix_sparse.__init__(self, parent) - self._matrix = sage_malloc(parent.nrows()*sizeof(mpq_vector)) + self._matrix = sig_malloc(parent.nrows()*sizeof(mpq_vector)) if self._matrix == NULL: raise MemoryError, "error allocating sparse matrix" # initialize the rows @@ -82,7 +82,7 @@ cdef class Matrix_rational_sparse(matrix_sparse.Matrix_sparse): for i from 0 <= i < self._nrows: mpq_vector_clear(&self._matrix[i]) if self._matrix != NULL: - sage_free(self._matrix) + sig_free(self._matrix) def __init__(self, parent, entries, copy, coerce): """ @@ -517,7 +517,7 @@ cdef class Matrix_rational_sparse(matrix_sparse.Matrix_sparse): [ 0 0 1 238/157] [ 0 0 0 0] - Trac #10319 has been fixed: + :trac:`10319` has been fixed:: sage: m = Matrix(QQ, [1], sparse=True); m.echelonize() sage: m = Matrix(QQ, [1], sparse=True); m.echelonize(); m @@ -582,7 +582,7 @@ cdef class Matrix_rational_sparse(matrix_sparse.Matrix_sparse): # Get rid of self's data self._dealloc() # Copy E's data to self's data. - self._matrix = sage_malloc(E._nrows * sizeof(mpq_vector)) + self._matrix = sig_malloc(E._nrows * sizeof(mpq_vector)) if self._matrix == NULL: raise MemoryError, "error allocating sparse matrix" for i from 0 <= i < E._nrows: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index f7603557180..376fa3c26bb 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -985,6 +985,8 @@ def _get_matrix_class(self): sage: type(matrix(GF(16007), 2, range(4))) + sage: type(matrix(CBF, 2, range(4))) + sage: type(matrix(GF(2), 2, range(4))) sage: type(matrix(GF(64,'z'), 2, range(4))) @@ -1032,11 +1034,18 @@ def _get_matrix_class(self): 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 + from sage.symbolic.ring import SR # causes circular imports if R is SR: import matrix_symbolic_dense return matrix_symbolic_dense.Matrix_symbolic_dense + + # ComplexBallField might become a lazy import, + # thus do not import it here too early. + from sage.rings.complex_arb import ComplexBallField + if isinstance(R, ComplexBallField): + import matrix_complex_ball_dense + return matrix_complex_ball_dense.Matrix_complex_ball_dense return matrix_generic_dense.Matrix_generic_dense else: @@ -1127,7 +1136,7 @@ def identity_matrix(self): sage: Er = MS2.identity_matrix() Traceback (most recent call last): ... - TypeError: self must be a space of square matrices + TypeError: identity matrix must be square TESTS:: @@ -1137,7 +1146,7 @@ def identity_matrix(self): ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M). """ if self.__nrows != self.__ncols: - raise TypeError("self must be a space of square matrices") + raise TypeError("identity matrix must be square") A = self.zero_matrix().__copy__() for i in xrange(self.__nrows): A[i,i] = 1 @@ -1331,7 +1340,8 @@ def matrix(self, x=0, coerce=True, copy=True): TESTS: - The following corner cases were problematic while working on #10628:: + The following corner cases were problematic while working on + :trac:`10628`:: sage: MS = MatrixSpace(ZZ,2,1) sage: MS([[1],[2]]) @@ -1343,7 +1353,7 @@ def matrix(self, x=0, coerce=True, copy=True): [ 1.00000000000000] [0.000000000000000] - Trac ticket #10628 allowed to provide the data be lists of matrices, but + :trac:`10628` allowed to provide the data as lists of matrices, but :trac:`13012` prohibited it:: sage: MS = MatrixSpace(ZZ,4,2) @@ -1384,6 +1394,15 @@ def matrix(self, x=0, coerce=True, copy=True): sage: MatrixSpace(h,2,1)([h[1], h[2]]) [h[1]] [h[2]] + + Converting sparse to dense matrices used to be too slow + (:trac:`20470`). Check that this is fixed:: + + sage: m = identity_matrix(GF(2), 2000, sparse=True) + sage: MS = MatrixSpace(GF(2), 2000, sparse=False) + sage: md = MS(m) # used to be slow + sage: md.parent() is MS + True """ if x is None or isinstance(x, (int, integer.Integer)) and x == 0: if self._copy_zero: # faster to copy than to create a new one. @@ -1401,6 +1420,11 @@ def matrix(self, x=0, coerce=True, copy=True): return x.__copy__() else: if x.nrows() == m and x.ncols() == n: + if (x.base_ring() == self.base_ring() + and x.is_sparse() and not sparse): + # If x is sparse and large, calling x.dense_matrix() + # is much faster than calling x.list(). See #20470. + return x.dense_matrix() x = x.list() else: raise ValueError("a matrix from %s cannot be converted to " @@ -1445,7 +1469,7 @@ def matrix(self, x=0, coerce=True, copy=True): copy=False, coerce=coerce) else: return MC(self, new_x, copy=False, coerce=coerce) - except TypeError: + except (TypeError, ValueError): pass if len(x) != m * n: raise TypeError("cannot construct an element of {} from {}!" @@ -1703,7 +1727,10 @@ def test_trivial_matrices_inverse(ring, sparse=True, checkrank=True): If ``checkrank`` is ``False`` then the rank is not checked. This is used the check matrix over ring where echelon form is not implemented. - TODO: must be adapted to category check framework when ready (see trac \#5274). + .. TODO:: + + This must be adapted to category check framework when ready + (see :trac:`5274`). TESTS:: @@ -1789,11 +1816,16 @@ def test_trivial_matrices_inverse(ring, sparse=True, checkrank=True): from sage.matrix.matrix_modn_dense_double import Matrix_modn_dense_double from sage.matrix.matrix_integer_dense import Matrix_integer_dense from sage.structure.sage_object import register_unpickle_override +def _MatrixSpace_ZZ_2x2(): + from sage.rings.integer_ring import ZZ + return MatrixSpace(ZZ,2) register_unpickle_override('sage.matrix.matrix_modn_dense', 'Matrix_modn_dense', Matrix_modn_dense_double) register_unpickle_override('sage.matrix.matrix_integer_2x2', 'Matrix_integer_2x2', Matrix_integer_dense) register_unpickle_override('sage.matrix.matrix_integer_2x2', 'MatrixSpace_ZZ_2x2_class', MatrixSpace) +register_unpickle_override('sage.matrix.matrix_integer_2x2', + 'MatrixSpace_ZZ_2x2', _MatrixSpace_ZZ_2x2) register_unpickle_override('sage.matrix.matrix_mod2e_dense', 'unpickle_matrix_mod2e_dense_v0', matrix_gf2e_dense.unpickle_matrix_gf2e_dense_v0) diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index e80b05f98f5..82b82c4e425 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -18,7 +18,7 @@ from sage.structure.element cimport Element, RingElement, ModuleElement, Vector from sage.rings.ring import is_Ring from sage.misc.misc import verbose -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from cpython cimport * @@ -238,10 +238,10 @@ cdef class Matrix_sparse(matrix.Matrix): right_nonzero = right.nonzero_positions(copy=False, column_order=True) len_left = len(left_nonzero) len_right = len(right_nonzero) - next_row = sage_malloc(sizeof(Py_ssize_t) * left._nrows) - next_col = sage_malloc(sizeof(Py_ssize_t) * right._ncols) + next_row = sig_malloc(sizeof(Py_ssize_t) * left._nrows) + next_col = sig_malloc(sizeof(Py_ssize_t) * right._ncols) if next_row == NULL or next_col == NULL: - if next_row != NULL: sage_free(next_row) + if next_row != NULL: sig_free(next_row) sig_off() raise MemoryError, "out of memory multiplying a matrix" @@ -286,8 +286,8 @@ cdef class Matrix_sparse(matrix.Matrix): k2 = next_col[col] k1 = next_row[row] - sage_free(next_row) - sage_free(next_col) + sig_free(next_row) + sig_free(next_col) sig_off() return left.new_matrix(left._nrows, right._ncols, entries=e, coerce=False, copy=False) @@ -976,7 +976,7 @@ cdef class Matrix_sparse(matrix.Matrix): TESTS: - Verify that Trac #12689 is fixed:: + Verify that :trac:`12689` is fixed:: sage: A = identity_matrix(QQ, 2, sparse=True) sage: B = identity_matrix(ZZ, 2, sparse=True) diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index a9ffef3e9ee..5901545de2b 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -239,7 +239,7 @@ cdef class Matrix_symbolic_dense(Matrix_generic_dense): sage: symbolic_evalue 1/2*sqrt(5) - 1/2 - sage: qqbar_evalue == symbolic_evalue + sage: bool(qqbar_evalue == symbolic_evalue) True A slightly larger matrix with a "nice" spectrum. :: diff --git a/src/sage/matrix/misc.pyx b/src/sage/matrix/misc.pyx index baf9949ce61..8e7dfca527d 100644 --- a/src/sage/matrix/misc.pyx +++ b/src/sage/matrix/misc.pyx @@ -65,7 +65,7 @@ def matrix_integer_dense_rational_reconstruction(Matrix_integer_dense A, Integer TEST: - Check that ticket #9345 is fixed:: + Check that :trac:`9345` is fixed:: sage: A = random_matrix(ZZ, 3) sage: sage.matrix.misc.matrix_integer_dense_rational_reconstruction(A, 0) @@ -148,7 +148,7 @@ def matrix_integer_sparse_rational_reconstruction(Matrix_integer_sparse A, Integ TEST: - Check that ticket #9345 is fixed:: + Check that :trac:`9345` is fixed:: sage: A = random_matrix(ZZ, 3, sparse=True) sage: sage.matrix.misc.matrix_integer_sparse_rational_reconstruction(A, 0) diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index c4cca1dd5bb..8c886ceebd3 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -44,7 +44,7 @@ def strassen_window_multiply(C, A,B, cutoff): AUTHORS: - David Harvey - - Simon King (2011-07): Improve memory efficiency; trac ticket #11610 + - Simon King (2011-07): Improve memory efficiency; :trac:`11610` """ strassen_window_multiply_c(C, A, B, cutoff) diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index 4038f2be2b1..b9110d1706f 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -1042,7 +1042,7 @@ cdef class BasisExchangeMatroid(Matroid): if not self._E: return SetSystem(self._E) cdef bitset_t *comp - comp = sage_malloc((self.full_rank()) * sizeof(bitset_t)) + comp = sig_malloc((self.full_rank()) * sizeof(bitset_t)) e = bitset_first(self._current_basis) i=0 while e>=0: @@ -1160,7 +1160,7 @@ cdef class BasisExchangeMatroid(Matroid): bitset_init(out_neighbors, self._groundset_size) bitset_init(R, self._groundset_size) cdef long* predecessor - predecessor = sage_malloc(self._groundset_size*sizeof(long)) + predecessor = sig_malloc(self._groundset_size*sizeof(long)) cdef long e, u, y cdef bint found_path = True while found_path: @@ -1235,7 +1235,7 @@ cdef class BasisExchangeMatroid(Matroid): bitset_free(todo) bitset_free(out_neighbors) bitset_free(R) - sage_free(predecessor) + sig_free(predecessor) return II, RR @@ -1263,8 +1263,8 @@ cdef class BasisExchangeMatroid(Matroid): cdef bitset_t *todo if self._matroid_rank == 0: return [0] - flats = sage_malloc((self.full_rank() + 1) * sizeof(bitset_t)) - todo = sage_malloc((self.full_rank() + 1) * sizeof(bitset_t)) + flats = sig_malloc((self.full_rank() + 1) * sizeof(bitset_t)) + todo = sig_malloc((self.full_rank() + 1) * sizeof(bitset_t)) for i in xrange(self.full_rank() + 1): bitset_init(flats[i], self._bitset_size) @@ -1278,8 +1278,8 @@ cdef class BasisExchangeMatroid(Matroid): for i in xrange(self.full_rank() + 1): bitset_free(flats[i]) bitset_free(todo[i]) - sage_free(flats) - sage_free(todo) + sig_free(flats) + sig_free(todo) return f_vec cdef _f_vector_rec(self, object f_vec, bitset_t* flats, bitset_t* todo, long elt, long i): @@ -1336,8 +1336,8 @@ cdef class BasisExchangeMatroid(Matroid): return SetSystem(self._E) if r == self.full_rank(): return SetSystem(self._E, subsets=[self.groundset()]) - flats = sage_malloc((r + 1) * sizeof(bitset_t)) - todo = sage_malloc((r + 1) * sizeof(bitset_t)) + flats = sig_malloc((r + 1) * sizeof(bitset_t)) + todo = sig_malloc((r + 1) * sizeof(bitset_t)) for i in xrange(r + 1): bitset_init(flats[i], self._bitset_size) @@ -1351,8 +1351,8 @@ cdef class BasisExchangeMatroid(Matroid): for i in xrange(r + 1): bitset_free(flats[i]) bitset_free(todo[i]) - sage_free(flats) - sage_free(todo) + sig_free(flats) + sig_free(todo) return Rflats cdef _flats_rec(self, SetSystem Rflats, long R, bitset_t* flats, bitset_t* todo, long elt, long i): @@ -1411,8 +1411,8 @@ cdef class BasisExchangeMatroid(Matroid): return SetSystem(self._E) if r == self.full_corank(): return SetSystem(self._E, subsets=[self.groundset()]) - coflats = sage_malloc((r + 1) * sizeof(bitset_t)) - todo = sage_malloc((r + 1) * sizeof(bitset_t)) + coflats = sig_malloc((r + 1) * sizeof(bitset_t)) + todo = sig_malloc((r + 1) * sizeof(bitset_t)) for i in xrange(r + 1): bitset_init(coflats[i], self._bitset_size) @@ -1426,8 +1426,8 @@ cdef class BasisExchangeMatroid(Matroid): for i in xrange(r + 1): bitset_free(coflats[i]) bitset_free(todo[i]) - sage_free(coflats) - sage_free(todo) + sig_free(coflats) + sig_free(todo) return Rcoflats cdef _coflats_rec(self, SetSystem Rcoflats, long R, bitset_t* coflats, bitset_t* todo, long elt, long i): @@ -1458,8 +1458,8 @@ cdef class BasisExchangeMatroid(Matroid): cdef bitset_t *todo if self._groundset_size == 0: return {}, tuple() - flats = sage_malloc((k + 1) * sizeof(bitset_t)) - todo = sage_malloc((k + 1) * sizeof(bitset_t)) + flats = sig_malloc((k + 1) * sizeof(bitset_t)) + todo = sig_malloc((k + 1) * sizeof(bitset_t)) for i in xrange(k + 1): bitset_init(flats[i], self._bitset_size) @@ -1473,8 +1473,8 @@ cdef class BasisExchangeMatroid(Matroid): for i in xrange(k + 1): bitset_free(flats[i]) bitset_free(todo[i]) - sage_free(flats) - sage_free(todo) + sig_free(flats) + sig_free(todo) fie = {} for e in range(self._groundset_size): t = tuple([f_inc[i][e] for i in xrange(k + 1)]) @@ -1571,8 +1571,8 @@ cdef class BasisExchangeMatroid(Matroid): return res r = self.full_rank() - I = sage_malloc((r + 1) * sizeof(bitset_t)) - T = sage_malloc((r + 1) * sizeof(bitset_t)) + I = sig_malloc((r + 1) * sizeof(bitset_t)) + T = sig_malloc((r + 1) * sizeof(bitset_t)) for i in range(r + 1): bitset_init(I[i], self._bitset_size) bitset_init(T[i], self._bitset_size) @@ -1597,8 +1597,8 @@ cdef class BasisExchangeMatroid(Matroid): for i in range(r + 1): bitset_free(I[i]) bitset_free(T[i]) - sage_free(I) - sage_free(T) + sig_free(I) + sig_free(T) return res cpdef independent_r_sets(self, long r): diff --git a/src/sage/matroids/lean_matrix.pyx b/src/sage/matroids/lean_matrix.pyx index 2c920b21519..9de3029eb19 100644 --- a/src/sage/matroids/lean_matrix.pyx +++ b/src/sage/matroids/lean_matrix.pyx @@ -30,7 +30,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include 'sage/data_structures/bitset.pxi' from libc.string cimport memcpy, memset from sage.matrix.matrix2 cimport Matrix @@ -986,7 +986,7 @@ cdef class BinaryMatrix(LeanMatrix): cdef long i, j self._nrows = m self._ncols = n - self._M = sage_malloc(self._nrows * sizeof(bitset_t)) + self._M = sig_malloc(self._nrows * sizeof(bitset_t)) if isinstance(M, BinaryMatrix): j = max(1, (M)._ncols) else: @@ -1035,7 +1035,7 @@ cdef class BinaryMatrix(LeanMatrix): cdef long i for i from 0 <= i < self._nrows: bitset_free(self._M[i]) - sage_free(self._M) + sig_free(self._M) bitset_free(self._temp) def __repr__(self): @@ -1095,9 +1095,9 @@ cdef class BinaryMatrix(LeanMatrix): for i from k <= i < self._nrows: bitset_free(self._M[i]) self._nrows = k - self._M = sage_realloc(self._M, k * sizeof(bitset_t)) + self._M = sig_realloc(self._M, k * sizeof(bitset_t)) if k > self._nrows: - self._M = sage_realloc(self._M, k * sizeof(bitset_t)) + self._M = sig_realloc(self._M, k * sizeof(bitset_t)) c = max(1, self._ncols) for i from self._nrows <= i < k: bitset_init(self._M[i], c) @@ -1321,7 +1321,7 @@ cdef class BinaryMatrix(LeanMatrix): cdef bitset_t mask bitset_init(mask, lc) bitset_clear(mask) - cols = sage_malloc(lc*sizeof(mp_bitcnt_t)) + cols = sig_malloc(lc*sizeof(mp_bitcnt_t)) g = 0 for c in columns: if csage_malloc(lc*sizeof(mp_bitcnt_t)) + gaps = sig_malloc(lc*sizeof(mp_bitcnt_t)) bitset_complement(mask, mask) g = 0 c = bitset_first(mask) @@ -1356,8 +1356,8 @@ cdef class BinaryMatrix(LeanMatrix): for g in xrange(lg): order[gaps[g]] = cols[g] # free up the two arrays and the bitset - sage_free(gaps) - sage_free(cols) + sig_free(gaps) + sig_free(cols) bitset_free(mask) return A, order @@ -1602,8 +1602,8 @@ cdef class TernaryMatrix(LeanMatrix): self._nrows = m self._ncols = n - self._M0 = sage_malloc(self._nrows * sizeof(bitset_t)) - self._M1 = sage_malloc(self._nrows * sizeof(bitset_t)) + self._M0 = sig_malloc(self._nrows * sizeof(bitset_t)) + self._M1 = sig_malloc(self._nrows * sizeof(bitset_t)) if isinstance(M, TernaryMatrix): j = max(1, (M)._ncols) @@ -1659,8 +1659,8 @@ cdef class TernaryMatrix(LeanMatrix): for i from 0 <= i < self._nrows: bitset_free(self._M0[i]) bitset_free(self._M1[i]) - sage_free(self._M0) - sage_free(self._M1) + sig_free(self._M0) + sig_free(self._M1) bitset_free(self._s) bitset_free(self._t) bitset_free(self._u) @@ -1744,11 +1744,11 @@ cdef class TernaryMatrix(LeanMatrix): bitset_free(self._M0[i]) bitset_free(self._M1[i]) self._nrows = k - self._M0 = sage_realloc(self._M0, k * sizeof(bitset_t)) - self._M1 = sage_realloc(self._M1, k * sizeof(bitset_t)) + self._M0 = sig_realloc(self._M0, k * sizeof(bitset_t)) + self._M1 = sig_realloc(self._M1, k * sizeof(bitset_t)) if k > self._nrows: - self._M0 = sage_realloc(self._M0, k * sizeof(bitset_t)) - self._M1 = sage_realloc(self._M1, k * sizeof(bitset_t)) + self._M0 = sig_realloc(self._M0, k * sizeof(bitset_t)) + self._M1 = sig_realloc(self._M1, k * sizeof(bitset_t)) c = max(1, self._ncols) for i from self._nrows <= i < k: bitset_init(self._M0[i], c) @@ -1995,7 +1995,7 @@ cdef class TernaryMatrix(LeanMatrix): cdef bitset_t mask bitset_init(mask, lc) bitset_clear(mask) - cols = sage_malloc(lc*sizeof(mp_bitcnt_t)) + cols = sig_malloc(lc*sizeof(mp_bitcnt_t)) g = 0 for c in columns: if csage_malloc(lc*sizeof(mp_bitcnt_t)) + gaps = sig_malloc(lc*sizeof(mp_bitcnt_t)) bitset_complement(mask, mask) g = 0 c = bitset_first(mask) @@ -2038,8 +2038,8 @@ cdef class TernaryMatrix(LeanMatrix): for g in xrange(lg): order[gaps[g]] = cols[g] # free up the two arrays and the bitset - sage_free(gaps) - sage_free(cols) + sig_free(gaps) + sig_free(cols) bitset_free(mask) return A, order @@ -2152,8 +2152,8 @@ cdef class QuaternaryMatrix(LeanMatrix): cdef long i, j self._nrows = m self._ncols = n - self._M0 = sage_malloc(self._nrows * sizeof(bitset_t)) - self._M1 = sage_malloc(self._nrows * sizeof(bitset_t)) + self._M0 = sig_malloc(self._nrows * sizeof(bitset_t)) + self._M1 = sig_malloc(self._nrows * sizeof(bitset_t)) if isinstance(M, QuaternaryMatrix): j = max(1, (M)._ncols) @@ -2234,8 +2234,8 @@ cdef class QuaternaryMatrix(LeanMatrix): for i from 0 <= i < self._nrows: bitset_free(self._M0[i]) bitset_free(self._M1[i]) - sage_free(self._M0) - sage_free(self._M1) + sig_free(self._M0) + sig_free(self._M1) bitset_free(self._s) bitset_free(self._t) bitset_free(self._u) @@ -2345,11 +2345,11 @@ cdef class QuaternaryMatrix(LeanMatrix): bitset_free(self._M0[i]) bitset_free(self._M1[i]) self._nrows = k - self._M0 = sage_realloc(self._M0, k * sizeof(bitset_t)) - self._M1 = sage_realloc(self._M1, k * sizeof(bitset_t)) + self._M0 = sig_realloc(self._M0, k * sizeof(bitset_t)) + self._M1 = sig_realloc(self._M1, k * sizeof(bitset_t)) if k > self._nrows: - self._M0 = sage_realloc(self._M0, k * sizeof(bitset_t)) - self._M1 = sage_realloc(self._M1, k * sizeof(bitset_t)) + self._M0 = sig_realloc(self._M0, k * sizeof(bitset_t)) + self._M1 = sig_realloc(self._M1, k * sizeof(bitset_t)) c = max(1, self._ncols) for i from self._nrows <= i < k: bitset_init(self._M0[i], c) @@ -2578,7 +2578,7 @@ cdef class QuaternaryMatrix(LeanMatrix): cdef bitset_t mask bitset_init(mask, lc) bitset_clear(mask) - cols = sage_malloc(lc*sizeof(mp_bitcnt_t)) + cols = sig_malloc(lc*sizeof(mp_bitcnt_t)) g = 0 for c in columns: if csage_malloc(lc*sizeof(mp_bitcnt_t)) + gaps = sig_malloc(lc*sizeof(mp_bitcnt_t)) bitset_complement(mask, mask) g = 0 c = bitset_first(mask) @@ -2621,8 +2621,8 @@ cdef class QuaternaryMatrix(LeanMatrix): for g in xrange(lg): order[gaps[g]] = cols[g] # free up the two arrays and the bitset - sage_free(gaps) - sage_free(cols) + sig_free(gaps) + sig_free(cols) bitset_free(mask) return A, order @@ -2782,7 +2782,7 @@ cdef class IntegerMatrix(LeanMatrix): cdef long i, j self._nrows = nrows self._ncols = ncols - self._entries = sage_malloc(nrows * ncols * sizeof(int)) + self._entries = sig_malloc(nrows * ncols * sizeof(int)) memset(self._entries, 0, nrows * ncols * sizeof(int)) def __init__(self, long nrows, long ncols, M=None, ring=None): @@ -2825,7 +2825,7 @@ cdef class IntegerMatrix(LeanMatrix): 2 sage: A = None """ - sage_free(self._entries) + sig_free(self._entries) def __repr__(self): """ @@ -2886,10 +2886,10 @@ cdef class IntegerMatrix(LeanMatrix): """ cdef long l = self._ncols * (self._nrows - k) if l > 0: - sage_realloc(self._entries, self._ncols * k * sizeof(int)) + sig_realloc(self._entries, self._ncols * k * sizeof(int)) memset(self._entries + self._nrows * self._ncols, 0, l * self._ncols * sizeof(int)) elif l < 0: - sage_realloc(self._entries, self._ncols * k * sizeof(int)) + sig_realloc(self._entries, self._ncols * k * sizeof(int)) self._nrows = k return 0 @@ -2991,13 +2991,13 @@ cdef class IntegerMatrix(LeanMatrix): Swap rows ``x`` and ``y``. """ cdef int* tmp - tmp = sage_malloc(self._ncols * sizeof(int)) + tmp = sig_malloc(self._ncols * sizeof(int)) if not tmp: raise MemoryError memcpy(tmp, self._entries + x * self._ncols, self._ncols * sizeof(int)) memcpy(self._entries + x * self._ncols, self._entries + y * self._ncols, self._ncols * sizeof(int)) memcpy(self._entries + y * self._ncols, tmp, self._ncols * sizeof(int)) - sage_free(tmp) + sig_free(tmp) return 0 cdef int rescale_row_c(self, long x, s, bint col_start) except -1: diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index c0319ad13f0..0cecaeb1f82 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -289,7 +289,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): sage: M = None """ if self._prow is not NULL: - sage_free(self._prow) + sig_free(self._prow) self._prow = NULL cdef list _setup_internal_representation(self, matrix, reduced_matrix, ring, keep_initial_representation): @@ -318,7 +318,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): else: self._A = (reduced_matrix).copy() # Deprecated Sage matrix operation P = range(self._A.nrows()) - self._prow = sage_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long)) + self._prow = sig_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long)) if matrix is not None: for r in xrange(len(P)): self._prow[P[r]] = r @@ -3024,7 +3024,7 @@ cdef class BinaryMatroid(LinearMatroid): BasisExchangeMatroid.__init__(self, groundset, bas) # Setup index of displayed basis - self._prow = sage_malloc((self._A.ncols()) * sizeof(long)) + self._prow = sig_malloc((self._A.ncols()) * sizeof(long)) for c in xrange(self._A.ncols()): self._prow[c] = -1 if matrix is not None: @@ -4066,7 +4066,7 @@ cdef class TernaryMatroid(LinearMatroid): BasisExchangeMatroid.__init__(self, groundset, bas) # Setup index of displayed basis - self._prow = sage_malloc((self._A.ncols()) * sizeof(long)) + self._prow = sig_malloc((self._A.ncols()) * sizeof(long)) for c in xrange(self._A.ncols()): self._prow[c] = -1 if matrix is not None: @@ -4943,7 +4943,7 @@ cdef class QuaternaryMatroid(LinearMatroid): BasisExchangeMatroid.__init__(self, groundset, bas) # Setup index of displayed basis - self._prow = sage_malloc((self._A.ncols()) * sizeof(long)) + self._prow = sig_malloc((self._A.ncols()) * sizeof(long)) for c in xrange(self._A.ncols()): self._prow[c] = -1 if matrix is not None: @@ -5647,7 +5647,7 @@ cdef class RegularMatroid(LinearMatroid): else: self._A = (reduced_matrix).copy() # Deprecated Sage matrix operation P = range(self._A.nrows()) - self._prow = sage_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long)) + self._prow = sig_malloc((self._A.nrows() + self._A.ncols()) * sizeof(long)) if matrix is not None: for r in xrange(len(P)): self._prow[P[r]] = r @@ -5967,7 +5967,7 @@ cdef class RegularMatroid(LinearMatroid): sage: M1._is_isomorphic(M2.delete('a')) True - Check that trac ticket #17316 was fixed:: + Check that :trac:`17316` was fixed:: sage: from sage.matroids.advanced import * sage: Mnew = RegularMatroid(groundset=range(12), matrix=Matrix(ZZ, diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index a2c5849528f..e33522c38f9 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -287,23 +287,23 @@ Testing. Note that the abstract base class does not support pickling:: REFERENCES ========== -.. [BC79] R. E. Bixby, W. H. Cunningham, Matroids, Graphs, and 3-Connectivity. In Graph theory and related topics (Proc. Conf., Univ. Waterloo, Waterloo, ON, 1977), 91-103 -.. [Cunningham86] W. H. Cunningham, Improved Bounds for Matroid Partition and Intersection Algorithms. SIAM Journal on Computing 1986 15:4, 948-957 -.. [CMO11] C. Chun, D. Mayhew, J. Oxley, A chain theorem for internally 4-connected binary matroids. J. Combin. Theory Ser. B 101 (2011), 141-189. -.. [CMO12] C. Chun, D. Mayhew, J. Oxley, Towards a splitter theorem for internally 4-connected binary matroids. J. Combin. Theory Ser. B 102 (2012), 688-700. -.. [Cunningham] W. H. Cunningham. Improved bounds for matroid partition and intersection algorithms. SIAM J. Comput. 15, 4 (November 1986), 948-957. +.. [BC79] \R. E. Bixby, W. H. Cunningham, Matroids, Graphs, and 3-Connectivity. In Graph theory and related topics (Proc. Conf., Univ. Waterloo, Waterloo, ON, 1977), 91-103 +.. [Cunningham86] \W. H. Cunningham, Improved Bounds for Matroid Partition and Intersection Algorithms. SIAM Journal on Computing 1986 15:4, 948-957 +.. [CMO11] \C. Chun, D. Mayhew, J. Oxley, A chain theorem for internally 4-connected binary matroids. J. Combin. Theory Ser. B 101 (2011), 141-189. +.. [CMO12] \C. Chun, D. Mayhew, J. Oxley, Towards a splitter theorem for internally 4-connected binary matroids. J. Combin. Theory Ser. B 102 (2012), 688-700. +.. [Cunningham] \W. H. Cunningham. Improved bounds for matroid partition and intersection algorithms. SIAM J. Comput. 15, 4 (November 1986), 948-957. .. [GG12] Jim Geelen and Bert Gerards, Characterizing graphic matroids by a system of linear equations, submitted, 2012. Preprint: http://www.gerardsbase.nl/papers/geelen_gerards=testing-graphicness%5B2013%5D.pdf -.. [GR01] C.Godsil and G.Royle, Algebraic Graph Theory. Graduate Texts in Mathematics, Springer, 2001. +.. [GR01] \C.Godsil and G.Royle, Algebraic Graph Theory. Graduate Texts in Mathematics, Springer, 2001. .. [Hlineny] Petr Hlineny, "Equivalence-free exhaustive generation of matroid representations", Discrete Applied Mathematics 154 (2006), pp. 1210-1222. .. [Hochstaettler] Winfried Hochstaettler, "About the Tic-Tac-Toe Matroid", preprint. -.. [Lyons] R. Lyons, Determinantal probability measures. Publications Mathematiques de l'Institut des Hautes Etudes Scientifiques 98(1) (2003), pp. 167-212. +.. [Lyons] \R. Lyons, Determinantal probability measures. Publications Mathematiques de l'Institut des Hautes Etudes Scientifiques 98(1) (2003), pp. 167-212. .. [Oxley1] James Oxley, "Matroid theory", Oxford University Press, 1992. .. [Oxley] James Oxley, "Matroid Theory, Second Edition". Oxford University Press, 2011. -.. [Pen12] R. Pendavingh, On the evaluation at `(-i, i)` of the Tutte polynomial of a binary matroid. Preprint: :arxiv:`1203.0910` -.. [PvZ] R. A. Pendavingh, S. H. M. van Zwam, Lifts of matroid +.. [Pen12] \R. Pendavingh, On the evaluation at `(-i, i)` of the Tutte polynomial of a binary matroid. Preprint: :arxiv:`1203.0910` +.. [PvZ] \R. A. Pendavingh, S. H. M. van Zwam, Lifts of matroid representations over partial fields, Journal of Combinatorial Theory, Series B, Volume 100, Issue 1, January 2010, Pages 36-67 -.. [Rajan] A. Rajan, Algorithmic applications of connectivity and related topics in matroid theory. Ph.D. Thesis, Northwestern university, 1987. +.. [Rajan] \A. Rajan, Algorithmic applications of connectivity and related topics in matroid theory. Ph.D. Thesis, Northwestern university, 1987. AUTHORS: @@ -3006,7 +3006,7 @@ cdef class Matroid(SageObject): REFERENCE: - .. [DLHK2007] J. A. De Loera, D. C. Haws, M. Köppe, Ehrhart polynomials + .. [DLHK2007] \J. A. De Loera, D. C. Haws, M. Köppe, Ehrhart polynomials of matroid polytopes and polymatroids. Discrete & Computational Geometry, Volume 42, Issue 4. :arxiv:`0710.4346`, :doi:`10.1007/s00454-008-9120-8` diff --git a/src/sage/matroids/set_system.pyx b/src/sage/matroids/set_system.pyx index 633d173d1ef..746a16bebff 100644 --- a/src/sage/matroids/set_system.pyx +++ b/src/sage/matroids/set_system.pyx @@ -26,7 +26,7 @@ Methods # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" include 'sage/data_structures/bitset.pxi' # SetSystem @@ -89,7 +89,7 @@ cdef class SetSystem: self._groundset_size = len(groundset) self._bitset_size = max(self._groundset_size, 1) self._capacity = capacity - self._subsets = sage_malloc(self._capacity * sizeof(bitset_t)) + self._subsets = sig_malloc(self._capacity * sizeof(bitset_t)) bitset_init(self._temp, self._bitset_size) self._len = 0 @@ -127,7 +127,7 @@ cdef class SetSystem: cdef long i for i in xrange(self._len): bitset_free(self._subsets[i]) - sage_free(self._subsets) + sig_free(self._subsets) bitset_free(self._temp) def __len__(self): @@ -270,7 +270,7 @@ cdef class SetSystem: bitset_free(self._subsets[i]) self._len = min(self._len, k) k2 = max(k, 1) - self._subsets = sage_realloc(self._subsets, k2 * sizeof(bitset_t)) + self._subsets = sig_realloc(self._subsets, k2 * sizeof(bitset_t)) self._capacity = k2 cdef inline _append(self, bitset_t X): @@ -728,7 +728,7 @@ cdef class SetSystem: sage: S._equivalence(lambda self, other, morph:True, T) {1: 'c', 2: 'd', 3: 'b', 4: 'a'} - Check that Trac #15189 is fixed:: + Check that :trac:`15189` is fixed:: sage: M = Matroid(ring=GF(5), reduced_matrix=[[1,0,3],[0,1,1],[1,1,0]]) sage: N = Matroid(ring=GF(5), reduced_matrix=[[1,0,1],[0,1,1],[1,1,0]]) diff --git a/src/sage/misc/all.py b/src/sage/misc/all.py index cab1bec9c2d..90e6985e3d7 100644 --- a/src/sage/misc/all.py +++ b/src/sage/misc/all.py @@ -15,8 +15,7 @@ newton_method_sizes, compose, self_compose, nest) -# Should be deprecated, but still needed in sagenb -lazy_import('sage.arith.srange', 'srange') +lazy_import('sage.arith.srange', 'srange', deprecation=20334) from banner import version, banner @@ -60,9 +59,10 @@ from pager import pager -from sagedoc import (search_src, search_def, search_doc, browse_sage_doc, - tutorial, reference, manual, developer, constructions, - python_help, help) +lazy_import('sage.misc.sagedoc', ['browse_sage_doc', + 'search_src', 'search_def', 'search_doc', + 'tutorial', 'reference', 'manual', 'developer', + 'constructions', 'python_help', 'help']) from classgraph import class_graph diff --git a/src/sage/misc/attached_files.py b/src/sage/misc/attached_files.py deleted file mode 100644 index 6c54b16c1f9..00000000000 --- a/src/sage/misc/attached_files.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -Deprecated attach module - -TESTS:: - - sage: import sage.misc.attached_files - sage: sage.misc.attached_files.load_attach_mode() - doctest:...: DeprecationWarning: - Importing load_attach_mode from here is deprecated. If you need to use it, please import it directly from sage.repl.attach - See http://trac.sagemath.org/17396 for details. - (False, True) -""" - -from sage.misc.lazy_import import lazy_import - -lazy_import('sage.repl.attach', '*', deprecation=17396) diff --git a/src/sage/misc/banner.py b/src/sage/misc/banner.py index 20c44480ad9..16d855f128a 100644 --- a/src/sage/misc/banner.py +++ b/src/sage/misc/banner.py @@ -29,14 +29,14 @@ def version(clone = False): EXAMPLES:: sage: version() - 'SageMath Version ..., Release Date: ...' + 'SageMath version ..., Release Date: ...' sage: version(clone=True) - ('SageMath Version ..., Release Date: ...', + ('SageMath version ..., Release Date: ...', 'Mercurial clone branch: ...') """ import os branch = os.popen("ls -l "+SAGE_SRC).read().split()[-1][5:] - v = 'SageMath Version %s, Release Date: %s'%(SAGE_VERSION, SAGE_DATE) + v = 'SageMath version %s, Release Date: %s'%(SAGE_VERSION, SAGE_DATE) if clone: return v,"Mercurial clone branch: %s"%branch return v @@ -58,7 +58,7 @@ def banner_text(): sage: print sage.misc.banner.banner_text() ┌────────────────────────────────────────────────────────────────────┐ - │ SageMath Version ... + │ SageMath version ... """ bars = u"─"*68 s = u'┌' + bars + u'┐' @@ -95,7 +95,7 @@ def banner(): sage: banner() ┌────────────────────────────────────────────────────────────────────┐ - │ SageMath Version ..., Release Date: ... + │ SageMath version ..., Release Date: ... │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ ... diff --git a/src/sage/misc/binary_tree.pyx b/src/sage/misc/binary_tree.pyx index 1683956c932..e98febf9c08 100644 --- a/src/sage/misc/binary_tree.pyx +++ b/src/sage/misc/binary_tree.pyx @@ -8,12 +8,12 @@ AUTHORS: - Tom Boothby (2007-02-15). Initial version free for any use (public domain). """ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from cpython.ref cimport PyObject, Py_INCREF, Py_XDECREF cdef binary_tree_node *BinaryTreeNode(int key, object value): cdef binary_tree_node *t - t = sage_malloc(sizeof(binary_tree_node)) + t = sig_malloc(sizeof(binary_tree_node)) t.key = key t.left = NULL t.right = NULL @@ -23,7 +23,7 @@ cdef binary_tree_node *BinaryTreeNode(int key, object value): cdef void free_binary_tree_node(binary_tree_node *self): Py_XDECREF(self.value) - sage_free(self) + sig_free(self) cdef inline void binary_tree_dealloc(binary_tree_node *self): if self != NULL: diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 9b40f1641af..7d44d021b21 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -331,18 +331,21 @@ For a typical category, few bases, if any, need to be added to force 100 The following can be used to search through the Sage named categories -for any that requires the addition of some bases:: +for any that requires the addition of some bases. The output may +change a bit when the category hierarchy is changed. As long as the +list below does not change radically, it's fine to just update this +doctest:: sage: from sage.categories.category import category_sample sage: sorted([C for C in category_sample() ....: if len(C._super_categories_for_classes) != len(C.super_categories())], ....: key=str) [Category of affine weyl groups, - Category of coxeter groups, Category of fields, Category of finite dimensional algebras with basis over Rational Field, Category of finite dimensional hopf algebras with basis over Rational Field, Category of finite permutation groups, + Category of finite weyl groups, Category of graded hopf algebras with basis over Rational Field] AUTHOR: diff --git a/src/sage/misc/cache.py b/src/sage/misc/cache.py index 0eec22d40dd..d0edf0ad5d7 100644 --- a/src/sage/misc/cache.py +++ b/src/sage/misc/cache.py @@ -1,5 +1,11 @@ """ A weakref cache factory + +TESTS:: + + sage: from sage.misc.cache import Cache + doctest:...: DeprecationWarning: sage.misc.cache is deprecated, use sage.misc.cachefunc instead + See http://trac.sagemath.org/20318 for details. """ #***************************************************************************** @@ -10,6 +16,9 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.misc.superseded import deprecation +deprecation(20318, "sage.misc.cache is deprecated, use sage.misc.cachefunc instead") + import weakref class Cache: diff --git a/src/sage/misc/cachefunc.pyx b/src/sage/misc/cachefunc.pyx index c32d67b55c8..c1104b869b7 100644 --- a/src/sage/misc/cachefunc.pyx +++ b/src/sage/misc/cachefunc.pyx @@ -426,7 +426,7 @@ in caches. This can be achieved by defining an appropriate method sage: hash(b) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' sage: @cached_method ....: def f(x): return x == a sage: f(b) @@ -2493,6 +2493,27 @@ cdef class CachedMethod(object): sage: a = A() sage: a.f(1, algorithm="default") is a.f(1) is a.f(1, algorithm="algorithm") True + + Cached methods can not be copied like usual methods, see :trac:`12603`. + Copying them can lead to very surprising results:: + + sage: class A: + ....: @cached_method + ....: def f(self): + ....: return 1 + sage: class B: + ....: g=A.f + ....: def f(self): + ....: return 2 + + sage: b=B() + sage: b.f() + 2 + sage: b.g() + 1 + sage: b.f() + 1 + """ def __init__(self, f, name=None, key=None): """ @@ -2574,12 +2595,12 @@ cdef class CachedMethod(object): sage: from sage.misc.superseded import deprecated_function_alias sage: class Foo(object): - ... def __init__(self, x): - ... self._x = x - ... @cached_method - ... def f(self,n=2): - ... return self._x^n - ... g = deprecated_function_alias(57, f) + ....: def __init__(self, x): + ....: self._x = x + ....: @cached_method + ....: def f(self,n=2): + ....: return self._x^n + ....: g = deprecated_function_alias(57, f) sage: a = Foo(2) sage: Foo.__dict__['f'](a) 4 diff --git a/src/sage/misc/classgraph.py b/src/sage/misc/classgraph.py index c36b501dd91..218e508ed50 100644 --- a/src/sage/misc/classgraph.py +++ b/src/sage/misc/classgraph.py @@ -36,9 +36,18 @@ def class_graph(top, depth=5, name_filter=None, classes=None, as_graph = True): sage: G = class_graph(sage.rings.polynomial.padics); G Digraph on 6 vertices sage: G.vertices() - ['Polynomial', 'Polynomial_generic_dense', 'Polynomial_generic_domain', 'Polynomial_padic', 'Polynomial_padic_capped_relative_dense', 'Polynomial_padic_flat'] + ['Polynomial', + 'Polynomial_generic_cdv', + 'Polynomial_generic_dense', + 'Polynomial_padic', + 'Polynomial_padic_capped_relative_dense', + 'Polynomial_padic_flat'] sage: G.edges(labels=False) - [('Polynomial_padic', 'Polynomial'), ('Polynomial_padic_capped_relative_dense', 'Polynomial_generic_domain'), ('Polynomial_padic_capped_relative_dense', 'Polynomial_padic'), ('Polynomial_padic_flat', 'Polynomial_generic_dense'), ('Polynomial_padic_flat', 'Polynomial_padic')] + [('Polynomial_padic', 'Polynomial'), + ('Polynomial_padic_capped_relative_dense', 'Polynomial_generic_cdv'), + ('Polynomial_padic_capped_relative_dense', 'Polynomial_padic'), + ('Polynomial_padic_flat', 'Polynomial_generic_dense'), + ('Polynomial_padic_flat', 'Polynomial_padic')] We construct the inheritance graph of a given class:: @@ -56,8 +65,9 @@ def class_graph(top, depth=5, name_filter=None, classes=None, as_graph = True): sage: class_graph(sage.rings.polynomial.padics, depth=2, as_graph=False) {'Polynomial_padic': ['Polynomial'], - 'Polynomial_padic_capped_relative_dense': ['Polynomial_generic_domain', 'Polynomial_padic'], - 'Polynomial_padic_flat': ['Polynomial_generic_dense', 'Polynomial_padic']} + 'Polynomial_padic_capped_relative_dense': ['Polynomial_generic_cdv', + 'Polynomial_padic'], + 'Polynomial_padic_flat': ['Polynomial_generic_dense', 'Polynomial_padic']} .. note:: the ``classes`` and ``as_graph`` options are mostly intended for internal recursive use. diff --git a/src/sage/misc/cython.py b/src/sage/misc/cython.py index 6649f772100..f61735f0e4b 100644 --- a/src/sage/misc/cython.py +++ b/src/sage/misc/cython.py @@ -173,7 +173,7 @@ def pyx_preparse(s): 'ntl'], ['.../include', '.../include/python...', - '.../lib/python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'], @@ -200,7 +200,7 @@ def pyx_preparse(s): ['bar', '.../include', '.../include/python...', - '.../lib/python.../site-packages/numpy/core/include', + '.../python.../numpy/core/include', '...', '.../sage/ext', '.../cysignals'] diff --git a/src/sage/misc/db.py b/src/sage/misc/db.py deleted file mode 100644 index 204979c4440..00000000000 --- a/src/sage/misc/db.py +++ /dev/null @@ -1,123 +0,0 @@ -""" -Saving Sage objects to a file (deprecated) -""" - -#***************************************************************************** -# Copyright (C) 2004 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 cPickle -import os -from sage.env import SAGE_ROOT - -PATH = os.path.join(SAGE_ROOT, "db") - -USE_DB = False - -def path(): - from sage.misc.superseded import deprecation - deprecation(17653, 'The sage.misc.db module is deprecated, use the load/save functions from sage.structure.sage_object instead') - from sage.misc.misc import sage_makedirs - sage_makedirs(PATH) - -def save(x, filename, bzip2=False, gzip=False): - """ - save(x, filename): - - Saves x to a file. Pretty much the only constraint on x is that - it have no circular references (it must be Python pickle-able). - This uses the pickle module, so data you save is *guaranteed* - to be readable by future versions of Python. - - INPUT: - x -- almost arbitrary object - filename -- a string - - OUTPUT: - Creates a file named filename, from which the object x - can be reconstructed. - """ - from sage.misc.superseded import deprecation - deprecation(17653, 'The sage.misc.db module is deprecated, use the load/save functions from sage.structure.sage_object instead') - - o=open(filename,"w") - # Note: don't use protocol 2 here (use 1), since loading doesn't work - # on my extension types. - cPickle.dump(x,o,1) - o.close() - if bzip2: - os.system("bzip2 -f %s"%filename) - if gzip: - os.system("gzip -f %s"%filename) - - -def load(filename, bzip2=False, gzip=False): - """ - load(filename): - - Loads an object from filename and returns it. - - INPUT: - filename -- a string that defines a valid file. If the - file doesn't exist then an IOError exception is raised. - - OUTPUT: - An almost arbitrary object. - """ - from sage.misc.superseded import deprecation - deprecation(17653, 'The sage.misc.db module is deprecated, use the load/save functions from sage.structure.sage_object instead') - - if bzip2: - os.system("bunzip2 -f -k %s"%(filename + ".bz2")) - if gzip: - os.system("cat %s.gz | gunzip -f > %s"%(filename,filename)) - assert os.path.exists(filename) - o = open(filename,"r") - X = cPickle.load(o) - if bzip2 or gzip: - os.remove(filename) - return X - - -def save_db(x): - """ - Save x to the database. x must define a filename method. - """ - from sage.misc.superseded import deprecation - deprecation(17653, 'The sage.misc.db module is deprecated, use the load/save functions from sage.structure.sage_object instead') - - path() - fn = PATH + x.filename() - save(x,fn) - os.system("bzip2 -f %s"%fn) - - -def load_db(x): - """ - Load x from the database. x must define a filename method. - """ - from sage.misc.superseded import deprecation - deprecation(17653, 'The sage.misc.db module is deprecated, use the load/save functions from sage.structure.sage_object instead') - - fn = PATH + x.filename() - if os.path.exists(fn + ".bz2"): - print("Loading {} from {}.".format(x, x.filename())) - os.system("bunzip2 -f -k {}".format(fn + ".bz2")) - o = open(fn, "r") - x = cPickle.load(o) - os.remove(fn) - return x - else: - return None diff --git a/src/sage/misc/dist.py b/src/sage/misc/dist.py index c15c94443f8..bc76eb0325f 100644 --- a/src/sage/misc/dist.py +++ b/src/sage/misc/dist.py @@ -67,7 +67,7 @@ def install_scripts(directory=None, ignore_existing=False): - Arthur Gaer: design - - John Palmieri: revision, 2011-07 (trac ticket #11602) + - John Palmieri: revision, 2011-07 (:trac:`11602`) EXAMPLES:: diff --git a/src/sage/misc/edit_module.py b/src/sage/misc/edit_module.py index 65d9e4341a1..3a251028b0e 100644 --- a/src/sage/misc/edit_module.py +++ b/src/sage/misc/edit_module.py @@ -85,7 +85,7 @@ def file_and_line(obj): sage: edit_module.file_and_line(sage) ('...sage/__init__.py', 0) - The following tests against a bug that was fixed in trac ticket #11298:: + The following tests against a bug that was fixed in :trac:`11298`:: sage: edit_module.file_and_line(x) ('...sage/symbolic/expression.pyx', ...) diff --git a/src/sage/misc/functional.py b/src/sage/misc/functional.py index dbcd382f6b1..61d15058821 100644 --- a/src/sage/misc/functional.py +++ b/src/sage/misc/functional.py @@ -231,7 +231,7 @@ def decomposition(x): [Dirichlet character modulo 4 of conductor 4 mapping 3 |--> -1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> zeta4] sage: d[0].parent() - Group of Dirichlet characters of modulus 4 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 4 with values in Cyclotomic Field of order 4 and degree 2 """ return x.decomposition() diff --git a/src/sage/misc/lazy_import_cache.py b/src/sage/misc/lazy_import_cache.py index a69a9102a08..7688a9565e5 100644 --- a/src/sage/misc/lazy_import_cache.py +++ b/src/sage/misc/lazy_import_cache.py @@ -19,7 +19,7 @@ def get_cache_file(): '...-lazy_import_cache.pickle' sage: get_cache_file().startswith(DOT_SAGE) True - sage: 'src' in get_cache_file() + sage: 'cache' in get_cache_file() True It shouldn't matter whether DOT_SAGE ends with a slash:: diff --git a/src/sage/misc/misc.py b/src/sage/misc/misc.py index 48a5053b961..7d30b1d0147 100644 --- a/src/sage/misc/misc.py +++ b/src/sage/misc/misc.py @@ -34,6 +34,11 @@ Importing srange from here is deprecated. If you need to use it, please import it directly from sage.arith.srange See http://trac.sagemath.org/20094 for details. [0, 1, 2, 3, 4] + sage: sage.misc.all.srange(5) + doctest:...: DeprecationWarning: + Importing srange from here is deprecated. If you need to use it, please import it directly from sage.arith.srange + See http://trac.sagemath.org/20334 for details. + [0, 1, 2, 3, 4] sage: sage.misc.misc.sxrange(5) doctest:...: DeprecationWarning: Importing sxrange from here is deprecated. If you need to use it, please import it directly from sage.arith.srange @@ -1664,7 +1669,7 @@ def __hash__(self): Note: a missing ``__hash__`` method here used to break the unique representation of parents taking ``attrcall`` objects - as input; see #8911. + as input; see :trac:`8911`. """ return hash((self.args, tuple(self.kwds.items()))) diff --git a/src/sage/misc/preparser.py b/src/sage/misc/preparser.py deleted file mode 100644 index 07b7d253089..00000000000 --- a/src/sage/misc/preparser.py +++ /dev/null @@ -1,17 +0,0 @@ -""" -Deprecated preparser module - -TESTS:: - - sage: import sage.misc.preparser - sage: sage.misc.preparser.is_loadable_filename('foo.sage') - doctest:...: DeprecationWarning: - Importing is_loadable_filename from here is deprecated. If you need to use it, please import it directly from sage.repl.load - See http://trac.sagemath.org/17396 for details. - True -""" - -from sage.misc.lazy_import import lazy_import - -lazy_import('sage.repl.preparse', '*', deprecation=17396) -lazy_import('sage.repl.load', '*', deprecation=17396) diff --git a/src/sage/misc/reset.pyx b/src/sage/misc/reset.pyx index b3fd4109602..bd6bc7863fc 100644 --- a/src/sage/misc/reset.pyx +++ b/src/sage/misc/reset.pyx @@ -49,7 +49,7 @@ def reset(vars=None, attached=False): TESTS: - Confirm that assumptions don't survive a reset (trac #10855):: + Confirm that assumptions don't survive a reset (:trac:`10855`):: sage: assume(x > 3) sage: assumptions() diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 2b268b5d574..5d52bdee308 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -23,7 +23,12 @@ sage: for line in open(docfilename): ....: if "#sage.symbolic.expression.Expression.N" in line: ....: print line - N(prec=None, digits=None, algorithm=None)... + N(prec=None, digits=None, algorithm=None)... + +Check that sphinx is not imported at Sage start-up:: + + sage: "sphinx" in sys.modules + False """ #***************************************************************************** # Copyright (C) 2005 William Stein @@ -37,8 +42,9 @@ from __future__ import print_function import os, re, sys import pydoc -from sage.misc.viewer import browser from sage.misc.temporary_file import tmp_dir +from .viewer import browser +from .sphinxify import sphinxify import sage.version from sage.env import SAGE_DOC_SRC, SAGE_DOC, SAGE_SRC @@ -215,7 +221,6 @@ def detex(s, embedded=False): if not embedded: # not in the notebook s = _rmcmd(s, 'mathop') s = _rmcmd(s, 'mathrm') - from sagenb.misc.sphinxify import sphinxify s = sphinxify(s, format='text') # Do math substitutions. The strings to be replaced should be # TeX commands like "\\blah". Do a regular expression @@ -533,10 +538,10 @@ def format(s, embedded=False): EXAMPLES:: sage: from sage.misc.sagedoc import format - sage: identity_matrix(2).rook_vector.__doc__[201:273] + sage: identity_matrix(2).rook_vector.__doc__[202:274] 'Let `A` be an `m` by `n` (0,1)-matrix. We identify `A` with a chessboard' - sage: format(identity_matrix(2).rook_vector.__doc__[201:273]) + sage: format(identity_matrix(2).rook_vector.__doc__[202:274]) 'Let A be an m by n (0,1)-matrix. We identify A with a chessboard\n' If the first line of the string is 'nodetex', remove 'nodetex' but @@ -1369,7 +1374,6 @@ def __call__(self, obj, output='html', view=True): # now s should be the reST version of the docstring if output == 'html': - from sagenb.misc.sphinxify import sphinxify html = sphinxify(s) if view: path = os.path.join(tmp_dir(), "temp.html") @@ -1438,7 +1442,6 @@ def __call__(self, obj, output='html', view=True): elif output == 'rst': return s elif output == 'text': - from sagenb.misc.sphinxify import sphinxify return sphinxify(s, format='text') else: raise ValueError("output type {} not recognized".format(output)) diff --git a/src/sage/misc/sphinxify.py b/src/sage/misc/sphinxify.py new file mode 100644 index 00000000000..c4a6e243c2d --- /dev/null +++ b/src/sage/misc/sphinxify.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -* +r""" +Process docstrings with Sphinx + +Processes docstrings with Sphinx. Can also be used as a commandline script: + +``python sphinxify.py `` + +AUTHORS: + +- Tim Joseph Dumol (2009-09-29): initial version +""" + +#***************************************************************************** +# Copyright (C) 2009 Tim Dumol +# +# Distributed under the terms of the BSD License +#***************************************************************************** + +import os +import re +import shutil +from tempfile import mkdtemp +from sage.env import SAGE_DOC_SRC +from sphinx.application import Sphinx + + +def sphinxify(docstring, format='html'): + r""" + Runs Sphinx on a ``docstring``, and outputs the processed + documentation. + + INPUT: + + - ``docstring`` -- string -- a ReST-formatted docstring + + - ``format`` -- string (optional, default 'html') -- either 'html' or + 'text' + + OUTPUT: + + - string -- Sphinx-processed documentation, in either HTML or + plain text format, depending on the value of ``format`` + + EXAMPLES:: + + sage: from sage.misc.sphinxify import sphinxify + sage: sphinxify('A test') + '...
    \n \n

    A test

    \n\n\n
    ' + sage: sphinxify('**Testing**\n`monospace`') + '...
    Testing\n\n\n\n
    ' + sage: sphinxify('`x=y`') + '...
    \n \n

    x=y

    \n\n\n
    ' + sage: sphinxify('`x=y`', format='text') + 'x=y\n' + sage: sphinxify(':math:`x=y`', format='text') + 'x=y\n' + + TESTS:: + + sage: n = len(sys.path) + sage: _ = sphinxify('A test') + sage: assert n == len(sys.path) + """ + srcdir = mkdtemp() + base_name = os.path.join(srcdir, 'docstring') + rst_name = base_name + '.rst' + + if format == 'html': + suffix = '.html' + else: + suffix = '.txt' + output_name = base_name + suffix + + with open(rst_name, 'w') as filed: + filed.write(docstring) + + # Sphinx constructor: Sphinx(srcdir, confdir, outdir, doctreedir, + # buildername, confoverrides, status, warning, freshenv). + confdir = os.path.join(SAGE_DOC_SRC, 'en', 'introspect') + + doctreedir = os.path.join(srcdir, 'doctrees') + confoverrides = {'html_context': {}, 'master_doc': 'docstring'} + + import sys + old_sys_path = list(sys.path) # Sphinx modifies sys.path + sphinx_app = Sphinx(srcdir, confdir, srcdir, doctreedir, format, + confoverrides, None, None, True) + sphinx_app.build(None, [rst_name]) + sys.path = old_sys_path + + # We need to remove "_" from __builtin__ that the gettext module installs + import __builtin__ + __builtin__.__dict__.pop('_', None) + + if os.path.exists(output_name): + output = open(output_name, 'r').read() + output = output.replace('
    ', '
    ')
    +
    +        # Translate URLs for media from something like
    +        #    "../../media/...path.../blah.png"
    +        # or
    +        #    "/media/...path.../blah.png"
    +        # to
    +        #    "/doc/static/reference/media/...path.../blah.png"
    +        output = re.sub("""src=['"](/?\.\.)*/?media/([^"']*)['"]""",
    +                          'src="/doc/static/reference/media/\\2"',
    +                          output)
    +        # Remove spurious \(, \), \[, \].
    +        output = output.replace('\\(', '').replace('\\)', '').replace('\\[', '').replace('\\]', '')
    +    else:
    +        print "BUG -- Sphinx error"
    +        if format == 'html':
    +            output = '
    %s
    ' % docstring + else: + output = docstring + + shutil.rmtree(srcdir, ignore_errors=True) + + return output + + +if __name__ == '__main__': + import sys + if len(sys.argv) == 2: + print sphinxify(sys.argv[1]) + else: + print """Usage: +%s 'docstring' + +docstring -- docstring to be processed +""" diff --git a/src/sage/modular/abvar/abvar.py b/src/sage/modular/abvar/abvar.py index 049c472de6b..46d5f4039f0 100644 --- a/src/sage/modular/abvar/abvar.py +++ b/src/sage/modular/abvar/abvar.py @@ -837,7 +837,7 @@ def __add__(self, other): TESTS: - This exposed a bug in HNF (see trac #4527):: + This exposed a bug in HNF (see :trac:`4527`):: sage: A = J0(206).new_subvariety().decomposition()[3] ; A # long time Simple abelian subvariety 206d(1,206) of dimension 4 of J0(206) diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 30d8644acb7..1d884da7366 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -750,7 +750,7 @@ def __init__(self, A, gens=None, category=None): TESTS: The following tests against a problem on 32 bit machines that - occured while working on trac ticket #9944:: + occured while working on :trac:`9944`:: sage: sage.modular.abvar.homspace.EndomorphismSubring(J1(12345)) Endomorphism ring of Abelian variety J1(12345) of dimension 5405473 diff --git a/src/sage/modular/arithgroup/arithgroup_element.pyx b/src/sage/modular/arithgroup/arithgroup_element.pyx index 46021d08082..167c100dc63 100644 --- a/src/sage/modular/arithgroup/arithgroup_element.pyx +++ b/src/sage/modular/arithgroup/arithgroup_element.pyx @@ -426,3 +426,45 @@ cdef class ArithmeticSubgroupElement(MultiplicativeGroupElement): """ from all import SL2Z return SL2Z, (self.__x,) + + def multiplicative_order(self): + r""" + Return the multiplicative order of this element. + + EXAMPLES:: + + sage: SL2Z.one().multiplicative_order() + 1 + sage: SL2Z([-1,0,0,-1]).multiplicative_order() + 2 + sage: s,t = SL2Z.gens() + sage: ((t^3*s*t^2) * s * ~(t^3*s*t^2)).multiplicative_order() + 4 + sage: (t^3 * s * t * t^-3).multiplicative_order() + 6 + sage: (t^3 * s * t * s * t^-2).multiplicative_order() + 3 + sage: SL2Z([2,1,1,1]).multiplicative_order() + +Infinity + sage: SL2Z([-2,1,1,-1]).multiplicative_order() + +Infinity + """ + m = self.matrix() + + if m.is_one(): + return ZZ(1) + elif (-m).is_one(): + return ZZ(2) + + t = m.trace() + if t <= -2 or t >= 2: + from sage.rings.infinity import infinity + return infinity + elif t == 0: + return ZZ(4) + elif t == 1: + return ZZ(6) + elif t == -1: + return ZZ(3) + + raise RuntimeError diff --git a/src/sage/modular/arithgroup/arithgroup_perm.py b/src/sage/modular/arithgroup/arithgroup_perm.py index efa7a120239..64da0a5408d 100644 --- a/src/sage/modular/arithgroup/arithgroup_perm.py +++ b/src/sage/modular/arithgroup/arithgroup_perm.py @@ -41,7 +41,7 @@ REFERENCES: -.. [AtSD71] A. O. L. Atkin and H. P. F. Swinnerton-Dyer, "Modular forms on +.. [AtSD71] \A. O. L. Atkin and H. P. F. Swinnerton-Dyer, "Modular forms on noncongruence subgroups", Proc. Symp. Pure Math., Combinatorics (T. S. Motzkin, ed.), vol. 19, AMS, Providence 1971 @@ -1531,7 +1531,7 @@ def surgroups(self): EXAMPLES:: sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)(5,6)", S3="(1,2,3)(4,5,6)") - sage: H = G.surgroups().next() + sage: H = next(G.surgroups()) sage: H Arithmetic subgroup with permutations of right cosets S2=(1,2) diff --git a/src/sage/modular/arithgroup/congroup.pyx b/src/sage/modular/arithgroup/congroup.pyx index 7c6223b479a..0a98d224821 100644 --- a/src/sage/modular/arithgroup/congroup.pyx +++ b/src/sage/modular/arithgroup/congroup.pyx @@ -18,7 +18,7 @@ functions are for internal use by routines elsewhere in the Sage library. # ################################################################################ -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" import random from congroup_gamma1 import Gamma1_constructor as Gamma1 @@ -121,7 +121,7 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): n = Gamma0(N).index() / Gamma0(M).index() k = 0 # number found so far Ndivt = N / t - R = sage_malloc(sizeof(int) * (4*n)) + R = sig_malloc(sizeof(int) * (4*n)) if R == 0: raise MemoryError halfmax = 2*(n+10) @@ -155,7 +155,7 @@ def degeneracy_coset_representatives_gamma0(int N, int M, int t): for i from 0 <= i < k: j = 4*i S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t]) - sage_free(R) + sig_free(R) return S def degeneracy_coset_representatives_gamma1(int N, int M, int t): @@ -225,7 +225,7 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): n = n / d k = 0 # number found so far Ndivt = N / t - R = sage_malloc(sizeof(int) * (4*n)) + R = sig_malloc(sizeof(int) * (4*n)) if R == 0: raise MemoryError halfmax = 2*(n+10) @@ -252,7 +252,7 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): # If our matrix is new add it to the list. if is_new: if k > n: - sage_free(R) + sig_free(R) raise RuntimeError, "bug!!" R[4*k] = aa R[4*k+1] = bb @@ -265,7 +265,7 @@ def degeneracy_coset_representatives_gamma1(int N, int M, int t): for i from 0 <= i < k: j = 4*i S.append([R[j], R[j+1], R[j+2]*t, R[j+3]*t]) - sage_free(R) + sig_free(R) return S def generators_helper(coset_reps, level): diff --git a/src/sage/modular/arithgroup/congroup_pyx.py b/src/sage/modular/arithgroup/congroup_pyx.py deleted file mode 100644 index 5602e97015c..00000000000 --- a/src/sage/modular/arithgroup/congroup_pyx.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Deprecated congruence subgroups module - -TESTS:: - - sage: from sage.modular.arithgroup.congroup_pyx import degeneracy_coset_representatives_gamma0 - sage: len(degeneracy_coset_representatives_gamma0(13, 1, 1)) - doctest:...: DeprecationWarning: - Importing degeneracy_coset_representatives_gamma0 from here is deprecated. If you need to use it, please import it directly from sage.modular.arithgroup.congroup - See http://trac.sagemath.org/17824 for details. - 14 -""" - -from sage.misc.lazy_import import lazy_import -from sage.misc.superseded import deprecation -lazy_import('sage.modular.arithgroup.congroup', - ('degeneracy_coset_representatives_gamma0', - 'degeneracy_coset_representatives_gamma1'), - deprecation=17824) - -def generators_helper(coset_reps, level, Mat2Z): - deprecation(17824, "The congroup_pyx module is deprecated, use sage.modular.arithgroup.congroup.generators_helper instead and remove the last Mat2Z argument") - from sage.modular.arithgroup.congroup import generators_helper - return generators_helper(coset_reps, level) diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index 55ab1ed6d44..9d95fb1f3d4 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -225,7 +225,7 @@ cdef class Farey: sage: F = Gamma0(40).farey_symbol() sage: table = F.pairing_matrices_to_tietze_index() sage: table[12] - -2 + (-2, -1) sage: F.pairing_matrices()[12] [ 3 -1] [ 40 -13] @@ -233,63 +233,72 @@ cdef class Farey: [ -3 1] [-40 13] """ - gens_dict = self._get_dict_of_generators() + gens_dict = {g:i+1 for i,g in enumerate(self.generators())} ans = [] for pm in self.pairing_matrices(): a, b, c, d = pm.matrix().list() newval = gens_dict.get(SL2Z([a, b, c, d])) if newval is not None: - ans.append(newval) + ans.append((newval,1)) continue newval = gens_dict.get(SL2Z([-a, -b, -c, -d])) if newval is not None: - ans.append(newval) + ans.append((newval,-1)) continue newval = gens_dict.get(SL2Z([d, -b, -c, a])) if newval is not None: - ans.append(-newval) + ans.append((-newval,1)) continue newval = gens_dict.get(SL2Z([-d, b, c, -a])) if newval is not None: - ans.append(-newval) + ans.append((-newval,-1)) continue raise RuntimeError("This should have not happened") return ans @cached_method - def _get_dict_of_generators(self): + def _get_minus_one(self): r""" - Obtain a dict indexed by the generators. + If -I belongs to self, return a Tietze word representing it. OUTPUT: - A dict of key-value pairs (g,i+1) and (-g,i+1) (the latter one -I belongs to self), - where the i-th generator is g. + A Tietze word representing the element -I if it belongs to self. + Otherwise return [] EXAMPLES:: - sage: G = Gamma1(30) - sage: F = G.farey_symbol() - sage: dict_gens = F._get_dict_of_generators() - sage: g = F.generators()[5] - sage: dict_gens[g] # Note that the answer is 1-based. - 6 - - The dict also contains -g if -I belongs to the arithmetic group:: - - sage: G = Gamma0(20) - sage: F = G.farey_symbol() - sage: dict_gens = F._get_dict_of_generators() - sage: E = G([-1,0,0,-1]) - sage: h = F.generators()[7] * E - sage: dict_gens[h] - 8 + sage: Gamma1(30).farey_symbol()._get_minus_one() + [] + sage: G = Gamma0(30).farey_symbol() + sage: G._get_minus_one() + [14] + sage: g = G.generators()[13] + sage: (-g.matrix()).is_one() + True + sage: G = Gamma(1).farey_symbol() + sage: G._get_minus_one() + [1, 1] + sage: g = G.generators()[0]**2 + sage: (-g.matrix()).is_one() + True + sage: G = Gamma0(3).farey_symbol() + sage: G._get_minus_one() + [2, 2, 2] + sage: g = G.generators()[1]**3 + sage: (-g.matrix()).is_one() + True """ - ans = {g:i+1 for i,g in enumerate(self.generators())} - E = SL2Z([-1,0,0,-1]) - if E in self.group: - ans.update({(g * E):i+1 for i,g in enumerate(self.generators())}) - return ans + for i,g in enumerate(self.generators()): + m = g.matrix() + if (-m).is_one(): + return [i+1] + t = m.trace() + if t == 0: # order = 4 + return 2 * [i+1] + elif t == 1: # order = 6 + return 3 * [i+1] + return [] def word_problem(self, M, output = 'standard', check = True): r""" @@ -362,7 +371,7 @@ cdef class Farey: sage: F = G.farey_symbol() sage: g = G([-701,-137,4600,899]) sage: g1 = prod(F.generators()[i]**a for i,a in F.word_problem(g, output = 'syllables')) - sage: g == g1 or g * G([-1,0,0,-1]) == g1 + sage: g == g1 True Check that it works for GammaH as well (:trac:`19660`):: @@ -371,11 +380,24 @@ cdef class Farey: sage: G.farey_symbol().word_problem(G([1,1,0,1])) (1,) + Check that :trac:`20347` is solved:: + + sage: from sage.misc.misc_c import prod + sage: G = ArithmeticSubgroup_Permutation(S2="(1,2)(3,4)",S3="(1,2,3)") + sage: S = G.farey_symbol() + sage: g1,g2 = S.generators() + sage: g = g1^3 * g2^-2 * g1 * g2 + sage: S.word_problem(g) + (2, 2, 2, 1, 1, 1, 2, 1, 2) + sage: h = prod(S.generators()[i]**a for i,a in S.word_problem(g, output = 'syllables')) + sage: g == h + True """ if output not in ['standard', 'syllables', 'gens']: raise ValueError('Unrecognized output format') if check: - assert M in self.group,"Matrix ( %s ) is not in group ( %s )"%(M,self.group) + if M not in self.group: + raise ValueError("Matrix ( %s ) is not in group ( %s )"%(M,self.group)) cdef Integer a = M.d() cdef Integer b = -M.b() cdef Integer c = -M.c() @@ -385,18 +407,51 @@ cdef class Farey: sig_on() result = self.this_ptr.word_problem(a.value, b.value, c.value, d.value, cpp_beta) sig_off() - beta = convert_to_SL2Z(cpp_beta[0]) + beta = convert_to_SL2Z(cpp_beta[0])**-1 + mbeta = SL2Z([-beta.a(),-beta.b(),-beta.c(),-beta.d()]) V = self.pairing_matrices_to_tietze_index() - tietze = [V[o-1] if o > 0 else -V[-o-1] for o in result] - if not beta.is_one() and beta != E: # We need to correct for beta - gens_dict = self._get_dict_of_generators() + sgn = 1 + tietze = [] + for o in result: + if o > 0: + tietze.append(V[o-1][0]) + sgn *= V[o-1][1] + else: + tietze.append(-V[-o-1][0]) + sgn *= V[-o-1][1] + if sgn == -1: + beta, mbeta = mbeta, beta + + gens_dict = {g:i+1 for i,g in enumerate(self.generators())} + extra_tietze = [] + if beta.is_one(): + found = True + elif mbeta.is_one(): + found = True + extra_tietze = self._get_minus_one() + else: + found = False + if not found: newval = gens_dict.get(beta) if newval is not None: - newval = -newval - else: - newval = gens_dict.get(beta**-1) - assert newval is not None - tietze.append(newval) + found = True + extra_tietze = [newval] + if not found: + newval = gens_dict.get(beta**-1) + if newval is not None: + found = True + extra_tietze = [-newval] + if not found: + newval = gens_dict.get(mbeta) + if newval is not None: + found = True + extra_tietze = [newval] + self._get_minus_one() + if not found: + newval = gens_dict.get(mbeta**-1) + if newval is not None: + found = True + extra_tietze = [-newval] + self._get_minus_one() + tietze.extend(extra_tietze) tietze.reverse() gens = self.generators() if check: @@ -611,7 +666,7 @@ cdef class Farey: def generators(self): r""" - Minmal set of generators of the group of the FareySymbol. + Minimal set of generators of the group of the FareySymbol. EXAMPLES: diff --git a/src/sage/modular/cusps.py b/src/sage/modular/cusps.py index 348ed351f19..71b86160f1f 100644 --- a/src/sage/modular/cusps.py +++ b/src/sage/modular/cusps.py @@ -124,7 +124,7 @@ def __call__(self, x): sage: Cusps(I) Traceback (most recent call last): ... - TypeError: Unable to convert I to a Cusp + TypeError: unable to convert I to a cusp """ return Cusp(x, parent=self) @@ -230,7 +230,7 @@ def __init__(self, a, b=None, parent=None, check=True): sage: Cusp(I) Traceback (most recent call last): ... - TypeError: Unable to convert I to a Cusp + TypeError: unable to convert I to a cusp :: @@ -265,21 +265,21 @@ def __init__(self, a, b=None, parent=None, check=True): sage: Cusp(0,0) Traceback (most recent call last): ... - TypeError: Unable to convert (0, 0) to a Cusp + TypeError: unable to convert (0, 0) to a cusp :: sage: Cusp(oo,oo) Traceback (most recent call last): ... - TypeError: Unable to convert (+Infinity, +Infinity) to a Cusp + TypeError: unable to convert (+Infinity, +Infinity) to a cusp :: sage: Cusp(Cusp(oo),oo) Traceback (most recent call last): ... - TypeError: Unable to convert (Infinity, +Infinity) to a Cusp + TypeError: unable to convert (Infinity, +Infinity) to a cusp """ if parent is None: parent = Cusps @@ -307,7 +307,7 @@ def __init__(self, a, b=None, parent=None, check=True): self.__b = ZZ(1) elif isinstance(a, (tuple, list)): if len(a) != 2: - raise TypeError("Unable to convert %s to a Cusp"%a) + raise TypeError("unable to convert %r to a cusp"%a) if ZZ(a[1]) == 0: self.__a = ZZ(1) self.__b = ZZ(0) @@ -317,25 +317,25 @@ def __init__(self, a, b=None, parent=None, check=True): self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): - raise TypeError("Unable to convert %s to a Cusp"%a) + raise TypeError("unable to convert %r to a cusp"%a) else: try: r = QQ(a) self.__a = r.numer() self.__b = r.denom() except (ValueError, TypeError): - raise TypeError("Unable to convert %s to a Cusp"%a) + raise TypeError("unable to convert %r to a cusp"%a) return if is_InfinityElement(b): if is_InfinityElement(a) or (isinstance(a, Cusp) and a.is_infinity()): - raise TypeError("Unable to convert (%s, %s) to a Cusp"%(a, b)) + raise TypeError("unable to convert (%r, %r) to a cusp"%(a, b)) self.__a = ZZ(0) self.__b = ZZ(1) return elif not b: if not a: - raise TypeError("Unable to convert (%s, %s) to a Cusp"%(a, b)) + raise TypeError("unable to convert (%r, %r) to a cusp"%(a, b)) self.__a = ZZ(1) self.__b = ZZ(0) return @@ -357,13 +357,13 @@ def __init__(self, a, b=None, parent=None, check=True): r = ZZ(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: - raise TypeError("Unable to convert (%s, %s) to a Cusp"%(a, b)) + raise TypeError("unable to convert (%r, %r) to a cusp"%(a, b)) r = ZZ(a[0]) / (ZZ(a[1]) * b) else: try: r = QQ(a) / b except (ValueError, TypeError): - raise TypeError("Unable to convert (%s, %s) to a Cusp"%(a, b)) + raise TypeError("unable to convert (%r, %r) to a cusp"%(a, b)) self.__a = r.numer() self.__b = r.denom() diff --git a/src/sage/modular/cusps_nf.py b/src/sage/modular/cusps_nf.py index 5b9c6a35343..ea7befba4c0 100644 --- a/src/sage/modular/cusps_nf.py +++ b/src/sage/modular/cusps_nf.py @@ -460,21 +460,21 @@ class NFCusp(Element): sage: NFCusp(k, I) Traceback (most recent call last): ... - TypeError: Unable to convert I to a cusp of the number field + TypeError: unable to convert I to a cusp of the number field :: sage: NFCusp(k, oo, oo) Traceback (most recent call last): ... - TypeError: Unable to convert (+Infinity, +Infinity) to a cusp of the number field + TypeError: unable to convert (+Infinity, +Infinity) to a cusp of the number field :: sage: NFCusp(k, 0, 0) Traceback (most recent call last): ... - TypeError: Unable to convert (0, 0) to a cusp of the number field + TypeError: unable to convert (0, 0) to a cusp of the number field :: @@ -539,7 +539,7 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): self.__b = R(1) elif isinstance(a, (tuple, list)): if len(a) != 2: - raise TypeError("Unable to convert %s to a cusp \ + raise TypeError("unable to convert %r to a cusp \ of the number field"%a) if a[1].is_zero(): self.__a = R(1) @@ -561,7 +561,7 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): self.__b = R(r.denominator()) self.__a = R(r * self.__b) except (ValueError, TypeError): - raise TypeError("Unable to convert %s to a cusp \ + raise TypeError("unable to convert %r to a cusp \ of the number field"%a) else: try: @@ -569,19 +569,19 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): self.__b = R(r.denominator()) self.__a = R(r * self.__b) except (ValueError, TypeError): - raise TypeError("Unable to convert %s to a cusp \ + raise TypeError("unable to convert %r to a cusp \ of the number field"%a) else:#'b' is given if is_InfinityElement(b): if is_InfinityElement(a) or (isinstance(a, NFCusp) and a.is_infinity()): - raise TypeError("Unable to convert (%s, %s) \ + raise TypeError("unable to convert (%r, %r) \ to a cusp of the number field"%(a, b)) self.__a = R(0) self.__b = R(1) return elif not b: if not a: - raise TypeError("Unable to convert (%s, %s) \ + raise TypeError("unable to convert (%r, %r) \ to a cusp of the number field"%(a, b)) self.__a = R(1) self.__b = R(0) @@ -610,14 +610,14 @@ def __init__(self, number_field, a, b=None, parent=None, lreps=None): r = R(a) / b elif isinstance(a, (tuple, list)): if len(a) != 2: - raise TypeError("Unable to convert (%s, %s) \ + raise TypeError("unable to convert (%r, %r) \ to a cusp of the number field"%(a, b)) r = R(a[0]) / (R(a[1]) * b) else: try: r = number_field(a) / b except (ValueError, TypeError): - raise TypeError("Unable to convert (%s, %s) \ + raise TypeError("unable to convert (%r, %r) \ to a cusp of the number field"%(a, b)) self.__b = R(r.denominator()) self.__a = R(r * self.__b) @@ -1204,12 +1204,19 @@ def number_of_Gamma0_NFCusps(N): sage: L = Gamma0_NFCusps(N) sage: len(L) == number_of_Gamma0_NFCusps(N) True + sage: k.
    = NumberField(x^2 + 7) + sage: N = k.ideal(9) + sage: number_of_Gamma0_NFCusps(N) + 6 + sage: N = k.ideal(a*9 + 7) + sage: number_of_Gamma0_NFCusps(N) + 24 """ k = N.number_field() # The number of Gamma0(N)-sub-orbits for each Gamma-orbit: from sage.arith.all import divisors - s = sum([len(list((d+N/d).invertible_residues_mod(k.unit_group().gens()))) \ - for d in divisors(N)]) + Ugens = [k(u) for u in k.unit_group().gens()] + s = sum([len((d+N/d).invertible_residues_mod(Ugens)) for d in divisors(N)]) # There are h Gamma-orbits, with h class number of underlying number field. return s*k.class_number() diff --git a/src/sage/modular/dirichlet.py b/src/sage/modular/dirichlet.py index 44f14662374..3e220f6258e 100644 --- a/src/sage/modular/dirichlet.py +++ b/src/sage/modular/dirichlet.py @@ -169,7 +169,7 @@ def is_DirichletCharacter(x): class DirichletCharacter(MultiplicativeGroupElement): """ - A Dirichlet character + A Dirichlet character. """ def __init__(self, parent, x, check=True): r""" @@ -203,7 +203,7 @@ def __init__(self, parent, x, check=True): sage: G. = DirichletGroup(13) sage: G - Group of Dirichlet characters of modulus 13 over Cyclotomic Field of order 12 and degree 4 + Group of Dirichlet characters modulo 13 with values in Cyclotomic Field of order 12 and degree 4 sage: e Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12 sage: loads(e.dumps()) == e @@ -242,7 +242,6 @@ def __init__(self, parent, x, check=True): ValueError: values (= (4, 8, 8) modulo 16) must have additive orders dividing (2, 16, 2), respectively """ MultiplicativeGroupElement.__init__(self, parent) - self.__modulus = parent.modulus() if check: orders = parent.integers_mod().unit_group().gens_orders() if len(x) != len(orders): @@ -252,7 +251,7 @@ def __init__(self, parent, x, check=True): if any(map(lambda u, v: v*u != 0, x, orders)): raise ValueError("values (= {} modulo {}) must have additive orders dividing {}, respectively" .format(x, parent.zeta_order(), orders)) - self.__element = x + self.element.set_cache(x) else: R = parent.base_ring() x = tuple(map(R, x)) @@ -262,11 +261,10 @@ def __init__(self, parent, x, check=True): self.__values_on_gens = x else: if free_module_element.is_FreeModuleElement(x): - self.__element = x + self.element.set_cache(x) else: self.__values_on_gens = x - @cached_method def __eval_at_minus_one(self): r""" @@ -330,8 +328,9 @@ def __call__(self, m): sage: parent(e(31*37)) Cyclotomic Field of order 4 and degree 2 """ - m = int(m % self.__modulus) - if self.values.is_in_cache() or m != self.__modulus - 1: + N = self.modulus() + m = m % N + if self.values.is_in_cache() or m != N - 1: return self.values()[m] else: return self.__eval_at_minus_one() @@ -351,7 +350,7 @@ def change_ring(self, R): sage: e = DirichletGroup(7, QQ).0 sage: f = e.change_ring(QuadraticField(3, 'a')) sage: f.parent() - Group of Dirichlet characters of modulus 7 over Number Field in a with defining polynomial x^2 - 3 + Group of Dirichlet characters modulo 7 with values in Number Field in a with defining polynomial x^2 - 3 :: @@ -370,22 +369,24 @@ def change_ring(self, R): sage: f = K.complex_embeddings()[0] sage: psi = chi.change_ring(f) sage: psi(2) - -1.00000000000000*I + -1.83697019872103e-16 - 1.00000000000000*I """ if self.base_ring() is R: return self G = self.parent().change_ring(R) - if isinstance(R, Map): - return G.element_class(G, self.element()) - return G(self) + return G.element_class(G, [R(x) for x in self.values_on_gens()]) - def __cmp__(self, other): + def _cmp_(self, other): """ - Compare self to other. Note that this only gets called when the parents - of self and other are identical, via a canonical coercion map; this - means that characters of different moduli compare as unequal, even if - they define identical functions on ZZ. + Compare ``self`` to ``other``. + + .. NOTE:: + + Since there is no coercion between Dirichlet groups + of different moduli, characters of different moduli + compare as unequal, even if they define identical + functions on ``ZZ``. EXAMPLES:: @@ -400,23 +401,26 @@ def __cmp__(self, other): sage: k = DirichletGroup(7)([-1]) sage: k == e False + """ - return cmp(self.element(), other.element()) + return cmp(self.values_on_gens(), other.values_on_gens()) def __hash__(self): """ + Return the hash of ``self``. + EXAMPLES:: sage: e = DirichletGroup(16)([-1, 1]) sage: hash(e) - 1498523633 # 32-bit - 3713082714464823281 # 64-bit + -1497246046 # 32-bit + -3713082714463545694 # 64-bit """ - return self.element()._hash() + return hash(self.values_on_gens()) def __invert__(self): """ - Return the multiplicative inverse of self. The notation is self. + Return the multiplicative inverse of self. EXAMPLES:: @@ -426,7 +430,11 @@ def __invert__(self): Dirichlet character modulo 13 of conductor 1 mapping 2 |--> 1 """ G = self.parent() - return G.element_class(G, -self.element(), check=False) + if G.zeta.is_in_cache(): + x = -self.element() + else: + x = tuple(~z for z in self.values_on_gens()) + return G.element_class(G, x, check=False) def _mul_(self, other): """ @@ -450,7 +458,11 @@ def _mul_(self, other): Dirichlet character modulo 3 of conductor 3 mapping 2 |--> -1 """ G = self.parent() - return G.element_class(G, self.element() + other.element(), check=False) + if G.zeta.is_in_cache(): + x = self.element() + other.element() + else: + x = tuple(y * z for y, z in zip(self.values_on_gens(), other.values_on_gens())) + return G.element_class(G, x, check=False) def __copy__(self): """ @@ -463,12 +475,14 @@ def __copy__(self): sage: a is b False sage: a.element() is b.element() + False + sage: a.values_on_gens() is b.values_on_gens() True """ # This method exists solely because of a bug in the cPickle module -- # see modsym/manin_symbols.py. G = self.parent() - return G.element_class(G, self.element(), check=False) + return G.element_class(G, self.values_on_gens(), check=False) def __pow__(self, n): """ @@ -483,7 +497,11 @@ def __pow__(self, n): Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> -1 """ G = self.parent() - return G.element_class(G, n * self.element(), check=False) + if G.zeta.is_in_cache(): + x = n * self.element() + else: + x = tuple(z**n for z in self.values_on_gens()) + return G.element_class(G, x, check=False) def _repr_short_(self): r""" @@ -635,11 +653,11 @@ def bernoulli(self, k, algorithm='recurrence', cache=True, **opts): REFERENCES: - .. [Cohen-II] H. Cohen, Number Theory and Diophantine + .. [Cohen-II] \H. Cohen, Number Theory and Diophantine Equations, Volume II. Graduate Texts in Mathematics 240. Springer, 2007. - .. [Diamond-Im] F. Diamond and J. Im, Modular forms and + .. [Diamond-Im] \F. Diamond and J. Im, Modular forms and modular curves. In: V. Kumar Murty (ed.), Seminar on Fermat's Last Theorem (Toronto, 1993-1994), 39-133. CMS Conference Proceedings 17. American Mathematical Society, @@ -747,7 +765,7 @@ def conductor(self): # divisibility holds equals Valuation(Order(x),p)+1. cond = p**(valuation(self.order(),p) + 1) if p == 2 and F[0][1] > 2 and self.values_on_gens()[1].multiplicative_order() != 1: - cond *= 2; + cond *= 2 return rings.Integer(cond) @cached_method @@ -764,9 +782,9 @@ def decomposition(self): sage: d = c.decomposition(); d [Dirichlet character modulo 4 of conductor 4 mapping 3 |--> -1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> zeta4] sage: d[0].parent() - Group of Dirichlet characters of modulus 4 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 4 with values in Cyclotomic Field of order 4 and degree 2 sage: d[1].parent() - Group of Dirichlet characters of modulus 5 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 5 with values in Cyclotomic Field of order 4 and degree 2 We can't multiply directly, since coercion of one element into the other parent fails in both cases:: @@ -774,7 +792,7 @@ def decomposition(self): sage: d[0]*d[1] == c Traceback (most recent call last): ... - TypeError: unsupported operand parent(s) for '*': 'Group of Dirichlet characters of modulus 4 over Cyclotomic Field of order 4 and degree 2' and 'Group of Dirichlet characters of modulus 5 over Cyclotomic Field of order 4 and degree 2' + TypeError: unsupported operand parent(s) for '*': 'Group of Dirichlet characters modulo 4 with values in Cyclotomic Field of order 4 and degree 2' and 'Group of Dirichlet characters modulo 5 with values in Cyclotomic Field of order 4 and degree 2' We can multiply if we're explicit about where we want the multiplication to take place. @@ -834,20 +852,26 @@ def galois_orbit(self, sort=True): sage: G = DirichletGroup(30); e = G.1 sage: e.galois_orbit() - [Dirichlet character modulo 30 of conductor 5 mapping 11 |--> 1, 7 |--> zeta4, Dirichlet character modulo 30 of conductor 5 mapping 11 |--> 1, 7 |--> -zeta4] + [Dirichlet character modulo 30 of conductor 5 mapping 11 |--> 1, 7 |--> -zeta4, + Dirichlet character modulo 30 of conductor 5 mapping 11 |--> 1, 7 |--> zeta4] Another example:: sage: G = DirichletGroup(13) sage: G.galois_orbits() [ - [Dirichlet character modulo 13 of conductor 1 mapping 2 |--> 1], ... [Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -1] + [Dirichlet character modulo 13 of conductor 1 mapping 2 |--> 1], + ..., + [Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -1] ] sage: e = G.0 sage: e Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12 sage: e.galois_orbit() - [Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12, Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12^3 - zeta12, Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -zeta12, Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -zeta12^3 + zeta12] + [Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12, + Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -zeta12^3 + zeta12, + Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12^3 - zeta12, + Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -zeta12] sage: e = G.0^2; e Dirichlet character modulo 13 of conductor 13 mapping 2 |--> zeta12^2 sage: e.galois_orbit() @@ -1119,7 +1143,7 @@ def jacobi_sum(self, char, check=True): TESTS: - This shows that ticket #6393 has been fixed:: + This shows that :trac:`6393` has been fixed:: sage: G = DirichletGroup(5); X = G.list(); Y = X[0]; Z = X[1] sage: # Y is trivial and Z is quartic @@ -1357,7 +1381,10 @@ def is_trivial(self): sage: (a^2).is_trivial() True """ - return (self.element() == 0) + if self.element.is_in_cache(): + return not self.element() + one = self.base_ring().one() + return all(x == one for x in self.values_on_gens()) def kernel(self): r""" @@ -1374,7 +1401,7 @@ def kernel(self): sage: b.kernel() [1, 11] """ - one = self.base_ring()(1) + one = self.base_ring().one() return [x for x in range(self.modulus()) if self(x) == one] def maximize_base_ring(self): @@ -1385,7 +1412,6 @@ def maximize_base_ring(self): \varepsilon : (\ZZ/N\ZZ)^* \to \QQ(\zeta_n) - be a Dirichlet character. This function returns an equal Dirichlet character @@ -1393,7 +1419,6 @@ def maximize_base_ring(self): \chi : (\ZZ/N\ZZ)^* \to \QQ(\zeta_m) - where `m` is the least common multiple of `n` and the exponent of `(\ZZ/N\ZZ)^*`. @@ -1492,7 +1517,7 @@ def modulus(self): sage: e.conductor() 4 """ - return self.__modulus + return self.parent().modulus() def level(self): """ @@ -1522,7 +1547,9 @@ def multiplicative_order(self): sage: e.multiplicative_order() 2 """ - return self.element().additive_order() + if self.parent().zeta.is_in_cache(): + return self.element().additive_order() + return lcm([z.multiplicative_order() for z in self.values_on_gens()]) def primitive_character(self): """ @@ -1623,7 +1650,7 @@ def values(self): G = self.parent() R = G.base_ring() - mod = self.__modulus + mod = self.parent().modulus() if mod == 1: return [R.one()] elif mod == 2: @@ -1659,7 +1686,6 @@ def values(self): exponents[i] = 0 i += 1 - def values_on_gens(self): r""" Return a tuple of the values of ``self`` on the standard @@ -1679,6 +1705,7 @@ def values_on_gens(self): self.__values_on_gens = v return v + @cached_method def element(self): r""" Return the underlying `\ZZ/n\ZZ`-module @@ -1698,53 +1725,69 @@ def element(self): sage: b.element() (0, 1) """ - try: - return self.__element - except AttributeError: - P = self.parent() - M = P._module - if is_ComplexField(P.base_ring()): - zeta = P._zeta - zeta_argument = zeta.argument() - v = M([int(round(x.argument()/zeta_argument)) - for x in self.values_on_gens()]) - else: - dlog = P._zeta_dlog - v = M([dlog[x] for x in self.values_on_gens()]) - self.__element = v - return v + P = self.parent() + M = P._module + if is_ComplexField(P.base_ring()): + zeta = P.zeta() + zeta_argument = zeta.argument() + v = M([int(round(x.argument()/zeta_argument)) + for x in self.values_on_gens()]) + else: + dlog = P._zeta_dlog + v = M([dlog[x] for x in self.values_on_gens()]) + return v class DirichletGroupFactory(UniqueFactory): r""" - The group of Dirichlet characters modulo `N` with values in the subgroup - `\langle \zeta_n\rangle` of the multiplicative group of the ``base_ring``. - If ``base_ring`` is omitted then we use `\QQ(\zeta_n)`, where `n` is the - exponent of `(\ZZ/N\ZZ)^*`. If `\zeta` is omitted then we compute and use a - maximal-order zeta in ``base_ring``, if possible. + Construct a group of Dirichlet characters modulo `N`. INPUT: - - ``N`` - an integer + - ``N`` -- positive integer - - ``base_ring`` - a ring (optional), where characters take their values - (should be an integral domain) + - ``base_ring`` -- commutative ring; the value ring for the + characters in this group (default: the cyclotomic field + `\QQ(\zeta_n)`, where `n` is the exponent of `(\ZZ/N\ZZ)^*`) - - ``zeta`` - an element (optional), a root of unity in ``base_ring`` + - ``zeta`` -- (optional) root of unity in ``base_ring`` - - ``zeta_order`` - an integer (optional), the order of zeta + - ``zeta_order`` -- (optional) positive integer; this must be the + order of ``zeta`` if both are specified - - ``names`` - ignored (needed so ``G.... = DirichletGroup(...)`` notation - works) + - ``names`` -- ignored (needed so ``G.<...> = DirichletGroup(...)`` + notation works) - - ``integral`` - a boolean (default: ``False``). If ``True``, return the - group with ``base_ring`` the ring of integers in the smallest choice of - :meth:`sage.rings.number_field.number_field.CyclotomicField`. Ignored if - ``base_ring`` is not ``None``. + - ``integral`` -- boolean (default: ``False``); whether to replace + the default cyclotomic field by its rings of integers as the + base ring. This is ignored if ``base_ring`` is not ``None``. OUTPUT: - a group of Dirichlet characters + The group of Dirichlet characters modulo `N` with values in a + subgroup `V` of the multiplicative group `R^*` of ``base_ring``. + This is the group of homomorphisms `(\ZZ/N\ZZ)^* \to V` with + pointwise multiplication. The group `V` is determined as follows: + + - If both ``zeta`` and ``zeta_order`` are omitted, then `V` is + taken to be `R^*`, or equivalently its `n`-torsion subgroup, + where `n` is the exponent of `(\ZZ/N\ZZ)^*`. Many operations, + such as finding a set of generators for the group, are only + implemented if `V` is cyclic and a generator for `V` can be + found. + + - If ``zeta`` is specified, then `V` is taken to be the cyclic + subgroup of `R^*` generated by ``zeta``. If ``zeta_order`` is + also given, it must be the multiplicative order of ``zeta``; + this is useful if the base ring is not exact or if the order of + ``zeta`` is very large. + + - If ``zeta`` is not specified but ``zeta_order`` is, then `V` is + taken to be the group of roots of unity of order dividing + ``zeta_order`` in `R`. In this case, `R` must be a domain (so + `V` is cyclic), and `V` must have order ``zeta_order``. + Furthermore, a generator ``zeta`` of `V` is computed, and an + error is raised if such ``zeta`` cannot be found. EXAMPLES: @@ -1752,13 +1795,13 @@ class DirichletGroupFactory(UniqueFactory): of `(\ZZ/N\ZZ)^*`:: sage: DirichletGroup(20) - Group of Dirichlet characters of modulus 20 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 20 with values in Cyclotomic Field of order 4 and degree 2 We create the group of Dirichlet character mod 20 with values in the rational numbers:: sage: G = DirichletGroup(20, QQ); G - Group of Dirichlet characters of modulus 20 over Rational Field + Group of Dirichlet characters modulo 20 with values in Rational Field sage: G.order() 4 sage: G.base_ring() @@ -1788,16 +1831,29 @@ class DirichletGroupFactory(UniqueFactory): sage: G.zeta_order() 4 - In this example we create a Dirichlet character with values in a - number field. We have to give ``zeta``, but not its order:: + In this example we create a Dirichlet group with values in a + number field:: sage: R. = PolynomialRing(QQ) sage: K. = NumberField(x^4 + 1) + sage: DirichletGroup(5, K) + Group of Dirichlet characters modulo 5 with values in Number Field in a with defining polynomial x^4 + 1 + + An example where we give ``zeta``, but not its order:: + sage: G = DirichletGroup(5, K, a); G - Group of Dirichlet characters of modulus 5 over Number Field in a with defining polynomial x^4 + 1 + Group of Dirichlet characters modulo 5 with values in the group of order 8 generated by a in Number Field in a with defining polynomial x^4 + 1 sage: G.list() [Dirichlet character modulo 5 of conductor 1 mapping 2 |--> 1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> a^2, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -a^2] + We can also restrict the order of the characters, either with or + without specifying a root of unity:: + + sage: DirichletGroup(5, K, zeta=-1, zeta_order=2) + Group of Dirichlet characters modulo 5 with values in the group of order 2 generated by -1 in Number Field in a with defining polynomial x^4 + 1 + sage: DirichletGroup(5, K, zeta_order=2) + Group of Dirichlet characters modulo 5 with values in the group of order 2 generated by -1 in Number Field in a with defining polynomial x^4 + 1 + :: sage: G. = DirichletGroup(13) @@ -1814,7 +1870,7 @@ class DirichletGroupFactory(UniqueFactory): sage: p = next_prime(10^40) sage: g = DirichletGroup(19, GF(p)); g - Group of Dirichlet characters of modulus 19 over Finite Field of size 10000000000000000000000000000000000000121 + Group of Dirichlet characters modulo 19 with values in Finite Field of size 10000000000000000000000000000000000000121 Note that the root of unity has small order, i.e., it is not the largest order root of unity in the field:: @@ -1844,10 +1900,45 @@ class DirichletGroupFactory(UniqueFactory): :: sage: DirichletGroup(60, integral=True) - Group of Dirichlet characters of modulus 60 over Gaussian Integers in Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 60 with values in Gaussian Integers in Cyclotomic Field of order 4 and degree 2 sage: parent(DirichletGroup(60, integral=True).gens()[2].values_on_gens()[2]) Gaussian Integers in Cyclotomic Field of order 4 and degree 2 + If the order of ``zeta`` cannot be determined automatically, we + can specify it using ``zeta_order``:: + + sage: DirichletGroup(7, CC, zeta=exp(2*pi*I/6)) + Traceback (most recent call last): + ... + NotImplementedError: order of element not known + + sage: DirichletGroup(7, CC, zeta=exp(2*pi*I/6), zeta_order=6) + Group of Dirichlet characters modulo 7 with values in the group of order 6 generated by 0.500000000000000 + 0.866025403784439*I in Complex Field with 53 bits of precision + + If the base ring is not a domain (in which case the group of roots + of unity is not necessarily cyclic), some operations still work, + such as creation of elements:: + + sage: G = DirichletGroup(5, Zmod(15)); G + Group of Dirichlet characters modulo 5 with values in Ring of integers modulo 15 + sage: chi = G([13]); chi + Dirichlet character modulo 5 of conductor 5 mapping 2 |--> 13 + sage: chi^2 + Dirichlet character modulo 5 of conductor 5 mapping 2 |--> 4 + sage: chi.multiplicative_order() + 4 + + Other operations only work if ``zeta`` is specified:: + + sage: G.gens() + Traceback (most recent call last): + ... + NotImplementedError: factorization of polynomials over rings with composite characteristic is not implemented + sage: G = DirichletGroup(5, Zmod(15), zeta=2); G + Group of Dirichlet characters modulo 5 with values in the group of order 4 generated by 2 in Ring of integers modulo 15 + sage: G.gens() + (Dirichlet character modulo 5 of conductor 5 mapping 2 |--> 2,) + TESTS: Dirichlet groups are cached, creating two groups with the same parameters @@ -1864,20 +1955,20 @@ def create_key(self, N, base_ring=None, zeta=None, zeta_order=None, names=None, TESTS:: sage: DirichletGroup.create_key(60) - (Cyclotomic Field of order 4 and degree 2, 60, zeta4, 4) + (Cyclotomic Field of order 4 and degree 2, 60, None, None) An example to illustrate that ``base_ring`` is a part of the key:: sage: k = DirichletGroup.create_key(2, base_ring=QQ); k - (Rational Field, 2, 1, 1) + (Rational Field, 2, None, None) sage: l = DirichletGroup.create_key(2, base_ring=CC); l - (Complex Field with 53 bits of precision, 2, 1.00000000000000, 1) + (Complex Field with 53 bits of precision, 2, None, None) sage: k == l False sage: G = DirichletGroup.create_object(None, k); G - Group of Dirichlet characters of modulus 2 over Rational Field + Group of Dirichlet characters modulo 2 with values in Rational Field sage: H = DirichletGroup.create_object(None, l); H - Group of Dirichlet characters of modulus 2 over Complex Field with 53 bits of precision + Group of Dirichlet characters modulo 2 with values in Complex Field with 53 bits of precision sage: G == H False @@ -1885,39 +1976,53 @@ def create_key(self, N, base_ring=None, zeta=None, zeta_order=None, names=None, equal and the caching would be broken:: sage: k = k[1:]; k - (2, 1, 1) + (2, None, None) sage: l = l[1:]; l - (2, 1.00000000000000, 1) + (2, None, None) sage: k == l True sage: DirichletGroup(2, base_ring=QQ) is DirichletGroup(2, base_ring=CC) False + If the base ring is not an integral domain, an error will be + raised if only ``zeta_order`` is specified:: + + sage: DirichletGroup(17, Integers(15)) + Group of Dirichlet characters modulo 17 with values in Ring of integers modulo 15 + sage: DirichletGroup(17, Integers(15), zeta_order=4) + Traceback (most recent call last): + ... + ValueError: base ring (= Ring of integers modulo 15) must be an integral domain if only zeta_order is specified + sage: G = DirichletGroup(17, Integers(15), zeta=7); G + Group of Dirichlet characters modulo 17 with values in the group of order 4 generated by 7 in Ring of integers modulo 15 + sage: G.order() + 4 + """ modulus = rings.Integer(N) if base_ring is None: if not (zeta is None and zeta_order is None): - raise ValueError("zeta and zeta_order must be None if base_ring not specified.") + raise ValueError("zeta and zeta_order must be None if base_ring not specified") e = rings.IntegerModRing(modulus).unit_group_exponent() base_ring = rings.CyclotomicField(e) if integral: base_ring = base_ring.ring_of_integers() if not is_Ring(base_ring): - raise TypeError("base_ring (=%s) must be a ring"%base_ring) - - if zeta is None: - e = rings.IntegerModRing(modulus).unit_group_exponent() - for d in reversed(e.divisors()): - try: - zeta = base_ring.zeta(d) - zeta_order = d - break - except ValueError: - pass - elif zeta_order is None: - zeta_order = zeta.multiplicative_order() + raise TypeError("base_ring (= %s) must be a ring" % base_ring) + + # If either zeta or zeta_order is given, compute the other. + if zeta is not None: + zeta = base_ring(zeta) + if zeta_order is None: + zeta_order = zeta.multiplicative_order() + elif zeta_order is not None: + if not base_ring.is_integral_domain(): + raise ValueError("base ring (= %s) must be an integral domain if only zeta_order is specified" + % base_ring) + zeta_order = rings.Integer(zeta_order) + zeta = base_ring.zeta(zeta_order) return (base_ring, modulus, zeta, zeta_order) @@ -1930,11 +2035,11 @@ def create_object(self, version, key, **extra_args): sage: K = CyclotomicField(4) sage: DirichletGroup.create_object(None, (K, 60, K.gen(), 4)) - Group of Dirichlet characters of modulus 60 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 60 with values in the group of order 4 generated by zeta4 in Cyclotomic Field of order 4 and degree 2 """ base_ring, modulus, zeta, zeta_order = key - return DirichletGroup_class(modulus, zeta, zeta_order) + return DirichletGroup_class(base_ring, modulus, zeta, zeta_order) DirichletGroup = DirichletGroupFactory("DirichletGroup") @@ -1962,15 +2067,21 @@ class DirichletGroup_class(WithEqualityById, Parent): Element = DirichletCharacter - def __init__(self, modulus, zeta, zeta_order): + def __init__(self, base_ring, modulus, zeta, zeta_order): """ Create a Dirichlet group. Not to be called directly (use the factory function ``DirichletGroup``). + The ``DirichletGroup`` factory ensures that either both + ``zeta`` and ``zeta_order`` are specified, or that both are + ``None``. In the former case, it also ensures that ``zeta`` + is an element of ``base_ring`` and that ``zeta_order`` is an + element of ``ZZ``. + TESTS:: - sage: G = DirichletGroup(7, base_ring=Integers(9), zeta=Integers(9)(2)) # indirect doctest + sage: G = DirichletGroup(7, base_ring=Integers(9), zeta=2) # indirect doctest sage: TestSuite(G).run() sage: G.base() # check that Parent.__init__ has been called Ring of integers modulo 9 @@ -1979,10 +2090,10 @@ def __init__(self, modulus, zeta, zeta_order): True sage: DirichletGroup(13) == DirichletGroup(13, QQ) False + """ from sage.categories.groups import Groups category = Groups().Commutative() - base_ring = zeta.parent() if base_ring.is_integral_domain() or base_ring.is_finite(): # The group of n-th roots of unity in the base ring is # finite, and hence this Dirichlet group is finite too. @@ -1992,27 +2103,9 @@ def __init__(self, modulus, zeta, zeta_order): category = category.Finite().FinitelyGenerated() Parent.__init__(self, base_ring, category=category) self._zeta = zeta - self._zeta_order = rings.Integer(zeta_order) + self._zeta_order = zeta_order self._modulus = modulus self._integers = rings.IntegerModRing(modulus) - a = base_ring.one() - v = {a:0} - w = [a] - if is_ComplexField(base_ring): - for i in range(1, self._zeta_order): - a = a * zeta - a._set_multiplicative_order(zeta_order/gcd(zeta_order, i)) - v[a] = i - w.append(a) - else: - for i in range(1, self._zeta_order): - a = a * zeta - v[a] = i - w.append(a) - self._zeta_powers = w # gives quickly the ith power of zeta - self._zeta_dlog = v # dictionary that computes log_{zeta}(power of zeta). - self._module = free_module.FreeModule(rings.IntegerModRing(zeta_order), - len(self._integers.unit_gens())) def __setstate__(self, state): """ @@ -2029,6 +2122,58 @@ def __setstate__(self, state): state['_zeta_order'] = rings.Integer(state['_zeta_order']) super(DirichletGroup_class, self).__setstate__(state) + @property + def _module(self): + """ + Return the free module used to represent Dirichlet characters. + + TESTS:: + + sage: DirichletGroup(12)._module + Vector space of dimension 2 over Ring of integers modulo 2 + """ + return free_module.FreeModule(rings.IntegerModRing(self.zeta_order()), + len(self.unit_gens())) + + @property + def _zeta_powers(self): + """ + Return a list of powers of the distinguished root of unity. + + TESTS:: + + sage: DirichletGroup(5)._zeta_powers + [1, zeta4, -1, -zeta4] + """ + R = self.base_ring() + a = R.one() + w = [a] + zeta = self.zeta() + zeta_order = self.zeta_order() + if is_ComplexField(R): + for i in range(1, zeta_order): + a = a * zeta + a._set_multiplicative_order(zeta_order/gcd(zeta_order, i)) + w.append(a) + else: + for i in range(1, zeta_order): + a = a * zeta + w.append(a) + return w + + @property + def _zeta_dlog(self): + """ + Return a dictionary that can be used to compute discrete + logarithms in the value group of this Dirichlet group. + + TESTS:: + + sage: DirichletGroup(5)._zeta_dlog + {-1: 2, -zeta4: 3, zeta4: 1, 1: 0} + """ + return {z: i for i, z in enumerate(self._zeta_powers)} + def change_ring(self, R, zeta=None, zeta_order=None): """ Return the base extension of ``self`` to ``R``. @@ -2039,12 +2184,16 @@ def change_ring(self, R, zeta=None, zeta_order=None): base ring of ``self``, or a ring homomorphism with the base ring of ``self`` as its domain + - ``zeta`` -- (optional) root of unity in ``R`` + + - ``zeta_order`` -- (optional) order of ``zeta`` + EXAMPLES:: sage: G = DirichletGroup(7,QQ); G - Group of Dirichlet characters of modulus 7 over Rational Field + Group of Dirichlet characters modulo 7 with values in Rational Field sage: G.change_ring(CyclotomicField(6)) - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order 6 and degree 2 + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 TESTS: @@ -2054,12 +2203,28 @@ def change_ring(self, R, zeta=None, zeta_order=None): sage: f = K.complex_embeddings()[0] sage: D = DirichletGroup(5, K) sage: D.change_ring(f) - Group of Dirichlet characters of modulus 5 over Complex Field with 53 bits of precision - - """ + Group of Dirichlet characters modulo 5 with values in Complex Field with 53 bits of precision + + """ + if zeta is None and self._zeta is not None: + # A root of unity was explicitly given; we use it over the + # new base ring as well. + zeta = self._zeta + if zeta_order is None: + # We reuse _zeta_order if we know that it stays the + # same; otherwise it will be recomputed as the order + # of R(zeta) by the DirichletGroup factory. + p = R.characteristic() + if p == 0 or p.gcd(self._zeta_order) == 1: + zeta_order = self._zeta_order + else: + # No root of unity specified; use the same zeta_order + # (which may still be None). + zeta_order = self._zeta_order + # Map zeta to the new parent + if zeta is not None: + zeta = R(zeta) if isinstance(R, Map): - if zeta is None: - zeta = R(self._zeta) R = R.codomain() return DirichletGroup(self.modulus(), R, zeta=zeta, @@ -2067,25 +2232,64 @@ def change_ring(self, R, zeta=None, zeta_order=None): def base_extend(self, R): """ - Returns the Dirichlet group over R obtained by extending scalars, with the same modulus and root of unity as self. + Return the base extension of ``self`` to ``R``. + + INPUT: + + - ``R`` -- either a ring admitting a *coercion* map from the + base ring of ``self``, or a ring homomorphism with the base + ring of ``self`` as its domain EXAMPLES:: sage: G = DirichletGroup(7,QQ); G - Group of Dirichlet characters of modulus 7 over Rational Field + Group of Dirichlet characters modulo 7 with values in Rational Field sage: H = G.base_extend(CyclotomicField(6)); H - Group of Dirichlet characters of modulus 7 over Cyclotomic Field of order 6 and degree 2 + Group of Dirichlet characters modulo 7 with values in Cyclotomic Field of order 6 and degree 2 + + Note that the root of unity can change:: + sage: H.zeta() - -1 + zeta6 + + This method (in contrast to :meth:`change_ring`) requires a + coercion map to exist:: + sage: G.base_extend(ZZ) Traceback (most recent call last): ... - TypeError: no coercion map from 'Rational Field' to 'Integer Ring' is defined + TypeError: no coercion map from Rational Field to Integer Ring is defined + + Base-extended Dirichlet groups do not silently get roots of + unity with smaller order than expected (:trac:`6018`):: + + sage: G = DirichletGroup(10, QQ).base_extend(CyclotomicField(4)) + sage: H = DirichletGroup(10, CyclotomicField(4)) + sage: G is H + True + + sage: G3 = DirichletGroup(31, CyclotomicField(3)) + sage: G5 = DirichletGroup(31, CyclotomicField(5)) + sage: K30 = CyclotomicField(30) + sage: G3.gen(0).base_extend(K30) * G5.gen(0).base_extend(K30) + Dirichlet character modulo 31 of conductor 31 mapping 3 |--> -zeta30^7 + zeta30^5 + zeta30^4 + zeta30^3 - zeta30 - 1 + + When a root of unity is specified, base extension still works + if the new base ring is not an integral domain:: + + sage: f = DirichletGroup(17, ZZ, zeta=-1).0 + sage: g = f.base_extend(Integers(15)) + sage: g(3) + 14 + sage: g.parent().zeta() + 14 """ - if not R.has_coerce_map_from(self.base_ring()): - raise TypeError("no coercion map from '%s' to '%s' is defined" % (self.base_ring(), R)) - return DirichletGroup(self.modulus(), R, zeta=R(self.zeta()), zeta_order=self.zeta_order()) + if not (isinstance(R, Map) + or R.has_coerce_map_from(self.base_ring())): + raise TypeError("no coercion map from %s to %s is defined" + % (self.base_ring(), R)) + return self.change_ring(R) def _element_constructor_(self, x): """ @@ -2104,7 +2308,7 @@ def _element_constructor_(self, x): sage: G(0) Traceback (most recent call last): ... - TypeError: no coercion of 0 into Group of Dirichlet characters of modulus 13 over Cyclotomic Field of order 12 and degree 4 defined + TypeError: cannot convert 0 to an element of Group of Dirichlet characters modulo 13 with values in Cyclotomic Field of order 12 and degree 4 sage: G = DirichletGroup(6) sage: G(DirichletGroup(3).0) @@ -2129,7 +2333,7 @@ def _element_constructor_(self, x): if isinstance(x, list): # list of values on each unit generator return self.element_class(self, x) elif not isinstance(x, DirichletCharacter): - raise TypeError("no coercion of %s into %s defined" % (x, self)) + raise TypeError("cannot convert %s to an element of %s" % (x, self)) elif not x.conductor().divides(self.modulus()): raise TypeError("conductor must divide modulus") a = [] @@ -2139,7 +2343,7 @@ def _element_constructor_(self, x): while x.modulus().gcd(v) != 1: v += self.modulus() a.append(R(x(v))) - return self(a) + return self.element_class(self, a) def _coerce_map_from_(self, X): """ @@ -2161,7 +2365,9 @@ def _coerce_map_from_(self, X): return (isinstance(X, DirichletGroup_class) and self.modulus() == X.modulus() and self.base_ring().has_coerce_map_from(X.base_ring()) and - X.zeta_order().divides(self.zeta_order())) + (self._zeta is None or + (X._zeta is not None and + self.base_ring()(X._zeta) in self._zeta_powers))) def __len__(self): """ @@ -2191,13 +2397,16 @@ def _repr_(self): sage: G = DirichletGroup(11) sage: repr(G) # indirect doctest - 'Group of Dirichlet characters of modulus 11 over Cyclotomic Field of order 10 and degree 4' + 'Group of Dirichlet characters modulo 11 with values in Cyclotomic Field of order 10 and degree 4' sage: G.rename('Dir(11)') sage: G Dir(11) """ - return "Group of Dirichlet characters of modulus %s over %s"%\ - (self.modulus(),self.base_ring()) + s = "Group of Dirichlet characters modulo %s with values in " % self.modulus() + if self._zeta is not None: + s += "the group of order %s generated by %s in " % (self._zeta_order, self._zeta) + s += str(self.base_ring()) + return s @cached_method def decomposition(self): @@ -2212,13 +2421,13 @@ def decomposition(self): sage: DirichletGroup(20).decomposition() [ - Group of Dirichlet characters of modulus 4 over Cyclotomic Field of order 4 and degree 2, - Group of Dirichlet characters of modulus 5 over Cyclotomic Field of order 4 and degree 2 + Group of Dirichlet characters modulo 4 with values in Cyclotomic Field of order 4 and degree 2, + Group of Dirichlet characters modulo 5 with values in Cyclotomic Field of order 4 and degree 2 ] sage: DirichletGroup(20,GF(5)).decomposition() [ - Group of Dirichlet characters of modulus 4 over Finite Field of size 5, - Group of Dirichlet characters of modulus 5 over Finite Field of size 5 + Group of Dirichlet characters modulo 4 with values in Finite Field of size 5, + Group of Dirichlet characters modulo 5 with values in Finite Field of size 5 ] """ R = self.base_ring() @@ -2242,7 +2451,7 @@ def exponent(self): sage: DirichletGroup(37).exponent() 36 """ - return self._zeta_order + return self.zeta_order() @cached_method def _automorphisms(self): @@ -2309,7 +2518,9 @@ def galois_orbits(self, v=None, reps_only=False, sort=True, check=True): sage: DirichletGroup(20).galois_orbits() [ - [Dirichlet character modulo 20 of conductor 1 mapping 11 |--> 1, 17 |--> 1], ... [Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -1] + [Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -1], + ..., + [Dirichlet character modulo 20 of conductor 1 mapping 11 |--> 1, 17 |--> 1] ] sage: DirichletGroup(17, Integers(6), zeta=Integers(6)(5)).galois_orbits() Traceback (most recent call last): @@ -2510,10 +2721,10 @@ def unit_gens(self): """ return self._integers.unit_gens() + @cached_method def zeta(self): """ - Returns the chosen root zeta of unity in the base ring - `R`. + Return the chosen root of unity in the base ring. EXAMPLES:: @@ -2528,12 +2739,23 @@ def zeta(self): sage: DirichletGroup(60, GF(25,'a')).zeta() 2 """ - return self._zeta + zeta = self._zeta + if zeta is None: + R = self.base_ring() + e = self._integers.unit_group_exponent() + for d in reversed(e.divisors()): + try: + zeta = R.zeta(d) + break + except ValueError: + pass + self.zeta_order.set_cache(d) + return zeta + @cached_method def zeta_order(self): """ - Returns the order of the chosen root zeta of unity in the base ring - `R`. + Return the order of the chosen root of unity in the base ring. EXAMPLES:: @@ -2546,8 +2768,7 @@ def zeta_order(self): sage: DirichletGroup(19).zeta_order() 18 """ - return self._zeta_order - - - - + order = self._zeta_order + if order is None: + order = self.zeta().multiplicative_order() + return order diff --git a/src/sage/modular/etaproducts.py b/src/sage/modular/etaproducts.py index c30066cbf3f..c9d816fe9ff 100644 --- a/src/sage/modular/etaproducts.py +++ b/src/sage/modular/etaproducts.py @@ -13,7 +13,7 @@ :math:`q^{1/24} \prod_{n = 1}^\infty(1-q^n)`. These are useful for obtaining explicit models of modular curves. -See trac ticket #3934 for background. +See :trac:`3934` for background. AUTHOR: diff --git a/src/sage/modular/modform/eis_series_cython.pyx b/src/sage/modular/modform/eis_series_cython.pyx index 40ea7c290bd..bd9b2c0bd8f 100644 --- a/src/sage/modular/modform/eis_series_cython.pyx +++ b/src/sage/modular/modform/eis_series_cython.pyx @@ -158,7 +158,7 @@ cpdef eisenstein_series_poly(int k, int prec = 10) : sage: eisenstein_series_poly(12, prec=5) 5 691 65520 134250480 11606736960 274945048560 """ - cdef mpz_t *val = sage_malloc(prec * sizeof(mpz_t)) + cdef mpz_t *val = sig_malloc(prec * sizeof(mpz_t)) cdef mpz_t one, mult, term, last, term_m1, last_m1 cdef unsigned long int expt cdef long ind, ppow, int_p @@ -225,7 +225,7 @@ cpdef eisenstein_series_poly(int k, int prec = 10) : fmpz_poly_scalar_mul_mpz(res.poly, res.poly, ((a0.denominator())).value) fmpz_poly_set_coeff_mpz(res.poly, 0, ((a0.numerator())).value) - sage_free(val) + sig_free(val) sig_off() diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index bc7c56b5358..90c9fb64b7a 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -573,13 +573,13 @@ def period(self, M, prec=53): to it, which can be computed using the method `:meth:~sage.schemes.elliptic_curves.ell_rational_field.EllipticCurve_rational_field.modular_symbol`. These can be used to express the periods of `f` as exact - linear combinations of a basis for the period lattice of `E`:: + linear combinations of the real and the imaginary period of `E`:: sage: s = E.modular_symbol(sign=+1) sage: t = E.modular_symbol(sign=-1) sage: s(3/11), t(3/11) - (1/10, 1) - sage: s(3/11)*omega1 + t(3/11)*omega2.imag()*I + (1/10, 1/2) + sage: s(3/11)*omega1 + t(3/11)*2*omega2.imag()*I 0.634604652139777 + 1.45881661693850*I ALGORITHM: @@ -595,7 +595,7 @@ def period(self, M, prec=53): REFERENCE: - .. [Cremona] J. E. Cremona, Algorithms for Modular Elliptic + .. [Cremona] \J. E. Cremona, Algorithms for Modular Elliptic Curves. Cambridge University Press, 1997. TESTS:: @@ -900,7 +900,7 @@ def symsquare_lseries(self, chi=None, embedding=0, prec=53): sage: phi = K.embeddings(RR)[0] sage: L = F.symsquare_lseries(embedding=phi) sage: L(5) - verbose -1 (370: dokchitser.py, __call__) Warning: Loss of 8 decimal digits due to cancellation + verbose -1 (...: dokchitser.py, __call__) Warning: Loss of 8 decimal digits due to cancellation -3.57698266793901e19 TESTS:: @@ -1004,7 +1004,7 @@ def petersson_norm(self, embedding=0, prec=53): EXAMPLE:: sage: CuspForms(1, 16).0.petersson_norm() - verbose -1 (370: dokchitser.py, __call__) Warning: Loss of 2 decimal digits due to cancellation + verbose -1 (...: dokchitser.py, __call__) Warning: Loss of 2 decimal digits due to cancellation 2.16906134759063e-6 The Petersson norm depends on a choice of embedding:: @@ -1576,7 +1576,7 @@ def __mul__(self, other): TESTS: - This shows that the issue at trac ticket #7548 is fixed:: + This shows that the issue at :trac:`7548` is fixed:: sage: M = CuspForms(Gamma0(5*3^2), 2) sage: f = M.basis()[0] @@ -1692,7 +1692,7 @@ def twist(self, chi, level=None): q - 2*q^2 - q^3 + 2*q^4 + q^5 + O(q^6) sage: eps = DirichletGroup(3).0 sage: eps.parent() - Group of Dirichlet characters of modulus 3 over Cyclotomic Field of order 2 and degree 1 + Group of Dirichlet characters modulo 3 with values in Cyclotomic Field of order 2 and degree 1 sage: f_eps = f.twist(eps) sage: f_eps.parent() Cuspidal subspace of dimension 9 of Modular Forms space of dimension 16 for Congruence Subgroup Gamma0(99) of weight 2 over Cyclotomic Field of order 2 and degree 1 @@ -1722,7 +1722,7 @@ def twist(self, chi, level=None): REFERENCES: - .. [Atkin-Li] A. O. L. Atkin and Wen-Ch'ing Winnie Li, Twists + .. [Atkin-Li] \A. O. L. Atkin and Wen-Ch'ing Winnie Li, Twists of newforms and pseudo-eigenvalues of `W`-operators. Inventiones math. 48 (1978), 221-243. diff --git a/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx b/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx index 0b203feb25c..da8a3852685 100644 --- a/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx +++ b/src/sage/modular/modform/l_series_gross_zagier_coeffs.pyx @@ -1,4 +1,4 @@ -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from sage.rings.fast_arith cimport arith_llong @@ -68,7 +68,7 @@ def bqf_theta_series(Q, long bound, var=None): a, b, c = Q cdef long* terms = bqf_theta_series_c(NULL, bound, a, b, c) L = [terms[i] for i from 0 <= i <= bound] - sage_free(terms) + sig_free(terms) return to_series(L, var) @@ -152,7 +152,7 @@ def gross_zagier_L_series(an_list, Q, long N, long u, var=None): try: terms = check_allocarray(bound, sizeof(long)) except MemoryError: - sage_free(con_terms) + sig_free(con_terms) raise i = 0 for an in an_list: @@ -172,6 +172,6 @@ def gross_zagier_L_series(an_list, Q, long N, long u, var=None): n += 1 sig_off() L = [terms[i] for i in range(bound - 1)] - sage_free(con_terms) - sage_free(terms) + sig_free(con_terms) + sig_free(terms) return to_series(L, var) diff --git a/src/sage/modular/modform_hecketriangle/abstract_space.py b/src/sage/modular/modform_hecketriangle/abstract_space.py index a3abf6b7d3b..75e1ba3ff03 100644 --- a/src/sage/modular/modform_hecketriangle/abstract_space.py +++ b/src/sage/modular/modform_hecketriangle/abstract_space.py @@ -748,7 +748,8 @@ def aut_factor(self, gamma, t): sage: full_factor = lambda mat, t: (mat[1][0]*t+mat[1][1])**4 sage: T = MF.group().T() sage: S = MF.group().S() - sage: z = AlgebraicField()(1+i/2) + sage: i = AlgebraicField()(i) + sage: z = 1 + i/2 sage: MF.aut_factor(S, z) 3/2*I - 7/16 @@ -766,7 +767,6 @@ def aut_factor(self, gamma, t): sage: MF = ModularForms(n=7, k=14/5, ep=-1) sage: T = MF.group().T() sage: S = MF.group().S() - sage: z = AlgebraicField()(1+i/2) sage: MF.aut_factor(S, z) 1.3655215324256...? + 0.056805991182877...?*I diff --git a/src/sage/modular/modform_hecketriangle/graded_ring_element.py b/src/sage/modular/modform_hecketriangle/graded_ring_element.py index dc813360ade..bd536312a93 100644 --- a/src/sage/modular/modform_hecketriangle/graded_ring_element.py +++ b/src/sage/modular/modform_hecketriangle/graded_ring_element.py @@ -18,7 +18,8 @@ from sage.rings.all import ZZ, infinity, LaurentSeries, O from sage.functions.all import exp -from sage.symbolic.all import pi, i +from sage.rings.number_field.number_field import QuadraticField +from sage.symbolic.all import pi from sage.structure.parent_gens import localvars from sage.modules.free_module_element import vector from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicPlane @@ -1317,6 +1318,8 @@ def order_at(self, tau=infinity): +Infinity """ + i = QuadraticField(-1, 'I').gen() + # if tau is a point of HyperbolicPlane then we use it's coordinates in the UHP model if (tau in HyperbolicPlane()): tau = tau.to_model('UHP').coordinates() @@ -1919,6 +1922,7 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): sage: f_inf(infinity) 0 + sage: i = I = QuadraticField(-1, 'I').gen() sage: z = -1/(-1/(2*i+30)-1) sage: z 2/965*I + 934/965 @@ -2120,6 +2124,8 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): True """ + i = QuadraticField(-1, 'I').gen() + # if tau is a point of HyperbolicPlane then we use it's coordinates in the UHP model if (tau in HyperbolicPlane()): tau = tau.to_model('UHP').coordinates() @@ -2130,12 +2136,12 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): num_prec = self.parent().default_num_prec() # In case the order is known - if (check or\ - tau == infinity or\ - tau == i or\ - tau == self.group().rho() or\ - tau == -self.group().rho().conjugate()): - try: + try: + if (check or\ + tau == infinity or\ + tau == i or\ + tau == self.group().rho() or\ + tau == -self.group().rho().conjugate()): order_tau = self.order_at(tau) if (order_tau > 0): @@ -2144,8 +2150,8 @@ def evaluate(self, tau, prec = None, num_prec = None, check=False): return infinity elif (tau == infinity): return self.q_expansion(prec=1)[0] - except NotImplementedError: - pass + except (TypeError, NotImplementedError): + pass # The general case num_prec = max(\ diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py index e2f4bc772c9..e286002cdc1 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -428,7 +428,7 @@ def string_repr(self, method="default"): S*T^3*S*T^(-2) """ if method == "default": - return super(MatrixGroupElement_generic, self)._repr_() + return MatrixGroupElement_generic._repr_(self) elif method == "basic": (L, sgn) = self._word_S_T_data() @@ -3312,4 +3312,4 @@ def as_hyperbolic_plane_isometry(self, model="UHP"): Set of Morphisms from Hyperbolic plane in the Klein Disk Model model to Hyperbolic plane in the Klein Disk Model model in Category of hyperbolic models of Hyperbolic plane """ - return HyperbolicPlane().UHP().get_isometry(self.matrix()).to_model(model) + return HyperbolicPlane().UHP().get_isometry(self._matrix).to_model(model) diff --git a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py index c8f82071c33..131212705f2 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_groups.py @@ -290,11 +290,11 @@ def rho(self): EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup - sage: HeckeTriangleGroup(3).rho() == 1/2 + sqrt(3)/2*i + sage: HeckeTriangleGroup(3).rho() == QQbar(1/2 + sqrt(3)/2*i) True - sage: HeckeTriangleGroup(4).rho() == sqrt(2)/2*(1 + i) + sage: HeckeTriangleGroup(4).rho() == QQbar(sqrt(2)/2*(1 + i)) True - sage: HeckeTriangleGroup(6).rho() == sqrt(3)/2 + 1/2*i + sage: HeckeTriangleGroup(6).rho() == QQbar(sqrt(3)/2 + 1/2*i) True sage: HeckeTriangleGroup(10).rho() 0.95105651629515...? + 0.30901699437494...?*I diff --git a/src/sage/modular/modsym/heilbronn.pyx b/src/sage/modular/modsym/heilbronn.pyx index bf1e21e6fd3..2e01d6f19d4 100644 --- a/src/sage/modular/modsym/heilbronn.pyx +++ b/src/sage/modular/modsym/heilbronn.pyx @@ -22,7 +22,7 @@ import sage.arith.all import sage.misc.misc include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * @@ -60,13 +60,13 @@ cdef struct list: cdef int* expand(int *v, int n, int new_length) except NULL: cdef int *w cdef int i - w = sage_malloc(new_length*sizeof(int)) + w = sig_malloc(new_length*sizeof(int)) if w == 0: return NULL if v: for i in range(n): w[i] = v[i] - sage_free(v) + sig_free(v) return w cdef int list_append(list* L, int a) except -1: @@ -85,7 +85,7 @@ cdef int list_append4(list* L, int a, int b, int c, int d) except -1: list_append(L, d) cdef void list_clear(list L): - sage_free(L.v) + sig_free(L.v) cdef void list_init(list* L): L.n = 16 @@ -562,9 +562,9 @@ def hecke_images_gamma0_weight2(int u, int v, int N, indices, R): H = HeilbronnCremona(n) if sage.arith.all.is_prime(n) else HeilbronnMerel(n) # Allocate memory to hold images of (u,v) under all Heilbronn matrices - a = sage_malloc(sizeof(int)*H.length) + a = sig_malloc(sizeof(int)*H.length) if not a: raise MemoryError - b = sage_malloc(sizeof(int)*H.length) + b = sig_malloc(sizeof(int)*H.length) if not b: raise MemoryError # Compute images of (u,v) under all Heilbronn matrices @@ -582,8 +582,8 @@ def hecke_images_gamma0_weight2(int u, int v, int N, indices, R): T._add_ui_unsafe_assuming_int(i,k,1) # Free a and b - sage_free(a) - sage_free(b) + sig_free(a) + sig_free(b) t = sage.misc.misc.verbose("finished computing non-reduced images", t, level=1, caller_name='hecke_images_gamma0_weight2') @@ -697,9 +697,9 @@ def hecke_images_nonquad_character_weight2(int u, int v, int N, indices, chi, R) H = HeilbronnCremona(n) if sage.arith.all.is_prime(n) else HeilbronnMerel(n) # Allocate memory to hold images of (u,v) under all Heilbronn matrices - a = sage_malloc(sizeof(int)*H.length) + a = sig_malloc(sizeof(int)*H.length) if not a: raise MemoryError - b = sage_malloc(sizeof(int)*H.length) + b = sig_malloc(sizeof(int)*H.length) if not b: raise MemoryError # Compute images of (u,v) under all Heilbronn matrices @@ -722,8 +722,8 @@ def hecke_images_nonquad_character_weight2(int u, int v, int N, indices, chi, R) T._matrix._add_col_j_of_A_to_col_i_of_self(i * T._ncols + k, chi_vals, scalar) # Free a and b - sage_free(a) - sage_free(b) + sig_free(a) + sig_free(b) return T * R @@ -785,16 +785,16 @@ def hecke_images_quad_character_weight2(int u, int v, int N, indices, chi, R): # Make a matrix over the rational numbers each of whose columns # are the values of the character chi. _chivals = chi.values() - cdef int *chi_vals = sage_malloc(sizeof(int)*len(_chivals)) + cdef int *chi_vals = sig_malloc(sizeof(int)*len(_chivals)) if not chi_vals: raise MemoryError for i in range(len(_chivals)): chi_vals[i] = _chivals[i] for i, n in enumerate(indices): H = HeilbronnCremona(n) if sage.arith.all.is_prime(n) else HeilbronnMerel(n) - a = sage_malloc(sizeof(int)*H.length) + a = sig_malloc(sizeof(int)*H.length) if not a: raise MemoryError - b = sage_malloc(sizeof(int)*H.length) + b = sig_malloc(sizeof(int)*H.length) if not b: raise MemoryError H.apply_only(u, v, N, a, b) @@ -808,9 +808,9 @@ def hecke_images_quad_character_weight2(int u, int v, int N, indices, chi, R): T._add_ui_unsafe_assuming_int(i, k, 1) elif chi_vals[scalar] < 0: T._sub_ui_unsafe_assuming_int(i, k, 1) - sage_free(a); sage_free(b) + sig_free(a); sig_free(b) - sage_free(chi_vals) + sig_free(chi_vals) return T * R @@ -883,16 +883,16 @@ def hecke_images_gamma0_weight_k(int u, int v, int i, int N, int k, indices, R): H = HeilbronnCremona(m) if sage.arith.all.is_prime(m) else HeilbronnMerel(m) # Allocate memory to hold images of (u,v) under all Heilbronn matrices - a = sage_malloc(sizeof(int)*H.length) + a = sig_malloc(sizeof(int)*H.length) if not a: raise MemoryError - b = sage_malloc(sizeof(int)*H.length) + b = sig_malloc(sizeof(int)*H.length) if not b: raise MemoryError # Compute images of (u,v) under all Heilbronn matrices H.apply_only(u, v, N, a, b) # Compute images of X^i Y^(2-k-i) under each Heilbronn matrix - poly = sage_malloc(sizeof(fmpz_poly_t)*H.length) + poly = sig_malloc(sizeof(fmpz_poly_t)*H.length) for j in range(H.length): fmpz_poly_init(poly[j]) @@ -914,13 +914,13 @@ def hecke_images_gamma0_weight_k(int u, int v, int i, int N, int k, indices, R): mpz_add(mpq_numref(T._matrix[z][n*w+p]), mpq_numref(T._matrix[z][n*w+p]), tmp) # Free a and b - sage_free(a) - sage_free(b) + sig_free(a) + sig_free(b) # Free poly part for j in range(H.length): fmpz_poly_clear(poly[j]) - sage_free(poly) + sig_free(poly) mpz_clear(tmp) diff --git a/src/sage/modular/modsym/modsym.py b/src/sage/modular/modsym/modsym.py index 4fbc03ceae2..bcb272e7e24 100644 --- a/src/sage/modular/modsym/modsym.py +++ b/src/sage/modular/modsym/modsym.py @@ -276,7 +276,7 @@ def ModularSymbols(group = 1, :: sage: G = DirichletGroup(13,GF(4,'a')); G - Group of Dirichlet characters of modulus 13 over Finite Field in a of size 2^2 + Group of Dirichlet characters modulo 13 with values in Finite Field in a of size 2^2 sage: e = G.list()[2]; e Dirichlet character modulo 13 of conductor 13 mapping 2 |--> a + 1 sage: M = ModularSymbols(e,4); M diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 734b480631c..b7514ea80e9 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -14,7 +14,7 @@ arith_llong = sage.rings.fast_arith.arith_llong() ctypedef long long llong include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' +include "cysignals/memory.pxi" ############################################################### # @@ -690,11 +690,11 @@ cdef class P1List: # Allocate memory for xgcd table. self.g = NULL; self.s = NULL; self.t = NULL - self.g = sage_malloc(sizeof(int)*N) + self.g = sig_malloc(sizeof(int)*N) if not self.g: raise MemoryError - self.s = sage_malloc(sizeof(int)*N) + self.s = sig_malloc(sizeof(int)*N) if not self.s: raise MemoryError - self.t = sage_malloc(sizeof(int)*N) + self.t = sig_malloc(sizeof(int)*N) if not self.t: raise MemoryError # Initialize xgcd table @@ -713,9 +713,9 @@ cdef class P1List: """ Deallocates memory for an object of the class P1List. """ - if self.g: sage_free(self.g) - if self.s: sage_free(self.s) - if self.t: sage_free(self.t) + if self.g: sig_free(self.g) + if self.s: sig_free(self.s) + if self.t: sig_free(self.t) def __cmp__(self, other): diff --git a/src/sage/modular/modsym/space.py b/src/sage/modular/modsym/space.py index 082ad745037..13aadfc4549 100644 --- a/src/sage/modular/modsym/space.py +++ b/src/sage/modular/modsym/space.py @@ -1144,7 +1144,7 @@ def q_eigenform_character(self, names=None): sage: eps = f.q_eigenform_character('a'); eps Dirichlet character modulo 13 of conductor 13 mapping 2 |--> -a - 1 sage: parent(eps) - Group of Dirichlet characters of modulus 13 over Number Field in a with defining polynomial x^2 + 3*x + 3 + Group of Dirichlet characters modulo 13 with values in Number Field in a with defining polynomial x^2 + 3*x + 3 sage: eps(3) a + 1 diff --git a/src/sage/modular/overconvergent/weightspace.py b/src/sage/modular/overconvergent/weightspace.py index 02251dc648b..d9ef56a7d7d 100644 --- a/src/sage/modular/overconvergent/weightspace.py +++ b/src/sage/modular/overconvergent/weightspace.py @@ -576,8 +576,8 @@ def __hash__(self): sage: w = pAdicWeightSpace(23)(12, DirichletGroup(23, QQ).0) sage: hash(w) - -2363716619315244394 # 64-bit - 470225558 # 32-bit + 2363715643371367891 # 64-bit + -1456525869 # 32-bit """ if self._chi.is_trivial(): return hash(self._k) diff --git a/src/sage/modular/quatalg/brandt.py b/src/sage/modular/quatalg/brandt.py index 53e4c7b7a54..8310db9bb8d 100644 --- a/src/sage/modular/quatalg/brandt.py +++ b/src/sage/modular/quatalg/brandt.py @@ -1442,7 +1442,7 @@ def quaternion_order_with_given_level(A, level): B = O.basis() for (p, r) in fact: - a = int((-p/2)) + a = int(-p) // 2 for v in GF(p)**4: x = sum([int(v[i]+a)*B[i] for i in range(4)]) D = x.reduced_trace()**2 - 4 * x.reduced_norm() diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index baff6dbc054..5fac41e73b3 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -580,8 +580,8 @@ def prepare(v, R, degree=None): sage: prepare(c, None) ([2.0, 3.0], Real Double Field) - This checks a bug listed at Trac #10595. Without good evidence for a ring, the default - is the integers. :: + This checks a bug listed at :trac:`10595`. Without good evidence + for a ring, the default is the integers. :: sage: prepare([], None) ([], Integer Ring) diff --git a/src/sage/modules/free_module_homspace.py b/src/sage/modules/free_module_homspace.py index b455db03d87..6d18e813015 100644 --- a/src/sage/modules/free_module_homspace.py +++ b/src/sage/modules/free_module_homspace.py @@ -153,8 +153,8 @@ def __call__(self, A, check=True): sage: phi(V.0) == V.1 True - The following tests against a bug that was fixed in trac - ticket #9944. The method ``zero()`` calls this hom space with + The following tests against a bug that was fixed in + :trac:`9944`. The method ``zero()`` calls this hom space with a function, not with a matrix, and that case had previously not been taken care of:: diff --git a/src/sage/modules/free_module_integer.py b/src/sage/modules/free_module_integer.py index d5dc47bc883..55c9e075fe6 100644 --- a/src/sage/modules/free_module_integer.py +++ b/src/sage/modules/free_module_integer.py @@ -721,7 +721,7 @@ def voronoi_cell(self, radius=None): REFERENCES: - .. [Vit1996] E. Viterbo, E. Biglieri. *Computing the Voronoi Cell + .. [Vit1996] \E. Viterbo, E. Biglieri. *Computing the Voronoi Cell of a Lattice: The Diamond-Cutting Algorithm*. IEEE Transactions on Information Theory, 1996. """ @@ -796,7 +796,7 @@ def closest_vector(self, t): REFERENCES: - .. [Mic2010] D. Micciancio, P. Voulgaris. *A Deterministic Single + .. [Mic2010] \D. Micciancio, P. Voulgaris. *A Deterministic Single Exponential Time Algorithm for Most Lattice Problems based on Voronoi Cell Computations*. Proceedings of the 42nd ACM Symposium Theory of Computation, 2010. diff --git a/src/sage/modules/vector_double_dense.pyx b/src/sage/modules/vector_double_dense.pyx index 6bf16751023..0cae3fb2a89 100644 --- a/src/sage/modules/vector_double_dense.pyx +++ b/src/sage/modules/vector_double_dense.pyx @@ -754,10 +754,10 @@ cdef class Vector_double_dense(FreeModuleElement): sage: v = vector(RDF, range(9)) sage: w = vector(CDF, [k+(9-k)*I for k in range(9)]) - sage: v.stats_kurtosis() - -1.2300000000000002 - sage: w.stats_kurtosis() - -1.2300000000000002 + sage: v.stats_kurtosis() # rel tol 5e-15 + -1.2300000000000000 + sage: w.stats_kurtosis() # rel tol 5e-15 + -1.2300000000000000 """ import scipy.stats return self._sage_dtype(scipy.stats.kurtosis(self._vector_numpy)) diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index ec33b8fe9a4..27d13bbb923 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -96,7 +96,7 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): self._degree = degree self._parent = parent self._is_mutable = 1 - self._entries = sage_malloc(sizeof(mpz_t) * degree) + self._entries = sig_malloc(sizeof(mpz_t) * degree) if self._entries == NULL: raise MemoryError @@ -127,7 +127,7 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): if self._entries: for i from 0 <= i < self._degree: mpz_clear(self._entries[i]) - sage_free(self._entries) + sig_free(self._entries) cpdef int _cmp_(left, Element right) except -2: """ diff --git a/src/sage/modules/vector_integer_sparse_c.pxi b/src/sage/modules/vector_integer_sparse_c.pxi index 2f69e1ea5f8..7365f2a0a00 100644 --- a/src/sage/modules/vector_integer_sparse_c.pxi +++ b/src/sage/modules/vector_integer_sparse_c.pxi @@ -22,16 +22,16 @@ cdef int allocate_mpz_vector(mpz_vector* v, Py_ssize_t num_nonzero) except -1: It does *not* clear the entries of v, if there are any. """ cdef Py_ssize_t i - v.entries = sage_malloc(num_nonzero*sizeof(mpz_t)) + v.entries = sig_malloc(num_nonzero*sizeof(mpz_t)) if v.entries == NULL: raise MemoryError, "Error allocating memory" for i from 0 <= i < num_nonzero: mpz_init(v.entries[i]) - v.positions = sage_malloc(num_nonzero*sizeof(Py_ssize_t)) + v.positions = sig_malloc(num_nonzero*sizeof(Py_ssize_t)) if v.positions == NULL: for i from 0 <= i < num_nonzero: mpz_clear(v.entries[i]) - sage_free(v.entries) + sig_free(v.entries) v.entries = NULL raise MemoryError, "Error allocating memory" return 0 @@ -53,8 +53,8 @@ cdef void mpz_vector_clear(mpz_vector* v): # These were allocated from the Python heap. # If mpz_vector_init was not called, then this # will (of course!) cause a core dump. - sage_free(v.entries) - sage_free(v.positions) + sig_free(v.entries) + sig_free(v.positions) cdef Py_ssize_t mpz_binary_search0(mpz_t* v, Py_ssize_t n, mpz_t x): """ @@ -198,8 +198,8 @@ cdef int mpz_vector_set_entry(mpz_vector* v, Py_ssize_t n, mpz_t x) except -1: mpz_set(v.entries[i-1], e[i]) mpz_clear(e[i]) v.positions[i-1] = pos[i] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) v.num_nonzero = v.num_nonzero - 1 else: # Allocate new memory and copy over elements from the @@ -228,8 +228,8 @@ cdef int mpz_vector_set_entry(mpz_vector* v, Py_ssize_t n, mpz_t x) except -1: mpz_set(v.entries[i], e[i-1]) mpz_clear(e[i-1]) v.positions[i] = pos[i-1] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) @@ -363,13 +363,13 @@ cdef int mpz_vector_scalar_multiply(mpz_vector* v, mpz_vector* w, mpz_t scalar) return mpz_vector_scale(v, scalar) else: mpz_vector_clear(v) - v.entries = sage_malloc(w.num_nonzero * sizeof(mpz_t)) + v.entries = sig_malloc(w.num_nonzero * sizeof(mpz_t)) if v.entries == NULL: v.positions = NULL raise MemoryError, "error allocating rational sparse vector mpz's" - v.positions = sage_malloc(w.num_nonzero * sizeof(Py_ssize_t)) + v.positions = sig_malloc(w.num_nonzero * sizeof(Py_ssize_t)) if v.positions == NULL: - sage_free(v.entries) + sig_free(v.entries) v.entries = NULL raise MemoryError, "error allocating rational sparse vector positions" v.num_nonzero = w.num_nonzero diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index a2ceed79ca6..6015ccc1821 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -149,7 +149,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): TESTS: - Check that ticket #8601 is fixed:: + Check that ticket :trac:`8601` is fixed:: sage: VS = VectorSpace(GF(2), 3) sage: VS((-1,-2,-3)) diff --git a/src/sage/modules/vector_modn_dense.pyx b/src/sage/modules/vector_modn_dense.pyx index 9dbf4f9aaf4..23e8ae62118 100644 --- a/src/sage/modules/vector_modn_dense.pyx +++ b/src/sage/modules/vector_modn_dense.pyx @@ -94,8 +94,7 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' -from sage.ext.memory cimport check_allocarray +include "cysignals/memory.pxi" from sage.rings.finite_rings.stdint cimport INTEGER_MOD_INT64_LIMIT @@ -174,7 +173,7 @@ cdef class Vector_modn_dense(free_module_element.FreeModuleElement): self._entries[i] = 0 def __dealloc__(self): - sage_free(self._entries) + sig_free(self._entries) cpdef int _cmp_(left, Element right) except -2: """ diff --git a/src/sage/modules/vector_modn_sparse_c.pxi b/src/sage/modules/vector_modn_sparse_c.pxi index 65a5411d8be..300a9e1cbb7 100644 --- a/src/sage/modules/vector_modn_sparse_c.pxi +++ b/src/sage/modules/vector_modn_sparse_c.pxi @@ -5,19 +5,19 @@ # http://www.gnu.org/licenses/ ############################################################################# -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include 'vector_modn_sparse_h.pxi' cdef int allocate_c_vector_modint(c_vector_modint* v, Py_ssize_t num_nonzero) except -1: """ Allocate memory for a c_vector_modint -- most user code won't call this. """ - v.entries = sage_malloc(num_nonzero*sizeof(int)) + v.entries = sig_malloc(num_nonzero*sizeof(int)) if v.entries == NULL: raise MemoryError, "Error allocating memory" - v.positions = sage_malloc(num_nonzero*sizeof(Py_ssize_t)) + v.positions = sig_malloc(num_nonzero*sizeof(Py_ssize_t)) if v.positions == NULL: - sage_free(v.entries) + sig_free(v.entries) raise MemoryError, "Error allocating memory" return 0 @@ -37,8 +37,8 @@ cdef int init_c_vector_modint(c_vector_modint* v, int p, Py_ssize_t degree, return 0 cdef void clear_c_vector_modint(c_vector_modint* v): - sage_free(v.entries) - sage_free(v.positions) + sig_free(v.entries) + sig_free(v.positions) cdef Py_ssize_t binary_search0_modn(Py_ssize_t* v, Py_ssize_t n, int x): """ @@ -161,8 +161,8 @@ cdef int set_entry(c_vector_modint* v, Py_ssize_t n, int x) except -1: for i from m < i < v.num_nonzero: v.entries[i-1] = e[i] v.positions[i-1] = pos[i] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) v.num_nonzero = v.num_nonzero - 1 else: # Allocate new memory and copy over elements from the @@ -187,8 +187,8 @@ cdef int set_entry(c_vector_modint* v, Py_ssize_t n, int x) except -1: for i from ins < i < v.num_nonzero: v.entries[i] = e[i-1] v.positions[i] = pos[i-1] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) cdef int add_c_vector_modint_init(c_vector_modint* sum, c_vector_modint* v, c_vector_modint* w, int multiple) except -1: diff --git a/src/sage/modules/vector_rational_dense.pyx b/src/sage/modules/vector_rational_dense.pyx index 1201b77810d..b56a0cb1fa9 100644 --- a/src/sage/modules/vector_rational_dense.pyx +++ b/src/sage/modules/vector_rational_dense.pyx @@ -52,8 +52,7 @@ TESTS:: ############################################################################### include "cysignals/signals.pxi" -include 'sage/ext/stdsage.pxi' -from sage.ext.memory cimport check_allocarray +include "cysignals/memory.pxi" from sage.structure.element cimport Element, ModuleElement, RingElement, Vector @@ -149,7 +148,7 @@ cdef class Vector_rational_dense(free_module_element.FreeModuleElement): # cannot raise exceptions! for i from 0 <= i < self._degree: mpq_clear(self._entries[i]) - sage_free(self._entries) + sig_free(self._entries) cpdef int _cmp_(left, Element right) except -2: """ diff --git a/src/sage/modules/vector_rational_sparse_c.pxi b/src/sage/modules/vector_rational_sparse_c.pxi index 31c9d97aedf..f5768cbfca5 100644 --- a/src/sage/modules/vector_rational_sparse_c.pxi +++ b/src/sage/modules/vector_rational_sparse_c.pxi @@ -9,7 +9,7 @@ include 'vector_rational_sparse_h.pxi' -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.libs.gmp.mpq cimport * @@ -29,16 +29,16 @@ cdef int allocate_mpq_vector(mpq_vector* v, Py_ssize_t num_nonzero) except -1: It does *not* clear the entries of v, if there are any. """ cdef Py_ssize_t i - v.entries = sage_malloc(num_nonzero*sizeof(mpq_t)) + v.entries = sig_malloc(num_nonzero*sizeof(mpq_t)) if v.entries == NULL: raise MemoryError, "Error allocating memory" for i from 0 <= i < num_nonzero: mpq_init(v.entries[i]) - v.positions = sage_malloc(num_nonzero*sizeof(Py_ssize_t)) + v.positions = sig_malloc(num_nonzero*sizeof(Py_ssize_t)) if v.positions == NULL: for i from 0 <= i < num_nonzero: mpq_clear(v.entries[i]) - sage_free(v.entries) + sig_free(v.entries) v.entries = NULL raise MemoryError, "Error allocating memory" return 0 @@ -62,8 +62,8 @@ cdef void mpq_vector_clear(mpq_vector* v): # These were allocated from the Python heap. # If mpq_vector_init was not called, then this # will (of course!) cause a core dump. - sage_free(v.entries) - sage_free(v.positions) + sig_free(v.entries) + sig_free(v.positions) cdef Py_ssize_t mpq_binary_search0(mpq_t* v, Py_ssize_t n, mpq_t x): """ @@ -207,8 +207,8 @@ cdef int mpq_vector_set_entry(mpq_vector* v, Py_ssize_t n, mpq_t x) except -1: mpq_set(v.entries[i-1], e[i]) mpq_clear(e[i]) v.positions[i-1] = pos[i] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) v.num_nonzero = v.num_nonzero - 1 else: # Allocate new memory and copy over elements from the @@ -237,8 +237,8 @@ cdef int mpq_vector_set_entry(mpq_vector* v, Py_ssize_t n, mpq_t x) except -1: mpq_set(v.entries[i], e[i-1]) mpq_clear(e[i-1]) v.positions[i] = pos[i-1] - sage_free(e) - sage_free(pos) + sig_free(e) + sig_free(pos) @@ -372,13 +372,13 @@ cdef int mpq_vector_scalar_multiply(mpq_vector* v, mpq_vector* w, mpq_t scalar) return mpq_vector_scale(v, scalar) else: mpq_vector_clear(v) - v.entries = sage_malloc(w.num_nonzero * sizeof(mpq_t)) + v.entries = sig_malloc(w.num_nonzero * sizeof(mpq_t)) if v.entries == NULL: v.positions = NULL raise MemoryError, "error allocating rational sparse vector mpq's" - v.positions = sage_malloc(w.num_nonzero * sizeof(Py_ssize_t)) + v.positions = sig_malloc(w.num_nonzero * sizeof(Py_ssize_t)) if v.positions == NULL: - sage_free(v.entries) + sig_free(v.entries) v.entries = NULL raise MemoryError, "error allocating rational sparse vector positions" v.num_nonzero = w.num_nonzero diff --git a/src/sage/modules/with_basis/subquotient.py b/src/sage/modules/with_basis/subquotient.py index 50e0560a162..12948cd392b 100644 --- a/src/sage/modules/with_basis/subquotient.py +++ b/src/sage/modules/with_basis/subquotient.py @@ -173,6 +173,8 @@ class SubmoduleWithBasis(CombinatorialFreeModule): :class:`module with basis ` `V`, or data that can be converted into such a family + - ``unitriangular`` -- if the lift morphism is unitriangular + - ``ambient`` -- the ambient space `V` - ``category`` -- a category @@ -190,7 +192,8 @@ class SubmoduleWithBasis(CombinatorialFreeModule): """ @staticmethod - def __classcall_private__(cls, basis, ambient=None, category=None, *args, **opts): + def __classcall_private__(cls, basis, ambient=None, unitriangular=False, + category=None, *args, **opts): r""" Normalize the input. @@ -209,9 +212,9 @@ def __classcall_private__(cls, basis, ambient=None, category=None, *args, **opts default_category = ModulesWithBasis(ambient.category().base_ring()).Subobjects() category = default_category.or_subcategory(category, join=True) return super(SubmoduleWithBasis, cls).__classcall__( - cls, basis, ambient, category, *args, **opts) + cls, basis, ambient, unitriangular, category, *args, **opts) - def __init__(self, basis, ambient, category): + def __init__(self, basis, ambient, unitriangular, category): r""" Initialization. @@ -234,6 +237,7 @@ def __init__(self, basis, ambient, category): category=category.Subobjects()) self._ambient = ambient self._basis = basis + self._unitriangular = unitriangular self.lift_on_basis = self._basis.__getitem__ def ambient(self): @@ -270,6 +274,7 @@ def lift(self): return self.module_morphism(self.lift_on_basis, codomain=self.ambient(), triangular="lower", + unitriangular=self._unitriangular, cmp=self.ambient().get_order_cmp(), inverse_on_support="compute") diff --git a/src/sage/monoids/indexed_free_monoid.py b/src/sage/monoids/indexed_free_monoid.py index 86892f5186b..274b373e9f8 100644 --- a/src/sage/monoids/indexed_free_monoid.py +++ b/src/sage/monoids/indexed_free_monoid.py @@ -25,6 +25,7 @@ from sage.categories.monoids import Monoids from sage.categories.poor_man_map import PoorManMap +from sage.categories.sets_cat import Sets from sage.rings.integer import Integer from sage.rings.infinity import infinity from sage.rings.all import ZZ @@ -688,6 +689,8 @@ def __init__(self, indices, prefix, category=None, names=None, **kwds): category = category.Finite() else: category = category.Infinite() + if indices in Sets().Finite(): + category = category.FinitelyGeneratedAsMagma() Parent.__init__(self, names=names, category=category) # ignore the optional 'key' since it only affects CachedRepresentation @@ -721,13 +724,12 @@ def _element_constructor_(self, x=None): sage: F(-5) Traceback (most recent call last): ... - ValueError: unable to convert -5, use gen() instead + TypeError: unable to convert -5, use gen() instead """ if x is None: return self.one() if x in self._indices: - raise ValueError("unable to convert {}, use gen() instead".format(x)) - # return self.gens()[x] + raise TypeError("unable to convert {!r}, use gen() instead".format(x)) return self.element_class(self, x) def _an_element_(self): diff --git a/src/sage/monoids/monoid.py b/src/sage/monoids/monoid.py index f34fe2ee22f..e4d66562375 100644 --- a/src/sage/monoids/monoid.py +++ b/src/sage/monoids/monoid.py @@ -28,7 +28,7 @@ def is_Monoid(x): return isinstance(x, Monoid_class) class Monoid_class(Parent): - def __init__(self,names): + def __init__(self, names): r""" EXAMPLES:: @@ -42,7 +42,8 @@ def __init__(self,names): sage: TestSuite(F).run() """ from sage.categories.monoids import Monoids - Parent.__init__(self, base=self,names=names,category=Monoids()) + category = Monoids().FinitelyGeneratedAsMagma() + Parent.__init__(self, base=self, names=names, category=category) @cached_method def gens(self): @@ -56,3 +57,17 @@ def gens(self): (a, b, c, d, e) """ return tuple(self.gen(i) for i in range(self.ngens())) + + def monoid_generators(self): + r""" + Returns the generators for ``self``. + + EXAMPLES:: + + sage: F. = FreeMonoid(5) + sage: F.monoid_generators() + Family (a, b, c, d, e) + """ + from sage.sets.family import Family + return Family(self.gens()) + diff --git a/src/sage/monoids/string_monoid.py b/src/sage/monoids/string_monoid.py index b66e5b2e68c..2f945c71f4c 100644 --- a/src/sage/monoids/string_monoid.py +++ b/src/sage/monoids/string_monoid.py @@ -903,7 +903,7 @@ def characteristic_frequency(self, table_name="beker_piper"): REFERENCES: - .. [BekPip82] H. Beker and F. Piper. *Cipher Systems: The + .. [BekPip82] \H. Beker and F. Piper. *Cipher Systems: The Protection of Communications*. John Wiley and Sons, 1982. .. [Lew00] Robert Edward Lewand. *Cryptological Mathematics*. diff --git a/src/sage/numerical/__init__.py b/src/sage/numerical/__init__.py index c9fecacd721..e69de29bb2d 100644 --- a/src/sage/numerical/__init__.py +++ b/src/sage/numerical/__init__.py @@ -1 +0,0 @@ -import all diff --git a/src/sage/numerical/backends/coin_backend.pxd b/src/sage/numerical/backends/coin_backend.pxd index 8d44d1ee11b..c45427cb2e5 100644 --- a/src/sage/numerical/backends/coin_backend.pxd +++ b/src/sage/numerical/backends/coin_backend.pxd @@ -197,7 +197,7 @@ cdef class CoinBackend(GenericBackend): cdef list col_names, row_names cdef str prob_name - cpdef CoinBackend copy(self) + cpdef __copy__(self) cpdef get_basis_status(self) cpdef int set_basis_status(self, list cstat, list rstat) except -1 cpdef get_binva_row(self, int i) diff --git a/src/sage/numerical/backends/coin_backend.pyx b/src/sage/numerical/backends/coin_backend.pyx index 6faff3aed5c..e042861f89b 100644 --- a/src/sage/numerical/backends/coin_backend.pyx +++ b/src/sage/numerical/backends/coin_backend.pyx @@ -17,7 +17,7 @@ AUTHORS: # http://www.gnu.org/licenses/ ############################################################################## -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from sage.numerical.mip import MIPSolverException @@ -25,6 +25,18 @@ from copy import copy cdef class CoinBackend(GenericBackend): + """ + MIP Backend that uses the COIN solver (CBC). + + TESTS: + + General backend testsuite:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Coin") # optional - cbc + sage: TestSuite(p).run(skip="_test_pickling") # optional - cbc + """ + def __cinit__(self, maximization = True): """ Cython constructor @@ -179,10 +191,21 @@ cdef class CoinBackend(GenericBackend): 4 sage: p.ncols() # optional - cbc 5 - sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - cbc + sage: p.add_variables(2, lower_bound=-2.0, integer=True, obj=42.0, names=['a','b']) # optional - cbc 6 - sage: p.col_name(5) # optional - cbc + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8, optional - cbc + (-2.0, None) + sage: p.is_variable_integer(5) # optional - cbc + True + sage: p.col_name(5) # optional - cbc 'a' + sage: p.objective_coefficient(5) # tol 1e-8, optional - cbc + 42.0 """ #cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer)) cdef int vtype = int(binary) + int(continuous) + int(integer) @@ -454,13 +477,13 @@ cdef class CoinBackend(GenericBackend): c = constraints[i] if c < 0 or c >= nrows: - sage_free(rows) + sig_free(rows) raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints") rows[i] = c self.si.deleteRows(m,rows) - sage_free(rows) + sig_free(rows) cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names = None): """ @@ -672,7 +695,7 @@ cdef class CoinBackend(GenericBackend): INPUT: - - ``indices`` (list of integers) -- this list constains the + - ``indices`` (list of integers) -- this list contains the indices of the constraints in which the variable's coefficient is nonzero @@ -712,6 +735,9 @@ cdef class CoinBackend(GenericBackend): self.si.addCol (1, c_indices, c_values, 0, self.si.getInfinity(), 0) + self.col_names.append("") + + cpdef int solve(self) except -1: r""" Solves the problem. @@ -1183,7 +1209,7 @@ cdef class CoinBackend(GenericBackend): else: return "" - cpdef CoinBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. @@ -1198,7 +1224,7 @@ cdef class CoinBackend(GenericBackend): 6.0 """ # create new backend - cdef CoinBackend p = CoinBackend(maximization = (1 if self.is_maximization() else -1)) + cdef CoinBackend p = type(self)(maximization = (1 if self.is_maximization() else -1)) # replace solver with copy of self's solver del p.si @@ -1310,8 +1336,8 @@ cdef class CoinBackend(GenericBackend): rstat = [c_rstat[j] for j in range(m)] return (cstat, rstat) finally: - sage_free(c_cstat) - sage_free(c_rstat) + sig_free(c_cstat) + sig_free(c_rstat) cpdef int set_basis_status(self, list cstat, list rstat) except -1: """ @@ -1440,8 +1466,8 @@ cdef class CoinBackend(GenericBackend): else: return result finally: - sage_free(c_cstat) - sage_free(c_rstat) + sig_free(c_cstat) + sig_free(c_rstat) cpdef get_binva_row(self, int i): """ @@ -1508,8 +1534,8 @@ cdef class CoinBackend(GenericBackend): ithrow = [c_z[j] for j in range(n)] return (ithrow, slack) finally: - sage_free(c_slack) - sage_free(c_z) + sig_free(c_slack) + sig_free(c_z) cpdef get_binva_col(self, int j): """ @@ -1569,7 +1595,7 @@ cdef class CoinBackend(GenericBackend): jthcol = [c_vec[i] for i in range(m)] return jthcol finally: - sage_free(c_vec) + sig_free(c_vec) cpdef get_basics(self): r""" @@ -1611,7 +1637,7 @@ cdef class CoinBackend(GenericBackend): indices = [c_indices[j] for j in range(m)] return indices finally: - sage_free(c_indices) + sig_free(c_indices) cpdef get_row_price(self): r""" @@ -1685,4 +1711,4 @@ cdef class CoinBackend(GenericBackend): raise MIPSolverException('CBC : Signal sent, getReducedCost() fails') else: cost = [c_cost[i] for i in range(n)] - return cost \ No newline at end of file + return cost diff --git a/src/sage/numerical/backends/cplex_backend.pxd b/src/sage/numerical/backends/cplex_backend.pxd index 27e720fcdaa..57c81bbc7d1 100644 --- a/src/sage/numerical/backends/cplex_backend.pxd +++ b/src/sage/numerical/backends/cplex_backend.pxd @@ -19,7 +19,7 @@ cdef class CPLEXBackend(GenericBackend): cdef c_cpxlp * lp cdef current_sol cdef str _logfilename - cpdef CPLEXBackend copy(self) + cpdef __copy__(self) cdef extern from "cplex.h": diff --git a/src/sage/numerical/backends/cplex_backend.pyx b/src/sage/numerical/backends/cplex_backend.pyx index 0abf7a44880..f4b633d7fe0 100644 --- a/src/sage/numerical/backends/cplex_backend.pyx +++ b/src/sage/numerical/backends/cplex_backend.pyx @@ -14,12 +14,24 @@ AUTHORS: # http://www.gnu.org/licenses/ ############################################################################## -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.numerical.mip import MIPSolverException cdef class CPLEXBackend(GenericBackend): + """ + MIP Backend that uses the CPLEX solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CPLEX") # optional - CPLEX + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - CPLEX + + """ + def __cinit__(self, maximization = True): """ Constructor @@ -170,8 +182,21 @@ cdef class CPLEXBackend(GenericBackend): 4 sage: p.ncols() # optional - CPLEX 5 - sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - CPLEX + sage: p.add_variables(2, lower_bound=-2.0, integer=True, obj=42.0, names=['a','b']) # optional - CPLEX 6 + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8, optional - CPLEX + (-2.0, None) + sage: p.is_variable_integer(5) # optional - CPLEX + True + sage: p.col_name(5) # optional - CPLEX + 'a' + sage: p.objective_coefficient(5) # tol 1e-8, optional - CPLEX + 42.0 """ cdef char * c_name cdef double c_coeff = obj @@ -338,11 +363,11 @@ cdef class CPLEXBackend(GenericBackend): cdef char * n if name == NULL: - n = sage_malloc(500*sizeof(char)) + n = sig_malloc(500*sizeof(char)) status = CPXgetprobname(self.env, self.lp, n, 500, &zero) check(status) s = str(n) - sage_free(n) + sig_free(n) return s @@ -387,8 +412,8 @@ cdef class CPLEXBackend(GenericBackend): cdef int status cdef int n = self.ncols() - cdef double * c_coeff = sage_malloc(n * sizeof(double)) - cdef int * c_indices = sage_malloc(n * sizeof(int)) + cdef double * c_coeff = sig_malloc(n * sizeof(double)) + cdef int * c_indices = sig_malloc(n * sizeof(int)) for i,v in enumerate(coeff): c_coeff[i] = v @@ -397,8 +422,8 @@ cdef class CPLEXBackend(GenericBackend): status = CPXchgobj(self.env, self.lp, n, c_indices, c_coeff) check(status) - sage_free(c_coeff) - sage_free(c_indices) + sig_free(c_coeff) + sig_free(c_indices) self.obj_constant_term = d @@ -487,11 +512,11 @@ cdef class CPLEXBackend(GenericBackend): raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.") cdef int status - cdef char * sense = sage_malloc(number * sizeof(char)) - cdef double * bound = sage_malloc(number * sizeof(double)) + cdef char * sense = sig_malloc(number * sizeof(char)) + cdef double * bound = sig_malloc(number * sizeof(double)) cdef double * rng = NULL cdef int i - cdef char ** c_names = sage_malloc(number * sizeof(char *)) + cdef char ** c_names = sig_malloc(number * sizeof(char *)) if upper_bound == lower_bound: sense[0] = 'E' @@ -501,7 +526,7 @@ cdef class CPLEXBackend(GenericBackend): if upper_bound < lower_bound: raise ValueError("The upper bound must be at least equal to the lower bound !") - rng = sage_malloc(number * sizeof(double)) + rng = sig_malloc(number * sizeof(double)) sense[0] = 'R' bound[0] = lower_bound @@ -528,9 +553,9 @@ cdef class CPLEXBackend(GenericBackend): status = CPXnewrows(self.env, self.lp, number, bound, sense, rng, c_names if names else NULL) - sage_free(sense) - sage_free(bound) - sage_free(c_names) + sig_free(sense) + sig_free(bound) + sig_free(c_names) check(status) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name = None): @@ -583,9 +608,9 @@ cdef class CPLEXBackend(GenericBackend): cdef double rng cdef double c - c_coeff = sage_malloc(n * sizeof(double)) - c_indices = sage_malloc(n * sizeof(int)) - c_row = sage_malloc(n * sizeof(int)) + c_coeff = sig_malloc(n * sizeof(double)) + c_indices = sig_malloc(n * sizeof(int)) + c_row = sig_malloc(n * sizeof(int)) for i, (j, c) in enumerate(coefficients): c_coeff[i] = c @@ -625,9 +650,9 @@ cdef class CPLEXBackend(GenericBackend): check(status) # Free memory - sage_free(c_coeff) - sage_free(c_indices) - sage_free(c_row) + sig_free(c_coeff) + sig_free(c_indices) + sig_free(c_row) cpdef row(self, int index): r""" @@ -663,8 +688,8 @@ cdef class CPLEXBackend(GenericBackend): cdef list indices = [] cdef list values = [] - cdef double * c_coeff = sage_malloc((self.ncols()+10) * sizeof(double)) - cdef int * c_indices = sage_malloc((self.ncols()+10) * sizeof(int)) + cdef double * c_coeff = sig_malloc((self.ncols()+10) * sizeof(double)) + cdef int * c_indices = sig_malloc((self.ncols()+10) * sizeof(int)) status = CPXgetrows(self.env, self.lp, &n, &zero, c_indices, c_coeff, self.ncols()+3, &zero, index, index) @@ -674,8 +699,8 @@ cdef class CPLEXBackend(GenericBackend): indices.append(c_indices[i]) values.append(c_coeff[i]) - sage_free(c_coeff) - sage_free(c_indices) + sig_free(c_coeff) + sig_free(c_indices) return (indices, values) @@ -812,9 +837,9 @@ cdef class CPLEXBackend(GenericBackend): check(status) - cdef double * c_coeff = sage_malloc(n * sizeof(double)) - cdef int * c_indices = sage_malloc(n * sizeof(int)) - cdef int * c_col = sage_malloc(n * sizeof(int)) + cdef double * c_coeff = sig_malloc(n * sizeof(double)) + cdef int * c_indices = sig_malloc(n * sizeof(int)) + cdef int * c_col = sig_malloc(n * sizeof(int)) for 0<= i < n: c_coeff[i] = coeffs[i] @@ -825,9 +850,9 @@ cdef class CPLEXBackend(GenericBackend): status = CPXchgcoeflist(self.env, self.lp, n, c_indices, c_col, c_coeff) check(status) - sage_free(c_coeff) - sage_free(c_indices) - sage_free(c_col) + sig_free(c_coeff) + sig_free(c_indices) + sig_free(c_col) cpdef int solve(self) except -1: r""" @@ -861,7 +886,7 @@ cdef class CPLEXBackend(GenericBackend): sage: p.solve() # optional - CPLEX Traceback (most recent call last): ... - MIPSolverException: 'CPLEX: The primal has no feasible solution' + MIPSolverException: CPLEX: The primal has no feasible solution """ cdef int status cdef int ptype @@ -1101,15 +1126,15 @@ cdef class CPLEXBackend(GenericBackend): cdef int zero cdef char * n - n = sage_malloc(500*sizeof(char)) + n = sig_malloc(500*sizeof(char)) status = CPXgetrowname(self.env, self.lp, &n, n, 500, &zero, index, index) if status == 1219: - sage_free(n) + sig_free(n) return "" check(status) s = str(n) - sage_free(n) + sig_free(n) return s @@ -1135,15 +1160,15 @@ cdef class CPLEXBackend(GenericBackend): cdef char * n cdef int zero - n = sage_malloc(500*sizeof(char)) + n = sig_malloc(500*sizeof(char)) status = CPXgetcolname(self.env, self.lp, &n, n, 500, &zero, index, index) if status == 1219: - sage_free(n) + sig_free(n) return "" check(status) s = str(n) - sage_free(n) + sig_free(n) return s cpdef bint is_variable_binary(self, int index): @@ -1424,7 +1449,7 @@ cdef class CPLEXBackend(GenericBackend): status = CPXwriteprob(self.env, self.lp, filename, ext) check(status) - cpdef CPLEXBackend copy(self): + cpdef __copy__(self): r""" Returns a copy of self. @@ -1438,7 +1463,7 @@ cdef class CPLEXBackend(GenericBackend): sage: copy(p).solve() # optional - CPLEX 6.0 """ - cdef CPLEXBackend p = CPLEXBackend() + cdef CPLEXBackend p = type(self)() p.lp = CPXcloneprob(p.env, self.lp, &status) check(status) @@ -1564,10 +1589,10 @@ cdef class CPLEXBackend(GenericBackend): check(CPXgetdblparam(self.env, paramid, &doublev)) return doublev else: - strv = sage_malloc(500*sizeof(char)) + strv = sig_malloc(500*sizeof(char)) status = CPXgetstrparam(self.env, paramid, strv) s = str(strv) - sage_free(strv) + sig_free(strv) check(status) return s else: diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index a23f32caf5c..aee42ddfcde 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -19,8 +19,31 @@ AUTHORS: from sage.numerical.mip import MIPSolverException from cvxopt import solvers +from copy import copy cdef class CVXOPTBackend(GenericBackend): + """ + MIP Backend that uses the CVXOPT solver. + + There is no support for integer variables. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(solver="CVXOPT") + + TESTS: + + :trac:`20332`:: + + sage: p + Mixed Integer Program ( maximization, 0 variables, 0 constraints ) + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="CVXOPT") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + cdef list objective_function #c_matrix cdef list G_matrix cdef str prob_name @@ -35,7 +58,6 @@ cdef class CVXOPTBackend(GenericBackend): cdef list col_name_var cdef dict answer cdef dict param - cdef str name def __cinit__(self, maximization = True): """ @@ -50,7 +72,7 @@ cdef class CVXOPTBackend(GenericBackend): self.objective_function = [] #c_matrix in the example for cvxopt self.G_matrix = [] - self.prob_name = None + self.prob_name = '' self.obj_constant_term = 0 self.is_maximize = 1 @@ -74,6 +96,43 @@ cdef class CVXOPTBackend(GenericBackend): else: self.set_sense(-1) + cpdef __copy__(self): + # Added a second inequality to this doctest, + # because otherwise CVXOPT complains: ValueError: Rank(A) < p or Rank([G; A]) < n + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "CVXOPT") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.add_constraint(b[2] <= 5) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6.0 + """ + cdef CVXOPTBackend cp = type(self)() + cp.objective_function = self.objective_function[:] + cp.G_matrix = [row[:] for row in self.G_matrix] + cp.prob_name = self.prob_name + cp.obj_constant_term = self.obj_constant_term + cp.is_maximize = self.is_maximize + + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + + cp.param = copy(self.param) + return cp cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, name=None) except -1: """ @@ -138,7 +197,7 @@ cdef class CVXOPTBackend(GenericBackend): ... RuntimeError: CVXOPT only supports continuous variables """ - if obj == None: + if obj is None: obj = 0.0 if binary or integer: raise RuntimeError("CVXOPT only supports continuous variables") @@ -150,7 +209,7 @@ cdef class CVXOPTBackend(GenericBackend): return len(self.objective_function) - 1 - cpdef int add_variables(self, int n, lower_bound=None, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: + cpdef int add_variables(self, int n, lower_bound=0.0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: """ Add ``n`` variables. @@ -187,11 +246,23 @@ cdef class CVXOPTBackend(GenericBackend): 4 sage: p.ncols() 5 - sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) + sage: p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) 6 + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8 + (-2.0, None) + sage: p.col_name(5) + 'a' + sage: p.objective_coefficient(5) # tol 1e-8 + 42.0 """ for i in range(n): - self.add_variable() + self.add_variable(lower_bound, upper_bound, binary, continuous, integer, obj, + None if names is None else names[i]) return len(self.objective_function) - 1; @@ -417,10 +488,17 @@ cdef class CVXOPTBackend(GenericBackend): ([], []) sage: p.row_bounds(4) (None, 2) + + TESTS: + + It does not add mysterious new variables:: + + sage: p.ncols() + 5 + """ for i in range(number): - self.add_linear_constraint(zip(range(self.ncols()+1),[0]*(self.ncols()+1)), - lower_bound, upper_bound, + self.add_linear_constraint([], lower_bound, upper_bound, name=None if names is None else names[i]) cpdef int solve(self) except -1: @@ -707,13 +785,15 @@ cdef class CVXOPTBackend(GenericBackend): sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "CVXOPT") + sage: p.problem_name() + '' sage: p.problem_name("There once was a french fry") sage: print p.problem_name() There once was a french fry """ if name == NULL: - return self.name - self.name = str(name) + return self.prob_name + self.prob_name = str(name) cpdef row(self, int i): @@ -1022,7 +1102,7 @@ cdef class CVXOPTBackend(GenericBackend): sage: p.solver_parameter("show_progress") True """ - if value == None: + if value is None: return self.param[name] else: self.param[name] = value diff --git a/src/sage/numerical/backends/generic_backend.pxd b/src/sage/numerical/backends/generic_backend.pxd index a24e51025ef..f7293ff8165 100644 --- a/src/sage/numerical/backends/generic_backend.pxd +++ b/src/sage/numerical/backends/generic_backend.pxd @@ -5,12 +5,17 @@ # http://www.gnu.org/licenses/ ############################################################################## -cdef class GenericBackend: +from sage.structure.sage_object cimport SageObject + +# We inherit from SageObject to make some testing infrastructure available. + +cdef class GenericBackend (SageObject): cpdef int add_variable(self, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, name=*) except -1 cpdef int add_variables(self, int, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, names=*) except -1 cpdef set_variable_type(self, int variable, int vtype) cpdef set_sense(self, int sense) cpdef objective_coefficient(self, int variable, coeff=*) + cpdef objective_constant_term(self, d=*) cpdef set_objective(self, list coeff, d=*) cpdef set_verbosity(self, int level) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=*) @@ -43,6 +48,8 @@ cdef class GenericBackend: cpdef solver_parameter(self, name, value=*) cpdef zero(self) cpdef base_ring(self) + cpdef __copy__(self) + cpdef copy(self) cpdef bint is_variable_basic(self, int index) cpdef bint is_variable_nonbasic_at_lower_bound(self, int index) cpdef bint is_slack_variable_basic(self, int index) @@ -50,4 +57,4 @@ cdef class GenericBackend: cdef object obj_constant_term -cpdef GenericBackend get_solver(constraint_generation = ?, solver = ?) +cpdef GenericBackend get_solver(constraint_generation = ?, solver = ?, base_ring = ?) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 1a4ce6f4d4b..9e92c265a81 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -29,6 +29,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** +from copy import copy + cdef class GenericBackend: cpdef base_ring(self): @@ -38,7 +40,7 @@ cdef class GenericBackend: cpdef zero(self): return self.base_ring()(0) - cpdef int add_variable(self, lower_bound=None, upper_bound=None, + cpdef int add_variable(self, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, name=None) except -1: """ @@ -92,7 +94,7 @@ cdef class GenericBackend: """ raise NotImplementedError() - cpdef int add_variables(self, int n, lower_bound=None, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: + cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: """ Add ``n`` variables. @@ -134,6 +136,30 @@ cdef class GenericBackend: """ raise NotImplementedError() + @classmethod + def _test_add_variables(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_variables() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + # Test from CVXOPT interface (part 1): + ncols_added = 5 + ncols_before = p.ncols() + add_variables_result = p.add_variables(ncols_added) + ncols_after = p.ncols() + tester.assertEqual(ncols_after, ncols_before+ncols_added, "Added the wrong number of columns") + cpdef set_variable_type(self, int variable, int vtype): """ Set the type of a variable @@ -155,7 +181,7 @@ cdef class GenericBackend: sage: p.ncols() # optional - Nonexistent_LP_solver 0 sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.set_variable_type(0,1) # optional - Nonexistent_LP_solver sage: p.is_variable_integer(0) # optional - Nonexistent_LP_solver True @@ -201,7 +227,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.objective_coefficient(0) # optional - Nonexistent_LP_solver 0.0 sage: p.objective_coefficient(0,2) # optional - Nonexistent_LP_solver @@ -210,6 +236,29 @@ cdef class GenericBackend: """ raise NotImplementedError() + cpdef objective_constant_term(self, d=None): + """ + Set or get the constant term in the objective function + + INPUT: + + - ``d`` (double) -- its coefficient. If `None` (default), return the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: p.objective_constant_term() # optional - Nonexistent_LP_solver + 0.0 + sage: p.objective_constant_term(42) # optional - Nonexistent_LP_solver + sage: p.objective_constant_term() # optional - Nonexistent_LP_solver + 42.0 + """ + if d is None: + return self.obj_constant_term + else: + self.obj_constant_term = d + cpdef set_objective(self, list coeff, d = 0.0): """ Set the objective function. @@ -226,7 +275,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variables(5) # optional - Nonexistent_LP_solver - 5 + 4 sage: p.set_objective([1, 1, 2, 1, 3]) # optional - Nonexistent_LP_solver sage: map(lambda x :p.objective_coefficient(x), range(5)) # optional - Nonexistent_LP_solver [1.0, 1.0, 2.0, 1.0, 3.0] @@ -270,10 +319,20 @@ cdef class GenericBackend: EXAMPLE:: - sage: from sage.numerical.backends.generic_backend import get_solver - sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver - sage: p.add_constraint(p[0] + p[1], max = 10) # optional - Nonexistent_LP_solver - sage: p.remove_constraint(0) # optional - Nonexistent_LP_solver + sage: p = MixedIntegerLinearProgram(solver="Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: v = p.new_variable(nonnegative=True) # optional - Nonexistent_LP_solver + sage: x,y = v[0], v[1] # optional - Nonexistent_LP_solver + sage: p.add_constraint(2*x + 3*y, max = 6) # optional - Nonexistent_LP_solver + sage: p.add_constraint(3*x + 2*y, max = 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(x + y + 7) # optional - Nonexistent_LP_solver + sage: p.set_integer(x); p.set_integer(y) # optional - Nonexistent_LP_solver + sage: p.solve() # optional - Nonexistent_LP_solver + 9.0 + sage: p.remove_constraint(0) # optional - Nonexistent_LP_solver + sage: p.solve() # optional - Nonexistent_LP_solver + 10.0 + sage: p.get_values([x,y]) # optional - Nonexistent_LP_solver + [0.0, 3.0] """ raise NotImplementedError() @@ -321,12 +380,18 @@ cdef class GenericBackend: EXAMPLE:: - sage: from sage.numerical.backends.generic_backend import GenericBackend - sage: solver = GenericBackend() - sage: solver.add_linear_constraint(zip(range(5), range(5)), 2.0, 2.0) - Traceback (most recent call last): - ... - NotImplementedError: add_linear_constraint + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: p.add_variables(5) # optional - Nonexistent_LP_solver + 4 + sage: p.add_linear_constraint( zip(range(5), range(5)), 2.0, 2.0) # optional - Nonexistent_LP_solver + sage: p.row(0) # optional - Nonexistent_LP_solver + ([0, 1, 2, 3, 4], [0.0, 1.0, 2.0, 3.0, 4.0]) + sage: p.row_bounds(0) # optional - Nonexistent_LP_solver + (2.0, 2.0) + sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') # optional - Nonexistent_LP_solver + sage: p.row_name(1) # optional - Nonexistent_LP_solver + 'foo' """ raise NotImplementedError('add_linear_constraint') @@ -361,15 +426,14 @@ cdef class GenericBackend: EXAMPLE:: + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) sage: upper = vector([5, 5]) sage: lower = vector([0, 0]) - sage: from sage.numerical.backends.generic_backend import GenericBackend - sage: solver = GenericBackend() - sage: solver.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') - Traceback (most recent call last): - ... - NotImplementedError: add_linear_constraint + sage: p.add_variables(2) # optional - Nonexistent_LP_solver + 1 + sage: p.add_linear_constraint_vector(2, coeffs, lower, upper, 'foo') # optional - Nonexistent_LP_solver """ for d in range(degree): coefficients_d = [] @@ -379,6 +443,31 @@ cdef class GenericBackend: upper_bound_d = None if upper_bound is None else upper_bound[d] self.add_linear_constraint(coefficients_d, lower_bound_d, upper_bound_d, name=name) + @classmethod + def _test_add_linear_constraint_vector(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraint_vector`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_linear_constraint_vector() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + from sage.modules.all import vector + # Ensure there are at least 2 variables + p.add_variables(2) + coeffs = ([0, vector([1, 2])], [1, vector([2, 3])]) + upper = vector([5, 5]) + p.add_linear_constraint_vector(2, coeffs, None, upper, 'foo') + # FIXME: Tests here. Careful what we expect regarding ranged constraints with some solvers. + cpdef add_col(self, list indices, list coeffs): """ Add a column. @@ -443,6 +532,36 @@ cdef class GenericBackend: """ raise NotImplementedError() + @classmethod + def _test_add_linear_constraints(cls, tester=None, **options): + """ + Run tests on the method :meth:`.add_linear_constraints`. + + TEST:: + + sage: from sage.numerical.backends.generic_backend import GenericBackend + sage: p = GenericBackend() + sage: p._test_add_linear_constraints() + Traceback (most recent call last): + ... + NotImplementedError + """ + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + nrows_before = p.nrows() + nrows_added = 5 + p.add_linear_constraints(nrows_added, None, 2) + nrows_after = p.nrows() + # Test correct number of rows + tester.assertEqual(nrows_after, nrows_before+nrows_added, "Added the wrong number of rows") + # Test contents of the new rows are correct (sparse zero) + for i in range(nrows_before, nrows_after): + tester.assertEqual(p.row(i), ([], [])) + tester.assertEqual(p.row_bounds(i), (None, 2.0)) + # FIXME: Not sure if we should test that no new variables were added. + # Perhaps some backend may need to introduce explicit slack variables? + cpdef int solve(self) except -1: """ Solve the problem. @@ -482,7 +601,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variables(2) # optional - Nonexistent_LP_solver - 2 + 1 sage: p.add_linear_constraint([(0,1), (1,2)], None, 3) # optional - Nonexistent_LP_solver sage: p.set_objective([2, 5]) # optional - Nonexistent_LP_solver sage: p.solve() # optional - Nonexistent_LP_solver @@ -580,7 +699,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variables(2) # optional - Nonexistent_LP_solver - 2 + 1 sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3) # optional - Nonexistent_LP_solver sage: p.set_objective([2, 5]) # optional - Nonexistent_LP_solver sage: p.solve() # optional - Nonexistent_LP_solver @@ -606,13 +725,18 @@ cdef class GenericBackend: sage: p.ncols() # optional - Nonexistent_LP_solver 0 sage: p.add_variables(2) # optional - Nonexistent_LP_solver - 2 + 1 sage: p.ncols() # optional - Nonexistent_LP_solver 2 """ raise NotImplementedError() + def _test_ncols_nonnegative(self, **options): + tester = self._tester(**options) + p = self + tester.assertGreaterEqual(self.ncols(), 0) + cpdef int nrows(self): """ Return the number of rows/constraints. @@ -660,7 +784,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.problem_name("There once was a french fry") # optional - Nonexistent_LP_solver - sage: print p.get_problem_name() # optional - Nonexistent_LP_solver + sage: print p.problem_name() # optional - Nonexistent_LP_solver There once was a french fry """ @@ -706,6 +830,61 @@ cdef class GenericBackend: """ raise NotImplementedError() + cpdef copy(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: copy(p).solve() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + + # Override this method in backends. + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: cp = copy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + raise NotImplementedError() + + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver + sage: b = p.new_variable() # optional - Nonexistent_LP_solver + sage: p.add_constraint(b[1] + b[2] <= 6) # optional - Nonexistent_LP_solver + sage: p.set_objective(b[1] + b[2]) # optional - Nonexistent_LP_solver + sage: cp = deepcopy(p.get_backend()) # optional - Nonexistent_LP_solver + sage: cp.solve() # optional - Nonexistent_LP_solver + 0 + sage: cp.get_objective_value() # optional - Nonexistent_LP_solver + 6.0 + """ + return self.__copy__() + cpdef row(self, int i): """ Return a row @@ -726,10 +905,10 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variables(5) # optional - Nonexistent_LP_solver - 5 + 4 sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) # optional - Nonexistent_LP_solver sage: p.row(0) # optional - Nonexistent_LP_solver - ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) + ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) ## FIXME: Why backwards? sage: p.row_bounds(0) # optional - Nonexistent_LP_solver (2.0, 2.0) """ @@ -754,10 +933,10 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variables(5) # optional - Nonexistent_LP_solver - 5 + 4 sage: p.add_linear_constraint(range(5), range(5), 2, 2) # optional - Nonexistent_LP_solver sage: p.row(0) # optional - Nonexistent_LP_solver - ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) + ([4, 3, 2, 1], [4.0, 3.0, 2.0, 1.0]) ## FIXME: Why backwards? sage: p.row_bounds(0) # optional - Nonexistent_LP_solver (2.0, 2.0) """ @@ -782,7 +961,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.col_bounds(0) # optional - Nonexistent_LP_solver (0.0, None) sage: p.variable_upper_bound(0, 5) # optional - Nonexistent_LP_solver @@ -806,7 +985,7 @@ cdef class GenericBackend: sage: p.ncols() # optional - Nonexistent_LP_solver 0 sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.set_variable_type(0,0) # optional - Nonexistent_LP_solver sage: p.is_variable_binary(0) # optional - Nonexistent_LP_solver True @@ -829,7 +1008,7 @@ cdef class GenericBackend: sage: p.ncols() # optional - Nonexistent_LP_solver 0 sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.set_variable_type(0,1) # optional - Nonexistent_LP_solver sage: p.is_variable_integer(0) # optional - Nonexistent_LP_solver True @@ -851,7 +1030,7 @@ cdef class GenericBackend: sage: p.ncols() # optional - Nonexistent_LP_solver 0 sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.is_variable_continuous(0) # optional - Nonexistent_LP_solver True sage: p.set_variable_type(0,1) # optional - Nonexistent_LP_solver @@ -873,7 +1052,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver - sage: p.add_linear_constraints(1, 2, None, name="Empty constraint 1") # optional - Nonexistent_LP_solver + sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) # optional - Nonexistent_LP_solver sage: p.row_name(0) # optional - Nonexistent_LP_solver 'Empty constraint 1' @@ -902,7 +1081,77 @@ cdef class GenericBackend: """ raise NotImplementedError() - cpdef variable_upper_bound(self, int index, value = None): + def _do_test_problem_data(self, tester, cp): + """ + TESTS: + + Test, with an actual working backend, that comparing a problem with itself works:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver='GLPK') + sage: tester = p._tester() + sage: p._do_test_problem_data(tester, p) + """ + tester.assertEqual(type(self), type(cp), + "Classes do not match") + def assert_equal_problem_data(method): + tester.assertEqual(getattr(self, method)(), getattr(cp, method)(), + "{} does not match".format(method)) + for method in ("ncols", "nrows", "objective_constant_term", "problem_name", "is_maximization"): + assert_equal_problem_data(method) + def assert_equal_col_data(method): + for i in range(self.ncols()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("objective_coefficient", "is_variable_binary", "is_variable_binary", "is_variable_integer", + "is_variable_continuous", "col_bounds", "col_name"): + # don't test variable_lower_bound, variable_upper_bound because we already test col_bounds. + # TODO: Add a test elsewhere to ensure that variable_lower_bound, variable_upper_bound + # are consistent with col_bounds. + assert_equal_col_data(method) + def assert_equal_row_data(method): + for i in range(self.nrows()): + tester.assertEqual(getattr(self, method)(i), getattr(cp, method)(i), + "{}({}) does not match".format(method, i)) + for method in ("row_bounds", "row", "row_name"): + assert_equal_row_data(method) + + def _test_copy(self, **options): + """ + Test whether the backend can be copied + and at least the problem data of the copy is equal to that of the original. + Does not test whether solutions or solver parameters are copied. + """ + tester = self._tester(**options) + cp = copy(self) + self._do_test_problem_data(tester, cp) + + def _test_copy_does_not_share_data(self, **options): + """ + Test whether copy makes an independent copy of the backend. + """ + tester = self._tester(**options) + cp = copy(self) + cpcp = copy(cp) + del cp + self._do_test_problem_data(tester, cpcp) + + # TODO: We should have a more systematic way of generating MIPs for testing. + @classmethod + def _test_copy_some_mips(cls, tester=None, **options): + p = cls() # fresh instance of the backend + if tester is None: + tester = p._tester(**options) + # From doctest of GenericBackend.solve: + p.add_linear_constraints(5, 0, None) + # p.add_col(range(5), range(5)) -- bad test because COIN sparsifies the 0s away on copy + p.add_col(range(5), range(1, 6)) + # From doctest of GenericBackend.problem_name: + p.problem_name("There once was a french fry") + p._test_copy(**options) + p._test_copy_does_not_share_data(**options) + + cpdef variable_upper_bound(self, int index, value = False): """ Return or define the upper bound on a variable @@ -919,7 +1168,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.col_bounds(0) # optional - Nonexistent_LP_solver (0.0, None) sage: p.variable_upper_bound(0, 5) # optional - Nonexistent_LP_solver @@ -928,7 +1177,7 @@ cdef class GenericBackend: """ raise NotImplementedError() - cpdef variable_lower_bound(self, int index, value = None): + cpdef variable_lower_bound(self, int index, value = False): """ Return or define the lower bound on a variable @@ -945,7 +1194,7 @@ cdef class GenericBackend: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "Nonexistent_LP_solver") # optional - Nonexistent_LP_solver sage: p.add_variable() # optional - Nonexistent_LP_solver - 1 + 0 sage: p.col_bounds(0) # optional - Nonexistent_LP_solver (0.0, None) sage: p.variable_lower_bound(0, 5) # optional - Nonexistent_LP_solver @@ -1113,6 +1362,9 @@ def default_mip_solver(solver = None): - GLPK (``solver="GLPK"``). See the `GLPK `_ web site. + - GLPK's implementation of an exact rational simplex + method (``solver="GLPK/exact"``). + - COIN Branch and Cut (``solver="Coin"``). See the `COIN-OR `_ web site. @@ -1122,14 +1374,20 @@ def default_mip_solver(solver = None): - CVXOPT (``solver="CVXOPT"``). See the `CVXOPT `_ web site. - - PPL (``solver="PPL"``). See the `PPL - `_ web site. - - Gurobi (``solver="Gurobi"``). See the `Gurobi `_ web site. + - PPL (``solver="PPL"``). See the `PPL + `_ web site. This solver is + an exact rational solver. + + - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical + implementation of the revised simplex method in Sage. It works over + any exact ordered field, the default is ``QQ``. + ``solver`` should then be equal to one of ``"GLPK"``, - ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``, ``"Gurobi"`` or ``"PPL"`` . + ``"Coin"``, ``"CPLEX"``, ``"CVXOPT"``, ``"Gurobi"``, ``"PPL"`, or + ``"InteractiveLP"``, - If ``solver=None`` (default), the current default solver's name is returned. @@ -1157,7 +1415,7 @@ def default_mip_solver(solver = None): sage: default_mip_solver("Yeahhhhhhhhhhh") Traceback (most recent call last): ... - ValueError: 'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'Gurobi', 'CVXOPT', 'PPL' or None. + ValueError: 'solver' should be set to ... sage: default_mip_solver(former_solver) """ global default_solver @@ -1212,23 +1470,29 @@ def default_mip_solver(solver = None): except ImportError: raise ValueError("Gurobi is not available. Please refer to the documentation to install it.") - elif solver == "Glpk": + elif solver == "Glpk" or solver == "Glpk/exact": + default_solver = solver + + elif solver == "Interactivelp": default_solver = solver else: - raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'Gurobi', 'CVXOPT', 'PPL' or None.") + raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', or None.") -cpdef GenericBackend get_solver(constraint_generation = False, solver = None): +cpdef GenericBackend get_solver(constraint_generation = False, solver = None, base_ring = None): """ Return a solver according to the given preferences INPUT: - - ``solver`` -- 6 solvers should be available through this class: + - ``solver`` -- 7 solvers should be available through this class: - GLPK (``solver="GLPK"``). See the `GLPK `_ web site. + - GLPK's implementation of an exact rational simplex + method (``solver="GLPK/exact"``). + - COIN Branch and Cut (``solver="Coin"``). See the `COIN-OR `_ web site. @@ -1242,11 +1506,27 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None): `_ web site. - PPL (``solver="PPL"``). See the `PPL - `_ web site. + `_ web site. This solver is + an exact rational solver. + + - ``InteractiveLPProblem`` (``solver="InteractiveLP"``). A didactical + implementation of the revised simplex method in Sage. It works over + any exact ordered field, the default is ``QQ``. + + ``solver`` should then be equal to one of the above strings, + or ``None`` (default), in which case the default solver is used + (see ``default_mip_solver`` method). + + ``solver`` can also be a callable, in which case it is called, + and its result is returned. + + - ``base_ring`` -- If not ``None``, request a solver that works over this + (ordered) field. If ``base_ring`` is not a field, its fraction field + is used. - ``solver`` should then be equal to one of ``"GLPK"``, ``"Coin"``, - ``"CPLEX"``, ``"CVXOPT"``,``"Gurobi"``, ``"PPL"``, or ``None``. If ``solver=None`` (default), - the default solver is used (see ``default_mip_solver`` method. + For example, is ``base_ring=ZZ`` is provided, the solver will work over + the rational numbers. This is unrelated to whether variables are + constrained to be integers or not. - ``constraint_generation`` -- Only used when ``solver=None``. @@ -1261,19 +1541,78 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None): - :func:`default_mip_solver` -- Returns/Sets the default MIP solver. - EXAMPLE:: + EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver() + sage: p = get_solver(base_ring=RDF) + sage: p.base_ring() + Real Double Field + sage: p = get_solver(base_ring=QQ); p + <...sage.numerical.backends.ppl_backend.PPLBackend...> + sage: p = get_solver(base_ring=ZZ); p + <...sage.numerical.backends.ppl_backend.PPLBackend...> + sage: p.base_ring() + Rational Field + sage: p = get_solver(base_ring=AA); p + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> + sage: p.base_ring() + Algebraic Real Field + sage: d = polytopes.dodecahedron() + sage: p = get_solver(base_ring=d.base_ring()); p + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> + sage: p.base_ring() + Number Field in sqrt5 with defining polynomial x^2 - 5 + sage: p = get_solver(solver='InteractiveLP', base_ring=QQ); p + <...sage.numerical.backends.interactivelp_backend.InteractiveLPBackend...> + sage: p.base_ring() + Rational Field + + Passing a callable as the 'solver':: + + sage: from sage.numerical.backends.glpk_backend import GLPKBackend + sage: p = get_solver(GLPKBackend); p + <...sage.numerical.backends.glpk_backend.GLPKBackend...> + + Passing a callable that customizes a backend:: + + sage: def glpk_exact_solver(): + ....: from sage.numerical.backends.generic_backend import get_solver + ....: b = get_solver(solver="GLPK") + ....: b.solver_parameter("simplex_or_intopt", "exact_simplex_only") + ....: return b + sage: delsarte_bound_additive_hamming_space(11,3,4,solver=glpk_exact_solver) # long time + glp_exact... + ... + OPTIMAL SOLUTION FOUND + 8 + """ if solver is None: + solver = default_mip_solver() + if base_ring is not None: + base_ring = base_ring.fraction_field() + from sage.rings.all import QQ, RDF + if base_ring is QQ: + solver = "Ppl" + elif solver in ["Interactivelp", "Ppl"] and not base_ring.is_exact(): + solver = "Glpk" + elif base_ring is not RDF: + solver = "Interactivelp" + # We do not want to use Coin for constraint_generation. It just does not # work if solver == "Coin" and constraint_generation: solver = "Glpk" + elif callable(solver): + kwds = {} + if base_ring is not None: + kwds['base_ring']=base_ring + return solver(**kwds) + else: solver = solver.capitalize() @@ -1285,6 +1624,10 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None): from sage.numerical.backends.glpk_backend import GLPKBackend return GLPKBackend() + elif solver == "Glpk/exact": + from sage.numerical.backends.glpk_exact_backend import GLPKExactBackend + return GLPKExactBackend() + elif solver == "Cplex": from sage.numerical.backends.cplex_backend import CPLEXBackend return CPLEXBackend() @@ -1299,7 +1642,11 @@ cpdef GenericBackend get_solver(constraint_generation = False, solver = None): elif solver == "Ppl": from sage.numerical.backends.ppl_backend import PPLBackend - return PPLBackend() + return PPLBackend(base_ring=base_ring) + + elif solver == "Interactivelp": + from sage.numerical.backends.interactivelp_backend import InteractiveLPBackend + return InteractiveLPBackend(base_ring=base_ring) else: - raise ValueError("'solver' should be set to 'GLPK', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL' or None (in which case the default one is used).") + raise ValueError("'solver' should be set to 'GLPK', 'GLPK/exact', 'Coin', 'CPLEX', 'CVXOPT', 'Gurobi', 'PPL', 'InteractiveLP', None (in which case the default one is used), or a callable.") diff --git a/src/sage/numerical/backends/glpk_backend.pxd b/src/sage/numerical/backends/glpk_backend.pxd index 2ccbd67dc81..f09f2909741 100644 --- a/src/sage/numerical/backends/glpk_backend.pxd +++ b/src/sage/numerical/backends/glpk_backend.pxd @@ -26,7 +26,7 @@ cdef class GLPKBackend(GenericBackend): cdef glp_smcp * smcp cdef int simplex_or_intopt cdef search_tree_data_t search_tree_data - cpdef GLPKBackend copy(self) + cpdef __copy__(self) cpdef int print_ranges(self, char * filename = *) except -1 cpdef double get_row_dual(self, int variable) cpdef double get_col_dual(self, int variable) diff --git a/src/sage/numerical/backends/glpk_backend.pyx b/src/sage/numerical/backends/glpk_backend.pyx index 702fd89dca3..bc1b894526e 100644 --- a/src/sage/numerical/backends/glpk_backend.pyx +++ b/src/sage/numerical/backends/glpk_backend.pyx @@ -21,17 +21,30 @@ AUTHORS: #***************************************************************************** +from sage.ext.memory_allocator cimport MemoryAllocator from sage.numerical.mip import MIPSolverException +from sage.libs.glpk.error import GLPKError from sage.libs.glpk.constants cimport * from sage.libs.glpk.lp cimport * from libc.float cimport DBL_MAX from libc.limits cimport INT_MAX -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" cdef class GLPKBackend(GenericBackend): + """ + MIP Backend that uses the GLPK solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + def __cinit__(self, maximization = True): """ Constructor @@ -42,9 +55,9 @@ cdef class GLPKBackend(GenericBackend): """ self.lp = glp_create_prob() self.simplex_or_intopt = glp_intopt_only - self.smcp = sage_malloc(sizeof(glp_smcp)) + self.smcp = sig_malloc(sizeof(glp_smcp)) glp_init_smcp(self.smcp) - self.iocp = sage_malloc(sizeof(glp_iocp)) + self.iocp = sig_malloc(sizeof(glp_iocp)) glp_init_iocp(self.iocp) self.iocp.cb_func = glp_callback # callback function @@ -102,7 +115,7 @@ cdef class GLPKBackend(GenericBackend): Traceback (most recent call last): ... ValueError: ... - sage: p.add_variable(name='x',obj=1.0) + sage: p.add_variable(name='x', obj=1.0) 3 sage: p.col_name(3) 'x' @@ -174,8 +187,21 @@ cdef class GLPKBackend(GenericBackend): 4 sage: p.ncols() 5 - sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) + sage: p.add_variables(2, lower_bound=-2.0, integer=True, obj=42.0, names=['a','b']) 6 + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8 + (-2.0, None) + sage: p.is_variable_integer(5) + True + sage: p.col_name(5) + 'a' + sage: p.objective_coefficient(5) + 42.0 """ cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer)) if vtype == 0: @@ -204,7 +230,7 @@ cdef class GLPKBackend(GenericBackend): self.objective_coefficient(n_var - i - 1, obj) if names is not None: - glp_set_col_name(self.lp, n_var, names[number - i - 1]) + glp_set_col_name(self.lp, n_var - i, names[number - i - 1]) return n_var - 1 @@ -462,20 +488,20 @@ cdef class GLPKBackend(GenericBackend): """ cdef int i, c cdef int m = len(constraints) - cdef int * rows = sage_malloc((m + 1) * sizeof(int *)) + cdef int * rows = sig_malloc((m + 1) * sizeof(int *)) cdef int nrows = glp_get_num_rows(self.lp) for i in xrange(m): c = constraints[i] if c < 0 or c >= nrows: - sage_free(rows) + sig_free(rows) raise ValueError("The constraint's index i must satisfy 0 <= i < number_of_constraints") rows[i+1] = c + 1 glp_del_rows(self.lp, m, rows) - sage_free(rows) + sig_free(rows) glp_std_basis(self.lp) cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): @@ -494,7 +520,7 @@ cdef class GLPKBackend(GenericBackend): - ``name`` - an optional name for this row (default: ``None``) - EXAMPLE:: + EXAMPLES:: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "GLPK") @@ -508,6 +534,18 @@ cdef class GLPKBackend(GenericBackend): sage: p.add_linear_constraint( zip(range(5), range(5)), 1.0, 1.0, name='foo') sage: p.row_name(1) 'foo' + + TESTS: + + This used to crash Sage, but was fixed in :trac:`19525`:: + + sage: p = MixedIntegerLinearProgram(solver="glpk") + sage: q = MixedIntegerLinearProgram(solver="glpk") + sage: q.add_constraint(p.new_variable()[0] <= 1) + Traceback (most recent call last): + ... + GLPKError: glp_set_mat_row: i = 1; len = 1; invalid row length + Error detected in file glpapi01.c at line ... """ if lower_bound is None and upper_bound is None: raise ValueError("At least one of 'upper_bound' or 'lower_bound' must be set.") @@ -515,17 +553,22 @@ cdef class GLPKBackend(GenericBackend): glp_add_rows(self.lp, 1) cdef int n = glp_get_num_rows(self.lp) + cdef MemoryAllocator mem = MemoryAllocator() cdef int * row_i cdef double * row_values - row_i = sage_malloc((len(coefficients)+1) * sizeof(int)) - row_values = sage_malloc((len(coefficients)+1) * sizeof(double)) + row_i = mem.allocarray(len(coefficients)+1, sizeof(int)) + row_values = mem.allocarray(len(coefficients)+1, sizeof(double)) - for i,(c,v) in enumerate(coefficients): - row_i[i+1] = c+1 - row_values[i+1] = v + cdef Py_ssize_t i = 1 + for c,v in coefficients: + row_i[i] = c+1 + row_values[i] = v + i += 1 + sig_on() glp_set_mat_row(self.lp, n, len(coefficients), row_i, row_values) + sig_off() if upper_bound is not None and lower_bound is None: glp_set_row_bnds(self.lp, n, GLP_UP, upper_bound, upper_bound) @@ -540,9 +583,6 @@ cdef class GLPKBackend(GenericBackend): if name is not None: glp_set_row_name(self.lp, n, name) - sage_free(row_i) - sage_free(row_values) - cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names=None): """ Add ``'number`` linear constraints. @@ -618,8 +658,9 @@ cdef class GLPKBackend(GenericBackend): (2.0, 2.0) """ cdef int n = glp_get_num_cols(self.lp) - cdef int * c_indices = sage_malloc((n+1)*sizeof(int)) - cdef double * c_values = sage_malloc((n+1)*sizeof(double)) + cdef MemoryAllocator mem = MemoryAllocator() + cdef int * c_indices = mem.allocarray(n+1, sizeof(int)) + cdef double * c_values = mem.allocarray(n+1, sizeof(double)) cdef list indices = [] cdef list values = [] cdef int i,j @@ -629,9 +670,6 @@ cdef class GLPKBackend(GenericBackend): indices.append(c_indices[j]-1) values.append(c_values[j]) - sage_free(c_indices) - sage_free(c_values) - return (indices, values) cpdef row_bounds(self, int index): @@ -750,8 +788,8 @@ cdef class GLPKBackend(GenericBackend): cdef int * col_i cdef double * col_values - col_i = sage_malloc((len(indices)+1) * sizeof(int)) - col_values = sage_malloc((len(indices)+1) * sizeof(double)) + col_i = sig_malloc((len(indices)+1) * sizeof(int)) + col_values = sig_malloc((len(indices)+1) * sizeof(double)) for i,v in enumerate(indices): col_i[i+1] = v+1 @@ -812,7 +850,7 @@ cdef class GLPKBackend(GenericBackend): Sage uses GLPK's ``glp_intopt`` to find solutions. This routine sometimes FAILS CATASTROPHICALLY - when given a system it cannot solve. (Ticket #12309.) + when given a system it cannot solve. (:trac:`12309`.) Here, "catastrophic" can mean either "infinite loop" or segmentation fault. Upstream considers this behavior "essentially innate" to their design, and suggests @@ -833,12 +871,12 @@ cdef class GLPKBackend(GenericBackend): sage: lp.solve() Traceback (most recent call last): ... - RuntimeError: GLPK : Signal sent, try preprocessing option + GLPKError: Assertion failed: ... sage: lp.solver_parameter("simplex_or_intopt", "simplex_then_intopt") sage: lp.solve() Traceback (most recent call last): ... - MIPSolverException: 'GLPK : Problem has no feasible solution' + MIPSolverException: GLPK: Problem has no feasible solution If we switch to "simplex_only", the integrality constraints are ignored, and we get an optimal solution to the continuous relaxation. @@ -927,23 +965,23 @@ cdef class GLPKBackend(GenericBackend): sage: lp.solve() Traceback (most recent call last): ... - MIPSolverException: 'GLPK : Problem has unbounded solution' + MIPSolverException: GLPK: Problem has unbounded solution sage: lp.set_objective(lp[1]) sage: lp.solver_parameter("primal_v_dual", "GLP_DUAL") sage: lp.solve() Traceback (most recent call last): ... - MIPSolverException: 'GLPK : Problem has unbounded solution' + MIPSolverException: GLPK: Problem has unbounded solution sage: lp.solver_parameter("simplex_or_intopt", "simplex_then_intopt") sage: lp.solve() Traceback (most recent call last): ... - MIPSolverException: 'GLPK : The LP (relaxation) problem has no dual feasible solution' + MIPSolverException: GLPK: The LP (relaxation) problem has no dual feasible solution sage: lp.solver_parameter("simplex_or_intopt", "intopt_only") sage: lp.solve() Traceback (most recent call last): ... - MIPSolverException: 'GLPK : The LP (relaxation) problem has no dual feasible solution' + MIPSolverException: GLPK: The LP (relaxation) problem has no dual feasible solution sage: lp.set_max(lp[1],5) sage: lp.solve() 5.0 @@ -984,13 +1022,13 @@ cdef class GLPKBackend(GenericBackend): else: solve_status = glp_simplex(self.lp, self.smcp) solution_status = glp_get_status(self.lp) - + if ((self.simplex_or_intopt == glp_intopt_only) or (self.simplex_or_intopt == glp_simplex_then_intopt) and (solution_status != GLP_UNDEF) and (solution_status != GLP_NOFEAS)): - sig_str('GLPK : Signal sent, try preprocessing option') + sig_on() solve_status = glp_intopt(self.lp, self.iocp) - sig_off() solution_status = glp_mip_status(self.lp) + sig_off() if solution_status == GLP_OPT: pass @@ -999,9 +1037,9 @@ cdef class GLPKBackend(GenericBackend): # no exception when time limit or iteration limit or mip gap tolerances or objective limits reached. pass elif solution_status == GLP_UNDEF: - raise MIPSolverException("GLPK : "+solve_status_msg.get(solve_status, "unknown error during call to GLPK : "+str(solve_status))) + raise MIPSolverException("GLPK: "+solve_status_msg.get(solve_status, "unknown error during call to GLPK : "+str(solve_status))) else: - raise MIPSolverException("GLPK : "+solution_status_msg.get(solution_status, "unknown error during call to GLPK : "+str(solution_status))) + raise MIPSolverException("GLPK: "+solution_status_msg.get(solution_status, "unknown error during call to GLPK : "+str(solution_status))) return 0 cpdef get_objective_value(self): @@ -1381,33 +1419,62 @@ cdef class GLPKBackend(GenericBackend): sage: P.set_max(x, 0) sage: P.get_max(x) 0.0 + + Check that :trac:`10232` is fixed:: + + sage: p = get_solver(solver="GLPK") + sage: p.variable_upper_bound(2) + Traceback (most recent call last): + ... + GLPKError: ... + sage: p.variable_upper_bound(3, 5) + Traceback (most recent call last): + ... + GLPKError: ... + + sage: p.add_variable() + 0 + sage: p.variable_upper_bound(0, 'hey!') + Traceback (most recent call last): + ... + TypeError: a float is required """ cdef double x cdef double min + cdef double dvalue if value is False: + sig_on() x = glp_get_col_ub(self.lp, index +1) + sig_off() if x == DBL_MAX: return None else: return x else: + sig_on() min = glp_get_col_lb(self.lp, index + 1) + sig_off() - if value is None and min == -DBL_MAX: - glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0, 0) - - elif value is None: - glp_set_col_bnds(self.lp, index + 1, GLP_LO, min, 0) - - elif min == -DBL_MAX: - glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0, value) + if value is None: + sig_on() + if min == -DBL_MAX: + glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0, 0) + else: + glp_set_col_bnds(self.lp, index + 1, GLP_LO, min, 0) + sig_off() + else: + dvalue = value - elif min == value: - glp_set_col_bnds(self.lp, index + 1, GLP_FX, value, value) + sig_on() + if min == -DBL_MAX: + glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0, dvalue) - else: - glp_set_col_bnds(self.lp, index + 1, GLP_DB, min, value) + elif min == dvalue: + glp_set_col_bnds(self.lp, index + 1, GLP_FX, dvalue, dvalue) + else: + glp_set_col_bnds(self.lp, index + 1, GLP_DB, min, dvalue) + sig_off() cpdef variable_lower_bound(self, int index, value = False): """ @@ -1443,33 +1510,62 @@ cdef class GLPKBackend(GenericBackend): sage: P.set_min(x, 0) sage: P.get_min(x) 0.0 + + Check that :trac:`10232` is fixed:: + + sage: p = get_solver(solver="GLPK") + sage: p.variable_lower_bound(2) + Traceback (most recent call last): + ... + GLPKError: ... + sage: p.variable_lower_bound(3, 5) + Traceback (most recent call last): + ... + GLPKError: ... + + sage: p.add_variable() + 0 + sage: p.variable_lower_bound(0, 'hey!') + Traceback (most recent call last): + ... + TypeError: a float is required """ cdef double x cdef double max + cdef double dvalue if value is False: + sig_on() x = glp_get_col_lb(self.lp, index +1) + sig_off() if x == -DBL_MAX: return None else: return x else: + sig_on() max = glp_get_col_ub(self.lp, index + 1) + sig_off() - if value is None and max == DBL_MAX: - glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0.0, 0.0) - - elif value is None: - glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0.0, max) - - elif max == DBL_MAX: - glp_set_col_bnds(self.lp, index + 1, GLP_LO, value, 0.0) - - elif max == value: - glp_set_col_bnds(self.lp, index + 1, GLP_FX, value, value) + if value is None: + sig_on() + if max == DBL_MAX: + glp_set_col_bnds(self.lp, index + 1, GLP_FR, 0.0, 0.0) + else: + glp_set_col_bnds(self.lp, index + 1, GLP_UP, 0.0, max) + sig_off() else: - glp_set_col_bnds(self.lp, index + 1, GLP_DB, value, max) + dvalue = value + + sig_on() + if max == DBL_MAX: + glp_set_col_bnds(self.lp, index + 1, GLP_LO, value, 0.0) + elif max == value: + glp_set_col_bnds(self.lp, index + 1, GLP_FX, value, value) + else: + glp_set_col_bnds(self.lp, index + 1, GLP_DB, value, max) + sig_off() cpdef write_lp(self, char * filename): """ @@ -1515,7 +1611,7 @@ cdef class GLPKBackend(GenericBackend): """ glp_write_mps(self.lp, modern, NULL, filename) - cpdef GLPKBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. @@ -1529,7 +1625,7 @@ cdef class GLPKBackend(GenericBackend): sage: copy(p).solve() 6.0 """ - cdef GLPKBackend p = GLPKBackend(maximization = (1 if self.is_maximization() else -1)) + cdef GLPKBackend p = type(self)(maximization = (1 if self.is_maximization() else -1)) p.simplex_or_intopt = self.simplex_or_intopt p.iocp.tm_lim = self.iocp.tm_lim glp_copy_prob(p.lp, self.lp, 1) @@ -2559,26 +2655,20 @@ cdef class GLPKBackend(GenericBackend): if k < 0 or k >= n + glp_get_num_rows(self.lp): raise ValueError("k = %s; Variable number out of range" % k) - cdef int * c_indices = sage_malloc((n+1)*sizeof(int)) - cdef double * c_values = sage_malloc((n+1)*sizeof(double)) - if c_indices == NULL or c_values == NULL: - sage_free(c_indices) - sage_free(c_values) - raise MemoryError + cdef MemoryAllocator mem = MemoryAllocator() + cdef int * c_indices = mem.allocarray(n+1, sizeof(int)) + cdef double * c_values = mem.allocarray(n+1, sizeof(double)) try: - sig_on() # To catch SIGABRT + sig_on() # to catch GLPKError i = glp_eval_tab_row(self.lp, k + 1, c_indices, c_values) sig_off() - except RuntimeError: # corresponds to SIGABRT + except GLPKError: raise MIPSolverException('GLPK: basis factorization does not exist; or variable must be basic') - else: - indices = [c_indices[j+1] - 1 for j in range(i)] - values = [c_values[j+1] for j in range(i)] - return (indices, values) - finally: - sage_free(c_indices) - sage_free(c_values) + + indices = [c_indices[j+1] - 1 for j in range(i)] + values = [c_values[j+1] for j in range(i)] + return (indices, values) cpdef eval_tab_col(self, int k): r""" @@ -2657,34 +2747,28 @@ cdef class GLPKBackend(GenericBackend): if k < 0 or k >= m + glp_get_num_cols(self.lp): raise ValueError("k = %s; Variable number out of range" % k) - cdef int * c_indices = sage_malloc((m+1)*sizeof(int)) - cdef double * c_values = sage_malloc((m+1)*sizeof(double)) - if c_indices == NULL or c_values == NULL: - sage_free(c_indices) - sage_free(c_values) - raise MemoryError + cdef MemoryAllocator mem = MemoryAllocator() + cdef int * c_indices = mem.allocarray(m+1, sizeof(int)) + cdef double * c_values = mem.allocarray(m+1, sizeof(double)) try: - sig_on() # To catch SIGABRT + sig_on() # To catch GLPKError i = glp_eval_tab_col(self.lp, k + 1, c_indices, c_values) sig_off() - except RuntimeError: # corresponds to SIGABRT + except GLPKError: raise MIPSolverException('GLPK: basis factorization does not exist; or variable must be non-basic') - else: - indices = [c_indices[j+1] - 1 for j in range(i)] - values = [c_values[j+1] for j in range(i)] - return (indices, values) - finally: - sage_free(c_indices) - sage_free(c_values) + + indices = [c_indices[j+1] - 1 for j in range(i)] + values = [c_values[j+1] for j in range(i)] + return (indices, values) def __dealloc__(self): """ Destructor """ glp_delete_prob(self.lp) - sage_free(self.iocp) - sage_free(self.smcp) + sig_free(self.iocp) + sig_free(self.smcp) cdef void glp_callback(glp_tree* tree, void* info): r""" diff --git a/src/sage/numerical/backends/glpk_exact_backend.pxd b/src/sage/numerical/backends/glpk_exact_backend.pxd new file mode 100644 index 00000000000..bdcfdab0fcb --- /dev/null +++ b/src/sage/numerical/backends/glpk_exact_backend.pxd @@ -0,0 +1,17 @@ +#***************************************************************************** +# Copyright (C) 2016 Matthias Koeppe +# +# 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 glpk_backend cimport GLPKBackend + +cdef class GLPKExactBackend(GLPKBackend): + + cpdef int add_variable(self, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, name=*) except -1 + cpdef int add_variables(self, int, lower_bound=*, upper_bound=*, binary=*, continuous=*, integer=*, obj=*, names=*) except -1 + cpdef set_variable_type(self, int variable, int vtype) diff --git a/src/sage/numerical/backends/glpk_exact_backend.pyx b/src/sage/numerical/backends/glpk_exact_backend.pyx new file mode 100644 index 00000000000..efe75ad6425 --- /dev/null +++ b/src/sage/numerical/backends/glpk_exact_backend.pyx @@ -0,0 +1,194 @@ + +""" +GLPK/exact Backend (simplex method in exact rational arithmetic) + +AUTHORS: + +- Matthias Koeppe (2016-03) +""" + +############################################################################## +# Copyright (C) 2016 Matthias Koeppe +# 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/ +############################################################################## + +cdef class GLPKExactBackend(GLPKBackend): + + """ + MIP Backend that runs the GLPK solver in exact rational simplex mode. + + The only access to data is via double-precision floats, however. It + reconstructs rationals from doubles and also provides results + as doubles. + + There is no support for integer variables. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK/exact") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + """ + + def __cinit__(self, maximization = True): + """ + Constructor + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(solver="GLPK/exact") + """ + # inherited __cinit__ is called automatically + import sage.numerical.backends.glpk_backend as glpk_backend + self.solver_parameter(glpk_backend.glp_simplex_or_intopt, glpk_backend.glp_exact_simplex_only) + + cpdef int add_variable(self, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, name=None) except -1: + """ + Add a variable. + + This amounts to adding a new column to the matrix. By default, + the variable is both nonnegative and real. + + In this backend, variables are always continuous (real). + If integer variables are requested via the parameters + ``binary`` and ``integer``, an error will be raised. + + INPUT: + + - ``lower_bound`` - the lower bound of the variable (default: 0) + + - ``upper_bound`` - the upper bound of the variable (default: ``None``) + + - ``binary`` - ``True`` if the variable is binary (default: ``False``). + + - ``continuous`` - ``True`` if the variable is continuous (default: ``True``). + + - ``integer`` - ``True`` if the variable is integer (default: ``False``). + + - ``obj`` - (optional) coefficient of this variable in the objective function (default: 0.0) + + - ``name`` - an optional name for the newly added variable (default: ``None``). + + OUTPUT: The index of the newly created variable + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "GLPK/exact") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.ncols() + 1 + sage: p.add_variable() + 1 + sage: p.add_variable(lower_bound=-2.0) + 2 + sage: p.add_variable(continuous=True) + 3 + sage: p.add_variable(name='x',obj=1.0) + 4 + sage: p.objective_coefficient(4) + 1.0 + + TESTS:: + + sage: p.add_variable(integer=True) + Traceback (most recent call last): + ... + ValueError: GLPK/exact only supports continuous variables + sage: p.add_variable(binary=True) + Traceback (most recent call last): + ... + ValueError: GLPK/exact only supports continuous variables + """ + if binary or integer: + raise ValueError("GLPK/exact only supports continuous variables") + return GLPKBackend.add_variable(self, lower_bound, upper_bound, binary, continuous, + integer, obj, name) + + cpdef int add_variables(self, int number, lower_bound=0.0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0.0, names=None) except -1: + """ + Add ``number`` variables. + + This amounts to adding new columns to the matrix. By default, + the variables are both nonnegative and real. + + In this backend, variables are always continuous (real). + If integer variables are requested via the parameters + ``binary`` and ``integer``, an error will be raised. + + INPUT: + + - ``n`` - the number of new variables (must be > 0) + + - ``lower_bound`` - the lower bound of the variable (default: 0) + + - ``upper_bound`` - the upper bound of the variable (default: ``None``) + + - ``binary`` - ``True`` if the variable is binary (default: ``False``). + + - ``continuous`` - ``True`` if the variable is binary (default: ``True``). + + - ``integer`` - ``True`` if the variable is binary (default: ``False``). + + - ``obj`` - (optional) coefficient of all variables in the objective function (default: 0.0) + + - ``names`` - optional list of names (default: ``None``) + + OUTPUT: The index of the variable created last. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "GLPK/exact") + sage: p.ncols() + 0 + sage: p.add_variables(5) + 4 + sage: p.ncols() + 5 + sage: p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) + 6 + """ + if binary or integer: + raise ValueError("GLPK/exact only supports continuous variables") + return GLPKBackend.add_variables(self, number, lower_bound, upper_bound, binary, continuous, + integer, obj, names) + + cpdef set_variable_type(self, int variable, int vtype): + """ + Set the type of a variable. + + In this backend, variables are always continuous (real). + If integer or binary variables are requested via the parameter + ``vtype``, an error will be raised. + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``vtype`` (integer) : + + * 1 Integer + * 0 Binary + * -1 Real + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "GLPK/exact") + sage: p.add_variables(5) + 4 + sage: p.set_variable_type(3, -1) + sage: p.set_variable_type(3, -2) + Traceback (most recent call last): + ... + ValueError: ... + """ + if vtype != -1: + raise ValueError("GLPK/exact only supports continuous variables") diff --git a/src/sage/numerical/backends/glpk_graph_backend.pyx b/src/sage/numerical/backends/glpk_graph_backend.pyx index aa9387b7cc7..bdfa213f147 100644 --- a/src/sage/numerical/backends/glpk_graph_backend.pyx +++ b/src/sage/numerical/backends/glpk_graph_backend.pyx @@ -73,7 +73,7 @@ from sage.libs.glpk.constants cimport * from sage.libs.glpk.graph cimport * from sage.numerical.mip import MIPSolverException -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" cdef class GLPKGraphBackend(object): """ @@ -390,7 +390,7 @@ cdef class GLPKGraphBackend(object): - ``vertex`` -- Name of the vertex - ``demand`` -- the numerical value representing demand of the vertex in - a mincost flow alorithm (it could be for instance `-1` to represent a + a mincost flow algorithm (it could be for instance `-1` to represent a sink, or `1` to represent a source and `0` for a neutral vertex). This can either be an ``int`` or ``float`` value. @@ -845,7 +845,7 @@ cdef class GLPKGraphBackend(object): if i < 0: raise RuntimeError("Vertex %s does not exist."%(vert)) - cdef int * num = sage_malloc(2 * sizeof(int)) + cdef int * num = sig_malloc(2 * sizeof(int)) num[1] = i + 1 cdef int ndel = 1 @@ -853,7 +853,7 @@ cdef class GLPKGraphBackend(object): raise MemoryError("Error allocating memory.") glp_del_vertices(self.graph, ndel, num) - sage_free(num) + sig_free(num) cpdef delete_vertices(self, list verts): r""" @@ -889,7 +889,7 @@ cdef class GLPKGraphBackend(object): i = verts_val.index(-1) raise RuntimeError("Vertex %s does not exist."%(verts[i])) - cdef int * num = sage_malloc((len(verts_val)+1) * sizeof(int)) + cdef int * num = sig_malloc((len(verts_val)+1) * sizeof(int)) if not num: raise MemoryError("Error allocating memory.") cdef int ndel = len(verts_val) @@ -899,7 +899,7 @@ cdef class GLPKGraphBackend(object): glp_del_vertices(self.graph, ndel, num) - sage_free(num) + sig_free(num) cpdef delete_edge(self, char* u, char* v, dict params=None): """ diff --git a/src/sage/numerical/backends/gurobi_backend.pxd b/src/sage/numerical/backends/gurobi_backend.pxd index 0b1025a4be3..560e3a61687 100644 --- a/src/sage/numerical/backends/gurobi_backend.pxd +++ b/src/sage/numerical/backends/gurobi_backend.pxd @@ -96,7 +96,7 @@ cdef class GurobiBackend(GenericBackend): cdef GRBenv * env cdef GRBenv * env_master cdef GRBmodel * model - cpdef GurobiBackend copy(self) + cpdef __copy__(self) cdef int num_vars diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index c88fed6bc5b..80f9dacfb49 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -26,11 +26,23 @@ Methods # http://www.gnu.org/licenses/ ############################################################################## -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.numerical.mip import MIPSolverException cdef class GurobiBackend(GenericBackend): + + """ + MIP Backend that uses the Gurobi solver. + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="Gurobi") # optional - Gurobi + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") # optional - Gurobi + """ + def __init__(self, maximization = True): """ Constructor @@ -196,8 +208,21 @@ cdef class GurobiBackend(GenericBackend): 4 sage: p.ncols() # optional - Gurobi 5 - sage: p.add_variables(2, lower_bound=-2.0, integer=True, names=['a','b']) # optional - Gurobi + sage: p.add_variables(2, lower_bound=-2.0, integer=True, obj=42.0, names=['a','b']) # optional - Gurobi 6 + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8, optional - Gurobi + (-2.0, None) + sage: p.is_variable_integer(5) # optional - Gurobi + True + sage: p.col_name(5) # optional - Gurobi + 'a' + sage: p.objective_coefficient(5) # tol 1e-8, optional - Gurobi + 42.0 """ cdef int i cdef int value @@ -496,8 +521,8 @@ cdef class GurobiBackend(GenericBackend): cdef int * row_i cdef double * row_values - row_i = sage_malloc((len(coefficients)) * sizeof(int)) - row_values = sage_malloc((len(coefficients)) * sizeof(double)) + row_i = sig_malloc((len(coefficients)) * sizeof(int)) + row_values = sig_malloc((len(coefficients)) * sizeof(double)) for i,(c,v) in enumerate(coefficients): @@ -526,8 +551,8 @@ cdef class GurobiBackend(GenericBackend): check(self.env,error) - sage_free(row_i) - sage_free(row_values) + sig_free(row_i) + sig_free(row_values) cpdef row(self, int index): r""" @@ -563,8 +588,8 @@ cdef class GurobiBackend(GenericBackend): error = GRBgetconstrs(self.model, length, NULL, NULL, NULL, index, 1 ) check(self.env,error) - cdef int * p_indices = sage_malloc(length[0] * sizeof(int)) - cdef double * p_values = sage_malloc(length[0] * sizeof(double)) + cdef int * p_indices = sig_malloc(length[0] * sizeof(int)) + cdef double * p_values = sig_malloc(length[0] * sizeof(double)) error = GRBgetconstrs(self.model, length, fake, p_indices, p_values, index, 1 ) check(self.env,error) @@ -577,8 +602,8 @@ cdef class GurobiBackend(GenericBackend): indices.append(p_indices[i]) values.append(p_values[i]) - sage_free(p_indices) - sage_free(p_values) + sig_free(p_indices) + sig_free(p_values) return indices, values @@ -686,7 +711,7 @@ cdef class GurobiBackend(GenericBackend): sage: p.solve() # optional - Gurobi Traceback (most recent call last): ... - MIPSolverException: 'Gurobi: The problem is infeasible' + MIPSolverException: Gurobi: The problem is infeasible """ cdef int error global mip_status @@ -1146,7 +1171,7 @@ cdef class GurobiBackend(GenericBackend): else: raise RuntimeError("This should not happen.") - cpdef GurobiBackend copy(self): + cpdef __copy__(self): """ Returns a copy of self. @@ -1160,7 +1185,7 @@ cdef class GurobiBackend(GenericBackend): sage: copy(p).solve() # optional - Gurobi 6.0 """ - cdef GurobiBackend p = GurobiBackend(maximization = self.is_maximization()) + cdef GurobiBackend p = type(self)(maximization = self.is_maximization()) p.model = GRBcopymodel(self.model) p.env = GRBgetenv(p.model) return p diff --git a/src/sage/numerical/backends/interactivelp_backend.pxd b/src/sage/numerical/backends/interactivelp_backend.pxd new file mode 100644 index 00000000000..7347e2ef7e7 --- /dev/null +++ b/src/sage/numerical/backends/interactivelp_backend.pxd @@ -0,0 +1,24 @@ +############################################################################## +# Copyright (C) 2010 Nathann Cohen +# Copyright (C) 2016 Matthias Koeppe +# 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.numerical.backends.generic_backend cimport GenericBackend + +cdef class InteractiveLPBackend(GenericBackend): + + cdef object lp + cdef object row_names + cdef object prob_name + + cdef object lp_std_form + cdef object std_form_transformation + cdef object final_dictionary + cdef int verbosity + + cpdef dictionary(self) + + cpdef interactive_lp_problem(self) diff --git a/src/sage/numerical/backends/interactivelp_backend.pyx b/src/sage/numerical/backends/interactivelp_backend.pyx new file mode 100644 index 00000000000..3ca3dcbcc38 --- /dev/null +++ b/src/sage/numerical/backends/interactivelp_backend.pyx @@ -0,0 +1,1325 @@ +r""" +InteractiveLP Backend + +AUTHORS: + +- Nathann Cohen (2010-10) : generic_backend template +- Matthias Koeppe (2016-03) : this backend + +""" + +#***************************************************************************** +# Copyright (C) 2010 Nathann Cohen +# Copyright (C) 2016 Matthias Koeppe +# +# 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.numerical.mip import MIPSolverException +from sage.numerical.interactive_simplex_method import InteractiveLPProblem, default_variable_name +from sage.modules.all import vector +from copy import copy + +cdef class InteractiveLPBackend: + """ + MIP Backend that works with :class:`InteractiveLPProblem`. + + This backend should be used only for linear programs over general fields, + or for educational purposes. For fast computations with floating point + arithmetic, use one of the numerical backends. For exact computations + with rational numbers, use backend 'PPL'. + + There is no support for integer variables. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + + TESTS: + + General backend testsuite:: + + sage: p = MixedIntegerLinearProgram(solver="InteractiveLP") + sage: TestSuite(p.get_backend()).run(skip="_test_pickling") + + """ + + def __cinit__(self, maximization = True, base_ring = None): + """ + Cython constructor + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + + This backend can work with irrational algebraic numbers:: + + sage: poly = polytopes.dodecahedron(base_ring=AA) + sage: lp, x = poly.to_linear_program(solver='InteractiveLP', return_variable=True) + sage: lp.set_objective(x[0] + x[1] + x[2]) + sage: lp.solve() + 2.291796067500631? + sage: lp.get_values(x[0], x[1], x[2]) + [0.763932022500211?, 0.763932022500211?, 0.763932022500211?] + sage: lp.set_objective(x[0] - x[1] - x[2]) + sage: lp.solve() + 2.291796067500631? + sage: lp.get_values(x[0], x[1], x[2]) + [0.763932022500211?, -0.763932022500211?, -0.763932022500211?] + """ + + if base_ring is None: + from sage.rings.all import QQ + base_ring = QQ + + self.lp = InteractiveLPProblem([], [], [], base_ring=base_ring) + self.set_verbosity(0) + + if maximization: + self.set_sense(+1) + else: + self.set_sense(-1) + + self.row_names = [] + + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "InteractiveLP") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6 + """ + cdef InteractiveLPBackend cp = type(self)(base_ring=self.base_ring()) + cp.lp = self.lp # it's considered immutable; so no need to copy. + cp.row_names = copy(self.row_names) + cp.prob_name = self.prob_name + return cp + + cpdef base_ring(self): + """ + Return the base ring. + + OUTPUT: + + A ring. The coefficients that the chosen solver supports. + + EXAMPLES:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.base_ring() + Rational Field + """ + return self.lp.base_ring() + + def _variable_type_from_bounds(self, lower_bound, upper_bound): + """ + Return a string designating a variable type in `InteractiveLPProblem`. + + INPUT: + + - ``lower_bound`` - the lower bound of the variable + + - ``upper_bound`` - the upper bound of the variable + + OUTPUT: + + - a string, one of "", "<=", ">=" + + The function raises an error if this pair of bounds cannot be + represented by an `InteractiveLPProblem` variable type. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p._variable_type_from_bounds(0, None) + '>=' + sage: p._variable_type_from_bounds(None, 0) + '<=' + sage: p._variable_type_from_bounds(None, None) + '' + sage: p._variable_type_from_bounds(None, 5) + Traceback (most recent call last): + ... + NotImplementedError: General variable bounds not supported + """ + if lower_bound is None: + if upper_bound is None: + return "" + elif upper_bound == 0: + return "<=" + else: + raise NotImplementedError("General variable bounds not supported") + elif lower_bound == 0: + if upper_bound is None: + return ">=" + else: + raise NotImplementedError("General variable bounds not supported") + else: + raise NotImplementedError("General variable bounds not supported") + + cpdef int add_variable(self, lower_bound=0, upper_bound=None, + binary=False, continuous=True, integer=False, + obj=None, name=None, coefficients=None) except -1: + ## coefficients is an extension in this backend, + ## and a proposed addition to the interface, to unify this with add_col. + """ + Add a variable. + + This amounts to adding a new column to the matrix. By default, + the variable is both nonnegative and real. + + In this backend, variables are always continuous (real). + If integer variables are requested via the parameters + ``binary`` and ``integer``, an error will be raised. + + INPUT: + + - ``lower_bound`` - the lower bound of the variable (default: 0) + + - ``upper_bound`` - the upper bound of the variable (default: ``None``) + + - ``binary`` - ``True`` if the variable is binary (default: ``False``). + + - ``continuous`` - ``True`` if the variable is binary (default: ``True``). + + - ``integer`` - ``True`` if the variable is binary (default: ``False``). + + - ``obj`` - (optional) coefficient of this variable in the objective function (default: 0) + + - ``name`` - an optional name for the newly added variable (default: ``None``). + + - ``coefficients`` -- (optional) an iterable of pairs ``(i, v)``. In each + pair, ``i`` is a variable index (integer) and ``v`` is a + value (element of :meth:`base_ring`). + + OUTPUT: The index of the newly created variable + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.ncols() + 1 + sage: p.add_variable(continuous=True, integer=True) + Traceback (most recent call last): + ... + ValueError: ... + sage: p.add_variable(name='x',obj=1) + 1 + sage: p.col_name(1) + 'x' + sage: p.objective_coefficient(1) + 1 + """ + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + cdef int vtype = int(binary) + int(continuous) + int(integer) + if vtype == 0: + continuous = True + elif vtype != 1: + raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.") + if not continuous: + raise NotImplementedError("Integer variables are not supported") + variable_types = variable_types + (self._variable_type_from_bounds(lower_bound, upper_bound),) + col = vector(ring, self.nrows()) + if coefficients is not None: + for (i, v) in coefficients: + col[i] = v + A = A.augment(col) + if obj is None: + obj = 0 + c = tuple(c) + (obj,) + if name is None: + var_names = default_variable_name("primal decision") + name = "{}{:d}".format(var_names, self.ncols() + 1) + x = tuple(x) + (name,) + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + return self.ncols() - 1 + + cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=None, names=None) except -1: + """ + Add ``n`` variables. + + This amounts to adding new columns to the matrix. By default, + the variables are both nonnegative and real. + + In this backend, variables are always continuous (real). + If integer variables are requested via the parameters + ``binary`` and ``integer``, an error will be raised. + + INPUT: + + - ``n`` - the number of new variables (must be > 0) + + - ``lower_bound`` - the lower bound of the variable (default: 0) + + - ``upper_bound`` - the upper bound of the variable (default: ``None``) + + - ``binary`` - ``True`` if the variable is binary (default: ``False``). + + - ``continuous`` - ``True`` if the variable is binary (default: ``True``). + + - ``integer`` - ``True`` if the variable is binary (default: ``False``). + + - ``obj`` - (optional) coefficient of all variables in the objective function (default: 0) + + - ``names`` - optional list of names (default: ``None``) + + OUTPUT: The index of the variable created last. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variables(5) + 4 + sage: p.ncols() + 5 + sage: p.add_variables(2, names=['a','b']) + 6 + """ + for i in range(n): + self.add_variable(lower_bound, upper_bound, binary, continuous, integer, obj, + None if names is None else names[i]) + return self.ncols() - 1 + + cpdef set_variable_type(self, int variable, int vtype): + """ + Set the type of a variable. + + In this backend, variables are always continuous (real). + If integer or binary variables are requested via the parameter + ``vtype``, an error will be raised. + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``vtype`` (integer) : + + * 1 Integer + * 0 Binary + * -1 Continuous + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.set_variable_type(0,-1) + sage: p.is_variable_continuous(0) + True + """ + if vtype == -1: + pass + else: + raise NotImplementedError() + + def _AbcxCVPRd(self): + """ + Retrieve all problem data from the LP. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p._AbcxCVPRd() + ([], (), (), (), (), (), 'max', Rational Field, 0) + """ + A, b, c, x = self.lp.Abcx() + constraint_types = self.lp.constraint_types() + variable_types = self.lp.variable_types() + problem_type = self.lp.problem_type() + base_ring = self.lp.base_ring() + d = self.lp.objective_constant_term() + return A, b, c, x, constraint_types, variable_types, problem_type, base_ring, d + + cpdef set_sense(self, int sense): + """ + Set the direction (maximization/minimization). + + INPUT: + + - ``sense`` (integer) : + + * +1 => Maximization + * -1 => Minimization + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + if sense == +1: + problem_type = "max" + else: + problem_type = "min" + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef objective_coefficient(self, int variable, coeff=None): + """ + Set or get the coefficient of a variable in the objective + function + + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``coeff`` (double) -- its coefficient + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variable() + 0 + sage: p.objective_coefficient(0) + 0 + sage: p.objective_coefficient(0,2) + sage: p.objective_coefficient(0) + 2 + """ + if coeff is None: + return self.lp.objective_coefficients()[variable] + else: + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + c = list(c) + c[variable] = coeff + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef objective_constant_term(self, d=None): + """ + Set or get the constant term in the objective function + + INPUT: + + - ``d`` (double) -- its coefficient. If `None` (default), return the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.objective_constant_term() + 0 + sage: p.objective_constant_term(42) + sage: p.objective_constant_term() + 42 + """ + if d is None: + return self.lp.objective_constant_term() + else: + A, b, c, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef set_objective(self, list coeff, d = 0): + """ + Set the objective function. + + INPUT: + + - ``coeff`` -- a list of real values, whose i-th element is the + coefficient of the i-th variable in the objective function. + + - ``d`` (real) -- the constant term in the linear function (set to `0` by default) + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(5) + 4 + sage: p.set_objective([1, 1, 2, 1, 3]) + sage: map(lambda x :p.objective_coefficient(x), range(5)) + [1, 1, 2, 1, 3] + + Constants in the objective function are respected:: + + sage: p = MixedIntegerLinearProgram(solver='InteractiveLP') + sage: x,y = p[0], p[1] + sage: p.add_constraint(2*x + 3*y, max = 6) + sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.set_objective(x + y + 7) + sage: p.solve() + 47/5 + + TESTS: + + Constants also work the right way for min:: + + sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', maximization=False) + sage: x,y = p[0], p[1] + sage: p.add_constraint(2*x + 3*y, max = 6) + sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.set_objective(-x - y - 7) + sage: p.solve() + -47/5 + + """ + A, b, _, x, constraint_types, variable_types, problem_type, ring, _ = self._AbcxCVPRd() + c = coeff + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef set_verbosity(self, int level): + """ + Set the log (verbosity) level + + INPUT: + + - ``level`` (integer) -- From 0 (no verbosity) to 3. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.set_verbosity(2) + """ + self.verbosity = level + + cpdef remove_constraint(self, int i): + r""" + Remove a constraint. + + INPUT: + + - ``i`` -- index of the constraint to remove. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(solver="InteractiveLP") + sage: v = p.new_variable(nonnegative=True) + sage: x,y = v[0], v[1] + sage: p.add_constraint(2*x + 3*y, max = 6) + sage: p.add_constraint(3*x + 2*y, max = 6) + sage: p.set_objective(x + y + 7) + sage: p.solve() + 47/5 + sage: p.remove_constraint(0) + sage: p.solve() + 10 + sage: p.get_values([x,y]) + [0, 3] + """ + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + A = A.delete_rows((i,)) + b = list(b); del b[i] + constraint_types=list(constraint_types); del constraint_types[i] + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef add_linear_constraint(self, coefficients, lower_bound, upper_bound, name=None): + """ + Add a linear constraint. + + INPUT: + + - ``coefficients`` -- an iterable of pairs ``(i, v)``. In each + pair, ``i`` is a variable index (integer) and ``v`` is a + value (element of :meth:`base_ring`). + + - ``lower_bound`` -- element of :meth:`base_ring` or + ``None``. The lower bound. + + - ``upper_bound`` -- element of :meth:`base_ring` or + ``None``. The upper bound. + + - ``name`` -- string or ``None``. Optional name for this row. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint( zip(range(5), range(5)), 2, 2) + sage: p.row(0) + ([1, 2, 3, 4], [1, 2, 3, 4]) + sage: p.row_bounds(0) + (2, 2) + sage: p.add_linear_constraint( zip(range(5), range(5)), 1, 1, name='foo') + sage: p.row_name(1) + 'foo' + """ + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + if lower_bound is None: + if upper_bound is None: + raise ValueError("At least one of lower_bound and upper_bound must be provided") + else: + constraint_types = constraint_types + ("<=",) + b = tuple(b) + (upper_bound,) + else: + if upper_bound is None: + constraint_types = constraint_types + (">=",) + b = tuple(b) + (lower_bound,) + elif lower_bound == upper_bound: + constraint_types = constraint_types + ("==",) + b = tuple(b) + (lower_bound,) + else: + raise NotImplementedError("Ranged constraints are not supported") + + row = vector(ring, self.ncols()) + for (i, v) in coefficients: + row[i] = v + A = A.stack(row) + + self.row_names.append(name) + + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + + cpdef add_col(self, list indices, list coeffs): + """ + Add a column. + + INPUT: + + - ``indices`` (list of integers) -- this list contains the + indices of the constraints in which the variable's + coefficient is nonzero + + - ``coeffs`` (list of real values) -- associates a coefficient + to the variable in each of the constraints in which it + appears. Namely, the i-th entry of ``coeffs`` corresponds to + the coefficient of the variable in the constraint + represented by the i-th entry in ``indices``. + + .. NOTE:: + + ``indices`` and ``coeffs`` are expected to be of the same + length. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.nrows() + 0 + sage: p.add_linear_constraints(5, 0, None) + sage: p.add_col(range(5), range(5)) + sage: p.nrows() + 5 + """ + self.add_variable(coefficients = zip(indices, coeffs)) + + cpdef add_linear_constraints(self, int number, lower_bound, upper_bound, names=None): + """ + Add constraints. + + INPUT: + + - ``number`` (integer) -- the number of constraints to add. + + - ``lower_bound`` - a lower bound, either a real value or ``None`` + + - ``upper_bound`` - an upper bound, either a real value or ``None`` + + - ``names`` - an optional list of names (default: ``None``) + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraints(5, 0, None) + sage: p.row(4) + ([], []) + sage: p.row_bounds(4) + (0, None) + """ + for i in range(number): + self.add_linear_constraint(zip(range(self.ncols()),[0]*(self.ncols())), + lower_bound, upper_bound, + name=None if names is None else names[i]) + + cpdef int solve(self) except -1: + """ + Solve the problem. + + .. NOTE:: + + This method raises ``MIPSolverException`` exceptions when + the solution can not be computed for any reason (none + exists, or the LP solver was not able to find it, etc...) + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_linear_constraints(5, 0, None) + sage: p.add_col(range(5), range(5)) + sage: p.solve() + 0 + sage: p.objective_coefficient(0,1) + sage: p.solve() + Traceback (most recent call last): + ... + MIPSolverException: ... + """ + ## FIXME: standard_form should allow to pass slack names (which we would take from row_names). + ## FIXME: Perhaps also pass the problem name as objective name + lp_std_form, transformation = self.lp.standard_form(transformation=True) + self.lp_std_form, self.std_form_transformation = lp_std_form, transformation + output = lp_std_form.run_revised_simplex_method() + ## FIXME: Display output as a side effect if verbosity is high enough + d = self.final_dictionary = lp_std_form.final_revised_dictionary() + if d.is_optimal(): + if lp_std_form.auxiliary_variable() in d.basic_variables(): + raise MIPSolverException("InteractiveLP: Problem has no feasible solution") + else: + return 0 + else: + raise MIPSolverException("InteractiveLP: Problem is unbounded") + + cpdef get_objective_value(self): + """ + Return the value of the objective function. + + .. NOTE:: + + Behavior is undefined unless ``solve`` has been called before. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([(0,1), (1,2)], None, 3) + sage: p.set_objective([2, 5]) + sage: p.solve() + 0 + sage: p.get_objective_value() + 15/2 + sage: p.get_variable_value(0) + 0 + sage: p.get_variable_value(1) + 3/2 + """ + d = self.final_dictionary + v = d.objective_value() + if self.lp_std_form.is_negative(): + v = - v + return v + + cpdef get_variable_value(self, int variable): + """ + Return the value of a variable given by the solver. + + .. NOTE:: + + Behavior is undefined unless ``solve`` has been called before. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(2) + 1 + sage: p.add_linear_constraint([(0,1), (1, 2)], None, 3) + sage: p.set_objective([2, 5]) + sage: p.solve() + 0 + sage: p.get_objective_value() + 15/2 + sage: p.get_variable_value(0) + 0 + sage: p.get_variable_value(1) + 3/2 + """ + solution = self.std_form_transformation(self.final_dictionary.basic_solution()) + return solution[variable] + + cpdef int ncols(self): + """ + Return the number of columns/variables. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variables(2) + 1 + sage: p.ncols() + 2 + """ + return self.lp.n_variables() + + cpdef int nrows(self): + """ + Return the number of rows/constraints. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.nrows() + 0 + sage: p.add_linear_constraints(2, 0, None) + sage: p.nrows() + 2 + """ + return self.lp.n_constraints() + + cpdef bint is_maximization(self): + """ + Test whether the problem is a maximization + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.is_maximization() + True + sage: p.set_sense(-1) + sage: p.is_maximization() + False + """ + return self.lp.problem_type() == "max" + + cpdef problem_name(self, char * name = NULL): + """ + Return or define the problem's name + + INPUT: + + - ``name`` (``char *``) -- the problem's name. When set to + ``NULL`` (default), the method returns the problem's name. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.problem_name("There_once_was_a_french_fry") + sage: print p.problem_name() + There_once_was_a_french_fry + """ + if name == NULL: + if self.prob_name is not None: + return self.prob_name + else: + return "" + else: + self.prob_name = str(name) + + cpdef row(self, int i): + """ + Return a row + + INPUT: + + - ``index`` (integer) -- the constraint's id. + + OUTPUT: + + A pair ``(indices, coeffs)`` where ``indices`` lists the + entries whose coefficient is nonzero, and to which ``coeffs`` + associates their coefficient on the model of the + ``add_linear_constraint`` method. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint(zip(range(5), range(5)), 0, None) + sage: p.row(0) + ([1, 2, 3, 4], [1, 2, 3, 4]) + """ + A, b, c, x = self.lp.Abcx() + indices = [] + coeffs = [] + for j in range(self.ncols()): + if A[i][j] != 0: + indices.append(j) + coeffs.append(A[i][j]) + return (indices, coeffs) + + cpdef row_bounds(self, int index): + """ + Return the bounds of a specific constraint. + + INPUT: + + - ``index`` (integer) -- the constraint's id. + + OUTPUT: + + A pair ``(lower_bound, upper_bound)``. Each of them can be set + to ``None`` if the constraint is not bounded in the + corresponding direction, and is a real value otherwise. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variables(5) + 4 + sage: p.add_linear_constraint(zip(range(5), range(5)), 2, 2) + sage: p.row_bounds(0) + (2, 2) + """ + A, b, c, x = self.lp.Abcx() + constraint_types = self.lp.constraint_types() + if constraint_types[index] == '>=': + return (b[index], None) + elif constraint_types[index] == '<=': + return (None, b[index]) + elif constraint_types[index] == '==': + return (b[index], b[index]) + else: + raise ValueError("Bad constraint_type") + + cpdef col_bounds(self, int index): + """ + Return the bounds of a specific variable. + + INPUT: + + - ``index`` (integer) -- the variable's id. + + OUTPUT: + + A pair ``(lower_bound, upper_bound)``. Each of them can be set + to ``None`` if the variable is not bounded in the + corresponding direction, and is a real value otherwise. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variable(lower_bound=None) + 0 + sage: p.col_bounds(0) + (None, None) + sage: p.variable_lower_bound(0, 0) + sage: p.col_bounds(0) + (0, None) + """ + t = self.lp.variable_types()[index] + if t == ">=": + return (0, None) + elif t == "<=": + return (None, 0) + elif t == "": + return (None, None) + else: + raise ValueError("Bad _variable_types") + + cpdef bint is_variable_binary(self, int index): + """ + Test whether the given variable is of binary type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.is_variable_binary(0) + False + + """ + return False + + cpdef bint is_variable_integer(self, int index): + """ + Test whether the given variable is of integer type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.is_variable_integer(0) + False + """ + return False + + cpdef bint is_variable_continuous(self, int index): + """ + Test whether the given variable is of continuous/real type. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.ncols() + 0 + sage: p.add_variable() + 0 + sage: p.is_variable_continuous(0) + True + + """ + return True + + cpdef row_name(self, int index): + """ + Return the ``index`` th row name + + INPUT: + + - ``index`` (integer) -- the row's id + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_linear_constraints(1, 2, None, names=['Empty constraint 1']) + sage: p.row_name(0) + 'Empty constraint 1' + + """ + return self.row_names[index] + + cpdef col_name(self, int index): + """ + Return the ``index``-th column name + + INPUT: + + - ``index`` (integer) -- the column id + + - ``name`` (``char *``) -- its name. When set to ``NULL`` + (default), the method returns the current name. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variable(name="I_am_a_variable") + 0 + sage: p.col_name(0) + 'I_am_a_variable' + """ + return str(self.lp.decision_variables()[index]) + + cpdef variable_upper_bound(self, int index, value = False): + """ + Return or define the upper bound on a variable + + INPUT: + + - ``index`` (integer) -- the variable's id + + - ``value`` -- real value, or ``None`` to mean that the + variable has not upper bound. When set to ``None`` + (default), the method returns the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variable(lower_bound=None) + 0 + sage: p.col_bounds(0) + (None, None) + sage: p.variable_upper_bound(0) is None + True + sage: p.variable_upper_bound(0, 0) + sage: p.col_bounds(0) + (None, 0) + sage: p.variable_upper_bound(0) + 0 + sage: p.variable_upper_bound(0, None) + sage: p.variable_upper_bound(0) is None + True + """ + bounds = self.col_bounds(index) + if value is False: + return bounds[1] + else: + if value != bounds[1]: + bounds = (bounds[0], value) + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + variable_types = list(variable_types) + variable_types[index] = self._variable_type_from_bounds(*bounds) + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef variable_lower_bound(self, int index, value = False): + """ + Return or define the lower bound on a variable + + INPUT: + + - ``index`` (integer) -- the variable's id + + - ``value`` -- real value, or ``None`` to mean that the + variable has no lower bound. When set to ``None`` + (default), the method returns the current value. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "InteractiveLP") + sage: p.add_variable(lower_bound=None) + 0 + sage: p.col_bounds(0) + (None, None) + sage: p.variable_lower_bound(0) is None + True + sage: p.variable_lower_bound(0, 0) + sage: p.col_bounds(0) + (0, None) + sage: p.variable_lower_bound(0) + 0 + sage: p.variable_lower_bound(0, None) + sage: p.variable_lower_bound(0) is None + True + """ + bounds = self.col_bounds(index) + if value is False: + return bounds[0] + else: + if value != bounds[0]: + bounds = (value, bounds[1]) + A, b, c, x, constraint_types, variable_types, problem_type, ring, d = self._AbcxCVPRd() + variable_types = list(variable_types) + variable_types[index] = self._variable_type_from_bounds(*bounds) + self.lp = InteractiveLPProblem(A, b, c, x, + constraint_types, variable_types, + problem_type, ring, objective_constant_term=d) + + cpdef bint is_variable_basic(self, int index): + """ + Test whether the given variable is basic. + + This assumes that the problem has been solved with the simplex method + and a basis is available. Otherwise an exception will be raised. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: # Backend-specific commands to instruct solver to use simplex method here + sage: b.solve() + 0 + sage: b.is_variable_basic(0) + True + sage: b.is_variable_basic(1) + False + """ + return self.lp_std_form.decision_variables()[index] in self.final_dictionary.basic_variables() + + cpdef bint is_variable_nonbasic_at_lower_bound(self, int index): + """ + Test whether the given variable is nonbasic at lower bound. + + This assumes that the problem has been solved with the simplex method + and a basis is available. Otherwise an exception will be raised. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: # Backend-specific commands to instruct solver to use simplex method here + sage: b.solve() + 0 + sage: b.is_variable_nonbasic_at_lower_bound(0) + False + sage: b.is_variable_nonbasic_at_lower_bound(1) + True + """ + return self.lp_std_form.decision_variables()[index] in self.final_dictionary.nonbasic_variables() + + cpdef bint is_slack_variable_basic(self, int index): + """ + Test whether the slack variable of the given row is basic. + + This assumes that the problem has been solved with the simplex method + and a basis is available. Otherwise an exception will be raised. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: # Backend-specific commands to instruct solver to use simplex method here + sage: b.solve() + 0 + sage: b.is_slack_variable_basic(0) + True + sage: b.is_slack_variable_basic(1) + False + """ + return self.lp_std_form.slack_variables()[index] in self.final_dictionary.basic_variables() + + cpdef bint is_slack_variable_nonbasic_at_lower_bound(self, int index): + """ + Test whether the given variable is nonbasic at lower bound. + + This assumes that the problem has been solved with the simplex method + and a basis is available. Otherwise an exception will be raised. + + INPUT: + + - ``index`` (integer) -- the variable's id + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: # Backend-specific commands to instruct solver to use simplex method here + sage: b.solve() + 0 + sage: b.is_slack_variable_nonbasic_at_lower_bound(0) + False + sage: b.is_slack_variable_nonbasic_at_lower_bound(1) + True + """ + return self.lp_std_form.slack_variables()[index] in self.final_dictionary.nonbasic_variables() + + cpdef dictionary(self): + # Proposed addition to the general interface, + # which would for other solvers return backend dictionaries (#18804) + """ + Return a dictionary representing the current basis. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: # Backend-specific commands to instruct solver to use simplex method here + sage: b.solve() + 0 + sage: d = b.dictionary(); d + LP problem dictionary ... + sage: set(d.basic_variables()) + {x1, x3} + sage: d.basic_solution() + (17/8, 0) + + TESTS: + + Compare with a dictionary obtained in another way:: + + sage: lp, basis = p.interactive_lp_problem() + sage: lp.dictionary(*basis).basic_solution() + (17/8, 0) + + """ + return self.final_dictionary + + cpdef interactive_lp_problem(self): + + """ + Return the :class:`InteractiveLPProblem` object associated with this backend. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram(maximization=True,\ + solver="InteractiveLP") + sage: x = p.new_variable(nonnegative=True) + sage: p.add_constraint(-x[0] + x[1] <= 2) + sage: p.add_constraint(8 * x[0] + 2 * x[1] <= 17) + sage: p.set_objective(11/2 * x[0] - 3 * x[1]) + sage: b = p.get_backend() + sage: b.interactive_lp_problem() + LP problem ... + """ + return self.lp diff --git a/src/sage/numerical/backends/ppl_backend.pyx b/src/sage/numerical/backends/ppl_backend.pyx index b696612fa93..bca5b2696a8 100644 --- a/src/sage/numerical/backends/ppl_backend.pyx +++ b/src/sage/numerical/backends/ppl_backend.pyx @@ -20,11 +20,23 @@ AUTHORS: #***************************************************************************** from sage.numerical.mip import MIPSolverException -from sage.libs.ppl import MIP_Problem, Variable, Linear_Expression, Constraint, Generator +from sage.libs.ppl import MIP_Problem, Variable, Variables_Set, Linear_Expression, Constraint, Generator from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational +from copy import copy cdef class PPLBackend(GenericBackend): + + """ + MIP Backend that uses the exact MIP solver from the Parma Polyhedra Library. + + General backend testsuite:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = get_solver(solver = "PPL") + sage: TestSuite(p).run(skip="_test_pickling") + """ + cdef object mip cdef list Matrix cdef list row_lower_bound @@ -36,18 +48,34 @@ cdef class PPLBackend(GenericBackend): cdef list col_name_var cdef int is_maximize cdef str name + cdef object integer_variables + # Common denominator for objective function in self.mip (not for the constant term) cdef Integer obj_denominator - def __cinit__(self, maximization = True): + def __cinit__(self, maximization = True, base_ring = None): """ Constructor EXAMPLE:: sage: p = MixedIntegerLinearProgram(solver = "PPL") + + TESTS: + + Raise an error if a ``base_ring`` is requested that is not supported:: + + sage: p = MixedIntegerLinearProgram(solver = "PPL", base_ring=AA) + Traceback (most recent call last): + ... + TypeError: The PPL backend only supports rational data. """ + if base_ring is not None: + from sage.rings.all import QQ + if base_ring is not QQ: + raise TypeError('The PPL backend only supports rational data.') + self.Matrix = [] self.row_lower_bound = [] self.row_upper_bound = [] @@ -59,6 +87,7 @@ cdef class PPLBackend(GenericBackend): self.name = '' self.obj_constant_term = Rational(0) self.obj_denominator = Integer(1) + self.integer_variables = set() if maximization: self.set_sense(+1) @@ -72,6 +101,39 @@ cdef class PPLBackend(GenericBackend): cpdef zero(self): return self.base_ring()(0) + cpdef __copy__(self): + """ + Returns a copy of self. + + EXAMPLE:: + + sage: from sage.numerical.backends.generic_backend import get_solver + sage: p = MixedIntegerLinearProgram(solver = "PPL") + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = copy(p.get_backend()) + sage: cp.solve() + 0 + sage: cp.get_objective_value() + 6 + """ + cdef PPLBackend cp = type(self)() + cp.Matrix = [row[:] for row in self.Matrix] + cp.row_lower_bound = self.row_lower_bound[:] + cp.row_upper_bound = self.row_upper_bound[:] + cp.col_lower_bound = self.col_lower_bound[:] + cp.col_upper_bound = self.col_upper_bound[:] + cp.objective_function = self.objective_function[:] + cp.row_name_var = self.row_name_var[:] + cp.col_name_var = self.col_name_var[:] + cp.name = self.name + cp.obj_constant_term = self.obj_constant_term + cp.obj_denominator = self.obj_denominator + cp.integer_variables = copy(self.integer_variables) + cp.is_maximize = self.is_maximize + return cp + def init_mip(self): """ Converting the matrix form of the MIP Problem to PPL MIP_Problem. @@ -94,6 +156,13 @@ cdef class PPLBackend(GenericBackend): self.mip.add_space_dimensions_and_embed(len(self.objective_function)) + # Integrality + + ivar = Variables_Set() + for i in self.integer_variables: + ivar.insert(Variable(i)) + self.mip.add_to_integer_space_dimensions(ivar) + # Objective function mip_obj = Linear_Expression(0) denom = Integer(1) @@ -133,7 +202,7 @@ cdef class PPLBackend(GenericBackend): else: self.mip.set_optimization_mode('minimization') - cpdef int add_variable(self, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=0, name=None) except -1: + cpdef int add_variable(self, lower_bound=0, upper_bound=None, binary=False, continuous=False, integer=False, obj=0, name=None) except -1: """ Add a variable. @@ -178,14 +247,29 @@ cdef class PPLBackend(GenericBackend): 'x' sage: p.objective_coefficient(2) 2/3 + sage: p.add_variable(integer=True) + 3 """ + cdef int vtype = int(bool(binary)) + int(bool(continuous)) + int(bool(integer)) + if vtype == 0: + continuous = True + elif vtype != 1: + raise ValueError("Exactly one parameter of 'binary', 'integer' and 'continuous' must be 'True'.") + for i in range(len(self.Matrix)): self.Matrix[i].append(0) self.col_lower_bound.append(lower_bound) self.col_upper_bound.append(upper_bound) self.objective_function.append(obj) self.col_name_var.append(name) - return len(self.objective_function) - 1 + + n = len(self.objective_function) - 1 + if binary: + self.set_variable_type(n,0) + elif integer: + self.set_variable_type(n,1) + + return n cpdef int add_variables(self, int n, lower_bound=0, upper_bound=None, binary=False, continuous=True, integer=False, obj=0, names=None) except -1: """ @@ -226,9 +310,22 @@ cdef class PPLBackend(GenericBackend): 4 sage: p.ncols() 5 - sage: p.add_variables(2, lower_bound=-2.0, names=['a','b']) + sage: p.add_variables(2, lower_bound=-2.0, obj=42.0, names=['a','b']) 6 + + TESTS: + + Check that arguments are used:: + + sage: p.col_bounds(5) # tol 1e-8 + (-2.0, None) + sage: p.col_name(5) + 'a' + sage: p.objective_coefficient(5) # tol 1e-8 + 42.0 """ + if binary or integer: + raise NotImplementedError("The PPL backend in Sage only supports continuous variables") for k in range(n): for i in range(len(self.Matrix)): self.Matrix[i].append(0) @@ -245,20 +342,54 @@ cdef class PPLBackend(GenericBackend): """ Set the type of a variable. + INPUT: + + - ``variable`` (integer) -- the variable's id + + - ``vtype`` (integer) : + + * 1 Integer + * 0 Binary + * -1 Continuous + EXAMPLE:: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") sage: p.add_variables(5) 4 + sage: p.set_variable_type(0,1) + sage: p.is_variable_integer(0) + True + sage: p.set_variable_type(3,0) + sage: p.is_variable_integer(3) or p.is_variable_binary(3) + True + sage: p.col_bounds(3) # tol 1e-6 + (0, 1) sage: p.set_variable_type(3, -1) + sage: p.is_variable_continuous(3) + True + + TESTS: + + Test that an exception is raised when an invalid type is passed:: + sage: p.set_variable_type(3, -2) Traceback (most recent call last): ... - Exception: ... + ValueError: ... """ - if vtype != -1: - raise Exception('This backend does not handle integer variables ! Read the doc !') + if vtype == -1: + if variable in self.integer_variables: + self.integer_variables.remove(variable) + elif vtype == 0: + self.integer_variables.add(variable) + self.variable_lower_bound(variable, 0) + self.variable_upper_bound(variable, 1) + elif vtype == 1: + self.integer_variables.add(variable) + else: + raise ValueError("Invalid variable type: {}".format(vtype)) cpdef set_sense(self, int sense): """ @@ -505,6 +636,7 @@ cdef class PPLBackend(GenericBackend): self.row_name_var.append(None) cpdef int solve(self) except -1: + # integer example copied from cplex_backend.pyx """ Solve the problem. @@ -512,9 +644,11 @@ cdef class PPLBackend(GenericBackend): This method raises ``MIPSolverException`` exceptions when the solution can not be computed for any reason (none - exists, or the LP solver was not able to find it, etc...) + exists, or the solver was not able to find it, etc...) - EXAMPLE:: + EXAMPLES: + + A linear optimization problem:: sage: from sage.numerical.backends.generic_backend import get_solver sage: p = get_solver(solver = "PPL") @@ -522,11 +656,24 @@ cdef class PPLBackend(GenericBackend): sage: p.add_col(range(5), range(5)) sage: p.solve() 0 + + An unbounded problem:: + sage: p.objective_coefficient(0,1) sage: p.solve() Traceback (most recent call last): ... MIPSolverException: ... + + An integer optimization problem:: + + sage: p = MixedIntegerLinearProgram(solver='PPL') + sage: x = p.new_variable(integer=True, nonnegative=True) + sage: p.add_constraint(2*x[0] + 3*x[1], max = 6) + sage: p.add_constraint(3*x[0] + 2*x[1], max = 6) + sage: p.set_objective(x[0] + x[1] + 7) + sage: p.solve() + 9 """ self.init_mip() @@ -573,7 +720,6 @@ cdef class PPLBackend(GenericBackend): sage: p.get_variable_value(1) 3/2 """ - self.init_mip() ans = self.mip.optimal_value() return ans / self.obj_denominator + self.obj_constant_term @@ -602,7 +748,6 @@ cdef class PPLBackend(GenericBackend): sage: p.get_variable_value(1) 3/2 """ - self.init_mip() g = self.mip.optimizing_point() return g.coefficient(Variable(variable)) / g.divisor() @@ -790,7 +935,7 @@ cdef class PPLBackend(GenericBackend): sage: p.is_variable_binary(0) False """ - return False + return index in self.integer_variables and self.col_bounds(index) == (0, 1) cpdef bint is_variable_integer(self, int index): """ @@ -811,7 +956,7 @@ cdef class PPLBackend(GenericBackend): sage: p.is_variable_integer(0) False """ - return False + return index in self.integer_variables and self.col_bounds(index) != (0, 1) cpdef bint is_variable_continuous(self, int index): """ @@ -832,7 +977,7 @@ cdef class PPLBackend(GenericBackend): sage: p.is_variable_continuous(0) True """ - return True + return index not in self.integer_variables cpdef row_name(self, int index): """ diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 344949309f2..58efe23bd49 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -592,6 +592,9 @@ class InteractiveLPProblem(SageObject): - ``is_primal`` -- (default: ``True``) whether this problem is primal or dual: each problem is of course dual to its own dual, this flag is mostly for internal use and affects default variable names only + + - ``objective_constant_term`` -- (default: 0) a constant term of the + objective EXAMPLES: @@ -632,7 +635,7 @@ class InteractiveLPProblem(SageObject): def __init__(self, A, b, c, x="x", constraint_type="<=", variable_type="", problem_type="max", - base_ring=None, is_primal=True): + base_ring=None, is_primal=True, objective_constant_term=0): r""" See :class:`InteractiveLPProblem` for documentation. @@ -671,6 +674,7 @@ def __init__(self, A, b, c, x="x", R = PolynomialRing(base_ring, x, order="neglex") x = vector(R, R.gens()) # All variables as a vector self._Abcx = A, b, c, x + self._constant_term = objective_constant_term if constraint_type in ["<=", ">=", "=="]: constraint_type = (constraint_type, ) * m @@ -731,6 +735,7 @@ def __eq__(self, other): """ return (isinstance(other, InteractiveLPProblem) and self.Abcx() == other.Abcx() and + self._constant_term == other._constant_term and self._problem_type == other._problem_type and self._is_negative == other._is_negative and self._constraint_types == other._constraint_types and @@ -753,7 +758,7 @@ def _latex_(self): sage: print P._latex_() \begin{array}{l} \begin{array}{lcrcrcl} - \max \mspace{-6mu}&\mspace{-6mu} \mspace{-6mu}&\mspace{-6mu} 10 C \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} 5 B \mspace{-6mu} \\ + \max \mspace{-6mu}&\mspace{-6mu} \mspace{-6mu}&\mspace{-6mu} 10 C \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} 5 B \mspace{-6mu}&\mspace{-6mu} \mspace{-6mu}&\mspace{-6mu} \\ \mspace{-6mu}&\mspace{-6mu} \mspace{-6mu}&\mspace{-6mu} C \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} B \mspace{-6mu}&\mspace{-6mu} \leq \mspace{-6mu}&\mspace{-6mu} 1000 \\ \mspace{-6mu}&\mspace{-6mu} \mspace{-6mu}&\mspace{-6mu} 3 C \mspace{-6mu}&\mspace{-6mu} + \mspace{-6mu}&\mspace{-6mu} B \mspace{-6mu}&\mspace{-6mu} \leq \mspace{-6mu}&\mspace{-6mu} 1500 \\ \end{array} \\ @@ -768,8 +773,13 @@ def _latex_(self): lines.append(r"\begin{array}{l" + "cr" * len(x) + "cl}") head = [r"{} \{}".format("- " if self._is_negative else "", self._problem_type)] - lines.append(_latex_product(c, x, head=head) + - (r"\\" if generate_real_LaTeX else r" \mspace{-6mu} \\")) + if self._constant_term == 0: + tail = ["", ""] + elif latex(self._constant_term).strip().startswith("-"): + tail = ["-", - self._constant_term] + else: + tail = ["+", self._constant_term] + lines.append(_latex_product(c, x, head=head, tail=tail) + r"\\") for Ai, ri, bi in zip(A.rows(), self._constraint_types, b): lines.append(_latex_product(Ai, x, head=[""], tail=[ri, bi]) + r" \\") @@ -801,6 +811,46 @@ def _repr_(self): LP problem (use typeset mode to see details) """ return "LP problem (use typeset mode to see details)" + + def _solution(self, x): + r""" + Return ``x`` as a normalized solution of ``self``. + + INPUT: + + - ``x`` -- anything that can be interpreted as a solution of this + problem, e.g. a vector or a list of correct length or a single + element list with such a vector + + OUTPUT: + + - ``x`` as a vector + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") + sage: P._solution([100, 200]) + (100, 200) + sage: P._solution([[100, 200]]) + (100, 200) + sage: P._solution([1000]) + Traceback (most recent call last): + ... + TypeError: given input is not a solution for this problem + """ + S = self.c().parent() + try: + return S(x) + except TypeError: + if len(x) == 1: + try: + return S(x[0]) + except TypeError: + pass + raise TypeError("given input is not a solution for this problem") @cached_method def _solve(self): @@ -846,11 +896,12 @@ def _solve(self): M, S = -Infinity, None else: M, S = min((c * vector(R, v), v) for v in F.vertices()) - if self._is_negative: - M = - M if S is not None: S = vector(R, S) S.set_immutable() + M += self._constant_term + if self._is_negative: + M = - M return S, M def Abcx(self): @@ -947,6 +998,25 @@ def constraint_coefficients(self): """ return self._Abcx[0] + def constraint_types(self): + r""" + Return a tuple listing the constraint types of all rows. + + OUTPUT: + + - a tuple of strings + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=", constraint_type=["<=", "=="]) + sage: P.constraint_types() + ('<=', '==') + """ + return self._constraint_types + def decision_variables(self): r""" Return decision variables of ``self``, i.e. `x`. @@ -1036,7 +1106,8 @@ def dual(self, y=None): problem_type = "-" + problem_type return InteractiveLPProblem(A, b, c, y, constraint_type, variable_type, problem_type, - is_primal=not self.is_primal()) + is_primal=not self.is_primal(), + objective_constant_term=self._constant_term) @cached_method def feasible_set(self): @@ -1095,27 +1166,72 @@ def is_bounded(self): sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") sage: P.is_bounded() True + + Note that infeasible problems are always bounded:: + + sage: b = (-1000, 1500) + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") + sage: P.is_feasible() + False + sage: P.is_bounded() + True """ - return self._solve()[0] is not None + return self.optimal_solution() is not None or not self.is_feasible() - def is_feasible(self): + def is_feasible(self, *x): r""" - Check if ``self`` is feasible. + Check if ``self`` or given solution is feasible. + + INPUT: + + - (optional) anything that can be interpreted as a valid solution for + this problem, i.e. a sequence of values for all decision variables OUTPUT: - - ``True`` is ``self`` is feasible, ``False`` otherwise + - ``True`` is this problem or given solution is feasible, ``False`` + otherwise EXAMPLES:: sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") sage: P.is_feasible() True + sage: P.is_feasible(100, 200) + True + sage: P.is_feasible(1000, 200) + False + sage: P.is_feasible([1000, 200]) + False + sage: P.is_feasible(1000) + Traceback (most recent call last): + ... + TypeError: given input is not a solution for this problem """ - return self._solve()[1] is not None + if x: + return self.feasible_set().contains(self._solution(x)) + return self.optimal_value() is not None + + def is_negative(self): + r""" + Return `True` when the problem is of type ``"-max"`` or ``"-min"``. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P.is_negative() + False + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=", problem_type="-min") + sage: P.is_negative() + True + """ + return self._is_negative def is_primal(self): r""" @@ -1140,6 +1256,37 @@ def is_primal(self): """ return self._is_primal + def is_optimal(self, *x): + r""" + Check if given solution is feasible. + + INPUT: + + - anything that can be interpreted as a valid solution for + this problem, i.e. a sequence of values for all decision variables + + OUTPUT: + + - ``True`` is the given solution is optimal, ``False`` otherwise + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (15, 5) + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") + sage: P.is_optimal(100, 200) + False + sage: P.is_optimal(500, 0) + True + sage: P.is_optimal(499, 3) + True + sage: P.is_optimal(501, -3) + False + """ + return (self.optimal_value() == self.objective_value(*x) and + self.is_feasible(*x)) + def n_constraints(self): r""" Return the number of constraints of ``self``, i.e. `m`. @@ -1202,6 +1349,59 @@ def objective_coefficients(self): (10, 5) """ return self._Abcx[2] + + def objective_constant_term(self): + r""" + Return the constant term of the objective. + + OUTPUT: + + - a number + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P.objective_constant_term() + 0 + sage: P.optimal_value() + 6250 + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], + ....: variable_type=">=", objective_constant_term=-1250) + sage: P.objective_constant_term() + -1250 + sage: P.optimal_value() + 5000 + """ + return self._constant_term + + def objective_value(self, *x): + r""" + Return the value of the objective on the given solution. + + INPUT: + + - anything that can be interpreted as a valid solution for + this problem, i.e. a sequence of values for all decision variables + + OUTPUT: + + - the value of the objective on the given solution taking into account + :meth:`objective_constant_term` and :meth:`is_negative` + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") + sage: P.objective_value(100, 200) + 2000 + """ + v = self.c() * self._solution(x) + self._constant_term + return - v if self._is_negative else v def optimal_solution(self): r""" @@ -1426,31 +1626,120 @@ def plot_feasible_set(self, xmin=None, xmax=None, ymin=None, ymax=None, result.set_aspect_ratio(1) return result - def standard_form(self, objective_name=None): + def problem_type(self): + r""" + Return the problem type. + + Needs to be used together with ``is_negative``. + + OUTPUT: + + - a string, one of ``"max"``, ``"min"``. + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P.problem_type() + 'max' + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=", problem_type="-min") + sage: P.problem_type() + 'min' + """ + return self._problem_type + + def standard_form(self, transformation=False, **kwds): r""" Construct the LP problem in standard form equivalent to ``self``. INPUT: - - ``objective_name`` -- a string or a symbolic expression for the - objective used in dictionaries, default depends on :func:`style` + - ``transformation`` -- (default: ``False``) if ``True``, a map + converting solutions of the problem in standard form to the original + one will be returned as well + + - you can pass (as keywords only) ``slack_variables``, + ``auxiliary_variable``,``objective_name`` to the constructor of + :class:`InteractiveLPProblemStandardForm` OUTPUT: - - an :class:`InteractiveLPProblemStandardForm` + - an :class:`InteractiveLPProblemStandardForm` by itself or a tuple + with variable transformation as the second component EXAMPLES:: sage: A = ([1, 1], [3, 1]) sage: b = (1000, 1500) sage: c = (10, 5) - sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=">=") + sage: P = InteractiveLPProblem(A, b, c, variable_type=">=") sage: DP = P.dual() sage: DPSF = DP.standard_form() sage: DPSF.b() (-10, -5) + sage: DPSF.slack_variables() + (y3, y4) + sage: DPSF = DP.standard_form(slack_variables=["L", "F"]) + sage: DPSF.slack_variables() + (L, F) + sage: DPSF, f = DP.standard_form(True) + sage: f + Vector space morphism represented by the matrix: + [1 0] + [0 1] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field + + A more complicated transformation map:: + + sage: P = InteractiveLPProblem(A, b, c, variable_type=["<=", ""], + ....: objective_constant_term=42) + sage: PSF, f = P.standard_form(True) + sage: f + Vector space morphism represented by the matrix: + [-1 0] + [ 0 1] + [ 0 -1] + Domain: Vector space of dimension 3 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field + sage: PSF.optimal_solution() + (0, 1000, 0) + sage: P.optimal_solution() + (0, 1000) + sage: P.is_optimal(PSF.optimal_solution()) + Traceback (most recent call last): + ... + TypeError: given input is not a solution for this problem + sage: P.is_optimal(f(PSF.optimal_solution())) + True + sage: PSF.optimal_value() + 5042 + sage: P.optimal_value() + 5042 + + TESTS: + + Above also works for the equivalent minimization problem:: + + sage: c = (-10, -5) + sage: P = InteractiveLPProblem(A, b, c, variable_type=["<=", ""], + ....: objective_constant_term=-42, + ....: problem_type="min") + sage: PSF, f = P.standard_form(True) + sage: PSF.optimal_solution() + (0, 1000, 0) + sage: P.optimal_solution() + (0, 1000) + sage: PSF.optimal_value() + -5042 + sage: P.optimal_value() + -5042 + """ A, b, c, x = self.Abcx() + f = identity_matrix(self.n()).columns() if not all(ct == "<=" for ct in self._constraint_types): newA = [] newb = [] @@ -1467,11 +1756,14 @@ def standard_form(self, objective_name=None): newA = [] newc = [] newx = [] - for vt, Aj, cj, xj in zip(self._variable_types, A.columns(), c, x): + newf = [] + for vt, Aj, cj, xj, fj in zip( + self._variable_types, A.columns(), c, x, f): xj = str(xj) if vt in [">=", ""]: newA.append(Aj) newc.append(cj) + newf.append(fj) if vt == ">=": newx.append(xj) if vt == "": @@ -1480,23 +1772,47 @@ def standard_form(self, objective_name=None): newA.append(-Aj) newc.append(-cj) newx.append(xj + "_n") + newf.append(-fj) A = column_matrix(newA) c = vector(newc) x = newx + f = newf - is_primal = self.is_primal() - if objective_name is None: - objective_name = default_variable_name( - "primal objective" if is_primal else "dual objective") - objective_name = SR(objective_name) + objective_name = SR(kwds.get("objective_name", default_variable_name( + "primal objective" if self.is_primal() else "dual objective"))) is_negative = self._is_negative + constant_term = self._constant_term if self._problem_type == "min": is_negative = not is_negative c = - c + constant_term = - constant_term objective_name = - objective_name - problem_type = "-max" if is_negative else "max" - return InteractiveLPProblemStandardForm(A, b, c, x, problem_type, - is_primal=is_primal, objective_name=objective_name) + kwds["objective_name"] = objective_name + kwds["problem_type"] = "-max" if is_negative else "max" + kwds["is_primal"] = self.is_primal() + kwds["objective_constant_term"] = constant_term + P = InteractiveLPProblemStandardForm(A, b, c, x, **kwds) + f = P.c().parent().hom(f, self.c().parent()) + return (P, f) if transformation else P + + def variable_types(self): + r""" + Return a tuple listing the variable types of all decision variables. + + OUTPUT: + + - a tuple of strings + + EXAMPLES:: + + sage: A = ([1, 1], [3, 1]) + sage: b = (1000, 1500) + sage: c = (10, 5) + sage: P = InteractiveLPProblem(A, b, c, ["C", "B"], variable_type=[">=", ""]) + sage: P.variable_types() + ('>=', '') + """ + return self._variable_types # Aliases for the standard notation A = constraint_coefficients @@ -1542,7 +1858,7 @@ class InteractiveLPProblemStandardForm(InteractiveLPProblem): problem type: either ``"max"`` or ``"-max"`` - ``slack_variables`` -- (default: depends on :func:`style`) - a vector of slack variables or a sting giving the base name + a vector of slack variables or a string giving the base name - ``auxiliary_variable`` -- (default: same as ``x`` parameter with adjoined ``"0"`` if it was given as a string, otherwise ``"x0"``) the auxiliary @@ -1560,6 +1876,9 @@ class InteractiveLPProblemStandardForm(InteractiveLPProblem): - ``objective_name`` -- a string or a symbolic expression for the objective used in dictionaries, default depends on :func:`style` + - ``objective_constant_term`` -- (default: 0) a constant term of the + objective + EXAMPLES:: sage: A = ([1, 1], [3, 1]) @@ -1581,7 +1900,8 @@ class InteractiveLPProblemStandardForm(InteractiveLPProblem): def __init__(self, A, b, c, x="x", problem_type="max", slack_variables=None, auxiliary_variable=None, - base_ring=None, is_primal=True, objective_name=None): + base_ring=None, is_primal=True, objective_name=None, + objective_constant_term=0): r""" See :class:`InteractiveLPProblemStandardForm` for documentation. @@ -1602,7 +1922,8 @@ def __init__(self, A, b, c, x="x", problem_type="max", constraint_type="<=", variable_type=">=", base_ring=base_ring, - is_primal=is_primal) + is_primal=is_primal, + objective_constant_term=objective_constant_term) n, m = self.n(), self.m() if slack_variables is None: slack_variables = default_variable_name( @@ -1834,6 +2155,7 @@ def feasible_dictionary(self, auxiliary_dictionary): A = A.matrix_from_columns(range(k) + range(k + 1, n)) b = copy(b) c = vector(self.base_ring(), n - 1) + v = self._constant_term for cj, xj in zip(*self.Abcx()[-2:]): if xj in N: c[N.index(xj)] += cj @@ -1938,7 +2260,7 @@ def initial_dictionary(self): A, b, c, x = self.Abcx() x = self._R.gens() m, n = self.m(), self.n() - return LPDictionary(A, b, c, 0, x[-m:], x[-m-n:-m], + return LPDictionary(A, b, c, self._constant_term, x[-m:], x[-m-n:-m], self.objective_name()) def inject_variables(self, scope=None, verbose=True): @@ -4387,7 +4709,8 @@ def objective_value(self): sage: D.objective_value() 0 """ - return self.y() * self.problem().b() + return (self.y() * self.problem().b() + + self.problem().objective_constant_term()) def problem(self): r""" diff --git a/src/sage/numerical/knapsack.py b/src/sage/numerical/knapsack.py index 60a67bb1fb2..a499deb2565 100644 --- a/src/sage/numerical/knapsack.py +++ b/src/sage/numerical/knapsack.py @@ -522,7 +522,7 @@ def subset_sum(self, N): REFERENCES: - .. [HPS08] J. Hoffstein, J. Pipher, and J.H. Silverman. *An + .. [HPS08] \J. Hoffstein, J. Pipher, and J.H. Silverman. *An Introduction to Mathematical Cryptography*. Springer, 2008. """ # input error handling diff --git a/src/sage/numerical/linear_functions.pxd b/src/sage/numerical/linear_functions.pxd index 822b516ac29..c531b11a1fe 100644 --- a/src/sage/numerical/linear_functions.pxd +++ b/src/sage/numerical/linear_functions.pxd @@ -1,15 +1,17 @@ -from sage.structure.parent cimport Parent +from sage.structure.parent cimport Parent, Parent_richcmp_element_without_coercion from sage.structure.element cimport ModuleElement, RingElement, Element cpdef is_LinearFunction(x) +cdef class LinearFunctionOrConstraint(ModuleElement): + cdef void chained_comparator_hack_nonzero(self) + cdef class LinearFunctionsParent_class(Parent): cpdef _element_constructor_(self, x) cpdef _coerce_map_from_(self, R) - cdef object _multiplication_symbol - cpdef object _get_multiplication_symbol(self) + cdef public _multiplication_symbol -cdef class LinearFunction(ModuleElement): +cdef class LinearFunction(LinearFunctionOrConstraint): cdef dict _f cpdef iteritems(self) cpdef ModuleElement _add_(self, ModuleElement b) @@ -26,12 +28,7 @@ cdef class LinearConstraintsParent_class(Parent): cpdef _element_constructor_(self, left, right=?, equality=?) cpdef _coerce_map_from_(self, R) -cdef class LinearConstraint(Element): +cdef class LinearConstraint(LinearFunctionOrConstraint): cdef bint equality cdef list constraints - cdef LinearConstraint _chained_comparator_hack_part1(LinearConstraint left, LinearConstraint right) - cdef _chained_comparator_hack_part2(self) cpdef equals(LinearConstraint left, LinearConstraint right) - -cdef LinearConstraint _chained_comparator_hack_search -cdef LinearConstraint _chained_comparator_hack_replace diff --git a/src/sage/numerical/linear_functions.pyx b/src/sage/numerical/linear_functions.pyx index eb4ceb82b56..3655b18c18c 100644 --- a/src/sage/numerical/linear_functions.pyx +++ b/src/sage/numerical/linear_functions.pyx @@ -42,14 +42,32 @@ inequalities as less or equal:: sage: x[5] >= ieq_01234 x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 - sage: (x[5]<=x[6]) >= ieq_01234 + sage: (x[5] <= x[6]) >= ieq_01234 x_0 <= x_1 <= x_2 <= x_3 <= x_4 <= x_5 <= x_6 - sage: (x[5]<=x[6]) <= ieq_01234 + sage: (x[5] <= x[6]) <= ieq_01234 x_5 <= x_6 <= x_0 <= x_1 <= x_2 <= x_3 <= x_4 +.. WARNING:: + + The implementation of chained inequalities uses a Python hack to + make it work, so it is not completely robust. In particular, while + constants are allowed, no two constants can appear next to + eachother. The following does not work for example:: + + sage: x[0] <= 3 <= 4 + True + + If you really need this for some reason, you can explicitly convert + the constants to a :class:`LinearFunction`:: + + sage: from sage.numerical.linear_functions import LinearFunctionsParent + sage: LF = LinearFunctionsParent(QQ) + sage: x[0] <= LF(3) <= LF(4) + x_0 <= 3 <= 4 + TESTS: -See :trac:`12091` :: +See :trac:`12091`:: sage: p = MixedIntegerLinearProgram() sage: b = p.new_variable() @@ -66,14 +84,16 @@ See :trac:`12091` :: #***************************************************************************** # Copyright (C) 2012 Nathann Cohen # Copyright (C) 2012 Volker Braun +# Copyright (C) 2016 Jeroen Demeyer # -# 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/ #***************************************************************************** -from cpython.object cimport * +from cpython.object cimport Py_EQ, Py_GE, Py_LE, Py_GT, Py_LT from sage.misc.fast_methods cimport hash_by_id from sage.structure.parent cimport Parent @@ -186,8 +206,8 @@ def LinearConstraintsParent(linear_functions_parent): EXAMPLES:: - sage: from sage.numerical.linear_functions import \ - ... LinearFunctionsParent, LinearConstraintsParent + sage: from sage.numerical.linear_functions import ( + ....: LinearFunctionsParent, LinearConstraintsParent) sage: LF = LinearFunctionsParent(QQ) sage: LinearConstraintsParent(LF) Linear constraints over Rational Field @@ -195,6 +215,287 @@ def LinearConstraintsParent(linear_functions_parent): return LinearConstraintsParent_class(linear_functions_parent) +#***************************************************************************** +# +# Elements of linear functions or constraints +# +#***************************************************************************** + +cdef chained_comparator_left = None +cdef chained_comparator_right = None +cdef LinearConstraint chained_comparator_replace = None + +cdef class LinearFunctionOrConstraint(ModuleElement): + """ + Base class for :class:`LinearFunction` and :class:`LinearConstraint`. + + This class exists solely to implement chaining of inequalities + in constraints. + """ + def __richcmp__(py_left, py_right, int op): + """ + Create an inequality or equality object, possibly chaining + several together. + + EXAMPLES:: + + sage: p. = MixedIntegerLinearProgram() + sage: x[0].__le__(x[1]) + x_0 <= x_1 + + :: + + sage: p = MixedIntegerLinearProgram() + sage: from sage.numerical.linear_functions import LinearFunction + sage: p({2 : 5, 3 : 2}) <= p({2 : 3, 9 : 2}) + 5*x_2 + 2*x_3 <= 3*x_2 + 2*x_9 + + sage: p({2 : 5, 3 : 2}) >= p({2 : 3, 9 : 2}) + 3*x_2 + 2*x_9 <= 5*x_2 + 2*x_3 + + sage: p({2 : 5, 3 : 2}) == p({2 : 3, 9 : 2}) + 5*x_2 + 2*x_3 == 3*x_2 + 2*x_9 + + We can chain multiple (in)equalities:: + + sage: p. = MixedIntegerLinearProgram() + sage: b[0] == 1 == b[1] == 2 == b[2] == 3 + x_0 == 1 == x_1 == 2 == x_2 == 3 + sage: b[0] <= 1 <= b[1] <= 2 <= b[2] <= 3 + x_0 <= 1 <= x_1 <= 2 <= x_2 <= 3 + sage: b[0] <= b[1] <= b[2] <= b[3] + x_0 <= x_1 <= x_2 <= x_3 + + Other comparison operators are not allowed:: + + sage: b[0] < b[1] + Traceback (most recent call last): + ... + ValueError: strict < is not allowed, use <= instead + sage: b[0] > b[1] + Traceback (most recent call last): + ... + ValueError: strict > is not allowed, use >= instead + sage: b[0] != b[1] + Traceback (most recent call last): + ... + ValueError: inequality != is not allowed, use one of <=, ==, >= + + Mixing operators is also not allowed:: + + sage: 1 <= b[1] >= 2 + Traceback (most recent call last): + ... + ValueError: incorrectly chained inequality + sage: 1 >= b[1] <= 2 + Traceback (most recent call last): + ... + ValueError: incorrectly chained inequality + sage: 1 == b[1] <= 2 + Traceback (most recent call last): + ... + ValueError: cannot mix equations and inequalities + sage: 1 >= b[1] == 2 + Traceback (most recent call last): + ... + ValueError: cannot mix equations and inequalities + + TESTS:: + + sage: p. = MixedIntegerLinearProgram() + sage: cm = sage.structure.element.get_coercion_model() + sage: cm.explain(10, p(1), operator.le) + Coercion on left operand via + Conversion map: + From: Integer Ring + To: Linear functions over Real Double Field + Arithmetic performed after coercions. + Result lives in Linear functions over Real Double Field + Linear functions over Real Double Field + + sage: operator.le(10, x[0]) + 10 <= x_0 + sage: x[0] <= 1 + x_0 <= 1 + sage: x[0] >= 1 + 1 <= x_0 + sage: 1 <= x[0] + 1 <= x_0 + sage: 1 >= x[0] + x_0 <= 1 + + This works with non-Sage types too, see :trac:`14540`:: + + sage: p. = MixedIntegerLinearProgram() + sage: int(1) <= b[0] <= int(2) + 1 <= x_0 <= 2 + sage: int(1) >= b[0] >= int(2) + 2 <= x_0 <= 1 + sage: int(1) == b[0] == int(2) + x_0 == 1 == 2 + sage: float(0) <= b[0] <= int(1) <= b[1] <= float(2) + 0 <= x_0 <= 1 <= x_1 <= 2 + """ + global chained_comparator_left + global chained_comparator_right + global chained_comparator_replace + + # Ensure that we use chained_comparator_replace only for this + # call to __richcmp__. + cdef LinearConstraint replace = chained_comparator_replace + chained_comparator_replace = None + + # At least one of py_left or py_right must be of type + # LinearFunctionOrConstraint: figure out its parent + try: + LC = (py_left)._parent + except TypeError: + LC = (py_right)._parent + + # We want the parent to be a LinearConstraintsParent + if not isinstance(LC, LinearConstraintsParent_class): + LC = LinearConstraintsParent(LC) + + # Check op and change >= to <= + cdef bint equality = False + if op == Py_LE: + pass + elif op == Py_GE: + py_left, py_right = py_right, py_left + elif op == Py_EQ: + equality = True + elif op == Py_LT: + raise ValueError("strict < is not allowed, use <= instead") + elif op == Py_GT: + raise ValueError("strict > is not allowed, use >= instead") + else: + raise ValueError("inequality != is not allowed, use one of <=, ==, >=") + + # Convert py_left and py_right to constraints left and right + cdef LinearConstraint left + cdef LinearConstraint right + + try: + left = py_left + except TypeError: + left = LC(py_left, equality=equality) + else: + if left._parent is not LC: + left = LC(left.constraints, equality=left.equality) + + try: + right = py_right + except TypeError: + right = LC(py_right, equality=equality) + else: + if right._parent is not LC: + right = LC(right.constraints, equality=right.equality) + + # HACK to allow chained constraints: Python translates + # x <= y <= z into: + # + # temp = x <= y # calls x.__richcmp__(y) + # if temp: # calls temp.__nonzero__() + # return y <= z # calls y.__richcmp__(z) + # else: + # return temp + # + # or, if x <= y is not implemented (for example, if x is a + # non-Sage type): + # + # temp = y >= x # calls y.__richcmp__(x) + # if temp: # calls temp.__nonzero__() + # return y <= z # calls y.__richcmp__(z) + # else: + # return temp + # + # but we would like x <= y <= z as output. The trick to make it + # work is to store x and y in the first call to __richcmp__ + # and temp in the call to __nonzero__. Then we can replace x + # or y by x <= y in the second call to __richcmp__. + if replace is not None: + # First, check for correctly-chained inequalities + # x <= y <= z or z <= y <= x. + if py_left is chained_comparator_right: + left = replace + elif py_right is chained_comparator_left: + right = replace + # Next, check for wrongly-chained inequalities like + # x <= y >= z. In case of equality, these are accepted + # anyway. + elif py_left is chained_comparator_left: + if not equality: + raise ValueError("incorrectly chained inequality") + left = replace + elif py_right is chained_comparator_right: + if not equality: + raise ValueError("incorrectly chained inequality") + right = replace + + chained_comparator_left = py_left + chained_comparator_right = py_right + + # Finally, chain the (in)equalities + if left.equality != equality or right.equality != equality: + raise ValueError("cannot mix equations and inequalities") + return LC(left.constraints + right.constraints, equality=equality) + + cdef void chained_comparator_hack_nonzero(self): + """ + Hack to allow chained (in)equalities, see ``__richcmp__``. + """ + global chained_comparator_replace + chained_comparator_replace = self + + def __hash__(self): + r""" + Return a hash from the ``id()``. + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram() + sage: f = p({2 : 5, 3 : 2}) + sage: f.__hash__() # random output + 103987752 + sage: d = {} + sage: d[f] = 3 + """ + # see __cmp__() if you want to change the hash function + return hash_by_id(self) + + def __cmp__(left, right): + """ + Implement comparison of two linear functions or constraints. + + EXAMPLES:: + + sage: p = MixedIntegerLinearProgram() + sage: f = p({2 : 5, 3 : 2}) + sage: cmp(f, f) + 0 + sage: abs(cmp(f, f+0)) # since we are comparing by id() + 1 + sage: abs(cmp(f, f+1)) + 1 + sage: len(set([f, f])) + 1 + sage: len(set([f, f+0])) + 2 + sage: len(set([f, f+1])) + 2 + sage: abs(cmp(f <= 0, f <= 0)) + 1 + """ + # Note: if you want to implement smarter comparison, you also + # need to change __hash__(). The comparison function must + # satisfy cmp(x,y)==0 => hash(x)==hash(y) + if left is right: + return 0 + if left < right: + return -1 + else: + return 1 + #***************************************************************************** # @@ -221,6 +522,20 @@ cdef class LinearFunctionsParent_class(Parent): sage: LinearFunctionsParent_class """ + def __cinit__(self): + """ + Cython initializer + + TESTS:: + + sage: from sage.numerical.linear_functions import LinearFunctionsParent_class + sage: LF = LinearFunctionsParent_class.__new__(LinearFunctionsParent_class) + sage: LF._multiplication_symbol + '*' + """ + # Do not use coercion framework for __richcmp__ + self.flags |= Parent_richcmp_element_without_coercion + self._multiplication_symbol = '*' def __init__(self, base_ring): """ @@ -234,7 +549,6 @@ cdef class LinearFunctionsParent_class(Parent): """ from sage.categories.modules_with_basis import ModulesWithBasis Parent.__init__(self, base=base_ring, category=ModulesWithBasis(base_ring)) - self._multiplication_symbol = '*' def set_multiplication_symbol(self, symbol='*'): """ @@ -264,7 +578,7 @@ cdef class LinearFunctionsParent_class(Parent): """ self._multiplication_symbol = symbol - cpdef _get_multiplication_symbol(self): + def _get_multiplication_symbol(self): """ Return the multiplication symbol. @@ -426,7 +740,7 @@ cdef class LinearFunctionsParent_class(Parent): # #***************************************************************************** -cdef class LinearFunction(ModuleElement): +cdef class LinearFunction(LinearFunctionOrConstraint): r""" An elementary algebra to represent symbolic linear functions. @@ -504,7 +818,7 @@ cdef class LinearFunction(ModuleElement): sage: x = p.new_variable() sage: f = 0.5 + 3/2*x[1] + 0.6*x[3] sage: for id, coeff in f.iteritems(): - ... print 'id =', id, ' coeff =', coeff + ....: print 'id =', id, ' coeff =', coeff id = 0 coeff = 3/2 id = 1 coeff = 3/5 id = -1 coeff = 1/2 @@ -740,10 +1054,24 @@ cdef class LinearFunction(ModuleElement): sage: f._coeff_formatter(RDF(12.3)) '12.3*' - sage: q = MixedIntegerLinearProgram(solver='ppl') + sage: p = MixedIntegerLinearProgram(solver='ppl') sage: f = p(1) sage: f._coeff_formatter(13/45) '13/45*' + + sage: from sage.rings.number_field.number_field import QuadraticField + sage: K. = QuadraticField(5, 'sqrt5') + sage: p = MixedIntegerLinearProgram(solver='interactivelp', base_ring=K) + sage: f = p(1) + sage: f._coeff_formatter(sqrt5) + 'sqrt5*' + + sage: from sage.rings.all import AA + sage: sqrt5 = AA(5).sqrt() + sage: p = MixedIntegerLinearProgram(solver='interactivelp', base_ring=AA) + sage: f = p(1) + sage: f._coeff_formatter(sqrt5) + '2.236067977499790?*' """ R = self.base_ring() if coeff == R.one() and not constant_term: @@ -751,12 +1079,12 @@ cdef class LinearFunction(ModuleElement): try: from sage.rings.all import ZZ coeff = ZZ(coeff) # print as integer if possible - except TypeError: + except (TypeError, ValueError): pass if constant_term: return str(coeff) else: - return str(coeff) + self.parent()._get_multiplication_symbol() + return str(coeff) + self.parent()._multiplication_symbol def _repr_(self): r""" @@ -838,121 +1166,6 @@ cdef class LinearFunction(ModuleElement): """ return (left-right).is_zero() - def __richcmp__(left, right, int op): - """ - Create an inequality or equality object. - - EXAMPLES:: - - sage: p = MixedIntegerLinearProgram() - sage: x = p.new_variable() - sage: x[0].__le__(x[1]) # indirect doctest - x_0 <= x_1 - - :: - - sage: p = MixedIntegerLinearProgram() - sage: from sage.numerical.linear_functions import LinearFunction - sage: p({2 : 5, 3 : 2}) <= p({2 : 3, 9 : 2}) - 5*x_2 + 2*x_3 <= 3*x_2 + 2*x_9 - - sage: p({2 : 5, 3 : 2}) >= p({2 : 3, 9 : 2}) - 3*x_2 + 2*x_9 <= 5*x_2 + 2*x_3 - - sage: p({2 : 5, 3 : 2}) == p({2 : 3, 9 : 2}) - 5*x_2 + 2*x_3 == 3*x_2 + 2*x_9 - - sage: p({2 : 5, 3 : 2}) < p({2 : 3, 9 : 2}) - Traceback (most recent call last): - ... - ValueError: strict < is not allowed, use <= instead. - - sage: p({2 : 5, 3 : 2}) > p({2 : 3, 9 : 2}) - Traceback (most recent call last): - ... - ValueError: strict > is not allowed, use >= instead. - - TESTS:: - - sage: cm = sage.structure.element.get_coercion_model() - sage: cm.explain(10, p(1), operator.le) - Coercion on left operand via - Conversion map: - From: Integer Ring - To: Linear functions over Real Double Field - Arithmetic performed after coercions. - Result lives in Linear functions over Real Double Field - Linear functions over Real Double Field - - sage: x = p.new_variable() - sage: operator.le(10, x[0]) - 10 <= x_0 - sage: x[0] <= 1 - x_0 <= 1 - sage: x[0] >= 1 - 1 <= x_0 - sage: 1 <= x[0] - 1 <= x_0 - sage: 1 >= x[0] - x_0 <= 1 - """ - LF = (left)._parent - LC = LinearConstraintsParent(LF) - equality = (op == Py_EQ) - cdef LinearConstraint left_constraint = LC(left, equality=equality) - cdef LinearConstraint right_constraint = LC(right, equality=equality) - if op == Py_EQ or op == Py_LE or op == Py_GE: - return PyObject_RichCompare(left_constraint, right_constraint, op) - elif op == Py_LT: - raise ValueError("strict < is not allowed, use <= instead.") - elif op == Py_GT: - raise ValueError("strict > is not allowed, use >= instead.") - elif op == Py_NE: - raise ValueError("inequality != is not allowed, use one of <=, ==, >=.") - else: - assert(False) # unreachable - - def __hash__(self): - r""" - Return a hash. - - EXAMPLES:: - - sage: p = MixedIntegerLinearProgram() - sage: f = p({2 : 5, 3 : 2}) - sage: f.__hash__() # random output - 103987752 - sage: d = {} - sage: d[f] = 3 - """ - # see __cmp__() if you want to change the hash function - return hash_by_id( self) - - def __cmp__(left, right): - """ - Implement comparison of two linear functions. - - EXAMPLES:: - - sage: p = MixedIntegerLinearProgram() - sage: f = p({2 : 5, 3 : 2}) - sage: cmp(f, f) - 0 - sage: abs(cmp(f, f+0)) # since we are comparing by id() - 1 - sage: abs(cmp(f, f+1)) - 1 - sage: len(set([f, f])) - 1 - sage: len(set([f, f+0])) - 2 - sage: len(set([f, f+1])) - 2 - """ - # Note: if you want to implement smarter comparison, you also - # need to change __hash__(). The comparison function must - # satisfy cmp(x,y)==0 => hash(x)==hash(y) - return cmp(id(left), id(right)) #***************************************************************************** # @@ -984,6 +1197,25 @@ cdef class LinearConstraintsParent_class(Parent): sage: LinearConstraintsParent(p.linear_functions_parent()) is LC True """ + def __cinit__(self, linear_functions_parent): + """ + Cython initializer + + TESTS:: + + sage: from sage.numerical.linear_functions import LinearConstraintsParent_class + sage: from sage.numerical.linear_functions import LinearFunctionsParent + sage: LF = LinearFunctionsParent(RDF) + sage: LinearConstraintsParent_class.__new__(LinearConstraintsParent_class, LF) + Linear constraints over Real Double Field + sage: LinearConstraintsParent_class.__new__(LinearConstraintsParent_class, None) + Traceback (most recent call last): + ... + TypeError: Cannot convert NoneType to sage.numerical.linear_functions.LinearFunctionsParent_class + """ + self._LF = linear_functions_parent + # Do not use coercion framework for __richcmp__ + self.flags |= Parent_richcmp_element_without_coercion def __init__(self, linear_functions_parent): """ @@ -1000,9 +1232,12 @@ cdef class LinearConstraintsParent_class(Parent): sage: from sage.numerical.linear_functions import LinearConstraintsParent sage: LinearConstraintsParent(LF) Linear constraints over Real Double Field + sage: LinearConstraintsParent(None) + Traceback (most recent call last): + ... + TypeError: Cannot convert NoneType to sage.numerical.linear_functions.LinearFunctionsParent_class """ Parent.__init__(self) - self._LF = linear_functions_parent def linear_functions_parent(self): """ @@ -1127,17 +1362,13 @@ cdef class LinearConstraintsParent_class(Parent): return self(0) <= LF.an_element() - #***************************************************************************** # # Elements of linear constraints # #***************************************************************************** -_chained_comparator_hack_search = None -_chained_comparator_hack_replace = None - -cdef class LinearConstraint(Element): +cdef class LinearConstraint(LinearFunctionOrConstraint): """ A class to represent formal Linear Constraints. @@ -1146,8 +1377,8 @@ cdef class LinearConstraint(Element): write ``LinearFunction1 <= LinearFunction2`` to define the corresponding constraint, which can potentially involve several layers of such - inequalities (``(A <= B <= C``), or even equalities - like ``A == B``. + inequalities (``A <= B <= C``), or even equalities + like ``A == B == C``. Trivial constraints (meaning that they have only one term and no relation) are also allowed. They are required for the coercion @@ -1231,51 +1462,6 @@ cdef class LinearConstraint(Element): return False return True - cdef LinearConstraint _chained_comparator_hack_part1(LinearConstraint left, LinearConstraint right): - """ - Evil hack to allow chained constraints - - Python translates ``x < y < z`` into:: - - temp = x <= y # calls x.__richcmp__(y) - if temp: # calls temp.__nonzero__() - return y <= z # calls y.__richcmp__(z) - else: - return temp - - but we would like ``x<=y<=z`` as output. The trick to make it - work is to store ``y`` in the first call to ``__richcmp__()`` - and ``temp`` in the call to ``__nonzero__()``. Then we can - replace ``y`` by ``x<=y`` in the second call to - ``__richcmp__``. - - This function implements the first part of this hack, to be - called from :meth:`__richcmp__`. - """ - # print '__richcmp__', left, ' compared with', right - global _chained_comparator_hack_search - global _chained_comparator_hack_replace - cdef LinearConstraint search = _chained_comparator_hack_search - cdef LinearConstraint replace = _chained_comparator_hack_replace - _chained_comparator_hack_search = right - if replace is None: - return left - assert search is not None - if search.equals(left): - _chained_comparator_hack_replace = None - return replace - else: - return left - - cdef _chained_comparator_hack_part2(self): - """ - This function implements the first part of this hack, to be - called from :meth:`__nonzero__`. - """ - # print '__nonzero__', self.constraints - global _chained_comparator_hack_replace - _chained_comparator_hack_replace = self - def is_equation(self): """ Whether the constraint is a chained equation @@ -1352,7 +1538,7 @@ cdef class LinearConstraint(Element): sage: list(ieq) [1, x_0, x_1, 3, x_2] sage: for term in ieq: - ... print term + ....: print term 1 x_0 x_1 @@ -1377,9 +1563,8 @@ cdef class LinearConstraint(Element): sage: b = p.new_variable() sage: eqns = 1 == b[0] == b[2] == 3 == b[3]; eqns 1 == x_0 == x_1 == 3 == x_2 - sage: for lhs, rhs in eqns.equations(): - ... print str(lhs) + ' == ' + str(rhs) + ....: print str(lhs) + ' == ' + str(rhs) 1 == x_0 x_0 == x_1 x_1 == 3 @@ -1412,7 +1597,7 @@ cdef class LinearConstraint(Element): 1 <= x_0 <= x_1 <= 3 <= x_2 sage: for lhs, rhs in ieq.inequalities(): - ... print str(lhs) + ' <= ' + str(rhs) + ....: print str(lhs) + ' <= ' + str(rhs) 1 <= x_0 x_0 <= x_1 x_1 <= 3 @@ -1467,51 +1652,5 @@ cdef class LinearConstraint(Element): sage: ieq <= ieq <= ieq x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 <= x_0 <= 9 + x_1 """ - self._chained_comparator_hack_part2() + self.chained_comparator_hack_nonzero() return True - - def __richcmp__(py_left, py_right, int op): - """ - Chain (in)equalities - - EXAMPLES:: - - sage: p = MixedIntegerLinearProgram() - sage: b = p.new_variable() - sage: b[0] <= b[1] <= b[2] <= b[3] - x_0 <= x_1 <= x_2 <= x_3 - sage: b[0] <= 1 <= b[1] <= 2 <= b[2] <= 3 - x_0 <= 1 <= x_1 <= 2 <= x_2 <= 3 - """ - cdef LinearConstraint left = py_left - LC = left._parent - cdef LinearConstraint right - try: - right = py_right - except TypeError: - right = LC(py_right, equality=left.is_equation()) - - if right._parent is not LC: - right = LC(right.constraints, equality=left.is_equation()) - - left = left._chained_comparator_hack_part1(right) - if op == Py_LT: - raise ValueError("strict < is not allowed, use <= instead.") - elif op == Py_EQ: - if not (left.is_equation() and right.is_equation()): - raise ValueError("can only chain together equations") - return LC(left.constraints + right.constraints, equality=True) - elif op == Py_GT: - raise ValueError("strict > is not allowed, use >= instead.") - elif op == Py_LE: - if not (left.is_less_or_equal() and right.is_less_or_equal()): - raise ValueError("can only chain together inequalities") - return LC(left.constraints + right.constraints) - elif op == Py_NE: - raise ValueError("inequality != is not allowed, use one of <=, ==, >=.") - elif op == Py_GE: - if not (left.is_less_or_equal() and right.is_less_or_equal()): - raise ValueError("can only chain together inequalities") - return LC(right.constraints + left.constraints) - else: - assert(False) # unreachable diff --git a/src/sage/numerical/linear_tensor_constraints.py b/src/sage/numerical/linear_tensor_constraints.py index 24d9daa922a..97d9222460c 100644 --- a/src/sage/numerical/linear_tensor_constraints.py +++ b/src/sage/numerical/linear_tensor_constraints.py @@ -19,7 +19,6 @@ [0 0 0] <= [0 x_0 + x_1 0 ] [0 0 0] [x_0 0 x_1] """ - #***************************************************************************** # Copyright (C) 2014 Volker Braun # @@ -28,6 +27,8 @@ # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +# python3 +from __future__ import division from sage.structure.parent import Parent from sage.structure.element import Element @@ -256,9 +257,10 @@ def _ascii_art_(self): [0 0] [3*x_0 4*x_0] """ from sage.typeset.ascii_art import AsciiArt + def matrix_art(m): lines = str(m).splitlines() - return AsciiArt(lines, baseline=len(lines)/2) + return AsciiArt(lines, baseline=len(lines) // 2) comparator = AsciiArt([' == ' if self.is_equation() else ' <= ']) return matrix_art(self.lhs()) + comparator + matrix_art(self.rhs()) diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index d374987f8a3..12820640347 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -267,6 +267,10 @@ cdef class MixedIntegerLinearProgram(SageObject): - If ``solver=None`` (default), the default solver is used (see :func:`default_mip_solver`) + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. + - ``maximization`` - When set to ``True`` (default), the ``MixedIntegerLinearProgram`` @@ -331,7 +335,7 @@ cdef class MixedIntegerLinearProgram(SageObject): def __init__(self, solver=None, maximization=True, constraint_generation=False, check_redundant=False, - names=tuple()): + names=tuple(), base_ring=None): r""" Constructor for the ``MixedIntegerLinearProgram`` class. @@ -342,6 +346,9 @@ cdef class MixedIntegerLinearProgram(SageObject): - GLPK (``solver="GLPK"``). See the `GLPK `_ web site. + - GLPK's implementation of an exact rational simplex + method (``solver="GLPK/exact"``). + - COIN Branch and Cut (``solver="Coin"``). See the `COIN-OR `_ web site. @@ -358,8 +365,12 @@ cdef class MixedIntegerLinearProgram(SageObject): - PPL (``solver="PPL"``). See the `PPL `_ web site. - -If ``solver=None`` (default), the default solver is used (see - ``default_mip_solver`` method. + - If ``solver=None`` (default), the default solver is used, see + :func:`default_mip_solver`. + + - ``solver`` can also be a callable, + see :func:`sage.numerical.backends.generic_backend.get_solver` for + examples. - ``maximization`` @@ -431,7 +442,8 @@ cdef class MixedIntegerLinearProgram(SageObject): self._first_variable_names = list(names) from sage.numerical.backends.generic_backend import get_solver self._backend = get_solver(solver=solver, - constraint_generation=constraint_generation) + constraint_generation=constraint_generation, + base_ring=base_ring) if not maximization: self._backend.set_sense(-1) @@ -535,8 +547,11 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: q.number_of_constraints() 1 """ + def copying_solver(**kwdargs): + return ( self._backend).copy() + cdef MixedIntegerLinearProgram p = \ - MixedIntegerLinearProgram(solver="GLPK") + MixedIntegerLinearProgram(solver=copying_solver) try: p._variables = copy(self._variables) except AttributeError: @@ -553,9 +568,37 @@ cdef class MixedIntegerLinearProgram(SageObject): except AttributeError: pass - p._backend = ( self._backend).copy() return p + def __deepcopy__(self, memo={}): + """ + Return a deep copy of ``self``. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram() + sage: b = p.new_variable() + sage: p.add_constraint(b[1] + b[2] <= 6) + sage: p.set_objective(b[1] + b[2]) + sage: cp = deepcopy(p) + sage: cp.solve() + 6.0 + + TEST: + + Test that `deepcopy` makes actual copies but preserves identities:: + + sage: mip = MixedIntegerLinearProgram() + sage: ll = [mip, mip] + sage: dcll=deepcopy(ll) + sage: ll[0] is dcll[0] + False + sage: dcll[0] is dcll[1] + True + + """ + return self.__copy__() + def __getitem__(self, v): r""" Returns the symbolic variable corresponding to the key @@ -597,6 +640,14 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p = MixedIntegerLinearProgram(solver='ppl') sage: p.base_ring() Rational Field + sage: from sage.rings.all import AA + sage: p = MixedIntegerLinearProgram(solver='InteractiveLP', base_ring=AA) + sage: p.base_ring() + Algebraic Real Field + sage: d = polytopes.dodecahedron() + sage: p = MixedIntegerLinearProgram(base_ring=d.base_ring()) + sage: p.base_ring() + Number Field in sqrt5 with defining polynomial x^2 - 5 """ return self._backend.base_ring() @@ -935,7 +986,7 @@ cdef class MixedIntegerLinearProgram(SageObject): # Weird Input else: - raise ValueError, "constraints() requires a list of integers, though it will accommodate None or an integer." + raise ValueError("constraints() requires a list of integers, though it will accommodate None or an integer.") def polyhedron(self, **kwds): r""" @@ -1121,6 +1172,18 @@ cdef class MixedIntegerLinearProgram(SageObject): Variables: x_0 is a continuous variable (min=0, max=+oo) x_1 is a continuous variable (min=0, max=+oo) + + With a constant term in the objective:: + + sage: p = MixedIntegerLinearProgram(solver='ppl') + sage: x = p.new_variable(nonnegative=True) + sage: p.set_objective(x[0] + 42) + sage: p.show() + Maximization: + x_0 + 42 + Constraints: + Variables: + x_0 is a continuous variable (min=0, max=+oo) """ cdef int i, j cdef GenericBackend b = self._backend @@ -1148,8 +1211,9 @@ cdef class MixedIntegerLinearProgram(SageObject): ("" if c == 1 else ("- " if c == -1 else str(c)+" "))+varid_name[i] ), first = False - if b.obj_constant_term > self._backend.zero(): print "+", b.obj_constant_term - elif b.obj_constant_term < self._backend.zero(): print "-", -b.obj_constant_term + d = b.objective_constant_term() + if d > self._backend.zero(): print "+", d, + elif d < self._backend.zero(): print "-", -d, print ##### Constraints @@ -1283,7 +1347,7 @@ cdef class MixedIntegerLinearProgram(SageObject): sage: p.solve() 6.0 - To return the optimal value of ``y[2,9]``:: + To return the optimal value of ``y[2,9]``:: sage: p.get_values(y[2,9]) 2.0 @@ -1306,10 +1370,42 @@ cdef class MixedIntegerLinearProgram(SageObject): Or:: sage: [x_sol, y_sol] = p.get_values([x, y]) + + TESTS: + + Test that an error is reported when we try to get the value + of something that is not a variable in this problem:: + + sage: p.get_values("Something strange") + Traceback (most recent call last): + ... + TypeError: Not a MIPVariable: ... + sage: p.get_values("Something stranger", 4711) + Traceback (most recent call last): + ... + TypeError: Not a MIPVariable: ... + sage: M1 = MixedIntegerLinearProgram() + sage: M2 = MixedIntegerLinearProgram() + sage: x = M1.new_variable() + sage: y = M1.new_variable() + sage: z = M2.new_variable() + sage: M2.add_constraint(z[0] <= 5) + sage: M2.solve() + 0.0 + sage: M2.get_values(x) + Traceback (most recent call last): + ... + ValueError: ... + sage: M2.get_values(y) + Traceback (most recent call last): + ... + ValueError: ... """ val = [] for l in lists: if isinstance(l, MIPVariable): + if self != l.mip(): + raise ValueError("Variable {!r} is a variable from a different problem".format(l)) c = {} for (k,v) in l.items(): c[k] = self._backend.get_variable_value(self._variables[v]) @@ -1323,6 +1419,8 @@ cdef class MixedIntegerLinearProgram(SageObject): val.append(c) elif l in self._variables: val.append(self._backend.get_variable_value(self._variables[l])) + else: + raise TypeError("Not a MIPVariable: {!r}".format(l)) if len(lists) == 1: return val[0] @@ -1336,7 +1434,7 @@ cdef class MixedIntegerLinearProgram(SageObject): INPUT: - ``obj`` -- A linear function to be optimized. - ( can also be set to ``None`` or ``0`` when just + ( can also be set to ``None`` or ``0`` or any number when just looking for a feasible solution ) EXAMPLE: @@ -1363,6 +1461,17 @@ cdef class MixedIntegerLinearProgram(SageObject): 6.66667 sage: p.set_objective(None) sage: _ = p.solve() + + TESTS: + + Test whether numbers as constant objective functions are accepted:: + + sage: p = MixedIntegerLinearProgram(maximization=True) + sage: x = p.new_variable(nonnegative=True) + sage: p.set_objective(42) + sage: p.solve() # tol 1e-8 + 42 + """ cdef list values = [] @@ -1372,10 +1481,16 @@ cdef class MixedIntegerLinearProgram(SageObject): # and do not care about any function being optimal. cdef int i - if obj is not None: - f = obj.dict() - else: + if obj is None: f = {-1 : 0} + else: + # See if it is a constant + R = self.base_ring() + try: + f = {-1: R(obj)} + except TypeError: + # Should be a linear function + f = obj.dict() d = f.pop(-1,self._backend.zero()) for i in range(self._backend.ncols()): @@ -2346,7 +2461,7 @@ cdef class MixedIntegerLinearProgram(SageObject): EXAMPLE: - This example uses the simplex algorthm and prints information:: + This example uses the simplex algorithm and prints information:: sage: p = MixedIntegerLinearProgram(solver="GLPK") sage: x, y = p[0], p[1] @@ -2605,67 +2720,50 @@ cdef class MixedIntegerLinearProgram(SageObject): class MIPSolverException(RuntimeError): r""" Exception raised when the solver fails. - """ - - def __init__(self, value): - r""" - Constructor for ``MIPSolverException``. - - ``MIPSolverException`` is the exception raised when the solver fails. - - EXAMPLE:: - - sage: from sage.numerical.mip import MIPSolverException - sage: MIPSolverException("Error") - MIPSolverException() - - TESTS: - No continuous solution:: + EXAMPLE:: - sage: p=MixedIntegerLinearProgram(solver="GLPK") - sage: v=p.new_variable(nonnegative=True) - sage: p.add_constraint(v[0],max=5.5) - sage: p.add_constraint(v[0],min=7.6) - sage: p.set_objective(v[0]) + sage: from sage.numerical.mip import MIPSolverException + sage: e = MIPSolverException("Error") + sage: e + MIPSolverException('Error',) + sage: print e + Error - Tests of GLPK's Exceptions:: + TESTS: - sage: p.solve() - Traceback (most recent call last): - ... - MIPSolverException: 'GLPK : Problem has no feasible solution' + No continuous solution:: - No integer solution:: + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: v = p.new_variable(nonnegative=True) + sage: p.add_constraint(v[0],max=5.5) + sage: p.add_constraint(v[0],min=7.6) + sage: p.set_objective(v[0]) - sage: p=MixedIntegerLinearProgram(solver="GLPK") - sage: v=p.new_variable(nonnegative=True) - sage: p.add_constraint(v[0],max=5.6) - sage: p.add_constraint(v[0],min=5.2) - sage: p.set_objective(v[0]) - sage: p.set_integer(v) + Tests of GLPK's Exceptions:: - Tests of GLPK's Exceptions:: + sage: p.solve() + Traceback (most recent call last): + ... + MIPSolverException: GLPK: Problem has no feasible solution - sage: p.solve() - Traceback (most recent call last): - ... - MIPSolverException: 'GLPK : Problem has no feasible solution' - """ - self.value = value + No integer solution:: - def __str__(self): - r""" - Returns the value of the instance of ``MIPSolverException``. + sage: p = MixedIntegerLinearProgram(solver="GLPK") + sage: v = p.new_variable(nonnegative=True) + sage: p.add_constraint(v[0],max=5.6) + sage: p.add_constraint(v[0],min=5.2) + sage: p.set_objective(v[0]) + sage: p.set_integer(v) - EXAMPLE:: + Tests of GLPK's Exceptions:: - sage: from sage.numerical.mip import MIPSolverException - sage: e = MIPSolverException("Error") - sage: print e - 'Error' - """ - return repr(self.value) + sage: p.solve() + Traceback (most recent call last): + ... + MIPSolverException: GLPK: Problem has no feasible solution + """ + pass cdef class MIPVariable(Element): @@ -2857,6 +2955,19 @@ cdef class MIPVariable(Element): """ return self._dict.values() + def mip(self): + r""" + Returns the :class:`MixedIntegerLinearProgram` in which ``self`` is a variable. + + EXAMPLE:: + + sage: p = MixedIntegerLinearProgram() + sage: v = p.new_variable(nonnegative=True) + sage: p == v.mip() + True + """ + return self._p + cdef _matrix_rmul_impl(self, m): """ Implement the action of a matrix multiplying from the right. diff --git a/src/sage/numerical/optimize.py b/src/sage/numerical/optimize.py index c5a03b7936a..1991a2ba638 100644 --- a/src/sage/numerical/optimize.py +++ b/src/sage/numerical/optimize.py @@ -415,7 +415,7 @@ def minimize_constrained(func,cons,x0,gradient=None,algorithm='default', **args) REFERENCES: - .. [ZBN97] C. Zhu, R. H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: + .. [ZBN97] \C. Zhu, R. H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization. ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp.550--560, 1997. diff --git a/src/sage/numerical/sdp.pyx b/src/sage/numerical/sdp.pyx index 6499c68cccc..e1d9887ec82 100644 --- a/src/sage/numerical/sdp.pyx +++ b/src/sage/numerical/sdp.pyx @@ -173,8 +173,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" - from sage.structure.parent cimport Parent from sage.structure.element cimport Element from sage.misc.cachefunc import cached_method diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py new file mode 100644 index 00000000000..6275e56d334 --- /dev/null +++ b/src/sage/parallel/map_reduce.py @@ -0,0 +1,1940 @@ +r""" +Parallel computations using RecursivelyEnumeratedSet and Map-Reduce + +There exists an efficient way to distribute computations when you have a set +`S` of objects defined by :func:`RecursivelyEnumeratedSet` (see +:mod:`sage.sets.recursively_enumerated_set` for more details) over which you +would like to perform the following kind of operations : + +* Compute the cardinality of a (very large) set defined recursively (through a + call to + :class:`RecursivelyEnumeratedSet of forest type`) + +* More generally, compute any kind of generating series over this set + +* Test a conjecture : i.e. find an element of `S` satisfying a specific + property; conversely, check that all of them do + +* Count/list the elements of `S` having a specific property + +* Apply any map/reduce kind of operation over the elements of `S` + +AUTHORS : + +- Florent Hivert -- code, documentation (2012-2016) + +- Jean Baptiste Priez -- prototype, debugging help on MacOSX (2011-June, 2016) +- Nathann Cohen -- Some doc (2012) + +Contents +-------- + +- :ref:`basic-usage` +- :ref:`advanced-use` +- :ref:`profiling` +- :ref:`logging` +- :ref:`protocol-description` +- :ref:`examples` + +How is this different from usual MapReduce ? +-------------------------------------------- + +This implementation is specific to +:class:`RecursivelyEnumeratedSet of forest type`, +and uses its properties to do its job. Not only mapping +and reducing is done on different processors but also **generating the elements +of** `S`. + +.. _basic-usage: + +How can I use all that stuff? +----------------------------- + +First, you need the information necessary to describe a +:class:`RecursivelyEnumeratedSet of forest +type` representing your set `S` (see +:mod:`sage.sets.recursively_enumerated_set`). Then, you need to provide a Map +function as well as a Reduce function. Here are some examples : + +* **Counting the number of elements**: In this situation, the map function + can be set to ``lambda x : 1``, and the reduce function just adds the + values together, i.e. ``lambda x,y : x+y``. + + Here's the Sage code for binary words of length `\leq 16` :: + + sage: seeds = [[]] + sage: succ = lambda l: [l+[0], l+[1]] if len(l) <= 15 else [] + sage: S = RecursivelyEnumeratedSet(seeds, succ, + ....: structure='forest', enumeration='depth') + sage: map_function = lambda x: 1 + sage: reduce_function = lambda x,y: x+y + sage: reduce_init = 0 + sage: S.map_reduce(map_function, reduce_function, reduce_init) + 131071 + + One can check that this is indeed the number of binary words of + length `\leq 16` :: + + sage: factor(131071 + 1) + 2^17 + + + Note that the function mapped and reduced here are equivalent to the default + values of the :meth:`sage.combinat.backtrack.SearchForest.map_reduce` method + so that to compute the number of element you only need to call:: + + sage: S.map_reduce() + 131071 + + You don't need to use :func:`RecursivelyEnumeratedSet`, you can use directly + :class:`RESetMapReduce`. This is needed if you want to have fine + control over the parallel execution (see :ref:`advanced-use` below):: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce( + ....: roots = [[]], + ....: children = lambda l: [l+[0], l+[1]] if len(l) <= 15 else [], + ....: map_function = lambda x : 1, + ....: reduce_function = lambda x,y: x+y, + ....: reduce_init = 0 ) + sage: S.run() + 131071 + +* **Generating series**: In this situation, the map function associates a + monomial to each element of `S`, while the Reduce function is still equal to + ``lambda x,y : x+y``. + + Here's the Sage code for binary words of length `\leq 16` :: + + sage: S = RecursivelyEnumeratedSet( + ....: [[]], lambda l: [l+[0], l+[1]] if len(l) < 16 else [], + ....: structure='forest', enumeration='depth') + sage: sp = S.map_reduce( + ....: map_function = lambda z: x**len(z), + ....: reduce_function = lambda x,y: x+y, + ....: reduce_init = 0 ) + sage: sp + 65536*x^16 + 32768*x^15 + 16384*x^14 + 8192*x^13 + 4096*x^12 + 2048*x^11 + 1024*x^10 + 512*x^9 + 256*x^8 + 128*x^7 + 64*x^6 + 32*x^5 + 16*x^4 + 8*x^3 + 4*x^2 + 2*x + 1 + + This is of course `\sum_{i=0}^{i=16} (2x)^i`:: + + sage: bool(sp == sum((2*x)^i for i in range(17))) + True + + Here is another example where we count permutations of size `\leq 8` (here + we use the default values):: + + sage: S = RecursivelyEnumeratedSet( [[]], + ....: lambda l: ([l[:i] + [len(l)] + l[i:] for i in range(len(l)+1)] + ....: if len(l) < 8 else []), + ....: structure='forest', enumeration='depth') + sage: sp = S.map_reduce(lambda z: x**len(z)); sp + 40320*x^8 + 5040*x^7 + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + This is of course `\sum_{i=0}^{i=8} i! x^i`:: + + sage: bool(sp == sum(factorial(i)*x^i for i in range(9))) + True + +* **Post Processing**: We now demonstrate the use of ``post_process``. We + generate the permutation as previously, but we only perform the map/reduce + computation on those of even ``len``. Of course we get the even part of the + previous generating series:: + + sage: S = RecursivelyEnumeratedSet( [[]], + ....: lambda l: ([l[:i] + [len(l)+1] + l[i:] for i in range(len(l)+1)] + ....: if len(l) < 8 else []), + ....: post_process = lambda l : l if len(l) % 2 == 0 else None, + ....: structure='forest', enumeration='depth') + sage: sp = S.map_reduce(lambda z: x**len(z)); sp + 40320*x^8 + 720*x^6 + 24*x^4 + 2*x^2 + 1 + + This is also useful for example to call a constructor on the generated + elements:: + + sage: S = RecursivelyEnumeratedSet( [[]], + ....: lambda l: ([l[:i] + [len(l)+1] + l[i:] for i in range(len(l)+1)] + ....: if len(l) < 5 else []), + ....: post_process = lambda l : Permutation(l) if len(l) == 5 else None, + ....: structure='forest', enumeration='depth') + sage: sp = S.map_reduce(lambda z: x**(len(z.inversions()))); sp + x^10 + 4*x^9 + 9*x^8 + 15*x^7 + 20*x^6 + 22*x^5 + 20*x^4 + 15*x^3 + 9*x^2 + 4*x + 1 + + We get here a polynomial called the `x`-factorial of `5` that is + `\prod_{i=1}^{i=5} \frac{1-x^i}{1-x}`:: + + sage: (prod((1-x^i)/(1-x) for i in range(1,6))).simplify_rational() + x^10 + 4*x^9 + 9*x^8 + 15*x^7 + 20*x^6 + 22*x^5 + 20*x^4 + 15*x^3 + 9*x^2 + 4*x + 1 + + +* **Listing the objects**: One can also compute the list of objects in a + :class:`RecursivelyEnumeratedSet of forest type` + using :class:`RESetMapReduce`. As an example, we compute the set of number + beetween 1 and 63, generated by their binary expansion:: + + sage: S = RecursivelyEnumeratedSet( [1], + ....: lambda l: [(l<<1)|0, (l<<1)|1] if l < 1<<5 else [], + ....: structure='forest', enumeration='depth') + + Here is the list computed without :class:`RESetMapReduce`:: + + sage: serial = list(S) + sage: serial + [1, 2, 4, 8, 16, 32, 33, 17, 34, 35, 9, 18, 36, 37, 19, 38, 39, 5, 10, 20, 40, 41, 21, 42, 43, 11, 22, 44, 45, 23, 46, 47, 3, 6, 12, 24, 48, 49, 25, 50, 51, 13, 26, 52, 53, 27, 54, 55, 7, 14, 28, 56, 57, 29, 58, 59, 15, 30, 60, 61, 31, 62, 63] + + Here is how to perform the parallel computation. The order of the lists + depends on the synchronisation of the various computation processes and + therefore should be considered as random:: + + sage: parall = S.map_reduce( lambda x: [x], lambda x,y: x+y, [] ) + sage: parall # random + [1, 3, 7, 15, 31, 63, 62, 30, 61, 60, 14, 29, 59, 58, 28, 57, 56, 6, 13, 27, 55, 54, 26, 53, 52, 12, 25, 51, 50, 24, 49, 48, 2, 5, 11, 23, 47, 46, 22, 45, 44, 10, 21, 43, 42, 20, 41, 40, 4, 9, 19, 39, 38, 18, 37, 36, 8, 17, 35, 34, 16, 33, 32] + sage: sorted(serial) == sorted(parall) + True + + +.. _advanced-use: + +Advanced use +------------ + +Fine control of the execution of a map/reduce computations is obtained by +passing parameters to the :meth:`RESetMapReduce.run` method. One can use the +three following parameters: + +- ``max_proc`` -- maximum number of process used. + default: number of processor on the machine +- ``timeout`` -- a timeout on the computation (default: ``None``) +- ``reduce_locally`` -- whether the workers should reduce locally + their work or sends results to the master as soon as possible. + See :class:`RESetMapReduceWorker` for details. + + +Here is an example or how to deal with timeout:: + + + sage: from sage.parallel.map_reduce import RESetMPExample, AbortError + sage: EX = RESetMPExample(maxl = 8) + sage: try: + ....: res = EX.run(timeout=0.1) + ....: except AbortError: + ....: print "Computation timeout" + ....: else: + ....: print "Computation normally finished" + ....: res + Computation timeout + +The following should not timeout even on a very slow machine:: + + sage: try: + ....: res = EX.run(timeout=60) + ....: except AbortError: + ....: print "Computation Timeout" + ....: else: + ....: print "Computation normally finished" + ....: res + Computation normally finished + 40320*x^8 + 5040*x^7 + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + +As for ``reduce_locally``, one should not see any difference, except for speed +during normal usage. Most of the time the user should leave it to ``True``, +unless he sets up a mecanism to consume the partial results as soon as they +arrive. See :class:`RESetParallelIterator` and in particular the ``__iter__`` +method for a example of consumer use. + + +.. _profiling: + +Profiling +--------- + +It is possible the profile a map/reduce computation. First we create a +:class:`RESetMapReduce` object:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce( + ....: roots = [[]], + ....: children = lambda l: [l+[0], l+[1]] if len(l) <= 15 else [], + ....: map_function = lambda x : 1, + ....: reduce_function = lambda x,y: x+y, + ....: reduce_init = 0 ) + +The profiling is activated by the ``profile`` parameter. The value provided +should be a prefix (including a possible directory) for the profile dump:: + + sage: prof = tmp_dir('RESetMR_profile')+'profcomp' + sage: res = S.run(profile=prof) # random + [RESetMapReduceWorker-1:58] (20:00:41.444) Profiling in /home/user/.sage/temp/mymachine.mysite/32414/RESetMR_profilewRCRAx/profcomp1 ... + ... + [RESetMapReduceWorker-1:57] (20:00:41.444) Profiling in /home/user/.sage/temp/mymachine.mysite/32414/RESetMR_profilewRCRAx/profcomp0 ... + sage: res + 131071 + +In this example, the profile have been dumped in files such as +``profcomp0``. One can then load and print them as follows. See +:class:`profile.profile` for more details:: + + sage: import cProfile, pstats + sage: st = pstats.Stats(prof+'0') + sage: st.strip_dirs().sort_stats('cumulative').print_stats() #random + ... + Ordered by: cumulative time + + ncalls tottime percall cumtime percall filename:lineno(function) + 1 0.023 0.023 0.432 0.432 map_reduce.py:1211(run_myself) + 11968 0.151 0.000 0.223 0.000 map_reduce.py:1292(walk_branch_locally) + ... + + +.. SEEALSO:: + + `The Python Profilers `_ + for more detail on profiling in python. + + +.. _logging: + +Logging +------- + +The computation progress is logged through a :class:`logging.Logger` in +:obj:`sage.parallel.map_reduce.logger` together with :class:`logging.StreamHandler` +and a :class:`logging.Formatter`. They are currently configured to print +warning message on the console. + +.. SEEALSO:: + + `Logging facility for Python `_ + for more detail on logging and log system configuration. + +.. note:: + + Calls to logger which involve printing the node are commented out in the + code, because the printing (to a string) of the node can be very time + consuming depending on the node and it happens before the decision whether + the logger should record the string or drop it. + + +.. _protocol-description: + +How does it work ? +------------------ + +The scheduling algorithm we use here is any adaptation of :wikipedia:`Work_stealing`: + + In a work stealing scheduler, each processor in a computer system has a + queue of work items (computational tasks, threads) to perform. [...]. Each + work items are initially put on the queue of the processor executing the + work item. When a processor runs out of work, it looks at the queues of + other processors and "steals" their work items. In effect, work stealing + distributes the scheduling work over idle processors, and as long as all + processors have work to do, no scheduling overhead occurs. + +For communication we use Python's basic :mod:`multiprocessing` module. We +first describe the different actors and communications tools used by the +system. The work is done under the coordination of a **master** object (an +instance of :class:`RESetMapReduce`) by a bunch of **worker** objects +(instances of :class:`RESetMapReduceWorker`). + +Each running map reduce instance work on a :class:`RecursivelyEnumeratedSet of +forest type` called here `C` and is +coordinated by a :class:`RESetMapReduce` object called the **master**. The +master is in charge of lauching the work, gathering the results and cleaning +up at the end of the computation. It doesn't perform any computation +associated to the generation of the element `C` nor the computation of the +mapped function. It however occasionally perform a reduce, but most reducing +is by default done by the workers. Also thanks to the work-stealing algorithm, +the master is only involved in detecting the termination of the computation +but all the load balancing is done at the level of the worker. + +Workers are instance of :class:`RESetMapReduceWorker`. They are responsible of +doing the actual computations: elements generation, mapping and reducing. They +are also responsible of the load balancing thanks to work-stealing. + +Here is a description of the attribute of the **master** relevant to the +map-reduce protocol: + +- ``master._results`` -- a :class:`~multiprocessing.queues.SimpleQueue` where + the master gathers the results sent by the workers. +- ``master._active_tasks`` -- a :class:`~multiprocessing.Semaphore` recording + the number of active task. The work is done when it gets to 0. +- ``master._done`` -- a :class:`~multiprocessing.Lock` which ensures that + shutdown is done only once. +- ``master._abort`` -- a :func:`~multiprocessing.Value` storing a shared + :class:`ctypes.c_bool` which is ``True`` if the computation was aborted before + all the worker runs out of work. +- ``master._workers`` -- a list of :class:`RESetMapReduceWorker` objects. Each worker is + identified by its position in this list. + +Each worker is a process (:class:`RESetMapReduceWorker` inherits from +:class:`~multiprocessing.Process`) which contains: + +- ``worker._iproc`` -- the identifier of the worker that is its position in the + master's list of workers +- ``worker._todo`` -- a :class:`collections.deque` storing of nodes of the + worker. It is used as a stack by the worker. Thiefs steal from the bottom of + this queue. +- ``worker._request`` -- a :class:`~multiprocessing.queues.SimpleQueue` storing + steal request submitted to ``worker``. +- ``worker._read_task``, ``worker._write_task`` -- a + :class:`~multiprocessing.queues.Pipe` used to transfert node during steal. +- ``worker._thief`` -- a :class:`~threading.Thread` which is in charge of stealing from + ``worker._todo``. + +Here is a schematic of the architecture: + +.. _figure-map_reduce_arch: + +.. figure:: ../../media/map_reduce_arch.png + + +How thefts are performed +------------------------ + +During normal time, that is when all worker are active) a worker ``W`` is +iterating though a loop inside +:meth:`RESetMapReduceWorker.walk_branch_locally`. Work nodes are taken from +and new nodes ``W._todo`` are appended to ``W._todo``. When a worker ``W`` is +running out of work, that is ``worker._todo`` is empty, then it tries to steal +some work (ie: a node) from another worker. This is performed in the +:meth:`RESetMapReduceWorker.steal` method. + +From the point of view of ``W`` here is what happens: + +- ``W`` signals to the master that it is idle :meth:`master._signal_task_done`; +- ``W`` chose a victim ``V`` at random; +- ``W`` sends a request to ``V`` : it puts its identifier into ``V._request``; +- ``W`` tries to read a node from ``W._read_task``. Then three things may happen: + + + a proper node is read. Then the theft was a success and ``W`` starts + working locally on the received node. + + ``None`` is received. This means that ``V`` was idle. Then ``W`` tries + another victim. + + ``AbortError`` is received. This means either that the computation was + aborted or that it simply succeded and that no more work is required by + ``W``. Therefore an ``AbortError`` exception is raised leading to ``W`` to + shutdown. + +We now describe the protocol on the victims side. Each worker process contains +a :class:`Thread` which we call ``T`` for thief which acts like some kinds of +Troyan horse during theft. It is normally blocked waiting for a steal request. + +From the point of view of ``V`` and ``T``, here is what happens: + +- during normal time ``T`` is blocked waiting on ``V._request``; +- upon steal request, ``T`` wakes up receiving the identification of ``W``; +- ``T`` signal to the master that a new task is starting by + :meth:`master._signal_task_start`; +- Two things may happen depending if the queue ``V._todo`` is empty or not. + Remark that due to the GIL, there is no parallel execution between the + victim ``V`` and its thief tread ``T``. + + + If ``V._todo`` is empty, then ``None`` is answered on + ``W._write_task``. The task is immediately signaled to end the the master + through :meth:`master._signal_task_done`. + + Otherwise, a node is removed from the bottom of ``V._todo``. The node is + sent to ``W`` on ``W._write_task``. The task will be ended by ``W``, that + is when finished working on the subtree rooted at the node, ``W`` will + call :meth:`master._signal_task_done`. + +The end of the computation +-------------------------- + +To detect when a computation is finished, we keep a synchronized integer which +count the number of active task. This is essentially a semaphore but semaphore +are broken on Darwin's OSes so we ship two implementations depending on the os +(see :class:`ActiveTaskCounter` and :class:`ActiveTaskCounterDarwin` and note +below). + +When a worker finishes working on a task, it calls +:meth:`master._signal_task_done`. This decrease the task counter +``master._active_tasks``. When it reaches 0, it means that there are no more +nodes: the work is done. The worker executes :meth:`master._shutdown` which +sends ``AbortError`` on all :meth:`worker._request` and +:meth:`worker._write_task` Queues. Each worker or thief thread receiving such +a message raise the corresponding exception, stoping therefore its work. A +lock called ``master._done`` ensures that shutdown is only done once. + +Finally, it is also possible to interrupt the computation before its ends +calling :meth:`master.abort()`. This is done by putting +``master._active_tasks`` to 0 and calling :meth:`master._shutdown`. + +.. warning:: The MacOSX Semaphore bug + + Darwin's OSes do not correctly implement POSIX's semaphore semantic. + Indeed, on this system, acquire may fail and return False not only because + the semaphore is equal to zero but also **because someone else is trying to + acquire** at the same time. This renders the usage of Semaphore impossible + on MacOSX so that on this system we use a synchronized integer. + + +.. _examples: + +Are there examples of classes ? +------------------------------- + +Yes ! Here, there are: + +- :class:`RESetMPExample` -- a simple basic example +- :class:`RESetParallelIterator` -- a more advanced example using non standard + communication configuration. + +Tests +----- + +Generating series for sum of strictly decreassing list of integer smaller than +15:: + + sage: y = var('y') + sage: R = RESetMapReduce( + ....: roots = [([], 0, 0)] +[([i], i, i) for i in range(1,15)], + ....: children = lambda (list, sum, last): + ....: [(list + [i], sum + i, i) for i in range(1,last)], + ....: map_function = lambda (li, sum, unused): y**sum) + sage: sg = R.run() + sage: bool(sg == expand(prod((1+y^i) for i in range(1,15)))) + True + + +Classes and methods +------------------- +""" +from multiprocessing import Process, Value, Semaphore, Lock, cpu_count +from multiprocessing.queues import Pipe, SimpleQueue +from multiprocessing.sharedctypes import RawArray +from threading import Thread +from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet # _generic +from sage.misc.lazy_attribute import lazy_attribute +import collections, copy, sys, random, ctypes + + +import logging +logger = logging.getLogger(__name__) +logger.__doc__ = """ +A logger for :mod:`sage.parallel.map_reduce` + +.. SEEALSO:: + + `Logging facility for Python `_ + for more detail on logging and log system configuration. +""" +logger.setLevel(logging.WARN) +#logger.setLevel(logging.INFO) +#logger.setLevel(logging.DEBUG) +ch = logging.StreamHandler() +ch.setLevel(logging.DEBUG) +formatter = logging.Formatter( + '[%(processName)s-%(threadName)s] (%(asctime)s.%(msecs)03.f) %(message)s', + datefmt='%H:%M:%S') +ch.setFormatter(formatter) +logger.addHandler(ch) + + + +def proc_number(max_proc = None): + r""" + Computing the number of process used + + INPUT: + + - ``max_proc`` -- the maximum number of process used + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import proc_number + sage: proc_number() # random + 8 + sage: proc_number(max_proc=1) + 1 + sage: proc_number(max_proc=2) in (1, 2) + True + """ + if max_proc is None: + return max(cpu_count(), 1) + else: + return min(max_proc, max(cpu_count(), 1)) + + +class AbortError(Exception): + r""" + Exception for aborting parallel computations + + This is used both as exception or as abort message + + TESTS:: + + sage: from sage.parallel.map_reduce import AbortError + sage: raise AbortError + Traceback (most recent call last): + ... + AbortError + """ + pass + + +class ActiveTaskCounterDarwin(object): + r""" + Handling the number of Active Tasks + + A class for handling the number of active task in distributed computation + process. This is essentially a semaphore, but Darwin's OSes do not + correctly implement POSIX's semaphore semantic. So we use a shared integer + with a lock. + """ + def __init__(self, task_number): + r""" + TESTS:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounterDarwin as ATC + sage: t = ATC(4) + sage: TestSuite(t).run(skip="_test_pickling", verbose=True) + """ + self._active_tasks = Value(ctypes.c_int, task_number) + self._lock = Lock() + + def __repr__(self): + """ + TESTS:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounterDarwin as ATC + sage: ATC(4) + ActiveTaskCounter(value=4) + """ + return "ActiveTaskCounter(value=%s)"%(self._active_tasks.value) + + def task_start(self): + r""" + Increment the task counter by one. + + OUTPUT: + + Calling :meth:`task_start` on a zero or negative counter returns 0, + otherwise increment the counter and returns its value after the + incrementation. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounterDarwin as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.task_start() + 5 + sage: c + ActiveTaskCounter(value=5) + + Calling :meth:`task_start` on a zero counter does nothing:: + + sage: c = ATC(0) + sage: c.task_start() + 0 + sage: c + ActiveTaskCounter(value=0) + """ + logger.debug("_signal_task_start called") + with self._lock: + # The following test is not necessary but is allows active thieves to + # stop before receiving the poison pill. + if self._active_tasks.value <= 0: + return 0 + self._active_tasks.value += 1 + return self._active_tasks.value + + def task_done(self): + r""" + Decrement the task counter by one. + + OUTPUT: + + Calling :meth:`task_done` decrement the counter and returns its value + after the decrementation. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounterDarwin as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.task_done() + 3 + sage: c + ActiveTaskCounter(value=3) + + sage: c = ATC(0) + sage: c.task_done() + -1 + """ + logger.debug("_signal_task_done called") + with self._lock: + self._active_tasks.value -= 1 + return self._active_tasks.value + + def abort(self): + r""" + Set the task counter to 0. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounterDarwin as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.abort() + sage: c + ActiveTaskCounter(value=0) + """ + with self._lock: + self._active_tasks.value = 0 + + +class ActiveTaskCounterPosix(object): + r""" + Handling the number of Active Tasks + + A class for handling the number of active task in distributed computation + process. This is the standard implementation on POSIX compliant OSes. We + essentially wrap a semaphore. + + .. note:: + + A legitimate question is whether there is a need in keeping the two + implementations. I ran the following experiment on my machine:: + + S = RecursivelyEnumeratedSet( [[]], + lambda l: ([l[:i] + [len(l)] + l[i:] for i in range(len(l)+1)] + if len(l) < NNN else []), + structure='forest', enumeration='depth') + %time sp = S.map_reduce(lambda z: x**len(z)); sp + + For NNN = 10, averaging a dozen of runs, I got: + + - Posix complient implementation : 17.04 s + - Darwin's implementation : 18.26 s + + So there is a non negligible overhead. It will probably be worth if we + tries to Cythonize the code. So I'm keeping both implementation. + """ + def __init__(self, task_number): + r""" + TESTS:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounter as ATC + sage: t = ATC(4) + sage: TestSuite(t).run(skip="_test_pickling", verbose=True) + """ + self._active_tasks = Semaphore(task_number) + + def __repr__(self): + """ + TESTS:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounter as ATC + sage: ATC(4) + ActiveTaskCounter(value=4) + """ + return "ActiveTaskCounter(value=%s)"%(self._active_tasks.get_value()) + + def task_start(self): + r""" + Increment the task counter by one. + + OUTPUT: + + Calling :meth:`task_start` on a zero or negative counter returns 0, + otherwise increment the counter and returns its value after the + incrementation. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounter as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.task_start() + 5 + sage: c + ActiveTaskCounter(value=5) + + Calling :meth:`task_start` on a zero counter does nothing:: + + sage: c = ATC(0) + sage: c.task_start() + 0 + sage: c + ActiveTaskCounter(value=0) + """ + logger.debug("_signal_task_start called") + # The following test is not necessary but is allows active thieves to + # stop before receiving the poison pill. + if self._active_tasks._semlock._is_zero(): + return 0 + self._active_tasks.release() + return self._active_tasks.get_value() + + task_start.__doc__ = ActiveTaskCounterDarwin.task_start.__doc__ + + def task_done(self): + r""" + Decrement the task counter by one. + + OUTPUT: + + Calling :meth:`task_done` decrement the counter and returns its value + after the decrementation. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounter as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.task_done() + 3 + sage: c + ActiveTaskCounter(value=3) + + sage: c = ATC(0) + sage: c.task_done() + -1 + """ + logger.debug("_signal_task_done called") + # We tests if the semaphore counting the number of active tasks is + # becoming negative. This should not happen in normal + # computations. However, in case of abort, we artificially put the + # semaphore to 0 to stop the computation so it is needed. + if not self._active_tasks.acquire(False): + return -1 + return self._active_tasks.get_value() + + def abort(self): + r""" + Set the task counter to 0. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import ActiveTaskCounter as ATC + sage: c = ATC(4); c + ActiveTaskCounter(value=4) + sage: c.abort() + sage: c + ActiveTaskCounter(value=0) + """ + while self._active_tasks.acquire(False): + pass + + +ActiveTaskCounter = (ActiveTaskCounterDarwin if sys.platform == 'darwin' + else ActiveTaskCounterPosix) + +# ActiveTaskCounter = ActiveTaskCounterDarwin # to debug DARWIN's implem + + + +class RESetMapReduce(object): + r""" + Map-Reduce on recursively enumerated sets + + INPUT: + + Description of the set: + + - either ``forest=f`` -- where ``f`` is a + :class:`RecursivelyEnumeratedSet of forest type` + + - or a triple ``roots, children, post_process`` as follows + + - ``roots=r`` -- The root of the enumeration + - ``children=c`` -- a function iterating through children node, given a parent nodes + - ``post_process=p`` -- a post processing function + + The option ``post_process`` allows for customizing the nodes that + are actually produced. Furthermore, if ``post_process(x)`` returns ``None``, + then ``x`` won't be output at all. + + Decription of the map/reduce operation: + + - ``map_function=f`` -- (default to ``None``) + - ``reduce_function=red`` -- (default to ``None``) + - ``reduce_init=init`` -- (default to ``None``) + + .. seealso:: + + :mod:`the Map/Reduce module ` for + details and examples. + """ + def __init__(self, roots = None, + children = None, + post_process = None, + map_function = None, + reduce_function = None, + reduce_init = None, + forest = None): + r""" + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: R = RESetMapReduce( [[]], lambda : [[]]) + sage: R + + + To silence the coverage checker:: + + sage: TestSuite(R).run(skip=['_test_pickling']) + """ + if forest is not None: + if not all(x is None for x in (roots, children, post_process)): + raise ValueError("forest arg is incompatible with roots, children and post_process") + self._forest = forest + self._roots = forest._roots + self.children = forest.children + if hasattr(forest, 'post_process'): + self.post_process = forest.post_process + else: + if roots is not None: self._roots = roots + if children is not None: self.children = children + if post_process is not None: self.post_process = post_process + if map_function is not None: self.map_function = map_function + if reduce_function is not None: self.reduce_function = reduce_function + if reduce_init is not None: self._reduce_init = reduce_init + self._profile = None + + @lazy_attribute + def _forest(self): + r""" + The forest underlying the map-reduce computation + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: EX = RESetMPExample() + sage: f = EX._forest; f + An enumerated set with a forest structure + sage: f.an_element() + [] + """ + return RecursivelyEnumeratedSet( + self.roots(), + self.children, + post_process=self.post_process, + structure='forest', enumeration='depth') + + + def roots(self): + r""" + Return the roots of ``self`` + + OUTPUT: + + an iterable of nodes + + .. note:: This should be overloaded in applications. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce(42) + sage: S.roots() + 42 + """ + return self._roots + + def map_function(self, o): + r""" + Return the function mapped by ``self`` + + INPUT: + + - ``o`` -- a node + + OUTPUT: + + By default ``1``. + + .. note:: This should be overloaded in applications. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.map_function(7) + 1 + sage: S = RESetMapReduce(map_function = lambda x: 3*x + 5) + sage: S.map_function(7) + 26 + """ + return 1 + + def reduce_function(self, a, b): + r""" + Return the reducer function for ``self`` + + INPUT: + + - ``a``, ``b`` -- two value to be reduced + + OUTPUT: + + by default the sum of ``a`` and ``b``. + + .. note:: This should be overloaded in applications. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.reduce_function(4, 3) + 7 + sage: S = RESetMapReduce(reduce_function=lambda x,y: x*y) + sage: S.reduce_function(4, 3) + 12 + """ + return a+b + + def post_process(self, a): + r""" + Return the post-processing function for ``self`` + + INPUT: ``a`` -- a node + + By default, returns ``a`` itself + + .. note:: This should be overloaded in applications. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.post_process(4) + 4 + sage: S = RESetMapReduce(post_process=lambda x: x*x) + sage: S.post_process(4) + 16 + """ + return a + + + _reduce_init = 0 + + def reduce_init(self): + r""" + Return the initial element for a reduction + + .. note:: This should be overloaded in applications. + + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.reduce_init() + 0 + sage: S = RESetMapReduce(reduce_init = 2) + sage: S.reduce_init() + 2 + """ + return copy.copy(self._reduce_init) + + + def setup_workers(self, max_proc = None, reduce_locally=True): + r""" + Setup the communication channels + + INPUT: + + - ``mac_proc`` -- an integer: the maximum number of workers + + - ``reduce_locally`` -- whether the workers should reduce locally + their work or sends results to the master as soon as possible. + See :class:`RESetMapReduceWorker` for details. + + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.setup_workers(2) + sage: S._results + + sage: len(S._workers) + 2 + """ + self._nprocess = proc_number(max_proc) + self._results = SimpleQueue() + self._active_tasks = ActiveTaskCounter(self._nprocess) + self._done = Lock() + self._abort = Value(ctypes.c_bool, False) + sys.stdout.flush() + sys.stderr.flush() + self._workers = [RESetMapReduceWorker(self, i, reduce_locally) + for i in range(self._nprocess)] + + def start_workers(self): + r""" + Lauch the workers + + The worker should have been created using :meth:`setup_workers`. + + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce(roots=[]) + sage: S.setup_workers(2) + sage: S.start_workers() + sage: all(w.is_alive() for w in S._workers) + True + + sage: sleep(1) + sage: all(not w.is_alive() for w in S._workers) + True + + Cleanups:: + + sage: S.finish() + """ + if self._nprocess == 0: + raise ValueError("No process connected") + logger.info("Starting work with %s processes", self._nprocess) + logger.debug("Distributing tasks") + for i, task in enumerate(self.roots()): + self._workers[i % len(self._workers)]._todo.append(task) + logger.debug("Starting processes") + sys.stdout.flush() + sys.stderr.flush() + for w in self._workers: w.start() + + def get_results(self): + r""" + Get the results from the queue + + OUTPUT: + + the reduction of the results of all the workers, that is the result of + the map/reduce computation. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMapReduce + sage: S = RESetMapReduce() + sage: S.setup_workers(2) + sage: for v in [1, 2, None, 3, None]: S._results.put(v) + sage: S.get_results() + 6 + + Cleanups:: + + sage: del S._results, S._active_tasks, S._done, S._workers + """ + res = self.reduce_init() + active_proc = self._nprocess + while active_proc > 0: + newres = self._results.get() + if newres is not None: + logger.debug("Got one result") + res = self.reduce_function(res, newres) + else: + active_proc -= 1 + return res + + + def finish(self): + r""" + Destroys the worker and all the communication objects. + + Also gathers the communication statistics before destroying the workers. + + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: S = RESetMPExample(maxl=5) + sage: S.setup_workers(2) # indirect doctest + sage: S._workers[0]._todo.append([]) + sage: for w in S._workers: w.start() + sage: _ = S.get_results() + sage: S._shutdown() + sage: S.print_communication_statistics() + Traceback (most recent call last): + ... + AttributeError: 'RESetMPExample' object has no attribute '_stats' + + sage: S.finish() + + sage: S.print_communication_statistics() + #proc: ... + ... + + sage: _ = S.run() # Cleanup + + .. seealso:: :meth:`print_communication_statistics` + """ + self._abort = self._abort.value + if not self._abort: + logger.debug("Joining worker processes...") + for worker in self._workers: + logger.debug("Joining %s"%worker.name) + worker.join() + logger.debug("Joining done") + else: + logger.debug("Killing worker processes...") + for worker in self._workers: + logger.debug("Terminating %s"%worker.name) + worker.terminate() + logger.debug("Killing done") + + del self._results, self._active_tasks, self._done + self._get_stats() + del self._workers + + + def abort(self): + r""" + Abort the current parallel computation + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 17 else []) + sage: it = iter(S) + sage: it.next() + [] + sage: S.abort() + sage: hasattr(S, 'work_queue') + False + + Cleanups:: + + sage: S.finish() + """ + logger.info("Abort called") + self._abort.value = True + self._active_tasks.abort() + self._shutdown() + + def _shutdown(self): + r""" + Called to shutdown the workers + + Sends a poison pill to all workers and their thief thread. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 20 else []) + sage: S.setup_workers(2) + sage: for w in S._workers: w.start() + sage: S._shutdown() + + Cleanups:: + + sage: S.finish() + """ + if self._done.acquire(False): + logger.debug("***************** FINISHED ******************") + logger.debug("Sending poison pills") + for worker in self._workers: + worker._request.put(AbortError) + for worker in self._workers: + worker._write_task.send(AbortError) + + def _signal_task_start(self): + r""" + Signal a starting task + + Used by the worker to signal that a new task is starting. As soon as + there are no more active task, the work is done, in which case an + :exc:`AbortError` is raised. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 20 else []) + sage: S.setup_workers(2) + sage: S._active_tasks + ActiveTaskCounter(value=2) + + sage: S._signal_task_start() + sage: S._active_tasks + ActiveTaskCounter(value=3) + + Signaling one time too many raise a ``AbortError``:: + + sage: S._signal_task_done() + sage: S._signal_task_done() + sage: S._signal_task_done() + Traceback (most recent call last): + ... + AbortError + """ + if self._active_tasks.task_start() == 0: + raise AbortError + + def _signal_task_done(self): + r""" + Signal a done task + + Used by the worker to signal that a task is done. As soon as + there are no more active task, the work is done, in which case an + :exc:`AbortError` is raised. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 20 else []) + sage: S.setup_workers(2) + sage: S._active_tasks + ActiveTaskCounter(value=2) + + sage: S._signal_task_done() + sage: S._active_tasks + ActiveTaskCounter(value=1) + + sage: S._signal_task_done() + Traceback (most recent call last): + ... + AbortError + + Cleanups:: + + sage: del S._results, S._active_tasks, S._done, S._workers + """ + # We tests if the semaphore counting the number of active tasks is + # becoming negative. This should not happen in normal + # computations. However, in case of abort, we artificially put the + # semaphore to 0 to stop the computation so that it is needed. + if self._active_tasks.task_done() <= 0: + logger.debug("raising AbortError") + self._shutdown() + raise AbortError + + def random_worker(self): + r""" + Returns a random workers + + OUTPUT: + + A worker for ``self`` chosed at random + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: from threading import Thread + sage: EX = RESetMPExample(maxl=6) + sage: EX.setup_workers(2) + sage: EX.random_worker() + + sage: EX.random_worker() in EX._workers + True + + Cleanups:: + + sage: del EX._results, EX._active_tasks, EX._done, EX._workers + """ + victim = random.randint(0, len(self._workers)-1) + return self._workers[victim] + + def run(self, + max_proc = None, + reduce_locally = True, + timeout=None, + profile=None): + r""" + Run the computations + + INPUT: + + - ``max_proc`` -- maximum number of process used. + default: number of processor on the machine + - ``reduce_locally`` -- See :class:`RESetMapReduceWorker` (default: ``True``) + - ``timeout`` -- a timeout on the computation (default: ``None``) + - ``profile`` -- directory/filename prefix for profiling, or ``None`` + for no profiling (default: ``None``) + + OUTPUT: + + the result of the map/reduce computation or an exception + :exc:`AbortError` if the computation was interrupted or timeout. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: EX = RESetMPExample(maxl = 8) + sage: EX.run() + 40320*x^8 + 5040*x^7 + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + Here is an example or how to deal with timeout:: + + sage: from sage.parallel.map_reduce import AbortError + sage: try: + ....: res = EX.run(timeout=0.1) + ....: except AbortError: + ....: print "Computation timeout" + ....: else: + ....: print "Computation normally finished" + ....: res + Computation timeout + + The following should not timeout even on a very slow machine:: + + sage: from sage.parallel.map_reduce import AbortError + sage: try: + ....: res = EX.run(timeout=60) + ....: except AbortError: + ....: print "Computation Timeout" + ....: else: + ....: print "Computation normally finished" + ....: res + Computation normally finished + 40320*x^8 + 5040*x^7 + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + """ + self._profile=profile + self.setup_workers(max_proc, reduce_locally) + self.start_workers() + if timeout is not None: + from threading import Timer + self._timer = Timer(timeout, self.abort) + self._timer.start() + self.result = self.get_results() + self.finish() + if timeout is not None: + self._timer.cancel() + logger.info("Returning") + if self._abort: + raise AbortError + else: + return self.result + + def _get_stats(self): + r""" + Gather the communication statistics and the end of a run + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: S = RESetMPExample(maxl=6) + sage: S.run() # indirect doctest + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + """ + res = [] + for i in range(self._nprocess): + res.append(tuple(self._workers[i]._stats)) + self._stats = res + + def print_communication_statistics(self, blocksize = 16): + r""" + Print the communication statistics in a nice way + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: S = RESetMPExample(maxl=6) + sage: S.run() + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + sage: S.print_communication_statistics() # random + #proc: 0 1 2 3 4 5 6 7 + reqs sent: 5 2 3 11 21 19 1 0 + reqs rcvs: 10 10 9 5 1 11 9 2 + - thefs: 1 0 0 0 0 0 0 0 + + thefs: 0 0 1 0 0 0 0 0 + """ + res = [""] # classical trick to have a local variable shared with the + # local function (see e.g: + # http://stackoverflow.com/questions/2609518/python-nested-function-scopes). + def pstat(name, start, end, ist): + res[0] += "\n" + name + res[0] += " ".join( + "%4i"%(self._stats[i][ist]) for i in range(start, end)) + for start in range(0, self._nprocess, blocksize): + end = min(start+blocksize, self._nprocess) + res[0] = "#proc: "+" ".join("%4i"%(i) for i in range(start, end)) + pstat("reqs sent: ", start, end, 0) + pstat("reqs rcvs: ", start, end, 1) + pstat("- thefs: ", start, end, 2) + pstat("+ thefs: ", start, end, 3) + print res[0] + + def run_serial(self): + r""" + Serial run of the computation (mostly for tests) + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: EX = RESetMPExample(maxl = 4) + sage: EX.run_serial() + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + """ + import functools + return functools.reduce(self.reduce_function, + (self.map_function(x) for x in self._forest), + self.reduce_init()) + + +class RESetMapReduceWorker(Process): + """ + Worker for generate-map-reduce + + This shouldn't be called directly, but instead created by + :meth:`RESetMapReduce.setup_workers`. + + INPUT: + + - ``mapred`` -- the instance of :class:`RESetMapReduce` for which + this process is working. + + - ``iproc`` -- the id of this worker. + + - ``reduce_locally`` -- when reducing the results. Three possible values + are supported: + + * ``True`` -- means the reducing work is done all locally, the result is + only sent back at the end of the work. This ensure the lowest level of + communication. + + * ``False`` -- results are sent back after each finished branches, when + the process is asking for more work. + """ + def __init__(self, mapred, iproc, reduce_locally): + r""" + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: EX = RESetMPExample() + sage: RESetMapReduceWorker(EX, 200, True) + + """ + Process.__init__(self) + self._iproc = iproc + self._todo = collections.deque() + self._request = SimpleQueue() # Faster than Queue + # currently this is not possible to have to simultaneous read or write + # on the following Pipe. So there is no need to have a queue. + self._read_task, self._write_task = Pipe(duplex=False) + self._mapred = mapred + self._stats = RawArray('i', 4) + self._reduce_locally = reduce_locally + + def _thief(self): + r""" + The thief thread of a worker process + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: from threading import Thread + sage: EX = RESetMPExample(maxl=6) + sage: EX.setup_workers(2) + + sage: w0, w1 = EX._workers + sage: w0._todo.append(42) + sage: thief0 = Thread(target = w0._thief, name="Thief") + sage: thief0.start() + + sage: w1.steal() + 42 + sage: w0._todo + deque([]) + """ + logger.debug("Thief started") + reqs = 0 + thefts = 0 + + try: + for ireq in iter(self._request.get, AbortError): + reqs +=1 + target = self._mapred._workers[ireq] + logger.debug("Got a Steal request from %s"%target.name) + self._mapred._signal_task_start() + try: + work = self._todo.popleft() + except IndexError: + target._write_task.send(None) + logger.debug("Failed Steal %s"%target.name) + self._mapred._signal_task_done() + else: + target._write_task.send(work) + logger.debug("Succesful Steal %s"%target.name) + thefts += 1 + except AbortError: + logger.debug("Thief aborted") + else: + logger.debug("Thief received poison pill") + if self._mapred._abort.value: # Computation was aborted + self._todo.clear() + else: # Check that there is no remaining work + assert len(self._todo) == 0, "Bad stop the result may be wrong" + + self._stats[1] = reqs + self._stats[2] = thefts + logger.debug("Thief Exiting") + + def steal(self): + r""" + Steal some node from another worker + + OUTPUT: + + a node stolen from another worker choosed at random + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: from threading import Thread + sage: EX = RESetMPExample(maxl=6) + sage: EX.setup_workers(2) + + sage: w0, w1 = EX._workers + sage: w0._todo.append(42) + sage: thief0 = Thread(target = w0._thief, name="Thief") + sage: thief0.start() + + sage: w1.steal() + 42 + """ + self._mapred._signal_task_done() + node = None + while node is None: + victim = self._mapred.random_worker() + if victim is not self: + logger.debug("Trying to steal from %s"%(victim.name)) + victim._request.put(self._iproc) + self._stats[0] += 1 + logger.debug("waiting from steal answer from %s"%(victim.name)) + node = self._read_task.recv() + # logger.debug("Request answer: %s"%(node,)) + if node is AbortError: + raise AbortError + # logger.debug("Received a stolen node: %s"%(node,)) + self._stats[3] += 1 + return node + + def run(self): + r""" + The main function executed by the worker + + Calls :meth:`run_myself` after possibly setting up parallel profiling. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: EX = RESetMPExample(maxl=6) + sage: EX.setup_workers(1) + + sage: w = EX._workers[0] + sage: w._todo.append(EX.roots()[0]) + + sage: w.run() + sage: sleep(1) + sage: w._todo.append(None) + + sage: EX.get_results() + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + Cleanups:: + + sage: del EX._results, EX._active_tasks, EX._done, EX._workers + """ + profile = self._mapred._profile + if profile is not None: + from multiprocessing import current_process + import cProfile + PROFILER = cProfile.Profile() + PROFILER.runcall(self.run_myself) + + output = profile + str(self._iproc) + logger.warn("Profiling in %s ..."%output) + PROFILER.dump_stats(output) + else: + self.run_myself() + + def run_myself(self): + r""" + The main function executed by the worker + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: EX = RESetMPExample(maxl=6) + sage: EX.setup_workers(1) + + sage: w = EX._workers[0] + sage: w._todo.append(EX.roots()[0]) + sage: w.run_myself() + + sage: sleep(1) + sage: w._todo.append(None) + + sage: EX.get_results() + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + Cleanups:: + + sage: del EX._results, EX._active_tasks, EX._done, EX._workers + """ + logger.debug("Started") + mapred = self._mapred + reduce_init = mapred.reduce_init + results = mapred._results + + self._stats[0] = 0 + self._stats[3] = 0 + logger.debug("Launching thief") + self._thief = Thread(target = self._thief, name="Thief") + self._thief.start() + self._res = reduce_init() + + try: + while True: + try: + node = self._todo.pop() + except IndexError: + node = self.steal() + self.walk_branch_locally(node) + if not self._reduce_locally: + self.send_partial_result() + except AbortError: + logger.debug("Worker Done !") + results.put(self._res) + results.put(None) + self._thief.join() + del self._request + self._read_task.close() + self._write_task.close() + del self._read_task, self._write_task + del self._mapred + del self._stats + logger.debug("Exiting") + + def send_partial_result(self): + r""" + Send results to the MapReduce process + + Send the result stored in ``self._res`` to the master an reinitialize it to + ``master.reduce_init``. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: EX = RESetMPExample(maxl=4) + sage: EX.setup_workers(1) + sage: w = EX._workers[0] + sage: w._res = 4 + sage: w.send_partial_result() + sage: w._res + 0 + sage: EX._results.get() + 4 + """ + self._mapred._results.put(self._res) + self._res = self._mapred.reduce_init() + + def walk_branch_locally(self, node): + r""" + Work locally + + Performs the map/reduce computation on the subtrees rooted at ``node``. + + INPUT: + + - ``node`` -- the root of the subtree explored. + + OUTPUT: + + nothing, the result are stored in ``self._res`` + + This is where the actual work is performed. + + EXAMPLES:: + + sage: from sage.parallel.map_reduce import RESetMPExample, RESetMapReduceWorker + sage: EX = RESetMPExample(maxl=4) + sage: w = RESetMapReduceWorker(EX, 0, True) + sage: def sync(): pass + sage: w.synchronize = sync + sage: w._res = 0 + + sage: w.walk_branch_locally([]) + sage: w._res + x^4 + x^3 + x^2 + x + 1 + + sage: w.walk_branch_locally(w._todo.pop()) + sage: w._res + 2*x^4 + x^3 + x^2 + x + 1 + + sage: while True: w.walk_branch_locally(w._todo.pop()) + Traceback (most recent call last): + ... + IndexError: pop from an empty deque + sage: w._res + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + """ + mapred = self._mapred + children = mapred.children + post_process = mapred.post_process + fun = mapred.map_function + reduc = mapred.reduce_function + + # logger.debug("Working on %s..."%(node,)) + while True: + res = post_process(node) + if res is not None: + self._res = reduc(self._res, fun(res)) + newnodes = iter(children(node)) + try: + node = newnodes.next() + except StopIteration: + return + self._todo.extend(newnodes) + + +class RESetMPExample(RESetMapReduce): + r""" + An example of map reduce class + + INPUT: + + - ``maxl`` -- the maximum size of permutations generated (default to `9`). + + This compute the generating series of permutations counted by their size + upto size ``maxl``. + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: EX = RESetMPExample() + sage: EX.run() + 362880*x^9 + 40320*x^8 + 5040*x^7 + 720*x^6 + 120*x^5 + 24*x^4 + 6*x^3 + 2*x^2 + x + 1 + + .. seealso:: This is an example of :class:`RESetMapReduce` + + """ + def __init__(self, maxl = 9): + r""" + TESTS:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: RESetMPExample() + + """ + RESetMapReduce.__init__(self) + from sage.calculus.var import var + self.x = var('x') + self.maxl = maxl + + def roots(self): + r""" + Return the empty permutation + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: RESetMPExample().roots() + [[]] + """ + return [[]] + + def children(self, l): + r""" + Return the chidren of the permutation `l` + + INPUT: + + - ``l`` -- a list containing a permutation + + OUTPUT: + + the lists of ``len(l)`` inserted at all possible positions into ``l`` + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: RESetMPExample().children([1,0]) + [[2, 1, 0], [1, 2, 0], [1, 0, 2]] + """ + return [ l[:i] + [len(l)] + l[i:] + for i in range(len(l)+1) ] if len(l) < self.maxl else [] + + def map_function(self, l): + r""" + The monomial associated to the permutation `l` + + INPUT: + + - ``l`` -- a list containing a permutation + + OUTPUT: + + ``x^len(l)``. + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetMPExample + sage: RESetMPExample().map_function([1,0]) + x^2 + """ + return self.x**len(l) + + +class RESetParallelIterator(RESetMapReduce): + r""" + A parallel iterator for recursively enumerated sets + + This demonstrate how to use :class:`RESetMapReduce` to get an iterator on + a recursively enumerated sets for which the computations are done in + parallel. + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 15 else []) + sage: sum(1 for _ in S) + 65535 + """ + def map_function(self, z): + r""" + Return a singleton tuple + + INPUT: ``z`` -- a node + + OUPUT: ``(z, )`` + + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 15 else []) + sage: S.map_function([1, 0]) + ([1, 0],) + """ + return (z,) + + reduce_init = tuple + + def __iter__(self): + r""" + EXAMPLE:: + + sage: from sage.parallel.map_reduce import RESetParallelIterator + sage: S = RESetParallelIterator( [[]], + ....: lambda l: [l+[0], l+[1]] if len(l) < 15 else []) + sage: it = iter(S) + sage: it.next() # random + [1, 1, 0] + sage: it.next() # random + [1, 1, 0, 1] + sage: sum(1 for _ in it) + 65533 + """ + self.setup_workers(reduce_locally=False) + self.start_workers() + active_proc = self._nprocess + while True: + newres = self._results.get() + if newres is not None: + logger.debug("Got some results") + for r in newres: + yield r + else: + active_proc -= 1 + if active_proc == 0: + break + self.finish() + diff --git a/src/sage/plot/complex_plot.pyx b/src/sage/plot/complex_plot.pyx index 8ecf534333e..54617e4496c 100644 --- a/src/sage/plot/complex_plot.pyx +++ b/src/sage/plot/complex_plot.pyx @@ -275,37 +275,66 @@ def complex_plot(f, xrange, yrange, **options): sage: complex_plot(sqrt(x), (-5, 5), (-5, 5)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(sqrt(x), (-5, 5), (-5, 5))) + :: sage: complex_plot(sin(x), (-5, 5), (-5, 5)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(sin(x), (-5, 5), (-5, 5))) + :: sage: complex_plot(log(x), (-10, 10), (-10, 10)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(log(x), (-10, 10), (-10, 10))) + :: sage: complex_plot(exp(x), (-10, 10), (-10, 10)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(exp(x), (-10, 10), (-10, 10))) + A function with some nice zeros and a pole:: sage: f(z) = z^5 + z - 1 + 1/z sage: complex_plot(f, (-3, 3), (-3, 3)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + def f(z): return z**5 + z - 1 + 1/z + sphinx_plot(complex_plot(f, (-3, 3), (-3, 3))) + Here is the identity, useful for seeing what values map to what colors:: sage: complex_plot(lambda z: z, (-3, 3), (-3, 3)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(lambda z: z, (-3, 3), (-3, 3))) + The Riemann Zeta function:: sage: complex_plot(zeta, (-30,30), (-30,30)) Graphics object consisting of 1 graphics primitive + .. PLOT:: + + sphinx_plot(complex_plot(zeta, (-30,30), (-30,30))) + Extra options will get passed on to show(), as long as they are valid:: sage: complex_plot(lambda z: z, (-3, 3), (-3, 3), figsize=[1,1]) diff --git a/src/sage/plot/matrix_plot.py b/src/sage/plot/matrix_plot.py index 3f9706ea1a2..f4c3c1c4152 100644 --- a/src/sage/plot/matrix_plot.py +++ b/src/sage/plot/matrix_plot.py @@ -233,18 +233,24 @@ def _render_on_subplot(self, subplot): @suboptions('colorbar', orientation='vertical', format=None) @suboptions('subdivision',boundaries=None, style=None) -@options(aspect_ratio=1, axes=False, cmap='gray', colorbar=False, +@options(aspect_ratio=1, axes=False, cmap='Greys', colorbar=False, frame=True, marker='.', norm=None, origin='upper', subdivisions=False, ticks_integer=True, vmin=None, vmax=None) def matrix_plot(mat, **options): r""" A plot of a given matrix or 2D array. + If the matrix is sparse, colors only indicate whether an element is + nonzero or zero, so the plot represents the sparsity pattern of the + matrix. + If the matrix is dense, each matrix element is given a different color value depending on its relative size compared to the other - elements in the matrix. If the matrix is sparse, colors only - indicate whether an element is nonzero or zero, so the plot - represents the sparsity pattern of the matrix. + elements in the matrix. + + The default is for the lowest number to be black and the highest + number to be white in a greyscale pattern; see the information about + normalizing below. To reverse this, use ``cmap='Greys'``. The tick marks drawn on the frame axes denote the row numbers (vertical ticks) and the column numbers (horizontal ticks) of the @@ -257,11 +263,14 @@ def matrix_plot(mat, **options): The following input must all be passed in as named parameters, if default not used: - - ``cmap`` - a colormap (default: 'gray'), the name of - a predefined colormap, a list of colors, - or an instance of a matplotlib Colormap. - Type: ``import matplotlib.cm; matplotlib.cm.datad.keys()`` - for available colormap names. + - ``cmap`` - a colormap (default: 'Greys'), the name of a predefined + colormap, a list of colors, or an instance of a matplotlib Colormap. + + The list of predefined color maps can be visualized in `matplotlib's + documentation + `__. You + can also type ``import matplotlib.cm; matplotlib.cm.datad.keys()`` to list + their names. - ``colorbar`` -- boolean (default: False) Show a colorbar or not (dense matrices only). diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 5da03517eca..56ee2a4ec0c 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -406,6 +406,12 @@ def y(x): return x*sin(x**2) sage: g2 = plot(cos(x), 0, 2*pi, linestyle = "--") sage: (g1+g2).show(ticks=pi/6, tick_formatter=pi) # long time # show their sum, nicely formatted +.. PLOT:: + + g1 = plot(sin(x), 0, 2*pi, ticks=pi/6, tick_formatter=pi) + g2 = plot(cos(x), 0, 2*pi, linestyle = "--", ticks=pi/6, tick_formatter=pi) + sphinx_plot(g1+g2) + An illustration of integration:: sage: f(x) = (x-3)*(x-5)*(x-7)+40 @@ -437,28 +443,36 @@ def f(x): return (x-3)*(x-5)*(x-7)+40 Sage includes Matplotlib, which provides 2D plotting with an interface that is a likely very familiar to people doing numerical -computation. For example, +computation. +You can use `plt.clf()` to clear the current image frame +and `plt.close()` to close it. +For example, :: - sage: from pylab import * - sage: t = arange(0.0, 2.0, 0.01) + sage: import pylab as plt + sage: t = plt.arange(0.0, 2.0, 0.01) sage: s = sin(2*pi*t) - sage: P = plot(t, s, linewidth=1.0) - sage: xl = xlabel('time (s)') - sage: yl = ylabel('voltage (mV)') - sage: t = title('About as simple as it gets, folks') - sage: grid(True) - sage: savefig(os.path.join(SAGE_TMP, 'sage.png')) + sage: P = plt.plot(t, s, linewidth=1.0) + sage: xl = plt.xlabel('time (s)') + sage: yl = plt.ylabel('voltage (mV)') + sage: t = plt.title('About as simple as it gets, folks') + sage: plt.grid(True) + sage: plt.savefig(os.path.join(SAGE_TMP, 'sage.png')) + sage: plt.clf() + sage: plt.savefig(os.path.join(SAGE_TMP, 'blank.png')) + sage: plt.close() + sage: plt.imshow([[1,2],[0,1]]) + We test that ``imshow`` works as well, verifying that :trac:`2900` is fixed (in Matplotlib). :: - sage: imshow([[(0,0,0)]]) + sage: plt.imshow([[(0,0,0)]]) - sage: savefig(os.path.join(SAGE_TMP, 'foo.png')) + sage: plt.savefig(os.path.join(SAGE_TMP, 'foo.png')) Since the above overwrites many Sage plotting functions, we reset the state of Sage, so that the examples below work! diff --git a/src/sage/plot/plot3d/base.pyx b/src/sage/plot/plot3d/base.pyx index a570a2a364e..ed596166554 100644 --- a/src/sage/plot/plot3d/base.pyx +++ b/src/sage/plot/plot3d/base.pyx @@ -494,7 +494,6 @@ cdef class Graphics3d(SageObject): a_max[i] = a_max[i] + 1 return a_min, a_max - def bounding_box(self): """ Return the lower and upper corners of a 3d bounding box for ``self``. @@ -1735,6 +1734,20 @@ end_scene""" % (render_params.antialiasing, string_list += ['
    '] return "".join(string_list) + def plot(self): + """ + Draw a 3D plot of this graphics object, which just returns this + object since this is already a 3D graphics object. + Needed to support PLOT in doctrings, see :trac:`17498` + + EXAMPLES:: + + sage: S = sphere((0,0,0), 2) + sage: S.plot() is S + True + + """ + return self # if you add any default parameters you must update some code below SHOW_DEFAULTS = {'viewer': 'jmol', @@ -1989,7 +2002,8 @@ class Graphics3dGroup(Graphics3d): all.append(g) return Graphics3dGroup(all) - + def plot(self): + return self class TransformGroup(Graphics3dGroup): """ diff --git a/src/sage/plot/plot3d/index_face_set.pyx b/src/sage/plot/plot3d/index_face_set.pyx index 51527c48f0b..dd350d9e7e0 100644 --- a/src/sage/plot/plot3d/index_face_set.pyx +++ b/src/sage/plot/plot3d/index_face_set.pyx @@ -32,7 +32,7 @@ AUTHORS: #***************************************************************************** include "cysignals/signals.pxi" -from sage.ext.memory cimport check_calloc, check_allocarray, check_reallocarray, sage_free +include "cysignals/memory.pxi" cdef extern from *: void memset(void *, int, Py_ssize_t) @@ -364,7 +364,7 @@ cdef class IndexFaceSet(PrimitiveObject): face.vertices[j] = point_map[face.vertices[j]] self.realloc(ix, self.fcount, self.icount) self.vcount = ix - sage_free(point_map) + sig_free(point_map) def _seperate_creases(self, threshold): """ @@ -402,7 +402,7 @@ cdef class IndexFaceSet(PrimitiveObject): try: point_faces = check_allocarray(total, sizeof(face_c*)) except MemoryError: - sage_free(point_counts) + sig_free(point_counts) raise sig_on() memset(point_counts, 0, sizeof(int) * self.vcount) @@ -441,8 +441,8 @@ cdef class IndexFaceSet(PrimitiveObject): try: self.vs = check_reallocarray(self.vs, ix, sizeof(point_c)) except MemoryError: - sage_free(point_counts) - sage_free(point_faces) + sig_free(point_counts) + sig_free(point_faces) self.vcount = self.fcount = self.icount = 0 # so we don't get segfaults on bad points sig_off() raise @@ -469,17 +469,17 @@ cdef class IndexFaceSet(PrimitiveObject): start = self.vcount self.vcount = ix - sage_free(point_counts) - sage_free(point_faces) + sig_free(point_counts) + sig_free(point_faces) sig_off() def _mem_stats(self): return self.vcount, self.fcount, self.icount def __dealloc__(self): - sage_free(self.vs) - sage_free(self._faces) - sage_free(self.face_indices) + sig_free(self.vs) + sig_free(self._faces) + sig_free(self.face_indices) def is_enclosed(self): """ @@ -522,6 +522,75 @@ cdef class IndexFaceSet(PrimitiveObject): for j from 0 <= j < self._faces[i].n] for i from 0 <= i < self.fcount] + def has_local_colors(self): + """ + Return ``True`` if and only if every face has an individual color. + + EXAMPLES:: + + sage: from sage.plot.plot3d.index_face_set import IndexFaceSet + sage: from sage.plot.plot3d.texture import Texture + sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)] + sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]] + sage: col = rainbow(10, 'rgbtuple') + sage: t_list=[Texture(col[i]) for i in range(10)] + sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list) + sage: S.has_local_colors() + True + + sage: from sage.plot.plot3d.shapes import * + sage: S = Box(1,2,3) + sage: S.has_local_colors() + False + """ + return not(self.global_texture) + + def index_faces_with_colors(self): + """ + Return the list over all faces of (indices of the vertices, color). + + This only works if every face has its own color. + + .. SEEALSO:: + + :meth:`has_local_colors` + + EXAMPLES: + + A simple colored one:: + + sage: from sage.plot.plot3d.index_face_set import IndexFaceSet + sage: from sage.plot.plot3d.texture import Texture + sage: point_list = [(2,0,0),(0,2,0),(0,0,2),(0,1,1),(1,0,1),(1,1,0)] + sage: face_list = [[0,4,5],[3,4,5],[2,3,4],[1,3,5]] + sage: col = rainbow(10, 'rgbtuple') + sage: t_list=[Texture(col[i]) for i in range(10)] + sage: S = IndexFaceSet(face_list, point_list, texture_list=t_list) + sage: S.index_faces_with_colors() + [([0, 4, 5], '#ff0000'), + ([3, 4, 5], '#ff9900'), + ([2, 3, 4], '#cbff00'), + ([1, 3, 5], '#33ff00')] + + When the texture is global, an error is raised:: + + sage: from sage.plot.plot3d.shapes import * + sage: S = Box(1,2,3) + sage: S.index_faces_with_colors() + Traceback (most recent call last): + ... + ValueError: the texture is global + """ + cdef Py_ssize_t i, j + if self.global_texture: + raise ValueError('the texture is global') + return [([self._faces[i].vertices[j] + for j from 0 <= j < self._faces[i].n], + Color(self._faces[i].color.r, + self._faces[i].color.g, + self._faces[i].color.b).html_color()) + for i from 0 <= i < self.fcount] + def faces(self): """ An iterator over the faces. @@ -761,7 +830,7 @@ cdef class IndexFaceSet(PrimitiveObject): ix += face.n face_set._clean_point_list() all[part] = face_set - sage_free(partition) + sig_free(partition) return all def tachyon_repr(self, render_params): diff --git a/src/sage/plot/plot3d/parametric_surface.pyx b/src/sage/plot/plot3d/parametric_surface.pyx index eac0fa72c49..b1ba9f18744 100644 --- a/src/sage/plot/plot3d/parametric_surface.pyx +++ b/src/sage/plot/plot3d/parametric_surface.pyx @@ -82,7 +82,7 @@ Another colored example:: # # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" include "point_c.pxi" @@ -518,7 +518,6 @@ cdef class ParametricSurface(IndexFaceSet): self.render_grid = urange, vrange - def get_grid(self, ds): """ TEST:: @@ -571,8 +570,8 @@ cdef class ParametricSurface(IndexFaceSet): v = vlist[j] self.eval_c(&self.vs[i*n+j], u, v) - sage_free(ulist) - sage_free(vlist) + sig_free(ulist) + sig_free(vlist) elif isinstance(self.f, tuple): @@ -628,8 +627,8 @@ cdef class ParametricSurface(IndexFaceSet): (fz).call_c(uv, &self.vs[i*n+j].z) - sage_free(ulist) - sage_free(vlist) + sig_free(ulist) + sig_free(vlist) if not (fast_x and fast_y and fast_z): ix = 0 @@ -673,6 +672,20 @@ cdef class ParametricSurface(IndexFaceSet): """ raise NotImplementedError + def plot(self): + """ + Draw a 3D plot of this graphics object, which just returns this + object since this is already a 3D graphics object. + Needed to support PLOT in doctrings, see :trac:`17498` + + EXAMPLES:: + + sage: S = parametric_plot3d( (sin, cos, lambda u: u/10), (0, 20)) + sage: S.plot() is S + True + + """ + return self class MoebiusStrip(ParametricSurface): """ @@ -764,7 +777,7 @@ class MoebiusStrip(ParametricSurface): cdef double* to_double_array(py_list) except NULL: - cdef double* c_list = sage_malloc(sizeof(double) * len(py_list)) + cdef double* c_list = sig_malloc(sizeof(double) * len(py_list)) if c_list == NULL: raise MemoryError cdef Py_ssize_t i = 0 diff --git a/src/sage/plot/plot3d/platonic.py b/src/sage/plot/plot3d/platonic.py index 2f7fd195629..b2b5120eacb 100644 --- a/src/sage/plot/plot3d/platonic.py +++ b/src/sage/plot/plot3d/platonic.py @@ -10,6 +10,13 @@ sage: G += icosahedron(center=(0,4,0), color='yellow') sage: G.show(aspect_ratio=[1,1,1]) +.. PLOT:: + + G = tetrahedron((0,-3.5,0), color='blue') + cube((0,-2,0),color=(.25,0,.5)) + G += octahedron(color='red') + dodecahedron((0,2,0), color='orange') + G += icosahedron(center=(0,4,0), color='yellow') + sphinx_plot(G) + All the platonic solids in the same place:: sage: G = tetrahedron(color='blue',opacity=0.7) @@ -18,11 +25,23 @@ sage: G += dodecahedron(color='orange', opacity=0.7) + icosahedron(opacity=0.7) sage: G.show(aspect_ratio=[1,1,1]) +.. PLOT:: + + G = tetrahedron(color='blue',opacity=0.7) + G += cube(color=(.25,0,.5), opacity=0.7) + G += octahedron(color='red', opacity=0.7) + G += dodecahedron(color='orange', opacity=0.7) + icosahedron(opacity=0.7) + sphinx_plot(G) + Display nice faces only:: sage: icosahedron().stickers(['red','blue'], .075, .1) Graphics3d Object +.. PLOT:: + + sphinx_plot(icosahedron().stickers(['red','blue'], .075, .1)) + AUTHORS: - Robert Bradshaw (2007, 2008): initial version @@ -48,7 +67,6 @@ from sage.rings.all import RDF from sage.matrix.constructor import matrix - from shapes import Box, ColorCube from shapes2 import frame3d from index_face_set import IndexFaceSet @@ -154,31 +172,55 @@ def tetrahedron(center=(0, 0, 0), size=1, **kwds): sage: tetrahedron() Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron()) + A transparent green tetrahedron in front of a solid red one:: sage: tetrahedron(opacity=0.8, color='green') + tetrahedron((-2,1,0),color='red') Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron(opacity=0.8, color='green') + tetrahedron((-2,1,0),color='red')) + A translucent tetrahedron sharing space with a sphere:: - sage: tetrahedron(color='yellow',opacity=0.7) + sphere(r=.5, color='red') + sage: tetrahedron(color='yellow',opacity=0.7) + sphere(size=.5, color='red') Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron(color='yellow',opacity=0.7) + sphere(size = .5, color='red')) + A big tetrahedron:: sage: tetrahedron(size=10) Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron(size=10)) + A wide tetrahedron:: sage: tetrahedron(aspect_ratio=[1,1,1]).scale((4,4,1)) Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron(aspect_ratio=[1,1,1]).scale((4,4,1))) + A red and blue tetrahedron touching noses:: sage: tetrahedron(color='red') + tetrahedron((0,0,-2)).scale([1,1,-1]) Graphics3d Object + .. PLOT:: + + sphinx_plot(tetrahedron(color='red') + tetrahedron((0,0,-2)).scale([1,1,-1])) + A Dodecahedral complex of 5 tetrahedrons (a more elaborate example from Peter Jipsen):: @@ -191,6 +233,17 @@ def tetrahedron(center=(0, 0, 0), size=1, **kwds): sage: t5=tetrahedron(color='orange', opacity=0.5).rotateZ(t).rotate(v,8*pi/5) sage: show(t1+t2+t3+t4+t5, frame=False, zoom=1.3) + .. PLOT:: + + v=(sqrt(5.)/2-5/6, 5/6*sqrt(3.)-sqrt(15.)/2, sqrt(5.)/3) + t=acos(sqrt(5.)/3)/2 + t1=tetrahedron(aspect_ratio=(1,1,1), opacity=0.5).rotateZ(t) + t2=tetrahedron(color='red', opacity=0.5).rotateZ(t).rotate(v,2*pi/5) + t3=tetrahedron(color='green', opacity=0.5).rotateZ(t).rotate(v,4*pi/5) + t4=tetrahedron(color='yellow', opacity=0.5).rotateZ(t).rotate(v,6*pi/5) + t5=tetrahedron(color='orange', opacity=0.5).rotateZ(t).rotate(v,8*pi/5) + sphinx_plot(t1+t2+t3+t4+t5) + AUTHORS: - Robert Bradshaw and William Stein @@ -241,40 +294,76 @@ def cube(center=(0, 0, 0), size=1, color=None, frame_thickness=0, sage: cube() Graphics3d Object + .. PLOT:: + + sphinx_plot(cube()) + A red cube:: sage: cube(color="red") Graphics3d Object + .. PLOT:: + + sphinx_plot(cube(color="red")) + A transparent grey cube that contains a red cube:: sage: cube(opacity=0.8, color='grey') + cube(size=3/4) Graphics3d Object + .. PLOT:: + + sphinx_plot(cube(opacity=0.8, color='grey') + cube(size=3/4)) + A transparent colored cube:: sage: cube(color=['red', 'green', 'blue'], opacity=0.5) Graphics3d Object + .. PLOT:: + + sphinx_plot(cube(color=['red', 'green', 'blue'], opacity=0.5)) + A bunch of random cubes:: sage: v = [(random(), random(), random()) for _ in [1..30]] sage: sum([cube((10*a,10*b,10*c), size=random()/3, color=(a,b,c)) for a,b,c in v]) Graphics3d Object + .. PLOT:: + + v = [(random(), random(), random()) for _ in range(30)] + sphinx_plot(sum([cube((10*a,10*b,10*c), size=random()/3, color=(a,b,c)) for a,b,c in v])) + Non-square cubes (boxes):: sage: cube(aspect_ratio=[1,1,1]).scale([1,2,3]) Graphics3d Object + + .. PLOT:: + + sphinx_plot(cube(aspect_ratio=[1,1,1]).scale([1,2,3])) + + :: + sage: cube(color=['red', 'blue', 'green'],aspect_ratio=[1,1,1]).scale([1,2,3]) Graphics3d Object + .. PLOT:: + + sphinx_plot(cube(color=['red', 'blue', 'green'],aspect_ratio=[1,1,1]).scale([1,2,3])) + And one that is colored:: sage: cube(color=['red', 'blue', 'green', 'black', 'white', 'orange'], ....: aspect_ratio=[1,1,1]).scale([1,2,3]) Graphics3d Object + .. PLOT:: + + sphinx_plot(cube(color=['red', 'blue', 'green', 'black', 'white', 'orange'],aspect_ratio=[1,1,1]).scale([1,2,3])) + A nice translucent color cube with a frame:: sage: c = cube(color=['red', 'blue', 'green'], frame=False, frame_thickness=2, @@ -282,6 +371,11 @@ def cube(center=(0, 0, 0), size=1, color=None, frame_thickness=0, sage: c Graphics3d Object + .. PLOT:: + + c = cube(color=['red', 'blue', 'green'], frame=False, frame_thickness=2,frame_color='brown', opacity=0.8) + sphinx_plot(c) + A raytraced color cube with frame and transparency:: sage: c.show(viewer='tachyon') @@ -290,7 +384,7 @@ def cube(center=(0, 0, 0), size=1, color=None, frame_thickness=0, sage: cube(center=(10, 10, 10), size=0.5).bounding_box() ((9.75, 9.75, 9.75), (10.25, 10.25, 10.25)) - + AUTHORS: - William Stein @@ -332,6 +426,13 @@ def octahedron(center=(0, 0, 0), size=1, **kwds): sage: G += octahedron((0,2,1), size=2, opacity=0.6) sage: G Graphics3d Object + + .. PLOT:: + + G = octahedron((1,4,3), color='orange') + G += octahedron((0,2,1), size=2, opacity=0.6) + sphinx_plot(G) + """ if 'aspect_ratio' not in kwds: kwds['aspect_ratio'] = [1, 1, 1] @@ -359,6 +460,10 @@ def dodecahedron(center=(0, 0, 0), size=1, **kwds): sage: dodecahedron() Graphics3d Object + .. PLOT:: + + sphinx_plot(dodecahedron()) + A translucent dodecahedron that contains a black sphere:: sage: G = dodecahedron(color='orange', opacity=0.8) @@ -366,6 +471,12 @@ def dodecahedron(center=(0, 0, 0), size=1, **kwds): sage: G Graphics3d Object + .. PLOT:: + + G = dodecahedron(color='orange', opacity=0.8) + G += sphere(size=0.5, color='black') + sphinx_plot(G) + CONSTRUCTION: This is how we construct a dodecahedron. We let one point be `Q = (0,1,0)`. @@ -462,12 +573,23 @@ def icosahedron(center=(0, 0, 0), size=1, **kwds): sage: icosahedron() Graphics3d Object + .. PLOT:: + + sphinx_plot(icosahedron()) + Two icosahedrons at different positions of different sizes. :: sage: p = icosahedron((-1/2,0,1), color='orange') - sage: p += icosahedron((2,0,1), size=1/2, aspect_ratio=[1,1,1]) + sage: p += icosahedron((2,0,1), size=1/2, color='red', aspect_ratio=[1,1,1]) sage: p Graphics3d Object + + .. PLOT:: + + p = icosahedron((-1/2,0,1), color='orange') + p += icosahedron((2,0,1), size = 0.5, color='red', aspect_ratio=[1,1,1]) + sphinx_plot(p) + """ if 'aspect_ratio' not in kwds: kwds['aspect_ratio'] = [1, 1, 1] diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 3bed8568480..1d3e7659b9c 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -9,6 +9,12 @@ sage: P = plot3d(f,(-3,3),(-3,3), adaptive=True, color=rainbow(60, 'rgbtuple'), max_bend=.1, max_depth=15) sage: P.show() +.. PLOT:: + + def f(x,y): return math.sin(y*y+x*x)/math.sqrt(x*x+y*y+.0001) + P = plot3d(f,(-3,3),(-3,3), adaptive=True, color=rainbow(60, 'rgbtuple'), max_bend=.1, max_depth=15) + sphinx_plot(P) + :: sage: def f(x,y): @@ -19,6 +25,14 @@ sage: S = P + axes(6, color='black') sage: S.show() +.. PLOT:: + + def f(x,y): return math.exp(x/5)*math.sin(y) + P = plot3d(f,(-5,5),(-5,5), adaptive=True, color=['red','yellow']) + from sage.plot.plot3d.plot3d import axes + S = P + axes(6, color='black') + sphinx_plot(S) + We plot "cape man":: sage: S = sphere(size=.5, color='yellow') @@ -38,11 +52,29 @@ sage: cape_man = P.scale(.2) + S.translate(1,0,0) sage: cape_man.show(aspect_ratio=[1,1,1]) +.. PLOT:: + + S = sphere(size=.5, color='yellow') + from sage.plot.plot3d.shapes import Cone + S += Cone(.5, .5, color='red').translate(0,0,.3) + S += sphere((.45,-.1,.15), size=.1, color='white') + sphere((.51,-.1,.17), size=.05, color='black') + S += sphere((.45, .1,.15),size=.1, color='white') + sphere((.51, .1,.17), size=.05, color='black') + S += sphere((.5,0,-.2),size=.1, color='yellow') + def f(x,y): return math.exp(x/5)*math.cos(y) + P = plot3d(f,(-5,5),(-5,5), adaptive=True, color=['red','yellow'], max_depth=10) + cape_man = P.scale(.2) + S.translate(1,0,0) + cape_man.aspect_ratio([1,1,1]) + sphinx_plot(cape_man) + Or, we plot a very simple function indeed:: sage: plot3d(pi, (-1,1), (-1,1)) Graphics3d Object +.. PLOT:: + + sphinx_plot(plot3d(pi, (-1,1), (-1,1))) + AUTHORS: - Tom Boothby: adaptive refinement triangles @@ -427,6 +459,12 @@ class Spherical(_Coordinates): sage: plot3d(phi * theta, (theta, 0, pi), (phi, 0, 1), transformation=T) Graphics3d Object + .. PLOT:: + + r, phi, theta = var('r phi theta') + T = Spherical('radius', ['azimuth', 'inclination']) + sphinx_plot(plot3d(phi * theta, (theta, 0, pi), (phi, 0, 1), transformation=T)) + We next graph the function where the inclination angle is constant:: sage: S=Spherical('inclination', ['radius', 'azimuth']) @@ -434,6 +472,12 @@ class Spherical(_Coordinates): sage: plot3d(3, (r,0,3), (theta, 0, 2*pi), transformation=S) Graphics3d Object + .. PLOT:: + + S=Spherical('inclination', ['radius', 'azimuth']) + r,theta=var('r,theta') + sphinx_plot(plot3d(r-r+3, (r,0,3), (theta, 0, 2*pi), transformation=S)) + See also :func:`spherical_plot3d` for more examples of plotting in spherical coordinates. """ @@ -484,6 +528,12 @@ class SphericalElevation(_Coordinates): sage: plot3d(phi * theta, (theta, 0, pi), (phi, 0, 1), transformation=T) Graphics3d Object + .. PLOT:: + + T = SphericalElevation('radius', ['azimuth', 'elevation']) + r, theta, phi = var('r theta phi') + sphinx_plot(plot3d(phi * theta, (theta, 0, pi), (phi, 0, 1), transformation=T)) + We next graph the function where the elevation angle is constant. This should be compared to the similar example for the ``Spherical`` coordinate system:: @@ -493,6 +543,12 @@ class SphericalElevation(_Coordinates): sage: plot3d(3, (r,0,3), (theta, 0, 2*pi), transformation=SE) Graphics3d Object + .. PLOT:: + + SE=SphericalElevation('elevation', ['radius', 'azimuth']) + r,theta=var('r,theta') + sphinx_plot(plot3d(3+r-r, (r,0,3), (theta, 0, 2*pi), transformation=SE)) + Plot a sin curve wrapped around the equator:: sage: P1=plot3d( (pi/12)*sin(8*theta), (r,0.99,1), (theta, 0, 2*pi), transformation=SE, plot_points=(10,200)) @@ -500,6 +556,14 @@ class SphericalElevation(_Coordinates): sage: P1+P2 Graphics3d Object + .. PLOT:: + + r,theta=var('r,theta') + SE=SphericalElevation('elevation', ['radius', 'azimuth']) + P1=plot3d( (pi/12)*sin(8*theta), (r,0.99,1), (theta, 0, 2*pi), transformation=SE, plot_points=(10,200)) + P2=sphere(center=(0,0,0), size=1, color='red', opacity=0.3) + sphinx_plot(P1+P2) + Now we graph several constant elevation functions alongside several constant inclination functions. This example illustrates the difference between the ``Spherical`` coordinate system and the ``SphericalElevation`` coordinate @@ -514,6 +578,19 @@ class SphericalElevation(_Coordinates): sage: P2 = [plot3d( a, (r,0,3), (theta, 0, 2*pi), transformation=S, opacity=0.85, color='red') for a in angles] sage: show(sum(P1+P2), aspect_ratio=1) + .. PLOT:: + + r, phi, theta = var('r phi theta') + SE = SphericalElevation('elevation', ['radius', 'azimuth']) + S = Spherical('inclination', ['radius', 'azimuth']) + angles = [pi/18, pi/12, pi/6] + P1=Graphics() + P2=Graphics() + for a in angles: + P1 += plot3d( a, (r,0,3), (theta, 0, 2*pi), transformation=SE, opacity=0.85, color='blue') + P2 += plot3d( a, (r,0,3), (theta, 0, 2*pi), transformation=S, opacity=0.85, color='red') + sphinx_plot(P1+P2) + See also :func:`spherical_plot3d` for more examples of plotting in spherical coordinates. """ @@ -565,6 +642,12 @@ class Cylindrical(_Coordinates): sage: plot3d(9-r^2, (r, 0, 3), (theta, 0, pi), transformation=T) Graphics3d Object + .. PLOT:: + + T = Cylindrical('height', ['radius', 'azimuth']) + r, theta, z = var('r theta z') + sphinx_plot(plot3d(9-r**2, (r, 0, 3), (theta, 0, pi), transformation=T)) + We next graph the function where the radius is constant:: sage: S=Cylindrical('radius', ['azimuth', 'height']) @@ -572,6 +655,12 @@ class Cylindrical(_Coordinates): sage: plot3d(3, (theta,0,2*pi), (z, -2, 2), transformation=S) Graphics3d Object + .. PLOT:: + + S=Cylindrical('radius', ['azimuth', 'height']) + theta,z=var('theta, z') + sphinx_plot(plot3d(3+z-z, (theta,0,2*pi), (z, -2, 2), transformation=S)) + See also :func:`cylindrical_plot3d` for more examples of plotting in cylindrical coordinates. """ @@ -700,34 +789,72 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2)) Graphics3d Object + .. PLOT:: + + sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2))) + We plot the same 3d function but using adaptive refinement:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True) Graphics3d Object + .. PLOT:: + + sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True)) + Adaptive refinement but with more points:: sage: plot3d(lambda x, y: x^2 + y^2, (-2,2), (-2,2), adaptive=True, initial_depth=5) Graphics3d Object + .. PLOT:: + + sphinx_plot(plot3d(lambda x, y: x**2 + y**2, (-2,2), (-2,2), adaptive=True, initial_depth=5)) + We plot some 3d symbolic functions:: sage: var('x,y') (x, y) sage: plot3d(x^2 + y^2, (x,-2,2), (y,-2,2)) Graphics3d Object + + .. PLOT:: + + var('x y') + sphinx_plot(plot3d(x**2 + y**2, (x,-2,2), (y,-2,2))) + + :: + sage: plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi)) Graphics3d Object + .. PLOT:: + + var('x y') + sphinx_plot(plot3d(sin(x*y), (x, -pi, pi), (y, -pi, pi))) + We give a plot with extra sample points:: sage: var('x,y') (x, y) sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=200) Graphics3d Object + + .. PLOT:: + + var('x y') + sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=200)) + + :: + sage: plot3d(sin(x^2+y^2),(x,-5,5),(y,-5,5), plot_points=[10,100]) Graphics3d Object + .. PLOT:: + + var('x y') + sphinx_plot(plot3d(sin(x**2+y**2),(x,-5,5),(y,-5,5), plot_points=[10,100])) + A 3d plot with a mesh:: sage: var('x,y') @@ -735,6 +862,11 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True) Graphics3d Object + .. PLOT:: + + var('x y') + sphinx_plot(plot3d(sin(x-y)*y*cos(x),(x,-3,3),(y,-3,3), mesh=True)) + Two wobby translucent planes:: sage: x,y = var('x,y') @@ -743,6 +875,13 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: P + Q Graphics3d Object + .. PLOT:: + + x,y=var('x y') + P = plot3d(x+y+sin(x*y), (x,-10,10),(y,-10,10), opacity=0.87, color='blue') + Q = plot3d(x-2*y-cos(x*y),(x,-10,10),(y,-10,10),opacity=0.3,color='red') + sphinx_plot(P+Q) + We draw two parametric surfaces and a transparent plane:: sage: L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8) @@ -751,30 +890,62 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): sage: L + P + Q Graphics3d Object + .. PLOT:: + + L = plot3d(lambda x,y: 0, (-5,5), (-5,5), color="lightblue", opacity=0.8) + P = plot3d(lambda x,y: 4 - x**3 - y**2, (-2,2), (-2,2), color='green') + Q = plot3d(lambda x,y: x**3 + y**2 - 4, (-2,2), (-2,2), color='orange') + sphinx_plot(L+P+Q) + We draw the "Sinus" function (water ripple-like surface):: sage: x, y = var('x y') sage: plot3d(sin(pi*(x^2+y^2))/2,(x,-1,1),(y,-1,1)) Graphics3d Object + .. PLOT:: + + x, y = var('x y') + sphinx_plot(plot3d(sin(pi*(x**2+y**2))/2,(x,-1,1),(y,-1,1))) + Hill and valley (flat surface with a bump and a dent):: sage: x, y = var('x y') sage: plot3d( 4*x*exp(-x^2-y^2), (x,-2,2), (y,-2,2)) Graphics3d Object + .. PLOT:: + + x, y = var('x y') + sphinx_plot(plot3d( 4*x*exp(-x**2-y**2), (x,-2,2), (y,-2,2))) + An example of a transformation:: sage: r, phi, z = var('r phi z') sage: trans=(r*cos(phi),r*sin(phi),z) sage: plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87).show(aspect_ratio=(1,1,2),frame=False) + .. PLOT:: + + r, phi, z = var('r phi z') + trans = (r*cos(phi),r*sin(phi),z) + P = plot3d(cos(r),(r,0,17*pi/2),(phi,0,2*pi),transformation=trans,opacity=0.87) + P.aspect_ratio([1,1,2]) + sphinx_plot(P) + An example of a transformation with symbolic vector:: sage: cylindrical(r,theta,z)=[r*cos(theta),r*sin(theta),z] sage: plot3d(3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical) Graphics3d Object + .. PLOT:: + + r, theta, z = var('r theta z') + cylindrical=(r*cos(theta),r*sin(theta),z) + P = plot3d(z-z+3,(theta,0,pi/2),(z,0,pi/2),transformation=cylindrical) + sphinx_plot(P) + Many more examples of transformations:: sage: u, v, w = var('u v w') @@ -914,6 +1085,13 @@ def plot3d_adaptive(f, x_range, y_range, color="automatic", sage: from sage.plot.plot3d.plot3d import plot3d_adaptive sage: x,y=var('x,y'); plot3d_adaptive(sin(x*y), (x,-pi,pi), (y,-pi,pi), initial_depth=5) Graphics3d Object + + .. PLOT:: + + from sage.plot.plot3d.plot3d import plot3d_adaptive + x,y=var('x,y') + sphinx_plot(plot3d_adaptive(sin(x*y), (x,-pi,pi), (y,-pi,pi), initial_depth=5)) + """ if initial_depth >= max_depth: max_depth = initial_depth @@ -1002,6 +1180,11 @@ def spherical_plot3d(f, urange, vrange, **kwds): sage: spherical_plot3d(2,(x,0,2*pi),(y,0,pi)) Graphics3d Object + .. PLOT:: + + x,y=var('x,y') + sphinx_plot(spherical_plot3d(x-x+2,(x,0,2*pi),(y,0,pi))) + The real and imaginary parts of a spherical harmonic with `l=2` and `m=1`:: sage: phi, theta = var('phi, theta') @@ -1010,17 +1193,35 @@ def spherical_plot3d(f, urange, vrange, **kwds): sage: ima = spherical_plot3d(abs(imag(Y)), (phi,0,2*pi), (theta,0,pi), color='red', opacity=0.6) sage: (rea + ima).show(aspect_ratio=1) # long time (4s on sage.math, 2011) + .. PLOT:: + + phi, theta = var('phi, theta') + Y = spherical_harmonic(2, 1, theta, phi) + rea = spherical_plot3d(abs(real(Y)), (phi,0,2*pi), (theta,0,pi), color='blue', opacity=0.6) + ima = spherical_plot3d(abs(imag(Y)), (phi,0,2*pi), (theta,0,pi), color='red', opacity=0.6) + sphinx_plot(rea+ima) + A drop of water:: sage: x,y=var('x,y') sage: spherical_plot3d(e^-y,(x,0,2*pi),(y,0,pi),opacity=0.5).show(frame=False) + .. PLOT:: + + x,y=var('x,y') + sphinx_plot(spherical_plot3d(e**-y,(x,0,2*pi),(y,0,pi),opacity=0.5)) + An object similar to a heart:: sage: x,y=var('x,y') sage: spherical_plot3d((2+cos(2*x))*(y+1),(x,0,2*pi),(y,0,pi),rgbcolor=(1,.1,.1)) Graphics3d Object + .. PLOT:: + + x,y=var('x,y') + sphinx_plot(spherical_plot3d((2+cos(2*x))*(y+1),(x,0,2*pi),(y,0,pi),rgbcolor=(1,.1,.1))) + Some random figures: :: @@ -1029,10 +1230,21 @@ def spherical_plot3d(f, urange, vrange, **kwds): sage: spherical_plot3d(1+sin(5*x)/5,(x,0,2*pi),(y,0,pi),rgbcolor=(1,0.5,0),plot_points=(80,80),opacity=0.7) Graphics3d Object + .. PLOT:: + + x,y=var('x,y') + sphinx_plot(spherical_plot3d(1+sin(5*x)/5,(x,0,2*pi),(y,0,pi),rgbcolor=(1,0.5,0),plot_points=(80,80),opacity=0.7)) + :: sage: x,y=var('x,y') sage: spherical_plot3d(1+2*cos(2*y),(x,0,3*pi/2),(y,0,pi)).show(aspect_ratio=(1,1,1)) + + .. PLOT:: + + x,y=var('x,y') + sphinx_plot(spherical_plot3d(1+2*cos(2*y),(x,0,3*pi/2),(y,0,pi))) + """ return plot3d(f, urange, vrange, transformation=Spherical('radius', ['azimuth', 'inclination']), **kwds) @@ -1047,6 +1259,13 @@ def cylindrical_plot3d(f, urange, vrange, **kwds): sage: plot3d(f, urange, vrange, transformation=T) Graphics3d Object + .. PLOT:: + + r,u,v=var('r,u,v') + f=u*v; urange=(u,0,pi); vrange=(v,0,pi) + T = (r*cos(u), r*sin(u), v, [u,v]) + sphinx_plot(plot3d(f, urange, vrange, transformation=T)) + or equivalently:: sage: T = Cylindrical('radius', ['azimuth', 'height']) @@ -1074,6 +1293,11 @@ def cylindrical_plot3d(f, urange, vrange, **kwds): sage: cylindrical_plot3d(2,(theta,0,3*pi/2),(z,-2,2)) Graphics3d Object + .. PLOT:: + + theta,z=var('theta,z') + sphinx_plot(cylindrical_plot3d(z-z+2,(theta,0,3*pi/2),(z,-2,2))) + Some random figures: :: @@ -1081,9 +1305,22 @@ def cylindrical_plot3d(f, urange, vrange, **kwds): sage: cylindrical_plot3d(cosh(z),(theta,0,2*pi),(z,-2,2)) Graphics3d Object + .. PLOT:: + + theta,z=var('theta,z') + sphinx_plot(cylindrical_plot3d(cosh(z),(theta,0,2*pi),(z,-2,2))) + :: sage: cylindrical_plot3d(e^(-z^2)*(cos(4*theta)+2)+1,(theta,0,2*pi),(z,-2,2),plot_points=[80,80]).show(aspect_ratio=(1,1,1)) + + .. PLOT:: + + theta,z=var('theta,z') + P = cylindrical_plot3d(e**(-z**2)*(cos(4*theta)+2)+1,(theta,0,2*pi),(z,-2,2),plot_points=[80,80]) + P.aspect_ratio([1,1,1]) + sphinx_plot(P) + """ return plot3d(f, urange, vrange, transformation=Cylindrical('radius', ['azimuth', 'height']), **kwds) @@ -1104,10 +1341,23 @@ def axes(scale=1, radius=None, **kwds): sage: S = axes(6, color='black'); S Graphics3d Object + .. PLOT:: + + from sage.plot.plot3d.plot3d import axes + S = axes(6, color='black') + sphinx_plot(S) + :: sage: T = axes(2, .5); T Graphics3d Object + + .. PLOT:: + + from sage.plot.plot3d.plot3d import axes + T = axes(2, .5) + sphinx_plot(T) + """ if radius is None: radius = scale/100.0 diff --git a/src/sage/plot/plot3d/shapes.pyx b/src/sage/plot/plot3d/shapes.pyx index 539b1a4bd25..ca2abbdbbad 100644 --- a/src/sage/plot/plot3d/shapes.pyx +++ b/src/sage/plot/plot3d/shapes.pyx @@ -18,8 +18,26 @@ EXAMPLES:: sage: S.show() sage: S.scale(1,1,2).show() +.. PLOT:: + + from sage.plot.plot3d.shapes import * + S = Sphere(.5, color='yellow') + S += Cone(.5, .5, color='red').translate(0,0,.3) + S += Sphere(.1, color='white').translate(.45,-.1,.15) + Sphere(.05, color='black').translate(.51,-.1,.17) + S += Sphere(.1, color='white').translate(.45, .1,.15) + Sphere(.05, color='black').translate(.51, .1,.17) + S += Sphere(.1, color='yellow').translate(.5, 0, -.2) + sphinx_plot(S) + +:: + sage: from sage.plot.plot3d.shapes import * sage: Torus(.7, .2, color=(0,.3,0)).show() + +.. PLOT:: + + from sage.plot.plot3d.shapes import * + sphinx_plot(Torus(.7, .2, color=(0,.3,0))) + """ @@ -49,11 +67,10 @@ cdef extern from "math.h": double atan(double) double M_PI + from sage.rings.real_double import RDF from sage.modules.free_module_element import vector - from sage.plot.misc import rename_keyword - from base import Graphics3dGroup, Graphics3d # Helper function to check that Box input is right @@ -106,18 +123,41 @@ class Box(IndexFaceSet): sage: show(Box([1,1,1]), color='black') + .. PLOT:: + + from sage.plot.plot3d.shapes import Box + sphinx_plot(Box([1,1,1], color='black')) + A red rectangular box:: sage: show(Box([2,3,4], color="red")) + .. PLOT:: + + from sage.plot.plot3d.shapes import Box + sphinx_plot(Box([2,3,4], color="red")) + A stack of boxes:: sage: show(sum([Box([2,3,1], color="red").translate((0,0,6*i)) for i in [0..3]])) + .. PLOT:: + + from sage.plot.plot3d.shapes import Box + P = sum([Box([2,3,1], color="red").translate((0,0,6*i)) for i in range(0,4)]) + sphinx_plot(P) + A sinusoidal stack of multicolored boxes:: sage: B = sum([Box([2,4,1/4], color=(i/4,i/5,1)).translate((sin(i),0,5-i)) for i in [0..20]]) sage: show(B, figsize=6) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Box + B = sum([Box([2,4,1/4], color=(i/4.0,i/5.0,1)).translate((sin(i),0,5-i)) for i in range(0,21)]) + sphinx_plot(B) + """ def __init__(self, *size, **kwds): """ @@ -180,6 +220,15 @@ def ColorCube(size, colors, opacity=1, **kwds): sage: from sage.plot.plot3d.shapes import ColorCube sage: c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5) sage: c.show() + + .. PLOT:: + + from sage.plot.plot3d.shapes import ColorCube + c = ColorCube([1,2,3], ['red', 'blue', 'green', 'black', 'white', 'orange'], opacity=0.5) + sphinx_plot(c) + + :: + sage: list(c.texture_set())[0].opacity 0.5 @@ -187,6 +236,13 @@ def ColorCube(size, colors, opacity=1, **kwds): repeated colors on opposing faces):: sage: c = ColorCube([0.5,0.5,0.5], ['red', 'blue', 'green']) + + .. PLOT:: + + from sage.plot.plot3d.shapes import ColorCube + c = ColorCube([0.5,0.5,0.5], ['red', 'blue', 'green']) + sphinx_plot(c) + """ if not isinstance(size, (tuple, list)): size = (size, size, size) @@ -224,21 +280,45 @@ cdef class Cone(ParametricSurface): sage: c = Cone(3/2, 1, color='red') + Cone(1, 2, color='yellow').translate(3, 0, 0) sage: c.show(aspect_ratio=1) + .. PLOT:: + + from sage.plot.plot3d.shapes import Cone + c = Cone(3/2, 1, color='red') + Cone(1, 2, color='yellow').translate(3, 0, 0) + sphinx_plot(c) + We may omit the base:: sage: Cone(1, 1, closed=False) Graphics3d Object + .. PLOT:: + + from sage.plot.plot3d.shapes import Cone + sphinx_plot(Cone(1, 1, closed=False)) + A spiky plot of the sine function:: sage: sum(Cone(.1, sin(n), color='yellow').translate(n, sin(n), 0) for n in [0..10, step=.1]) Graphics3d Object + .. PLOT:: + + from sage.plot.plot3d.shapes import Cone + sphinx_plot(sum(Cone(.1, sin(n/10.0), color='yellow').translate(n/10.0, sin(n/10.0), 0) for n in range(0,100))) + A Christmas tree:: sage: T = sum(Cone(exp(-n/5), 4/3*exp(-n/5), color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5)) for n in [1..7]) sage: T += Cone(1/8, 1, color='brown').translate(0, 0, -3) sage: T.show(aspect_ratio=1, frame=False) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Cone + T = sum(Cone(exp(-n/5.0), 4/3*exp(-n/5.0), color=(0, .5, 0)).translate(0, 0, -3*exp(-n/5.0)) for n in range(8)) + T += Cone(1/8, 1, color='brown').translate(0, 0, -3) + sphinx_plot(T) + """ def __init__(self, radius, height, closed=True, **kwds): """ @@ -316,11 +396,22 @@ cdef class Cylinder(ParametricSurface): sage: c = Cylinder(3/2, 1, color='red') + Cylinder(1, 2, color='yellow').translate(3, 0, 0) sage: c.show(aspect_ratio=1) + .. PLOT:: + + from sage.plot.plot3d.shapes import Cylinder + c = Cylinder(3/2, 1, color='red') + Cylinder(1, 2, color='yellow').translate(3, 0, 0) + sphinx_plot(c) + We may omit the base:: sage: Cylinder(1, 1, closed=False) Graphics3d Object + .. PLOT:: + + from sage.plot.plot3d.shapes import Cylinder + sphinx_plot(Cylinder(1, 1, closed=False)) + Some gears:: sage: G = Cylinder(1, .5) + Cylinder(.25, 3).translate(0, 0, -3) @@ -328,6 +419,16 @@ cdef class Cylinder(ParametricSurface): sage: G += G.translate(2.3, 0, -.5) sage: G += G.translate(3.5, 2, -1) sage: G.show(aspect_ratio=1, frame=False) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Cylinder + G = Cylinder(1, .5) + Cylinder(.25, 3).translate(0, 0, -3) + G += sum(Cylinder(.2, 1).translate(cos(2*pi*n/9.0), sin(2*pi*n/9.0), 0) for n in range(10)) + G += G.translate(2.3, 0, -.5) + G += G.translate(3.5, 2, -1) + sphinx_plot(G) + """ def __init__(self, radius, height, closed=True, **kwds): """ @@ -523,11 +624,34 @@ def LineSegment(start, end, thickness=1, radius=None, **kwds): sage: S += Sphere(.2, color='blue').translate(Q) sage: S += LineSegment(P, Q, .05, color='black') sage: S.show() + + .. PLOT:: + + from sage.plot.plot3d.shapes import LineSegment, Sphere + P = (0,0,0.1) + Q = (0.5,0.6,0.7) + S = Sphere(.2, color='red').translate(P) + S += Sphere(.2, color='blue').translate(Q) + S += LineSegment(P, Q, .05, color='black') + sphinx_plot(S) + + :: + sage: S = Sphere(.1, color='red').translate(P) sage: S += Sphere(.1, color='blue').translate(Q) sage: S += LineSegment(P, Q, .15, color='black') sage: S.show() + .. PLOT:: + + from sage.plot.plot3d.shapes import LineSegment, Sphere + P = (0,0,0.1) + Q = (0.5,0.6,0.7) + S = Sphere(.1, color='red').translate(P) + S += Sphere(.1, color='blue').translate(Q) + S += LineSegment(P, Q, .15, color='black') + sphinx_plot(S) + AUTHOR: - Robert Bradshaw @@ -571,32 +695,61 @@ def arrow3d(start, end, width=1, radius=None, head_radius=None, head_len=None, * sage: arrow3d((0,0,0), (1,1,1), 1) Graphics3d Object + .. PLOT:: + + sphinx_plot(arrow3d((0,0,0), (1,1,1), 1)) + A fat arrow:: sage: arrow3d((0,0,0), (1,1,1), radius=0.1) Graphics3d Object + .. PLOT:: + + sphinx_plot(arrow3d((0,0,0), (1,1,1), radius=0.1)) + A green arrow:: sage: arrow3d((0,0,0), (1,1,1), color='green') Graphics3d Object + .. PLOT:: + + sphinx_plot(arrow3d((0,0,0), (1,1,1), color='green')) + A fat arrow head:: sage: arrow3d((2,1,0), (1,1,1), color='green', head_radius=0.3, aspect_ratio=[1,1,1]) Graphics3d Object + .. PLOT:: + + sphinx_plot(arrow3d((2,1,0), (1,1,1), color='green', head_radius=0.3, aspect_ratio=[1,1,1])) + Many arrows arranged in a circle (flying spears?):: sage: sum([arrow3d((cos(t),sin(t),0),(cos(t),sin(t),1)) for t in [0,0.3,..,2*pi]]) Graphics3d Object + .. PLOT:: + + t=0 + G=Graphics() + while (t<=2*pi): + G += arrow3d((cos(t),sin(t),0),(cos(t),sin(t),1)) + t +=0.3 + sphinx_plot(G) + Change the width of the arrow. (Note: for an arrow that scales with zoom, please consider the ``line3d`` function with the option ``arrow_head=True``):: sage: arrow3d((0,0,0), (1,1,1), width=1) Graphics3d Object + .. PLOT:: + + sphinx_plot(arrow3d((0,0,0), (1,1,1), width=1)) + TESTS: If the arrow is too long, the shaft and part of the head is cut off. :: @@ -657,15 +810,33 @@ cdef class Sphere(ParametricSurface): sage: Sphere(3) Graphics3d Object + .. PLOT:: + + from sage.plot.plot3d.shapes import Sphere + sphinx_plot(Sphere(3)) + Plot with aspect_ratio=1 to see it unsquashed:: sage: S = Sphere(3, color='blue') + Sphere(2, color='red').translate(0,3,0) sage: S.show(aspect_ratio=1) + .. PLOT:: + + from sage.plot.plot3d.shapes import Sphere + S = Sphere(3, color='blue') + Sphere(2, color='red').translate(0,3,0) + sphinx_plot(S) + Scale to get an ellipsoid:: sage: S = Sphere(1).scale(1,2,1/2) sage: S.show(aspect_ratio=1) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Sphere + S = Sphere(1).scale(1,2,1/2) + sphinx_plot(S) + """ def __init__(self, radius, **kwds): """ @@ -821,17 +992,43 @@ cdef class Torus(ParametricSurface): sage: from sage.plot.plot3d.shapes import Torus sage: Torus(1, .2).show(aspect_ratio=1) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Torus + sphinx_plot(Torus(1, .2)) + + :: + sage: Torus(1, .7, color='red').show(aspect_ratio=1) + .. PLOT:: + + from sage.plot.plot3d.shapes import Torus + sphinx_plot(Torus(1, .7, color='red')) + A rubberband ball:: sage: show(sum([Torus(1, .03, color=(1, t/30.0, 0)).rotate((1,1,1),t) for t in range(30)])) + .. PLOT:: + + from sage.plot.plot3d.shapes import Torus + sphinx_plot(sum([Torus(1, .03, color=(1, t/30.0, 0)).rotate((1,1,1),t) for t in range(30)])) + Mmm... doughnuts:: sage: D = Torus(1, .4, color=(.5, .3, .2)) + Torus(1, .3, color='yellow').translate(0, 0, .15) sage: G = sum(D.translate(RDF.random_element(-.2, .2), RDF.random_element(-.2, .2), .8*t) for t in range(10)) sage: G.show(aspect_ratio=1, frame=False) + + .. PLOT:: + + from sage.plot.plot3d.shapes import Torus + D = Torus(1, .4, color=(.5, .3, .2)) + Torus(1, .3, color='yellow').translate(0, 0, .15) + G = sum(D.translate(RDF.random_element(-.2, .2), RDF.random_element(-.2, .2), .8*t) for t in range(10)) + sphinx_plot(G) + """ def __init__(self, R=1, r=.3, **kwds): """ @@ -879,9 +1076,24 @@ class Text(PrimitiveObject): sage: from sage.plot.plot3d.shapes import Text sage: Text("Just a lonely label.") Graphics3d Object + + .. PLOT:: + + from sage.plot.plot3d.shapes import Text + sphinx_plot(Text("Just a lonely label. ")) + + :: + sage: pts = [(RealField(10)^3).random_element() for k in range(20)] sage: sum(Text(str(P)).translate(P) for P in pts) Graphics3d Object + + .. PLOT:: + + from sage.plot.plot3d.shapes import Text + pts = [(RealField(10)**3).random_element() for k in range(20)] + sphinx_plot(sum(Text(str(P)).translate(P) for P in pts)) + """ def __init__(self, string, **kwds): """ @@ -927,7 +1139,7 @@ class Text(PrimitiveObject): ## transform = render_params.transform ## if not (transform is None or transform.is_uniform()): ## return ParametricSurface.tachyon_repr(self, render_params) - +## ## if transform is None: ## cen = (0,0,0) ## rad = self.radius diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 0bb110288bd..674607de852 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -10,15 +10,10 @@ #***************************************************************************** # Copyright (C) 2007 William Stein and Jonathan Hanke # -# 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/ #***************************************************************************** @@ -34,7 +29,6 @@ from sage.arith.all import GCD, LCM from sage.rings.all import Ideal 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 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -384,8 +378,17 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ EXAMPLES:: sage: s = QuadraticForm(ZZ, 4, range(10)) + sage: s.dim() + 4 + + TESTS:: + sage: s == loads(dumps(s)) True + sage: QuadraticForm(ZZ, -1) + Traceback (most recent call last): + ... + ValueError: the size must be a non-negative integer, not -1 """ ## Deal with: QuadraticForm(ring, matrix) matrix_init_flag = False @@ -400,10 +403,8 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ M_ring = R matrix_init_flag = True - ## Deal with: QuadraticForm(matrix) - if is_Matrix(R) and (n is None): - + if n is None and is_Matrix(R): ## Test if R is symmetric and has even diagonal if not self._is_even_symmetric_matrix_(R): raise TypeError("Oops! The matrix is not a symmetric with even diagonal.") @@ -414,8 +415,8 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ matrix_init_flag = True ## Perform the quadratic form initialization - if matrix_init_flag == True: - self.__n = M.nrows() + if matrix_init_flag: + self.__n = ZZ(M.nrows()) self.__base_ring = M_ring self.__coeffs = [] for i in range(M.nrows()): @@ -430,20 +431,17 @@ def __init__(self, R, n=None, entries=None, unsafe_initialization=False, number_ ## ----------------------------------------------------------- ## Verify the size of the matrix is an integer >= 0 - try: - n = int(n) - except Exception: - raise TypeError("Oops! The size " + str(n) + " must be an integer.") - if (n < 0): - raise TypeError("Oops! The size " + str(n) + " must be a non-negative integer.") + n = ZZ(n) + if n < 0: + raise ValueError("the size must be a non-negative integer, not {}".format(n)) ## TODO: Verify that R is a ring... ## Store the relevant variables - N = n*(n+1)//2 - self.__n = int(n) + N = n*(n+1) // 2 + self.__n = n self.__base_ring = R - self.__coeffs = [self.__base_ring(0) for i in range(N)] + self.__coeffs = [self.__base_ring(0) for i in range(N)] ## Check if entries is a list for the current size, and if so, write the upper-triangular matrix if isinstance(entries, list) and (len(entries) == N): @@ -1265,8 +1263,6 @@ def adjoint_primitive(self): """ return QuadraticForm(self.Hessian_matrix().adjoint()).primitive() - - def dim(self): """ Gives the number of variables of the quadratic form. @@ -1276,11 +1272,16 @@ def dim(self): sage: Q = QuadraticForm(ZZ, 2, [1,2,3]) sage: Q.dim() 2 - + sage: parent(Q.dim()) + Integer Ring + sage: Q = QuadraticForm(Q.matrix()) + sage: Q.dim() + 2 + sage: parent(Q.dim()) + Integer Ring """ return self.__n - def base_ring(self): """ Gives the ring over which the quadratic form is defined. diff --git a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py index 225dfc2fbd3..62f063194e9 100644 --- a/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py +++ b/src/sage/quadratic_forms/quadratic_form__mass__Siegel_densities.py @@ -1,14 +1,16 @@ """ Local Masses and Siegel Densities """ -###################################################################################################### +######################################################################## ## Computes the local masses (rep'n densities of a form by itself) for a quadratic forms over ZZ ## using the papers of Pall [PSPUM VIII (1965), pp95--105] for p>2, and Watson [Mathematika ## 23, no. 1, (1976), pp 94--106] for p=2. These formulas will also work for any local field ## which is unramified at p=2. ## ## Copyright by Jonathan Hanke 2007 -###################################################################################################### +######################################################################## +# python3 +from __future__ import division import copy @@ -47,19 +49,18 @@ def mass__by_Siegel_densities(self, odd_algorithm="Pall", even_algorithm="Watson EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) - sage: Q.mass__by_Siegel_densities() + sage: m = Q.mass__by_Siegel_densities(); m 1/384 - sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1) + sage: m - (2^Q.dim() * factorial(Q.dim()))^(-1) 0 :: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1]) - sage: Q.mass__by_Siegel_densities() + sage: m = Q.mass__by_Siegel_densities(); m 1/48 - sage: Q.mass__by_Siegel_densities() - (2^Q.dim() * factorial(Q.dim()))^(-1) + sage: m - (2^Q.dim() * factorial(Q.dim()))^(-1) 0 - """ ## Setup n = self.dim() @@ -84,7 +85,7 @@ def mass__by_Siegel_densities(self, odd_algorithm="Pall", even_algorithm="Watson #print "\n---", prod([zeta__exact(ZZ(j)) for j in range(2, 2*s+1, 2)]) #print "gp3 = ", generic_prod if (n % 2 == 0): - generic_prod *= ZZ(1) * quadratic_L_function__exact(n/2, (-1)**(n/2) * char_d) + generic_prod *= quadratic_L_function__exact(n//2, ZZ(-1)**(n//2) * char_d) #print " NEW = ", ZZ(1) * quadratic_L_function__exact(n/2, (-1)**(n/2) * char_d) #print #print "gp4 = ", generic_prod @@ -92,12 +93,12 @@ def mass__by_Siegel_densities(self, odd_algorithm="Pall", even_algorithm="Watson #print "generic_prod =", generic_prod ## Determine the adjustment factors - adj_prod = 1 + adj_prod = ZZ.one() for p in prime_divisors(2 * self.det()): ## Cancel out the generic factors p_adjustment = prod([1 - ZZ(p)**(-j) for j in range(2, 2*s+1, 2)]) if (n % 2 == 0): - p_adjustment *= ZZ(1) * (1 - kronecker((-1)**(n/2) * char_d, p) * ZZ(p)**(-n/2)) + p_adjustment *= (1 - kronecker((-1)**(n//2) * char_d, p) * ZZ(p)**(-n//2)) #print " EXTRA = ", ZZ(1) * (1 - kronecker((-1)**(n/2) * char_d, p) * ZZ(p)**(-n/2)) #print "Factor to cancel the generic one:", p_adjustment @@ -254,7 +255,7 @@ def Watson_mass_at_2(self): for (s,L) in Jordan_Blocks: n_dict[s+1] = L.dim() if diag_dict[s].dim() == 0: - m_dict[s+1] = ZZ(1)/ZZ(2) * L.dim() + m_dict[s+1] = ZZ.one()/ZZ(2) * L.dim() else: m_dict[s+1] = floor(ZZ(L.dim() - 1) / ZZ(2)) #print " ==>", ZZ(L.dim() - 1) / ZZ(2), floor(ZZ(L.dim() - 1) / ZZ(2)) @@ -369,13 +370,13 @@ def Kitaoka_mass_at_2(self): q += Jordan_Blocks[j][1].dim() + 1 ## When N_{j+1} is "odd", add n_j + 1 ## Compute P = product of the P_j - P = QQ(1) + P = QQ.one() for j in range(s_min, s_max + 1): tmp_m = dim2_dict[j].dim() // 2 - P *= prod([QQ(1) - QQ(4**(-k)) for j in range(1, tmp_m + 1)]) + P *= prod(QQ.one() - QQ(4**(-k)) for k in range(1, tmp_m + 1)) ## Compute the product E := prod_j (1 / E_j) - E = QQ(1) + E = QQ.one() for j in range(s_min - 1, s_max + 2): if (diag_dict[j-1].dim() == 0) and (diag_dict[j+1].dim() == 0) and \ ((diag_dict[j].dim() != 2) or (((diag_dict[j][0,0] - diag_dict[j][1,1]) % 4) != 0)): @@ -383,9 +384,9 @@ def Kitaoka_mass_at_2(self): ## Deal with the complicated case: tmp_m = dim2_dict[j].dim() // 2 if dim2_dict[j].is_hyperbolic(2): - E *= 2 / (1 + 2**(-tmp_m)) + E *= QQ(2) / (1 + 2**(-tmp_m)) else: - E *= 2 / (1 - 2**(-tmp_m)) + E *= QQ(2) / (1 - 2**(-tmp_m)) else: E *= 2 @@ -398,7 +399,7 @@ def Kitaoka_mass_at_2(self): #print "E = ", E ## Compute the exponent w - w = QQ(0) + w = QQ.zero() for j in range(s_min, s_max+1): n_j = Jordan_Blocks[j][1].dim() for k in range(j+1, s_max+1): @@ -411,7 +412,6 @@ def Kitaoka_mass_at_2(self): return mass_at_2 - def mass_at_two_by_counting_mod_power(self, k): """ Computes the local mass at `p=2` assuming that it's stable `(mod 2^k)`. diff --git a/src/sage/quadratic_forms/special_values.py b/src/sage/quadratic_forms/special_values.py index 472ab670da0..3a173d9444d 100644 --- a/src/sage/quadratic_forms/special_values.py +++ b/src/sage/quadratic_forms/special_values.py @@ -6,6 +6,8 @@ - :func:`quadratic_L_function__exact` -- Exact values of the Dirichlet L-functions of quadratic characters at critical values - :func:`quadratic_L_function__numerical` -- Numerical values of the Dirichlet L-functions of quadratic characters in the domain of convergence """ +# python3 +from __future__ import division from sage.combinat.combinat import bernoulli_polynomial from sage.misc.functional import denominator @@ -63,20 +65,18 @@ def gamma__exact(n): TypeError: you must give an integer or half-integer argument """ from sage.all import sqrt - # SANITY CHECK - if (not n in QQ) or (denominator(n) > 2): - raise TypeError("you must give an integer or half-integer argument") + n = QQ(n) if denominator(n) == 1: if n <= 0: return infinity if n > 0: return factorial(n-1) - else: - ans = QQ(1) - while (n != QQ(1)/2): + elif denominator(n) == 2: + ans = QQ.one() + while n != QQ((1, 2)): if n < 0: - ans *= QQ(1)/n + ans /= n n += 1 elif n > 0: n += -1 @@ -84,6 +84,8 @@ def gamma__exact(n): ans *= sqrt(pi) return ans + else: + raise TypeError("you must give an integer or half-integer argument") # ------------- The Riemann Zeta Function -------------- @@ -120,6 +122,12 @@ def zeta__exact(n): TESTS:: + sage: zeta__exact(4) + 1/90*pi^4 + sage: zeta__exact(-3) + 1/120 + sage: zeta__exact(0) + -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... @@ -135,13 +143,13 @@ def zeta__exact(n): return bernoulli(1-n)/(n-1) elif n > 1: if (n % 2 == 0): - return ZZ(-1)**(n/2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) + return ZZ(-1)**(n//2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") - elif n==1: + elif n == 1: return infinity - elif n==0: - return -1/2 + elif n == 0: + return QQ((-1, 2)) # ---------- Dirichlet L-functions with quadratic characters ---------- @@ -193,6 +201,8 @@ def quadratic_L_function__exact(n, d): 1/4*pi sage: quadratic_L_function__exact(-4, -4) 5/2 + sage: quadratic_L_function__exact(2, 1) + 1/6*pi^2 TESTS:: @@ -227,7 +237,7 @@ def quadratic_L_function__exact(n, d): ans = SR(ZZ(-1)**(1+(n-delta)/2)) ans *= (2*pi/f)**n ans *= GS # Evaluate the Gauss sum here! =0 - ans *= 1/(2 * I**delta) + ans *= QQ.one()/(2 * I**delta) ans *= QuadraticBernoulliNumber(n,d)/factorial(n) return ans else: @@ -279,7 +289,7 @@ def quadratic_L_function__numerical(n, d, num_terms=1000): raise ValueError('the Dirichlet series does not converge here') d1 = fundamental_discriminant(d) - ans = R(0) + ans = R.zero() for i in range(1,num_terms): ans += R(kronecker_symbol(d1,i) / R(i)**n) return ans diff --git a/src/sage/quivers/algebra_elements.pxi b/src/sage/quivers/algebra_elements.pxi index 066fd7af9ce..32e7adb7d12 100644 --- a/src/sage/quivers/algebra_elements.pxi +++ b/src/sage/quivers/algebra_elements.pxi @@ -17,7 +17,7 @@ AUTHORS: #***************************************************************************** include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "sage/data_structures/bitset.pxi" from cpython.ref cimport * @@ -287,7 +287,7 @@ freelist.pool = check_allocarray(poolsize, sizeof(path_term_t*)) cdef inline path_term_t *term_free_force(path_term_t *T): mon_free(T.mon) cdef path_term_t *out = T.nxt - sage_free(T) + sig_free(T) return out cdef class _FreeListProtector: @@ -314,8 +314,8 @@ cdef class _FreeListProtector: for i in range(freelist.used): term_free_force(freelist.pool[i]) sig_check() - sage_free(freelist.pool) - sage_free(freelist) + sig_free(freelist.pool) + sig_free(freelist) _freelist_protector = _FreeListProtector() @@ -635,7 +635,7 @@ cdef inline void poly_dealloc(path_poly_t *P): # used by the polynomial. cdef inline void poly_free(path_poly_t *P): poly_dealloc(P) - sage_free(P) + sig_free(P) # Fill "out" with a copy of the terms of P. Note that previous contents # of "out" will NOT be freed---this function should thus only be called @@ -665,7 +665,7 @@ cdef bint poly_icopy_scale(path_poly_t *out, path_poly_t *P, object coef) except out.lead = NULL while res.coef == NULL: sig_check() - sage_free(res) + sig_free(res) T = T.nxt if T == NULL: return True @@ -677,7 +677,7 @@ cdef bint poly_icopy_scale(path_poly_t *out, path_poly_t *P, object coef) except sig_check() res.nxt = term_scale(T, coef) if res.nxt.coef == NULL: - sage_free(res.nxt) + sig_free(res.nxt) else: res = res.nxt out.nterms += 1 @@ -1203,7 +1203,7 @@ cdef void homog_poly_free(path_homog_poly_t *P): while P!=NULL: nxt = P.nxt poly_free(P.poly) - sage_free(P) + sig_free(P) P = nxt # Return a copy of H diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index ea6c64fa856..e615fe4e8e0 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -1222,7 +1222,7 @@ cdef class PathAlgebraElement(RingElement): # memory occupied by out first. outnxt = out.nxt poly_free(out.poly) - sage_free(out) + sig_free(out) return self._new_(outnxt) return self._new_(out) @@ -1253,7 +1253,7 @@ cdef class PathAlgebraElement(RingElement): # memory occupied by out first. outnxt = out.nxt poly_free(out.poly) - sage_free(out) + sig_free(out) return self._new_(outnxt) return self._new_(out) @@ -1390,8 +1390,8 @@ cdef class PathAlgebraElement(RingElement): while out_orig != NULL and out_orig.poly.lead == NULL: tmp = out_orig.nxt sig_check() - sage_free(out_orig.poly) - sage_free(out_orig) + sig_free(out_orig.poly) + sig_free(out_orig) out_orig = tmp if out_orig == NULL: return self._new_(NULL) @@ -1400,8 +1400,8 @@ cdef class PathAlgebraElement(RingElement): if tmp.nxt.poly.lead == NULL: sig_check() nxt = tmp.nxt.nxt - sage_free(tmp.nxt.poly) - sage_free(tmp.nxt) + sig_free(tmp.nxt.poly) + sig_free(tmp.nxt) tmp.nxt = nxt else: tmp = tmp.nxt diff --git a/src/sage/repl/image.py b/src/sage/repl/image.py index 174703ca40d..ea0d96ccfa6 100644 --- a/src/sage/repl/image.py +++ b/src/sage/repl/image.py @@ -39,7 +39,7 @@ class Image(SageObject): - def __init__(self, mode, size, color=0): + def __init__(self, mode, size, color='white'): """ Creates a new image with the given mode and size. @@ -76,13 +76,12 @@ def __init__(self, mode, size, color=0): - ``size`` -- 2-tuple, containing (width, height) in pixels. - - ``color`` -- string or numeric. What colour to use for the - image. Default is black. If given, this should be a single - integer or floating point value for single-band modes, and a - tuple for multi-band modes (one value per band). When - creating RGB images, you can also use colour strings as - supported by the ImageColor module. If the colour is None, - the image is not initialised. + - ``color`` -- string or tuple of numeric. What colour to use + for the image. Default is black. If given, this should be a + a tuple with one value per band. When creating RGB images, + you can also use colour strings as supported by the + ImageColor module. If the colour is None, the image is not + initialised. OUTPUT: @@ -91,7 +90,7 @@ def __init__(self, mode, size, color=0): EXAMPLES:: sage: from sage.repl.image import Image - sage: Image('P', (16, 16), 13) + sage: Image('P', (16, 16), (13,)) 16x16px 8-bit Color image """ self._pil = PIL.Image.new(mode, size, color) @@ -233,7 +232,7 @@ def save(self, filename): EXAMPLES:: sage: from sage.repl.image import Image - sage: img = Image('P', (12, 34), 13) + sage: img = Image('P', (12, 34), (13,)) sage: filename = tmp_filename(ext='.png') sage: img.save(filename) sage: open(filename).read().startswith('\x89PNG') diff --git a/src/sage/repl/ipython_extension.py b/src/sage/repl/ipython_extension.py index fb7abc80d8a..fe3de38ca02 100644 --- a/src/sage/repl/ipython_extension.py +++ b/src/sage/repl/ipython_extension.py @@ -59,8 +59,8 @@ from IPython.core.magic import Magics, magics_class, line_magic from sage.repl.load import load_wrap - from sage.env import SAGE_IMPORTALL, SAGE_STARTUP_FILE +from sage.misc.lazy_import import LazyImport @magics_class class SageMagics(Magics): @@ -394,13 +394,11 @@ def init_inspector(self): # that we could override; however, IPython looks them up in # the global :class:`IPython.core.oinspect` module namespace. # Thus, we have to monkey-patch. - import sage.misc.sagedoc as sagedoc - import sage.misc.sageinspect as sageinspect import IPython.core.oinspect - IPython.core.oinspect.getdoc = sageinspect.sage_getdoc - IPython.core.oinspect.getsource = sagedoc.my_getsource - IPython.core.oinspect.find_file = sageinspect.sage_getfile - IPython.core.oinspect.getargspec = sageinspect.sage_getargspec + IPython.core.oinspect.getdoc = LazyImport("sage.misc.sageinspect", "sage_getdoc") + IPython.core.oinspect.getsource = LazyImport("sage.misc.sagedoc", "my_getsource") + IPython.core.oinspect.find_file = LazyImport("sage.misc.sageinspect", "sage_getfile") + IPython.core.oinspect.getargspec = LazyImport("sage.misc.sageinspect", "sage_getargspec") def init_line_transforms(self): """ diff --git a/src/sage/repl/ipython_kernel/kernel.py b/src/sage/repl/ipython_kernel/kernel.py index 0d6a63ef3b9..10556e458b6 100644 --- a/src/sage/repl/ipython_kernel/kernel.py +++ b/src/sage/repl/ipython_kernel/kernel.py @@ -67,7 +67,7 @@ def banner(self): sage: from sage.repl.ipython_kernel.kernel import SageKernel sage: sk = SageKernel.__new__(SageKernel) sage: sk.banner - '\xe2\x94\x8c\xe2...SageMath Version...' + '\xe2\x94\x8c\xe2...SageMath version...' """ from sage.misc.banner import banner_text return banner_text() diff --git a/src/sage/repl/preparse.py b/src/sage/repl/preparse.py index 5bcd255423e..25fcf2d775b 100644 --- a/src/sage/repl/preparse.py +++ b/src/sage/repl/preparse.py @@ -1477,7 +1477,7 @@ def handle_encoding_declaration(contents, out): AUTHORS: - Lars Fischer - - Dan Drake (2010-12-08, rewrite for ticket #10440) + - Dan Drake (2010-12-08, rewrite for :trac:`10440`) """ lines = contents.splitlines() for num, line in enumerate(lines[:2]): diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index e36c5558fac..b66f1880275 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -308,7 +308,7 @@ instead. (See also the examples in :meth:`ExactTerm.rpow() ` for a detailed explanation.) - Another way is to use a larger coefficent ring:: + Another way is to use a larger coefficient ring:: sage: M_QQ. = AsymptoticRing(growth_group='QQ^n * n^QQ', coefficient_ring=QQ) sage: (1/2)^n @@ -367,7 +367,7 @@ Asymptotic Ring over Rational Field Here, the coefficient ring was extended to allow `1/2` as a -coefficent. Another example is +coefficient. Another example is :: sage: C. = AsymptoticRing(growth_group='c^ZZ', coefficient_ring=ZZ['e']) diff --git a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py index 1ab30d0c973..09fd4d3cbf6 100644 --- a/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +++ b/src/sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py @@ -15,7 +15,7 @@ The algorithms and formulas implemented here come from [RaWi2008a]_ and [RaWi2012]_. For a general reference take a look in the book [PeWi2013]. -.. [AiYu1983] I.A. Aizenberg and A.P. Yuzhakov. +.. [AiYu1983] \I.A. Aizenberg and A.P. Yuzhakov. *Integral representations and residues in multidimensional complex analysis*. Translations of Mathematical Monographs, **58**. American Mathematical Society, Providence, RI. (1983). x+283 pp. ISBN: 0-8218-4511-X. diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index d3fa97482a2..26372689f13 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -751,13 +751,17 @@ def subfactors(F): 'no common parent was found, and ' 'splitting the factors was unsuccessful.' % (self, other, var)) - + # A wrapper around an iterator that stores additional intermediate data. + # This deviates slightly from the iterator protocol: + # At the end of the iteration the data is reset to None instead + # of raising a StopIteration. class it: def __init__(self, it): self.it = it self.var = None self.factors = None - def next(self): + + def next_custom(self): try: self.var, factors = next(self.it) self.factors = tuple(factors) @@ -772,24 +776,24 @@ def next(self): newS = [] newO = [] - S.next() - O.next() + S.next_custom() + O.next_custom() while S.var is not None or O.var is not None: if S.var is not None and S.var < O.var: newS.extend(S.factors) newO.extend(S.factors) - S.next() + S.next_custom() elif O.var is not None and S.var > O.var: newS.extend(O.factors) newO.extend(O.factors) - O.next() + O.next_custom() else: SL, OL = pushout_univariate_factors(self, other, S.var, S.factors, O.factors) newS.extend(SL) newO.extend(OL) - S.next() - O.next() + S.next_custom() + O.next_custom() assert(len(newS) == len(newO)) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index fdc8bb59826..895107646c4 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -107,6 +107,11 @@ TESTS:: sage: polygen(CBF, x)^3 x^3 +:: + + sage: SR.coerce(CBF(0.42 + 3.33*I)) + [0.4200000000000000 +/- 1.56e-17] + [3.330000000000000 +/- 7.11e-17]*I + Check that :trac:`19839` is fixed:: sage: log(SR(CBF(0.42))).pyobject().parent() @@ -124,7 +129,6 @@ Classes and Methods # http://www.gnu.org/licenses/ #***************************************************************************** include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" import operator @@ -133,6 +137,8 @@ import sage.categories.fields cimport sage.rings.integer cimport sage.rings.rational +import sage.rings.number_field.number_field as number_field + from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -144,13 +150,14 @@ from sage.libs.arb.acb_hypgeom cimport * from sage.libs.arb.acb_modular cimport * from sage.libs.arb.arf cimport arf_init, arf_get_mpfr, arf_set_mpfr, arf_clear, arf_set_mag, arf_set from sage.libs.arb.mag cimport mag_init, mag_clear, mag_add, mag_set_d, MAG_BITS, mag_is_inf, mag_is_finite, mag_zero -from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear +from sage.libs.flint.fmpz cimport fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_abs from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.integer_ring import ZZ -from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi +from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic +from sage.rings.real_arb cimport mpfi_to_arb, arb_to_mpfi, real_part_of_quadratic_element_to_arb from sage.rings.real_arb import RealBallField from sage.rings.real_mpfr cimport RealField_class, RealField, RealNumber from sage.rings.ring import Field @@ -201,6 +208,7 @@ cdef int acb_to_ComplexIntervalFieldElement( arb_to_mpfi(target.__im, acb_imagref(source), precision) return 0 + class ComplexBallField(UniqueRepresentation, Field): r""" An approximation of the field of complex numbers using pairs of mid-rad @@ -423,6 +431,10 @@ class ComplexBallField(UniqueRepresentation, Field): True sage: CBF.has_coerce_map_from(RealBallField(52)) False + sage: CBF.has_coerce_map_from(QuadraticField(-2)) + True + sage: CBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False Check that there are no coercions from interval or floating-point parents:: @@ -437,6 +449,10 @@ class ComplexBallField(UniqueRepresentation, Field): """ if isinstance(other, (RealBallField, ComplexBallField)): return (other._prec >= self._prec) + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, x=None, y=None): r""" @@ -479,6 +495,9 @@ class ComplexBallField(UniqueRepresentation, Field): [0.3333333333333333 +/- 7.04e-17] + [0.1666666666666667 +/- 7.04e-17]*I sage: ComplexBallField(106)(1/3, 1/6) [0.33333333333333333333333333333333 +/- 6.94e-33] + [0.16666666666666666666666666666666 +/- 7.70e-33]*I + sage: NF.
    = QuadraticField(-2) + sage: CBF(1/5 + a/2) + [0.2000000000000000 +/- 4.45e-17] + [0.707106781186547 +/- 5.86e-16]*I sage: CBF(infinity, NaN) [+/- inf] + nan*I sage: CBF(x) @@ -495,7 +514,7 @@ class ComplexBallField(UniqueRepresentation, Field): sage: CBF(1+I, 2) Traceback (most recent call last): ... - TypeError: unable to convert I + 1 to a RealBall + ValueError: nonzero imaginary part """ try: return self.element_class(self, x, y) @@ -519,7 +538,7 @@ class ComplexBallField(UniqueRepresentation, Field): return self.element_class(self, x) except TypeError: pass - raise TypeError("unable to convert {} to a ComplexBall".format(x)) + raise TypeError("unable to convert {!r} to a ComplexBall".format(x)) else: x = self._base(x) y = self._base(y) @@ -689,9 +708,19 @@ cdef class ComplexBall(RingElement): Traceback (most recent call last): ... TypeError: unsupported initializer + sage: NF. = QuadraticField(-1, embedding=CC(0, -1)) + sage: CBF(a) + -1.000000000000000*I + sage: NF. = QuadraticField(-1, embedding=None) + sage: CBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding """ cdef fmpz_t tmpz cdef fmpq_t tmpq + cdef NumberFieldElement_quadratic x_as_qe + cdef long myprec RingElement.__init__(self, parent) @@ -721,6 +750,25 @@ cdef class ComplexBall(RingElement): elif isinstance(x, ComplexIntervalFieldElement): ComplexIntervalFieldElement_to_acb(self.value, x) + elif isinstance(x, NumberFieldElement_quadratic): + x_as_qe = x + real_part_of_quadratic_element_to_arb(acb_realref(self.value), + x_as_qe, prec(self)) + myprec = prec(self) + 4 + if mpz_sgn(x_as_qe.D.value) < 0: + if x_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + fmpz_init(tmpz) + fmpz_set_mpz(tmpz, x_as_qe.D.value) + fmpz_abs(tmpz, tmpz) + arb_sqrt_fmpz(acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.b) + arb_mul_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, myprec) + fmpz_set_mpz(tmpz, x_as_qe.denom) + arb_div_fmpz(acb_imagref(self.value), acb_imagref(self.value), tmpz, prec(self)) + fmpz_clear(tmpz) + if not x_as_qe.standard_embedding: + acb_conj(self.value, self.value) else: raise TypeError("unsupported initializer") elif isinstance(x, RealBall) and isinstance(y, RealBall): diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 3bb20bb89bd..cc42c2904a0 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -68,7 +68,6 @@ from sage.misc.randstate cimport randstate, current_randstate from sage.libs.pari.paridecl cimport * include "cysignals/signals.pxi" -include 'sage/libs/pari/pari_err.pxi' from sage.libs.gsl.complex cimport * @@ -715,12 +714,12 @@ cdef inline ComplexDoubleElement pari_to_cdf(pari_gen g): PariError: incorrect type in gtofp (t_POL) """ cdef ComplexDoubleElement z = ComplexDoubleElement.__new__(ComplexDoubleElement) - pari_catch_sig_on() + sig_on() if typ(g.g) == t_COMPLEX: z._complex = gsl_complex_rect(gtodouble(gel(g.g, 1)), gtodouble(gel(g.g, 2))) else: z._complex = gsl_complex_rect(gtodouble(g.g), 0.0) - pari_catch_sig_off() + sig_off() return z cdef class ComplexDoubleElement(FieldElement): @@ -947,17 +946,17 @@ cdef class ComplexDoubleElement(FieldElement): sage: float(a) Traceback (most recent call last): ... - TypeError: Unable to convert 2.0 + 1.0*I to float; use abs() or real_part() as desired + TypeError: unable to convert 2.0 + 1.0*I to float; use abs() or real_part() as desired sage: a.__float__() Traceback (most recent call last): ... - TypeError: Unable to convert 2.0 + 1.0*I to float; use abs() or real_part() as desired + TypeError: unable to convert 2.0 + 1.0*I to float; use abs() or real_part() as desired sage: float(abs(CDF(1,1))) 1.4142135623730951 """ if self._complex.dat[1]==0: return float(self._complex.dat[0]) - raise TypeError, "Unable to convert %s to float; use abs() or real_part() as desired"%self + raise TypeError("unable to convert {!r} to float; use abs() or real_part() as desired".format(self)) def __complex__(self): """ @@ -1148,7 +1147,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: pari(CDF(I)) 1.00000000000000*I """ - pari_catch_sig_on() + sig_on() return pari.new_gen(self._gen()) ####################################################################### @@ -2402,7 +2401,7 @@ cdef class ComplexDoubleElement(FieldElement): sage: CDF(1,5).algdep(2) x^2 - 2*x + 26 """ - pari_catch_sig_on() + sig_on() f = pari.new_gen(algdep0(self._gen(), n, 0)) from polynomial.polynomial_ring_constructor import PolynomialRing from integer_ring import ZZ diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index f4c659bbfa9..1bcf4adc6dc 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -55,6 +55,8 @@ cimport real_mpfi cimport real_mpfr from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.mpfr cimport MPFR_RNDU, MPFR_RNDD + cdef double LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 # a small overestimate of log(10,2) @@ -778,38 +780,22 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2,-3)._div_(CIF(1,-2)) 1.600000000000000? + 0.200000000000000?*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() - cdef mpfi_t a, b, t0, t1, right_nm - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) - mpfi_init2(a, self._prec) - mpfi_init2(b, self._prec) - mpfi_init2(right_nm, self._prec) - - mpfi_sqr(t0, (right).__re) - mpfi_sqr(t1, (right).__im) - mpfi_add(right_nm, t0, t1) - - mpfi_div(a, (right).__re, right_nm) - mpfi_div(b, (right).__im, right_nm) - - ## Do this: x.__re = a * self.__re + b * self.__im - mpfi_mul(t0, a, self.__re) - mpfi_mul(t1, b, self.__im) - mpfi_add(x.__re, t0, t1) - - ## Do this: x.__im = a * self.__im - b * self.__re - mpfi_mul(t0, a, self.__im) - mpfi_mul(t1, b, self.__re) - mpfi_sub(x.__im, t0, t1) - mpfi_clear(t0) - mpfi_clear(t1) - mpfi_clear(a) - mpfi_clear(b) - mpfi_clear(right_nm) - return x + sage: a = CIF((1, 2), (3, 4)) + sage: b = CIF(-1, (2, 3)) + sage: c = a/b + sage: c.endpoints() + (0.500000000000000 - 1.60000000000000*I, + 1.50000000000000 - 0.600000000000000*I, + 0.500000000000000 - 0.600000000000000*I, + 1.50000000000000 - 1.60000000000000*I) + sage: c = b/a + sage: c.endpoints() + (0.246153846153846 + 0.317647058823529*I, + 0.841176470588236 + 0.761538461538462*I, + 0.246153846153846 + 0.761538461538462*I, + 0.841176470588236 + 0.317647058823529*I) + """ + return self * right.__invert__() def __rdiv__(self, left): """ @@ -818,7 +804,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): EXAMPLES:: sage: CIF(2,-3).__rdiv__(CIF(1,-2)) - 0.6153846153846154? - 0.0769230769230769?*I + 0.6153846153846154? - 0.0769230769230770?*I """ return ComplexIntervalFieldElement(self._parent, left)/self @@ -1130,26 +1116,198 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: I = CIF.0 sage: a = ~(5+I) # indirect doctest sage: a * (5+I) - 1.000000000000000? + 0.?e-16*I - """ - cdef ComplexIntervalFieldElement x - x = self._new() + 1.000000000000000? + -1.?e-16*I + sage: a = CIF((1, 2), (3, 4)) + sage: c = a.__invert__() + sage: c.endpoints() + (0.0588235294117647 - 0.300000000000000*I, + 0.153846153846154 - 0.200000000000000*I, + 0.0588235294117647 - 0.200000000000000*I, + 0.153846153846154 - 0.300000000000000*I) - cdef mpfi_t t0, t1 - mpfi_init2(t0, self._prec) - mpfi_init2(t1, self._prec) + TESTS: - mpfi_sqr(t0, self.__re) - mpfi_sqr(t1, self.__im) + Check that the code is valid in all kind of complex intervals:: - mpfi_add(t0, t0, t1) # now t0 is the norm - mpfi_div(x.__re, self.__re, t0) # x.__re = self.__re/norm + sage: rpts = [0, -194323/42, -110/439423, -411923/122212, \ + ....: 15423/906, 337/59976, 145151/145112] + sage: rpts = [RIF(a, b) if a <= b else RIF(b,a) \ + ....: for a in rpts for b in rpts] + sage: cpts = [CIF(a, b) for a in rpts for b in rpts if not CIF(a, b).contains_zero()] + sage: for x in cpts: + ....: assert (x * (~x) - 1).contains_zero() - mpfi_neg(t1, self.__im) - mpfi_div(x.__im, t1, t0) # x.__im = -self.__im/norm + REFERENCES: - mpfi_clear(t0) - mpfi_clear(t1) + .. [RL] \J. Rokne, P. Lancaster. Complex interval arithmetic. + Communications of the ACM 14. 1971. + """ + + cdef ComplexIntervalFieldElement x + x = self._new() + + cdef mpfr_t a, b, c, d + mpfr_init2(a, self._prec) + mpfr_init2(b, self._prec) + mpfr_init2(c, self._prec) + mpfr_init2(d, self._prec) + + cdef mpfr_t rmin, rmax, imin, imax + mpfr_init2(rmin, self._prec) + mpfr_init2(rmax, self._prec) + mpfr_init2(imin, self._prec) + mpfr_init2(imax, self._prec) + + cdef mpfr_t r + mpfr_init2(r, self._prec) + + mpfi_get_left(a, self.__re) + mpfi_get_right(b, self.__re) + mpfi_get_left(c, self.__im) + mpfi_get_right(d, self.__im) + + cdef mpfr_t a2, b2, d2, c2 + mpfr_init2(a2, self._prec) + mpfr_init2(b2, self._prec) + mpfr_init2(c2, self._prec) + mpfr_init2(d2, self._prec) + + cdef mpfr_t div1, div2, aux, aux2 + mpfr_init2(div1, self._prec) + mpfr_init2(div2, self._prec) + mpfr_init2(aux, self._prec) + mpfr_init2(aux2, self._prec) + + if mpfr_sgn(a) >= 0 and mpfr_sgn(c)>=0: #input interval lies in first quadrant + # left endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_add(div2, b2, d2, MPFR_RNDU) + mpfr_div(rmin, a, div1, MPFR_RNDD) + mpfr_div(aux, b, div2, MPFR_RNDD) + mpfr_min(rmin, rmin, aux, MPFR_RNDD) + #higher endpoint + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + mpfr_div(aux2, d, div2, MPFR_RNDU) + mpfr_sub(aux2, aux, aux2, MPFR_RNDU) + mpfr_max(imax, aux2, imax, MPFR_RNDU) + # lower endpoint, it is the lowest point of the circle or one of + if mpfr_cmp(d, a) >=0 and mpfr_cmp(c, a) <= 0: + mpfr_add(imin, a, a, MPFR_RNDD) + mpfr_set_si(aux, -1, MPFR_RNDD) + mpfr_div(imin, aux, imin, MPFR_RNDD) + elif mpfr_cmp(c, a) > 0: + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(imin, c, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + else: + mpfr_mul(d2, d, d, MPFR_RNDD) + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDD) + mpfr_div(imin, d, div1, MPFR_RNDU) + mpfr_set_si(aux, 0, MPFR_RNDD) + mpfr_sub(imin, aux, imin, MPFR_RNDD) + #right endpoint + if mpfr_cmp(c, a) >=0 and mpfr_cmp(b, c) >= 0: + mpfr_add(rmax, c, c, MPFR_RNDD) + mpfr_set_si(aux, 1, MPFR_RNDU) + mpfr_div(rmax, aux, rmax, MPFR_RNDU) + elif mpfr_cmp(a,c) > 0: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmax, a, div1, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + elif mpfr_sgn(c) > 0 and mpfr_sgn(b) > 0: #between first and second quadrant + # left endpoint + mpfr_abs(aux, a, MPFR_RNDU) + if mpfr_cmp(aux, c) >= 0: + mpfr_set_str(aux, '-0.5', 10, MPFR_RNDD) + mpfr_div(rmin, aux, c, MPFR_RNDD) + else: + mpfr_mul(a2, a, a, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, a2, c2, MPFR_RNDD) + mpfr_div(rmin, a, div1, MPFR_RNDU) + # lower endpoint + mpfr_set_si(aux2, -1, MPFR_RNDD) + mpfr_div(imin, aux2, c, MPFR_RNDD) + #right endpoint + if mpfr_cmp(b, c) >=0: + mpfr_set_str(aux2, '0.5', 10, MPFR_RNDU) + mpfr_div(rmax, aux2, c, MPFR_RNDU) + else: + mpfr_mul(b2, b, b, MPFR_RNDD) + mpfr_mul(c2, c, c, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDD) + mpfr_div(rmax, b, div1, MPFR_RNDU) + # upper endpoint + mpfr_mul(a2, a, a, MPFR_RNDU) + mpfr_mul(b2, b, b, MPFR_RNDU) + mpfr_mul(c2, c, c, MPFR_RNDU) + mpfr_mul(d2, d, d, MPFR_RNDU) + mpfr_add(div1, a2, c2, MPFR_RNDU) + mpfr_div(imax, c, div1, MPFR_RNDD) + mpfr_add(div1, b2, c2, MPFR_RNDU) + mpfr_div(aux, c, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, a2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_add(div1, b2, d2, MPFR_RNDU) + mpfr_div(aux, d, div1, MPFR_RNDD) + if mpfr_cmp(imax, aux) > 0: + mpfr_set(imax, aux, MPFR_RNDD) + mpfr_set_zero(aux, -1) + mpfr_sub(imax, aux, imax, MPFR_RNDU) + elif mpfr_sgn(b) <= 0 and mpfr_sgn(d) >=0: #second quadrant or between second and thirthd + I = self.parent().gen(0) + return -I*(-I*self).__invert__() + elif mpfr_sgn(a) <= 0 and mpfr_sgn(d) <= 0: # thirthd quadrant or between thirthd and fourth + return -(-self).__invert__() + elif mpfr_sgn(a) >=0: #fourth or between fourth and first + I = self.parent().gen(0) + return I*(I*self).__invert__() + + + mpfi_set_fr(x.__re, rmin) + mpfi_put_fr(x.__re, rmax) + + mpfi_set_fr(x.__im, imin) + mpfi_put_fr(x.__im, imax) + + mpfr_clear(a) + mpfr_clear(b) + mpfr_clear(c) + mpfr_clear(d) + mpfr_clear(imin) + mpfr_clear(imax) + mpfr_clear(rmin) + mpfr_clear(rmax) + mpfr_clear(r) + mpfr_clear(a2) + mpfr_clear(b2) + mpfr_clear(c2) + mpfr_clear(d2) + mpfr_clear(div1) + mpfr_clear(div2) + mpfr_clear(aux) + mpfr_clear(aux2) return x @@ -1766,7 +1924,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(1,1).tan() 0.27175258531952? + 1.08392332733870?*I sage: CIF(2).tan() - -2.18503986326152? + -2.185039863261519? sage: CIF(0,2).tan() 0.964027580075817?*I """ @@ -1855,7 +2013,7 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): sage: CIF(2).tanh() 0.964027580075817? sage: CIF(0,2).tanh() - -2.18503986326152?*I + -2.185039863261519?*I """ return self.sinh() / self.cosh() diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index fcc607780da..bc1e672e700 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -819,7 +819,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): # set imag part if b is None: if a is None: - raise TypeError, "Unable to convert z (='%s') to a MPComplexNumber." %z + raise TypeError("unable to convert {!r} to a MPComplexNumber".format(z)) else: mpfr_set_str(self.value.im, b, base, rnd_im(rnd)) return diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 27c9e6d7201..2842059cf43 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -1092,18 +1092,18 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: float(a) Traceback (most recent call last): ... - TypeError: Unable to convert 2.00000000000000 + 1.00000000000000*I to float; use abs() or real_part() as desired + TypeError: unable to convert 2.00000000000000 + 1.00000000000000*I to float; use abs() or real_part() as desired sage: a.__float__() Traceback (most recent call last): ... - TypeError: Unable to convert 2.00000000000000 + 1.00000000000000*I to float; use abs() or real_part() as desired + TypeError: unable to convert 2.00000000000000 + 1.00000000000000*I to float; use abs() or real_part() as desired sage: float(abs(ComplexNumber(1,1))) 1.4142135623730951 """ if mpfr_zero_p(self.__im): return mpfr_get_d(self.__re, rnd) else: - raise TypeError, "Unable to convert %s to float; use abs() or real_part() as desired"%self + raise TypeError("unable to convert {!r} to float; use abs() or real_part() as desired".format(self)) def __complex__(self): r""" diff --git a/src/sage/rings/continued_fraction.py b/src/sage/rings/continued_fraction.py index d28d63b81a8..e207f4ecd1a 100644 --- a/src/sage/rings/continued_fraction.py +++ b/src/sage/rings/continued_fraction.py @@ -198,6 +198,9 @@ - Vincent Delecroix (2014): cleaning, refactorisation, documentation from the old implementation in ``contfrac`` (:trac:`14567`). """ +# python3 +from __future__ import division + from sage.structure.sage_object import SageObject from integer import Integer from infinity import Infinity @@ -2057,7 +2060,7 @@ def check_and_reduce_pair(x1,x2=None): # check that y2 is not a pure power (in a very naive way!!) n2 = len(y2) - for i in xrange(1,(n2+2)/2): + for i in xrange(1, (n2 + 2) // 2): if n2 % i == 0 and y2[:-i] == y2[i:]: y2 = y2[:i] break diff --git a/src/sage/rings/finite_rings/conway_polynomials.py b/src/sage/rings/finite_rings/conway_polynomials.py index 2850c273f7e..451a407bc01 100644 --- a/src/sage/rings/finite_rings/conway_polynomials.py +++ b/src/sage/rings/finite_rings/conway_polynomials.py @@ -198,7 +198,7 @@ def polynomial(self, n): REFERENCE: - .. [HL99] L. Heath and N. Loehr (1999). New algorithms for + .. [HL99] \L. Heath and N. Loehr (1999). New algorithms for generating Conway polynomials over finite fields. Proceedings of the tenth annual ACM-SIAM symposium on discrete algorithms, pp. 429-437. diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 2b30fa2cf80..d32b5cf517d 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -53,7 +53,6 @@ AUTHORS: include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" from sage.libs.pari.paridecl cimport * -include "sage/libs/pari/pari_err.pxi" from sage.misc.randstate cimport randstate, current_randstate from sage.rings.finite_rings.finite_field_base cimport FiniteField @@ -447,7 +446,7 @@ cdef class Cache_givaro(SageObject): cdef GEN t cdef long c if isinstance(e, gen): - pari_catch_sig_on() + sig_on() t = (e).g if typ(t) == t_FFELT: t = FF_to_FpXQ(t) @@ -456,7 +455,7 @@ cdef class Cache_givaro(SageObject): if typ(t) == t_INT: res = self.int_to_log(itos(t)) - pari_catch_sig_off() + sig_off() elif typ(t) == t_POL: res = self._zero_element @@ -467,7 +466,7 @@ cdef class Cache_givaro(SageObject): c = gtolong(gel(t, i+2)) res = self.objectptr.axpyin(res, self.int_to_log(c), x) x = self.objectptr.mul(x,x,g) - pari_catch_sig_off() + sig_off() else: raise TypeError("bad PARI type %r" % e.type()) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 76f6f9c2481..56995046d37 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -19,11 +19,10 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" from sage.libs.pari.paridecl cimport * -include "sage/libs/pari/pari_err.pxi" from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, ModuleElement, RingElement @@ -368,7 +367,7 @@ cdef class Cache_ntl_gf2e(SageObject): cdef GEN t if isinstance(e, gen): - pari_catch_sig_on() + sig_on() t = (e).g if typ(t) == t_FFELT: t = FF_to_FpXQ(t) @@ -377,7 +376,7 @@ cdef class Cache_ntl_gf2e(SageObject): if typ(t) == t_INT: GF2E_conv_long(res.x, itos(t)) - pari_catch_sig_off() + sig_off() elif typ(t) == t_POL: g = self._gen x = self._new() @@ -387,7 +386,7 @@ cdef class Cache_ntl_gf2e(SageObject): if gtolong(gel(t, i+2)): GF2E_add(res.x, res.x, x.x) GF2E_mul(x.x, x.x, g.x) - pari_catch_sig_off() + sig_off() else: raise TypeError("bad PARI type %r" % e.type()) @@ -414,7 +413,7 @@ cdef class Cache_ntl_gf2e(SageObject): TESTS: - We test that #17027 is fixed:: + We test that :trac:`17027` is fixed:: sage: K. = GF(2^16) sage: K._cache.fetch_int(0r) @@ -442,13 +441,13 @@ cdef class Cache_ntl_gf2e(SageObject): else: raise TypeError, "number %s is not an integer"%number - p = sage_malloc(n) + p = sig_malloc(n) for i from 0 <= i < n: p[i] = (number%256) number = number >> 8 GF2XFromBytes(_a, p, n) GF2E_conv_GF2X(a.x, _a) - sage_free(p) + sig_free(p) return a def polynomial(self): diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 8a204d8e4f1..004b98eb1ea 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -18,11 +18,10 @@ AUTHORS: #***************************************************************************** -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from sage.libs.pari.paridecl cimport * from sage.libs.pari.paripriv cimport * -include "sage/libs/pari/pari_err.pxi" from element_base cimport FinitePolyExtElement from integer_mod import IntegerMod_abstract @@ -47,8 +46,8 @@ cdef GEN _INT_to_FFELT(GEN g, GEN x) except NULL: Convert the t_INT `x` to an element of the field of definition of the t_FFELT `g`. - This function must be called within ``pari_catch_sig_on()`` - ... ``pari_catch_sig_off()``. + This function must be called within ``sig_on()`` + ... ``sig_off()``. TESTS: @@ -83,7 +82,7 @@ cdef GEN _INT_to_FFELT(GEN g, GEN x) except NULL: set_gel(f, 1, gmael(g, 2, 1)) f[2] = itou(x) else: - pari_catch_sig_off() + sig_off() raise TypeError("unknown PARI finite field type") result = cgetg(5, t_FFELT) result[1] = t @@ -169,7 +168,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): Cython deconstructor. """ if self.block: - sage_free(self.block) + sig_free(self.block) cdef FiniteFieldElement_pari_ffelt _new(FiniteFieldElement_pari_ffelt self): """ @@ -183,7 +182,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): cdef void construct(FiniteFieldElement_pari_ffelt self, GEN g): """ Initialise ``self`` to the FFELT ``g``, reset the PARI stack, - and call pari_catch_sig_off(). + and call sig_off(). This should be called exactly once on every instance. """ @@ -201,28 +200,28 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): if isinstance(x, FiniteFieldElement_pari_ffelt): if self._parent is (x)._parent: - pari_catch_sig_on() + sig_on() self.construct((x).val) else: raise TypeError("no coercion defined") elif isinstance(x, Integer): g = (self._parent._gen_pari).g - pari_catch_sig_on() + sig_on() x_GEN = pari._new_GEN_from_mpz_t((x).value) self.construct(_INT_to_FFELT(g, x_GEN)) elif isinstance(x, int) or isinstance(x, long): g = (self._parent._gen_pari).g x = pari(x) - pari_catch_sig_on() + sig_on() x_GEN = (x).g self.construct(_INT_to_FFELT(g, x_GEN)) elif isinstance(x, IntegerMod_abstract): if self._parent.characteristic().divides(x.modulus()): g = (self._parent._gen_pari).g - pari_catch_sig_on() + sig_on() x_GEN = pari._new_GEN_from_mpz_t(Integer(x).value) self.construct(_INT_to_FFELT(g, x_GEN)) else: @@ -230,14 +229,14 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): elif x is None: g = (self._parent._gen_pari).g - pari_catch_sig_on() + sig_on() self.construct(FF_zero(g)) elif isinstance(x, pari_gen): g = (self._parent._gen_pari).g x_GEN = (x).g - pari_catch_sig_on() + sig_on() if gequal0(x_GEN): self.construct(FF_zero(g)) return @@ -256,7 +255,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): self.construct(FF_div(_INT_to_FFELT(g, gel(x_GEN, 1)), _INT_to_FFELT(g, gel(x_GEN, 2)))) else: - pari_catch_sig_off() + sig_off() raise TypeError("no coercion defined") elif (isinstance(x, FreeModuleElement) @@ -266,7 +265,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): n = len(x) while n > 0 and x[n - 1] == 0: n -= 1 - pari_catch_sig_on() + sig_on() if n == 0: self.construct(FF_zero(g)) return @@ -284,7 +283,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): if t == t_FF_F2xq: f = Flx_to_F2x(f) else: - pari_catch_sig_off() + sig_off() raise TypeError("unknown PARI finite field type") result = cgetg(5, t_FFELT) result[1] = t @@ -334,7 +333,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): sage: c^20 # indirect doctest c^4 + 2*c^3 """ - pari_catch_sig_on() + sig_on() return str(pari.new_gen(self.val)) def __hash__(FiniteFieldElement_pari_ffelt self): @@ -380,7 +379,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): False """ cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(self.val) return x @@ -439,9 +438,9 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): False """ cdef int r - pari_catch_sig_on() + sig_on() r = cmp_universal(self.val, (other).val) - pari_catch_sig_off() + sig_off() return r cpdef ModuleElement _add_(FiniteFieldElement_pari_ffelt self, ModuleElement right): @@ -455,7 +454,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): a^2 + a """ cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_add((self).val, (right).val)) return x @@ -471,7 +470,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): 0 """ cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_sub((self).val, (right).val)) return x @@ -487,7 +486,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): a^15 + 2*a^12 + a^11 + 2*a^10 + 2 """ cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_mul((self).val, (right).val)) return x @@ -505,7 +504,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): if FF_equal0((right).val): raise ZeroDivisionError cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_div((self).val, (right).val)) return x @@ -575,7 +574,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): 2*a """ cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_neg_i((self).val)) return x @@ -596,7 +595,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): if FF_equal0(self.val): raise ZeroDivisionError cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_inv((self).val)) return x @@ -641,7 +640,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): raise ZeroDivisionError exp = Integer(exp)._pari_() cdef FiniteFieldElement_pari_ffelt x = self._new() - pari_catch_sig_on() + sig_on() x.construct(FF_pow(self.val, (exp).g)) return x @@ -671,7 +670,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): sage: (a**2 + 1).polynomial().parent() Univariate Polynomial Ring in alpha over Finite Field of size 3 """ - pari_catch_sig_on() + sig_on() return self._parent.polynomial_ring()(pari.new_gen(FF_to_FpXQ_i(self.val))) def charpoly(FiniteFieldElement_pari_ffelt self, object var='x'): @@ -689,7 +688,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): sage: a.charpoly('y') y^2 + 1 """ - pari_catch_sig_on() + sig_on() return self._parent.polynomial_ring(var)(pari.new_gen(FF_charpoly(self.val))) def is_square(FiniteFieldElement_pari_ffelt self): @@ -718,9 +717,9 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): True """ cdef long i - pari_catch_sig_on() + sig_on() i = FF_issquare(self.val) - pari_catch_sig_off() + sig_off() return bool(i) def sqrt(FiniteFieldElement_pari_ffelt self, extend=False, all=False): @@ -780,7 +779,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): raise NotImplementedError cdef GEN s cdef FiniteFieldElement_pari_ffelt x, mx - pari_catch_sig_on() + sig_on() if FF_issquareall(self.val, &s): x = self._new() x.construct(s) @@ -789,12 +788,12 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): elif gequal0(x.val) or self._parent.characteristic() == 2: return [x] else: - pari_catch_sig_on() + sig_on() mx = self._new() mx.construct(FF_neg_i(x.val)) return [x, mx] else: - pari_catch_sig_off() + sig_off() if all: return [] else: @@ -862,7 +861,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): # because PARI assumes by default that this element generates # the multiplicative group. cdef GEN x, base_order, self_order - pari_catch_sig_on() + sig_on() base_order = FF_order((base).val, NULL) self_order = FF_order(self.val, NULL) if not dvdii(base_order, self_order): @@ -887,7 +886,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): if self.is_zero(): raise ArithmeticError("Multiplicative order of 0 not defined.") cdef GEN order - pari_catch_sig_on() + sig_on() order = FF_order(self.val, NULL) return Integer(pari.new_gen(order)) @@ -988,7 +987,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): sage: b._pari_() a^2 + 2*a + 1 """ - pari_catch_sig_on() + sig_on() return pari.new_gen(self.val) def _pari_init_(self): diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index d26f34baca1..511c2530311 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -473,7 +473,7 @@ cdef class FiniteField(Field): REFERENCES: - .. [Coh] H. Cohen, A Course in Computational Algebraic Number + .. [Coh] \H. Cohen, A Course in Computational Algebraic Number Theory. Springer-Verlag, 1993. .. [Yun] Yun, David YY. On square-free decomposition algorithms. @@ -1162,7 +1162,7 @@ cdef class FiniteField(Field): TESTS: - We check that trac #18915 is fixed:: + We check that :trac:`18915` is fixed:: sage: F = GF(2) sage: F.extension(int(3), 'a') diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index a80898f11d4..8ed49508101 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -128,7 +128,7 @@ def Mod(n, m, parent=None): sage: mod(12,5) 2 - Illustrates that trac #5971 is fixed. Consider `n` modulo `m` when + Illustrates that :trac:`5971` is fixed. Consider `n` modulo `m` when `m = 0`. Then `\ZZ/0\ZZ` is isomorphic to `\ZZ` so `n` modulo `0` is equivalent to `n` for any integer value of `n`:: @@ -549,12 +549,12 @@ cdef class IntegerMod_abstract(FiniteRingElement): ... ZeroDivisionError: Inverse does not exist. - We check that #9205 is fixed:: + We check that :trac:`9205` is fixed:: sage: Mod(5,9).log(Mod(2, 9)) 5 - We test against a bug (side effect on PARI) fixed in #9438:: + We test against a bug (side effect on PARI) fixed in :trac:`9438`:: sage: R. = QQ[] sage: pari(b) @@ -1202,7 +1202,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): ... except ValueError: ... pass - We check that #13172 is resolved:: + We check that :trac:`13172` is resolved:: sage: mod(-1, 4489).nth_root(2, all=True) [] @@ -1587,10 +1587,10 @@ cdef class IntegerMod_abstract(FiniteRingElement): ArithmeticError: multiplicative order of 0 not defined since it is not a unit modulo 5 """ try: - return sage.rings.integer.Integer(self._pari_().order()) # pari's "order" is by default multiplicative + return sage.rings.integer.Integer(self._pari_().znorder()) except PariError: - raise ArithmeticError, "multiplicative order of %s not defined since it is not a unit modulo %s"%( - self, self.__modulus.sageInteger) + raise ArithmeticError("multiplicative order of %s not defined since it is not a unit modulo %s"%( + self, self.__modulus.sageInteger)) def valuation(self, p): """ @@ -3847,7 +3847,7 @@ def lucas_q1(mm, IntegerMod_abstract P): REFERENCES: - .. [Pos88] H. Postl. 'Fast evaluation of Dickson Polynomials' Contrib. to + .. [Pos88] \H. Postl. 'Fast evaluation of Dickson Polynomials' Contrib. to General Algebra, Vol. 6 (1988) pp. 223-225 AUTHORS: diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index 86d12adfb26..847c9b7ad99 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -10,7 +10,7 @@ AUTHORS: REFERENCES: -.. [Ho72] E. Horowitz, "Algorithms for Rational Function Arithmetic +.. [Ho72] \E. Horowitz, "Algorithms for Rational Function Arithmetic Operations", Annual ACM Symposium on Theory of Computing, Proceedings of the Fourth Annual ACM Symposium on Theory of Computing, pp. 108--118, 1972 diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 10a3bfd3494..b53af0c9ee8 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -7,8 +7,9 @@ - Robert Bradshaw (2010-05-30): added is_finite() -- Julian Rueth (2011-06-08, 2011-09-14, 2014-06-23): fixed hom(), extension(); - use @cached_method; added derivation() +- Julian Rueth (2011-06-08, 2011-09-14, 2014-06-23, 2014-06-24): fixed hom(), + extension(); use @cached_method; added derivation(); added support for + relative vector spaces - Maarten Derickx (2011-09-11): added doctests @@ -324,6 +325,60 @@ def _coerce_map_from_(self, R): return True return False + def _intermediate_fields(self, base): + r""" + Return the fields which lie in between ``base`` and this field in the + tower of function fields. + + INPUT: + + - ``base`` -- a function field, either this field or a field from which + this field has been created as an extension + + OUTPUT: + + A list of fields. The first entry is ``base``, the last entry is this field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K._intermediate_fields(K) + [Rational function field in x over Rational Field] + + sage: R. = K[] + sage: L. = K.extension(y^2-x) + sage: L._intermediate_fields(K) + [Function field in y defined by y^2 - x, Rational function field in x over Rational Field] + + sage: R. = L[] + sage: M. = L.extension(z^2-y) + sage: M._intermediate_fields(L) + [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x] + sage: M._intermediate_fields(K) + [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x, Rational function field in x over Rational Field] + + TESTS:: + + sage: K._intermediate_fields(M) + Traceback (most recent call last): + ... + ValueError: field has not been constructed as a finite extension of base + sage: K._intermediate_fields(QQ) + Traceback (most recent call last): + ... + TypeError: base must be a function field + + """ + if not is_FunctionField(base): + raise TypeError("base must be a function field") + + ret = [self] + while ret[-1] is not base: + ret.append(ret[-1].base_field()) + if ret[-1] is ret[-2]: + raise ValueError("field has not been constructed as a finite extension of base") + return ret + class FunctionField_polymod(FunctionField): """ A function field defined by a univariate polynomial, as an @@ -600,10 +655,17 @@ def constant_base_field(self): """ return self.base_field().constant_base_field() - def degree(self): + @cached_method(key=lambda self, base: self.base_field() if base is None else base) + def degree(self, base=None): """ - Return the degree of this function field over its base - function field. + Return the degree of this function field over the function field + ``base``. + + INPUT: + + - ``base`` -- a function field or ``None`` (default: ``None``), a + function field from which this field has been constructed as a finite + extension. EXAMPLES:: @@ -613,8 +675,30 @@ def degree(self): Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x sage: L.degree() 5 + sage: L.degree(L) + 1 + + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.degree(L) + 2 + sage: M.degree(K) + 10 + + TESTS:: + + sage: L.degree(M) + Traceback (most recent call last): + ... + ValueError: base must be None or the rational function field + """ - return self._polynomial.degree() + if base is None: + base = self.base_field() + if base is self: + from sage.rings.integer_ring import ZZ + return ZZ(1) + return self._polynomial.degree() * self.base_field().degree(base) def _repr_(self): """ @@ -688,21 +772,27 @@ def polynomial_ring(self): """ return self._ring - @cached_method - def vector_space(self): + @cached_method(key=lambda self, base: self.base_field() if base is None else base) + def vector_space(self, base=None): """ - Return a vector space V and isomorphisms self --> V and V --> self. + Return a vector space `V` and isomorphisms from this field to `V` and + from `V` to this field. + + This function allows us to identify the elements of this field with + elements of a vector space over the base field, which is useful for + representation and arithmetic with orders, ideals, etc. + + INPUT: - This function allows us to identify the elements of self with - elements of a vector space over the base field, which is - useful for representation and arithmetic with orders, ideals, - etc. + - ``base`` -- a function field or ``None`` (default: ``None``), the + returned vector space is over ``base`` which defaults to the base + field of this function field. OUTPUT: - - ``V`` -- a vector space over base field - - ``from_V`` -- an isomorphism from V to self - - ``to_V`` -- an isomorphism from self to V + - ``V`` -- a vector space over base field + - ``from_V`` -- an isomorphism from V to this field + - ``to_V`` -- an isomorphism from this field to V EXAMPLES: @@ -761,9 +851,22 @@ def vector_space(self): To: Function field in z defined by z^2 - y, Isomorphism morphism: From: Function field in z defined by z^2 - y To: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x) + + We can also get the vector space of ``M`` over ``K``:: + + sage: M.vector_space(K) + (Vector space of dimension 10 over Rational function field in x over Rational Field, Isomorphism morphism: + From: Vector space of dimension 10 over Rational function field in x over Rational Field + To: Function field in z defined by z^2 - y, Isomorphism morphism: + From: Function field in z defined by z^2 - y + To: Vector space of dimension 10 over Rational function field in x over Rational Field) + """ - V = self.base_field()**self.degree() from maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace + if base is None: + base = self.base_field() + degree = self.degree(base) + V = base**degree; from_V = MapVectorSpaceToFunctionField(V, self) to_V = MapFunctionFieldToVectorSpace(self, V) return (V, from_V, to_V) @@ -1024,7 +1127,7 @@ def genus(self): def is_RationalFunctionField(x): """ - Return True if ``x`` is of rational function field type. + Return ``True`` if ``x`` is of rational function field type. EXAMPLES:: @@ -1036,8 +1139,6 @@ def is_RationalFunctionField(x): """ if isinstance(x, RationalFunctionField): return True -# if (x in FunctionFields()): -# return x == x.base_field() else: return False @@ -1362,18 +1463,28 @@ def random_element(self, *args, **kwds): """ return self(self._field.random_element(*args, **kwds)) - def degree(self): + def degree(self, base=None): """ Return the degree over the base field of this rational function field. Since the base field is the rational function field itself, the degree is 1. + INPUT: + + - ``base`` -- must be this field or ``None``; this parameter is ignored + and exists to resemble the interface of + :meth:`FunctionField_polymod.degree`. + EXAMPLES:: sage: K. = FunctionField(QQ) sage: K.degree() 1 """ + if base is None: + base = self + if base is not self: + raise ValueError("base must be None or the rational function field") from sage.rings.integer_ring import ZZ return ZZ(1) @@ -1532,6 +1643,59 @@ def genus(self): """ return 0 + @cached_method(key=lambda self, base: None) + def vector_space(self, base=None): + """ + Return a vector space `V` and isomorphisms from this field to `V` and + from `V` to this field. + + This function allows us to identify the elements of this field with + elements of a one-dimensional vector space over the field itself. This + method exists so that all function fields (rational or not) have the + same interface. + + INPUT: + + - ``base`` -- must be this field or ``None`` (default: ``None``); this + parameter is ignored and merely exists to have the same interface as + :meth:`FunctionField_polymod.vector_space`. + + OUTPUT: + + - ``V`` -- a vector space over base field + - ``from_V`` -- an isomorphism from V to this field + - ``to_V`` -- an isomorphism from this field to V + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.vector_space() + (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism morphism: + From: Vector space of dimension 1 over Rational function field in x over Rational Field + To: Rational function field in x over Rational Field, Isomorphism morphism: + From: Rational function field in x over Rational Field + To: Vector space of dimension 1 over Rational function field in x over Rational Field) + + TESTS:: + + sage: K.vector_space() + (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism morphism: + From: Vector space of dimension 1 over Rational function field in x over Rational Field + To: Rational function field in x over Rational Field, Isomorphism morphism: + From: Rational function field in x over Rational Field + To: Vector space of dimension 1 over Rational function field in x over Rational Field) + + """ + from maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace + if base is None: + base = self + if base is not self: + raise ValueError("base must be the rational function field or None") + V = base**1 + from_V = MapVectorSpaceToFunctionField(V, self) + to_V = MapFunctionFieldToVectorSpace(self, V) + return (V, from_V, to_V) + @cached_method def derivation(self): r""" diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 01462a9e5fd..efe3979c417 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -264,16 +264,47 @@ def __init__(self, V, K): def _call_(self, v): """ + Map ``v`` to the function field. + EXAMPLES:: - sage: K. = FunctionField(QQ); R. = K[] + sage: K. = FunctionField(QQ) + sage: R. = K[] sage: L. = K.extension(y^2 - x*y + 4*x^3) sage: V, f, t = L.vector_space() - sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest + sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest 1/x^3*y + x + + TESTS: + + Test that this map is a bijection for some random inputs:: + + sage: R. = L[] + sage: M. = L.extension(z^3 - y - x) + sage: for F in [K,L,M]: + ....: for base in F._intermediate_fields(K): + ....: V, f, t = F.vector_space(base) + ....: for i in range(100): + ....: a = F.random_element() + ....: assert(f(t(a)) == a) + """ - f = self._R(self._V(v).list()) - return self._K(f) + fields = self._K._intermediate_fields(self._V.base_field()) + fields.pop() + degrees = [k.degree() for k in fields] + gens = [k.gen() for k in fields] + + # construct the basis composed of powers of the generators of all the + # intermediate fields, i.e., 1, x, y, x*y, ... + from sage.misc.misc_c import prod + from itertools import product + exponents = product(*[range(d) for d in degrees]) + basis = [prod(g**e for g,e in zip(gens,es)) for es in exponents] + + # multiply the entries of v with the values in basis + coefficients = self._V(v).list() + ret = sum([c*b for (c,b) in zip(coefficients,basis)]) + return self._K(ret) def domain(self): """ @@ -377,18 +408,39 @@ def _repr_type(self): def _call_(self, x): """ + Map ``x`` to the vector space. + EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] sage: L. = K.extension(y^2 - x*y + 4*x^3) sage: V, f, t = L.vector_space() - sage: t(x + (1/x^3)*y) # indirect doctest + sage: t(x + (1/x^3)*y) # indirect doctest (x, 1/x^3) - """ - y = self._K(x) - v = y.list() - w = v + [self._zero]*(self._n - len(v)) - return self._V(w) + + TESTS: + + Test that this map is a bijection for some random inputs:: + + sage: R. = L[] + sage: M. = L.extension(z^3 - y - x) + sage: for F in [K,L,M]: + ....: for base in F._intermediate_fields(K): + ....: V, f, t = F.vector_space(base) + ....: for i in range(100): + ....: a = V.random_element() + ....: assert(t(f(a)) == a) + + """ + ret = [x] + fields = self._K._intermediate_fields(self._V.base_field()) + fields.pop() + from itertools import chain + for k in fields: + ret = chain.from_iterable([y.list() for y in ret]) + ret = list(ret) + assert all([t.parent() is self._V.base_field() for t in ret]) + return self._V(ret) class FunctionFieldMorphism(RingHomomorphism): r""" diff --git a/src/sage/rings/infinity.py b/src/sage/rings/infinity.py index 7f4b3dedea9..ae946aec66c 100644 --- a/src/sage/rings/infinity.py +++ b/src/sage/rings/infinity.py @@ -206,7 +206,6 @@ sage: m.rows() [(+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 @@ -214,7 +213,8 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** - +# python3 +from __future__ import division from sys import maxsize from sage.rings.ring import Ring @@ -297,21 +297,19 @@ def _pari_(self): """ Convert ``self`` to a Pari object. - This always raises an exception since Pari does not have - infinities. - - TESTS:: + EXAMPLES:: - sage: pari(-oo) # indirect doctest - Traceback (most recent call last): - ... - TypeError: cannot convert infinity to Pari + sage: pari(-oo) + -oo sage: pari(oo) - Traceback (most recent call last): - ... - TypeError: cannot convert infinity to Pari + +oo """ - raise TypeError('cannot convert infinity to Pari') + # For some reason, it seems problematic to import sage.libs.all.pari, + # so we call it directly. + if self._sign >= 0: + return sage.libs.all.pari('oo') + else: + return sage.libs.all.pari('-oo') def _latex_(self): r""" @@ -553,6 +551,12 @@ def __init__(self): sage: sage.rings.infinity.UnsignedInfinityRing_class() is sage.rings.infinity.UnsignedInfinityRing_class() is UnsignedInfinityRing True + + Sage can understand SymPy's complex infinity (:trac:`17493`):: + + sage: import sympy + sage: SR(sympy.zoo) + Infinity """ ParentWithGens.__init__(self, self, names=('oo',), normalize=False) @@ -903,6 +907,23 @@ def _mul_(self, other): return self raise ValueError("unsigned oo times smaller number not defined") + def _sympy_(self): + """ + Converts ``unsigned_infinity`` to sympy ``zoo``. + + EXAMPLE:: + + sage: import sympy + sage: sympy.sympify(unsigned_infinity) + zoo + sage: gamma(-3)._sympy_() is sympy.factorial(-2) + True + sage: gamma(-3) is sympy.factorial(-2)._sage_() + True + """ + import sympy + return sympy.zoo + unsigned_infinity = UnsignedInfinityRing.gen(0) less_than_infinity = UnsignedInfinityRing.less_than_infinity() @@ -1665,7 +1686,9 @@ def test_comparison(ring): AssertionError: testing -1000.0 in Symbolic Ring: id = ... """ from sage.symbolic.ring import SR - elements = [-1e3, 99.9999, -SR(2).sqrt(), 0, 1, 3^(-1/3), SR.pi(), 100000] + from sage.rings.rational_field import QQ + elements = [-1e3, 99.9999, -SR(2).sqrt(), 0, 1, + 3 ** (-QQ.one()/3), SR.pi(), 100000] elements.append(ring.an_element()) elements.extend(ring.some_elements()) for z in elements: diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 1ffbe6794ae..2cef2bbdb9b 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -142,7 +142,7 @@ from sage.misc.long cimport pyobject_to_long include "cysignals/signals.pxi" include "sage/ext/cdefs.pxi" include "sage/ext/stdsage.pxi" -from sage.ext.memory cimport check_malloc, check_allocarray +include "cysignals/memory.pxi" from cpython.list cimport * from cpython.number cimport * from cpython.int cimport * @@ -153,7 +153,6 @@ from sage.structure.element cimport (Element, EuclideanDomainElement, parent_c, coercion_model) include "sage/ext/python_debug.pxi" from sage.libs.pari.paridecl cimport * -include "sage/libs/pari/pari_err.pxi" from sage.rings.rational cimport Rational from sage.libs.gmp.rational_reconstruction cimport mpq_rational_reconstruction from sage.libs.gmp.pylong cimport * @@ -1054,7 +1053,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): mpz_get_str(s, base, self.value) sig_off() k = s - sage_free(s) + sig_free(s) return k def __format__(self, *args, **kwargs): @@ -2574,9 +2573,9 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): cdef list output try: - pari_catch_sig_on() + sig_on() d = divisorsu(n) - pari_catch_sig_off() + sig_off() output = [smallInteger(d[i]) for i in range(1,lg(d))] return output finally: @@ -2730,7 +2729,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): mpz_set_ui(z.value, sorted_c[i]) sorted.append(z) fits_c = False - sage_free(ptr) + sig_free(ptr) ptr = NULL # The two cases below are essentially the same algorithm, one @@ -2810,7 +2809,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): mpz_set_ui(z.value, sorted_c[i]) sorted.append(z) finally: - sage_free(ptr) + sig_free(ptr) return sorted @@ -4043,7 +4042,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): for i from 0 <= i < prod_count: mpz_clear(sub_prods[i]) - sage_free(sub_prods) + sig_free(sub_prods) return z @@ -6130,12 +6129,12 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): ... ZeroDivisionError: Inverse does not exist. - We check that #10625 is fixed:: + We check that :trac:`10625` is fixed:: sage: ZZ(2).inverse_mod(ZZ.ideal(3)) 2 - We check that #9955 is fixed:: + We check that :trac:`9955` is fixed:: sage: Rational(3)%Rational(-1) 0 @@ -6907,7 +6906,7 @@ cdef void fast_tp_dealloc(PyObject* o): # Again, we move to the mpz_t and clear it. As in fast_tp_new, # we free the memory directly. - sage_free(o_mpz._mp_d) + sig_free(o_mpz._mp_d) # Free the object. This assumes that Py_TPFLAGS_HAVE_GC is not # set. If it was set another free function would need to be @@ -6921,7 +6920,7 @@ cdef hook_fast_tp_functions(): """ global global_dummy_Integer, sizeof_Integer, integer_pool - integer_pool = sage_malloc(integer_pool_size * sizeof(PyObject*)) + integer_pool = sig_malloc(integer_pool_size * sizeof(PyObject*)) cdef PyObject* o o = global_dummy_Integer @@ -6955,7 +6954,7 @@ def free_integer_pool(): integer_pool_size = 0 integer_pool_count = 0 - sage_free(integer_pool) + sig_free(integer_pool) # Replace default allocation and deletion with faster custom ones hook_fast_tp_functions() diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 8e27adf8f85..165cdf6a3ce 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -134,7 +134,6 @@ cdef class LaurentSeries(AlgebraElement): # self is that t^n * u: - cdef long val if not f: if n == infinity: self.__n = 0 @@ -144,7 +143,10 @@ cdef class LaurentSeries(AlgebraElement): self.__u = f else: val = f.valuation() - if val == 0: + if val == infinity: + self.__n = 0 + self.__u = f + elif val == 0: self.__n = n # power of the variable self.__u = f # unit part else: @@ -303,7 +305,7 @@ cdef class LaurentSeries(AlgebraElement): sage: latex(f) \frac{\frac{17}{2}}{x^{2}} + x + x^{2} + 3x^{4} + O(x^{7}) - Verify that trac #6656 has been fixed:: + Verify that :trac:`6656` has been fixed:: sage: R.=PolynomialRing(QQ) sage: T.=LaurentSeriesRing(R) diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index e5dc77f2e03..6e70a70b97e 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -210,7 +210,7 @@ def _element_class(self): We test that a method inherited from PermutationGroup_generic returns - the right type of element (see trac #133):: + the right type of element (see :trac:`133`):: sage: phi = G.random_element() sage: type(phi) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 56962246aa3..fdc8e05f0e1 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -381,7 +381,9 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, l sage: sqrtn3 + zeta 2*zeta^5 + zeta + 1 - Comparison depends on the (real) embedding specified (or the one selected by default).:: + Comparison depends on the (real) embedding specified (or the one selected by default). + Note that the codomain of the embedding must be `QQbar` or `AA` for this to work + (see :trac:`20184`):: sage: N. = NumberField(x^3+2,embedding=1) sage: 1 < g @@ -854,7 +856,7 @@ def QuadraticField(D, name='a', check=True, embedding=True, latex_name='sqrt', * sage: latex(QuadraticField(-1, 'a', latex_name=None).gen()) a - The name of the generator does not interfere with Sage preparser, see #1135:: + The name of the generator does not interfere with Sage preparser, see :trac:`1135`:: sage: K1 = QuadraticField(5, 'x') sage: K2. = QuadraticField(5) @@ -4900,6 +4902,37 @@ def gen(self, n=0): self.__gen = self._element_class(self, X) return self.__gen + @cached_method + def _generator_matrix(self): + """ + Return the matrix form of the generator of ``self``. + + .. SEEALSO:: + + :meth:`~sage.rings.number_field.number_field_element.NumberFieldElement.matrix` + + EXAMPLES:: + + sage: x = QQ['x'].gen() + sage: K. = NumberField(x^4 + 514*x^2 + 64321) + sage: R. = NumberField(x^2 + 4*v*x + 5*v^2 + 514) + sage: R._generator_matrix() + [ 0 1] + [-5*v^2 - 514 -4*v] + """ + x = self.gen() + a = x + d = self.relative_degree() + v = x.list() + for n in range(d-1): + a *= x + v += a.list() + from sage.matrix.matrix_space import MatrixSpace + M = MatrixSpace(self.base_ring(), d) + ret = M(v) + ret.set_immutable() + return ret + def is_field(self, proof=True): """ Return True since a number field is a field. @@ -5163,13 +5196,13 @@ def _pari_integral_basis(self, v=None, important=True): elif self._assume_disc_small: B = f.nfbasis(1) elif not important: - # Trial divide the discriminant - m = self.pari_polynomial().poldisc().abs().factor(limit=0) + # Trial divide the discriminant with primes up to 10^6 + m = self.pari_polynomial().poldisc().abs().factor(limit=10**6) # Since we only need a *squarefree* factorization for # primes with exponent 1, we need trial division up to D^(1/3) # instead of D^(1/2). - trialdivlimit2 = pari(pari._primelimit()**2) - trialdivlimit3 = pari(pari._primelimit()**3) + trialdivlimit2 = pari(10**12) + trialdivlimit3 = pari(10**18) if all([ p < trialdivlimit2 or (e == 1 and p < trialdivlimit3) or p.isprime() for p,e in zip(m[0],m[1]) ]): B = f.nfbasis(fa = m) else: @@ -8978,8 +9011,7 @@ def _gap_init_(self): The following was the motivating example to introduce a genuine representation of cyclotomic fields in the - GAP interface -- see ticket #5618. - :: + GAP interface -- see :trac:`5618`. :: sage: H = AlternatingGroup(4) sage: g = H.list()[1] @@ -9520,14 +9552,14 @@ def _coerce_from_gap(self, x): 1 sage: F(b[1,2]) 1 - sage: matrix(b, F) + sage: matrix(F, b) [ zeta8^2 1] [ 0 -zeta8^3 + zeta8 + 1] It also word with libGAP instead of GAP:: sage: b = libgap.eval('[[E(4), 1], [0, 1+E(8)-E(8)^3]]') - sage: matrix(b, F) + sage: matrix(F, b) [ zeta8^2 1] [ 0 -zeta8^3 + zeta8 + 1] """ diff --git a/src/sage/rings/number_field/number_field_element.pxd b/src/sage/rings/number_field/number_field_element.pxd index 25412cd7fdb..8b27454cc51 100644 --- a/src/sage/rings/number_field/number_field_element.pxd +++ b/src/sage/rings/number_field/number_field_element.pxd @@ -32,6 +32,8 @@ cdef class NumberFieldElement(FieldElement): cpdef ModuleElement _sub_(self, ModuleElement right) cpdef ModuleElement _neg_(self) + cpdef list _coefficients(self) + cpdef bint is_rational(self) cpdef bint is_one(self) cdef int _randomize(self, num_bound, den_bound, distribution) except -1 diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index b2f0a9285ec..48a2bc8e849 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -37,16 +37,21 @@ include "sage/libs/ntl/decl.pxi" from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * +from sage.libs.mpfi cimport mpfi_t, mpfi_init, mpfi_set, mpfi_clear, mpfi_div_z, mpfi_init2, mpfi_get_prec, mpfi_set_prec +from sage.libs.mpfr cimport mpfr_less_p, mpfr_greater_p, mpfr_greaterequal_p from cpython.object cimport Py_EQ, Py_NE, Py_LT, Py_GT, Py_LE, Py_GE from sage.structure.sage_object cimport rich_to_bool import sage.rings.infinity import sage.rings.polynomial.polynomial_element +from sage.rings.polynomial.evaluation cimport ZZX_evaluation_mpfi import sage.rings.rational_field import sage.rings.rational import sage.rings.integer_ring import sage.rings.integer +from sage.rings.real_mpfi cimport RealIntervalFieldElement + cimport number_field_base import number_field @@ -768,22 +773,42 @@ cdef class NumberFieldElement(FieldElement): P = left._parent except TypeError: P = left._parent.number_field() - cdef size_t i = 0 + cdef size_t i = 0 # level of the approximation + cdef RealIntervalFieldElement v # approximation of the nf generator + cdef mpfi_t la, ra # left and right approximations + cdef mpz_t ld, rd # left and right denominators if P._embedded_real: - # TODO: optimize this computation without any call to the method - # polynomial! - lp = left.polynomial() - rp = _right.polynomial() - la = lp(P._get_embedding_approx(0)) - ra = rp(P._get_embedding_approx(0)) - while la.overlaps(ra): + mpz_init(ld) + mpz_init(rd) + ZZ_to_mpz(ld, &left.__denominator) + ZZ_to_mpz(rd, &_right.__denominator) + + v = P._get_embedding_approx(0) + mpfi_init2(la, mpfi_get_prec(v.value)) + mpfi_init2(ra, mpfi_get_prec(v.value)) + ZZX_evaluation_mpfi(la, left.__numerator, v.value) + mpfi_div_z(la, la, ld) + ZZX_evaluation_mpfi(ra, _right.__numerator, v.value) + mpfi_div_z(ra, ra, rd) + while mpfr_greaterequal_p(&la.right, &ra.left) \ + and mpfr_greaterequal_p(&ra.right, &la.left): i += 1 - la = lp(P._get_embedding_approx(i)) - ra = rp(P._get_embedding_approx(i)) + v = P._get_embedding_approx(i) + mpfi_set_prec(la, mpfi_get_prec(v.value)) + mpfi_set_prec(ra, mpfi_get_prec(v.value)) + ZZX_evaluation_mpfi(la, left.__numerator, v.value) + mpfi_div_z(la, la, ld) + ZZX_evaluation_mpfi(ra, _right.__numerator, v.value) + mpfi_div_z(ra, ra, rd) if op == Py_LT or op == Py_LE: - return la.upper() < ra.lower() + res = mpfr_less_p(&la.right, &ra.left) elif op == Py_GT or op == Py_GE: - return la.lower() > ra.upper() + res = mpfr_greater_p(&la.left, &ra.right) + mpfi_clear(la) + mpfi_clear(ra) + mpz_clear(ld) + mpz_clear(rd) + return bool(res) else: return rich_to_bool(op, 1) @@ -1707,7 +1732,7 @@ cdef class NumberFieldElement(FieldElement): TESTS: Check that the output is correct even for numbers that are - very close to zero (ticket #9596):: + very close to zero (:trac:`9596`):: sage: K. = QuadraticField(2) sage: a = 30122754096401; b = 21300003689580 @@ -2316,6 +2341,43 @@ cdef class NumberFieldElement(FieldElement): ZZX_getitem_as_mpz(num.value, &self.__numerator, 0) return num / (ZZ)._coerce_ZZ(&self.__denominator) + def _algebraic_(self, parent): + r""" + Convert this element to an algebraic number, if possible. + + EXAMPLES:: + + sage: NF. = NumberField(x^5 + 7*x + 3, embedding=CC(0,1)) + sage: QQbar(alpha) + -1.032202770009288? + 1.168103873894207?*I + sage: AA(alpha) + Traceback (most recent call last): + ... + ValueError: Cannot coerce algebraic number with non-zero imaginary + part to algebraic real + + sage: NF. = NumberField(x^5 + 7*x + 3) + sage: QQbar(alpha) + Traceback (most recent call last): + ... + ValueError: need a real or complex embedding to convert number + field element to algebraic number + + TESTS:: + + sage: C. = CyclotomicField(7) + sage: a = 2*z^2 + 5*z^4 + sage: E = C.algebraic_closure() + sage: E(a) + -4.949886207424724? - 0.2195628712241434?*I + """ + emb = self._parent.coerce_embedding() + if emb is None: + raise ValueError("need a real or complex embedding to convert " + "number field element to algebraic number") + emb = number_field.refine_embedding(emb, infinity) + return parent(emb(self)) + def _symbolic_(self, SR): """ If an embedding into CC is specified, then a representation of this @@ -2584,7 +2646,7 @@ cdef class NumberFieldElement(FieldElement): """ return hash(self.polynomial()) - def _coefficients(self): + cpdef list _coefficients(self): """ Return the coefficients of the underlying polynomial corresponding to this number field element. @@ -2592,7 +2654,7 @@ cdef class NumberFieldElement(FieldElement): OUTPUT: - a list whose length corresponding to the degree of this - element written in terms of a generator. + element written in terms of a generator EXAMPLES:: @@ -2600,7 +2662,7 @@ cdef class NumberFieldElement(FieldElement): sage: (b^2 + 1)._coefficients() [1, 0, 1] """ - coeffs = [] + cdef list coeffs = [] cdef Integer den = (ZZ)._coerce_ZZ(&self.__denominator) cdef Integer numCoeff cdef int i @@ -3146,9 +3208,9 @@ cdef class NumberFieldElement(FieldElement): sage: F. = CyclotomicField(5) ; t = 3*z**3 + 4*z**2 + 2 sage: t.matrix(F) [3*z^3 + 4*z^2 + 2] - sage: x=QQ['x'].gen() - sage: K.=NumberField(x^4 + 514*x^2 + 64321) - sage: R.=NumberField(x^2 + 4*v*x + 5*v^2 + 514) + sage: x = QQ['x'].gen() + sage: K. = NumberField(x^4 + 514*x^2 + 64321) + sage: R. = NumberField(x^2 + 4*v*x + 5*v^2 + 514) sage: r.matrix() [ 0 1] [-5*v^2 - 514 -4*v] @@ -3157,13 +3219,13 @@ cdef class NumberFieldElement(FieldElement): [-5*v^2 - 514 -4*v] sage: r.matrix(R) [r] - sage: foo=R.random_element() + sage: foo = R.random_element() sage: foo.matrix(R) == matrix(1,1,[foo]) True """ - from sage.matrix.constructor import matrix + import sage.matrix.matrix_space if base is self.parent(): - return matrix(1,1,[self]) + return sage.matrix.matrix_space.MatrixSpace(base,1)([self]) if base is not None and base is not self.base_ring(): if number_field.is_NumberField(base): return self._matrix_over_base(base) @@ -3175,17 +3237,16 @@ cdef class NumberFieldElement(FieldElement): # and transpose. if self.__matrix is None: K = self.number_field() - v = [] - x = K.gen() - a = K(1) d = K.relative_degree() - for n in range(d): - v += (a*self).list() - a *= x - k = K.base_ring() - import sage.matrix.matrix_space - M = sage.matrix.matrix_space.MatrixSpace(k, d) + cur = self.vector() + X = K._generator_matrix() + v = cur.list() + for n in range(d-1): + cur = cur * X + v += cur.list() + M = sage.matrix.matrix_space.MatrixSpace(K.base_ring(), d) self.__matrix = M(v) + self.__matrix.set_immutable() return self.__matrix def valuation(self, P): @@ -3233,7 +3294,7 @@ cdef class NumberFieldElement(FieldElement): raise ValueError, "P must be prime" if self == 0: return infinity - return Integer_sage(self.number_field().pari_nf().elementval(self._pari_(), P.pari_prime())) + return Integer_sage(self.number_field().pari_nf().nfeltval(self, P.pari_prime())) ord = valuation @@ -3246,7 +3307,7 @@ cdef class NumberFieldElement(FieldElement): - ``P`` - a prime ideal of the parent of self - - ``prec`` (int) -- desired floating point precision (defult: + - ``prec`` (int) -- desired floating point precision (default: default RealField precision). - ``weighted`` (bool, default False) -- if True, apply local @@ -3416,7 +3477,7 @@ cdef class NumberFieldElement(FieldElement): INPUT: - - ``prec`` (int) -- desired floating point precision (defult: + - ``prec`` (int) -- desired floating point precision (default: default RealField precision). OUTPUT: @@ -3443,7 +3504,7 @@ cdef class NumberFieldElement(FieldElement): INPUT: - - ``prec`` (int) -- desired floating point precision (defult: + - ``prec`` (int) -- desired floating point precision (default: default RealField precision). OUTPUT: @@ -3984,7 +4045,7 @@ cdef class NumberFieldElement_absolute(NumberFieldElement): otherwise 'sage' is used. The constant TUNE_CHARPOLY_NF should give reasonable performance on all architectures; however, if you feel the need to customize it to your own - machine, see trac ticket 5213 for a tuning script. + machine, see :trac:`5213` for a tuning script. EXAMPLES: @@ -4331,7 +4392,7 @@ cdef class NumberFieldElement_relative(NumberFieldElement): otherwise 'sage' is used. The constant TUNE_CHARPOLY_NF should give reasonable performance on all architectures; however, if you feel the need to customize it to your own - machine, see trac ticket 5213 for a tuning script. + machine, see :trac:`5213` for a tuning script. EXAMPLES:: @@ -4492,9 +4553,10 @@ cdef class OrderElement_absolute(NumberFieldElement_absolute): cpdef RingElement _div_(self, RingElement other): r""" Implement division, checking that the result has the right parent. + It's not so crucial what the parent actually is, but it is crucial that the returned value really is an element of its supposed - parent! This fixes trac #4190. + parent! This fixes :trac:`4190`. EXAMPLES:: @@ -4547,7 +4609,9 @@ cdef class OrderElement_absolute(NumberFieldElement_absolute): def __invert__(self): r""" Implement inversion, checking that the return value has the right - parent. See trac #4190. + parent. + + See :trac:`4190`. EXAMPLE:: @@ -4617,9 +4681,10 @@ cdef class OrderElement_relative(NumberFieldElement_relative): cpdef RingElement _div_(self, RingElement other): r""" Implement division, checking that the result has the right parent. + It's not so crucial what the parent actually is, but it is crucial that the returned value really is an element of its supposed - parent. This fixes trac #4190. + parent. This fixes trac :trac:`4190`. EXAMPLES:: @@ -4643,7 +4708,8 @@ cdef class OrderElement_relative(NumberFieldElement_relative): def __invert__(self): r""" Implement division, checking that the result has the right parent. - See trac #4190. + + See :trac:`4190`. EXAMPLES:: diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pxd b/src/sage/rings/number_field/number_field_element_quadratic.pxd index 6d5edb7ea23..c1d9942b79e 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pxd +++ b/src/sage/rings/number_field/number_field_element_quadratic.pxd @@ -11,6 +11,8 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cpdef NumberFieldElement galois_conjugate(self) cdef bint is_sqrt_disc(self) + cpdef list _coefficients(self) + cdef int _randomize(self, num_bound, den_bound, distribution) except -1 diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 33218320d64..9793a3407f7 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -1371,6 +1371,29 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpq_canonicalize(res.value) return res + def _algebraic_(self, parent): + r""" + Convert this element to an algebraic number, if possible. + + EXAMPLES:: + + sage: NF. = QuadraticField(-1) + sage: QQbar(1+i) + I + 1 + sage: NF. = QuadraticField(2) + sage: AA(sqrt3) + 1.414213562373095? + """ + import sage.rings.qqbar as qqbar + if (parent is qqbar.QQbar + and list(self._parent.polynomial()) == [1, 0, 1]): + # AlgebraicNumber.__init__ does a better job than + # NumberFieldElement._algebraic_ in this case, but + # QQbar._element_constructor_ calls the latter first. + return qqbar.AlgebraicNumber(self) + else: + return NumberFieldElement._algebraic_(self, parent) + cpdef bint is_one(self): r""" Check whether this number field element is `1`. @@ -1556,7 +1579,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpz_neg(q.b, self.b) return q - def _coefficients(self): + cpdef list _coefficients(self): """ EXAMPLES:: diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index eaa7bcf5b71..18a69cc5c6b 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -747,7 +747,7 @@ def free_module(self): TESTS: Sage can find the free module associated to quite large ideals - quickly (see trac #4627):: + quickly (see :trac:`4627`):: sage: y = polygen(ZZ) sage: M. = NumberField(y^20 - 2*y^19 + 10*y^17 - 15*y^16 + 40*y^14 - 64*y^13 + 46*y^12 + 8*y^11 - 32*y^10 + 8*y^9 + 46*y^8 - 64*y^7 + 40*y^6 - 15*y^4 + 10*y^3 - 2*y + 1) @@ -983,7 +983,7 @@ def intersection(self, other): TESTS: - Test that this works with non-integral ideals (#10767):: + Test that this works with non-integral ideals (:trac:`10767`):: sage: K = QuadraticField(-2) sage: I = K.ideal(1/2) @@ -993,7 +993,7 @@ def intersection(self, other): L = self.number_field() other = L.ideal(other) nf = L.pari_nf() - hnf = nf.idealintersection(self.pari_hnf(), other.pari_hnf()) + hnf = nf.idealintersect(self.pari_hnf(), other.pari_hnf()) I = L.ideal(self._NumberFieldIdeal__elements_from_hnf(hnf)) I.__pari_hnf = hnf return I @@ -1386,7 +1386,8 @@ def smallest_integer(self): sage: I.smallest_integer() 0 - # See trac\# 4392: + See :trac:`4392`:: + sage: K.=QuadraticField(-5) sage: I=K.ideal(7) sage: I.smallest_integer() @@ -1447,18 +1448,20 @@ def valuation(self, p): sage: i.valuation(0) Traceback (most recent call last): ... - ValueError: p (= 0) must be nonzero + ValueError: p (= Ideal (0) of Number Field in a with defining polynomial x^5 + 2) must be nonzero + sage: K.ideal(0).valuation(K.factor(2)[0][0]) + +Infinity """ - if p==0: - raise ValueError("p (= %s) must be nonzero"%p) - if not isinstance(p, NumberFieldFractionalIdeal): + if not isinstance(p, NumberFieldIdeal): p = self.number_field().ideal(p) + if not p: + raise ValueError("p (= %s) must be nonzero"%p) if not p.is_prime(): raise ValueError("p (= %s) must be a prime"%p) if p.ring() != self.number_field(): raise ValueError("p (= %s) must be an ideal in %s"%self.number_field()) nf = self.number_field().pari_nf() - return ZZ(nf.idealval(self.pari_hnf(), p.pari_prime())) + return nf.idealval(self.pari_hnf(), p.pari_prime()).sage() def decomposition_group(self): r""" @@ -2422,7 +2425,8 @@ def is_coprime(self, other): sage: I.is_coprime(6+i) True - # See trac \# 4536: + See :trac:`4536`:: + sage: E. = NumberField(x^5 + 7*x^4 + 18*x^2 + x - 3) sage: OE = E.ring_of_integers() sage: i,j,k = [u[0] for u in factor(3*OE)] diff --git a/src/sage/rings/number_field/number_field_rel.py b/src/sage/rings/number_field/number_field_rel.py index 9de39430405..06ce39d9190 100644 --- a/src/sage/rings/number_field/number_field_rel.py +++ b/src/sage/rings/number_field/number_field_rel.py @@ -1242,7 +1242,7 @@ def is_galois_relative(self): sage: M.is_galois_relative() False - The following example previously gave the wrong result; see #9390:: + The next example previously gave a wrong result; see :trac:`9390`:: sage: F. = NumberField([x^2 - 2, x^2 - 3]) sage: F.is_galois_relative() diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index 69afbabbd12..30d4b9f0e58 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -1460,7 +1460,7 @@ def _element_constructor_(self, x): sage: OK([3, 4]) 4*a + 3 - The following used to fail; see trac #5276:: + The following used to fail; see :trac:`5276`:: sage: S. = OK[]; S Univariate Polynomial Ring in y over Maximal Relative Order in Number Field in a with defining polynomial x^2 + 2 over its base field diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index d5555e7e902..c1efad37198 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -24,7 +24,7 @@ for example Theorem 4.8.13, p199 of [C]_), the ideal generated by `(p, \alpha - n)` is prime and of degree one. -.. [C] H. Cohen. A Course in Computational Algebraic Number Theory. +.. [C] \H. Cohen. A Course in Computational Algebraic Number Theory. Springer-Verlag, 1993. .. warning:: diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 4288737ac9e..4017878f6f6 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -304,7 +304,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False counts[i] = 0 B_pari = pari(B) - f_out = sage_malloc((n_int+1)*sizeof(int)) + f_out = sig_malloc((n_int+1)*sizeof(int)) if f_out == NULL: raise MemoryError for i from 0 <= i < n_int: f_out[i] = 0 @@ -344,7 +344,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False # Trivial case if n == 1: - sage_free(f_out) + sig_free(f_out) if return_seqs: return [[0,0,0,0],[[1,[-1,1]]]] elif return_pari_objects: @@ -444,7 +444,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False elif n_int == 3 and B >= 49 and ((not use_t2) or 5 >= t2val): jp_file.write(str([3,[1,-2,-1,1]]) + "\n") jp_file.close() - sage_free(f_out) + sig_free(f_out) return # Convert S to a sorted list of pairs [d, f], taking care to use @@ -482,11 +482,11 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False fsock.close() sys.stdout = saveout - sage_free(f_out) + sig_free(f_out) # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(counts[i]) for i in range(4)], - [[ZZ(s[0]), map(QQ, s[1].reverse().Vec())] for s in S]] + [[ZZ(s[0]), map(QQ, s[1].polrecip().Vec())] for s in S]] elif return_pari_objects: return S else: diff --git a/src/sage/rings/number_field/totallyreal_data.pyx b/src/sage/rings/number_field/totallyreal_data.pyx index 4820401c43e..35067298919 100644 --- a/src/sage/rings/number_field/totallyreal_data.pyx +++ b/src/sage/rings/number_field/totallyreal_data.pyx @@ -25,7 +25,7 @@ AUTHORS: include "sage/ext/cdefs.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.arith.all import binomial, gcd from sage.rings.rational_field import RationalField @@ -91,7 +91,7 @@ def hermite_constant(n): .. [CE] Henry Cohn and Noam Elkies, New upper bounds on sphere packings I, Ann. Math. 157 (2003), 689--714. - .. [CS] J.H. Conway and N.J.A. Sloane, Sphere packings, lattices + .. [CS] \J.H. Conway and N.J.A. Sloane, Sphere packings, lattices and groups, 3rd. ed., Grundlehren der Mathematischen Wissenschaften, vol. 290, Springer-Verlag, New York, 1999. @@ -499,13 +499,13 @@ cdef class tr_data: self.gamma = hermite_constant(n-1) # Declare the coefficients of the polynomials (and max such). - self.a = sage_malloc(sizeof(int)*(n+1)) + self.a = sig_malloc(sizeof(int)*(n+1)) if self.a == NULL: raise MemoryError - self.amax = sage_malloc(sizeof(int)*(n+1)) + self.amax = sig_malloc(sizeof(int)*(n+1)) if self.amax == NULL: raise MemoryError # df is memory set aside for the derivative, as # used in Newton iteration above. - self.df = sage_malloc(sizeof(int)*(n+1)) + self.df = sig_malloc(sizeof(int)*(n+1)) if self.df == NULL: raise MemoryError for i from 0 <= i < n+1: @@ -515,10 +515,10 @@ cdef class tr_data: # beta is an array of arrays (of length n) which list the # roots of the derivatives. - self.beta = sage_malloc(sizeof(double)*n*(n+1)) + self.beta = sig_malloc(sizeof(double)*n*(n+1)) if self.beta == NULL: raise MemoryError # gnk is the collection of (normalized) derivatives. - self.gnk = sage_malloc(sizeof(int)*(n+1)*n) + self.gnk = sig_malloc(sizeof(int)*(n+1)*n) if self.gnk == NULL: raise MemoryError for i from 0 <= i < (n+1)*n: @@ -583,11 +583,11 @@ cdef class tr_data: r""" Destructor. """ - sage_free(self.df) - sage_free(self.a) - sage_free(self.amax) - sage_free(self.beta) - sage_free(self.gnk) + sig_free(self.df) + sig_free(self.a) + sig_free(self.amax) + sig_free(self.beta) + sig_free(self.gnk) def increment(self, verbose=False, haltk=0, phc=False): r""" @@ -629,7 +629,7 @@ cdef class tr_data: cdef int *f_out cdef int i - f_out = sage_malloc(sizeof(int) * (self.n + 1)) + f_out = sig_malloc(sizeof(int) * (self.n + 1)) if f_out == NULL: raise MemoryError, "unable to allocate coefficient list" for i from 0 <= i < self.n: @@ -641,7 +641,7 @@ cdef class tr_data: g = [0] * (1 + self.n) for i from 0 <= i <= self.n: g[i] = f_out[i] - sage_free(f_out) + sig_free(f_out) return g diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index 6bd6e1830ad..e8c8e291d17 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -735,7 +735,7 @@ def enumerate_totallyreal_fields_rel(F, m, B, a = [], verbose=0, # Trivial case if m == 1: - g = pari(F.defining_polynomial()).reverse().Vec() + g = pari(F.defining_polynomial()).polrecip().Vec() if return_seqs: return [[0,0,0,0], [1, [-1, 1], g]] elif return_pari_objects: @@ -878,7 +878,7 @@ def enumerate_totallyreal_fields_rel(F, m, B, a = [], verbose=0, # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(x) for x in counts], - [[s[0], [QQ(x) for x in s[1].reverse().Vec()], s[2].coefficients(sparse=False)] + [[s[0], [QQ(x) for x in s[1].polrecip().Vec()], s[2].coefficients(sparse=False)] for s in S] ] elif return_pari_objects: @@ -999,7 +999,7 @@ def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(_) for _ in counts], - [[ZZ(s[0]), [QQ(_) for _ in s[1].reverse().Vec()]] for s in S]] + [[ZZ(s[0]), [QQ(_) for _ in s[1].polrecip().Vec()]] for s in S]] elif return_pari_objects: return S else: diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 02b2d2aaec2..26557694de0 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -83,7 +83,10 @@ cdef class CAElement(pAdicTemplateElement): self.absprec = aprec else: self.absprec = min(aprec, val + rprec) - cconv(self.value, x, self.absprec, 0, self.prime_pow) + if isinstance(x,CAElement) and x.parent() is self.parent(): + cshift(self.value, (x).value, 0, self.absprec, self.prime_pow, True) + else: + cconv(self.value, x, self.absprec, 0, self.prime_pow) cdef CAElement _new_c(self): """ @@ -188,12 +191,17 @@ cdef class CAElement(pAdicTemplateElement): 5 + O(13^4) sage: R(12) + R(1) 13 + O(13^4) + + Check that :trac:`20245` is resolved:: + + sage: R(1,1) + R(169,3) + 1 + O(13) """ cdef CAElement right = _right cdef CAElement ans = self._new_c() ans.absprec = min(self.absprec, right.absprec) cadd(ans.value, self.value, right.value, ans.absprec, ans.prime_pow) - creduce_small(ans.value, ans.value, ans.absprec, ans.prime_pow) + creduce(ans.value, ans.value, ans.absprec, ans.prime_pow) return ans cpdef ModuleElement _sub_(self, ModuleElement _right): @@ -212,7 +220,7 @@ cdef class CAElement(pAdicTemplateElement): cdef CAElement ans = self._new_c() ans.absprec = min(self.absprec, right.absprec) csub(ans.value, self.value, right.value, ans.absprec, ans.prime_pow) - creduce_small(ans.value, ans.value, ans.absprec, ans.prime_pow) + creduce(ans.value, ans.value, ans.absprec, ans.prime_pow) return ans def __invert__(self): @@ -349,7 +357,7 @@ cdef class CAElement(pAdicTemplateElement): cdef long relprec, val, rval cdef mpz_t tmp cdef Integer right - cdef CAElement base, pright, ans + cdef CAElement pright, ans cdef bint exact_exp if isinstance(_right, Integer) or isinstance(_right, (int, long)) \ or isinstance(_right, Rational): @@ -711,6 +719,23 @@ cdef class CAElement(pAdicTemplateElement): ans.absprec = absprec return ans + def _cache_key(self): + r""" + Return a hashable key which identifies this element for caching. + + TESTS:: + + sage: R. = ZqCA(9) + sage: (9*a)._cache_key() + (..., ((), (), (0, 1)), 20) + + .. SEEALSO:: + + :meth:`sage.misc.cachefunc._cache_key` + """ + tuple_recursive = lambda l: tuple(tuple_recursive(x) for x in l) if isinstance(l, list) else l + return (self.parent(), tuple_recursive(self.list()), self.precision_absolute()) + def list(self, lift_mode = 'simple', start_val = None): """ Returns a list of coefficients of `p` starting with `p^0`. @@ -1321,6 +1346,336 @@ cdef class pAdicConvert_QQ_CA(Morphism): cconv_mpq_t(ans.value, (x).value, ans.absprec, True, self._zero.prime_pow) return ans +cdef class pAdicCoercion_CA_frac_field(RingHomomorphism_coercion): + """ + The canonical inclusion of Zq into its fraction field. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: K.coerce_map_from(R) + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + """ + def __init__(self, R, K): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R); type(f) + + """ + RingHomomorphism_coercion.__init__(self, R.Hom(K), check=False) + self._zero = K(0) + self._section = pAdicConvert_CA_frac_field(K, R) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(a) + a + O(3^20) + """ + cdef CAElement x = _x + cdef CRElement ans = self._zero._new_c() + ans.ordp = cremove(ans.unit, x.value, x.absprec, x.prime_pow) + ans.relprec = x.absprec - ans.ordp + return ans + + cpdef Element _call_with_args(self, _x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in + (relative or absolute or both). + + See the documentation for + :meth:`pAdicCappedAbsoluteElement.__init__` for more details. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(a, 3) + a + O(3^3) + sage: b = 9*a + sage: f(b, 3) + a*3^2 + O(3^3) + sage: f(b, 4, 1) + a*3^2 + O(3^3) + sage: f(b, 4, 3) + a*3^2 + O(3^4) + sage: f(b, absprec=4) + a*3^2 + O(3^4) + sage: f(b, relprec=3) + a*3^2 + O(3^5) + sage: f(b, absprec=1) + O(3) + sage: f(R(0)) + O(3^20) + """ + cdef long aprec, rprec + cdef CAElement x = _x + cdef CRElement ans = self._zero._new_c() + cdef bint reduce = False + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, ans.prime_pow) + if x.absprec < aprec: + aprec = x.absprec + reduce = True + ans.ordp = cremove(ans.unit, x.value, aprec, x.prime_pow) + ans.relprec = aprec - ans.ordp + if rprec < ans.relprec: + ans.relprec = rprec + reduce = True + if ans.relprec < 0: + ans.relprec = 0 + ans.ordp = aprec + csetzero(ans.unit, x.prime_pow) + elif reduce: + creduce(ans.unit, ans.unit, ans.relprec, x.prime_pow) + return ans + + def section(self): + """ + Returns a map back to the ring that converts elements of + non-negative valuation. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(K.gen()) + a + O(3^20) + """ + return self._section + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + _slots['_zero'] = self._zero + _slots['_section'] = self._section + return RingHomomorphism_coercion._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCA(9, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + self._zero = _slots['_zero'] + self._section = _slots['_section'] + RingHomomorphism_coercion._update_slots(self, _slots) + +cdef class pAdicConvert_CA_frac_field(Morphism): + """ + The section of the inclusion from `\ZZ_q`` to its fraction field. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); f + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + """ + def __init__(self, K, R): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); type(f) + + """ + Morphism.__init__(self, Hom(K, R, SetsWithPartialMaps())) + self._zero = R(0) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: f(K.gen()) + a + O(3^20) + """ + cdef CRElement x = _x + if x.ordp < 0: raise ValueError("negative valuation") + cdef CAElement ans = self._zero._new_c() + cdef bint reduce = False + ans.absprec = x.relprec + x.ordp + if ans.absprec > ans.prime_pow.ram_prec_cap: + ans.absprec = ans.prime_pow.ram_prec_cap + reduce = True + if x.ordp >= ans.absprec: + csetzero(ans.value, ans.prime_pow) + else: + cshift(ans.value, x.unit, x.ordp, ans.absprec, ans.prime_pow, reduce) + return ans + + cpdef Element _call_with_args(self, _x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in + (relative or absolute or both). + + See the documentation for + :meth:`pAdicCappedAbsoluteElement.__init__` for more details. + + EXAMPLES:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); a = K(a) + sage: f(a, 3) + a + O(3^3) + sage: b = 9*a + sage: f(b, 3) + a*3^2 + O(3^3) + sage: f(b, 4, 1) + a*3^2 + O(3^3) + sage: f(b, 4, 3) + a*3^2 + O(3^4) + sage: f(b, absprec=4) + a*3^2 + O(3^4) + sage: f(b, relprec=3) + a*3^2 + O(3^5) + sage: f(b, absprec=1) + O(3) + sage: f(K(0)) + O(3^20) + """ + cdef long aprec, rprec + cdef CRElement x = _x + if x.ordp < 0: raise ValueError("negative valuation") + cdef CAElement ans = self._zero._new_c() + cdef bint reduce = False + _process_args_and_kwds(&aprec, &rprec, args, kwds, True, ans.prime_pow) + if x.relprec < rprec: + rprec = x.relprec + reduce = True + ans.absprec = rprec + x.ordp + if aprec < ans.absprec: + ans.absprec = aprec + reduce = True + if x.ordp >= ans.absprec: + csetzero(ans.value, ans.prime_pow) + else: + sig_on() + cshift(ans.value, x.unit, x.ordp, ans.absprec, ans.prime_pow, reduce) + sig_off() + return ans + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCA(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: a = K(a) + sage: g = copy(f) # indirect doctest + sage: g + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + _slots['_zero'] = self._zero + return Morphism._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCA(9, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: a = f(a) + sage: g = copy(f) # indirect doctest + sage: g + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped absolute precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + self._zero = _slots['_zero'] + Morphism._update_slots(self, _slots) + def unpickle_cae_v2(cls, parent, value, absprec): """ Unpickle capped absolute elements. @@ -1347,7 +1702,7 @@ def unpickle_cae_v2(cls, parent, value, absprec): """ cdef CAElement ans = cls.__new__(cls) ans._parent = parent - ans.prime_pow = parent.prime_pow + ans.prime_pow = parent.prime_pow cconstruct(ans.value, ans.prime_pow) cunpickle(ans.value, value, ans.prime_pow) ans.absprec = absprec diff --git a/src/sage/rings/padics/CA_template_header.pxi b/src/sage/rings/padics/CA_template_header.pxi index d454b9aaf22..47229881a55 100644 --- a/src/sage/rings/padics/CA_template_header.pxi +++ b/src/sage/rings/padics/CA_template_header.pxi @@ -42,3 +42,8 @@ cdef class pAdicConvert_QQ_CA(Morphism): cdef CAElement _zero cdef RingMap _section # There should also be a pAdicConvert_CA_QQ for extension rings.... +cdef class pAdicCoercion_CA_frac_field(RingHomomorphism_coercion): + cdef CRElement _zero + cdef Morphism _section +cdef class pAdicConvert_CA_frac_field(Morphism): + cdef CAElement _zero diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index 99ac0784144..a919df34d86 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -142,7 +142,10 @@ cdef class CRElement(pAdicTemplateElement): else: self.relprec = min(rprec, aprec - val) self.ordp = val - cconv(self.unit, x, self.relprec, val, self.prime_pow) + if isinstance(x,CRElement) and x.parent() is self.parent(): + cshift(self.unit, (x).unit, 0, self.relprec, self.prime_pow, True) + else: + cconv(self.unit, x, self.relprec, val, self.prime_pow) cdef int _set_exact_zero(self) except -1: """ @@ -289,8 +292,9 @@ cdef class CRElement(pAdicTemplateElement): cdef CRElement ans = self._new_c() ans.relprec = self.relprec ans.ordp = self.ordp - cneg(ans.unit, self.unit, ans.relprec, ans.prime_pow) - creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) + if ans.relprec != 0: + cneg(ans.unit, self.unit, ans.relprec, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) return ans cpdef ModuleElement _add_(self, ModuleElement _right): @@ -316,8 +320,9 @@ cdef class CRElement(pAdicTemplateElement): # possibly decreasing if we got cancellation ans.ordp = self.ordp ans.relprec = min(self.relprec, right.relprec) - cadd(ans.unit, self.unit, right.unit, ans.relprec, ans.prime_pow) - ans._normalize() + if ans.relprec != 0: + cadd(ans.unit, self.unit, right.unit, ans.relprec, ans.prime_pow) + ans._normalize() else: if self.ordp > right.ordp: # Addition is commutative, swap so self.ordp < right.ordp @@ -328,9 +333,10 @@ cdef class CRElement(pAdicTemplateElement): ans = self._new_c() ans.ordp = self.ordp ans.relprec = min(self.relprec, tmpL + right.relprec) - cshift(ans.unit, right.unit, tmpL, ans.relprec, ans.prime_pow, False) - cadd(ans.unit, ans.unit, self.unit, ans.relprec, ans.prime_pow) - creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) + if ans.relprec != 0: + cshift(ans.unit, right.unit, tmpL, ans.relprec, ans.prime_pow, False) + cadd(ans.unit, ans.unit, self.unit, ans.relprec, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) return ans cpdef ModuleElement _sub_(self, ModuleElement _right): @@ -354,8 +360,9 @@ cdef class CRElement(pAdicTemplateElement): # possibly decreasing if we got cancellation ans.ordp = self.ordp ans.relprec = min(self.relprec, right.relprec) - csub(ans.unit, self.unit, right.unit, ans.relprec, ans.prime_pow) - ans._normalize() + if ans.relprec != 0: + csub(ans.unit, self.unit, right.unit, ans.relprec, ans.prime_pow) + ans._normalize() elif self.ordp < right.ordp: tmpL = right.ordp - self.ordp if tmpL > self.relprec: @@ -363,9 +370,10 @@ cdef class CRElement(pAdicTemplateElement): ans = self._new_c() ans.ordp = self.ordp ans.relprec = min(self.relprec, tmpL + right.relprec) - cshift(ans.unit, right.unit, tmpL, ans.relprec, ans.prime_pow, False) - csub(ans.unit, self.unit, ans.unit, ans.relprec, ans.prime_pow) - creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) + if ans.relprec != 0: + cshift(ans.unit, right.unit, tmpL, ans.relprec, ans.prime_pow, False) + csub(ans.unit, self.unit, ans.unit, ans.relprec, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) else: tmpL = self.ordp - right.ordp if tmpL > right.relprec: @@ -373,9 +381,10 @@ cdef class CRElement(pAdicTemplateElement): ans = self._new_c() ans.ordp = right.ordp ans.relprec = min(right.relprec, tmpL + self.relprec) - cshift(ans.unit, self.unit, tmpL, ans.relprec, ans.prime_pow, False) - csub(ans.unit, ans.unit, right.unit, ans.relprec, ans.prime_pow) - creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) + if ans.relprec != 0: + cshift(ans.unit, self.unit, tmpL, ans.relprec, ans.prime_pow, False) + csub(ans.unit, ans.unit, right.unit, ans.relprec, ans.prime_pow) + creduce(ans.unit, ans.unit, ans.relprec, ans.prime_pow) return ans def __invert__(self): @@ -1152,6 +1161,23 @@ cdef class CRElement(pAdicTemplateElement): ccopy(ans.unit, self.unit, ans.prime_pow) return ans + def _cache_key(self): + r""" + Return a hashable key which identifies this element for caching. + + TESTS:: + + sage: K. = Qq(9) + sage: (9*a)._cache_key() + (..., ((0, 1),), 2, 20) + + .. SEEALSO:: + + :meth:`sage.misc.cachefunc._cache_key` + """ + tuple_recursive = lambda l: tuple(tuple_recursive(x) for x in l) if isinstance(l, list) else l + return (self.parent(), tuple_recursive(self.list()), self.valuation(), self.precision_relative()) + def list(self, lift_mode = 'simple', start_val = None): """ Returns a list of coefficients in a power series expansion of @@ -1986,7 +2012,7 @@ cdef class pAdicConvert_QQ_CR(Morphism): sage: g == f True sage: g(1/6) - 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + O(5^20) + 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + O(5^20) sage: g(1/6) == f(1/6) True """ @@ -2005,7 +2031,7 @@ cdef class pAdicConvert_QQ_CR(Morphism): sage: g == f True sage: g(1/6) - 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + O(5^20) + 1 + 4*5 + 4*5^3 + 4*5^5 + 4*5^7 + 4*5^9 + 4*5^11 + 4*5^13 + 4*5^15 + 4*5^17 + 4*5^19 + O(5^20) sage: g(1/6) == f(1/6) True """ @@ -2099,6 +2125,336 @@ cdef class pAdicConvert_QQ_CR(Morphism): """ return self._section +cdef class pAdicCoercion_CR_frac_field(RingHomomorphism_coercion): + """ + The canonical inclusion of Zq into its fraction field. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: K.coerce_map_from(R) + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + """ + def __init__(self, R, K): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R); type(f) + + """ + RingHomomorphism_coercion.__init__(self, R.Hom(K), check=False) + self._zero = K(0) + self._section = pAdicConvert_CR_frac_field(K, R) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(a) + a + O(3^20) + sage: f(R(0)) + 0 + """ + cdef CRElement x = _x + cdef CRElement ans = self._zero._new_c() + ans.ordp = x.ordp + ans.relprec = x.relprec + cshift(ans.unit, x.unit, 0, ans.relprec, x.prime_pow, False) + return ans + + cpdef Element _call_with_args(self, _x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in + (relative or absolute or both). + + See the documentation for + :meth:`pAdicCappedAbsoluteElement.__init__` for more details. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(a, 3) + a + O(3^3) + sage: b = 9*a + sage: f(b, 3) + a*3^2 + O(3^3) + sage: f(b, 4, 1) + a*3^2 + O(3^3) + sage: f(b, 4, 3) + a*3^2 + O(3^4) + sage: f(b, absprec=4) + a*3^2 + O(3^4) + sage: f(b, relprec=3) + a*3^2 + O(3^5) + sage: f(b, absprec=1) + O(3) + sage: f(R(0)) + 0 + """ + cdef long aprec, rprec + cdef CRElement x = _x + cdef CRElement ans = self._zero._new_c() + cdef bint reduce = False + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, ans.prime_pow) + if aprec <= x.ordp: + csetzero(ans.unit, x.prime_pow) + ans.relprec = 0 + ans.ordp = aprec + else: + if rprec < x.relprec: + reduce = True + else: + rprec = x.relprec + if aprec < rprec + x.ordp: + rprec = aprec - x.ordp + reduce = True + ans.ordp = x.ordp + ans.relprec = rprec + cshift(ans.unit, x.unit, 0, rprec, x.prime_pow, reduce) + return ans + + def section(self): + """ + Returns a map back to the ring that converts elements of + non-negative valuation. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: f(K.gen()) + a + O(3^20) + """ + return self._section + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + _slots['_zero'] = self._zero + _slots['_section'] = self._section + return RingHomomorphism_coercion._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCR(9, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = K.coerce_map_from(R) + sage: g = copy(f) # indirect doctest + sage: g + Ring Coercion morphism: + From: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + To: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + self._zero = _slots['_zero'] + self._section = _slots['_section'] + RingHomomorphism_coercion._update_slots(self, _slots) + +cdef class pAdicConvert_CR_frac_field(Morphism): + """ + The section of the inclusion from `\ZZ_q`` to its fraction field. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); f + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + """ + def __init__(self, K, R): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); type(f) + + """ + Morphism.__init__(self, Hom(K, R, SetsWithPartialMaps())) + self._zero = R(0) + + cpdef Element _call_(self, _x): + """ + Evaluation. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: f(K.gen()) + a + O(3^20) + """ + cdef CRElement x = _x + if x.ordp < 0: raise ValueError("negative valuation") + cdef CRElement ans = self._zero._new_c() + ans.relprec = x.relprec + ans.ordp = x.ordp + cshift(ans.unit, x.unit, 0, ans.relprec, ans.prime_pow, False) + return ans + + cpdef Element _call_with_args(self, _x, args=(), kwds={}): + """ + This function is used when some precision cap is passed in + (relative or absolute or both). + + See the documentation for + :meth:`pAdicCappedAbsoluteElement.__init__` for more details. + + EXAMPLES:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K); a = K(a) + sage: f(a, 3) + a + O(3^3) + sage: b = 9*a + sage: f(b, 3) + a*3^2 + O(3^3) + sage: f(b, 4, 1) + a*3^2 + O(3^3) + sage: f(b, 4, 3) + a*3^2 + O(3^4) + sage: f(b, absprec=4) + a*3^2 + O(3^4) + sage: f(b, relprec=3) + a*3^2 + O(3^5) + sage: f(b, absprec=1) + O(3) + sage: f(K(0)) + 0 + """ + cdef long aprec, rprec + cdef CRElement x = _x + if x.ordp < 0: raise ValueError("negative valuation") + cdef CRElement ans = self._zero._new_c() + cdef bint reduce = False + _process_args_and_kwds(&aprec, &rprec, args, kwds, False, ans.prime_pow) + if aprec <= x.ordp: + csetzero(ans.unit, x.prime_pow) + ans.relprec = 0 + ans.ordp = aprec + else: + if rprec < x.relprec: + reduce = True + else: + rprec = x.relprec + if aprec < rprec + x.ordp: + rprec = aprec - x.ordp + reduce = True + ans.ordp = x.ordp + ans.relprec = rprec + cshift(ans.unit, x.unit, 0, rprec, x.prime_pow, reduce) + return ans + + cdef dict _extra_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCR(27, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: a = K(a) + sage: g = copy(f) # indirect doctest + sage: g + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^3 + (O(3^20))*x^2 + (2 + O(3^20))*x + (1 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + _slots['_zero'] = self._zero + return Morphism._extra_slots(self, _slots) + + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + TESTS:: + + sage: R. = ZqCR(9, implementation='FLINT') + sage: K = R.fraction_field() + sage: f = R.convert_map_from(K) + sage: a = K(a) + sage: g = copy(f) # indirect doctest + sage: g + Generic morphism: + From: Unramified Extension of 3-adic Field with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + To: Unramified Extension of 3-adic Ring with capped relative precision 20 in a defined by (1 + O(3^20))*x^2 + (2 + O(3^20))*x + (2 + O(3^20)) + sage: g == f + True + sage: g is f + False + sage: g(a) + a + O(3^20) + sage: g(a) == f(a) + True + + """ + self._zero = _slots['_zero'] + Morphism._update_slots(self, _slots) + def unpickle_cre_v2(cls, parent, unit, ordp, relprec): """ Unpickles a capped relative element. @@ -2115,7 +2471,7 @@ def unpickle_cre_v2(cls, parent, unit, ordp, relprec): """ cdef CRElement ans = cls.__new__(cls) ans._parent = parent - ans.prime_pow = parent.prime_pow + ans.prime_pow = parent.prime_pow cconstruct(ans.unit, ans.prime_pow) cunpickle(ans.unit, unit, ans.prime_pow) ans.ordp = ordp diff --git a/src/sage/rings/padics/CR_template_header.pxi b/src/sage/rings/padics/CR_template_header.pxi index 9a75da89426..4b2f7386a87 100644 --- a/src/sage/rings/padics/CR_template_header.pxi +++ b/src/sage/rings/padics/CR_template_header.pxi @@ -48,3 +48,8 @@ cdef class pAdicConvert_CR_QQ(RingMap): cdef class pAdicConvert_QQ_CR(Morphism): cdef CRElement _zero cdef RingMap _section +cdef class pAdicCoercion_CR_frac_field(RingHomomorphism_coercion): + cdef CRElement _zero + cdef Morphism _section +cdef class pAdicConvert_CR_frac_field(Morphism): + cdef CRElement _zero diff --git a/src/sage/rings/padics/FM_template.pxi b/src/sage/rings/padics/FM_template.pxi index f7c446439bb..ac1e1bc3b8d 100644 --- a/src/sage/rings/padics/FM_template.pxi +++ b/src/sage/rings/padics/FM_template.pxi @@ -82,7 +82,10 @@ cdef class FMElement(pAdicTemplateElement): 4*5^2 + 2*5^3 + O(5^5) """ cconstruct(self.value, self.prime_pow) - cconv(self.value, x, self.prime_pow.prec_cap, 0, self.prime_pow) + if isinstance(x,FMElement) and x.parent() is self.parent(): + cshift(self.value, (x).value, 0, 0, self.prime_pow, False) + else: + cconv(self.value, x, self.prime_pow.prec_cap, 0, self.prime_pow) cdef FMElement _new_c(self): """ @@ -1137,7 +1140,7 @@ def unpickle_fme_v2(cls, parent, value): """ cdef FMElement ans = cls.__new__(cls) ans._parent = parent - ans.prime_pow = parent.prime_pow + ans.prime_pow = parent.prime_pow cconstruct(ans.value, ans.prime_pow) cunpickle(ans.value, value, ans.prime_pow) return ans diff --git a/src/sage/rings/padics/common_conversion.pxd b/src/sage/rings/padics/common_conversion.pxd index d00b7da6b6a..1be0e481fa1 100644 --- a/src/sage/rings/padics/common_conversion.pxd +++ b/src/sage/rings/padics/common_conversion.pxd @@ -1,9 +1,14 @@ - from sage.rings.integer cimport Integer from sage.rings.padics.pow_computer cimport PowComputer_class +from sage.libs.gmp.mpz cimport mpz_t, mpq_t cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000 cdef long get_preccap(x, PowComputer_class prime_pow) except? -10000 cdef long comb_prec(iprec, long prec) except? -10000 cdef int _process_args_and_kwds(long *aprec, long *rprec, args, kwds, bint absolute, PowComputer_class prime_pow) except -1 +cdef inline long cconv_mpq_t_shared(mpz_t out, mpq_t x, long prec, bint absolute, PowComputer_class prime_pow) except? -10000 +cdef inline int cconv_mpq_t_out_shared(mpq_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1 +cdef inline int cconv_shared(mpz_t out, x, long prec, long valshift, PowComputer_class prime_pow) except -2 +cdef inline long cconv_mpz_t_shared(mpz_t out, mpz_t x, long prec, bint absolute, PowComputer_class prime_pow) except -2 +cdef inline int cconv_mpz_t_out_shared(mpz_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1 diff --git a/src/sage/rings/padics/common_conversion.pyx b/src/sage/rings/padics/common_conversion.pyx index 999d0d1bbd3..69c44009292 100644 --- a/src/sage/rings/padics/common_conversion.pyx +++ b/src/sage/rings/padics/common_conversion.pyx @@ -29,6 +29,7 @@ AUTHORS: from cpython.int cimport * from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.all cimport * +from sage.libs.gmp.rational_reconstruction cimport mpq_rational_reconstruction from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.padics.padic_generic_element cimport pAdicGenericElement @@ -39,8 +40,7 @@ from sage.rings.infinity import infinity cdef long maxordp = (1L << (sizeof(long) * 8 - 2)) - 1 # The following Integer is used so that the functions here don't need to initialize an mpz_t. -cdef Integer tmp = PY_NEW(Integer) - +cdef Integer temp = PY_NEW(Integer) cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: """ @@ -62,7 +62,7 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: - ``x`` -- data defining a new p-adic element: a Python int, an Integer, Rational, an element of Zp or Qp with the same prime, a - PARI p-adic element, or an IntegerMod. + PARI p-adic element, a list, a tuple, or an IntegerMod. - a PowComputer associated to a `p`-adic ring, which determines the prime and the ramification degree. @@ -72,7 +72,7 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: - a long, giving the valuation of the resulting `p`-adic element. If the input is zero, returns ``maxordp`` """ - cdef long k, n, p + cdef long k, n, p, curterm, shift, f, e = prime_pow.e cdef Integer value cdef GEN pari_tmp if isinstance(x, int): @@ -81,7 +81,7 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: try: n = PyInt_AsLong(x) except OverflowError: - x = Integer(x) + return get_ordp(Integer(x), prime_pow) else: if mpz_fits_slong_p(prime_pow.prime.value) == 0: # x is not divisible by p @@ -91,18 +91,42 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: while n % p == 0: k += 1 n = n / p - if isinstance(x, Integer): + elif isinstance(x, Integer): if mpz_sgn((x).value) == 0: return maxordp - k = mpz_remove(tmp.value, (x).value, prime_pow.prime.value) + k = mpz_remove(temp.value, (x).value, prime_pow.prime.value) elif isinstance(x, Rational): if mpq_sgn((x).value) == 0: return maxordp - k = mpz_remove(tmp.value, mpq_numref((x).value), prime_pow.prime.value) + k = mpz_remove(temp.value, mpq_numref((x).value), prime_pow.prime.value) if k == 0: - k = -mpz_remove(tmp.value, mpq_denref((x).value), prime_pow.prime.value) - elif isinstance(x, pAdicGenericElement) and (x)._is_base_elt(prime_pow.prime): + k = -mpz_remove(temp.value, mpq_denref((x).value), prime_pow.prime.value) + elif isinstance(x, (list,tuple)): + f = prime_pow.f + if (e == 1 and len(x) > f) or (e != 1 and len(x) > e): + # could reduce modulo the defining polynomial but that isn't currently supported + raise ValueError("List too long") + k = maxordp + shift = 0 + for a in x: + if isinstance(a, (list,tuple)): + if e == 1 or f == 1: + raise ValueError("nested lists not allowed for unramified and eisenstein extensions") + for b in a: + if isinstance(b, (list,tuple)): + raise ValueError("list nesting too deep") + curterm = get_ordp(b, prime_pow) + k = min(k, curterm + shift) + else: + curterm = get_ordp(a, prime_pow) + k = min(k, curterm + shift) + if e != 1: shift += 1 + # We don't want to multiply by e again. + return k + elif isinstance(x, pAdicGenericElement): k = (x).valuation_c() + if not (x)._is_base_elt(prime_pow.prime): + k //= x.parent().ramification_index() elif isinstance(x, pari_gen): pari_tmp = (x).g if typ(pari_tmp) == t_PADIC: @@ -113,11 +137,12 @@ cdef long get_ordp(x, PowComputer_class prime_pow) except? -10000: value = x.lift() if mpz_sgn(value.value) == 0: return maxordp - k = mpz_remove(tmp.value, value.value, prime_pow.prime.value) + k = mpz_remove(temp.value, value.value, prime_pow.prime.value) else: - raise RuntimeError + from sage.structure.element import parent + raise NotImplementedError("Can not determine p-adic valuation of an element of %s"%parent(x)) # Should check for overflow - return k * prime_pow.e + return k * e cdef long get_preccap(x, PowComputer_class prime_pow) except? -10000: """ @@ -139,7 +164,7 @@ cdef long get_preccap(x, PowComputer_class prime_pow) except? -10000: - ``x`` -- data defining a new p-adic element: an Integer, Rational, an element of Zp or Qp with the same prime, a PARI - p-adic element, or an IntegerMod. + p-adic element, a list, a tuple, or an IntegerMod. - ``prime_pow`` -- the PowComputer for the ring into which ``x`` is being converted. This is used to determine the prime and the ramification degree. @@ -149,27 +174,45 @@ cdef long get_preccap(x, PowComputer_class prime_pow) except? -10000: - a long, giving the absolute precision modulo which the input is defined. If the input is exact, returns ``maxordp`` """ - cdef long k + cdef long k, shift, e = prime_pow.e cdef Integer prec cdef GEN pari_tmp if isinstance(x, int) or isinstance(x, Integer) or isinstance(x, Rational): return maxordp - elif isinstance(x, pAdicGenericElement) and (x)._is_base_elt(prime_pow.prime): + elif isinstance(x, (list,tuple)): + k = maxordp + shift = 0 + for a in x: + if isinstance(a, (list,tuple)): + for b in a: + curterm = get_preccap(b, prime_pow) + k = min(k, curterm + shift) + else: + curterm = get_preccap(a, prime_pow) + k = min(k, curterm + shift) + if e != 1: shift += 1 + # We don't want to multiply by e again. + return k + elif isinstance(x, pAdicGenericElement): if (x)._is_exact_zero(): return maxordp prec = x.precision_absolute() k = mpz_get_si(prec.value) + if not (x)._is_base_elt(prime_pow.prime): + # since x lives in a subfield, the ramification index of x's parent will divide e. + return k * (e // x.parent().ramification_index()) elif isinstance(x, pari_gen): pari_tmp = (x).g # since get_ordp has been called typ(x.g) == t_PADIC k = valp(pari_tmp) + precp(pari_tmp) elif sage.rings.finite_rings.integer_mod.is_IntegerMod(x): - k = mpz_remove(tmp.value, (x.modulus()).value, prime_pow.prime.value) - if mpz_cmp_ui(tmp.value, 1) != 0: + k = mpz_remove(temp.value, (x.modulus()).value, prime_pow.prime.value) + if mpz_cmp_ui(temp.value, 1) != 0: raise TypeError("cannot coerce from the given integer mod ring (not a power of the same prime)") else: - raise RuntimeError - return k * prime_pow.e + from sage.structure.element import parent + raise NotImplementedError("Can not determine p-adic precision of an element of %s"%parent(x)) + return k * e cdef long comb_prec(iprec, long prec) except? -10000: """ @@ -255,3 +298,180 @@ cdef int _process_args_and_kwds(long *aprec, long *rprec, args, kwds, bint absol else: rprec[0] = comb_prec(relprec, prime_pow.prec_cap) aprec[0] = comb_prec(absprec, maxordp) + +cdef inline long cconv_mpq_t_shared(mpz_t out, mpq_t x, long prec, bint absolute, PowComputer_class prime_pow) except? -10000: + """ + A fast pathway for conversion of rationals that doesn't require + precomputation of the valuation. + + INPUT: + + - ``out`` -- an ``mpz_t`` to store the output. + - ``x`` -- an ``mpq_t`` giving the integer to be converted. + - ``prec`` -- a long, giving the precision desired: absolute or + relative depending on the ``absolute`` input. + - ``absolute`` -- if False then extracts the valuation and returns + it, storing the unit in ``out``; if True then + just reduces ``x`` modulo the precision. + - ``prime_pow`` -- a PowComputer for the ring. + + OUTPUT: + + - If ``absolute`` is False then returns the valuation that was + extracted (``maxordp`` when `x = 0`). + """ + cdef long numval, denval + cdef bint success + if prec <= 0: + raise ValueError + if absolute: + success = mpz_invert(out, mpq_denref(x), prime_pow.pow_mpz_t_tmp(prec)) + if not success: + raise ValueError("p divides denominator") + mpz_mul(out, out, mpq_numref(x)) + mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) + elif mpq_sgn(x) == 0: + mpz_set_ui(out, 0) + return maxordp + else: + denval = mpz_remove(out, mpq_denref(x), prime_pow.prime.value) + mpz_invert(out, out, prime_pow.pow_mpz_t_tmp(prec)) + if denval == 0: + numval = mpz_remove(temp.value, mpq_numref(x), prime_pow.prime.value) + mpz_mul(out, out, temp.value) + else: + numval = 0 + mpz_mul(out, out, mpq_numref(x)) + mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) + return numval - denval + +cdef inline int cconv_mpq_t_out_shared(mpq_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1: + """ + Converts the underlying `p`-adic element into a rational + + - ``out`` -- gives a rational approximating the input. Currently uses rational reconstruction but + may change in the future to use a more naive method + - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element + - ``valshift`` -- a long giving the power of `p` to shift `x` by + -` ``prec`` -- a long, the precision of ``x``, used in rational reconstruction + - ``prime_pow`` -- a PowComputer for the ring + """ + mpq_rational_reconstruction(out, x, prime_pow.pow_mpz_t_tmp(prec)) + + # if valshift is nonzero then we starte with x as a p-adic unit, + # so there will be no powers of p in the numerator or denominator + # and the following operations yield reduced rationals. + if valshift > 0: + mpz_mul(mpq_numref(out), mpq_numref(out), prime_pow.pow_mpz_t_tmp(valshift)) + elif valshift < 0: + mpz_mul(mpq_denref(out), mpq_denref(out), prime_pow.pow_mpz_t_tmp(-valshift)) + +cdef inline int cconv_shared(mpz_t out, x, long prec, long valshift, PowComputer_class prime_pow) except -2: + """ + Conversion from other Sage types. + + INPUT: + + - ``out`` -- an ``mpz_t`` to store the output. + + - ``x`` -- a Sage element that can be converted to a `p`-adic element. + + - ``prec`` -- a long, giving the precision desired: absolute if + `valshift = 0`, relative if `valshift != 0`. + + - ``valshift`` -- the power of the uniformizer to divide by before + storing the result in ``out``. + + - ``prime_pow`` -- a PowComputer for the ring. + + """ + if PyInt_Check(x): + x = Integer(x) + elif isinstance(x, pari_gen): + x = x.sage() + if isinstance(x, pAdicGenericElement) or sage.rings.finite_rings.integer_mod.is_IntegerMod(x): + x = x.lift() + if isinstance(x, Integer): + if valshift > 0: + mpz_divexact(out, (x).value, prime_pow.pow_mpz_t_tmp(valshift)) + mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) + elif valshift < 0: + raise RuntimeError("Integer should not have negative valuation") + else: + mpz_mod(out, (x).value, prime_pow.pow_mpz_t_tmp(prec)) + elif isinstance(x, Rational): + if valshift == 0: + mpz_invert(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(prec)) + mpz_mul(out, out, mpq_numref((x).value)) + elif valshift < 0: + mpz_divexact(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(-valshift)) + mpz_invert(out, out, prime_pow.pow_mpz_t_tmp(prec)) + mpz_mul(out, out, mpq_numref((x).value)) + else: + mpz_invert(out, mpq_denref((x).value), prime_pow.pow_mpz_t_tmp(prec)) + mpz_divexact(temp.value, mpq_numref((x).value), prime_pow.pow_mpz_t_tmp(valshift)) + mpz_mul(out, out, temp.value) + mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) + elif isinstance(x, list): + if valshift == 0: + if len(x) == 0: + cconv_shared(out, Integer(0), prec, valshift, prime_pow) + elif len(x) == 1: + cconv_shared(out, x[0], prec, valshift, prime_pow) + else: + raise NotImplementedError("conversion not implemented from non-prime residue field") + else: + raise NotImplementedError + else: + raise NotImplementedError("No conversion defined for %s which is a %s in %s"%(x,type(x),x.parent() if hasattr(x,"parent") else "no parent")) + +cdef inline long cconv_mpz_t_shared(mpz_t out, mpz_t x, long prec, bint absolute, PowComputer_class prime_pow) except -2: + """ + A fast pathway for conversion of integers that doesn't require + precomputation of the valuation. + + INPUT: + + - ``out`` -- an ``mpz_t`` to store the output. + - ``x`` -- an ``mpz_t`` giving the integer to be converted. + - ``prec`` -- a long, giving the precision desired: absolute or + relative depending on the ``absolute`` input. + - ``absolute`` -- if False then extracts the valuation and returns + it, storing the unit in ``out``; if True then + just reduces ``x`` modulo the precision. + - ``prime_pow`` -- a PowComputer for the ring. + + OUTPUT: + + - If ``absolute`` is False then returns the valuation that was + extracted (``maxordp`` when `x = 0`). + """ + cdef long val + if absolute: + mpz_mod(out, x, prime_pow.pow_mpz_t_tmp(prec)) + elif mpz_sgn(x) == 0: + mpz_set_ui(out, 0) + return maxordp + else: + val = mpz_remove(out, x, prime_pow.prime.value) + mpz_mod(out, out, prime_pow.pow_mpz_t_tmp(prec)) + return val + +cdef inline int cconv_mpz_t_out_shared(mpz_t out, mpz_t x, long valshift, long prec, PowComputer_class prime_pow) except -1: + """ + Converts the underlying `p`-adic element into an integer if + possible. + + - ``out`` -- stores the resulting integer as an integer between 0 + and `p^{prec + valshift}`. + - ``x`` -- an ``mpz_t`` giving the underlying `p`-adic element. + - ``valshift`` -- a long giving the power of `p` to shift `x` by. + -` ``prec`` -- a long, the precision of ``x``: currently not used. + - ``prime_pow`` -- a PowComputer for the ring. + """ + if valshift == 0: + mpz_set(out, x) + elif valshift < 0: + raise ValueError("negative valuation") + else: + mpz_mul(out, x, prime_pow.pow_mpz_t_tmp(valshift)) diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 70abdcf4d85..394719a722e 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -457,7 +457,9 @@ class Qp_class(UniqueFactory): sage: K = Qp(15, check=False); a = K(999); a 9 + 6*15 + 4*15^2 + O(15^20) """ - def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_terms = None, check = True): + def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, + halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_terms = None, check = True): """ Creates a key from input parameters for ``Qp``. @@ -489,9 +491,12 @@ def create_object(self, version, key): if isinstance(type, Integer): # lazy raise NotImplementedError("lazy p-adics need more work. Sorry.") - if version[0] < 4 or (len(version) > 1 and version[0] == 4 and version[1] < 5) or (len(version) > 2 and version[0] == 4 and version[1] == 5 and version[2] < 3): - # keys changed in order to reduce irrelevant duplications: e.g. two Qps with print_mode 'series' that are identical except for different 'print_alphabet' now return the same object. - key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) + if (version[0] < 4 or (len(version) > 1 and version[0] == 4 and version[1] < 5) or + (len(version) > 2 and version[0] == 4 and version[1] == 5 and version[2] < 3)): + # keys changed in order to reduce irrelevant duplications: e.g. two Qps with print_mode 'series' + # that are identical except for different 'print_alphabet' now return the same object. + key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, + print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) try: obj = self._cache[version, key]() if obj is not None: @@ -502,9 +507,11 @@ def create_object(self, version, key): if type == 'capped-rel': if print_mode == 'terse': - return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_terse_terms': print_max_terms}, name) + return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_terse_terms': print_max_terms}, name) else: - return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + return pAdicFieldCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms}, name) else: raise ValueError("unexpected type") @@ -518,7 +525,7 @@ def create_object(self, version, key): def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): """ Given a prime power `q = p^n`, return the unique unramified extension of `\mathbb{Q}_p` of degree `n`. @@ -985,7 +992,8 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, if not isinstance(prec, Integer): prec = Integer(prec) if not isinstance(halt, Integer): halt = Integer(halt) - base = Qp(p=p, prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_terms=print_max_ram_terms, check=check) + base = Qp(p=p, prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, print_pos=print_pos, + print_sep=print_sep, print_max_terms=print_max_ram_terms, check=check) if k == 1: return base @@ -1005,8 +1013,12 @@ def Qq(q, prec = DEFAULT_PREC, type = 'capped-rel', modulus = None, names=None, if modulus is None: 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) + 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, implementation=implementation) ###################################################### # Short constructor names for different types @@ -1047,7 +1059,7 @@ def QpCR(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = def QqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): """ A shortcut function to create capped relative unramified `p`-adic fields. @@ -1062,8 +1074,8 @@ def QqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, """ return Qq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, halt=halt, ram_name=ram_name, print_pos=print_pos, 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, - type = 'capped-rel') + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'capped-rel') #def QqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, @@ -1441,7 +1453,9 @@ class Zp_class(UniqueFactory): sage: a + b 1 + 5 + O(5^10) """ - def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_terms = None, check = True): + def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = None, halt = DEFAULT_HALT, + names = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, + print_max_terms = None, check = True): """ Creates a key from input parameters for ``Zp``. @@ -1454,7 +1468,8 @@ def create_key(self, p, prec = DEFAULT_PREC, type = 'capped-rel', print_mode = N sage: Zp.create_key(5,40,print_mode='digits') (5, 40, 'capped-rel', 'digits', '5', True, '|', ('0', '1', '2', '3', '4'), -1) """ - return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, print_max_terms, check, ['capped-rel', 'fixed-mod', 'capped-abs']) + return get_key_base(p, prec, type, print_mode, halt, names, ram_name, print_pos, print_sep, print_alphabet, + print_max_terms, check, ['capped-rel', 'fixed-mod', 'capped-abs']) def create_object(self, version, key): """ @@ -1467,7 +1482,8 @@ def create_object(self, version, key): sage: Zp.create_object((3,4,2),(5, 41, 'capped-rel', 'series', '5', True, '|', (), -1)) 5-adic Ring with capped relative precision 41 """ - if version[0] < 3 or (len(version) > 1 and version[0] == 3 and version[1] < 2) or (len(version) > 2 and version[0] == 3 and version[1] == 2 and version[2] < 3): + if (version[0] < 3 or (len(version) > 1 and version[0] == 3 and version[1] < 2) or + (len(version) > 2 and version[0] == 3 and version[1] == 2 and version[2] < 3)): p, prec, type, print_mode, name = key print_pos, print_sep, print_alphabet, print_max_terms = None, None, None, None else: @@ -1475,9 +1491,12 @@ def create_object(self, version, key): if isinstance(type, Integer): # lazy raise NotImplementedError("lazy p-adics need more work. Sorry.") - if version[0] < 4 or (len(version) > 1 and version[0] == 4 and version[1] < 5) or (len(version) > 2 and version[0] == 4 and version[1] == 5 and version[2] < 3): - # keys changed in order to reduce irrelevant duplications: e.g. two Zps with print_mode 'series' that are identical except for different 'print_alphabet' now return the same object. - key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) + if (version[0] < 4 or (len(version) > 1 and version[0] == 4 and version[1] < 5) or + (len(version) > 2 and version[0] == 4 and version[1] == 5 and version[2] < 3)): + # keys changed in order to reduce irrelevant duplications: e.g. two Zps with print_mode 'series' + # that are identical except for different 'print_alphabet' now return the same object. + key = get_key_base(p, prec, type, print_mode, 0, name, None, print_pos, print_sep, print_alphabet, + print_max_terms, False, ['capped-rel', 'fixed-mod', 'capped-abs']) try: obj = self._cache[version, key]() if obj is not None: @@ -1486,11 +1505,14 @@ def create_object(self, version, key): pass p, prec, type, print_mode, name, print_pos, print_sep, print_alphabet, print_max_terms = key if type == 'capped-rel': - return pAdicRingCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + return pAdicRingCappedRelative(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms}, name) elif type == 'fixed-mod': - return pAdicRingFixedMod(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + return pAdicRingFixedMod(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms}, name) elif type == 'capped-abs': - return pAdicRingCappedAbsolute(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'ram_name': name, 'max_ram_terms': print_max_terms}, name) + return pAdicRingCappedAbsolute(p, prec, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'ram_name': name, 'max_ram_terms': print_max_terms}, name) else: raise ValueError("unexpected type") @@ -1504,7 +1526,7 @@ def create_object(self, version, key): def Zq(q, prec = DEFAULT_PREC, type = 'capped-abs', modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, res_name = None, print_pos = None, print_sep = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): """ Given a prime power `q = p^n`, return the unique unramified extension of `\mathbb{Z}_p` of degree `n`. @@ -1563,6 +1585,9 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-abs', modulus = None, names=None, - ``check`` -- bool (default ``True``) whether to check inputs. + - ``implementation`` -- string (default ``FLINT``) which + implementation to use. ``NTL`` is the other option. + OUTPUT: - The corresponding unramified `p`-adic ring. @@ -1995,7 +2020,8 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-abs', modulus = None, names=None, else: F = q q = F[0][0]**F[0][1] - base = Zp(p=F[0][0], prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, print_pos=print_pos, print_sep=print_sep, print_max_terms=print_max_ram_terms, check=False) + base = Zp(p=F[0][0], prec=prec, type=type, print_mode=print_mode, halt=halt, names=ram_name, + print_pos=print_pos, print_sep=print_sep, print_max_terms=print_max_ram_terms, check=False) if F[0][1] == 1: return base elif names is None: @@ -2007,7 +2033,12 @@ def Zq(q, prec = DEFAULT_PREC, type = 'capped-abs', modulus = None, names=None, if ram_name is None: ram_name = str(F[0][0]) modulus = PolynomialRing(base, 'x')(GF(q, 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) + 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, implementation=implementation) ###################################################### # Short constructor names for different types @@ -2079,7 +2110,7 @@ def ZpFM(p, prec = DEFAULT_PREC, print_mode = None, halt = DEFAULT_HALT, names = def ZqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation = 'FLINT'): """ A shortcut function to create capped relative unramified `p`-adic rings. @@ -2093,13 +2124,13 @@ def ZqCR(q, prec = DEFAULT_PREC, modulus = None, names=None, """ return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, halt=halt, ram_name=ram_name, print_pos=print_pos, 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, - type = 'capped-rel') + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'capped-rel') def ZqCA(q, prec = DEFAULT_PREC, modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation='FLINT'): """ A shortcut function to create capped absolute unramified `p`-adic rings. @@ -2112,13 +2143,13 @@ def ZqCA(q, prec = DEFAULT_PREC, modulus = None, names=None, """ return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, halt=halt, ram_name=ram_name, print_pos=print_pos, 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, - type = 'capped-abs') + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'capped-abs') def ZqFM(q, prec = DEFAULT_PREC, modulus = None, names=None, print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, - print_max_unram_terms = None, print_max_terse_terms = None, check = True): + print_max_unram_terms = None, print_max_terse_terms = None, check = True, implementation='FLINT'): """ A shortcut function to create fixed modulus unramified `p`-adic rings. @@ -2131,8 +2162,8 @@ def ZqFM(q, prec = DEFAULT_PREC, modulus = None, names=None, """ return Zq(q, prec=prec, modulus=modulus, names=names, print_mode=print_mode, halt=halt, ram_name=ram_name, print_pos=print_pos, 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, - type = 'fixed-mod') + print_max_unram_terms=print_max_unram_terms, print_max_terse_terms=print_max_terse_terms, + check=check, implementation=implementation, type = 'fixed-mod') #def ZqL(q, prec = DEFAULT_PREC, modulus = None, names=None, # print_mode=None, halt = DEFAULT_HALT, ram_name = None, print_pos = None, @@ -2171,7 +2202,12 @@ class pAdicExtension_class(UniqueFactory): sage: W.precision_cap() 12 """ - def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = None, halt = None, names = None, var_name = None, res_name = None, unram_name = None, ram_name = None, print_pos = None, print_sep = None, print_alphabet = None, print_max_ram_terms = None, print_max_unram_terms = None, print_max_terse_terms = None, check = True, unram = False): + def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = None, + halt = None, names = None, var_name = None, res_name = None, + unram_name = None, ram_name = None, print_pos = None, + print_sep = None, print_alphabet = None, print_max_ram_terms = None, + print_max_unram_terms = None, print_max_terse_terms = None, + check = True, unram = False, implementation='FLINT'): """ Creates a key from input parameters for pAdicExtension. @@ -2182,7 +2218,7 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = sage: R = Zp(5,3) sage: S. = ZZ[] sage: pAdicExtension.create_key_and_extra_args(R, x^4-15,names='w') - (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1), {'shift_seed': (3 + O(5^3))}) + (('e', 5-adic Ring with capped relative precision 3, x^4 - 15, (1 + O(5^3))*x^4 + (O(5^4))*x^3 + (O(5^4))*x^2 + (O(5^4))*x + (2*5 + 4*5^2 + 4*5^3 + O(5^4)), ('w', None, None, 'w'), 12, None, 'series', True, '|', (), -1, -1, -1, 'NTL'), {'shift_seed': (3 + O(5^3))}) """ if print_mode is None: print_mode = base.print_mode() @@ -2305,9 +2341,11 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = shift_seed = -preseed.change_ring(base) shift_seed /= base.uniformizer() if prec is None: - prec = min([c.precision_absolute() for c in shift_seed.list() if not c._is_exact_zero()] + [modulus.leading_coefficient().precision_absolute()] + [base.precision_cap()]) * e + prec = min([c.precision_absolute() for c in shift_seed.list() if not c._is_exact_zero()] + + [modulus.leading_coefficient().precision_absolute()] + [base.precision_cap()]) * e else: - prec = min([c.precision_absolute() * e for c in shift_seed.list() if not c._is_exact_zero()] + [modulus.leading_coefficient().precision_absolute() * e] + [base.precision_cap() * e] + [prec]) + prec = min([c.precision_absolute() * e for c in shift_seed.list() if not c._is_exact_zero()] + + [modulus.leading_coefficient().precision_absolute() * e] + [base.precision_cap() * e] + [prec]) modulus = truncate_to_prec(modulus, (prec/e).ceil() + 1) else: if unram_name is None: @@ -2320,10 +2358,16 @@ def create_key_and_extra_args(self, base, premodulus, prec = None, print_mode = polytype = 'p' #print "polytype = %s"%polytype if polytype == 'u' or polytype == 'e': - key = (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, print_max_terse_terms) + if polytype == 'e': + implementation = "NTL" # for testing - FLINT ramified extensions not implemented yet + key = (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, + print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, + print_max_terse_terms, implementation) else: upoly, epoly, prec = split(modulus, prec) - key = (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, print_max_terse_terms) + key = (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, + print_sep, tuple(print_alphabet), print_max_ram_terms, print_max_unram_terms, + print_max_terse_terms, implementation) return key, {'shift_seed': shift_seed} def create_object(self, version, key, shift_seed): @@ -2336,17 +2380,30 @@ def create_object(self, version, key, shift_seed): sage: R = Zp(5,3) sage: S. = R[] - sage: pAdicExtension.create_object(version = (3,4,2), key = ('e', R, x^4 - 15, x^4 - 15, ('w', None, None, 'w'), 12, None, 'series', True, '|', (),-1,-1,-1), shift_seed = S(3 + O(5^3))) + sage: pAdicExtension.create_object(version = (6,4,2), key = ('e', R, x^4 - 15, x^4 - 15, ('w', None, None, 'w'), 12, None, 'series', True, '|', (),-1,-1,-1,'NTL'), shift_seed = S(3 + O(5^3))) Eisenstein Extension of 5-adic Ring with capped relative precision 3 in w defined by (1 + O(5^3))*x^4 + (2*5 + 4*5^2 + 4*5^3 + O(5^4)) """ polytype = key[0] + if version[0] < 6 or version[0] == 6 and version[1] < 1: + key = list(key) + key.append('NTL') if polytype == 'u' or polytype == 'e': - polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms = key - return ext_table[polytype, type(base.ground_ring_of_tower()).__base__](premodulus, modulus, prec, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, shift_seed, names) + (polytype, base, premodulus, modulus, names, prec, halt, print_mode, print_pos, print_sep, + print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, implementation) = key + T = ext_table[polytype, type(base.ground_ring_of_tower()).__base__] + return T(premodulus, modulus, prec, halt, + {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, + shift_seed, names, implementation) elif polytype == 'p': - polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms = key + (polytype, base, premodulus, upoly, epoly, names, prec, halt, print_mode, print_pos, print_sep, + print_alphabet, print_max_ram_terms, print_max_unram_terms, print_max_terse_terms, implementation) = key precmult = epoly.degree() - return ext_table['p', type(base.ground_ring_of_tower()).__base__](premodulus, upoly, epoly, prec*precmult, halt, {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, names) + T = ext_table['p', type(base.ground_ring_of_tower()).__base__] + return T(premodulus, upoly, epoly, prec*precmult, halt, + {'mode': print_mode, 'pos': print_pos, 'sep': print_sep, 'alphabet': print_alphabet, + 'max_ram_terms': print_max_ram_terms, 'max_unram_terms': print_max_unram_terms, 'max_terse_terms': print_max_terse_terms}, + names, implementation) ExtensionFactory = pAdicExtension = pAdicExtension_class("pAdicExtension") @@ -2377,7 +2434,7 @@ def split(poly, prec): TESTS: - This checks that ticket #6186 is still fixed: + This checks that :trac:`6186` is still fixed:: sage: k = Qp(13) sage: x = polygen(k) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 04d0b9289cf..6f67706fa9b 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -159,7 +159,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" +from sage.ext.stdsage cimport PY_NEW include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" @@ -1833,7 +1833,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): sage: W(0).list() [0] sage: A(0,4).list() - [[]] + [] """ if lift_mode == 'simple': ulist = self.ext_p_list(1) diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 1e0375be257..fd689bdcad3 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -182,7 +182,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" +from sage.ext.stdsage cimport PY_NEW include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" @@ -513,7 +513,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): sage: hash(a) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' However, we want to cache computations which depend on them. Therefore they define a ``_cache_key`` which is hashable and uniquely identifies @@ -531,7 +531,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): sage: K.zero()._cache_key() (..., 0) sage: K(0,1)._cache_key() - (..., 0, 1) + (..., 1, 0) """ if self._is_exact_zero(): @@ -1340,11 +1340,11 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): EXAMPLES:: - sage: R. = ZqCR(125); b = 5*a + 4; c = 10*a^2 + 6; d = b + c + sage: R. = ZqCR(125,implementation="NTL"); b = 5*a + 4; c = 10*a^2 + 6; d = b + c sage: d._is_normalized() False - sage: d - (2*a^2 + a + 2)*5 + O(5^20) + sage: d.valuation() + 1 sage: d._is_normalized() True """ @@ -2694,7 +2694,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): sage: W(0,4).list() [0] sage: A(0,4).list() - [[]] + [] """ cdef pAdicZZpXCRElement zero cdef Integer ordp @@ -2922,7 +2922,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): TESTS: - We check that #8239 is resolved:: + We check that :trac:`8239` is resolved:: sage: K. = Qq(25) sage: K.teichmuller(K(2/5)) diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 417ece9cfc1..04284ad0452 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1255,7 +1255,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: W(0).list() [0] sage: A(0,4).list() - [[]] + [] """ cdef pAdicZZpXFMElement zero if lift_mode == 'simple': diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index ed0366aff88..891f6a5b218 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -389,7 +389,7 @@ cdef class pAdicZZpXElement(pAdicExtElement): sage: ((1+2*w)).norm()^5 1 + 5^2 + O(5^5) - Check that #11586 has been resolved:: + Check that :trac:`11586` has been resolved:: sage: R. = QQ[] sage: f = x^2 + 3*x + 1 @@ -498,7 +498,7 @@ cdef class pAdicZZpXElement(pAdicExtElement): EXAMPLES:: sage: QQ(Qq(125,names='a')(-1/5)) #indirect doctest - 95367431640624/5 + -1/5 """ if self.valuation() < 0: pk = self.parent().prime()**(-self.ordp()).ceil() @@ -576,21 +576,21 @@ def _test_preprocess_list(R, L): sage: from sage.rings.padics.padic_ZZ_pX_element import _test_preprocess_list sage: from sage.libs.ntl.all import ZZ as ntl_ZZ, ZZ_p as ntl_ZZ_p - sage: _test_preprocess_list(Zq(25,names='a'), [1,2,3]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1,2,3]) ([1, 2, 3], 0, None) - sage: _test_preprocess_list(Zq(25,names='a'), [10,20,30]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [10,20,30]) ([10, 20, 30], 0, None) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,2/5,3]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,2/5,3]) ([1, 2, 15], -1, NTL modulus 95367431640625) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,mod(2,625),3]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,mod(2,625),3]) ([1, 10, 15], -1, NTL modulus 3125) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,mod(2,625),ntl_ZZ_p(3,25)]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,mod(2,625),ntl_ZZ_p(3,25)]) ([1, 10, 15], -1, NTL modulus 125) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,mod(2,625),Zp(5)(5,3)]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3)]) ([1, 10, 1], -1, NTL modulus 625) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,mod(2,625),Zp(5)(5,3),0]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3),0]) ([1, 10, 1, 0], -1, NTL modulus 625) - sage: _test_preprocess_list(Zq(25,names='a'), [1/5,mod(2,625),Zp(5)(5,3),mod(0,3125)]) + sage: _test_preprocess_list(Zq(25,names='a',implementation="NTL"), [1/5,mod(2,625),Zp(5)(5,3),mod(0,3125)]) ([1, 10, 1, 0], -1, NTL modulus 625) """ return preprocess_list(R(0), L) @@ -702,13 +702,13 @@ def _find_val_aprec_test(R, L): sage: from sage.rings.padics.padic_ZZ_pX_element import _find_val_aprec_test sage: from sage.libs.ntl.all import ZZ as ntl_ZZ, ZZ_p as ntl_ZZ_p - sage: _find_val_aprec_test(Zq(25,names='a'), [15, int(75), ntl_ZZ(625)]) + sage: _find_val_aprec_test(Zq(25,names='a',implementation="NTL"), [15, int(75), ntl_ZZ(625)]) (1, 340282366920938463463374607431768211457, 2) - sage: _find_val_aprec_test(Zq(25,names='a'), [5, int(25), 7/25]) + sage: _find_val_aprec_test(Zq(25,names='a',implementation="NTL"), [5, int(25), 7/25]) (-2, 340282366920938463463374607431768211457, 1) - sage: _find_val_aprec_test(Zq(25,names='a'), [mod(4,125), Zp(5)(5,5), ntl_ZZ_p(16,625), 4/125]) + sage: _find_val_aprec_test(Zq(25,names='a',implementation="NTL"), [mod(4,125), Zp(5)(5,5), ntl_ZZ_p(16,625), 4/125]) (-3, 3, 0) - sage: _find_val_aprec_test(Zq(25,names='a'), [mod(25,125), Zp(5)(5,5), ntl_ZZ_p(15,625)]) + sage: _find_val_aprec_test(Zq(25,names='a',implementation="NTL"), [mod(25,125), Zp(5)(5,5), ntl_ZZ_p(15,625)]) (0, 3, 0) """ return find_val_aprec(R.prime_pow, L) @@ -779,42 +779,42 @@ def _test_get_val_prec(R, a): sage: from sage.rings.padics.padic_ZZ_pX_element import _test_get_val_prec sage: from sage.libs.ntl.all import ZZ as ntl_ZZ, ZZ_p as ntl_ZZ_p - sage: _test_get_val_prec(Zq(25,names='a'), 15) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), 15) (1, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), ntl_ZZ(15)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), ntl_ZZ(15)) (1, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), int(15)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), int(15)) (1, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), 1/15) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), 1/15) (-1, 340282366920938463463374607431768211457, 1) - sage: _test_get_val_prec(Zq(25,names='a'), Zp(5)(15,4)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), Zp(5)(15,4)) (0, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), Qp(5)(1/15,4)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), Qp(5)(1/15,4)) (-1, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), mod(15,625)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), mod(15,625)) (0, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), ntl_ZZ_p(15,625)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), ntl_ZZ_p(15,625)) (0, 4, 0) TESTS:: - sage: _test_get_val_prec(Zq(25,names='a'), 0) #indirect doctest + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), 0) #indirect doctest (340282366920938463463374607431768211457, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), ntl_ZZ(0)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), ntl_ZZ(0)) (340282366920938463463374607431768211457, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), int(0)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), int(0)) (340282366920938463463374607431768211457, 340282366920938463463374607431768211457, 2) - sage: _test_get_val_prec(Zq(25,names='a'), 0/1) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), 0/1) (340282366920938463463374607431768211457, 340282366920938463463374607431768211457, 1) - sage: _test_get_val_prec(Zq(25,names='a'), Zp(5)(25,4)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), Zp(5)(25,4)) (0, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), Qp(5)(1/25,4)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), Qp(5)(1/25,4)) (-2, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), Zp(5)(0)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), Zp(5)(0)) (340282366920938463463374607431768211457, 340282366920938463463374607431768211457, 1) - sage: _test_get_val_prec(Zq(25,names='a'), mod(0,625)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), mod(0,625)) (0, 4, 0) - sage: _test_get_val_prec(Zq(25,names='a'), ntl_ZZ_p(0,625)) + sage: _test_get_val_prec(Zq(25,names='a',implementation="NTL"), ntl_ZZ_p(0,625)) (0, 4, 0) """ return get_val_prec(R.prime_pow, a) diff --git a/src/sage/rings/padics/padic_base_generic.py b/src/sage/rings/padics/padic_base_generic.py index 08ab098135d..dd3e4e5c734 100644 --- a/src/sage/rings/padics/padic_base_generic.py +++ b/src/sage/rings/padics/padic_base_generic.py @@ -26,6 +26,7 @@ from sage.rings.padics.padic_fixed_mod_element import pAdicCoercion_ZZ_FM, pAdicConvert_QQ_FM class pAdicBaseGeneric(pAdicGeneric): + _implementation = 'GMP' def __init__(self, p, prec, print_mode, names, element_class): """ Initialization @@ -34,7 +35,7 @@ def __init__(self, p, prec, print_mode, names, element_class): sage: R = Zp(5) #indirect doctest """ - self.prime_pow = PowComputer(p, max(min(prec - 1, 30), 1), prec, self.is_field()) + self.prime_pow = PowComputer(p, max(min(prec - 1, 30), 1), prec, self.is_field(), self._prec_type()) pAdicGeneric.__init__(self, self, p, prec, print_mode, names, element_class) if self.is_field(): coerce_list = [pAdicCoercion_ZZ_CR(self), pAdicCoercion_QQ_CR(self)] diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pxd b/src/sage/rings/padics/padic_capped_absolute_element.pxd index 74f1ec4dae0..a86ce1a5c2a 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pxd +++ b/src/sage/rings/padics/padic_capped_absolute_element.pxd @@ -1,5 +1,6 @@ from sage.libs.gmp.types cimport mpz_t from sage.libs.pari.gen cimport gen as pari_gen +from sage.rings.padics.padic_capped_relative_element cimport CRElement ctypedef mpz_t celement include "CA_template_header.pxi" @@ -7,3 +8,7 @@ include "CA_template_header.pxi" cdef class pAdicCappedAbsoluteElement(CAElement): cdef lift_c(self) cdef pari_gen _to_gen(self) + +from sage.rings.padics.pow_computer cimport PowComputer_base +cdef class PowComputer_(PowComputer_base): + pass diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index dccebcbbe25..5dbf7270aa4 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -28,6 +28,25 @@ from sage.libs.pari.pari_instance cimport PariInstance cdef PariInstance P = sage.libs.pari.pari_instance.pari from sage.rings.finite_rings.integer_mod import Mod +cdef class PowComputer_(PowComputer_base): + """ + A PowComputer for a capped-absolute padic ring. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field): + """ + Initialization. + + EXAMPLES:: + + sage: R = ZpCA(5) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'capped-abs' + """ + self._prec_type = 'capped-abs' + PowComputer_base.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field) + cdef class pAdicCappedAbsoluteElement(CAElement): """ Constructs new element with given parent and value. diff --git a/src/sage/rings/padics/padic_capped_relative_element.pxd b/src/sage/rings/padics/padic_capped_relative_element.pxd index 05298020e8f..e72b96dea2a 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pxd +++ b/src/sage/rings/padics/padic_capped_relative_element.pxd @@ -7,3 +7,7 @@ include "CR_template_header.pxi" cdef class pAdicCappedRelativeElement(CRElement): cdef lift_c(self) cdef pari_gen _to_gen(self) + +from sage.rings.padics.pow_computer cimport PowComputer_base +cdef class PowComputer_(PowComputer_base): + pass diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index 09669ebd4a6..e1fc71aa8db 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -27,6 +27,26 @@ include "CR_template.pxi" from sage.libs.pari.pari_instance cimport PariInstance cdef PariInstance P = sage.libs.pari.pari_instance.pari from sage.rings.finite_rings.integer_mod import Mod +from sage.rings.padics.pow_computer cimport PowComputer_class + +cdef class PowComputer_(PowComputer_base): + """ + A PowComputer for a capped-relative padic ring or field. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field): + """ + Initialization. + + EXAMPLES:: + + sage: R = ZpCR(5) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'capped-rel' + """ + self._prec_type = 'capped-rel' + PowComputer_base.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field) cdef class pAdicCappedRelativeElement(CRElement): """ @@ -83,14 +103,14 @@ cdef class pAdicCappedRelativeElement(CRElement): sage: R(Integers(49)(3)) Traceback (most recent call last): ... - TypeError: cannot coerce from the given integer mod ring (not a power of the same prime) + TypeError: p does not divide modulus 49 :: sage: R(Integers(48)(3)) Traceback (most recent call last): ... - TypeError: cannot coerce from the given integer mod ring (not a power of the same prime) + TypeError: p does not divide modulus 48 Some other conversions:: diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index 2b29ac84752..cb7308a8de6 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -436,7 +436,7 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: a.residue(2) Traceback (most recent call last): ... - NotImplementedError: residue() not implemented in extensions for absprec larger than one. + NotImplementedError: reduction modulo p^n with n>1. Eisenstein case:: @@ -472,7 +472,7 @@ cdef class pAdicExtElement(pAdicGenericElement): sage: a.residue(16) Traceback (most recent call last): ... - PrecisionError: not enough precision known in order to compute residue. + PrecisionError: insufficient precision to reduce modulo p^16. """ if absprec < 0: diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 196b5ffc105..31bbfb1b7c3 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -72,7 +72,13 @@ def _coerce_map_from_(self, R): """ # Far more functionality needs to be added here later. if isinstance(R, pAdicExtensionGeneric) and R.fraction_field() is self: - return True + if self._implementation == 'NTL': + return True + elif R._prec_type() == 'capped-abs': + from sage.rings.padics.qadic_flint_CA import pAdicCoercion_CA_frac_field as coerce_map + elif R._prec_type() == 'capped-rel': + from sage.rings.padics.qadic_flint_CR import pAdicCoercion_CR_frac_field as coerce_map + return coerce_map(R, self) def __cmp__(self, other): """ @@ -259,9 +265,9 @@ def fraction_field(self, print_mode=None): #we don't want to set the print options due to the ground ring since #different extension fields (with different options) can share the same ground ring. if self.is_lazy(): - return K.extension(self._pre_poly, prec = self.precision_cap(), halt = self.halting_parameter(), res_name = self.residue_field().variable_name(), print_mode=print_mode) + return K.extension(self._pre_poly, prec = self.precision_cap(), halt = self.halting_parameter(), res_name = self.residue_field().variable_name(), print_mode=print_mode, implementation=self._implementation) else: - return K.extension(self._pre_poly, prec = self.precision_cap(), res_name = self.residue_field().variable_name(), print_mode=print_mode) + return K.extension(self._pre_poly, prec = self.precision_cap(), res_name = self.residue_field().variable_name(), print_mode=print_mode, implementation=self._implementation) def integer_ring(self, print_mode=None): r""" diff --git a/src/sage/rings/padics/padic_extension_leaves.py b/src/sage/rings/padics/padic_extension_leaves.py index 644a715cd77..835f6e7e040 100644 --- a/src/sage/rings/padics/padic_extension_leaves.py +++ b/src/sage/rings/padics/padic_extension_leaves.py @@ -20,7 +20,11 @@ # http://www.gnu.org/licenses/ #***************************************************************************** +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.rings.finite_rings.integer_mod_ring import Zmod from pow_computer_ext import PowComputer_ext_maker +from pow_computer_flint import PowComputer_flint_maker from sage.libs.ntl.ntl_ZZ_pX import ntl_ZZ_pX from unramified_extension_generic import UnramifiedExtensionGeneric @@ -45,7 +49,45 @@ from padic_ZZ_pX_FM_element import pAdicZZpXFMElement from padic_ZZ_pX_CR_element import pAdicZZpXCRElement from padic_ZZ_pX_CA_element import pAdicZZpXCAElement +from qadic_flint_CR import qAdicCappedRelativeElement +from qadic_flint_CA import qAdicCappedAbsoluteElement +from qadic_flint_FM import qAdicFixedModElement +def _make_integral_poly(prepoly, p, prec): + """ + Converts a defining polynomial into one with integral coefficients. + + INPUTS: + + - ``prepoly`` - a univariate polynomial or symbolic expression + + - ``p`` -- a prime + + - ``prec`` -- the precision + + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_leaves import _make_integral_poly + sage: R. = QQ[] + sage: f = _make_integral_poly(x^2 - 2, 5, 3); f + x^2 - 2 + sage: f.parent() + Univariate Polynomial Ring in x over Integer Ring + sage: f = _make_integral_poly(x^2 - 2/7, 5, 3); f + x^2 + 89 + sage: f.parent() + Univariate Polynomial Ring in x over Integer Ring + """ + try: + Zpoly = prepoly.change_ring(ZZ) + except AttributeError: + # should be a symoblic expression + Zpoly = prepoly.polynomial(QQ) + except (TypeError, ValueError): + Zpoly = prepoly.change_ring(QQ) + if Zpoly.base_ring() is not ZZ: + Zpoly = Zpoly.change_ring(Zmod(p**prec)).change_ring(ZZ) + return Zpoly class UnramifiedExtensionRingCappedRelative(UnramifiedExtensionGeneric, pAdicCappedRelativeRingGeneric): """ @@ -54,7 +96,7 @@ class UnramifiedExtensionRingCappedRelative(UnramifiedExtensionGeneric, pAdicCap sage: R. = ZqCR(27,10000); R == loads(dumps(R)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A capped relative representation of Zq. @@ -86,14 +128,26 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): sage: R. = ZqCR(next_prime(10^30)^3, 3); R.prime() 1000000000000000000000000000057 """ - ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) - if prec <= 30: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, False, ntl_poly, "small", "u") - else: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, False, ntl_poly, "big", "u") self._shift_seed = None self._pre_poly = prepoly - UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) + self._implementation = implementation + if implementation == 'NTL': + ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) + if prec <= 30: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, False, ntl_poly, "small", "u") + else: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, False, ntl_poly, "big", "u") + element_class = pAdicZZpXCRElement + else: + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = min(prec, 30) + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, False, Zpoly, prec_type='capped-rel') + element_class = qAdicCappedRelativeElement + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, element_class) + if implementation != 'NTL': + from qadic_flint_CR import pAdicCoercion_ZZ_CR, pAdicConvert_QQ_CR + self.register_coercion(pAdicCoercion_ZZ_CR(self)) + self.register_conversion(pAdicConvert_QQ_CR(self)) class UnramifiedExtensionFieldCappedRelative(UnramifiedExtensionGeneric, pAdicCappedRelativeFieldGeneric): """ @@ -102,7 +156,7 @@ class UnramifiedExtensionFieldCappedRelative(UnramifiedExtensionGeneric, pAdicCa sage: R. = QqCR(27,10000); R == loads(dumps(R)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A representation of Qq. @@ -135,14 +189,26 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): 1000000000000000000000000000057 """ # Currently doesn't support polynomials with non-integral coefficients - ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) - if prec <= 30: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, True, ntl_poly, "small", "u") - else: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, True, ntl_poly, "big", "u") self._shift_seed = None self._pre_poly = prepoly - UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) + self._implementation = implementation + if implementation == 'NTL': + ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) + if prec <= 30: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, True, ntl_poly, "small", "u") + else: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, True, ntl_poly, "big", "u") + element_class = pAdicZZpXCRElement + else: + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = min(prec, 30) + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, True, Zpoly, prec_type='capped-rel') + element_class = qAdicCappedRelativeElement + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, element_class) + if implementation != 'NTL': + from qadic_flint_CR import pAdicCoercion_ZZ_CR, pAdicCoercion_QQ_CR + self.register_coercion(pAdicCoercion_ZZ_CR(self)) + self.register_coercion(pAdicCoercion_QQ_CR(self)) #class UnramifiedExtensionRingLazy(UnramifiedExtensionGeneric, pAdicLazyRingGeneric): # def __init__(self, poly, prec, halt, print_mode, names): @@ -161,7 +227,7 @@ class UnramifiedExtensionRingCappedAbsolute(UnramifiedExtensionGeneric, pAdicCap sage: R. = ZqCA(27,10000); R == loads(dumps(R)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A capped absolute representation of Zq. @@ -194,14 +260,26 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): 1000000000000000000000000000057 """ # Currently doesn't support polynomials with non-integral coefficients - ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) - if prec <= 30: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, True, ntl_poly, "small", "u") - else: - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, True, ntl_poly, "big", "u") self._shift_seed = None self._pre_poly = prepoly - UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCAElement) + self._implementation = implementation + if implementation == 'NTL': + ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) + if prec <= 30: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), prec, prec, prec, True, ntl_poly, "small", "u") + else: + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, prec, prec, True, ntl_poly, "big", "u") + element_class = pAdicZZpXCAElement + else: + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = min(prec, 30) + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, False, Zpoly, prec_type='capped-abs') + element_class = qAdicCappedAbsoluteElement + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, element_class) + if implementation != 'NTL': + from qadic_flint_CA import pAdicCoercion_ZZ_CA, pAdicConvert_QQ_CA + self.register_coercion(pAdicCoercion_ZZ_CA(self)) + self.register_conversion(pAdicConvert_QQ_CA(self)) class UnramifiedExtensionRingFixedMod(UnramifiedExtensionGeneric, pAdicFixedModRingGeneric): """ @@ -210,7 +288,7 @@ class UnramifiedExtensionRingFixedMod(UnramifiedExtensionGeneric, pAdicFixedModR sage: R. = ZqFM(27,10000); R == loads(dumps(R)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A fixed modulus representation of Zq. @@ -242,11 +320,23 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): sage: R. = ZqFM(next_prime(10^30)^3, 3); R.prime() 1000000000000000000000000000057 """ - ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) - self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), max(min(prec - 1, 30), 1), prec, prec, False, ntl_poly, "FM", "u") self._shift_seed = None self._pre_poly = prepoly - UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXFMElement) + self._implementation = implementation + if implementation == 'NTL': + ntl_poly = ntl_ZZ_pX([a.lift() for a in poly.list()], poly.base_ring().prime()**prec) + self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), max(min(prec - 1, 30), 1), prec, prec, False, ntl_poly, "FM", "u") + element_class = pAdicZZpXFMElement + else: + Zpoly = _make_integral_poly(prepoly, poly.base_ring().prime(), prec) + cache_limit = 0 # prevents caching + self.prime_pow = PowComputer_flint_maker(poly.base_ring().prime(), cache_limit, prec, prec, False, Zpoly, prec_type='fixed-mod') + element_class = qAdicFixedModElement + UnramifiedExtensionGeneric.__init__(self, poly, prec, print_mode, names, element_class) + if implementation != 'NTL': + from qadic_flint_FM import pAdicCoercion_ZZ_FM, pAdicConvert_QQ_FM + self.register_coercion(pAdicCoercion_ZZ_FM(self)) + self.register_conversion(pAdicConvert_QQ_FM(self)) #def coerce_map_explicit(self, S): # from sage.rings.padics.morphism import Morphism_ZZ_UnrFM, Morphism_ZpFM_UnrFM @@ -264,7 +354,7 @@ class EisensteinExtensionRingCappedRelative(EisensteinExtensionGeneric, pAdicCap sage: W. = R.ext(f); W == loads(dumps(W)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A capped relative representation of an eisenstein extension of Zp. @@ -311,6 +401,7 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, unram_prec, prec, False, ntl_poly, "big", "e", shift_poly) self._shift_seed = shift_seed self._pre_poly = prepoly + self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) class EisensteinExtensionFieldCappedRelative(EisensteinExtensionGeneric, pAdicCappedRelativeFieldGeneric): @@ -321,7 +412,7 @@ class EisensteinExtensionFieldCappedRelative(EisensteinExtensionGeneric, pAdicCa sage: W. = R.ext(f); W == loads(dumps(W)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A capped relative representation of an eisenstein extension of Qp. @@ -369,6 +460,7 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, unram_prec, prec, True, ntl_poly, "big", "e", shift_poly) self._shift_seed = shift_seed self._pre_poly = prepoly + self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCRElement) #class EisensteinExtensionRingLazy(EisensteinExtensionGeneric, pAdicLazyRingGeneric): @@ -389,7 +481,7 @@ class EisensteinExtensionRingCappedAbsolute(EisensteinExtensionGeneric, pAdicCap sage: W. = R.ext(f); W == loads(dumps(W)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation): """ A capped absolute representation of an eisenstein extension of Zp. @@ -436,6 +528,7 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), 30, unram_prec, prec, False, ntl_poly, "big", "e", shift_poly) self._shift_seed = shift_seed self._pre_poly = prepoly + self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXCAElement) class EisensteinExtensionRingFixedMod(EisensteinExtensionGeneric, pAdicFixedModRingGeneric): @@ -446,7 +539,7 @@ class EisensteinExtensionRingFixedMod(EisensteinExtensionGeneric, pAdicFixedModR sage: W. = R.ext(f); W == loads(dumps(W)) True """ - def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): + def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names, implementation='NTL'): """ A fixed modulus representation of an eisenstein extension of Zp. @@ -492,6 +585,7 @@ def __init__(self, prepoly, poly, prec, halt, print_mode, shift_seed, names): self.prime_pow = PowComputer_ext_maker(poly.base_ring().prime(), max(min(unram_prec - 1, 30), 1), unram_prec, prec, False, ntl_poly, "FM", "e", shift_poly) self._shift_seed = shift_seed self._pre_poly = prepoly + self._implementation = implementation EisensteinExtensionGeneric.__init__(self, poly, prec, print_mode, names, pAdicZZpXFMElement) #def coerce_map_explicit(self, S): diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pxd b/src/sage/rings/padics/padic_fixed_mod_element.pxd index 159f15993d0..a5749a9e963 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pxd +++ b/src/sage/rings/padics/padic_fixed_mod_element.pxd @@ -7,3 +7,7 @@ include "FM_template_header.pxi" cdef class pAdicFixedModElement(FMElement): cdef lift_c(self) cdef pari_gen _to_gen(self) + +from sage.rings.padics.pow_computer cimport PowComputer_base +cdef class PowComputer_(PowComputer_base): + pass diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 15b1e83acae..c6dbe43aa36 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -28,6 +28,25 @@ from sage.libs.pari.pari_instance cimport PariInstance cdef PariInstance P = sage.libs.pari.pari_instance.pari from sage.rings.finite_rings.integer_mod import Mod +cdef class PowComputer_(PowComputer_base): + """ + A PowComputer for a fixed-modulus padic ring. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field): + """ + Initialization. + + EXAMPLES:: + + sage: R = ZpFM(5) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'fixed-mod' + """ + self._prec_type = 'fixed-mod' + PowComputer_base.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field) + cdef class pAdicFixedModElement(FMElement): r""" INPUT: @@ -99,12 +118,12 @@ cdef class pAdicFixedModElement(FMElement): sage: R(Integers(49)(3)) Traceback (most recent call last): ... - TypeError: cannot coerce from the given integer mod ring (not a power of the same prime) + TypeError: p does not divide modulus 49 sage: R(Integers(48)(3)) Traceback (most recent call last): ... - TypeError: cannot coerce from the given integer mod ring (not a power of the same prime) + TypeError: p does not divide modulus 48 Some other conversions:: diff --git a/src/sage/rings/padics/padic_generic.py b/src/sage/rings/padics/padic_generic.py index 9406ddf4282..19c9122207e 100644 --- a/src/sage/rings/padics/padic_generic.py +++ b/src/sage/rings/padics/padic_generic.py @@ -73,11 +73,18 @@ def some_elements(self): EXAMPLES:: - sage: Zp(2).some_elements() - [0, 1 + O(2^20), 2 + O(2^21)] - - """ - return [self.zero(), self.one(), self(self.prime())] + sage: Zp(2,4).some_elements() + [0, 1 + O(2^4), 2 + O(2^5), 1 + 2^2 + 2^3 + O(2^4), 2 + 2^2 + 2^3 + 2^4 + O(2^5)] + """ + p = self(self.prime()) + a = self.gen() + one = self.one() + L = [self.zero(), one, p, (one+p+p).inverse_of_unit(), p-p**2] + if a != p: + L.extend([a, (one + a + p).inverse_of_unit()]) + if self.is_field(): + L.extend([~(p-p-a),p**(-20)]) + return L def _modified_print_mode(self, print_mode): """ @@ -453,7 +460,7 @@ def teichmuller_system(self): # """ # raise NotImplementedError - def extension(self, modulus, prec = None, names = None, print_mode = None, halt = None, **kwds): + def extension(self, modulus, prec = None, names = None, print_mode = None, halt = None, implementation='FLINT', **kwds): """ Create an extension of this p-adic ring. @@ -492,7 +499,7 @@ def extension(self, modulus, prec = None, names = None, print_mode = None, halt print_mode[option] = kwds[option] else: print_mode[option] = self._printer.dict()[option] - return ExtensionFactory(base=self, premodulus=modulus, prec=prec, halt=halt, names=names, check = True, **print_mode) + return ExtensionFactory(base=self, premodulus=modulus, prec=prec, halt=halt, names=names, check = True, implementation=implementation, **print_mode) def _test_add(self, **options): """ @@ -500,7 +507,7 @@ def _test_add(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -536,7 +543,7 @@ def _test_sub(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -572,7 +579,7 @@ def _test_invert(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -607,7 +614,7 @@ def _test_mul(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -634,7 +641,7 @@ def _test_div(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -666,7 +673,7 @@ def _test_neg(self, **options): INPUT: - - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. EXAMPLES:: @@ -687,6 +694,37 @@ def _test_neg(self, **options): tester.assertEqual(x.is_zero(),y.is_zero()) tester.assertEqual(x.is_unit(),y.is_unit()) + def _test_teichmuller(self, **options): + """ + Test Teichmuller lifts. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester`. + + EXAMPLES:: + + sage: Zp(3)._test_teichmuller() + + .. SEEALSO:: + + :class:`TestSuite` + + """ + tester = self._tester(**options) + + for x in tester.some_elements(): + try: + y = self.teichmuller(x) + except ValueError: + tester.assertTrue(x.valuation() < 0 or x.precision_absolute()==0) + else: + try: + tester.assertEqual(x.residue(), y.residue()) + except (NotImplementedError, AttributeError): + pass + tester.assertEqual(y**self.residue_field().order(), y) + @cached_method def _log_unit_part_p(self): """ diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index f4464b036fa..ad647c3b2ce 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -27,8 +27,7 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" - +from sage.ext.stdsage cimport PY_NEW cimport sage.rings.padics.local_generic_element from sage.libs.gmp.mpz cimport mpz_set_si from sage.rings.padics.local_generic_element cimport LocalGenericElement @@ -1722,9 +1721,9 @@ cdef class pAdicGenericElement(LocalGenericElement): .. TODO:: - There is a soft-linear time algorith for logarithm described - by Dan Berstein at - http://cr.yp.to/lineartime/multapps-20041007.pdf + There is a soft-linear time algorithm for logarithm described + by Dan Berstein at + http://cr.yp.to/lineartime/multapps-20041007.pdf ALGORITHM: diff --git a/src/sage/rings/padics/padic_printing.pyx b/src/sage/rings/padics/padic_printing.pyx index 1e58a01d0da..98822e889d0 100644 --- a/src/sage/rings/padics/padic_printing.pyx +++ b/src/sage/rings/padics/padic_printing.pyx @@ -992,23 +992,28 @@ cdef class pAdicPrinter_class(SageObject): s += self._plus_ellipsis(do_latex) else: # not self.base if mode == terse: - if elt.parent().is_capped_relative(): - poly, k = elt._ntl_rep_abs() - s = repr(poly) + if elt.parent()._implementation == 'FLINT': + poly, k = elt._flint_rep_abs() + L = [repr(a) for a in poly.coefficients(sparse=False)] + ZZ_pEX = 1 else: - s = repr(elt._ntl_rep()) - k = 0 - L = s.split("] [") # this splits a ZZ_pEX into the ZZ_pX components - ZZ_pEX = L[0].count("[") # will equal 2 if elt was a ZZ_pEX element, 1 if it was a ZZ_pX element - L[0] = L[0].replace("[","") - L[-1] = L[-1].replace("]","") + if elt.parent().is_capped_relative(): + poly, k = elt._ntl_rep_abs() + s = repr(poly) + else: + s = repr(elt._ntl_rep()) + k = 0 + L = s.split("] [") # this splits a ZZ_pEX into the ZZ_pX components + ZZ_pEX = L[0].count("[") # will equal 2 if elt was a ZZ_pEX element, 1 if it was a ZZ_pX element + L[0] = L[0].replace("[","") + L[-1] = L[-1].replace("]","") + L = L[0].split() if ZZ_pEX == 2: L = [a.split() for a in L] L = [[("" if b == "0" else b) for b in a] for a in L] L, ellipsis = self._truncate_list(L, self.max_ram_terms, "") raise NotImplementedError else: - L = L[0].split() L = [("" if b == "0" else b) for b in L] L, ellipsis = self._truncate_list(L, self.max_terse_terms, "") s = "" diff --git a/src/sage/rings/padics/padic_template_element.pxi b/src/sage/rings/padics/padic_template_element.pxi index 84739618a43..0eb8187096c 100644 --- a/src/sage/rings/padics/padic_template_element.pxi +++ b/src/sage/rings/padics/padic_template_element.pxi @@ -89,8 +89,24 @@ cdef class pAdicTemplateElement(pAdicGenericElement): sage: type(a) sage: TestSuite(a).run() + + TESTS:: + + sage: QQq. = Qq(25,4) + sage: FFp = Zp(5,5).residue_field() + sage: QQq(FFp.zero()) + O(5) + sage: QQq(FFp.one()) + 1 + O(5) + sage: QQq(IntegerModRing(25)(15)) + 3*5 + O(5^2) + sage: QQq(IntegerModRing(9)(0)) + Traceback (most recent call last): + ... + TypeError: p does not divide modulus 9 + """ - self.prime_pow = parent.prime_pow + self.prime_pow = parent.prime_pow pAdicGenericElement.__init__(self, parent) cdef long val, xprec cdef GEN pari_tmp @@ -103,11 +119,26 @@ cdef class pAdicTemplateElement(pAdicGenericElement): INT_to_mpz((x).value, pari_tmp) elif typ(pari_tmp) == t_FRAC: x = Rational(x) - elif not (isinstance(x, Integer) or \ - isinstance(x, Rational) or \ - isinstance(x, pAdicGenericElement) or \ - isinstance(x, pari_gen) or \ - sage.rings.finite_rings.integer_mod.is_IntegerMod(x)): + elif isinstance(x, pAdicGenericElement): + if not ((x)._is_base_elt(self.prime_pow.prime) or x.parent() is self.parent()): + raise NotImplementedError("conversion between padic extensions not implemented") + elif sage.rings.finite_rings.integer_mod.is_IntegerMod(x): + if not Integer(self.prime_pow.prime).divides(x.parent().order()): + raise TypeError("p does not divide modulus %s"%x.parent().order()) + elif sage.rings.finite_rings.element_base.is_FiniteFieldElement(x): + k = self.parent().residue_field() + if not k.has_coerce_map_from(x.parent()): + raise NotImplementedError("conversion from finite fields which do not embed into the residue field not implemented.") + + x = k(x) + if not k.is_prime_field(): + x = [k.prime_subfield()(c) for c in x.polynomial().list()] + x = x + [k.prime_subfield().zero()] * (k.degree() - len(x)) + elif isinstance(x, (Integer, Rational, list, tuple)): + pass + elif sage.rings.polynomial.polynomial_element.is_Polynomial(x) and x.variable_name() == self.parent().variable_name(): + x = x.list() + else: x = Rational(x) val = get_ordp(x, self.prime_pow) if val < 0 and self.prime_pow.in_field == 0: @@ -365,6 +396,12 @@ cdef class pAdicTemplateElement(pAdicGenericElement): n -= self.valuation() return L[:n] + [zero] * (n - len(L)) + def _ext_p_list(self, pos): + if pos: + return self.unit_part().list('simple') + else: + return self.unit_part().list('smallest') + cpdef pAdicTemplateElement unit_part(self): """ Returns the unit part of this element. @@ -394,7 +431,88 @@ cdef class pAdicTemplateElement(pAdicGenericElement): """ return self.prime_pow.prime == p and self.prime_pow.deg == 1 -cdef Integer exact_pow_helper(long *ansrelprec, long relprec, _right, PowComputer_class prime_pow): + def _prime_pow(self): + """ + Provides access to this element's ``prime_pow``. + + EXAMPLES:: + + sage: R = ZpCR(5,5) + sage: R(1)._prime_pow() + PowComputer for 5 + """ + return self.prime_pow + + def residue(self, absprec=1): + r""" + Reduce this element modulo `p^\mathrm{absprec}`. + + INPUT: + + - ``absprec`` -- ``0`` or ``1``. + + OUTPUT: + + This element reduced modulo `p^\mathrm{absprec}` as an element of the + residue field or the null ring. + + EXAMPLES:: + + sage: R. = ZqFM(27, 4) + sage: (3 + 3*a).residue() + 0 + sage: (a + 1).residue() + a0 + 1 + + TESTS:: + + sage: a.residue(0) + 0 + sage: a.residue(2) + Traceback (most recent call last): + ... + NotImplementedError: reduction modulo p^n with n>1. + sage: a.residue(10) + Traceback (most recent call last): + ... + PrecisionError: insufficient precision to reduce modulo p^10. + + sage: R. = ZqCA(27, 4) + sage: (3 + 3*a).residue() + 0 + sage: (a + 1).residue() + a0 + 1 + + sage: R. = Qq(27, 4) + sage: (3 + 3*a).residue() + 0 + sage: (a + 1).residue() + a0 + 1 + sage: (a/3).residue() + Traceback (most recent call last): + ... + ValueError: element must have non-negative valuation in order to compute residue. + + """ + if absprec < 0: + raise ValueError("cannot reduce modulo a negative power of the uniformizer.") + if self.valuation() < 0: + raise ValueError("element must have non-negative valuation in order to compute residue.") + if absprec > self.precision_absolute(): + raise PrecisionError("insufficient precision to reduce modulo p^%s."%absprec) + if absprec == 0: + from sage.rings.all import IntegerModRing + return IntegerModRing(1).zero() + elif absprec == 1: + parent = self.parent().residue_field() + if self.valuation() > 0: + return parent.zero() + digits = self.padded_list(1) + return parent(digits[0]) + else: + raise NotImplementedError("reduction modulo p^n with n>1.") + +cdef Integer exact_pow_helper(long *ansrelprec, long relprec, _right, PowComputer_ prime_pow): """ This function is used by exponentiation in both CR_template.pxi and CA_template.pxi to determine the extra precision gained from @@ -434,7 +552,7 @@ cdef Integer exact_pow_helper(long *ansrelprec, long relprec, _right, PowCompute return right cdef long padic_pow_helper(celement result, celement base, long base_val, long base_relprec, - celement right_unit, long right_val, long right_relprec, PowComputer_class prime_pow) except -1: + celement right_unit, long right_val, long right_relprec, PowComputer_ prime_pow) except -1: """ INPUT: diff --git a/src/sage/rings/padics/padic_template_element_header.pxi b/src/sage/rings/padics/padic_template_element_header.pxi index 14cdcf7fa32..0b3002b4ad3 100644 --- a/src/sage/rings/padics/padic_template_element_header.pxi +++ b/src/sage/rings/padics/padic_template_element_header.pxi @@ -32,10 +32,9 @@ AUTHORS: from sage.structure.element cimport ModuleElement, RingElement from sage.rings.padics.padic_generic_element cimport pAdicGenericElement -from sage.rings.padics.pow_computer cimport PowComputer_class cdef class pAdicTemplateElement(pAdicGenericElement): - cdef PowComputer_class prime_pow + cdef PowComputer_ prime_pow cdef int _set(self, x, long val, long xprec, absprec, relprec) except -1 cdef pAdicTemplateElement _lshift_c(self, long shift) cdef pAdicTemplateElement _rshift_c(self, long shift) diff --git a/src/sage/rings/padics/pow_computer.pxd b/src/sage/rings/padics/pow_computer.pxd index 8563d37418e..7d754e8a28c 100644 --- a/src/sage/rings/padics/pow_computer.pxd +++ b/src/sage/rings/padics/pow_computer.pxd @@ -5,7 +5,8 @@ from sage.rings.integer cimport Integer cdef class PowComputer_class(SageObject): cdef Integer prime cdef bint in_field - cdef int _initialized + cdef int __allocated + cdef public object _prec_type # the following three should be set by the subclasses cdef long ram_prec_cap # = prec_cap * e @@ -16,11 +17,10 @@ cdef class PowComputer_class(SageObject): cdef unsigned long cache_limit cdef unsigned long prec_cap - cdef mpz_t temp_m - cdef Integer pow_Integer(self, long n) cdef mpz_srcptr pow_mpz_t_top(self) - cdef mpz_srcptr pow_mpz_t_tmp(self, long n) + cdef mpz_srcptr pow_mpz_t_tmp(self, unsigned long n) + cdef mpz_t temp_m cdef class PowComputer_base(PowComputer_class): cdef mpz_t* small_powers diff --git a/src/sage/rings/padics/pow_computer.pyx b/src/sage/rings/padics/pow_computer.pyx index 92039d32f5a..fda891959d3 100644 --- a/src/sage/rings/padics/pow_computer.pyx +++ b/src/sage/rings/padics/pow_computer.pyx @@ -36,15 +36,16 @@ import weakref from sage.rings.infinity import infinity from sage.libs.gmp.mpz cimport * +from sage.ext.stdsage cimport PY_NEW include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" cdef long maxpreccap = (1L << (sizeof(long) * 8 - 2)) - 1 cdef class PowComputer_class(SageObject): def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed=None): """ - Creates a new PowComputer_class. + Memory allocation. EXAMPLES:: @@ -52,10 +53,10 @@ cdef class PowComputer_class(SageObject): sage: PC.pow_Integer_Integer(2) 9 """ - self.prime = prime - self.in_field = in_field - self.cache_limit = cache_limit - self.prec_cap = prec_cap + sig_on() + mpz_init(self.temp_m) + sig_off() + self.__allocated = 1 def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed=None): """ @@ -86,7 +87,11 @@ cdef class PowComputer_class(SageObject): sage: PC.pow_Integer_Integer(2) 9 """ - self._initialized = 1 + self.prime = prime + self.in_field = in_field + self.cache_limit = cache_limit + self.prec_cap = prec_cap + self.ram_prec_cap = ram_prec_cap def __cmp__(self, other): """ @@ -181,7 +186,7 @@ cdef class PowComputer_class(SageObject): raise ValueError, "result too big" return self.pow_Integer(mpz_get_ui(_n.value)) - cdef mpz_srcptr pow_mpz_t_tmp(self, long n): + cdef mpz_srcptr pow_mpz_t_tmp(self, unsigned long n): """ Provides fast access to an ``mpz_srcptr`` pointing to self.prime^n. @@ -206,11 +211,12 @@ cdef class PowComputer_class(SageObject): sage: PC = PowComputer(5, 5, 10) - When you cal pow_mpz_t_tmp with an input that is not stored - (ie n > self.cache_limit and n != self.prec_cap), - it stores the result in self.temp_m and returns a pointer - to that mpz_t. So if you try to use the results of two - calls at once, things will break. + When you call pow_mpz_t_tmp with an input that is not stored + (ie n > self.cache_limit and n != self.prec_cap), + it stores the result in self.temp_m and returns a pointer + to that mpz_t. So if you try to use the results of two + calls at once, things will break. :: + sage: PC._pow_mpz_t_tmp_demo(6, 8) # 244140625 on some architectures and 152587890625 on others: random 244140625 sage: 5^6*5^8 @@ -218,9 +224,10 @@ cdef class PowComputer_class(SageObject): sage: 5^6*5^6 244140625 - Note that this does not occur if you try a stored value, - because the result of one of the calls points to that - stored value. + Note that this does not occur if you try a stored value, + because the result of one of the calls points to that + stored value. :: + sage: PC._pow_mpz_t_tmp_demo(6, 10) 152587890625 sage: 5^6*5^10 @@ -229,7 +236,7 @@ cdef class PowComputer_class(SageObject): m = Integer(m) n = Integer(n) if m < 0 or n < 0: - raise ValueError, "m, n must be non-negative" + raise ValueError("m, n must be non-negative") cdef Integer ans = PY_NEW(Integer) mpz_mul(ans.value, self.pow_mpz_t_tmp(mpz_get_ui((m).value)), self.pow_mpz_t_tmp(mpz_get_ui((n).value))) return ans @@ -293,7 +300,7 @@ cdef class PowComputer_class(SageObject): mpz_set(ans.value, self.pow_mpz_t_top()) return ans - def __repr__(self): + def _repr_(self): """ Returns a string representation of self. @@ -403,7 +410,7 @@ cdef class PowComputer_class(SageObject): else: _n = n if mpz_fits_slong_p(_n.value) == 0: - raise ValueError, "n too big" + raise ValueError("n too big") if _n < 0: return ~self.pow_Integer(-mpz_get_si(_n.value)) else: @@ -412,39 +419,70 @@ cdef class PowComputer_class(SageObject): cdef class PowComputer_base(PowComputer_class): def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed=None): """ - Initializes a PowComputer_base. + Allocates a PowComputer_base. EXAMPLES:: sage: PC = PowComputer(5, 7, 10) sage: PC(3) 125 + """ - (self)._initialized = 0 + cdef Py_ssize_t i + sig_on() - self.small_powers = sage_malloc(sizeof(mpz_t) * (cache_limit + 1)) - sig_off() - if self.small_powers == NULL: - raise MemoryError, "out of memory allocating power storing" - mpz_init(self.top_power) - mpz_init(self.temp_m) + try: + self.small_powers = sig_malloc(sizeof(mpz_t) * (cache_limit + 1)) + if self.small_powers == NULL: + raise MemoryError("out of memory allocating power storing") + try: + mpz_init(self.top_power) + try: + for i in range(cache_limit + 1): + try: + mpz_init(self.small_powers[i]) + except BaseException: + while i: + i-=1 + mpz_clear(self.small_powers[i]) + raise + except BaseException: + mpz_clear(self.top_power) + raise + except BaseException: + sig_free(self.small_powers) + raise + finally: + sig_off() + + self.__allocated = 2 + + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed=None): + """ + Initialization. + + TESTS:: + + sage: PC = PowComputer(5, 7, 10) + sage: PC(3) + 125 + + """ + PowComputer_class.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly, shift_seed) cdef Py_ssize_t i cdef Integer x - mpz_init_set_ui(self.small_powers[0], 1) + mpz_set_ui(self.small_powers[0], 1) if cache_limit > 0: - mpz_init_set(self.small_powers[1], prime.value) - - for i from 2 <= i <= cache_limit: - mpz_init(self.small_powers[i]) + mpz_set(self.small_powers[1], prime.value) + for i in range(2, cache_limit + 1): mpz_mul(self.small_powers[i], self.small_powers[i - 1], prime.value) mpz_pow_ui(self.top_power, prime.value, prec_cap) self.deg = 1 self.e = 1 self.f = 1 self.ram_prec_cap = prec_cap - (self)._initialized = 1 def __dealloc__(self): """ @@ -458,13 +496,13 @@ cdef class PowComputer_base(PowComputer_class): PowComputer for 5 """ cdef Py_ssize_t i - if (self)._initialized: - for i from 0 <= i <= self.cache_limit: + + if self.__allocated >= 2: + for i in range(self.cache_limit + 1): mpz_clear(self.small_powers[i]) - sage_free(self.small_powers) mpz_clear(self.top_power) mpz_clear(self.temp_m) - + sig_free(self.small_powers) def __reduce__(self): """ @@ -491,7 +529,7 @@ cdef class PowComputer_base(PowComputer_class): """ return self.top_power - cdef mpz_srcptr pow_mpz_t_tmp(self, long n): + cdef mpz_srcptr pow_mpz_t_tmp(self, unsigned long n): """ Computes self.prime^n. @@ -509,7 +547,7 @@ cdef class PowComputer_base(PowComputer_class): return self.temp_m pow_comp_cache = {} -cdef PowComputer_base PowComputer_c(Integer m, Integer cache_limit, Integer prec_cap, in_field): +cdef PowComputer_base PowComputer_c(Integer m, Integer cache_limit, Integer prec_cap, in_field, prec_type=None): """ Returns a PowComputer. @@ -526,18 +564,26 @@ cdef PowComputer_base PowComputer_c(Integer m, Integer cache_limit, Integer prec if mpz_cmp_si((prec_cap).value, maxpreccap) >= 0: raise ValueError, "cannot create p-adic parents with precision cap larger than (1 << (sizeof(long)*8 - 2))" - key = (m, cache_limit, prec_cap, in_field) + key = (m, cache_limit, prec_cap, in_field, prec_type) if key in pow_comp_cache: PC = pow_comp_cache[key]() if PC is not None: return PC - PC = PowComputer_base(m, mpz_get_ui(cache_limit.value), mpz_get_ui(prec_cap.value), mpz_get_ui(prec_cap.value), in_field) + if prec_type == 'capped-rel': + from padic_capped_relative_element import PowComputer_ as PC_class + elif prec_type == 'capped-abs': + from padic_capped_absolute_element import PowComputer_ as PC_class + elif prec_type == 'fixed-mod': + from padic_fixed_mod_element import PowComputer_ as PC_class + else: + PC_class = PowComputer_base + PC = PC_class(m, mpz_get_ui(cache_limit.value), mpz_get_ui(prec_cap.value), mpz_get_ui(prec_cap.value), in_field) pow_comp_cache[key] = weakref.ref(PC) return PC # To speed up the creation of PowComputers with the same m, we might eventually want to copy over data from an existing PowComputer. -def PowComputer(m, cache_limit, prec_cap, in_field = False): +def PowComputer(m, cache_limit, prec_cap, in_field = False, prec_type=None): r""" Returns a PowComputer that caches the values `1, m, m^2, \ldots, m^{C}`, where `C` is ``cache_limit``. @@ -570,4 +616,4 @@ def PowComputer(m, cache_limit, prec_cap, in_field = False): cache_limit = Integer(cache_limit) if not isinstance(prec_cap, Integer): prec_cap = Integer(prec_cap) - return PowComputer_c(m, cache_limit, prec_cap, in_field) + return PowComputer_c(m, cache_limit, prec_cap, in_field, prec_type) diff --git a/src/sage/rings/padics/pow_computer_ext.pxd b/src/sage/rings/padics/pow_computer_ext.pxd index 72028a03868..7bb16e2811b 100644 --- a/src/sage/rings/padics/pow_computer_ext.pxd +++ b/src/sage/rings/padics/pow_computer_ext.pxd @@ -1,17 +1,19 @@ from sage.rings.padics.pow_computer cimport PowComputer_class from sage.libs.ntl.ntl_ZZ_pContext cimport ntl_ZZ_pContext_class from sage.libs.ntl.types cimport * +from sage.libs.gmp.types cimport mpz_t cdef class PowComputer_ext(PowComputer_class): cdef ZZ_c* small_powers cdef ZZ_c top_power cdef ZZ_c temp_z + cdef long _initialized + cdef mpz_t temp_m2 # the following are for unpickling cdef object _poly cdef object _shift_seed cdef object _ext_type - cdef object _prec_type cdef ZZ_c* pow_ZZ_tmp(self, long n) cdef ZZ_c* pow_ZZ_top(self) diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 9a693295d88..e922861aa77 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -220,7 +220,7 @@ cdef int ZZ_pX_Eis_init(PowComputer_ZZ_pX prime_pow, ntl_ZZ_pX shift_seed) excep ##printer.x = (low_shifter[0]).val() ##print printer ZZ_pX_InvMod_newton_ram(shift_seed_inv, shift_seed.x, prime_pow.get_top_modulus()[0], prime_pow.get_top_context().x) - for i from 0 <= i < low_length: + for i in range(low_length): # Currently tmp = p / x^(2^(i-1)). Squaring yields p^2 / x^(2^i) #ZZ_pX_SqrMod(tmp, tmp, modup) # Now we divide by p. @@ -266,7 +266,7 @@ cdef int ZZ_pX_Eis_init(PowComputer_ZZ_pX prime_pow, ntl_ZZ_pX shift_seed) excep else: high_shifter_p[0] = into_multiplier # Now we cache powers of p/x^e. This is a unit, so we don't have to worry about precision issues (yay!) - for i from 1 <= i < high_length: + for i in range(1, high_length): ZZ_pX_SqrMod_pre(into_multiplier, into_multiplier, prime_pow.get_top_modulus()[0]) if multiplier: ZZ_pX_Multiplier_build(high_shifter_m[i], into_multiplier, prime_pow.get_top_modulus()[0]) @@ -322,7 +322,7 @@ cdef int ZZ_pX_eis_shift_p(PowComputer_ZZ_pX self, ZZ_pX_c* x, ZZ_pX_c* a, long sage: yr = xr^2 sage: dtr = xr.derivative() sage: f_dtr = yr*dtr; f_dtr - (a^6 + 6*a^36 + 2*a^66 + 7*a^96 + 4*a^126 + O(a^156))*t^5 + (a^8 + 2*a^38 + 8*a^68 + 3*a^98 + O(a^158))*t^7 + (8*a^40 + 10*a^100 + 5*a^130 + O(a^160))*t^9 + (2*a^12 + 5*a^42 + 3*a^72 + 7*a^102 + O(a^162))*t^11 + (8*a^14 + a^44 + 6*a^74 + 4*a^104 + 7*a^134 + O(a^164))*t^13 + (2*a^16 + 8*a^46 + 5*a^106 + 4*a^136 + O(a^166))*t^15 + (a^18 + 6*a^48 + 5*a^78 + 2*a^108 + 9*a^138 + O(a^168))*t^17 + (8*a^50 + 2*a^110 + O(a^170))*t^19 + (4*a^52 + 2*a^82 + 7*a^112 + 5*a^142 + O(a^172))*t^21 + (2*a^54 + 3*a^84 + 8*a^114 + 6*a^144 + O(a^174))*t^23 + (a^26 + 6*a^56 + 4*a^86 + 9*a^116 + 3*a^146 + O(a^176))*t^25 + (10*a^28 + 5*a^58 + 4*a^88 + 10*a^118 + 6*a^148 + O(a^178))*t^27 + (5*a^30 + 5*a^60 + 4*a^90 + 9*a^120 + 3*a^150 + O(a^180))*t^29 + (4*a^32 + 10*a^62 + 5*a^92 + 7*a^122 + 3*a^152 + O(a^182))*t^31 + (5*a^34 + 9*a^94 + 3*a^124 + 6*a^154 + O(a^184))*t^33 + (4*a^36 + 3*a^66 + 10*a^96 + 2*a^126 + 6*a^156 + O(a^186))*t^35 + (6*a^38 + 9*a^68 + 7*a^128 + 10*a^158 + O(a^188))*t^37 + (7*a^40 + 3*a^70 + 4*a^100 + 4*a^130 + 8*a^160 + O(a^190))*t^39 + (a^42 + 10*a^72 + 10*a^102 + a^132 + 7*a^162 + O(a^192))*t^41 + (8*a^74 + 8*a^104 + 9*a^134 + 7*a^164 + O(a^194))*t^43 + (10*a^136 + 2*a^166 + O(a^196))*t^45 + (7*a^48 + 10*a^78 + 5*a^108 + 8*a^138 + 3*a^168 + O(a^198))*t^47 + (6*a^50 + 5*a^80 + a^110 + 6*a^170 + O(a^200))*t^49 + (a^52 + 8*a^82 + 2*a^112 + 10*a^172 + O(a^202))*t^51 + (9*a^54 + 2*a^84 + 6*a^114 + 4*a^144 + O(a^204))*t^53 + (2*a^56 + 5*a^86 + 2*a^116 + 4*a^146 + a^176 + O(a^206))*t^55 + (3*a^58 + 3*a^88 + a^118 + 5*a^148 + 2*a^178 + O(a^208))*t^57 + (5*a^60 + 10*a^90 + 9*a^120 + a^150 + 6*a^180 + O(a^210))*t^59 + (4*a^62 + 9*a^92 + 7*a^122 + 7*a^152 + 9*a^182 + O(a^212))*t^61 + (10*a^64 + 8*a^94 + 6*a^124 + 8*a^154 + 4*a^184 + O(a^214))*t^63 + (4*a^126 + 10*a^156 + 9*a^186 + O(a^216))*t^65 + (7*a^98 + 4*a^128 + 6*a^158 + 6*a^188 + O(a^218))*t^67 + (3*a^70 + 6*a^100 + 8*a^130 + 9*a^160 + 10*a^190 + O(a^220))*t^69 + (9*a^72 + 5*a^102 + 9*a^132 + 3*a^162 + 10*a^192 + O(a^222))*t^71 + (3*a^74 + 8*a^104 + 7*a^134 + 2*a^164 + O(a^224))*t^73 + (10*a^76 + a^106 + 2*a^136 + 4*a^166 + 9*a^196 + O(a^226))*t^75 + (3*a^78 + 6*a^108 + 9*a^138 + 4*a^168 + 5*a^198 + O(a^228))*t^77 + (4*a^80 + 10*a^110 + 7*a^170 + 8*a^200 + O(a^230))*t^79 + (5*a^82 + 4*a^112 + 9*a^142 + 8*a^172 + 8*a^202 + O(a^232))*t^81 + (4*a^84 + 9*a^114 + 8*a^144 + 2*a^174 + 6*a^204 + O(a^234))*t^83 + (3*a^86 + 5*a^116 + 4*a^146 + 8*a^206 + O(a^236))*t^85 + (a^118 + 7*a^148 + 6*a^208 + O(a^238))*t^87 + (4*a^90 + 9*a^120 + 9*a^150 + 6*a^180 + 6*a^210 + O(a^240))*t^89 + (10*a^122 + 3*a^152 + 8*a^182 + 4*a^212 + 2*a^242 + O(a^244))*t^91 + (9*a^154 + 10*a^184 + 10*a^214 + 7*a^244 + 9*a^274 + O(a^276))*t^93 + (9*a^186 + 4*a^216 + 5*a^246 + a^276 + 10*a^306 + O(a^308))*t^95 + O(a^456)*t^2 + O(a^306)*t^3 + O(a^156)*t^4 + (a^6 + 6*a^36 + 2*a^66 + 7*a^96 + 4*a^126 + O(a^156))*t^5 + O(a^158)*t^6 + (a^8 + 2*a^38 + 8*a^68 + 3*a^98 + O(a^158))*t^7 + O(a^160)*t^8 + (8*a^40 + 10*a^100 + 5*a^130 + O(a^160))*t^9 + O(a^162)*t^10 + (2*a^12 + 5*a^42 + 3*a^72 + 7*a^102 + O(a^162))*t^11 + O(a^164)*t^12 + (8*a^14 + a^44 + 6*a^74 + 4*a^104 + 7*a^134 + O(a^164))*t^13 + O(a^166)*t^14 + (2*a^16 + 8*a^46 + 5*a^106 + 4*a^136 + O(a^166))*t^15 + O(a^168)*t^16 + (a^18 + 6*a^48 + 5*a^78 + 2*a^108 + 9*a^138 + O(a^168))*t^17 + O(a^170)*t^18 + (8*a^50 + 2*a^110 + O(a^170))*t^19 + O(a^172)*t^20 + (4*a^52 + 2*a^82 + 7*a^112 + 5*a^142 + O(a^172))*t^21 + O(a^174)*t^22 + (2*a^54 + 3*a^84 + 8*a^114 + 6*a^144 + O(a^174))*t^23 + O(a^176)*t^24 + (a^26 + 6*a^56 + 4*a^86 + 9*a^116 + 3*a^146 + O(a^176))*t^25 + O(a^178)*t^26 + (10*a^28 + 5*a^58 + 4*a^88 + 10*a^118 + 6*a^148 + O(a^178))*t^27 + O(a^180)*t^28 + (5*a^30 + 5*a^60 + 4*a^90 + 9*a^120 + 3*a^150 + O(a^180))*t^29 + O(a^182)*t^30 + (4*a^32 + 10*a^62 + 5*a^92 + 7*a^122 + 3*a^152 + O(a^182))*t^31 + O(a^184)*t^32 + (5*a^34 + 9*a^94 + 3*a^124 + 6*a^154 + O(a^184))*t^33 + O(a^186)*t^34 + (4*a^36 + 3*a^66 + 10*a^96 + 2*a^126 + 6*a^156 + O(a^186))*t^35 + O(a^188)*t^36 + (6*a^38 + 9*a^68 + 7*a^128 + 10*a^158 + O(a^188))*t^37 + O(a^190)*t^38 + (7*a^40 + 3*a^70 + 4*a^100 + 4*a^130 + 8*a^160 + O(a^190))*t^39 + O(a^192)*t^40 + (a^42 + 10*a^72 + 10*a^102 + a^132 + 7*a^162 + O(a^192))*t^41 + O(a^194)*t^42 + (8*a^74 + 8*a^104 + 9*a^134 + 7*a^164 + O(a^194))*t^43 + O(a^196)*t^44 + (10*a^136 + 2*a^166 + O(a^196))*t^45 + O(a^198)*t^46 + (7*a^48 + 10*a^78 + 5*a^108 + 8*a^138 + 3*a^168 + O(a^198))*t^47 + O(a^200)*t^48 + (6*a^50 + 5*a^80 + a^110 + 6*a^170 + O(a^200))*t^49 + O(a^202)*t^50 + (a^52 + 8*a^82 + 2*a^112 + 10*a^172 + O(a^202))*t^51 + O(a^204)*t^52 + (9*a^54 + 2*a^84 + 6*a^114 + 4*a^144 + O(a^204))*t^53 + O(a^206)*t^54 + (2*a^56 + 5*a^86 + 2*a^116 + 4*a^146 + a^176 + O(a^206))*t^55 + O(a^208)*t^56 + (3*a^58 + 3*a^88 + a^118 + 5*a^148 + 2*a^178 + O(a^208))*t^57 + O(a^210)*t^58 + (5*a^60 + 10*a^90 + 9*a^120 + a^150 + 6*a^180 + O(a^210))*t^59 + O(a^212)*t^60 + (4*a^62 + 9*a^92 + 7*a^122 + 7*a^152 + 9*a^182 + O(a^212))*t^61 + O(a^214)*t^62 + (10*a^64 + 8*a^94 + 6*a^124 + 8*a^154 + 4*a^184 + O(a^214))*t^63 + O(a^216)*t^64 + (4*a^126 + 10*a^156 + 9*a^186 + O(a^216))*t^65 + O(a^218)*t^66 + (7*a^98 + 4*a^128 + 6*a^158 + 6*a^188 + O(a^218))*t^67 + O(a^220)*t^68 + (3*a^70 + 6*a^100 + 8*a^130 + 9*a^160 + 10*a^190 + O(a^220))*t^69 + O(a^222)*t^70 + (9*a^72 + 5*a^102 + 9*a^132 + 3*a^162 + 10*a^192 + O(a^222))*t^71 + O(a^224)*t^72 + (3*a^74 + 8*a^104 + 7*a^134 + 2*a^164 + O(a^224))*t^73 + O(a^226)*t^74 + (10*a^76 + a^106 + 2*a^136 + 4*a^166 + 9*a^196 + O(a^226))*t^75 + O(a^228)*t^76 + (3*a^78 + 6*a^108 + 9*a^138 + 4*a^168 + 5*a^198 + O(a^228))*t^77 + O(a^230)*t^78 + (4*a^80 + 10*a^110 + 7*a^170 + 8*a^200 + O(a^230))*t^79 + O(a^232)*t^80 + (5*a^82 + 4*a^112 + 9*a^142 + 8*a^172 + 8*a^202 + O(a^232))*t^81 + O(a^234)*t^82 + (4*a^84 + 9*a^114 + 8*a^144 + 2*a^174 + 6*a^204 + O(a^234))*t^83 + O(a^236)*t^84 + (3*a^86 + 5*a^116 + 4*a^146 + 8*a^206 + O(a^236))*t^85 + O(a^238)*t^86 + (a^118 + 7*a^148 + 6*a^208 + O(a^238))*t^87 + O(a^240)*t^88 + (4*a^90 + 9*a^120 + 9*a^150 + 6*a^180 + 6*a^210 + O(a^240))*t^89 + O(a^244)*t^90 + (10*a^122 + 3*a^152 + 8*a^182 + 4*a^212 + 2*a^242 + O(a^244))*t^91 + O(a^276)*t^92 + (9*a^154 + 10*a^184 + 10*a^214 + 7*a^244 + 9*a^274 + O(a^276))*t^93 + O(a^308)*t^94 + (9*a^186 + 4*a^216 + 5*a^246 + a^276 + 10*a^306 + O(a^308))*t^95 """ ##print "starting..." cdef ZZ_pX_c low_part @@ -499,6 +499,8 @@ cdef class PowComputer_ext(PowComputer_class): sage: PC = PowComputer_ext_maker(5, 10, 10, 20, False, ntl.ZZ_pX([-5, 0, 1], 5^10), 'small', 'e',ntl.ZZ_pX([1],5^10)) #indirect doctest """ + PowComputer_class.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly, shift_seed) + self._initialized = 0 self.small_powers = Allocate_ZZ_array(cache_limit + 1) if self.small_powers == NULL: @@ -513,12 +515,13 @@ cdef class PowComputer_ext(PowComputer_class): mpz_to_ZZ(&(self.small_powers[1]), prime.value) sig_on() - for i from 2 <= i <= cache_limit: + for i in range(2, cache_limit + 1): ZZ_mul(self.small_powers[i], self.small_powers[i-1], self.small_powers[1]) mpz_to_ZZ(&self.top_power, prime.value) ZZ_power(self.top_power, self.top_power, prec_cap) sig_off() mpz_init(self.temp_m) + mpz_init(self.temp_m2) self._poly = poly self._shift_seed = shift_seed @@ -532,7 +535,7 @@ cdef class PowComputer_ext(PowComputer_class): sage: PC = PowComputer_ext_maker(5, 5, 10, 20, False, ntl.ZZ_pX([-5,0,1],5^10), 'FM', 'e',ntl.ZZ_pX([1],5^10)) sage: del PC # indirect doctest """ - if (self)._initialized: + if (self)._initialized: self.cleanup_ext() def __repr__(self): @@ -578,8 +581,9 @@ cdef class PowComputer_ext(PowComputer_class): """ Delete_ZZ_array(self.small_powers) mpz_clear(self.temp_m) + mpz_clear(self.temp_m2) - cdef mpz_srcptr pow_mpz_t_tmp(self, long n): + cdef mpz_srcptr pow_mpz_t_tmp(self, unsigned long n): """ Provides fast access to an mpz_t* pointing to self.prime^n. @@ -751,6 +755,16 @@ cdef class PowComputer_ext(PowComputer_class): cdef class PowComputer_ZZ_pX(PowComputer_ext): def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly, shift_seed = None): + """ + Initialization. + + For input types see :func:`PowComputer_ext_maker` + + TESTS:: + + sage: PC = PowComputer_ext_maker(5, 5, 10, 20, False, ntl.ZZ_pX([-5,0,1],5^10), 'FM', 'e',ntl.ZZ_pX([1],5^10)) + sage: TestSuite(PC).run() + """ if not isinstance(poly, ntl_ZZ_pX): raise TypeError self.deg = ZZ_pX_deg((poly).x) @@ -863,7 +877,7 @@ cdef class PowComputer_ZZ_pX(PowComputer_ext): end = mpz_get_ui((Integer(runs)).value) _n = mpz_get_ui((Integer(n)).value) t = cputime() - for i from 0 <= i < end: + for i in range(end): # Put the function you want speed tested here. self.get_modulus(_n) return cputime(t) @@ -1218,7 +1232,7 @@ cdef class PowComputer_ZZ_pX(PowComputer_ext): ZZ_pX_add(xnew_q, xnew_q, x[0]) # while x != xnew: # x = xnew - # xnew = x + u*(x^p - x) + # xnew = x + u*(x^q - x) while x[0] != xnew_q: x[0] = xnew_q ZZ_pX_PowerMod_pre(xnew_q, x[0], q, self.get_modulus(absprec)[0]) @@ -1612,7 +1626,7 @@ cdef class PowComputer_ZZ_pX_small(PowComputer_ZZ_pX): if isinstance(poly, ntl_ZZ_pX): pol = (poly).x self.c.append(None) - for i from 1 <= i <= cache_limit: + for i in range(1, cache_limit + 1): self.c.append(PowComputer_ZZ_pX.get_context(self,i)) # create a temporary polynomial with the highest modulus to @@ -1620,7 +1634,7 @@ cdef class PowComputer_ZZ_pX_small(PowComputer_ZZ_pX): (self.c[cache_limit]).restore_c() tmp = (poly).x - for i from 1 <= i <= cache_limit: + for i in range(1, cache_limit + 1): (self.c[i]).restore_c() ZZ_pX_conv_modulus(tmp, pol, (self.c[i]).x) ZZ_pX_Modulus_build(self.mod[i], tmp) @@ -1775,6 +1789,18 @@ cdef class PowComputer_ZZ_pX_small_Eis(PowComputer_ZZ_pX_small): These are only stored at maximal precision: in order to get lower precision versions just reduce mod p^n. """ def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly, shift_seed = None): + """ + Initialization. + + For input types see :func:`PowComputer_ext_maker` + + TESTS:: + + sage: A = PowComputer_ext_maker(5, 10, 10, 40, False, ntl.ZZ_pX([-5,75,15,0,1],5^10), 'small', 'e',ntl.ZZ_pX([1,-15,-3],5^10)) + sage: type(A) + + sage: TestSuite(A).run() + """ self._ext_type = 'e' if not isinstance(shift_seed, ntl_ZZ_pX): raise TypeError, "shift_seed must be an ntl_ZZ_pX" @@ -1958,7 +1984,7 @@ cdef class PowComputer_ZZ_pX_big(PowComputer_ZZ_pX): if isinstance(poly, ntl_ZZ_pX): pol = (poly).x self.context_list.append(None) - for i from 1 <= i <= cache_limit: + for i in range(1, cache_limit + 1): self.context_list.append(PowComputer_ZZ_pX.get_context(self,i)) # create a temporary polynomial with the highest modulus to @@ -1967,7 +1993,7 @@ cdef class PowComputer_ZZ_pX_big(PowComputer_ZZ_pX): (self.top_context).restore_c() tmp = (poly).x - for i from 1 <= i <= cache_limit: + for i in range(1, cache_limit + 1): (self.context_list[i]).restore_c() ZZ_pX_conv_modulus(tmp, pol, (self.context_list[i]).x) ZZ_pX_Modulus_build(self.modulus_list[i], tmp) @@ -2191,6 +2217,18 @@ cdef class PowComputer_ZZ_pX_big_Eis(PowComputer_ZZ_pX_big): These are only stored at maximal precision: in order to get lower precision versions just reduce mod p^n. """ def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly, shift_seed = None): + """ + Initialization. + + For input types see :func:`PowComputer_ext_maker` + + TESTS:: + + sage: A = PowComputer_ext_maker(5, 3, 10, 40, False, ntl.ZZ_pX([-5,75,15,0,1],5^10), 'big', 'e',ntl.ZZ_pX([1,-15,-3],5^10)) + sage: type(A) + + sage: TestSuite(A).run() + """ self._ext_type = 'e' if not isinstance(shift_seed, ntl_ZZ_pX): raise TypeError, "shift_seed must be an ntl_ZZ_pX" diff --git a/src/sage/rings/padics/pow_computer_flint.pxd b/src/sage/rings/padics/pow_computer_flint.pxd new file mode 100644 index 00000000000..671423ce593 --- /dev/null +++ b/src/sage/rings/padics/pow_computer_flint.pxd @@ -0,0 +1,37 @@ +from sage.libs.gmp.mpz cimport mpz_t +from sage.libs.flint.padic cimport padic_ctx_t +from sage.libs.flint.fmpz cimport fmpz_t +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.rings.padics.pow_computer cimport PowComputer_class + +cdef class PowComputer_flint(PowComputer_class): + cdef padic_ctx_t ctx + cdef fmpz_t fprime + cdef fmpz_t half_prime + cdef fmpz_t _fpow_array + cdef fmpz_t _fpow_variable + cdef mpz_t top_power + + cdef fmpz_t* pow_fmpz_t_tmp(self, unsigned long n) except NULL + cdef unsigned long capdiv(self, unsigned long n) + + cdef fmpz_t tfmpz + +cdef class PowComputer_flint_1step(PowComputer_flint): + cdef fmpz_t q + cdef fmpz_poly_t modulus + cdef fmpz_poly_t* _moduli + cdef fmpz_poly_t* get_modulus(self, unsigned long n) + cdef fmpz_poly_t* get_modulus_capdiv(self, unsigned long n) + cdef _new_fmpz_poly(self, fmpz_poly_t value, var=*) + +cdef class PowComputer_flint_unram(PowComputer_flint_1step): + # WARNING: + # These variables are modified by the linkage and must no be used anywhere else + # (other than in __(c)init__) + cdef fmpz_t fmpz_ccmp, fmpz_cval, fmpz_cinv, fmpz_cinv2, fmpz_clist, fmpz_clist2, fmpz_ctm, fmpz_cconv + cdef fmpz_poly_t poly_cconv, poly_ctm, poly_ccmp, poly_cinv, poly_cisunit, poly_cinv2, poly_flint_rep, poly_matmod + cdef mpz_t mpz_cpow, mpz_ctm, mpz_cconv, mpz_matmod + +cdef class PowComputer_flint_eis(PowComputer_flint_1step): + pass diff --git a/src/sage/rings/padics/pow_computer_flint.pyx b/src/sage/rings/padics/pow_computer_flint.pyx new file mode 100644 index 00000000000..1696e78d069 --- /dev/null +++ b/src/sage/rings/padics/pow_computer_flint.pyx @@ -0,0 +1,600 @@ +include "cysignals/signals.pxi" +include "cysignals/memory.pxi" + +from sage.libs.gmp.mpz cimport mpz_init, mpz_clear, mpz_pow_ui +from sage.libs.flint.padic cimport * +from sage.libs.flint.fmpz_poly cimport * +from sage.libs.flint.nmod_vec cimport * +from sage.libs.flint.fmpz_vec cimport * +from sage.libs.flint.fmpz cimport fmpz_init, fmpz_one, fmpz_mul, fmpz_set, fmpz_get_mpz, fmpz_clear, fmpz_pow_ui, fmpz_set_mpz, fmpz_fdiv_q_2exp +from sage.rings.integer cimport Integer +from sage.rings.all import ZZ +from sage.rings.polynomial.polynomial_integer_dense_flint cimport Polynomial_integer_dense_flint + +cdef class PowComputer_flint(PowComputer_class): + """ + A PowComputer for use in `p`-adics implemented via FLINT. + + For a description of inputs see :func:`PowComputer_flint_maker`. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint + sage: PowComputer_flint(5, 20, 20, 20, False) + FLINT PowComputer for 5 + """ + def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed = None): + """ + Memory initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint + sage: type(PowComputer_flint(5, 20, 20, 20, False)) + + + """ + # fmpz_init does not allocate memory + fmpz_init(self.fprime) + fmpz_init(self.half_prime) + fmpz_set_mpz(self.fprime, prime.value) + fmpz_init(self._fpow_variable) + fmpz_fdiv_q_2exp(self.half_prime, self.fprime, 1) + fmpz_init(self.tfmpz) + + sig_on() + try: + mpz_init(self.top_power) + padic_ctx_init(self.ctx, self.fprime, 0, prec_cap, PADIC_SERIES) + finally: + sig_off() + + self.__allocated = 4 + + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None, shift_seed=None): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_maker + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_maker(5, 20, 20, 20, False, f, 'capped-rel') # indirect doctest + sage: TestSuite(A).run() + + """ + PowComputer_class.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly, shift_seed) + + mpz_pow_ui(self.top_power, prime.value, prec_cap) + + def __dealloc__(self): + """ + Deallocation. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint + sage: A = PowComputer_flint(5, 20, 20, 20, False) + sage: del A + """ + if self.__allocated >= 4: + fmpz_clear(self.fprime) + fmpz_clear(self.half_prime) + fmpz_clear(self._fpow_variable) + fmpz_clear(self.tfmpz) + mpz_clear(self.top_power) + padic_ctx_clear(self.ctx) + + def __reduce__(self): + """ + Pickling. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_maker + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_maker(5, 20, 20, 20, False, f, 'capped-rel') # indirect doctest + sage: A._test_pickling() # indirect doctest + + """ + return PowComputer_flint_maker, (self.prime, self.cache_limit, self.prec_cap, self.ram_prec_cap, self.in_field, self.polynomial(), self._prec_type) + + def _repr_(self): + """ + String representation of this powcomputer. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint + sage: A = PowComputer_flint(5, 20, 20, 20, False); A + FLINT PowComputer for 5 + """ + return "FLINT PowComputer for %s"%(self.prime) + + cdef fmpz_t* pow_fmpz_t_tmp(self, unsigned long n) except NULL: + """ + Returns a pointer to a FLINT ``fmpz_t`` holding `p^n`. + + Analogous to + :meth:`sage.rings.padics.pow_computer.PowComputer_class.pow_mpz_t_tmp` + but with FLINT ``fmpz_t`` rather than GMP ``mpz_t``. The same + important warnings apply. + """ + cdef padic_ctx_struct ctx = (self.ctx)[0] + if ctx.min <= n and n < ctx.max: + self._fpow_array[0] = (ctx.pow + (n - ctx.min))[0] + return &self._fpow_array + else: + fmpz_pow_ui(self._fpow_variable, self.fprime, n) + return &self._fpow_variable + + cdef mpz_srcptr pow_mpz_t_tmp(self, unsigned long n): + """ + Returns a pointer to an ``mpz_t`` holding `p^n`. + + See + :meth:`sage.rings.padics.pow_computer.PowComputer_class.pow_mpz_t_tmp` + for important warnings. + """ + fmpz_get_mpz(self.temp_m, self.pow_fmpz_t_tmp(n)[0]) + return self.temp_m + + cdef mpz_srcptr pow_mpz_t_top(self): + """ + Returns a pointer to an ``mpz_t`` holding `p^N`, where `N` is + the precision cap. + """ + return self.top_power + + cdef unsigned long capdiv(self, unsigned long n): + """ + Returns ceil(n / e). + """ + if self.e == 1: return n + if n == 0: return 0 + return (n-1) / self.e + 1 + + def polynomial(self, n=None, var='x'): + """ + Returns ``None``. + + For consistency with subclasses. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint + sage: A = PowComputer_flint(5, 20, 20, 20, False, None) + sage: A.polynomial() is None + True + + """ + return None + +cdef class PowComputer_flint_1step(PowComputer_flint): + """ + A PowComputer for a `p`-adic extension defined by a single polynomial. + + For a description of inputs see :func:`PowComputer_flint_maker`. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f); A + FLINT PowComputer for 5 with polynomial x^3 - 8*x - 2 + + """ + def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, _poly): + """ + Memory initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f) + sage: type(A) + + + """ + cdef Polynomial_integer_dense_flint poly = _poly + cdef long length = fmpz_poly_length(poly.__poly) + + cdef Py_ssize_t i + + # fmpz_init does not allocate memory + fmpz_init(self.q) + + sig_on() + try: + self._moduli = sig_malloc(sizeof(fmpz_poly_t) * (cache_limit + 2)) + if self._moduli == NULL: + raise MemoryError + try: + fmpz_poly_init2(self.modulus, length) + try: + for i in range(1,cache_limit+2): + try: + fmpz_poly_init2(self._moduli[i], length) + except BaseException: + i-=1 + while i: + fmpz_poly_clear(self._moduli[i]) + i-=1 + raise + except BaseException: + fmpz_poly_clear(self.modulus) + raise + except BaseException: + sig_free(self._moduli) + raise + finally: + sig_off() + + self.__allocated = 8 + + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, _poly, shift_seed=None): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_maker + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_maker(5, 20, 20, 20, False, f, 'capped-rel') + sage: TestSuite(A).run() + + """ + PowComputer_flint.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, _poly, shift_seed) + + cdef Polynomial_integer_dense_flint poly = _poly + cdef long length = fmpz_poly_length(poly.__poly) + self.deg = length - 1 + + fmpz_poly_set(self.modulus, poly.__poly) + + cdef Py_ssize_t i + cdef fmpz* coeffs = self.modulus.coeffs + fmpz_one(self.tfmpz) + for i in range(1,cache_limit+1): + fmpz_mul(self.tfmpz, self.tfmpz, self.fprime) + _fmpz_vec_scalar_mod_fmpz((self._moduli[i])[0].coeffs, coeffs, length, self.tfmpz) + _fmpz_poly_set_length(self._moduli[i], length) + + _fmpz_poly_set_length(self._moduli[cache_limit+1], length) + + def __dealloc__(self): + """ + Deallocation. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f) + sage: del A + """ + cdef Py_ssize_t i + + if self.__allocated >= 8: + fmpz_clear(self.q) + fmpz_poly_clear(self.modulus) + for i in range(1, self.cache_limit + 1): + fmpz_poly_clear(self._moduli[i]) + sig_free(self._moduli) + + def _repr_(self): + """ + String representation of this powcomputer. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f); A + FLINT PowComputer for 5 with polynomial x^3 - 8*x - 2 + """ + return "FLINT PowComputer for %s with polynomial %s"%(self.prime, self.polynomial()) + + def __cmp__(self, other): + """ + Comparison. + + Lexicographic on class, prime, precision cap, cache_limit and polynomial. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = x^3 - 8*x - 2; g = x^3 - (8 + 5^22)*x - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f) + sage: B = PowComputer_flint_1step(5, 20, 20, 20, False, g) + sage: A == B + False + """ + c = PowComputer_flint.__cmp__(self, other) + if c: return c + cdef PowComputer_flint_1step o = other + if fmpz_poly_equal(self.modulus, o.modulus): return 0 + #return cmp(self.polynomial(), other.polynomial()) + return 1 + + cdef fmpz_poly_t* get_modulus(self, unsigned long k): + """ + Returns the defining polynomial reduced modulo `p^k`. + + The same warnings apply as for + :meth:`sage.rings.padics.pow_computer.PowComputer_class.pow_mpz_t_tmp`. + """ + cdef long c + if k <= self.cache_limit: + return &(self._moduli[k]) + else: + c = self.cache_limit+1 + _fmpz_vec_scalar_mod_fmpz((self._moduli[c])[0].coeffs, + (self.modulus)[0].coeffs, + self.deg + 1, + self.pow_fmpz_t_tmp(k)[0]) + return &(self._moduli[c]) + + cdef fmpz_poly_t* get_modulus_capdiv(self, unsigned long k): + """ + Returns the defining polynomial reduced modulo `p^a`, where + `a` is the ceiling of `k/e`. + + The same warnings apply as for + :meth:`sage.rings.padics.pow_computer.PowComputer_class.pow_mpz_t_tmp`. + """ + return self.get_modulus(self.capdiv(k)) + + def polynomial(self, _n=None, var='x'): + """ + Returns the polynomial attached to this ``PowComputer``, possibly reduced modulo a power of `p`. + + INPUT: + + - ``_n`` -- (default ``None``) an integer, the power of `p` + modulo which to reduce. + + - ``var`` -- (default ``'x'``) the variable for the returned polynomial + + .. NOTE:: + + From Cython you should use :meth:`get_modulus` instead for + speed. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_1step + sage: R. = ZZ[]; f = y^3 - 8*y - 2 + sage: A = PowComputer_flint_1step(5, 20, 20, 20, False, f) + sage: A.polynomial() + x^3 - 8*x - 2 + sage: A.polynomial(var='y') + y^3 - 8*y - 2 + sage: A.polynomial(2) + x^3 + 17*x + 23 + """ + R = ZZ[var] + x = R.gen() + cdef Polynomial_integer_dense_flint ans = (x)._new() + if _n is None: + fmpz_poly_set(ans.__poly, self.modulus) + else: + fmpz_poly_set(ans.__poly, self.get_modulus(_n)[0]) + return ans + + cdef _new_fmpz_poly(self, fmpz_poly_t value, var='x'): + """ + Returns a polynomial with the value stored in ``value`` and + variable name ``var``. + """ + R = ZZ[var] + x = R.gen() + cdef Polynomial_integer_dense_flint ans = (x)._new() + fmpz_poly_set(ans.__poly, value) + return ans + +cdef class PowComputer_flint_unram(PowComputer_flint_1step): + """ + A PowComputer for a `p`-adic extension defined by an unramified polynomial. + + For a description of inputs see :func:`PowComputer_flint_maker`. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_unram + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_unram(5, 20, 20, 20, False, f); A + FLINT PowComputer for 5 with polynomial x^3 - 8*x - 2 + + """ + def __cinit__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, _poly, shift_seed=None): + """ + Memory initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_unram + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_unram(5, 20, 20, 20, False, f) + sage: type(A) + + + """ + # fmpz_init does not allocate memory + fmpz_init(self.fmpz_ccmp) + fmpz_init(self.fmpz_cval) + fmpz_init(self.fmpz_cinv) + fmpz_init(self.fmpz_cinv2) + fmpz_init(self.fmpz_clist) + fmpz_init(self.fmpz_clist2) + fmpz_init(self.fmpz_ctm) + fmpz_init(self.fmpz_cconv) + + # While the following allocations have the potential to leak + # small amounts of memory when interrupted or when one of the + # init methods raises a MemoryError, the only leak-free + # solution we could devise used 11-nested try blocks. We + # choose readable code in this case. + sig_on() + fmpz_poly_init(self.poly_cconv) + fmpz_poly_init(self.poly_ctm) + fmpz_poly_init(self.poly_ccmp) + fmpz_poly_init(self.poly_cinv) + fmpz_poly_init(self.poly_cisunit) + fmpz_poly_init(self.poly_cinv2) + fmpz_poly_init(self.poly_flint_rep) + fmpz_poly_init(self.poly_matmod) + mpz_init(self.mpz_cpow) + mpz_init(self.mpz_ctm) + mpz_init(self.mpz_cconv) + mpz_init(self.mpz_matmod) + sig_off() + + self.__allocated = 16 + + def __dealloc__(self): + """ + Deallocation. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_unram + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_unram(5, 20, 20, 20, False, f) + sage: del A + + """ + if self.__allocated >= 16: + fmpz_clear(self.fmpz_ccmp) + fmpz_clear(self.fmpz_cval) + fmpz_clear(self.fmpz_cinv) + fmpz_clear(self.fmpz_cinv2) + fmpz_clear(self.fmpz_clist) + fmpz_clear(self.fmpz_clist2) + fmpz_clear(self.fmpz_ctm) + fmpz_clear(self.fmpz_cconv) + mpz_clear(self.mpz_cconv) + mpz_clear(self.mpz_ctm) + mpz_clear(self.mpz_cpow) + mpz_clear(self.mpz_matmod) + fmpz_poly_clear(self.poly_cconv) + fmpz_poly_clear(self.poly_ctm) + fmpz_poly_clear(self.poly_ccmp) + fmpz_poly_clear(self.poly_cinv) + fmpz_poly_clear(self.poly_cisunit) + fmpz_poly_clear(self.poly_cinv2) + fmpz_poly_clear(self.poly_flint_rep) + fmpz_poly_clear(self.poly_matmod) + + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_maker + sage: R. = ZZ[]; f = x^3 - 8*x - 2 + sage: A = PowComputer_flint_maker(5, 20, 20, 20, False, f, 'capped-rel') + sage: TestSuite(A).run() + + """ + PowComputer_flint_1step.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + + self.e = 1 + self.f = fmpz_poly_degree(self.modulus) + fmpz_pow_ui(self.q, self.fprime, self.f) + +cdef class PowComputer_flint_eis(PowComputer_flint_1step): + """ + A PowComputer for a `p`-adic extension defined by an Eisenstein polynomial. + + For a description of inputs see :func:`PowComputer_flint_maker`. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_eis + sage: R. = ZZ[]; f = x^3 - 25*x + 5 + sage: A = PowComputer_flint_eis(5, 20, 20, 60, False, f); A + FLINT PowComputer for 5 with polynomial x^3 - 25*x + 5 + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + TESTS:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_eis + sage: R. = ZZ[]; f = x^3 - 25*x + 5 + sage: A = PowComputer_flint_eis(5, 20, 20, 60, False, f) + sage: type(A) + + + """ + PowComputer_flint_1step.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + + self.e = fmpz_poly_degree(self.modulus) + self.f = 1 + fmpz_set(self.q, self.fprime) + +def PowComputer_flint_maker(prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly, prec_type): + """ + Return an appropriate FLINT PowComputer for the given input. + + INPUT: + + - ``prime`` -- an integral prime + + - ``cache_limit`` -- a non-negative integer, controlling the + caching. Powers of ``prime``, reductions of ``poly`` modulo + different powers of ``prime`` and inverses of the leading + coefficient modulo different powers of ``prime`` are cached. + Additional data is cached for ramified extensions. + + - ``prec_cap`` -- the power of `p` modulo which elements of + largest precision are defined. + + - ``ram_prec_cap`` -- Approximately ``e*prec_cap``, where ``e`` is + the ramification degree of the extension. For a ramified + extension this is what Sage calls the precision cap of the ring. + In fact, it's possible to have rings with precision cap not a + multiple of `e`, in which case the actual relationship between + ``ram_prec_cap`` and ``prec_cap`` is that + ``prec_cap = ceil(n/e)`` + + - ``in_field`` -- (boolean) whether the associated ring is + actually a field. + + - ``poly`` -- the polynomial defining the extension. + + - `prec_type`` -- one of ``"capped-rel"``, ``"capped-abs"`` or + ``"fixed-mod"``, the precision type of the ring. + + .. NOTE:: + + Because of the way templates work, this function imports the + class of its return value from the appropriate element files. + This means that the returned PowComputer will have the + appropriate compile-time-type for Cython. + + EXAMPLES:: + + sage: from sage.rings.padics.pow_computer_flint import PowComputer_flint_maker + sage: R. = ZZ[] + sage: A = PowComputer_flint_maker(3, 20, 20, 20, False, x^3 + 2*x + 1, 'capped-rel'); type(A) + + sage: A = PowComputer_flint_maker(3, 20, 20, 20, False, x^3 + 2*x + 1, 'capped-abs'); type(A) + + sage: A = PowComputer_flint_maker(3, 20, 20, 20, False, x^3 + 2*x + 1, 'fixed-mod'); type(A) + + + """ + if prec_type == 'capped-rel': + from qadic_flint_CR import PowComputer_ + elif prec_type == 'capped-abs': + from qadic_flint_CA import PowComputer_ + elif prec_type == 'fixed-mod': + from qadic_flint_FM import PowComputer_ + else: + raise ValueError("unknown prec_type `%s`"%prec_type) + return PowComputer_(prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) diff --git a/src/sage/rings/padics/qadic_flint_CA.pxd b/src/sage/rings/padics/qadic_flint_CA.pxd new file mode 100644 index 00000000000..1caf164afeb --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_CA.pxd @@ -0,0 +1,20 @@ +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint_unram +from sage.rings.padics.qadic_flint_CR cimport CRElement + +cdef class PowComputer_(PowComputer_flint_unram): + pass +ctypedef fmpz_poly_t celement + +include "CA_template_header.pxi" + +cdef class qAdicCappedAbsoluteElement(CAElement): + pass + +cdef class qAdicCoercion_Zq_Qq(RingHomomorphism_coercion): + cdef CRElement _zero + cdef Morphism _section + +cdef class qAdicConvert_Qq_Zq(Morphism): + cdef CAElement _zero diff --git a/src/sage/rings/padics/qadic_flint_CA.pyx b/src/sage/rings/padics/qadic_flint_CA.pyx new file mode 100644 index 00000000000..3bad4c75f47 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_CA.pyx @@ -0,0 +1,100 @@ +from types import MethodType + +include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" +include "sage/libs/linkages/padics/unram_shared.pxi" +include "CA_template.pxi" + +cdef class PowComputer_(PowComputer_flint_unram): + """ + A PowComputer for a capped-absolute unramified ring. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + EXAMPLES:: + + sage: R. = Zq(125) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'capped-abs' + """ + self._prec_type = 'capped-abs' + PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + +cdef class qAdicCappedAbsoluteElement(CAElement): + frobenius = MethodType(frobenius_unram, None, qAdicCappedAbsoluteElement) + trace = MethodType(trace_unram, None, qAdicCappedAbsoluteElement) + norm = MethodType(norm_unram, None, qAdicCappedAbsoluteElement) + + def matrix_mod_pn(self): + """ + Returns the matrix of right multiplication by the element on + the power basis `1, x, x^2, \ldots, x^{d-1}` for this + extension field. Thus the *rows* of this matrix give the + images of each of the `x^i`. The entries of the matrices are + IntegerMod elements, defined modulo ``p^(self.absprec() / e)``. + + EXAMPLES:: + + sage: R. = ZqCA(5^5,5) + sage: b = (5 + 15*a)^3 + sage: b.matrix_mod_pn() + [ 125 1125 250 250 0] + [ 0 125 1125 250 250] + [2375 2125 125 1125 250] + [2375 1375 2125 125 1125] + [2875 1000 1375 2125 125] + + sage: M = R(0,3).matrix_mod_pn(); M == 0 + True + sage: M.base_ring() + Ring of integers modulo 125 + """ + return cmatrix_mod_pn(self.value, self.absprec, 0, self.prime_pow) + + def _flint_rep(self, var='x'): + """ + Replacement for _ntl_rep for use in printing and debugging. + + EXAMPLES:: + + sage: R. = ZqCA(27, 4) + sage: (1+a).inverse_of_unit()._flint_rep() + 41*x^2 + 40*x + 42 + sage: (1+a)*(41*a^2+40*a+42) + 1 + O(3^4) + """ + return self.prime_pow._new_fmpz_poly(self.value, var) + + def _flint_rep_abs(self, var='x'): + """ + Replacement for _ntl_rep_abs for use in printing and debugging. + + EXAMPLES:: + + sage: R. = ZqCA(27, 4) + sage: (3+3*a)._flint_rep_abs() + (3*x + 3, 0) + """ + return self._flint_rep(var), Integer(0) + + def __hash__(self): + r""" + Raise a ``TypeError`` since this element is not hashable + (:trac:`11895`.) + + TESTS:: + + sage: K. = ZqCA(9) + sage: hash(a) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CA.qAdicCappedAbsoluteElement' + + """ + # Eventually, hashing will be disabled for all (non-fixed-mod) p-adic + # elements (#11895), until then, we only to this for types which did + # not support hashing before we switched some elements to FLINT + raise TypeError("unhashable type: 'sage.rings.padics.qadic_flint_CA.qAdicCappedAbsoluteElement'") diff --git a/src/sage/rings/padics/qadic_flint_CR.pxd b/src/sage/rings/padics/qadic_flint_CR.pxd new file mode 100644 index 00000000000..e9dcaeaf3c1 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_CR.pxd @@ -0,0 +1,11 @@ +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint_unram +cdef class PowComputer_(PowComputer_flint_unram): + pass +ctypedef fmpz_poly_t celement + +include "CR_template_header.pxi" + +cdef class qAdicCappedRelativeElement(CRElement): + pass diff --git a/src/sage/rings/padics/qadic_flint_CR.pyx b/src/sage/rings/padics/qadic_flint_CR.pyx new file mode 100644 index 00000000000..988316ab748 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_CR.pyx @@ -0,0 +1,124 @@ +from types import MethodType + +include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" +include "sage/libs/linkages/padics/unram_shared.pxi" +include "CR_template.pxi" + +cdef class PowComputer_(PowComputer_flint_unram): + """ + A PowComputer for a capped-relative unramified ring or field. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqCR(125) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'capped-rel' + """ + self._prec_type = 'capped-rel' + PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + +cdef class qAdicCappedRelativeElement(CRElement): + frobenius = MethodType(frobenius_unram, None, qAdicCappedRelativeElement) + trace = MethodType(trace_unram, None, qAdicCappedRelativeElement) + norm = MethodType(norm_unram, None, qAdicCappedRelativeElement) + + def matrix_mod_pn(self): + """ + Returns the matrix of right multiplication by the element on + the power basis `1, x, x^2, \ldots, x^{d-1}` for this + extension field. Thus the *rows* of this matrix give the + images of each of the `x^i`. The entries of the matrices are + IntegerMod elements, defined modulo ``p^(self.absprec() / e)``. + + Raises an error if ``self`` has negative valuation. + + EXAMPLES:: + + sage: R. = QqCR(5^5,5) + sage: b = (5 + 15*a)^3 + sage: b.matrix_mod_pn() + [ 125 1125 3375 3375 0] + [ 0 125 1125 3375 3375] + [380500 377125 125 1125 3375] + [380500 367000 377125 125 1125] + [387250 376000 367000 377125 125] + + sage: M = R(0,3).matrix_mod_pn(); M == 0 + True + sage: M.base_ring() + Ring of integers modulo 125 + + Check that :trac:`13617` has been fixed:: + + sage: R(0).matrix_mod_pn() + [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] + """ + if self.ordp < 0: + raise ValueError("self must be integral") + if exactzero(self.ordp): + from sage.matrix.all import matrix + return matrix(ZZ, self.prime_pow.deg, self.prime_pow.deg) + else: + return cmatrix_mod_pn(self.unit, self.ordp + self.relprec, self.ordp, self.prime_pow) + + def _flint_rep(self, var='x'): + """ + Replacement for _ntl_rep for use in printing and debugging. + + EXAMPLES:: + + sage: R. = Qq(27, 4) + sage: (~(1+a))._flint_rep() + 41*x^2 + 40*x + 42 + sage: (1+a)*(41*a^2+40*a+42) + 1 + O(3^4) + """ + return self.prime_pow._new_fmpz_poly(self.unit, var) + + def _flint_rep_abs(self, var='x'): + """ + Replacement for _ntl_rep_abs for use in printing and debugging. + + EXAMPLES:: + + sage: R. = Qq(27, 4) + sage: (~(3+3*a))._flint_rep_abs() + (41*x^2 + 40*x + 42, -1) + sage: (3+3*a)*(41*a^2+40*a+42) + 3 + O(3^5) + sage: (3+3*a)._flint_rep_abs() + (3*x + 3, 0) + """ + if self.ordp < 0: + return self._flint_rep(var), Integer(self.ordp) + cshift(self.prime_pow.poly_flint_rep, self.unit, self.ordp, self.ordp + self.relprec, self.prime_pow, False) + return self.prime_pow._new_fmpz_poly(self.prime_pow.poly_flint_rep, var), Integer(0) + + def __hash__(self): + r""" + Raise a ``TypeError`` since this element is not hashable + (:trac:`11895`.) + + TESTS:: + + sage: K. = Qq(9) + sage: hash(a) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' + + """ + # Eventually, hashing will be disabled for all (non-fixed-mod) p-adic + # elements (#11895), until then, we only to this for types which did + # not support hashing before we switched some elements to FLINT + raise TypeError("unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement'") diff --git a/src/sage/rings/padics/qadic_flint_FM.pxd b/src/sage/rings/padics/qadic_flint_FM.pxd new file mode 100644 index 00000000000..92d240c9144 --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_FM.pxd @@ -0,0 +1,11 @@ +from sage.libs.pari.gen cimport gen as pari_gen +from sage.libs.flint.fmpz_poly cimport fmpz_poly_t +from sage.rings.padics.pow_computer_flint cimport PowComputer_flint_unram +cdef class PowComputer_(PowComputer_flint_unram): + pass +ctypedef fmpz_poly_t celement + +include "FM_template_header.pxi" + +cdef class qAdicFixedModElement(FMElement): + pass diff --git a/src/sage/rings/padics/qadic_flint_FM.pyx b/src/sage/rings/padics/qadic_flint_FM.pyx new file mode 100644 index 00000000000..0075726647e --- /dev/null +++ b/src/sage/rings/padics/qadic_flint_FM.pyx @@ -0,0 +1,81 @@ +from types import MethodType + +include "sage/libs/linkages/padics/fmpz_poly_unram.pxi" +include "sage/libs/linkages/padics/unram_shared.pxi" +include "FM_template.pxi" + +cdef class PowComputer_(PowComputer_flint_unram): + """ + A PowComputer for a fixed-modulus unramified ring. + """ + def __init__(self, Integer prime, long cache_limit, long prec_cap, long ram_prec_cap, bint in_field, poly=None): + """ + Initialization. + + EXAMPLES:: + + sage: R. = ZqFM(125) + sage: type(R.prime_pow) + + sage: R.prime_pow._prec_type + 'fixed-mod' + """ + self._prec_type = 'fixed-mod' + PowComputer_flint_unram.__init__(self, prime, cache_limit, prec_cap, ram_prec_cap, in_field, poly) + +cdef class qAdicFixedModElement(FMElement): + frobenius = MethodType(frobenius_unram, None, qAdicFixedModElement) + trace = MethodType(trace_unram, None, qAdicFixedModElement) + norm = MethodType(norm_unram, None, qAdicFixedModElement) + + def matrix_mod_pn(self): + """ + Returns the matrix of right multiplication by the element on + the power basis `1, x, x^2, \ldots, x^{d-1}` for this + extension field. Thus the *rows* of this matrix give the + images of each of the `x^i`. The entries of the matrices are + IntegerMod elements, defined modulo ``p^(self.absprec() / e)``. + + EXAMPLES:: + + sage: R. = ZqFM(5^5,5) + sage: b = (5 + 15*a)^3 + sage: b.matrix_mod_pn() + [ 125 1125 250 250 0] + [ 0 125 1125 250 250] + [2375 2125 125 1125 250] + [2375 1375 2125 125 1125] + [2875 1000 1375 2125 125] + + sage: M = R(0,3).matrix_mod_pn(); M == 0 + True + sage: M.base_ring() + Ring of integers modulo 3125 + """ + return cmatrix_mod_pn(self.value, self.prime_pow.prec_cap, 0, self.prime_pow) + + def _flint_rep(self, var='x'): + """ + Replacement for _ntl_rep for use in printing and debugging. + + EXAMPLES:: + + sage: R. = ZqFM(27, 4) + sage: (1+a).inverse_of_unit()._flint_rep() + 41*x^2 + 40*x + 42 + sage: (1+a)*(41*a^2+40*a+42) + 1 + O(3^4) + """ + return self.prime_pow._new_fmpz_poly(self.value, var) + + def _flint_rep_abs(self, var='x'): + """ + Replacement for _ntl_rep_abs for use in printing and debugging. + + EXAMPLES:: + + sage: R. = ZqFM(27, 4) + sage: (3+3*a)._flint_rep_abs() + (3*x + 3, 0) + """ + return self._flint_rep(var), Integer(0) diff --git a/src/sage/rings/padics/tutorial.py b/src/sage/rings/padics/tutorial.py index fe2c0dda0c4..9ad500abbde 100644 --- a/src/sage/rings/padics/tutorial.py +++ b/src/sage/rings/padics/tutorial.py @@ -1,5 +1,6 @@ r""" -Introduction to the p-adics +Introduction to the `p`-adics +========================================== This tutorial outlines what you need to know in order to use `p`-adics in Sage effectively. diff --git a/src/sage/rings/polynomial/cyclotomic.pyx b/src/sage/rings/polynomial/cyclotomic.pyx index ba986c5144e..0f0124cdd5d 100644 --- a/src/sage/rings/polynomial/cyclotomic.pyx +++ b/src/sage/rings/polynomial/cyclotomic.pyx @@ -27,7 +27,7 @@ method of univariate polynomial ring objects and the top-level import sys -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from libc.string cimport memset @@ -149,7 +149,7 @@ def cyclotomic_coeffs(nn, sparse=None): if (max_deg)*sizeof(long) > sys.maxsize: raise MemoryError, "Not enough memory to calculate cyclotomic polynomial of %s" % n - cdef long* coeffs = sage_malloc(sizeof(long) * (max_deg+1)) + cdef long* coeffs = sig_malloc(sizeof(long) * (max_deg+1)) if coeffs == NULL: raise MemoryError, "Not enough memory to calculate cyclotomic polynomial of %s" % n memset(coeffs, 0, sizeof(long) * (max_deg+1)) @@ -195,7 +195,7 @@ def cyclotomic_coeffs(nn, sparse=None): else: L = [coeffs[k] for k from offset <= k <= deg] - sage_free(coeffs) + sig_free(coeffs) return L def cyclotomic_value(n, x): @@ -298,7 +298,7 @@ def cyclotomic_value(n, x): P = parent_c(x) try: - return P(pari.polcyclo_eval(n, x).sage()) + return P(pari.polcyclo(n, x).sage()) except Exception: pass one = P(1) @@ -330,7 +330,7 @@ def cyclotomic_value(n, x): return x xd = [x] # the x^d for d | n cdef char mu - cdef char* md = sage_malloc(sizeof(char) * (1 << L)) # the mu(d) for d | n + cdef char* md = sig_malloc(sizeof(char) * (1 << L)) # the mu(d) for d | n try: md[0] = 1 if L & 1: @@ -358,7 +358,7 @@ def cyclotomic_value(n, x): else: den *= xpow - one finally: - sage_free(md) + sig_free(md) try: ans = num / den except ZeroDivisionError: diff --git a/src/sage/rings/polynomial/infinite_polynomial_ring.py b/src/sage/rings/polynomial/infinite_polynomial_ring.py index 48b6095afb2..c3ee136dfd7 100644 --- a/src/sage/rings/polynomial/infinite_polynomial_ring.py +++ b/src/sage/rings/polynomial/infinite_polynomial_ring.py @@ -1020,7 +1020,7 @@ def tensor_with_ring(self, R): sage: R Infinite polynomial ring in a, b over Integer Ring - The following tests against a bug that was fixed at trac ticket #10468:: + The following tests against a bug that was fixed at :trac:`10468`:: sage: R. = InfinitePolynomialRing(QQ) sage: R.tensor_with_ring(QQ) is R @@ -1087,7 +1087,7 @@ def is_field(self, *args, **kwds): sage: R.is_field() False - Ticket #9443:: + :trac:`9443`:: sage: W = PowerSeriesRing(InfinitePolynomialRing(QQ,'a'),'x') sage: W.is_field() @@ -1261,7 +1261,7 @@ def is_integral_domain(self, *args, **kwds): TESTS: - Ticket #9443:: + :trac:`9443`:: sage: W = PolynomialRing(InfinitePolynomialRing(QQ,'a'),2,'x,y') sage: W.is_integral_domain() @@ -1536,7 +1536,7 @@ def tensor_with_ring(self, R): sage: R Infinite polynomial ring in a, b over Integer Ring - The following tests against a bug that was fixed at trac ticket #10468:: + The following tests against a bug that was fixed at :trac:`10468`:: sage: R. = InfinitePolynomialRing(QQ, implementation='sparse') sage: R.tensor_with_ring(QQ) is R diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index a2740da751e..4f75b9b74d5 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1351,7 +1351,19 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): sage: L. = LaurentPolynomialRing(QQ) sage: len({hash(w^i*z^j) for i in [-2..2] for j in [-2..2]}) > 20 True + + Check that :trac:`20490` is fixed:: + + sage: R. = LaurentPolynomialRing(ZZ) + sage: p = a*~a + sage: p._fraction_pair() + (a, a) + sage: p == R.one() + True + sage: hash(p) == hash(R.one()) + True """ + self._normalize() return hash(self._poly) ^ hash(self._mon) cdef _new_c(self): diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index c33c068e9f2..edac652abb1 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -393,7 +393,7 @@ class LaurentPolynomialRing_generic(CommutativeRing, ParentWithGens): EXAMPLES: This base class inherits from :class:`~sage.rings.ring.CommutativeRing`. - Since trac ticket #11900, it is also initialised as such:: + Since :trac:`11900`, it is also initialised as such:: sage: R. = LaurentPolynomialRing(QQ) sage: R.category() @@ -483,7 +483,7 @@ def is_integral_domain(self, proof = True): sage: LaurentPolynomialRing(QQ,2,'x').is_integral_domain() True - The following used to fail; see #7530:: + The following used to fail; see :trac:`7530`:: sage: L = LaurentPolynomialRing(ZZ, 'X') sage: L['Y'] diff --git a/src/sage/rings/polynomial/multi_polynomial.pyx b/src/sage/rings/polynomial/multi_polynomial.pyx index 9860a60800d..684e8ece2d9 100644 --- a/src/sage/rings/polynomial/multi_polynomial.pyx +++ b/src/sage/rings/polynomial/multi_polynomial.pyx @@ -588,7 +588,7 @@ cdef class MPolynomial(CommutativeRingElement): sage: hash(t) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' """ cdef long result = 0 # store it in a c-int and just let the overflowing additions wrap @@ -1554,44 +1554,44 @@ cdef class MPolynomial(CommutativeRingElement): raise NotImplementedError def inverse_mod(self, I): - """ - Returns an inverse of self modulo the polynomial ideal `I`, - namely a multivariate polynomial `f` such that - ``self * f - 1`` belongs to `I`. - - INPUT: - - ``I`` -- an ideal of the polynomial ring in which self lives - - OUTPUT: - - - a multivariate polynomial representing the inverse of ``f`` modulo ``I`` - - EXAMPLES:: - - sage: R. = QQ[] - sage: I = R.ideal(x2**2 + x1 - 2, x1**2 - 1) - sage: f = x1 + 3*x2^2; g = f.inverse_mod(I); g - 1/16*x1 + 3/16 - sage: (f*g).reduce(I) - 1 - - Test a non-invertible element:: - - sage: R. = QQ[] - sage: I = R.ideal(x2**2 + x1 - 2, x1**2 - 1) - sage: f = x1 + x2 - sage: f.inverse_mod(I) - Traceback (most recent call last): - ... - ArithmeticError: element is non-invertible - """ - P = self.parent() - B = I.gens() - try: - XY = P.one().lift((self,) + tuple(B)) - return P(XY[0]) - except ValueError: - raise ArithmeticError, "element is non-invertible" + """ + Returns an inverse of self modulo the polynomial ideal `I`, + namely a multivariate polynomial `f` such that + ``self * f - 1`` belongs to `I`. + + INPUT: + - ``I`` -- an ideal of the polynomial ring in which self lives + + OUTPUT: + + - a multivariate polynomial representing the inverse of ``f`` modulo ``I`` + + EXAMPLES:: + + sage: R. = QQ[] + sage: I = R.ideal(x2**2 + x1 - 2, x1**2 - 1) + sage: f = x1 + 3*x2^2; g = f.inverse_mod(I); g + 1/16*x1 + 3/16 + sage: (f*g).reduce(I) + 1 + + Test a non-invertible element:: + + sage: R. = QQ[] + sage: I = R.ideal(x2**2 + x1 - 2, x1**2 - 1) + sage: f = x1 + x2 + sage: f.inverse_mod(I) + Traceback (most recent call last): + ... + ArithmeticError: element is non-invertible + """ + P = self.parent() + B = I.gens() + try: + XY = P.one().lift((self,) + tuple(B)) + return P(XY[0]) + except ValueError: + raise ArithmeticError, "element is non-invertible" def weighted_degree(self, *weights): """ @@ -1701,6 +1701,71 @@ cdef class MPolynomial(CommutativeRingElement): deg = l return deg + def gcd(self, other): + """ + Return a greatest common divisor of this polynomial and ``other``. + + INPUT: + + - ``other`` -- a polynomial with the same parent as this polynomial + + EXAMPLES:: + + sage: Q. = Frac(QQ['z']) + sage: R. = Q[] + sage: r = x*y - (2*z-1)/(z^2+z+1) * x + y/z + sage: p = r * (x + z*y - 1/z^2) + sage: q = r * (x*y*z + 1) + sage: gcd(p,q) + (z^3 + z^2 + z)*x*y + (-2*z^2 + z)*x + (z^2 + z + 1)*y + + Polynomials over polynomial rings are converted to a simpler polynomial + ring with all variables to compute the gcd:: + + sage: A. = ZZ[] + sage: B. = A[] + sage: r = x*y*z*t+1 + sage: p = r * (x - y + z - t + 1) + sage: q = r * (x*z - y*t) + sage: gcd(p,q) + z*t*x*y + 1 + sage: _.parent() + Multivariate Polynomial Ring in x, y over Multivariate Polynomial Ring in z, t over Integer Ring + + Some multivariate polynomial rings have no gcd implementation:: + + sage: R. =GaussianIntegers()[] + sage: x.gcd(x) + Traceback (most recent call last): + ... + NotImplementedError: GCD is not implemented for multivariate polynomials over Gaussian Integers in Number Field in I with defining polynomial x^2 + 1 + """ + variables = self._parent.variable_names_recursive() + if len(variables) > self._parent.ngens(): + base = self._parent._mpoly_base_ring() + d1 = self._mpoly_dict_recursive() + d2 = other._mpoly_dict_recursive() + ring = PolynomialRing(base, variables) + try: + return self._parent(ring(d1).gcd(ring(d2))) + except (AttributeError, NotImplementedError): + pass + + try: + self._parent._singular_().set_ring() + g = self._singular_().gcd(other._singular_()) + return self._parent(g) + except (TypeError, AttributeError): + pass + + x = self._parent.gens()[-1] + uniself = self.polynomial(x) + unibase = uniself.base_ring() + if hasattr(unibase, "_gcd_univariate_polynomial"): + return self._parent(unibase._gcd_univariate_polynomial(uniself, other.polynomial(x))) + else: + raise NotImplementedError("GCD is not implemented for multivariate polynomials over {}".format(self._parent._mpoly_base_ring())) + cdef remove_from_tuple(e, int ind): w = list(e) del w[ind] diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 53abd654fd8..6f20a037d3d 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -1035,7 +1035,7 @@ def is_term(self): product of generators times some coefficient, which need not be 1. - Use is_monomial to require that the coefficent be 1. + Use :meth:`is_monomial` to require that the coefficient be 1. EXAMPLES:: diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 5d80d9a97d4..1d8a9c926f7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -1971,12 +1971,12 @@ def transformed_basis(self, algorithm="gwalk", other_ring=None, singular=singula @libsingular_gb_standard_options def elimination_ideal(self, variables): r""" - Returns the elimination ideal this ideal with respect to the + Return the elimination ideal of this ideal with respect to the variables given in ``variables``. INPUT: - - ``variables`` - a list or tuple of variables in ``self.ring()`` + - ``variables`` -- a list or tuple of variables in ``self.ring()`` EXAMPLE:: @@ -3647,6 +3647,15 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal 5*b*c + 156*c^2 + 112*b + 948*c, 50*c^2 + 600*b + 650*c, a + 2*b + 2*c + 999, 125*b] + :: + + sage: R. = PolynomialRing(Zmod(2233497349584)) + sage: I = R.ideal([z*(x-3*y), 3^2*x^2-y*z, z^2+y^2]) + sage: I.groebner_basis() + verbose 0 (...: multi_polynomial_ideal.py, groebner_basis) Warning: falling back to very slow toy implementation. + [2*z^4, y*z^2 + 81*z^3, 248166372176*z^3, 9*x^2 - y*z, y^2 + z^2, x*z + + 2233497349581*y*z, 248166372176*y*z] + Sage also supports local orderings:: sage: P. = PolynomialRing(QQ,3,order='negdegrevlex') @@ -3724,7 +3733,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal ch = self.ring().base_ring().characteristic() R = self.ring().change_ring(ZZ) - I = R.ideal([R(f) for f in self.gens()+(ch,)]) + I = R.ideal([R(f) for f in self.gens()] + [R(ch)]) gb = toy_d_basis.d_basis(I, *args, **kwds) diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 6802d8cca68..4fa3e73e8f8 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -143,7 +143,7 @@ TESTS:: sage: p(u/v) (u + v)/v -Check if #6160 is fixed:: +Check if :trac:`6160` is fixed:: sage: x=var('x') sage: K. = NumberField(x-1728) @@ -164,7 +164,7 @@ Check if #6160 is fixed:: # * pNext and pIter don't need currRing # * p_Normalize apparently needs currRing -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" # singular types @@ -179,7 +179,7 @@ from sage.libs.singular.decl cimport ( pDivide, p_IsConstant, p_ExpVectorEqual, p_String, p_LmInit, n_Copy, p_IsUnit, pInvers, p_Head, idInit, fast_map, id_Delete, pIsHomogeneous, pHomogen, p_Totaldegree, singclap_pdivide, singclap_factorize, - delete, idLift, IDELEMS, On, Off, SW_USE_CHINREM_GCD, SW_USE_EZGCD, + idLift, IDELEMS, On, Off, SW_USE_CHINREM_GCD, SW_USE_EZGCD, p_LmIsConstant, pTakeOutComp1, singclap_gcd, pp_Mult_qq, p_GetMaxExp, pLength, kNF, singclap_isSqrFree, p_Neg, p_Minus_mm_Mult_qq, p_Plus_mm_Mult_qq, pDiff, singclap_resultant, p_Normalize, @@ -340,7 +340,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): TEST: Make sure that a faster coercion map from the base ring is used; - see trac ticket #9944:: + see :trac:`9944`:: sage: R. = PolynomialRing(ZZ) sage: R.coerce_map_from(R.base_ring()) @@ -551,13 +551,13 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): sage: P("31367566080") 31367566080 - Check if #7582 is fixed:: + Check if :trac:`7582` is fixed:: sage: R. = PolynomialRing(CyclotomicField(2),3) sage: R.coerce(1) 1 - Check if #6160 is fixed:: + Check if :trac:`6160` is fixed:: sage: x=var('x') sage: K. = NumberField(x-1728) @@ -697,7 +697,7 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): sage: S(5*x*y + x + 17*y) xx + 2*yy - See #5292:: + See :trac:`5292`:: sage: R. = QQ[]; S. = QQ[]; F = FractionField(S); sage: x in S @@ -705,14 +705,14 @@ cdef class MPolynomialRing_libsingular(MPolynomialRing_generic): sage: x in F False - Check if #8228 is fixed:: + Check if :trac:`8228` is fixed:: sage: P. = Zmod(10)[]; P(0) 0 sage: P. = Zmod(2^10)[]; P(0) 0 - And trac #7597 is fixed if this doesn't segfault:: + And :trac:`7597` is fixed if this does not segfault:: sage: F2 = GF(2) sage: F. = GF(2^8) @@ -2065,7 +2065,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: f(5,3,10) 4580 - See #8502:: + See :trac:`8502`:: sage: x = polygen(QQ) sage: K. = NumberField(x^2+47) @@ -2559,7 +2559,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: x.degree(x), y.degree(y), z.degree(z) (1, 1, 1) - The following example is inspired by trac 11652:: + The following example is inspired by :trac:`11652`:: sage: R. = ZZ[] sage: poly = p+q^2+t^3 @@ -2777,7 +2777,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn cdef int i cdef int flag cdef int gens = self._parent.ngens() - cdef int *exps = sage_malloc(sizeof(int)*gens) + cdef int *exps = sig_malloc(sizeof(int)*gens) for i from 0<=i = K[] sage: f = (a + 1)*x^145*y^84 + (a + 1)*x^205*y^17 + x^32*y^112 + x^92*y^45 sage: for i in range(100): - ... assert len(f.factor()) == 4 + ....: assert len(f.factor()) == 4 + + Test for :trac:`20435`:: + + sage: x,y = polygen(ZZ,'x,y') + sage: p = x**2-y**2 + sage: z = factor(p); z + (x - y) * (x + y) + sage: z[0][0].parent() + Multivariate Polynomial Ring in x, y over Integer Ring """ cdef ring *_ring = self._parent_ring cdef poly *ptemp @@ -4217,7 +4226,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn try: frac_field = self._parent._base.fraction_field() F = self.change_ring(frac_field).factor() - FF = [(f[0].change_ring(self._parent), f[1]) for f in F] + FF = [(self._parent(f[0]), f[1]) for f in F] U = self._parent._base(F.unit()).factor() return Factorization(list(U) + FF, unit=U.unit()) except Exception: @@ -4232,7 +4241,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn iv = NULL sig_on() if _ring!=currRing: rChangeCurrRing(_ring) # singclap_factorize - I = singclap_factorize ( ptemp, &iv , 0) #delete iv at some pointa + I = singclap_factorize ( ptemp, &iv , 0) sig_off() ivv = iv.ivGetVec() @@ -4243,7 +4252,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn F = Factorization(v,unit) F.sort() - delete(iv) + del iv id_Delete(&I,_ring) return F @@ -5298,7 +5307,7 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn sage: f.numerator() / f.denominator() == f True - The following tests against a bug that has been fixed in trac ticket #11780:: + The following tests against a bug fixed in :trac:`11780`:: sage: P. = ZZ[] sage: Q. = QQ[] diff --git a/src/sage/rings/polynomial/multi_polynomial_ring.py b/src/sage/rings/polynomial/multi_polynomial_ring.py index 60146b8a43b..f0fe2c076ae 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring.py +++ b/src/sage/rings/polynomial/multi_polynomial_ring.py @@ -521,114 +521,77 @@ def __call__(self, x, check=True): c = self.base_ring()(x) return MPolynomial_polydict(self, {self._zero_tuple:c}) -class MPolynomialRing_polydict_domain(IntegralDomain, - MPolynomialRing_polydict): - def __init__(self, base_ring, n, names, order): - order = TermOrder(order,n) - MPolynomialRing_polydict.__init__(self, base_ring, n, names, order) - - def is_integral_domain(self, proof = True): - return True +### The following methods are handy for implementing Groebner +### basis algorithms. They do only superficial type/sanity checks +### and should be called carefully. - def is_field(self, proof = True): - if self.ngens() == 0: - return self.base_ring().is_field(proof) - return False - - def ideal(self, *gens, **kwds): - """ - Create an ideal in this polynomial ring. - """ - do_coerce = False - if len(gens) == 1: - from sage.rings.ideal import is_Ideal - if is_Ideal(gens[0]): - if gens[0].ring() is self: - return gens[0] - gens = gens[0].gens() - elif isinstance(gens[0], (list, tuple)): - gens = gens[0] - if not self._has_singular: - # pass through - MPolynomialRing_generic.ideal(self,gens,**kwds) - if is_SingularElement(gens): - gens = list(gens) - do_coerce = True - if is_Macaulay2Element(gens): - gens = list(gens) - do_coerce = True - elif not isinstance(gens, (list, tuple)): - gens = [gens] - if ('coerce' in kwds and kwds['coerce']) or do_coerce: - gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly! - return multi_polynomial_ideal.MPolynomialIdeal(self, gens, **kwds) + def monomial_quotient(self, f, g, coeff=False): + r""" + Return ``f/g``, where both ``f`` and ``g`` are treated as monomials. - def monomial_quotient(self,f, g, coeff=False): - """ - Return f/g, where both f and g are treated as monomials. Coefficients are ignored by default. INPUT: + - ``f`` - monomial. - - ``f`` - monomial - - - ``g`` - monomial + - ``g`` - monomial. - ``coeff`` - divide coefficients as well (default: - False) + False). + OUTPUT: monomial. EXAMPLE:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: P.=MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex') - sage: P.monomial_quotient(3/2*x*y,x) + sage: P. = MPolynomialRing_polydict_domain(QQ, 3, order='degrevlex') + sage: P.monomial_quotient(3/2*x*y, x) y :: - sage: P.monomial_quotient(3/2*x*y,2*x,coeff=True) + sage: P.monomial_quotient(3/2*x*y, 2*x, coeff=True) 3/4*y TESTS:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: R.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: P.monomial_quotient(x*y,x) + sage: R. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P.monomial_quotient(x*y, x) y :: - sage: P.monomial_quotient(x*y,R.gen()) + sage: P.monomial_quotient(x*y, R.gen()) y :: - sage: P.monomial_quotient(P(0),P(1)) + sage: P.monomial_quotient(P(0), P(1)) 0 :: - sage: P.monomial_quotient(P(1),P(0)) + sage: P.monomial_quotient(P(1), P(0)) Traceback (most recent call last): ... ZeroDivisionError :: - sage: P.monomial_quotient(P(3/2),P(2/3), coeff=True) + sage: P.monomial_quotient(P(3/2), P(2/3), coeff=True) 9/4 :: - sage: P.monomial_quotient(x,y) # Note the wrong result + sage: P.monomial_quotient(x, y) # Note the wrong result x*y^-1 :: - sage: P.monomial_quotient(x,P(1)) + sage: P.monomial_quotient(x, P(1)) x .. note:: @@ -645,7 +608,7 @@ def monomial_quotient(self,f, g, coeff=False): raise ZeroDivisionError if not coeff: - coeff= self.base_ring()(1) + coeff = self.base_ring()(1) else: coeff = self.base_ring()(f.dict().values()[0] / g.dict().values()[0]) @@ -664,41 +627,41 @@ def monomial_lcm(self, f, g): INPUT: + - ``f`` - monomial. - - ``f`` - monomial - - - ``g`` - monomial + - ``g`` - monomial. + OUTPUT: monomial. EXAMPLE:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: P.monomial_lcm(3/2*x*y,x) + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P.monomial_lcm(3/2*x*y, x) x*y TESTS:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: R.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: P.monomial_lcm(x*y,R.gen()) + sage: R. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P.monomial_lcm(x*y, R.gen()) x*y :: - sage: P.monomial_lcm(P(3/2),P(2/3)) + sage: P.monomial_lcm(P(3/2), P(2/3)) 1 :: - sage: P.monomial_lcm(x,P(1)) + sage: P.monomial_lcm(x, P(1)) x """ one = self.base_ring()(1) - f=f.dict().keys()[0] - g=g.dict().keys()[0] + f = f.dict().keys()[0] + g = g.dict().keys()[0] length = len(f) @@ -713,12 +676,12 @@ def monomial_lcm(self, f, g): return res def monomial_reduce(self, f, G): - """ - Try to find a g in G where g.lm() divides f. If found (g,flt) is - returned, (0,0) otherwise, where flt is f/g.lm(). + r""" + Try to find a ``g`` in ``G`` where ``g.lm()`` divides ``f``. - It is assumed that G is iterable and contains ONLY elements in - self. + If found, ``(flt,g)`` is returned, ``(0,0)`` otherwise, where + ``flt`` is ``f/g.lm()``. It is assumed that ``G`` is iterable and contains + ONLY elements in this ring. INPUT: @@ -733,16 +696,25 @@ def monomial_reduce(self, f, G): sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') sage: f = x*y^2 - sage: G = [ 3/2*x^3 + y^2 + 1/2, 1/4*x*y + 2/7, P(1/2) ] + sage: G = [3/2*x^3 + y^2 + 1/2, 1/4*x*y + 2/7, P(1/2)] sage: P.monomial_reduce(f,G) (y, 1/4*x*y + 2/7) + :: + + sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain + sage: P. = MPolynomialRing_polydict_domain(Zmod(23432),3, order='degrevlex') + sage: f = x*y^2 + sage: G = [3*x^3 + y^2 + 2, 4*x*y + 7, P(2)] + sage: P.monomial_reduce(f,G) + (y, 4*x*y + 7) + TESTS:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') sage: f = x*y^2 - sage: G = [ 3/2*x^3 + y^2 + 1/2, 1/4*x*y + 2/7, P(1/2) ] + sage: G = [3/2*x^3 + y^2 + 1/2, 1/4*x*y + 2/7, P(1/2)] :: @@ -767,19 +739,19 @@ def monomial_reduce(self, f, G): def monomial_divides(self, a, b): """ - Return False if a does not divide b and True otherwise. + Return False if ``a`` does not divide ``b`` and True otherwise. INPUT: + - ``a`` - monomial. - - ``a`` - monomial - - - ``b`` - monomial + - ``b`` - monomial. + OUTPUT: Boolean. EXAMPLES:: - sage: P.=PolynomialRing(ZZ,3, order='degrevlex') + sage: P. = PolynomialRing(ZZ,3, order='degrevlex') sage: P.monomial_divides(x*y*z, x^3*y^2*z^4) True sage: P.monomial_divides(x^3*y^2*z^4, x*y*z) @@ -787,20 +759,19 @@ def monomial_divides(self, a, b): TESTS:: - sage: P.=PolynomialRing(ZZ,3, order='degrevlex') + sage: P. = PolynomialRing(ZZ,3, order='degrevlex') sage: P.monomial_divides(P(1), P(0)) True sage: P.monomial_divides(P(1), x) True """ - if not b: return True if not a: raise ZeroDivisionError - a=a.dict().keys()[0] - b=b.dict().keys()[0] + a = a.dict().keys()[0] + b = b.dict().keys()[0] for i in b.common_nonzero_positions(a): if b[i] - a[i] < 0: @@ -808,22 +779,23 @@ def monomial_divides(self, a, b): return True def monomial_pairwise_prime(self, h, g): - """ - Return True if h and g are pairwise prime. Both are treated as - monomials. + r""" + Return True if ``h`` and ``g`` are pairwise prime. - INPUT: + Both are treated as monomials. + INPUT: - - ``h`` - monomial + - ``h`` - monomial. - - ``g`` - monomial + - ``g`` - monomial. + OUTPUT: Boolean. EXAMPLES:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') sage: P.monomial_pairwise_prime(x^2*z^3, y^4) True @@ -835,8 +807,8 @@ def monomial_pairwise_prime(self, h, g): TESTS:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') - sage: Q.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: Q. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') sage: P.monomial_pairwise_prime(x^2*z^3, Q('y^4')) True @@ -861,23 +833,21 @@ def monomial_pairwise_prime(self, h, g): return self.monomial_lcm(g,h) == g*h - def monomial_all_divisors(self,t): - """ - Return a list of all monomials that divide t, coefficients are + def monomial_all_divisors(self, t): + r""" + Return a list of all monomials that divide ``t``, coefficients are ignored. INPUT: + - ``t`` - a monomial. - - ``t`` - a monomial - - - OUTPUT: a list of monomials + OUTPUT: a list of monomials. EXAMPLE:: sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain - sage: P.=MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') + sage: P. = MPolynomialRing_polydict_domain(QQ,3, order='degrevlex') sage: P.monomial_all_divisors(x^2*z^3) [x, x^2, z, x*z, x^2*z, z^2, x*z^2, x^2*z^2, z^3, x*z^3, x^2*z^3] @@ -893,7 +863,7 @@ def addwithcarry(tempvector, maxvector, pos): return tempvector if not t.is_monomial(): - raise TypeError("Only monomials are supported") + raise TypeError("only monomials are supported") R = self one = self.base_ring()(1) @@ -913,3 +883,47 @@ def addwithcarry(tempvector, maxvector, pos): +class MPolynomialRing_polydict_domain(IntegralDomain, + MPolynomialRing_polydict): + def __init__(self, base_ring, n, names, order): + order = TermOrder(order,n) + MPolynomialRing_polydict.__init__(self, base_ring, n, names, order) + + def is_integral_domain(self, proof = True): + return True + + def is_field(self, proof = True): + if self.ngens() == 0: + return self.base_ring().is_field(proof) + return False + + def ideal(self, *gens, **kwds): + """ + Create an ideal in this polynomial ring. + """ + do_coerce = False + if len(gens) == 1: + from sage.rings.ideal import is_Ideal + if is_Ideal(gens[0]): + if gens[0].ring() is self: + return gens[0] + gens = gens[0].gens() + elif isinstance(gens[0], (list, tuple)): + gens = gens[0] + if not self._has_singular: + # pass through + MPolynomialRing_generic.ideal(self,gens,**kwds) + if is_SingularElement(gens): + gens = list(gens) + do_coerce = True + if is_Macaulay2Element(gens): + gens = list(gens) + do_coerce = True + elif not isinstance(gens, (list, tuple)): + gens = [gens] + if ('coerce' in kwds and kwds['coerce']) or do_coerce: + gens = [self(x) for x in gens] # this will even coerce from singular ideals correctly! + return multi_polynomial_ideal.MPolynomialIdeal(self, gens, **kwds) + + + diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx index f25a79df93b..50c3d9f0f02 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_generic.pyx @@ -45,7 +45,7 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): TESTS: - Check that containment works correctly (ticket #10355):: + Check that containment works correctly (:trac:`10355`):: sage: A1. = PolynomialRing(QQ) sage: A2. = PolynomialRing(QQ) @@ -1028,13 +1028,13 @@ cdef class MPolynomialRing_generic(sage.rings.ring.CommutativeRing): REFERENCES: - .. [CLO] D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. + .. [CLO] \D. Cox, J. Little, D. O'Shea. Using Algebraic Geometry. Springer, 2005. - .. [Can] J. Canny. Generalised characteristic polynomials. + .. [Can] \J. Canny. Generalised characteristic polynomials. J. Symbolic Comput. Vol. 9, No. 3, 1990, 241--250. - .. [Mac] F.S. Macaulay. The algebraic theory of modular systems + .. [Mac] \F.S. Macaulay. The algebraic theory of modular systems Cambridge university press, 1916. AUTHORS: diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index 955d809f102..da41cb0e86e 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -145,7 +145,7 @@ would be called ``Ideal`` but an ideal is a very distinct object from its generators and thus this is not an ideal in Sage. -.. [BPW06] J. Buchmann, A. Pychkine, R.-P. Weinmann +.. [BPW06] \J. Buchmann, A. Pychkine, R.-P. Weinmann *Block Ciphers Sensitive to Groebner Basis Attacks* in Topics in Cryptology -- CT RSA'06; LNCS 3860; pp. 313--331; Springer Verlag 2006; pre-print available at http://eprint.iacr.org/2005/200 diff --git a/src/sage/rings/polynomial/padics/polynomial_padic.py b/src/sage/rings/polynomial/padics/polynomial_padic.py index c99fe1e2423..da9a0e65904 100644 --- a/src/sage/rings/polynomial/padics/polynomial_padic.py +++ b/src/sage/rings/polynomial/padics/polynomial_padic.py @@ -73,6 +73,68 @@ def _repr(self, name=None): s += (x + var) return s or "0" + def content(self): + """ + Compute the content of this polynomial. + + OUTPUT: + + If this is the zero polynomial, return the constant coefficient. + Otherwise, since the content is only defined up to a unit, return the + content as `\pi^k` with maximal precision where `k` is the minimal + valuation of any of the coefficients. + + EXAMPLES:: + + sage: K = Zp(13,7) + sage: R. = K[] + sage: f = 13^7*t^3 + K(169,4)*t - 13^4 + sage: f.content() + 13^2 + O(13^9) + sage: R(0).content() + 0 + sage: f = R(K(0,3)); f + (O(13^3)) + sage: f.content() + O(13^3) + + sage: P. = ZZ[] + sage: f = x + 2 + sage: f.content() + 1 + sage: fp = f.change_ring(pAdicRing(2, 10)) + sage: fp + (1 + O(2^10))*x + (2 + O(2^11)) + sage: fp.content() + 1 + O(2^10) + sage: (2*fp).content() + 2 + O(2^11) + + Over a field it would be sufficient to return only zero or one, as the + content is only defined up to multiplication with a unit. However, we + return `\pi^k` where `k` is the minimal valuation of any coefficient:: + + sage: K = Qp(13,7) + sage: R. = K[] + sage: f = 13^7*t^3 + K(169,4)*t - 13^-4 + sage: f.content() + 13^-4 + O(13^3) + sage: f = R.zero() + sage: f.content() + 0 + sage: f = R(K(0,3)) + sage: f.content() + O(13^3) + sage: f = 13*t^3 + K(0,1)*t + sage: f.content() + 13 + O(13^8) + + """ + if self.is_zero(): + return self[0] + else: + return self.base_ring()(self.base_ring().prime_pow(min([x.valuation() for x in self.coefficients(sparse=False)]))) + def factor(self): """ Return the factorization of this polynomial. 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 93a023ac9d5..5725c316c31 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 @@ -29,10 +29,11 @@ ZZ = sage.rings.integer_ring.ZZ PrecisionError = precision_error.PrecisionError Integer = sage.rings.integer.Integer -Polynomial_generic_domain = sage.rings.polynomial.polynomial_element_generic.Polynomial_generic_domain Polynomial_integer_dense = sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_dense_ntl +Polynomial_generic_cdv = sage.rings.polynomial.polynomial_element_generic.Polynomial_generic_cdv -class Polynomial_padic_capped_relative_dense(Polynomial_generic_domain, Polynomial_padic): + +class Polynomial_padic_capped_relative_dense(Polynomial_generic_cdv, Polynomial_padic): def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, absprec = infinity, relprec = infinity): """ TESTS:: @@ -53,6 +54,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct = False, """ Polynomial.__init__(self, parent, is_gen=is_gen) + self._polygon = None parentbr = parent.base_ring() from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing if construct: @@ -160,9 +162,8 @@ def _new_constant_poly(self, a, P): sage: R. = Zp(5)[] sage: t._new_constant_poly(O(5),R) (O(5)) - """ - return self.__class__(P,[a], check=False) + return self.__class__(P, [a], check=False) def _normalize(self): # Currently slow: need to optimize @@ -292,12 +293,13 @@ def _getvalpoly(self, n): def list(self): """ - Returns a list of coefficients of self. + Return a list of coefficients of ``self``. - NOTE: - The length of the list returned may be greater - than expected since it includes any leading zeros - that have finite absolute precision. + .. NOTE:: + + The length of the list returned may be greater + than expected since it includes any leading zeros + that have finite absolute precision. EXAMPLES:: @@ -311,61 +313,22 @@ def list(self): 13^2 + O(13^9), 0, 2 + O(13^7)] - """ - + """ if self._list is None: self._comp_list() return list(self._list) - def content(self): - """ - Returns the content of self. - - The content is returned to maximum precision: since it's only - defined up to a unit, we can choose p^k as the representative. - - Returns an error if the base ring is actually a field: this is - probably not a function you want to be using then, since any - nonzero answer will be correct. - - The content of the exact zero polynomial is zero. - - EXAMPLES:: - - sage: K = Zp(13,7) - sage: R. = K[] - sage: a = 13^7*t^3 + K(169,4)*t - 13^4 - sage: a.content() - 13^2 + O(13^9) - sage: R(0).content() - 0 - sage: P. = ZZ[] - sage: f = x + 2 - sage: f.content() - 1 - sage: fp = f.change_ring(pAdicRing(2, 10)) - sage: fp - (1 + O(2^10))*x + (2 + O(2^11)) - sage: fp.content() - 1 + O(2^10) - sage: (2*fp).content() - 2 + O(2^11) - """ - if self.base_ring().is_field(): - raise TypeError("ground ring is a field. Answer is only defined up to units.") - if self._normalized: - return self.base_ring()(self.base_ring().prime_pow(self._valbase)) - if self._valaddeds is None: - self._comp_valaddeds() - return self.base_ring()(self.base_ring().prime_pow(min(self._valaddeds) + self._valbase)) - def lift(self): """ - Returns an integer polynomial congruent to this one modulo the precision of each coefficient. + Return an integer polynomial congruent to this one modulo the + precision of each coefficient. - NOTE: The lift that is returned will not necessarily be the same for polynomials with - the same coefficients (ie same values and precisions): it will depend on how - the polynomials are created. + .. NOTE:: + + The lift that is returned will not necessarily be the same + for polynomials with the same coefficients (i.e. same values + and precisions): it will depend on how the polynomials are + created. EXAMPLES:: @@ -379,6 +342,9 @@ 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:: @@ -438,7 +404,7 @@ def __getitem__(self, n): def _add_(self, right): """ - Returns the sum of self and right. + Return the sum of ``self`` and ``right``. EXAMPLES:: @@ -472,7 +438,7 @@ def _add_(self, right): def _sub_(self, right): """ - Returns the sum of self and right. + Return the difference of ``self`` and ``right``. EXAMPLES:: @@ -506,7 +472,7 @@ def _sub_(self, right): def _mul_(self, right): r""" - Multiplies self and right. + Multiplies ``self`` and ``right``. ALGORITHM: We use an algorithm thought up by Joe Wetherell to find the precisions of the product. It works as follows: @@ -587,7 +553,7 @@ def _lmul_(self, right): def _rmul_(self, left): """ - Returns self multiplied by a constant + Return ``self`` multiplied by a constant. EXAMPLES:: @@ -614,7 +580,7 @@ def _rmul_(self, left): def _neg_(self): """ - Returns the negation of self. + Return the negation of ``self``. EXAMPLES:: @@ -628,7 +594,7 @@ def _neg_(self): def lshift_coeffs(self, shift, no_list = False): """ - Returns a new polynomials whose coefficients are multiplied by p^shift. + Return a new polynomials whose coefficients are multiplied by p^shift. EXAMPLES:: @@ -647,7 +613,7 @@ def lshift_coeffs(self, shift, no_list = False): def rshift_coeffs(self, shift, no_list = False): """ - Returns a new polynomial whose coefficients are p-adiclly + Return a new polynomial whose coefficients are p-adically shifted to the right by shift. NOTES: Type Qp(5)(0).__rshift__? for more information. @@ -690,7 +656,9 @@ def rshift_coeffs(self, shift, no_list = False): def _unsafe_mutate(self, n, value): """ - It's a really bad idea to use this function for p-adic polynomials. There are speed issues, and it may not be bug-free currently. + It's a really bad idea to use this function for p-adic + polynomials. There are speed issues, and it may not be + bug-free currently. """ n = int(n) value = self.base_ring()(value) @@ -752,54 +720,112 @@ def _unsafe_mutate(self, n, value): zero = self._base_ring()(0) self._list.extend([zero] * (n - len(self._list)) + [value]) - def _pari_(self, variable = None): + def _pari_(self, variable=None): + """ + Return ``self`` as a Pari object. + """ if variable is None: variable = self.parent().variable_name() return pari(self.list()).Polrev(variable) def __copy__(self): + """ + Return a copy of ``self``. + """ return Polynomial_padic_capped_relative_dense(self.parent(), (copy.copy(self._poly), self._valbase, copy.copy(self._relprecs), self._normalized, copy.copy(self._valaddeds), copy.copy(self._list)), construct = True) - def degree(self): + def degree(self, secure=False): """ - Returns the degree of self, i.e., the largest $n$ so that the - coefficient of $x^n$ does not compare equal to $0$. + Return the degree of ``self``. + + INPUT: + + - secure -- a boolean (default: ``False``) + + If ``secure`` is ``True`` and the degree of this polynomial + is not determined (because the leading coefficient is + indistinguishable from 0), an error is raised. + + If ``secure`` is ``False``, the returned value is the largest + $n$ so that the coefficient of $x^n$ does not compare equal + to $0$. EXAMPLES:: sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.degree() + 1 + sage: (f-T).degree() + 0 + sage: (f-T).degree(secure=True) + Traceback (most recent call last): + ... + PrecisionError: the leading coefficient is indistinguishable from 0 + sage: x = O(3^5) - sage: li =[3^i * x for i in range(0,5)]; li + sage: li = [3^i * x for i in range(0,5)]; li [O(3^5), O(3^6), O(3^7), O(3^8), O(3^9)] - sage: R. = K[] sage: f = R(li); f (O(3^9))*T^4 + (O(3^8))*T^3 + (O(3^7))*T^2 + (O(3^6))*T + (O(3^5)) sage: f.degree() -1 + sage: f.degree(secure=True) + Traceback (most recent call last): + ... + PrecisionError: the leading coefficient is indistinguishable from 0 """ self._normalize() - return Integer(self._poly.degree()) + deg = Integer(self._poly.degree()) + if secure and deg < self.prec_degree(): + raise PrecisionError("the leading coefficient is " + "indistinguishable from 0") + return deg def prec_degree(self): """ - Returns the largest $n$ so that precision information is + Return the largest $n$ so that precision information is stored about the coefficient of $x^n$. Always greater than or equal to degree. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.prec_degree() + 1 """ return len(self._relprecs) - 1 def precision_absolute(self, n = None): """ - Returns absolute precision information about self. + Return absolute precision information about ``self``. INPUT: - self -- a p-adic polynomial - n -- None or an integer (default None). + + ``self`` -- a p-adic polynomial + + n -- ``None`` or an integer (default ``None``). OUTPUT: - If n == None, returns a list of absolute precisions of coefficients. Otherwise, - returns the absolute precision of the coefficient of x^n. + + If n == None, returns a list of absolute precisions of + coefficients. Otherwise, returns the absolute precision of + the coefficient of x^n. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.precision_absolute() + [10, 10] """ if n is None: return [c + self._valbase for c in self._relprecs] @@ -807,15 +833,28 @@ def precision_absolute(self, n = None): def precision_relative(self, n = None): """ - Returns relative precision information about self. + Return relative precision information about ``self``. INPUT: - self -- a p-adic polynomial - n -- None or an integer (default None). + + ``self`` -- a p-adic polynomial + + n -- ``None`` or an integer (default ``None``). OUTPUT: - If n == None, returns a list of relative precisions of coefficients. Otherwise, - returns the relative precision of the coefficient of x^n. + + If n == None, returns a list of relative precisions of + coefficients. Otherwise, returns the relative precision of + the coefficient of x^n. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.precision_relative() + [10, 10] """ if n is None: self._normalize() @@ -828,40 +867,64 @@ def precision_relative(self, n = None): else: return self._relprecs[n] - self._valaddeds[n] - def valuation_of_coefficient(self, n = None): + def valuation_of_coefficient(self, n=None): """ - Returns valuation information about self's coefficients. + Return valuation information about ``self``'s coefficients. INPUT: - self -- a p-adic polynomial - n -- None or an integer (default None). + + ``self`` -- a p-adic polynomial + + n -- ``None`` or an integer (default ``None``). OUTPUT: + If n == None, returns a list of valuations of coefficients. Otherwise, returns the valuation of the coefficient of x^n. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.valuation_of_coefficient(1) + 0 """ + if self._valaddeds is None: + self._comp_valaddeds() if n is None: self._normalize() - return [c + self._valbase for c in self._valadded] + return [ c + self._valbase for c in self._valaddeds ] n = int(n) if n < 0 or n >= len(self._relprecs): return infinity - if self._valaddeds is None: - return self._valbase + self._poly[n].valuation(self.base_ring().prime()) - else: - return self._valbase + self._valaddeds[n] + return self._valbase + self._valaddeds[n] - def valuation(self, val_of_var = None): + def valuation(self, val_of_var=None): """ - Returns the valuation of self + Return the valuation of ``self``. INPUT: - self -- a p-adic polynomial - val_of_var -- None or a rational (default None). + + ``self`` -- a p-adic polynomial + + val_of_var -- ``None`` or a rational (default ``None``). OUTPUT: - If val_of_var == None, returns the largest power of the variable dividing self. Otherwise, - returns the valuation of self where the variable is assigned valuation val_of_var + + If val_of_var == None, returns the largest power of the + variable dividing self. Otherwise, returns the valuation of + ``self`` where the variable is assigned valuation val_of_var + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.valuation() + 0 """ if val_of_var is None: return self._poly.valuation() @@ -869,12 +932,16 @@ def valuation(self, val_of_var = None): self._comp_valaddeds() return self._valbase + min([self._valaddeds[i] + val_of_var * i for i in range(len(self._valaddeds))]) - def reverse(self, n = None): + def reverse(self, n=None): """ - Returns a new polynomial whose coefficients are the reversed coefficients of self, where self is considered as a polynomial of degree n. + Return a new polynomial whose coefficients are the reversed + coefficients of ``self``, where ``self`` is considered as a + polynomial of degree n. + + If n is ``None``, defaults to the degree of ``self``. - If n is None, defaults to the degree of self. - If n is smaller than the degree of self, some coefficients will be discarded. + If n is smaller than the degree of ``self``, some coefficients + will be discarded. EXAMPLES:: @@ -895,19 +962,19 @@ def reverse(self, n = None): """ if n is None: n = self._poly.degree() - zzlist = self._poly.list()[:(n+1)] + [0] * (n - self._poly.degree()) + zzlist = self._poly.list()[:(n + 1)] + [0] * (n - self._poly.degree()) zzlist.reverse() - relprec = self._relprecs[:(n+1)] + [infinity] * (n - self.prec_degree()) + relprec = self._relprecs[:(n + 1)] + [infinity] * (n - self.prec_degree()) relprec.reverse() if self._valaddeds is None: valadded = None else: - valadded = self._valaddeds[:(n+1)] + [infinity] * (n - self.prec_degree()) + valadded = self._valaddeds[:(n + 1)] + [infinity] * (n - self.prec_degree()) valadded.reverse() if self._list is None: L = None else: - L = self._list[:(n+1)] + [self.base_ring()(0)] * (n - self.prec_degree()) + L = self._list[:(n + 1)] + [self.base_ring()(0)] * (n - self.prec_degree()) L.reverse() return Polynomial_padic_capped_relative_dense(self.parent(), (self._poly.parent()(zzlist), self._valbase, relprec, self._normalized, valadded, L), construct = True) @@ -915,7 +982,10 @@ def rescale(self, a): r""" Return f(a*X) - NOTE: Need to write this function for integer polynomials before this works. + .. TODO:: + + Need to write this function for integer polynomials before + this works. EXAMPLES:: @@ -950,18 +1020,35 @@ def rescale(self, a): zzpoly = self._poly.rescale(Integer(a)) return Polynomial_padic_capped_relative_dense(self.parent(), (zzpoly, self._valbase, relprec, False, valadded, None), construct = True) - def quo_rem(self, right): - return self._quo_rem_naive(right) + def quo_rem(self, right, secure=False): + """ + Return the quotient and remainder in division of ``self`` by ``right``. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2 + sage: g = T**4 + 3*T+22 + sage: g.quo_rem(f) + ((1 + O(3^10))*T^3 + (1 + 2*3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + O(3^10))*T^2 + (1 + 3 + O(3^10))*T + (1 + 3 + 2*3^2 + 2*3^3 + 2*3^4 + 2*3^5 + 2*3^6 + 2*3^7 + 2*3^8 + 2*3^9 + O(3^10)), + (2 + 3 + 3^3 + O(3^10))) + """ + return self._quo_rem_list(right, secure=secure) def _quo_rem_naive(self, right): """ - An implementation of quo_rem that doesn't have good run-time or precision characteristics. + An implementation of quo_rem that doesn't have good run-time + or precision characteristics. + + A better one is :meth:`_quo_rem_list`. """ K = self.base_ring().fraction_field() f = self.base_extend(K) g = right.base_extend(K) if g == 0: - raise ZeroDivisionError("cannot divide by a polynomial indistinguishable from 0") + raise ZeroDivisionError("cannot divide by a polynomial " + "indistinguishable from 0") x = f.parent().gen() quo = f.parent()(0) while f.degree() >= g.degree(): @@ -970,6 +1057,36 @@ def _quo_rem_naive(self, right): f = f - a * (x ** (f.degree() - g.degree())) * g return (quo, f) + def _quo_rem_list(self, right, secure): + """ + An implementation of quo_rem using lists of coefficients. + + Faster than :meth:`_quo_rem_naive`. + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + if right.is_zero(): + raise ZeroDivisionError("cannot divide by a polynomial " + "indistinguishable from 0") + a = self.list() + da = len(a) - 1 + b = right.list() + db = right.degree(secure=secure) + inv = ~b[db] + q = [ ] + for i in range(da, db - 1, -1): + c = inv * a[i] + q.append(c) + for j in range(db): + a[j + i - db] -= c * b[j] + q.reverse() + K = self.base_ring().fraction_field() + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + parent = PolynomialRing(K, name=self.parent().variable_name()) + return parent(q), parent(a[:db]) + #def gcd(self, right): # raise NotImplementedError @@ -1034,7 +1151,7 @@ def disc(self): def newton_polygon(self): r""" - Returns the Newton polygon of this polynomial. + Return the Newton polygon of this polynomial. .. NOTE:: @@ -1100,9 +1217,71 @@ def newton_polygon(self): raise PrecisionError("The coefficient of %s^%s has not enough precision" % (self.parent().variable_name(), x)) return polygon + def is_eisenstein(self, secure=False): + """ + Return ``True`` if this polynomial is an Eisenstein polynomial. + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 5*t + t^4 + sage: f.is_eisenstein() + True + + TESTS:: + + sage: f = R([K(5,1),0,0,1]); f + (1 + O(5^20))*t^3 + (O(5)) + sage: f.is_eisenstein() + Traceback (most recent call last): + ... + PrecisionError: Not enough precision on the constant coefficient + + sage: g = R([5,K(0,0),0,1]); g + (1 + O(5^20))*t^3 + (O(5^0))*t + (5 + O(5^21)) + sage: g.is_eisenstein() + True + sage: g.is_eisenstein(secure=True) + Traceback (most recent call last): + ... + PrecisionError: Not enough precision on the coefficient of t + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + deg = self.degree() + if secure and self.prec_degree() > deg: + raise PrecisionError("The degree of the polynomial is not determined") + if self._valaddeds is None: + self._comp_valaddeds() + compval = 1 - self._valbase + valaddeds = self._valaddeds + relprecs = self._relprecs + if relprecs[0] <= compval: # not enough precision + if valaddeds[0] < relprecs[0]: return False + raise PrecisionError("Not enough precision on the constant coefficient") + else: + if valaddeds[0] != compval: return False + for i in range(1, deg): + if relprecs[i] < compval: # not enough precision + if valaddeds[i] < relprecs[i]: return False + if secure: + if i == 1: + raise PrecisionError("Not enough precision on the coefficient of %s" % self.variable_name()) + else: + raise PrecisionError("Not enough precision on the coefficient of %s^%s" % (self.variable_name(), i)) + else: + if valaddeds[i] < compval: + return False + if valaddeds[deg] != -self._valbase: + return False + return True + def newton_slopes(self, repetition=True): """ - Returns a list of the Newton slopes of this polynomial. + Return a list of the Newton slopes of this polynomial. These are the valuations of the roots of this polynomial. @@ -1123,7 +1302,8 @@ def newton_slopes(self, repetition=True): sage: R. = K[] sage: f = 5 + 3*t + t^4 + 25*t^10 sage: f.newton_polygon() - Finite Newton polygon with 4 vertices: (0, 1), (1, 0), (4, 0), (10, 2) + Finite Newton polygon with 4 vertices: (0, 1), (1, 0), (4, 0), + (10, 2) sage: f.newton_slopes() [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] @@ -1137,12 +1317,9 @@ def newton_slopes(self, repetition=True): polygon = self.newton_polygon() return [-s for s in polygon.slopes(repetition=repetition)] - def hensel_lift(self, a): - raise NotImplementedError - def factor_mod(self): r""" - Returns the factorization of self modulo p. + Return the factorization of ``self`` modulo `p`. """ self._normalize() if self._valbase < 0: @@ -1153,9 +1330,11 @@ def factor_mod(self): raise PrecisionError("Polynomial is not known to high enough precision") return self._poly.factor_mod(self.base_ring().prime()) + def _extend_by_infinity(L, n): return L + [infinity] * (n - len(L)) + def make_padic_poly(parent, x, version): if version == 0: return parent(x, construct = True) diff --git a/src/sage/rings/polynomial/pbori.pyx b/src/sage/rings/polynomial/pbori.pyx index ffe05d0d11b..70e88cc188f 100644 --- a/src/sage/rings/polynomial/pbori.pyx +++ b/src/sage/rings/polynomial/pbori.pyx @@ -183,7 +183,7 @@ REFERENCES: """ include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from cpython.object cimport Py_EQ, Py_NE import operator @@ -364,7 +364,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): if n < 1: raise ValueError, "Number of variables must be greater than 1." - self.pbind = sage_malloc(n*sizeof(Py_ssize_t)) + self.pbind = sig_malloc(n*sizeof(Py_ssize_t)) cdef char *_n order = TermOrder(order, n) @@ -447,7 +447,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): def __dealloc__(self): - sage_free(self.pbind) + sig_free(self.pbind) # destruction of _pbring handled by C++ object def __reduce__(self): @@ -566,7 +566,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): names, and any polynomial ring with compatible variable names and base ring. - Before trac ticket #9138, boolean polynomial rings had + Before :trac:`9138`, boolean polynomial rings had a custom containment test, but that is not needed now since it now uses Sage's new coercion model. So, we move the tests from the old ``__contains__`` to here. @@ -600,7 +600,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: 7 in GF(2) True - We test that #10173 is fixed:: + We test that :trac:`10173` is fixed:: sage: R = BooleanPolynomialRing(256,'x') sage: S = PolynomialRing(GF(2),256,'y') @@ -756,7 +756,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): sage: P(x) x - Test that #10797 is really fixed:: + Test that :trac:`10797` is really fixed:: sage: B. = BooleanPolynomialRing() sage: I = ideal(a*b + a + b*e + c*e + 1, a + b + c*d + c + 1, a*c + c + d*f + d + 1, a*c + c*f + c + d*f + 1, c*f + c + d + e + 1, a + b*c + b*d + e*f + 1) @@ -887,7 +887,7 @@ cdef class BooleanPolynomialRing(MPolynomialRing_generic): TypeError: cannot convert polynomial z*x^2 + 5*y^3 to Boolean PolynomialRing in x, y: name z not defined We test that univariate polynomials convert into the - boolean polynomial ring (trac ticket #9138):: + boolean polynomial ring (:trac:`9138`):: sage: R. = ZZ[] sage: p = x^3+2*x^2+x+1 @@ -1821,7 +1821,7 @@ class BooleanMonomialMonoid(UniqueRepresentation,Monoid_class): sage: type(M.gen(0)) - Since trac ticket #9138, boolean monomial monoids are + Since :trac:`9138`, boolean monomial monoids are unique parents and are fit into the category framework:: sage: loads(dumps(M)) is M @@ -2375,7 +2375,10 @@ cdef class BooleanMonomial(MonoidElement): sage: m.index() 0 - # Check that Ticket #13133 is resolved: + TESTS: + + Check that :trac:`13133` is resolved:: + sage: B(1).lm().index() Traceback (most recent call last): ... @@ -6291,7 +6294,7 @@ cdef class ReductionStrategy: TESTS: - Check if #8966 is fixed:: + Check if :trac:`8966` is fixed:: sage: red = ReductionStrategy(B) sage: red.add_generator(None) @@ -7690,7 +7693,7 @@ cdef BooleanPolynomialRing BooleanPolynomialRing_from_PBRing(PBRing _ring): cdef int n = _ring.nVariables() - self.pbind = sage_malloc(n*sizeof(Py_ssize_t)) + self.pbind = sig_malloc(n*sizeof(Py_ssize_t)) T = TermOrder_from_PBRing(_ring) diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 97b6f2afe1f..f647df8829b 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -102,7 +102,7 @@ TESTS:: True """ -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from sage.categories.algebras import Algebras @@ -2017,7 +2017,7 @@ cdef class NCPolynomial_plural(RingElement): cdef int i cdef int flag cdef int gens = self._parent.ngens() - cdef int *exps = sage_malloc(sizeof(int)*gens) + cdef int *exps = sig_malloc(sizeof(int)*gens) for i from 0<=i = QQ[] sage: R3. = R2[] @@ -934,12 +934,12 @@ cdef class ETuple: if isinstance(data, ETuple): self._length = (data)._length self._nonzero = (data)._nonzero - self._data = sage_malloc(sizeof(int)*self._nonzero*2) + self._data = sig_malloc(sizeof(int)*self._nonzero*2) memcpy(self._data,(data)._data,sizeof(int)*self._nonzero*2) elif isinstance(data, dict) and isinstance(length, int): self._length = length self._nonzero = len(data) - self._data = sage_malloc(sizeof(int)*self._nonzero*2) + self._data = sig_malloc(sizeof(int)*self._nonzero*2) nz_elts = sorted(data.iteritems()) ind = 0 for index,exp in nz_elts: @@ -953,7 +953,7 @@ cdef class ETuple: if v != 0: self._nonzero += 1 ind = 0 - self._data = sage_malloc(sizeof(int)*self._nonzero*2) + self._data = sig_malloc(sizeof(int)*self._nonzero*2) for i from 0 <= i < self._length: v = data[i] if v != 0: @@ -968,7 +968,7 @@ cdef class ETuple: def __dealloc__(self): if self._data != 0: - sage_free(self._data) + sig_free(self._data) # methods to simulate tuple @@ -988,7 +988,7 @@ cdef class ETuple: cdef ETuple result = ETuple.__new__(ETuple) result._length = self._length+other._length result._nonzero = self._nonzero+other._nonzero - result._data = sage_malloc(sizeof(int)*result._nonzero*2) + result._data = sig_malloc(sizeof(int)*result._nonzero*2) for index from 0 <= index < self._nonzero: result._data[2*index] = self._data[2*index] result._data[2*index+1] = self._data[2*index+1] @@ -1017,7 +1017,7 @@ cdef class ETuple: cdef size_t f result._length = self._length*factor result._nonzero = self._nonzero*factor - result._data = sage_malloc(sizeof(int)*result._nonzero*2) + result._data = sig_malloc(sizeof(int)*result._nonzero*2) for index from 0 <= index < self._nonzero: for f from 0 <= f < factor: result._data[2*(f*self._nonzero+index)] = self._data[2*index]+f*self._length @@ -1245,7 +1245,7 @@ cdef class ETuple: sage: e.eadd(f) (1, 1, 3) - Verify that trac 6428 has been addressed:: + Verify that :trac:`6428` has been addressed:: sage: R. = Frac(QQ['x'])[] sage: type(y) @@ -1269,7 +1269,7 @@ cdef class ETuple: alloc_len = self._length cdef ETuple result = self._new() result._nonzero = 0 # we don't know the correct length quite yet - result._data = sage_malloc(sizeof(int)*alloc_len*2) + result._data = sig_malloc(sizeof(int)*alloc_len*2) while dual_etuple_iter(self,other,&ind1,&ind2,&index,&exp1,&exp2): s = exp1 + exp2 # Check for overflow and underflow @@ -1310,7 +1310,7 @@ cdef class ETuple: cdef ETuple result = self._new() result._nonzero = self._nonzero - result._data = sage_malloc(sizeof(int)*alloc_len*2) + result._data = sig_malloc(sizeof(int)*alloc_len*2) for index from 0 <= index < self._nonzero: if self._data[2*index] == pos: @@ -1373,7 +1373,7 @@ cdef class ETuple: alloc_len = self._length cdef ETuple result = self._new() result._nonzero = 0 # we don't know the correct length quite yet - result._data = sage_malloc(sizeof(int)*alloc_len*2) + result._data = sig_malloc(sizeof(int)*alloc_len*2) while dual_etuple_iter(self,other,&ind1,&ind2,&index,&exp1,&exp2): # Check for overflow and underflow d = exp1 - exp2 @@ -1400,10 +1400,10 @@ cdef class ETuple: cdef ETuple result = self._new() if factor == 0: result._nonzero = 0 # all zero, no non-zero entries! - result._data = sage_malloc(sizeof(int)*result._nonzero*2) + result._data = sig_malloc(sizeof(int)*result._nonzero*2) else: result._nonzero = self._nonzero - result._data = sage_malloc(sizeof(int)*result._nonzero*2) + result._data = sig_malloc(sizeof(int)*result._nonzero*2) for ind from 0 <= ind < self._nonzero: result._data[2*ind] = self._data[2*ind] result._data[2*ind+1] = self._data[2*ind+1]*factor @@ -1444,7 +1444,7 @@ cdef class ETuple: alloc_len = self._length cdef ETuple result = self._new() result._nonzero = 0 # we don't know the correct length quite yet - result._data = sage_malloc(sizeof(int)*alloc_len*2) + result._data = sig_malloc(sizeof(int)*alloc_len*2) while dual_etuple_iter(self,other,&ind1,&ind2,&index,&exp1,&exp2): if exp1 >= exp2 and exp1 != 0: result._data[2*result._nonzero] = index @@ -1485,7 +1485,7 @@ cdef class ETuple: alloc_len = self._length cdef ETuple result = self._new() result._nonzero = 0 # we don't know the correct length quite yet - result._data = sage_malloc(sizeof(int)*alloc_len*2) + result._data = sig_malloc(sizeof(int)*alloc_len*2) while dual_etuple_iter(self,other,&ind1,&ind2,&index,&exp1,&exp2): if exp1 <= exp2 and exp1 != 0: result._data[2*result._nonzero] = index @@ -1575,7 +1575,7 @@ cdef class ETuple: cdef size_t ind cdef ETuple result = self._new() result._nonzero = self._nonzero - result._data = sage_malloc(sizeof(int)*result._nonzero*2) + result._data = sig_malloc(sizeof(int)*result._nonzero*2) for ind from 0 <= ind < self._nonzero: result._data[2*(result._nonzero-ind-1)] = self._length-self._data[2*ind]-1 result._data[2*(result._nonzero-ind-1)+1] = self._data[2*ind+1] diff --git a/src/sage/rings/polynomial/polynomial_element.pxd b/src/sage/rings/polynomial/polynomial_element.pxd index 677138ae93d..e35185bd752 100644 --- a/src/sage/rings/polynomial/polynomial_element.pxd +++ b/src/sage/rings/polynomial/polynomial_element.pxd @@ -30,4 +30,7 @@ cdef class Polynomial_generic_dense(Polynomial): cdef list __coeffs cdef int __normalize(self) except -1 +cdef class Polynomial_generic_dense_inexact(Polynomial_generic_dense): + pass + cpdef is_Polynomial(f) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index be4a3ab914b..8eff4f21b57 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1032,7 +1032,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: hash(f) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' sage: f._cache_key() (Univariate Polynomial Ring in x over Unramified Extension of 2-adic Field with capped relative precision 20 in u defined by (1 + O(2^20))*x^2 + (1 + O(2^20))*x + (1 + O(2^20)), 0, @@ -1082,7 +1082,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: hash(t) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' """ cdef long result = 0 # store it in a c-int and just let the overflowing additions wrap @@ -2204,7 +2204,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: latex(x+2) x + 2.0 - The following illustrates the fix of trac #2586:: + The following illustrates the fix of :trac:`2586`:: sage: latex(ZZ['alpha']['b']([0, ZZ['alpha'].0])) \alpha b @@ -3538,7 +3538,7 @@ cdef class Polynomial(CommutativeAlgebraElement): TESTS: - This came up in ticket #7088:: + This came up in :trac:`7088`:: sage: R.=PolynomialRing(ZZ) sage: f = 12*x^10 + x^9 + 432*x^3 + 9011 @@ -3549,7 +3549,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: F = f^2 * g^3 * 7; F.factor() 7 * (12*x^10 + x^9 + 432*x^3 + 9011)^2 * (13*x^11 + 89*x^3 + 1)^3 - This example came up in ticket #7097:: + This example came up in :trac:`7097`:: sage: x = polygen(QQ) sage: f = 8*x^9 + 42*x^6 + 6*x^3 - 1 @@ -3629,7 +3629,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: A(x^2 - 1/3).factor() (T - a) * (T + a) - Test that ticket #10279 is fixed:: + Test that :trac:`10279` is fixed:: sage: R. = PolynomialRing(QQ) sage: K. = NumberField(t^4 - t^2 + 1) @@ -3646,7 +3646,7 @@ cdef class Polynomial(CommutativeAlgebraElement): ... sage: pari.default("debug", 0) - Test that ticket #10369 is fixed:: + Test that :trac:`10369` is fixed:: sage: x = polygen(QQ) sage: K. = NumberField(x^6 + x^5 + x^4 + x^3 + x^2 + x + 1) @@ -3673,7 +3673,7 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: f = (x+a)^50 - (a-1)^50 sage: len(factor(f)) 6 - sage: pari(K.discriminant()).factor(limit=0) + sage: pari(K.discriminant()).factor(limit=10^6) [-1, 1; 3, 15; 23, 1; 887, 1; 12583, 1; 2354691439917211, 1] sage: factor(K.discriminant()) -1 * 3^15 * 23 * 887 * 12583 * 6335047 * 371692813 @@ -4211,7 +4211,38 @@ cdef class Polynomial(CommutativeAlgebraElement): sage: p.gcd(q) x + a sage: del O._gcd_univariate_polynomial + + Use multivariate implementation for polynomials over polynomials rings:: + + sage: R. = ZZ[] + sage: S. = R[] + sage: T. = S[] + sage: r = 2*x*y + z + sage: p = r * (3*x*y*z - 1) + sage: q = r * (x + y + z - 2) + sage: p.gcd(q) + z + 2*x*y + + sage: R. = QQ[] + sage: S. = R[] + sage: r = 2*x*y + 1 + sage: p = r * (x - 1/2 * y) + sage: q = r * (x*y^2 - x + 1/3) + sage: p.gcd(q) + 2*x*y + 1 """ + variables = self._parent.variable_names_recursive() + if len(variables) > 1: + base = self._parent._mpoly_base_ring() + ring = PolynomialRing(base, variables) + if ring._has_singular: + try: + d1 = self._mpoly_dict_recursive() + d2 = other._mpoly_dict_recursive() + return self._parent(ring(d1).gcd(ring(d2))) + except NotImplementedError: + pass + if hasattr(self.base_ring(), '_gcd_univariate_polynomial'): return self.base_ring()._gcd_univariate_polynomial(self, other) else: @@ -5235,6 +5266,90 @@ cdef class Polynomial(CommutativeAlgebraElement): return slopes + def dispersion_set(self, other=None): + r""" + Compute the dispersion set of two polynomials. + + The dispersion set of `f` and `g` is the set of nonnegative integers + `n` such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion set of + ``self``, i.e., its dispersion set with itself. + + ALGORITHM: + + See Section 4 of Man & Wright [ManWright1994]_. + + .. [ManWright1994] Yiu-Kwong Man and Francis J. Wright. + *Fast Polynomial Dispersion Computation and its Application to + Indefinite Summation*. ISSAC 1994. + + .. SEEALSO:: :meth:`dispersion` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion_set(x + 1) + [1] + sage: (x + 1).dispersion_set(x) + [] + + sage: pol = x^3 + x - 7 + sage: (pol*pol(x+3)^2).dispersion_set() + [0, 3] + """ + other = self if other is None else self._parent.coerce(other) + x = self._parent.gen() + dispersions = set() + for p, _ in self.factor(): + # need both due to the semantics of is_primitive() over fields + assert p.is_monic() or p.is_primitive() + for q, _ in other.factor(): + m, n = p.degree(), q.degree() + assert q.is_monic() or q.is_primitive() + if m != n or p[n] != q[n]: + continue + alpha = (q[n-1] - p[n-1])/(n*p[n]) + if alpha.is_integer(): # ZZ() might work for non-integers... + alpha = ZZ(alpha) + else: + continue + if alpha < 0 or alpha in dispersions: + continue + if n >= 1 and p(x + alpha) != q: + continue + dispersions.add(alpha) + return list(dispersions) + + def dispersion(self, other=None): + r""" + Compute the dispersion of a pair of polynomials. + + The dispersion of `f` and `g` is the largest nonnegative integer `n` + such that `f(x + n)` and `g(x)` have a nonconstant common factor. + + When ``other`` is ``None``, compute the auto-dispersion of ``self``, + i.e., its dispersion with itself. + + .. SEEALSO:: :meth:`dispersion_set` + + EXAMPLES:: + + sage: Pol. = QQ[] + sage: x.dispersion(x + 1) + 1 + sage: (x + 1).dispersion(x) + -Infinity + + sage: Pol. = QQbar[] + sage: pol = Pol([sqrt(5), 1, 3/2]) + sage: pol.dispersion() + 0 + sage: (pol*pol(x+3)).dispersion() + 3 + """ + dispersions = self.dispersion_set(other) + return max(dispersions) if len(dispersions) > 0 else infinity.minus_infinity ##################################################################### # Conversions to other systems @@ -7470,7 +7585,7 @@ cdef class Polynomial(CommutativeAlgebraElement): REFERENCES: - .. [BD89] R. J. Bradford and J. H. Davenport, Effective tests + .. [BD89] \R. J. Bradford and J. H. Davenport, Effective tests for cyclotomic polynomials, Symbolic and Algebraic Computation (1989) pp. 244 -- 251, :doi:`10.1007/3-540-51084-2_22` """ @@ -8330,8 +8445,13 @@ cdef class Polynomial_generic_dense(Polynomial): Returns the quotient and remainder of the Euclidean division of ``self`` and ``other``. - Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if ``other`` has - a nonunit leading coefficient. + Raises ZerodivisionError if ``other`` is zero. Raises ArithmeticError if the division is not exact. + + AUTHORS: + + - Kwankyu Lee (2013-06-02) + + - Bruno Grenet (2014-07-13) EXAMPLES:: @@ -8346,17 +8466,31 @@ cdef class Polynomial_generic_dense(Polynomial): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): ... ZeroDivisionError: Division by zero polynomial + + TESTS: + + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = QQ[] + sage: R. = P[] + sage: f = (2*x^3+1)*y^2 + (x^2-x+3)*y + (3*x+2) + sage: g = (-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1 + sage: h = f * g + sage: h.quo_rem(f) + ((-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1, 0) + sage: h += (2/3*x^2-3*x+1)*y + 7/17*x+6/5 + sage: q,r = h.quo_rem(f) + sage: h == q*f + r and r.degree() < f.degree() + True """ if other.is_zero(): raise ZeroDivisionError("Division by zero polynomial") - if not other.leading_coefficient().is_unit(): - raise ArithmeticError("Nonunit leading coefficient") if self.is_zero(): return self, self @@ -8370,7 +8504,10 @@ cdef class Polynomial_generic_dense(Polynomial): quo = list() for k from m-n >= k >= 0: - q = x[n+k-1]/y[n-1] + try: + q = R(x[n+k-1]/y[n-1]) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") x[n+k-1] = R.zero() for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] @@ -8422,6 +8559,7 @@ cdef class Polynomial_generic_dense(Polynomial): def make_generic_polynomial(parent, coeffs): return parent(coeffs) + @cached_function def universal_discriminant(n): r""" @@ -8458,6 +8596,132 @@ def universal_discriminant(n): p = pr2(list(pr1.gens())) return (1 - (n&2))*p.resultant(p.derivative())//pr1.gen(n) + +cdef class Polynomial_generic_dense_inexact(Polynomial_generic_dense): + """ + A dense polynomial over an inexact ring. + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + cdef int __normalize(self) except -1: + r""" + TESTS:: + + Coefficients indistinguishable from 0 are not removed. + + sage: R = Zp(5) + sage: S. = R[] + sage: S([1,R(0,20)]) + (O(5^20))*x + (1 + O(5^20)) + """ + cdef list x = self.__coeffs + cdef Py_ssize_t n = len(x) - 1 + cdef RingElement c + while n >= 0: + c = x[n] + if c.is_zero() and c.precision_absolute() is infinity.Infinity: + del x[n] + n -= 1 + else: + break + + def degree(self, secure=False): + r""" + INPUT: + + - secure -- a boolean (default: False) + + OUTPUT: + + The degree of self. + + If ``secure`` is True and the degree of this polynomial + is not determined (because the leading coefficient is + indistinguishable from 0), an error is raised + + If ``secure`` is False, the returned value is the largest + $n$ so that the coefficient of $x^n$ does not compare equal + to `0`. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.degree() + 1 + sage: (f-T).degree() + 0 + sage: (f-T).degree(secure=True) + Traceback (most recent call last): + ... + PrecisionError: the leading coefficient is indistinguishable from 0 + + sage: x = O(3^5) + sage: li = [3^i * x for i in range(0,5)]; li + [O(3^5), O(3^6), O(3^7), O(3^8), O(3^9)] + sage: f = R(li); f + (O(3^9))*T^4 + (O(3^8))*T^3 + (O(3^7))*T^2 + (O(3^6))*T + (O(3^5)) + sage: f.degree() + -1 + sage: f.degree(secure=True) + Traceback (most recent call last): + ... + PrecisionError: the leading coefficient is indistinguishable from 0 + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + coeffs = self.__coeffs + d = len(coeffs) - 1 + while d >= 0: + c = coeffs[d] + if c.is_zero(): + if secure: + from sage.rings.padics.precision_error import PrecisionError + raise PrecisionError("the leading coefficient is indistinguishable from 0") + else: + d -= 1 + else: + break + return d + + def prec_degree(self): + r""" + Returns the largest `n` so that precision information is + stored about the coefficient of `x^n`. + + Always greater than or equal to degree. + + EXAMPLES:: + + sage: K = Qp(3,10) + sage: R. = K[] + sage: f = T + 2; f + (1 + O(3^10))*T + (2 + O(3^10)) + sage: f.degree() + 1 + sage: f.prec_degree() + 1 + + sage: g = f - T; g + (O(3^10))*T + (2 + O(3^10)) + sage: g.degree() + 0 + sage: g.prec_degree() + 1 + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + return len(self.__coeffs) - 1 + + cdef class ConstantPolynomialSection(Map): """ This class is used for conversion from a polynomial ring to its base ring. diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index c0768301658..4869659028e 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -30,7 +30,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.polynomial.polynomial_element import Polynomial, Polynomial_generic_dense +from sage.rings.polynomial.polynomial_element import Polynomial, Polynomial_generic_dense, Polynomial_generic_dense_inexact from sage.structure.element import IntegralDomainElement, EuclideanDomainElement from sage.rings.polynomial.polynomial_singular_interface import Polynomial_singular_repr @@ -41,6 +41,7 @@ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer +from sage.structure.factorization import Factorization class Polynomial_generic_sparse(Polynomial): @@ -121,7 +122,7 @@ def __init__(self, parent, x=None, check=True, is_gen=False, construct=False): def dict(self): """ Return a new copy of the dict of the underlying - elements of self. + elements of ``self``. EXAMPLES:: @@ -138,7 +139,7 @@ def dict(self): def coefficients(self,sparse=True): """ - Return the coefficients of the monomials appearing in self. + Return the coefficients of the monomials appearing in ``self``. EXAMPLES:: @@ -155,7 +156,7 @@ def coefficients(self,sparse=True): def exponents(self): """ - Return the exponents of the monomials appearing in self. + Return the exponents of the monomials appearing in ``self``. EXAMPLES:: @@ -171,6 +172,8 @@ def exponents(self): def valuation(self): """ + Return the valuation of ``self``. + EXAMPLES:: sage: R. = PolynomialRing(GF(9,'a'), sparse=True) @@ -192,9 +195,10 @@ def _derivative(self, var=None): Computes formal derivative of this polynomial with respect to the given variable. - If var is None or is the generator of this ring, the derivative - is with respect to the generator. Otherwise, _derivative(var) is called - recursively for each coefficient of this polynomial. + If ``var`` is ``None`` or is the generator of this ring, the + derivative is with respect to the generator. Otherwise, + _derivative(var) is called recursively for each coefficient of + this polynomial. .. seealso:: :meth:`.derivative` @@ -303,7 +307,8 @@ def _dict_unsafe(self): ** DO NOT use this, unless you really really know what you are doing. ** - EXAMPLES: + EXAMPLES:: + sage: R. = PolynomialRing(ZZ, sparse=True) sage: f = w^15 - w*3; f w^15 - 3*w @@ -456,7 +461,8 @@ def _unsafe_mutate(self, n, value): sage: f 1.00000000000000*z^2 + 10.0000000000000 - Much more nasty: + Much more nasty:: + sage: z._unsafe_mutate(1, 0) sage: z 0 @@ -475,7 +481,7 @@ def _unsafe_mutate(self, n, value): def list(self): """ Return a new copy of the list of the underlying - elements of self. + elements of ``self``. EXAMPLES:: @@ -697,8 +703,10 @@ def _cmp_(self, other): def shift(self, n): r""" - Returns this polynomial multiplied by the power `x^n`. If `n` is negative, - terms below `x^n` will be discarded. Does not change this polynomial. + Returns this polynomial multiplied by the power `x^n`. + + If `n` is negative, terms below `x^n` will be discarded. Does + not change this polynomial. EXAMPLES:: @@ -765,7 +773,7 @@ def quo_rem(self, other): sage: f.quo_rem(g) Traceback (most recent call last): ... - ArithmeticError: Nonunit leading coefficient + ArithmeticError: Division non exact (consider coercing to polynomials over the fraction field) sage: g = 0 sage: f.quo_rem(g) Traceback (most recent call last): @@ -794,14 +802,18 @@ def quo_rem(self, other): sage: g == f*q + r and r.degree() < f.degree() True + The following shows that :trac:`16649` is indeed fixed. :: + + sage: P. = PolynomialRing(ZZ, sparse=True) + sage: (4*x).quo_rem(2*x) + (2, 0) + AUTHORS: - Bruno Grenet (2014-07-09) """ if other.is_zero(): raise ZeroDivisionError("Division by zero polynomial") - if not other.leading_coefficient().is_unit(): - raise ArithmeticError("Nonunit leading coefficient") if self.is_zero(): return self, self @@ -813,11 +825,12 @@ def quo_rem(self, other): quo = R.zero() rem = self - inv_lc = R.base_ring().one()/other.leading_coefficient() while rem.degree() >= d: - - c = rem.leading_coefficient()*inv_lc + try: + c = R(rem.leading_coefficient() * ~other.leading_coefficient()) + except TypeError: + raise ArithmeticError("Division non exact (consider coercing to polynomials over the fraction field)") e = rem.degree() - d quo += c*R.one().shift(e) # we know that the leading coefficient of rem vanishes @@ -960,7 +973,7 @@ def __init__(self, parent, is_gen=False, construct=False): def is_unit(self): r""" - Return True if this polynomial is a unit. + Return ``True`` if this polynomial is a unit. *EXERCISE* (Atiyah-McDonald, Ch 1): Let `A[x]` be a polynomial ring in one variable. Then `f=\sum a_i x^i \in A[x]` is a @@ -995,7 +1008,7 @@ class Polynomial_generic_field(Polynomial_singular_repr, def quo_rem(self, other): """ Returns a tuple (quotient, remainder) where - self = quotient*other + remainder. + self = quotient * other + remainder. EXAMPLES:: @@ -1049,6 +1062,340 @@ class Polynomial_generic_dense_field(Polynomial_generic_dense, Polynomial_generi def __init__(self, parent, x=None, check=True, is_gen = False, construct=False): Polynomial_generic_dense.__init__(self, parent, x, check, is_gen) + +########################################## +# Over discrete valuation rings and fields +########################################## + +class Polynomial_generic_cdv(Polynomial_generic_domain): + """ + A generic class for polynomials over complete discrete + valuation domains and fields. + + AUTHOR: + + - Xavier Caruso (2013-03) + """ + def newton_slopes(self, repetition=True): + """ + Returns a list of the Newton slopes of this polynomial. + + These are the valuations of the roots of this polynomial. + + If ``repetition`` is ``True``, each slope is repeated a number of + times equal to its multiplicity. Otherwise it appears only + one time. + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 3*t + t^4 + 25*t^10 + sage: f.newton_polygon() + Finite Newton polygon with 4 vertices: (0, 1), (1, 0), (4, 0), (10, 2) + sage: f.newton_slopes() + [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] + + sage: f.newton_slopes(repetition=False) + [1, 0, -1/3] + + AUTHOR: + + - Xavier Caruso (2013-03-20) + """ + polygon = self.newton_polygon() + return [-s for s in polygon.slopes(repetition=repetition)] + + def newton_polygon(self): + r""" + Returns a list of vertices of the Newton polygon of this polynomial. + + .. NOTE:: + + If some coefficients have not enough precision an error is raised. + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 3*t + t^4 + 25*t^10 + sage: f.newton_polygon() + Finite Newton polygon with 4 vertices: (0, 1), (1, 0), (4, 0), (10, 2) + + sage: g = f + K(0,0)*t^4; g + (5^2 + O(5^22))*t^10 + (O(5^0))*t^4 + (3 + O(5^20))*t + (5 + O(5^21)) + sage: g.newton_polygon() + Traceback (most recent call last): + ... + PrecisionError: The coefficient of t^4 has not enough precision + + AUTHOR: + + - Xavier Caruso (2013-03-20) + """ + d = self.degree() + from sage.geometry.newton_polygon import NewtonPolygon + polygon = NewtonPolygon([(x, self[x].valuation()) for x in range(d+1)]) + polygon_prec = NewtonPolygon([ (x, self[x].precision_absolute()) for x in range(d+1) ]) + vertices = polygon.vertices(copy=False) + vertices_prec = polygon_prec.vertices(copy=False) + if vertices[0][0] > vertices_prec[0][0]: + raise PrecisionError("first term with non-infinite valuation must have determined valuation") + elif vertices[-1][0] < vertices_prec[-1][0]: + raise PrecisionError("last term with non-infinite valuation must have determined valuation") + else: + for (x, y) in vertices: + if polygon_prec(x) <= y: + raise PrecisionError("The coefficient of %s^%s has not enough precision" % (self.parent().variable_name(), x)) + return polygon + + def hensel_lift(self, a): + """ + Lift `a` to a root of this polynomial (using + Newton iteration). + + If `a` is not close enough to a root (so that + Newton iteration does not converge), an error + is raised. + + EXAMPLES:: + + sage: K = Qp(5, 10) + sage: P. = PolynomialRing(K) + sage: f = x^2 + 1 + sage: root = f.hensel_lift(2); root + 2 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + 2*5^6 + 3*5^7 + 3*5^9 + O(5^10) + sage: f(root) + O(5^10) + + sage: g = (x^2 + 1)*(x - 7) + sage: g.hensel_lift(2) # here, 2 is a multiple root modulo p + Traceback (most recent call last): + ... + ValueError: a is not close enough to a root of this polynomial + + AUTHOR: + + - Xavier Caruso (2013-03-23) + """ + base = self.base_ring() + selfa = self(a) + der = self.derivative() + dera = der(a) + if selfa.valuation() <= 2 * dera.valuation(): + raise ValueError("a is not close enough to a root of this polynomial") + # Newton iteration + # Todo: compute everything up to the adequate precision at each step + b = ~dera + while(True): + na = a - selfa * b + if na == a: return a + a = na + selfa = self(a) + dera = der(a) + b *= 2 - dera*b + + def _factor_of_degree(self, deg): + """ + Return a factor of ``self`` of degree ``deg``. + + Algorithm is Newton iteration. + + This fails if ``deg`` is not a breakpoint in the Newton + polygon of ``self``. + + Only for internal use! + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 3*t + t^4 + 25*t^10 + + sage: g = f._factor_of_degree(4) + sage: (f % g).is_zero() + True + + sage: g = f._factor_of_degree(3) # not tested + Traceback (most recent call last) + ... + KeyboardInterrupt: + + AUTHOR: + + - Xavier Caruso (2013-03-20) + + TODO: + + Precision is not optimal, and can be improved. + """ + coeffs = self.list() + a = self.truncate(deg + 1) + b = v = self.parent()(1) + x = self % a + while(not x.is_zero()): + a += (v * x) % a + b, x = self.quo_rem(a) + b %= a + v = (v * (2 - b*v)) % a + + return a + + def factor_of_slope(self, slope=None): + """ + INPUT: + + - slope -- a rational number (default: the first slope + in the Newton polygon of ``self``) + + OUTPUT: + + The factor of ``self`` corresponding to the slope ``slope`` (i.e. + the unique monic divisor of ``self`` whose slope is ``slope`` and + degree is the length of ``slope`` in the Newton polygon). + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 3*t + t^4 + 25*t^10 + sage: f.newton_slopes() + [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] + + sage: g = f.factor_of_slope(0) + sage: g.newton_slopes() + [0, 0, 0] + sage: (f % g).is_zero() + True + + sage: h = f.factor_of_slope() + sage: h.newton_slopes() + [1] + sage: (f % h).is_zero() + True + + If ``slope`` is not a slope of ``self``, the corresponding factor + is `1`:: + + sage: f.factor_of_slope(-1) + (1 + O(5^20)) + + AUTHOR: + + - Xavier Caruso (2013-03-20) + """ + one = self.parent()(1) + vertices = self.newton_polygon().vertices(copy=False) + if len(vertices) < 2: + if slope is Infinity: + return self.parent().gen() ** self.degree() + else: + return one + if slope is None: + deg_first = vertices[0][0] + deg_last = vertices[1][0] + else: + (deg_first, y_first) = vertices[0] + for i in range(1, len(vertices)): + (deg_last, y_last) = vertices[i] + slope_cur = (y_first - y_last) / (deg_last - deg_first) + if slope_cur == slope: + break + elif slope_cur < slope: + return one + deg_first = deg_last + y_first = y_last + if slope_cur > slope: + return one + if deg_last == self.degree(): + div = self + else: + div = self._factor_of_degree(deg_last) + if deg_first > 0: + div2 = div._factor_of_degree(deg_first) + div,_ = div.quo_rem(div2) + return div.monic() + + def slope_factorization(self): + """ + Return a factorization of ``self`` into a product of factors + corresponding to each slope in the Newton polygon. + + EXAMPLES:: + + sage: K = Qp(5) + sage: R. = K[] + sage: K = Qp(5) + sage: R. = K[] + sage: f = 5 + 3*t + t^4 + 25*t^10 + sage: f.newton_slopes() + [1, 0, 0, 0, -1/3, -1/3, -1/3, -1/3, -1/3, -1/3] + + sage: F = f.slope_factorization() + sage: F.prod() == f + True + sage: for (f,_) in F: + ....: print f.newton_slopes() + [-1/3, -1/3, -1/3, -1/3, -1/3, -1/3] + [0, 0, 0] + [1] + + AUTHOR: + + - Xavier Caruso (2013-03-20) + """ + vertices = self.newton_polygon().vertices(copy=False) + + unit = self.leading_coefficient() + P = ~unit * self + + deg_first = vertices[0][0] + factors = [ ] + if deg_first > 0: + P >>= deg_first + factors.append((self._parent.gen(), deg_first)) + if len(vertices) > 2: + for i in range(1, len(vertices)-1): + deg = vertices[i][0] + div = P._factor_of_degree(deg-deg_first) + factors.append((div,1)) + P,_ = P.quo_rem(div) + deg_first = deg + if len(vertices) > 1: + factors.append((P, 1)) + factors.reverse() + return Factorization(factors, sort=False, unit=unit) + +class Polynomial_generic_dense_cdv(Polynomial_generic_dense_inexact, Polynomial_generic_cdv): + pass + +class Polynomial_generic_sparse_cdv(Polynomial_generic_sparse, Polynomial_generic_cdv): + pass + + +class Polynomial_generic_cdvr(Polynomial_generic_cdv): + pass + +class Polynomial_generic_dense_cdvr(Polynomial_generic_dense_cdv, Polynomial_generic_cdvr): + pass + +class Polynomial_generic_sparse_cdvr(Polynomial_generic_sparse_cdv, Polynomial_generic_cdvr): + pass + + +class Polynomial_generic_cdvf(Polynomial_generic_cdv, Polynomial_generic_field): + pass + +class Polynomial_generic_dense_cdvf(Polynomial_generic_dense_cdv, Polynomial_generic_cdvf): + pass + +class Polynomial_generic_sparse_cdvf(Polynomial_generic_sparse_cdv, Polynomial_generic_cdvf): + pass + ############################################################################ # XXX: Ensures that the generic polynomials implemented in SAGE via PARI # # until at least until 4.5.0 unpickle correctly as polynomials implemented # diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index e4223a7c90c..62f9162fe24 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -682,7 +682,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sage: z.quo_rem(2*x) (0, 0) - Ticket #383, make sure things get coerced correctly:: + :trac:`383`, make sure things get coerced correctly:: sage: f = x+1; parent(f) Univariate Polynomial Ring in x over Integer Ring @@ -1373,8 +1373,8 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_poly_set_ZZX(fac.__poly, v[i][0]) F.append( (fac,e[i]) ) del v[i] - sage_free(v) - sage_free(e) + sig_free(v) + sig_free(e) return Factorization(F, unit=z, sort=False) diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index bbbf621f4dd..0f56867f8ab 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -897,8 +897,8 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): z.__poly = v[i][0] F.append((z, e[i])) del v[i] - sage_free(v) - sage_free(e) + sig_free(v) + sig_free(e) return Factorization(F, unit=c, sort=False) def _factor_pari(self): diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 77b2868d393..16168e8e787 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -457,7 +457,7 @@ def _element_constructor_(self, x): try: return self.element_class(self, self.__ring(x), check=False) except TypeError: - raise TypeError("unable to convert %s into an element of %s"%(x,repr(self))) + raise TypeError("unable to convert %r to an element of %s"%(x, self)) def _coerce_map_from_(self, R): """ diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 73e2d48b7a7..91a77cd7336 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -240,14 +240,14 @@ cdef class Polynomial_rational_flint(Polynomial): L1 = [e if isinstance(e, Rational) else Rational(e) for e in x] n = len(x) sig_on() - L2 = sage_malloc(n * sizeof(mpq_t)) + L2 = sig_malloc(n * sizeof(mpq_t)) for deg from 0 <= deg < n: mpq_init(L2[deg]) mpq_set(L2[deg], ( L1[deg]).value) fmpq_poly_set_array_mpq(self.__poly, L2, n) for deg from 0 <= deg < n: mpq_clear(L2[deg]) - sage_free(L2) + sig_free(L2) sig_off() # deg = 0 @@ -969,7 +969,7 @@ cdef class Polynomial_rational_flint(Polynomial): TESTS: - The following example used to crash (cf. #11771):: + The following example used to crash (cf. :trac:`11771`):: sage: R. = QQ[] sage: f = 10**383 * (t+1) diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index b37e6f6e00f..3eefe39c229 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -22,9 +22,8 @@ Check that operations with numpy elements work well (see :trac:`18076` and 1.50000000000000*x """ -include "sage/ext/stdsage.pxi" include "cysignals/signals.pxi" -from sage.ext.memory cimport check_reallocarray, check_allocarray, sage_free +include "cysignals/memory.pxi" from cpython cimport PyInt_AS_LONG, PyFloat_AS_DOUBLE @@ -100,7 +99,7 @@ cdef class PolynomialRealDense(Polynomial): sage: (a*x).complex_roots() Traceback (most recent call last): ... - TypeError: Unable to convert x (='a') to real number. + TypeError: unable to convert 'a' to a real number Check that :trac:`17190` is fixed:: @@ -165,7 +164,7 @@ cdef class PolynomialRealDense(Polynomial): if self._coeffs != NULL: for i from 0 <= i <= self._degree: mpfr_clear(self._coeffs[i]) - sage_free(self._coeffs) + sig_free(self._coeffs) def __reduce__(self): """ diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 336fb73f4fb..063b400cf45 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -373,7 +373,7 @@ def _element_constructor_(self, x=None, check=True, is_gen = False, construct=Fa sage: RealField(300)['x']( [ 1, ComplexField(300).gen(), 0 ]) Traceback (most recent call last): ... - TypeError: Unable to convert x (='1.00...00*I') to real number. + TypeError: unable to convert '1.00...00*I' to a real number Check that the bug in :trac:`11239` is fixed:: @@ -383,6 +383,11 @@ def _element_constructor_(self, x=None, check=True, is_gen = False, construct=Fa sage: L['x'](f) x + b^3 + b^2 + b + 3 + A test from :trac:`14485` :: + + sage: x = SR.var('x') + sage: QQbar[x](x^6+x^5+x^4-x^3+x^2-x+2/5) + x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5 """ C = self.element_class if isinstance(x, list): @@ -1491,7 +1496,7 @@ def weyl_algebra(self): class PolynomialRing_integral_domain(PolynomialRing_commutative, ring.IntegralDomain): def __init__(self, base_ring, name="x", sparse=False, implementation=None, - element_class=None): + element_class=None, category=None): """ TESTS:: @@ -1523,7 +1528,7 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, else: raise ValueError("Unknown implementation %s for ZZ[x]"%implementation) PolynomialRing_commutative.__init__(self, base_ring, name=name, - sparse=sparse, element_class=element_class) + sparse=sparse, element_class=element_class, category=category) def _repr_(self): """ @@ -1540,7 +1545,7 @@ def _repr_(self): class PolynomialRing_field(PolynomialRing_integral_domain, PolynomialRing_singular_repr, ring.PrincipalIdealDomain): - def __init__(self, base_ring, name="x", sparse=False, element_class=None): + def __init__(self, base_ring, name="x", sparse=False, element_class=None, category=None): """ TESTS:: @@ -1584,7 +1589,7 @@ def __init__(self, base_ring, name="x", sparse=False, element_class=None): else: element_class = polynomial_element_generic.Polynomial_generic_dense_field - PolynomialRing_integral_domain.__init__(self, base_ring, name=name, sparse=sparse, element_class=element_class) + PolynomialRing_integral_domain.__init__(self, base_ring, name=name, sparse=sparse, element_class=element_class, category=category) self._has_singular = can_convert_to_singular(self) @@ -1679,7 +1684,7 @@ def divided_difference(self, points, full_table=False): REFERENCES: - .. [MF99] J.H. Mathews and K.D. Fink. *Numerical Methods Using + .. [MF99] \J.H. Mathews and K.D. Fink. *Numerical Methods Using MATLAB*. 3rd edition, Prentice-Hall, 1999. """ @@ -1840,7 +1845,7 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r REFERENCES: - .. [BF05] R.L. Burden and J.D. Faires. *Numerical Analysis*. + .. [BF05] \R.L. Burden and J.D. Faires. *Numerical Analysis*. 8th edition, Thomson Brooks/Cole, 2005. """ @@ -2034,14 +2039,77 @@ def irreducible_element(self, n, algorithm=None): else: raise ValueError("no such algorithm for finding an irreducible polynomial: %s" % algorithm) -class PolynomialRing_dense_padic_ring_generic(PolynomialRing_integral_domain): - pass -class PolynomialRing_dense_padic_field_generic(PolynomialRing_field): - pass +class PolynomialRing_cdvr(PolynomialRing_integral_domain): + r""" + A class for polynomial ring over complete discrete valuation rings + """ + def __init__(self, base_ring, name=None, sparse=False, element_class=None, category=None): + r""" + TESTS:: + + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_cdvr + + sage: S. = ZZ[] + sage: isinstance(S, PolynomialRing_cdvr) + False + + sage: S. = Zp(5)[] + sage: isinstance(S, PolynomialRing_cdvr) + True + """ + if element_class is None: + if sparse: + from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_sparse_cdvr + element_class = Polynomial_generic_sparse_cdvr + else: + from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_dense_cdvr + element_class = Polynomial_generic_dense_cdvr + PolynomialRing_integral_domain.__init__(self, base_ring, name, sparse, element_class=element_class, category=category) + +class PolynomialRing_cdvf(PolynomialRing_cdvr, PolynomialRing_field): + """ + A class for polynomial ring over complete discrete valuation fields + """ + def __init__(self, base_ring, name=None, sparse=False, element_class=None, category=None): + r""" + TESTS:: + + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_cdvf + + sage: S. = QQ[] + sage: isinstance(S, PolynomialRing_cdvf) + False + + sage: S. = Qp(5)[] + sage: isinstance(S, PolynomialRing_cdvf) + True + """ + if element_class is None: + if sparse: + from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_sparse_cdvf + element_class = Polynomial_generic_sparse_cdvf + else: + from sage.rings.polynomial.polynomial_element_generic import Polynomial_generic_dense_cdvf + element_class = Polynomial_generic_dense_cdvf + PolynomialRing_field.__init__(self, base_ring, name, sparse, element_class=element_class, category=category) + +class PolynomialRing_dense_padic_ring_generic(PolynomialRing_cdvr): + r""" + A class for dense polynomial ring over padic rings + """ + def __init__(self, base_ring, name=None, element_class=None, category=None): + PolynomialRing_cdvr.__init__(self, base_ring, sparse=False, name=name, element_class=element_class, category=category) + +class PolynomialRing_dense_padic_field_generic(PolynomialRing_cdvf): + r""" + A class for dense polynomial ring over padic fields + """ + def __init__(self, base_ring, name=None, element_class=None, category=None): + PolynomialRing_cdvf.__init__(self, base_ring, sparse=False, name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_ring_capped_relative(PolynomialRing_dense_padic_ring_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2057,10 +2125,10 @@ def __init__(self, base_ring, name=None, element_class=None): Polynomial_padic_capped_relative_dense element_class = Polynomial_padic_capped_relative_dense PolynomialRing_dense_padic_ring_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_ring_capped_absolute(PolynomialRing_dense_padic_ring_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2075,10 +2143,10 @@ def __init__(self, base_ring, name=None, element_class=None): Polynomial_padic_flat element_class = Polynomial_padic_flat PolynomialRing_dense_padic_ring_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_ring_fixed_mod(PolynomialRing_dense_padic_ring_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2094,10 +2162,10 @@ def __init__(self, base_ring, name=None, element_class=None): Polynomial_padic_flat element_class = Polynomial_padic_flat PolynomialRing_dense_padic_ring_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_ring_lazy(PolynomialRing_dense_padic_ring_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2113,10 +2181,10 @@ def __init__(self, base_ring, name=None, element_class=None): if element_class is None: element_class = polynomial_element_generic.Polynomial_generic_dense PolynomialRing_dense_padic_ring_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_field_capped_relative(PolynomialRing_dense_padic_field_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2132,10 +2200,10 @@ def __init__(self, base_ring, name=None, element_class=None): Polynomial_padic_capped_relative_dense element_class = Polynomial_padic_capped_relative_dense PolynomialRing_dense_padic_field_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_padic_field_lazy(PolynomialRing_dense_padic_field_generic): - def __init__(self, base_ring, name=None, element_class=None): + def __init__(self, base_ring, name=None, element_class=None, category=None): """ TESTS:: @@ -2150,11 +2218,11 @@ def __init__(self, base_ring, name=None, element_class=None): if element_class is None: element_class = polynomial_element_generic.Polynomial_generic_dense PolynomialRing_dense_padic_field_generic.__init__(self, base_ring, - name=name, element_class=element_class) + name=name, element_class=element_class, category=category) class PolynomialRing_dense_mod_n(PolynomialRing_commutative): def __init__(self, base_ring, name=None, element_class=None, - implementation=None): + implementation=None, category=None): """ TESTS:: @@ -2206,7 +2274,7 @@ def __init__(self, base_ring, name=None, element_class=None, else: element_class = modn_dense_ntl.Polynomial_dense_modn_ntl_ZZ PolynomialRing_commutative.__init__(self, base_ring, name=name, - element_class=element_class) + element_class=element_class, category=category) @cached_method def modulus(self): @@ -2274,7 +2342,7 @@ def residue_field(self, ideal, names=None): class PolynomialRing_dense_mod_p(PolynomialRing_dense_finite_field, PolynomialRing_dense_mod_n, PolynomialRing_singular_repr): - def __init__(self, base_ring, name="x", implementation=None): + def __init__(self, base_ring, name="x", implementation=None, category=None): """ TESTS:: @@ -2325,7 +2393,7 @@ def __init__(self, base_ring, name="x", implementation=None): self._implementation_names = ('NTL',) self._implementation_repr = ' (using NTL)' PolynomialRing_dense_mod_n.__init__(self, base_ring, name=name, - element_class=element_class) + element_class=element_class, category=category) from sage.rings.polynomial.polynomial_singular_interface import can_convert_to_singular self._has_singular = can_convert_to_singular(self) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index a3a67ce453b..1f91ec93a26 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -40,6 +40,9 @@ from sage.categories.integral_domains import IntegralDomains from sage.categories.commutative_rings import CommutativeRings _CommutativeRings = CommutativeRings() +from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields +_CompleteDiscreteValuationRings = CompleteDiscreteValuationRings() +_CompleteDiscreteValuationFields = CompleteDiscreteValuationFields() import sage.misc.weak_dict _cache = sage.misc.weak_dict.WeakValueDictionary() @@ -524,6 +527,12 @@ def _single_variate(base_ring, name, sparse, implementation): elif isinstance(base_ring, padic_base_leaves.pAdicRingFixedMod): R = m.PolynomialRing_dense_padic_ring_fixed_mod(base_ring, name) + elif base_ring in _CompleteDiscreteValuationRings: + R = m.PolynomialRing_cdvr(base_ring, name, sparse) + + elif base_ring in _CompleteDiscreteValuationFields: + R = m.PolynomialRing_cdvf(base_ring, name, sparse) + elif base_ring.is_field(proof = False): R = m.PolynomialRing_field(base_ring, name, sparse) diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index e696aeee486..e2d8532ea97 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -337,7 +337,7 @@ cdef class Polynomial_zmod_flint(Polynomial_template): sage: r.parent() is GF(19) True - The following example shows that #11782 has been fixed:: + The following example shows that :trac:`11782` has been fixed:: sage: R. = ZZ.quo(9)['x'] sage: f = 2*x^3 + x^2 + x; g = 6*x^2 + 2*x + 1 diff --git a/src/sage/rings/polynomial/polynomial_zz_pex.pyx b/src/sage/rings/polynomial/polynomial_zz_pex.pyx index 02ad5f9b334..dabff28e310 100644 --- a/src/sage/rings/polynomial/polynomial_zz_pex.pyx +++ b/src/sage/rings/polynomial/polynomial_zz_pex.pyx @@ -227,7 +227,7 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): TESTS: - The following was fixed at trac ticket #10475:: + The following was fixed at :trac:`10475`:: sage: F. = GF(4) sage: P. = F[] diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 4390760e1fd..50079322f7f 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -3952,7 +3952,14 @@ def real_roots(p, bounds=None, seed=None, skip_squarefree=False, do_logging=Fals sage: real_roots(p, retval='interval') [(1.000000000000000?, 1), (1.414213562373095?, 2), (2.000000000000000?, 3)] - Check that #10803 is fixed :: + TESTS: + + Check that :trac:`20269` is fixed:: + + sage: real_roots(polygen(AA))[0][0][0].parent() + Rational Field + + Check that :trac:`10803` is fixed:: sage: f = 2503841067*x^13 - 15465014877*x^12 + 37514382885*x^11 - 44333754994*x^10 + 24138665092*x^9 - 2059014842*x^8 - 3197810701*x^7 + 803983752*x^6 + 123767204*x^5 - 26596986*x^4 - 2327140*x^3 + 75923*x^2 + 7174*x + 102 sage: len(real_roots(f,seed=1)) @@ -4000,7 +4007,7 @@ def real_roots(p, bounds=None, seed=None, skip_squarefree=False, do_logging=Fals if strategy=='warp': if factor.constant_coefficient() == 0: x = factor.parent().gen() - extra_roots.append(((0, 0), x, exp, None, None)) + extra_roots.append(((QQ.zero(), QQ.zero()), x, exp, None, None)) factor = factor // x if ar_input: oc = ocean(ctx, bernstein_polynomial_factory_ar(factor, False), warp_map(False)) diff --git a/src/sage/rings/polynomial/symmetric_ideal.py b/src/sage/rings/polynomial/symmetric_ideal.py index 0f02faff4ae..5171ddbfcaf 100644 --- a/src/sage/rings/polynomial/symmetric_ideal.py +++ b/src/sage/rings/polynomial/symmetric_ideal.py @@ -98,18 +98,18 @@ class SymmetricIdeal( Ideal_generic ): Buchberger type that computes a Groebner basis `G` for `I` that allows for computation of a unique normal form, that is zero precisely for the elements of `I` -- see [AB2008]_. See - :meth:`.groebner_basis` for more details. + :meth:`groebner_basis` for more details. Our implementation allows more than one generator and also provides degree lexicographic and degree reverse lexicographic monomial orderings -- we do, however, not guarantee termination of the Buchberger algorithm in these cases. - .. [AB2007] M. Aschenbrenner, C. Hillar, + .. [AB2007] \M. Aschenbrenner, C. Hillar, Finite generation of symmetric ideals. Trans. Amer. Math. Soc. 359 (2007), no. 11, 5171--5192. - .. [AB2008] M. Aschenbrenner, C. Hillar, + .. [AB2008] \M. Aschenbrenner, C. Hillar, `An Algorithm for Finding Symmetric Groebner Bases in Infinite Dimensional Rings. `_ diff --git a/src/sage/rings/polynomial/term_order.py b/src/sage/rings/polynomial/term_order.py index 39528efcd93..17345c0e5b0 100644 --- a/src/sage/rings/polynomial/term_order.py +++ b/src/sage/rings/polynomial/term_order.py @@ -337,7 +337,7 @@ sage: T = TermOrder("royalorder") Traceback (most recent call last): ... - TypeError: Unknown term order 'royalorder' + ValueError: unknown term order 'royalorder' sage: T = TermOrder("royalorder",force=True) sage: T royalorder term order @@ -355,6 +355,15 @@ - Kwankyu Lee: implemented matrix and weighted degree term orders, refactoring """ +#***************************************************************************** +# 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 re from sage.structure.sage_object import SageObject @@ -604,6 +613,13 @@ def __init__(self, name='lex', n=0, force=False): sage: t4 Degree negative lexicographic term order + We allow blocks of length 0, these are simply ignored:: + + sage: TermOrder('lex(0),degrevlex(5),deglex(0),deglex(2)') + Block term order with blocks: + (Degree reverse lexicographic term order of length 5, + Degree lexicographic term order of length 2) + .. note:: The optional `n` parameter is not necessary if only @@ -631,17 +647,16 @@ def __init__(self, name='lex', n=0, force=False): sage: R. = PolynomialRing(QQ,order=T) Traceback (most recent call last): ... - ValueError: TermOrder copy constructor called with different number of variables (15) than input term order (16). - + ValueError: the length of the given term order (16) differs from the number of variables (15) """ if isinstance(name, TermOrder): self.__copy(name) - if n > 0: + if n: if not name.is_block_order() and not name.is_weighted_degree_order(): self._length = n - else: - if n != len(name): - raise ValueError("TermOrder copy constructor called with different number of variables (%d) than input term order (%d)."%(n,len(name))) + elif self._length != n: + raise ValueError("the length of the given term order ({}) differs from the number of variables ({})" + .format(self._length, n)) return if isinstance(name, str): @@ -652,7 +667,7 @@ def __init__(self, name='lex', n=0, force=False): name = name.list() # name may be a matrix name = tuple(name) except Exception: - raise TypeError("%s is not a valid term order"%(name,)) + raise ValueError("{!r} is not a valid term order".format(name)) self._blocks = tuple() self._weights = None @@ -662,16 +677,12 @@ def __init__(self, name='lex', n=0, force=False): if name == "block": # block term order with blocks in a list length = 0 blocks = [] - name_str = [] singular_str = [] macaulay2_str = [] for t in n: if not isinstance(t, TermOrder): - try: - t = TermOrder(t,force=True) - except Exception: - raise TypeError + t = TermOrder(t, force=True) if t.name() == 'block': blocks = blocks + list(t.blocks()) singular_str.append("%s"%(t.singular_str()[1:-1],)) # [1:-1] is needed to remove parenthesis @@ -708,7 +719,7 @@ def __init__(self, name='lex', n=0, force=False): block_names = re.findall(split_pattern,name) if len(block_names) == 0: - raise TypeError("No term order specified") + raise ValueError("no term order specified") elif len(block_names) == 1: name = block_names[0] match = re.match('m\(([-+0-9,]+)\)$',name) @@ -717,7 +728,7 @@ def __init__(self, name='lex', n=0, force=False): self.__copy(TermOrder(m)) else: # simple order if name not in print_name_mapping.keys() and name not in singular_name_mapping.values(): - raise TypeError("Unknown term order '%s'"%(name,)) + raise ValueError("unknown term order {!r}".format(name)) self._length = n self._name = name self._singular_str = singular_name_mapping.get(name,name) @@ -726,7 +737,6 @@ def __init__(self, name='lex', n=0, force=False): else: # len(block_names) > 1, and hence block order represented by a string length = 0 blocks = [] - name_str = [] singular_str = [] macaulay2_str = [] @@ -735,24 +745,22 @@ def __init__(self, name='lex', n=0, force=False): try: block_name, block_length, _ = re.split(length_pattern,block.strip()) block_length = int(block_length) - assert( block_length > 0) - - blocks.append( TermOrder(block_name, block_length, force=force) ) - name_str.append("%s(%d)"%(block_name, block_length)) - singular_str.append("%s(%d)"%(singular_name_mapping.get(block_name, block_name), block_length)) - macaulay2_str.append("%s => %d"%(macaulay2_name_mapping.get(block_name, block_name), block_length)) - length += block_length + if block_length > 0: # ignore blocks with length 0 + blocks.append( TermOrder(block_name, block_length, force=force) ) + singular_str.append("%s(%d)"%(singular_name_mapping.get(block_name, block_name), block_length)) + macaulay2_str.append("%s => %d"%(macaulay2_name_mapping.get(block_name, block_name), block_length)) + length += block_length except ValueError: block_name = block.strip() if block_name.lower() != "c": - raise TypeError("%s is not a valid term ordering (wrong part: '%s')"%(name, block)) + raise ValueError("{!r} is not a valid term order (wrong part: {!r})".format(name, block)) - if n != 0 and length != n: - raise TypeError("Term order length does not match the number of generators") + if n and length != n: + raise ValueError("term order length does not match the number of generators") self.__copy(TermOrder('block', blocks)) elif isinstance(name, str) and (isinstance(n, tuple) or isinstance(n,list)): # weighted degree term orders if name not in print_name_mapping.keys() and name not in singular_name_mapping.values() and not force: - raise TypeError("Unknown term order '%s'"%(name,)) + raise ValueError("unknown term order {!r}".format(name)) weights = tuple(int(w) for w in n) # n is a tuple of weights if any([w<=0 for w in weights]): raise ValueError("the degree weights must be positive integers") @@ -764,11 +772,11 @@ def __init__(self, name='lex', n=0, force=False): self._magma_str = "" self._weights = weights # defined only for weighted degree orders elif isinstance(name, tuple): # name represents a matrix - if n == 0: + if not n: from math import sqrt n = int(sqrt(len(name))) - if n**2 != len(name): - raise TypeError("%s does not specify a square matrix"%(name,)) + if n*n != len(name): + raise ValueError("{} does not specify a square matrix".format(name)) int_str = ','.join([str(int(e)) for e in name]) @@ -783,7 +791,7 @@ def __init__(self, name='lex', n=0, force=False): self._matrix.set_immutable() self._weights = name[:n] # the first row of the matrix gives weights else: - raise TypeError("%s is not a valid term order"%(name,)) + raise ValueError("{!r} is not a valid term order".format(name)) if self._length != 0: self._singular_str = self._singular_str%dict(ngens=self._length) @@ -2056,6 +2064,7 @@ def is_weighted_degree_order(self): """ return self._weights is not None + def termorder_from_singular(S): """ Return the Sage term order of the basering in the given Singular interface @@ -2091,7 +2100,6 @@ def termorder_from_singular(S): from sage.all import ZZ singular = S T = singular('ringlist(basering)[3]') - nblocks = ZZ(singular.eval('size(%s)'%T.name())) order = [] for block in T: blocktype = singular.eval('%s[1]'%block.name()) @@ -2107,7 +2115,7 @@ def termorder_from_singular(S): else: order.append(TermOrder(inv_singular_name_mapping[blocktype], ZZ(singular.eval("size(%s[2])"%block.name())))) if not order: - raise ValueError("Invalid term order in Singular") + raise ValueError("invalid term order in Singular") out = order.pop(0) while order: out = out + order.pop(0) diff --git a/src/sage/rings/power_series_ring.py b/src/sage/rings/power_series_ring.py index 14d7545357f..6602ac1d36d 100644 --- a/src/sage/rings/power_series_ring.py +++ b/src/sage/rings/power_series_ring.py @@ -703,6 +703,19 @@ def _element_constructor_(self, f, prec=infinity, check=True): sage: PowerSeriesRing(PowerSeriesRing(QQ,'x'),'x')(x).coefficients() [x] + Conversion from symbolic series:: + + sage: x,y = var('x,y') + sage: s=(1/(1-x)).series(x,3); s + 1 + 1*x + 1*x^2 + Order(x^3) + sage: R. = PowerSeriesRing(QQ) + sage: R(s) + 1 + x + x^2 + O(x^3) + sage: ex=(gamma(1-y)).series(y,3) + sage: R. = PowerSeriesRing(SR) + sage: R(ex) + 1 + euler_gamma*y + (1/2*euler_gamma^2 + 1/12*pi^2)*y^2 + O(y^3) + Laurent series with non-negative valuation are accepted (see :trac:`6431`):: @@ -728,6 +741,7 @@ def _element_constructor_(self, f, prec=infinity, check=True): prec = integer.Integer(prec) if prec < 0: raise ValueError("prec (= %s) must be non-negative" % prec) + from sage.symbolic.series import SymbolicSeries if isinstance(f, power_series_ring_element.PowerSeries) and f.parent() is self: if prec >= f.prec(): return f @@ -744,6 +758,12 @@ def _element_constructor_(self, f, prec=infinity, check=True): num = self.element_class(self, f.numerator(), prec, check=check) den = self.element_class(self, f.denominator(), prec, check=check) return self.coerce(num/den) + elif isinstance(f, SymbolicSeries): + if str(f.default_variable()) == self.variable_name(): + return self.element_class(self, f.list(), + f.degree(f.default_variable()), check=check) + else: + raise TypeError("Can only convert series into ring with same variable name.") return self.element_class(self, f, prec, check=check) def construction(self): diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 95bed3b36d7..2b394fb3487 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -106,22 +106,18 @@ sage: disc1(b, c, d) == disc2(s1, s2, s3) True -We can coerce from symbolic expressions:: +We can convert from symbolic expressions:: sage: QQbar(sqrt(-5)) 2.236067977499790?*I sage: AA(sqrt(2) + sqrt(3)) 3.146264369941973? - sage: QQbar(sqrt(2)) + sqrt(3) - 3.146264369941973? - sage: sqrt(2) + QQbar(sqrt(3)) - 3.146264369941973? sage: QQbar(I) I sage: AA(I) Traceback (most recent call last): ... - TypeError: Illegal initializer for algebraic number + ValueError: Cannot coerce algebraic number with non-zero imaginary part to algebraic real sage: QQbar(I * golden_ratio) 1.618033988749895?*I sage: AA(golden_ratio)^2 - AA(golden_ratio) @@ -137,6 +133,14 @@ ... ValueError: Cannot coerce algebraic number with non-zero imaginary part to algebraic real +The coercion, however, goes in the other direction, since not all +symbolic expressions are algebraic numbers:: + + sage: QQbar(sqrt(2)) + sqrt(3) + sqrt(3) + 1.414213562373095? + sage: QQbar(sqrt(2) + QQbar(sqrt(3))) + 3.146264369941973? + Note the different behavior in taking roots: for ``AA`` we prefer real roots if they exist, but for ``QQbar`` we take the principal root:: @@ -245,7 +249,7 @@ True sage: r.norm() == 4 True - sage: (r+I).norm().minpoly() + sage: (r+QQbar(I)).norm().minpoly() x^2 - 10*x + 13 sage: r = AA.polynomial_root(x^2 - x - 1, RIF(-1, 0)); r -0.618033988749895? @@ -496,13 +500,14 @@ (0, [(-0.3535533905932738?*x + 1/2)/(x^2 - 1.414213562373095?*x + 1), (0.3535533905932738?*x + 1/2)/(x^2 + 1.414213562373095?*x + 1)]) """ import itertools +import operator import sage.rings.ring from sage.misc.fast_methods import Singleton from sage.misc.cachefunc import cached_method from sage.structure.sage_object import SageObject from sage.rings.real_mpfr import RR -from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement +from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement, RealIntervalField_class from sage.rings.complex_field import ComplexField from sage.rings.complex_interval_field import ComplexIntervalField, is_ComplexIntervalField from sage.rings.complex_interval import is_ComplexIntervalFieldElement @@ -522,22 +527,6 @@ is_SymbolicExpressionRing = None -def _late_import(): - r""" - Import the name "is_SymbolicExpressionRing" (which would cause an infinite - loop if imported at startup time). - - EXAMPLE:: - - sage: sage.rings.qqbar._late_import() - sage: sage.rings.qqbar.is_SymbolicExpressionRing == sage.symbolic.ring.is_SymbolicExpressionRing - True - """ - global is_SymbolicExpressionRing - if is_SymbolicExpressionRing is None: - import sage.symbolic.ring - is_SymbolicExpressionRing = sage.symbolic.ring.is_SymbolicExpressionRing - class AlgebraicField_common(sage.rings.ring.Field): r""" Common base class for the classes :class:`~AlgebraicRealField` and @@ -788,12 +777,11 @@ def _coerce_map_from_(self, from_par): True sage: a + AA(3) 5.645751311064590? + sage: AA.has_coerce_map_from(SR) + False """ - if from_par == ZZ or from_par == QQ or from_par == int or from_par == long: - return True - if from_par == AA: - return True - return False + return (from_par is ZZ or from_par is QQ + or from_par is AA) def completion(self, p, prec, extras = {}): r""" @@ -1184,15 +1172,11 @@ def _coerce_map_from_(self, from_par): True sage: QQbar.has_coerce_map_from(CC) False + sage: QQbar.has_coerce_map_from(SR) + False """ - if from_par == ZZ or from_par == QQ or from_par == int or from_par == long: - return True - if from_par == AA or from_par == QQbar: - return True - _late_import() - if is_SymbolicExpressionRing(from_par): - return True - return False + return (from_par is ZZ or from_par is QQ + or from_par is AA or from_par is QQbar) def completion(self, p, prec, extras = {}): r""" @@ -2540,186 +2524,19 @@ def __call__(self, elt): -a^2 + 2 """ if self._trivial: - return elt.rational_value() - if elt.is_rational(): - return self._field(elt.rational_value()) + return elt._value + if isinstance(elt, ANRational): + return self._field(elt._value) if elt.generator() is self: return elt.field_element_value() gen = elt.generator() sp = gen.super_poly(self) - # print gen - # print self assert(not(sp is None)) - # print sp - # print self._field return self._field(elt.field_element_value().polynomial()(sp)) -# These are the functions used to add, subtract, multiply, and divide -# algebraic numbers. Basically, we try to compute exactly if both -# arguments are already known to be in the same number field. Otherwise -# we fall back to floating-point computation to be backed up by exact -# symbolic computation only as required. -# These choices are motivated partly by efficiency considerations -# (not backed up by benchmarks, so other possibilities might be more -# efficient), and partly by concerns for the prettiness of output: -# we want algebraic numbers to print as Gaussian rationals, rather -# than as intervals, as often as possible. - -def an_addsub_rational(a, b, sub): - r""" - Used to add and subtract algebraic numbers. Used when both are actually rational. - - EXAMPLE:: - - sage: from sage.rings.qqbar import an_addsub_rational - sage: f = an_addsub_rational(QQbar(2), QQbar(3/7), False); f - 17/7 - sage: type(f) - - """ - va = a._descr._value - vb = b._descr._value - if sub: - v = va - vb - else: - v = va + vb - return ANRational(v) - -def an_muldiv_rational(a, b, div): - r""" - Used to multiply and divide algebraic numbers. Used when both are actually rational. - - EXAMPLE:: - - sage: from sage.rings.qqbar import an_muldiv_rational - sage: f = an_muldiv_rational(QQbar(2), QQbar(3/7), False); f - 6/7 - sage: type(f) - - """ - va = a._descr._value - va = a._descr._value - vb = b._descr._value - if div: - v = va / vb - else: - v = va * vb - return ANRational(v) - -def an_addsub_expr(a, b, sub): - r""" - Add or subtract algebraic numbers represented as multi-part expressions. - - EXAMPLE:: - - sage: a = QQbar(sqrt(2)) + QQbar(sqrt(3)) - sage: b = QQbar(sqrt(3)) + QQbar(sqrt(5)) - sage: type(a._descr); type(b._descr) - - - sage: from sage.rings.qqbar import an_addsub_expr - sage: x = an_addsub_expr(a, b, False); x - - sage: x.exactify() - -6/7*a^7 + 2/7*a^6 + 71/7*a^5 - 26/7*a^4 - 125/7*a^3 + 72/7*a^2 + 43/7*a - 47/7 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.12580...? - """ - return ANBinaryExpr(a, b, ('-' if sub else '+')) - -def an_muldiv_expr(a, b, div): - r""" - Multiply or divide algebraic numbers represented as multi-part expressions. - - EXAMPLE:: - - sage: a = QQbar(sqrt(2)) + QQbar(sqrt(3)) - sage: b = QQbar(sqrt(3)) + QQbar(sqrt(5)) - sage: type(a._descr) - - sage: from sage.rings.qqbar import an_muldiv_expr - sage: x = an_muldiv_expr(a, b, False); x - - sage: x.exactify() - 2*a^7 - a^6 - 24*a^5 + 12*a^4 + 46*a^3 - 22*a^2 - 22*a + 9 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.1258...? - """ - return ANBinaryExpr(a, b, ('/' if div else '*')) - -def an_muldiv_element(a, b, div): - r""" - Multiply or divide two elements represented as elements of number fields. - - EXAMPLES:: - - sage: a = QQbar(sqrt(2) + sqrt(3)); a.exactify() - sage: b = QQbar(sqrt(3) + sqrt(5)); b.exactify() - sage: type(a._descr) - - sage: from sage.rings.qqbar import an_muldiv_element - sage: an_muldiv_element(a,b,False) - - """ - ad = a._descr - bd = b._descr - adg = ad.generator() - bdg = bd.generator() - if adg == qq_generator or adg == bdg: - if div: - return ANExtensionElement(bdg, ad._value / bd._value) - else: - return ANExtensionElement(bdg, ad._value * bd._value) - if bdg == qq_generator: - if div: - return ANExtensionElement(adg, ad._value / bd._value) - else: - return ANExtensionElement(adg, ad._value * bd._value) - return ANBinaryExpr(a, b, ('/' if div else '*')) - -def an_addsub_element(a, b, sub): - r""" - Add or subtract two elements represented as elements of number fields. - - EXAMPLES:: - - sage: a = QQbar(sqrt(2) + sqrt(3)); a.exactify() - sage: b = QQbar(sqrt(3) + sqrt(5)); b.exactify() - sage: from sage.rings.qqbar import an_addsub_element - sage: an_addsub_element(a,b,False) - - """ - ad = a._descr - bd = b._descr - adg = ad.generator() - bdg = bd.generator() - if adg == qq_generator or adg == bdg: - if sub: - return ANExtensionElement(bdg, ad._value - bd._value) - else: - return ANExtensionElement(bdg, ad._value + bd._value) - elif bdg == qq_generator: - if sub: - return ANExtensionElement(adg, ad._value - bd._value) - else: - return ANExtensionElement(adg, ad._value + bd._value) - else: - return ANBinaryExpr(a, b, ('-' if sub else '+')) - -# Here we hand-craft a simple multimethod dispatch. -_mul_algo = {} -_add_algo = {} -_descriptors = ('rational', 'element', 'other') -for a in _descriptors: - for b in _descriptors: - key = (a, b) - - if a == b == 'rational': - _mul_algo[key] = an_muldiv_rational - _add_algo[key] = an_addsub_rational - elif a in ('rational', 'element') and b in ('rational', 'element'): - _mul_algo[key] = an_muldiv_element - _add_algo[key] = an_addsub_element - else: - _mul_algo[key] = an_muldiv_expr - _add_algo[key] = an_addsub_expr +# dictionary for multimethod dispatch +_binop_algo = {} class ANDescr(SageObject): r""" @@ -2730,22 +2547,6 @@ class ANDescr(SageObject): ``ANDescr`` and all of its subclasses are for internal use, and should not be used directly. """ - def is_exact(self): - """ - Returns True if self is an ANRational, or ANExtensionElement. - - EXAMPLES:: - - sage: from sage.rings.qqbar import ANRational - sage: ANRational(1/2).is_exact() - True - sage: QQbar(3+I)._descr.is_exact() - True - sage: QQbar.zeta(17)._descr.is_exact() - True - """ - return False - def is_simple(self): r""" Checks whether this descriptor represents a value with the same @@ -2774,38 +2575,6 @@ def is_simple(self): """ return False - def is_rational(self): - r""" - Returns ``True`` if self is an ``ANRational`` object. (Note that the - constructors for ``ANExtensionElement`` will actually return - ``ANRational`` objects for rational numbers.) - - EXAMPLES:: - - sage: from sage.rings.qqbar import ANRational - sage: ANRational(3/7).is_rational() - True - """ - return False - - def is_field_element(self): - r""" - Returns ``True`` if self is an ``ANExtensionElement``. - - EXAMPLES:: - - sage: from sage.rings.qqbar import ANExtensionElement, ANRoot, AlgebraicGenerator - sage: _. = QQ['y'] - sage: x = polygen(QQbar) - sage: nf2 = NumberField(y^2 - 2, name='a', check=False) - sage: root2 = ANRoot(x^2 - 2, RIF(1, 2)) - sage: gen2 = AlgebraicGenerator(nf2, root2) - sage: sqrt2 = ANExtensionElement(gen2, nf2.gen()) - sage: sqrt2.is_field_element() - True - """ - return False - # Unitary operators: the second argument "n" is an AlgebraicNumber_base # wrapper around self. @@ -2913,6 +2682,7 @@ def norm(self, n): else: return (n*n)._descr + class AlgebraicNumber_base(sage.structure.element.FieldElement): r""" This is the common base class for algebraic numbers (complex @@ -3019,7 +2789,7 @@ def _repr_(self): sage: AA(19).sqrt() 4.358898943540674? """ - if self._descr.is_rational(): + if isinstance(self._descr, ANRational): return repr(self._descr) if isinstance(self._descr, ANExtensionElement) and self._descr._generator is QQbar_I_generator: return repr(self._descr._value) @@ -3050,7 +2820,7 @@ def _latex_(self): 4.358898943540674? """ from sage.misc.latex import latex - if self._descr.is_rational(): + if isinstance(self._descr, ANRational): return latex(self._descr._value) if isinstance(self._descr, ANExtensionElement) and self._descr._generator is QQbar_I_generator: return latex(self._descr._value) @@ -3117,11 +2887,9 @@ def _mul_(self, other): sage: AA(sqrt(2)) * AA(sqrt(8)) # indirect doctest 4.000000000000000? """ - sd = self._descr - od = other._descr - sdk = sd.kind() - odk = od.kind() - return type(self)(_mul_algo[sdk, odk](self, other, False)) + sk = type(self._descr) + ok = type(other._descr) + return type(self)(_binop_algo[sk,ok](self, other, operator.mul)) def _div_(self, other): """ @@ -3130,11 +2898,9 @@ def _div_(self, other): sage: AA(sqrt(2)) / AA(sqrt(8)) # indirect doctest 0.500000000000000? """ - sd = self._descr - od = other._descr - sdk = sd.kind() - odk = od.kind() - return type(self)(_mul_algo[sdk, odk](self, other, True)) + sk = type(self._descr) + ok = type(other._descr) + return type(self)(_binop_algo[sk,ok](self, other, operator.div)) def __invert__(self): """ @@ -3155,11 +2921,9 @@ def _add_(self, other): sage: rt1 + rt2 # indirect doctest 1.000000000000000? """ - sd = self._descr - od = other._descr - sdk = sd.kind() - odk = od.kind() - return type(self)(_add_algo[sdk, odk](self, other, False)) + sk = type(self._descr) + ok = type(other._descr) + return type(self)(_binop_algo[sk,ok](self, other, operator.add)) def _sub_(self, other): """ @@ -3168,11 +2932,9 @@ def _sub_(self, other): sage: AA(golden_ratio) * 2 - AA(5).sqrt() # indirect doctest 1.000000000000000? """ - sd = self._descr - od = other._descr - sdk = sd.kind() - odk = od.kind() - return type(self)(_add_algo[sdk, odk](self, other, True)) + sk = type(self._descr) + ok = type(other._descr) + return type(self)(_binop_algo[sk,ok](self, other, operator.sub)) def _neg_(self): """ @@ -3511,7 +3273,7 @@ def exactify(self): 2 """ od = self._descr - if od.is_exact(): return + if isinstance(od, (ANRational, ANExtensionElement)): return self._set_descr(self._descr.exactify()) def _set_descr(self, new_descr): @@ -3575,7 +3337,7 @@ def _exact_field(self): """ sd = self._descr - if sd.is_exact(): + if isinstance(sd, (ANRational, ANExtensionElement)): return sd.generator() self.exactify() return self._exact_field() @@ -3595,7 +3357,7 @@ def _exact_value(self): 2*a^4 + 2*a^3 - 34*a^2 - 17*a + 150 where a^5 - 2*a^4 - 18*a^3 + 38*a^2 + 82*a - 181 = 0 and a in 2.554256611698490? """ sd = self._descr - if sd.is_exact(): + if isinstance(sd, (ANRational, ANExtensionElement)): return sd self.exactify() return self._descr @@ -3752,11 +3514,37 @@ def interval(self, field): sage: AA(-5).sqrt().interval(RIF) Traceback (most recent call last): ... - TypeError: unable to convert 2.236067977499789697?*I to real interval + TypeError: unable to convert 2.236067977499790?*I to real interval + + TESTS: + + Check that :trac:`20209` is fixed:: + + sage: RIF(QQbar(2).sqrt()) + 1.414213562373095? + sage: RIF(QQbar.gen() + QQbar(2).sqrt() - QQbar.gen()) + 1.414213562373095? + sage: RIF((QQbar.gen() + QQbar(2).sqrt() - QQbar.gen()).sqrt()) + 1.189207115002722? + + sage: RealIntervalField(129)(QQbar(3).sqrt()) + 1.73205080756887729352744634150587236695? + sage: RIF(QQbar.gen()) + Traceback (most recent call last): + ... + TypeError: unable to convert I to real interval """ target = RR(1.0) >> field.prec() val = self.interval_diameter(target) - return field(val) + if isinstance(field, RealIntervalField_class) and is_ComplexIntervalFieldElement(val): + if val.imag().is_zero(): + return field(val.real()) + elif self.imag().is_zero(): + return field(self.real()) + else: + raise TypeError("unable to convert {} to real interval".format(self)) + else: + return field(val) _real_mpfi_ = interval @@ -3890,8 +3678,8 @@ def __cmp__(self, other): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-182 - 0.7598602580415435?*I, - 0.?e-249 + 0.7598602580415435?*I, + 0.?e-215 - 0.7598602580415435?*I, + 0.?e-229 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] @@ -4000,9 +3788,9 @@ def __eq__(self, other): except TypeError: return False if self is other: return True - if other._descr.is_rational() and other._descr.rational_value() == 0: + if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): return not self - if self._descr.is_rational() and self._descr.rational_value() == 0: + if isinstance(self._descr, ANRational) and self._descr._value.is_zero(): return not other return not self._sub_(other) @@ -4037,17 +3825,16 @@ def __nonzero__(self): True """ val = self._value - if not val.contains_zero(): - return True - if self._descr.is_field_element(): - # The ANExtensionElement constructor returns an ANRational - # instead, if the number is zero. + d = self._descr + if not val.contains_zero() or isinstance(d, ANExtensionElement): return True - if self._descr.is_rational(): - return self._descr.rational_value().__nonzero__() - if self._value.prec() < 128: + elif isinstance(d, ANRational): + return bool(d._value) + + while self._value.prec() < 128: self._more_precision() - return self.__nonzero__() + if not self._value.contains_zero(): + return True # Sigh... self.exactify() @@ -4292,7 +4079,7 @@ def _rational_(self): ... ValueError: Cannot coerce irrational Algebraic Real 1.414213562373095? to Rational sage: v1 = QQbar(1/3 + I*sqrt(5))^7 - sage: v2 = QQbar(100336/729*golden_ratio - 50168/729)*I + sage: v2 = QQbar((100336/729*golden_ratio - 50168/729)*I) sage: v = v1 + v2; v -259.6909007773206? + 0.?e-15*I sage: v._rational_() @@ -4438,7 +4225,7 @@ def complex_exact(self, field): EXAMPLES:: - sage: a = QQbar.zeta(9) + I + QQbar.zeta(9).conjugate(); a + sage: a = QQbar.zeta(9) + QQbar(I) + QQbar.zeta(9).conjugate(); a 1.532088886237957? + 1.000000000000000?*I sage: a.complex_exact(CIF) 1.532088886237957? + 1*I @@ -4605,9 +4392,9 @@ def __cmp__(self, other): -1 """ if self is other: return 0 - if other._descr.is_rational() and other._descr.rational_value() == 0: + if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): return self.sign() - elif self._descr.is_rational() and self._descr.rational_value() == 0: + elif isinstance(self._descr, ANRational) and self._descr._value.is_zero(): return -other.sign() else: return self._sub_(other).sign() @@ -4728,10 +4515,10 @@ def _integer_(self, Z=None): raise ValueError("Cannot coerce non-integral Algebraic Real %s to Integer" % self) self.exactify() - if not self._descr.is_rational(): + if not isinstance(self._descr, ANRational): raise ValueError("Cannot coerce irrational Algebraic Real %s to Integer" % self) - return ZZ(self._descr.rational_value()) + return ZZ(self._descr._value) def _floor_ceil(self, method): r""" @@ -4762,7 +4549,7 @@ def _floor_ceil(self, method): return candidate self._more_precision() # field elements are irrational by construction - if i == 2 and not self._descr.is_field_element(): + if i == 2 and not isinstance(self._descr, ANExtensionElement): try: return method(self._rational_()) except (ValueError, TypeError): @@ -4858,10 +4645,10 @@ def _rational_(self): 25/8 """ self.exactify() - if not self._descr.is_rational(): + if not isinstance(self._descr, ANRational): raise ValueError("Cannot coerce irrational Algebraic Real %s to Rational" % self) - return QQ(self._descr.rational_value()) + return QQ(self._descr._value) def real(self): """ @@ -4927,15 +4714,9 @@ def sign(self): return 1 elif self._value.upper() < 0: return -1 - elif self._descr.is_rational(): - val = self._descr.rational_value() - if val > 0: - return 1 - elif val < 0: - return -1 - else: - return 0 - elif self._descr.is_field_element(): + elif isinstance(self._descr, ANRational): + return self._descr._value.sign() + elif isinstance(self._descr, ANExtensionElement): # All field elements are irrational by construction # (the ANExtensionElement constructor will return an ANRational # instead, if the number is actually rational). @@ -5274,24 +5055,6 @@ def handle_sage_input(self, sib, coerce, is_qqbar): v = sib.name('QQbar' if is_qqbar else 'AA')(v) return (v, False) - def kind(self): - r""" - Return a string describing what kind of element this is. Since this is - a rational number, the result is either ``'zero'`` or ``'rational'``. - - EXAMPLES:: - - sage: a = QQbar(3)._descr; type(a) - - sage: a.kind() - 'rational' - sage: a = QQbar(0)._descr; type(a) - - sage: a.kind() - 'rational' - """ - return 'rational' - def _interval_fast(self, prec): r""" Return an approximation to self in a real interval field of precision prec. @@ -5326,33 +5089,6 @@ def is_complex(self): """ return False - def is_rational(self): - r""" - Return True, since this is a rational number. - - EXAMPLE:: - - sage: QQbar(34/9)._descr.is_rational() - True - sage: QQbar(0)._descr.is_rational() - True - """ - return True - - def rational_value(self): - r""" - Return self as a rational number. - - EXAMPLE:: - - sage: a = QQbar(789/19) - sage: b = a._descr.rational_value(); b - 789/19 - sage: type(b) - - """ - return self._value - def exactify(self): r""" Calculate self exactly. Since self is a rational number, return self. @@ -5365,17 +5101,6 @@ def exactify(self): """ return self - def is_exact(self): - r""" - Return True, since rationals are exact. - - EXAMPLE:: - - sage: QQbar(1/3)._descr.is_exact() - True - """ - return True - def is_simple(self): """ Checks whether this descriptor represents a value with the same @@ -5883,17 +5608,6 @@ def handle_sage_input(self, sib, coerce, is_qqbar): good_intv = intv return (parent.polynomial_root(poly, sib(good_intv)), True) - def kind(self): - r""" - Return a string indicating what kind of element this is. - - EXAMPLE:: - - sage: (x^2 - x - 1).roots(ring=AA, multiplicities=False)[1]._descr.kind() - 'other' - """ - return 'other' - def is_complex(self): r""" Whether this is a root in `\overline{\QQ}` (rather than `\mathbf{A}`). @@ -6329,8 +6043,6 @@ def exactify(self): sage: two = ANRoot((x-2)*(x-sqrt(QQbar(2))), RIF(1.9, 2.1)) sage: two.exactify() 2 - sage: two.exactify().rational_value() - 2 sage: strange = ANRoot(x^2 + sqrt(QQbar(3))*x - sqrt(QQbar(2)), RIF(-0, 1)) sage: strange.exactify() a where a^8 - 6*a^6 + 5*a^4 - 12*a^2 + 4 = 0 and a in 0.6051012265139511? @@ -6491,8 +6203,6 @@ def _interval_fast(self, prec): self._more_precision() return self._interval_fast(prec) -qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly.gen() - 1, RIF(1))) - class ANExtensionElement(ANDescr): r""" The subclass of ``ANDescr`` that represents a number field @@ -6606,23 +6316,6 @@ def handle_sage_input(self, sib, coerce, is_qqbar): v = sib.name('QQbar' if is_qqbar else 'AA')(v) return (v, True) - def kind(self): - r""" - Return a string describing what kind of element this is. - - EXAMPLE:: - - sage: x = QQbar(sqrt(2) + sqrt(3)) - sage: x.exactify() - sage: x._descr.kind() - 'element' - sage: x = QQbar(I) + 1 - sage: x.exactify() - sage: x._descr.kind() - 'element' - """ - return 'element' - def is_complex(self): r""" Return True if the number field that defines this element is not real. @@ -6645,24 +6338,6 @@ def is_complex(self): """ return not self._exactly_real - def is_exact(self): - r""" - Return True, since ANExtensionElements are exact. - - EXAMPLE:: - - sage: rt2 = QQbar(sqrt(2)) - sage: rtm3 = QQbar(sqrt(-3)) - sage: x = rtm3 + rt2 - rtm3 - sage: x.exactify() - sage: y = x._descr - sage: type(y) - - sage: y.is_exact() - True - """ - return True - def is_simple(self): r""" Checks whether this descriptor represents a value with the same @@ -6695,18 +6370,6 @@ def is_simple(self): self._is_simple = (self.minpoly().degree() == self.generator().field().degree()) return self._is_simple - def is_field_element(self): - r""" - Return True if self is an element of a number field (always true for ANExtensionElements) - - EXAMPLE:: - - sage: v = (x^2 - x - 1).roots(ring=AA, multiplicities=False)[1]._descr.exactify() - sage: v.is_field_element() - True - """ - return True - def generator(self): r""" Return the :class:`~AlgebraicGenerator` object corresponding to self. @@ -7073,21 +6736,6 @@ def handle_sage_input(self, sib, coerce, is_qqbar): return (v, True) - def kind(self): - r""" - Return a string describing what kind of element this is. - - EXAMPLE:: - - sage: x = -QQbar(sqrt(2)) - sage: y = x._descr - sage: type(y) - - sage: y.kind() - 'other' - """ - return 'other' - def is_complex(self): r""" Return whether or not this element is complex. Note that this is a data @@ -7311,7 +6959,7 @@ def handle_sage_input(self, sib, coerce, is_qqbar): sage: from sage.rings.qqbar import * sage: from sage.misc.sage_input import SageInputBuilder sage: sib = SageInputBuilder() - sage: binexp = ANBinaryExpr(AA(3), AA(5), '*') + sage: binexp = ANBinaryExpr(AA(3), AA(5), operator.mul) sage: binexp.handle_sage_input(sib, False, False) ({binop:* {atomic:3} {call: {atomic:AA}({atomic:5})}}, True) sage: binexp.handle_sage_input(sib, False, True) @@ -7344,14 +6992,16 @@ def handle_sage_input(self, sib, coerce, is_qqbar): v1 = sib(arg1, arg1_coerced) v2 = sib(arg2, arg2_coerced) - if op == '+': + if op is operator.add: v = sib.sum([v1, v2], simplify=True) - elif op == '-': + elif op is operator.sub: v = sib.sum([v1, -v2], simplify=True) - elif op == '*': + elif op is operator.mul: v = sib.prod([v1, v2], simplify=True) - else: + elif op is operator.div: v = v1 / v2 + else: + raise RuntimeError("op is {}".format(op)) if result_is_qqbar != is_qqbar: # The following version is not safe with respect to caching; @@ -7365,20 +7015,6 @@ def handle_sage_input(self, sib, coerce, is_qqbar): return (v, True) - def kind(self): - r""" - Return a string describing what kind of element this is. Returns ``'other'``. - - EXAMPLE:: - - sage: x = (QQbar(sqrt(2)) + QQbar(sqrt(5)))._descr - sage: type(x) - - sage: x.kind() - 'other' - """ - return 'other' - def is_complex(self): r""" Whether this element is complex. Does not trigger exact computation, so @@ -7412,19 +7048,7 @@ def _interval_fast(self, prec): if not (is_ComplexIntervalFieldElement(lv) or is_ComplexIntervalFieldElement(rv)): self._complex = False - if op == '-': - return lv - rv - - if op == '+': - return lv + rv - - if op == '/': - return lv / rv - - if op == '*': - return lv * rv - - raise NotImplementedError + return op(lv, rv) def exactify(self): """ @@ -7459,16 +7083,7 @@ def exactify(self): left_value = gen(left._exact_value()) right_value = gen(right._exact_value()) - op = self._op - - if op == '+': - value = left_value + right_value - if op == '-': - value = left_value - right_value - if op == '*': - value = left_value * right_value - if op == '/': - value = left_value / right_value + value = self._op(left_value, right_value) if gen.is_trivial(): return ANRational(value) @@ -7477,6 +7092,151 @@ def exactify(self): finally: sys.setrecursionlimit(old_recursion_limit) +# These are the functions used to add, subtract, multiply, and divide +# algebraic numbers. Basically, we try to compute exactly if both +# arguments are already known to be in the same number field. Otherwise +# we fall back to floating-point computation to be backed up by exact +# symbolic computation only as required. + +def an_binop_rational(a, b, op): + r""" + Used to add, subtract, multiply or divide algebraic numbers. + + Used when both are actually rational. + + EXAMPLES:: + + sage: from sage.rings.qqbar import an_binop_rational + sage: f = an_binop_rational(QQbar(2), QQbar(3/7), operator.add) + sage: f + 17/7 + sage: type(f) + + + sage: f = an_binop_rational(QQbar(2), QQbar(3/7), operator.mul) + sage: f + 6/7 + sage: type(f) + + """ + return ANRational(op(a._descr._value, b._descr._value)) + +def an_binop_expr(a, b, op): + r""" + Add, subtract, multiply or divide algebraic numbers represented as + binary expressions. + + INPUT: + + - ``a``, ``b`` -- two elements + + - ``op`` -- an operator + + EXAMPLE:: + + sage: a = QQbar(sqrt(2)) + QQbar(sqrt(3)) + sage: b = QQbar(sqrt(3)) + QQbar(sqrt(5)) + sage: type(a._descr); type(b._descr) + + + sage: from sage.rings.qqbar import an_binop_expr + sage: x = an_binop_expr(a, b, operator.add); x + + sage: x.exactify() + -6/7*a^7 + 2/7*a^6 + 71/7*a^5 - 26/7*a^4 - 125/7*a^3 + 72/7*a^2 + 43/7*a - 47/7 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.12580...? + + sage: a = QQbar(sqrt(2)) + QQbar(sqrt(3)) + sage: b = QQbar(sqrt(3)) + QQbar(sqrt(5)) + sage: type(a._descr) + + sage: x = an_binop_expr(a, b, operator.mul); x + + sage: x.exactify() + 2*a^7 - a^6 - 24*a^5 + 12*a^4 + 46*a^3 - 22*a^2 - 22*a + 9 where a^8 - 12*a^6 + 23*a^4 - 12*a^2 + 1 = 0 and a in 3.1258...? + """ + return ANBinaryExpr(a, b, op) + +def an_binop_element(a, b, op): + r""" + Add, subtract, multiply or divide two elements represented as elements of + number fields. + + EXAMPLES:: + + sage: sqrt2 = QQbar(2).sqrt() + sage: sqrt3 = QQbar(3).sqrt() + sage: sqrt5 = QQbar(5).sqrt() + + sage: a = sqrt2 + sqrt3; a.exactify() + sage: b = sqrt3 + sqrt5; b.exactify() + sage: type(a._descr) + + sage: from sage.rings.qqbar import an_binop_element + sage: an_binop_element(a, b, operator.add) + + sage: an_binop_element(a, b, operator.sub) + + sage: an_binop_element(a, b, operator.mul) + + sage: an_binop_element(a, b, operator.div) + + + The code tries to use existing unions of number fields:: + + sage: sqrt17 = QQbar(17).sqrt() + sage: sqrt19 = QQbar(19).sqrt() + sage: a = sqrt17 + sqrt19 + sage: b = sqrt17 * sqrt19 - sqrt17 + sqrt19 * (sqrt17 + 2) + sage: b, type(b._descr) + (40.53909377268655?, ) + sage: a.exactify() + sage: b = sqrt17 * sqrt19 - sqrt17 + sqrt19 * (sqrt17 + 2) + sage: b, type(b._descr) + (40.53909377268655?, ) + """ + ad = a._descr + bd = b._descr + adg = ad.generator() + bdg = bd.generator() + if adg == qq_generator or adg == bdg: + return ANExtensionElement(bdg, op(ad._value, bd._value)) + + if bdg == qq_generator: + return ANExtensionElement(adg, op(ad._value, bd._value)) + + if adg in bdg._unions or bdg in adg._unions: + p = bdg._unions[adg] if adg in bdg._unions else adg._unions[bdg] + p = p.parent + adg2 = adg.super_poly(p) + bdg2 = bdg.super_poly(p) + av = ad._value.polynomial()(adg2) + bv = bd._value.polynomial()(bdg2) + v = op(av, bv) + return ANExtensionElement(p, op(av, bv)) + + adg2 = adg.super_poly(bdg) + if adg2 is not None: + av = ad._value.polynomial()(adg2) + return ANExtensionElement(bdg, op(av, bd._value)) + + bdg2 = bdg.super_poly(adg) + if bdg2 is not None: + bv = bd._value.polynomial()(bdg2) + return ANExtensionElement(adg, op(ad._value, bv)) + + return ANBinaryExpr(a, b, op) + +# instanciation of the multimethod dispatch +_binop_algo[ANRational, ANRational] = an_binop_rational +_binop_algo[ANRational, ANExtensionElement] = \ +_binop_algo[ANExtensionElement, ANRational] = \ +_binop_algo[ANExtensionElement, ANExtensionElement ] = an_binop_element +for t1 in (ANRational, ANRoot, ANExtensionElement, ANUnaryExpr, ANBinaryExpr): + for t2 in (ANUnaryExpr, ANBinaryExpr, ANRoot): + _binop_algo[t1, t2] = _binop_algo[t2, t1] = an_binop_expr + +qq_generator = AlgebraicGenerator(QQ, ANRoot(AAPoly.gen() - 1, RIF(1))) + def _init_qqbar(): """ diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index b4301d5b03c..20a1a2d3061 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -189,7 +189,7 @@ def _repr_(self): sage: a-2*a*b # indirect doctest -2*a*b + a - In trac ticket #11068, the case of quotient rings without + In :trac:`11068`, the case of quotient rings without assigned names has been covered as well:: sage: S = SteenrodAlgebra(2) @@ -703,7 +703,7 @@ def lm(self): def lc(self): """ - Return the leading coefficent of this quotient ring element. + Return the leading coefficient of this quotient ring element. EXAMPLE:: diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index fe427f4c02e..f9e4747276c 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -328,7 +328,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: Rational('1/0') Traceback (most recent call last): ... - TypeError: unable to convert 1/0 to a rational + TypeError: unable to convert '1/0' to a rational sage: Rational(1.5) 3/2 sage: Rational('9/6') @@ -514,18 +514,18 @@ cdef class Rational(sage.structure.element.FieldElement): s = xstr.replace('.','') +'/'+pstr n = mpq_set_str( self.value, s, base) if n or mpz_cmp_si(mpq_denref(self.value), 0) == 0: - raise TypeError, "unable to convert %s to a rational"%x + raise TypeError("unable to convert {!r} to a rational".format(x)) mpq_canonicalize(self.value) else: n = mpq_set_str(self.value, xstr, base) if n or mpz_cmp_si(mpq_denref(self.value), 0) == 0: - raise TypeError, "unable to convert %s to a rational"%x + raise TypeError("unable to convert {!r} to a rational".format(x)) mpq_canonicalize(self.value) elif isinstance(x, str): n = mpq_set_str(self.value, x, base) if n or mpz_cmp_si(mpq_denref(self.value), 0) == 0: - raise TypeError, "unable to convert %s to a rational"%x + raise TypeError("unable to convert {!r} to a rational".format(x)) mpq_canonicalize(self.value) elif hasattr(x, "_rational_"): @@ -2724,38 +2724,38 @@ cdef class Rational(sage.structure.element.FieldElement): def __int__(self): """ - Return coercion of ``self`` to Python ``int``. + Convert this rational to a Python ``int``. - This takes the floor of ``self`` if ``self`` has a denominator (which - is consistent with Python's ``long(floats)``). + This truncates ``self`` if ``self`` has a denominator (which is + consistent with Python's ``long(floats)``). EXAMPLES:: sage: int(7/3) 2 sage: int(-7/3) - -3 + -2 """ return int(self.__long__()) def __long__(self): """ - Return coercion of ``self`` to Python ``long``. + Convert this rational to a Python ``long``. - This takes the floor of ``self`` if ``self`` has a denominator (which - is consistent with Python's ``long(floats)``). + This truncates ``self`` if ``self`` has a denominator (which is + consistent with Python's ``long(floats)``). EXAMPLES:: sage: long(7/3) 2L sage: long(-7/3) - -3L + -2L """ cdef mpz_t x if mpz_cmp_si(mpq_denref(self.value),1) != 0: mpz_init(x) - mpz_fdiv_q(x, mpq_numref(self.value), mpq_denref(self.value)) + mpz_tdiv_q(x, mpq_numref(self.value), mpq_denref(self.value)) n = mpz_get_pylong(x) mpz_clear(x) return n diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 84e2fcbaee4..aa8b9147c04 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -85,7 +85,7 @@ class RationalField(Singleton, number_field_base.NumberField): sage: QQ('sage') Traceback (most recent call last): ... - TypeError: unable to convert sage to a rational + TypeError: unable to convert 'sage' to a rational Conversion from the reals to the rationals is done by default using continued fractions. diff --git a/src/sage/rings/real_arb.pxd b/src/sage/rings/real_arb.pxd index 7f39f9c4efd..34d2407cde9 100644 --- a/src/sage/rings/real_arb.pxd +++ b/src/sage/rings/real_arb.pxd @@ -1,3 +1,5 @@ +cimport sage.rings.number_field.number_field_element_quadratic as nfeq + from sage.libs.arb.arb cimport arb_t from sage.libs.mpfi cimport mpfi_t from sage.rings.real_mpfi cimport RealIntervalField_class, RealIntervalFieldElement @@ -6,6 +8,7 @@ from sage.structure.element cimport RingElement cdef void mpfi_to_arb(arb_t target, const mpfi_t source, const long precision) cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except -1 +cdef int real_part_of_quadratic_element_to_arb(arb_t res, nfeq.NumberFieldElement_quadratic x, const long prec) except -1 cdef class RealBall(RingElement): cdef arb_t value diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index ffe10d5989e..f97557eab33 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -160,11 +160,21 @@ values and should be preferred:: TESTS:: sage: (RBF(pi) * identity_matrix(QQ, 3)).parent() - Full MatrixSpace of 3 by 3 dense matrices over Real ball field with 53 bits precision + Full MatrixSpace of 3 by 3 dense matrices over Real ball field + with 53 bits precision sage: polygen(RBF, x)^3 x^3 +:: + + sage: SR.coerce(RBF(0.42)) + [0.4200000000000000 +/- 1.56e-17] + sage: RBF(0.42) + SR(1) + [1.420000000000000 +/- 2.94e-16] + sage: _.parent() + Symbolic Ring + Classes and Methods =================== """ @@ -179,7 +189,6 @@ Classes and Methods include "cysignals/signals.pxi" - from cpython.float cimport PyFloat_AS_DOUBLE from cpython.int cimport PyInt_AS_LONG from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE @@ -198,7 +207,7 @@ from sage.libs.flint.fmpz cimport ( fmpz_t, fmpz_init, fmpz_get_mpz, fmpz_set_mpz, fmpz_clear, fmpz_fdiv_ui ) from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear -from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si +from sage.libs.gmp.mpz cimport mpz_fits_ulong_p, mpz_fits_slong_p, mpz_get_ui, mpz_get_si, mpz_sgn from sage.libs.mpfi cimport mpfi_get_left, mpfi_get_right, mpfi_interv_fr from sage.libs.mpfr cimport mpfr_t, mpfr_init2, mpfr_clear, mpfr_sgn, MPFR_PREC_MIN, mpfr_equal_p from sage.libs.mpfr cimport MPFR_RNDN, MPFR_RNDU, MPFR_RNDD, MPFR_RNDZ @@ -212,6 +221,8 @@ from sage.structure.element cimport Element, ModuleElement, RingElement import operator import sage.categories.fields +import sage.rings.number_field.number_field as number_field + from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_mpfi import RealIntervalField, RealIntervalField_class @@ -294,6 +305,56 @@ cdef int arb_to_mpfi(mpfi_t target, arb_t source, const long precision) except - mpfr_clear(left) mpfr_clear(right) +cdef int real_part_of_quadratic_element_to_arb(arb_t res, + nfeq.NumberFieldElement_quadratic x, const long prec) except -1: + r""" + Convert the real part of a quadratic element to an arb object of type + ``arb_t``. + + This function does *not* check that the parent has a real or complex + embedding. + + TESTS:: + + sage: NF. = QuadraticField(2) + sage: a = (sqrt2 - 1)^1000 + sage: RBF(a) + [1.676156872756536e-383 +/- 4.39e-399] + + sage: NF. = QuadraticField(-2) + sage: CBF(1/3 + a).real() + [0.3333333333333333 +/- 7.04e-17] + """ + cdef fmpz_t tmpz + cdef arb_t rootD + cdef long myprec = prec + 6 + fmpz_init(tmpz) + arb_init(rootD) + while True: # a-b√D might cancel + fmpz_set_mpz(tmpz, x.a) + arb_set_fmpz(res, tmpz) + if mpz_sgn(x.D.value) > 0: + if _do_sig(myprec): sig_on() + fmpz_set_mpz(tmpz, x.D.value) + arb_sqrt_fmpz(rootD, tmpz, myprec) + fmpz_set_mpz(tmpz, x.b) + if x.standard_embedding: + arb_addmul_fmpz(res, rootD, tmpz, myprec) + else: + arb_submul_fmpz(res, rootD, tmpz, myprec) + if _do_sig(myprec): sig_off() + if arb_rel_accuracy_bits(res) < prec - 4: + myprec *= 2 + continue + break + if _do_sig(myprec): sig_on() + fmpz_set_mpz(tmpz, x.denom) + arb_div_fmpz(res, res, tmpz, prec) + arb_clear(rootD) + fmpz_clear(tmpz) + if _do_sig(myprec): sig_off() + return 0 + class RealBallField(UniqueRepresentation, Field): r""" An approximation of the field of real numbers using mid-rad intervals, also @@ -425,11 +486,19 @@ class RealBallField(UniqueRepresentation, Field): False sage: RealBallField().has_coerce_map_from(RR) False + sage: RBF.has_coerce_map_from(QuadraticField(2)) + True + sage: RBF.has_coerce_map_from(QuadraticField(2, embedding=None)) + False + sage: RBF.has_coerce_map_from(QuadraticField(-2)) + False """ if isinstance(other, RealBallField): return (other._prec >= self._prec) - else: - return False + elif isinstance(other, number_field.NumberField_quadratic): + emb = other.coerce_embedding() + if emb is not None: + return self.has_coerce_map_from(emb.codomain()) def _element_constructor_(self, mid=None, rad=None): """ @@ -479,7 +548,7 @@ class RealBallField(UniqueRepresentation, Field): return self.element_class(self, mid, rad) except TypeError: pass - raise TypeError("unable to convert {} to a RealBall".format(mid)) + raise TypeError("unable to convert {!r} to a RealBall".format(mid)) def _repr_option(self, key): """ @@ -1062,6 +1131,12 @@ cdef class RealBall(RingElement): sage: RBF(pi, 0.125r) [3e+0 +/- 0.267] + :: + + sage: NF. = QuadraticField(2) + sage: RBF(1/5 + sqrt2/2) + [0.907106781186547 +/- 5.33e-16] + Note that integers and floating-point numbers are ''not'' rounded to the parent's precision:: @@ -1090,6 +1165,28 @@ cdef class RealBall(RingElement): sage: RBF(NaN) nan + Strings can be given as input. Strings must contain decimal + floating-point literals. A valid string must consist of a midpoint, + a midpoint and a radius separated by "+/-", or just a + radius prefixed by "+/-". Optionally, the whole string can be enclosed + in square brackets. In general, the string representation of a + real ball as returned by ``str()`` can be parsed back (the result + will be larger than the original ball if rounding occurs). + A few examples:: + + sage: RBF("1.1") + [1.100000000000000 +/- 3.56e-16] + sage: RBF(str(RBF("1.1"))) + [1.100000000000000 +/- 7.12e-16] + sage: RBF("3.25") + 3.250000000000000 + sage: RBF("-3.1 +/- 1e-10") + [-3.100000000 +/- 1.01e-10] + sage: RBF("[+/-1]") + [+/- 1.01] + sage: RBF("inf +/- inf") + [+/- inf] + .. SEEALSO:: :meth:`RealBallField._element_constructor_` TESTS:: @@ -1109,12 +1206,48 @@ cdef class RealBall(RingElement): [2.718281828459045 +/- 5.35e-16] sage: RealBall(RBF, sage.symbolic.constants.EulerGamma()) [0.577215664901533 +/- 3.57e-16] + sage: RBF("1 +/- 0.001") + [1.00 +/- 1.01e-3] + sage: RBF("2.3e10000000000000000000000 +/- 0.00005e10000000000000000000000") + [2.3000e+10000000000000000000000 +/- 5.01e+9999999999999999999995] + sage: RBF("0.3 +/- 0.2 +/- 0.1") + Traceback (most recent call last): + ... + ValueError: unsupported string format + + sage: NF. = QuadraticField(2) + sage: RBF.coerce(a) + [1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=-1.4) + sage: RBF(a) + [-1.414213562373095 +/- 3.03e-16] + sage: NF. = QuadraticField(2, embedding=None) + sage: RBF(a) + Traceback (most recent call last): + ... + ValueError: need an embedding + sage: RBF.coerce(a) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... + sage: QQi. = QuadraticField(-1) + sage: RBF(QQi(3)) + 3.000000000000000 + sage: RBF(i) + Traceback (most recent call last): + ... + ValueError: nonzero imaginary part + sage: RBF.coerce(QQi(3)) + Traceback (most recent call last): + ... + TypeError: no canonical coercion... """ import sage.symbolic.constants cdef fmpz_t tmpz cdef fmpq_t tmpq cdef arf_t tmpr cdef mag_t tmpm + cdef nfeq.NumberFieldElement_quadratic mid_as_qe Element.__init__(self, parent) @@ -1146,6 +1279,14 @@ cdef class RealBall(RingElement): arb_set_arf(self.value, tmpr) # no rounding! arf_clear(tmpr) if _do_sig(prec(self)): sig_off() + elif isinstance(mid, nfeq.NumberFieldElement_quadratic): + mid_as_qe = mid + if mpz_sgn(mid_as_qe.b) != 0: + if mpz_sgn(mid_as_qe.D.value) < 0: + raise ValueError("nonzero imaginary part") + elif mid_as_qe._parent._embedding is None: + raise ValueError("need an embedding") + real_part_of_quadratic_element_to_arb(self.value, mid_as_qe, prec(self)) elif isinstance(mid, sage.rings.infinity.AnInfinity): if isinstance(mid, sage.rings.infinity.PlusInfinity): arb_pos_inf(self.value) @@ -1182,6 +1323,9 @@ cdef class RealBall(RingElement): mpfi_to_arb(self.value, ( mid).value, prec(self)) + elif isinstance(mid, str): + if arb_set_str(self.value, mid, prec(self)) != 0: + raise ValueError("unsupported string format") else: raise TypeError("unsupported midpoint type") diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 39160ae8a28..9a5a09b3d4f 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -1034,7 +1034,7 @@ cdef class LazyWrapper(LazyFieldElement): if mor is not None and self.parent() not in mor.domains(): return mor(self._value) else: - raise TypeError("unable to convert {} to an element of {}".format(self._value, R)) + raise TypeError("unable to convert {!r} to an element of {}".format(self._value, R)) def __reduce__(self): """ diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 75875491056..f5d4083b72c 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -1922,9 +1922,9 @@ cdef class RealIntervalFieldElement(RingElement): sig_off() if lower_s == 0: - raise RuntimeError, "Unable to convert interval lower bound to a string" + raise RuntimeError("unable to convert interval lower bound to a string") if upper_s == 0: - raise RuntimeError, "Unable to convert interval upper bound to a string" + raise RuntimeError("unable to convert interval upper bound to a string") # MPFR returns an exponent assuming that the implicit radix point # is to the left of the first mantissa digit. We'll be doing diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 7dfaca29aeb..0f6f0b7eabe 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -117,7 +117,6 @@ import re include "cysignals/signals.pxi" include "sage/ext/stdsage.pxi" -include 'sage/libs/pari/pari_err.pxi' from sage.libs.gmp.mpz cimport * from sage.misc.randstate cimport randstate, current_randstate @@ -614,7 +613,7 @@ cdef class RealField_class(sage.rings.ring.Field): sage: R('2', base=2) Traceback (most recent call last): ... - TypeError: Unable to convert x (='2') to real number. + TypeError: unable to convert '2' to a real number sage: a = R('1.1001', base=2); a 1.5625 sage: a.str(2) @@ -1454,7 +1453,7 @@ cdef class RealNumber(sage.structure.element.RingElement): elif s_lower == '-infinity': mpfr_set_inf(self.value, -1) else: - raise TypeError("Unable to convert x (='%s') to real number."%s) + raise TypeError("unable to convert {!r} to a real number".format(s)) cdef _set_from_GEN_REAL(self, GEN g): """ @@ -1914,7 +1913,7 @@ cdef class RealNumber(sage.structure.element.RingElement): self.value, (self._parent).rnd) sig_off() if s == 0: - raise RuntimeError, "Unable to convert an mpfr number to a string." + raise RuntimeError("unable to convert an mpfr number to a string") t = str(s) mpfr_free_str(s) @@ -1993,7 +1992,7 @@ cdef class RealNumber(sage.structure.element.RingElement): r = mpfr_asprintf(&s, "%Ra", self.value) sig_off() if r < 0: # MPFR free()s its buffer itself in this case - raise RuntimeError("Unable to convert an mpfr number to a string.") + raise RuntimeError("unable to convert an mpfr number to a string") t = str(s) mpfr_free_str(s) return t @@ -3105,7 +3104,7 @@ cdef class RealNumber(sage.structure.element.RingElement): # by using internal interfaces of MPFR, which are documented # as subject-to-change. - pari_catch_sig_on() + sig_on() if mpfr_nan_p(self.value) or mpfr_inf_p(self.value): raise ValueError, 'Cannot convert NaN or infinity to Pari float' @@ -3804,8 +3803,17 @@ cdef class RealNumber(sage.structure.element.RingElement): False sage: RR('inf').__nonzero__() True + + TESTS: + + Check that :trac:`20502` is fixed:: + + sage: RR('nan').__nonzero__() + True + sage: RR('nan').is_zero() + False """ - return mpfr_sgn(self.value) != 0 + return not mpfr_zero_p(self.value) cpdef int _cmp_(left, Element right) except -2: """ @@ -5466,7 +5474,7 @@ def create_RealNumber(s, int base=10, int pad=0, rnd="RNDN", int min_prec=53): sage: RealNumber("deadbeefxxx", base=16) Traceback (most recent call last): ... - TypeError: Unable to convert x (='deadbeefxxx') to real number. + TypeError: unable to convert 'deadbeefxxx' to a real number sage: RealNumber("z", base=36) 35.0000000000000 sage: RealNumber("AAA", base=37) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 2db6cbd0194..fcd44d16e2a 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -2291,7 +2291,7 @@ cdef class Field(PrincipalIdealDomain): REFERENCES: - .. [Cohen1996] H. Cohen, A Course in Computational Algebraic + .. [Cohen1996] \H. Cohen, A Course in Computational Algebraic Number Theory. Graduate Texts in Mathematics 138. Springer-Verlag, 1996. diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 14c33abed0e..14795f25ec0 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -23,7 +23,7 @@ REFERENCES: -.. [Bre97] T. Breuer "Integral bases for subfields of cyclotomic fields" AAECC 8, 279--289 (1997). +.. [Bre97] \T. Breuer "Integral bases for subfields of cyclotomic fields" AAECC 8, 279--289 (1997). EXAMPLES:: diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index 6f33c08c6a2..a2c11b3ef66 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -339,7 +339,6 @@ from sage.matrix.constructor import matrix, identity_matrix from sage.misc.all import prod, det, forall, tmp_filename, random, randint, exists, denominator from sage.arith.srange import xsrange -from sage.misc.sagedoc import detex from sage.misc.superseded import deprecation from sage.modules.free_module_element import vector from sage.plot.colors import rainbow @@ -452,6 +451,7 @@ def help(verbose=True): # occurrence of a period or question mark. If neither of these appear # in the string, take the sentence to be the empty string. If the # latter occurs, something should be changed. + from sage.misc.sagedoc import detex methods = [] for i in sorted(Sandpile.__dict__.keys()): if i[0]!='_': @@ -607,7 +607,7 @@ def __init__(self, g, sink=None): # create digraph and initialize some variables DiGraph.__init__(self,g,weighted=True) self._dict = deepcopy(g) - if sink==None: + if sink is None: sink = self.vertices()[0] self._sink = sink # key for sink self._sink_ind = self.vertices().index(sink) @@ -2136,7 +2136,7 @@ def stable_configs(self, smax=None): sage: s = sandpiles.Complete(3) sage: a = s.stable_configs() - sage: a.next() + sage: next(a) {1: 0, 2: 0} sage: [i.values() for i in a] [[0, 1], [1, 0], [1, 1]] @@ -2144,7 +2144,7 @@ def stable_configs(self, smax=None): sage: list(b) [{1: 0, 2: 0}, {1: 1, 2: 0}] """ - if smax==None: + if smax is None: smax = self.max_stable().values() else: c = SandpileConfig(self,smax) @@ -2174,42 +2174,42 @@ def markov_chain(self,state, distrib=None): sage: s = sandpiles.Complete(4) sage: m = s.markov_chain([0,0,0]) - sage: m.next() # random + sage: next(m) # random {1: 0, 2: 0, 3: 0} - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 1, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [0, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [2, 2, 1] sage: m = s.markov_chain(s.zero_div(), [0.1,0.1,0.1,0.7]) - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 0, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 1] - sage: m.next().values() # random + sage: next(m).values() # random [0, 0, 1, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 0] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 1] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 2] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 3] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 2, 4] - sage: m.next().values() # random + sage: next(m).values() # random [1, 1, 3, 4] .. NOTE:: @@ -2244,7 +2244,7 @@ def markov_chain(self,state, distrib=None): st = SandpileDivisor(self,st) else: raise SyntaxError(state) - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) if isinstance(st,SandpileConfig): @@ -2932,6 +2932,7 @@ def help(verbose=True): # occurrence of a period or question mark. If neither of these appear # in the string, take the sentence to be the empty string. If the # latter occurs, something should be changed. + from sage.misc.sagedoc import detex methods = [] for i in sorted(SandpileConfig.__dict__.keys()): if i[0]!='_': @@ -3822,7 +3823,7 @@ def add_random(self, distrib=None): c = deepcopy(self) ind = self._sandpile._sink_ind n = self._sandpile.num_verts() - if distrib==None: # default = uniform distribution on nonsink vertices + if distrib is None: # default = uniform distribution on nonsink vertices distrib = [1/(n-1)]*(n-1) if len(distrib)==n-1: # prob. dist. on nonsink vertices X = GeneralDiscreteDistribution(distrib) @@ -4285,6 +4286,7 @@ def help(verbose=True): # occurrence of a period or question mark. If neither of these appear # in the string, take the sentence to be the empty string. If the # latter occurs, something should be changed. + from sage.misc.sagedoc import detex methods = [] for i in sorted(SandpileDivisor.__dict__.keys()): if i[0]!='_': @@ -5148,7 +5150,7 @@ def simulate_threshold(self, distrib=None): S = E.sandpile() V = S.vertices() n = S.num_verts() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) while not E.is_alive(): @@ -6032,7 +6034,7 @@ def add_random(self, distrib=None): D = deepcopy(self) S = self.sandpile() V = S.vertices() - if distrib==None: # default = uniform distribution + if distrib is None: # default = uniform distribution n = S.num_verts() distrib = [1/n]*n X = GeneralDiscreteDistribution(distrib) diff --git a/src/sage/sat/solvers/cryptominisat/cryptominisat.pyx b/src/sage/sat/solvers/cryptominisat/cryptominisat.pyx index b0848ae34e4..2f048870706 100644 --- a/src/sage/sat/solvers/cryptominisat/cryptominisat.pyx +++ b/src/sage/sat/solvers/cryptominisat/cryptominisat.pyx @@ -28,7 +28,7 @@ AUTHORS: ############################################################################## include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from libc.stdint cimport uint32_t from decl cimport lbool, Var, Lit, Clause, l_Undef, l_False, RetClause @@ -413,7 +413,7 @@ cdef class CryptoMiniSat(SatSolver): r = [] for i in range(num): r.append( (-1)**int(learnt1[i]&1) * (int(learnt1[i]>>1)+1) ) - sage_free(learnt1) + sig_free(learnt1) if unitary_only: return tuple(r) @@ -425,9 +425,9 @@ cdef class CryptoMiniSat(SatSolver): for i in range(num): clause = learnt[i] C = [(-1)**int(clause[j]&1) * (int(clause[j]>>1)+1) for j in range(1,clause[0]+1)] - sage_free(clause) + sig_free(clause) r.append(tuple(C)) - sage_free(learnt) + sig_free(learnt) return tuple(r) def clauses(self, filename=None): diff --git a/src/sage/sat/solvers/cryptominisat/cryptominisat_helper.h b/src/sage/sat/solvers/cryptominisat/cryptominisat_helper.h index b807800060f..7c382d8fe50 100644 --- a/src/sage/sat/solvers/cryptominisat/cryptominisat_helper.h +++ b/src/sage/sat/solvers/cryptominisat/cryptominisat_helper.h @@ -3,15 +3,15 @@ * this rather ugly workaround to call get_sorted_learnts */ -static CYTHON_INLINE void *sage_malloc(size_t); +static CYTHON_INLINE void *sig_malloc(size_t); uint32_t ** get_sorted_learnts_helper(CMSat::Solver* solver, uint32_t *num) { const CMSat::vec& learnt = solver->get_sorted_learnts(); *num = learnt.size(); - uint32_t **ret = (uint32_t**)sage_malloc(sizeof(uint32_t*)* learnt.size()); + uint32_t **ret = (uint32_t**)sig_malloc(sizeof(uint32_t*)* learnt.size()); for(size_t i=0; isize()+1)); + ret[i] = (uint32_t*)sig_malloc(sizeof(uint32_t)*(clause->size()+1)); ret[i][0] = clause->size(); for(size_t j=0; jsize(); j++) { ret[i][j+1] = (*clause)[j].toInt(); @@ -24,7 +24,7 @@ uint32_t ** get_sorted_learnts_helper(CMSat::Solver* solver, uint32_t *num) { uint32_t * get_unitary_learnts_helper(CMSat::Solver* solver, uint32_t *num) { const CMSat::vec learnt = solver->get_unitary_learnts(); *num = learnt.size(); - uint32_t *ret = (uint32_t*)sage_malloc(sizeof(uint32_t) * learnt.size()); + uint32_t *ret = (uint32_t*)sig_malloc(sizeof(uint32_t) * learnt.size()); for(size_t i=0; i sage_malloc(5 * sizeof(mpz_t)) + cdef mpz_t *coeffs = sig_malloc(5 * sizeof(mpz_t)) for i from 0 <= i <= 4: mpz_init(coeffs[i]) mpz_set_ui(coeffs[1], ui0) # @@ -969,14 +969,14 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len if not selmer_only: # allocate space for ratpoints - coeffs_ratp = sage_malloc(5 * sizeof(mpz_t)) + coeffs_ratp = sig_malloc(5 * sizeof(mpz_t)) for i from 0 <= i <= 4: mpz_init(coeffs_ratp[i]) # Get prime divisors, and put them in an mpz_t array # (this block, by setting check_negs, takes care of # local solubility over RR) - cdef mpz_t *p_div_d_mpz = sage_malloc((p_list_len+1) * sizeof(mpz_t)) + cdef mpz_t *p_div_d_mpz = sig_malloc((p_list_len+1) * sizeof(mpz_t)) n_primes = 0 for i from 0 <= i < p_list_len: if mpz_divisible_p(d_mpz, p_list[i]): @@ -1056,15 +1056,15 @@ cdef int count(mpz_t c_mpz, mpz_t d_mpz, mpz_t *p_list, unsigned long p_list_len if not selmer_only: for i from 0 <= i <= 4: mpz_clear(coeffs_ratp[i]) - sage_free(coeffs_ratp) + sig_free(coeffs_ratp) mpz_clear(j) for i from 0 <= i < n_primes: mpz_clear(p_div_d_mpz[i]) - sage_free(p_div_d_mpz) + sig_free(p_div_d_mpz) mpz_clear(n_divisors) for i from 0 <= i <= 4: mpz_clear(coeffs[i]) - sage_free(coeffs) + sig_free(coeffs) return 0 def two_descent_by_two_isogeny(E, @@ -1220,7 +1220,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, mpz_neg(d_prime_mpz, d_prime_mpz) if mpz_fits_ulong_p(d_mpz) and mpz_fits_ulong_p(d_prime_mpz): # Factor very quickly using FLINT. - p_list_mpz = sage_malloc(20 * sizeof(mpz_t)) + p_list_mpz = sig_malloc(20 * sizeof(mpz_t)) mpz_init_set_ui(p_list_mpz[0], ui2) p_list_len = 1 n_factor_init(&fact) @@ -1255,7 +1255,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, P = Integer(2) if P not in primes: primes.append(P) p_list_len = len(primes) - p_list_mpz = sage_malloc(p_list_len * sizeof(mpz_t)) + p_list_mpz = sig_malloc(p_list_len * sizeof(mpz_t)) for i from 0 <= i < p_list_len: P = Integer(primes[i]) mpz_init_set(p_list_mpz[i], P.value) @@ -1282,7 +1282,7 @@ def two_descent_by_two_isogeny_work(Integer c, Integer d, for i from 0 <= i < p_list_len: mpz_clear(p_list_mpz[i]) - sage_free(p_list_mpz) + sig_free(p_list_mpz) if verbosity > 0: print "\nResults:" diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index 5dccd9e6bb0..99b14d6951c 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -845,12 +845,12 @@ class EllipticCurveIsogeny(Morphism): sage: isogs[0] Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + (-t^2)*x over Rational function field in t over Rational Field to Elliptic Curve defined by y^2 = x^3 + 4*t^2*x over Rational function field in t over Rational Field sage: isogs[0].rational_maps() - ((x^2 - t^2)/x, (x^3*y + t^2*x*y)/x^3) + ((x^2 - t^2)/x, (x^2*y + t^2*y)/x^2) sage: duals = [phi.dual() for phi in isogs] sage: duals[0] Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 4*t^2*x over Rational function field in t over Rational Field to Elliptic Curve defined by y^2 = x^3 + (-t^2)*x over Rational function field in t over Rational Field sage: duals[0].rational_maps() - ((1/4*x^2 + t^2)/x, (1/8*x^3*y + (-1/2*t^2)*x*y)/x^3) + ((1/4*x^2 + t^2)/x, (1/8*x^2*y + (-1/2*t^2)*y)/x^2) sage: duals[0] Isogeny of degree 2 from Elliptic Curve defined by y^2 = x^3 + 4*t^2*x over Rational function field in t over Rational Field to Elliptic Curve defined by y^2 = x^3 + (-t^2)*x over Rational function field in t over Rational Field """ @@ -2564,7 +2564,7 @@ def __compute_omega_general(self, E, psi, psi_pr, phi, phi_pr): sage: phi._EllipticCurveIsogeny__compute_omega_general(E, psi, psi_pr, fi, fi_pr) x^3*y + (alpha^2 + 1)*x^2*y + (alpha^2 + alpha + 1)*x^2 + (alpha^2 + 1)*x*y + (alpha^2 + alpha)*x + (alpha)*y + (alpha) - A bug fixed in ticket #7907:: + A bug fixed in :trac:`7907`:: sage: F = GF(128,'a') sage: a = F.gen() @@ -3358,7 +3358,7 @@ def dual(self): sage: (Xm, Ym) == E.multiplication_by_m(5) True - Test (for trac ticket 7096):: + Test (for :trac:`7096`):: sage: E = EllipticCurve('11a1') sage: phi = E.isogeny(E(5,5)) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index b3fa3f78755..90091555635 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2130,7 +2130,7 @@ def multiplication_by_m_isogeny(self, m): NOTE: This function is currently *much* slower than the result of ``self.multiplication_by_m()``, because constructing an isogeny precomputes a significant amount - of information. See trac tickets #7368 and #8014 for the + of information. See :trac:`7368` and :trac:`8014` for the status of improving this situation. INPUT: @@ -2411,7 +2411,7 @@ def short_weierstrass_model(self, complete_cube=True): sage: E.short_weierstrass_model(complete_cube=False) Elliptic Curve defined by y^2 = x^3 + x + 2 over Finite Field of size 3 - This used to be different see trac #3973:: + This used to be different see :trac:`3973`:: sage: E.short_weierstrass_model() Elliptic Curve defined by y^2 = x^3 + x + 2 over Finite Field of size 3 diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index a2c85e94832..d1ac3d4ef91 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -254,10 +254,10 @@ def _find_scaling_L_ratio(self): sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) - ... E = EllipticCurve(la) - ... me = E.modular_symbol(use_eclib = True) - ... ms = E.modular_symbol(use_eclib = False) - ... print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) + ....: E = EllipticCurve(la) + ....: me = E.modular_symbol(use_eclib = True) + ....: ms = E.modular_symbol(use_eclib = False) + ....: print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 @@ -268,14 +268,14 @@ def _find_scaling_L_ratio(self): sage: [EllipticCurve(la).modular_symbol(use_eclib=True)(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) - ... E = EllipticCurve(la) - ... m = E.modular_symbol(use_eclib = True) - ... lp = E.padic_lseries(5) - ... for D in [5,17,12,8]: - ... ED = E.quadratic_twist(D) - ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) - ... etaa = lp._quotient_of_periods_to_twist(D) - ... assert ED.lseries().L_ratio()*ED.real_components()*etaa == md + ....: E = EllipticCurve(la) + ....: m = E.modular_symbol(use_eclib = True) + ....: lp = E.padic_lseries(5) + ....: for D in [5,17,12,8]: + ....: ED = E.quadratic_twist(D) + ....: md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) + ....: etaD = lp._quotient_of_periods_to_twist(D) + ....: assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E @@ -361,13 +361,15 @@ def __lalg__(self,D): E = self._E ED = E.quadratic_twist(D) lv = ED.lseries().L_ratio() # this is L(ED,1) divided by the Neron period omD of ED - lv *= ED.real_components() + lv *= ED.real_components() # now it is by the least positive period omD = ED.period_lattice().basis()[0] if D > 0 : om = E.period_lattice().basis()[0] q = sqrt(D)*omD/om * 8 else : om = E.period_lattice().basis()[1].imag() + if E.real_components() == 1: + om *= 2 q = sqrt(-D)*omD/om*8 # see padic_lseries.pAdicLeries._quotient_of_periods_to_twist @@ -409,8 +411,15 @@ def __scale_by_periods_only__(self): print "Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2." cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] - q = QQ(int(round(q*200)))/200 + if self._sign == 1: + q = E0.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[1].imag() + if E0.real_components() == 1: + q *= 2 + if self._E.real_components() == 1: + q /= 2 + q = ZZ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q @@ -589,7 +598,7 @@ def __init__(self, E, sign, normalize="L_ratio"): 1 sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1) sage: M(1/3) - 1 + 1/2 This is a rank 1 case with vanishing positive twists. The modular symbol is adjusted by -2:: @@ -597,9 +606,9 @@ def __init__(self, E, sign, normalize="L_ratio"): sage: E=EllipticCurve('121b1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1,normalize='L_ratio') sage: M(1/3) - 2 + 1 sage: M._scaling - -2 + -1 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=False) sage: M(0) @@ -679,7 +688,14 @@ def _find_scaling_period(self): else : cr0 = Integer(crla[0]).str() + crla[1] + '1' E0 = EllipticCurve(cr0) - q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + if self._sign == 1: + q = E0.period_lattice().basis()[0]/E.period_lattice().basis()[0] + else: + q = E0.period_lattice().basis()[1].imag()/E.period_lattice().basis()[1].imag() + if E0.real_components() == 1: + q *= 2 + if E.real_components() == 1: + q /= 2 q = QQ(int(round(q*200)))/200 verbose('scale modular symbols by %s'%q) self._scaling = q diff --git a/src/sage/schemes/elliptic_curves/ell_rational_field.py b/src/sage/schemes/elliptic_curves/ell_rational_field.py index 47cf1c21383..1986ca214a5 100644 --- a/src/sage/schemes/elliptic_curves/ell_rational_field.py +++ b/src/sage/schemes/elliptic_curves/ell_rational_field.py @@ -1115,6 +1115,8 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): of ``lseries()``, where the value is also divided by the number of connected components of `E(\RR)`). In particular the modular symbol depends on `E` and not only the isogeny class of `E`. + For the negative part the corresponding period is purely imaginary of + smallest positive imaginary part. INPUT: @@ -1209,6 +1211,9 @@ def modular_symbol(self, sign=1, use_eclib = False, normalize = "L_ratio"): 1 sage: E.modular_symbol(use_eclib=False, normalize='period')(0) 1/25 + sage: E.modular_symbol(sign=-1, use_eclib=False, normalize='L_ratio')(1/3) + 1/2 + """ typ = (sign, normalize, use_eclib) @@ -1664,7 +1669,7 @@ def analytic_rank_upper_bound(self, REFERENCES: - .. [Bob13] J.W. Bober. Conditionally bounding analytic ranks of elliptic curves. + .. [Bob13] \J.W. Bober. Conditionally bounding analytic ranks of elliptic curves. ANTS 10. http://msp.org/obs/2013/1-1/obs-v1-n1-p07-s.pdf """ @@ -3350,7 +3355,7 @@ def lseries_gross_zagier(self, A): REFERENCES: - .. [GrossZagier] B. Gross and D. Zagier, *Heegner points and + .. [GrossZagier] \B. Gross and D. Zagier, *Heegner points and derivatives of L-series.* Invent. Math. 84 (1986), no. 2, 225-320. """ try: @@ -4015,23 +4020,10 @@ def _torsion_bound(self,number_of_places = 20): k += 1 return bound - - def torsion_subgroup(self, algorithm="pari"): + def torsion_subgroup(self, algorithm=None): """ Returns the torsion subgroup of this elliptic curve. - INPUT: - - - - ``algorithm`` - string: - - - ``"pari"`` - (default) use the PARI library - - - ``"doud"`` - use Doud's algorithm - - - ``"lutz_nagell"`` - use the Lutz-Nagell theorem - - OUTPUT: The EllipticCurveTorsionSubgroup instance associated to this elliptic curve. @@ -4064,27 +4056,17 @@ def torsion_subgroup(self, algorithm="pari"): try: return self.__torsion_subgroup except AttributeError: + # algorithm is deprecated: if not None, this will give a warning. + # deprecation(20219) self.__torsion_subgroup = ell_torsion.EllipticCurveTorsionSubgroup(self, algorithm) self.__torsion_order = self.__torsion_subgroup.order() return self.__torsion_subgroup - def torsion_points(self, algorithm="pari"): + def torsion_points(self, algorithm=None): """ Returns the torsion points of this elliptic curve as a sorted list. - INPUT: - - - - ``algorithm`` - string: - - - "pari" - (default) use the PARI library - - - "doud" - use Doud's algorithm - - - "lutz_nagell" - use the Lutz-Nagell theorem - - OUTPUT: A list of all the torsion points on this elliptic curve. EXAMPLES:: @@ -4153,6 +4135,8 @@ def torsion_points(self, algorithm="pari"): (244 : -3902 : 1), (244 : 3658 : 1)] """ + # algorithm is deprecated: if not None, this will give a warning. + # deprecation(20219) return sorted(self.torsion_subgroup(algorithm).points()) @cached_method @@ -5900,7 +5884,7 @@ def integral_points(self, mw_base='auto', both_signs=False, verbose=False): REFERENCES: - .. [Co1] H. Cohen, Number Theory, Vol. I: Tools and + .. [Co1] \H. Cohen, Number Theory, Vol. I: Tools and Diophantine Equations. GTM 239, Springer, 2007. AUTHORS: diff --git a/src/sage/schemes/elliptic_curves/ell_torsion.py b/src/sage/schemes/elliptic_curves/ell_torsion.py index 6cac60424ed..24be339fb94 100644 --- a/src/sage/schemes/elliptic_curves/ell_torsion.py +++ b/src/sage/schemes/elliptic_curves/ell_torsion.py @@ -132,13 +132,6 @@ def __init__(self, E, algorithm=None): - ``E`` - An elliptic curve defined over a number field (including `\Q`) - - ``algorithm`` - (string, default None): If not None, must be one - of 'PARI', 'doud', 'lutz_nagell'. For curves - defined over `\QQ`, PARI is then used with the - appropriate flag passed to PARI's ``elltors()`` - function; this parameter is ignored for curves - whose base field is not `\QQ`. - EXAMPLES:: sage: from sage.schemes.elliptic_curves.ell_torsion import EllipticCurveTorsionSubgroup @@ -158,15 +151,15 @@ def __init__(self, E, algorithm=None): sage: T == loads(dumps(T)) # known bug, see http://trac.sagemath.org/sage_trac/ticket/11599#comment:7 True """ + if algorithm is not None: + from sage.misc.superseded import deprecation + deprecation(20219, "the keyword 'algorithm' is deprecated and no longer used") + self.__E = E self.__K = E.base_field() - pari_torsion_algorithms = ["pari","doud","lutz_nagell"] - - if self.__K is RationalField() and algorithm in pari_torsion_algorithms: - flag = pari_torsion_algorithms.index(algorithm) - - G = self.__E.pari_curve().elltors(flag) + if self.__K is RationalField(): + G = self.__E.pari_curve().elltors() order = G[0].python() structure = G[1].python() gens = G[2].python() diff --git a/src/sage/schemes/elliptic_curves/height.py b/src/sage/schemes/elliptic_curves/height.py index 32b6072630f..d6040e17e54 100644 --- a/src/sage/schemes/elliptic_curves/height.py +++ b/src/sage/schemes/elliptic_curves/height.py @@ -13,16 +13,16 @@ REFERENCES: -.. [CS] J.E.Cremona, and S. Siksek, Computing a Lower Bound for the +.. [CS] \J.E.Cremona, and S. Siksek, Computing a Lower Bound for the Canonical Height on Elliptic Curves over `\QQ`, ANTS VII Proceedings: F.Hess, S.Pauli and M.Pohst (eds.), ANTS VII, Lecture Notes in Computer Science 4076 (2006), pages 275-286. -.. [TT] T. Thongjunthug, Computing a lower bound for the canonical +.. [TT] \T. Thongjunthug, Computing a lower bound for the canonical height on elliptic curves over number fields, Math. Comp. 79 (2010), pages 2431-2449. -.. [CPS] J.E. Cremona, M. Prickett and S. Siksek, Height Difference +.. [CPS] \J.E. Cremona, M. Prickett and S. Siksek, Height Difference Bounds For Elliptic Curves over Number Fields, Journal of Number Theory 116(1) (2006), pages 42-68. diff --git a/src/sage/schemes/elliptic_curves/isogeny_class.py b/src/sage/schemes/elliptic_curves/isogeny_class.py index 7428d9d14e0..4fc051e7528 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_class.py +++ b/src/sage/schemes/elliptic_curves/isogeny_class.py @@ -402,7 +402,7 @@ def graph(self): REFERENCES: - .. [M78] B. Mazur. Rational Isogenies of Prime Degree. + .. [M78] \B. Mazur. Rational Isogenies of Prime Degree. *Inventiones mathematicae* 44,129-162 (1978). """ from sage.graphs.graph import Graph diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index e3723185996..63e6ef7a8ff 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -22,8 +22,8 @@ REFERENCES: -.. [CW2005] J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. -.. [KT2013] K. Tsukazaki, Explicit Isogenies of Elliptic Curves, +.. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. +.. [KT2013] \K. Tsukazaki, Explicit Isogenies of Elliptic Curves, PhD thesis, University of Warwick, 2013. diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index ec26a0b5f7a..501169cf210 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -125,6 +125,7 @@ class pAdicLseries(SageObject): 2 + 4*5 + 4*5^2 + O(5^4) + O(5)*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + O(5)*T^4 + O(T^5) + A prime p such that E[p] is reducible:: sage: L = EllipticCurve('11a').padic_lseries(5) @@ -140,11 +141,11 @@ class pAdicLseries(SageObject): sage: E=EllipticCurve('11a1') sage: lp=E.padic_lseries(7) sage: lp.series(4,eta=1) - 6 + 2*7^3 + 5*7^4 + O(7^6) + (4*7 + 2*7^2 + O(7^3))*T + (2 + 3*7^2 + O(7^3))*T^2 + (1 + 2*7 + 2*7^2 + O(7^3))*T^3 + (1 + 3*7^2 + O(7^3))*T^4 + O(T^5) + 3 + 7^3 + 6*7^4 + 3*7^5 + O(7^6) + (2*7 + 7^2 + O(7^3))*T + (1 + 5*7^2 + O(7^3))*T^2 + (4 + 4*7 + 4*7^2 + O(7^3))*T^3 + (4 + 3*7 + 7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=2) 5 + 6*7 + 4*7^2 + 2*7^3 + 3*7^4 + 2*7^5 + O(7^6) + (6 + 4*7 + 7^2 + O(7^3))*T + (3 + 2*7^2 + O(7^3))*T^2 + (1 + 4*7 + 7^2 + O(7^3))*T^3 + (6 + 6*7 + 6*7^2 + O(7^3))*T^4 + O(T^5) sage: lp.series(4,eta=3) - O(7^6) + (3 + 2*7 + 5*7^2 + O(7^3))*T + (5 + 4*7 + 5*7^2 + O(7^3))*T^2 + (3*7 + 7^2 + O(7^3))*T^3 + (2*7 + 7^2 + O(7^3))*T^4 + O(T^5) + O(7^6) + (5 + 4*7 + 2*7^2 + O(7^3))*T + (6 + 5*7 + 2*7^2 + O(7^3))*T^2 + (5*7 + O(7^3))*T^3 + (7 + 4*7^2 + O(7^3))*T^4 + O(T^5) (Note that the last series vanishes at `T = 0`, which is consistent with :: @@ -208,15 +209,13 @@ def __add_negative_space(self): sage: E = EllipticCurve('11a1') sage: lp = E.padic_lseries(5) sage: lp.modular_symbol(1/7,sign=-1) #indirect doctest - -1 + -1/2 """ if self._use_eclib: verbose('Currently there is no negative modular symbols in eclib, so we have to fall back on the implementation of modular symbols in sage') # once there is a eclib implementation of -1, this should be changed. - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) - else: - self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) + self._negative_modular_symbol = self._E.modular_symbol(sign=-1, use_eclib = False, normalize=self._normalize) def __cmp__(self,other): r""" @@ -297,7 +296,7 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): Note also that this function does not check if the condition on the quadratic_twist=D is satisfied. So the result will only be correct if for each prime `\ell` dividing `D`, we have - `ord_{\ell(N)}<= ord_{\ell}(D)`, where `N` is the conductor of the curve. + `ord_{\ell}(N)<= ord_{\ell}(D)`, where `N` is the conductor of the curve. INPUT: @@ -314,13 +313,15 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): sage: [lp.modular_symbol(r) for r in [0,1/5,oo,1/11]] [1/5, 6/5, 0, 0] sage: [lp.modular_symbol(r,sign=-1) for r in [0,1/3,oo,1/7]] - [0, 1, 0, -1] + [0, 1/2, 0, -1/2] sage: [lp.modular_symbol(r,quadratic_twist=-20) for r in [0,1/5,oo,1/11]] - [2, 2, 0, 1] + [1, 1, 0, 1/2] - sage: lpt = E.quadratic_twist(-3).padic_lseries(5) - sage: et = E.padic_lseries(5)._quotient_of_periods_to_twist(-3) - sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-3)/et + sage: E = EllipticCurve('20a1') + sage: Et = E.quadratic_twist(-4) + sage: lpt = Et.padic_lseries(5) + sage: eta = lpt._quotient_of_periods_to_twist(-4) + sage: lpt.modular_symbol(0) == lp.modular_symbol(0,quadratic_twist=-4) / eta True """ @@ -341,7 +342,9 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): raise NotImplementedError("Quadratic twists for negative modular symbols are not yet implemented.") if D > 0: m = self._modular_symbol - s = +1 + return sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,D) ] ) + else: try: m = self._negative_modular_symbol @@ -349,10 +352,8 @@ def modular_symbol(self, r, sign=+1, quadratic_twist= +1): if not hasattr(self, '_modular_symbol_negative'): self.__add_negative_space() m = self._negative_modular_symbol - s = -1 - # without the ZZ here the u is treated as a 'int' and dividing by D gives 0 - # this only happens when it is called from __init__ (?) - return s * sum([kronecker_symbol(D,u) * m(r+ZZ(u)/D) for u in range(1,abs(D))]) + return -sum([ kronecker_symbol(D,u) * m(r+ZZ(u)/D) \ + for u in range(1,-D) ] ) def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): @@ -411,7 +412,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): sage: E = EllipticCurve('11a1') sage: a = E.quadratic_twist(-3).padic_lseries(5).measure(1,2,prec=15) sage: b = E.padic_lseries(5).measure(1,2, quadratic_twist=-3,prec=15) - sage: a == b/E.padic_lseries(5)._quotient_of_periods_to_twist(-3) + sage: a == b * E.padic_lseries(5)._quotient_of_periods_to_twist(-3) True """ @@ -422,7 +423,7 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): raise NotImplementedError("Quadratic twists not implemented for sign -1") if quadratic_twist < 0: - s = -1 + s = ZZ(-1) try: p, alpha, z, w, f = self.__measure_data[(n,prec,s)] @@ -447,14 +448,17 @@ def measure(self, a, n, prec, quadratic_twist=+1, sign = +1): if quadratic_twist == 1: if self._E.conductor() % p == 0: return z * f(a/(p*w)) - return z * f(a/(p*w)) - (z/alpha) * f(a/w) + return z * ( f(a/(p*w)) - f(a/w) / alpha) else: D = quadratic_twist - chip = kronecker_symbol(D,p) + if self.is_ordinary(): + chip = kronecker_symbol(D,p) + else: + chip = 1 # alpha is +- sqrt(-p) anyway if self._E.conductor() % p == 0: - mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,D.abs())]) else: - mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) + mu = chip**n * z * sum([kronecker_symbol(D,u) *( f(a/(p*w)+ZZ(u)/D) - chip /alpha * f(a/w+ZZ(u)/D) ) for u in range(1,D.abs())]) return s*mu def alpha(self, prec=20): @@ -483,9 +487,9 @@ def alpha(self, prec=20): sage: L = E.padic_lseries(3) sage: alpha = L.alpha(10); alpha - (1 + O(3^10))*alpha + alpha + O(alpha^21) sage: alpha^2 - E.ap(3)*alpha + 3 - (O(3^10))*alpha^2 + (O(3^11))*alpha + (O(3^11)) + O(alpha^22) A reducible prime:: @@ -519,8 +523,9 @@ def alpha(self, prec=20): return K(a) raise RunTimeError("bug in p-adic L-function alpha") else: # supersingular case - f = f.change_ring(Qp(p, prec, print_mode='series')) - a = f.root_field('alpha', check_irreducible=False).gen() + f = f.change_ring(K) + A = K.extension(f, names="alpha") + a = A.gen() self._alpha[prec] = a return a @@ -592,13 +597,6 @@ def order_of_vanishing(self): return v n += 1 - -# def _c_bounds(self, n): -# raise NotImplementedError - -# def _prec_bounds(self, n,prec): -# raise NotImplementedError - def teichmuller(self, prec): r""" Return Teichmuller lifts to the given precision. @@ -702,10 +700,12 @@ def _set_series_in_cache(self, n, prec, D, eta, f): def _quotient_of_periods_to_twist(self,D): r""" - For a fundamental discriminant `D` of a quadratic number field this computes the constant `\eta` such that - `\sqrt{D}\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. As in [MTT]_ page 40. - This is either 1 or 2 unless the condition on the twist is not satisfied, e.g. if we are 'twisting back' - to a semi-stable curve. + For a fundamental discriminant `D` of a quadratic number field this + computes the constant `\eta` such that + `\sqrt{\vert D\vert }\cdot\Omega_{E_D}^{+} =\eta\cdot \Omega_E^{sign(D)}`. + As in [MTT]_ page 40. This is either 1 or 2 unless the condition + on the twist is not satisfied, e.g. if we are 'twisting back' to a + semi-stable curve. REFERENCES: @@ -738,7 +738,7 @@ def _quotient_of_periods_to_twist(self,D): sage: Et = E.quadratic_twist(-3) sage: lpt = Et.padic_lseries(5) sage: lpt._quotient_of_periods_to_twist(-3) - 6 + 3 """ from sage.functions.all import sqrt @@ -747,13 +747,14 @@ def _quotient_of_periods_to_twist(self,D): # Note that the number of real components does not change by twisting. if D == 1: return 1 + Et = self._E.quadratic_twist(D) if D > 1: - Et = self._E.quadratic_twist(D) qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[0] qt *= sqrt(qt.parent()(D)) else: - Et = self._E.quadratic_twist(D) - qt = Et.period_lattice().basis()[0]/self._E.period_lattice().basis()[1].imag() + qt = Et.period_lattice().basis()[1].imag()/self._E.period_lattice().basis()[0] + if Et.real_components() == 1: + qt *= 2 qt *= sqrt(qt.parent()(-D)) verbose('the real approximation is %s'%qt) # we know from MTT that the result has a denominator 1 @@ -851,9 +852,9 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): sage: L = EllipticCurve('110a1').padic_lseries(5) sage: for j in [0..3]: print L.series(4, eta=j) O(5^6) + (2 + 2*5 + 2*5^2 + O(5^3))*T + (5 + 5^2 + O(5^3))*T^2 + (4 + 4*5 + 2*5^2 + O(5^3))*T^3 + (1 + 5 + 3*5^2 + O(5^3))*T^4 + O(T^5) - 3 + 2*5 + 2*5^3 + 3*5^4 + O(5^6) + (2 + 5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + 2*5^2 + O(5^3))*T^2 + (1 + 5 + 5^2 + O(5^3))*T^3 + (2 + 4*5 + 4*5^2 + O(5^3))*T^4 + O(T^5) + 4 + 3*5 + 2*5^2 + 3*5^3 + 5^4 + O(5^6) + (1 + 3*5 + 4*5^2 + O(5^3))*T + (3 + 4*5 + 3*5^2 + O(5^3))*T^2 + (3 + 3*5^2 + O(5^3))*T^3 + (1 + 2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) 2 + O(5^6) + (1 + 5 + O(5^3))*T + (2 + 4*5 + 3*5^2 + O(5^3))*T^2 + (4 + 5 + 2*5^2 + O(5^3))*T^3 + (4 + O(5^3))*T^4 + O(T^5) - 1 + 3*5 + 4*5^2 + 2*5^3 + 5^4 + 4*5^5 + O(5^6) + (2 + 4*5 + 3*5^2 + O(5^3))*T + (2 + 3*5 + 5^2 + O(5^3))*T^2 + (1 + O(5^3))*T^3 + (2*5 + 2*5^2 + O(5^3))*T^4 + O(T^5) + 3 + 5 + 2*5^2 + 5^3 + 3*5^4 + 4*5^5 + O(5^6) + (1 + 2*5 + 4*5^2 + O(5^3))*T + (1 + 4*5 + O(5^3))*T^2 + (3 + 2*5 + 2*5^2 + O(5^3))*T^3 + (5 + 5^2 + O(5^3))*T^4 + O(T^5) """ n = ZZ(n) if n < 1: @@ -892,7 +893,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): # set prec arbitrary to 20. K = Qp(p, 20, print_mode='series') R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) chip = kronecker_symbol(D,p) if self._E.conductor() % p == 0: L *= 1 - chip/self.alpha() @@ -928,6 +929,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): gamma_power = K(1) teich = self.teichmuller(padic_prec) p_power = p**(n-1) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p_power)) verbose_level = get_verbose() @@ -939,7 +941,7 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign = 1-2*(eta % 2)).lift() + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si).lift() L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma @@ -952,7 +954,8 @@ def series(self, n=2, quadratic_twist=+1, prec=5, eta=0): L = R(L,res_series_prec) aj = L.list() if len(aj) > 0: - aj = [aj[0].add_bigoh(padic_prec-2)] + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] + aj = [aj[0].add_bigoh(padic_prec-2)] + \ + [aj[j].add_bigoh(bounds[j]) for j in range(1,len(aj))] L = R(aj,res_series_prec ) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() @@ -1066,7 +1069,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Here the normalization of the `p`-adic L-series is chosen such that `L_p(E,1) = (1-1/\alpha)^2 L(E,1)/\Omega_E` - where `\alpha` is the unit root of the characteristic + where `\alpha` is a root of the characteristic polynomial of Frobenius on `T_pE` and `\Omega_E` is the Neron period of `E`. @@ -1082,6 +1085,12 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): Teichmueller character on the group of roots of unity in `\ZZ_p^\times`) + OUTPUT: + + a power series with coefficients in a quadratic ramified extension of + the `p`-adic numbers generated by a root `alpha` of the characteristic + polynomial of Frobenius on `T_pE`. + ALIAS: power_series is identical to series. EXAMPLES: @@ -1093,18 +1102,16 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): sage: L.series(2) O(T^3) sage: L.series(4) # takes a long time (several seconds) - (O(3))*alpha + (O(3^2)) + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T + ((O(3^-1))*alpha + (2*3^-1 + O(3^0)))*T^2 + O(T^5) + O(alpha) + (alpha^-2 + O(alpha^0))*T + (alpha^-2 + O(alpha^0))*T^2 + O(T^5) sage: L.alpha(2).parent() - Univariate Quotient Polynomial Ring in alpha over 3-adic Field with capped - relative precision 2 with modulus (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) + Eisenstein Extension of 3-adic Field with capped relative precision 2 in alpha defined by (1 + O(3^2))*x^2 + (3 + O(3^3))*x + (3 + O(3^3)) An example where we only compute the leading term (:trac:`15737`):: sage: E = EllipticCurve("17a1") sage: L = E.padic_lseries(3) sage: L.series(4,prec=1) - (O(3^18))*alpha^2 + (2*3^-1 + 1 + 3 + 3^2 + 3^3 + ... + 3^18 + O(3^19))*alpha + (2*3^-1 + 1 + 3 + 3^2 + 3^3 + 3^4 + ... + 3^18 + O(3^19)) + O(T) - + alpha^-2 + alpha^-1 + 2 + 2*alpha + ... + O(alpha^38) + O(T) """ n = ZZ(n) if n < 1: @@ -1131,7 +1138,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): p = self._p eta = ZZ(eta) % (p-1) if p == 2 and self._normalize : - print 'Warning : for p == 2 the normalization might not be correct !' + print 'Warning : for p = 2 the normalization might not be correct !' if prec == 1: if eta == 0: @@ -1141,27 +1148,22 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): alpha = self.alpha(prec=20) K = alpha.parent() R = PowerSeriesRing(K,'T',1) - L = self.modular_symbol(0, sign=+1, quadratic_twist= D) - if self._E.has_nonsplit_multiplicative_reduction(p): - L *= 2 - if self._E.has_split_multiplicative_reduction(p): - L *= 0 - else: - chip = kronecker_symbol(D,p) - L *= (1-chip/self.alpha())**2 + L = self.modular_symbol(0, sign=+1, quadratic_twist=D) + L *= (1-1/self.alpha())**2 L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() L = R(L, 1) return L else: # here we need some sums anyway bounds = self._prec_bounds(n,prec) - padic_prec = 20 + alphaadic_prec = 20 else: prec = min(p**(n-1), prec) bounds = self._prec_bounds(n,prec) - padic_prec = max(sum(bounds[1:],[])) + 5 + alphaadic_prec = max(bounds[1:]) + 5 - verbose("using p-adic precision of %s"%padic_prec) + padic_prec = alphaadic_prec//2+1 + verbose("using alpha-adic precision of %s"%padic_prec) ans = self._get_series_from_cache(n, prec, quadratic_twist,eta) if not ans is None: verbose("found series in cache") @@ -1176,6 +1178,7 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): one_plus_T_factor = R(1) gamma_power = 1 teich = self.teichmuller(padic_prec) + si = 1-2*(eta % 2) verbose("Now iterating over %s summands"%((p-1)*p**(n-1))) verbose_level = get_verbose() @@ -1187,18 +1190,22 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): count_verb += 3 for a in range(1,p): b = teich[a] * gamma_power - s += teich[a]**eta * self.measure(b, n, padic_prec,quadratic_twist=D, sign=1-2*(eta % 2)) + s += teich[a]**eta * self.measure(b, n, padic_prec, quadratic_twist=D, sign=si) L += s * one_plus_T_factor one_plus_T_factor *= 1+T gamma_power *= gamma # Now create series but with each coefficient truncated # so it is proven correct: + # the coefficients are now treated as alpha-adic numbers (trac 20254) L = R(L,prec) aj = L.list() if len(aj) > 0: - bj = [aj[0][0].add_bigoh(padic_prec-2) + alpha * aj[0][1].add_bigoh(padic_prec-2)] - bj += [aj[j][0].add_bigoh(bounds[j][0]) + alpha * aj[j][1].add_bigoh(bounds[j][1]) for j in range(1,len(aj))] + bj = [aj[0].add_bigoh(2*(padic_prec-2))] + j = 1 + while j < len(aj): + bj.append( aj[j].add_bigoh(bounds[j]) ) + j += 1 L = R(bj, prec) L /= self._quotient_of_periods_to_twist(D)*self._E.real_components() self._set_series_in_cache(n, prec, quadratic_twist, eta, L) @@ -1236,7 +1243,7 @@ def _prec_bounds(self, n,prec): r""" A helper function not designed for direct use. - It returns the `p`-adic precisions of the approximation + It returns the `\alpha`-adic precisions of the approximation to the `p`-adic L-function. EXAMPLES:: @@ -1244,19 +1251,46 @@ def _prec_bounds(self, n,prec): sage: E = EllipticCurve('11a1') sage: Lp = E.padic_lseries(19) sage: Lp._prec_bounds(3,5) - [[+Infinity, +Infinity], [-1, -1], [-1, -1], [-1, -1], [-1, -1]] + [+Infinity, -1, -1, -1, -1] sage: Lp._prec_bounds(2,5) - [[+Infinity, +Infinity], [-1, -2], [-1, -2], [-1, -2], [-1, -2]] + [+Infinity, -2, -2, -2, -2] sage: Lp._prec_bounds(10,5) - [[+Infinity, +Infinity], [3, 2], [3, 2], [3, 2], [3, 2]] + [+Infinity, 6, 6, 6, 6] """ - p = self._p e = self._e_bounds(n-1,prec) - c0 = ZZ(n+2)/2 - c1 = ZZ(n+3)/2 - return [[infinity,infinity]] + [[(e[j] - c0).floor(), (e[j] - c1).floor()] for j in range(1,len(e))] + c0 = ZZ(n+2) + return [infinity] + [ 2* e[j] - c0 for j in range(1,len(e))] + def _poly(self, a): + """ + Given an element a in Qp[alpha] this returns the list + containing the two coordinates in Qp. + + EXAMPLES:: + + sage: E = EllipticCurve("14a1") + sage: lp = E.padic_lseries(5) + sage: K = lp.alpha().parent() + sage: a = K(5) + sage: a + 4*alpha^2 + alpha^4 + O(alpha^42) + sage: lp._poly(a) + [5 + O(5^21), O(5^21)] + """ + # this should be implemented in elements of Eisenstein rings at some point trac 20248 + + if a.is_zero(): + return [0,0] + v, k = a._ntl_rep_abs() + K = a.base_ring() + pi = K.uniformiser() + v0 = K(v[0]._sage_()) * pi**k + v1 = K(v[1]._sage_()) * pi**k + alpha = a.parent().gen() + assert v0 + v1*alpha == a + return [ v0, v1 ] + def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): r""" Returns a vector of two components which are p-adic power series. @@ -1289,7 +1323,7 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): sage: E = EllipticCurve('14a') sage: L = E.padic_lseries(5) sage: L.Dp_valued_series(4) # long time (9s on sage.math, 2011) - (1 + 4*5 + 4*5^3 + O(5^4) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), O(5^4) + O(5)*T + O(5)*T^2 + O(5)*T^3 + (2 + O(5))*T^4 + O(T^5)) + (1 + 4*5 + O(5^2) + (4 + O(5))*T + (1 + O(5))*T^2 + (4 + O(5))*T^3 + (2 + O(5))*T^4 + O(T^5), 5^2 + O(5^3) + O(5^2)*T + (4*5 + O(5^2))*T^2 + (2*5 + O(5^2))*T^3 + (2 + 2*5 + O(5^2))*T^4 + O(T^5)) """ E = self._E p = self._p @@ -1298,8 +1332,14 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): # now split up the series in two lps = G + H * alpha R = lps.base_ring().base_ring() # Qp QpT , T = PowerSeriesRing(R,'T',prec).objgen() - G = QpT([lps[n][0] for n in range(0,lps.prec())], prec) - H = QpT([lps[n][1] for n in range(0,lps.prec())], prec) + Gli = [] + Hli = [] + for n in range(0,lps.prec()): + v = self._poly(lps[n]) + Gli.append( v[0] ) + Hli.append( v[1] ) + G = QpT( Gli, prec ) + H = QpT( Hli, prec ) # now compute phi phi = matrix.matrix([[0,-1/p],[1,E.ap(p)/p]]) diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 30f1237b30e..7cce73eea37 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -79,7 +79,7 @@ REFERENCES: -.. [CT] J. E. Cremona and T. Thongjunthug, The Complex AGM, periods of +.. [CT] \J. E. Cremona and T. Thongjunthug, The Complex AGM, periods of elliptic curves over $\CC$ and complex elliptic logarithms. Journal of Number Theory Volume 133, Issue 8, August 2013, pages 2813-2841. @@ -137,7 +137,7 @@ def __init__(self, E, embedding=None): - ``E`` -- an elliptic curve - - ``embedding`` (defult: ``None``) -- an embedding of the base + - ``embedding`` (default: ``None``) -- an embedding of the base field `K` of ``E`` into a real or complex field. If ``None``: @@ -1557,7 +1557,9 @@ def elliptic_logarithm(self, P, prec=None, reduce=True): sage: L.coordinates(L.elliptic_logarithm(E(e3,0))) (0.500000000000000, 0.000000000000000) - TESTS (see #10026 and #11767):: + TESTS: + + (see :trac:`10026` and :trac:`11767`):: sage: K. = QuadraticField(2) sage: E = EllipticCurve([ 0, -1, 1, -3*w -4, 3*w + 4 ]) @@ -1708,7 +1710,7 @@ def elliptic_exponential(self, z, to_curve=True): sage: P.parent() Abelian group of points on Elliptic Curve defined by y^2 + 1.00000000000000*y = x^3 + (-1.00000000000000)*x over Complex Field with 53 bits of precision - Very small `z` are handled properly (see #8820):: + Very small `z` are handled properly (see :trac:`8820`):: sage: K. = QuadraticField(-1) sage: E = EllipticCurve([0,0,0,a,0]) diff --git a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx index 8d1fa38296d..99df07e5947 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice_region.pyx +++ b/src/sage/schemes/elliptic_curves/period_lattice_region.pyx @@ -16,7 +16,7 @@ AUTHORS: REFERENCES: -.. [T] T. Thongjunthug, Computing a lower bound for the canonical +.. [T] \T. Thongjunthug, Computing a lower bound for the canonical height on elliptic curves over number fields, Math. Comp. 79 (2010), pages 2431-2449. diff --git a/src/sage/schemes/elliptic_curves/sha_tate.py b/src/sage/schemes/elliptic_curves/sha_tate.py index e06375103c4..26843257a21 100644 --- a/src/sage/schemes/elliptic_curves/sha_tate.py +++ b/src/sage/schemes/elliptic_curves/sha_tate.py @@ -89,6 +89,7 @@ from sage.misc.all import verbose import sage.arith.all as arith from sage.rings.padics.factory import Qp +from sage.modules.free_module_element import vector factor = arith.factor valuation = arith.valuation @@ -444,7 +445,7 @@ def an_padic(self, p, prec=0, use_twists=True): REFERENCES: - .. [MTT] B. Mazur, J. Tate, and J. Teitelbaum, On `p`-adic + .. [MTT] \B. Mazur, J. Tate, and J. Teitelbaum, On `p`-adic analogues of the conjectures of Birch and Swinnerton-Dyer, Inventiones mathematicae 84, (1986), 1-48. @@ -542,22 +543,27 @@ def an_padic(self, p, prec=0, use_twists=True): E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 - reg = E.padic_regulator(p) r = E.rank() - + if r > 0 : + reg = E.padic_regulator(p) + else: + if E.is_supersingular(p): + reg = vector([ Qp(p,20)(1), 0 ]) + else: + reg = Qp(p,20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: - D = D/p + D = ZZ(D/p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: - D = D/ell + D = ZZ(D/ell) ve = valuation(D,2) - de = (D/2**ve).abs() + de = ZZ( (D/2**ve).abs() ) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) @@ -1072,10 +1078,10 @@ def bound_kato(self): applications arithmétiques III, Astérisque vol 295, SMF, Paris, 2004. - .. [Gri] G. Grigorov, Kato's Euler System and the Main Conjecture, + .. [Gri] \G. Grigorov, Kato's Euler System and the Main Conjecture, Harvard Ph.D. Thesis (2005). - .. [GJPST] G. Grigorov, A. Jorza, S. Patrikis, W. A. Stein, + .. [GJPST] \G. Grigorov, A. Jorza, S. Patrikis, W. A. Stein, and C. Tarniţǎ, Computational verification of the Birch and Swinnerton-Dyer conjecture for individual elliptic curves, Math. Comp. 78 (2009), 2397-2425. diff --git a/src/sage/schemes/generic/algebraic_scheme.py b/src/sage/schemes/generic/algebraic_scheme.py index 3cf8ecc4c69..8bf50a9107d 100644 --- a/src/sage/schemes/generic/algebraic_scheme.py +++ b/src/sage/schemes/generic/algebraic_scheme.py @@ -1168,7 +1168,7 @@ def irreducible_components(self): ] We verify that the irrelevant ideal isn't accidently returned - (see trac 6920):: + (see :trac:`6920`):: sage: PP. = ProjectiveSpace(3,QQ) sage: f = x^3 + y^3 + z^3 + w^3 diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py index e0da50c4474..867a61d1cfd 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py @@ -137,7 +137,7 @@ def weierstrass_points(self): f, h = self.hyperelliptic_polynomials() if h != 0: raise NotImplementedError() - return [self((0,1,0))] + [self((x, 0, 1)) for x in f.roots()] + return [self((0,1,0))] + [self((x, 0, 1)) for x in f.roots(multiplicities=False)] def is_in_weierstrass_disc(self,P): """ diff --git a/src/sage/schemes/hyperelliptic_curves/mestre.py b/src/sage/schemes/hyperelliptic_curves/mestre.py index fbca19f1992..092a3a3086a 100644 --- a/src/sage/schemes/hyperelliptic_curves/mestre.py +++ b/src/sage/schemes/hyperelliptic_curves/mestre.py @@ -142,13 +142,13 @@ def HyperellipticCurve_from_invariants(i, reduced=True, precision=None, REFERENCES: - .. [LY2001] K. Lauter and T. Yang, "Computing genus 2 curves from + .. [LY2001] \K. Lauter and T. Yang, "Computing genus 2 curves from invariants on the Hilbert moduli space", Journal of Number Theory 131 (2011), pages 936 - 958 - .. [M1991] J.-F. Mestre, "Construction de courbes de genre 2 a partir de + .. [M1991] \J.-F. Mestre, "Construction de courbes de genre 2 a partir de leurs modules", in Effective methods in algebraic geometry (Castiglioncello, 1990), volume 94 of Progr. Math., pages 313 - 334 - .. [W1999] P. van Wamelen, Pari-GP code, section "thecubic" + .. [W1999] \P. van Wamelen, Pari-GP code, section "thecubic" https://www.math.lsu.edu/~wamelen/Genus2/FindCurve/igusa2curve.gp """ from sage.structure.sequence import Sequence diff --git a/src/sage/schemes/plane_conics/con_rational_function_field.py b/src/sage/schemes/plane_conics/con_rational_function_field.py index 06b50a90408..12df6f55843 100644 --- a/src/sage/schemes/plane_conics/con_rational_function_field.py +++ b/src/sage/schemes/plane_conics/con_rational_function_field.py @@ -361,7 +361,7 @@ def _reduce_conic(self): that `(x,y,z) \in F(t)` is a solution of the reduced conic if and only if `(\lambda x, \mu y, \nu z)` is a solution of `self`. - ALGORITMH: + ALGORITHM: The algorithm used is the algorithm ReduceConic in [HC2006]_. @@ -445,7 +445,7 @@ def find_point(self, supports, roots, case, solution = 0): A point `(x,y,z) \in F(t)` of ``self``. Output is undefined when the input solubility certificate is incorrect. - ALGORITMH: + ALGORITHM: The algorithm used is the algorithm FindPoint in [HC2006]_, with a simplification from [ACKERMANS2016]_. diff --git a/src/sage/schemes/product_projective/wehlerK3.py b/src/sage/schemes/product_projective/wehlerK3.py index bcb8210de8e..83f3c1703b6 100644 --- a/src/sage/schemes/product_projective/wehlerK3.py +++ b/src/sage/schemes/product_projective/wehlerK3.py @@ -16,15 +16,15 @@ REFERENCES: - .. [FaHu] J. A. de Faria, B. Hutz. Combinatorics of Cycle Lengths on + .. [FaHu] \J. A. de Faria, B. Hutz. Combinatorics of Cycle Lengths on Wehler K3 Surfaces over finite fields :arxiv:`1309.6598`, 2013. - .. [CaSi] G. Call and J. Silverman. Computing the Canonical Height on + .. [CaSi] \G. Call and J. Silverman. Computing the Canonical Height on K3 Surfaces. Mathematics of Comp. , 65 (1996), 259-290. - .. [Wehl] J. Wehler. Hypersurfaces of the Flag Variety: Deformation + .. [Wehl] \J. Wehler. Hypersurfaces of the Flag Variety: Deformation Theory and the Theorems of Kodaira-Spencer, Torelli, Lefschetz, M. Noether, and Serre. Math. Z. 198 (1988), 21-38 - .. [Hutzthesis] B. Hutz. Arithmetic Dynamics on Varieties of dimension greater + .. [Hutzthesis] \B. Hutz. Arithmetic Dynamics on Varieties of dimension greater than one. PhD Thesis, Brown University 2007 """ diff --git a/src/sage/schemes/projective/endPN_minimal_model.py b/src/sage/schemes/projective/endPN_minimal_model.py index 0e7dcd59e64..83b37fbaf96 100644 --- a/src/sage/schemes/projective/endPN_minimal_model.py +++ b/src/sage/schemes/projective/endPN_minimal_model.py @@ -11,11 +11,11 @@ REFERENCES: -.. [Bruin-Molnar] N. Bruin and A. Molnar, *Minimal models for rational +.. [Bruin-Molnar] \N. Bruin and A. Molnar, *Minimal models for rational functions in a dynamical setting*, LMS Journal of Computation and Mathematics, Volume 15 (2012), pp 400-417. -.. [Molnar] A. Molnar, *Fractional Linear Minimal Models of Rational Functions*, +.. [Molnar] \A. Molnar, *Fractional Linear Minimal Models of Rational Functions*, M.Sc. Thesis. """ diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index fe6f9c08b70..743f20b4454 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -677,10 +677,10 @@ def dynatomic_polynomial(self, period): REFERENCES: - .. [Hutz] B. Hutz. Determination of all rational preperiodic points + .. [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 + .. [MoPa] \P. Morton and P. Patel. The Galois theory of periodic points of polynomial maps. Proc. London Math. Soc., 68 (1994), 225-263. INPUT: @@ -874,6 +874,16 @@ def dynatomic_polynomial(self, period): sage: f = H([t*x^2-1*y^2, t*y^2]) sage: f.dynatomic_polynomial([1, 2]).parent() Symbolic Ring + + :: + + sage: R. = PolynomialRing(QQ) + sage: S = R.quo(R.ideal(y^2-x+1)) + sage: P. = ProjectiveSpace(FractionField(S),1) + sage: H = End(P) + sage: f = H([u^2 + S(x^2)*v^2, v^2]) + sage: f.dynatomic_polynomial([1,1]) + v^3*xbar^2 + u^2*v + u*v^2 """ if self.domain().ngens() > 2: raise TypeError("does not make sense in dimension >1") @@ -905,7 +915,7 @@ def dynatomic_polynomial(self, period): QR = PHI.numerator().quo_rem(PHI.denominator()) if not QR[1]: return(QR[0]) - except TypeError: # something Singular can't handle + except (TypeError, NotImplementedError): # something Singular can't handle pass #even when the ring can be passed to singular in quo_rem, #it can't always do the division, so we call Maxima @@ -3218,8 +3228,10 @@ def multiplier_spectra(self, n, formal=True, embedding=None): sage: H = End(P) sage: f = H([x^2 - w/4*y^2, y^2]) sage: f.multiplier_spectra(2, False, embedding=K.embeddings(QQbar)[0]) - [0, 5.931851652578137? + 0.?e-17*I, 0.0681483474218635? - 1.930649271699173?*I, - 0.0681483474218635? + 1.930649271699173?*I] + [0, + 5.931851652578137? + 0.?e-49*I, + 0.0681483474218635? - 1.930649271699173?*I, + 0.0681483474218635? + 1.930649271699173?*I] :: @@ -3570,7 +3582,10 @@ def rational_periodic_points(self, **kwds): r""" Determine the set of rational periodic points for an endomorphism of projective space. - Must be defined over `\QQ`. + 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 trouble getting a particular map to finish, try first computing the possible periods, then @@ -3601,6 +3616,9 @@ def rational_periodic_points(self, **kwds): - ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional) + - ``ncpus`` - number of cpus to use in parallel. (optional) + default: all available cpus. + OUTPUT: - a list of rational points in projective space. @@ -3632,53 +3650,126 @@ def rational_periodic_points(self, **kwds): 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)] + + :: + + sage: R. = QQ[] + sage: K. = NumberField(x^2-x+1) + sage: P. = ProjectiveSpace(K,1) + sage: H = End(P) + sage: f = H([u^2 + v^2,v^2]) + sage: f.rational_periodic_points() + [(w : 1), (-w + 1 : 1), (1 : 0)] + + :: + + sage: R. = QQ[] + sage: K. = NumberField(x^2-x+1) + sage: P. = ProjectiveSpace(K,1) + sage: H = End(P) + sage: f = H([u^2+v^2,u*v]) + sage: f.rational_periodic_points() + Traceback (most recent call last): + ... + NotImplementedError: rational periodic points for number fields only implemented for polynomials """ if not self.is_endomorphism(): raise NotImplementedError("must be an endomorphism of projective space") - if self.domain().base_ring() != QQ: - raise NotImplementedError("must be QQ") #for p-adic lifting + PS = self.domain() + K = PS.base_ring() + if K in _NumberFields: + if not K.is_absolute(): + 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 periodic 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 + #is not a polynomial, the Weil restriction will not be a morphism and we cannot + #apply this algorithm. + g = self.dehomogenize(1) + inf = PS([1,0]) + k = 1 + if isinstance(g[0], FractionFieldElement): + g = self.dehomogenize(0) + inf = PS([0,1]) + k = 0 + if isinstance(g[0], FractionFieldElement): + raise NotImplementedError("rational periodic points for number fields only implemented for polynomials") + #determine rational periodic points + #infinity is a totally ramified fixed point for a polynomial + periodic_points = set([inf]) + #compute the weil resctriction + G = g.weil_restriction() + F = G.homogenize(d) + #find the QQ rational periodic points for the weil restriction + Fper = F.rational_periodic_points(**kwds) + for P in Fper: + #take the 'good' points in the weil restriction and find the + #associated number field points. + if P[d] == 1: + pt = [sum([P[i]*w**i for i in range(d)])] + pt.insert(k,1) + Q = PS(pt) + #for each periodic point get the entire cycle + if not Q in periodic_points: + #check periodic not preperiodic and add all points in cycle + orb = set([Q]) + Q2 = self(Q) + while Q2 not in orb: + orb.add(Q2) + Q2 = self(Q2) + if Q2 == Q: + periodic_points = periodic_points.union(orb) + return list(periodic_points) + else: + primebound = kwds.pop("prime_bound", [1, 20]) + p = kwds.pop("lifting_prime", 23) + periods = kwds.pop("periods", None) + badprimes = kwds.pop("bad_primes", None) + num_cpus = kwds.pop("ncpus", ncpus()) - primebound = kwds.pop("prime_bound", [1, 20]) - p = kwds.pop("lifting_prime", 23) - periods = kwds.pop("periods", None) - badprimes = kwds.pop("bad_primes", None) + if (isinstance(primebound, (list, tuple)) == False): + try: + primebound = [1, ZZ(primebound)] + except TypeError: + 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") - if (isinstance(primebound, (list, tuple)) == False): - try: - primebound = [1, ZZ(primebound)] - except TypeError: - 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") + if badprimes is None: + badprimes = self.primes_of_bad_reduction() + if periods is None: + periods = self.possible_periods(prime_bound=primebound, bad_primes=badprimes, ncpus=num_cpus) + PS = self.domain() + R = PS.base_ring() + periodic = set() + while p in badprimes: + p = next_prime(p + 1) + B = e ** self.height_difference_bound() - if badprimes is None: - badprimes = self.primes_of_bad_reduction() - if periods is None: - periods = self.possible_periods(prime_bound=primebound, bad_primes=badprimes) - PS = self.domain() - R = PS.base_ring() - periodic = set() - while p in badprimes: - p = next_prime(p + 1) - B = e ** self.height_difference_bound() - - f = self.change_ring(GF(p)) - all_points = f.possible_periods(True) #return the list of points and their periods. - pos_points = [] - for i in range(len(all_points)): - if all_points[i][1] in periods and (all_points[i] in pos_points) == False: #check period, remove duplicates - pos_points.append(all_points[i]) - periodic_points = self.lift_to_rational_periodic(pos_points,B) - for p,n in periodic_points: - for k in range(n): - p.normalize_coordinates() - periodic.add(p) - p = self(p) - return(list(periodic)) + f = self.change_ring(GF(p)) + all_points = f.possible_periods(True) #return the list of points and their periods. + pos_points = [] + for i in range(len(all_points)): + if all_points[i][1] in periods and (all_points[i] in pos_points) == False: #check period, remove duplicates + pos_points.append(all_points[i]) + periodic_points = self.lift_to_rational_periodic(pos_points,B) + for p,n in periodic_points: + for k in range(n): + p.normalize_coordinates() + periodic.add(p) + p = self(p) + return list(periodic) + else: + raise TypeError("base field must be an absolute number field") def rational_preimages(self, Q): r""" @@ -3917,11 +4008,22 @@ def all_rational_preimages(self, points): 25/16 : 1), (w^2 - 29/16 : 1), (w^2 - 21/16 : 1), (w^2 + w - 25/16 : 1), (-w - 1/2 : 1), (w : 1), (-w : 1), (-w^2 + 29/16 : 1), (w^2 + w - 33/16 : 1)] + + :: + + sage: K. = QuadraticField(3) + sage: P. = ProjectiveSpace(K,1) + sage: H = End(P) + sage: f = H([u^2+v^2, v^2]) + sage: f.all_rational_preimages(P(4)) + [(-w : 1), (w : 1)] """ if not self.is_endomorphism(): 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") + if not isinstance(points, (list, tuple)): + points = [points] PS = self.domain() RPS = PS.base_ring() @@ -3972,6 +4074,9 @@ def rational_preperiodic_points(self, **kwds): - ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional) + - ``ncpus`` - number of cpus to use in parallel. (optional) + default: all available cpus. + OUTPUT: - a list of rational points in projective space. @@ -4075,15 +4180,16 @@ def rational_preperiodic_points(self, **kwds): badprimes = kwds.pop("bad_primes", None) periods = kwds.pop("periods", None) primebound = kwds.pop("prime_bound", [1, 20]) + num_cpus = kwds.pop("ncpus", ncpus()) if badprimes is None: badprimes = self.primes_of_bad_reduction() if periods is None: - periods = self.possible_periods(prime_bound=primebound, bad_primes=badprimes) #determine the set of possible periods + periods = self.possible_periods(prime_bound=primebound, bad_primes=badprimes, ncpus=num_cpus) #determine the set of possible periods if periods == []: return([]) #no rational preperiodic points else: p = kwds.pop("lifting_prime", 23) - T = self.rational_periodic_points(prime_bound=primebound, lifting_prime=p, periods=periods, bad_primes=badprimes) #find the rational preperiodic points + T = self.rational_periodic_points(prime_bound=primebound, lifting_prime=p, periods=periods, bad_primes=badprimes, ncpus=num_cpus) #find the rational preperiodic points preper = self.all_rational_preimages(T) #find the preperiodic points preper = list(preper) return(preper) @@ -4123,6 +4229,9 @@ def rational_preperiodic_graph(self, **kwds): - ``bad_primes`` - a list or tuple of integer primes, the primes of bad reduction. (optional) + - ``ncpus`` - number of cpus to use in parallel. (optional) + default: all available cpus. + OUTPUT: - a digraph representing the orbits of the rational preperiodic points in projective space. @@ -4433,7 +4542,7 @@ def possible_periods(self, return_points=False): REFERENCES: - .. [Hutz-gr] B. Hutz. Good reduction of periodic points, Illinois Journal of + .. [Hutz-gr] \B. Hutz. Good reduction of periodic points, Illinois Journal of Mathematics 53 (Winter 2009), no. 4, 1109-1126. ALGORITHM: diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index fe38a03368f..9e861d67f24 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -67,6 +67,8 @@ - Ben Hutz: (June 2012): support for rings - Ben Hutz (9/2014): added support for Cartesian products + +- Rebecca Lauren Miller (March 2016) : added point_transformation_matrix """ #***************************************************************************** @@ -1149,6 +1151,139 @@ def subscheme_from_Chow_form(self, Ch, dim): X = self.subscheme(ch) return X + def point_transformation_matrix(self, points_source, points_target): + r""" + + Returns a unique element of PGL that transforms one set of points to another. + + Given a projective space of degree n and a set of n+2 source points and a set of n+2 target + points in the same projective space, such that no n+1 points of each set are linearly dependent + finds the unique element of PGL that translates the source points to the target points. + + + Warning :: will not work over precision fields + + INPUT: + + - ``points_source`` - points in source projective space. + + - ``points_target`` - points in target projective space. + + OUTPUT: Transformation matrix - element of PGL. + + EXAMPLES:: + + sage: P1.=ProjectiveSpace(QQ, 2) + sage: points_source=[P1([1,4,1]),P1([1,2,2]),P1([3,5,1]),P1([1,-1,1])] + sage: points_target=[P1([5,-2,7]),P1([3,-2,3]),P1([6,-5,9]), P1([3,6,7])] + sage: m = P1.point_transformation_matrix(points_source, points_target); m + [ -13/59 -128/59 -25/59] + [538/177 8/59 26/177] + [ -45/59 -196/59 1] + sage: [P1(list(m*vector(list(points_source[i])))) == points_target[i] for i in range(4)] + [True, True, True, True] + + :: + + sage: P. = ProjectiveSpace(GF(13),1) + sage: points_source = [P([-6,7]), P([1,4]), P([3,2])] + sage: points_target = [P([-1,2]), P([0,2]), P([-1,6])] + sage: P.point_transformation_matrix(points_source, points_target) + [10 4] + [10 1] + + :: + + sage: P. = ProjectiveSpace(QQ,1) + sage: points_source = [P([-6,-4]), P([1,4]), P([3,2])] + sage: points_target = [P([-1,2]), P([0,2]), P([-7,-3])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: source points not independent + + :: + + sage: P. = ProjectiveSpace(QQ,1) + sage: points_source = [P([-6,-1]), P([1,4]), P([3,2])] + sage: points_target = [P([-1,2]), P([0,2]), P([-2,4])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: target points not independent + + :: + + sage: P.=ProjectiveSpace(QQ, 2) + sage: points_source=[P([1,4,1]),P([2,-7,9]),P([3,5,1])] + sage: points_target=[P([5,-2,7]),P([3,-2,3]),P([6,-5,9]),P([6,-1,1])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: incorrect number of points in source, need 4 points + + :: + + sage: P.=ProjectiveSpace(QQ, 2) + sage: points_source=[P([1,4,1]),P([2,-7,9]),P([3,5,1]),P([1,-1,1])] + sage: points_target=[P([5,-2,7]),P([3,-2,3]),P([6,-5,9]),P([6,-1,1]),P([7,8,-9])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: incorrect number of points in target, need 4 points + + :: + + sage: P.=ProjectiveSpace(QQ, 2) + sage: P1.=ProjectiveSpace(QQ, 2) + sage: points_source=[P([1,4,1]),P([2,-7,9]),P([3,5,1]),P1([1,-1,1])] + sage: points_target=[P([5,-2,7]),P([3,-2,3]),P([6,-5,9]),P([6,-1,1])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: source points not in self + + :: + + sage: P.=ProjectiveSpace(QQ, 2) + sage: P1.=ProjectiveSpace(QQ, 2) + sage: points_source=[P([1,4,1]),P([2,-7,9]),P([3,5,1]),P([1,-1,1])] + sage: points_target=[P([5,-2,7]),P([3,-2,3]),P([6,-5,9]),P1([6,-1,1])] + sage: P.point_transformation_matrix(points_source, points_target) + Traceback (most recent call last): + ... + ValueError: target points not in self + """ + r = self.base_ring() + n = self.dimension_relative() + P = ProjectiveSpace(r, n**2+2*n,'p') + # makes sure there aren't to few or two many points + if len(points_source)!= n + 2: + raise ValueError ("incorrect number of points in source, need %d points"%(n+2)) + if len(points_target)!= n + 2: + raise ValueError ("incorrect number of points in target, need %d points"%(n+2)) + if any([x.codomain()!=self for x in points_source]): + raise ValueError ("source points not in self") + if any([x.codomain()!=self for x in points_target]): + raise ValueError ("target points not in self") + # putting points as the rows of the matrix + Ms = matrix(r, [list(s) for s in points_source]) + if any([m == 0 for m in Ms.minors(n+1)]): + raise ValueError("source points not independent") + Mt = matrix(r, [list(t) for t in points_target]) + if any([l == 0 for l in Mt.minors(n+1)]): + raise ValueError("target points not independent") + A = matrix(P.coordinate_ring(), n+1, n+1, P.gens()) + #transpose to get image points and then get the list of image points with columns + funct = (A*Ms.transpose()).columns() + eq = [] + for k in range(n+2):# n+2 num f point and n is size of pts + eq = eq+ [funct[k][i]*points_target[k][j] - funct[k][j]*points_target[k][i]\ + for i in range(0,n+1) for j in range(i+1, n+1)] + v = P.subscheme(eq) + w = v.rational_points() + return matrix(r, n+1, n+1, list(w[0])) + class ProjectiveSpace_finite_field(ProjectiveSpace_field): def _point(self, *args, **kwds): """ diff --git a/src/sage/schemes/toric/ideal.py b/src/sage/schemes/toric/ideal.py index d8fa89ade5c..bb666adec2f 100644 --- a/src/sage/schemes/toric/ideal.py +++ b/src/sage/schemes/toric/ideal.py @@ -176,7 +176,7 @@ class ToricIdeal(MPolynomialIdeal): ``z1``, ... - ``base_ring`` -- a ring (optional). Default: `\QQ`. The base - ring of the ideal. A toric ideal uses only coefficents `\pm 1`. + ring of the ideal. A toric ideal uses only coefficients `\pm 1`. - ``polynomial_ring`` -- a polynomial ring (optional). The polynomial ring to construct the ideal in. diff --git a/src/sage/sets/recursively_enumerated_set.pxd b/src/sage/sets/recursively_enumerated_set.pxd index 8544620e970..c767c152627 100644 --- a/src/sage/sets/recursively_enumerated_set.pxd +++ b/src/sage/sets/recursively_enumerated_set.pxd @@ -25,4 +25,3 @@ cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic): cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic): cdef set _get_next_graded_component(self, set B) - diff --git a/src/sage/sets/recursively_enumerated_set.pyx b/src/sage/sets/recursively_enumerated_set.pyx index 9d224a70dde..cc83767b560 100644 --- a/src/sage/sets/recursively_enumerated_set.pyx +++ b/src/sage/sets/recursively_enumerated_set.pyx @@ -173,6 +173,7 @@ Depth first search:: #***************************************************************************** from sage.structure.parent cimport Parent from sage.categories.enumerated_sets import EnumeratedSets +from sage.combinat.backtrack import SearchForest #from sage.misc.classcall_metaclass import ClasscallMetaclass, typecall from collections import deque @@ -304,8 +305,7 @@ def RecursivelyEnumeratedSet(seeds, successors, structure=None, enumeration, max_depth, facade=facade, category=category) if structure == 'forest': if enumeration is None: enumeration = 'depth' - from sage.combinat.backtrack import SearchForest - return SearchForest(roots=seeds, children=successors, + return RecursivelyEnumeratedSet_forest(roots=seeds, children=successors, algorithm=enumeration, post_process=post_process, facade=facade, category=category) if structure == 'graded': @@ -516,18 +516,25 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): r""" TESTS:: - sage: RecursivelyEnumeratedSet([1], lambda x: [x+1, x-1], structure=None) + sage: f = lambda x: [x-1, x+1] + sage: RecursivelyEnumeratedSet([1], f, structure=None) A recursively enumerated set (breadth first search) :: - sage: RecursivelyEnumeratedSet([1], lambda x: [x+1, x-1], structure='graded') + sage: RecursivelyEnumeratedSet([1], f, structure='graded') A recursively enumerated set with a graded structure (breadth first search) :: - sage: RecursivelyEnumeratedSet([1], lambda x: [x-1, x+1], structure='symmetric') + sage: RecursivelyEnumeratedSet([1], f, structure='symmetric') A recursively enumerated set with a symmetric structure (breadth first search) + + When ``max_depth`` is set:: + + sage: RecursivelyEnumeratedSet([1], f, structure='symmetric', max_depth=4) + A recursively enumerated set with a symmetric structure (breadth + first search) with max_depth=4 """ L = ["A recursively enumerated set"] classname = self.__class__.__name__ @@ -537,15 +544,14 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): L.append("with a symmetric structure") elif classname.startswith('RecursivelyEnumeratedSet_forest'): L.append("with a forest structure") - #elif classname.startswith('RecursivelyEnumeratedSet_generic'): - # pass - #else: - # pass if self._enumeration in ['depth', 'breadth']: L.append("({} first search)".format(self._enumeration)) else: L.append("({} search)".format(self._enumeration)) + + if not self._max_depth == float('inf'): + L.append("with max_depth={}".format(self._max_depth)) return " ".join(L) cpdef seeds(self): @@ -682,7 +688,7 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): r""" Iterate on the elements of ``self`` (breadth first). - This code remembers every elements generated. + This code remembers every element generated. INPUT: @@ -843,6 +849,63 @@ cdef class RecursivelyEnumeratedSet_generic(Parent): for y in self.successors(x): stack.append(y) + def to_digraph(self, max_depth=None, loops=True, multiedges=True): + r""" + Return the directed graph of the recursively enumerated set. + + INPUT: + + - ``max_depth`` -- (default: ``None``) specifies the maximal depth + for which outgoing edges of elements are computed; if ``None``, the + value of ``self._max_depth`` is used + - ``loops`` -- (default: ``True``) option for the digraph + - ``multiedges`` -- (default: ``True``) option of the digraph + + OUTPUT: + + A directed graph + + .. WARNING:: + + If the set is infinite, this will loop forever unless ``max_depth`` + is finite. + + EXAMPLES:: + + sage: child = lambda i: [(i+3) % 10, (i+8) % 10] + sage: R = RecursivelyEnumeratedSet([0], child) + sage: R.to_digraph() + Looped multi-digraph on 10 vertices + + Digraph of an recursively enumerated set with a symmetric structure of + infinite cardinality using ``max_depth`` argument:: + + sage: succ = lambda a: [(a[0]-1,a[1]), (a[0],a[1]-1), (a[0]+1,a[1]), (a[0],a[1]+1)] + sage: seeds = [(0,0)] + sage: C = RecursivelyEnumeratedSet(seeds, succ, structure='symmetric') + sage: C.to_digraph(max_depth=4) + Looped multi-digraph on 41 vertices + + The ``max_depth`` argument can be given at the creation of the set:: + + sage: C = RecursivelyEnumeratedSet(seeds, succ, structure='symmetric', max_depth=3) + sage: C.to_digraph() + Looped multi-digraph on 25 vertices + + Digraph of an recursively enumerated set with a graded structure:: + + sage: f = lambda a: [a+1, a+I] + sage: C = RecursivelyEnumeratedSet([0], f, structure='graded') + sage: C.to_digraph(max_depth=4) + Looped multi-digraph on 21 vertices + """ + successors = self.successors + it = self.breadth_first_search_iterator(max_depth=max_depth) + E = [(u,v) for u in it for v in successors(u)] + from sage.graphs.digraph import DiGraph + return DiGraph(E, format='list_of_edges', loops=loops, + multiedges=multiedges) + cdef class RecursivelyEnumeratedSet_symmetric(RecursivelyEnumeratedSet_generic): r""" Generic tool for constructing ideals of a symmetric relation. @@ -987,7 +1050,8 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic): sage: f = lambda a: [(a[0]+1,a[1]), (a[0],a[1]+1)] sage: C = RecursivelyEnumeratedSet([(0,0)], f, structure='graded', max_depth=3) sage: C - A recursively enumerated set with a graded structure (breadth first search) + A recursively enumerated set with a graded structure (breadth first + search) with max_depth=3 sage: sorted(C) [(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (3, 0)] @@ -1054,10 +1118,27 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic): [(0, 1), (1, 0)] [(0, 2), (1, 1), (2, 0)] [(0, 3), (1, 2), (2, 1), (3, 0)] + + TESTS: + + Make sure that :trac:`20225` is fixed:: + + sage: child = lambda k:[2*k,2*k+1] if k<8 else [] + sage: root = [0] + sage: R = RecursivelyEnumeratedSet(root, child, structure='graded') + sage: it = R.graded_component_iterator() + sage: for _ in range(7): next(it) + {0} + {1} + {2, 3} + {4, 5, 6, 7} + {8, 9, 10, 11, 12, 13, 14, 15} + set() + set() """ cdef set B B = set(self._seeds) - while B: + while True: yield B B = self._get_next_graded_component(B) @@ -1095,3 +1176,4 @@ cdef class RecursivelyEnumeratedSet_graded(RecursivelyEnumeratedSet_generic): C.add(y) return C +RecursivelyEnumeratedSet_forest = SearchForest diff --git a/src/sage/stats/distributions/discrete_gaussian_lattice.py b/src/sage/stats/distributions/discrete_gaussian_lattice.py index b3b59d88d05..6cd78d812fc 100644 --- a/src/sage/stats/distributions/discrete_gaussian_lattice.py +++ b/src/sage/stats/distributions/discrete_gaussian_lattice.py @@ -270,7 +270,7 @@ def __init__(self, B, sigma=1, c=None, precision=None): try: B = matrix(B) - except ValueError: + except (TypeError, ValueError): pass try: diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index f5f304e9afe..0120ba78103 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -345,7 +345,7 @@ cdef class GaussianHiddenMarkovModel(HiddenMarkovModel): ... ValueError: length must be nonnegative - Example in which the starting state is 0 (see trac 11452):: + Example in which the starting state is 0 (see :trac:`11452`):: sage: set_random_seed(23); m.generate_sequence(2) ([0.6501, -2.0151], [0, 1]) diff --git a/src/sage/stats/intlist.pyx b/src/sage/stats/intlist.pyx index cab1951558d..9451de95d4f 100644 --- a/src/sage/stats/intlist.pyx +++ b/src/sage/stats/intlist.pyx @@ -30,7 +30,7 @@ max_print = 10 from libc.string cimport memcpy from sage.rings.integer import Integer from sage.finance.time_series cimport TimeSeries -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" include "cysignals/signals.pxi" from cpython.string cimport * @@ -99,7 +99,7 @@ cdef class IntList: else: self._length = len(values) - self._values = sage_malloc(sizeof(int)*self._length) + self._values = sig_malloc(sizeof(int)*self._length) if self._values == NULL: raise MemoryError cdef Py_ssize_t i @@ -148,7 +148,7 @@ cdef class IntList: Deallocate memory used by the IntList, if it was allocated. """ if self._values: - sage_free(self._values) + sig_free(self._values) def __repr__(self): """ @@ -498,7 +498,7 @@ cdef class IntList: # We just reach into the data structure underlying T, since we # want this function to be *very* fast. T._length = self._length - T._values = sage_malloc(sizeof(double)*self._length) + T._values = sig_malloc(sizeof(double)*self._length) cdef Py_ssize_t i for i in range(self._length): T._values[i] = self._values[i] @@ -550,7 +550,7 @@ cdef IntList new_int_list(Py_ssize_t length): raise ValueError, "length must be nonnegative" cdef IntList t = IntList.__new__(IntList) t._length = length - t._values = sage_malloc(sizeof(int)*length) + t._values = sig_malloc(sizeof(int)*length) return t diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index 37d7bc5ea2a..fe2ea2fa452 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -56,7 +56,6 @@ from __future__ import division # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' cimport generators cimport sage_object from sage.categories.category import Category diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index e22f3a120a3..5e804c63fa1 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -75,7 +75,7 @@ see the documentation for Parent. #***************************************************************************** from cpython.object cimport (PyObject, PyTypeObject, - PyObject_CallObject, PyObject_RichCompare) + PyObject_CallObject, PyObject_RichCompare, Py_TYPE) from cpython.weakref cimport PyWeakref_GET_OBJECT, PyWeakref_NewRef from libc.string cimport strncmp @@ -85,7 +85,7 @@ cdef dict operator_dict = operator.__dict__ from operator import add, sub, mul, div, truediv, iadd, isub, imul, idiv from .sage_object cimport SageObject, rich_to_bool -from .parent cimport Set_PythonType +from .parent cimport Set_PythonType, Parent_richcmp_element_without_coercion from .element cimport arith_error_message, parent_c from .coerce_actions import LeftModuleAction, RightModuleAction, IntegerMulAction from .coerce_exceptions import CoercionException @@ -258,6 +258,10 @@ cpdef bint is_numpy_type(t): True sage: is_numpy_type(numpy.float) # Alias for Python float False + sage: is_numpy_type(numpy.ndarray) + True + sage: is_numpy_type(numpy.matrix) + True sage: is_numpy_type(int) False sage: is_numpy_type(Integer) @@ -269,7 +273,13 @@ cpdef bint is_numpy_type(t): """ if not isinstance(t, type): return False - return strncmp((t).tp_name, "numpy.", 6) == 0 + cdef PyTypeObject* T = t + if strncmp(T.tp_name, "numpy.", 6) == 0: + return True + # Check base type. This is needed to detect numpy.matrix. + if strncmp(T.tp_base.tp_name, "numpy.", 6) == 0: + return True + return False cdef object _Integer @@ -1792,12 +1802,22 @@ cdef class CoercionModel_cache_maps(CoercionModel): if y is None or y is Ellipsis: return rich_to_bool(op, 0 if x is y else 1) + # Check for manual __richcmp__ override (only on y since + # x.__richmp__ would already have been called) + if isinstance(y, Element): + if (y)._parent.get_flag(Parent_richcmp_element_without_coercion): + return Py_TYPE(y).tp_richcompare(x, y, op) + # Coerce to a common parent try: x, y = self.canonical_coercion(x, y) except (TypeError, NotImplementedError): pass else: + # The common parent should not be one which explicitly + # asked to *not* use coercion for comparisons. + assert not (isinstance(x, Element) and + (x)._parent.get_flag(Parent_richcmp_element_without_coercion)) return PyObject_RichCompare(x, y, op) # Comparing with coercion didn't work, try something else. diff --git a/src/sage/structure/element_wrapper.pxd b/src/sage/structure/element_wrapper.pxd new file mode 100644 index 00000000000..ae6e333f1a5 --- /dev/null +++ b/src/sage/structure/element_wrapper.pxd @@ -0,0 +1,12 @@ + +from sage.structure.element cimport Element + +cdef class ElementWrapper(Element): + cdef public object value + + cpdef bint _lt_by_value(self, other) + cpdef int _cmp_by_value(self, other) + +cdef class ElementWrapperCheckWrappedClass(ElementWrapper): + pass + diff --git a/src/sage/structure/element_wrapper.pyx b/src/sage/structure/element_wrapper.pyx index fa17bc62ab1..5cfb14d4699 100644 --- a/src/sage/structure/element_wrapper.pyx +++ b/src/sage/structure/element_wrapper.pyx @@ -19,7 +19,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -from cpython cimport bool from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE from sage.structure.parent cimport Parent @@ -84,8 +83,6 @@ cdef class ElementWrapper(Element): Versions before :trac:`14519` had parent as the second argument and the value as the first. """ - cdef public object value - def __init__(self, parent, value): """ EXAMPLES:: @@ -216,6 +213,21 @@ cdef class ElementWrapper(Element): from sage.misc.latex import latex return latex(self.value) + def _ascii_art_(self): + r""" + EXAMPLES:: + + sage: from sage.structure.element_wrapper import DummyParent + sage: ElementWrapper(DummyParent("A parent"), 1)._ascii_art_() + 1 + sage: x = var('x') + sage: ElementWrapper(DummyParent("A parent"), x^2 + x)._ascii_art_() + 2 + x + x + """ + from sage.typeset.ascii_art import ascii_art + return ascii_art(self.value) + def __hash__(self): """ Return the same hash as for the wrapped element. @@ -316,7 +328,7 @@ cdef class ElementWrapper(Element): return self.value != (right).value return False - cpdef bool _lt_by_value(self, other): + cpdef bint _lt_by_value(self, other): """ Return whether ``self`` is strictly smaller than ``other``. @@ -384,11 +396,9 @@ cdef class ElementWrapper(Element): sage: cmp(l11, 1) in [-1,1] # class differ True """ - if self.__class__ != other.__class__: - return cmp(self.__class__, other.__class__) - if self.parent() != other.parent(): - return cmp(self.parent(), other.parent()) - return cmp(self.value, other.value) + return cmp(self.__class__, other.__class__) or \ + cmp(self.parent(), other.parent()) or \ + cmp(self.value, other.value) def __copy__(self): """ diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index 3982866d5a0..668d72ab147 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -143,7 +143,7 @@ AUTHORS: include "sage/ext/stdsage.pxi" -from sage.ext.memory cimport check_reallocarray +include "cysignals/memory.pxi" from cpython.list cimport * from cpython.int cimport * from cpython.ref cimport * @@ -1315,7 +1315,7 @@ cdef class ClonableIntArray(ClonableElement): def __dealloc__(self): if self._list is not NULL: - sage_free(self._list) + sig_free(self._list) self._len = -1 self._list = NULL diff --git a/src/sage/structure/misc.pyx b/src/sage/structure/misc.pyx index 55d857160cb..ce3bfff49c4 100644 --- a/src/sage/structure/misc.pyx +++ b/src/sage/structure/misc.pyx @@ -221,7 +221,7 @@ def getattr_from_other_class(self, cls, str name): ... AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__' - This was caught by #8296 for which we do a couple more tests:: + This was caught by :trac:`8296` for which we do a couple more tests:: sage: "__weakref__" in dir(A) True @@ -311,7 +311,7 @@ def dir_with_other_class(self, cls): TESTS: - Check that #13043 is fixed:: + Check that :trac:`13043` is fixed:: sage: len(dir(RIF))==len(set(dir(RIF))) True diff --git a/src/sage/structure/parent.pxd b/src/sage/structure/parent.pxd index 9b52e4c3321..df70f16d725 100644 --- a/src/sage/structure/parent.pxd +++ b/src/sage/structure/parent.pxd @@ -1,16 +1,15 @@ -############################################################################### -# SAGE: System for Algebra and Geometry Experimentation -# Copyright (C) 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/ -############################################################################### +#***************************************************************************** cimport sage.structure.category_object from sage.structure.coerce_dict cimport MonoDict, TripleDict cdef class Parent(category_object.CategoryObject): - cdef public _element_constructor cdef public _convert_method_name cdef public bint _element_init_pass_parent @@ -20,6 +19,11 @@ cdef class Parent(category_object.CategoryObject): cdef public _initial_convert_list cdef readonly bint _coercions_used + # Flags, see below + cdef int flags + cdef inline bint get_flag(self, int flag): + return self.flags & flag + cpdef bint is_coercion_cached(self, domain) cpdef bint is_conversion_cached(self, domain) cpdef register_coercion(self, mor) @@ -58,7 +62,6 @@ cdef class Parent(category_object.CategoryObject): cpdef an_element(self) cdef public object _cache_an_element - # For internal use cpdef _generic_convert_map(self, S) cdef discover_coerce_map_from(self, S) @@ -96,4 +99,10 @@ cdef class Parent(category_object.CategoryObject): # An optional single Morphism that describes a canonical coercion out of self cdef _embedding +# Flags for Parent.flags +cdef enum: + # If this flag is set, call __richcmp__ on elements without + # coercion. This allows a completely custom comparison function. + Parent_richcmp_element_without_coercion = 1 + cpdef Parent Set_PythonType(theType) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index bfcd798f836..f76cf393036 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -175,7 +175,6 @@ cdef object BuiltinMethodType = type(repr) from cpython.object cimport * from cpython.bool cimport * -include 'sage/ext/stdsage.pxi' def is_Parent(x): @@ -237,7 +236,6 @@ cdef inline bint good_as_convert_domain(S): return isinstance(S,SageObject) or isinstance(S,type) cdef class Parent(category_object.CategoryObject): - def __init__(self, base=None, *, category=None, element_constructor=None, gens=None, names=None, normalize=True, facade=None, **kwds): """ @@ -1570,7 +1568,7 @@ cdef class Parent(category_object.CategoryObject): TESTS: - We test the workaround described in #12956 to let categories + We test the workaround described in :trac:`12956` to let categories override this default implementation:: sage: class As(Category): diff --git a/src/sage/structure/sage_object.pyx b/src/sage/structure/sage_object.pyx index f1edb93bf04..86d8e871ba0 100644 --- a/src/sage/structure/sage_object.pyx +++ b/src/sage/structure/sage_object.pyx @@ -380,7 +380,7 @@ cdef class SageObject: sage: hash(b) Traceback (most recent call last): ... - TypeError: unhashable type: 'sage.rings.padics.padic_ZZ_pX_CR_element.pAdicZZpXCRElement' + TypeError: unhashable type: 'sage.rings.padics.qadic_flint_CR.qAdicCappedRelativeElement' sage: @cached_method ....: def f(x): return x==a sage: f(b) diff --git a/src/sage/structure/sequence.py b/src/sage/structure/sequence.py index 3c88dc04734..8e077f3ef47 100644 --- a/src/sage/structure/sequence.py +++ b/src/sage/structure/sequence.py @@ -38,7 +38,7 @@ sage: w[0] = 'hi' Traceback (most recent call last): ... - TypeError: unable to convert hi to a rational + TypeError: unable to convert 'hi' to a rational However, if you do ``w = Sequence(v)`` and the resulting universe is ``Objects()``, the elements are not guaranteed to have any diff --git a/src/sage/symbolic/assumptions.py b/src/sage/symbolic/assumptions.py index 5b2ef9d6cb2..585e599695c 100644 --- a/src/sage/symbolic/assumptions.py +++ b/src/sage/symbolic/assumptions.py @@ -79,8 +79,6 @@ class GenericDeclaration(SageObject): sage: decl = GenericDeclaration(x, 'integer') sage: decl.assume() sage: sin(x*pi) - sin(pi*x) - sage: sin(x*pi).simplify() 0 sage: decl.forget() sage: sin(x*pi) @@ -115,8 +113,6 @@ def __init__(self, var, assumption): sage: decl = GenericDeclaration(x, 'integer') sage: decl.assume() sage: sin(x*pi) - sin(pi*x) - sage: sin(x*pi).simplify() 0 sage: decl.forget() sage: sin(x*pi) @@ -425,8 +421,6 @@ def assume(*args): Simplifying certain well-known identities works as well:: sage: sin(n*pi) - sin(pi*n) - sage: sin(n*pi).simplify() 0 sage: forget() sage: sin(n*pi).simplify() @@ -492,6 +486,26 @@ def assume(*args): sin(pi*n) sage: sin(m*pi).simplify() sin(pi*m) + + Check that positive integers can be created (:trac:`20132`) + + sage: forget() + sage: x = SR.var('x', domain='positive') + sage: assume(x, 'integer') + sage: x.is_positive() and x.is_integer() + True + + sage: forget() + sage: x = SR.var('x', domain='integer') + sage: assume(x > 0) + sage: x.is_positive() and x.is_integer() + True + + sage: forget() + sage: assume(x, "integer") + sage: assume(x > 0) + sage: x.is_positive() and x.is_integer() + True """ for x in preprocess_assumptions(args): if isinstance(x, (tuple, list)): @@ -519,6 +533,7 @@ def forget(*args): We define and forget multiple assumptions:: + sage: forget() sage: var('x,y,z') (x, y, z) sage: assume(x>0, y>0, z == 1, y>0) diff --git a/src/sage/symbolic/comparison.pxd b/src/sage/symbolic/comparison.pxd new file mode 100644 index 00000000000..d8e919f191f --- /dev/null +++ b/src/sage/symbolic/comparison.pxd @@ -0,0 +1,11 @@ +from ginac cimport * +from sage.symbolic.expression cimport Expression + + +cpdef int print_order(lhs, rhs) except -2 +cdef int print_order_c(Expression lhs, Expression rhs) + +cpdef print_sorted(expression_list) + +# cpdef int math_order_c(Expression lhs, Expression rhs) except -2 + diff --git a/src/sage/symbolic/comparison.pyx b/src/sage/symbolic/comparison.pyx new file mode 100644 index 00000000000..de129a18a1b --- /dev/null +++ b/src/sage/symbolic/comparison.pyx @@ -0,0 +1,394 @@ +""" +Comparison of Symbolic Expressions + +There are two useful ways to compare symbolic expressions: + +* :func:`print_order` is how the terms are ordered. This is always + defined. If you need a fast comparison, this is it. + +* :func:`math_order` is the "mathematical" comparison. This may raise + an exception if the answer is unknown (to Sage) or cannot, in + principle, evaluated to a boolean (for example, if it involves + symbolic variables). Can be very slow as it potentially calls + Maxima to prove the inequality. +""" +from cpython cimport * + +from sage.symbolic.ring import SR +from sage.symbolic.expression cimport is_Expression + + +cdef int print_order_c(Expression lhs, Expression rhs): + """ + Print comparison. + + See :meth:`print_order` for details. + """ + return print_order_compare((lhs)._gobj, (rhs)._gobj) + + +cpdef int print_order(lhs, rhs) except -2: + """ + Comparison in the print order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order + sage: print_order(1, oo) + 1 + sage: print_order(e, oo) + -1 + sage: print_order(pi, oo) + 1 + sage: print_order(1, sqrt(2)) + 1 + + Check that :trac:`12967` is fixed:: + + sage: cmp(SR(oo), sqrt(2)) + 1 + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + return print_order_c(lhs, rhs) + + +class _print_key(object): + + def __init__(self, ex): + """ + Sort key to sort in print order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _print_key + sage: _print_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_order, _print_key + sage: print_order(1, 2) + -1 + sage: _print_key(1) < _print_key(2) + True + sage: print_order(1, sqrt(2)) + 1 + sage: _print_key(1) < _print_key(sqrt(2)) + False + """ + return print_order_c(self.ex, other.ex) < 0 + + +cpdef print_sorted(expressions): + """ + Sort a list in print order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by :meth:`print_order`. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import print_sorted + sage: print_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [e, sqrt(2), pi, 1] + """ + return sorted(expressions, key=_print_key) + + +class _math_key(object): + + def __init__(self, ex): + """ + Sort key to sort in "Mathematics" order. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_print_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _math_key + sage: _math_key(1) < _math_key(2) + True + sage: _math_key(1) < _math_key(sqrt(2)) + True + + Check that :trac:`12967` is fixed:: + + sage: _math_key(1) < _math_key(oo) + True + """ + less_than = bool(self.ex < other.ex) + greater_than = bool(self.ex > other.ex) + if less_than: + if not greater_than: + return True + else: + assert False # unreachable + else: + if greater_than: + return False + else: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + + +cpdef math_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mathematics" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + The list sorted by ascending (real) value. If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import math_sorted + sage: math_sorted([SR(1), SR(e), SR(pi), sqrt(2)]) + [1, sqrt(2), e, pi] + """ + return sorted(expressions, key=_math_key) + + +cpdef int mixed_order(lhs, rhs) except -2: + """ + Comparison in the mixed order + + INPUT: + + - ``lhs``, ``rhs`` -- two symbolic expressions or something that + can be converted to one. + + OUTPUT: + + Either `-1`, `0`, or `+1` indicating the comparison. An exception + is raised if the arguments cannot be converted into the symbolic + ring. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_order + sage: mixed_order(1, oo) + -1 + sage: mixed_order(e, oo) + -1 + sage: mixed_order(pi, oo) + -1 + sage: mixed_order(1, sqrt(2)) + -1 + sage: mixed_order(x + x^2, x*(x+1)) + -1 + + Check that :trac:`12967` is fixed:: + + sage: cmp(SR(oo), sqrt(2)) + 1 + """ + if not is_Expression(lhs): + lhs = SR(lhs) + if not is_Expression(rhs): + rhs = SR(rhs) + less_than = _mixed_key(lhs) < _mixed_key(rhs) + if less_than: + return -1 + greater_than = _mixed_key(lhs) > _mixed_key(rhs) + if greater_than: + return 1 + else: + return 0 + + +class _mixed_key(object): + + def __init__(self, ex): + """ + Sort key to sort in mixed order. + + Mixed order is print order if variables are present, + mathematical/numeric if not. This should enable quick + and correct results. + + INPUT: + + - ``ex`` -- symbolic expression or something that can be + converted into one. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) + + """ + self.ex = ex if is_Expression(ex) else SR(ex) + + def __lt__(self, other): + """ + Implement "less than" to make the key comparable. + + INPUT: + + - ``other`` -- another :class:`_mixed_key` instance. + + OUTPUT: + + Boolean. A ``ValueError`` is raised if we do not know how to + perform the comparison. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import _mixed_key + sage: _mixed_key(1) < _mixed_key(2) + True + sage: _mixed_key(1) < _mixed_key(sqrt(2)) + True + + Check that :trac:`12967` is fixed:: + + sage: _mixed_key(1) < _mixed_key(oo) + True + """ + from sage.rings.real_mpfi import RIF + selfv = len(self.ex.variables()) + otherv = len(other.ex.variables()) + if selfv: + if otherv: + return _print_key(self.ex) < _print_key(other.ex) + else: + return False + else: + if otherv: + return True + + # no variables involved from here on + rel = self.ex < other.ex + if (self.ex.is_infinity() or other.ex.is_infinity()): + pynac_result = decide_relational((rel)._gobj) + if pynac_result == relational_undecidable: + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + return pynac_result == relational_true + + det_ex = self.ex - other.ex + if not has_symbol_or_function((rel)._gobj): + while hasattr(det_ex, 'pyobject') and isinstance(det_ex, Expression): + try: + det_ex = det_ex.pyobject() + except TypeError: + break + if not isinstance(det_ex, Expression): + return det_ex < 0 + from sage.rings.qqbar import QQbar + try: + from sage.rings.qqbar import QQbar + num = QQbar(det_ex) + except (TypeError, AttributeError,ValueError,NotImplementedError): + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + else: + return num < 0 + + # here we have expressions containing functions + try: + num = det_ex.expand().n(RIF.prec()+5) + except (TypeError, AttributeError): + raise ValueError('cannot compare {0} and {1}'.format(self.ex, other.ex)) + else: + return num < 0 + + + +cpdef mixed_sorted(expressions): + """ + Sort a list of symbolic numbers in the "Mixed" order + + INPUT: + + - ``expressions`` -- a list/tuple/iterable of symbolic + expressions, or something that can be converted to one. + + OUTPUT: + + In the list the numeric values are sorted by ascending (real) value, + and the expressions with variables according to print order. + If an entry does not + define a real value (or plus/minus infinity), or if the comparison + is not known, a ``ValueError`` is raised. + + EXAMPLES:: + + sage: from sage.symbolic.comparison import mixed_sorted + sage: mixed_sorted([SR(1), SR(e), SR(pi), sqrt(2), x, sqrt(x), sin(1/x)]) + [1, sqrt(2), e, pi, sin(1/x), sqrt(x), x] + """ + return sorted(expressions, key=_mixed_key) + diff --git a/src/sage/symbolic/constants.py b/src/sage/symbolic/constants.py index 20b93ff9788..fc6a7e48647 100644 --- a/src/sage/symbolic/constants.py +++ b/src/sage/symbolic/constants.py @@ -357,6 +357,19 @@ def domain(self): """ return self._domain + def __lt__(self, other): + """ + Perform float comparison with constant. + + EXAMPLES:: + + sage: cmp(pi, 0) + 1 + sage: cmp(pi, SR(0)) + 1 + """ + return self.__float__() < other + def expression(self): """ Returns an expression for this constant. @@ -622,7 +635,7 @@ def _sympy_(self): sage: RR(I) Traceback (most recent call last): ... - TypeError: Unable to convert x (='1.00000000000000*I') to real number. + TypeError: unable to convert '1.00000000000000*I' to a real number Expressions involving I that are real-valued can be converted to real fields:: @@ -1080,8 +1093,10 @@ class Khinchin(Constant): sage: m = mathematica(khinchin); m # optional - mathematica Khinchin sage: m.N(200) # optional - mathematica - 2.68545200106530644530971483548179569382038229399446295305115234555721885953715200280114117493184769799515346590528809008289767771641096305179253348325966838185231542133211949962603932852204481940961807 # 32-bit - 2.6854520010653064453097148354817956938203822939944629530511523455572188595371520028011411749318476979951534659052880900828976777164109630517925334832596683818523154213321194996260393285220448194096181 # 64-bit + 2.6854520010653064453097148354817956938203822939944629530511523455572 + > 188595371520028011411749318476979951534659052880900828976777164109630517 + > 925334832596683818523154213321194996260393285220448194096181 + """ def __init__(self, name='khinchin'): """ diff --git a/src/sage/symbolic/expression.pxd b/src/sage/symbolic/expression.pxd index c5390ef72bc..6df0fab4f91 100644 --- a/src/sage/symbolic/expression.pxd +++ b/src/sage/symbolic/expression.pxd @@ -1,5 +1,6 @@ from ginac cimport * +cdef class Expression from sage.structure.element cimport CommutativeRingElement cdef class Expression(CommutativeRingElement): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 8e8412172cd..a03b55296f6 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -20,7 +20,7 @@ Notice that squaring the relation squares both sides. sage: eqn.expand() x^2 - 2*x + 1 <= x^2 - 2*x + 3 -The can transform a true relational into a false one:: +This can transform a true relation into a false one:: sage: eqn = SR(-5) < SR(-3); eqn -5 < -3 @@ -31,7 +31,7 @@ The can transform a true relational into a false one:: sage: bool(eqn^2) False -We can do arithmetic with relationals:: +We can do arithmetic with relations:: sage: e = x+1 <= x-2 sage: e + 2 @@ -53,7 +53,7 @@ We can do arithmetic with relationals:: sage: -2/e -2/(x + 1) <= -2/(x - 2) -We can even add together two relations, so long as the operators are +We can even add together two relations, as long as the operators are the same:: sage: (x^3 + x <= x - 17) + (-x <= x - 10) @@ -142,10 +142,12 @@ import sage.rings.integer import sage.rings.rational from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT from sage.structure.element cimport ModuleElement, RingElement, Element +from sage.symbolic.comparison import mixed_order from sage.symbolic.getitem cimport OperandsWrapper from sage.symbolic.series cimport SymbolicSeries from sage.symbolic.complexity_measures import string_length from sage.symbolic.function import get_sfunction_from_serial, SymbolicFunction +cimport sage.symbolic.comparison from sage.rings.rational import Rational # Used for sqrt. from sage.misc.derivative import multi_derivative from sage.misc.superseded import deprecated_function_alias @@ -154,6 +156,7 @@ from sage.misc.decorators import rename_keyword from sage.structure.dynamic_class import dynamic_class from sage.symbolic.operators import FDerivativeOperator, add_vararg, mul_vararg + # a small overestimate of log(10,2) LOG_TEN_TWO_PLUS_EPSILON = 3.321928094887363 @@ -3172,7 +3175,7 @@ cdef class Expression(CommutativeRingElement): sage: (-1.0*x)*(1.0/x) -1.00000000000000 sage: sin(1.0*pi) - sin(1.00000000000000*pi) + 0 """ cdef GEx x cdef Expression _right = right @@ -3318,8 +3321,8 @@ cdef class Expression(CommutativeRingElement): 1/x sage: ~SR(3) 1/3 - sage: v1=var('v1'); a = (2*erf(2*v1*arcsech(0))/v1); ~a - 1/2*v1/erf(2*v1*arcsech(0)) + sage: v1=var('v1'); a = (2*erf(2*v1*arcsech(1/2))/v1); ~a + 1/2*v1/erf(2*v1*arcsech(1/2)) """ return 1/self @@ -3458,8 +3461,25 @@ cdef class Expression(CommutativeRingElement): I*x - 1/2 sage: t.subs(x=I*x).subs(x=0).is_positive() False + + Check if :trac:`16397` is fixed: + + sage: cmp(1, sqrt(2)) + -1 + sage: cmp(SR(1), sqrt(2)) + -1 + sage: cmp(log(8), 3*log(2)) + 0 + sage: RLF(1) < RLF(sqrt(2)) + True + sage: RealSet((0, pi),[pi, pi],(pi,4)) + (0, 4) + sage: RealSet((0, pi),[0, pi],(pi,4)) + [0, 4) + sage: RealSet((0, pi),[0, 3.5],(pi,4)) + [0, 4) """ - return print_order_compare(left._gobj, (right)._gobj) + return mixed_order(left, right) cpdef int _cmp_add(Expression left, Expression right) except -2: """ @@ -4213,7 +4233,15 @@ cdef class Expression(CommutativeRingElement): sage: expand((x-1)^3/(y-1)) x^3/(y - 1) - 3*x^2/(y - 1) + 3*x/(y - 1) - 1/(y - 1) sage: expand((x+sin((x+y)^2))^2) - x^2 + 2*x*sin((x + y)^2) + sin((x + y)^2)^2 + x^2 + 2*x*sin(x^2 + 2*x*y + y^2) + sin(x^2 + 2*x*y + y^2)^2 + + Observe that :meth:`expand` also expands function arguments:: + + sage: f(x) = function('f')(x) + sage: fx = f(x*(x+1)); fx + f((x + 1)*x) + sage: fx.expand() + f(x^2 + x) We can expand individual sides of a relation:: @@ -4518,6 +4546,7 @@ cdef class Expression(CommutativeRingElement): sage: ((x^y)^z).find(w0^w1) [(x^y)^z] """ + from sage.symbolic.comparison import print_sorted cdef Expression p = self.coerce_in(pattern) cdef GExList found self._gobj.find(p._gobj, found) @@ -4526,7 +4555,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(found.end()): res.append(new_Expression_from_GEx(self._parent, itr.obj())) itr.inc() - res.sort(cmp) + res = print_sorted(res) return res def has(self, pattern): @@ -4696,10 +4725,22 @@ cdef class Expression(CommutativeRingElement): sage: cmd = '{} /. {} -> {}' # optional - mathematica sage: for s1,s2 in subs: # optional - mathematica ....: mathematica.eval(cmd.format(E,s1,s2)) # optional - mathematica - 'y^4+y^2+y' - 'x^4+y+x' - 'x^4+y' - 'y' + 2 4 + y + y + y + 4 + x + x + y + 4 + x + y + y + + The same, with formatting more suitable for cut and paste:: + + sage: for s1,s2 in subs: # optional - mathematica + ....: mathematica(cmd.format(E,s1,s2)) # optional - mathematica + y + y^2 + y^4 + x + x^4 + y + x^4 + y + y TESTS: @@ -4935,6 +4976,7 @@ cdef class Expression(CommutativeRingElement): """ from sage.symbolic.ring import SR + from sage.symbolic.comparison import print_sorted cdef GExSet sym_set g_list_symbols(self._gobj, sym_set) res = [] @@ -4942,7 +4984,7 @@ cdef class Expression(CommutativeRingElement): while itr.is_not_equal(sym_set.end()): res.append(new_Expression_from_GEx(SR, itr.obj())) itr.inc() - res.sort(cmp=lambda x,y: -cmp(x,y)) + res = print_sorted(res)[::-1] return tuple(res) def arguments(self): @@ -8785,8 +8827,8 @@ cdef class Expression(CommutativeRingElement): TESTS: - There are two square roots of `$(x + 1)^2$`, so this should - not be simplified to `$x + 1$`, :trac:`12737`:: + There are two square roots of `(x + 1)^2`, so this should + not be simplified to `x + 1`, see :trac:`12737`:: sage: f = sqrt((x + 1)^2) sage: f.simplify_full() @@ -10669,9 +10711,8 @@ cdef class Expression(CommutativeRingElement): Solve Brahmagupta-Pell equations:: sage: sol = solve_diophantine(x^2 - 2*y^2 == 1); sol - (sqrt(2)*(2*sqrt(2) + 3)^t - sqrt(2)*(-2*sqrt(2) + 3)^t + 3/2*(2*sqrt(2) + 3)^t + 3/2*(-2*sqrt(2) + 3)^t, - 3/4*sqrt(2)*(2*sqrt(2) + 3)^t - 3/4*sqrt(2)*(-2*sqrt(2) + 3)^t + (2*sqrt(2) + 3)^t + (-2*sqrt(2) + 3)^t) - sage: print [(sol[0].subs(t=t).simplify_full(),sol[1].subs(t=t).simplify_full()) for t in range(-1,5)] + [(-sqrt(2)*(2*sqrt(2) + 3)^t + sqrt(2)*(-2*sqrt(2) + 3)^t - 3/2*(2*sqrt(2) + 3)^t - 3/2*(-2*sqrt(2) + 3)^t,... + sage: print [(sol[1][0].subs(t=t).simplify_full(),sol[1][1].subs(t=t).simplify_full()) for t in range(-1,5)] [(1, 0), (3, 2), (17, 12), (99, 70), (577, 408), (3363, 2378)] TESTS:: @@ -11648,8 +11689,10 @@ def solve_diophantine(f, *args, **kwds): sage: solve_diophantine(a^2-3*b^2+1) [] sage: solve_diophantine(a^2-3*b^2+2) - (1/2*sqrt(3)*(sqrt(3) + 2)^t - 1/2*sqrt(3)*(-sqrt(3) + 2)^t + 1/2*(sqrt(3) + 2)^t + 1/2*(-sqrt(3) + 2)^t, - 1/6*sqrt(3)*(sqrt(3) + 2)^t - 1/6*sqrt(3)*(-sqrt(3) + 2)^t + 1/2*(sqrt(3) + 2)^t + 1/2*(-sqrt(3) + 2)^t) + [(1/2*sqrt(3)*(sqrt(3) + 2)^t - 1/2*sqrt(3)*(-sqrt(3) + 2)^t + 1/2*(sqrt(3) + 2)^t + 1/2*(-sqrt(3) + 2)^t, + 1/6*sqrt(3)*(sqrt(3) + 2)^t - 1/6*sqrt(3)*(-sqrt(3) + 2)^t + 1/2*(sqrt(3) + 2)^t + 1/2*(-sqrt(3) + 2)^t), + (-1/2*sqrt(3)*(sqrt(3) + 2)^t + 1/2*sqrt(3)*(-sqrt(3) + 2)^t - 1/2*(sqrt(3) + 2)^t - 1/2*(-sqrt(3) + 2)^t, + -1/6*sqrt(3)*(sqrt(3) + 2)^t + 1/6*sqrt(3)*(-sqrt(3) + 2)^t - 1/2*(sqrt(3) + 2)^t - 1/2*(-sqrt(3) + 2)^t)] """ from sage.symbolic.ring import SR diff --git a/src/sage/symbolic/expression_conversions.py b/src/sage/symbolic/expression_conversions.py index 798346ecc1d..380c24d2809 100644 --- a/src/sage/symbolic/expression_conversions.py +++ b/src/sage/symbolic/expression_conversions.py @@ -21,6 +21,7 @@ from sage.symbolic.pynac import I from sage.functions.all import exp from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg +from sage.functions.piecewise import piecewise from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_quadratic from functools import reduce GaussianField = I.pyobject().parent() @@ -840,7 +841,7 @@ def arithmetic(self, ex, operator): if base == e and expt / (pi*I) in QQ: return exp(expt)._algebraic_(self.field) - raise TypeError("unable to convert %s to %s"%(ex, self.field)) + raise TypeError("unable to convert %r to %s"%(ex, self.field)) def composition(self, ex, operator): """ @@ -902,8 +903,8 @@ def composition(self, ex, operator): #We have to handle the case where we get the same symbolic #expression back. For example, QQbar(zeta(7)). See #ticket #12665. - if cmp(res, ex) == 0: - raise TypeError("unable to convert %s to %s"%(ex, self.field)) + if (res - ex).is_trivial_zero(): + raise TypeError("unable to convert %r to %s"%(ex, self.field)) return self.field(res) def algebraic(ex, field): @@ -1298,7 +1299,7 @@ def symbol(self, ex): try: return self.ff.fast_float_constant(float(ex)) except TypeError: - raise ValueError("free variable: %s" % repr(ex)) + raise NotImplementedError, "free variable: %s" % repr(ex) def arithmetic(self, ex, operator): """ diff --git a/src/sage/symbolic/function.pxd b/src/sage/symbolic/function.pxd index 7174f58bf83..db1ba6c5d54 100644 --- a/src/sage/symbolic/function.pxd +++ b/src/sage/symbolic/function.pxd @@ -12,6 +12,7 @@ cdef class Function(SageObject): cdef _register_function(self) cdef class BuiltinFunction(Function): + cdef object _preserved_arg cdef _is_registered(self) cdef class GinacFunction(BuiltinFunction): diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 7910bd46b84..020f27c4274 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -452,7 +452,6 @@ cdef class Function(SageObject): else: symbolic_input = False - cdef Py_ssize_t i if coerce: try: @@ -469,27 +468,8 @@ cdef class Function(SageObject): method = getattr(args[0], self._name, None) if callable(method): return method() + raise TypeError("cannot coerce arguments: %s" % (err)) - # There is no natural coercion from QQbar to the symbolic ring - # in order to support - # sage: QQbar(sqrt(2)) + sqrt(3) - # 3.146264369941973? - # to work around this limitation, we manually convert - # elements of QQbar to symbolic expressions here - from sage.rings.qqbar import QQbar, AA - nargs = [None]*len(args) - for i in range(len(args)): - carg = args[i] - if isinstance(carg, Element) and \ - (carg)._parent is QQbar or \ - (carg)._parent is AA: - nargs[i] = SR(carg) - else: - try: - nargs[i] = SR.coerce(carg) - except Exception: - raise TypeError("cannot coerce arguments: %s" % (err)) - args = nargs else: # coerce == False for a in args: if not isinstance(a, Expression): @@ -797,7 +777,7 @@ cdef class GinacFunction(BuiltinFunction): There is also no need to register these functions. """ def __init__(self, name, nargs=1, latex_name=None, conversions=None, - ginac_name=None, evalf_params_first=True): + ginac_name=None, evalf_params_first=True, preserved_arg=None): """ TESTS:: @@ -812,7 +792,7 @@ cdef class GinacFunction(BuiltinFunction): """ self._ginac_name = ginac_name BuiltinFunction.__init__(self, name, nargs, latex_name, conversions, - evalf_params_first=evalf_params_first) + evalf_params_first=evalf_params_first, preserved_arg=preserved_arg) def __call__(self, *args, **kwds): """ @@ -919,7 +899,7 @@ cdef class BuiltinFunction(Function): of this class. """ def __init__(self, name, nargs=1, latex_name=None, conversions=None, - evalf_params_first=True, alt_name=None): + evalf_params_first=True, alt_name=None, preserved_arg=None): """ TESTS:: @@ -928,6 +908,10 @@ cdef class BuiltinFunction(Function): sage: c(pi/2) 0 """ + self._preserved_arg = preserved_arg + if preserved_arg and (preserved_arg < 1 or preserved_arg > nargs): + raise ValueError("preserved_arg must be between 1 and nargs") + # If we have an _evalf_ method, change _eval_ to a # wrapper function which first tries to call _evalf_. if hasattr(self, '_evalf_'): @@ -988,10 +972,28 @@ cdef class BuiltinFunction(Function): res = super(BuiltinFunction, self).__call__( *args, coerce=coerce, hold=hold) - # If none of the input arguments was a Sage Element but the - # output is, then convert the output back to the corresponding + # Convert the output back to the corresponding # Python type if possible. if any(isinstance(x, Element) for x in args): + if (self._preserved_arg + and isinstance(args[self._preserved_arg-1], Element)): + from sage.structure.all import parent + arg_parent = parent(args[self._preserved_arg-1]) + if arg_parent is SR: + return res + from sage.rings.polynomial.polynomial_ring import PolynomialRing_commutative + from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict_domain + if (isinstance(arg_parent, PolynomialRing_commutative) + or isinstance(arg_parent, MPolynomialRing_polydict_domain)): + try: + return res.polynomial(ring=arg_parent) + except TypeError: + return res + else: + try: + return arg_parent(res) + except TypeError: + return res return res if not isinstance(res, Element): return res diff --git a/src/sage/symbolic/ginac.pxd b/src/sage/symbolic/ginac.pxd index 7a15668a911..453d5c6e7c3 100644 --- a/src/sage/symbolic/ginac.pxd +++ b/src/sage/symbolic/ginac.pxd @@ -604,7 +604,3 @@ cdef extern from "pynac/order.h": (GEx left, GEx right) except + bint print_order_compare_mul "GiNaC::print_order_mul().compare" \ (GEx left, GEx right) except + - bint print_order "GiNaC::print_order()" \ - (GEx left, GEx right) except + - bint print_order_mul "GiNaC::print_order_mul()" \ - (GEx left, GEx right) except + diff --git a/src/sage/symbolic/integration/integral.py b/src/sage/symbolic/integration/integral.py index 99d03a2812c..0fc765b9e82 100644 --- a/src/sage/symbolic/integration/integral.py +++ b/src/sage/symbolic/integration/integral.py @@ -496,7 +496,7 @@ def integrate(expression, v=None, a=None, b=None, algorithm=None, hold=False): sage: (x^y - z).integrate(y, algorithm="sympy") # see Trac #14694 Traceback (most recent call last): ... - AttributeError: 'Piecewise' object has no attribute '_sage_' + AttributeError: 'ExprCondPair' object has no attribute '_sage_' We integrate the above function in Maple now:: diff --git a/src/sage/symbolic/pynac.pyx b/src/sage/symbolic/pynac.pyx index 761798a81d2..ae68bfeeb54 100644 --- a/src/sage/symbolic/pynac.pyx +++ b/src/sage/symbolic/pynac.pyx @@ -2391,7 +2391,7 @@ Note that conversions to real fields will give TypeErrors:: sage: RR(I) Traceback (most recent call last): ... - TypeError: Unable to convert x (='1.00000000000000*I') to real number. + TypeError: unable to convert '1.00000000000000*I' to a real number We can convert to complex fields:: diff --git a/src/sage/symbolic/random_tests.py b/src/sage/symbolic/random_tests.py index fa7650d07f3..42726363a88 100644 --- a/src/sage/symbolic/random_tests.py +++ b/src/sage/symbolic/random_tests.py @@ -338,8 +338,8 @@ def assert_strict_weak_order(a,b,c, cmp_func): ....: cmp[i,j] = x[i].__cmp__(x[j]) sage: cmp [ 0 -1 -1] - [ 1 0 1] - [ 1 -1 0] + [ 1 0 -1] + [ 1 1 0] """ from sage.matrix.constructor import matrix from sage.combinat.permutation import Permutations diff --git a/src/sage/symbolic/relation.py b/src/sage/symbolic/relation.py index f19d2ef0f3b..b6971329979 100644 --- a/src/sage/symbolic/relation.py +++ b/src/sage/symbolic/relation.py @@ -76,7 +76,7 @@ sage: x = var('x') sage: m = x == 5*x + 1 - sage: n = sin(x) == sin(x+2*pi) + sage: n = sin(x) == sin(x+2*pi, hold=True) sage: m * n x*sin(x) == (5*x + 1)*sin(2*pi + x) sage: m = 2*x == 3*x^2 - 5 @@ -87,11 +87,11 @@ sage: x = var('x') sage: m = x == 5*x + 1 - sage: n = sin(x) == sin(x+2*pi) + sage: n = sin(x) == sin(x+2*pi, hold=True) sage: m/n x/sin(x) == (5*x + 1)/sin(2*pi + x) sage: m = x != 5*x + 1 - sage: n = sin(x) != sin(x+2*pi) + sage: n = sin(x) != sin(x+2*pi, hold=True) sage: m/n x/sin(x) != (5*x + 1)/sin(2*pi + x) diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 82b86bfb1fc..724bfb22609 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -110,6 +110,10 @@ cdef class SymbolicRing(CommutativeRing): True sage: SR.has_coerce_map_from(GF(9, 'a')) True + sage: SR.has_coerce_map_from(RealBallField()) + True + sage: SR.has_coerce_map_from(ComplexBallField()) + True TESTS: @@ -134,6 +138,11 @@ cdef class SymbolicRing(CommutativeRing): True sage: SR.has_coerce_map_from(SR.subring(no_variables=True)) True + + sage: SR.has_coerce_map_from(AA) + True + sage: SR.has_coerce_map_from(QQbar) + True """ if isinstance(R, type): if R in [int, float, long, complex, bool]: @@ -161,6 +170,8 @@ cdef class SymbolicRing(CommutativeRing): from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing from sage.rings.real_mpfi import is_RealIntervalField from sage.rings.complex_interval_field import is_ComplexIntervalField + from sage.rings.real_arb import RealBallField + from sage.rings.complex_arb import ComplexBallField from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing @@ -173,19 +184,15 @@ cdef class SymbolicRing(CommutativeRing): from subring import GenericSymbolicSubring if ComplexField(mpfr_prec_min()).has_coerce_map_from(R): - # Anything with a coercion into any precision of CC - - # In order to have coercion from SR to AA or QQbar, - # we disable coercion in the reverse direction. - # This makes the following work: - # sage: QQbar(sqrt(2)) + sqrt(3) - # 3.146264369941973? - return R not in (RLF, CLF, AA, QQbar) + # Almost anything with a coercion into any precision of CC + return R not in (RLF, CLF) elif is_PolynomialRing(R) or is_MPolynomialRing(R) or is_FractionField(R): base = R.base_ring() return base is not self and self.has_coerce_map_from(base) elif (R is InfinityRing or is_RealIntervalField(R) or is_ComplexIntervalField(R) + or isinstance(R, RealBallField) + or isinstance(R, ComplexBallField) or is_IntegerModRing(R) or is_FiniteField(R)): return True elif isinstance(R, (Maxima, PariInstance)): diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index e8c4a7ba30c..25fb4bea244 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -92,6 +92,7 @@ # Sage library from ring import SR from expression import Expression +from sage.interfaces.tab_completion import ExtraTabCompletion ############################################################################### # Unit conversions dictionary. @@ -929,11 +930,11 @@ def vars_in_str(s): INPUT: - - `s` -- string + - ``s`` -- a string OUTPUT: - - list of strings (unit names) + - a list of strings (unit names) EXAMPLES:: @@ -950,11 +951,11 @@ def unit_derivations_expr(v): INPUT: - - `v` -- string, name of a unit type such as 'area', 'volume', etc. + - ``v`` -- a string, name of a unit type such as 'area', 'volume', etc. OUTPUT: - - symbolic expression + - a symbolic expression EXAMPLES:: @@ -1016,11 +1017,11 @@ class that derives from symbolic expression, and has a specialized INPUT: - - ``name`` -- string + - ``name`` -- a string OUTPUT: - - UnitExpression + - a :class:`UnitExpression` EXAMPLES:: @@ -1032,9 +1033,9 @@ class that derives from symbolic expression, and has a specialized """ return UnitExpression(SR, SR.var(name)) -class Units: +class Units(ExtraTabCompletion): """ - A collection of units of a some type. + A collection of units of some type. EXAMPLES:: @@ -1101,16 +1102,30 @@ def __cmp__(self, other): return cmp(type(self), type(other)) return cmp((self.__name, self.__data), (other.__name, other.__data)) - def trait_names(self): + def _tab_completion(self): """ - Return completions of this unit objects. This is used by the - Sage command line and notebook to create the list of method - names. + Return tab completions. + + This complements the usual content of :func:`dir`, with the + list of the names of the unit collections (resp. units) for + :obj:`units` (resp. its subcollections), in particular for tab + completion purposes. + + .. SEEALSO:: :class:`ExtraTabCompletion` EXAMPLES:: - sage: units.area.trait_names() + sage: units.area._tab_completion() ['acre', 'are', 'barn', 'hectare', 'rood', 'section', 'square_chain', 'square_meter', 'township'] + sage: units._tab_completion() + ['acceleration', ..., 'volume'] + sage: units.force._tab_completion() + ['dyne', ..., 'ton_force'] + + sage: dir(units) + ['_Units__data', ..., 'acceleration', ..., 'volume'] + sage: dir(units.force) + ['_Units__data', ..., 'dyne', ..., 'ton_force'] """ return sorted([x for x in self.__data.keys() if '/' not in x]) @@ -1168,11 +1183,11 @@ def unitdocs(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - ``string`` + - a string EXAMPLES:: @@ -1199,11 +1214,11 @@ def is_unit(s): INPUT: - - `s` -- an object + - ``s`` -- an object OUTPUT: - - ``bool`` + - a boolean EXAMPLES:: @@ -1231,13 +1246,13 @@ def convert(expr, target): INPUT: - - `expr` -- the symbolic expression converting from + - ``expr`` -- the symbolic expression converting from - - `target` -- (default None) the symbolic expression converting to + - ``target`` -- (default None) the symbolic expression converting to OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1323,11 +1338,11 @@ def base_units(unit): INPUT: - - ``unit`` + - ``unit`` -- a unit OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: @@ -1373,12 +1388,12 @@ def convert_temperature(expr, target): INPUT: - - `expr` -- a unit of temperature - - `target` -- a units of temperature + - ``expr`` -- a unit of temperature + - ``target`` -- a units of temperature OUTPUT: - - `symbolic expression` + - a symbolic expression EXAMPLES:: diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 07c03d2d2aa..0858893bfdc 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -263,7 +263,7 @@ class Components(SageObject): The "frame" can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or non-tensorial quantities, - such as connection coefficients or structure coefficents. The symmetries + such as connection coefficients or structure coefficients. The symmetries over some indices are dealt by subclasses of the class :class:`Components`. INPUT: @@ -2508,7 +2508,7 @@ class CompWithSym(Components): The "frame" can be a basis of some vector space or a vector frame on some manifold (i.e. a field of bases). The stored quantities can be tensor components or non-tensorial quantities, - such as connection coefficients or structure coefficents. + such as connection coefficients or structure coefficients. Subclasses of :class:`CompWithSym` are diff --git a/src/sage/tests/book_stein_modform.py b/src/sage/tests/book_stein_modform.py index 730cd854210..77852a68c82 100644 --- a/src/sage/tests/book_stein_modform.py +++ b/src/sage/tests/book_stein_modform.py @@ -312,8 +312,7 @@ [Dirichlet character modulo 5 of conductor 1 mapping 2 |--> 1, Dirichlet character modulo 5 of conductor 5 mapping 2 |--> -1] sage: G = DirichletGroup(200) sage: G -Group of Dirichlet characters of modulus 200 over -Cyclotomic Field of order 20 and degree 8 +Group of Dirichlet characters modulo 200 with values in Cyclotomic Field of order 20 and degree 8 sage: G.exponent() 20 sage: G.gens() @@ -338,18 +337,17 @@ sage: G = DirichletGroup(20) sage: G.galois_orbits() [ -[Dirichlet character modulo 20 of conductor 1 mapping 11 |--> 1, 17 |--> 1], -[Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> zeta4, -Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> -zeta4], -[Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> -1], +[Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -1], +[Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -zeta4, + Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> zeta4], [Dirichlet character modulo 20 of conductor 4 mapping 11 |--> -1, 17 |--> 1], -[Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> zeta4, -Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -zeta4], -[Dirichlet character modulo 20 of conductor 20 mapping 11 |--> -1, 17 |--> -1] +[Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> -1], +[Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> -zeta4, + Dirichlet character modulo 20 of conductor 5 mapping 11 |--> 1, 17 |--> zeta4], +[Dirichlet character modulo 20 of conductor 1 mapping 11 |--> 1, 17 |--> 1] ] sage: G = DirichletGroup(11, QQ); G -Group of Dirichlet characters of modulus 11 over -Rational Field +Group of Dirichlet characters modulo 11 with values in Rational Field sage: list(G) [Dirichlet character modulo 11 of conductor 1 mapping 2 |--> 1, Dirichlet character modulo 11 of conductor 11 mapping 2 |--> -1] @@ -369,8 +367,7 @@ Cyclotomic Field of order 4 and degree 2 sage: G = DirichletGroup(15, R) sage: G -Group of Dirichlet characters of modulus 15 over -Cyclotomic Field of order 4 and degree 2 +Group of Dirichlet characters modulo 15 with values in Cyclotomic Field of order 4 and degree 2 sage: list(G) [Dirichlet character modulo 15 of conductor 1 mapping 11 |--> 1, 7 |--> 1, Dirichlet character modulo 15 of conductor 3 mapping 11 |--> -1, 7 |--> 1, @@ -391,8 +388,7 @@ [0, 1, zeta4, 0, -1, 0, 0, zeta4, -zeta4, 0, 0, 1, 0, -zeta4, -1] sage: G = DirichletGroup(15, GF(5)); G -Group of Dirichlet characters of modulus 15 - over Finite Field of size 5 +Group of Dirichlet characters modulo 15 with values in Finite Field of size 5 sage: list(G) [Dirichlet character modulo 15 of conductor 1 mapping 11 |--> 1, 7 |--> 1, Dirichlet character modulo 15 of conductor 3 mapping 11 |--> 4, 7 |--> 1, @@ -515,16 +511,14 @@ 2 with sign 0 over Rational Field sage: G = DirichletGroup(13) sage: G -Group of Dirichlet characters of modulus 13 over -Cyclotomic Field of order 12 and degree 4 +Group of Dirichlet characters modulo 13 with values in Cyclotomic Field of order 12 and degree 4 sage: dimension_modular_forms(Gamma1(13),2) 13 sage: [dimension_modular_forms(e,2) for e in G] [1, 0, 3, 0, 2, 0, 2, 0, 2, 0, 3, 0] sage: G = DirichletGroup(100) sage: G -Group of Dirichlet characters of modulus 100 over -Cyclotomic Field of order 20 and degree 8 +Group of Dirichlet characters modulo 100 with values in Cyclotomic Field of order 20 and degree 8 sage: dimension_modular_forms(Gamma1(100),2) 370 sage: v = [dimension_modular_forms(e,2) for e in G]; v diff --git a/src/sage/tests/gap_packages.py b/src/sage/tests/gap_packages.py index b0961c78421..05372c3b470 100644 --- a/src/sage/tests/gap_packages.py +++ b/src/sage/tests/gap_packages.py @@ -6,9 +6,8 @@ sage: from sage.tests.gap_packages import all_installed_packages, test_packages sage: pkgs = all_installed_packages(ignore_dot_gap=True) sage: test_packages(pkgs, only_failures=True) # optional - gap_packages - Status Package GAP Output - +---------+----------+------------+ - Failure HAPcryst fail + Status Package GAP Output + +--------+---------+------------+ These are packages in the ``database_gap`` package:: diff --git a/src/sage/typeset/unicode_art.py b/src/sage/typeset/unicode_art.py index a086ae7eb50..29a374362fa 100644 --- a/src/sage/typeset/unicode_art.py +++ b/src/sage/typeset/unicode_art.py @@ -86,10 +86,9 @@ def unicode_art(*obj, **kwds): sage: unicode_art(integral(exp(sqrt(x))/(x+pi), x)) ⌠ - ⎮ ___ - ⎮ ╲╱ x - ⎮ ℯ - ⎮ ────── dx + ⎮ √x + ⎮ ℯ + ⎮ ───── dx ⎮ x + π ⌡ sage: ident = lambda n: identity_matrix(ZZ, n) diff --git a/src/sage/version.py b/src/sage/version.py index 48c68fdd0a9..3d2e654790c 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.1.rc0' -date = '2016-03-10' +version = '7.2.beta6' +date = '2016-04-28' diff --git a/src/sage_setup/autogen/interpreters.py b/src/sage_setup/autogen/interpreters.py index 17cd24f7fa7..91ff7718b25 100644 --- a/src/sage_setup/autogen/interpreters.py +++ b/src/sage_setup/autogen/interpreters.py @@ -475,7 +475,7 @@ def alloc_chunk_data(self, name, len): sage: from sage_setup.autogen.interpreters import * sage: print(ty_mpfr.alloc_chunk_data('args', 'MY_LENGTH')) self._n_args = MY_LENGTH - self._args = sage_malloc(sizeof(mpfr_t) * MY_LENGTH) + self._args = sig_malloc(sizeof(mpfr_t) * MY_LENGTH) if self._args == NULL: raise MemoryError for i in range(MY_LENGTH): mpfr_init2(self._args[i], self.domain.prec()) @@ -483,7 +483,7 @@ def alloc_chunk_data(self, name, len): """ return je(""" self._n_{{ name }} = {{ len }} - self._{{ name }} = <{{ myself.c_ptr_type() }}>sage_malloc(sizeof({{ myself.c_decl_type() }}) * {{ len }}) + self._{{ name }} = <{{ myself.c_ptr_type() }}>sig_malloc(sizeof({{ myself.c_decl_type() }}) * {{ len }}) if self._{{ name }} == NULL: raise MemoryError {% if myself.needs_cython_init_clear() %} for i in range({{ len }}): @@ -502,13 +502,13 @@ def dealloc_chunk_data(self, name): sage: from sage_setup.autogen.interpreters import * sage: print(ty_double.dealloc_chunk_data('args')) if self._args: - sage_free(self._args) + sig_free(self._args) sage: print(ty_mpfr.dealloc_chunk_data('constants')) if self._constants: for i in range(self._n_constants): mpfr_clear(self._constants[i]) - sage_free(self._constants) + sig_free(self._constants) """ return je(""" @@ -517,7 +517,7 @@ def dealloc_chunk_data(self, name): for i in range(self._n_{{ name }}): {{ myself.cython_clear('self._%s[i]' % name) }} {% endif %} - sage_free(self._{{ name }}) + sig_free(self._{{ name }}) """, myself=self, name=name) class StorageTypeAssignable(StorageType): @@ -1093,7 +1093,7 @@ class members. sage: print(mc.init_class_members()) count = args['args'] self._n_args = count - self._args = sage_malloc(sizeof(mpfr_t) * count) + self._args = sig_malloc(sizeof(mpfr_t) * count) if self._args == NULL: raise MemoryError for i in range(count): mpfr_init2(self._args[i], self.domain.prec()) @@ -1115,7 +1115,7 @@ class members. if self._args: for i in range(self._n_args): mpfr_clear(self._args[i]) - sage_free(self._args) + sig_free(self._args) """ return "" @@ -1270,7 +1270,7 @@ class members. sage: print(mc.init_class_members()) count = args['args'] self._n_args = count - self._args = sage_malloc(sizeof(double) * count) + self._args = sig_malloc(sizeof(double) * count) if self._args == NULL: raise MemoryError """ @@ -1293,7 +1293,7 @@ class members. if self._args: for i in range(self._n_args): mpfr_clear(self._args[i]) - sage_free(self._args) + sig_free(self._args) """ return self.storage_type.dealloc_chunk_data(self.name) @@ -1333,7 +1333,7 @@ class members. sage: print(mc.init_class_members()) val = args['constants'] self._n_constants = len(val) - self._constants = sage_malloc(sizeof(mpfr_t) * len(val)) + self._constants = sig_malloc(sizeof(mpfr_t) * len(val)) if self._constants == NULL: raise MemoryError for i in range(len(val)): mpfr_init2(self._constants[i], self.domain.prec()) @@ -3268,7 +3268,7 @@ def write_wrapper(self, write): # distutils: sources = sage/ext/interpreters/interp_{{ s.name }}.c {{ s.pyx_header }} -include "sage/ext/stdsage.pxi" +include "cysignals/memory.pxi" from cpython.ref cimport PyObject cdef extern from "Python.h": void Py_DECREF(PyObject *o) @@ -3580,7 +3580,7 @@ def get_wrapper(self): sage: print(rdf_wrapper) # Automatically generated by ... - include "sage/ext/stdsage.pxi" + include "cysignals/memory.pxi" from cpython.ref cimport PyObject cdef extern from "Python.h": void Py_DECREF(PyObject *o) @@ -3652,7 +3652,7 @@ def __init__(self, args): # ... val = args['constants'] self._n_constants = len(val) - self._constants = sage_malloc(sizeof(double) * len(val)) + self._constants = sig_malloc(sizeof(double) * len(val)) if self._constants == NULL: raise MemoryError for i in range(len(val)): self._constants[i] = val[i] @@ -3670,7 +3670,7 @@ def __init__(self, args): ... val = args['constants'] self._n_constants = len(val) - self._constants = sage_malloc(sizeof(mpfr_t) * len(val)) + self._constants = sig_malloc(sizeof(mpfr_t) * len(val)) if self._constants == NULL: raise MemoryError for i in range(len(val)): mpfr_init2(self._constants[i], self.domain.prec()) @@ -3701,7 +3701,7 @@ def __init__(self, args): def __dealloc__(self): ... if self._constants: - sage_free(self._constants) + sig_free(self._constants) ... The RRInterpreter code is more complicated again because it has @@ -3715,7 +3715,7 @@ def __dealloc__(self): if self._constants: for i in range(self._n_constants): mpfr_clear(self._constants[i]) - sage_free(self._constants) + sig_free(self._constants) ... But the ElementInterpreter code is extremely simple -- diff --git a/src/sage_setup/autogen/pari/args.py b/src/sage_setup/autogen/pari/args.py index f7e2a29eb6d..aff465b1f60 100644 --- a/src/sage_setup/autogen/pari/args.py +++ b/src/sage_setup/autogen/pari/args.py @@ -38,7 +38,18 @@ def __init__(self, namesiter, default, index): start counting at 1. """ self.index = index - self.name = self.get_argument_name(namesiter) + try: + self.name = self.get_argument_name(namesiter) + except StopIteration: + # No more names available, use something default. + # This is used in listcreate() and polsturm() for example + # which have deprecated arguments which are not listed in + # the help. + self.name = "_arg%s" % index + self.undocumented = True + else: + self.undocumented = False + if self.index == 0: # "self" argument can never have a default self.default = None elif default is None: @@ -80,17 +91,11 @@ def get_argument_name(self, namesiter): Return the name for this argument, given ``namesiter`` which is an iterator over the argument names given by the help string. """ + n = next(namesiter) try: - n = next(namesiter) - try: - return replacements[n] - except KeyError: - return n - except StopIteration: - # No more names available, use something default. - # This is used in listcreate() for example which has a - # deprecated argument which is not listed in the help. - return "_arg%s" % self.index + return replacements[n] + except KeyError: + return n def prototype_code(self): """ @@ -98,6 +103,19 @@ def prototype_code(self): """ raise NotImplementedError + def deprecation_warning_code(self, function): + """ + Return code to appear in the function body to give a + deprecation warning for this argument, if applicable. + ``function`` is the function name to appear in the message. + """ + if not self.undocumented: + return "" + s = " if {name} is not None:\n" + s += " from warnings import warn\n" + s += " warn('argument {index} of the PARI/GP function {function} is undocumented and deprecated', DeprecationWarning)\n" + return s.format(name=self.name, index=self.index, function=function) + def convert_code(self): """ Return code to appear in the function body to convert this @@ -285,6 +303,7 @@ def convert_code(self): pari_arg_types = { 'G': PariArgumentGEN, + 'W': PariArgumentGEN, 'r': PariArgumentString, 's': PariArgumentString, 'L': PariArgumentLong, @@ -297,7 +316,6 @@ def convert_code(self): # Codes which are known but not actually supported for Sage '&': None, 'V': None, - 'W': None, 'I': None, 'E': None, 'J': None, diff --git a/src/sage_setup/autogen/pari/generator.py b/src/sage_setup/autogen/pari/generator.py index 9b78d88d77a..6212fa4a01e 100644 --- a/src/sage_setup/autogen/pari/generator.py +++ b/src/sage_setup/autogen/pari/generator.py @@ -128,7 +128,7 @@ def bnfinit(P, long flag=0, tech=None, long precision=0): tech = objtogen(tech) _tech = (tech).g precision = prec_bits_to_words(precision) - pari_catch_sig_on() + sig_on() cdef GEN _ret = bnfinit0(_P, flag, _tech, precision) return pari_instance.new_gen(_ret) @@ -146,7 +146,7 @@ def ellmodulareqn(self, long N, x=None, y=None): cdef long _y = -1 if y is not None: _y = pari_instance.get_var(y) - pari_catch_sig_on() + sig_on() cdef GEN _ret = ellmodulareqn(N, _x, _y) return pari_instance.new_gen(_ret) @@ -164,7 +164,7 @@ def setrand(n): :literal:`setrand(1)` twice will generate the exact same output. ... cdef GEN _n = n.g - pari_catch_sig_on() + sig_on() setrand(_n) pari_instance.clear_stack() @@ -216,9 +216,11 @@ def write_method(self, function, cname, args, ret, cargs, file, doc): s = " def {function}({protoargs}):\n" s += ' r"""\n {doc}\n """\n' + for a in args: + s += a.deprecation_warning_code(function) for a in args: s += a.convert_code() - s += " pari_catch_sig_on()\n" + s += " sig_on()\n" s += ret.assign_code("{cname}({callargs})") s += ret.return_code() diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index 9203ec21fce..069c0a9123e 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -767,7 +767,7 @@ class Foo(object): env_pickle = os.path.join(self._doctrees_dir(), 'environment.pickle') try: - env = BuildEnvironment.frompickle(config, env_pickle) + env = BuildEnvironment.frompickle(self.dir, config, env_pickle) logger.debug("Opened Sphinx environment: %s", env_pickle) return env except IOError as err: @@ -1620,7 +1620,7 @@ def main(): # Delete empty directories. This is needed in particular for empty # directories due to "git checkout" which never deletes empty # directories it leaves behind. See Trac #20010. - delete_empty_directories(SAGE_DOC) + delete_empty_directories(SAGE_DOC_SRC) # Set up Intersphinx cache C = IntersphinxCache() diff --git a/src/sage_setup/docbuild/ext/multidocs.py b/src/sage_setup/docbuild/ext/multidocs.py index 5d23a17b8c8..2f83dc427e7 100644 --- a/src/sage_setup/docbuild/ext/multidocs.py +++ b/src/sage_setup/docbuild/ext/multidocs.py @@ -34,6 +34,7 @@ def merge_environment(app, env): """ Merges the following attributes of the sub-docs environment into the main environment: + - titles # Titles - todo_all_todos # ToDo's - indexentries # global python index - all_docs # needed by the js index @@ -53,35 +54,37 @@ def merge_environment(app, env): len(docenv.citations) ), nonl=1) + # merge titles + for t in docenv.titles: + env.titles[fixpath(t)] = docenv.titles[t] # merge the todo links for dct in docenv.todo_all_todos: - dct['docname']=fixpath(dct['docname']) + dct['docname'] = fixpath(dct['docname']) env.todo_all_todos += docenv.todo_all_todos # merge the html index links newindex = {} for ind in docenv.indexentries: if ind.startswith('sage/'): - newind = fixpath(ind) - newindex[newind] = docenv.indexentries[ind] + newindex[fixpath(ind)] = docenv.indexentries[ind] else: newindex[ind] = docenv.indexentries[ind] env.indexentries.update(newindex) # merge the all_docs links, needed by the js index newalldoc = {} for ind in docenv.all_docs: - newalldoc[fixpath(ind)]=docenv.all_docs[ind] + newalldoc[fixpath(ind)] = docenv.all_docs[ind] env.all_docs.update(newalldoc) # needed by env.check_consistency (sphinx.environement, line 1734) for ind in newalldoc: # treat subdocument source as orphaned file and don't complain - md = env.metadata.get(ind, set()) - md.add('orphan') + md = env.metadata.get(ind, dict()) + md['orphan'] = 1 env.metadata[ind] = md # merge the citations newcite = {} for ind, (path, tag) in docenv.citations.iteritems(): # TODO: Warn on conflicts - newcite[ind]=(fixpath(path), tag) + newcite[ind] = (fixpath(path), tag) env.citations.update(newcite) # merge the py:module indexes newmodules = {} diff --git a/src/sage_setup/docbuild/ext/sage_autodoc.py b/src/sage_setup/docbuild/ext/sage_autodoc.py index 54da7dcd4bc..b26c47aeee3 100644 --- a/src/sage_setup/docbuild/ext/sage_autodoc.py +++ b/src/sage_setup/docbuild/ext/sage_autodoc.py @@ -9,7 +9,7 @@ the doctree, thus avoiding duplication between docstrings and documentation for those who like elaborate docstrings. - :copyright: Copyright 2007-2014 by the Sphinx team, see AUTHORS. + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. AUTHORS: @@ -27,10 +27,13 @@ import traceback from types import FunctionType, BuiltinFunctionType, MethodType +from six import PY2, iterkeys, iteritems, itervalues, text_type, class_types, \ + string_types, StringIO from docutils import nodes from docutils.utils import assemble_option_dict from docutils.statemachine import ViewList +import sphinx from sphinx.util import rpartition, force_decode from sphinx.locale import _ from sphinx.pycode import ModuleAnalyzer, PycodeError @@ -38,14 +41,14 @@ from sphinx.util.nodes import nested_parse_with_titles from sphinx.util.compat import Directive from sphinx.util.inspect import getargspec, isdescriptor, safe_getmembers, \ - safe_getattr, safe_repr, is_builtin_class_method -from sphinx.util.pycompat import base_exception, class_types + safe_getattr, object_description, is_builtin_class_method from sphinx.util.docstrings import prepare_docstring 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 :: py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name @@ -62,6 +65,7 @@ class DefDict(dict): def __init__(self, default): dict.__init__(self) self.default = default + def __getitem__(self, key): try: return dict.__getitem__(self, key) @@ -71,7 +75,9 @@ def __nonzero__(self): # docutils check "if option_spec" return True -identity = lambda x: x + +def identity(x): + return x class Options(dict): @@ -84,6 +90,8 @@ def __getattr__(self, name): ALL = object() +INSTANCEATTR = object() + def members_option(arg): """Used to convert the :members: option to auto directives.""" @@ -91,15 +99,28 @@ def members_option(arg): return ALL return [x.strip() for x in arg.split(',')] + def members_set_option(arg): """Used to convert the :members: option to auto directives.""" if arg is None: return ALL return set(x.strip() for x in arg.split(',')) +SUPPRESS = object() + + +def annotation_option(arg): + if arg is None: + # suppress showing the representation of the object + return SUPPRESS + else: + return arg + + def bool_option(arg): """Used to convert flag options to auto directives. (Instead of - directives.flag(), which returns None.)""" + directives.flag(), which returns None). + """ return True @@ -172,6 +193,7 @@ def process(app, what_, name, obj, options, lines): lines.append('') return process + def between(marker, what=None, keepempty=False, exclude=False): """Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line @@ -182,6 +204,7 @@ def between(marker, what=None, keepempty=False, exclude=False): be processed. """ marker_re = re.compile(marker) + def process(app, what_, name, obj, options, lines): if what and what_ not in what: return @@ -229,6 +252,8 @@ class Documenter(object): priority = 0 #: order if autodoc_member_order is set to 'groupwise' member_order = 0 + #: true if the generated content may contain titles + titles_allowed = False option_spec = {'noindex': bool_option} @@ -263,6 +288,9 @@ def __init__(self, directive, name, indent=u''): self.retann = None # the object to document (set after import_object succeeds) self.object = None + self.object_name = None + # the parent/owner of the object to document + self.parent = None # the module analyzer to get at attribute docs, or None self.analyzer = None @@ -291,7 +319,7 @@ def parse_name(self): # an autogenerated one try: explicit_modname, path, base, args, retann = \ - py_ext_sig_re.match(self.name).groups() + py_ext_sig_re.match(self.name).groups() except AttributeError: self.directive.warn('invalid signature for auto%s (%r)' % (self.objtype, self.name)) @@ -306,7 +334,7 @@ def parse_name(self): parents = [] self.modname, self.objpath = \ - self.resolve_name(modname, parents, path, base) + self.resolve_name(modname, parents, path, base) if not self.modname: return False @@ -323,18 +351,39 @@ def import_object(self): Returns True if successful, False if an error occurred. """ + dbg = self.env.app.debug + if self.objpath: + dbg('[autodoc] from %s import %s', + self.modname, '.'.join(self.objpath)) try: + dbg('[autodoc] import %s', self.modname) __import__(self.modname) + parent = None obj = self.module = sys.modules[self.modname] + dbg('[autodoc] => %r', obj) for part in self.objpath: + parent = obj + dbg('[autodoc] getattr(_, %r)', part) obj = self.get_attr(obj, part) + dbg('[autodoc] => %r', obj) + self.object_name = part + self.parent = parent self.object = obj return True - except (SyntaxError, ImportError, AttributeError) as err: - self.directive.warn( - 'autodoc can\'t import/find %s %r, it reported error: ' - '"%s", please check your spelling and sys.path' % - (self.objtype, str(self.fullname), err)) + # this used to only catch SyntaxError, ImportError and AttributeError, + # but importing modules with side effects can raise all kinds of errors + except Exception: + if self.objpath: + errmsg = 'autodoc: failed to import %s %r from module %r' % \ + (self.objtype, '.'.join(self.objpath), self.modname) + else: + errmsg = 'autodoc: failed to import %s %r' % \ + (self.objtype, self.fullname) + errmsg += '; the following exception was raised:\n%s' % \ + traceback.format_exc() + dbg(errmsg) + self.directive.warn(errmsg) + self.env.note_reread() return False def get_real_modname(self): @@ -346,10 +395,12 @@ def get_real_modname(self): return self.get_attr(self.object, '__module__', None) or self.modname def check_module(self): - """ - Check if *self.object* is really defined in the module given by + """Check if *self.object* is really defined in the module given by *self.modname*. """ + if self.options.imported_members: + return True + modname = self.get_attr(self.object, '__module__', None) if modname and modname != self.modname: return False @@ -383,9 +434,13 @@ def format_signature(self): args = "(%s)" % self.args else: # try to introspect the signature - args = self.format_args() - if args is None: - return '' + try: + args = self.format_args() + except Exception as err: + self.directive.warn('error while formatting arguments for ' + '%s: %s' % (self.fullname, err)) + args = None + retann = self.retann result = self.env.app.emit_firstresult( @@ -401,11 +456,10 @@ def format_signature(self): def add_directive_header(self, sig): """Add the directive header and options to the generated content.""" + domain = getattr(self, 'domain', 'py') directive = getattr(self, 'directivetype', self.objtype) - # the name to put into the generated directive -- doesn't contain - # the module (except for module directive of course) - name_in_directive = '.'.join(self.objpath) or self.modname - self.add_line(u'.. %s:: %s%s' % (directive, name_in_directive, sig), + name = self.format_name() + self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig), '') if self.options.noindex: self.add_line(u' :noindex:', '') @@ -414,13 +468,17 @@ def add_directive_header(self, sig): # etc. don't support a prepended module name self.add_line(u' :module: %s' % self.modname, '') - def get_doc(self, encoding=None): + def get_doc(self, encoding=None, ignore=1): """Decode and return lines of the docstring(s) for the object.""" docstring = sage_getdoc_original(self.object) - if docstring: - # make sure we have Unicode docstrings, then sanitize and split - # into lines - return [prepare_docstring(force_decode(docstring, encoding))] + # make sure we have Unicode docstrings, then sanitize and split + # into lines + if isinstance(docstring, unicode): + return [prepare_docstring(docstring, ignore)] + elif isinstance(docstring, str): # this will not trigger on Py3 + return [prepare_docstring(force_decode(docstring, encoding), + ignore)] + # ... else it is something strange, let's ignore it return [] def process_doc(self, docstrings): @@ -481,38 +539,60 @@ def get_object_members(self, want_all): If *want_all* is True, return all members. Else, only return those members given by *self.options.members* (which may also be none). """ + analyzed_member_names = set() + if self.analyzer: + attr_docs = self.analyzer.find_attr_docs() + namespace = '.'.join(self.objpath) + for item in attr_docs.iteritems(): + if item[0][0] == namespace: + analyzed_member_names.add(item[0][1]) if not want_all: if not self.options.members: return False, [] # specific members given - ret = [] + members = [] for mname in self.options.members: try: - ret.append((mname, self.get_attr(self.object, mname))) + members.append((mname, self.get_attr(self.object, mname))) except AttributeError: - self.directive.warn('missing attribute %s in object %s' - % (mname, self.fullname)) - return False, ret + if mname not in analyzed_member_names: + self.directive.warn('missing attribute %s in object %s' + % (mname, self.fullname)) elif self.options.inherited_members: # safe_getmembers() uses dir() which pulls in members from all # base classes - return False, safe_getmembers(self.object) + members = safe_getmembers(self.object, attr_getter=self.get_attr) else: # __dict__ contains only the members directly defined in # the class (but get them via getattr anyway, to e.g. get # unbound method objects instead of function objects); # using keys() because apparently there are objects for which # __dict__ changes while getting attributes - return False, sorted([ - (mname, self.get_attr(self.object, mname, None)) - for mname in self.get_attr(self.object, '__dict__').keys()]) + try: + obj_dict = self.get_attr(self.object, '__dict__') + except AttributeError: + members = [] + else: + members = [(mname, self.get_attr(self.object, mname, None)) + for mname in obj_dict.keys()] + membernames = set(m[0] for m in members) + # add instance attributes from the analyzer + for aname in analyzed_member_names: + if aname not in membernames and \ + (want_all or aname in self.options.members): + members.append((aname, INSTANCEATTR)) + return False, sorted(members) def filter_members(self, members, want_all): - """ - Filter the given member list: members are skipped if + """Filter the given member list. + + Members are skipped if - - they are private (except if given explicitly) - - they are undocumented (except if undoc-members is given) + - they are private (except if given explicitly or the private-members + option is set) + - they are special methods (except if given explicitly or the + special-members option is set) + - they are undocumented (except if the undoc-members option is set) The user can override the skipping decision by connecting to the ``autodoc-skip-member`` event. @@ -537,18 +617,38 @@ def filter_members(self, members, want_all): # if isattr is True, the member is documented as an attribute isattr = False - if want_all and membername.startswith('_'): + doc = self.get_attr(member, '__doc__', None) + # if the member __doc__ is the same as self's __doc__, it's just + # inherited and therefore not the member's doc + cls = self.get_attr(member, '__class__', None) + if cls: + cls_doc = self.get_attr(cls, '__doc__', None) + if cls_doc == doc: + doc = None + has_doc = bool(doc) + + keep = False + if want_all and membername.startswith('__') and \ + membername.endswith('__') and len(membername) > 4: + # special __methods__ + if self.options.special_members is ALL and \ + membername != '__doc__': + keep = has_doc or self.options.undoc_members + elif self.options.special_members and \ + self.options.special_members is not ALL and \ + membername in self.options.special_members: + keep = has_doc or self.options.undoc_members + elif want_all and membername.startswith('_'): # ignore members whose name starts with _ by default - skip = True + keep = self.options.private_members and \ + (has_doc or self.options.undoc_members) elif (namespace, membername) in attr_docs: # keep documented attributes - skip = False + keep = True isattr = True else: - # ignore undocumented members if :undoc-members: - # is not given - doc = sage_getdoc_original(member) - skip = not self.options.undoc_members and not doc + # ignore undocumented members if :undoc-members: is not given + keep = has_doc or self.options.undoc_members # give the user a chance to decide whether this member # should be skipped @@ -556,13 +656,12 @@ def filter_members(self, members, want_all): # let extensions preprocess docstrings skip_user = self.env.app.emit_firstresult( 'autodoc-skip-member', self.objtype, membername, member, - skip, self.options) + not keep, self.options) if skip_user is not None: - skip = skip_user - if skip: - continue + keep = not skip_user - ret.append((membername, member, isattr)) + if keep: + ret.append((membername, member, isattr)) return ret @@ -573,12 +672,12 @@ def document_members(self, all_members=False): *self.options.members*. """ # set current namespace for finding members - self.env.autodoc_current_module = self.modname + self.env.temp_data['autodoc:module'] = self.modname if self.objpath: - self.env.autodoc_current_class = self.objpath[0] + self.env.temp_data['autodoc:class'] = self.objpath[0] want_all = all_members or self.options.inherited_members or \ - self.options.members is ALL + self.options.members is ALL # find out which members are documentable members_check_module, members = self.get_object_members(want_all) @@ -600,24 +699,32 @@ def document_members(self, all_members=False): # give explicitly separated module name, so that members # of inner classes can be documented full_mname = self.modname + '::' + \ - '.'.join(self.objpath + [mname]) - memberdocumenters.append( - classes[-1](self.directive, full_mname, self.indent)) - - if (self.options.member_order or self.env.config.autodoc_member_order) \ - == 'groupwise': + '.'.join(self.objpath + [mname]) + documenter = classes[-1](self.directive, full_mname, self.indent) + memberdocumenters.append((documenter, isattr)) + member_order = self.options.member_order or \ + self.env.config.autodoc_member_order + if member_order == 'groupwise': # sort by group; relies on stable sort to keep items in the # same group sorted alphabetically - memberdocumenters.sort(key=lambda d: d.member_order) + memberdocumenters.sort(key=lambda e: e[0].member_order) + elif member_order == 'bysource' and self.analyzer: + # sort by source order, by virtue of the module analyzer + tagorder = self.analyzer.tagorder + + def keyfunc(entry): + fullname = entry[0].name.split('::')[1] + return tagorder.get(fullname, len(tagorder)) + memberdocumenters.sort(key=keyfunc) - for documenter in memberdocumenters: - documenter.generate(all_members=True, - real_modname=self.real_modname, - check_module=members_check_module) + for documenter, isattr in memberdocumenters: + documenter.generate( + all_members=True, real_modname=self.real_modname, + check_module=members_check_module and not isattr) # reset current objects - self.env.autodoc_current_module = None - self.env.autodoc_current_class = None + self.env.temp_data['autodoc:module'] = None + self.env.temp_data['autodoc:class'] = None def generate(self, more_content=None, real_modname=None, check_module=False, all_members=False): @@ -655,7 +762,7 @@ def generate(self, more_content=None, real_modname=None, # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() - except PycodeError, err: + except PycodeError as err: self.env.app.debug('[autodoc] module analyzer failed: %s', err) # no source file -- e.g. for builtin and C modules self.analyzer = None @@ -703,6 +810,7 @@ class ModuleDocumenter(Documenter): """ objtype = 'module' content_indent = u'' + titles_allowed = True option_spec = { 'members': members_option, 'undoc-members': bool_option, @@ -710,6 +818,8 @@ class ModuleDocumenter(Documenter): 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, 'member-order': identity, 'exclude-members': members_set_option, + 'private-members': bool_option, 'special-members': members_option, + 'imported-members': bool_option, } @classmethod @@ -760,7 +870,7 @@ def get_object_members(self, want_all): self.directive.warn( 'missing attribute mentioned in :members: or __all__: ' 'module %s, attribute %s' % ( - safe_getattr(self.object, '__name__', '???'), mname)) + safe_getattr(self.object, '__name__', '???'), mname)) return False, ret @@ -776,11 +886,10 @@ def resolve_name(self, modname, parents, path, base): else: # if documenting a toplevel object without explicit module, # it can be contained in another auto directive ... - if hasattr(self.env, 'autodoc_current_module'): - modname = self.env.autodoc_current_module + modname = self.env.temp_data.get('autodoc:module') # ... or in the scope of a module directive if not modname: - modname = self.env.currmodule + modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid return modname, parents + [base] @@ -799,26 +908,74 @@ def resolve_name(self, modname, parents, path, base): # if documenting a class-level object without path, # there must be a current class, either from a parent # auto directive ... - if hasattr(self.env, 'autodoc_current_class'): - mod_cls = self.env.autodoc_current_class + mod_cls = self.env.temp_data.get('autodoc:class') # ... or from a class directive if mod_cls is None: - mod_cls = self.env.currclass + mod_cls = self.env.ref_context.get('py:class') # ... if still None, there's no way to know if mod_cls is None: return None, [] modname, cls = rpartition(mod_cls, '.') parents = [cls] # if the module name is still missing, get it like above - if not modname and hasattr(self.env, 'autodoc_current_module'): - modname = self.env.autodoc_current_module if not modname: - modname = self.env.currmodule + modname = self.env.temp_data.get('autodoc:module') + if not modname: + modname = self.env.ref_context.get('py:module') # ... else, it stays None, which means invalid return modname, parents + [base] -class FunctionDocumenter(ModuleLevelDocumenter): +class DocstringSignatureMixin(object): + """ + Mixin for FunctionDocumenter and MethodDocumenter to provide the + feature of reading the signature from the docstring. + """ + + def _find_signature(self, encoding=None): + docstrings = Documenter.get_doc(self, encoding) + if len(docstrings) != 1: + return + doclines = docstrings[0] + setattr(self, '__new_doclines', doclines) + if not doclines: + return + # match first line of docstring against signature RE + match = py_ext_sig_re.match(doclines[0]) + if not match: + return + exmod, path, base, args, retann = match.groups() + # the base name must match ours + if not self.objpath or base != self.objpath[-1]: + return + # re-prepare docstring to ignore indentation after signature + docstrings = Documenter.get_doc(self, encoding, 2) + doclines = docstrings[0] + # ok, now jump over remaining empty lines and set the remaining + # lines as the new doclines + i = 1 + while i < len(doclines) and not doclines[i].strip(): + i += 1 + setattr(self, '__new_doclines', doclines[i:]) + return args, retann + + def get_doc(self, encoding=None, ignore=1): + lines = getattr(self, '__new_doclines', None) + if lines is not None: + return [lines] + return Documenter.get_doc(self, encoding, ignore) + + def format_signature(self): + if self.args is None and self.env.config.autodoc_docstring_signature: + # only act if a signature is not explicitly given already, and if + # the feature is enabled + result = self._find_signature() + if result is not None: + self.args, self.retann = result + return Documenter.format_signature(self) + + +class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for functions. """ @@ -887,6 +1044,7 @@ class ClassDocumenter(ModuleLevelDocumenter): 'noindex': bool_option, 'inherited-members': bool_option, 'show-inheritance': bool_option, 'member-order': identity, 'exclude-members': members_set_option, + 'private-members': bool_option, 'special-members': members_option, } @classmethod @@ -952,10 +1110,16 @@ def format_args(self): initmeth = self.get_attr(self.object, '__init__', None) # classes without __init__ method, default __init__ or # __init__ written in C? - if initmeth is None or initmeth is object.__init__ or not \ - (inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): + if initmeth is None or \ + is_builtin_class_method(self.object, '__init__') or \ + not(inspect.ismethod(initmeth) or inspect.isfunction(initmeth)): + return None + try: + argspec = sage_getargspec(initmeth) + except TypeError: + # still not possible: happens e.g. for old-style classes + # with __init__ in C return None - argspec = sage_getargspec(initmeth) if argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] return sage_formatargspec(*argspec) @@ -963,6 +1127,18 @@ def format_args(self): def format_signature(self): if self.doc_as_attr: return '' + + # get __init__ method signature from __init__.__doc__ + if self.env.config.autodoc_docstring_signature: + # only act if the feature is enabled + init_doc = MethodDocumenter(self.directive, '__init__') + init_doc.object = self.get_attr(self.object, '__init__', None) + init_doc.objpath = ['__init__'] + result = init_doc._find_signature() + if result is not None: + # use args only for Class signature + return '(%s)' % result[0] + return ModuleLevelDocumenter.format_signature(self) def add_directive_header(self, sig): @@ -981,7 +1157,7 @@ def add_directive_header(self, sig): self.add_line(_(u' Bases: %s') % ', '.join(bases), '') - def get_doc(self, encoding=None): + def get_doc(self, encoding=None, ignore=1): content = self.env.config.autoclass_content docstrings = [] @@ -992,10 +1168,22 @@ def get_doc(self, encoding=None): # for classes, what the "docstring" is can be controlled via a # config value; the default is only the class docstring if content in ('both', 'init'): - initdocstring = sage_getdoc_original( - self.get_attr(self.object, '__init__', None)) + # get __init__ method document from __init__.__doc__ + if self.env.config.autodoc_docstring_signature: + # only act if the feature is enabled + init_doc = MethodDocumenter(self.directive, '__init__') + init_doc.object = self.get_attr(self.object, '__init__', None) + init_doc.objpath = ['__init__'] + init_doc._find_signature() # this effects to get_doc() result + initdocstring = '\n'.join( + ['\n'.join(l) for l in init_doc.get_doc(encoding)]) + else: + initdocstring = sage_getdoc_original( + self.get_attr(self.object, '__init__', None)) # for new-style classes, no __init__ means default __init__ - if initdocstring == object.__init__.__doc__: + if (initdocstring is not None and + (initdocstring == object.__init__.__doc__ or # for pypy + initdocstring.strip() == object.__init__.__doc__)): #for !pypy initdocstring = None if initdocstring: if content == 'init': @@ -1039,7 +1227,7 @@ class ExceptionDocumenter(ClassDocumenter): @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(member, class_types) and \ - issubclass(member, base_exception) + issubclass(member, BaseException) class DataDocumenter(ModuleLevelDocumenter): @@ -1048,44 +1236,78 @@ class DataDocumenter(ModuleLevelDocumenter): """ objtype = 'data' member_order = 40 + priority = -10 + option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec["annotation"] = annotation_option @classmethod def can_document_member(cls, member, membername, isattr, parent): return isinstance(parent, ModuleDocumenter) and isattr + def add_directive_header(self, sig): + ModuleLevelDocumenter.add_directive_header(self, sig) + if not self.options.annotation: + try: + objrepr = object_description(self.object) + except ValueError: + pass + else: + self.add_line(u' :annotation: = ' + objrepr, '') + elif self.options.annotation is SUPPRESS: + pass + else: + self.add_line(u' :annotation: %s' % self.options.annotation, + '') + def document_members(self, all_members=False): pass -class MethodDocumenter(ClassLevelDocumenter): +class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' member_order = 50 + priority = 1 # must be more than FunctionDocumenter @classmethod def can_document_member(cls, member, membername, isattr, parent): return inspect.isroutine(member) and \ - not isinstance(parent, ModuleDocumenter) - - def import_object(self): - ret = ClassLevelDocumenter.import_object(self) - if isinstance(self.object, classmethod) or \ - (isinstance(self.object, MethodType) and - self.object.im_self is not None): - self.directivetype = 'classmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - elif isinstance(self.object, FunctionType) or \ - (isinstance(self.object, BuiltinFunctionType) and - self.object.__self__ is not None): - self.directivetype = 'staticmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' - return ret + not isinstance(parent, ModuleDocumenter) + + if sys.version_info >= (3, 0): + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + obj_from_parent = self.parent.__dict__.get(self.object_name) + if isinstance(obj_from_parent, classmethod): + self.directivetype = 'classmethod' + self.member_order = self.member_order - 1 + elif isinstance(obj_from_parent, staticmethod): + self.directivetype = 'staticmethod' + self.member_order = self.member_order - 1 + else: + self.directivetype = 'method' + return ret + else: + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + if isinstance(self.object, classmethod) or \ + (isinstance(self.object, MethodType) and + self.object.im_self is not None): + self.directivetype = 'classmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 + elif isinstance(self.object, FunctionType) or \ + (isinstance(self.object, BuiltinFunctionType) and + hasattr(self.object, '__self__') and + self.object.__self__ is not None): + self.directivetype = 'staticmethod' + # document class and static members before ordinary ones + self.member_order = self.member_order - 1 + else: + self.directivetype = 'method' + return ret # Sage Trac #9976: This function has been rewritten to support the @@ -1129,6 +1351,10 @@ class AttributeDocumenter(ClassLevelDocumenter): """ objtype = 'attribute' member_order = 60 + option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec["annotation"] = annotation_option + + method_types = (FunctionType, BuiltinFunctionType, MethodType) @classmethod def can_document_member(cls, member, membername, isattr, parent): @@ -1143,6 +1369,68 @@ def can_document_member(cls, member, membername, isattr, parent): def document_members(self, all_members=False): pass + def import_object(self): + ret = ClassLevelDocumenter.import_object(self) + if isdescriptor(self.object) and \ + not isinstance(self.object, self.method_types): + self._datadescriptor = True + else: + # if it's not a data descriptor + self._datadescriptor = False + return ret + + def get_real_modname(self): + return self.get_attr(self.parent or self.object, '__module__', None) \ + or self.modname + + def add_directive_header(self, sig): + ClassLevelDocumenter.add_directive_header(self, sig) + if not self.options.annotation: + if not self._datadescriptor: + try: + objrepr = object_description(self.object) + except ValueError: + pass + else: + self.add_line(u' :annotation: = ' + objrepr, '') + elif self.options.annotation is SUPPRESS: + pass + else: + self.add_line(u' :annotation: %s' % self.options.annotation, + '') + + def add_content(self, more_content, no_docstring=False): + ClassLevelDocumenter.add_content(self, more_content, no_docstring) + + +class InstanceAttributeDocumenter(AttributeDocumenter): + """ + Specialized Documenter subclass for attributes that cannot be imported + because they are instance attributes (e.g. assigned in __init__). + """ + objtype = 'instanceattribute' + directivetype = 'attribute' + member_order = 60 + + # must be higher than AttributeDocumenter + priority = 11 + + @classmethod + def can_document_member(cls, member, membername, isattr, parent): + """This documents only INSTANCEATTR members.""" + return isattr and (member is INSTANCEATTR) + + def import_object(self): + """Never import anything.""" + # disguise as an attribute + self.objtype = 'attribute' + self._datadescriptor = False + return True + + def add_content(self, more_content, no_docstring=False): + """Never try to get a docstring from the object.""" + AttributeDocumenter.add_content(self, more_content, no_docstring=True) + class AutoDirective(Directive): """ @@ -1165,6 +1453,12 @@ class AutoDirective(Directive): # a registry of type -> getattr function _special_attrgetters = {} + # flags that can be given in autodoc_default_flags + _default_flags = set([ + 'members', 'undoc-members', 'inherited-members', 'show-inheritance', + 'private-members', 'special-members', + ]) + # standard docutils directive settings has_content = True required_arguments = 1 @@ -1184,12 +1478,34 @@ def run(self): self.warnings = [] self.result = ViewList() + try: + source, lineno = self.reporter.get_source_and_line(self.lineno) + except AttributeError: + source = lineno = None + self.env.app.debug('[autodoc] %s:%s: input:\n%s', + source, lineno, self.block_text) + # find out what documenter to call objtype = self.name[4:] doc_class = self._registry[objtype] + # add default flags + for flag in self._default_flags: + if flag not in doc_class.option_spec: + continue + negated = self.options.pop('no-' + flag, 'not given') is None + if flag in self.env.config.autodoc_default_flags and \ + not negated: + self.options[flag] = None # process the options with the selected documenter's option_spec - self.genopt = Options(assemble_option_dict( - self.options.items(), doc_class.option_spec)) + try: + self.genopt = Options(assemble_option_dict( + self.options.items(), doc_class.option_spec)) + except (KeyError, ValueError, TypeError) as err: + # an option is either unknown or has a wrong type + msg = self.reporter.error('An option to %s is either unknown or ' + 'has an invalid value: %s' % (self.name, err), + line=self.lineno) + return [msg] # generate the output documenter = doc_class(self, self.arguments[0]) documenter.generate(more_content=self.content) @@ -1201,7 +1517,7 @@ def run(self): # record all filenames as dependencies -- this will at least # partially make automatic invalidation possible for fn in self.filename_set: - self.env.note_dependency(fn) + self.state.document.settings.record_dependencies.add(fn) # use a custom reporter that correctly assigns lines to source # filename/description and lineno @@ -1209,7 +1525,7 @@ def run(self): self.state.memo.reporter = AutodocReporter(self.result, self.state.memo.reporter) - if self.name == 'automodule': + if documenter.titles_allowed: node = nodes.section() # necessary so that the child nodes get the right source/line set node.document = self.state.document @@ -1228,7 +1544,7 @@ def add_documenter(cls): raise ExtensionError('autodoc documenter %r must be a subclass ' 'of Documenter' % cls) # actually, it should be possible to override Documenters - #if cls.objtype in AutoDirective._registry: + # if cls.objtype in AutoDirective._registry: # raise ExtensionError('autodoc documenter for %r is already ' # 'registered' % cls.objtype) AutoDirective._registry[cls.objtype] = cls @@ -1254,10 +1570,13 @@ def add_autodoc_attrgetter(type, getter): app.add_autodocumenter(FunctionDocumenter) app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(AttributeDocumenter) + app.add_autodocumenter(InstanceAttributeDocumenter) app.add_config_value('autoclass_content', 'class', True) app.add_config_value('autodoc_member_order', 'alphabetic', True) app.add_config_value('autodoc_builtin_argspec', None, True) + app.add_config_value('autodoc_default_flags', [], True) + app.add_config_value('autodoc_docstring_signature', False, True) app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') diff --git a/src/sage_setup/docbuild/sphinxbuild.py b/src/sage_setup/docbuild/sphinxbuild.py index f26133cdd9e..c68292ceb3c 100644 --- a/src/sage_setup/docbuild/sphinxbuild.py +++ b/src/sage_setup/docbuild/sphinxbuild.py @@ -52,7 +52,9 @@ def _init_chatter(self): 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'), + re.compile('WARNING: html_static_path entry .* does not exist'), + re.compile('WARNING: while setting up extension'), + re.compile('WARNING: Any IDs not assiend for figure node'), ) # replacements: pairs of regular expressions and their replacements, @@ -64,7 +66,7 @@ def _init_chatter(self): # 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: citation not found:'), re.compile('WARNING: search index couldn\'t be loaded, but not all documents will be built: the index will be incomplete.') ) @@ -95,7 +97,7 @@ def _filter_out(self, line): return True line = re.sub(self.ansi_color, '', line) for regex in self.useless_chatter: - if regex.match(line) is not None: + if regex.search(line) is not None: return True return False diff --git a/src/setup.py b/src/setup.py index cf0013689f4..a2e04e3940c 100755 --- a/src/setup.py +++ b/src/setup.py @@ -24,7 +24,7 @@ def excepthook(*exc): try: logfile = os.path.join(os.environ['SAGE_LOGS'], - "sage-%s.log" % os.environ['SAGE_VERSION']) + "sagelib-%s.log" % os.environ['SAGE_VERSION']) except: pass else: @@ -71,24 +71,10 @@ def excepthook(*exc): extra_compile_args = [ "-fno-strict-aliasing" ] extra_link_args = [ ] -# comment these four lines out to turn on warnings from gcc -import distutils.sysconfig -NO_WARN = True -if NO_WARN and distutils.sysconfig.get_config_var('CC').startswith("gcc"): - extra_compile_args.append('-w') - DEVEL = False if DEVEL: extra_compile_args.append('-ggdb') -# Work around GCC-4.8.0 bug which miscompiles some sig_on() statements, -# as witnessed by a doctest in sage/libs/gap/element.pyx if the -# compiler flag -Og is used. See also -# * http://trac.sagemath.org/sage_trac/ticket/14460 -# * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56982 -if subprocess.call("""$CC --version | grep -i 'gcc.* 4[.]8' >/dev/null """, shell=True) == 0: - extra_compile_args.append('-fno-tree-dominator-opts') - ######################################################### ### Testing related stuff #########################################################
  • Introduction to the p-adics