diff --git a/README.md b/README.md index e140caea73b..c5ca76a1fec 100644 --- a/README.md +++ b/README.md @@ -377,20 +377,10 @@ do. sage --sdist -2. To make a binary distribution with your currently installed packages, - type: + The result is placed in the directory "$SAGE_ROOT/dist/". - sage --bdist - -3. To make a binary that will run on the widest range of target - machines, set the SAGE_FAT_BINARY environment variable to "yes" - before building Sage: - - export SAGE_FAT_BINARY="yes" - make distclean && make - ./sage --bdist - -In all cases, the result is placed in the directory "$SAGE_ROOT/dist/". +2. To make a binary distribution with your currently installed packages, + visit https://github.com/sagemath/binary-pkg Changes to Included Software diff --git a/VERSION.txt b/VERSION.txt index 962601e185e..d2288b09f36 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 8.0.beta5, Release Date: 2017-05-04 +SageMath version 8.0.beta8, Release Date: 2017-05-24 diff --git a/build/bin/sage-spkg b/build/bin/sage-spkg index 5086e5e1fa4..c7e29d04fd5 100755 --- a/build/bin/sage-spkg +++ b/build/bin/sage-spkg @@ -749,6 +749,13 @@ else fi fi +if [ "$UNAME" = "CYGWIN" ]; then + # Rebase after installing each package--in case any packages load this + # package at build time we need to ensure during the build that no binaries + # have conflicting address spaces + sage-rebase.sh "$SAGE_LOCAL" 2>/dev/null +fi + echo "Successfully installed $PKG_NAME" if [ "$SAGE_CHECK" = "yes" ]; then diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 6816671ce21..df43fa28bbd 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=339f2474b789d33051dde8f69d047ae723a36379 -md5=9bb01c255b39648bb19f62a3fa24a80d -cksum=2072910001 +sha1=d19b80dca421a261fd1cf19be8c861aef46d403b +md5=5cfae5d5a32e87c9fff3215773b56dc8 +cksum=133013502 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 037ba971962..c200906efd2 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -219 +222 diff --git a/build/pkgs/conway_polynomials/dependencies b/build/pkgs/conway_polynomials/dependencies index 8c8b7765a8d..3e374be7f45 100644 --- a/build/pkgs/conway_polynomials/dependencies +++ b/build/pkgs/conway_polynomials/dependencies @@ -1,7 +1,4 @@ -| $(SAGERUNTIME) - -CONWAY_POLYNOMIALS depends on SAGERUNTIME because it runs Sage code to -generate a Sage object (.sobj). +$(PYTHON) $(inst_six) ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/conway_polynomials/spkg-install b/build/pkgs/conway_polynomials/spkg-install index 15ba836d6a9..b0fe157964c 100755 --- a/build/pkgs/conway_polynomials/spkg-install +++ b/build/pkgs/conway_polynomials/spkg-install @@ -1,11 +1,10 @@ -#!/usr/bin/env python +#!/usr/bin/env sage-python23 import os from six.moves import cPickle as pickle -from sage.env import SAGE_SHARE +SAGE_SHARE = os.getenv('SAGE_SHARE') install_root = os.path.join(SAGE_SHARE, 'conway_polynomials') - def create_db(): db = {} from src import conway_polynomials diff --git a/build/pkgs/coxeter3/patches/test.output.expected b/build/pkgs/coxeter3/patches/test.output.expected index 0a4fca16a11..d36e4b8c22d 100644 --- a/build/pkgs/coxeter3/patches/test.output.expected +++ b/build/pkgs/coxeter3/patches/test.output.expected @@ -1,4 +1,4 @@ -This is Coxeter version 3.0_beta2. +This is Coxeter version 3.1.sage. Enter help if you need assistance, carriage return to start the program. coxeter : diff --git a/build/pkgs/cython/package-version.txt b/build/pkgs/cython/package-version.txt index 34379825894..887ddb8eb13 100644 --- a/build/pkgs/cython/package-version.txt +++ b/build/pkgs/cython/package-version.txt @@ -1 +1 @@ -0.25.2.p1 +0.25.2.p2 diff --git a/build/pkgs/cython/patches/cdef_public_dll_linkage.patch b/build/pkgs/cython/patches/cdef_public_dll_linkage.patch new file mode 100644 index 00000000000..c4dc9016503 --- /dev/null +++ b/build/pkgs/cython/patches/cdef_public_dll_linkage.patch @@ -0,0 +1,32 @@ +Patch for https://github.com/cython/cython/pull/1687 +Only needed for Cygwin systems + +commit 632f18f2710eaf6ee2b3a3aaa0749c7886299e64 +Author: Jeroen Demeyer +Date: Thu Apr 27 17:13:39 2017 +0200 + + Do not use special dll linkage for "cdef public" functions + +diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py +index 6356e78..76fe4e5 100644 +--- a/Cython/Compiler/ModuleNode.py ++++ b/Cython/Compiler/ModuleNode.py +@@ -214,8 +214,7 @@ class ModuleNode(Nodes.Node, Nodes.BlockNode): + def generate_public_declaration(self, entry, h_code, i_code): + h_code.putln("%s %s;" % ( + Naming.extern_c_macro, +- entry.type.declaration_code( +- entry.cname, dll_linkage="DL_IMPORT"))) ++ entry.type.declaration_code(entry.cname))) + if i_code: + i_code.putln("cdef extern %s" % ( + entry.type.declaration_code(entry.cname, pyrex=1))) +@@ -2849,7 +2848,7 @@ def generate_cfunction_declaration(entry, env, code, definition): + dll_linkage = "DL_IMPORT" + elif entry.visibility == 'public': + storage_class = Naming.extern_c_macro +- dll_linkage = "DL_EXPORT" ++ dll_linkage = None + elif entry.visibility == 'private': + storage_class = "static" + dll_linkage = None diff --git a/build/pkgs/ecm/SPKG.txt b/build/pkgs/ecm/SPKG.txt index 9d3efd62a19..78c9dacbfa3 100644 --- a/build/pkgs/ecm/SPKG.txt +++ b/build/pkgs/ecm/SPKG.txt @@ -39,12 +39,9 @@ LGPL V3+ "-mcpu=...", and perhaps pass a more generic "--host=..." to 'configure'. (MPIR honors '--enable-fat' to some extent, but this option isn't used on anything other than x86 / x86_64.) + * We currently work around a linker bug on MacOS X 10.5 PPC (with + GCC 4.2.1) which breaks 'configure' if debug symbols are enabled. + This *might* get fixed in later upstream releases. * We could save some space by removing the `src/build.vc10/` directory which isn't used in Sage. (It gets probably more worth in case also directories / files for later versions of Microsoft Visual C get added.) - -=== Patches === - * asm-align.patch: on OS X using Xcode 7, ".align 64" in the files - "x86_64/mulredc*.asm" causes an error, and similarly with ".align - 32,,16". These need to be changed to ".p2align 6" and ".p2align - 5,,4", respectively diff --git a/build/pkgs/ecm/checksums.ini b/build/pkgs/ecm/checksums.ini index e244bada1ca..9c24b0797ae 100644 --- a/build/pkgs/ecm/checksums.ini +++ b/build/pkgs/ecm/checksums.ini @@ -1,4 +1,4 @@ -tarball=ecm-VERSION.tar.bz2 -sha1=97a6f9fc68337d8dadebc4754a61fdfe97bb0136 -md5=6d70cd667b46e34080615ae361608fa7 -cksum=134247046 +tarball=ecm-VERSION.tar.gz +sha1=407e41af6f93214509810b954b88251e4b89e82b +md5=559b13454b2094fbf26b33ff4fc824b7 +cksum=3785569195 diff --git a/build/pkgs/ecm/package-version.txt b/build/pkgs/ecm/package-version.txt index ca08977a763..af19623c101 100644 --- a/build/pkgs/ecm/package-version.txt +++ b/build/pkgs/ecm/package-version.txt @@ -1 +1 @@ -6.4.4.p0 +7.0.4.p0 diff --git a/build/pkgs/ecm/patches/asm-align.patch b/build/pkgs/ecm/patches/asm-align.patch deleted file mode 100644 index 88e7a8f4f80..00000000000 --- a/build/pkgs/ecm/patches/asm-align.patch +++ /dev/null @@ -1,627 +0,0 @@ -diff -r -u ecm-6.4.4-orig/x86_64/mulredc10.asm ecm-6.4.4/x86_64/mulredc10.asm ---- ecm-6.4.4-orig/x86_64/mulredc10.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc10.asm 2015-09-23 21:02:14.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc10 - TYPE(GSYM_PREFIX`'mulredc`'10,`function') - -@@ -535,7 +535,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc11.asm ecm-6.4.4/x86_64/mulredc11.asm ---- ecm-6.4.4-orig/x86_64/mulredc11.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc11.asm 2015-09-23 21:05:49.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc11 - TYPE(GSYM_PREFIX`'mulredc`'11,`function') - -@@ -579,7 +579,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc12.asm ecm-6.4.4/x86_64/mulredc12.asm ---- ecm-6.4.4-orig/x86_64/mulredc12.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc12.asm 2015-09-23 21:05:55.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc12 - TYPE(GSYM_PREFIX`'mulredc`'12,`function') - -@@ -623,7 +623,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc13.asm ecm-6.4.4/x86_64/mulredc13.asm ---- ecm-6.4.4-orig/x86_64/mulredc13.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc13.asm 2015-09-23 21:05:57.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc13 - TYPE(GSYM_PREFIX`'mulredc`'13,`function') - -@@ -667,7 +667,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc14.asm ecm-6.4.4/x86_64/mulredc14.asm ---- ecm-6.4.4-orig/x86_64/mulredc14.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc14.asm 2015-09-23 21:05:59.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc14 - TYPE(GSYM_PREFIX`'mulredc`'14,`function') - -@@ -711,7 +711,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc15.asm ecm-6.4.4/x86_64/mulredc15.asm ---- ecm-6.4.4-orig/x86_64/mulredc15.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc15.asm 2015-09-23 21:06:02.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc15 - TYPE(GSYM_PREFIX`'mulredc`'15,`function') - -@@ -755,7 +755,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc16.asm ecm-6.4.4/x86_64/mulredc16.asm ---- ecm-6.4.4-orig/x86_64/mulredc16.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc16.asm 2015-09-23 21:06:03.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc16 - TYPE(GSYM_PREFIX`'mulredc`'16,`function') - -@@ -799,7 +799,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc17.asm ecm-6.4.4/x86_64/mulredc17.asm ---- ecm-6.4.4-orig/x86_64/mulredc17.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc17.asm 2015-09-23 21:06:05.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc17 - TYPE(GSYM_PREFIX`'mulredc`'17,`function') - -@@ -843,7 +843,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc18.asm ecm-6.4.4/x86_64/mulredc18.asm ---- ecm-6.4.4-orig/x86_64/mulredc18.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc18.asm 2015-09-23 21:06:07.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc18 - TYPE(GSYM_PREFIX`'mulredc`'18,`function') - -@@ -887,7 +887,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc19.asm ecm-6.4.4/x86_64/mulredc19.asm ---- ecm-6.4.4-orig/x86_64/mulredc19.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc19.asm 2015-09-23 21:06:10.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc19 - TYPE(GSYM_PREFIX`'mulredc`'19,`function') - -@@ -931,7 +931,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_10.asm ecm-6.4.4/x86_64/mulredc1_10.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_10.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_10.asm 2015-09-23 20:52:08.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 bytes long - GLOBL GSYM_PREFIX`'mulredc1_10 - TYPE(GSYM_PREFIX`'mulredc1_`'10,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_11.asm ecm-6.4.4/x86_64/mulredc1_11.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_11.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_11.asm 2015-09-23 21:03:24.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_11 - TYPE(GSYM_PREFIX`'mulredc1_`'11,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_12.asm ecm-6.4.4/x86_64/mulredc1_12.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_12.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_12.asm 2015-09-23 21:03:25.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_12 - TYPE(GSYM_PREFIX`'mulredc1_`'12,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_13.asm ecm-6.4.4/x86_64/mulredc1_13.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_13.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_13.asm 2015-09-23 21:03:25.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_13 - TYPE(GSYM_PREFIX`'mulredc1_`'13,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_14.asm ecm-6.4.4/x86_64/mulredc1_14.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_14.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_14.asm 2015-09-23 21:03:25.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_14 - TYPE(GSYM_PREFIX`'mulredc1_`'14,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_15.asm ecm-6.4.4/x86_64/mulredc1_15.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_15.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_15.asm 2015-09-23 21:03:25.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_15 - TYPE(GSYM_PREFIX`'mulredc1_`'15,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_16.asm ecm-6.4.4/x86_64/mulredc1_16.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_16.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_16.asm 2015-09-23 21:03:26.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_16 - TYPE(GSYM_PREFIX`'mulredc1_`'16,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_17.asm ecm-6.4.4/x86_64/mulredc1_17.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_17.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_17.asm 2015-09-23 21:03:26.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_17 - TYPE(GSYM_PREFIX`'mulredc1_`'17,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_18.asm ecm-6.4.4/x86_64/mulredc1_18.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_18.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_18.asm 2015-09-23 21:03:26.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_18 - TYPE(GSYM_PREFIX`'mulredc1_`'18,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_19.asm ecm-6.4.4/x86_64/mulredc1_19.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_19.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_19.asm 2015-09-23 21:03:26.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_19 - TYPE(GSYM_PREFIX`'mulredc1_`'19,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_2.asm ecm-6.4.4/x86_64/mulredc1_2.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_2.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_2.asm 2015-09-23 21:03:26.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_2 - TYPE(GSYM_PREFIX`'mulredc1_`'2,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_20.asm ecm-6.4.4/x86_64/mulredc1_20.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_20.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_20.asm 2015-09-23 21:03:27.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_20 - TYPE(GSYM_PREFIX`'mulredc1_`'20,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_3.asm ecm-6.4.4/x86_64/mulredc1_3.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_3.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_3.asm 2015-09-23 21:03:27.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_3 - TYPE(GSYM_PREFIX`'mulredc1_`'3,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_4.asm ecm-6.4.4/x86_64/mulredc1_4.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_4.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_4.asm 2015-09-23 21:03:28.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_4 - TYPE(GSYM_PREFIX`'mulredc1_`'4,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_5.asm ecm-6.4.4/x86_64/mulredc1_5.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_5.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_5.asm 2015-09-23 21:03:28.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_5 - TYPE(GSYM_PREFIX`'mulredc1_`'5,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_6.asm ecm-6.4.4/x86_64/mulredc1_6.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_6.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_6.asm 2015-09-23 21:03:28.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_6 - TYPE(GSYM_PREFIX`'mulredc1_`'6,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_7.asm ecm-6.4.4/x86_64/mulredc1_7.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_7.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_7.asm 2015-09-23 21:03:28.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_7 - TYPE(GSYM_PREFIX`'mulredc1_`'7,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_8.asm ecm-6.4.4/x86_64/mulredc1_8.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_8.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_8.asm 2015-09-23 21:03:28.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_8 - TYPE(GSYM_PREFIX`'mulredc1_`'8,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc1_9.asm ecm-6.4.4/x86_64/mulredc1_9.asm ---- ecm-6.4.4-orig/x86_64/mulredc1_9.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc1_9.asm 2015-09-23 21:03:29.000000000 -0700 -@@ -18,7 +18,7 @@ - define(`INVM_PARAM',`%r8')dnl' - )dnl - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc1_9 - TYPE(GSYM_PREFIX`'mulredc1_`'9,`function') - -diff -r -u ecm-6.4.4-orig/x86_64/mulredc2.asm ecm-6.4.4/x86_64/mulredc2.asm ---- ecm-6.4.4-orig/x86_64/mulredc2.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc2.asm 2015-09-23 21:04:57.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc2 - TYPE(GSYM_PREFIX`'mulredc`'2,`function') - -@@ -183,7 +183,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc20.asm ecm-6.4.4/x86_64/mulredc20.asm ---- ecm-6.4.4-orig/x86_64/mulredc20.asm 2013-02-27 07:17:53.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc20.asm 2015-09-23 21:05:17.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc20 - TYPE(GSYM_PREFIX`'mulredc`'20,`function') - -@@ -975,7 +975,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc3.asm ecm-6.4.4/x86_64/mulredc3.asm ---- ecm-6.4.4-orig/x86_64/mulredc3.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc3.asm 2015-09-23 21:06:14.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc3 - TYPE(GSYM_PREFIX`'mulredc`'3,`function') - -@@ -227,7 +227,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc4.asm ecm-6.4.4/x86_64/mulredc4.asm ---- ecm-6.4.4-orig/x86_64/mulredc4.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc4.asm 2015-09-23 21:06:18.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc4 - TYPE(GSYM_PREFIX`'mulredc`'4,`function') - -@@ -271,7 +271,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc5.asm ecm-6.4.4/x86_64/mulredc5.asm ---- ecm-6.4.4-orig/x86_64/mulredc5.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc5.asm 2015-09-23 21:06:23.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc5 - TYPE(GSYM_PREFIX`'mulredc`'5,`function') - -@@ -315,7 +315,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc6.asm ecm-6.4.4/x86_64/mulredc6.asm ---- ecm-6.4.4-orig/x86_64/mulredc6.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc6.asm 2015-09-23 21:06:27.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc6 - TYPE(GSYM_PREFIX`'mulredc`'6,`function') - -@@ -359,7 +359,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc7.asm ecm-6.4.4/x86_64/mulredc7.asm ---- ecm-6.4.4-orig/x86_64/mulredc7.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc7.asm 2015-09-23 21:06:31.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc7 - TYPE(GSYM_PREFIX`'mulredc`'7,`function') - -@@ -403,7 +403,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc8.asm ecm-6.4.4/x86_64/mulredc8.asm ---- ecm-6.4.4-orig/x86_64/mulredc8.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc8.asm 2015-09-23 21:06:34.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc8 - TYPE(GSYM_PREFIX`'mulredc`'8,`function') - -@@ -447,7 +447,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m -diff -r -u ecm-6.4.4-orig/x86_64/mulredc9.asm ecm-6.4.4/x86_64/mulredc9.asm ---- ecm-6.4.4-orig/x86_64/mulredc9.asm 2013-02-27 07:17:52.000000000 -0800 -+++ ecm-6.4.4/x86_64/mulredc9.asm 2015-09-23 21:06:38.000000000 -0700 -@@ -23,7 +23,7 @@ - include(`config.m4') - - TEXT --.align 64 # Opteron L1 code cache line is 64 bytes long -+.p2align 6 # Opteron L1 code cache line is 64 5,,4 - GLOBL GSYM_PREFIX`'mulredc9 - TYPE(GSYM_PREFIX`'mulredc`'9,`function') - -@@ -491,7 +491,7 @@ - # i > 0 passes - ######################################################################### - --.align 32,,16 -+.p2align 5,,4 - LABEL_SUFFIX(1) - - # register values at loop entry: %TP = tmp, %I = i, %YP = y, %MP = m diff --git a/build/pkgs/ecm/patches/cygwin.patch b/build/pkgs/ecm/patches/cygwin.patch deleted file mode 100644 index 219d8f34ed9..00000000000 --- a/build/pkgs/ecm/patches/cygwin.patch +++ /dev/null @@ -1,26 +0,0 @@ -Make sure the assembly routines use the appropriate calling convention -for Windows in Cygwin as well. See https://trac.sagemath.org/ticket/21223 -diff -ruN a/configure.in b/configure.in ---- a/configure.in 2016-08-11 13:51:46.869520300 +0200 -+++ b/configure.in 2016-08-11 13:52:46.240830400 +0200 -@@ -256,7 +256,7 @@ - GMP_ASM_TYPE - - case $host in -- *-*-mingw32) GMP_DEFINE([WINDOWS64_ABI], 1) -+ *-*-mingw32|*cygwin*) GMP_DEFINE([WINDOWS64_ABI], 1) - AC_DEFINE([WINDOWS64_ABI], 1,[Define to 1 if x86_64 mulredc*() functions should be called with Windows ABI]);; - *) ;; - esac -diff -ruN a/configure b/configure ---- a/configure 2016-08-11 13:51:46.936572300 +0200 -+++ b/configure 2016-08-11 13:53:31.281801500 +0200 -@@ -12957,7 +12957,7 @@ - - - case $host in -- *-*-mingw32) -+ *-*-mingw32|*cygwin*) - echo 'define(, <1>)' >>$gmp_tmpconfigm4 - - diff --git a/build/pkgs/ecm/spkg-check b/build/pkgs/ecm/spkg-check index 0a5c34e24a7..9c121b1ec4e 100755 --- a/build/pkgs/ecm/spkg-check +++ b/build/pkgs/ecm/spkg-check @@ -15,6 +15,9 @@ echo echo "Now running GMP-ECM's test suite..." $MAKE check +# There are potential race conditions in the long check, +# running it serially. +$MAKE -j1 longcheck if [ $? -ne 0 ]; then echo >&2 "Error: GMP-ECM's test suite failed." diff --git a/build/pkgs/ecm/spkg-install b/build/pkgs/ecm/spkg-install index f087ed4247d..ce92c947aa4 100755 --- a/build/pkgs/ecm/spkg-install +++ b/build/pkgs/ecm/spkg-install @@ -87,6 +87,11 @@ else echo "to, or '--with-pic' was given in ECM_CONFIGURE." fi +if [ "$UNAME" == "CYGWIN" ]; then + # Force shared library build only on Cygwin; it will not build otherwise + ECM_CONFIGURE="$ECM_CONFIGURE --enable-shared --disable-static" +fi + if [ "$SAGE_DEBUG" = yes ]; then # Add debug symbols and disable optimization: diff --git a/build/pkgs/enum34/spkg-install b/build/pkgs/enum34/spkg-install index c1a2289ade0..cd57e98526f 100755 --- a/build/pkgs/enum34/spkg-install +++ b/build/pkgs/enum34/spkg-install @@ -1,3 +1,8 @@ #!/usr/bin/env bash +if [ "$SAGE_PYTHON3" = "yes" ]; then + echo "Not installing enum34: it is only needed for Python version 3.3 or older." + exit 0 +fi + cd src && $PIP_INSTALL . diff --git a/build/pkgs/libpng/SPKG.txt b/build/pkgs/libpng/SPKG.txt index 9ef5facecad..fd1619c178c 100644 --- a/build/pkgs/libpng/SPKG.txt +++ b/build/pkgs/libpng/SPKG.txt @@ -27,26 +27,26 @@ This spkg depends on: == Special Update/Build Instructions == - * No changes went into src. There are two different tarballs, but we use the - one with configure. - * On Darwin, the symbolic links libpng.* created by libpng12 may interfere - with a system-wide libPng.dylib. - This system-wide library is likely to be a different version and on top of - that the symbols exported there are prefixed with "_cg" (for "Core Graphics"), - so even if by chance the functionalities of the two libraries were interchangeable, - libraries or applications looking for one and being presented the other won't - find the symbols they expect. - Note the uppercase "P" which could prevent this conflict; unfortunately, the - default filesystem used by Apple is case-insensitive. - Also note there would be no problem if the system-wide library was not looked - for when Sage is being built or run, but that's not the case either; - it is at least looked for by the "ImageIO" framework: + * On Darwin, the symbolic links libpng.* created by libpng16 may + interfere with a system-wide libPng.dylib. + + This system-wide library is likely to be a different version and on + top of that, the symbols exported there are prefixed with "_cg" + (for "Core Graphics"). So even if by chance the functionalities of + the two libraries were interchangeable, libraries or applications + looking for one and being presented the other won't find the symbols + they expect. Note the uppercase "P" which could prevent this + conflict; unfortunately, the default filesystem used by Apple is + case-insensitive. + + Note there would be no problem if the system-wide library was not + looked for when Sage is being built or run, but that's not the case + either; it is at least looked for by the "ImageIO" framework: - when Python is built with Mac OS extensions, fixed in #4008; - when Mercurial is built because it uses $EDITOR, cf. #4678; - when R is built and it finds -lpng, cf. #4409 and #11696. - As not all of these problems are easily dealt with and new ones may arise, - we chose to delete the $SAGE_LOCAL/lib/libpng.* symlinks on Darwin. - Therefore, some package as matplotlib (cf. #11686) which by default looks for - -lpng are patched to look for -lpng12 (unless pkg-config is installed; in - this case indeed we can use a non-conflicting libpng.pc symlinking to - libpng12.pc). + + As not all of these problems are easily dealt with and new ones may + arise, we chose to delete the $SAGE_LOCAL/lib/libpng.* symlinks. + Therefore, some packages like Tachyon, which by default look for + -lpng are patched to look for -lpng16 instead. diff --git a/build/pkgs/libpng/checksums.ini b/build/pkgs/libpng/checksums.ini index 91023d7b42e..c6c7adab90b 100644 --- a/build/pkgs/libpng/checksums.ini +++ b/build/pkgs/libpng/checksums.ini @@ -1,4 +1,4 @@ tarball=libpng-VERSION.tar.gz -sha1=5175be08c4fa767b0d3d025f636e73e78780f988 -md5=e358f9a265f2063b36f10dc454ee0e17 -cksum=291518548 +sha1=012c842e6454dc38c6390623ed31ec4005c00584 +md5=68553080685f812d1dd7a6b8215c37d8 +cksum=1294784956 diff --git a/build/pkgs/libpng/package-version.txt b/build/pkgs/libpng/package-version.txt index ff64d17007a..69e4e62134a 100644 --- a/build/pkgs/libpng/package-version.txt +++ b/build/pkgs/libpng/package-version.txt @@ -1 +1 @@ -1.2.51.p0 +1.6.29 diff --git a/build/pkgs/libpng/patches/pngconf.h.patch b/build/pkgs/libpng/patches/pngconf.h.patch deleted file mode 100644 index 94ef6868a8b..00000000000 --- a/build/pkgs/libpng/patches/pngconf.h.patch +++ /dev/null @@ -1,24 +0,0 @@ ---- src/pngconf.h 2009-02-14 22:59:26.000000000 +0100 -+++ src.new/pngconf.h 2013-12-21 13:59:30.898443798 +0100 -@@ -311,6 +311,12 @@ - # define PNG_SETJMP_SUPPORTED - #endif - -+ -+#if !(defined(PNG_SKIP_SETJMP_CHECK) && PNG_SKIP_SETJMP_CHECK) -+ -+/* This is an attempt to get libfreetype 2.5.2 compiled (in sage) with -+ *this* version of libpng */ -+ - #ifdef PNG_SETJMP_SUPPORTED - /* This is an attempt to force a single setjmp behaviour on Linux. If - * the X config stuff didn't define _BSD_SOURCE we wouldn't need this. -@@ -343,6 +349,8 @@ - # endif /* __linux__ */ - #endif /* PNG_SETJMP_SUPPORTED */ - -+#endif /* !(defined(PNG_SKIP_SETJMP_CHECK) && PNG_SKIP_SETJMP_CHECK) */ -+ - #ifdef BSD - # include - #else diff --git a/build/pkgs/libpng/spkg-install b/build/pkgs/libpng/spkg-install index ff25ceb026a..cc8f94fa7b6 100755 --- a/build/pkgs/libpng/spkg-install +++ b/build/pkgs/libpng/spkg-install @@ -46,32 +46,6 @@ if [ $? -ne 0 ]; then exit 1 fi -# On Darwin, the symbolic links libpng.* created by libpng12 may interfere -# with a system-wide libPng.dylib. -# This system-wide library is likely to be a different version and on top of -# that the symbols exported there are prefixed with "_cg" (for "Core Graphics"), -# so even if by chance the functionalities of the two libraries were interchangeable, -# libraries or applications looking for one and being presented the other won't -# find the symbols they expect. -# Note the uppercase "P" which could prevent this conflict; unfortunately, the -# default filesystem used by Apple is case-insensitive. -# Also note there would be no problem if the system-wide library was not looked -# for when Sage is being built or run, but that's not the case either; -# it is at least looked for by the "ImageIO" framework: -# - when Python is built with Mac OS extensions, fixed in #4008; -# - when Mercurial is built because it uses $EDITOR, cf. #4678; -# - when R is built and it finds -lpng, cf. #4409 and #11696. -# As not all of these problems are easily dealt with and new ones may arise, -# we chose to delete the $SAGE_LOCAL/lib/libpng.* symlinks on Darwin. -# Therefore, some package as matplotlib (cf. #11686) which by default looks for -# -lpng are patched to look for -lpng12 (unless pkg-config is installed; in -# this case indeed we can use a non-conflicting libpng.pc symlinking to -# libpng12.pc). -if [ "$UNAME" = "Darwin" ]; then - echo "Deleting libpng.*..." - rm -rf "$SAGE_LOCAL"/lib/libpng.* - if [ $? -ne 0 ]; then - echo >&2 "Error deleting symlinks." - exit 1 - fi -fi + +# Delete libpng.* symlinks, see SPKG.txt for details +rm -f "$SAGE_LOCAL"/lib/libpng.* diff --git a/build/pkgs/linbox/package-version.txt b/build/pkgs/linbox/package-version.txt index 2a175dcdb19..9c9dbee1221 100644 --- a/build/pkgs/linbox/package-version.txt +++ b/build/pkgs/linbox/package-version.txt @@ -1 +1 @@ -1.4.2.p0 +1.4.2.p1 diff --git a/build/pkgs/linbox/patches/ticket-23029.patch b/build/pkgs/linbox/patches/ticket-23029.patch new file mode 100644 index 00000000000..677f8ceca39 --- /dev/null +++ b/build/pkgs/linbox/patches/ticket-23029.patch @@ -0,0 +1,38 @@ +Do not use 'B0' as a variable name as it conflicts with a macro in the standard +header termios.h. See https://trac.sagemath.org/ticket/23029 +diff --git a/linbox/solutions/det.h b/linbox/solutions/det.h +index f3390be..2057234 100644 +--- a/linbox/solutions/det.h ++++ b/linbox/solutions/det.h +@@ -236,9 +236,9 @@ namespace LinBox + } + + Diagonal D (diag); +- Compose > B0 (&A, &D); ++ Compose > B_0 (&A, &D); + typedef Compose,Compose > > Blackbox1; +- Blackbox1 B(&D, &B0); ++ Blackbox1 B(&D, &B_0); + + BlackboxContainerSymmetric TF (&B, F, iter); + +diff --git a/linbox/solutions/rank.inl b/linbox/solutions/rank.inl +index fad66a8..7fa231d 100755 +--- a/linbox/solutions/rank.inl ++++ b/linbox/solutions/rank.inl +@@ -142,9 +142,9 @@ namespace LinBox + + + typedef Compose,Blackbox >, Diagonal > BlackBox1; +- Diagonal D0 (d1); +- Compose,Blackbox > B0 (&D0, &A); +- BlackBox1 B (&B0, &D0); ++ Diagonal D_0 (d1); ++ Compose,Blackbox > B_0 (&D_0, &A); ++ BlackBox1 B (&B_0, &D_0); + + BlackboxContainerSymmetric TF (&B, F, iter); + MasseyDomain > WD (&TF, M.earlyTermThreshold ()); +-- +2.8.3 + diff --git a/build/pkgs/meataxe/package-version.txt b/build/pkgs/meataxe/package-version.txt index 208b2a0070d..715ebf8a08c 100644 --- a/build/pkgs/meataxe/package-version.txt +++ b/build/pkgs/meataxe/package-version.txt @@ -1 +1 @@ -2.4.24.p1 +2.4.24.p2 diff --git a/build/pkgs/meataxe/patches/mtxdirs.patch b/build/pkgs/meataxe/patches/mtxdirs.patch new file mode 100644 index 00000000000..794cbf24f66 --- /dev/null +++ b/build/pkgs/meataxe/patches/mtxdirs.patch @@ -0,0 +1,37 @@ +Declare array sizes in header file, use 1024 as size + +diff -ru meataxe-2.4.24//src/args.c b/src/args.c +--- meataxe-2.4.24//src/args.c 2009-05-23 21:35:19.000000000 +0200 ++++ b/src/args.c 2017-02-22 10:32:01.324405681 +0100 +@@ -125,7 +125,7 @@ + ** directory, which was selected when building the MeatAxe, is used. + **/ + +-char MtxBinDir[250] = MTXBIN; ++char MtxBinDir[] = MTXBIN; + + + /** +@@ -139,7 +139,7 @@ + ** @see MtxBinDir + **/ + +-char MtxLibDir[250] = MTXLIB; ++char MtxLibDir[] = MTXLIB; + + + +diff -ru meataxe-2.4.24//src/meataxe.h b/src/meataxe.h +--- meataxe-2.4.24//src/meataxe.h 2011-08-08 00:15:19.000000000 +0200 ++++ b/src/meataxe.h 2017-02-22 10:31:52.685290030 +0100 +@@ -246,8 +246,8 @@ + Application framework + ------------------------------------------------------------------ */ + +-extern char MtxBinDir[]; /* MeatAxe library directory */ +-extern char MtxLibDir[]; /* MeatAxe program directory */ ++extern char MtxBinDir[1024]; /* MeatAxe library directory */ ++extern char MtxLibDir[1024]; /* MeatAxe program directory */ + + #define APP_MAX_ARGS 50 + diff --git a/build/pkgs/normaliz/checksums.ini b/build/pkgs/normaliz/checksums.ini index e54ddf63dcc..afe7ac682f2 100644 --- a/build/pkgs/normaliz/checksums.ini +++ b/build/pkgs/normaliz/checksums.ini @@ -1,4 +1,4 @@ tarball=normaliz-VERSION.tar.gz -sha1=87bd2846a9132eb87ed6e9fa8cf875a0d6f5cc37 -md5=0eab32959e10b0c41b92c768ef562136 -cksum=1044635248 +sha1=81c89b5b2f8ff15d6939a3fd5822a288440f4d51 +md5=245b1c025ee45d0e2ffd3a77a585768e +cksum=3791680398 diff --git a/build/pkgs/normaliz/package-version.txt b/build/pkgs/normaliz/package-version.txt index 0aec50e6ede..e4604e3afd0 100644 --- a/build/pkgs/normaliz/package-version.txt +++ b/build/pkgs/normaliz/package-version.txt @@ -1 +1 @@ -3.1.4 +3.2.1 diff --git a/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch b/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch new file mode 100644 index 00000000000..2a4e9e35c40 --- /dev/null +++ b/build/pkgs/normaliz/patches/22684_cone_reduce_memory_usage.patch @@ -0,0 +1,12 @@ +diff -druN src/source/libnormaliz/cone.cpp patches/source/libnormaliz/cone.cpp +--- src/source/libnormaliz/cone.cpp 2017-02-22 17:59:08.000000000 +0100 ++++ patches/source/libnormaliz/cone.cpp 2017-04-26 16:33:57.460366489 +0200 +@@ -3223,7 +3223,7 @@ + Deg1Elements=Matrix(0,dim); + ModuleGenerators=Matrix(0,dim); + +- Matrix Raw=ApproxCone.getDeg1ElementsMatrix(); ++ const Matrix& Raw=ApproxCone.getDeg1ElementsMatrix(); + Matrix Result(0,dim); + Matrix Eq=BasisChange.getEquations(); + Matrix Cong=BasisChange.getCongruences(); diff --git a/build/pkgs/normaliz/spkg-install b/build/pkgs/normaliz/spkg-install index 0e6526ed38d..a73ea271a5a 100755 --- a/build/pkgs/normaliz/spkg-install +++ b/build/pkgs/normaliz/spkg-install @@ -14,6 +14,3 @@ cd src $MAKE || die "Error building normaliz" $MAKE install || die "Error installing normaliz" -cd Singular || die "Normaliz distribution has no Singular subdirectory" -cp -pf normaliz.lib "$SAGE_LOCAL/share/singular/" || die "Error installing normaliz.lib for Singular." - diff --git a/build/pkgs/pygments/SPKG.txt b/build/pkgs/pygments/SPKG.txt index b0b7e9a56f2..ae7f23d4cb9 100644 --- a/build/pkgs/pygments/SPKG.txt +++ b/build/pkgs/pygments/SPKG.txt @@ -4,9 +4,18 @@ Pygments is a syntax highlighting package written in Python. -It is a generic syntax highlighter for general use in all kinds of -software such as forum systems, wikis or other applications that need -to prettify source code. +It is a generic syntax highlighter suitable for use in code hosting, +forums, wikis or other applications that need to prettify source code. +Highlights are: + +* a wide range of over 300 languages and other text formats is + supported +* special attention is paid to details, increasing quality by a fair + amount +* support for new languages and formats are added easily +* a number of output formats, presently HTML, LaTeX, RTF, SVG, all image + formats that PIL supports and ANSI sequences +* it is usable as a command-line tool and as a library == License == @@ -28,16 +37,3 @@ Patches included: * sage_prompt.patch: patch pygments/lexers/agile.py to treat the "sage:" prompt like Python's ">>>" prompt. This allows a very kludgy patch to be removed from the Sphinx package (see #10118). - -== Changelog == - -=== pygments-1.3.1.p0 (Jeroen Demeyer, November 18th, 2010) === - - * Upgrade to Pygments 1.3.1 - - * Add sage_prompt.patch - -=== pygments-0.11.1.p0 (Mike Hansen, September 15th, 2008) === - - * Initial version. - diff --git a/build/pkgs/pygments/checksums.ini b/build/pkgs/pygments/checksums.ini index 4d54f5140f0..6b7ab23a910 100644 --- a/build/pkgs/pygments/checksums.ini +++ b/build/pkgs/pygments/checksums.ini @@ -1,4 +1,4 @@ tarball=Pygments-VERSION.tar.gz -sha1=ecc79e8a3cd5abbaffe33f78061be70a34ebe994 -md5=ed3fba2467c8afcda4d317e4ef2c6150 -cksum=1054380650 +sha1=5c6714bd6fd950c1478889f7b72fc7f6771d5163 +md5=13037baca42f16917cbd5ad2fab50844 +cksum=1747340458 diff --git a/build/pkgs/pygments/package-version.txt b/build/pkgs/pygments/package-version.txt index a37572fb25c..d2ccf5ed805 100644 --- a/build/pkgs/pygments/package-version.txt +++ b/build/pkgs/pygments/package-version.txt @@ -1 +1 @@ -2.1.3.p1 +2.2.0.p0 diff --git a/build/pkgs/pynac/checksums.ini b/build/pkgs/pynac/checksums.ini index d5ada9ee7fe..22a7b185139 100644 --- a/build/pkgs/pynac/checksums.ini +++ b/build/pkgs/pynac/checksums.ini @@ -1,4 +1,4 @@ tarball=pynac-VERSION.tar.bz2 -sha1=6ff23c35da9f424751522267ab925b46d5aaeff1 -md5=0400e2c1333ee0b657c543cd788ee250 -cksum=4257838880 +sha1=13d73ad7b1bdac7726f5140dc5230ad93a987f40 +md5=78f29f401fa67d5bf7dc6ee2d5e3da81 +cksum=1873135840 diff --git a/build/pkgs/pynac/package-version.txt b/build/pkgs/pynac/package-version.txt index c006218557f..9379314273e 100644 --- a/build/pkgs/pynac/package-version.txt +++ b/build/pkgs/pynac/package-version.txt @@ -1 +1 @@ -0.7.6 +0.7.7.p0 diff --git a/build/pkgs/pynac/patches/coeff-to-ex.patch b/build/pkgs/pynac/patches/coeff-to-ex.patch deleted file mode 100644 index cc11bb5a6de..00000000000 --- a/build/pkgs/pynac/patches/coeff-to-ex.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/ginac/mpoly-singular.cpp b/ginac/mpoly-singular.cpp -index 4ddb961..74b8815 100644 ---- a/ginac/mpoly-singular.cpp -+++ b/ginac/mpoly-singular.cpp -@@ -255,7 +255,7 @@ static ex coeff_to_ex(const CanonicalForm& f, const exvector& revmap) - if (f.isImm()) - return numeric(f.intval()); - if (f.inZ()) -- throw std::runtime_error("can't happen in coeff_to_ex"); -+ return can2num(f); - if (f.inQ()) { - CanonicalForm num = f.num(); - CanonicalForm den = f.den(); -@@ -277,7 +277,7 @@ static ex canonical_to_ex(const CanonicalForm& f, const exvector& revmap) - if (f.isImm()) - return numeric(f.intval()); - if (f.inZ()) -- throw std::runtime_error("can't happen in canonical_to_ex #1"); -+ return can2num(f); - if (f.inQ()) { - CanonicalForm num = f.num(); - CanonicalForm den = f.den(); diff --git a/build/pkgs/pynac/patches/polylog-evalf.patch b/build/pkgs/pynac/patches/polylog-evalf.patch new file mode 100644 index 00000000000..636901c1b85 --- /dev/null +++ b/build/pkgs/pynac/patches/polylog-evalf.patch @@ -0,0 +1,212 @@ +diff --git a/ginac/numeric.cpp b/ginac/numeric.cpp +index eb247a5..07a6588 100644 +--- a/ginac/numeric.cpp ++++ b/ginac/numeric.cpp +@@ -166,84 +166,11 @@ inline void py_error(const char* errmsg) { + #if PY_MAJOR_VERSION < 3 + #define PyNumber_TrueDivide PyNumber_Divide + +-inline int Pynac_PyObj_Cmp(PyObject *optr1, PyObject *optr2, const char *errmsg) { +- int result; +- if (PyObject_Cmp(optr1, optr2, &result) == -1) +- py_error(errmsg); +- return result; +-} +- +-inline bool Pynac_PyObj_RichCmp(PyObject *optr1, PyObject *optr2, int opid, const char *errmsg) { +- PyObject *obj1(nullptr), *obj2(nullptr); +- if (PyComplex_CheckExact(optr1)) { +- if (PyComplex_ImagAsDouble(optr1) != 0.0) +- return false; +- obj1 = PyFloat_FromDouble(PyComplex_RealAsDouble(optr1)); +- optr1 = obj1; +- } +- if (PyComplex_CheckExact(optr2)) { +- if (PyComplex_ImagAsDouble(optr2) != 0.0) +- return false; +- obj2 = PyFloat_FromDouble(PyComplex_RealAsDouble(optr2)); +- optr2 = obj2; +- } +- int result; +- if (PyObject_Cmp(optr1, optr2, &result) == -1) +- py_error(errmsg); +- if (obj1 != nullptr) +- Py_DECREF(obj1); +- if (obj2 != nullptr) +- Py_DECREF(obj2); +- switch (result) { +- case -1: return opid == Py_LT || opid == Py_LE || opid == Py_NE; +- case 0: return opid == Py_LE || opid == Py_EQ || opid == Py_GE; +- case 1: return opid == Py_GT || opid == Py_GE || opid == Py_NE; +- default: return false; +- } +-} + #else + #define PyInt_Check PyLong_Check + #define PyInt_AsLong PyLong_AsLong + #define PyInt_FromLong PyLong_FromLong + #define PyString_FromString PyBytes_FromString +- +-inline int Pynac_PyObj_Cmp(PyObject *optr1, PyObject *optr2, const char *errmsg) { +- int result = PyObject_RichCompareBool(optr1, optr2, Py_LT); +- if (result == 1) +- return -1; +- else if (result == -1) +- py_error(errmsg); +- else { // result == 0 +- result = PyObject_RichCompareBool(optr1, optr2, Py_GT); +- if (result == -1) +- py_error(errmsg); +- } +- return result; +-} +- +-inline bool Pynac_PyObj_RichCmp(PyObject *optr1, PyObject *optr2, int opid, const char *errmsg) { +- PyObject *obj1(nullptr), *obj2(nullptr); +- if (PyComplex_CheckExact(optr1)) { +- if (PyComplex_ImagAsDouble(optr1) != 0.0) +- return false; +- obj1 = PyFloat_FromDouble(PyComplex_RealAsDouble(optr1)); +- optr1 = obj1; +- } +- if (PyComplex_CheckExact(optr2)) { +- if (PyComplex_ImagAsDouble(optr2) != 0.0) +- return false; +- obj2 = PyFloat_FromDouble(PyComplex_RealAsDouble(optr2)); +- optr2 = obj2; +- } +- int result = PyObject_RichCompareBool(optr1, optr2, opid); +- if (result == -1) +- py_error(errmsg); +- if (obj1 != nullptr) +- Py_DECREF(obj1); +- if (obj2 != nullptr) +- Py_DECREF(obj2); +- return result; +-} + #endif + + // The following variable gets changed to true once +@@ -609,7 +536,21 @@ int numeric::compare_same_type(const numeric& right) const { + ret = -1; + return ret; + case PYOBJECT: +- return Pynac_PyObj_Cmp(v._pyobject, right.v._pyobject, "compare_same_type"); ++ { ++ int result = PyObject_RichCompareBool(v._pyobject, ++ right.v._pyobject, Py_LT); ++ if (result == 1) ++ return -1; ++ else if (result == -1) ++ py_error("richcmp failed"); ++ else { // result == 0 ++ result = PyObject_RichCompareBool(v._pyobject, ++ right.v._pyobject, Py_GT); ++ if (result == -1) ++ py_error("richcmp failed"); ++ } ++ return result; ++ } + default: + stub("invalid type: compare_same_type type not handled"); + } +@@ -1601,15 +1542,17 @@ int numeric::csgn() const { + case PYOBJECT: + int result; + if (is_real()) { +- result = Pynac_PyObj_Cmp(v._pyobject, ZERO, "csgn"); ++ numeric z(ZERO); ++ Py_INCREF(ZERO); ++ return compare_same_type(z); + } else { +- PyObject *tmp = py_funcs.py_real(v._pyobject); +- result = Pynac_PyObj_Cmp(tmp, ZERO, "csgn"); +- Py_DECREF(tmp); ++ numeric re = real(); ++ numeric z(ZERO); ++ Py_INCREF(ZERO); ++ result = re.compare_same_type(z); + if (result == 0) { +- tmp = py_funcs.py_imag(v._pyobject); +- result = Pynac_PyObj_Cmp(tmp, ZERO, "csgn"); +- Py_DECREF(tmp); ++ numeric im = imag(); ++ result = im.compare_same_type(z); + } + } + return result; +@@ -1689,7 +1632,7 @@ bool numeric::is_positive() const { + case MPQ: + return mpq_cmp_si(v._bigrat, 0, 1) > 0; + case PYOBJECT: +- return is_real() and Pynac_PyObj_RichCmp(v._pyobject, ZERO, Py_GT, "is_positive"); ++ return is_real() and PyObject_RichCompareBool(v._pyobject, ZERO, Py_GT) == 1; + default: + stub("invalid type: is_positive() type not handled"); + } +@@ -1706,7 +1649,7 @@ bool numeric::is_negative() const { + case MPQ: + return mpq_cmp_si(v._bigrat, 0, 1) < 0; + case PYOBJECT: +- return is_real() and Pynac_PyObj_RichCmp(v._pyobject, ZERO, Py_LT, "is_negative"); ++ return is_real() and PyObject_RichCompareBool(v._pyobject, ZERO, Py_LT) == 1; + default: + stub("invalid type: is_negative() type not handled"); + } +@@ -1765,7 +1708,7 @@ bool numeric::is_nonneg_integer() const { + case MPQ: + return (is_integer() and (is_positive() or is_zero())); + case PYOBJECT: +- return is_integer() and Pynac_PyObj_RichCmp(v._pyobject, ZERO, Py_GE, "is_nonneg_integer"); ++ return is_integer() and PyObject_RichCompareBool(v._pyobject, ZERO, Py_GE) == 1; + default: + stub("invalid type: is_nonneg_integer() type not handled"); + } +@@ -2026,7 +1969,7 @@ bool numeric::operator<(const numeric &right) const { + case MPQ: + return mpq_cmp(v._bigrat, right.v._bigrat) < 0; + case PYOBJECT: +- return Pynac_PyObj_RichCmp(v._pyobject, right.v._pyobject, Py_LT, "<"); ++ return PyObject_RichCompareBool(v._pyobject, right.v._pyobject, Py_LT) == 1; + default: + stub("invalid type: operator< type not handled"); + } +@@ -2050,7 +1993,7 @@ bool numeric::operator<=(const numeric &right) const { + case MPQ: + return mpq_cmp(v._bigrat, right.v._bigrat) <= 0; + case PYOBJECT: +- return Pynac_PyObj_RichCmp(v._pyobject, right.v._pyobject, Py_LE, "<="); ++ return PyObject_RichCompareBool(v._pyobject, right.v._pyobject, Py_LE) == 1; + default: + stub("invalid type: operator<= type not handled"); + } +@@ -2074,7 +2017,7 @@ bool numeric::operator>(const numeric &right) const { + case MPQ: + return mpq_cmp(v._bigrat, right.v._bigrat) > 0; + case PYOBJECT: +- return Pynac_PyObj_RichCmp(v._pyobject, right.v._pyobject, Py_GT, ">"); ++ return PyObject_RichCompareBool(v._pyobject, right.v._pyobject, Py_GT) == 1; + default: + stub("invalid type: operator> type not handled"); + } +@@ -2098,7 +2041,7 @@ bool numeric::operator>=(const numeric &right) const { + case MPQ: + return mpq_cmp(v._bigrat, right.v._bigrat) >= 0; + case PYOBJECT: +- return Pynac_PyObj_RichCmp(v._pyobject, right.v._pyobject, Py_GE, ">="); ++ return PyObject_RichCompareBool(v._pyobject, right.v._pyobject, Py_GE) == 1; + default: + stub("invalid type: operator!= type not handled"); + } +@@ -2640,9 +2583,9 @@ const numeric numeric::Li2(const numeric &n, PyObject* parent) const { + + numeric rnum(ret); + if (is_real() and n.is_integer() and rnum.real()<(*_num1_p)) +- return rnum.real(); ++ return ex_to(rnum.real().evalf(0, parent)); + else +- return rnum; ++ return ex_to(rnum.evalf(0, parent)); + } + + const numeric numeric::lgamma() const { diff --git a/build/pkgs/pynormaliz/checksums.ini b/build/pkgs/pynormaliz/checksums.ini index 8867a73bbf6..e4ca9b4c1aa 100644 --- a/build/pkgs/pynormaliz/checksums.ini +++ b/build/pkgs/pynormaliz/checksums.ini @@ -1,4 +1,4 @@ tarball=PyNormaliz-VERSION.tar.gz -sha1=e69046012a18f73cc820cf072d43f7d852020d49 -md5=27a5f7d3fa0d96766d4f811495b0f5ce -cksum=1643770354 +sha1=619d2b01d9ee20bfcf880cb25ea04b72cc2d2942 +md5=22eea4624caff219618640ed67003b9d +cksum=4251733870 diff --git a/build/pkgs/pynormaliz/package-version.txt b/build/pkgs/pynormaliz/package-version.txt index d3827e75a5c..c239c60cba2 100644 --- a/build/pkgs/pynormaliz/package-version.txt +++ b/build/pkgs/pynormaliz/package-version.txt @@ -1 +1 @@ -1.0 +1.5 diff --git a/build/pkgs/python3/checksums.ini b/build/pkgs/python3/checksums.ini index 2f96cb3d1ad..e8a071a8610 100644 --- a/build/pkgs/python3/checksums.ini +++ b/build/pkgs/python3/checksums.ini @@ -1,4 +1,4 @@ tarball=Python-VERSION.tar.gz -sha1=b7f832e6d84b406db4c854e3eb46411e6931bc98 -md5=be78e48cdfc1a7ad90efff146dce6cfe -cksum=667413425 +sha1=6e91434cf22414af8240dfa1bf8ab2d043b04998 +md5=2d0fc9f3a5940707590e07f03ecb08b9 +cksum=1747228928 diff --git a/build/pkgs/python3/package-version.txt b/build/pkgs/python3/package-version.txt index ffe309035c5..9575d51bad2 100644 --- a/build/pkgs/python3/package-version.txt +++ b/build/pkgs/python3/package-version.txt @@ -1 +1 @@ -3.5.1.p0 +3.6.1 diff --git a/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch index 1059ca4ad45..d1e0aa7352e 100644 --- a/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch +++ b/build/pkgs/python3/patches/2.6.5-FD_SETSIZE.patch @@ -2,11 +2,13 @@ This patch has never been submitted upstream for some reason, but it simply increases the default number of file descriptors the Python process can have on Cygwin, which mitigates some issues; see https://cygwin.com/ml/cygwin/2011-03/msg00651.html ---- a/Modules/selectmodule.c 2012-02-02 22:35:21.835125000 -0500 -+++ b/Modules/selectmodule.c 2012-02-02 22:41:41.210125000 -0500 -@@ -6,6 +6,21 @@ - >= 0. - */ +diff --git a/Python-3.6.1/Modules/selectmodule.c.orig b/Python-3.6.1/Modules/selectmodule.c +index 47da493..ae60234 100644 +--- a/Modules/selectmodule.c ++++ b/Modules/selectmodule.c +@@ -8,6 +8,21 @@ + #define _GNU_SOURCE + #endif +/* Windows #defines FD_SETSIZE to 64 if FD_SETSIZE isn't already defined. + 64 is too small (too many people have bumped into that limit). @@ -26,7 +28,7 @@ https://cygwin.com/ml/cygwin/2011-03/msg00651.html #include "Python.h" #include -@@ -16,16 +31,6 @@ +@@ -26,16 +41,6 @@ #undef HAVE_BROKEN_POLL #endif diff --git a/build/pkgs/python3/patches/2.7.3-dylib.patch b/build/pkgs/python3/patches/2.7.3-dylib.patch index c45fc26f698..be0de05d388 100644 --- a/build/pkgs/python3/patches/2.7.3-dylib.patch +++ b/build/pkgs/python3/patches/2.7.3-dylib.patch @@ -1,13 +1,15 @@ This patch provides a workaround to one long-standing problem with building extension modules on Cygwin; see https://bugs.python.org/issue2445 for more background. ---- a/Lib/distutils/unixccompiler.py 2012-11-27 07:44:15.409993500 -0500 -+++ b/Lib/distutils/unixccompiler.py 2012-11-27 08:09:57.801770900 -0500 -@@ -141,6 +141,7 @@ - static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s" +diff --git a/Lib/distutils/unixccompiler.py.orig b/Lib/distutils/unixccompiler.py +index 3f321c2..fb5a8fc 100644 +--- a/Lib/distutils/unixccompiler.py ++++ b/Lib/distutils/unixccompiler.py +@@ -81,6 +81,7 @@ class UnixCCompiler(CCompiler): + xcode_stub_lib_format = dylib_lib_format if sys.platform == "cygwin": exe_extension = ".exe" + dylib_lib_extension = ".dll.a" - def preprocess(self, source, - output_file=None, macros=None, include_dirs=None, + def preprocess(self, source, output_file=None, macros=None, + include_dirs=None, extra_preargs=None, extra_postargs=None): diff --git a/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch index ce4e403a2e6..bded83b383b 100644 --- a/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch +++ b/build/pkgs/python3/patches/3.2.6-getpath-exe-extension.patch @@ -2,7 +2,7 @@ Fixes a known issue with sys.executable ambiguity on Cygwin. This is a simpler version of the patch found at http://bugs.python.org/issue28441 --- a/Modules/getpath.c +++ b/Modules/getpath.c -@@ -491,6 +491,28 @@ calculate_path(void) +@@ -551,6 +551,28 @@ calculate_path(void) if (isxfile(progpath)) break; diff --git a/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch index 7b642805925..ea9b7cecd03 100644 --- a/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch +++ b/build/pkgs/python3/patches/3.2.6-no-enable-new-dtags.patch @@ -4,7 +4,7 @@ diff --git a/Lib/distutils/unixccompiler.py b/Lib/distutils/unixccompiler.py index 69045f8..7ed0a5f 100644 --- a/Lib/distutils/unixccompiler.py +++ b/Lib/distutils/unixccompiler.py -@@ -239,9 +239,13 @@ class UnixCCompiler(CCompiler): +@@ -243,9 +243,13 @@ class UnixCCompiler(CCompiler): # -Wl whenever gcc was used in the past it is probably # safest to keep doing so. if sysconfig.get_config_var("GNULD") == "yes": diff --git a/build/pkgs/python3/patches/3.2.6-no-native-tls.patch b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch index 8675880c2b5..9e1acab59b7 100644 --- a/build/pkgs/python3/patches/3.2.6-no-native-tls.patch +++ b/build/pkgs/python3/patches/3.2.6-no-native-tls.patch @@ -21,7 +21,7 @@ index de42f1a..a4eab78 100644 #define Py_HAVE_NATIVE_TLS int -@@ -643,3 +649,5 @@ PyThread_get_key_value(int key) +@@ -648,3 +654,5 @@ PyThread_get_key_value(int key) void PyThread_ReInitTLS(void) {} @@ -31,7 +31,7 @@ diff --git a/configure.ac b/configure.ac index 0ab4430..89e422a 100644 --- a/configure.ac +++ b/configure.ac -@@ -1610,6 +1610,19 @@ if test "$have_pthread_t" = yes ; then +@@ -2258,6 +2258,19 @@ if test "$have_pthread_t" = yes ; then #endif ]) fi @@ -55,7 +55,7 @@ diff --git a/configure b/configure index b107bf2..59f80cb 100755 --- a/configure +++ b/configure -@@ -7564,6 +7564,35 @@ _ACEOF +@@ -8981,6 +8981,35 @@ _ACEOF fi @@ -95,7 +95,7 @@ diff --git a/pyconfig.h.in b/pyconfig.h.in index cf0ea1f..0bd8387 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in -@@ -973,6 +973,9 @@ +@@ -1238,6 +1238,9 @@ /* Define if POSIX semaphores aren't enabled on your system */ #undef POSIX_SEMAPHORES_NOT_ENABLED diff --git a/build/pkgs/python3/patches/3.4.5-issue13756.patch b/build/pkgs/python3/patches/3.4.5-issue13756.patch index 7ac330d7aa0..6d35b4eebec 100644 --- a/build/pkgs/python3/patches/3.4.5-issue13756.patch +++ b/build/pkgs/python3/patches/3.4.5-issue13756.patch @@ -4,7 +4,7 @@ diff --git a/Lib/distutils/command/build_ext.py b/Lib/distutils/command/build_ex index acbe648..6bedbb2 100644 --- a/Lib/distutils/command/build_ext.py +++ b/Lib/distutils/command/build_ext.py -@@ -695,10 +695,6 @@ class build_ext(Command): +@@ -715,10 +715,6 @@ class build_ext(Command): return ext.libraries + [pythonlib] else: return ext.libraries @@ -19,7 +19,7 @@ diff --git a/Makefile.pre.in b/Makefile.pre.in index e19fc00..87b6dad 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -636,9 +636,9 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ +@@ -672,9 +672,9 @@ $(PYTHONFRAMEWORKDIR)/Versions/$(VERSION)/$(PYTHONFRAMEWORK): \ $(LN) -fsn Versions/Current/$(PYTHONFRAMEWORK) $(PYTHONFRAMEWORKDIR)/$(PYTHONFRAMEWORK) $(LN) -fsn Versions/Current/Resources $(PYTHONFRAMEWORKDIR)/Resources @@ -35,7 +35,7 @@ diff --git a/Modules/makesetup b/Modules/makesetup index 8b5cc28..abc7eee 100755 --- a/Modules/makesetup +++ b/Modules/makesetup -@@ -91,7 +91,7 @@ CYGWIN*) if test $libdir = . +@@ -92,7 +92,7 @@ CYGWIN*) if test $libdir = . else ExtraLibDir='$(LIBPL)' fi diff --git a/build/pkgs/python3/patches/3.4.5-struct.patch b/build/pkgs/python3/patches/3.4.5-struct.patch index 3b677a72e90..6138b1359b4 100644 --- a/build/pkgs/python3/patches/3.4.5-struct.patch +++ b/build/pkgs/python3/patches/3.4.5-struct.patch @@ -3,6 +3,15 @@ as been fixed upstream for Python 3.7; see https://bugs.python.org/issue21124 diff -r b244bf74b638 Modules/_struct.c --- a/Modules/_struct.c Sun Oct 02 13:49:05 2016 +0300 +++ b/Modules/_struct.c Sun Oct 02 19:54:56 2016 +0900 +@@ -1627,7 +1627,7 @@ unpackiter_iternext(unpackiterobject *self) + } + + static PyTypeObject unpackiter_type = { +- PyVarObject_HEAD_INIT(&PyType_Type, 0) ++ PyVarObject_HEAD_INIT(NULL, 0) + "unpack_iterator", /* tp_name */ + sizeof(unpackiterobject), /* tp_basicsize */ + 0, @@ -2301,6 +2301,9 @@ if (PyType_Ready(&PyStructType) < 0) return NULL; diff --git a/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch index c2ee6104051..e0c76403d52 100644 --- a/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch +++ b/build/pkgs/python3/patches/3.5.2-struct_siginfo_si_band.patch @@ -5,7 +5,7 @@ diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c index 753d987..c69c0ce 100644 --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c -@@ -962,7 +962,11 @@ fill_siginfo(siginfo_t *si) +@@ -957,7 +957,11 @@ fill_siginfo(siginfo_t *si) PyStructSequence_SET_ITEM(result, 4, _PyLong_FromUid(si->si_uid)); PyStructSequence_SET_ITEM(result, 5, PyLong_FromLong((long)(si->si_status))); @@ -39,15 +39,13 @@ index 2fbe259..8281f95 100755 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for time.h that defines altzone" >&5 $as_echo_n "checking for time.h that defines altzone... " >&6; } diff --git a/configure.ac b/configure.ac -index 8ef1760..6bfee80 100644 +index 9eacf52..b4da665 100644 --- a/configure.ac +++ b/configure.ac -@@ -3692,7 +3692,9 @@ - AC_CHECK_MEMBERS([struct stat.st_flags]) - AC_CHECK_MEMBERS([struct stat.st_gen]) - AC_CHECK_MEMBERS([struct stat.st_birthtime]) --AC_STRUCT_ST_BLOCKS -+AC_CHECK_MEMBERS([struct stat.st_blocks]) +@@ -3943,6 +3943,8 @@ AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_passwd], [], [], [[ + #include + #include + ]]) +# Issue #21085: In Cygwin, siginfo_t does not have si_band field. +AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[#include ]]) @@ -57,7 +55,7 @@ diff --git a/pyconfig.h.in b/pyconfig.h.in index 6304a8e..eda0cf9 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in -@@ -837,6 +837,9 @@ +@@ -856,6 +856,9 @@ /* Define to 1 if you have the `sigaltstack' function. */ #undef HAVE_SIGALTSTACK diff --git a/build/pkgs/python3/patches/cygwin-ctypes.patch b/build/pkgs/python3/patches/cygwin-ctypes.patch index 7b2f0717733..d3a0cc9053e 100644 --- a/build/pkgs/python3/patches/cygwin-ctypes.patch +++ b/build/pkgs/python3/patches/cygwin-ctypes.patch @@ -1,9 +1,11 @@ This patch is needed for the ctypes library to get the correct name of the libpython DLL, and is related to https://bugs.python.org/issue13756 ---- a/Lib/ctypes/__init__.py 2017-03-21 15:16:01.564958900 +0100 -+++ b/Lib/ctypes/__init__.py 2017-03-21 15:18:10.476792300 +0100 -@@ -430,7 +430,8 @@ - if _os.name in ("nt", "ce"): +diff --git a/Lib/ctypes/__init__.py.orig b/Lib/ctypes/__init__.py +index f870968..8c87b7c 100644 +--- a/Lib/ctypes/__init__.py ++++ b/Lib/ctypes/__init__.py +@@ -431,7 +431,8 @@ pydll = LibraryLoader(PyDLL) + if _os.name == "nt": pythonapi = PyDLL("python dll", None, _sys.dllhandle) elif _sys.platform == "cygwin": - pythonapi = PyDLL("libpython%d.%d.dll" % _sys.version_info[:2]) diff --git a/build/pkgs/python3/patches/cygwin-readline.patch b/build/pkgs/python3/patches/cygwin-readline.patch index 220f1664744..5702428ff50 100644 --- a/build/pkgs/python3/patches/cygwin-readline.patch +++ b/build/pkgs/python3/patches/cygwin-readline.patch @@ -6,7 +6,7 @@ the appropriate alternative). Also adds the correct link flags for curses on Cygwin. --- a/setup.py 2017-03-20 17:39:35.351935000 +0100 +++ b/setup.py 2017-03-20 18:16:33.670791400 +0100 -@@ -686,7 +686,8 @@ +@@ -719,7 +719,8 @@ if not os.path.exists(self.build_temp): os.makedirs(self.build_temp) # Determine if readline is already linked against curses or tinfo. @@ -16,7 +16,7 @@ Also adds the correct link flags for curses on Cygwin. if cross_compiling: ret = os.system("%s -d %s | grep '(NEEDED)' > %s" \ % (sysconfig.get_config_var('READELF'), -@@ -1322,6 +1323,9 @@ +@@ -1371,6 +1371,9 @@ if curses_library.startswith('ncurses'): curses_libs = [curses_library] diff --git a/build/pkgs/python3/patches/no_strict_proto-issue_5755.patch b/build/pkgs/python3/patches/no_strict_proto-issue_5755.patch index 153972fc3f4..e511dbdb9e9 100644 --- a/build/pkgs/python3/patches/no_strict_proto-issue_5755.patch +++ b/build/pkgs/python3/patches/no_strict_proto-issue_5755.patch @@ -1,7 +1,7 @@ diff -ru src/configure.ac b/configure.ac --- src/configure.ac 2013-04-06 16:02:41.000000000 +0200 +++ b/configure.ac 2013-04-11 18:11:17.947929754 +0200 -@@ -1047,9 +1047,6 @@ +@@ -1451,9 +1451,6 @@ then case $GCC in yes) @@ -14,7 +14,7 @@ diff -ru src/configure.ac b/configure.ac diff -ru src/configure b/configure --- src/configure 2013-04-06 16:02:41.000000000 +0200 +++ b/configure 2013-04-11 18:11:25.737930322 +0200 -@@ -5914,9 +5914,6 @@ +@@ -6843,9 +6843,6 @@ then case $GCC in yes) diff --git a/build/pkgs/python3/patches/permissions.patch b/build/pkgs/python3/patches/permissions.patch index d982758d9e1..553f879ddf3 100644 --- a/build/pkgs/python3/patches/permissions.patch +++ b/build/pkgs/python3/patches/permissions.patch @@ -1,6 +1,6 @@ --- src.orig/Makefile.pre.in 2013-04-06 15:02:34.000000000 +0100 +++ src/Makefile.pre.in 2013-04-07 12:59:46.675111329 +0100 -@@ -59,7 +59,7 @@ +@@ -70,7 +70,7 @@ # Shared libraries must be installed with executable mode on some systems; # rather than figuring out exactly which, we always give them executable mode. # Also, making them read-only seems to be a good idea... diff --git a/build/pkgs/python3/patches/pyport-apple-c++.patch b/build/pkgs/python3/patches/pyport-apple-c++.patch deleted file mode 100644 index 61aea6e2cef..00000000000 --- a/build/pkgs/python3/patches/pyport-apple-c++.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- a/Include/pyport.h 2017-04-07 13:26:39.000000000 -0700 -+++ b/Include/pyport.h 2017-04-07 13:27:54.000000000 -0700 -@@ -688,6 +688,12 @@ - #endif - - #ifdef _PY_PORT_CTYPE_UTF8_ISSUE -+#ifndef __cplusplus -+ /* The workaround below is unsafe in C++ because -+ * the defines these symbols as real functions, -+ * with a slightly different signature. -+ * See issue #10910 -+ */ - #include - #include - #undef isalnum -@@ -705,6 +711,7 @@ - #undef toupper - #define toupper(c) towupper(btowc(c)) - #endif -+#endif - - - /* Declarations for symbol visibility. diff --git a/build/pkgs/python3/patches/sdist.patch b/build/pkgs/python3/patches/sdist.patch index c6b36df6046..552d265474f 100644 --- a/build/pkgs/python3/patches/sdist.patch +++ b/build/pkgs/python3/patches/sdist.patch @@ -1,6 +1,6 @@ --- src/Lib/distutils/command/sdist.py.orig 2011-05-20 15:24:44.936515549 +1200 +++ src/Lib/distutils/command/sdist.py 2011-05-20 15:25:54.920519189 +1200 -@@ -336,7 +336,7 @@ +@@ -317,7 +317,7 @@ * the build tree (typically "build") * the release tree itself (only an issue if we ran "sdist" previously with --keep-temp, or it aborted) @@ -9,7 +9,7 @@ """ build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() -@@ -351,7 +351,7 @@ +@@ -330,7 +330,7 @@ else: seps = '/' diff --git a/build/pkgs/tachyon/patches/Make-config.patch b/build/pkgs/tachyon/patches/Make-config.patch index 21cf0c7c6b9..c5ef209c73a 100644 --- a/build/pkgs/tachyon/patches/Make-config.patch +++ b/build/pkgs/tachyon/patches/Make-config.patch @@ -71,7 +71,7 @@ -PNGLIB= +USEPNG= -DUSEPNG +PNGINC= -I$(SAGE_LOCAL)/include -+PNGLIB= -L$(SAGE_LOCAL)/lib -lpng12 -lz ++PNGLIB= -L$(SAGE_LOCAL)/lib -lpng16 -lz -# Uncomment the following lines to enable PNG support -#USEPNG= -DUSEPNG diff --git a/build/pkgs/zlib/checksums.ini b/build/pkgs/zlib/checksums.ini index 51f394e2518..4b1b46adcaa 100644 --- a/build/pkgs/zlib/checksums.ini +++ b/build/pkgs/zlib/checksums.ini @@ -1,4 +1,4 @@ -tarball=zlib-VERSION.tar.bz2 -sha1=e63277b761e138a75f8c6a9ededb8d95198739a7 -md5=72106d8198c120d46676dd2f15cf3685 -cksum=3965901566 +tarball=zlib-VERSION.tar.gz +sha1=e6d119755acdf9104d7ba236b1242696940ed6dd +md5=1c9f62f0778697a09d36121ead88e08e +cksum=2150653187 diff --git a/build/pkgs/zlib/package-version.txt b/build/pkgs/zlib/package-version.txt index f09a5d00bfb..c11470058fa 100644 --- a/build/pkgs/zlib/package-version.txt +++ b/build/pkgs/zlib/package-version.txt @@ -1 +1 @@ -1.2.8.p0 +1.2.11 diff --git a/build/pkgs/zlib/patches/cygwin_symbols.patch b/build/pkgs/zlib/patches/cygwin_symbols.patch deleted file mode 100644 index c611accae4f..00000000000 --- a/build/pkgs/zlib/patches/cygwin_symbols.patch +++ /dev/null @@ -1,10 +0,0 @@ -diff -druN src.orig/win32/zlib.def src/win32/zlib.def ---- src.orig/win32/zlib.def 2013-08-06 23:12:37.370530629 +0200 -+++ src/win32/zlib.def 2013-08-06 23:13:09.034279643 +0200 -@@ -83,4 +83,5 @@ - inflateUndermine - inflateResetKeep - deflateResetKeep -- gzopen_w -+; disable the following for Cygwin -+; gzopen_w diff --git a/src/Makefile.in b/src/Makefile.in index bf04a2c52e6..eba5e9a4c75 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -31,15 +31,18 @@ all: sage ## [sagelib-7.4.beta4] running install ## [sagelib-7.4.beta4] error: install-base or install-platbase supplied, but installation scheme is incomplete sage: - (cd $(srcdir) \ - && export SAGE_ROOT=/doesnotexist \ - SAGE_SRC=/doesnotexist \ - SAGE_SRC_ROOT=/doesnotexist \ - SAGE_DOC_SRC=/doesnotexist \ - SAGE_BUILD_DIR=/doesnotexist \ - SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ - SAGE_CYTHONIZED=$(abs_builddir)/build/cythonized \ - && sage-python23 -u setup.py --no-user-cfg build install) + cd $(srcdir) && export \ + SAGE_ROOT=/doesnotexist \ + SAGE_SRC=/doesnotexist \ + SAGE_SRC_ROOT=/doesnotexist \ + SAGE_DOC_SRC=/doesnotexist \ + SAGE_BUILD_DIR=/doesnotexist \ + SAGE_PKGS=$(abs_top_srcdir)/build/pkgs \ + SAGE_CYTHONIZED=$(abs_builddir)/build/cythonized \ + && sage-python23 -u setup.py --no-user-cfg build install + if [ "$$UNAME" = "CYGWIN" ]; then \ + sage-rebase.sh "$$SAGE_LOCAL" 2>/dev/null; \ + fi clean: @echo "Deleting Sage library build artifacts..." diff --git a/src/bin/sage b/src/bin/sage index 42d3ab38271..2091144bd04 100755 --- a/src/bin/sage +++ b/src/bin/sage @@ -27,7 +27,8 @@ usage() { echo " 'default', 'sagenb', 'jupyter', and 'export')" echo " -n, --notebook -- shortcut for --notebook=default" echo " -optional -- list all optional packages that can be installed" - echo " -python [...] -- run the Python interpreter" + echo " -python [...] -- run the Python 2 interpreter" + echo " -python3 [...] -- run the Python 3 interpreter" echo " -R [...] -- run Sage's R with given arguments" echo " -singular [...] -- run Sage's singular with given arguments" echo " -sqlite3 [...] -- run Sage's sqlite3 with given arguments" @@ -572,6 +573,16 @@ if [ "$1" = '-python' -o "$1" = '--python' ]; then exec python "$@" fi +if [ "$1" = '-python2' -o "$1" = '--python2' ]; then + shift + exec python2 "$@" +fi + +if [ "$1" = '-python3' -o "$1" = '--python3' ]; then + shift + exec python3 "$@" +fi + if [ "$1" = '-R' -o "$1" = '--R' ]; then shift exec R "$@" diff --git a/src/bin/sage-banner b/src/bin/sage-banner index 4ee00d5aff2..8abb4ce9de3 100644 --- a/src/bin/sage-banner +++ b/src/bin/sage-banner @@ -1,5 +1,5 @@ ┌────────────────────────────────────────────────────────────────────┐ -│ SageMath version 8.0.beta5, Release Date: 2017-05-04 │ +│ SageMath version 8.0.beta8, Release Date: 2017-05-24 │ │ Type "notebook()" for the browser-based notebook interface. │ │ Type "help()" for help. │ └────────────────────────────────────────────────────────────────────┘ diff --git a/src/bin/sage-rebase.bat b/src/bin/sage-rebase.bat index 68777b06dfe..de481343786 100755 --- a/src/bin/sage-rebase.bat +++ b/src/bin/sage-rebase.bat @@ -1,24 +1,52 @@ @echo off rem Author: rem * Jean-Pierre Flori +rem * Erik M. Bray rem -rem Rebase all dlls in the SAGE_ROOT directory (and its subdirectories), +rem Rebase all dlls in the SAGE_LOCAL directory (and its subdirectories), rem but do not touch the ones already stored in the system database, rem and do not update it. rem Note that subsequent calls to 'rebaseall' will not update the Sage dlls. rem -rem Invoke this script from a Windows command prompt, -rem after adjusting SAGE_ROOT to the Windows location of the Sage directory, +rem Invoke this script from a Windows command prompt rem and, if Cygwin is installed in a non-standard location, rem adjusting CYGWIN_ROOT. -set CYGWIN_ROOT=C:\cygwin\ -set SAGE_ROOT=C:\cygwin\usr\local\sage\ +setlocal ENABLEEXTENSIONS + +set THIS_BIN=%~dp0 + +rem SAGE_LOCAL should be one level up from the bin/ this script is in +rem This is about the most elegant way to do this I can find thanks +rem http://stackoverflow.com/a/33404867/982257 +call :NORMALIZEPATH "%THIS_BIN%.." +set SAGE_LOCAL=%RETVAL% + +rem Cygwin saves its installation root here +rem If there is more than one Cygwin installation on the system this +rem will just pick up the first one +call :READREGISTRY HKEY_LOCAL_MACHINE\SOFTWARE\Cygwin\setup rootdir +set CYGWIN_ROOT=%RETVAL% rem Make sure bash can be called from MSDOS prompt: path %CYGWIN_ROOT%\bin;%path% rem Suppress warning about MSDOS-style path: set CYGWIN=%CYGWIN% nodosfilewarning rem Call the bash script to do the real work: -cd %SAGE_ROOT% -bash .\local\bin\sage-rebase.sh +cd %SAGE_LOCAL% +bash bin\sage-rebase.sh + + +:: ========== FUNCTIONS ========== +exit /B + +:READREGISTRY + for /F "usebackq skip=2 tokens=3" %%V in (`reg query %1 /v %2 2^>nul`) do ( + set RETVAL=%%V + break + ) + exit /B + +:NORMALIZEPATH + set RETVAL=%~dpfn1 + exit /B diff --git a/src/bin/sage-rebase.sh b/src/bin/sage-rebase.sh index 68f425cd1d6..79b0b4bdc1c 100755 --- a/src/bin/sage-rebase.sh +++ b/src/bin/sage-rebase.sh @@ -2,15 +2,74 @@ # Author: # * Jean-Pierre Flori +# * Gary Zablackis +# * Dmitrii Pasechnik +# * Erik M. Bray # -# Rebase all dlls in the SAGE_ROOT directory (and its subdirectories), +# Rebase all dlls in the SAGE_LOCAL directory (and its subdirectories), # but do not touch the ones already stored in the system database, # and do not update it. # Note that subsequent calls to 'rebaseall' will not update the Sage dlls. # -# Invoke this script from a shell after going to the SAGE_ROOT directory. +# Usage: +# +# sage-rebase.sh [--all] [sage_local] [-- additional_flags] +# +# Positional arguments: +# +# sage_local optionally, provide the path to the $SAGE_LOCAL directory to +# search for DLLs to rebase; otherwise the current working +# directory is assumed to be $SAGE_LOCAL unless $SAGE_LOCAL +# is already set in the environment +# +# Optional arguments: +# +# --all run rebaseall instead of rebase (originally the call to +# rebaseall was in the sage-rebaseall.sh script, but now that is +# just a wrapper around this script) +# +# -- additional arguments passed in after -- are passed to the +# rebase/rebaseall call in addition to the default arguments +# passed in by this script +# +# Invoke this script from a shell after going to the SAGE_LOCAL directory. +ALL=0 +REBASEFLAGS="" + +while [ $# -gt 0 ]; do + case "$1" in + --all) + ALL=1 + ;; + --) + shift + REBASEFLAGS="$REBASEFLAGS $1" + ;; + *) + if [ -z "$REBASEFLAGS" ]; then + SAGE_LOCAL="${1%/}" + else + REBASEFLAGS="$REBASEFLAGS $1" + fi + ;; + esac + shift +done + +if [ -z "$SAGE_LOCAL" ]; then + # Assume we are in $SAGE_LOCAL by default (the old behavior of this script) + SAGE_LOCAL=. +fi + +FINDFLAGS="-type f ( -name *.dll -o -name *.so -o -name *.fas ) -print" +FINDFLAGS="$FINDFLAGS -o -path "$SAGE_LOCAL"/var/tmp -prune" echo "Getting list of dlls. This may take a while..." -/bin/find local -name "*.dll" -o -name "*.so" > /tmp/sage-dlls.lst +/bin/find "$SAGE_LOCAL" $FINDFLAGS > /tmp/sage-dlls.lst echo "Now rebasing..." -/bin/rebase -O -T /tmp/sage-dlls.lst + +if [ $ALL -eq 0 ]; then + /bin/rebase -O -T /tmp/sage-dlls.lst $REBASEFLAGS +else + /bin/rebaseall -s dll -s exe -s so -s fas -T /tmp/sage-dlls.lst $REBASEFLAGS +fi diff --git a/src/bin/sage-rebaseall.bat b/src/bin/sage-rebaseall.bat index 75122e335a3..0ae927f5e64 100755 --- a/src/bin/sage-rebaseall.bat +++ b/src/bin/sage-rebaseall.bat @@ -2,28 +2,55 @@ rem Authors: rem * Dmitrii Pasechnik rem * Jean-Pierre Flori +rem * Erik M. Bray rem -rem Rebase all dlls in the SAGE_ROOT directory (and its subdirectories), +rem Rebase all dlls in the SAGE_LOCAL directory (and its subdirectories), rem as well as the ones already stored in the system database, rem and update the database. rem This system-wide database is located in '/etc/rebase.db.i386' and rem includes the Cygwin system dlls. rem -rem Invoke this script from a Windows command prompt, -rem after adjusting SAGE_ROOT to the Windows location of the Sage directory, +rem Invoke this script from a Windows command prompt rem and, if Cygwin is installed in a non-standard location, rem adjusting CYGWIN_ROOT. +rem rem Ensure that no other Cygwin processes are currently running. rem Note that you need write access to the system-wide rebase database rem (which usually means admin rights). -set CYGWIN_ROOT=C:\cygwin\ -set SAGE_ROOT=C:\cygwin\usr\local\sage\ +set THIS_BIN=%~dp0 + +rem SAGE_LOCAL should be one level up from the bin/ this script is in +rem This is about the most elegant way to do this I can find thanks +rem http://stackoverflow.com/a/33404867/982257 +call :NORMALIZEPATH "%THIS_BIN%.." +set SAGE_LOCAL=%RETVAL% + +rem Cygwin saves its installation root here +rem If there is more than one Cygwin installation on the system this +rem will just pick up the first one +call :READREGISTRY HKEY_LOCAL_MACHINE\SOFTWARE\Cygwin\setup rootdir +set CYGWIN_ROOT=%RETVAL% rem Make sure dash can be called from MSDOS prompt: path %CYGWIN_ROOT%\bin;%path% rem Suppress warning about MSDOS-style path: set CYGWIN=%CYGWIN% nodosfilewarning rem Call the dash script to do the real work: -cd %SAGE_ROOT% -dash .\local\bin\sage-rebaseall.sh +cd %SAGE_LOCAL% +dash bin\sage-rebaseall.sh + + +:: ========== FUNCTIONS ========== +exit /B + +:READREGISTRY + for /F "usebackq skip=2 tokens=3" %%V in (`reg query %1 /v %2 2^>nul`) do ( + set RETVAL=%%V + break + ) + exit /B + +:NORMALIZEPATH + set RETVAL=%~dpfn1 + exit /B diff --git a/src/bin/sage-rebaseall.sh b/src/bin/sage-rebaseall.sh index 09398ca8fbf..aa0ab9df20f 100755 --- a/src/bin/sage-rebaseall.sh +++ b/src/bin/sage-rebaseall.sh @@ -5,18 +5,16 @@ # * Dmitrii Pasechnik # * Jean-Pierre Flori # -# Rebase all dlls in the SAGE_ROOT directory (and its subdirectories) +# Rebase all dlls in the SAGE_LOCAL directory (and its subdirectories) # as well as the ones already stored in the system database, # and update the database. # This system-wide database is located in '/etc/rebase.db.i386' and # includes the Cygwin system dlls. # -# Invoke this script from a dash shell after going to the SAGE_ROOT directory. +# Invoke this script from a dash shell after going to the SAGE_LOCAL directory. # Ensure that no other Cygwin processes are currently running. # Note that you need write access to the system-wide rebase database # (which usually means admin rights). -echo "Getting list of dlls. This may take a while..." -/bin/find local -name "*.dll" -o -name "*.so" > /tmp/sage-dlls.lst -echo "Now rebasing..." -/bin/rebaseall -T /tmp/sage-dlls.lst +DIR=$(dirname "$(readlink -f "$0")") +exec "$DIR"/sage-rebase.sh" --all $@ diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 3dd582b0ed1..f11eedfcf82 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='8.0.beta5' -SAGE_RELEASE_DATE='2017-05-04' +SAGE_VERSION='8.0.beta8' +SAGE_RELEASE_DATE='2017-05-24' diff --git a/src/doc/en/a_tour_of_sage/index.rst b/src/doc/en/a_tour_of_sage/index.rst index ef3222ef4c1..d998c42b836 100644 --- a/src/doc/en/a_tour_of_sage/index.rst +++ b/src/doc/en/a_tour_of_sage/index.rst @@ -1,3 +1,5 @@ +.. _a-tour-of-sage: + ============== A Tour of Sage ============== diff --git a/src/doc/en/constructions/index.rst b/src/doc/en/constructions/index.rst index 6051fcafdcc..b6e660c245c 100644 --- a/src/doc/en/constructions/index.rst +++ b/src/doc/en/constructions/index.rst @@ -2,6 +2,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _constructions: + Welcome to the Sage Constructions documentation! ================================================ diff --git a/src/doc/en/developer/coding_in_python.rst b/src/doc/en/developer/coding_in_python.rst index bd37d0fb53c..4cd2570e350 100644 --- a/src/doc/en/developer/coding_in_python.rst +++ b/src/doc/en/developer/coding_in_python.rst @@ -522,19 +522,17 @@ documentation for more information on its behaviour and optional arguments. lazy_import('sage.new.module.name', '*', deprecation=666) lazy_import('sage.other.module', ('func1', 'func2'), deprecation=666) -* **Remove a name from the global namespace:** the function - :func:`~sage.misc.superseded.deprecated_callable_import` imports an object into - the global namespace. Any user who calls it sees a message inviting him to - import the object manually:: - - from sage.misc.superseded import deprecated_callable_import - deprecated_callable_import(666, - 'sage.combinat.name_of_the_module', - globals(), - locals(), - ["name_of_the_function"]) - - Alternatively, a lazy_import with deprecation would also work in this case. +* **Remove a name from a global namespace:** this is when you want to + remove a name from a global namespace (say, ``sage.all`` or some + other ``all.py`` file) but you want to keep the functionality + available with an explicit import. + This case is similar as the previous one: use a lazy import with + deprecation. One detail: in this case, you don't want the name + ``lazy_import`` to be visible in the global namespace, so we add + a leading underscore:: + + from sage.misc.lazy_import import lazy_import as _lazy_import + _lazy_import('sage.some.package', 'some_function', deprecation=666) * **Any other case:** if none of the cases above apply, call :func:`~sage.misc.superseded.deprecation` in the function that you want to diff --git a/src/doc/en/developer/index.rst b/src/doc/en/developer/index.rst index 8726af7f7ae..6647e3a06f0 100644 --- a/src/doc/en/developer/index.rst +++ b/src/doc/en/developer/index.rst @@ -1,3 +1,5 @@ +.. _developers-guide: + ====================================== Welcome to the Sage Developer's Guide! ====================================== diff --git a/src/doc/en/faq/index.rst b/src/doc/en/faq/index.rst index 5ccf5ddbbaa..31d5cb59040 100644 --- a/src/doc/en/faq/index.rst +++ b/src/doc/en/faq/index.rst @@ -3,6 +3,8 @@ .. your liking, but it should at least contain the root `toctree` .. directive. +.. _faq: + Welcome to the Sage FAQ! ======================== diff --git a/src/doc/en/installation/index.rst b/src/doc/en/installation/index.rst index ec1ce3d302c..06ebb3d7a26 100644 --- a/src/doc/en/installation/index.rst +++ b/src/doc/en/installation/index.rst @@ -1,3 +1,5 @@ +.. _installation-guide: + Welcome to the Sage Installation Guide ====================================== diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 49684ee743f..ef483873009 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -153,10 +153,6 @@ variable :envvar:`SAGE_INSTALL_GCC`, see :ref:`section_compilers` and Other notes ^^^^^^^^^^^ -Although some of Sage is written in `Python `_, you do -not need Python pre-installed on your computer, since the Sage installation -includes virtually everything you need. - After extracting the Sage tarball, the subdirectory :file:`upstream` contains the source distributions for everything on which Sage depends. If cloned from a git repository, the upstream tarballs will be downloaded, @@ -359,28 +355,23 @@ but provide additional capabilities: - **dvipng**. - **ffmpeg**. - **ImageMagick**. -- **latex**: highly recommended. +- **LaTeX**: highly recommended. It is highly recommended that you have -`Latex `_ +`LaTeX `_ installed, but it is not required. - -The most popular packaging is `TeX Live `_ , -which you can install locally inside Sage with the commands:: - - sage -sh -c '$SAGE_ROOT/src/ext/texlive/texlive-install' - +The most popular packaging is `TeX Live `_, +which can be installed following the directions on their web site. On Linux systems you can alternatively install your distribution's texlive packages:: sudo apt-get install texlive # debian sudo yum install texlive # redhat -or similar commands. In addition to the base texlive install you will -probably need a number of optional texlive packages, for example +or similar commands. In addition to the base TeX Live install, you may +need some optional TeX Live packages, for example country-specific babel packages for the localized Sage -documentation. The required texlive packages are listed in -``SAGE_ROOT/src/ext/texlive/package-list.txt``. +documentation. If you don't have either ImageMagick or ffmpeg, you won't be able to view animations. @@ -1033,7 +1024,7 @@ Here are some of the more commonly used variables affecting the build process: - :envvar:`SAGE_PROFILE` - controls profiling support. If this is set to ``yes``, profiling support is enabled where possible. Note that - Python-level profiling is always avaliable; This option enables + Python-level profiling is always available; This option enables profiling in Cython modules. - :envvar:`SAGE_SPKG_INSTALL_DOCS` - if set to ``yes``, then install @@ -1439,47 +1430,6 @@ the directory where you want to install Sage. processes. You can also omit ``long`` to skip tests which take a long time. -Some common problems --------------------- - -ATLAS -~~~~~ - -Usually Sage will build ATLAS with architectural defaults that are not tuned -to your particular CPU. -In particular, if your CPU has powersaving enabled then no accurate timings -can be made to tune the ATLAS build for your hardware. -If BLAS performance is critical for you, you must recompile ATLAS after -installing Sage either with architecture settings that match your hardware, -or run through ATLAS' automatic tuning process where timings of different -implementations are compared and the best choice used to build a custom ATLAS -library. -To do so, you have to - -- Leave the computer idle while you are reinstalling ATLAS. - Most of ATLAS will intentionally only compile/run on a single core. - Accurate timings of cache edges require that the CPU is otherwise idle. - -- Make sure that CPU powersaving mode (that is, anything but the - ``performance`` CPU scaling governor in Linux) is turned off when building - ATLAS. - This requires administrator privileges. - -- If your architecture is listed in :envvar:`SAGE_ATLAS_ARCH`, you should set - it as it can help ATLAS in narrowing down the timing search. - -To help you disable CPU power saving, Sage includes an ``atlas-config`` script -that will turn off CPU powersave and rebuild ATLAS. -The script will call ``sudo`` to gain the necessary rights, which may prompt -you for your password. For example:: - - atlas-config - -will run through the full automated tuning, and:: - - SAGE_ATLAS_ARCH=Corei2,AVX,SSE3,SSE2,SSE1 atlas-config - -would be appropriate if you have a Core i3/5/7 processor with AVX support. diff --git a/src/doc/en/prep/Advanced-2DPlotting.rst b/src/doc/en/prep/Advanced-2DPlotting.rst index 00967b9e683..5d2042684ca 100644 --- a/src/doc/en/prep/Advanced-2DPlotting.rst +++ b/src/doc/en/prep/Advanced-2DPlotting.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-advanced-2dplotting: + Tutorial for Advanced 2d Plotting ================================= diff --git a/src/doc/en/prep/Calculus.rst b/src/doc/en/prep/Calculus.rst index 81b0fed4346..53e90de0764 100644 --- a/src/doc/en/prep/Calculus.rst +++ b/src/doc/en/prep/Calculus.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-calculus: + Tutorial for Calculus ===================== diff --git a/src/doc/en/prep/Intro-Tutorial.rst b/src/doc/en/prep/Intro-Tutorial.rst index 82aef6ad728..2def3bef370 100644 --- a/src/doc/en/prep/Intro-Tutorial.rst +++ b/src/doc/en/prep/Intro-Tutorial.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-intro-tutorial: + Introductory Sage Tutorial ========================== diff --git a/src/doc/en/prep/Logging-On.rst b/src/doc/en/prep/Logging-On.rst index b9216cc69f4..24c356b3eba 100644 --- a/src/doc/en/prep/Logging-On.rst +++ b/src/doc/en/prep/Logging-On.rst @@ -2,6 +2,7 @@ .. linkall +.. _prep-logging-on: .. _logging-on: Logging on to a Sage Server and Creating a Worksheet diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index 595216d4411..cafca74b2d5 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-programming: + Sage Introductory Programming Tutorial ====================================== diff --git a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst index dfe894a4602..042b786420c 100644 --- a/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst +++ b/src/doc/en/prep/Quickstarts/Abstract-Algebra.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-abstract-algebra: + Sage Quickstart for Abstract Algebra ==================================== diff --git a/src/doc/en/prep/Quickstarts/Differential-Equations.rst b/src/doc/en/prep/Quickstarts/Differential-Equations.rst index 539b21344fa..3c023424db8 100644 --- a/src/doc/en/prep/Quickstarts/Differential-Equations.rst +++ b/src/doc/en/prep/Quickstarts/Differential-Equations.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-differential-equations: + Sage Quickstart for Differential Equations ========================================== diff --git a/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst b/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst index aa7a308854a..9406595ddec 100644 --- a/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst +++ b/src/doc/en/prep/Quickstarts/Graphs-and-Discrete.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-graphs-and-discrete: + Sage Quickstart for Graph Theory and Discrete Mathematics ========================================================= diff --git a/src/doc/en/prep/Quickstarts/Interact.rst b/src/doc/en/prep/Quickstarts/Interact.rst index 9898017ee22..666ad9023ce 100644 --- a/src/doc/en/prep/Quickstarts/Interact.rst +++ b/src/doc/en/prep/Quickstarts/Interact.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-interact: + Sage Interact Quickstart ======================== diff --git a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst index e14d7896b41..3f6b6feb3c0 100644 --- a/src/doc/en/prep/Quickstarts/Linear-Algebra.rst +++ b/src/doc/en/prep/Quickstarts/Linear-Algebra.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-linear-algebra: + Sage Quickstart for Linear Algebra ================================== @@ -188,7 +190,7 @@ for our purposes. :: sage: type( ring_vec ) - + sage: type( field_vec ) diff --git a/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst b/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst index dc534d0eef7..c5db7bfd4d1 100644 --- a/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst +++ b/src/doc/en/prep/Quickstarts/Multivariable-Calculus.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-multivariate-calculus: + Sage Quickstart for Multivariable Calculus ========================================== diff --git a/src/doc/en/prep/Quickstarts/NumAnalysis.rst b/src/doc/en/prep/Quickstarts/NumAnalysis.rst index 69e27e51b9b..70fc163fed8 100644 --- a/src/doc/en/prep/Quickstarts/NumAnalysis.rst +++ b/src/doc/en/prep/Quickstarts/NumAnalysis.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-numerical-analysis: + Sage Quickstart for Numerical Analysis ====================================== diff --git a/src/doc/en/prep/Quickstarts/Number-Theory.rst b/src/doc/en/prep/Quickstarts/Number-Theory.rst index d9596ee7e47..5b1f73d8138 100644 --- a/src/doc/en/prep/Quickstarts/Number-Theory.rst +++ b/src/doc/en/prep/Quickstarts/Number-Theory.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-number-theory: + Sage Quickstart for Number Theory ================================= diff --git a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst index 419d3b121eb..1192d8d84b9 100644 --- a/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst +++ b/src/doc/en/prep/Quickstarts/Statistics-and-Distributions.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-quickstart-statistics-and-distributions: + Sage Quickstart for Statistics ============================== diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index f6494f40da6..e8e74915cfd 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -2,6 +2,8 @@ .. linkall +.. _prep-symbolics-and-basic-plotting: + Tutorial for Symbolics and Plotting =================================== @@ -486,6 +488,6 @@ We close this tutorial with a cool plot that we define *implicitly* as a Graphics3d Object The next tutorial will use all that you have learned about Sage basics, -symbolics, and plotting in a specific mathematical venue \- the calculus -sequence! +symbolics, and plotting in a specific mathematical venue \- the +:ref:`calculus sequence `! diff --git a/src/doc/en/prep/index.rst b/src/doc/en/prep/index.rst index be762634ec1..33c3f3aaf26 100644 --- a/src/doc/en/prep/index.rst +++ b/src/doc/en/prep/index.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _prep-tutorials: + PREP Tutorials ============== diff --git a/src/doc/en/prep/quickstart.rst b/src/doc/en/prep/quickstart.rst index 336d2b7b92c..38167a8ca11 100644 --- a/src/doc/en/prep/quickstart.rst +++ b/src/doc/en/prep/quickstart.rst @@ -3,6 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _prep-quickstart-tutorials: + PREP Quickstart Tutorials ========================= diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst index 4be912c1a72..7f88853c7bf 100644 --- a/src/doc/en/reference/algebras/lie_algebras.rst +++ b/src/doc/en/reference/algebras/lie_algebras.rst @@ -5,11 +5,13 @@ Lie Algebras :maxdepth: 2 sage/algebras/lie_algebras/abelian + sage/algebras/lie_algebras/affine_lie_algebra sage/algebras/lie_algebras/classical_lie_algebra sage/algebras/lie_algebras/examples sage/algebras/lie_algebras/heisenberg sage/algebras/lie_algebras/lie_algebra sage/algebras/lie_algebras/lie_algebra_element + sage/algebras/lie_algebras/poincare_birkhoff_witt sage/algebras/lie_algebras/structure_coefficients sage/algebras/lie_algebras/virasoro diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index 1d4ed57a230..9e51d5b15b6 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -125,6 +125,7 @@ Individual Categories sage/categories/lie_algebras sage/categories/lie_algebras_with_basis sage/categories/lie_groups + sage/categories/loop_crystals sage/categories/l_trivial_semigroups sage/categories/magmas sage/categories/magmas_and_additive_magmas diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 8713d4f31ee..06a3214d6ef 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -65,13 +65,16 @@ Comprehensive Module list sage/combinat/crystals/highest_weight_crystals sage/combinat/crystals/induced_structure sage/combinat/crystals/infinity_crystals - sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/kirillov_reshetikhin sage/combinat/crystals/kyoto_path_model sage/combinat/crystals/letters sage/combinat/crystals/littelmann_path sage/combinat/crystals/monomial_crystals sage/combinat/crystals/multisegments + sage/combinat/crystals/mv_polytopes + sage/combinat/crystals/pbw_crystal + sage/combinat/crystals/pbw_datum + sage/combinat/crystals/polyhedral_realization sage/combinat/crystals/spins sage/combinat/crystals/star_crystal sage/combinat/crystals/tensor_product @@ -213,6 +216,7 @@ Comprehensive Module list sage/combinat/root_system/all sage/combinat/root_system/ambient_space sage/combinat/root_system/associahedron + sage/combinat/root_system/braid_move_calculator sage/combinat/root_system/branching_rules sage/combinat/root_system/cartan_matrix sage/combinat/root_system/cartan_type diff --git a/src/doc/en/reference/functions/index.rst b/src/doc/en/reference/functions/index.rst index 6465c8f81ba..f33b8da08a6 100644 --- a/src/doc/en/reference/functions/index.rst +++ b/src/doc/en/reference/functions/index.rst @@ -8,6 +8,7 @@ Functions sage/functions/trig sage/functions/hyperbolic sage/functions/transcendental + sage/functions/error sage/functions/piecewise sage/functions/spike_function sage/functions/orthogonal_polys diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 3607297d28e..51f598b2e39 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -1,14 +1,13 @@ -******** -Contents -******** +.. _reference-manual: -Welcome to Sage's Reference Manual! +************************************ +Welcome to the Sage Reference Manual +************************************ -This manual is a thematic index of all of `Sage's `_ -features. It also contains many examples that illustrate their use, all of them -systematically tested with each release. - -Enjoy Sage! +This manual contains documentation for (almost) all of `Sage's +`_ features, each illustrated with examples +that are systematically tested with each release. A thematic index is +available below. User Interface ============== @@ -56,7 +55,7 @@ Calculus and Analysis * :doc:`Symbolic Calculus ` * :doc:`Mathematical Constants ` * :doc:`Elementary and Special Functions ` -* :doc:`Asymptotic Expansions ` (experimental) +* :doc:`Asymptotic Expansions ` * :doc:`Numerical Optimization ` Probability and Statistics diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 9b0b6e96d9e..9339004c54b 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -41,6 +41,7 @@ to be aware of the modules described in this chapter. sage/libs/singular/groebner_strategy sage/libs/ppl sage/libs/linbox/linbox + sage/libs/linbox/linbox_flint_interface sage/libs/flint/flint sage/libs/flint/fmpz_poly sage/libs/flint/arith diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 9e4acb5c008..db31cdf7e3e 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -322,6 +322,11 @@ REFERENCES: complexes and posets. I*. Trans. of Amer. Math. Soc. **348** No. 4. (1996) +.. [BZ01] \A. Berenstein, A. Zelevinsky + *Tensor product multiplicities, canonical bases + and totally positive varieties* + Invent. Math. **143** No. 1. (2002), 77-128. + .. _ref-C: **C** @@ -558,6 +563,10 @@ REFERENCES: Functions, in NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/10 +.. [DLMF-Error] \N. M. Temme: 7. Error Functions, Dawson’s and Fresnel + Integrals, in NIST Digital Library of Mathematical + Functions. http://dlmf.nist.gov/7 + .. [DLMF-Struve] \R. B. Paris: 11. Struve and Related Functions, in NIST Digital Library of Mathematical Functions. http://dlmf.nist.gov/11 @@ -668,6 +677,10 @@ REFERENCES: curves". LMS Journal of Computation and Mathematics (2014), volume 17, issue 01, pp. 1-23. +.. [FOS2010] \G. Fourier, M. Okado, A. Schilling. *Perfectness of + Kirillov-Reshetikhin crystals for nonexceptional types*. + Contemp. Math. 506 (2010) 127-143 ( :arxiv:`0811.1604` ) + .. [FP1996] Komei Fukuda, Alain Prodon: Double Description Method Revisited, Combinatorics and Computer Science, volume 1120 of Lecture Notes in Computer Science, page @@ -779,6 +792,10 @@ REFERENCES: .. [GS1999] Venkatesan Guruswami and Madhu Sudan, Improved Decoding of Reed-Solomon Codes and Algebraic-Geometric Codes, 1999 +.. [GT1996] \P. Gianni and B. Trager. "Square-free algorithms in + positive characteristic". Applicable Algebra in Engineering, + Communication and Computing, 7(1), 1-14 (1996) + .. [GT2014] \M.S. Gowda and J. Tao. On the bilinearity rank of a proper cone and Lyapunov-like transformations. Mathematical Programming, 147 (2014) @@ -812,8 +829,8 @@ REFERENCES: function fields and related topics," J. Symbolic Comput. 33 (2002), no. 4, 425--445. -.. [Hig2008] \N. J. Higham, "Functions of matrices: theory and computation", - Society for Industrial and Applied Mathematics (2008). +.. [Hig2008] \N. J. Higham, "Functions of matrices: theory and computation", + Society for Industrial and Applied Mathematics (2008). .. [HJ2004] Tom Hoeholdt and Joern Justesen, A Course In Error-Correcting Codes, EMS, 2004 @@ -940,6 +957,13 @@ REFERENCES: .. [Kal1980] \T. Kaliath, "Linear Systems", Prentice-Hall, 1980, 383--386. +.. [Kam2007] Joel Kamnitzer, + *The crystal structure on the set of Mirković-Vilonen polytopes*, + Adv. Math. **215** (2007), 66-93. + +.. [Kam2010] Joel Kamnitzer, *Mirković-Vilonen cycles and polytopes*, + Ann. Math. (2) **171** (2010), 731-777. + .. [Kan1958] \D. M. Kan, *A combinatorial definition of homotopy groups*, Ann. Math. (2) 67 (1958), 282-312. @@ -959,6 +983,10 @@ REFERENCES: \M. Grötschel, \L Lovász, *Handbook of combinatorics*, Vol. 1, Chapter 18, 1995 +.. [KKMMNN1992] S-J. Kang, M. Kashiwara, K. C. Misra, T. Miwa, T. Nakashima, + and A. Nakayashiki. *Affine crystals and vertex models*. + Int. J. Mod. Phys. A, **7** (suppl. 1A), (1992) pp. 449-484. + .. [KL2008] Chris Kurth and Ling Long, "Computations with finite index subgroups of `{\rm PSL}_2(\ZZ)` using Farey symbols", Advances in algebra and combinatorics, 225--242, World @@ -1500,6 +1528,24 @@ REFERENCES: manifolds and complexes into a cubic lattice", *Uspekhi Mat. Nauk* 47 (1992), 219-220. +.. [SS2015] Anne Schilling and Travis Scrimshaw. + *Crystal structure on rigged configurations and the filling map*. + Electon. J. Combin., **22(1)** (2015) #P1.73. :arxiv:`1409.2920`. + +.. [SS2015II] Ben Salisbury and Travis Scrimshaw. + *A rigged configuration model for* `B(\infty)`. + J. Combin. Theory Ser. A, **133** (2015) pp. 29-75. + :arxiv:`1404.6539`. + +.. [SS2017] Ben Salisbury and Travis Scrimshaw. + *Rigged configurations for all symmetrizable types*. + Electon. J. Combin., **24(1)** (2017) #P1.30. :arxiv:`1509.07833`. + +.. [ST2011] \A. Schilling, P. Tingley. *Demazure crystals, + Kirillov-Reshetikhin crystals, and the energy function*. + Electronic Journal of Combinatorics. **19(2)**. 2012. + :arXiv:`1104.2359` + .. [Sta2007] Stanley, Richard: *Hyperplane Arrangements*, Geometric Combinatorics (E. Miller, V. Reiner, and B. Sturmfels, eds.), IAS/Park City Mathematics Series, vol. 13, @@ -1665,6 +1711,8 @@ REFERENCES: .. [WP-Bessel] :wikipedia:`Bessel_function` +.. [WP-Error] :wikipedia:`Error_function` + .. [WP-Struve] :wikipedia:`Struve_function` .. _ref-X: diff --git a/src/doc/en/reference/repl/options.rst b/src/doc/en/reference/repl/options.rst index a714b73e0b3..e87c58ad1df 100644 --- a/src/doc/en/reference/repl/options.rst +++ b/src/doc/en/reference/repl/options.rst @@ -71,7 +71,8 @@ Command-line options for Sage - ``--M2 [...]`` -- run Sage's Macaulay2 with the given arguments - ``--maxima [...]`` -- run Sage's Maxima with the given arguments - ``--mwrank [...]`` -- run Sage's mwrank with the given arguments -- ``--python [...]`` -- run the Python interpreter +- ``--python [...]``, ``--python2 [...]`` -- run the Python 2 interpreter +- ``--python3 [...]`` -- run the Python 3 interpreter - ``-R [...]`` -- run Sage's R with the given arguments - ``--scons [...]`` -- run Sage's scons - ``--singular [...]`` -- run Sage's singular with the given arguments diff --git a/src/doc/en/thematic_tutorials/coding_theory.rst b/src/doc/en/thematic_tutorials/coding_theory.rst index 155fdbe435b..d7014393137 100644 --- a/src/doc/en/thematic_tutorials/coding_theory.rst +++ b/src/doc/en/thematic_tutorials/coding_theory.rst @@ -491,7 +491,7 @@ We can describe these boundaries in two ways: Static error rate channel creating between 1 and 14 errors, of input and output space Vector space of dimension 40 over Finite Field of size 59 We already know that a channel has a -:meth:`sage.coding.channel_constuctions.Channel.transmit` method which will +:meth:`sage.coding.channel_constructions.Channel.transmit` method which will perform transmission over the channel; in this case it will return the transmitted word with some errors in it. This method will always check if the provided word belongs to @@ -502,7 +502,7 @@ if one is simulating millions of transmissions. For this usage there is :meth:`sage.coding.channel_constructions.Channel.transmit_unsafe` which does the same as -:meth:`sage.coding.channel_constuctions.Channel.transmit` +:meth:`sage.coding.channel_constructions.Channel.transmit` but without checking the input, as illustrated thereafter:: sage: c = C.random_element() @@ -513,7 +513,7 @@ but without checking the input, as illustrated thereafter:: False Note there exists a useful shortcut for -:meth:`sage.coding.channel_constuctions.Channel.transmit` :: +:meth:`sage.coding.channel_constructions.Channel.transmit` :: sage: r = Chan(c) sage: r in C diff --git a/src/doc/en/thematic_tutorials/coercion_and_categories.rst b/src/doc/en/thematic_tutorials/coercion_and_categories.rst index ea441a31ab6..8cbbe2ea643 100644 --- a/src/doc/en/thematic_tutorials/coercion_and_categories.rst +++ b/src/doc/en/thematic_tutorials/coercion_and_categories.rst @@ -308,24 +308,24 @@ considerations: etc. **We do not override the default double underscore __add__, __mul__**, since otherwise, we could not use Sage's coercion model. -- Comparisons can be implemented using ``_cmp_``. This automatically - makes the relational operators like ``==`` and ``<`` work. In order - to support the Python ``cmp()`` function, it is safest to define both - ``_cmp_`` and ``__cmp__`` (because ``__cmp__`` is not inherited if - other comparison operators or ``__hash__`` are defined). Of course you - can just do ``__cmp__ = _cmp_``. +- Comparisons can be implemented using ``_richcmp_`` or + ``_cmp_``. This automatically makes the relational operators like + ``==`` and ``<`` work. **Beware**: in these methods, calling the + Python2-only ``cmp`` function should be avoided for compatibility + with Python3. You can use instead the ``richcmp`` function provided + by sage. - Note that ``_cmp_`` should be provided, since otherwise comparison - does not work:: + Note that either ``_cmp_`` or ``_richcmp_`` should be provided, + since otherwise comparison does not work:: sage: class Foo(sage.structure.element.Element): ....: def __init__(self, parent, x): ....: self.x = x ....: def _repr_(self): - ....: return "<%s>"%self.x + ....: return "<%s>" % self.x sage: a = Foo(ZZ, 1) sage: b = Foo(ZZ, 2) - sage: cmp(a,b) + sage: a <= b Traceback (most recent call last): ... NotImplementedError: comparison not implemented for @@ -366,9 +366,9 @@ This gives rise to the following code:: ....: return self.d ....: def _repr_(self): ....: return "(%s):(%s)"%(self.n,self.d) - ....: def _cmp_(self, other): - ....: return cmp(self.n*other.denominator(), other.numerator()*self.d) - ....: __cmp__ = _cmp_ + ....: def _richcmp_(self, other, op): + ....: from sage.structure.sage_object import richcmp + ....: return richcmp(self.n*other.denominator(), other.numerator()*self.d, op) ....: def _add_(self, other): ....: C = self.__class__ ....: D = self.d*other.denominator() @@ -1857,12 +1857,9 @@ Appendix: The complete code # into the same parent, which is a fraction field. Hence, we # are allowed to use the denominator() and numerator() methods # on the second argument. - def _cmp_(self, other): - return cmp(self.n*other.denominator(), other.numerator()*self.d) - - # Support for cmp() (in this example, we don't define __hash__ - # so this is not strictly needed) - __cmp__ = _cmp_ + def _richcmp_(self, other, op): + from sage.structure.sage_object import richcmp + return richcmp(self.n*other.denominator(), other.numerator()*self.d, op) # Arithmetic methods, single underscore. We can assume that both # arguments are coerced into the same parent. diff --git a/src/doc/en/thematic_tutorials/index.rst b/src/doc/en/thematic_tutorials/index.rst index 8f73b34ffc1..bcae758a13e 100644 --- a/src/doc/en/thematic_tutorials/index.rst +++ b/src/doc/en/thematic_tutorials/index.rst @@ -2,6 +2,8 @@ .. Aug 21 20:15:55 2008. You can adapt this file completely to your .. liking, but it should at least contain the root `toctree` directive. +.. _thematic-tutorials: + Welcome to the Sage Thematic Tutorials! ======================================= diff --git a/src/doc/en/thematic_tutorials/lie/crystals.rst b/src/doc/en/thematic_tutorials/lie/crystals.rst index 77ba5c45e4b..8047c549a1f 100644 --- a/src/doc/en/thematic_tutorials/lie/crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/crystals.rst @@ -772,7 +772,7 @@ You can see how its done as follows:: sage: T = crystals.Tableaux("A4",shape=[3,2]) sage: v = T.highest_weight_vector().f(1).f(2).f(3).f(2).f(1).f(4).f(2).f(3); v [[1, 2, 5], [3, 4]] - sage: v._list + sage: list(v) [3, 1, 4, 2, 5] We've looked at the internal representation of `v`, where it is diff --git a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst index 3cbce10f7b8..70b470a8dfb 100644 --- a/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst +++ b/src/doc/en/thematic_tutorials/lie/infinity_crystals.rst @@ -298,8 +298,8 @@ See [SalisburyScrimshaw2015]_ for more information:: sage: RiggedConfigurations.options(display="horizontal") sage: RC = crystals.infinity.RiggedConfigurations(['C',3,1]) sage: nu = RC.highest_weight_vector().f_string([0,1,2,3,2,1,0]); nu - -2[ ]-1 2[ ][ ]1 0[ ][ ]0 0[ ]0 - -2[ ]-1 + -2[ ]-1 2[ ]1 0[ ]0 0[ ]0 + -2[ ]-1 2[ ]1 0[ ]0 sage: nu.weight() -2*Lambda[0] + 2*Lambda[1] - 2*delta sage: RiggedConfigurations.options._reset() diff --git a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst index f8e269a615a..7260886965e 100644 --- a/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst +++ b/src/doc/en/thematic_tutorials/structures_in_coding_theory.rst @@ -81,6 +81,7 @@ Let us now write the constructor for our code class, that we store in some file called ``repetition_code.py``:: sage: from sage.coding.linear_code import AbstractLinearCode + sage: from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF sage: class BinaryRepetitionCode(AbstractLinearCode): ....: _registered_encoders = {} ....: _registered_decoders = {} @@ -523,9 +524,9 @@ we do not wish to store all our classes directly in Sage's global namespace. We propose several catalog files to store our constructions, namely: - ``codes_catalog.py``, -- ``encoders_catalog``, -- ``decoders_catalog`` and -- ``channels_catalog``. +- ``encoders_catalog.py``, +- ``decoders_catalog.py`` and +- ``channels_catalog.py``. Everytime one creates a new object, it should be added in the dedicated catalog file instead of coding theory folder's ``all.py``. @@ -534,19 +535,19 @@ Here it means the following: - add the following in ``codes_catalog.py``:: - from repetition_code import BinaryRepetitionCode + from sage.coding.repetition_code import BinaryRepetitionCode - add the following in ``encoders_catalog.py``:: - from repetition_code import BinaryRepetitionCodeGeneratorMatrixEncoder + from sage.coding.repetition_code import BinaryRepetitionCodeGeneratorMatrixEncoder - add the following in ``decoders_catalog.py``:: - from repetition_code import BinaryRepetitionCodeMajorityVoteDecoder + from sage.coding.repetition_code import BinaryRepetitionCodeMajorityVoteDecoder - add the following in ``channels_catalog.py``:: - from channel_constructions import BinaryStaticErrorRateChannel + from sage.coding.channel_constructions import BinaryStaticErrorRateChannel VII. Complete code of this tutorial =================================== @@ -559,6 +560,7 @@ derive from the one that follows. from sage.coding.linear_code import AbstractLinearCode from sage.coding.encoder import Encoder from sage.coding.decoder import Decoder + from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF class BinaryRepetitionCode(AbstractLinearCode): @@ -701,11 +703,20 @@ derive from the one that follows. w[i] += F.one() return w -``codes_catalog.py`` (continued, do the same in ``encoders_catalog.py``, -``decoders_catalog.py`` and ``channels_catalog.py``):: +``codes_catalog.py`` (continued):: - :class:`repetition_code.BinaryRepetitionCode ` + :class:`sage.coding.repetition_code.BinaryRepetitionCode ` #the line above creates a link to the class in the html documentation of coding theory library - from repetition_code import BinaryRepetitionCode - from channel_constructions import (ErrorErasureChannel, StaticErrorRateChannel, BinaryStaticErrorRateChannel) + from sage.coding.repetition_code import BinaryRepetitionCode +``encoders_catalog.py`` (continued):: + + from sage.coding.repetition_code import (BinaryRepetitionCodeGeneratorMatrixEncoder, BinaryRepetitionCodeStraightforwardEncoder) + +``decoders_catalog.py`` (continued):: + + from sage.coding.repetition_code import BinaryRepetitionCodeMajorityVoteDecoder + +``channels_catalog.py`` (continued):: + + from sage.coding.channel_constructions import (ErrorErasureChannel, StaticErrorRateChannel, BinaryStaticErrorRateChannel) 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 3f964bf172c..be07dc1467f 100644 --- a/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst +++ b/src/doc/en/thematic_tutorials/tutorial-objects-and-classes.rst @@ -282,16 +282,19 @@ Some particular actions modify the data structure of ``el``:: sage: F = CombinatorialFreeModule(QQ, Permutations()) sage: el = 3*F([1,3,2])+ F([1,2,3]) sage: el.rename("foo") + sage: el.blah = 42 sage: el.__class__ sage: el.__dict__ {'__custom_name': 'foo', - '_monomial_coefficients': {[1, 2, 3]: 1, [1, 3, 2]: 3}} + 'blah': 42} Lots of Sage objects are not Python objects but compiled Cython - objects. Python sees them as builtin objects and you don't have access to - the data structure. Examples include integers and permutation group - elements:: + objects. Python sees them as builtin objects and you don't have + access to some of their data structure. In particular, we do not + see the attribute ``_monomial_coefficients`` in the ``__dict__`` + above. Other examples of compiled Cython objects include integers + and permutation group elements:: sage: e = Integer(9) sage: type(e) diff --git a/src/doc/en/tutorial/index.rst b/src/doc/en/tutorial/index.rst index 72b80ad2b5d..858cbb1f2c8 100644 --- a/src/doc/en/tutorial/index.rst +++ b/src/doc/en/tutorial/index.rst @@ -2,6 +2,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +.. _tutorial: + Welcome to the Sage Tutorial! ============================= diff --git a/src/doc/en/tutorial/tour_coercion.rst b/src/doc/en/tutorial/tour_coercion.rst index 8850341d674..0ee15f3c808 100644 --- a/src/doc/en/tutorial/tour_coercion.rst +++ b/src/doc/en/tutorial/tour_coercion.rst @@ -56,7 +56,7 @@ providing different implementations of the same mathematical structure sage: R. = PolynomialRing(ZZ, implementation='NTL') sage: type(a); type(b); type(c) - + That poses two problems: On the one hand, if one has elements that are diff --git a/src/doc/fr/tutorial/tour_coercion.rst b/src/doc/fr/tutorial/tour_coercion.rst index 1be851e1976..43215c2a167 100644 --- a/src/doc/fr/tutorial/tour_coercion.rst +++ b/src/doc/fr/tutorial/tour_coercion.rst @@ -58,7 +58,7 @@ contre matrices creuses par exemple). sage: R. = PolynomialRing(ZZ, implementation='NTL') sage: type(a); type(b); type(c) - + Deux problèmes se posent alors. D'une part, si deux éléments sont instances de diff --git a/src/doc/ja/tutorial/tour_coercion.rst b/src/doc/ja/tutorial/tour_coercion.rst index f7194d4bd45..15c3ce07c3d 100644 --- a/src/doc/ja/tutorial/tour_coercion.rst +++ b/src/doc/ja/tutorial/tour_coercion.rst @@ -43,7 +43,7 @@ Pythonは(ダイナミックではあっても)強い型付けがなされる言 sage: R. = PolynomialRing(ZZ, implementation='NTL') sage: type(a); type(b); type(c) - + diff --git a/src/mac-app/Makefile b/src/mac-app/Makefile index 7aa1a6007bb..430121647aa 100644 --- a/src/mac-app/Makefile +++ b/src/mac-app/Makefile @@ -81,17 +81,18 @@ $(APP)/Contents/Resources/sage: $(APP) @# because $(APP) is newer than (the rsync preserves time) test -d $@ && touch $@ -# .DS_Store tells finder the icon position and background image to use -# when opening the dmg. -$(TARGET)/.DS_Store: tools/createDSStore/createDSStore.py - @# On some old Mac OS X, createDSStore fails when there already - @# is an existing file - rm -f $(TARGET)/.DS_Store - tools/createDSStore/createDSStore.py $(TARGET) $(TARGET) $(APP_NAME) - -$(TARGET)/.background.png: sage-dmg-background.png - cp sage-dmg-background.png $(TARGET)/.background.png +# The background image for the DMG. +# It contains the arrow pointing from the .app to the Applications +# folder. +# +# The background image should be at 72dpi. if a different resolution is +# set in the metadata, Finder produces weird results. +# +$(TARGET)/.background/sage.png: sage-dmg-background.png + mkdir -p $(TARGET)/.background + cp sage-dmg-background.png $(TARGET)/.background/sage.png +# Link to Applications $(TARGET)/Applications: rm -f $(TARGET)/Applications ln -s /Applications/ $(TARGET)/Applications @@ -100,19 +101,44 @@ $(TARGET)/Applications: $(TARGET)/README.txt: $(SAGE_ROOT)/src/bin/sage-README-osx.txt cp $^ $@ -non_app_files: $(TARGET)/README.txt $(TARGET)/.DS_Store $(TARGET)/.background.png $(TARGET)/Applications +non_app_files: $(TARGET)/README.txt $(TARGET)/.background/sage.png $(TARGET)/Applications sage_symlink: $(APP)/Contents/Resources/sage/sage rm -f $(APP)/sage ln -s Contents/Resources/sage/sage $(APP)/sage -# Compress the built directory into a DMG +# The arrangement of the icons and the "alias" for the background +# image are stored in a .DS_Store file. The .DS_Store file does +# not store the background image by relative path, but by "alias" +# which contains the volume name as well - so we cannot just +# generate one .DS_Store and copy it from version to version since +# the volume name contains the version. +# +# It would be nice if we could generate the .DS_Store directly +# instead of mounting the image before compressing it. +# Unfortunately, the .DS_Store format is proprietary and changed +# in Mac OS 10.12, so trying this with the ds_store python package +# did not work out (https://bitbucket.org/al45tair/ds_store/issues/7). +# Also see Ticket 20119 and 22739. + # Dependencies need to be in that order because non_app_files # needs the directory created by $(APP)/Contents/Resources/sage -$(DMG): $(APP)/Contents/Resources/sage sage_symlink non_app_files - hdiutil create -srcfolder $(TARGET) -volname $(TARGET) -format UDBZ $(DMG) +tmp-$(DMG): $(APP)/Contents/Resources/sage sage_symlink non_app_files + @# Create initial DMG + hdiutil create -srcfolder $(TARGET) -volname $(TARGET) -format UDRW tmp-$(DMG) + + @# Mount it so that we can run the Apple Script file on it + @# to arrange the icons and set the background image + mkdir -p mnt + hdiutil attach tmp-$(DMG) -mountpoint mnt + osascript arrangeIcons.applescript mnt $(APP_NAME) + hdiutil detach mnt + +# Compress the built directory into a DMG +$(DMG): tmp-$(DMG) + hdiutil convert -format UDBZ -o $(DMG) tmp-$(DMG) .PHONY: clean default debug app non_app_files clean: - rm -rf $(APP) build $(DMG) + rm -rf $(TARGET) build $(DMG) tmp-$(DMG) mnt diff --git a/src/mac-app/arrangeIcons.applescript b/src/mac-app/arrangeIcons.applescript new file mode 100644 index 00000000000..157b4d5a0f1 --- /dev/null +++ b/src/mac-app/arrangeIcons.applescript @@ -0,0 +1,38 @@ +on run (volname, appname) + + tell application "Finder" + tell disk (volname as string) + open + set opts to the icon view options of container window + tell opts + set arrangement to not arranged + set icon size to 88 + end tell + + # Path .background/sage.png needs to be converted + # .background:sage.png + set background picture of opts to file ".background:sage.png" + + tell container window + set current view to icon view + set toolbar visible to false + set statusbar visible to false + # The size of the window should match that of the image + set the bounds to {0, 0, 563, 348} + + set position of item "README.txt" to {462, 52} + set position of item "Applications" to {462, 212} + set position of item (appname as string) to {104, 212} + end tell + + # Some people report that the above settings are not applied + # unless we wait for a couple of seconds in some versions of + # Mac OS X. + update without registering applications + delay 3 + + close + end tell + end tell + +end run \ No newline at end of file diff --git a/src/mac-app/sage-dmg-background.png b/src/mac-app/sage-dmg-background.png index a2600b9b83e..2348b2fe65d 100644 Binary files a/src/mac-app/sage-dmg-background.png and b/src/mac-app/sage-dmg-background.png differ diff --git a/src/mac-app/sage-dmg-background.svg b/src/mac-app/sage-dmg-background.svg index a05ed1105b6..a23bc099602 100644 --- a/src/mac-app/sage-dmg-background.svg +++ b/src/mac-app/sage-dmg-background.svg @@ -10,13 +10,16 @@ xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="286.32553mm" - height="177.07654mm" - viewBox="0 0 1014.5392 627.43655" + width="198.55716mm" + height="122.79665mm" + viewBox="0 0 703.54893 435.1062" id="svg2" version="1.1" inkscape:version="0.91 r13725" - sodipodi:docname="sage-dmg-background.svg"> + sodipodi:docname="sage-dmg-background.svg" + inkscape:export-filename="/Users/matthias/Desktop/shared/sage_src/sage/src/mac-app/sage-dmg-background.png" + inkscape:export-xdpi="71.982414" + inkscape:export-ydpi="71.982414"> + gradientTransform="matrix(0.69346635,0,0,0.69346635,333.67367,116.84807)" /> + gradientTransform="matrix(0.69346635,0,0,0.69346635,333.67367,116.84807)" /> + transform="translate(-384.98959,-335.08768)"> + + style="opacity:0.45899999;fill:#000000;fill-opacity:1;stroke:none;stroke-width:12.5;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:12;stroke-opacity:1;filter:url(#filter5453)" + transform="matrix(0.69346635,0,0,0.69346635,333.67367,102.71565)" /> + transform="matrix(0.69346635,0,0,0.69346635,333.67367,73.567542)" /> @@ -236,7 +247,7 @@ xml:space="preserve" id="flowRoot5540" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:27.5px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#1818df;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - transform="translate(76.459086,-181.94754)"> - diff --git a/src/mac-app/tools/createDSStore/README.txt b/src/mac-app/tools/createDSStore/README.txt deleted file mode 100644 index eca2dd2a659..00000000000 --- a/src/mac-app/tools/createDSStore/README.txt +++ /dev/null @@ -1,10 +0,0 @@ -All dependencies of createDSStore are available on http://pypi.python.org/ and -are included here in the following versions and under the following licenses: - -six 1.10.0 MIT -mac_alias 1.1.0 MIT -biplist 1.0.1 BSD -ds_store 1.0.1 MIT - -There are no dependencies on Mac OS X specific libraries so the code can -be run from Linux as well. diff --git a/src/mac-app/tools/createDSStore/biplist/__init__.py b/src/mac-app/tools/createDSStore/biplist/__init__.py deleted file mode 100644 index c2b93079ffc..00000000000 --- a/src/mac-app/tools/createDSStore/biplist/__init__.py +++ /dev/null @@ -1,871 +0,0 @@ -"""biplist -- a library for reading and writing binary property list files. - -Binary Property List (plist) files provide a faster and smaller serialization -format for property lists on OS X. This is a library for generating binary -plists which can be read by OS X, iOS, or other clients. - -The API models the plistlib API, and will call through to plistlib when -XML serialization or deserialization is required. - -To generate plists with UID values, wrap the values with the Uid object. The -value must be an int. - -To generate plists with NSData/CFData values, wrap the values with the -Data object. The value must be a string. - -Date values can only be datetime.datetime objects. - -The exceptions InvalidPlistException and NotBinaryPlistException may be -thrown to indicate that the data cannot be serialized or deserialized as -a binary plist. - -Plist generation example: - - from biplist import * - from datetime import datetime - plist = {'aKey':'aValue', - '0':1.322, - 'now':datetime.now(), - 'list':[1,2,3], - 'tuple':('a','b','c') - } - try: - writePlist(plist, "example.plist") - except (InvalidPlistException, NotBinaryPlistException), e: - print "Something bad happened:", e - -Plist parsing example: - - from biplist import * - try: - plist = readPlist("example.plist") - print plist - except (InvalidPlistException, NotBinaryPlistException), e: - print "Not a plist:", e -""" - -from collections import namedtuple -import datetime -import io -import math -import plistlib -from struct import pack, unpack, unpack_from -from struct import error as struct_error -import sys -import time - -try: - unicode - unicodeEmpty = r'' -except NameError: - unicode = str - unicodeEmpty = '' -try: - long -except NameError: - long = int -try: - {}.iteritems - iteritems = lambda x: x.iteritems() -except AttributeError: - iteritems = lambda x: x.items() - -__all__ = [ - 'Uid', 'Data', 'readPlist', 'writePlist', 'readPlistFromString', - 'writePlistToString', 'InvalidPlistException', 'NotBinaryPlistException' -] - -# Apple uses Jan 1, 2001 as a base for all plist date/times. -apple_reference_date = datetime.datetime.utcfromtimestamp(978307200) - -class Uid(object): - """Wrapper around integers for representing UID values. This - is used in keyed archiving.""" - integer = 0 - def __init__(self, integer): - self.integer = integer - - def __repr__(self): - return "Uid(%d)" % self.integer - - def __eq__(self, other): - if isinstance(self, Uid) and isinstance(other, Uid): - return self.integer == other.integer - return False - - def __cmp__(self, other): - return self.integer - other.integer - - def __lt__(self, other): - return self.integer < other.integer - - def __hash__(self): - return self.integer - - def __int__(self): - return int(self.integer) - -class Data(bytes): - """Wrapper around bytes to distinguish Data values.""" - -class InvalidPlistException(Exception): - """Raised when the plist is incorrectly formatted.""" - -class NotBinaryPlistException(Exception): - """Raised when a binary plist was expected but not encountered.""" - -def readPlist(pathOrFile): - """Raises NotBinaryPlistException, InvalidPlistException""" - didOpen = False - result = None - if isinstance(pathOrFile, (bytes, unicode)): - pathOrFile = open(pathOrFile, 'rb') - didOpen = True - try: - reader = PlistReader(pathOrFile) - result = reader.parse() - except NotBinaryPlistException as e: - try: - pathOrFile.seek(0) - result = None - if hasattr(plistlib, 'loads'): - contents = None - if isinstance(pathOrFile, (bytes, unicode)): - with open(pathOrFile, 'rb') as f: - contents = f.read() - else: - contents = pathOrFile.read() - result = plistlib.loads(contents) - else: - result = plistlib.readPlist(pathOrFile) - result = wrapDataObject(result, for_binary=True) - except Exception as e: - raise InvalidPlistException(e) - finally: - if didOpen: - pathOrFile.close() - return result - -def wrapDataObject(o, for_binary=False): - if isinstance(o, Data) and not for_binary: - v = sys.version_info - if not (v[0] >= 3 and v[1] >= 4): - o = plistlib.Data(o) - elif isinstance(o, (bytes, plistlib.Data)) and for_binary: - if hasattr(o, 'data'): - o = Data(o.data) - elif isinstance(o, tuple): - o = wrapDataObject(list(o), for_binary) - o = tuple(o) - elif isinstance(o, list): - for i in range(len(o)): - o[i] = wrapDataObject(o[i], for_binary) - elif isinstance(o, dict): - for k in o: - o[k] = wrapDataObject(o[k], for_binary) - return o - -def writePlist(rootObject, pathOrFile, binary=True): - if not binary: - rootObject = wrapDataObject(rootObject, binary) - if hasattr(plistlib, "dump"): - if isinstance(pathOrFile, (bytes, unicode)): - with open(pathOrFile, 'wb') as f: - return plistlib.dump(rootObject, f) - else: - return plistlib.dump(rootObject, pathOrFile) - else: - return plistlib.writePlist(rootObject, pathOrFile) - else: - didOpen = False - if isinstance(pathOrFile, (bytes, unicode)): - pathOrFile = open(pathOrFile, 'wb') - didOpen = True - writer = PlistWriter(pathOrFile) - result = writer.writeRoot(rootObject) - if didOpen: - pathOrFile.close() - return result - -def readPlistFromString(data): - return readPlist(io.BytesIO(data)) - -def writePlistToString(rootObject, binary=True): - if not binary: - rootObject = wrapDataObject(rootObject, binary) - if hasattr(plistlib, "dumps"): - return plistlib.dumps(rootObject) - elif hasattr(plistlib, "writePlistToBytes"): - return plistlib.writePlistToBytes(rootObject) - else: - return plistlib.writePlistToString(rootObject) - else: - ioObject = io.BytesIO() - writer = PlistWriter(ioObject) - writer.writeRoot(rootObject) - return ioObject.getvalue() - -def is_stream_binary_plist(stream): - stream.seek(0) - header = stream.read(7) - if header == b'bplist0': - return True - else: - return False - -PlistTrailer = namedtuple('PlistTrailer', 'offsetSize, objectRefSize, offsetCount, topLevelObjectNumber, offsetTableOffset') -PlistByteCounts = namedtuple('PlistByteCounts', 'nullBytes, boolBytes, intBytes, realBytes, dateBytes, dataBytes, stringBytes, uidBytes, arrayBytes, setBytes, dictBytes') - -class PlistReader(object): - file = None - contents = '' - offsets = None - trailer = None - currentOffset = 0 - - def __init__(self, fileOrStream): - """Raises NotBinaryPlistException.""" - self.reset() - self.file = fileOrStream - - def parse(self): - return self.readRoot() - - def reset(self): - self.trailer = None - self.contents = '' - self.offsets = [] - self.currentOffset = 0 - - def readRoot(self): - result = None - self.reset() - # Get the header, make sure it's a valid file. - if not is_stream_binary_plist(self.file): - raise NotBinaryPlistException() - self.file.seek(0) - self.contents = self.file.read() - if len(self.contents) < 32: - raise InvalidPlistException("File is too short.") - trailerContents = self.contents[-32:] - try: - self.trailer = PlistTrailer._make(unpack("!xxxxxxBBQQQ", trailerContents)) - offset_size = self.trailer.offsetSize * self.trailer.offsetCount - offset = self.trailer.offsetTableOffset - offset_contents = self.contents[offset:offset+offset_size] - offset_i = 0 - while offset_i < self.trailer.offsetCount: - begin = self.trailer.offsetSize*offset_i - tmp_contents = offset_contents[begin:begin+self.trailer.offsetSize] - tmp_sized = self.getSizedInteger(tmp_contents, self.trailer.offsetSize) - self.offsets.append(tmp_sized) - offset_i += 1 - self.setCurrentOffsetToObjectNumber(self.trailer.topLevelObjectNumber) - result = self.readObject() - except TypeError as e: - raise InvalidPlistException(e) - return result - - def setCurrentOffsetToObjectNumber(self, objectNumber): - self.currentOffset = self.offsets[objectNumber] - - def readObject(self): - result = None - tmp_byte = self.contents[self.currentOffset:self.currentOffset+1] - marker_byte = unpack("!B", tmp_byte)[0] - format = (marker_byte >> 4) & 0x0f - extra = marker_byte & 0x0f - self.currentOffset += 1 - - def proc_extra(extra): - if extra == 0b1111: - #self.currentOffset += 1 - extra = self.readObject() - return extra - - # bool, null, or fill byte - if format == 0b0000: - if extra == 0b0000: - result = None - elif extra == 0b1000: - result = False - elif extra == 0b1001: - result = True - elif extra == 0b1111: - pass # fill byte - else: - raise InvalidPlistException("Invalid object found at offset: %d" % (self.currentOffset - 1)) - # int - elif format == 0b0001: - extra = proc_extra(extra) - result = self.readInteger(pow(2, extra)) - # real - elif format == 0b0010: - extra = proc_extra(extra) - result = self.readReal(extra) - # date - elif format == 0b0011 and extra == 0b0011: - result = self.readDate() - # data - elif format == 0b0100: - extra = proc_extra(extra) - result = self.readData(extra) - # ascii string - elif format == 0b0101: - extra = proc_extra(extra) - result = self.readAsciiString(extra) - # Unicode string - elif format == 0b0110: - extra = proc_extra(extra) - result = self.readUnicode(extra) - # uid - elif format == 0b1000: - result = self.readUid(extra) - # array - elif format == 0b1010: - extra = proc_extra(extra) - result = self.readArray(extra) - # set - elif format == 0b1100: - extra = proc_extra(extra) - result = set(self.readArray(extra)) - # dict - elif format == 0b1101: - extra = proc_extra(extra) - result = self.readDict(extra) - else: - raise InvalidPlistException("Invalid object found: {format: %s, extra: %s}" % (bin(format), bin(extra))) - return result - - def readInteger(self, byteSize): - result = 0 - original_offset = self.currentOffset - data = self.contents[self.currentOffset:self.currentOffset + byteSize] - result = self.getSizedInteger(data, byteSize, as_number=True) - self.currentOffset = original_offset + byteSize - return result - - def readReal(self, length): - result = 0.0 - to_read = pow(2, length) - data = self.contents[self.currentOffset:self.currentOffset+to_read] - if length == 2: # 4 bytes - result = unpack('>f', data)[0] - elif length == 3: # 8 bytes - result = unpack('>d', data)[0] - else: - raise InvalidPlistException("Unknown real of length %d bytes" % to_read) - return result - - def readRefs(self, count): - refs = [] - i = 0 - while i < count: - fragment = self.contents[self.currentOffset:self.currentOffset+self.trailer.objectRefSize] - ref = self.getSizedInteger(fragment, len(fragment)) - refs.append(ref) - self.currentOffset += self.trailer.objectRefSize - i += 1 - return refs - - def readArray(self, count): - result = [] - values = self.readRefs(count) - i = 0 - while i < len(values): - self.setCurrentOffsetToObjectNumber(values[i]) - value = self.readObject() - result.append(value) - i += 1 - return result - - def readDict(self, count): - result = {} - keys = self.readRefs(count) - values = self.readRefs(count) - i = 0 - while i < len(keys): - self.setCurrentOffsetToObjectNumber(keys[i]) - key = self.readObject() - self.setCurrentOffsetToObjectNumber(values[i]) - value = self.readObject() - result[key] = value - i += 1 - return result - - def readAsciiString(self, length): - result = unpack("!%ds" % length, self.contents[self.currentOffset:self.currentOffset+length])[0] - self.currentOffset += length - return str(result.decode('ascii')) - - def readUnicode(self, length): - actual_length = length*2 - data = self.contents[self.currentOffset:self.currentOffset+actual_length] - # unpack not needed?!! data = unpack(">%ds" % (actual_length), data)[0] - self.currentOffset += actual_length - return data.decode('utf_16_be') - - def readDate(self): - result = unpack(">d", self.contents[self.currentOffset:self.currentOffset+8])[0] - # Use timedelta to workaround time_t size limitation on 32-bit python. - result = datetime.timedelta(seconds=result) + apple_reference_date - self.currentOffset += 8 - return result - - def readData(self, length): - result = self.contents[self.currentOffset:self.currentOffset+length] - self.currentOffset += length - return Data(result) - - def readUid(self, length): - return Uid(self.readInteger(length+1)) - - def getSizedInteger(self, data, byteSize, as_number=False): - """Numbers of 8 bytes are signed integers when they refer to numbers, but unsigned otherwise.""" - result = 0 - # 1, 2, and 4 byte integers are unsigned - if byteSize == 1: - result = unpack('>B', data)[0] - elif byteSize == 2: - result = unpack('>H', data)[0] - elif byteSize == 4: - result = unpack('>L', data)[0] - elif byteSize == 8: - if as_number: - result = unpack('>q', data)[0] - else: - result = unpack('>Q', data)[0] - elif byteSize <= 16: - # Handle odd-sized or integers larger than 8 bytes - # Don't naively go over 16 bytes, in order to prevent infinite loops. - result = 0 - if hasattr(int, 'from_bytes'): - result = int.from_bytes(data, 'big') - else: - for byte in data: - if not isinstance(byte, int): # Python3.0-3.1.x return ints, 2.x return str - byte = unpack_from('>B', byte)[0] - result = (result << 8) | byte - else: - raise InvalidPlistException("Encountered integer longer than 16 bytes.") - return result - -class HashableWrapper(object): - def __init__(self, value): - self.value = value - def __repr__(self): - return "" % [self.value] - -class BoolWrapper(object): - def __init__(self, value): - self.value = value - def __repr__(self): - return "" % self.value - -class FloatWrapper(object): - _instances = {} - def __new__(klass, value): - # Ensure FloatWrapper(x) for a given float x is always the same object - wrapper = klass._instances.get(value) - if wrapper is None: - wrapper = object.__new__(klass) - wrapper.value = value - klass._instances[value] = wrapper - return wrapper - def __repr__(self): - return "" % self.value - -class StringWrapper(object): - __instances = {} - - encodedValue = None - encoding = None - - def __new__(cls, value): - '''Ensure we only have a only one instance for any string, - and that we encode ascii as 1-byte-per character when possible''' - - encodedValue = None - - for encoding in ('ascii', 'utf_16_be'): - try: - encodedValue = value.encode(encoding) - except Exception: - pass - if encodedValue is not None: - if encodedValue not in cls.__instances: - cls.__instances[encodedValue] = super(StringWrapper, cls).__new__(cls) - cls.__instances[encodedValue].encodedValue = encodedValue - cls.__instances[encodedValue].encoding = encoding - return cls.__instances[encodedValue] - - raise ValueError('Unable to get ascii or utf_16_be encoding for %s' % repr(value)) - - def __len__(self): - '''Return roughly the number of characters in this string (half the byte length)''' - if self.encoding == 'ascii': - return len(self.encodedValue) - else: - return len(self.encodedValue)//2 - - @property - def encodingMarker(self): - if self.encoding == 'ascii': - return 0b0101 - else: - return 0b0110 - - def __repr__(self): - return '' % (self.encoding, self.encodedValue) - -class PlistWriter(object): - header = b'bplist00bybiplist1.0' - file = None - byteCounts = None - trailer = None - computedUniques = None - writtenReferences = None - referencePositions = None - wrappedTrue = None - wrappedFalse = None - - def __init__(self, file): - self.reset() - self.file = file - self.wrappedTrue = BoolWrapper(True) - self.wrappedFalse = BoolWrapper(False) - - def reset(self): - self.byteCounts = PlistByteCounts(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - self.trailer = PlistTrailer(0, 0, 0, 0, 0) - - # A set of all the uniques which have been computed. - self.computedUniques = set() - # A list of all the uniques which have been written. - self.writtenReferences = {} - # A dict of the positions of the written uniques. - self.referencePositions = {} - - def positionOfObjectReference(self, obj): - """If the given object has been written already, return its - position in the offset table. Otherwise, return None.""" - return self.writtenReferences.get(obj) - - def writeRoot(self, root): - """ - Strategy is: - - write header - - wrap root object so everything is hashable - - compute size of objects which will be written - - need to do this in order to know how large the object refs - will be in the list/dict/set reference lists - - write objects - - keep objects in writtenReferences - - keep positions of object references in referencePositions - - write object references with the length computed previously - - computer object reference length - - write object reference positions - - write trailer - """ - output = self.header - wrapped_root = self.wrapRoot(root) - self.computeOffsets(wrapped_root, asReference=True, isRoot=True) - self.trailer = self.trailer._replace(**{'objectRefSize':self.intSize(len(self.computedUniques))}) - self.writeObjectReference(wrapped_root, output) - output = self.writeObject(wrapped_root, output, setReferencePosition=True) - - # output size at this point is an upper bound on how big the - # object reference offsets need to be. - self.trailer = self.trailer._replace(**{ - 'offsetSize':self.intSize(len(output)), - 'offsetCount':len(self.computedUniques), - 'offsetTableOffset':len(output), - 'topLevelObjectNumber':0 - }) - - output = self.writeOffsetTable(output) - output += pack('!xxxxxxBBQQQ', *self.trailer) - self.file.write(output) - - def wrapRoot(self, root): - if isinstance(root, bool): - if root is True: - return self.wrappedTrue - else: - return self.wrappedFalse - elif isinstance(root, float): - return FloatWrapper(root) - elif isinstance(root, set): - n = set() - for value in root: - n.add(self.wrapRoot(value)) - return HashableWrapper(n) - elif isinstance(root, dict): - n = {} - for key, value in iteritems(root): - n[self.wrapRoot(key)] = self.wrapRoot(value) - return HashableWrapper(n) - elif isinstance(root, list): - n = [] - for value in root: - n.append(self.wrapRoot(value)) - return HashableWrapper(n) - elif isinstance(root, tuple): - n = tuple([self.wrapRoot(value) for value in root]) - return HashableWrapper(n) - elif isinstance(root, (str, unicode)) and not isinstance(root, Data): - return StringWrapper(root) - elif isinstance(root, bytes): - return Data(root) - else: - return root - - def incrementByteCount(self, field, incr=1): - self.byteCounts = self.byteCounts._replace(**{field:self.byteCounts.__getattribute__(field) + incr}) - - def computeOffsets(self, obj, asReference=False, isRoot=False): - def check_key(key): - if key is None: - raise InvalidPlistException('Dictionary keys cannot be null in plists.') - elif isinstance(key, Data): - raise InvalidPlistException('Data cannot be dictionary keys in plists.') - elif not isinstance(key, StringWrapper): - raise InvalidPlistException('Keys must be strings.') - - def proc_size(size): - if size > 0b1110: - size += self.intSize(size) - return size - # If this should be a reference, then we keep a record of it in the - # uniques table. - if asReference: - if obj in self.computedUniques: - return - else: - self.computedUniques.add(obj) - - if obj is None: - self.incrementByteCount('nullBytes') - elif isinstance(obj, BoolWrapper): - self.incrementByteCount('boolBytes') - elif isinstance(obj, Uid): - size = self.intSize(obj.integer) - self.incrementByteCount('uidBytes', incr=1+size) - elif isinstance(obj, (int, long)): - size = self.intSize(obj) - self.incrementByteCount('intBytes', incr=1+size) - elif isinstance(obj, FloatWrapper): - size = self.realSize(obj) - self.incrementByteCount('realBytes', incr=1+size) - elif isinstance(obj, datetime.datetime): - self.incrementByteCount('dateBytes', incr=2) - elif isinstance(obj, Data): - size = proc_size(len(obj)) - self.incrementByteCount('dataBytes', incr=1+size) - elif isinstance(obj, StringWrapper): - size = proc_size(len(obj)) - self.incrementByteCount('stringBytes', incr=1+size) - elif isinstance(obj, HashableWrapper): - obj = obj.value - if isinstance(obj, set): - size = proc_size(len(obj)) - self.incrementByteCount('setBytes', incr=1+size) - for value in obj: - self.computeOffsets(value, asReference=True) - elif isinstance(obj, (list, tuple)): - size = proc_size(len(obj)) - self.incrementByteCount('arrayBytes', incr=1+size) - for value in obj: - asRef = True - self.computeOffsets(value, asReference=True) - elif isinstance(obj, dict): - size = proc_size(len(obj)) - self.incrementByteCount('dictBytes', incr=1+size) - for key, value in iteritems(obj): - check_key(key) - self.computeOffsets(key, asReference=True) - self.computeOffsets(value, asReference=True) - else: - raise InvalidPlistException("Unknown object type: %s (%s)" % (type(obj).__name__, repr(obj))) - - def writeObjectReference(self, obj, output): - """Tries to write an object reference, adding it to the references - table. Does not write the actual object bytes or set the reference - position. Returns a tuple of whether the object was a new reference - (True if it was, False if it already was in the reference table) - and the new output. - """ - position = self.positionOfObjectReference(obj) - if position is None: - self.writtenReferences[obj] = len(self.writtenReferences) - output += self.binaryInt(len(self.writtenReferences) - 1, byteSize=self.trailer.objectRefSize) - return (True, output) - else: - output += self.binaryInt(position, byteSize=self.trailer.objectRefSize) - return (False, output) - - def writeObject(self, obj, output, setReferencePosition=False): - """Serializes the given object to the output. Returns output. - If setReferencePosition is True, will set the position the - object was written. - """ - def proc_variable_length(format, length): - result = b'' - if length > 0b1110: - result += pack('!B', (format << 4) | 0b1111) - result = self.writeObject(length, result) - else: - result += pack('!B', (format << 4) | length) - return result - - def timedelta_total_seconds(td): - # Shim for Python 2.6 compatibility, which doesn't have total_seconds. - # Make one argument a float to ensure the right calculation. - return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10.0**6) / 10.0**6 - - if setReferencePosition: - self.referencePositions[obj] = len(output) - - if obj is None: - output += pack('!B', 0b00000000) - elif isinstance(obj, BoolWrapper): - if obj.value is False: - output += pack('!B', 0b00001000) - else: - output += pack('!B', 0b00001001) - elif isinstance(obj, Uid): - size = self.intSize(obj.integer) - output += pack('!B', (0b1000 << 4) | size - 1) - output += self.binaryInt(obj.integer) - elif isinstance(obj, (int, long)): - byteSize = self.intSize(obj) - root = math.log(byteSize, 2) - output += pack('!B', (0b0001 << 4) | int(root)) - output += self.binaryInt(obj, as_number=True) - elif isinstance(obj, FloatWrapper): - # just use doubles - output += pack('!B', (0b0010 << 4) | 3) - output += self.binaryReal(obj) - elif isinstance(obj, datetime.datetime): - try: - timestamp = (obj - apple_reference_date).total_seconds() - except AttributeError: - timestamp = timedelta_total_seconds(obj - apple_reference_date) - output += pack('!B', 0b00110011) - output += pack('!d', float(timestamp)) - elif isinstance(obj, Data): - output += proc_variable_length(0b0100, len(obj)) - output += obj - elif isinstance(obj, StringWrapper): - output += proc_variable_length(obj.encodingMarker, len(obj)) - output += obj.encodedValue - elif isinstance(obj, bytes): - output += proc_variable_length(0b0101, len(obj)) - output += obj - elif isinstance(obj, HashableWrapper): - obj = obj.value - if isinstance(obj, (set, list, tuple)): - if isinstance(obj, set): - output += proc_variable_length(0b1100, len(obj)) - else: - output += proc_variable_length(0b1010, len(obj)) - - objectsToWrite = [] - for objRef in obj: - (isNew, output) = self.writeObjectReference(objRef, output) - if isNew: - objectsToWrite.append(objRef) - for objRef in objectsToWrite: - output = self.writeObject(objRef, output, setReferencePosition=True) - elif isinstance(obj, dict): - output += proc_variable_length(0b1101, len(obj)) - keys = [] - values = [] - objectsToWrite = [] - for key, value in iteritems(obj): - keys.append(key) - values.append(value) - for key in keys: - (isNew, output) = self.writeObjectReference(key, output) - if isNew: - objectsToWrite.append(key) - for value in values: - (isNew, output) = self.writeObjectReference(value, output) - if isNew: - objectsToWrite.append(value) - for objRef in objectsToWrite: - output = self.writeObject(objRef, output, setReferencePosition=True) - return output - - def writeOffsetTable(self, output): - """Writes all of the object reference offsets.""" - all_positions = [] - writtenReferences = list(self.writtenReferences.items()) - writtenReferences.sort(key=lambda x: x[1]) - for obj,order in writtenReferences: - # Porting note: Elsewhere we deliberately replace empty unicdoe strings - # with empty binary strings, but the empty unicode string - # goes into writtenReferences. This isn't an issue in Py2 - # because u'' and b'' have the same hash; but it is in - # Py3, where they don't. - if bytes != str and obj == unicodeEmpty: - obj = b'' - position = self.referencePositions.get(obj) - if position is None: - raise InvalidPlistException("Error while writing offsets table. Object not found. %s" % obj) - output += self.binaryInt(position, self.trailer.offsetSize) - all_positions.append(position) - return output - - def binaryReal(self, obj): - # just use doubles - result = pack('>d', obj.value) - return result - - def binaryInt(self, obj, byteSize=None, as_number=False): - result = b'' - if byteSize is None: - byteSize = self.intSize(obj) - if byteSize == 1: - result += pack('>B', obj) - elif byteSize == 2: - result += pack('>H', obj) - elif byteSize == 4: - result += pack('>L', obj) - elif byteSize == 8: - if as_number: - result += pack('>q', obj) - else: - result += pack('>Q', obj) - elif byteSize <= 16: - try: - result = pack('>Q', 0) + pack('>Q', obj) - except struct_error as e: - raise InvalidPlistException("Unable to pack integer %d: %s" % (obj, e)) - else: - raise InvalidPlistException("Core Foundation can't handle integers with size greater than 16 bytes.") - return result - - def intSize(self, obj): - """Returns the number of bytes necessary to store the given integer.""" - # SIGNED - if obj < 0: # Signed integer, always 8 bytes - return 8 - # UNSIGNED - elif obj <= 0xFF: # 1 byte - return 1 - elif obj <= 0xFFFF: # 2 bytes - return 2 - elif obj <= 0xFFFFFFFF: # 4 bytes - return 4 - # SIGNED - # 0x7FFFFFFFFFFFFFFF is the max. - elif obj <= 0x7FFFFFFFFFFFFFFF: # 8 bytes signed - return 8 - elif obj <= 0xffffffffffffffff: # 8 bytes unsigned - return 16 - else: - raise InvalidPlistException("Core Foundation can't handle integers with size greater than 8 bytes.") - - def realSize(self, obj): - return 8 diff --git a/src/mac-app/tools/createDSStore/createDSStore.py b/src/mac-app/tools/createDSStore/createDSStore.py deleted file mode 100755 index c935837360e..00000000000 --- a/src/mac-app/tools/createDSStore/createDSStore.py +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/python - -""" -Creates a .DS_Store file that instructs Mac OS X's finder to use icon view with -a certain background image, and places the icons. - -This file is intended to be placed into the directory from which a dmg is -created for packaging an application. It needs as additional information the -name of the volume that will be used when creating the dmg and the name of the -application, including the .app, e.g., Sage-6.9.app. - -We could just always use the same .DS_Store that if the volume name and -application name were not changing. - -Most of the other values set in the .DS_Store have been taken from dmgbuild -(on pypi). However, dmgbuild requires the creation of a writable dmg, then -puts the .DS_Store file into it, and converts it to a read-only dmg. We avoid -this by using the volume name of the dmg instead. -See getBackgroundImage_alias. -""" -from __future__ import print_function - -import ds_store, mac_alias, biplist -import datetime, sys, os - -# Hard-coded data for finder -# The .background.png's dpi should be set to 72, otherwise, we get weird -# behavior (especially on Retina displays). This can be fixed with -# convert -density 72 -units pixelsperinch file.png file.png -backgroundImageSize = (563, 348) -backgroundImageName = '.background.png' -iconPositions = { - 'README.txt' : (497, 52), - 'Applications' : (462, 212), - 'app' : (104, 212) -} - -def getWindowData_bwsp(): - global backgroundImageSize - - return { - 'ShowTabView': False, - 'ShowStatusBar': False, - 'WindowBounds': '{{100, 100}, {%d, %d}}' % backgroundImageSize, - 'PreviewPaneVisibility': False, - 'ContainerShowSidebar': False, - 'ShowToolbar': False, - 'ShowPathbar': False, - 'SidebarWidth': 180, - 'ShowSidebar': False - } - -def getSomeTime(): - class UTC(datetime.tzinfo): - def utcoffset(self, dt): - return datetime.timedelta(0) - def tzname(self, dt): - return "UTC" - def dst(self, dt): - return datetime.timedelta(0) - - return datetime.datetime(2000, 1, 1, tzinfo = UTC()) - -def getBackgroundImage_alias(volume_name): - """ - The location of the background image is stored as "Alias" which consists - of the volume name and an absolute path with respect to that volume. - - Currently, we only support having the background image at the root of the - volume. - """ - - # Also see Alias.for_file which only works after the volume is mounted - - global backgroundImageName - - carbon_path = '%s:%s' % (volume_name, backgroundImageName) - - volume = mac_alias.VolumeInfo(volume_name, - getSomeTime(), - 'H+', - 0, - 0, - '\x00\x00') - - target = mac_alias.TargetInfo(0, - backgroundImageName, - 0, 0, - getSomeTime(), - '\x00\x00\x00\x00', - '\x00\x00\x00\x00', - folder_name = volume_name, - carbon_path = carbon_path) - - return mac_alias.Alias(volume = volume, target = target) - -def getBackgroundImage_icvp(volume_name): - return { - 'gridSpacing': 100.0, - 'textSize': 16.0, - 'viewOptionsVersion': 1, - 'backgroundColorBlue': 1.0, - 'scrollPositionX': 0.0, - 'iconSize': 88.0, - 'backgroundColorGreen': 1.0, - 'arrangeBy': 'none', - 'showIconPreview': False, - 'gridOffsetX': 0.0, - 'gridOffsetY': 0.0, - 'showItemInfo': False, - 'labelOnBottom': True, - 'backgroundType': 2, - 'scrollPositionY': 0.0, - 'backgroundColorRed': 1.0, - 'backgroundImageAlias': biplist.Data( - getBackgroundImage_alias(volume_name).to_bytes()) - } - -def createDSStore(target_dir, volume_name, app_name): - global iconPositions - - filePath = os.path.join(target_dir, '.DS_Store') - - with ds_store.DSStore.open(filePath, 'w+') as d: - d['.']['vSrn'] = ('long', 1) - d['.']['icvl'] = ('type', 'icnv') - d['.']['bwsp'] = getWindowData_bwsp() - d['.']['icvp'] = getBackgroundImage_icvp(volume_name) - - for key, iconPosition in iconPositions.items(): - if key == 'app': - name = app_name - else: - name = key - d[name]['Iloc'] = iconPosition - -if __name__ == '__main__': - if len(sys.argv) != 4: - print( - "Usage: %s TARGET_DIR VOLUME_NAME APP_NAME.app" % sys.argv[0], - file=sys.stderr) - print(" creates .DS_Store", file=sys.stderr) - sys.exit(1) - - createDSStore( - target_dir = sys.argv[1], - volume_name = sys.argv[2], - app_name = sys.argv[3]) diff --git a/src/mac-app/tools/createDSStore/ds_store/__init__.py b/src/mac-app/tools/createDSStore/ds_store/__init__.py deleted file mode 100644 index a6b81210495..00000000000 --- a/src/mac-app/tools/createDSStore/ds_store/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .store import DSStore, DSStoreEntry - -__all__ = ['DSStore', 'DSStoreEntry'] diff --git a/src/mac-app/tools/createDSStore/ds_store/buddy.py b/src/mac-app/tools/createDSStore/ds_store/buddy.py deleted file mode 100644 index 8811ffc6981..00000000000 --- a/src/mac-app/tools/createDSStore/ds_store/buddy.py +++ /dev/null @@ -1,456 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import bisect -import struct -import binascii -import six - -class BuddyError (Exception): - pass - -class Block(object): - def __init__(self, allocator, offset, size): - self._allocator = allocator - self._offset = offset - self._size = size - self._value = bytearray(allocator.read (offset, size)) - self._pos = 0 - self._dirty = False - - def __len__(self): - return self._size - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - def close(self): - if self._dirty: - self.flush() - - def flush(self): - if self._dirty: - self._dirty = False - self._allocator.write (self._offset, self._value) - - def invalidate(self): - self._dirty = False - - def zero_fill(self): - len = self._size - self._pos - zeroes = '\0' * len - self._value[self._pos:self._size] = zeroes - self._dirty = True - - def tell(self): - return self._pos - - def seek(self, pos, whence=os.SEEK_SET): - if whence == os.SEEK_CUR: - pos += self._pos - elif whence == os.SEEK_END: - pos = self._size - pos - - if pos < 0 or pos > self._size: - raise ValueError('Seek out of range in Block instance') - - self._pos = pos - - def read(self, size_or_format): - if isinstance(size_or_format, basestring): - size = struct.calcsize (size_or_format) - fmt = size_or_format - else: - size = size_or_format - fmt = None - - if self._size - self._pos < size: - raise BuddyError('Unable to read %lu bytes in block' % size) - - data = self._value[self._pos:self._pos + size] - self._pos += size - - if fmt is not None: - return struct.unpack (size_or_format, data) - else: - return data - - def write(self, data_or_format, *args): - if len(args): - data = struct.pack (data_or_format, *args) - else: - data = data_or_format - - if self._pos + len(data) > self._size: - raise ValueError('Attempt to write past end of Block') - - self._value[self._pos:self._pos + len(data)] = data - self._pos += len(data) - - self._dirty = True - - def insert(self, data_or_format, *args): - if len(args): - data = struct.pack (data_or_format, *args) - else: - data = data_or_format - - del self._value[-len(data):] - self._value[self._pos:self._pos] = data - self._pos += len(data) - - self._dirty = True - - def delete(self, size): - if self._pos + size > self._size: - raise ValueError('Attempt to delete past end of Block') - del self._value[self._pos:self._pos + size] - self._value += b'\0' * size - self._dirty = True - - def __str__(self): - return binascii.b2a_hex(self._value) - -class Allocator (object): - def __init__(self, the_file): - self._file = the_file - self._dirty = False - - self._file.seek(0) - - # Read the header - magic1, magic2, offset, size, offset2, self._unknown1 \ - = self.read (-4, '>I4sIII16s') - - if magic2 != 'Bud1' or magic1 != 1: - raise BuddyError('Not a buddy file') - - if offset != offset2: - raise BuddyError('Root addresses differ') - - self._root = Block(self, offset, size) - - # Read the block offsets - count, self._unknown2 = self._root.read('>II') - self._offsets = [] - c = (count + 255) & ~255 - while c: - self._offsets += self._root.read('>256I') - c -= 256 - self._offsets = self._offsets[:count] - - # Read the TOC - self._toc = {} - count = self._root.read('>I')[0] - for n in range(count): - nlen = self._root.read('B')[0] - name = str(self._root.read(nlen)) - value = self._root.read('>I')[0] - self._toc[name] = value - - # Read the free lists - self._free = [] - for n in range(32): - count = self._root.read('>I') - self._free.append(list(self._root.read('>%uI' % count))) - - @classmethod - def open(cls, file_or_name, mode='r+'): - if isinstance(file_or_name, basestring): - f = open(file_or_name, mode) - else: - f = file_or_name - - if mode == 'w' or mode == 'w+': - # Create an empty file in this case - f.truncate() - - # An empty root block needs 1264 bytes: - # - # 0 4 offset count - # 4 4 unknown - # 8 4 root block offset (2048) - # 12 255 * 4 padding (offsets are in multiples of 256) - # 1032 4 toc count (0) - # 1036 228 free list - # total 1264 - - # The free list will contain the following: - # - # 0 5 * 4 no blocks of width less than 5 - # 20 6 * 8 1 block each of widths 5 to 10 - # 68 4 no blocks of width 11 (allocated for the root) - # 72 19 * 8 1 block each of widths 12 to 30 - # 224 4 no blocks of width 31 - # total 228 - # - # (The reason for this layout is that we allocate 2**5 bytes for - # the header, which splits the initial 2GB region into every size - # below 2**31, including *two* blocks of size 2**5, one of which - # we take. The root block itself then needs a block of size - # 2**11. Conveniently, each of these initial blocks will be - # located at offset 2**n where n is its width.) - - # Write the header - header = struct.pack(b'>I4sIII16s', - 1, b'Bud1', - 2048, 1264, 2048, - b'\x00\x00\x10\x0c' - b'\x00\x00\x00\x87' - b'\x00\x00\x20\x0b' - b'\x00\x00\x00\x00') - f.write (header) - f.write(b'\0' * 2016) - - # Write the root block - free_list = [struct.pack(b'>5I', 0, 0, 0, 0, 0)] - for n in range(5, 11): - free_list.append (struct.pack (b'>II', 1, 2**n)) - free_list.append (struct.pack (b'>I', 0)) - for n in range(12, 31): - free_list.append (struct.pack (b'>II', 1, 2**n)) - free_list.append (struct.pack (b'>I', 0)) - - root = ''.join([struct.pack(b'>III', 1, 0, 2048 | 5), - struct.pack(b'>I', 0) * 255, - struct.pack(b'>I', 0)] + free_list) - f.write (root) - - return Allocator (f) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - def close(self): - self.flush() - self._file.close() - - def flush(self): - if self._dirty: - size = self._root_block_size() - self.allocate (size, 0) - with self.get_block(0) as rblk: - self._write_root_block_into (rblk) - - addr = self._offsets[0] - offset = addr & ~0x1f - size = 1 << (addr & 0x1f) - - self._file.seek(0, os.SEEK_SET) - self._file.write(struct.pack(b'>I4sIII16s', - 1, b'Bud1', - offset, size, offset, - self._unknown1)) - - self._dirty = False - - self._file.flush() - - def read(self, offset, size_or_format): - """Read data at `offset', or raise an exception. `size_or_format' - may either be a byte count, in which case we return raw data, - or a format string for `struct.unpack', in which case we - work out the size and unpack the data before returning it.""" - # N.B. There is a fixed offset of four bytes(!) - self._file.seek (offset + 4, os.SEEK_SET) - - if isinstance(size_or_format, basestring): - size = struct.calcsize (size_or_format) - fmt = size_or_format - else: - size = size_or_format - fmt = None - - ret = self._file.read (size) - if len(ret) < size: - ret += b'\0' * (size - len(ret)) - - if fmt is not None: - ret = struct.unpack (fmt, ret) - - return ret - - def write(self, offset, data_or_format, *args): - """Write data at `offset', or raise an exception. `data_or_format' - may either be the data to write, or a format string for `struct.pack', - in which case we pack the additional arguments and write the - resulting data.""" - # N.B. There is a fixed offset of four bytes(!) - self._file.seek (offset + 4, os.SEEK_SET) - - if len(args): - data = struct.pack (data_or_format, *args) - else: - data = data_or_format - - self._file.write (data) - - def get_block(self, block): - try: - addr = self._offsets[block] - except IndexError: - return None - - offset = addr & ~0x1f - size = 1 << (addr & 0x1f) - - return Block(self, offset, size) - - def _root_block_size(self): - """Return the number of bytes required by the root block.""" - # Offsets - size = 8 - size += 4 * ((len(self._offsets) + 255) & ~255) - - # TOC - size += 4 - size += sum([5 + len(s) for s in self._toc]) - - # Free list - size += sum([4 + 4 * len(fl) for fl in self._free]) - - return size - - def _write_root_block_into(self, block): - # Offsets - block.write('>II', len(self._offsets), self._unknown2) - block.write('>%uI' % len(self._offsets), *self._offsets) - extra = len(self._offsets) & 255 - if extra: - block.write(b'\0\0\0\0' * (256 - extra)) - - # TOC - keys = self._toc.keys() - keys.sort() - - block.write('>I', len(keys)) - for k in keys: - b = k.encode('utf-8') - block.write('B', len(b)) - block.write(b) - block.write('>I', self._toc[k]) - - # Free list - for w, f in enumerate(self._free): - block.write('>I', len(f)) - if len(f): - block.write('>%uI' % len(f), *f) - - def _buddy(self, offset, width): - f = self._free[width] - b = offset ^ (1 << width) - - try: - ndx = f.index(b) - except ValueError: - ndx = None - - return (f, b, ndx) - - def _release(self, offset, width): - # Coalesce - while True: - f,b,ndx = self._buddy (offset, width) - - if ndx is None: - break - - offset &= b - width += 1 - del f[ndx] - - # Add to the list - bisect.insort(f, offset) - - # Mark as dirty - self._dirty = True - - def _alloc(self, width): - w = width - while not self._free[w]: - w += 1 - while w > width: - offset = self._free[w].pop(0) - w -= 1 - self._free[w] = [offset, offset ^ (1 << w)] - self._dirty = True - return self._free[width].pop(0) - - def allocate(self, bytes, block=None): - """Allocate or reallocate a block such that it has space for at least - `bytes' bytes.""" - if block is None: - # Find the first unused block - try: - block = self._offsets.index(0) - except ValueError: - block = len(self._offsets) - self._offsets.append(0) - - # Compute block width - width = max(bytes.bit_length(), 5) - - addr = self._offsets[block] - offset = addr & ~0x1f - - if addr: - blkwidth = addr & 0x1f - if blkwidth == width: - return block - self._release (offset, width) - self._offsets[block] = 0 - - offset = self._alloc(width) - self._offsets[block] = offset | width - return block - - def release(self, block): - addr = self._offsets[block] - - if addr: - width = addr & 0x1f - offset = addr & ~0x1f - self._release (offset, width) - - if block == len(self._offsets): - del self._offsets[block] - else: - self._offsets[block] = 0 - - def __len__(self): - return len(self._toc) - - def __getitem__(self, key): - if not isinstance(key, basestring): - raise TypeError('Keys must be of string type') - return self._toc[key] - - def __setitem__(self, key, value): - if not isinstance(key, basestring): - raise TypeError('Keys must be of string type') - self._toc[key] = value - self._dirty = True - - def __delitem__(self, key): - if not isinstance(key, basestring): - raise TypeError('Keys must be of string type') - del self._toc[key] - self._dirty = True - - def iterkeys(self): - return six.iterkeys(self._toc) - - def keys(self): - return six.iterkeys(self._toc) - - def __iter__(self): - return six.iterkeys(self._toc) - - def __contains__(self, key): - return key in self._toc - diff --git a/src/mac-app/tools/createDSStore/ds_store/store.py b/src/mac-app/tools/createDSStore/ds_store/store.py deleted file mode 100644 index 7e62fc194de..00000000000 --- a/src/mac-app/tools/createDSStore/ds_store/store.py +++ /dev/null @@ -1,1201 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from __future__ import print_function - -import binascii -import struct -import biplist -import six - -from . import buddy - -class ILocCodec (object): - @staticmethod - def encode(point): - return struct.pack(b'>IIII', point[0], point[1], - 0xffffffff, 0xffff0000) - - @staticmethod - def decode(bytes): - x, y = struct.unpack(b'>II', bytes[:8]) - return (x, y) - -class PlistCodec (object): - @staticmethod - def encode(plist): - return biplist.writePlistToString(plist) - - @staticmethod - def decode(bytes): - return biplist.readPlistFromString(bytes) - -# This list tells the code how to decode particular kinds of entry in the -# .DS_Store file. This is really a convenience, and we currently only -# support a tiny subset of the possible entry types. -codecs = { - 'Iloc': ILocCodec, - 'bwsp': PlistCodec, - 'lsvp': PlistCodec, - 'lsvP': PlistCodec, - 'icvp': PlistCodec, - } - -class DSStoreEntry (object): - """Holds the data from an entry in a ``.DS_Store`` file. Note that this is - not meant to represent the entry itself---i.e. if you change the type - or value, your changes will *not* be reflected in the underlying file. - - If you want to make a change, you should either use the :class:`DSStore` - object's :meth:`DSStore.insert` method (which will replace a key if it - already exists), or the mapping access mode for :class:`DSStore` (often - simpler anyway). - """ - def __init__(self, filename, code, typecode, value=None): - self.filename = filename - self.code = code - self.type = typecode - self.value = value - - @classmethod - def read(cls, block): - """Read a ``.DS_Store`` entry from the containing Block""" - # First read the filename - nlen = block.read(b'>I')[0] - filename = block.read(2 * nlen).decode('utf-16be') - - # Next, read the code and type - code, typecode = block.read(b'>4s4s') - - # Finally, read the data - if typecode == 'bool': - value = block.read(b'>?')[0] - elif typecode == 'long' or typecode == 'shor': - value = block.read(b'>I')[0] - elif typecode == 'blob': - vlen = block.read(b'>I')[0] - value = block.read(vlen) - - codec = codecs.get(code, None) - if codec: - value = codec.decode(value) - typecode = codec - elif typecode == 'ustr': - vlen = block.read(b'>I')[0] - value = block.read(2 * vlen).decode('utf-16be') - elif typecode == 'type': - value = block.read(b'>4s')[0] - elif typecode == 'comp' or typecode == 'dutc': - value = block.read(b'>Q')[0] - else: - raise ValueError('Unknown type code "%s"' % typecode) - - return DSStoreEntry (filename, code, typecode, value) - - def __lt__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl < ofl - or (self.filename == other.filename - and self.code < other.code)) - - def __le__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl < ofl - or (sfl == ofl - and self.code <= other.code)) - - def __eq__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl == ofl - and self.code == other.code) - - def __ne__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl != ofl - or self.code != other.code) - - def __gt__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl > ofl - or (sfl == ofl - and self.code > other.code)) - - def __ge__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - sfl = self.filename.lower() - ofl = other.filename.lower() - return (sfl > ofl - or (sfl == ofl - and self.code >= other.code)) - - def __cmp__(self, other): - if not isinstance(other, DSStoreEntry): - raise TypeError('Can only compare against other DSStoreEntry objects') - r = cmp (self.filename.lower(), other.filename.lower()) - if r: - return r - return cmp (self.code, other.code) - - def byte_length(self): - """Compute the length of this entry, in bytes""" - utf16 = self.filename.encode('utf-16be') - l = 4 + len(utf16) + 8 - - if isinstance(self.type, basestring): - entry_type = self.type - value = self.value - else: - entry_type = 'blob' - value = self.type.encode(self.value) - - if entry_type == 'bool': - l += 1 - elif entry_type == 'long' or entry_type == 'shor': - l += 4 - elif entry_type == 'blob': - l += 4 + len(value) - elif entry_type == 'ustr': - utf16 = value.encode('utf-16be') - l += 4 + len(utf16) - elif entry_type == 'type': - l += 4 - elif entry_type == 'comp' or entry_type == 'dutc': - l += 8 - else: - raise ValueError('Unknown type code "%s"' % entry_type) - - return l - - def write(self, block, insert=False): - """Write this entry to the specified Block""" - if insert: - w = block.insert - else: - w = block.write - - if isinstance(self.type, basestring): - entry_type = self.type - value = self.value - else: - entry_type = 'blob' - value = self.type.encode(self.value) - - utf16 = self.filename.encode('utf-16be') - w(b'>I', len(utf16) / 2) - w(utf16) - w(b'>4s4s', self.code.encode('utf-8'), entry_type.encode('utf-8')) - - if entry_type == 'bool': - w(b'>?', value) - elif entry_type == 'long' or entry_type == 'shor': - w(b'>I', value) - elif entry_type == 'blob': - w(b'>I', len(value)) - w(value) - elif entry_type == 'ustr': - utf16 = value.encode('utf-16be') - w(b'>I', len(utf16) / 2) - w(utf16) - elif entry_type == 'type': - w(b'>4s', value.encode('utf-8')) - elif entry_type == 'comp' or entry_type == 'dutc': - w(b'>Q', value) - else: - raise ValueError('Unknown type code "%s"' % entry_type) - - def __repr__(self): - return '<%s %s>' % (self.filename, self.code) - -class DSStore (object): - """Python interface to a ``.DS_Store`` file. Works by manipulating the file - on the disk---so this code will work with ``.DS_Store`` files for *very* - large directories. - - A :class:`DSStore` object can be used as if it was a mapping, e.g.:: - - d['foobar.dat']['Iloc'] - - will fetch the "Iloc" record for "foobar.dat", or raise :class:`KeyError` if - there is no such record. If used in this manner, the :class:`DSStore` object - will return (type, value) tuples, unless the type is "blob" and the module - knows how to decode it. - - Currently, we know how to decode "Iloc", "bwsp", "lsvp", "lsvP" and "icvp" - blobs. "Iloc" decodes to an (x, y) tuple, while the others are all decoded - using ``biplist``. - - Assignment also works, e.g.:: - - d['foobar.dat']['note'] = ('ustr', u'Hello World!') - - as does deletion with ``del``:: - - del d['foobar.dat']['note'] - - This is usually going to be the most convenient interface, though - occasionally (for instance when creating a new ``.DS_Store`` file) you - may wish to drop down to using :class:`DSStoreEntry` objects directly.""" - def __init__(self, store): - self._store = store - self._superblk = self._store['DSDB'] - with self._get_block(self._superblk) as s: - self._rootnode, self._levels, self._records, \ - self._nodes, self._page_size = s.read(b'>IIIII') - self._min_usage = 2 * self._page_size / 3 - self._dirty = False - - @classmethod - def open(cls, file_or_name, mode='r+', initial_entries=None): - """Open a ``.DS_Store`` file; pass either a Python file object, or a - filename in the ``file_or_name`` argument and a file access mode in - the ``mode`` argument. If you are creating a new file using the "w" - or "w+" modes, you may also specify a list of entries with which - to initialise the file.""" - store = buddy.Allocator.open (file_or_name, mode) - - if mode == 'w' or mode == 'w+': - superblk = store.allocate(20) - store['DSDB'] = superblk - page_size = 4096 - - if not initial_entries: - root = store.allocate(page_size) - - with store.get_block(root) as rootblk: - rootblk.zero_fill() - - with store.get_block(superblk) as s: - s.write(b'>IIIII', root, 0, 0, 1, page_size) - else: - # Make sure they're in sorted order - initial_entries = list(initial_entries) - initial_entries.sort() - - # Construct the tree - current_level = initial_entries - next_level = [] - levels = [] - ptr_size = 0 - node_count = 0 - while True: - total = 8 - nodes = [] - node = [] - for e in current_level: - new_total = total + ptr_size + e.byte_length() - if new_total > page_size: - nodes.append(node) - next_level.append(e) - total = 8 - node = [] - else: - total = new_total - node.append(e) - if node: - nodes.append(node) - - node_count += len(nodes) - levels.append(nodes) - - if len(nodes) == 1: - break - - current_level = next_level - next_level = [] - ptr_size = 4 - - # Allocate nodes - ptrs = [store.allocate(page_size) for n in range(node_count)] - - # Generate nodes - pointers = [] - prev_pointers = None - for level in levels: - ppndx = 0 - lptrs = ptrs[-len(level):] - del ptrs[-len(level):] - for node in level: - ndx = lptrs.pop(0) - if prev_pointers is None: - with store.get_block(ndx) as block: - block.write(b'>II', 0, len(node)) - for e in node: - e.write(block) - else: - next_node = prev_pointers[ppndx + len(node)] - node_ptrs = prev_pointers[ppndx:ppndx+len(node)] - - with store.get_block(ndx) as block: - block.write(b'>II', next_node, len(node)) - for ptr, e in zip(node_ptrs, node): - block.write(b'>I', ptr) - e.write(block) - - pointers.append(ndx) - prev_pointers = pointers - pointers = [] - - root = prev_pointers[0] - - with store.get_block(superblk) as s: - s.write(b'>IIIII', root, len(levels), len(initial_entries), - node_count, page_size) - - return DSStore(store) - - def _get_block(self, number): - return self._store.get_block(number) - - def flush(self): - """Flush any dirty data back to the file.""" - if self._dirty: - self._dirty = False - - with self._get_block(self._superblk) as s: - s.write(b'>IIIII', self._rootnode, self._levels, self._records, - self._nodes, self._page_size) - self._store.flush() - - def close(self): - """Flush dirty data and close the underlying file.""" - self.flush() - self._store.close() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - - # Internal B-Tree nodes look like this: - # - # [ next | count | (ptr0 | rec0) | (ptr1 | rec1) ... (ptrN | recN) ] - - # Leaf nodes look like this: - # - # [ 0 | count | rec0 | rec1 ... recN ] - - # Iterate over the tree, starting at `node' - def _traverse(self, node): - if node is None: - node = self._rootnode - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - if next_node: - for n in range(count): - ptr = block.read(b'>I')[0] - for e in self._traverse(ptr): - yield e - e = DSStoreEntry.read(block) - yield e - for e in self._traverse(next_node): - yield e - else: - for n in range(count): - e = DSStoreEntry.read(block) - yield e - - # Display the data in `node' - def _dump_node(self, node): - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - print('next: %u\ncount: %u\n' % (next_node, count)) - for n in range(count): - if next_node: - ptr = block.read(b'>I')[0] - print('%8u ' % ptr, end=' ') - else: - print(' ', end=' ') - e = DSStoreEntry.read(block) - print(e, ' (%u)' % e.byte_length()) - print('used: %u' % block.tell()) - - # Display the data in the super block - def _dump_super(self): - print('root: %u\nlevels: %u\nrecords: %u\nnodes: %u\npage-size: %u' \ - % (self._rootnode, self._levels, self._records, - self._nodes, self._page_size)) - - # Splits entries across two blocks, returning one pivot - # - # Tries to balance the block usage across the two as best it can - def _split2(self, blocks, entries, pointers, before, internal): - left_block = blocks[0] - right_block = blocks[1] - - count = len(entries) - - # Find the feasible splits - best_split = None - best_diff = None - total = before[count] - - if 8 + total <= self._page_size: - # We can use a *single* node for this - best_split = count - else: - # Split into two nodes - for n in range(1, count - 1): - left_size = 8 + before[n] - right_size = 8 + total - before[n + 1] - - if left_size > self._page_size: - break - if right_size > self._page_size: - continue - - diff = abs(left_size - right_size) - - if best_split is None or diff < best_diff: - best_split = n - best_diff = diff - - if best_split is None: - return None - - # Write the nodes - left_block.seek(0) - if internal: - next_node = pointers[best_split] - else: - next_node = 0 - left_block.write(b'>II', next_node, best_split) - - for n in range(best_split): - if internal: - left_block.write(b'>I', pointers[n]) - entries[n].write(left_block) - - left_block.zero_fill() - - if best_split == count: - return [] - - right_block.seek(0) - if internal: - next_node = pointers[count] - else: - next_node = 0 - right_block.write(b'>II', next_node, count - best_split - 1) - - for n in range(best_split + 1, count): - if internal: - right_block.write(b'>I', pointers[n]) - entries[n].write(right_block) - - right_block.zero_fill() - - pivot = entries[best_split] - - return [pivot] - - def _split(self, node, entry, right_ptr=0): - self._nodes += 1 - self._dirty = True - new_right = self._store.allocate(self._page_size) - with self._get_block(node) as block, \ - self._get_block(new_right) as right_block: - - # First, measure and extract all the elements - entry_size = entry.byte_length() - entry_pos = None - next_node, count = block.read(b'>II') - if next_node: - entry_size += 4 - pointers = [] - entries = [] - before = [] - total = 0 - for n in range(count): - pos = block.tell() - if next_node: - ptr = block.read(b'>I')[0] - pointers.append(ptr) - e = DSStoreEntry.read(block) - if e > entry: - entry_pos = n - entries.append(entry) - pointers.append(right_ptr) - before.append(total) - total += entry_Size - entries.append(e) - before.append(total) - total += block.tell() - pos - before.append(total) - if next_node: - pointers.append(next_node) - - pivot = self._split2([left_block, right_block], - entries, pointers, before, - bool(next_node))[0] - - self._records += 1 - self._nodes += 1 - self._dirty = True - - return (pivot, new_right) - - # Allocate a new root node containing the element `pivot' and the pointers - # `left' and `right' - def _new_root(self, left, pivot, right): - new_root = self._store.allocate(self._page_size) - with self._get_block(new_root) as block: - block.write(b'>III', right, 1, left) - pivot.write(block) - self._rootnode = new_root - self._levels += 1 - self._nodes += 1 - self._dirty = True - - # Insert an entry into an inner node; `path' is the path from the root - # to `node', not including `node' itself. `right_ptr' is the new node - # pointer (inserted to the RIGHT of `entry') - def _insert_inner(self, path, node, entry, right_ptr): - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - insert_pos = None - insert_ndx = None - n = 0 - while n < count: - pos = block.tell() - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - if e == entry: - if n == count - 1: - right_ptr = next_node - next_node = ptr - block_seek(pos) - else: - right_ptr = block.read(b'>I')[0] - block.seek(pos + 4) - insert_pos = pos - insert_ndx = n - block.delete(e.byte_length() + 4) - count -= 1 - self._records += 1 - self._dirty = True - continue - elif insert_pos is None and e > entry: - insert_pos = pos - insert_ndx = n - n += 1 - if insert_pos is None: - insert_pos = block.tell() - insert_ndx = count - remaining = self._page_size - block.tell() - - if remaining < entry.byte_length() + 4: - pivot, new_right = self._split (node, entry, right_ptr) - if path: - self._insert_inner(path[:-1], path[-1], pivot, new_right) - else: - self._new_root(node, pivot, new_right) - else: - if insert_ndx == count: - block.seek(insert_pos) - block.write(b'>I', next_node) - entry.write(block) - next_node = right_ptr - else: - block.seek(insert_pos + 4) - entry.write(block, True) - block.insert('>I', right_ptr) - block.seek(0) - count += 1 - block.write(b'>II', next_node, count) - self._records += 1 - self._dirty = True - - # Insert `entry' into the leaf node `node' - def _insert_leaf(self, path, node, entry): - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - insert_pos = None - insert_ndx = None - n = 0 - while n < count: - pos = block.tell() - e = DSStoreEntry.read(block) - if e == entry: - insert_pos = pos - insert_ndx = n - block.seek(pos) - block.delete(e.byte_length()) - count -= 1 - self._records += 1 - self._dirty = True - continue - elif insert_pos is None and e > entry: - insert_pos = pos - insert_ndx = n - n += 1 - if insert_pos is None: - insert_pos = block.tell() - insert_ndx = count - remaining = self._page_size - block.tell() - - if remaining < entry.byte_length(): - pivot, new_right = self._split (node, entry) - if path: - self._insert_inner(path[:-1], path[-1], pivot, new_right) - else: - self._new_root(node, pivot, new_right) - else: - block.seek(insert_pos) - entry.write(block, True) - block.seek(0) - count += 1 - block.write(b'>II', next_node, count) - self._records += 1 - self._dirty = True - - def insert(self, entry): - """Insert ``entry`` (which should be a :class:`DSStoreEntry`) - into the B-Tree.""" - path = [] - node = self._rootnode - while True: - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - if next_node: - for n in range(count): - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - if entry < e: - next_node = ptr - break - elif entry == e: - # If we find an existing entry the same, replace it - self._insert_inner (path, node, entry, None) - return - path.append(node) - node = next_node - else: - self._insert_leaf (path, node, entry) - return - - # Return usage information for the specified `node' - def _block_usage(self, node): - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - - for n in range(count): - if next_node: - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - - used = block.tell() - - return (count, used) - - # Splits entries across three blocks, returning two pivots - def _split3(self, blocks, entries, pointers, before, internal): - count = len(entries) - - # Find the feasible splits - best_split = None - best_diff = None - total = before[count] - for n in range(1, count - 3): - left_size = 8 + before[n] - remaining = 16 + total - before[n + 1] - - if left_size > self._page_size: - break - if remaining > 2 * self._page_size: - continue - - for m in range(n + 2, count - 1): - mid_size = 8 + before[m] - before[n + 1] - right_size = 8 + total - before[m + 1] - - if mid_size > self._page_size: - break - if right_size > self._page_size: - continue - - diff = abs(left_size - mid_size) * abs(right_size - mid_size) - - if best_split is None or diff < best_diff: - best_split = (n, m, count) - best_diff = diff - - if best_split is None: - return None - - # Write the nodes - prev_split = -1 - for block, split in zip(blocks, best_split): - block.seek(0) - if internal: - next_node = pointers[split] - else: - next_node = 0 - block.write(b'>II', next_node, split) - - for n in range(prev_split + 1, split): - if internal: - block.write(b'>I', pointers[n]) - entries[n].write(block) - - block.zero_fill() - - prev_split = split - - return (entries[best_split[0]], entries[best_split[1]]) - - # Extract all of the entries from the specified list of `blocks', - # separating them by the specified `pivots'. Also computes the - # amount of space used before each entry. - def _extract(self, blocks, pivots): - pointers = [] - entries = [] - before = [] - total = 0 - ppivots = pivots + [None] - for b,p in zip(blocks, ppivots): - b.seek(0) - next_node, count = b.read(b'>II') - for n in range(count): - pos = b.tell() - if next_node: - ptr = b.read(b'>I')[0] - pointers.append(ptr) - e = DSStoreEntry.read(b) - entries.append(e) - before.append(total) - total += b.tell() - pos - if next_node: - pointers.append(next_node) - if p: - entries.append(p) - before.append(total) - total += p.byte_length() - if next_node: - total += 4 - before.append(total) - - return (entries, pointers, before) - - # Rebalance the specified `node', whose path from the root is `path'. - def _rebalance(self, path, node): - # Can't rebalance the root - if not path: - return - - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - - with self._get_block(path[-1]) as parent: - # Find the left and right siblings and respective pivots - parent_next, parent_count = parent.read(b'>II') - left_pos = None - left_node = None - left_pivot = None - node_pos = None - right_pos = None - right_node = None - right_pivot = None - prev_e = prev_ptr = prev_pos = None - for n in range(parent_count): - pos = parent.tell() - ptr = parent.read(b'>I')[0] - e = DSStoreEntry.read(parent) - - if ptr == node: - node_pos = pos - right_pivot = e - left_pos = prev_pos - left_pivot = prev_e - left_node = prev_ptr - elif prev_ptr == node: - right_node = ptr - right_pos = pos - break - - prev_e = e - prev_ptr = ptr - prev_pos = pos - - if parent_next == node: - node_pos = parent.tell() - left_pos = prev_pos - left_pivot = prev_e - left_node = prev_ptr - elif right_node is None: - right_node = parent_next - right_pos = parent.tell() - - parent_used = parent.tell() - - if left_node and right_node: - with self._get_block(left_node) as left, \ - self._get_block(right_node) as right: - blocks = [left, block, right] - pivots = [left_pivot, right_pivot] - - entries, pointers, before = self._extract(blocks, pivots) - - # If there's a chance that we could use two pages instead - # of three, go for it - pivots = self._split2 (blocks, entries, pointers, - before, bool(next_node)) - if pivots is None: - ptrs = [left_node, node, right_node] - pivots = self._split3 (blocks, entries, pointers, - before, bool(next_node)) - else: - if pivots: - ptrs = [left_node, node] - else: - ptrs = [left_node] - self._store.release (node) - self._nodes -= 1 - node = left_node - self._store.release (right_node) - self._nodes -= 1 - self._dirty = True - - # Remove the pivots from the parent - with self._get_block(path[-1]) as parent: - if right_node == parent_next: - parent.seek(left_pos) - parent.delete(right_pos - left_pos) - parent_next = left_node - else: - parent.seek(left_pos + 4) - parent.delete(right_pos - left_pos) - parent.seek(0) - parent_count -= 2 - parent.write(b'>II', parent_next, parent_count) - self._records -= 2 - - # Replace with those in pivots - for e,rp in zip(pivots, ptrs[1:]): - self._insert_inner(path[:-1], path[-1], e, rp) - elif left_node: - with self._get_block(left_node) as left: - blocks = [left, block] - pivots = [left_pivot] - - entries, pointers, before = self._extract(blocks, pivots) - - pivots = self._split2 (blocks, entries, pointers, - before, bool(next_node)) - - # Remove the pivot from the parent - with self._get_block(path[-1]) as parent: - if node == parent_next: - parent.seek(left_pos) - parent.delete(node_pos - left_pos) - parent_next = left_node - else: - parent.seek(left_pos + 4) - parent.delete(node_pos - left_pos) - parent.seek(0) - parent_count -= 1 - parent.write(b'>II', parent_next, parent_count) - self._records -= 1 - - # Replace the pivot - if pivots: - self._insert_inner(path[:-1], path[-1], pivots[0], node) - elif right_node: - with self._get_block(right_node) as right: - blocks = [block, right] - pivots = [right_pivot] - - entries, pointers, before = self._extract(blocks, pivots) - - pivots = self._split2 (blocks, entries, pointers, - before, bool(next_node)) - - # Remove the pivot from the parent - with self._get_block(path[-1]) as parent: - if right_node == parent_next: - parent.seek(pos) - parent.delete(right_pos - node_pos) - parent_next = node - else: - parent.seek(pos + 4) - parent.delete(right_pos - node_pos) - parent.seek(0) - parent_count -= 1 - parent.write(b'>II', parent_next, parent_count) - self._records -= 1 - - # Replace the pivot - if pivots: - self._insert_inner(path[:-1], path[-1], pivots[0], - right_node) - - if not path and not parent_count: - self._store.release (path[-1]) - self._nodes -= 1 - self._dirty = True - self._rootnode = node - else: - count, used = self._block_usage(path[-1]) - - if used < self._page_size / 2: - self._rebalance (path[:-1], path[-1]) - - # Delete from the leaf node `node'. `filename_lc' has already been - # lower-cased. - def _delete_leaf(self, node, filename_lc, code): - found = False - - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - - for n in range(count): - pos = block.tell() - e = DSStoreEntry.read(block) - if e.filename.lower() == filename_lc \ - and (code is None or e.code == code): - block.seek(pos) - block.delete(e.byte_length()) - found = True - - # This does not affect the loop; THIS IS NOT A BUG - count -= 1 - - self._records -= 1 - self._dirty = True - - if found: - used = block.tell() - - block.seek(0) - block.write(b'>II', next_node, count) - - return used < self._page_size / 2 - else: - return False - - # Remove the largest entry from the subtree starting at `node' (with - # path from root `path'). Returns a tuple (rebalance, entry) where - # rebalance is either None if no rebalancing is required, or a - # (path, node) tuple giving the details of the node to rebalance. - def _take_largest(self, path, node): - path = list(path) - rebalance = None - while True: - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - - if next_node: - path.append(node) - node = next_node - continue - - for n in range(count): - pos = block.tell() - e = DSStoreEntry.read(block) - - count -= 1 - block.seek(0) - block.write(b'>II', next_node, count) - - if pos < self._page_size / 2: - rebalance = (path, node) - break - - return rebalance, e - - # Delete an entry from an inner node, `node' - def _delete_inner(self, path, node, filename_lc, code): - rebalance = False - - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - - for n in range(count): - pos = block.tell() - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - if e.filename.lower() == filename_lc \ - and (code is None or e.code == code): - # Take the largest from the left subtree - rebalance, largest = self._take_largest(path, ptr) - - # Delete this entry - if n == count - 1: - right_ptr = next_node - next_node = ptr - block.seek(pos) - else: - right_ptr = block.read(b'>I')[0] - block.seek(pos + 4) - - block.delete(e.byte_length() + 4) - - count -= 1 - block.seek(0) - block.write(b'>II', next_node, count) - - self._records -= 1 - self._dirty = True - - break - - # Replace the pivot value - self._insert_inner(path, node, largest, right_ptr) - - # Rebalance from the node we stole from - if rebalance: - self._rebalance (rebalance[0], rebalance[1]) - return True - return False - - def delete(self, filename, code): - """Delete an item, identified by ``filename`` and ``code`` - from the B-Tree.""" - if isinstance(filename, DSStoreEntry): - code = filename.code - filename = filename.filename - - # If we're deleting *every* node for "filename", we must recurse - if code is None: - ###TODO: Fix this so we can do bulk deletes - raise ValueError('You must delete items individually. Sorry') - - # Otherwise, we're deleting *one* specific node - filename_lc = filename.lower() - path = [] - node = self._rootnode - while True: - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - if next_node: - for n in range(count): - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - e_lc = e.filename.lower() - if filename_lc < e_lc \ - or (filename_lc == e_lc and code < e.code): - next_node = ptr - break - elif filename_lc == e_lc and code == e.code: - self._delete_inner (path, node, filename_lc, code) - return - path.append(node) - node = next_node - else: - if self._delete_leaf (node, filename_lc, code): - self._rebalance (path, node) - return - - # Find implementation - def _find(self, node, filename_lc, code=None): - with self._get_block(node) as block: - next_node, count = block.read(b'>II') - if next_node: - for n in range(count): - ptr = block.read(b'>I')[0] - e = DSStoreEntry.read(block) - if filename_lc < e.filename.lower(): - for e in self._find(ptr, filename_lc, code): - yield e - return - elif filename_lc == e.filename.lower(): - if code is None or (code and code < e.code): - for e in self._find(ptr, filename_lc, code): - yield e - if code is None or code == e.code: - yield e - elif code < e.code: - return - for e in self._find(next_node, filename_lc, code): - yield e - else: - for n in range(count): - e = DSStoreEntry.read(block) - if filename_lc == e.filename.lower(): - if code is None or code == e.code: - yield e - elif code < e.code: - return - - def find(self, filename, code=None): - """Returns a generator that will iterate over matching entries in - the B-Tree.""" - if isinstance(filename, DSStoreEntry): - code = filename.code - filename = filename.filename - - filename_lc = filename.lower() - - return self._find(self._rootnode, filename_lc, code) - - def __len__(self): - return self._records - - def __iter__(self): - return self._traverse(self._rootnode) - - class Partial (object): - """This is used to implement indexing.""" - def __init__(self, store, filename): - self._store = store - self._filename = filename - - def __getitem__(self, code): - if code is None: - raise KeyError('no such key - [%s][None]' % self._filename) - - try: - item = six.advance_iterator( - self._store.find(self._filename, code)) - except StopIteration: - raise KeyError('no such key - [%s][%s]' % (self._filename, - code)) - - if not isinstance(item.type, basestring): - return item.value - - return (item.type, item.value) - - def __setitem__(self, code, value): - if code is None: - raise KeyError('bad key - [%s][None]' % self._filename) - - codec = codecs.get(code, None) - if codec: - entry_type = codec - entry_value = value - else: - entry_type = value[0] - entry_value = value[1] - - self._store.insert(DSStoreEntry (self._filename, code, - entry_type, entry_value)) - - def __delitem__(self, code): - if code is None: - raise KeyError('no such key - [%s][None]' % self._filename) - - self._store.delete(self._filename, code) - - def __iter__(self): - for item in self._store.find(self._filename): - yield item - - def __getitem__(self, filename): - return self.Partial(self, filename) - diff --git a/src/mac-app/tools/createDSStore/mac_alias/__init__.py b/src/mac-app/tools/createDSStore/mac_alias/__init__.py deleted file mode 100644 index 8c9220c9981..00000000000 --- a/src/mac-app/tools/createDSStore/mac_alias/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -from .alias import * - -__all__ = [ 'ALIAS_KIND_FILE', 'ALIAS_KIND_FOLDER', - 'ALIAS_HFS_VOLUME_SIGNATURE', - 'ALIAS_FIXED_DISK', 'ALIAS_NETWORK_DISK', 'ALIAS_400KB_FLOPPY_DISK', - 'ALIAS_800KB_FLOPPY_DISK', 'ALIAS_1_44MB_FLOPPY_DISK', - 'ALIAS_EJECTABLE_DISK', - 'ALIAS_NO_CNID', - 'AppleShareInfo', - 'VolumeInfo', - 'TargetInfo', - 'Alias' ] - - diff --git a/src/mac-app/tools/createDSStore/mac_alias/alias.py b/src/mac-app/tools/createDSStore/mac_alias/alias.py deleted file mode 100644 index b35f4029b6c..00000000000 --- a/src/mac-app/tools/createDSStore/mac_alias/alias.py +++ /dev/null @@ -1,587 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals -from __future__ import division - -import struct -import datetime -import io -import re -import os -import os.path -import stat -import sys - -if sys.platform == 'darwin': - from . import osx - -from .utils import * - -ALIAS_KIND_FILE = 0 -ALIAS_KIND_FOLDER = 1 - -ALIAS_HFS_VOLUME_SIGNATURE = b'H+' - -ALIAS_FIXED_DISK = 0 -ALIAS_NETWORK_DISK = 1 -ALIAS_400KB_FLOPPY_DISK = 2 -ALIAS_800KB_FLOPPY_DISK = 3 -ALIAS_1_44MB_FLOPPY_DISK = 4 -ALIAS_EJECTABLE_DISK = 5 - -ALIAS_NO_CNID = 0xffffffff - -class AppleShareInfo (object): - def __init__(self, zone=None, server=None, user=None): - #: The AppleShare zone - self.zone = zone - #: The AFP server - self.server = server - #: The username - self.user = user - - def __repr__(self): - return 'AppleShareInfo(%r,%r,%r)' % (self.zone, self.server, self.user) - -class VolumeInfo (object): - def __init__(self, name, creation_date, fs_type, disk_type, - attribute_flags, fs_id, appleshare_info=None, - driver_name=None, posix_path=None, disk_image_alias=None, - dialup_info=None, network_mount_info=None): - #: The name of the volume on which the target resides - self.name = name - - #: The creation date of the target's volume - self.creation_date = creation_date - - #: The filesystem type (a two character code, e.g. ``b'H+'`` for HFS+) - self.fs_type = fs_type - - #: The type of disk; should be one of - #: - #: * ALIAS_FIXED_DISK - #: * ALIAS_NETWORK_DISK - #: * ALIAS_400KB_FLOPPY_DISK - #: * ALIAS_800KB_FLOPPY_DISK - #: * ALIAS_1_44MB_FLOPPY_DISK - #: * ALIAS_EJECTABLE_DISK - self.disk_type = disk_type - - #: Filesystem attribute flags (from HFS volume header) - self.attribute_flags = attribute_flags - - #: Filesystem identifier - self.fs_id = fs_id - - #: AppleShare information (for automatic remounting of network shares) - #: *(optional)* - self.appleshare_info = appleshare_info - - #: Driver name (*probably* contains a disk driver name on older Macs) - #: *(optional)* - self.driver_name = driver_name - - #: POSIX path of the mount point of the target's volume - #: *(optional)* - self.posix_path = posix_path - - #: :class:`Alias` object pointing at the disk image on which the - #: target's volume resides *(optional)* - self.disk_image_alias = disk_image_alias - - #: Dialup information (for automatic establishment of dialup connections) - self.dialup_info = dialup_info - - #: Network mount information (for automatic remounting) - self.network_mount_info = network_mount_info - - def __repr__(self): - args = ['name', 'creation_date', 'fs_type', 'disk_type', - 'attribute_flags', 'fs_id'] - values = [] - for a in args: - v = getattr(self, a) - values.append(repr(v)) - - kwargs = ['appleshare_info', 'driver_name', 'posix_path', - 'disk_image_alias', 'dialup_info', 'network_mount_info'] - for a in kwargs: - v = getattr(self, a) - if v is not None: - values.append('%s=%r' % (a, v)) - return 'VolumeInfo(%s)' % ','.join(values) - -class TargetInfo (object): - def __init__(self, kind, filename, folder_cnid, cnid, creation_date, - creator_code, type_code, levels_from=-1, levels_to=-1, - folder_name=None, cnid_path=None, carbon_path=None, - posix_path=None, user_home_prefix_len=None): - #: Either ALIAS_KIND_FILE or ALIAS_KIND_FOLDER - self.kind = kind - - #: The filename of the target - self.filename = filename - - #: The CNID (Catalog Node ID) of the target's containing folder; - #: CNIDs are similar to but different than traditional UNIX inode - #: numbers - self.folder_cnid = folder_cnid - - #: The CNID (Catalog Node ID) of the target - self.cnid = cnid - - #: The target's *creation* date. - self.creation_date = creation_date - - #: The target's Mac creator code (a four-character binary string) - self.creator_code = creator_code - - #: The target's Mac type code (a four-character binary string) - self.type_code = type_code - - #: The depth of the alias? Always seems to be -1 on OS X. - self.levels_from = levels_from - - #: The depth of the target? Always seems to be -1 on OS X. - self.levels_to = levels_to - - #: The (POSIX) name of the target's containing folder. *(optional)* - self.folder_name = folder_name - - #: The path from the volume root as a sequence of CNIDs. *(optional)* - self.cnid_path = cnid_path - - #: The Carbon path of the target *(optional)* - self.carbon_path = carbon_path - - #: The POSIX path of the target relative to the volume root. Note - #: that this may or may not have a leading '/' character, but it is - #: always relative to the containing volume. *(optional)* - self.posix_path = posix_path - - #: If the path points into a user's home folder, the number of folders - #: deep that we go before we get to that home folder. *(optional)* - self.user_home_prefix_len = user_home_prefix_len - - def __repr__(self): - args = ['kind', 'filename', 'folder_cnid', 'cnid', 'creation_date', - 'creator_code', 'type_code'] - values = [] - for a in args: - v = getattr(self, a) - values.append(repr(v)) - - if self.levels_from != -1: - values.append('levels_from=%r' % self.levels_from) - if self.levels_to != -1: - values.append('levels_to=%r' % self.levels_to) - - kwargs = ['folder_name', 'cnid_path', 'carbon_path', - 'posix_path', 'user_home_prefix_len'] - for a in kwargs: - v = getattr(self, a) - values.append('%s=%r' % (a, v)) - - return 'TargetInfo(%s)' % ','.join(values) - -TAG_CARBON_FOLDER_NAME = 0 -TAG_CNID_PATH = 1 -TAG_CARBON_PATH = 2 -TAG_APPLESHARE_ZONE = 3 -TAG_APPLESHARE_SERVER_NAME = 4 -TAG_APPLESHARE_USERNAME = 5 -TAG_DRIVER_NAME = 6 -TAG_NETWORK_MOUNT_INFO = 9 -TAG_DIALUP_INFO = 10 -TAG_UNICODE_FILENAME = 14 -TAG_UNICODE_VOLUME_NAME = 15 -TAG_HIGH_RES_VOLUME_CREATION_DATE = 16 -TAG_HIGH_RES_CREATION_DATE = 17 -TAG_POSIX_PATH = 18 -TAG_POSIX_PATH_TO_MOUNTPOINT = 19 -TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE = 20 -TAG_USER_HOME_LENGTH_PREFIX = 21 - -class Alias (object): - def __init__(self, appinfo=b'\0\0\0\0', version=2, volume=None, - target=None, extra=[]): - """Construct a new :class:`Alias` object with the specified - contents.""" - - #: Application specific information (four byte byte-string) - self.appinfo = appinfo - - #: Version (we support only version 2) - self.version = version - - #: A :class:`VolumeInfo` object describing the target's volume - self.volume = volume - - #: A :class:`TargetInfo` object describing the target - self.target = target - - #: A list of extra `(tag, value)` pairs - self.extra = list(extra) - - @classmethod - def _from_fd(cls, b): - appinfo, recsize, version = struct.unpack(b'>4shh', b.read(8)) - - if recsize < 150: - raise ValueError('Incorrect alias length') - - if version != 2: - raise ValueError('Unsupported alias version %u' % version) - - kind, volname, voldate, fstype, disktype, \ - folder_cnid, filename, cnid, crdate, creator_code, type_code, \ - levels_from, levels_to, volattrs, volfsid, reserved = \ - struct.unpack(b'>h28pI2shI64pII4s4shhI2s10s', b.read(142)) - - voldate = mac_epoch + datetime.timedelta(seconds=voldate) - crdate = mac_epoch + datetime.timedelta(seconds=crdate) - - alias = Alias() - alias.appinfo = appinfo - - alias.volume = VolumeInfo (volname.replace('/',':'), - voldate, fstype, disktype, - volattrs, volfsid) - alias.target = TargetInfo (kind, filename.replace('/',':'), - folder_cnid, cnid, - crdate, creator_code, type_code) - alias.target.levels_from = levels_from - alias.target.levels_to = levels_to - - tag = struct.unpack(b'>h', b.read(2))[0] - - while tag != -1: - length = struct.unpack(b'>h', b.read(2))[0] - value = b.read(length) - if length & 1: - b.read(1) - - if tag == TAG_CARBON_FOLDER_NAME: - alias.target.folder_name = value.replace('/',':') - elif tag == TAG_CNID_PATH: - alias.target.cnid_path = struct.unpack(b'>%uI' % (length // 4), - value) - elif tag == TAG_CARBON_PATH: - alias.target.carbon_path = value - elif tag == TAG_APPLESHARE_ZONE: - if alias.volume.appleshare_info is None: - alias.volume.appleshare_info = AppleShareInfo() - alias.volume.appleshare_info.zone = value - elif tag == TAG_APPLESHARE_SERVER_NAME: - if alias.volume.appleshare_info is None: - alias.volume.appleshare_info = AppleShareInfo() - alias.volume.appleshare_info.server = value - elif tag == TAG_APPLESHARE_USERNAME: - if alias.volume.appleshare_info is None: - alias.volume.appleshare_info = AppleShareInfo() - alias.volume.appleshare_info.user = value - elif tag == TAG_DRIVER_NAME: - alias.volume.driver_name = value - elif tag == TAG_NETWORK_MOUNT_INFO: - alias.volume.network_mount_info = value - elif tag == TAG_DIALUP_INFO: - alias.volume.dialup_info = value - elif tag == TAG_UNICODE_FILENAME: - alias.target.filename = value[2:].decode('utf-16be') - elif tag == TAG_UNICODE_VOLUME_NAME: - alias.volume.name = value[2:].decode('utf-16be') - elif tag == TAG_HIGH_RES_VOLUME_CREATION_DATE: - seconds = struct.unpack(b'>Q', value)[0] / 65536.0 - alias.volume.creation_date \ - = mac_epoch + datetime.timedelta(seconds=seconds) - elif tag == TAG_HIGH_RES_CREATION_DATE: - seconds = struct.unpack(b'>Q', value)[0] / 65536.0 - alias.target.creation_date \ - = mac_epoch + datetime.timedelta(seconds=seconds) - elif tag == TAG_POSIX_PATH: - alias.target.posix_path = value - elif tag == TAG_POSIX_PATH_TO_MOUNTPOINT: - alias.volume.posix_path = value - elif tag == TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE: - alias.volume.disk_image_alias = Alias.from_bytes(value) - elif tag == TAG_USER_HOME_LENGTH_PREFIX: - alias.target.user_home_prefix_len = struct.unpack(b'>h', value)[0] - else: - alias.extra.append((tag, value)) - - tag = struct.unpack(b'>h', b.read(2))[0] - - return alias - - @classmethod - def from_bytes(cls, bytes): - """Construct an :class:`Alias` object given binary Alias data.""" - with io.BytesIO(bytes) as b: - return cls._from_fd(b) - - @classmethod - def for_file(cls, path): - """Create an :class:`Alias` that points at the specified file.""" - if sys.platform != 'darwin': - raise Exception('Not implemented (requires special support)') - - a = Alias() - - # Find the filesystem - st = osx.statfs(path) - vol_path = st.f_mntonname - - # Grab its attributes - attrs = [osx.ATTR_CMN_CRTIME, - osx.ATTR_VOL_NAME, - 0, 0, 0] - volinfo = osx.getattrlist(vol_path, attrs, 0) - - vol_crtime = volinfo[0] - vol_name = volinfo[1] - - # Also grab various attributes of the file - attrs = [(osx.ATTR_CMN_OBJTYPE - | osx.ATTR_CMN_CRTIME - | osx.ATTR_CMN_FNDRINFO - | osx.ATTR_CMN_FILEID - | osx.ATTR_CMN_PARENTID), 0, 0, 0, 0] - info = osx.getattrlist(path, attrs, osx.FSOPT_NOFOLLOW) - - if info[0] == osx.VDIR: - kind = ALIAS_KIND_FOLDER - else: - kind = ALIAS_KIND_FILE - - cnid = info[3] - folder_cnid = info[4] - - dirname, filename = os.path.split(path) - - if dirname == '' or dirname == '.': - dirname = os.getcwd() - - foldername = os.path.basename(dirname) - - creation_date = info[1] - - if kind == ALIAS_KIND_FILE: - creator_code = struct.pack(b'I', info[2].fileInfo.fileCreator) - type_code = struct.pack(b'I', info[2].fileInfo.fileType) - else: - creator_code = b'\0\0\0\0' - type_code = b'\0\0\0\0' - - a.target = TargetInfo(kind, filename, folder_cnid, cnid, creation_date, - creator_code, type_code) - a.volume = VolumeInfo(vol_name, vol_crtime, b'H+', - ALIAS_FIXED_DISK, 0, b'\0\0') - - a.target.folder_name = foldername - a.volume.posix_path = vol_path - - rel_path = os.path.relpath(path, vol_path) - - # Leave off the initial '/' if vol_path is '/' (no idea why) - if vol_path == '/': - a.target.posix_path = rel_path - else: - a.target.posix_path = '/' + rel_path - - # Construct the Carbon and CNID paths - carbon_path = [] - cnid_path = [] - head, tail = os.path.split(rel_path) - if not tail: - head, tail = os.path.split(head) - while head or tail: - if head: - attrs = [osx.ATTR_CMN_FILEID, 0, 0, 0, 0] - info = osx.getattrlist(os.path.join(vol_path, head), attrs, 0) - cnid_path.append(info[0]) - carbon_tail = tail.replace(':','/') - carbon_path.insert(0, carbon_tail) - head, tail = os.path.split(head) - carbon_path = vol_name + ':' + ':\0'.join(carbon_path) - - a.target.carbon_path = carbon_path - a.target.cnid_path = cnid_path - - return a - - def _to_fd(self, b): - # We'll come back and fix the length when we're done - pos = b.tell() - b.write(struct.pack(b'>4shh', self.appinfo, 0, self.version)) - - carbon_volname = self.volume.name.replace(':','/').encode('utf-8') - carbon_filename = self.target.filename.replace(':','/').encode('utf-8') - voldate = (self.volume.creation_date - mac_epoch).total_seconds() - crdate = (self.target.creation_date - mac_epoch).total_seconds() - - # NOTE: crdate should be in local time, but that's system dependent - # (so doing so is ridiculous, and nothing could rely on it). - b.write(struct.pack(b'>h28pI2shI64pII4s4shhI2s10s', - self.target.kind, - carbon_volname, voldate, - self.volume.fs_type, - self.volume.disk_type, - self.target.folder_cnid, - carbon_filename, - self.target.cnid, - crdate, - self.target.creator_code, - self.target.type_code, - self.target.levels_from, - self.target.levels_to, - self.volume.attribute_flags, - self.volume.fs_id, - b'\0'*10)) - - # Excuse the odd order; we're copying Finder - if self.target.folder_name: - carbon_foldername = self.target.folder_name.replace(':','/')\ - .encode('utf-8') - b.write(struct.pack(b'>hh', TAG_CARBON_FOLDER_NAME, - len(carbon_foldername))) - b.write(carbon_foldername) - if len(carbon_foldername) & 1: - b.write(b'\0') - - b.write(struct.pack(b'>hhQhhQ', - TAG_HIGH_RES_VOLUME_CREATION_DATE, - 8, long(voldate * 65536), - TAG_HIGH_RES_CREATION_DATE, - 8, long(crdate * 65536))) - - if self.target.cnid_path: - cnid_path = struct.pack(b'>%uI' % len(self.target.cnid_path), - *self.target.cnid_path) - b.write(struct.pack(b'>hh', TAG_CNID_PATH, - len(cnid_path))) - b.write(cnid_path) - - if self.target.carbon_path: - carbon_path=self.target.carbon_path.encode('utf-8') - b.write(struct.pack(b'>hh', TAG_CARBON_PATH, - len(carbon_path))) - b.write(carbon_path) - if len(carbon_path) & 1: - b.write(b'\0') - - if self.volume.appleshare_info: - ai = self.volume.appleshare_info - if ai.zone: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_ZONE, - len(ai.zone))) - b.write(ai.zone) - if len(ai.zone) & 1: - b.write(b'\0') - if ai.server: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_SERVER_NAME, - len(ai.server))) - b.write(ai.server) - if len(ai.server) & 1: - b.write(b'\0') - if ai.username: - b.write(struct.pack(b'>hh', TAG_APPLESHARE_USERNAME, - len(ai.username))) - b.write(ai.username) - if len(ai.username) & 1: - b.write(b'\0') - - if self.volume.driver_name: - driver_name = self.volume.driver_name.encode('utf-8') - b.write(struct.pack(b'>hh', TAG_DRIVER_NAME, - len(driver_name))) - b.write(driver_name) - if len(driver_name) & 1: - b.write(b'\0') - - if self.volume.network_mount_info: - b.write(struct.pack(b'>hh', TAG_NETWORK_MOUNT_INFO, - len(self.volume.network_mount_info))) - b.write(self.volume.network_mount_info) - if len(self.volume.network_mount_info) & 1: - b.write(b'\0') - - if self.volume.dialup_info: - b.write(struct.pack(b'>hh', TAG_DIALUP_INFO, - len(self.volume.network_mount_info))) - b.write(self.volume.network_mount_info) - if len(self.volume.network_mount_info) & 1: - b.write(b'\0') - - utf16 = self.target.filename.replace(':','/').encode('utf-16-be') - b.write(struct.pack(b'>hhh', TAG_UNICODE_FILENAME, - len(utf16) + 2, - len(utf16) // 2)) - b.write(utf16) - - utf16 = self.volume.name.replace(':','/').encode('utf-16-be') - b.write(struct.pack(b'>hhh', TAG_UNICODE_VOLUME_NAME, - len(utf16) + 2, - len(utf16) // 2)) - b.write(utf16) - - if self.target.posix_path: - posix_path = self.target.posix_path.encode('utf-8') - b.write(struct.pack(b'>hh', TAG_POSIX_PATH, - len(posix_path))) - b.write(posix_path) - if len(posix_path) & 1: - b.write(b'\0') - - if self.volume.posix_path: - posix_path = self.volume.posix_path.encode('utf-8') - b.write(struct.pack(b'>hh', TAG_POSIX_PATH_TO_MOUNTPOINT, - len(posix_path))) - b.write(posix_path) - if len(posix_path) & 1: - b.write(b'\0') - - if self.volume.disk_image_alias: - d = self.volume.disk_image_alias.to_bytes() - b.write(struct.pack(b'>hh', TAG_RECURSIVE_ALIAS_OF_DISK_IMAGE, - len(d))) - b.write(d) - if len(d) & 1: - b.write(b'\0') - - if self.target.user_home_prefix_len is not None: - b.write(struct.pack(b'>hhh', TAG_USER_HOME_LENGTH_PREFIX, - 2, self.target.user_home_prefix_len)) - - for t,v in self.extra: - b.write(struct.pack(b'>hh', t, len(v))) - b.write(v) - if len(v) & 1: - b.write(b'\0') - - b.write(struct.pack(b'>hh', -1, 0)) - - blen = b.tell() - pos - b.seek(pos + 4, os.SEEK_SET) - b.write(struct.pack(b'>h', blen)) - - def to_bytes(self): - """Returns the binary representation for this :class:`Alias`.""" - with io.BytesIO() as b: - self._to_fd(b) - return b.getvalue() - - def __str__(self): - return '' % self.target.filename - - def __repr__(self): - values = [] - if self.appinfo != b'\0\0\0\0': - values.append('appinfo=%r' % self.appinfo) - if self.version != 2: - values.append('version=%r' % self.version) - if self.volume is not None: - values.append('volume=%r' % self.volume) - if self.target is not None: - values.append('target=%r' % self.target) - if self.extra: - values.append('extra=%r' % self.extra) - return 'Alias(%s)' % ','.join(values) diff --git a/src/mac-app/tools/createDSStore/mac_alias/osx.py b/src/mac-app/tools/createDSStore/mac_alias/osx.py deleted file mode 100644 index bdd5d09e959..00000000000 --- a/src/mac-app/tools/createDSStore/mac_alias/osx.py +++ /dev/null @@ -1,823 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from ctypes import * -import struct -import os -import datetime -import uuid - -from .utils import * - -libc = cdll.LoadLibrary('/usr/lib/libc.dylib') - -# Constants -FSOPT_NOFOLLOW = 0x00000001 -FSOPT_NOINMEMUPDATE = 0x00000002 -FSOPT_REPORT_FULLSIZE = 0x00000004 -FSOPT_PACK_INVAL_ATTRS = 0x00000008 - -VOL_CAPABILITIES_FORMAT = 0 -VOL_CAPABILITIES_INTERFACES = 1 - -VOL_CAP_FMT_PERSISTENTOBJECTIDS = 0x00000001 -VOL_CAP_FMT_SYMBOLICLINKS = 0x00000002 -VOL_CAP_FMT_HARDLINKS = 0x00000004 -VOL_CAP_FMT_JOURNAL = 0x00000008 -VOL_CAP_FMT_JOURNAL_ACTIVE = 0x00000010 -VOL_CAP_FMT_NO_ROOT_TIMES = 0x00000020 -VOL_CAP_FMT_SPARSE_FILES = 0x00000040 -VOL_CAP_FMT_ZERO_RUNS = 0x00000080 -VOL_CAP_FMT_CASE_SENSITIVE = 0x00000100 -VOL_CAP_FMT_CASE_PRESERVING = 0x00000200 -VOL_CAP_FMT_FAST_STATFS = 0x00000400 -VOL_CAP_FMT_2TB_FILESIZE = 0x00000800 -VOL_CAP_FMT_OPENDENYMODES = 0x00001000 -VOL_CAP_FMT_HIDDEN_FILES = 0x00002000 -VOL_CAP_FMT_PATH_FROM_ID = 0x00004000 -VOL_CAP_FMT_NO_VOLUME_SIZES = 0x00008000 -VOL_CAP_FMT_DECMPFS_COMPRESSION = 0x00010000 -VOL_CAP_FMT_64BIT_OBJECT_IDS = 0x00020000 - -VOL_CAP_INT_SEARCHFS = 0x00000001 -VOL_CAP_INT_ATTRLIST = 0x00000002 -VOL_CAP_INT_NFSEXPORT = 0x00000004 -VOL_CAP_INT_READDIRATTR = 0x00000008 -VOL_CAP_INT_EXCHANGEDATA = 0x00000010 -VOL_CAP_INT_COPYFILE = 0x00000020 -VOL_CAP_INT_ALLOCATE = 0x00000040 -VOL_CAP_INT_VOL_RENAME = 0x00000080 -VOL_CAP_INT_ADVLOCK = 0x00000100 -VOL_CAP_INT_FLOCK = 0x00000200 -VOL_CAP_INT_EXTENDED_SECURITY = 0x00000400 -VOL_CAP_INT_USERACCESS = 0x00000800 -VOL_CAP_INT_MANLOCK = 0x00001000 -VOL_CAP_INT_NAMEDSTREAMS = 0x00002000 -VOL_CAP_INT_EXTENDED_ATTR = 0x00004000 - -ATTR_CMN_NAME = 0x00000001 -ATTR_CMN_DEVID = 0x00000002 -ATTR_CMN_FSID = 0x00000004 -ATTR_CMN_OBJTYPE = 0x00000008 -ATTR_CMN_OBJTAG = 0x00000010 -ATTR_CMN_OBJID = 0x00000020 -ATTR_CMN_OBJPERMANENTID = 0x00000040 -ATTR_CMN_PAROBJID = 0x00000080 -ATTR_CMN_SCRIPT = 0x00000100 -ATTR_CMN_CRTIME = 0x00000200 -ATTR_CMN_MODTIME = 0x00000400 -ATTR_CMN_CHGTIME = 0x00000800 -ATTR_CMN_ACCTIME = 0x00001000 -ATTR_CMN_BKUPTIME = 0x00002000 -ATTR_CMN_FNDRINFO = 0x00004000 -ATTR_CMN_OWNERID = 0x00008000 -ATTR_CMN_GRPID = 0x00010000 -ATTR_CMN_ACCESSMASK = 0x00020000 -ATTR_CMN_FLAGS = 0x00040000 -ATTR_CMN_USERACCESS = 0x00200000 -ATTR_CMN_EXTENDED_SECURITY = 0x00400000 -ATTR_CMN_UUID = 0x00800000 -ATTR_CMN_GRPUUID = 0x01000000 -ATTR_CMN_FILEID = 0x02000000 -ATTR_CMN_PARENTID = 0x04000000 -ATTR_CMN_FULLPATH = 0x08000000 -ATTR_CMN_ADDEDTIME = 0x10000000 -ATTR_CMN_RETURNED_ATTRS = 0x80000000 -ATTR_CMN_ALL_ATTRS = 0x9fe7ffff - -ATTR_VOL_FSTYPE = 0x00000001 -ATTR_VOL_SIGNATURE = 0x00000002 -ATTR_VOL_SIZE = 0x00000004 -ATTR_VOL_SPACEFREE = 0x00000008 -ATTR_VOL_SPACEAVAIL = 0x00000010 -ATTR_VOL_MINALLOCATION = 0x00000020 -ATTR_VOL_ALLOCATIONCLUMP = 0x00000040 -ATTR_VOL_IOBLOCKSIZE = 0x00000080 -ATTR_VOL_OBJCOUNT = 0x00000100 -ATTR_VOL_FILECOUNT = 0x00000200 -ATTR_VOL_DIRCOUNT = 0x00000400 -ATTR_VOL_MAXOBJCOUNT = 0x00000800 -ATTR_VOL_MOUNTPOINT = 0x00001000 -ATTR_VOL_NAME = 0x00002000 -ATTR_VOL_MOUNTFLAGS = 0x00004000 -ATTR_VOL_MOUNTEDDEVICE = 0x00008000 -ATTR_VOL_ENCODINGSUSED = 0x00010000 -ATTR_VOL_CAPABILITIES = 0x00020000 -ATTR_VOL_UUID = 0x00040000 -ATTR_VOL_ATTRIBUTES = 0x40000000 -ATTR_VOL_INFO = 0x80000000 -ATTR_VOL_ALL_ATTRS = 0xc007ffff - -ATTR_DIR_LINKCOUNT = 0x00000001 -ATTR_DIR_ENTRYCOUNT = 0x00000002 -ATTR_DIR_MOUNTSTATUS = 0x00000004 -DIR_MNTSTATUS_MNTPOINT = 0x00000001 -DIR_MNTSTATUS_TRIGGER = 0x00000002 -ATTR_DIR_ALL_ATTRS = 0x00000007 - -ATTR_FILE_LINKCOUNT = 0x00000001 -ATTR_FILE_TOTALSIZE = 0x00000002 -ATTR_FILE_ALLOCSIZE = 0x00000004 -ATTR_FILE_IOBLOCKSIZE = 0x00000008 -ATTR_FILE_DEVTYPE = 0x00000020 -ATTR_FILE_DATALENGTH = 0x00000200 -ATTR_FILE_DATAALLOCSIZE = 0x00000400 -ATTR_FILE_RSRCLENGTH = 0x00001000 -ATTR_FILE_RSRCALLOCSIZE = 0x00002000 - -ATTR_FILE_ALL_ATTRS = 0x0000362f - -ATTR_FORK_TOTALSIZE = 0x00000001 -ATTR_FORK_ALLOCSIZE = 0x00000002 -ATTR_FORK_ALL_ATTRS = 0x00000003 - -# These can't be used -ATTR_FILE_FORKCOUNT = 0x00000080 -ATTR_FILE_FORKLIST = 0x00000100 -ATTR_CMN_NAMEDATTRCOUNT = 0x00080000 -ATTR_CMN_NAMEDATTRLIST = 0x00100000 -ATTR_FILE_DATAEXTENTS = 0x00000800 -ATTR_FILE_RSRCEXTENTS = 0x00004000 -ATTR_FILE_CLUMPSIZE = 0x00000010 -ATTR_FILE_FILETYPE = 0x00000040 - -class attrlist(Structure): - _fields_ = [('bitmapcount', c_ushort), - ('reserved', c_ushort), - ('commonattr', c_uint), - ('volattr', c_uint), - ('dirattr', c_uint), - ('fileattr', c_uint), - ('forkattr', c_uint)] - -class attribute_set_t(Structure): - _fields_ = [('commonattr', c_uint), - ('volattr', c_uint), - ('dirattr', c_uint), - ('fileattr', c_uint), - ('forkattr', c_uint)] - -class fsobj_id_t(Structure): - _fields_ = [('fid_objno', c_uint), - ('fid_generation', c_uint)] - -class timespec(Structure): - _fields_ = [('tv_sec', c_long), - ('tv_nsec', c_long)] - -class attrreference_t(Structure): - _fields_ = [('attr_dataoffset', c_int), - ('attr_length', c_uint)] - -class fsid_t(Structure): - _fields_ = [('val', c_uint * 2)] - -class guid_t(Structure): - _fields_ = [('g_guid', c_byte*16)] - -class kauth_ace(Structure): - _fields_ = [('ace_applicable', guid_t), - ('ace_flags', c_uint)] - -class kauth_acl(Structure): - _fields_ = [('acl_entrycount', c_uint), - ('acl_flags', c_uint), - ('acl_ace', kauth_ace * 128)] - -class kauth_filesec(Structure): - _fields_ = [('fsec_magic', c_uint), - ('fsec_owner', guid_t), - ('fsec_group', guid_t), - ('fsec_acl', kauth_acl)] - -class diskextent(Structure): - _fields_ = [('startblock', c_uint), - ('blockcount', c_uint)] - -OSType = c_uint -UInt16 = c_ushort -SInt16 = c_short -SInt32 = c_int - -class Point(Structure): - _fields_ = [('x', SInt16), - ('y', SInt16)] -class Rect(Structure): - _fields_ = [('x', SInt16), - ('y', SInt16), - ('w', SInt16), - ('h', SInt16)] -class FileInfo(Structure): - _fields_ = [('fileType', OSType), - ('fileCreator', OSType), - ('finderFlags', UInt16), - ('location', Point), - ('reservedField', UInt16), - ('reserved1', SInt16 * 4), - ('extendedFinderFlags', UInt16), - ('reserved2', SInt16), - ('putAwayFolderID', SInt32)] -class FolderInfo(Structure): - _fields_ = [('windowBounds', Rect), - ('finderFlags', UInt16), - ('location', Point), - ('reservedField', UInt16), - ('scrollPosition', Point), - ('reserved1', SInt32), - ('extendedFinderFlags', UInt16), - ('reserved2', SInt16), - ('putAwayFolderID', SInt32)] -class FinderInfo(Union): - _fields_ = [('fileInfo', FileInfo), - ('folderInfo', FolderInfo)] - -extentrecord = diskextent * 8 - -vol_capabilities_set_t = c_uint * 4 - -class vol_capabilities_attr_t(Structure): - _fields_ = [('capabilities', vol_capabilities_set_t), - ('valid', vol_capabilities_set_t)] - -class vol_attributes_attr_t(Structure): - _fields_ = [('validattr', attribute_set_t), - ('nativeattr', attribute_set_t)] - -dev_t = c_uint - -fsobj_type_t = c_uint - -VNON = 0 -VREG = 1 -VDIR = 2 -VBLK = 3 -VCHR = 4 -VLNK = 5 -VSOCK = 6 -VFIFO = 7 -VBAD = 8 -VSTR = 9 -VCPLX = 10 - -fsobj_tag_t = c_uint - -VT_NON = 0 -VT_UFS = 1 -VT_NFS = 2 -VT_MFS = 3 -VT_MSDOSFS = 4 -VT_LFS = 5 -VT_LOFS = 6 -VT_FDESC = 7 -VT_PORTAL = 8 -VT_NULL = 9 -VT_UMAP = 10 -VT_KERNFS = 11 -VT_PROCFS = 12 -VT_AFS = 13 -VT_ISOFS = 14 -VT_UNION = 15 -VT_HFS = 16 -VT_ZFS = 17 -VT_DEVFS = 18 -VT_WEBDAV = 19 -VT_UDF = 20 -VT_AFP = 21 -VT_CDDA = 22 -VT_CIFS = 23 -VT_OTHER = 24 - -fsfile_type_t = c_uint -fsvolid_t = c_uint -text_encoding_t = c_uint -uid_t = c_uint -gid_t = c_uint -int32_t = c_int -uint32_t = c_uint -int64_t = c_longlong -uint64_t = c_ulonglong -off_t = c_long -size_t = c_ulong -uuid_t = c_byte*16 - -NAME_MAX = 255 -PATH_MAX = 1024 - -class struct_statfs(Structure): - _fields_ = [('f_bsize', uint32_t), - ('f_iosize', int32_t), - ('f_blocks', uint64_t), - ('f_bfree', uint64_t), - ('f_bavail', uint64_t), - ('f_files', uint64_t), - ('f_ffree', uint64_t), - ('f_fsid', fsid_t), - ('f_owner', uid_t), - ('f_type', uint32_t), - ('f_flags', uint32_t), - ('f_fssubtype', uint32_t), - ('f_fstypename', c_char * 16), - ('f_mntonname', c_char * PATH_MAX), - ('f_mntfromname', c_char * PATH_MAX), - ('f_reserved', uint32_t * 8)] - -# Calculate the maximum number of bytes required for the attribute buffer -_attr_info = ( - # Common attributes - (0, ATTR_CMN_RETURNED_ATTRS, sizeof(attribute_set_t)), - (0, ATTR_CMN_NAME, sizeof(attrreference_t) + NAME_MAX * 3 + 1), - (0, ATTR_CMN_DEVID, sizeof(dev_t)), - (0, ATTR_CMN_FSID, sizeof(fsid_t)), - (0, ATTR_CMN_OBJTYPE, sizeof(fsobj_type_t)), - (0, ATTR_CMN_OBJTAG, sizeof(fsobj_tag_t)), - (0, ATTR_CMN_OBJPERMANENTID, sizeof(fsobj_id_t)), - (0, ATTR_CMN_PAROBJID, sizeof(fsobj_id_t)), - (0, ATTR_CMN_SCRIPT, sizeof(text_encoding_t)), - (0, ATTR_CMN_CRTIME, sizeof(timespec)), - (0, ATTR_CMN_MODTIME, sizeof(timespec)), - (0, ATTR_CMN_CHGTIME, sizeof(timespec)), - (0, ATTR_CMN_ACCTIME, sizeof(timespec)), - (0, ATTR_CMN_BKUPTIME, sizeof(timespec)), - (0, ATTR_CMN_FNDRINFO, sizeof(FinderInfo)), - (0, ATTR_CMN_OWNERID, sizeof(uid_t)), - (0, ATTR_CMN_GRPID, sizeof(gid_t)), - (0, ATTR_CMN_ACCESSMASK, sizeof(uint32_t)), - (0, ATTR_CMN_NAMEDATTRCOUNT, None), - (0, ATTR_CMN_NAMEDATTRLIST, None), - (0, ATTR_CMN_FLAGS, sizeof(uint32_t)), - (0, ATTR_CMN_USERACCESS, sizeof(uint32_t)), - (0, ATTR_CMN_EXTENDED_SECURITY, sizeof(attrreference_t) + sizeof(kauth_filesec)), - (0, ATTR_CMN_UUID, sizeof(guid_t)), - (0, ATTR_CMN_GRPUUID, sizeof(guid_t)), - (0, ATTR_CMN_FILEID, sizeof(uint64_t)), - (0, ATTR_CMN_PARENTID, sizeof(uint64_t)), - (0, ATTR_CMN_FULLPATH, sizeof(attrreference_t) + PATH_MAX), - (0, ATTR_CMN_ADDEDTIME, sizeof(timespec)), - - # Volume attributes - (1, ATTR_VOL_FSTYPE, sizeof(uint32_t)), - (1, ATTR_VOL_SIGNATURE, sizeof(uint32_t)), - (1, ATTR_VOL_SIZE, sizeof(off_t)), - (1, ATTR_VOL_SPACEFREE, sizeof(off_t)), - (1, ATTR_VOL_SPACEAVAIL, sizeof(off_t)), - (1, ATTR_VOL_MINALLOCATION, sizeof(off_t)), - (1, ATTR_VOL_ALLOCATIONCLUMP, sizeof(off_t)), - (1, ATTR_VOL_IOBLOCKSIZE, sizeof(uint32_t)), - (1, ATTR_VOL_OBJCOUNT, sizeof(uint32_t)), - (1, ATTR_VOL_FILECOUNT, sizeof(uint32_t)), - (1, ATTR_VOL_DIRCOUNT, sizeof(uint32_t)), - (1, ATTR_VOL_MAXOBJCOUNT, sizeof(uint32_t)), - (1, ATTR_VOL_MOUNTPOINT, sizeof(attrreference_t) + PATH_MAX), - (1, ATTR_VOL_NAME, sizeof(attrreference_t) + NAME_MAX + 1), - (1, ATTR_VOL_MOUNTFLAGS, sizeof(uint32_t)), - (1, ATTR_VOL_MOUNTEDDEVICE, sizeof(attrreference_t) + PATH_MAX), - (1, ATTR_VOL_ENCODINGSUSED, sizeof(c_ulonglong)), - (1, ATTR_VOL_CAPABILITIES, sizeof(vol_capabilities_attr_t)), - (1, ATTR_VOL_UUID, sizeof(uuid_t)), - (1, ATTR_VOL_ATTRIBUTES, sizeof(vol_attributes_attr_t)), - - # Directory attributes - (2, ATTR_DIR_LINKCOUNT, sizeof(uint32_t)), - (2, ATTR_DIR_ENTRYCOUNT, sizeof(uint32_t)), - (2, ATTR_DIR_MOUNTSTATUS, sizeof(uint32_t)), - - # File attributes - (3, ATTR_FILE_LINKCOUNT, sizeof(uint32_t)), - (3, ATTR_FILE_TOTALSIZE, sizeof(off_t)), - (3, ATTR_FILE_ALLOCSIZE, sizeof(off_t)), - (3, ATTR_FILE_IOBLOCKSIZE, sizeof(uint32_t)), - (3, ATTR_FILE_CLUMPSIZE, sizeof(uint32_t)), - (3, ATTR_FILE_DEVTYPE, sizeof(uint32_t)), - (3, ATTR_FILE_FILETYPE, sizeof(uint32_t)), - (3, ATTR_FILE_FORKCOUNT, sizeof(uint32_t)), - (3, ATTR_FILE_FORKLIST, None), - (3, ATTR_FILE_DATALENGTH, sizeof(off_t)), - (3, ATTR_FILE_DATAALLOCSIZE, sizeof(off_t)), - (3, ATTR_FILE_DATAEXTENTS, sizeof(extentrecord)), - (3, ATTR_FILE_RSRCLENGTH, sizeof(off_t)), - (3, ATTR_FILE_RSRCALLOCSIZE, sizeof(off_t)), - (3, ATTR_FILE_RSRCEXTENTS, sizeof(extentrecord)), - - # Fork attributes - (4, ATTR_FORK_TOTALSIZE, sizeof(off_t)), - (4, ATTR_FORK_ALLOCSIZE, sizeof(off_t)) - ) - -def _attrbuf_size(attrs): - size = 4 - for entry in _attr_info: - if attrs[entry[0]] & entry[1]: - if entry[2] is None: - raise ValueError('Unsupported attribute (%u, %x)' - % (entry[0], entry[1])) - size += entry[2] - return size - -_getattrlist = libc.getattrlist -_getattrlist.argtypes = [c_char_p, POINTER(attrlist), c_void_p, c_ulong, c_ulong] -_getattrlist.restype = c_int - -_fgetattrlist = libc.fgetattrlist -_fgetattrlist.argtypes = [c_int, POINTER(attrlist), c_void_p, c_ulong, c_ulong] -_fgetattrlist.restype = c_int - -_statfs = libc['statfs$INODE64'] -_statfs.argtypes = [c_char_p, POINTER(struct_statfs)] -_statfs.restype = c_int - -_fstatfs = libc['fstatfs$INODE64'] -_fstatfs.argtypes = [c_int, POINTER(struct_statfs)] -_fstatfs.restype = c_int - -def _datetime_from_timespec(ts): - td = datetime.timedelta(seconds=ts.tv_sec + 1.0e-9 * ts.tv_nsec) - return unix_epoch + td - -def _decode_utf8_nul(sz): - nul = sz.find('\0') - if nul > -1: - sz = sz[:nul] - return sz.decode('utf-8') - -def _decode_attrlist_result(buf, attrs, options): - result = [] - - assert len(buf) >= 4 - total_size = uint32_t.from_buffer(buf, 0).value - assert total_size <= len(buf) - - offset = 4 - - # Common attributes - if attrs[0] & ATTR_CMN_RETURNED_ATTRS: - a = attribute_set_t.from_buffer(buf, offset) - result.append(a) - offset += sizeof (attribute_set_t) - if not (options & FSOPT_PACK_INVAL_ATTRS): - attrs = [a.commonattr, a.volattr, a.dirattr, a.fileattr, a.forkattr] - if attrs[0] & ATTR_CMN_NAME: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) - result.append(name) - if attrs[0] & ATTR_CMN_DEVID: - a = dev_t.from_buffer(buf, offset) - offset += sizeof(dev_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_FSID: - a = fsid_t.from_buffer(buf, offset) - offset += sizeof(fsid_t) - result.append(a) - if attrs[0] & ATTR_CMN_OBJTYPE: - a = fsobj_type_t.from_buffer(buf, offset) - offset += sizeof(fsobj_type_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_OBJTAG: - a = fsobj_tag_t.from_buffer(buf, offset) - offset += sizeof(fsobj_tag_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_OBJID: - a = fsobj_id_t.from_buffer(buf, offset) - offset += sizeof(fsobj_id_t) - result.append(a) - if attrs[0] & ATTR_CMN_OBJPERMANENTID: - a = fsobj_id_t.from_buffer(buf, offset) - offset += sizeof(fsobj_id_t) - result.append(a) - if attrs[0] & ATTR_CMN_PAROBJID: - a = fsobj_id_t.from_buffer(buf, offset) - offset += sizeof(fsobj_id_t) - result.append(a) - if attrs[0] & ATTR_CMN_SCRIPT: - a = text_encoding_t.from_buffer(buf, offset) - offset += sizeof(text_encoding_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_CRTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - if attrs[0] & ATTR_CMN_MODTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - if attrs[0] & ATTR_CMN_CHGTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - if attrs[0] & ATTR_CMN_ACCTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - if attrs[0] & ATTR_CMN_BKUPTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - if attrs[0] & ATTR_CMN_FNDRINFO: - a = FinderInfo.from_buffer(buf, offset) - offset += sizeof(FinderInfo) - result.append(a) - if attrs[0] & ATTR_CMN_OWNERID: - a = uid_t.from_buffer(buf, offset) - offset += sizeof(uid_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_GRPID: - a = gid_t.from_buffer(buf, offset) - offset += sizeof(gid_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_ACCESSMASK: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_FLAGS: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_USERACCESS: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_EXTENDED_SECURITY: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - offset += sizeof(attrreference_t) - ec = uint32_t.from_buffer(buf, ofs + 36).value - class kauth_acl(Structure): - _fields_ = [('acl_entrycount', c_uint), - ('acl_flags', c_uint), - ('acl_ace', kauth_ace * ec)] - class kauth_filesec(Structure): - _fields_ = [('fsec_magic', c_uint), - ('fsec_owner', guid_t), - ('fsec_group', guid_t), - ('fsec_acl', kauth_acl)] - a = kauth_filesec.from_buffer(buf, ofs) - result.append(a) - if attrs[0] & ATTR_CMN_UUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) - offset += sizeof(guid_t) - if attrs[0] & ATTR_CMN_GRPUUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) - offset += sizeof(guid_t) - if attrs[0] & ATTR_CMN_FILEID: - a = uint64_t.from_buffer(buf, offset) - offset += sizeof(uint64_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_PARENTID: - a = uint64_t.from_buffer(buf, offset) - offset += sizeof(uint64_t) - result.append(a.value) - if attrs[0] & ATTR_CMN_FULLPATH: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) - result.append(path) - if attrs[0] & ATTR_CMN_ADDEDTIME: - a = timespec.from_buffer(buf, offset) - offset += sizeof(timespec) - result.append(_datetime_from_timespec(a)) - - # Volume attributes - if attrs[1] & ATTR_VOL_FSTYPE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_SIGNATURE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_SIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_SPACEFREE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_SPACEAVAIL: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_MINALLOCATION: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_ALLOCATIONCLUMP: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_IOBLOCKSIZE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_OBJCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_FILECOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_DIRCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_MAXOBJCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_MOUNTPOINT: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) - result.append(path) - if attrs[1] & ATTR_VOL_NAME: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - name = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) - result.append(name) - if attrs[1] & ATTR_VOL_MOUNTFLAGS: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[1] & ATTR_VOL_MOUNTEDDEVICE: - a = attrreference_t.from_buffer(buf, offset) - ofs = offset + a.attr_dataoffset - path = _decode_utf8_nul(buf[ofs:ofs+a.attr_length]) - offset += sizeof (attrreference_t) - result.append(path) - if attrs[1] & ATTR_VOL_ENCODINGSUSED: - a = c_ulonglong.from_buffer(buf, offset) - offset += sizeof(c_ulonglong) - result.append(a.value) - if attrs[1] & ATTR_VOL_CAPABILITIES: - a = vol_capabilities_attr_t.from_buffer(buf, offset) - offset += sizeof(vol_capabilities_attr_t) - result.append(a) - if attrs[1] & ATTR_VOL_UUID: - result.append(uuid.UUID(bytes=buf[offset:offset+16])) - offset += sizeof(uuid_t) - if attrs[1] & ATTR_VOL_ATTRIBUTES: - a = vol_attributes_attr_t.from_buffer(buf, offset) - offset += sizeof(vol_attributes_attr_t) - result.append(a) - - # Directory attributes - if attrs[2] & ATTR_DIR_LINKCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[2] & ATTR_DIR_ENTRYCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[2] & ATTR_DIR_MOUNTSTATUS: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - - # File attributes - if attrs[3] & ATTR_FILE_LINKCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_TOTALSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_ALLOCSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_IOBLOCKSIZE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_CLUMPSIZE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_DEVTYPE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_FILETYPE: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_FORKCOUNT: - a = uint32_t.from_buffer(buf, offset) - offset += sizeof(uint32_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_DATALENGTH: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_DATAALLOCSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_DATAEXTENTS: - a = extentrecord.from_buffer(buf, offset) - offset += sizeof(extentrecord) - result.append(a.value) - if attrs[3] & ATTR_FILE_RSRCLENGTH: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_RSRCALLOCSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[3] & ATTR_FILE_RSRCEXTENTS: - a = extentrecord.from_buffer(buf, offset) - offset += sizeof(extentrecord) - result.append(a.value) - - # Fork attributes - if attrs[4] & ATTR_FORK_TOTALSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - if attrs[4] & ATTR_FORK_ALLOCSIZE: - a = off_t.from_buffer(buf, offset) - offset += sizeof(off_t) - result.append(a.value) - - return result - -# Sadly, ctypes.get_errno() seems not to work -__error = libc.__error -__error.restype = POINTER(c_int) - -def _get_errno(): - return __error().contents.value - -def getattrlist(path, attrs, options): - attrs = list(attrs) - if attrs[1]: - attrs[1] |= ATTR_VOL_INFO - alist = attrlist(bitmapcount=5, - commonattr=attrs[0], - volattr=attrs[1], - dirattr=attrs[2], - fileattr=attrs[3], - forkattr=attrs[4]) - - bufsize = _attrbuf_size(attrs) - buf = create_string_buffer(bufsize) - - ret = _getattrlist(path, byref(alist), buf, bufsize, - options | FSOPT_REPORT_FULLSIZE) - - if ret < 0: - err = _get_errno() - raise OSError(err, os.strerror(err), path) - - return _decode_attrlist_result(buf, attrs, options) - -def fgetattrlist(fd, attrs, options): - if hasattr(fd, 'fileno'): - fd = fd.fileno() - attrs = list(attrs) - if attrs[1]: - attrs[1] |= ATTR_VOL_INFO - alist = attrlist(bitmapcount=5, - commonattr=attrs[0], - volattr=attrs[1], - dirattr=attrs[2], - fileattr=attrs[3], - forkattr=attrs[4]) - - bufsize = _attrbuf_size(attrs) - buf = create_string_buffer(bufsize) - - ret = _fgetattrlist(fd, byref(alist), buf, bufsize, - options | FSOPT_REPORT_FULLSIZE) - - if ret < 0: - err = _get_errno() - raise OSError(err, os.strerror(err)) - - return _decode_attrlist_result(buf, attrs, options) - -def statfs(path): - result = struct_statfs() - ret = _statfs(path, byref(result)) - if ret < 0: - err = _get_errno() - raise OSError(err, os.strerror(err), path) - return result - -def fstatfs(fd): - if hasattr(fd, 'fileno'): - fd = fd.fileno() - result = struct_statfs() - ret = _fstatfs(fd, byref(result)) - if ret < 0: - err = _get_errno() - raise OSError(err, os.strerror(err)) - return result diff --git a/src/mac-app/tools/createDSStore/mac_alias/utils.py b/src/mac-app/tools/createDSStore/mac_alias/utils.py deleted file mode 100644 index 3b4613a43c9..00000000000 --- a/src/mac-app/tools/createDSStore/mac_alias/utils.py +++ /dev/null @@ -1,17 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import unicode_literals - -import datetime - -ZERO = datetime.timedelta(0) -class UTC (datetime.tzinfo): - def utcoffset(self, dt): - return ZERO - def dst(self, dt): - return ZERO - def tzname(self, dt): - return 'UTC' - -utc = UTC() -mac_epoch = datetime.datetime(1904,1,1,0,0,0,0,utc) -unix_epoch = datetime.datetime(1970,1,1,0,0,0,0,utc) diff --git a/src/mac-app/tools/createDSStore/six.py b/src/mac-app/tools/createDSStore/six.py deleted file mode 100644 index 190c0239cd7..00000000000 --- a/src/mac-app/tools/createDSStore/six.py +++ /dev/null @@ -1,868 +0,0 @@ -"""Utilities for writing code that runs on Python 2 and 3""" - -# Copyright (c) 2010-2015 Benjamin Peterson -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -from __future__ import absolute_import - -import functools -import itertools -import operator -import sys -import types - -__author__ = "Benjamin Peterson " -__version__ = "1.10.0" - - -# Useful for very coarse version differentiation. -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY34 = sys.version_info[0:2] >= (3, 4) - -if PY3: - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - - MAXSIZE = sys.maxsize -else: - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - - if sys.platform.startswith("java"): - # Jython always uses 32 bits. - MAXSIZE = int((1 << 31) - 1) - else: - # It's possible to have sizeof(long) != sizeof(Py_ssize_t). - class X(object): - - def __len__(self): - return 1 << 31 - try: - len(X()) - except OverflowError: - # 32-bit - MAXSIZE = int((1 << 31) - 1) - else: - # 64-bit - MAXSIZE = int((1 << 63) - 1) - del X - - -def _add_doc(func, doc): - """Add documentation to a function.""" - func.__doc__ = doc - - -def _import_module(name): - """Import module, returning the module after the last dot.""" - __import__(name) - return sys.modules[name] - - -class _LazyDescr(object): - - def __init__(self, name): - self.name = name - - def __get__(self, obj, tp): - result = self._resolve() - setattr(obj, self.name, result) # Invokes __set__. - try: - # This is a bit ugly, but it avoids running this again by - # removing this descriptor. - delattr(obj.__class__, self.name) - except AttributeError: - pass - return result - - -class MovedModule(_LazyDescr): - - def __init__(self, name, old, new=None): - super(MovedModule, self).__init__(name) - if PY3: - if new is None: - new = name - self.mod = new - else: - self.mod = old - - def _resolve(self): - return _import_module(self.mod) - - def __getattr__(self, attr): - _module = self._resolve() - value = getattr(_module, attr) - setattr(self, attr, value) - return value - - -class _LazyModule(types.ModuleType): - - def __init__(self, name): - super(_LazyModule, self).__init__(name) - self.__doc__ = self.__class__.__doc__ - - def __dir__(self): - attrs = ["__doc__", "__name__"] - attrs += [attr.name for attr in self._moved_attributes] - return attrs - - # Subclasses should override this - _moved_attributes = [] - - -class MovedAttribute(_LazyDescr): - - def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): - super(MovedAttribute, self).__init__(name) - if PY3: - if new_mod is None: - new_mod = name - self.mod = new_mod - if new_attr is None: - if old_attr is None: - new_attr = name - else: - new_attr = old_attr - self.attr = new_attr - else: - self.mod = old_mod - if old_attr is None: - old_attr = name - self.attr = old_attr - - def _resolve(self): - module = _import_module(self.mod) - return getattr(module, self.attr) - - -class _SixMetaPathImporter(object): - - """ - A meta path importer to import six.moves and its submodules. - - This class implements a PEP302 finder and loader. It should be compatible - with Python 2.5 and all existing versions of Python3 - """ - - def __init__(self, six_module_name): - self.name = six_module_name - self.known_modules = {} - - def _add_module(self, mod, *fullnames): - for fullname in fullnames: - self.known_modules[self.name + "." + fullname] = mod - - def _get_module(self, fullname): - return self.known_modules[self.name + "." + fullname] - - def find_module(self, fullname, path=None): - if fullname in self.known_modules: - return self - return None - - def __get_module(self, fullname): - try: - return self.known_modules[fullname] - except KeyError: - raise ImportError("This loader does not know module " + fullname) - - def load_module(self, fullname): - try: - # in case of a reload - return sys.modules[fullname] - except KeyError: - pass - mod = self.__get_module(fullname) - if isinstance(mod, MovedModule): - mod = mod._resolve() - else: - mod.__loader__ = self - sys.modules[fullname] = mod - return mod - - def is_package(self, fullname): - """ - Return true, if the named module is a package. - - We need this method to get correct spec objects with - Python 3.4 (see PEP451) - """ - return hasattr(self.__get_module(fullname), "__path__") - - def get_code(self, fullname): - """Return None - - Required, if is_package is implemented""" - self.__get_module(fullname) # eventually raises ImportError - return None - get_source = get_code # same as get_code - -_importer = _SixMetaPathImporter(__name__) - - -class _MovedItems(_LazyModule): - - """Lazy loading of moved objects""" - __path__ = [] # mark as package - - -_moved_attributes = [ - MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), - MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), - MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"), - MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), - MovedAttribute("intern", "__builtin__", "sys"), - MovedAttribute("map", "itertools", "builtins", "imap", "map"), - MovedAttribute("getcwd", "os", "os", "getcwdu", "getcwd"), - MovedAttribute("getcwdb", "os", "os", "getcwd", "getcwdb"), - MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("reload_module", "__builtin__", "importlib" if PY34 else "imp", "reload"), - MovedAttribute("reduce", "__builtin__", "functools"), - MovedAttribute("shlex_quote", "pipes", "shlex", "quote"), - MovedAttribute("StringIO", "StringIO", "io"), - MovedAttribute("UserDict", "UserDict", "collections"), - MovedAttribute("UserList", "UserList", "collections"), - MovedAttribute("UserString", "UserString", "collections"), - MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), - MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), - MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"), - MovedModule("builtins", "__builtin__"), - MovedModule("configparser", "ConfigParser"), - MovedModule("copyreg", "copy_reg"), - MovedModule("dbm_gnu", "gdbm", "dbm.gnu"), - MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"), - MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), - MovedModule("http_cookies", "Cookie", "http.cookies"), - MovedModule("html_entities", "htmlentitydefs", "html.entities"), - MovedModule("html_parser", "HTMLParser", "html.parser"), - MovedModule("http_client", "httplib", "http.client"), - MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"), - MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"), - MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"), - MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"), - MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), - MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), - MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), - MovedModule("cPickle", "cPickle", "pickle"), - MovedModule("queue", "Queue"), - MovedModule("reprlib", "repr"), - MovedModule("socketserver", "SocketServer"), - MovedModule("_thread", "thread", "_thread"), - MovedModule("tkinter", "Tkinter"), - MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), - MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), - MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), - MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), - MovedModule("tkinter_tix", "Tix", "tkinter.tix"), - MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"), - MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), - MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), - MovedModule("tkinter_colorchooser", "tkColorChooser", - "tkinter.colorchooser"), - MovedModule("tkinter_commondialog", "tkCommonDialog", - "tkinter.commondialog"), - MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), - MovedModule("tkinter_font", "tkFont", "tkinter.font"), - MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), - MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", - "tkinter.simpledialog"), - MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"), - MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"), - MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"), - MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), - MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"), - MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"), -] -# Add windows specific modules. -if sys.platform == "win32": - _moved_attributes += [ - MovedModule("winreg", "_winreg"), - ] - -for attr in _moved_attributes: - setattr(_MovedItems, attr.name, attr) - if isinstance(attr, MovedModule): - _importer._add_module(attr, "moves." + attr.name) -del attr - -_MovedItems._moved_attributes = _moved_attributes - -moves = _MovedItems(__name__ + ".moves") -_importer._add_module(moves, "moves") - - -class Module_six_moves_urllib_parse(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_parse""" - - -_urllib_parse_moved_attributes = [ - MovedAttribute("ParseResult", "urlparse", "urllib.parse"), - MovedAttribute("SplitResult", "urlparse", "urllib.parse"), - MovedAttribute("parse_qs", "urlparse", "urllib.parse"), - MovedAttribute("parse_qsl", "urlparse", "urllib.parse"), - MovedAttribute("urldefrag", "urlparse", "urllib.parse"), - MovedAttribute("urljoin", "urlparse", "urllib.parse"), - MovedAttribute("urlparse", "urlparse", "urllib.parse"), - MovedAttribute("urlsplit", "urlparse", "urllib.parse"), - MovedAttribute("urlunparse", "urlparse", "urllib.parse"), - MovedAttribute("urlunsplit", "urlparse", "urllib.parse"), - MovedAttribute("quote", "urllib", "urllib.parse"), - MovedAttribute("quote_plus", "urllib", "urllib.parse"), - MovedAttribute("unquote", "urllib", "urllib.parse"), - MovedAttribute("unquote_plus", "urllib", "urllib.parse"), - MovedAttribute("urlencode", "urllib", "urllib.parse"), - MovedAttribute("splitquery", "urllib", "urllib.parse"), - MovedAttribute("splittag", "urllib", "urllib.parse"), - MovedAttribute("splituser", "urllib", "urllib.parse"), - MovedAttribute("uses_fragment", "urlparse", "urllib.parse"), - MovedAttribute("uses_netloc", "urlparse", "urllib.parse"), - MovedAttribute("uses_params", "urlparse", "urllib.parse"), - MovedAttribute("uses_query", "urlparse", "urllib.parse"), - MovedAttribute("uses_relative", "urlparse", "urllib.parse"), -] -for attr in _urllib_parse_moved_attributes: - setattr(Module_six_moves_urllib_parse, attr.name, attr) -del attr - -Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes - -_importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"), - "moves.urllib_parse", "moves.urllib.parse") - - -class Module_six_moves_urllib_error(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_error""" - - -_urllib_error_moved_attributes = [ - MovedAttribute("URLError", "urllib2", "urllib.error"), - MovedAttribute("HTTPError", "urllib2", "urllib.error"), - MovedAttribute("ContentTooShortError", "urllib", "urllib.error"), -] -for attr in _urllib_error_moved_attributes: - setattr(Module_six_moves_urllib_error, attr.name, attr) -del attr - -Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes - -_importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"), - "moves.urllib_error", "moves.urllib.error") - - -class Module_six_moves_urllib_request(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_request""" - - -_urllib_request_moved_attributes = [ - MovedAttribute("urlopen", "urllib2", "urllib.request"), - MovedAttribute("install_opener", "urllib2", "urllib.request"), - MovedAttribute("build_opener", "urllib2", "urllib.request"), - MovedAttribute("pathname2url", "urllib", "urllib.request"), - MovedAttribute("url2pathname", "urllib", "urllib.request"), - MovedAttribute("getproxies", "urllib", "urllib.request"), - MovedAttribute("Request", "urllib2", "urllib.request"), - MovedAttribute("OpenerDirector", "urllib2", "urllib.request"), - MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"), - MovedAttribute("ProxyHandler", "urllib2", "urllib.request"), - MovedAttribute("BaseHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"), - MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"), - MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"), - MovedAttribute("FileHandler", "urllib2", "urllib.request"), - MovedAttribute("FTPHandler", "urllib2", "urllib.request"), - MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"), - MovedAttribute("UnknownHandler", "urllib2", "urllib.request"), - MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"), - MovedAttribute("urlretrieve", "urllib", "urllib.request"), - MovedAttribute("urlcleanup", "urllib", "urllib.request"), - MovedAttribute("URLopener", "urllib", "urllib.request"), - MovedAttribute("FancyURLopener", "urllib", "urllib.request"), - MovedAttribute("proxy_bypass", "urllib", "urllib.request"), -] -for attr in _urllib_request_moved_attributes: - setattr(Module_six_moves_urllib_request, attr.name, attr) -del attr - -Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes - -_importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"), - "moves.urllib_request", "moves.urllib.request") - - -class Module_six_moves_urllib_response(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_response""" - - -_urllib_response_moved_attributes = [ - MovedAttribute("addbase", "urllib", "urllib.response"), - MovedAttribute("addclosehook", "urllib", "urllib.response"), - MovedAttribute("addinfo", "urllib", "urllib.response"), - MovedAttribute("addinfourl", "urllib", "urllib.response"), -] -for attr in _urllib_response_moved_attributes: - setattr(Module_six_moves_urllib_response, attr.name, attr) -del attr - -Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes - -_importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"), - "moves.urllib_response", "moves.urllib.response") - - -class Module_six_moves_urllib_robotparser(_LazyModule): - - """Lazy loading of moved objects in six.moves.urllib_robotparser""" - - -_urllib_robotparser_moved_attributes = [ - MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"), -] -for attr in _urllib_robotparser_moved_attributes: - setattr(Module_six_moves_urllib_robotparser, attr.name, attr) -del attr - -Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes - -_importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"), - "moves.urllib_robotparser", "moves.urllib.robotparser") - - -class Module_six_moves_urllib(types.ModuleType): - - """Create a six.moves.urllib namespace that resembles the Python 3 namespace""" - __path__ = [] # mark as package - parse = _importer._get_module("moves.urllib_parse") - error = _importer._get_module("moves.urllib_error") - request = _importer._get_module("moves.urllib_request") - response = _importer._get_module("moves.urllib_response") - robotparser = _importer._get_module("moves.urllib_robotparser") - - def __dir__(self): - return ['parse', 'error', 'request', 'response', 'robotparser'] - -_importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"), - "moves.urllib") - - -def add_move(move): - """Add an item to six.moves.""" - setattr(_MovedItems, move.name, move) - - -def remove_move(name): - """Remove item from six.moves.""" - try: - delattr(_MovedItems, name) - except AttributeError: - try: - del moves.__dict__[name] - except KeyError: - raise AttributeError("no such move, %r" % (name,)) - - -if PY3: - _meth_func = "__func__" - _meth_self = "__self__" - - _func_closure = "__closure__" - _func_code = "__code__" - _func_defaults = "__defaults__" - _func_globals = "__globals__" -else: - _meth_func = "im_func" - _meth_self = "im_self" - - _func_closure = "func_closure" - _func_code = "func_code" - _func_defaults = "func_defaults" - _func_globals = "func_globals" - - -try: - advance_iterator = next -except NameError: - def advance_iterator(it): - return it.next() -next = advance_iterator - - -try: - callable = callable -except NameError: - def callable(obj): - return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - - -if PY3: - def get_unbound_function(unbound): - return unbound - - create_bound_method = types.MethodType - - def create_unbound_method(func, cls): - return func - - Iterator = object -else: - def get_unbound_function(unbound): - return unbound.im_func - - def create_bound_method(func, obj): - return types.MethodType(func, obj, obj.__class__) - - def create_unbound_method(func, cls): - return types.MethodType(func, None, cls) - - class Iterator(object): - - def next(self): - return type(self).__next__(self) - - callable = callable -_add_doc(get_unbound_function, - """Get the function out of a possibly unbound function""") - - -get_method_function = operator.attrgetter(_meth_func) -get_method_self = operator.attrgetter(_meth_self) -get_function_closure = operator.attrgetter(_func_closure) -get_function_code = operator.attrgetter(_func_code) -get_function_defaults = operator.attrgetter(_func_defaults) -get_function_globals = operator.attrgetter(_func_globals) - - -if PY3: - def iterkeys(d, **kw): - return iter(d.keys(**kw)) - - def itervalues(d, **kw): - return iter(d.values(**kw)) - - def iteritems(d, **kw): - return iter(d.items(**kw)) - - def iterlists(d, **kw): - return iter(d.lists(**kw)) - - viewkeys = operator.methodcaller("keys") - - viewvalues = operator.methodcaller("values") - - viewitems = operator.methodcaller("items") -else: - def iterkeys(d, **kw): - return d.iterkeys(**kw) - - def itervalues(d, **kw): - return d.itervalues(**kw) - - def iteritems(d, **kw): - return d.iteritems(**kw) - - def iterlists(d, **kw): - return d.iterlists(**kw) - - viewkeys = operator.methodcaller("viewkeys") - - viewvalues = operator.methodcaller("viewvalues") - - viewitems = operator.methodcaller("viewitems") - -_add_doc(iterkeys, "Return an iterator over the keys of a dictionary.") -_add_doc(itervalues, "Return an iterator over the values of a dictionary.") -_add_doc(iteritems, - "Return an iterator over the (key, value) pairs of a dictionary.") -_add_doc(iterlists, - "Return an iterator over the (key, [values]) pairs of a dictionary.") - - -if PY3: - def b(s): - return s.encode("latin-1") - - def u(s): - return s - unichr = chr - import struct - int2byte = struct.Struct(">B").pack - del struct - byte2int = operator.itemgetter(0) - indexbytes = operator.getitem - iterbytes = iter - import io - StringIO = io.StringIO - BytesIO = io.BytesIO - _assertCountEqual = "assertCountEqual" - if sys.version_info[1] <= 1: - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" - else: - _assertRaisesRegex = "assertRaisesRegex" - _assertRegex = "assertRegex" -else: - def b(s): - return s - # Workaround for standalone backslash - - def u(s): - return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape") - unichr = unichr - int2byte = chr - - def byte2int(bs): - return ord(bs[0]) - - def indexbytes(buf, i): - return ord(buf[i]) - iterbytes = functools.partial(itertools.imap, ord) - import StringIO - StringIO = BytesIO = StringIO.StringIO - _assertCountEqual = "assertItemsEqual" - _assertRaisesRegex = "assertRaisesRegexp" - _assertRegex = "assertRegexpMatches" -_add_doc(b, """Byte literal""") -_add_doc(u, """Text literal""") - - -def assertCountEqual(self, *args, **kwargs): - return getattr(self, _assertCountEqual)(*args, **kwargs) - - -def assertRaisesRegex(self, *args, **kwargs): - return getattr(self, _assertRaisesRegex)(*args, **kwargs) - - -def assertRegex(self, *args, **kwargs): - return getattr(self, _assertRegex)(*args, **kwargs) - - -if PY3: - exec_ = getattr(moves.builtins, "exec") - - def reraise(tp, value, tb=None): - if value is None: - value = tp() - if value.__traceback__ is not tb: - raise value.with_traceback(tb) - raise value - -else: - def exec_(_code_, _globs_=None, _locs_=None): - """Execute code in a namespace.""" - if _globs_ is None: - frame = sys._getframe(1) - _globs_ = frame.f_globals - if _locs_ is None: - _locs_ = frame.f_locals - del frame - elif _locs_ is None: - _locs_ = _globs_ - exec("""exec _code_ in _globs_, _locs_""") - - exec_("""def reraise(tp, value, tb=None): - raise tp, value, tb -""") - - -if sys.version_info[:2] == (3, 2): - exec_("""def raise_from(value, from_value): - if from_value is None: - raise value - raise value from from_value -""") -elif sys.version_info[:2] > (3, 2): - exec_("""def raise_from(value, from_value): - raise value from from_value -""") -else: - def raise_from(value, from_value): - raise value - - -print_ = getattr(moves.builtins, "print", None) -if print_ is None: - def print_(*args, **kwargs): - """The new-style print function for Python 2.4 and 2.5.""" - fp = kwargs.pop("file", sys.stdout) - if fp is None: - return - - def write(data): - if not isinstance(data, basestring): - data = str(data) - # If the file has an encoding, encode unicode with it. - if (isinstance(fp, file) and - isinstance(data, unicode) and - fp.encoding is not None): - errors = getattr(fp, "errors", None) - if errors is None: - errors = "strict" - data = data.encode(fp.encoding, errors) - fp.write(data) - want_unicode = False - sep = kwargs.pop("sep", None) - if sep is not None: - if isinstance(sep, unicode): - want_unicode = True - elif not isinstance(sep, str): - raise TypeError("sep must be None or a string") - end = kwargs.pop("end", None) - if end is not None: - if isinstance(end, unicode): - want_unicode = True - elif not isinstance(end, str): - raise TypeError("end must be None or a string") - if kwargs: - raise TypeError("invalid keyword arguments to print()") - if not want_unicode: - for arg in args: - if isinstance(arg, unicode): - want_unicode = True - break - if want_unicode: - newline = unicode("\n") - space = unicode(" ") - else: - newline = "\n" - space = " " - if sep is None: - sep = space - if end is None: - end = newline - for i, arg in enumerate(args): - if i: - write(sep) - write(arg) - write(end) -if sys.version_info[:2] < (3, 3): - _print = print_ - - def print_(*args, **kwargs): - fp = kwargs.get("file", sys.stdout) - flush = kwargs.pop("flush", False) - _print(*args, **kwargs) - if flush and fp is not None: - fp.flush() - -_add_doc(reraise, """Reraise an exception.""") - -if sys.version_info[0:2] < (3, 4): - def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, - updated=functools.WRAPPER_UPDATES): - def wrapper(f): - f = functools.wraps(wrapped, assigned, updated)(f) - f.__wrapped__ = wrapped - return f - return wrapper -else: - wraps = functools.wraps - - -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(meta): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - return type.__new__(metaclass, 'temporary_class', (), {}) - - -def add_metaclass(metaclass): - """Class decorator for creating a class with a metaclass.""" - def wrapper(cls): - orig_vars = cls.__dict__.copy() - slots = orig_vars.get('__slots__') - if slots is not None: - if isinstance(slots, str): - slots = [slots] - for slots_var in slots: - orig_vars.pop(slots_var) - orig_vars.pop('__dict__', None) - orig_vars.pop('__weakref__', None) - return metaclass(cls.__name__, cls.__bases__, orig_vars) - return wrapper - - -def python_2_unicode_compatible(klass): - """ - A decorator that defines __unicode__ and __str__ methods under Python 2. - Under Python 3 it does nothing. - - To support Python 2 and 3 with a single code base, define a __str__ method - returning text and apply this decorator to the class. - """ - if PY2: - if '__str__' not in klass.__dict__: - raise ValueError("@python_2_unicode_compatible cannot be applied " - "to %s because it doesn't define __str__()." % - klass.__name__) - klass.__unicode__ = klass.__str__ - klass.__str__ = lambda self: self.__unicode__().encode('utf-8') - return klass - - -# Complete the moves implementation. -# This code is at the end of this module to speed up module loading. -# Turn this module into a package. -__path__ = [] # required for PEP 302 and PEP 451 -__package__ = __name__ # see PEP 366 @ReservedAssignment -if globals().get("__spec__") is not None: - __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable -# Remove other six meta path importers, since they cause problems. This can -# happen if six is removed from sys.modules and then reloaded. (Setuptools does -# this for some reason.) -if sys.meta_path: - for i, importer in enumerate(sys.meta_path): - # Here's some real nastiness: Another "instance" of the six module might - # be floating around. Therefore, we can't use isinstance() to check for - # the six meta path importer, since the other six instance will have - # inserted an importer with different class. - if (type(importer).__name__ == "_SixMetaPathImporter" and - importer.name == __name__): - del sys.meta_path[i] - break - del i, importer -# Finally, add the importer to the meta path import hook. -sys.meta_path.append(_importer) diff --git a/src/module_list.py b/src/module_list.py index 4355076ff72..7b5f2fe7064 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1034,6 +1034,9 @@ def uname_specific(name, value, alternative): Extension('sage.modules.vector_real_double_dense', ['sage/modules/vector_real_double_dense.pyx']), + Extension('sage.modules.with_basis.indexed_element', + sources = ['sage/modules/with_basis/indexed_element.pyx']), + ################################ ## ## sage.numerical diff --git a/src/sage/algebras/cluster_algebra.py b/src/sage/algebras/cluster_algebra.py index 1deae5fd5c5..02f61a8554b 100644 --- a/src/sage/algebras/cluster_algebra.py +++ b/src/sage/algebras/cluster_algebra.py @@ -1261,7 +1261,7 @@ def __init__(self, Q, **kwargs): # the result to polynomials but then we get "rational" coefficients self._U = PolynomialRing(QQ, ['u%s' % i for i in range(self._n)]) - # Setup infrastruture to store computed data + # Setup infrastructure to store computed data self.clear_computed_data() # Determine the names of the initial cluster variables diff --git a/src/sage/algebras/free_algebra.py b/src/sage/algebras/free_algebra.py index e294fa94b22..de58a4e65c9 100644 --- a/src/sage/algebras/free_algebra.py +++ b/src/sage/algebras/free_algebra.py @@ -147,7 +147,7 @@ from sage.rings.ring import Algebra from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular from sage.categories.algebras_with_basis import AlgebrasWithBasis -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.combinat.words.word import Word from sage.structure.category_object import normalize_names @@ -615,7 +615,7 @@ def exp_to_monomial(T): if T[i]: out.append((i%ngens,T[i])) return M(out) - return self.element_class(self, dict([(exp_to_monomial(T),c) for T,c in six.iteritems(x.letterplace_polynomial().dict())])) + return self.element_class(self, {exp_to_monomial(T):c for T,c in six.iteritems(x.letterplace_polynomial().dict())}) # ok, not a free algebra element (or should not be viewed as one). if isinstance(x, six.string_types): from sage.all import sage_eval @@ -1338,7 +1338,7 @@ def expansion(self, t): return sum([i[1] * self._alg.lie_polynomial(i[0]) for i in list(t)], self._alg.zero()) - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): def expand(self): """ Expand ``self`` in the monomials of the free algebra. diff --git a/src/sage/algebras/free_algebra_element.py b/src/sage/algebras/free_algebra_element.py index be582c054c3..b719b53b733 100644 --- a/src/sage/algebras/free_algebra_element.py +++ b/src/sage/algebras/free_algebra_element.py @@ -37,7 +37,8 @@ from sage.misc.misc import repr_lincomb from sage.monoids.free_monoid_element import FreeMonoidElement -from sage.combinat.free_module import CombinatorialFreeModuleElement +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.element import AlgebraElement from sage.structure.sage_object import richcmp @@ -45,8 +46,7 @@ import six -# We need to have AlgebraElement first to avoid a segfault... -class FreeAlgebraElement(AlgebraElement, CombinatorialFreeModuleElement): +class FreeAlgebraElement(IndexedFreeModuleElement, AlgebraElement): """ A free algebra element. """ @@ -61,24 +61,20 @@ def __init__(self, A, x): sage: TestSuite(elt).run() """ if isinstance(x, FreeAlgebraElement): + # We should have an input for when we know we don't need to + # convert the keys/values x = x._monomial_coefficients R = A.base_ring() if isinstance(x, AlgebraElement): #and x.parent() == A.base_ring(): - x = { A.monoid()(1):R(x) } + x = {A.monoid()(1): R(x)} elif isinstance(x, FreeMonoidElement): - x = { x:R(1) } + x = {x: R(1)} elif True: - x = dict([ (A.monoid()(e1),R(e2)) for e1,e2 in x.items()]) + x = {A.monoid()(e1): R(e2) for e1,e2 in x.items()} else: raise TypeError("Argument x (= {}) is of the wrong type.".format(x)) - CombinatorialFreeModuleElement.__init__(self, A, x) - - # ...however AlgebraElement has a default error raising version of these - # so we must explicitly pull them from CombinatorialFreeModuleElement - _add_ = CombinatorialFreeModuleElement._add_ - _sub_ = CombinatorialFreeModuleElement._sub_ - _neg_ = CombinatorialFreeModuleElement._neg_ + IndexedFreeModuleElement.__init__(self, A, x) def _repr_(self): """ @@ -86,7 +82,7 @@ def _repr_(self): EXAMPLES:: - sage: A.=FreeAlgebra(ZZ,3) + sage: A. = FreeAlgebra(ZZ,3) sage: repr(-x+3*y*z) # indirect doctest '-x + 3*y*z' @@ -143,10 +139,10 @@ def __call__(self, *x, **kwds): - Joel B. Mohler (2007-10-27) """ - if len(kwds)>0 and len(x)>0: + if kwds and x: raise ValueError("must not specify both a keyword and positional argument") - if len(kwds)>0: + if kwds: p = self.parent() def extract_from(kwds,g): for x in g: @@ -157,14 +153,14 @@ def extract_from(kwds,g): return None x = [extract_from(kwds,(p.gen(i),p.variable_name(i))) for i in range(p.ngens())] - elif isinstance(x[0],tuple): + elif isinstance(x[0], tuple): x = x[0] if len(x) != self.parent().ngens(): raise ValueError("must specify as many values as generators in parent") # I don't start with 0, because I don't want to preclude evaluation with - #arbitrary objects (e.g. matrices) because of funny coercion. + # arbitrary objects (e.g. matrices) because of funny coercion. result = None for m, c in six.iteritems(self._monomial_coefficients): if result is None: diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index 05bd7da97f1..fa70cb30520 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -33,7 +33,7 @@ from sage.arith.all import is_square from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.family import Family -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule def normalized_laurent_polynomial(R, p): r""" @@ -1657,7 +1657,7 @@ def goldman_involution_on_basis(self, w): H = self.realization_of() return (-H._q_prod)**w.length() * self.monomial(w.inverse()).inverse() - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): r""" A class for elements of an Iwahori-Hecke algebra in the `T` basis. diff --git a/src/sage/algebras/lie_algebras/affine_lie_algebra.py b/src/sage/algebras/lie_algebras/affine_lie_algebra.py new file mode 100644 index 00000000000..727eabc4193 --- /dev/null +++ b/src/sage/algebras/lie_algebras/affine_lie_algebra.py @@ -0,0 +1,500 @@ +""" +Affine Lie Algebras + +AUTHORS: + +- Travis Scrimshaw (2013-05-03): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2013-2017 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.misc import repr_lincomb +from sage.structure.element import RingElement, parent +from sage.categories.lie_algebras import LieAlgebras + +from sage.algebras.lie_algebras.lie_algebra import LieAlgebra, FinitelyGeneratedLieAlgebra +from sage.algebras.lie_algebras.lie_algebra_element import UntwistedAffineLieAlgebraElement +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.cartan_matrix import CartanMatrix +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.categories.cartesian_product import cartesian_product +from sage.rings.integer_ring import ZZ +from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets +from sage.sets.family import Family + +class AffineLieAlgebra(FinitelyGeneratedLieAlgebra): + r""" + An (untwisted) affine Lie algebra. + + Let `R` be a ring. Given a finite-dimensional simple Lie algebra + `\mathfrak{g}` over `R`, the affine Lie algebra + `\widehat{\mathfrak{g}}^{\prime}` associated to `\mathfrak{g}` is + defined as + + .. MATH:: + + \widehat{\mathfrak{g}}' = \bigl( \mathfrak{g} \otimes + R[t, t^{-1}] \bigr) \oplus R c, + + where `c` is the canonical central element and `R[t, t^{-1}]` is the + Laurent polynomial ring over `R`. The Lie bracket is defined as + + .. MATH:: + + [x \otimes t^m + \lambda c, y \otimes t^n + \mu c] = + [x, y] \otimes t^{m+n} + m \delta_{m,-n} ( x | y ) c, + + where `( x | y )` is the Killing form on `\mathfrak{g}`. + + There is a canonical derivation `d` on `\widehat{\mathfrak{g}}'` + that is defined by + + .. MATH:: + + d(x \otimes t^m + \lambda c) = a \otimes m t^m, + + or equivalently by `d = t \frac{d}{dt}`. + + The affine Kac-Moody algebra `\widehat{\mathfrak{g}}` is formed by + adjoining the derivation `d` such that + + .. MATH:: + + \widehat{\mathfrak{g}} = \bigl( \mathfrak{g} \otimes R[t,t^{-1}] + \bigr) \oplus R c \oplus R d. + + Specifically, the bracket on `\widehat{\mathfrak{g}}` is defined as + + .. MATH:: + + [t^m \otimes x \oplus \lambda c \oplus \mu d, t^n \otimes y \oplus + \lambda_1 c \oplus \mu_1 d] = \bigl( t^{m+n} [x,y] + \mu n t^n \otimes + y - \mu_1 m t^m \otimes x\bigr) \oplus m \delta_{m,-n} (x|y) c . + + Note that the derived subalgebra of the Kac-Moody algebra is the + affine Lie algebra. + + INPUT: + + Can be one of the following: + + - a base ring and an affine Cartan type: constructs the affine + (Kac-Moody) Lie algebra of the classical Lie algebra in the + bracket representation over the base ring + + - a classical Lie algebra: constructs the corresponding affine + (Kac-Moody) Lie algebra + + There is the optional argument ``kac_moody``, which can be set + to ``False`` to obtain the affine Lie algebra instead of the affine + Kac-Moody algebra. + + EXAMPLES: + + We begin by constructing an affine Kac-Moody algebra of type `G_2^{(1)}` + from the classical Lie algebra of type `G_2`:: + + sage: g = LieAlgebra(QQ, cartan_type=['G',2]) + sage: A = g.affine() + sage: A + Affine Kac-Moody algebra of ['G', 2] in the Chevalley basis + + Next, we construct the generators and perform some computations:: + + sage: A.inject_variables() + Defining e1, e2, f1, f2, h1, h2, e0, f0, c, d + sage: e1.bracket(f1) + (h1)#t^0 + sage: e0.bracket(f0) + (-h1 - 2*h2)#t^0 + 8*c + sage: e0.bracket(f1) + 0 + sage: A[d, f0] + (-E[3*alpha[1] + 2*alpha[2]])#t^-1 + sage: A([[e0, e2], [[[e1, e2], [e0, [e1, e2]]], e1]]) + (-6*E[-3*alpha[1] - alpha[2]])#t^2 + sage: f0.bracket(f1) + 0 + sage: f0.bracket(f2) + (E[3*alpha[1] + alpha[2]])#t^-1 + sage: A[h1+3*h2, A[[[f0, f2], f1], [f1,f2]] + f1] + (E[-alpha[1]])#t^0 + (2*E[alpha[1]])#t^-1 + + We can construct its derived subalgebra, the affine Lie algebra + of type `G_2^{(1)}`. In this case, there is no canonical derivation, + so the generator `d` is `0`:: + + sage: D = A.derived_subalgebra() + sage: D.d() + 0 + + REFERENCES: + + - [Ka1990]_ + """ + @staticmethod + def __classcall_private__(cls, arg0, cartan_type=None, kac_moody=True): + """ + Parse input to ensure a unique representation. + + INPUT: + + - ``arg0`` -- a simple Lie algebra or a base ring + - ``cartan_type`` -- a Cartan type + + EXAMPLES:: + + sage: L1 = lie_algebras.Affine(QQ, ['A',4,1]) + sage: cl = lie_algebras.sl(QQ, 5) + sage: L2 = lie_algebras.Affine(cl) + sage: L1 is L2 + True + sage: cl.affine() is L1 + True + """ + if isinstance(arg0, LieAlgebra): + ct = arg0.cartan_type() + if not ct.is_finite(): + raise ValueError("the base Lie algebra is not simple") + cartan_type = ct.affine() + g = arg0 + else: + # arg0 is the base ring + cartan_type = CartanType(cartan_type) + if not cartan_type.is_affine(): + raise ValueError("the Cartan type must be affine") + g = LieAlgebra(arg0, cartan_type=cartan_type.classical()) + + if not cartan_type.is_untwisted_affine(): + raise NotImplementedError("only currently implemented for untwisted affine types") + return super(AffineLieAlgebra, cls).__classcall__(cls, g, kac_moody) + + def __init__(self, g, kac_moody): + """ + Initalize ``self``. + + EXAMPLES:: + + sage: asl = lie_algebras.Affine(QQ, ['A',4,1]) + sage: TestSuite(asl).run() + """ + self._g = g + self._cartan_type = g.cartan_type().affine() + R = g.base_ring() + names = list(g.variable_names()) + ['e0', 'f0', 'c'] + + if kac_moody: + names += ['d'] + self._kac_moody = kac_moody + + names = tuple(names) + self._ordered_indices = names + cat = LieAlgebras(R).WithBasis() + FinitelyGeneratedLieAlgebra.__init__(self, R, names, names, category=cat) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['D',4,1]) + sage: g + Affine Kac-Moody algebra of ['D', 4] in the Chevalley basis + sage: g.derived_subalgebra() + Affine Lie algebra of ['D', 4] in the Chevalley basis + """ + base = "Affine " + rep = repr(self._g) + if self._kac_moody: + old_len = len(rep) + rep = rep.replace("Lie", "Kac-Moody") + if len(rep) == old_len: # We did not replace anything + base += "Kac-Moody " + return base + rep + + @cached_method + def basis(self): + r""" + Return the basis of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['D',4,1]) + sage: B = g.basis() + sage: al = RootSystem(['D',4]).root_lattice().simple_roots() + sage: B[al[1]+al[2]+al[4],4] + (E[alpha[1] + alpha[2] + alpha[4]])#t^4 + sage: B[-al[1]-2*al[2]-al[3]-al[4],2] + (E[-alpha[1] - 2*alpha[2] - alpha[3] - alpha[4]])#t^2 + sage: B[al[4],-2] + (E[alpha[4]])#t^-2 + sage: B['c'] + c + sage: B['d'] + d + """ + K = cartesian_product([self._g.basis().keys(), ZZ]) + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + c = FiniteEnumeratedSet(['c']) + if self._kac_moody: + d = FiniteEnumeratedSet(['d']) + keys = DisjointUnionEnumeratedSets([c, d, K]) + else: + keys = DisjointUnionEnumeratedSets([c, K]) + return Family(keys, self.monomial) + + def _element_constructor_(self, x): + r""" + Construct an element of ``self`` from ``x``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['A',1]) + sage: A = g.affine() + sage: D = A.derived_subalgebra() + sage: A(D.an_element()) + (E[alpha[1]] + h1 + E[-alpha[1]])#t^0 + + (E[-alpha[1]])#t^1 + (E[alpha[1]])#t^-1 + c + sage: A(g.an_element()) + (E[alpha[1]] + h1 + E[-alpha[1]])#t^0 + """ + P = parent(x) + if P is self.derived_subalgebra(): + return self.element_class(self, x.t_dict(), x.c_coefficient(), + x.d_coefficient()) + if P == self._g: + zero = self.base_ring().zero() + return self.element_class(self, {0: x}, zero, zero) + return super(AffineLieAlgebra, self)._element_constructor_(x) + + def _coerce_map_from_(self, R): + """ + Return the coerce map from ``R`` to ``self`` or ``True`` if + a coerce map exists. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['G',2]) + sage: A = g.affine() + sage: A.has_coerce_map_from(g) + True + sage: D = A.derived_subalgebra() + sage: A.has_coerce_map_from(D) + True + """ + if R is self.derived_subalgebra() or R is self._g: + return True + return super(AffineLieAlgebra, self)._coerce_map_from_(R) + + def derived_subalgebra(self): + """ + Return the derived subalgebra of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['B',3,1]) + sage: g + Affine Kac-Moody algebra of ['B', 3] in the Chevalley basis + sage: D = g.derived_subalgebra(); D + Affine Lie algebra of ['B', 3] in the Chevalley basis + sage: D.derived_subalgebra() == D + True + """ + if self._kac_moody: + return AffineLieAlgebra(self._g, kac_moody=False) + return self + + def derived_series(self): + """ + Return the derived series of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['B',3,1]) + sage: g.derived_series() + [Affine Kac-Moody algebra of ['B', 3] in the Chevalley basis, + Affine Lie algebra of ['B', 3] in the Chevalley basis] + sage: g.lower_central_series() + [Affine Kac-Moody algebra of ['B', 3] in the Chevalley basis, + Affine Lie algebra of ['B', 3] in the Chevalley basis] + + sage: D = g.derived_subalgebra() + sage: D.derived_series() + [Affine Lie algebra of ['B', 3] in the Chevalley basis] + """ + if self._kac_moody: + return [self, self.derived_subalgebra()] + return [self] + + lower_central_series = derived_series + + def is_nilpotent(self): + """ + Return ``False`` as ``self`` is semisimple. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['B',3,1]) + sage: g.is_nilpotent() + False + sage: g.is_solvable() + False + """ + return False + + is_solvable = is_nilpotent + + def cartan_type(self): + """ + Return the Cartan type of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['C',3,1]) + sage: g.cartan_type() + ['C', 3, 1] + """ + return self._cartan_type + + def classical(self): + r""" + Return the classical Lie algebra of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['F',4,1]) + sage: g.classical() + Lie algebra of ['F', 4] in the Chevalley basis + + sage: so5 = lie_algebras.so(QQ, 5, 'matrix') + sage: A = so5.affine() + sage: A.classical() == so5 + True + """ + return self._g + + @cached_method + def zero(self): + r""" + Return the element `0`. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['F',4,1]) + sage: g.zero() + 0 + """ + zero = self.base_ring().zero() + return self.element_class(self, {}, zero, zero) + + @cached_method + def c(self): + r""" + Return the canonical central element `c` of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['A',3,1]) + sage: g.c() + c + """ + R = self.base_ring() + return self.element_class(self, {}, R.one(), R.zero()) + + @cached_method + def d(self): + r""" + Return the canonical derivation `d` of ``self``. + + If ``self`` is the affine Lie algebra, then this returns `0`. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['A',3,1]) + sage: g.d() + d + sage: D = g.derived_subalgebra() + sage: D.d() + 0 + """ + if not self._kac_moody: + return self.zero() + R = self.base_ring() + return self.element_class(self, {}, R.zero(), R.one()) + + @cached_method + def lie_algebra_generators(self): + r""" + Return the Lie algebra generators of ``self``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['A',1,1]) + sage: list(g.lie_algebra_generators()) + [(E[alpha[1]])#t^0, + (E[-alpha[1]])#t^0, + (h1)#t^0, + (E[-alpha[1]])#t^1, + (E[alpha[1]])#t^-1, + c, + d] + """ + zero = self.base_ring().zero() + one = self.base_ring().one() + d = {} + if self._kac_moody: + d['d'] = self.d() + d['c'] = self.c() + try: + finite_gens = dict(self._g.lie_algebra_generators(True)) + except TypeError: + finite_gens = dict(self._g.lie_algebra_generators()) + for k,g in finite_gens.items(): + d[k] = self.element_class(self, {0: g}, zero, zero) + # e_0 = f_{\theta} t + d['e0'] = self.element_class(self, {1: self._g.highest_root_basis_elt(False)}, + zero, zero) + # f_0 = e_{\theta} t^-1 + d['f0'] = self.element_class(self, {-1: self._g.highest_root_basis_elt(True)}, + zero, zero) + return Family(self.variable_names(), d.__getitem__) + + def monomial(self, m): + r""" + Construct the monomial indexed by ``m``. + + EXAMPLES:: + + sage: g = LieAlgebra(QQ, cartan_type=['B',4,1]) + sage: al = RootSystem(['B',4]).root_lattice().simple_roots() + sage: g.monomial((al[1]+al[2]+al[3],4)) + (E[alpha[1] + alpha[2] + alpha[3]])#t^4 + sage: g.monomial((-al[1]-al[2]-2*al[3]-2*al[4],2)) + (E[-alpha[1] - alpha[2] - 2*alpha[3] - 2*alpha[4]])#t^2 + sage: g.monomial((al[4],-2)) + (E[alpha[4]])#t^-2 + sage: g.monomial('c') + c + sage: g.monomial('d') + d + """ + if m == 'c': + return self.c() + if m == 'd': + return self.d() + G = self._g.basis() + zero = self.base_ring().zero() + return self.element_class(self, {m[1]: G[m[0]]}, zero, zero) + + Element = UntwistedAffineLieAlgebraElement + diff --git a/src/sage/algebras/lie_algebras/classical_lie_algebra.py b/src/sage/algebras/lie_algebras/classical_lie_algebra.py index ef99be1e6e8..7ea6bb64d27 100644 --- a/src/sage/algebras/lie_algebras/classical_lie_algebra.py +++ b/src/sage/algebras/lie_algebras/classical_lie_algebra.py @@ -262,9 +262,10 @@ def highest_root_basis_elt(self, pos=True): else: gens = self._f cur = gens[i] - for j in w: + for j in reversed(w): for k in range(-r.scalar(coroots[j])): cur = self.bracket(gens[j], cur) + r = r.reflection(coroots[j], True) return cur @cached_method @@ -307,13 +308,20 @@ def basis(self): expanded = True return Family(basis) - # TODO: Uncomment once #16825 is done - #def affine(self, kac_moody=False): - # """ - # Return the (untwisted) affine (Kac-Moody) Lie algebra of ``self``. - # """ - # from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra - # return AffineLieAlgebra(self, kac_moody) + def affine(self, kac_moody=False): + """ + Return the affine (Kac-Moody) Lie algebra of ``self``. + + EXAMPLES:: + + sage: so5 = lie_algebras.so(QQ, 5, 'matrix') + sage: so5 + Special orthogonal Lie algebra of rank 5 over Rational Field + sage: so5.affine() + Affine Special orthogonal Kac-Moody algebra of rank 5 over Rational Field + """ + from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra + return AffineLieAlgebra(self, kac_moody) class gl(LieAlgebraFromAssociative): r""" @@ -747,7 +755,8 @@ class f4(ExceptionalMatrixLieAlgebra): The matrix Lie algebra `\mathfrak{f}_4`. The simple Lie algebra `\mathfrak{f}_f` of type `F_4`. The matrix - representation is given following [HRT2000]_. + representation is given following [HRT2000]_ but indexed in the + reversed order (i.e., interchange 1 with 4 and 2 with 3). """ def __init__(self, R): """ @@ -783,6 +792,9 @@ def __init__(self, R): f[0][14,12] = 2*one f[1][15,13] = 2*one + # Our Cartan matrix convension is dual to that of [HRT2000]_ + e.reverse() + f.reverse() ExceptionalMatrixLieAlgebra.__init__(self, R, CartanType(['F', 4]), e, f) class g2(ExceptionalMatrixLieAlgebra): @@ -979,6 +991,9 @@ def e_coeff(r, s): names += ['h{}'.format(i) for i in range(1, num_sroots+1)] category = LieAlgebras(R).FiniteDimensional().WithBasis() index_set = p_roots + n_roots + list(alphacheck) + names = tuple(names) + from sage.sets.finite_enumerated_set import FiniteEnumeratedSet + index_set = FiniteEnumeratedSet(index_set) LieAlgebraWithStructureCoefficients.__init__(self, R, s_coeffs, names, index_set, category, prefix='E', bracket='[', sorting_key=self._basis_key) @@ -1141,13 +1156,20 @@ def weyl_group(self): from sage.combinat.root_system.weyl_group import WeylGroup return WeylGroup(self._cartan_type) - # TODO: Uncomment once #16825 is done - #def affine(self, kac_moody=False): - # """ - # Return the (untwisted) affine Lie algebra of ``self``. - # """ - # from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra - # return AffineLieAlgebra(self, kac_moody) + def affine(self, kac_moody=False): + """ + Return the affine Lie algebra of ``self``. + + EXAMPLES:: + + sage: sp6 = lie_algebras.sp(QQ, 6) + sage: sp6 + Lie algebra of ['C', 3] in the Chevalley basis + sage: sp6.affine() + Affine Kac-Moody algebra of ['C', 3] in the Chevalley basis + """ + from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra + return AffineLieAlgebra(self, kac_moody) # Useful in creating the UEA @cached_method diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index 92dbce05411..88a364c0d2a 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -32,6 +32,7 @@ from sage.algebras.lie_algebras.classical_lie_algebra import gl, sl, so, sp from sage.algebras.lie_algebras.virasoro import VirasoroAlgebra # this is used, just not in this file +from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra as Affine def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): r""" diff --git a/src/sage/algebras/lie_algebras/lie_algebra.py b/src/sage/algebras/lie_algebras/lie_algebra.py index 03e1181911c..006e9ad6844 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra.py +++ b/src/sage/algebras/lie_algebras/lie_algebra.py @@ -31,7 +31,6 @@ from sage.categories.morphism import SetMorphism from sage.categories.homset import Hom -from sage.algebras.free_algebra import FreeAlgebra from sage.algebras.lie_algebras.lie_algebra_element import (LieAlgebraElementWrapper, LieAlgebraMatrixWrapper) from sage.rings.all import ZZ @@ -301,6 +300,10 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, if ct is not None: from sage.combinat.root_system.cartan_type import CartanType ct = CartanType(ct) + if ct.is_affine(): + from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra + return AffineLieAlgebra(R, cartan_type=ct, + kac_moody=kwds.get("kac_moody", True)) if not ct.is_finite(): raise NotImplementedError("non-finite types are not implemented yet, see trac #14901 for details") rep = kwds.get("representation", "bracket") @@ -380,6 +383,7 @@ def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, # Construct the free Lie algebra from polynomials in the # free (associative unital) algebra # TODO: Change this to accept an index set once FreeAlgebra accepts one + from sage.algebras.free_algebra import FreeAlgebra F = FreeAlgebra(R, names) if index_set is None: index_set = F.variable_names() diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pxd b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd index 47c46336c51..c9a6484ea63 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pxd +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pxd @@ -1,3 +1,4 @@ +from sage.structure.element cimport Element from sage.structure.element_wrapper cimport ElementWrapper cdef class LieAlgebraElementWrapper(ElementWrapper): @@ -14,3 +15,18 @@ cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): cpdef dict monomial_coefficients(self, bint copy=*) #cpdef lift(self) +cdef class UntwistedAffineLieAlgebraElement(Element): + cdef dict _t_dict + cdef _c_coeff + cdef _d_coeff + cdef long _hash + + cpdef dict t_dict(self) + cpdef c_coefficient(self) + cpdef d_coefficient(self) + + cpdef bracket(self, y) + cpdef _bracket_(self, y) + cpdef canonical_derivation(self) + cpdef monomial_coefficients(self, bint copy=*) + diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index c5b47732616..1162172fd3b 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -18,11 +18,14 @@ AUTHORS: #***************************************************************************** from copy import copy +from cpython.object cimport Py_EQ, Py_NE from sage.misc.misc import repr_lincomb from sage.combinat.free_module import CombinatorialFreeModule -from sage.structure.element cimport have_same_parent, coercion_model +from sage.structure.element cimport have_same_parent, coercion_model, parent from sage.structure.element_wrapper cimport ElementWrapper +from sage.structure.sage_object cimport richcmp +from sage.data_structures.blas_dict cimport axpy, negate, scal # TODO: Inherit from IndexedFreeModuleElement and make cdef once #22632 is merged # TODO: Do we want a dense version? @@ -43,8 +46,8 @@ class LieAlgebraElement(CombinatorialFreeModule.Element): x*y - z """ if self.is_zero() or y.is_zero(): - return self.parent().zero() - if y in self.base_ring(): + return parent(self).zero() + if y in parent(self).base_ring(): return y * self # Otherwise we lift to the UEA return self.lift() * y @@ -281,9 +284,11 @@ cdef class LieAlgebraElementWrapper(ElementWrapper): sage: S(elt) # not tested: needs #16822 (2,3) - (1,3) """ - if self.value == 0 or x == 0: - return self._parent.zero() - if x in self.base_ring(): + if not isinstance(self, LieAlgebraElementWrapper): + x, self = self, x + if not self or not x: + return parent(self).zero() + if x in parent(self).base_ring(): return self._acted_upon_(x, True) # Otherwise we lift to the UEA return self.lift() * x @@ -330,10 +335,10 @@ cdef class LieAlgebraElementWrapper(ElementWrapper): # enough information to detect apriori that this method only # accepts scalars; so it tries on some elements(), and we need # to make sure to report an error. - if hasattr( scalar, 'parent' ) and scalar.parent() != self.base_ring(): + if hasattr( scalar, 'parent' ) and scalar.parent() != self._parent.base_ring(): # Temporary needed by coercion (see Polynomial/FractionField tests). - if self.base_ring().has_coerce_map_from(scalar.parent()): - scalar = self.base_ring()( scalar ) + if self._parent.base_ring().has_coerce_map_from(scalar.parent()): + scalar = self._parent.base_ring()( scalar ) else: return None if self_on_left: @@ -487,7 +492,7 @@ cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): sage: list(elt) [('x', 1), ('y', -3/2)] """ - zero = self.base_ring().zero() + zero = self.parent().base_ring().zero() I = self.parent()._indices cdef int i for i,v in enumerate(self.value): @@ -555,3 +560,476 @@ cdef class StructureCoefficientsElement(LieAlgebraMatrixWrapper): """ return self.value[self._parent._indices.index(i)] +cdef class UntwistedAffineLieAlgebraElement(Element): + """ + An element of an untwisted affine Lie algebra. + """ + def __init__(self, parent, dict t_dict, c_coeff, d_coeff): + """ + Initialize ``self``. + + TESTS:: + + sage: L = lie_algebras.Affine(QQ, ['A',2,1]) + sage: x = L.an_element() + sage: TestSuite(x).run() + """ + Element.__init__(self, parent) + self._t_dict = t_dict + self._c_coeff = c_coeff + self._d_coeff = d_coeff + self._hash = -1 + + def __reduce__(self): + """ + Used in pickling. + + TESTS:: + + sage: L = lie_algebras.Affine(QQ, ['B',3,1]) + sage: x = L.an_element() + sage: loads(dumps(x)) == x + True + """ + return (_build_untwisted_affine_element, + (self.parent(), self._t_dict, self._c_coeff, self._d_coeff)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: list(L.lie_algebra_generators()) + [(E[alpha[1]])#t^0, + (E[-alpha[1]])#t^0, + (h1)#t^0, + (E[-alpha[1]])#t^1, + (E[alpha[1]])#t^-1, + c, + d] + sage: L.an_element() + (E[alpha[1]] + h1 + E[-alpha[1]])#t^0 + + (E[-alpha[1]])#t^1 + (E[alpha[1]])#t^-1 + + c + d + sage: L.zero() + 0 + + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: e1 + 2*f1 - h1 + e0 + 3*c - 2*d + (E[alpha[1]] - h1 + 2*E[-alpha[1]])#t^0 + (-E[-alpha[1]])#t^1 + + 3*c + -2*d + """ + ret = ' + '.join('({})#t^{}'.format(g, t) + for t,g in self._t_dict.iteritems()) + if self._c_coeff != 0: + if ret: + ret += ' + ' + if self._c_coeff != 1: + ret += repr(self._c_coeff) + '*c' + else: + ret += 'c' + + if self._d_coeff != 0: + if ret: + ret += ' + ' + if self._d_coeff != 1: + ret += repr(self._d_coeff) + '*d' + else: + ret += 'd' + + if not ret: + return '0' + return ret + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: [latex(g) for g in L.lie_algebra_generators()] + [(E_{\alpha_{1}}) \otimes t^{0}, + (E_{-\alpha_{1}}) \otimes t^{0}, + (E_{\alpha^\vee_{1}}) \otimes t^{0}, + (E_{-\alpha_{1}}) \otimes t^{1}, + (E_{\alpha_{1}}) \otimes t^{-1}, + c, + d] + sage: latex(L.an_element()) + (E_{\alpha_{1}} + E_{\alpha^\vee_{1}} + E_{-\alpha_{1}}) \otimes t^{0} + + (E_{-\alpha_{1}}) \otimes t^{1} + (E_{\alpha_{1}}) \otimes t^{-1} + + c + d + sage: latex(L.zero()) + 0 + + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: latex(e1 + 2*f1 - h1 + e0 + 3*c - 2*d) + (E_{\alpha_{1}} - E_{\alpha^\vee_{1}} + 2E_{-\alpha_{1}}) \otimes t^{0} + + (-E_{-\alpha_{1}}) \otimes t^{1} + 3 c + -2 d + """ + from sage.misc.latex import latex + ret = ' + '.join('({}) \otimes t^{{{}}}'.format(latex(g), t) + for t,g in self._t_dict.iteritems()) + if self._c_coeff != 0: + if ret: + ret += ' + ' + if self._c_coeff != 1: + ret += latex(self._c_coeff) + ' c' + else: + ret += 'c' + + if self._d_coeff != 0: + if ret: + ret += ' + ' + if self._d_coeff != 1: + ret += latex(self._d_coeff) + ' d' + else: + ret += 'd' + + if not ret: + return '0' + return ret + + cpdef dict t_dict(self): + r""" + Return the ``dict``, whose keys are powers of `t` and values are + elements of the classical Lie algebra, of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: x = L.an_element() + sage: x.t_dict() + {-1: E[alpha[1]], + 0: E[alpha[1]] + h1 + E[-alpha[1]], + 1: E[-alpha[1]]} + """ + return self._t_dict.copy() + + cpdef c_coefficient(self): + r""" + Return the coefficient of `c` of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: x = L.an_element() - 3 * L.c() + sage: x.c_coefficient() + -2 + """ + return self._c_coeff + + cpdef d_coefficient(self): + r""" + Return the coefficient of `d` of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: x = L.an_element() + L.d() + sage: x.d_coefficient() + 2 + """ + return self._d_coeff + + cpdef _richcmp_(self, other, int op): + """ + Return the rich comparison of ``self`` with ``other``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['C',2,1]) + sage: x = L.an_element() + sage: c = L.basis()['c'] + sage: d = L.basis()['d'] + sage: c == d + False + sage: x != c + True + sage: 2*c - d == c + c - d + True + sage: x - c != x - c + False + sage: x - c != x - d + True + """ + if op != Py_EQ and op != Py_NE: + return NotImplemented + cdef UntwistedAffineLieAlgebraElement rt = other + return richcmp((self._t_dict, self._c_coeff, self._d_coeff), + (rt._t_dict, rt._c_coeff, rt._d_coeff), + op) + + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: asl = lie_algebras.Affine(QQ, ['A',4,1]) + sage: x = asl.an_element() + sage: hash(x) == hash(x) + True + sage: hash(asl.zero()) + 0 + """ + if not self: + self._hash = 0 + if self._hash == -1: + self._hash = hash((tuple([self._t_dict[i] for i in sorted(self._t_dict)]), + self._c_coeff, self._d_coeff)) + return self._hash + + def __nonzero__(self): + """ + Return ``self`` as a boolean. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['C',2,1]) + sage: x = L.an_element() + sage: bool(x) + True + sage: bool(L.zero()) + False + """ + return bool(self._t_dict) or bool(self._c_coeff) or bool(self._d_coeff) + + __bool__ = __nonzero__ + + cdef _add_(self, other): + """ + Add ``self`` and ``other``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: e0.bracket(e1) + d + e1 + c + 3*d + (E[alpha[1]])#t^0 + (-h1)#t^1 + c + 4*d + """ + cdef UntwistedAffineLieAlgebraElement rt = other + return type(self)(self._parent, axpy(1, self._t_dict, rt._t_dict.copy()), + self._c_coeff + rt._c_coeff, + self._d_coeff + rt._d_coeff) + + cdef _sub_(self, other): + """ + Subtract ``self`` and ``other``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: e0.bracket(e1) + d - e1 + c - 3*d + (-E[alpha[1]])#t^0 + (-h1)#t^1 + c + -2*d + sage: e0.bracket(f0) - 4*c + (h1)#t^0 + sage: e0.bracket(f0) - 4*c - h1 + 0 + sage: e0.bracket(f0) - 4*c - h1 == L.zero() + True + """ + cdef UntwistedAffineLieAlgebraElement rt = other + return type(self)(self._parent, axpy(-1, self._t_dict, rt._t_dict.copy()), + self._c_coeff - rt._c_coeff, + self._d_coeff - rt._d_coeff) + + cdef _neg_(self): + """ + Negate ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: x = e0.bracket(e1) + d + e1 + c + 3*d + sage: -x + (-E[alpha[1]])#t^0 + (h1)#t^1 + -1*c + -4*d + """ + return type(self)(self._parent, negate(self._t_dict), + -self._c_coeff, -self._d_coeff) + + cpdef _acted_upon_(self, x, bint self_on_left): + """ + Return ``self`` acted upon by ``x``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: x = e1 + f0.bracket(f1) + 3*c - 2/5 * d + sage: x + (-E[alpha[1]])#t^0 + (-h1)#t^-1 + 3*c + -2/5*d + sage: -2 * x + (2*E[alpha[1]])#t^0 + (2*h1)#t^-1 + -6*c + 4/5*d + """ + return type(self)(self._parent, scal(x, self._t_dict, self_on_left), + x * self._c_coeff, + x * self._d_coeff) + + cpdef monomial_coefficients(self, bint copy=True): + """ + Return the monomial coefficients of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['C',2,1]) + sage: x = L.an_element() + sage: sorted(x.monomial_coefficients(), key=str) + [(-2*alpha[1] - alpha[2], 1), + (-alpha[1], 0), + (-alpha[2], 0), + (2*alpha[1] + alpha[2], -1), + (alpha[1], 0), + (alpha[2], 0), + (alphacheck[1], 0), + (alphacheck[2], 0), + 'c', + 'd'] + """ + cdef dict d = {} + for t,g in self._t_dict.iteritems(): + for k,c in g.monomial_coefficients(copy=False).iteritems(): + d[k,t] = c + if self._c_coeff: + d['c'] = self._c_coeff + if self._d_coeff: + d['d'] = self._d_coeff + return d + + cpdef bracket(self, right): + """ + Return the Lie bracket ``[self, right]``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: e0.bracket(f0) + (-h1)#t^0 + 4*c + sage: e1.bracket(0) + 0 + sage: e1.bracket(1) + Traceback (most recent call last): + ... + TypeError: no common canonical parent for objects with parents: + 'Affine Kac-Moody algebra of ['A', 1] in the Chevalley basis' + and 'Integer Ring' + """ + if not have_same_parent(self, right): + self, right = coercion_model.canonical_coercion(self, right) + return self._bracket_(right) + + cpdef _bracket_(self, y): + """ + Return the Lie bracket ``[self, y]``. + + EXAMPLES:: + + sage: L = LieAlgebra(QQ, cartan_type=['A',1,1]) + sage: e1,f1,h1,e0,f0,c,d = list(L.lie_algebra_generators()) + sage: al = RootSystem(['A',1]).root_lattice().simple_roots() + sage: x = L.basis()[al[1], 5] + sage: y = L.basis()[-al[1], -3] + sage: z = L.basis()[-al[1], -5] + sage: x._bracket_(y) + (h1)#t^2 + sage: x._bracket_(z) + (h1)#t^0 + 20*c + sage: x._bracket_(e1) + 0 + sage: x._bracket_(f1) + (h1)#t^5 + sage: x._bracket_(h1) + (-2*E[alpha[1]])#t^5 + sage: x._bracket_(d) + (-5*E[alpha[1]])#t^5 + sage: all(c._bracket_(g) == 0 for g in L.lie_algebra_generators()) + True + """ + if not self or not y: + return self._parent.zero() + + gd = self._parent._g.basis() + cdef dict d = {} + cdef UntwistedAffineLieAlgebraElement rt = (y) + c = self._parent.base_ring().zero() + for tl,gl in self._t_dict.iteritems(): + # d contribution from the left + if rt._d_coeff: + if tl in d: + d[tl] -= rt._d_coeff * gl * tl + else: + d[tl] = -rt._d_coeff * gl * tl + if not d[tl]: + del d[tl] + # main bracket of the central extension + for tr,gr in rt._t_dict.iteritems(): + b = gl.bracket(gr) + if b: + if tl+tr in d: + d[tl+tr] += b + else: + d[tl+tr] = b + if not d[tl+tr]: + del d[tl+tr] + if tl + tr == 0: + c += gl.killing_form(gr) * tl + + # d contribution from the right + if self._d_coeff: + for tr,gr in rt._t_dict.iteritems(): + if tr in d: + d[tr] += self._d_coeff * gr * tr + else: + d[tr] = self._d_coeff * gr * tr + if not d[tr]: + del d[tr] + + return type(self)(self._parent, d, c, + self._parent.base_ring().zero()) + + cpdef canonical_derivation(self): + r""" + Return the canonical derivation `d` applied to ``self``. + + The canonical derivation `d` is defined as + + .. MATH:: + + d(a \otimes t^m + \alpha c) = a \otimes m t^m. + + Another formulation is by `d = t \frac{d}{dt}`. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['E',6,1]) + sage: al = RootSystem(['E',6]).root_lattice().simple_roots() + sage: x = L.basis()[al[2]+al[3]+2*al[4]+al[5],5] + 4*L.c() + L.d() + sage: x.canonical_derivation() + (5*E[alpha[2] + alpha[3] + 2*alpha[4] + alpha[5]])#t^5 + """ + cdef dict d = {tl: tl * gl for tl,gl in self._t_dict.iteritems() if tl != 0} + zero = self._parent.base_ring().zero() + return type(self)(self.parent(), d, zero, zero) + +def _build_untwisted_affine_element(P, t_dict, c, d): + """ + Used to unpickle an element. + + EXAMPLES:: + + sage: L = lie_algebras.Affine(QQ, ['A',2,1]) + sage: from sage.algebras.lie_algebras.lie_algebra_element import _build_untwisted_affine_element + sage: _build_untwisted_affine_element(L, {}, 0, 0) == L.zero() + True + sage: x = L.an_element() + sage: loads(dumps(x)) == x # indirect doctest + True + """ + return P.element_class(P, t_dict, c, d) + diff --git a/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py new file mode 100644 index 00000000000..82b366196f0 --- /dev/null +++ b/src/sage/algebras/lie_algebras/poincare_birkhoff_witt.py @@ -0,0 +1,369 @@ +""" +The Poincare-Birkhoff-Witt Basis For A Universal Enveloping Algebra + +AUTHORS: + +- Travis Scrimshaw (2013-11-03): Initial version +""" + +#***************************************************************************** +# Copyright (C) 2013-2017 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod +from sage.categories.algebras import Algebras +from sage.monoids.indexed_free_monoid import IndexedFreeAbelianMonoid +from sage.combinat.free_module import CombinatorialFreeModule +from sage.sets.family import Family +from sage.rings.all import ZZ + +class PoincareBirkhoffWittBasis(CombinatorialFreeModule): + r""" + The Poincare-Birkhoff-Witt (PBW) basis of the universal enveloping + algebra of a Lie algebra. + + Consider a Lie algebra `\mathfrak{g}` with ordered basis + `(b_1,\dots,b_n)`. Then the universal enveloping algebra `U(\mathfrak{g})` + is generated by `b_1,\dots,b_n` and subject to the relations + + .. MATH:: + + [b_i, b_j] = \sum_{k = 1}^n c_{ij}^k b_k + + where `c_{ij}^k` are the structure coefficients of `\mathfrak{g}`. The + Poincare-Birkhoff-Witt (PBW) basis is given by the monomials + `b_1^{e_1} b_2^{e_2} \cdots b_n^{e_n}`. Specifically, we can rewrite + `b_j b_i = b_i b_j + [b_j, b_i]` where `j > i`, and we can repeat + this to sort any monomial into + + .. MATH:: + + b_{i_1} \cdots b_{i_k} = b_1^{e_1} \cdots b_n^{e_n} + LOT + + where `LOT` are lower order terms. Thus the PBW basis is a filtered basis + for `U(\mathfrak{g})`. + + EXAMPLES: + + We construct the PBW basis of `\mathfrak{sl}_2`:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3, names=['E','F','H']) + sage: PBW = L.pbw_basis() + + We then do some computations; in particular, we check that `[E, F] = H`:: + + sage: E,F,H = PBW.algebra_generators() + sage: E*F + PBW['E']*PBW['F'] + sage: F*E + PBW['E']*PBW['F'] - PBW['H'] + sage: E*F - F*E + PBW['H'] + + Next we construct another instance of the PBW basis, but sorted in the + reverse order:: + + sage: def neg_key(x): + ....: return -L.basis().keys().index(x) + sage: PBW2 = L.pbw_basis(prefix='PBW2', basis_key=neg_key) + + We then check the multiplication is preserved:: + + sage: PBW2(E) * PBW2(F) + PBW2['F']*PBW2['E'] + PBW2['H'] + sage: PBW2(E*F) + PBW2['F']*PBW2['E'] + PBW2['H'] + sage: F * E + H + PBW['E']*PBW['F'] + + We now construct the PBW basis for Lie algebra of regular + vector fields on `\CC^{\times}`:: + + sage: L = lie_algebras.regular_vector_fields(QQ) + sage: PBW = L.pbw_basis() + sage: G = PBW.algebra_generators() + sage: G[2] * G[3] + PBW[2]*PBW[3] + sage: G[3] * G[2] + PBW[2]*PBW[3] - PBW[5] + sage: G[-2] * G[3] * G[2] + PBW[-2]*PBW[2]*PBW[3] - PBW[-2]*PBW[5] + """ + @staticmethod + def __classcall_private__(cls, g, basis_key=None, prefix='PBW', **kwds): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: from sage.algebras.lie_algebras.poincare_birkhoff_witt import PoincareBirkhoffWittBasis + sage: L = lie_algebras.sl(QQ, 2) + sage: P1 = PoincareBirkhoffWittBasis(L) + sage: P2 = PoincareBirkhoffWittBasis(L, prefix='PBW') + sage: P1 is P2 + True + """ + return super(PoincareBirkhoffWittBasis, cls).__classcall__(cls, + g, basis_key, prefix, **kwds) + + def __init__(self, g, basis_key, prefix, **kwds): + """ + Initialize ``self``. + + TESTS:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + sage: E,F,H = PBW.algebra_generators() + sage: TestSuite(PBW).run(elements=[E, F, H]) + sage: TestSuite(PBW).run(elements=[E, F, H, E*F + H]) # long time + """ + if basis_key is not None: + self._basis_key = basis_key + + R = g.base_ring() + self._g = g + monomials = IndexedFreeAbelianMonoid(g.basis().keys(), prefix, + sorting_key=self._monoid_key, **kwds) + CombinatorialFreeModule.__init__(self, R, monomials, + prefix='', bracket=False, latex_bracket=False, + sorting_key=self._monomial_key, + category=Algebras(R).WithBasis()) + + def _basis_key(self, x): + """ + Return a key for sorting for the index ``x``. + + TESTS:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3, names=['E','F','H']) + sage: PBW = L.pbw_basis() + sage: PBW._basis_key('E') < PBW._basis_key('H') + True + + :: + + sage: L = lie_algebras.sl(QQ, 2) + sage: def neg_key(x): + ....: return -L.basis().keys().index(x) + sage: PBW = L.pbw_basis(basis_key=neg_key) + sage: prod(PBW.gens()) # indirect doctest + PBW[alphacheck[1]]*PBW[-alpha[1]]*PBW[alpha[1]] + + PBW[alphacheck[1]]^2 + """ + K = self._g.basis().keys() + if K.cardinality() == float('inf'): + return x + lst = list(K) + return lst.index(x) + + def _monoid_key(self, x): + """ + Comparison function for the underlying monoid. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: def neg_key(x): + ....: return -L.basis().keys().index(x) + sage: PBW = L.pbw_basis(basis_key=neg_key) + sage: M = PBW.basis().keys() + sage: prod(M.gens()) # indirect doctest + PBW[alphacheck[1]]*PBW[-alpha[1]]*PBW[alpha[1]] + """ + return self._basis_key(x[0]) + + def _monomial_key(self, x): + """ + Compute the key for ``x`` so that the comparison is done by + reverse degree lexicographic order. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + sage: E,F,H = PBW.algebra_generators() + sage: H*F*F*E # indirect doctest + PBW[alpha[1]]*PBW[-alpha[1]]^2*PBW[alphacheck[1]] + - 2*PBW[alpha[1]]*PBW[-alpha[1]]^2 + - 2*PBW[-alpha[1]]*PBW[alphacheck[1]]^2 + + 6*PBW[-alpha[1]]*PBW[alphacheck[1]] - 4*PBW[-alpha[1]] + + sage: def neg_key(x): + ....: return -L.basis().keys().index(x) + sage: PBW = L.pbw_basis(basis_key=neg_key) + sage: E,F,H = PBW.algebra_generators() + sage: E*F*F*H # indirect doctest + PBW[alphacheck[1]]*PBW[-alpha[1]]^2*PBW[alpha[1]] + + 2*PBW[alphacheck[1]]^2*PBW[-alpha[1]] + + 2*PBW[-alpha[1]]^2*PBW[alpha[1]] + + 6*PBW[alphacheck[1]]*PBW[-alpha[1]] + 4*PBW[-alpha[1]] + + """ + return (-len(x), [self._basis_key(l) for l in x.to_word_list()]) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: L.pbw_basis() + Universal enveloping algebra of + Lie algebra of ['A', 1] in the Chevalley basis + in the Poincare-Birkhoff-Witt basis + """ + return "Universal enveloping algebra of {} in the Poincare-Birkhoff-Witt basis".format(self._g) + + def _coerce_map_from_(self, R): + """ + Return ``True`` if there is a coercion map from ``R`` to ``self``. + + EXAMPLES: + + We lift from the Lie algebra:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + sage: PBW.has_coerce_map_from(L) + True + sage: [PBW(g) for g in L.basis()] + [PBW[alpha[1]], PBW[-alpha[1]], PBW[alphacheck[1]]] + + We can go between PBW bases under different sorting orders:: + + sage: def neg_key(x): + ....: return -L.basis().keys().index(x) + sage: PBW2 = L.pbw_basis(basis_key=neg_key) + sage: E,F,H = PBW.algebra_generators() + sage: PBW2(E*F*H) + PBW[alphacheck[1]]*PBW[-alpha[1]]*PBW[alpha[1]] + + PBW[alphacheck[1]]^2 + """ + if R == self._g: + # Make this into the lift map + I = self._indices + basis_function = lambda x: self.monomial(I.gen(x)) + # TODO: this diagonal, but with a smaller indexing set... + return self._g.module_morphism(basis_function, codomain=self, + triangular='upper', unitriangular=True) + + if isinstance(R, PoincareBirkhoffWittBasis) and self._g == R._g: + I = self._indices + def basis_function(x): + return self.prod(self.monomial(I.gen(g)**e) for g,e in x._sorted_items()) + # TODO: this diagonal, but with a smaller indexing set... + return R.module_morphism(basis_function, codomain=self) + + return super(PoincareBirkhoffWittBasis, self)._coerce_map_from_(R) + + def lie_algebra(self): + """ + Return the underlying Lie algebra of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + sage: PBW.lie_algebra() is L + True + """ + return self._g + + def algebra_generators(self): + """ + Return the algebra generators of ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + sage: PBW.algebra_generators() + Finite family {-alpha[1]: PBW[-alpha[1]], + alpha[1]: PBW[alpha[1]], + alphacheck[1]: PBW[alphacheck[1]]} + """ + G = self._indices.gens() + return Family(self._indices._indices, lambda x: self.monomial(G[x]), + name="generator map") + + gens = algebra_generators + + @cached_method + def one_basis(self): + """ + Return the basis element indexing `1`. + + EXAMPLES:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3, names=['E','F','H']) + sage: PBW = L.pbw_basis() + sage: ob = PBW.one_basis(); ob + 1 + sage: ob.parent() + Free abelian monoid indexed by {'E', 'F', 'H'} + """ + return self._indices.one() + + def product_on_basis(self, lhs, rhs): + """ + Return the product of the two basis elements ``lhs`` and ``rhs``. + + EXAMPLES:: + + sage: L = lie_algebras.three_dimensional_by_rank(QQ, 3, names=['E','F','H']) + sage: PBW = L.pbw_basis() + sage: I = PBW.indices() + sage: PBW.product_on_basis(I.gen('E'), I.gen('F')) + PBW['E']*PBW['F'] + sage: PBW.product_on_basis(I.gen('E'), I.gen('H')) + PBW['E']*PBW['H'] + sage: PBW.product_on_basis(I.gen('H'), I.gen('E')) + PBW['E']*PBW['H'] + 2*PBW['E'] + sage: PBW.product_on_basis(I.gen('F'), I.gen('E')) + PBW['E']*PBW['F'] - PBW['H'] + sage: PBW.product_on_basis(I.gen('F'), I.gen('H')) + PBW['F']*PBW['H'] + sage: PBW.product_on_basis(I.gen('H'), I.gen('F')) + PBW['F']*PBW['H'] - 2*PBW['F'] + sage: PBW.product_on_basis(I.gen('H')**2, I.gen('F')**2) + PBW['F']^2*PBW['H']^2 - 8*PBW['F']^2*PBW['H'] + 16*PBW['F']^2 + + sage: E,F,H = PBW.algebra_generators() + sage: E*F - F*E + PBW['H'] + sage: H * F * E + PBW['E']*PBW['F']*PBW['H'] - PBW['H']^2 + sage: E * F * H * E + PBW['E']^2*PBW['F']*PBW['H'] + 2*PBW['E']^2*PBW['F'] + - PBW['E']*PBW['H']^2 - 2*PBW['E']*PBW['H'] + """ + # Some trivial base cases + if lhs == self.one_basis(): + return self.monomial(rhs) + if rhs == self.one_basis(): + return self.monomial(lhs) + + I = self._indices + trail = lhs.trailing_support() + lead = rhs.leading_support() + if self._basis_key(trail) <= self._basis_key(lead): + return self.monomial(lhs * rhs) + + # Create the commutator + # We have xy - yx = [x, y] -> xy = yx + [x, y] and we have x > y + terms = self._g.monomial(trail).bracket(self._g.monomial(lead)) + lead = I.gen(lead) + trail = I.gen(trail) + terms = self.sum_of_terms((I.gen(t), c) for t,c in terms) + terms += self.monomial(lead * trail) + return self.monomial(lhs // trail) * terms * self.monomial(rhs // lead) + diff --git a/src/sage/algebras/lie_algebras/structure_coefficients.py b/src/sage/algebras/lie_algebras/structure_coefficients.py index 394e0070b2a..226010a206a 100644 --- a/src/sage/algebras/lie_algebras/structure_coefficients.py +++ b/src/sage/algebras/lie_algebras/structure_coefficients.py @@ -244,7 +244,7 @@ def to_vector(tuples): def structure_coefficients(self, include_zeros=False): """ - Return the dictonary of structure coefficients of ``self``. + Return the dictionary of structure coefficients of ``self``. EXAMPLES:: diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 3f8b3eaeaf7..243445657ef 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -454,8 +454,7 @@ from __future__ import print_function, absolute_import from six.moves import range -from sage.combinat.free_module import CombinatorialFreeModule, \ - CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method from sage.categories.all import ModulesWithBasis, tensor, Hom @@ -3073,22 +3072,22 @@ def is_generic(self): # element class ###################################################### - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): r""" Class for elements of the Steenrod algebra. Since the Steenrod algebra class is based on :class:`CombinatorialFreeModule `, this is - based on :class:`CombinatorialFreeModuleElement - `. + based on :class:`IndexedFreeModuleElement + `. It has new methods reflecting its role, like :meth:`degree` for computing the degree of an element. EXAMPLES: Since this class inherits from - :class:`CombinatorialFreeModuleElement - `, + :class:`IndexedFreeModuleElement + `, elements can be used as iterators, and there are other useful methods:: diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index 9476c44c3d9..bf04407ada0 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -1124,9 +1124,6 @@ def steenrod_basis_error_check(dim, p, **kwds): import sage.misc.misc as misc generic = kwds.get('generic', False if p==2 else True ) - # In this test function, we don't want to use caching. - # Hence, the uncached versions of steenrod_algebra_basis - # and of convert_to_milnor_matrix are used. if not generic: bases = ('adem','woody', 'woodz', 'wall', 'arnona', 'arnonc', 'pst_rlex', 'pst_llex', 'pst_deg', 'pst_revz', diff --git a/src/sage/algebras/steenrod/steenrod_algebra_mult.py b/src/sage/algebras/steenrod/steenrod_algebra_mult.py index a00d9647120..329e624809e 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_mult.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_mult.py @@ -7,7 +7,7 @@ multiplication. - John H. Palmieri (2010-06-30: version 1.0) multiplication of Serre-Cartan basis elements using the Adem relations. - - Simon King (2011-10-25): Fix the use of cached functions. +- Simon King (2011-10-25): Fix the use of cached functions. .. rubric:: Milnor multiplication, `p=2` @@ -187,9 +187,8 @@ where `s_k \geq \epsilon_{k+1} + p s_{k+1}` for all `k`. As at the prime 2, these form a basis for the Steenrod algebra. -The main function for this is :func:`make_mono_admissible_` (and in -practice, one should use the cached version, -``make_mono_admissible``), which converts a product of Steenrod +The main function for this is :func:`make_mono_admissible`, +which converts a product of Steenrod squares or pth power operations and Bocksteins into a dictionary representing a sum of admissible monomials. """ @@ -707,12 +706,6 @@ def adem(a, b, c=0, p=2, generic=None): a dictionary representing the mod `p` Adem relations applied to `P^a P^b` or (if `c` present) to `P^a \beta^b P^c`. - .. note:: - - Users should use :func:`adem` instead of this function (which - has a trailing underscore in its name): :func:`adem` - is the cached version of this one, and so will be faster. - The mod `p` Adem relations for the mod `p` Steenrod algebra are as follows: if `p=2`, then if `a < 2b`, @@ -876,13 +869,6 @@ def make_mono_admissible(mono, p=2, generic=None): tuples `(i_1, ..., i_{j-1}, NEW, i_{j+2}, ...)`, keeping track of the coefficients. - .. note:: - - Users should use :func:`make_mono_admissible` instead of this - function (which has a trailing underscore in its name): - :func:`make_mono_admissible` is the cached version of this - one, and so will be faster. - EXAMPLES:: sage: from sage.algebras.steenrod.steenrod_algebra_mult import make_mono_admissible diff --git a/src/sage/algebras/weyl_algebra.py b/src/sage/algebras/weyl_algebra.py index 075b8e375c1..daafa6d8eff 100644 --- a/src/sage/algebras/weyl_algebra.py +++ b/src/sage/algebras/weyl_algebra.py @@ -26,7 +26,6 @@ from sage.categories.algebras_with_basis import AlgebrasWithBasis from sage.sets.family import Family import sage.data_structures.blas_dict as blas -from sage.combinat.free_module import _divide_if_possible from sage.rings.ring import Algebra from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.multi_polynomial_ring_generic import MPolynomialRing_generic @@ -498,7 +497,7 @@ def __truediv__(self, x): return self.__class__(F, D) - return self.__class__(F, {t: _divide_if_possible(D[t], x) for t in D}) + return self.__class__(F, {t: D[t]._divide_if_possible(x) for t in D}) __div__ = __truediv__ diff --git a/src/sage/calculus/calculus.py b/src/sage/calculus/calculus.py index e0ae5df1739..385691fc375 100644 --- a/src/sage/calculus/calculus.py +++ b/src/sage/calculus/calculus.py @@ -15,9 +15,6 @@ - Tom Coates (2010-06-11): fixed :trac:`9217` -The Sage calculus module is loosely based on the Sage Enhancement -Proposal found at: http://www.sagemath.org:9001/CalculusSEP. - EXAMPLES: The basic units of the calculus package are symbolic expressions which @@ -809,6 +806,103 @@ def nintegral(ex, x, a, b, nintegrate = nintegral +def symbolic_product(expression, v, a, b, algorithm='maxima', hold=False): + r""" + Return the symbolic product `\prod_{v = a}^b expression` with respect + to the variable `v` with endpoints `a` and `b`. + + INPUT: + + - ``expression`` - a symbolic expression + + - ``v`` - a variable or variable name + + - ``a`` - lower endpoint of the product + + - ``b`` - upper endpoint of the prduct + + - ``algorithm`` - (default: ``'maxima'``) one of + + - ``'maxima'`` - use Maxima (the default) + + - ``'giac'`` - (optional) use Giac + + - ``'sympy'`` - use SymPy + + - ``hold`` - (default: ``False``) if ``True`` don't evaluate + + EXAMPLES:: + + sage: i, k, n = var('i,k,n') + sage: from sage.calculus.calculus import symbolic_product + sage: symbolic_product(k, k, 1, n) + factorial(n) + sage: symbolic_product(x + i*(i+1)/2, i, 1, 4) + x^4 + 20*x^3 + 127*x^2 + 288*x + 180 + sage: symbolic_product(i^2, i, 1, 7) + 25401600 + sage: f = function('f') + sage: symbolic_product(f(i), i, 1, 7) + f(7)*f(6)*f(5)*f(4)*f(3)*f(2)*f(1) + sage: symbolic_product(f(i), i, 1, n) + product(f(i), i, 1, n) + sage: assume(k>0) + sage: symbolic_product(integrate (x^k, x, 0, 1), k, 1, n) + 1/factorial(n + 1) + sage: symbolic_product(f(i), i, 1, n).log().log_expand() + sum(log(f(i)), i, 1, n) + """ + if not is_SymbolicVariable(v): + if isinstance(v, str): + v = var(v) + else: + raise TypeError("need a multiplication variable") + + if v in SR(a).variables() or v in SR(b).variables(): + raise ValueError("product limits must not depend on the multiplication variable") + + if hold == True: + from sage.functions.other import symbolic_prod as sprod + return sprod(expression, v, a, b) + + if algorithm == 'maxima': + return maxima.sr_prod(expression,v,a,b) + + elif algorithm == 'mathematica': + try: + prod = "Product[%s, {%s, %s, %s}]" % tuple([repr(expr._mathematica_()) for expr in (expression, v, a, b)]) + except TypeError: + raise ValueError("Mathematica cannot make sense of input") + from sage.interfaces.mathematica import mathematica + try: + result = mathematica(prod) + except TypeError: + raise ValueError("Mathematica cannot make sense of: %s" % sum) + return result.sage() + + elif algorithm == 'giac': + prod = "product(%s, %s, %s, %s)" % tuple([repr(expr._giac_()) for expr in (expression, v, a, b)]) + from sage.interfaces.giac import giac + try: + result = giac(prod) + except TypeError: + raise ValueError("Giac cannot make sense of: %s" % sum) + return result.sage() + + elif algorithm == 'sympy': + expression,v,a,b = [expr._sympy_() for expr in (expression, v, a, b)] + from sympy import product as sproduct + result = sproduct(expression, (v, a, b)) + try: + return result._sage_() + except AttributeError: + raise AttributeError("Unable to convert SymPy result (={}) into" + " Sage".format(result)) + + else: + raise ValueError("unknown algorithm: %s" % algorithm) + + def minpoly(ex, var='x', algorithm=None, bits=None, degree=None, epsilon=0): r""" Return the minimal polynomial of self, if possible. @@ -1039,8 +1133,8 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): INPUT: - ``dir`` - (default: None); dir may have the value - 'plus' (or '+' or 'right') for a limit from above, - 'minus' (or '-' or 'left') for a limit from below, or may be omitted + 'plus' (or '+' or 'right' or 'above') for a limit from above, + 'minus' (or '-' or 'left' or 'below') for a limit from below, or may be omitted (implying a two-sided limit is to be computed). - ``taylor`` - (default: False); if True, use Taylor @@ -1158,8 +1252,8 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): sage: lim(x^2, x=2, dir='nugget') Traceback (most recent call last): ... - ValueError: dir must be one of None, 'plus', '+', 'right', - 'minus', '-', 'left' + ValueError: dir must be one of None, 'plus', '+', 'above', 'right', + 'minus', '-', 'below', 'left' We check that :trac:`3718` is fixed, so that Maxima gives correct limits for the floor function:: @@ -1211,6 +1305,11 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): sage: sequence = -(3*n^2 + 1)*(-1)^n/sqrt(n^5 + 8*n^3 + 8) sage: limit(sequence, n=infinity) 0 + + Check if :trac:`23048` is fixed:: + + sage: (1/(x-3)).limit(x=3, dir='below') + -Infinity """ if not isinstance(ex, Expression): ex = SR(ex) @@ -1227,21 +1326,21 @@ def limit(ex, dir=None, taylor=False, algorithm='maxima', **argv): if dir not in [None, 'plus', '+', 'right', 'minus', '-', 'left', 'above', 'below']: - raise ValueError("dir must be one of None, 'plus', '+', 'right', 'minus', '-', 'left'") + raise ValueError("dir must be one of None, 'plus', '+', 'above', 'right', 'minus', '-', 'below', 'left'") if algorithm == 'maxima': if dir is None: l = maxima.sr_limit(ex, v, a) - elif dir in ['plus', '+', 'right']: + elif dir in ['plus', '+', 'right', 'above']: l = maxima.sr_limit(ex, v, a, 'plus') - elif dir in ['minus', '-', 'left']: + elif dir in ['minus', '-', 'left', 'below']: l = maxima.sr_limit(ex, v, a, 'minus') elif algorithm == 'maxima_taylor': if dir is None: l = maxima.sr_tlimit(ex, v, a) - elif dir == 'plus' or dir == 'above' or dir == 'from_right': + elif dir in ['plus', '+', 'right', 'above']: l = maxima.sr_tlimit(ex, v, a, 'plus') - elif dir == 'minus' or dir == 'below' or dir == 'from_left': + elif dir in ['minus', '-', 'left', 'below']: l = maxima.sr_tlimit(ex, v, a, 'minus') elif algorithm == 'sympy': if dir is None: @@ -1299,8 +1398,9 @@ def laplace(ex, t, s, algorithm='maxima'): are auxiliary convergence conditions. .. SEEALSO:: + :func:`inverse_laplace` - + EXAMPLES: We compute a few Laplace transforms:: @@ -1390,7 +1490,7 @@ def laplace(ex, t, s, algorithm='maxima'): sage: laplace(heaviside(t-1), t, s, algorithm='sympy') (e^(-s)/s, 0, True) - TESTS:: + TESTS: Testing Giac:: @@ -1399,15 +1499,14 @@ def laplace(ex, t, s, algorithm='maxima'): sage: laplace(5*cos(3*t-2)*heaviside(t-2), t, s, algorithm='giac') 5*(s*cos(4)*e^(-2*s) - 3*e^(-2*s)*sin(4))/(s^2 + 9) - Testing unevaluated expression from Giac:: + Check unevaluated expression from Giac (it is locale-dependent, see + :trac:`22833`):: sage: var('n') n sage: laplace(t^n, t, s, algorithm='giac') - Traceback (most recent call last): - ... - NotImplementedError: Unable to parse Giac output: integrate(t^n*exp(-s*t),t,0,+infinity) - + laplace(t^n, t, s) + Testing SymPy:: sage: laplace(t^n, t, s, algorithm='sympy') @@ -1454,7 +1553,10 @@ def laplace(ex, t, s, algorithm='maxima'): result = giac.laplace(ex, t, s) except TypeError: raise ValueError("Giac cannot make sense of: %s" % ex_gi) - return result.sage() + if 'integrate' in format(result) or 'integration' in format(result): + return dummy_laplace(ex, t, s) + else: + return result.sage() else: raise ValueError("Unknown algorithm: %s" % algorithm) @@ -1496,8 +1598,9 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): - ``'giac'`` - use Giac .. SEEALSO:: + :func:`laplace` - + EXAMPLES:: sage: var('w, m') @@ -1543,7 +1646,7 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): sage: inverse_laplace(1, s, t, algorithm='giac') dirac_delta(t) - TESTS:: + TESTS: Testing unevaluated expression from Maxima:: @@ -1566,10 +1669,8 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): sage: n = var('n') sage: inverse_laplace(1/s^n, s, t, algorithm='giac') - Traceback (most recent call last): - ... - NotImplementedError: Unable to parse Giac output: ilaplace([s^(-n),s,t]) - + ilt(1/(s^n), t, s) + Try with Maxima:: sage: inverse_laplace(1/s^n, s, t, algorithm='maxima') @@ -1585,12 +1686,10 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): sage: inverse_laplace(cos(s), s, t, algorithm='sympy') ilt(cos(s), t, s) - Testing unevaluated expression from Giac:: + Testing the same with Giac:: sage: inverse_laplace(cos(s), s, t, algorithm='giac') - Traceback (most recent call last): - ... - NotImplementedError: Unable to parse Giac output: ilaplace([cos(s),s,t]) + ilt(cos(s), t, s) """ if not isinstance(ex, Expression): ex = SR(ex) @@ -1617,7 +1716,10 @@ def inverse_laplace(ex, s, t, algorithm='maxima'): result = giac.invlaplace(ex, s, t) except TypeError: raise ValueError("Giac cannot make sense of: %s" % ex) - return result.sage() + if 'ilaplace' in format(result): + return dummy_inverse_laplace(ex, t, s) + else: + return result.sage() else: raise ValueError("Unknown algorithm: %s" % algorithm) @@ -1702,21 +1804,6 @@ def at(ex, *args, **kwds): return ex.subs(**kwds) - -def dummy_limit(*args): - """ - This function is called to create formal wrappers of limits that - Maxima can't compute: - - EXAMPLES:: - - sage: a = lim(exp(x^2)*(1-erf(x)), x=infinity); a - -limit((erf(x) - 1)*e^(x^2), x, +Infinity) - sage: a = sage.calculus.calculus.dummy_limit(sin(x)/x, x, 0);a - limit(sin(x)/x, x, 0) - """ - return _limit(args[0], var(repr(args[1])), SR(args[2])) - def dummy_diff(*args): """ This function is called when 'diff' appears in a Maxima string. @@ -1797,59 +1884,6 @@ def dummy_inverse_laplace(*args): # ####################################################### -def _limit_latex_(self, f, x, a, direction=None): - r""" - Return latex expression for limit of a symbolic function. - - EXAMPLES:: - - sage: from sage.calculus.calculus import _limit_latex_ - sage: var('x,a') - (x, a) - sage: f = function('f') - sage: _limit_latex_(0, f(x), x, a) - '\\lim_{x \\to a}\\, f\\left(x\\right)' - sage: latex(limit(f(x), x=oo)) - \lim_{x \to +\infty}\, f\left(x\right) - - TESTS: - - When one-sided limits are converted back from maxima, the direction - argument becomes a symbolic variable. We check if typesetting these works:: - - sage: var('minus,plus') - (minus, plus) - sage: _limit_latex_(0, f(x), x, a, minus) - '\\lim_{x \\to a^-}\\, f\\left(x\\right)' - sage: _limit_latex_(0, f(x), x, a, plus) - '\\lim_{x \\to a^+}\\, f\\left(x\\right)' - sage: latex(limit(f(x),x=a,dir='+')) - \lim_{x \to a^+}\, f\left(x\right) - sage: latex(limit(f(x),x=a,dir='right')) - \lim_{x \to a^+}\, f\left(x\right) - sage: latex(limit(f(x),x=a,dir='-')) - \lim_{x \to a^-}\, f\left(x\right) - sage: latex(limit(f(x),x=a,dir='left')) - \lim_{x \to a^-}\, f\left(x\right) - - Check if :trac:`13181` is fixed:: - - sage: t = var('t') - sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x,dir='-')) - \lim_{t \to x^-}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) - sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x,dir='+')) - \lim_{t \to x^+}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) - sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x)) - \lim_{t \to x}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) - """ - if repr(direction) == 'minus': - dir_str = '^-' - elif repr(direction) == 'plus': - dir_str = '^+' - else: - dir_str = '' - return "\\lim_{%s \\to %s%s}\\, %s"%(latex(x), latex(a), dir_str, latex(f)) - def _laplace_latex_(self, *args): r""" Return LaTeX expression for Laplace transform of a symbolic function. @@ -1887,7 +1921,6 @@ def _inverse_laplace_latex_(self, *args): return "\\mathcal{L}^{-1}\\left(%s\\right)"%(', '.join([latex(x) for x in args])) # Return un-evaluated expression as instances of SFunction class -_limit = function_factory('limit', print_latex_func=_limit_latex_) _laplace = function_factory('laplace', print_latex_func=_laplace_latex_) _inverse_laplace = function_factory('ilt', print_latex_func=_inverse_laplace_latex_) @@ -2010,11 +2043,12 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): sage: sefms('%inf') +Infinity """ + global _syms syms = symbol_table.get('maxima', {}).copy() - if len(x) == 0: + if not len(x): raise RuntimeError("invalid symbolic expression -- ''") - maxima.set('_tmp_',x) + maxima.set('_tmp_', x) # This is inefficient since it so rarely is needed: #r = maxima._eval_line('listofvars(_tmp_);')[1:-1] @@ -2022,16 +2056,19 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): s = maxima._eval_line('_tmp_;') formal_functions = maxima_tick.findall(s) - if len(formal_functions) > 0: + if len(formal_functions): for X in formal_functions: - syms[X[1:]] = function_factory(X[1:]) + try: + syms[X[1:]] = _syms[X[1:]] + except KeyError: + syms[X[1:]] = function_factory(X[1:]) # You might think there is a potential very subtle bug if 'foo # is in a string literal -- but string literals should *never* # ever be part of a symbolic expression. s = s.replace("'","") delayed_functions = maxima_qp.findall(s) - if len(delayed_functions) > 0: + if len(delayed_functions): for X in delayed_functions: if X == '?%at': # we will replace Maxima's "at" with symbolic evaluation, not an SFunction pass @@ -2090,7 +2127,6 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): search = sci_not.search(s) # have to do this here, otherwise maxima_tick catches it - syms['limit'] = dummy_limit syms['diff'] = dummy_diff syms['integrate'] = dummy_integrate syms['laplace'] = dummy_laplace @@ -2102,7 +2138,6 @@ def symbolic_expression_from_maxima_string(x, equals_sub=False, maxima=maxima): # use a global flag so all expressions obtained via # evaluation of maxima code are assumed pre-simplified is_simplified = True - global _syms _syms = symbol_table['functions'].copy() try: global _augmented_syms diff --git a/src/sage/calculus/wester.py b/src/sage/calculus/wester.py index bda8129431d..89b65be312f 100644 --- a/src/sage/calculus/wester.py +++ b/src/sage/calculus/wester.py @@ -509,6 +509,8 @@ sage: # Verify(D(x) Abs(x), Sign(x)); sage: diff(abs(x)) 1/2*(x + conjugate(x))/abs(x) + sage: _.simplify_full() + x/abs(x) sage: _ = var('x', domain='real') sage: diff(abs(x)) x/abs(x) diff --git a/src/sage/categories/algebras_with_basis.py b/src/sage/categories/algebras_with_basis.py index 526ad8c9091..45f2b1d80a8 100644 --- a/src/sage/categories/algebras_with_basis.py +++ b/src/sage/categories/algebras_with_basis.py @@ -96,7 +96,7 @@ class AlgebrasWithBasis(CategoryWithAxiom_over_base_ring): sage: A.__class__ sage: A.element_class - + Please see the source code of `A` (with ``A??``) for how to implement other algebras with basis. diff --git a/src/sage/categories/commutative_rings.py b/src/sage/categories/commutative_rings.py index cc41fd0c463..19f3949b7f0 100644 --- a/src/sage/categories/commutative_rings.py +++ b/src/sage/categories/commutative_rings.py @@ -85,7 +85,7 @@ def cyclotomic_cosets(self, q, cosets=None): .. NOTE:: When `R = \ZZ / n \ZZ` the smallest element of each coset is - sometimes callled a *coset leader*. This function returns + sometimes called a *coset leader*. This function returns sorted lists so that the coset leader will always be the first element of the coset. diff --git a/src/sage/categories/crystals.py b/src/sage/categories/crystals.py index 08ad389ccda..5093ef189ce 100644 --- a/src/sage/categories/crystals.py +++ b/src/sage/categories/crystals.py @@ -237,7 +237,8 @@ def an_element(self): def weight_lattice_realization(self): """ - Returns the weight lattice realization used to express weights. + Return the weight lattice realization used to express weights + in ``self``. This default implementation uses the ambient space of the root system for (non relabelled) finite types and the @@ -245,6 +246,9 @@ def weight_lattice_realization(self): ambient spaces were partially implemented, and may be changed in the future. + For affine types, this returns the extended weight lattice + by default. + EXAMPLES:: sage: C = crystals.Letters(['A', 5]) @@ -253,10 +257,43 @@ def weight_lattice_realization(self): sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) sage: K.weight_lattice_realization() Weight lattice of the Root system of type ['A', 2, 1] + + TESTS: + + Check that crystals have the correct weight lattice realization:: + + sage: A = crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() + sage: A.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 2, 1] + + sage: B = crystals.AlcovePaths(['A',2,1],[1,0,0]) + sage: B.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 2, 1] + + sage: C = crystals.AlcovePaths("B3",[1,0,0]) + sage: C.weight_lattice_realization() + Ambient space of the Root system of type ['B', 3] + + sage: M = crystals.infinity.NakajimaMonomials(['A',3,2]) + sage: M.weight_lattice_realization() + Extended weight lattice of the Root system of type ['B', 2, 1]^* + sage: M = crystals.infinity.NakajimaMonomials(['A',2]) + sage: M.weight_lattice_realization() + Ambient space of the Root system of type ['A', 2] + sage: A = CartanMatrix([[2,-3],[-3,2]]) + sage: M = crystals.infinity.NakajimaMonomials(A) + sage: M.weight_lattice_realization() + Weight lattice of the Root system of type Dynkin diagram of rank 2 + + sage: Y = crystals.infinity.GeneralizedYoungWalls(3) + sage: Y.weight_lattice_realization() + Extended weight lattice of the Root system of type ['A', 3, 1] """ F = self.cartan_type().root_system() if self.cartan_type().is_finite() and F.ambient_space() is not None: return F.ambient_space() + if self.cartan_type().is_affine(): + return F.weight_lattice(extended=True) return F.weight_lattice() def cartan_type(self): @@ -391,9 +428,9 @@ def subcrystal(self, index_set=None, generators=None, max_depth=float("inf"), sage: len(S) 6 sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)])) - [[[1, 4]], [[2, 4]], [[1, 3]], [[2, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]], [[2, 3]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], max_depth=1)) - [[[1, 4]], [[2, 4]], [[1, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], direction='upper')) [[[1, 4]], [[1, 3]]] sage: list(C.subcrystal(index_set=[1,3], generators=[C(1,4)], direction='lower')) @@ -592,7 +629,7 @@ def crystal_morphism(self, on_gens, codomain=None, [The crystal of tableaux of type ['A', 2] and shape(s) [[1]], The crystal of tableaux of type ['A', 2] and shape(s) [[1]], The crystal of tableaux of type ['A', 2] and shape(s) [[1]]] - Defn: [2, 1, 1] |--> [[[1]], [[2]], [[1]]] + Defn: [[1, 1], [2]] |--> [[[1]], [[2]], [[1]]] sage: b = B.module_generators[0] sage: b.pp() 1 1 @@ -634,7 +671,7 @@ def crystal_morphism(self, on_gens, codomain=None, ['D', 4] -> ['D', 4, 1] Virtual Crystal morphism: From: The crystal of tableaux of type ['D', 4] and shape(s) [[1, 1]] To: Kirillov-Reshetikhin crystal of type ['D', 4, 1] with (r,s)=(2,2) - Defn: [2, 1] |--> [[1], [2]] + Defn: [[1], [2]] |--> [[1], [2]] sage: b = B.module_generators[0] sage: psi(b) [[1], [2]] @@ -738,7 +775,7 @@ def crystal_morphism(self, on_gens, codomain=None, def digraph(self, subset=None, index_set=None): """ - Returns the DiGraph associated to ``self``. + Return the :class:`DiGraph` associated to ``self``. INPUT: @@ -826,12 +863,7 @@ def digraph(self, subset=None, index_set=None): .. TODO:: Add more tests. """ from sage.graphs.all import DiGraph - from sage.categories.highest_weight_crystals import HighestWeightCrystals d = {} - if self in HighestWeightCrystals: - f = lambda u_v_label: ({}) - else: - f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) # Parse optional arguments if subset is None: @@ -852,9 +884,8 @@ def digraph(self, subset=None, index_set=None): G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", - edge_labels = True, - color_by_label = self.cartan_type()._index_set_coloring, - edge_options = f) + edge_labels=True, + color_by_label=self.cartan_type()._index_set_coloring) return G def latex_file(self, filename): @@ -1139,7 +1170,7 @@ def tensor(self, *crystals, **options): The crystal of letters for type ['A', 2], The crystal of letters for type ['A', 2]] sage: T.module_generators - [[2, 1, 1], [1, 2, 1]] + ([2, 1, 1], [1, 2, 1]) """ from sage.combinat.crystals.tensor_product import TensorProductOfCrystals return TensorProductOfCrystals(self, *crystals, **options) @@ -1655,9 +1686,9 @@ def subcrystal(self, index_set=None, max_depth=float("inf"), direction="both", sage: C = crystals.KirillovReshetikhin(['A',3,1], 1, 2) sage: elt = C(1,4) sage: list(elt.subcrystal(index_set=[1,3])) - [[[1, 4]], [[2, 4]], [[1, 3]], [[2, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]], [[2, 3]]] sage: list(elt.subcrystal(index_set=[1,3], max_depth=1)) - [[[1, 4]], [[2, 4]], [[1, 3]]] + [[[1, 4]], [[1, 3]], [[2, 4]]] sage: list(elt.subcrystal(index_set=[1,3], direction='upper')) [[[1, 4]], [[1, 3]]] sage: list(elt.subcrystal(index_set=[1,3], direction='lower')) @@ -1990,8 +2021,8 @@ def _repr_defn(self): sage: psi = H((None, b, b, None), generators=T.highest_weight_vectors()) sage: print(psi._repr_defn()) [[[1]], [[1]], [[1]]] |--> None - [[[2]], [[1]], [[1]]] |--> [2, 1, 1] - [[[1]], [[2]], [[1]]] |--> [2, 1, 1] + [[[2]], [[1]], [[1]]] |--> [[1, 1], [2]] + [[[1]], [[2]], [[1]]] |--> [[1, 1], [2]] [[[3]], [[2]], [[1]]] |--> None """ return '\n'.join(['{} |--> {}'.format(mg, im) @@ -2314,7 +2345,7 @@ class CrystalHomset(Homset): To: Direct sum of the crystals Family (The crystal of tableaux of type ['A', 1] and shape(s) [[2]], The crystal of tableaux of type ['A', 1] and shape(s) [[]]) - Defn: [[[1]], [[1]]] |--> [1, 1] + Defn: [[[1]], [[1]]] |--> [[1, 1]] [[[2]], [[1]]] |--> [] sage: psi.is_isomorphism() True @@ -2368,15 +2399,15 @@ class CrystalHomset(Homset): ['B', 3] -> ['D', 4] Virtual Crystal morphism: From: The crystal of tableaux of type ['B', 3] and shape(s) [[1]] To: The crystal of tableaux of type ['D', 4] and shape(s) [[2]] - Defn: [1] |--> [1, 1] + Defn: [[1]] |--> [[1, 1]] sage: for b in B: print("{} |--> {}".format(b, psi(b))) - [1] |--> [1, 1] - [2] |--> [2, 2] - [3] |--> [3, 3] - [0] |--> [3, -3] - [-3] |--> [-3, -3] - [-2] |--> [-2, -2] - [-1] |--> [-1, -1] + [[1]] |--> [[1, 1]] + [[2]] |--> [[2, 2]] + [[3]] |--> [[3, 3]] + [[0]] |--> [[3, -3]] + [[-3]] |--> [[-3, -3]] + [[-2]] |--> [[-2, -2]] + [[-1]] |--> [[-1, -1]] """ def __init__(self, X, Y, category=None): """ @@ -2416,7 +2447,7 @@ def _coerce_impl(self, x): sage: H = Hom(B, B) sage: H(H.an_element()) # indirect doctest ['B', 3] Crystal endomorphism of The crystal of tableaux of type ['B', 3] and shape(s) [[2, 1]] - Defn: [2, 1, 1] |--> None + Defn: [[1, 1], [2]] |--> None """ if not isinstance(x, CrystalMorphism): raise TypeError @@ -2502,7 +2533,7 @@ def _an_element_(self): ['A', 2] Crystal morphism: From: The crystal of tableaux of type ['A', 2] and shape(s) [[2, 1]] To: The infinity crystal of tableaux of type ['A', 2] - Defn: [2, 1, 1] |--> None + Defn: [[1, 1], [2]] |--> None """ return self.element_class(self, lambda x: None) diff --git a/src/sage/categories/euclidean_domains.py b/src/sage/categories/euclidean_domains.py index d88dc767caf..25b9aa1d067 100644 --- a/src/sage/categories/euclidean_domains.py +++ b/src/sage/categories/euclidean_domains.py @@ -273,3 +273,4 @@ def quo_rem(self, other): sage: x.quo_rem(x) (1, 0) """ + diff --git a/src/sage/categories/examples/filtered_modules_with_basis.py b/src/sage/categories/examples/filtered_modules_with_basis.py index 9954d6a2ff5..e4f8a52dd9e 100644 --- a/src/sage/categories/examples/filtered_modules_with_basis.py +++ b/src/sage/categories/examples/filtered_modules_with_basis.py @@ -63,10 +63,10 @@ class FilteredPartitionModule(CombinatorialFreeModule): 'P[4, 3]' - There is a class for elements, which inherits from - :class:`CombinatorialFreeModuleElement - `. An - element is determined by a dictionary whose keys are partitions and whose - corresponding values are the coefficients. The class implements + :class:`IndexedFreeModuleElement + `. + An element is determined by a dictionary whose keys are partitions and + whose corresponding values are the coefficients. The class implements two things: an :meth:`is_homogeneous ` method and a :meth:`degree ` method. diff --git a/src/sage/categories/examples/graded_modules_with_basis.py b/src/sage/categories/examples/graded_modules_with_basis.py index e0f95880199..6e32d51601c 100644 --- a/src/sage/categories/examples/graded_modules_with_basis.py +++ b/src/sage/categories/examples/graded_modules_with_basis.py @@ -77,10 +77,10 @@ class GradedPartitionModule(CombinatorialFreeModule): 'P[4, 3]' - There is a class for elements, which inherits from - :class:`CombinatorialFreeModuleElement - `. An - element is determined by a dictionary whose keys are partitions and whose - corresponding values are the coefficients. The class implements + :class:`IndexedFreeModuleElement + `. + An element is determined by a dictionary whose keys are partitions and + whose corresponding values are the coefficients. The class implements two things: an :meth:`is_homogeneous ` method and a :meth:`degree ` method. diff --git a/src/sage/categories/examples/lie_algebras_with_basis.py b/src/sage/categories/examples/lie_algebras_with_basis.py index 6c122daa56e..af51f57a25d 100644 --- a/src/sage/categories/examples/lie_algebras_with_basis.py +++ b/src/sage/categories/examples/lie_algebras_with_basis.py @@ -135,7 +135,7 @@ def __init__(self, R, indices, **kwds): def _repr_(self): """ - Return a string represenation of ``self``. + Return a string representation of ``self``. EXAMPLES:: diff --git a/src/sage/categories/fields.py b/src/sage/categories/fields.py index 2243bf31085..de4123ba6d3 100644 --- a/src/sage/categories/fields.py +++ b/src/sage/categories/fields.py @@ -313,7 +313,7 @@ def _squarefree_decomposition_univariate_polynomial(self, f): - ``f`` -- a univariate non-zero polynomial over this field ALGORITHM: For rings of characteristic zero, we use the algorithm - descriped in [Yun1976]_. Other fields may provide their own + described in [Yun1976]_. Other fields may provide their own implementation by overriding this method. EXAMPLES:: diff --git a/src/sage/categories/finite_dimensional_algebras_with_basis.py b/src/sage/categories/finite_dimensional_algebras_with_basis.py index e0c0951db95..d8ecb7d95df 100644 --- a/src/sage/categories/finite_dimensional_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_algebras_with_basis.py @@ -641,7 +641,7 @@ def cartan_invariants_matrix(self): EXAMPLES: For a semisimple algebra, in particular for group algebras - in chararacteristic zero, the Cartan invariants matrix is + in characteristic zero, the Cartan invariants matrix is the identity:: sage: A3 = SymmetricGroup(3).algebra(QQ) diff --git a/src/sage/categories/finite_lattice_posets.py b/src/sage/categories/finite_lattice_posets.py index a70997e6eca..1abf7841a5a 100644 --- a/src/sage/categories/finite_lattice_posets.py +++ b/src/sage/categories/finite_lattice_posets.py @@ -26,7 +26,7 @@ class FiniteLatticePosets(CategoryWithAxiom): .. SEEALSO:: - :class:`FinitePosets`, :class:`LatticePosets`, :class:`LatticePoset` + :class:`FinitePosets`, :class:`LatticePosets`, :class:`~sage.combinat.posets.lattices.FiniteLatticePoset` TESTS:: @@ -55,9 +55,9 @@ def join_irreducibles(self): .. SEEALSO:: - :meth:`meet_irreducibles`, - :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.double_irreducibles`, - :meth:`meet_irreducibles_poset` + - Dual function: :meth:`meet_irreducibles` + - Other: :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.double_irreducibles`, + :meth:`join_irreducibles_poset` """ return [x for x in self if len(self.lower_covers(x)) == 1] @@ -75,7 +75,10 @@ def join_irreducibles_poset(self): sage: L.join_irreducibles_poset() Finite poset containing 3 elements - .. SEEALSO:: :meth:`join_irreducibles` + .. SEEALSO:: + + - Dual function: :meth:`meet_irreducibles_poset` + - Other: :meth:`join_irreducibles` """ return self.subposet(self.join_irreducibles()) @@ -95,9 +98,9 @@ def meet_irreducibles(self): .. SEEALSO:: - :meth:`join_irreducibles`, - :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.double_irreducibles`, - :meth:`meet_irreducibles_poset` + - Dual function: :meth:`join_irreducibles` + - Other: :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] @@ -115,10 +118,51 @@ def meet_irreducibles_poset(self): sage: L.join_irreducibles_poset() Finite poset containing 3 elements - .. SEEALSO:: :meth:`meet_irreducibles` + .. SEEALSO:: + + - Dual function: :meth:`join_irreducibles_poset` + - Other: :meth:`meet_irreducibles` """ return self.subposet(self.meet_irreducibles()) + def irreducibles_poset(self): + """ + Return the poset of meet- or join-irreducibles of the lattice. + + A *join-irreducible* element of a lattice is an element with + exactly one lower cover. Dually a *meet-irreducible* element + has exactly one upper cover. + + This is the smallest poset with completion by cuts being + isomorphic to the lattice. As a special case this returns + one-element poset from one-element lattice. + + .. SEEALSO:: + + :meth:`~sage.combinat.posets.posets.FinitePoset.completion_by_cuts`. + + EXAMPLES:: + + sage: L = LatticePoset({1: [2, 3, 4], 2: [5, 6], 3: [5], + ....: 4: [6], 5: [9, 7], 6: [9, 8], 7: [10], + ....: 8: [10], 9: [10], 10: [11]}) + sage: L_ = L.irreducibles_poset() + sage: sorted(L_) + [2, 3, 4, 7, 8, 9, 10, 11] + sage: L_.completion_by_cuts().is_isomorphic(L) + True + + TESTS:: + + sage: LatticePoset().irreducibles_poset() + Finite poset containing 0 elements + sage: Posets.ChainPoset(1).irreducibles_poset() + Finite poset containing 1 elements + """ + if self.cardinality() == 1: + from sage.combinat.posets.posets import Poset + return Poset({self[0]: []}) + return self.subposet(self.join_irreducibles()+self.meet_irreducibles()) ########################################################################## # Lattice morphisms diff --git a/src/sage/categories/finite_posets.py b/src/sage/categories/finite_posets.py index 3be703a291b..2d6f1c1d534 100644 --- a/src/sage/categories/finite_posets.py +++ b/src/sage/categories/finite_posets.py @@ -85,9 +85,15 @@ def is_selfdual(self): sage: P.is_selfdual() True + TESTS:: + sage: P = Poset( {} ) sage: P.is_selfdual() True + + .. SEEALSO:: + + - Stronger properties: :meth:`~sage.combinat.posets.lattices.FiniteLattice.is_orthocomplemented` """ # Two quick checks before full isomorphic test. if sorted(self._hasse_diagram.in_degree()) != sorted(self._hasse_diagram.out_degree()): diff --git a/src/sage/categories/highest_weight_crystals.py b/src/sage/categories/highest_weight_crystals.py index 0a38a5691a3..b247ce0ea84 100644 --- a/src/sage/categories/highest_weight_crystals.py +++ b/src/sage/categories/highest_weight_crystals.py @@ -837,7 +837,7 @@ def _call_(self, x): [The T crystal of type ['A', 2] and weight Lambda[2], The crystal of tableaux of type ['A', 2] and shape(s) [[1]]] To: The crystal of tableaux of type ['A', 2] and shape(s) [[2, 1]] - Defn: [Lambda[2], [[3]]] |--> [2, 1, 3] + Defn: [Lambda[2], [[3]]] |--> [[1, 3], [2]] sage: psi(Bp.highest_weight_vector()) [[1, 1], [2]] """ diff --git a/src/sage/categories/hopf_algebras_with_basis.py b/src/sage/categories/hopf_algebras_with_basis.py index 8ce84dc4405..a3bf6777746 100644 --- a/src/sage/categories/hopf_algebras_with_basis.py +++ b/src/sage/categories/hopf_algebras_with_basis.py @@ -101,7 +101,7 @@ class HopfAlgebrasWithBasis(CategoryWithAxiom_over_base_ring): sage: A.__class__ sage: A.element_class - + Let us look at the code for implementing A:: diff --git a/src/sage/categories/left_modules.py b/src/sage/categories/left_modules.py index d2ef58880ab..c54d187e102 100644 --- a/src/sage/categories/left_modules.py +++ b/src/sage/categories/left_modules.py @@ -17,7 +17,7 @@ class LeftModules(Category_over_base_ring): """ The category of left modules left modules over an rng (ring not necessarily with unit), i.e. - an abelian group with left multiplation by elements of the rng + an abelian group with left multiplication by elements of the rng EXAMPLES:: diff --git a/src/sage/categories/lie_algebras_with_basis.py b/src/sage/categories/lie_algebras_with_basis.py index 573e4de307f..55c98c041a2 100644 --- a/src/sage/categories/lie_algebras_with_basis.py +++ b/src/sage/categories/lie_algebras_with_basis.py @@ -17,7 +17,7 @@ #***************************************************************************** from sage.misc.abstract_method import abstract_method -#from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_method from sage.categories.category_with_axiom import CategoryWithAxiom_over_base_ring from sage.categories.lie_algebras import LieAlgebras @@ -143,6 +143,24 @@ def dimension(self): """ return self.basis().cardinality() + def pbw_basis(self, basis_key=None, **kwds): + """ + Return the Poincare-Birkhoff-Witt basis of the universal + enveloping algebra corresponding to ``self``. + + EXAMPLES:: + + sage: L = lie_algebras.sl(QQ, 2) + sage: PBW = L.pbw_basis() + """ + from sage.algebras.lie_algebras.poincare_birkhoff_witt \ + import PoincareBirkhoffWittBasis + return PoincareBirkhoffWittBasis(self, basis_key, **kwds) + + poincare_birkhoff_witt_basis = pbw_basis + + _construct_UEA = pbw_basis + class ElementMethods: def _bracket_(self, y): """ diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py new file mode 100644 index 00000000000..700b352714a --- /dev/null +++ b/src/sage/categories/loop_crystals.py @@ -0,0 +1,1220 @@ +r""" +Loop Crystals +""" +#***************************************************************************** +# Copyright (C) 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function, division, absolute_import + +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.crystals import Crystals +from sage.categories.regular_crystals import RegularCrystals +from sage.categories.tensor import TensorProductsCategory +from sage.categories.map import Map +from sage.graphs.dot2tex_utils import have_dot2tex +from sage.functions.other import ceil +from sage.rings.all import ZZ + + +class LoopCrystals(Category_singleton): + r""" + The category of `U_q'(\mathfrak{g})`-crystals, where `\mathfrak{g}` + is of affine type. + + The category is called loop crystals as we can also consider them + as crystals corresponding to the loop algebra `\mathfrak{g}_0[t]`, + where `\mathfrak{g}_0` is the corresponding classical type. + + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: C = LoopCrystals() + sage: C + Category of loop crystals + sage: C.super_categories() + [Category of crystals] + sage: C.example() + Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(1,1) + + TESTS:: + + sage: TestSuite(C).run() + sage: B = FiniteCrystals().example() + sage: TestSuite(B).run() + """ + @cached_method + def super_categories(self): + r""" + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: LoopCrystals().super_categories() + [Category of crystals] + """ + return [Crystals()] + + def example(self, n = 3): + """ + Return an example of Kirillov-Reshetikhin crystals, as per + :meth:`Category.example`. + + EXAMPLES:: + + sage: from sage.categories.loop_crystals import LoopCrystals + sage: B = LoopCrystals().example(); B + Kirillov-Reshetikhin crystal of type ['A', 3, 1] with (r,s)=(1,1) + """ + from sage.combinat.crystals.kirillov_reshetikhin import KirillovReshetikhinCrystal + return KirillovReshetikhinCrystal(['A', n, 1], 1, 1) + + class ParentMethods: + def weight_lattice_realization(self): + """ + Return the weight lattice realization used to express weights + of elements in ``self``. + + The default is to use the non-extended affine weight lattice. + + EXAMPLES:: + + sage: C = crystals.Letters(['A', 5]) + sage: C.weight_lattice_realization() + Ambient space of the Root system of type ['A', 5] + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.weight_lattice_realization() + Weight lattice of the Root system of type ['A', 2, 1] + """ + F = self.cartan_type().root_system() + return F.weight_lattice(extended=False) + + def digraph(self, subset=None, index_set=None): + r""" + Return the :class:`DiGraph` associated to ``self``. + + INPUT: + + - ``subset`` -- (optional) a subset of vertices for + which the digraph should be constructed + + - ``index_set`` -- (optional) the index set to draw arrows + + .. SEEALSO:: + + :meth:`sage.categories.crystals.Crystals.ParentMethods.digraph` + + EXAMPLES:: + + sage: C = crystals.KirillovReshetikhin(['D',4,1], 2, 1) + sage: G = C.digraph() + sage: G.latex_options() # optional - dot2tex + LaTeX options for Digraph on 29 vertices: + {...'edge_options': at 0x...>,...} + sage: view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window) + """ + G = Crystals().parent_class.digraph(self, subset, index_set) + if have_dot2tex(): + f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) + G.set_latex_options(edge_options=f) + return G + +# TODO: Should we make "regular" an axiom? +class RegularLoopCrystals(Category_singleton): + r""" + The category of regular `U_q'(\mathfrak{g})`-crystals, where + `\mathfrak{g}` is of affine type. + """ + @cached_method + def super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.loop_crystals import RegularLoopCrystals + sage: RegularLoopCrystals().super_categories() + [Category of regular crystals, + Category of loop crystals] + """ + return [RegularCrystals(), LoopCrystals()] + + class ElementMethods: + def classical_weight(self): + """ + Return the classical weight of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: hw = LS.classically_highest_weight_vectors() + sage: [(v.weight(), v.classical_weight()) for v in hw] + [(-2*Lambda[0] + 2*Lambda[1], (2, 0, 0)), + (-Lambda[0] + Lambda[2], (1, 1, 0))] + """ + CT = self.cartan_type().classical() + I0 = CT.index_set() + La = CT.root_system().ambient_space().fundamental_weights() + return sum(La[i] * (self.phi(i) - self.epsilon(i)) for i in I0) + +class KirillovReshetikhinCrystals(Category_singleton): + """ + Category of Kirillov-Reshetikhin crystals. + """ + @cached_method + def super_categories(self): + r""" + EXAMPLES:: + + sage: from sage.categories.loop_crystals import KirillovReshetikhinCrystals + sage: KirillovReshetikhinCrystals().super_categories() + [Category of finite regular loop crystals] + """ + return [RegularLoopCrystals().Finite()] + + class ParentMethods: + @abstract_method + def r(self): + r""" + Return the value `r` in ``self`` written as `B^{r,s}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,4) + sage: K.r() + 2 + """ + + @abstract_method + def s(self): + r""" + Return the value `s` in ``self`` written as `B^{r,s}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,4) + sage: K.s() + 4 + """ + + @abstract_method(optional=True) + def classical_decomposition(self): + """ + Return the classical decomposition of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,2) + sage: K.classical_decomposition() + The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] + """ + + @cached_method + def classically_highest_weight_vectors(self): + """ + Return the classically highest weight elements of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1],1,1) + sage: K.classically_highest_weight_vectors() + ([(1,)],) + """ + I0 = self.cartan_type().classical().index_set() + return tuple([x for x in self if x.is_highest_weight(I0)]) + + # TODO: This is duplicated in tensor product category + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,1) + sage: K.cardinality() + 27 + sage: K = crystals.KirillovReshetikhin(['C',6,1], 4,3) + sage: K.cardinality() + 4736732 + """ + CWLR = self.cartan_type().classical().root_system().ambient_space() + return sum(CWLR.weyl_dimension(mg.classical_weight()) + for mg in self.classically_highest_weight_vectors()) + + @cached_method + def maximal_vector(self): + r""" + Return the unique element of classical weight `s \Lambda_r` + in ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,2) + sage: K.maximal_vector() + [[1, 1]] + sage: K = crystals.KirillovReshetikhin(['E',6,1],1,1) + sage: K.maximal_vector() + [(1,)] + + sage: K = crystals.KirillovReshetikhin(['D',4,1],2,1) + sage: K.maximal_vector() + [[1], [2]] + """ + R = self.weight_lattice_realization() + Lambda = R.fundamental_weights() + r = self.r() + s = self.s() + weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() + + # First check the module generators as it is likely to be in here + for b in self.module_generators: + if b.weight() == weight: + return b + + # Otherwise check all of the elements + for b in self: + if b not in self.module_generators and b.weight() == weight: + return b + + assert False, "BUG: invalid Kirillov-Reshetikhin crystal" + + def module_generator(self): + r""" + Return the unique module generator of classical weight + `s \Lambda_r` of the Kirillov-Reshetikhin crystal `B^{r,s}`. + + EXAMPLES:: + + sage: La = RootSystem(['G',2,1]).weight_space().fundamental_weights() + sage: K = crystals.ProjectedLevelZeroLSPaths(La[1]) + sage: K.module_generator() + (-Lambda[0] + Lambda[1],) + """ + return self.maximal_vector() + + # TODO: Should this be in one of the super categories? + def affinization(self): + """ + Return the corresponding affinization crystal of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.affinization() + Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1, model='KR') + sage: K.affinization() + Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) + """ + from sage.combinat.crystals.affinization import AffinizationOfCrystal + return AffinizationOfCrystal(self) + + @cached_method + def R_matrix(self, K): + r""" + Return the combinatorial `R`-matrix of ``self`` to ``K``. + + The *combinatorial* `R`-*matrix* is the affine crystal + isomorphism `R : L \otimes K \to K \otimes L` which maps + `u_{L} \otimes u_K` to `u_K \otimes u_{L}`, where `u_K` + is the unique element in `K = B^{r,s}` of weight + `s\Lambda_r - s c \Lambda_0` (see :meth:`maximal_vector`). + + INPUT: + + - ``self`` -- a crystal `L` + - ``K`` -- a Kirillov-Reshetikhin crystal of the same type as `L` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: L = crystals.KirillovReshetikhin(['A',2,1],1,2) + sage: f = K.R_matrix(L) + sage: [[b,f(b)] for b in crystals.TensorProduct(K,L)] + [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]], + [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]], + [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]], + [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]], + [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]], + [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]], + [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]], + [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]], + [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]], + [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]], + [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]], + [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]], + [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]], + [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]], + [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]], + [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]], + [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]], + [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]] + + sage: K = crystals.KirillovReshetikhin(['D',4,1],1,1) + sage: L = crystals.KirillovReshetikhin(['D',4,1],2,1) + sage: f = K.R_matrix(L) + sage: T = crystals.TensorProduct(K,L) + sage: b = T( K(rows=[[1]]), L(rows=[]) ) + sage: f(b) + [[[2], [-2]], [[1]]] + + Alternatively, one can compute the combinatorial `R`-matrix + using the isomorphism method of digraphs:: + + sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) + sage: T1 = crystals.TensorProduct(K1,K2) + sage: T2 = crystals.TensorProduct(K2,K1) + sage: T1.digraph().is_isomorphic(T2.digraph(), edge_labels=True, certificate=True) #todo: not implemented (see #10904 and #10549) + (True, {[[[1]], [[2], [3]]]: [[[1], [3]], [[2]]], [[[3]], [[2], [3]]]: [[[2], [3]], [[3]]], + [[[3]], [[1], [3]]]: [[[1], [3]], [[3]]], [[[1]], [[1], [3]]]: [[[1], [3]], [[1]]], [[[1]], + [[1], [2]]]: [[[1], [2]], [[1]]], [[[2]], [[1], [2]]]: [[[1], [2]], [[2]]], [[[3]], + [[1], [2]]]: [[[2], [3]], [[1]]], [[[2]], [[1], [3]]]: [[[1], [2]], [[3]]], [[[2]], [[2], [3]]]: [[[2], [3]], [[2]]]}) + """ + from sage.combinat.crystals.tensor_product import TensorProductOfCrystals + T1 = TensorProductOfCrystals(self, K) + T2 = TensorProductOfCrystals(K, self) + gen1 = T1(self.maximal_vector(), K.maximal_vector()) + gen2 = T2(K.maximal_vector(), self.maximal_vector()) + return T1.crystal_morphism({gen1: gen2}, check=False) + + @cached_method + def local_energy_function(self, B): + r""" + Return the local energy function of ``self`` and ``B``. + + See + :class:`~sage.categories.loop_crystals.LocalEnergyFunction` + for a definition. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) + sage: Kp = crystals.KirillovReshetikhin(['A',6,2], 1,1) + sage: H = K.local_energy_function(Kp); H + Local energy function of + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) + tensor + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) + """ + return LocalEnergyFunction(self, B) + + @cached_method + def b_sharp(self): + r""" + Return the element `b^{\sharp}` of ``self``. + + Let `B` be a KR crystal. The element `b^{\sharp}` is the unique + element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with + `\ell = \min \{ \langle c, \varphi(b) \rangle \mid b \in B \}`. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) + sage: K.b_sharp() + [] + sage: K.b_sharp().Phi() + Lambda[0] + + sage: K = crystals.KirillovReshetikhin(['C',3,1], 1,3) + sage: K.b_sharp() + [[-1]] + sage: K.b_sharp().Phi() + 2*Lambda[0] + + sage: K = crystals.KirillovReshetikhin(['D',6,2], 2,2) + sage: K.b_sharp() # long time + [] + sage: K.b_sharp().Phi() # long time + 2*Lambda[0] + """ + ell = float('inf') + bsharp = None + for b in self: + phi = b.Phi() + if phi.support() == [0] and phi[0] < ell: + bsharp = b + ell = phi[0] + return bsharp + + def is_perfect(self, ell=None): + r""" + Check if ``self`` is a perfect crystal of level ``ell``. + + A crystal `\mathcal{B}` is perfect of level `\ell` if: + + #. `\mathcal{B}` is isomorphic to the crystal graph of a + finite-dimensional `U_q'(\mathfrak{g})`-module. + #. `\mathcal{B} \otimes \mathcal{B}` is connected. + #. There exists a `\lambda\in X`, such that + `\mathrm{wt}(\mathcal{B}) \subset \lambda + \sum_{i\in I} + \ZZ_{\le 0} \alpha_i` and there is a unique element in + `\mathcal{B}` of classical weight `\lambda`. + #. For all `b \in \mathcal{B}`, + `\mathrm{level}(\varepsilon (b)) \geq \ell`. + #. For all `\Lambda` dominant weights of level `\ell`, there + exist unique elements `b_{\Lambda}, b^{\Lambda} \in + \mathcal{B}`, such that `\varepsilon(b_{\Lambda}) = + \Lambda = \varphi(b^{\Lambda})`. + + Points (1)-(3) are known to hold. This method checks + points (4) and (5). + + If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, + then it was proven for non-exceptional types in [FOS2010]_ + that it is perfect if and only if `s/c_r` is an integer + (where `c_r` is a constant related to the type of the crystal). + + It is conjectured this is true for all affine types. + + INPUT: + + - ``ell`` -- (default: `s / c_r`) integer; the level + + REFERENCES: + + [FOS2010]_ + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.is_perfect() + True + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: K.is_perfect() + False + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K.is_perfect() + True + + sage: K = crystals.KirillovReshetikhin(['E',6,1], 1,3) + sage: K.is_perfect() + True + + .. TODO:: + + Implement a version for tensor products of KR crystals. + """ + if ell is None: + ell = self.s() / self.cartan_type().c()[self.r()] + if ell not in ZZ: + return False + + if ell not in ZZ: + raise ValueError("perfectness not defined for non-integral levels") + + # [FOS2010]_ check + if self.cartan_type().classical().type() not in ['E','F','G']: + return ell == self.s() / self.cartan_type().c()[self.r()] + + # Check by definition + # TODO: This is duplicated from ProjectedLevelZeroLSPaths, combine the two methods. + # TODO: Similarly, don't duplicate in the tensor product category, maybe + # move this to the derived affine category? + MPhi = [] + for b in self: + p = b.Phi().level() + assert p == b.Epsilon().level() + if p < ell: + return False + if p == ell: + MPhi += [b] + weights = [] + I = self.index_set() + rank = len(I) + La = self.weight_lattice_realization().basis() + from sage.combinat.integer_vector import IntegerVectors + for n in range(1, ell+1): + for c in IntegerVectors(n, rank): + w = sum(c[i]*La[i] for i in I) + if w.level() == ell: + weights.append(w) + return sorted(b.Phi() for b in MPhi) == sorted(weights) + + def level(self): + r""" + Return the level of ``self`` when ``self`` is a perfect crystal. + + .. SEEALSO:: + + :meth:`~sage.categories.loop_crystals.KirillovReshetikhinCrystals.ParentMethods.is_perfect` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: K.level() + 1 + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K.level() + 1 + sage: K = crystals.KirillovReshetikhin(['D',4,1], 1, 3) + sage: K.level() + 3 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: K.level() + Traceback (most recent call last): + ... + ValueError: this crystal is not perfect + """ + if not self.is_perfect(): + raise ValueError("this crystal is not perfect") + return self.s() / self.cartan_type().c()[self.r()] + + def q_dimension(self, q=None, prec=None, use_product=False): + """ + Return the `q`-dimension of ``self``. + + The `q`-dimension of a KR crystal is defined as the `q`-dimension of + the underlying classical crystal. + + EXAMPLES:: + + sage: KRC = crystals.KirillovReshetikhin(['A',2,1], 2,2) + sage: KRC.q_dimension() + q^4 + q^3 + 2*q^2 + q + 1 + sage: KRC = crystals.KirillovReshetikhin(['D',4,1], 2,1) + sage: KRC.q_dimension() + q^10 + q^9 + 3*q^8 + 3*q^7 + 4*q^6 + 4*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 2 + """ + return self.classical_decomposition().q_dimension(q, prec, use_product) + + class ElementMethods: + def lusztig_involution(self): + r""" + Return the result of the classical Lusztig involution on ``self``. + + EXAMPLES:: + + sage: KRT = crystals.KirillovReshetikhin(['D',4,1], 2, 3, model='KR') + sage: mg = KRT.module_generators[1] + sage: mg.lusztig_involution() + [[-2, -2, 1], [-1, -1, 2]] + sage: elt = mg.f_string([2,1,3,2]); elt + [[3, -2, 1], [4, -1, 2]] + sage: elt.lusztig_involution() + [[-4, -2, 1], [-3, -1, 2]] + """ + Cl = self.parent().cartan_type().classical() + I = Cl.index_set() + aut = Cl.opposition_automorphism() + hw = self.to_highest_weight(I)[1] + hw.reverse() + return self.to_lowest_weight(I)[0].e_string(aut[i] for i in hw) + + @cached_method + def energy_function(self): + r""" + Return the energy function of ``self``. + + Let `B` be a KR crystal. Let `b^{\sharp}` denote the unique + element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with + `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. Let + `u_B` denote the maximal element of `B`. The *energy* of + `b \in B` is given by + + .. MATH:: + + D(b) = H(b \otimes b^{\sharp}) - H(u_B \otimes b^{\sharp}), + + where `H` is the :meth:`local energy function + `. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['D',4,1], 2,1) + sage: for x in K.classically_highest_weight_vectors(): + ....: x, x.energy_function() + ([], 1) + ([[1], [2]], 0) + + sage: K = crystals.KirillovReshetikhin(['D',4,3], 1,2) + sage: for x in K.classically_highest_weight_vectors(): + ....: x, x.energy_function() + ([], 2) + ([[1]], 1) + ([[1, 1]], 0) + """ + B = self.parent() + bsharp = B.b_sharp() + T = B.tensor(B) + H = B.local_energy_function(B) + return H(T(self, bsharp)) - H(T(B.maximal_vector(), bsharp)) + + class TensorProducts(TensorProductsCategory): + """ + The category of tensor products of Kirillov-Reshetikhin crystals. + """ + @cached_method + def extra_super_categories(self): + """ + EXAMPLES:: + + sage: from sage.categories.loop_crystals import KirillovReshetikhinCrystals + sage: KirillovReshetikhinCrystals().TensorProducts().extra_super_categories() + [Category of finite regular loop crystals] + """ + return [RegularLoopCrystals().Finite()] + + class ParentMethods: + @cached_method + def maximal_vector(self): + """ + Return the maximal vector of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: T.maximal_vector() + [[[1]], [[1]], [[1]]] + """ + return self(*[K.maximal_vector() for K in self.crystals]) + + @cached_method + def classically_highest_weight_vectors(self): + r""" + Return the classically highest weight elements of ``self``. + + This works by using a backtracking algorithm since if + `b_2 \otimes b_1` is classically highest weight then `b_1` + is classically highest weight. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: T.classically_highest_weight_vectors() + ([[[1]], [[1]], [[1]]], + [[[2]], [[1]], [[1]]], + [[[1]], [[2]], [[1]]], + [[[3]], [[2]], [[1]]]) + """ + n = len(self.crystals) + I0 = self.cartan_type().classical().index_set() + it = [ iter(self.crystals[-1].classically_highest_weight_vectors()) ] + path = [] + ret = [] + while it: + try: + x = next(it[-1]) + except StopIteration: + it.pop() + if path: + path.pop(0) + continue + + b = self.element_class(self, [x] + path) + if not b.is_highest_weight(index_set=I0): + continue + path.insert(0, x) + if len(path) == n: + ret.append(b) + path.pop(0) + else: + it.append( iter(self.crystals[-len(path)-1]) ) + return tuple(ret) + + # TODO: This is duplicated in KR crystals category + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) + sage: RC.cardinality() + 100 + sage: len(RC.list()) + 100 + + sage: RC = RiggedConfigurations(['E', 7, 1], [[1,1]]) + sage: RC.cardinality() + 134 + sage: len(RC.list()) + 134 + + sage: RC = RiggedConfigurations(['B', 3, 1], [[2,2],[1,2]]) + sage: RC.cardinality() + 5130 + """ + CWLR = self.cartan_type().classical().root_system().ambient_space() + return sum(CWLR.weyl_dimension(mg.classical_weight()) + for mg in self.classically_highest_weight_vectors()) + + def one_dimensional_configuration_sum(self, q=None, group_components=True): + r""" + Compute the one-dimensional configuration sum of ``self``. + + INPUT: + + - ``q`` -- (default: ``None``) a variable or ``None``; + if ``None``, a variable `q` is set in the code + - ``group_components`` -- (default: ``True``) boolean; if + ``True``, then the terms are grouped by classical component + + The one-dimensional configuration sum is the sum of the + weights of all elements in the crystal weighted by the + energy function. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: T.one_dimensional_configuration_sum() + B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] + sage: R. = ZZ[] + sage: T.one_dimensional_configuration_sum(t, False) + B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + + (t+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: LS.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum() # long time + True + + TESTS:: + + sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) + sage: T = crystals.TensorProduct(K1,K2) + sage: T.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum(group_components=False) + True + + sage: RC = RiggedConfigurations(['A',3,1],[[1,1],[1,2]]) + sage: B = crystals.KirillovReshetikhin(['A',3,1],1,1) + sage: B1 = crystals.KirillovReshetikhin(['A',3,1],1,2) + sage: T = crystals.TensorProduct(B,B1) + sage: RC.fermionic_formula() == T.one_dimensional_configuration_sum() + True + """ + if q is None: + from sage.rings.all import QQ + q = QQ['q'].gens()[0] + P0 = self.weight_lattice_realization().classical() + B = P0.algebra(q.parent()) + if group_components: + G = self.digraph(index_set=self.cartan_type().classical().index_set()) + C = G.connected_components() + return B.sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) + for c in C) + return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self) + + class ElementMethods: + def energy_function(self, algorithm=None): + r""" + Return the energy function of ``self``. + + ALGORITHM: + + .. RUBRIC:: definition + + Let `T` be a tensor product of Kirillov-Reshetikhin + crystals. Let `R_i` and `H_i` be the combinatorial + `R`-matrix and local energy functions, respectively, acting + on the `i` and `i+1` factors. Let `D_B` be the energy + function of a single Kirillov-Reshetikhin crystal. The + *energy function* is given by + + .. MATH:: + + D = \sum_{j > i} H_i R_{i+1} R_{i+2} \cdots R_{j-1} + + \sum_j D_B R_1 R_2 \cdots R_{j-1}, + + where `D_B` acts on the rightmost factor. + + .. RUBRIC:: grading + + If ``self`` is an element of `T`, a tensor product of + perfect crystals of the same level, then use the affine + grading to determine the energy. Specifically, let `g` + denote the affine grading of ``self`` and `d` the affine + grading of the maximal vector in `T`. Then the energy + of ``self`` is given by `d - g`. + + For more details, see Theorem 7.5 in [ST2011]_. + + INPUT: + + - ``algorithm`` -- (default: ``None``) use one of the + following algorithms to determine the energy function: + + * ``'definition'`` - use the definition of the energy + function; + * ``'grading'`` - use the affine grading; + + if not specified, then this uses ``'grading'`` if all + factors are perfect of the same level and otherwise + this uses ``'definition'`` + + OUTPUT: an integer + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print("{} {}".format(b, b.energy_function())) + [[[1]], [[1]], [[1]]] 0 + [[[2]], [[1]], [[1]]] 1 + [[[1]], [[2]], [[1]]] 2 + [[[3]], [[2]], [[1]]] 3 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: T = crystals.TensorProduct(K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print("{} {}".format(b, b.energy_function())) + [[], []] 4 + [[[1, 1]], []] 3 + [[], [[1, 1]]] 1 + [[[1, 1]], [[1, 1]]] 0 + [[[1, 2]], [[1, 1]]] 1 + [[[2, 2]], [[1, 1]]] 2 + [[[-1, -1]], [[1, 1]]] 2 + [[[1, -1]], [[1, 1]]] 2 + [[[2, -1]], [[1, 1]]] 2 + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) + sage: T = crystals.TensorProduct(K) + sage: t = T.module_generators[0] + sage: t.energy_function('grading') + Traceback (most recent call last): + ... + NotImplementedError: all crystals in the tensor product + need to be perfect of the same level + + TESTS:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) + sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2, 2) + sage: T = tensor([K, K2]) + sage: hw = T.classically_highest_weight_vectors() + sage: all(b.energy_function() == b.energy_function(algorithm='definition') + ....: for b in hw) + True + """ + C = self.parent().crystals[0] + ell = ceil(C.s()/C.cartan_type().c()[C.r()]) + is_perfect = all(ell == K.s()/K.cartan_type().c()[K.r()] + for K in self.parent().crystals) + if algorithm is None: + if is_perfect: + algorithm = 'grading' + else: + algorithm = 'definition' + + if algorithm == 'grading': + if not is_perfect: + raise NotImplementedError("all crystals in the tensor product need to be perfect of the same level") + d = self.parent().maximal_vector().affine_grading() + return d - self.affine_grading() + + if algorithm == 'definition': + # Setup + energy = ZZ.zero() + R_mats = [[K.R_matrix(Kp) for Kp in self.parent().crystals[i+1:]] + for i,K in enumerate(self.parent().crystals)] + H_funcs = [[K.local_energy_function(Kp) for Kp in self.parent().crystals[i+1:]] + for i,K in enumerate(self.parent().crystals)] + + for i,b in enumerate(self): + for j,R in enumerate(R_mats[i]): + H = H_funcs[i][j] + bp = self[i+j+1] + T = R.domain() + t = T(b, bp) + energy += H(t) + b = R(t)[1] + energy += b.energy_function() # D contribution + return energy + else: + raise ValueError("invalid algorithm") + + def affine_grading(self): + r""" + Return the affine grading of ``self``. + + The affine grading is calculated by finding a path + from ``self`` to a ground state path (using the helper method + :meth:`e_string_to_ground_state`) and counting the number + of affine Kashiwara operators `e_0` applied on the way. + + OUTPUT: an integer + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0] + sage: t.affine_grading() + 1 + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.affine_grading() + [[[1]], [[1]], [[1]]] 3 + [[[2]], [[1]], [[1]]] 2 + [[[1]], [[2]], [[1]]] 1 + [[[3]], [[2]], [[1]]] 0 + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) + sage: T = crystals.TensorProduct(K,K,K) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: print b, b.affine_grading() + [[[1]], [[1]], [[1]]] 2 + [[[2]], [[1]], [[1]]] 1 + [[[-1]], [[1]], [[1]]] 1 + [[[1]], [[2]], [[1]]] 1 + [[[-2]], [[2]], [[1]]] 0 + [[[1]], [[-1]], [[1]]] 0 + """ + return self.e_string_to_ground_state().count(0) + + @cached_method + def e_string_to_ground_state(self): + r""" + Return a string of integers in the index set + `(i_1, \ldots, i_k)` such that `e_{i_k} \cdots e_{i_1}` + of ``self`` is the ground state. + + This method calculates a path from ``self`` to a ground + state path using Demazure arrows as defined in Lemma 7.3 + in [ST2011]_. + + OUTPUT: a tuple of integers `(i_1, \ldots, i_k)` + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0] + sage: t.e_string_to_ground_state() + (0, 2) + + sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) + sage: T = crystals.TensorProduct(K,K) + sage: t = T.module_generators[0]; t + [[[1]], [[1]]] + sage: t.e_string_to_ground_state() + (0,) + sage: x = t.e(0) + sage: x.e_string_to_ground_state() + () + sage: y = t.f_string([1,2,1,1,0]); y + [[[2]], [[1]]] + sage: y.e_string_to_ground_state() + () + + TESTS: + + Check that :trac:`22882` is fixed:: + + sage: K = crystals.KirillovReshetikhin(CartanType(['A',6,2]).dual(), 1,1) + sage: T = tensor([K,K,K]) + sage: hw = [x for x in T if x.is_highest_weight([1,2,3])] + sage: gs = T(K(0), K(0), K(0)) + sage: all(elt.e_string(elt.e_string_to_ground_state()) == gs + ....: for elt in hw) + True + sage: all(elt.energy_function() == elt.energy_function('definition') + ....: for elt in hw) + True + """ + ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) + for K in self.parent().crystals) + if self.cartan_type().dual().type() == 'BC': + I = self.cartan_type().index_set() + for i in I[:-1]: + if self.epsilon(i) > 0: + return (i,) + (self.e(i)).e_string_to_ground_state() + if self.epsilon(I[-1]) > ell: + return (I[-1],) + (self.e(I[-1])).e_string_to_ground_state() + return () + + I = self.cartan_type().classical().index_set() + for i in I: + if self.epsilon(i) > 0: + return (i,) + self.e(i).e_string_to_ground_state() + if self.epsilon(0) > ell: + return (0,) + self.e(0).e_string_to_ground_state() + return () + + +##################################################################### +## Local energy function + +class LocalEnergyFunction(Map): + r""" + The local energy function. + + Let `B` and `B'` be Kirillov-Reshetikhin crystals with maximal + vectors `u_B` and `u_{B'}` respectively. The *local energy function* + `H : B \otimes B' \to \ZZ` is the function which satisfies + + .. MATH:: + + H(e_0(b \otimes b')) = H(b \otimes b') + \begin{cases} + 1 & \text{if } i = 0 \text{ and LL}, \\ + -1 & \text{if } i = 0 \text{ and RR}, \\ + 0 & \text{otherwise,} + \end{cases} + + where LL (resp. RR) denote `e_0` acts on the left (resp. right) + on both `b \otimes b'` and `R(b \otimes b')`, and + normalized by `H(u_B \otimes u_{B'}) = 0`. + + INPUT: + + - ``B`` -- a Kirillov-Reshetikhin crystal + - ``Bp`` -- a Kirillov-Reshetikhin crystal + - ``normalization`` -- (default: 0) the normalization value + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['C',2,1], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2,1) + sage: H = K.local_energy_function(K2) + sage: T = tensor([K, K2]) + sage: hw = T.classically_highest_weight_vectors() + sage: for b in hw: + ....: b, H(b) + ([[], [[1], [2]]], 1) + ([[[1, 1]], [[1], [2]]], 0) + ([[[2, -2]], [[1], [2]]], 1) + ([[[1, -2]], [[1], [2]]], 1) + + REFERENCES: + + [KKMMNN1992]_ + """ + def __init__(self, B, Bp, normalization=0): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A',7,2], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['A',7,2], 2,1) + sage: H = K.local_energy_function(K2) + sage: TestSuite(H).run(skip=['_test_category', '_test_pickling']) + + TESTS: + + Check that :trac:`23014` is fixed:: + + sage: La = RootSystem(['G',2,1]).weight_space().fundamental_weights() + sage: K = crystals.ProjectedLevelZeroLSPaths(La[1]) + sage: H = K.local_energy_function(K) + sage: hw = H.domain().classically_highest_weight_vectors() + sage: [H(x) for x in hw] + [0, 1, 2, 1] + """ + self._B = B + self._Bp = Bp + self._R_matrix = self._B.R_matrix(self._Bp) + T = B.tensor(Bp) + self._known_values = {T(*[K.maximal_vector() for K in T.crystals]): + ZZ(normalization)} + self._I0 = T.cartan_type().classical().index_set() + from sage.categories.homset import Hom + Map.__init__(self, Hom(T, ZZ)) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['A', 6, 2], 2, 1) + sage: Kp = crystals.KirillovReshetikhin(['A', 6, 2], 1, 1) + sage: H = K.local_energy_function(Kp); H + Local energy function of + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) + tensor + Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) + """ + return "Local energy function of {} tensor {}".format(self._B, self._Bp) + + def _call_(self, x): + """ + Return the local energy of ``x``. + + EXAMPLES:: + + sage: K = crystals.KirillovReshetikhin(['B',4,1], 1,2) + sage: K2 = crystals.KirillovReshetikhin(['B',4,1], 2,1) + sage: H = K.local_energy_function(K2) + sage: T = tensor([K, K2]) + sage: hw = [x for x in T if x.is_highest_weight([1,2])] + sage: H(hw[0]) + 1 + """ + # Setup variables + visited = {x: 0} + check0 = [x] + + # Helper function + def to_classical_hw(cur): + for i in self._I0: + b = cur.e(i) + if b is not None and b not in visited: + visited[b] = visited[cur] # No change + return b + return None # is classically HW or all have been visited + + cur = x + # Get the affine node (it might not be 0 if the type + # has been relabeled) + i0 = x.parent().cartan_type().special_node() + while cur not in self._known_values: + # We first go towards the classically highest weight since + # the maximal vector is classically highest weight + b = to_classical_hw(cur) + + # If classically HW, then try 0 arrows + while b is None: + b = check0.pop() + c = b.e(i0) + # If there is no 0 arrow or we have already seen c, move along + if c is None or c in visited: + b = None + continue + + bp = self._R_matrix(b) + cp = bp.e(i0) + if b[1] == c[1] and bp[1] == cp[1]: # LL case + visited[c] = visited[b] + 1 + elif b[0] == c[0] and bp[0] == cp[0]: # RR case + visited[c] = visited[b] - 1 + else: + visited[c] = visited[b] # Otherwise no change + b = c + + cur = b + check0.append(b) + + baseline = self._known_values[cur] - visited[cur] + for y in visited: + self._known_values[y] = baseline + visited[y] + + return self._known_values[x] + diff --git a/src/sage/categories/map.pyx b/src/sage/categories/map.pyx index fde87261ba5..07846e65d74 100644 --- a/src/sage/categories/map.pyx +++ b/src/sage/categories/map.pyx @@ -8,26 +8,22 @@ AUTHORS: - Sebastien Besnier (2014-05-5): :class:`FormalCompositeMap` contains a list of Map instead of only two Map. See :trac:`16291`. """ + #***************************************************************************** # Copyright (C) 2008 Robert Bradshaw # -# 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 __future__ import absolute_import -from __future__ import print_function -include "sage/ext/stdsage.pxi" +from __future__ import absolute_import, print_function + from . import homset import weakref +from sage.ext.stdsage cimport HAS_DICTIONARY from sage.structure.parent cimport Set_PythonType from sage.misc.constant_function import ConstantFunction from sage.misc.superseded import deprecated_function_alias diff --git a/src/sage/categories/modules.py b/src/sage/categories/modules.py index c5235746eff..f51953b3b21 100644 --- a/src/sage/categories/modules.py +++ b/src/sage/categories/modules.py @@ -498,9 +498,18 @@ def extra_super_categories(self): True sage: Modules(ZZ).FiniteDimensional().is_subcategory(Sets().Finite()) False + + sage: Modules(Rings().Finite()).FiniteDimensional().is_subcategory(Sets().Finite()) + True + sage: Modules(Rings()).FiniteDimensional().is_subcategory(Sets().Finite()) + False """ - if self.base_ring() in Sets().Finite(): - return [Sets().Finite()] + base_ring = self.base_ring() + FiniteSets = Sets().Finite() + if (isinstance(base_ring, Category) and + base_ring.is_subcategory(FiniteSets)) or \ + base_ring in FiniteSets: + return [FiniteSets] else: return [] diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 21734e04131..96ad41ba971 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -1961,7 +1961,7 @@ def __call_on_basis__(self, **options): This method simply delegates the work to :meth:`ModulesWithBasis.ParentMethods.module_morphism`. It is used by :meth:`Homset.__call__` to handle the - ``on_basis`` argument, and will disapear as soon as + ``on_basis`` argument, and will disappear as soon as the logic will be generalized. EXAMPLES:: diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index daec2127216..9926cd3d268 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -414,7 +414,7 @@ class implements: sage: R. = PolynomialRing(QQ, sparse=True) sage: pQ = R ( p ) sage: type(pQ) - + sage: pQ.factor() (6) * (x + 1)^2 diff --git a/src/sage/categories/right_modules.py b/src/sage/categories/right_modules.py index b5cdca0600f..bdc23bd92ff 100644 --- a/src/sage/categories/right_modules.py +++ b/src/sage/categories/right_modules.py @@ -17,7 +17,7 @@ class RightModules(Category_over_base_ring): """ The category of right modules right modules over an rng (ring not necessarily with unit), i.e. - an abelian group with right multiplation by elements of the rng + an abelian group with right multiplication by elements of the rng EXAMPLES:: diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 414a41bb134..8e1ff35786b 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1015,6 +1015,30 @@ def is_unit(self): return False raise NotImplementedError + def _divide_if_possible(self, y): + """ + Divide ``self`` by ``y`` if possible and raise a + ``ValueError`` otherwise. + + EXAMPLES:: + + sage: 4._divide_if_possible(2) + 2 + sage: _.parent() + Integer Ring + + :: + + sage: 4._divide_if_possible(3) + Traceback (most recent call last): + ... + ValueError: 4 is not divisible by 3 + """ + q, r = self.quo_rem(y) + if r != 0: + raise ValueError("%s is not divisible by %s"%(self, y)) + return q + def _gen_names(elts): r""" Used to find a name for a generator when rings are created using the diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index a44509d014e..c498cc55447 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -965,7 +965,7 @@ def _element_constructor_(self): sage: B = GroupAlgebra(SymmetricGroup(3), ZZ) sage: B.element_class - + sage: B._element_constructor """ @@ -2581,29 +2581,33 @@ def inject_shorthands(self, verbose=True): INPUT: - - ``verbose`` -- boolean (default ``True``) if ``True``, prints the defined shorthands + - ``verbose`` -- boolean (default ``True``); if ``True``, + prints the defined shorthands EXAMPLES:: sage: Q = QuasiSymmetricFunctions(ZZ) sage: Q.inject_shorthands() Injecting M as shorthand for Quasisymmetric functions over - the Integer Ring in the Monomial basis + the Integer Ring in the Monomial basis Injecting F as shorthand for Quasisymmetric functions over - the Integer Ring in the Fundamental basis + the Integer Ring in the Fundamental basis + Injecting E as shorthand for Quasisymmetric functions over + the Integer Ring in the Essential basis + doctest:...: RuntimeWarning: redefining global value `E` Injecting dI as shorthand for Quasisymmetric functions over - the Integer Ring in the dualImmaculate basis + the Integer Ring in the dualImmaculate basis Injecting QS as shorthand for Quasisymmetric functions over - the Integer Ring in the Quasisymmetric Schur basis + the Integer Ring in the Quasisymmetric Schur basis sage: F[1,2,1] + 5*M[1,3] + F[2]^2 5*F[1, 1, 1, 1] - 5*F[1, 1, 2] - 3*F[1, 2, 1] + 6*F[1, 3] + 2*F[2, 2] + F[3, 1] + F[4] sage: F Quasisymmetric functions over the Integer Ring in the - Fundamental basis + Fundamental basis sage: M Quasisymmetric functions over the Integer Ring in the - Monomial basis + Monomial basis """ from sage.misc.misc import inject_variable if not hasattr(self, "_shorthands"): diff --git a/src/sage/categories/unital_algebras.py b/src/sage/categories/unital_algebras.py index abb60701dd2..166db1d0c2e 100644 --- a/src/sage/categories/unital_algebras.py +++ b/src/sage/categories/unital_algebras.py @@ -249,7 +249,7 @@ def from_base_ring(self): def from_base_ring_from_one_basis(self, r): """ - Implement the canonical embeding from the ground ring. + Implement the canonical embedding from the ground ring. INPUT: diff --git a/src/sage/categories/weyl_groups.py b/src/sage/categories/weyl_groups.py index 677f81e4f82..bb6c1f5f9e4 100644 --- a/src/sage/categories/weyl_groups.py +++ b/src/sage/categories/weyl_groups.py @@ -221,7 +221,8 @@ def length(x): from sage.graphs.digraph import DiGraph return DiGraph(visited, name="Parabolic Quantum Bruhat Graph of %s for nodes %s"%(self, index_set), - format="dict_of_dicts") + format="dict_of_dicts", + data_structure="static_sparse") class ElementMethods: diff --git a/src/sage/coding/all.py b/src/sage/coding/all.py index 78e3e7b0d4d..7a4f825f9aa 100644 --- a/src/sage/coding/all.py +++ b/src/sage/coding/all.py @@ -5,34 +5,6 @@ _lazy_import("sage.coding.code_constructions", ["permutation_action", "walsh_matrix"]) -from sage.misc.superseded import \ - deprecated_callable_import as _deprecated_callable_import, \ - deprecated_function_alias as _deprecated_function_alias - -_deprecated_callable_import(19315, - "sage.coding.code_bounds", - globals(), - locals(), - ["codesize_upper_bound", - "dimension_upper_bound", - "volume_hamming", - "gilbert_lower_bound", - "plotkin_upper_bound", - "griesmer_upper_bound", - "elias_upper_bound", - "hamming_upper_bound", - "singleton_upper_bound", - "gv_info_rate", - "entropy", - "gv_bound_asymp", - "hamming_bound_asymp", - "singleton_bound_asymp", - "plotkin_bound_asymp", - "elias_bound_asymp", - "mrrw1_bound_asymp"], - ("This method soon will not be available in that way." - "Please call codes.bounds.%(name)s instead")) - _lazy_import("sage.coding.linear_code", [ "LinearCode", "LinearCodeFromVectorSpace", @@ -55,16 +27,10 @@ _lazy_import('sage.coding.delsarte_bounds','krawtchouk', "Kravchuk", deprecation=(20908, "Kravchuk will be removed from the global namespace. Please use codes.bounds.krawtchouk instead.")) -_deprecated_callable_import(20908, - "sage.coding.delsarte_bounds", - globals(), - locals(), - ["delsarte_bound_hamming_space", - "delsarte_bound_additive_hamming_space"], - ("This function will soon be removed from the global namespace. " - "Please call it using codes.bounds.%(name)s instead")) - - +_lazy_import('sage.coding.delsarte_bounds', + ["delsarte_bound_hamming_space", "delsarte_bound_additive_hamming_space"], + deprecation=(20908, "This function will soon be removed from the global namespace. " + "Please call it using codes.bounds.... instead")) _lazy_import('sage.coding', 'codes_catalog', 'codes') _lazy_import('sage.coding', 'channels_catalog', 'channels') diff --git a/src/sage/coding/bch.py b/src/sage/coding/bch.py index c17b2d8ef89..126d919d2db 100644 --- a/src/sage/coding/bch.py +++ b/src/sage/coding/bch.py @@ -146,7 +146,7 @@ def __init__(self, base_field, length, designed_distance, s = Zmod(length)(q).multiplicative_order() if gcd(jump_size, q ** s - 1) != 1: raise ValueError("jump_size must be coprime with the order of " - "the multicative group of the splitting field") + "the multiplicative group of the splitting field") D = [(offset + jump_size * i) % length for i in range(designed_distance - 1)] diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 9a204dfc726..cd35de932a7 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -3812,7 +3812,7 @@ cdef class BinaryCodeClassifier: sage: from sage.coding.binary_code import * sage: BC = BinaryCodeClassifier() sage: B = BinaryCode(codes.GolayCode(GF(2)).generator_matrix()) - sage: B.apply_permutation(range(24,-1,-1)) + sage: B.apply_permutation(list(range(24,-1,-1))) sage: B Binary [24,12] linear code, generator matrix [011000111010100000000000] diff --git a/src/sage/coding/channels_catalog.py b/src/sage/coding/channels_catalog.py index e2fa3d82b4e..1ee638e949c 100644 --- a/src/sage/coding/channels_catalog.py +++ b/src/sage/coding/channels_catalog.py @@ -28,3 +28,5 @@ _lazy_import('sage.coding.channel_constructions', ['ErrorErasureChannel', 'QarySymmetricChannel', 'StaticErrorRateChannel']) +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/coding/code_bounds.py b/src/sage/coding/code_bounds.py index 9fa04f3c52f..5d81f6e5057 100644 --- a/src/sage/coding/code_bounds.py +++ b/src/sage/coding/code_bounds.py @@ -18,8 +18,10 @@ - Dima Pasechnik (2012-10): added LP bounds. -Let `F` be a finite field (we denote the finite field with `q` elements by -`\GF{q}`). A subset `C` of `V=F^n` is called a code of length `n`. A subspace +Let `F` be a finite set of size `q`. +A subset `C` of `V=F^n` is called a code of length `n`. +Often one considers the case where `F` is a finite field, +denoted by `\GF{q}`. Then `V` is an `F`-vector space. A subspace of `V` (with the standard basis) is called a linear code of length `n`. If its dimension is denoted `k` then we typically store a basis of `C` as a `k\times n` matrix (the rows are the basis vectors). If `F=\GF{2}` then `C` is called a @@ -33,7 +35,7 @@ where `\vert C\vert` denotes the number of elements of `C`. If `{\bf -v}=(v_1,v_2,...,v_n)`, `{\bf w}=(w_1,w_2,...,w_n)` are vectors in `V=F^n` then +v}=(v_1,v_2,...,v_n)`, `{\bf w}=(w_1,w_2,...,w_n)` are elements of `V=F^n` then we define @@ -43,8 +45,11 @@ to be the Hamming distance between `{\bf v}` and `{\bf w}`. The function -`d:V\times V\rightarrow \Bold{N}` is called the Hamming metric. The weight of a -vector (in the Hamming metric) is `d({\bf v},{\bf 0})`. The minimum distance of +`d:V\times V\rightarrow \Bold{N}` is called the Hamming metric. The weight of +an element (in the Hamming metric) is `d({\bf v},{\bf 0})`, +where `0` is a distinguished element of `F`; +in particular it is `0` of the field if `F` is a field. +The minimum distance of a linear code is the smallest non-zero weight of a codeword in `C`. The relatively minimum distance is denoted @@ -59,8 +64,8 @@ distance `d` is called an `(n,M,d)_q`-code (using parentheses instead of square brackets). Of course, `k=\log_q(M)` for linear codes. -What is the "best" code of a given length? Let `F` be a finite field with `q` -elements. Let `A_q(n,d)` denote the largest `M` such that there exists a +What is the "best" code of a given length? +Let `A_q(n,d)` denote the largest `M` such that there exists a `(n,M,d)` code in `F^n`. Let `B_q(n,d)` (also denoted `A^{lin}_q(n,d)`) denote the largest `k` such that there exists a `[n,k,d]` code in `F^n`. (Of course, `A_q(n,d)\geq B_q(n,d)`.) Determining `A_q(n,d)` and `B_q(n,d)` is one of the @@ -171,17 +176,58 @@ from sage.interfaces.all import gap from sage.rings.all import QQ, RR, ZZ, RDF +from sage.arith.misc import is_prime_power from sage.arith.all import factorial from sage.functions.all import log, sqrt -from sage.misc.decorators import rename_keyword from .delsarte_bounds import delsarte_bound_hamming_space, \ delsarte_bound_additive_hamming_space -@rename_keyword(deprecation=6094, method="algorithm") +def _check_n_q_d(n, q, d, field_based=True): + r""" + Check that the length `n`, alphabet size `q` and minimum distance `d` type + check and make sense for a code over a field. + + More precisely, checks that the parameters are positive integers, that `q` + is a prime power for codes over a field, or, more generally, that + `q` is of size at least 2, and that `n >= d`. Raises a ``ValueError`` + otherwise. + + TESTS:: + + sage: from sage.coding.code_bounds import _check_n_q_d + sage: _check_n_q_d(20, 16, 5) + True + sage: _check_n_q_d(20, 16, 6, field_based=False) + True + sage: _check_n_q_d(20, 21, 16) + Traceback (most recent call last): + ... + ValueError: The alphabet size does not make sense for a code over a field + sage: _check_n_q_d(20, -21, 16) + Traceback (most recent call last): + ... + ValueError: The alphabet size must be an integer >1 + sage: _check_n_q_d(20, 2, 26) + Traceback (most recent call last): + ... + ValueError: The length or minimum distance does not make sense + """ + if (q not in ZZ) or (q<2): + raise ValueError("The alphabet size must be an integer >1") + if field_based==True and (not is_prime_power(q)): + raise ValueError("The alphabet size does not make sense for a code over a field") + if not( d > 0 and n >= d and n in ZZ and d in ZZ ): + raise ValueError("The length or minimum distance does not make sense") + return True + + def codesize_upper_bound(n,d,q,algorithm=None): r""" - This computes the minimum value of the upper bound using the - methods of Singleton, Hamming, Plotkin, and Elias. + Returns an upper bound on the number of codewords in a (possibly non-linear) + code. + + This function computes the minimum value of the upper bounds of Singleton, + Hamming, Plotkin, and Elias. If algorithm="gap" then this returns the best known upper bound `A(n,d)=A_q(n,d)` for the size of a code of length n, @@ -213,7 +259,23 @@ def codesize_upper_bound(n,d,q,algorithm=None): sage: codes.bounds.codesize_upper_bound(11,3,4,algorithm="LP") 109226 + TESTS: + + Make sure :trac:`22961` is fixed:: + + sage: codes.bounds.codesize_upper_bound(19,10,2) + 20 + sage: codes.bounds.codesize_upper_bound(19,10,2,algorithm="gap") # optional - gap_packages (Guava package) + 20 + + Meaningless parameters are rejected:: + + sage: codes.bounds.codesize_upper_bound(10, -20, 6) + Traceback (most recent call last): + ... + ValueError: The length or minimum distance does not make sense """ + _check_n_q_d(n, q, d, field_based=False) if algorithm=="gap": gap.load_package('guava') return int(gap.eval("UpperBound(%s,%s,%s)"%( n, d, q ))) @@ -221,15 +283,15 @@ def codesize_upper_bound(n,d,q,algorithm=None): return int(delsarte_bound_hamming_space(n,d,q)) else: eub = elias_upper_bound(n,q,d) - gub = griesmer_upper_bound(n,q,d) hub = hamming_upper_bound(n,q,d) pub = plotkin_upper_bound(n,q,d) sub = singleton_upper_bound(n,q,d) - return min([eub,gub,hub,pub,sub]) + return min([eub,hub,pub,sub]) -@rename_keyword(deprecation=6094, method="algorithm") def dimension_upper_bound(n,d,q,algorithm=None): r""" + Returns an upper bound for the dimension of a linear code. + Returns an upper bound `B(n,d) = B_q(n,d)` for the dimension of a linear code of length n, minimum distance d over a field of size q. @@ -244,19 +306,29 @@ def dimension_upper_bound(n,d,q,algorithm=None): sage: codes.bounds.dimension_upper_bound(30,15,4,algorithm="LP") 12 + TESTS: + + Meaningless code parameters are rejected:: + + sage: codes.bounds.dimension_upper_bound(13,3,6) + Traceback (most recent call last): + ... + ValueError: The alphabet size does not make sense for a code over a field """ + _check_n_q_d(n, q, d) q = ZZ(q) if algorithm=="LP": return delsarte_bound_additive_hamming_space(n,d,q) - else: # algorithm==None or algorithm=="gap": return int(log(codesize_upper_bound(n,d,q,algorithm=algorithm),q)) def volume_hamming(n,q,r): r""" - Returns number of elements in a Hamming ball of radius r in `\GF{q}^n`. - Agrees with Guava's SphereContent(n,r,GF(q)). + Returns the number of elements in a Hamming ball. + + Returns the number of elements in a Hamming ball of radius `r` in + `\GF{q}^n`. EXAMPLES:: @@ -268,24 +340,30 @@ def volume_hamming(n,q,r): def gilbert_lower_bound(n,q,d): r""" - Returns lower bound for number of elements in the largest code of - minimum distance d in `\GF{q}^n`. + Returns the Gilbert-Varshamov lower bound. + + Returns the Gilbert-Varshamov lower bound for number of elements in a largest code of + minimum distance d in `\GF{q}^n`. See :wikipedia:`Gilbert-Varshamov_bound` EXAMPLES:: sage: codes.bounds.gilbert_lower_bound(10,2,3) 128/7 """ + _check_n_q_d(n, q, d, field_based=False) ans=q**n/volume_hamming(n,q,d-1) return ans -@rename_keyword(deprecation=6094, method="algorithm") def plotkin_upper_bound(n,q,d, algorithm=None): r""" - Returns Plotkin upper bound for number of elements in the largest - code of minimum distance d in `\GF{q}^n`. + Returns the Plotkin upper bound. - The algorithm="gap" option wraps Guava's UpperBoundPlotkin. + Returns the Plotkin upper bound for the number of elements in a largest + code of minimum distance `d` in `\GF{q}^n`. + More precisely this is a generalization of Plotkin's result for `q=2` + to bigger `q` due to Berlekamp. + + The ``algorithm="gap"`` option wraps Guava's ``UpperBoundPlotkin``. EXAMPLES:: @@ -294,6 +372,7 @@ def plotkin_upper_bound(n,q,d, algorithm=None): sage: codes.bounds.plotkin_upper_bound(10,2,3,algorithm="gap") # optional - gap_packages (Guava package) 192 """ + _check_n_q_d(n, q, d, field_based=False) if algorithm=="gap": gap.load_package("guava") ans=gap.eval("UpperBoundPlotkin(%s,%s,%s)"%(n,d,q)) @@ -312,47 +391,70 @@ def plotkin_upper_bound(n,q,d, algorithm=None): fact = int(fact) + 1 return int(d/( d - t * fact)) * q**(n - fact) -@rename_keyword(deprecation=6094, method="algorithm") def griesmer_upper_bound(n,q,d,algorithm=None): r""" - Returns the Griesmer upper bound for number of elements in the - largest code of minimum distance d in `\GF{q}^n`. - Wraps GAP's UpperBoundGriesmer. + Returns the Griesmer upper bound. - EXAMPLES:: + Returns the Griesmer upper bound for the number of elements in a + largest linear code of minimum distance `d` in `\GF{q}^n`, cf. [HP2003]_. + If the method is "gap", it wraps GAP's ``UpperBoundGriesmer``. + + The bound states: + + .. MATH:: + + `n\geq \sum_{i=0}^{k-1} \lceil d/q^i \rceil.` + + + EXAMPLES: + + The bound is reached for the ternary Golay codes:: + + sage: codes.bounds.griesmer_upper_bound(12,3,6) + 729 + sage: codes.bounds.griesmer_upper_bound(11,3,5) + 729 + + :: sage: codes.bounds.griesmer_upper_bound(10,2,3) 128 sage: codes.bounds.griesmer_upper_bound(10,2,3,algorithm="gap") # optional - gap_packages (Guava package) 128 + + TESTS:: + + sage: codes.bounds.griesmer_upper_bound(11,3,6) + 243 + sage: codes.bounds.griesmer_upper_bound(11,3,6) + 243 """ + _check_n_q_d(n, q, d) if algorithm=="gap": gap.load_package("guava") ans=gap.eval("UpperBoundGriesmer(%s,%s,%s)"%(n,d,q)) return QQ(ans) else: + #To compute the bound, we keep summing up the terms on the RHS + #until we start violating the inequality. + from sage.functions.other import ceil den = 1 s = 0 k = 0 - add = 0 while s <= n: - if not(add == 1): - if d%den==0: - add = int(d/den) - else: - add = int(d/den)+1 - s = s + add - den = den * q + s += ceil(d/den) + den *= q k = k + 1 return q**(k-1) -@rename_keyword(deprecation=6094, method="algorithm") def elias_upper_bound(n,q,d,algorithm=None): r""" + Returns the Elias upper bound. + Returns the Elias upper bound for number of elements in the largest - code of minimum distance d in `\GF{q}^n`. Wraps - GAP's UpperBoundElias. + code of minimum distance `d` in `\GF{q}^n`, cf. [HP2003]_. + If the method is "gap", it wraps GAP's ``UpperBoundElias``. EXAMPLES:: @@ -360,8 +462,8 @@ def elias_upper_bound(n,q,d,algorithm=None): 232 sage: codes.bounds.elias_upper_bound(10,2,3,algorithm="gap") # optional - gap_packages (Guava package) 232 - """ + _check_n_q_d(n, q, d, field_based=False) r = 1-1/q if algorithm=="gap": gap.load_package("guava") @@ -382,16 +484,18 @@ def get_list(n,d,q): def hamming_upper_bound(n,q,d): r""" + Returns the Hamming upper bound. + Returns the Hamming upper bound for number of elements in the - largest code of minimum distance d in `\GF{q}^n`. - Wraps GAP's UpperBoundHamming. + largest code of length n and minimum distance d over alphabet + of size q. The Hamming bound (also known as the sphere packing bound) returns - an upper bound on the size of a code of length n, minimum distance - d, over a field of size q. The Hamming bound is obtained by - dividing the contents of the entire space - `\GF{q}^n` by the contents of a ball with radius - floor((d-1)/2). As all these balls are disjoint, they can never + an upper bound on the size of a code of length `n`, minimum distance + `d`, over an alphabet of size `q`. The Hamming bound is obtained by + dividing the contents of the entire Hamming space + `q^n` by the contents of a ball with radius + `floor((d-1)/2)`. As all these balls are disjoint, they can never contain more than the whole vector space. @@ -401,26 +505,28 @@ def hamming_upper_bound(n,q,d): - where M is the maximum number of codewords and `V(n,e)` is + where `M` is the maximum number of codewords and `V(n,e)` is equal to the contents of a ball of radius e. This bound is useful - for small values of d. Codes for which equality holds are called - perfect. + for small values of `d`. Codes for which equality holds are called + perfect. See e.g. [HP2003]_. EXAMPLES:: sage: codes.bounds.hamming_upper_bound(10,2,3) 93 """ + _check_n_q_d(n, q, d, field_based=False) return int((q**n)/(volume_hamming(n, q, int((d-1)/2)))) def singleton_upper_bound(n,q,d): r""" - Returns the Singleton upper bound for number of elements in the + Returns the Singleton upper bound. + + Returns the Singleton upper bound for number of elements in a largest code of minimum distance d in `\GF{q}^n`. - Wraps GAP's UpperBoundSingleton. This bound is based on the shortening of codes. By shortening an - `(n, M, d)` code d-1 times, an `(n-d+1,M,1)` code + `(n, M, d)` code `d-1` times, an `(n-d+1,M,1)` code results, with `M \leq q^n-d+1`. Thus @@ -429,7 +535,6 @@ def singleton_upper_bound(n,q,d): M \leq q^{n-d+1}. - Codes that meet this bound are called maximum distance separable (MDS). @@ -438,12 +543,15 @@ def singleton_upper_bound(n,q,d): sage: codes.bounds.singleton_upper_bound(10,2,3) 256 """ + _check_n_q_d(n, q, d, field_based=False) return q**(n - d + 1) def gv_info_rate(n,delta,q): """ - GV lower bound for information rate of a q-ary code of length n - minimum distance delta\*n + The Gilbert-Varshamov lower bound for information rate. + + The Gilbert-Varshamov lower bound for information rate of a `q`-ary code of + length `n` and minimum distance `n\delta`. EXAMPLES:: @@ -550,7 +658,7 @@ def entropy_inverse(x, q=2): def gv_bound_asymp(delta,q): """ - Computes the asymptotic GV bound for the information rate, R. + The asymptotic Gilbert-Varshamov bound for the information rate, R. EXAMPLES:: @@ -565,7 +673,7 @@ def gv_bound_asymp(delta,q): def hamming_bound_asymp(delta,q): """ - Computes the asymptotic Hamming bound for the information rate. + The asymptotic Hamming bound for the information rate. EXAMPLES:: @@ -579,7 +687,7 @@ def hamming_bound_asymp(delta,q): def singleton_bound_asymp(delta,q): """ - Computes the asymptotic Singleton bound for the information rate. + The asymptotic Singleton bound for the information rate. EXAMPLES:: @@ -593,8 +701,9 @@ def singleton_bound_asymp(delta,q): def plotkin_bound_asymp(delta,q): """ - Computes the asymptotic Plotkin bound for the information rate, - provided `0 < \delta < 1-1/q`. + The asymptotic Plotkin bound for the information rate. + + This only makes sense when `0 < \delta < 1-1/q`. EXAMPLES:: @@ -606,8 +715,9 @@ def plotkin_bound_asymp(delta,q): def elias_bound_asymp(delta,q): """ - Computes the asymptotic Elias bound for the information rate, - provided `0 < \delta < 1-1/q`. + The asymptotic Elias bound for the information rate. + + This only makes sense when `0 < \delta < 1-1/q`. EXAMPLES:: @@ -619,8 +729,9 @@ def elias_bound_asymp(delta,q): def mrrw1_bound_asymp(delta,q): """ - Computes the first asymptotic McEliese-Rumsey-Rodemich-Welsh bound - for the information rate, provided `0 < \delta < 1-1/q`. + The first asymptotic McEliese-Rumsey-Rodemich-Welsh bound. + + This only makes sense when `0 < \delta < 1-1/q`. EXAMPLES:: diff --git a/src/sage/coding/codes_catalog.py b/src/sage/coding/codes_catalog.py index 0f2c006ee8e..6a1bd4bdac4 100644 --- a/src/sage/coding/codes_catalog.py +++ b/src/sage/coding/codes_catalog.py @@ -11,6 +11,11 @@ sage: from sage.coding.codes_catalog import * +TESTS:: + + sage: import sage.coding.codes_catalog + sage: 'absolute_import' in dir(sage.coding.codes_catalog) + False """ #***************************************************************************** # Copyright (C) 2009 David Lucas @@ -28,12 +33,12 @@ # in the global namespace. from __future__ import absolute_import -from sage.misc.lazy_import import lazy_import as _lazy_import +from sage.misc.lazy_import import lazy_import from .linear_code import LinearCode from sage.coding.linear_code import LinearCode -_lazy_import('sage.coding.code_constructions', +lazy_import('sage.coding.code_constructions', ['BCHCode', 'BinaryGolayCode', 'CyclicCodeFromGeneratingPolynomial', 'CyclicCode', 'CyclicCodeFromCheckPolynomial', 'DuadicCodeEvenPair', 'DuadicCodeOddPair', 'ExtendedBinaryGolayCode', @@ -47,27 +52,29 @@ 'ReedSolomonCode', 'TernaryGolayCode', 'ToricCode', 'WalshCode']) -_lazy_import('sage.coding.bch', 'BCHCode') -_lazy_import('sage.coding.cyclic_code', 'CyclicCode') -_lazy_import('sage.coding.extended_code', 'ExtendedCode') -_lazy_import('sage.coding.golay_code', 'GolayCode') -_lazy_import('sage.coding.grs', 'GeneralizedReedSolomonCode') -_lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', +lazy_import('sage.coding.bch', 'BCHCode') +lazy_import('sage.coding.cyclic_code', 'CyclicCode') +lazy_import('sage.coding.extended_code', 'ExtendedCode') +lazy_import('sage.coding.golay_code', 'GolayCode') +lazy_import('sage.coding.grs', 'GeneralizedReedSolomonCode') +lazy_import('sage.coding.guava', ['QuasiQuadraticResidueCode', 'RandomLinearCodeGuava']) -_lazy_import('sage.coding.hamming_code', 'HammingCode') -_lazy_import('sage.coding.parity_check_code', 'ParityCheckCode') -_lazy_import('sage.coding.punctured_code', 'PuncturedCode') -_lazy_import('sage.coding.reed_muller_code', ['BinaryReedMullerCode', +lazy_import('sage.coding.hamming_code', 'HammingCode') +lazy_import('sage.coding.parity_check_code', 'ParityCheckCode') +lazy_import('sage.coding.punctured_code', 'PuncturedCode') +lazy_import('sage.coding.reed_muller_code', ['BinaryReedMullerCode', 'ReedMullerCode']) -_lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') +lazy_import('sage.coding.subfield_subcode', 'SubfieldSubcode') from . import decoders_catalog as decoders from . import encoders_catalog as encoders from . import bounds_catalog as bounds -_lazy_import('sage.coding','databases') +lazy_import('sage.coding','databases') + +from sage.misc.rest_index_of_methods import gen_rest_table_index +import sys +__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=gen_rest_table_index(sys.modules[__name__], only_local_functions=False)) -del _lazy_import -from sage.misc.rest_index_of_methods import gen_rest_table_index as _gen_rest_table_index -import sys as _sys -__doc__ = __doc__.format(INDEX_OF_FUNCTIONS=_gen_rest_table_index(_sys.modules[__name__], only_local_functions=False)) +# We don't want this to appear in tab completion +del absolute_import, lazy_import, sys, gen_rest_table_index diff --git a/src/sage/coding/databases.py b/src/sage/coding/databases.py index 21c12e922ec..b5700aca512 100644 --- a/src/sage/coding/databases.py +++ b/src/sage/coding/databases.py @@ -3,6 +3,8 @@ Databases and accessors of online databases for coding theory """ from six.moves import range +from sage.interfaces.all import gap +from sage.misc.package import is_package_installed, PackageNotFoundError #Don't put any global imports here since this module is accessible as sage.codes.databases. @@ -40,7 +42,8 @@ def best_linear_code_in_guava(n, k, F): between 2 and 4. Use ``bounds_on_minimum_distance_in_guava(10,5,GF(2))`` for further details. """ - from sage.interfaces.all import gap + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') gap.load_package("guava") q = F.order() C = gap("BestKnownLinearCode(%s,%s,GF(%s))"%(n,k,q)) @@ -107,7 +110,8 @@ def bounds_on_minimum_distance_in_guava(n, k, F): upperBound := 4, upperBoundExplanation := ... ) """ - from sage.interfaces.all import gap + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') gap.load_package("guava") q = F.order() gap.eval("data := BoundsMinimumDistance(%s,%s,GF(%s))"%(n,k,q)) diff --git a/src/sage/coding/guava.py b/src/sage/coding/guava.py index bf1ccd8de58..0d1a7265881 100644 --- a/src/sage/coding/guava.py +++ b/src/sage/coding/guava.py @@ -40,6 +40,7 @@ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.interfaces.gap import gfq_gap_to_sage from .linear_code import LinearCode +from sage.misc.package import is_package_installed, PackageNotFoundError def QuasiQuadraticResidueCode(p): @@ -70,6 +71,8 @@ def QuasiQuadraticResidueCode(p): AUTHOR: David Joyner (11-2005) """ + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') F = GF(2) gap.load_package("guava") gap.eval("C:=QQRCode(" + str(p) + ")") @@ -110,6 +113,8 @@ def RandomLinearCodeGuava(n, k, F): current_randstate().set_seed_gap() q = F.order() + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') gap.load_package("guava") gap.eval("C:=RandomLinearCode("+str(n)+","+str(k)+", GF("+str(q)+"))") gap.eval("G:=GeneratorMat(C)") diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index 4e24ceb6522..95ca3784fdd 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -228,6 +228,7 @@ class should inherit from this class. Also ``AbstractLinearCode`` should never from sage.misc.cachefunc import cached_method from sage.misc.superseded import deprecation, deprecated_function_alias from sage.misc.randstate import current_randstate +from sage.misc.package import is_package_installed, PackageNotFoundError from .encoder import Encoder from .decoder import Decoder, DecodingError from sage.combinat.subset import Subsets @@ -1279,15 +1280,33 @@ def covering_radius(self): If the covering radius a code equals its minimum distance, then the code is called perfect. + .. NOTE:: + + This method is currently not implemented on codes over base fields + of cardinality greater than 256 due to limitations in the underlying + algorithm of GAP. + EXAMPLES:: sage: C = codes.HammingCode(GF(2), 5) sage: C.covering_radius() # optional - gap_packages (Guava package) 1 + + sage: C = codes.random_linear_code(GF(263), 5, 1) + sage: C.covering_radius() # optional - gap_packages (Guava package) + Traceback (most recent call last): + ... + NotImplementedError: the GAP algorithm that Sage is using is limited to computing with fields of size at most 256 """ + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') + gap.load_package("guava") F = self.base_ring() - G = self.generator_matrix() - gapG = gap(G) + if F.cardinality() > 256: + raise NotImplementedError("the GAP algorithm that Sage is using " + "is limited to computing with fields " + "of size at most 256") + gapG = gap(self.generator_matrix()) C = gapG.GeneratorMatCode(gap(F)) r = C.CoveringRadius() try: @@ -2486,6 +2505,8 @@ def minimum_distance(self, algorithm=None): NotImplementedError: the GAP algorithm that Sage is using is limited to computing with fields of size at most 256 """ + if algorithm == "guava" and not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') # If the minimum distance has already been computed or provided by # the user then simply return the stored value. # This is done only if algorithm is None. @@ -2504,10 +2525,7 @@ def minimum_distance(self, algorithm=None): n = self.length() k = self.dimension() if (q == 2 or q == 3) and algorithm=="guava": - try: - gap.load_package("guava") - except RuntimeError: - raise ValueError("You have to install the optional package GUAVA to use algorithm=\"guava\"") + gap.load_package("guava") C = gap(G).GeneratorMatCode(gap(F)) d = C.MinimumWeight() return ZZ(d) @@ -2559,10 +2577,9 @@ def _minimum_weight_codeword(self, algorithm = None): current_randstate().set_seed_gap() if algorithm=="guava": - try: - gap.load_package("guava") - except RuntimeError: - raise ValueError("You have to install the optional package GUAVA to use algorithm=\"guava\"") + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') + gap.load_package("guava") from sage.interfaces.gap import gfq_gap_to_sage gap.eval("G:="+Gmat) C = gap(Gmat).GeneratorMatCode(F) @@ -2726,6 +2743,8 @@ def permutation_automorphism_group(self, algorithm="partition"): n = len(G.columns()) k = len(G.rows()) if "gap" in algorithm: + if not is_package_installed('gap_packages'): + raise PackageNotFoundError('gap_packages') gap.load_package('guava') wts = self.weight_distribution() # bottleneck 1 nonzerowts = [i for i in range(len(wts)) if wts[i]!=0] diff --git a/src/sage/coding/reed_muller_code.py b/src/sage/coding/reed_muller_code.py index 8345c823af8..1f934109278 100644 --- a/src/sage/coding/reed_muller_code.py +++ b/src/sage/coding/reed_muller_code.py @@ -24,6 +24,7 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from six.moves import range from operator import mul from sage.matrix.constructor import matrix @@ -657,7 +658,7 @@ def generator_matrix(self): matrix_list = [] max_individual_degree = min(order, (q - 1)) for degree in range(order + 1): - exponents = Subsets(range(num_of_var) * max_individual_degree, + exponents = Subsets(list(range(num_of_var)) * max_individual_degree, degree, submultiset=True) matrix_list += [[reduce(mul, [x[i] for i in exponent], 1) for x in points] for exponent in exponents] diff --git a/src/sage/combinat/binary_recurrence_sequences.py b/src/sage/combinat/binary_recurrence_sequences.py index d8068c2dc27..d944e615679 100644 --- a/src/sage/combinat/binary_recurrence_sequences.py +++ b/src/sage/combinat/binary_recurrence_sequences.py @@ -451,7 +451,7 @@ def period(self, m): p1fac = list((p-1).factor()) #The order of any matrix in GL_2(F_p) either divides p(p-1) or (p-1)(p+1). - #The order divides p-1 if it is diagaonalizable. In any case, det(F^(p-1))=1, + #The order divides p-1 if it is diagonalizable. In any case, det(F^(p-1))=1, #so if tr(F^(p-1)) = 2, then it must be triangular of the form [[1,a],[0,1]]. #The order of the subgroup of matrices of this form is p, so the order must divide #p(p-1) -- in fact it must be a multiple of p. If this is not the case, then the @@ -503,7 +503,7 @@ def period(self, m): n = b perp = n - #Now compute the period mod p^e by steping up by multiples of p + #Now compute the period mod p^e by stepping up by multiples of p F = A.change_ring(Integers(p**e)) v = w.change_ring(Integers(p**e)) FF = F**perp diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index 75f73dda864..5a10fde2bfb 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -410,11 +410,12 @@ def bell_number(n, algorithm='flint', **options): raise ValueError("unknown algorithm %r" % algorithm) + def catalan_number(n): r""" Return the `n`-th Catalan number. - Catalan numbers: The `n`-th Catalan number is given + The `n`-th Catalan number is given directly in terms of binomial coefficients by .. MATH:: @@ -422,26 +423,23 @@ def catalan_number(n): C_n = \frac{1}{n+1}\binom{2n}{n} = \frac{(2n)!}{(n+1)!\,n!} \qquad\mbox{ for }\quad n\ge 0. - - Consider the set `S = \{ 1, ..., n \}`. A noncrossing partition of `S` is a partition in which no two blocks "cross" each other, i.e., if `a` and `b` belong to one block and `x` and `y` to another, they are not arranged in the order `axby`. `C_n` is the number of noncrossing partitions of the set - `S`. There are many other interpretations (see - REFERENCES). + `S`. There are many other interpretations (see REFERENCES). - When `n=-1`, this function raises a ZeroDivisionError; for + When `n=-1`, this function returns the limit value `-1/2`. For other `n<0` it returns `0`. INPUT: - - ``n`` - integer - - OUTPUT: integer + - ``n`` -- integer + OUTPUT: + integer EXAMPLES:: @@ -449,23 +447,24 @@ def catalan_number(n): [1, 1, 2, 5, 14, 42, 132] sage: taylor((-1/2)*sqrt(1 - 4*x^2), x, 0, 15) 132*x^14 + 42*x^12 + 14*x^10 + 5*x^8 + 2*x^6 + x^4 + x^2 - 1/2 - sage: [catalan_number(i) for i in range(-7,7) if i != -1] - [0, 0, 0, 0, 0, 0, 1, 1, 2, 5, 14, 42, 132] - sage: catalan_number(-1) - Traceback (most recent call last): - ... - ZeroDivisionError + sage: [catalan_number(i) for i in range(-7,7)] + [0, 0, 0, 0, 0, 0, -1/2, 1, 1, 2, 5, 14, 42, 132] sage: [catalan_number(n).mod(2) for n in range(16)] [1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1] REFERENCES: - - http://en.wikipedia.org/wiki/Catalan_number + - :wikipedia:`Catalan_number` - http://www-history.mcs.st-andrews.ac.uk/~history/Miscellaneous/CatalanNumbers/catalan.html """ n = ZZ(n) - return binomial(2*n,n).divide_knowing_divisible_by(n+1) + if n < -1: + return ZZ.zero() + if n == -1: + return QQ((-1, 2)) + return binomial(2 * n, n).divide_knowing_divisible_by(n + 1) + def euler_number(n, algorithm='flint'): """ @@ -626,7 +625,9 @@ def lucas_number1(n, P, Q): Can you use Sage to find a counterexample to the conjecture? """ - n = ZZ(n); P = QQ(P); Q = QQ(Q) + n = ZZ(n) + P = QQ(P) + Q = QQ(Q) from sage.libs.gap.libgap import libgap return libgap.Lucas(P, Q, n)[0].sage() @@ -674,7 +675,9 @@ def lucas_number2(n, P, Q): sage: [lucas_number2(n,1,-1) for n in range(10)] [2, 1, 3, 4, 7, 11, 18, 29, 47, 76] """ - n = ZZ(n); P = QQ(P); Q = QQ(Q) + n = ZZ(n) + P = QQ(P) + Q = QQ(Q) from sage.libs.gap.libgap import libgap return libgap.Lucas(P, Q, n)[1].sage() @@ -915,31 +918,31 @@ def __str__(self): """ return str(self._list) - def __cmp__(self, other): + def _repr_(self): """ EXAMPLES:: sage: c = CombinatorialObject([1,2,3]) - sage: d = CombinatorialObject([3,2,1]) - sage: cmp(c, d) - -1 - sage: cmp(d, c) - 1 - sage: cmp(c, c) - 0 + sage: c.__repr__() + '[1, 2, 3]' + """ + return repr(self._list) - Check that :trac:`14065` is fixed:: + def __eq__(self, other): + """ + Test equality of self and other. - sage: from sage.structure.element import Element - sage: class Foo(CombinatorialObject, Element): pass - sage: L = [Foo([4-i]) for i in range(4)]; L - [[4], [3], [2], [1]] - sage: sorted(L) - [[1], [2], [3], [4]] - sage: f = Foo([4]) - sage: f is None + EXAMPLES:: + + sage: c = CombinatorialObject([1,2,3]) + sage: d = CombinatorialObject([2,3,4]) + sage: c == [1,2,3] + True + sage: c == [2,3,4] False - sage: f is not None + sage: c == d + False + sage: c == c True .. WARNING:: @@ -959,34 +962,6 @@ def __cmp__(self, other): ... NotImplementedError: comparison not implemented for """ - if isinstance(other, CombinatorialObject): - return cmp(self._list, other._list) - else: - return cmp(self._list, other) - - def _repr_(self): - """ - EXAMPLES:: - - sage: c = CombinatorialObject([1,2,3]) - sage: c.__repr__() - '[1, 2, 3]' - """ - return repr(self._list) - - def __eq__(self, other): - """ - EXAMPLES:: - - sage: c = CombinatorialObject([1,2,3]) - sage: d = CombinatorialObject([2,3,4]) - sage: c == [1,2,3] - True - sage: c == [2,3,4] - False - sage: c == d - False - """ if isinstance(other, CombinatorialObject): return self._list == other._list else: @@ -1002,6 +977,22 @@ def __lt__(self, other): True sage: c < [2,3,4] True + sage: c < c + False + + Check that :trac:`14065` is fixed:: + + sage: from sage.structure.element import Element + sage: class Foo(CombinatorialObject, Element): pass + sage: L = [Foo([4-i]) for i in range(4)]; L + [[4], [3], [2], [1]] + sage: sorted(L) + [[1], [2], [3], [4]] + sage: f = Foo([4]) + sage: f is None + False + sage: f is not None + True """ if isinstance(other, CombinatorialObject): return self._list < other._list @@ -1441,10 +1432,11 @@ def __contains__(self, x): """ raise NotImplementedError - def __cmp__(self, x): + def __eq__(self, other): """ - Compares two different combinatorial classes. For now, the - comparison is done just on their repr's. + Compare two different combinatorial classes. + + For now, the comparison is done just on their repr's. EXAMPLES:: @@ -1455,7 +1447,20 @@ def __cmp__(self, x): sage: p5 == p6 False """ - return cmp(repr(self), repr(x)) + return repr(self) == repr(other) + + def __ne__(self, other): + """ + Test unequality of self and other. + + EXAMPLES:: + + sage: p5 = Partitions(5) + sage: p6 = Partitions(6) + sage: p5 != p6 + True + """ + return not (self == other) def __cardinality_from_iterator(self): """ diff --git a/src/sage/combinat/combinat_cython.pyx b/src/sage/combinat/combinat_cython.pyx index f4c3ef65c1e..0d87fabcc04 100644 --- a/src/sage/combinat/combinat_cython.pyx +++ b/src/sage/combinat/combinat_cython.pyx @@ -9,8 +9,7 @@ AUTHORS: """ -include "sage/ext/stdsage.pxi" - +from cysignals.memory cimport check_allocarray, sig_free from sage.libs.gmp.all cimport * from sage.rings.integer cimport Integer @@ -93,7 +92,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 = sig_malloc((max_bc+1) * sizeof(mpz_t)) + bc = check_allocarray(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]) @@ -131,7 +130,6 @@ def _stirling_number2(n, k): This is wrapped again by stirling_number2 in combinat.py. """ - cdef Integer s - s = PY_NEW(Integer) + cdef Integer s = Integer.__new__(Integer) mpz_stirling_s2(s.value, n, k) return s diff --git a/src/sage/combinat/crystals/affine.py b/src/sage/combinat/crystals/affine.py index a90ceffd1c1..286784f5174 100644 --- a/src/sage/combinat/crystals/affine.py +++ b/src/sage/combinat/crystals/affine.py @@ -13,8 +13,7 @@ #**************************************************************************** from sage.misc.abstract_method import abstract_method -from sage.categories.regular_crystals import RegularCrystals -from sage.categories.finite_crystals import FiniteCrystals +from sage.categories.loop_crystals import RegularLoopCrystals from sage.structure.element import parent from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation @@ -82,7 +81,7 @@ def __classcall__(cls, cartan_type, *args, **options): ct = CartanType(cartan_type) return super(AffineCrystalFromClassical, cls).__classcall__(cls, ct, *args, **options) - def __init__(self, cartan_type, classical_crystal, category = None): + def __init__(self, cartan_type, classical_crystal, category=None): """ Input is an affine Cartan type ``cartan_type``, a classical crystal ``classical_crystal``, and automorphism and its inverse @@ -113,7 +112,7 @@ def __init__(self, cartan_type, classical_crystal, category = None): sage: TestSuite(A).run() """ if category is None: - category = (RegularCrystals(), FiniteCrystals()) + category = RegularLoopCrystals() self._cartan_type = cartan_type Parent.__init__(self, category = category) self.classical_crystal = classical_crystal; @@ -133,6 +132,20 @@ def _repr_(self): """ return "An affine crystal for type {}".format(self.cartan_type()) + def cardinality(self): + """ + Return the cardinality of ``self``. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3],shape=[1]) + sage: pr = attrcall("promotion") + sage: pr_inverse = attrcall("promotion_inverse") + sage: A = crystals.AffineFromClassicalAndPromotion(['A',3,1],C,pr,pr_inverse,1) + sage: A.cardinality() == C.cardinality() + True + """ + return self.classical_crystal.cardinality() def __iter__(self): r""" @@ -566,7 +579,7 @@ class AffineCrystalFromClassicalAndPromotion(AffineCrystalFromClassical): [[[2]], [[1]], [[3]]] """ - def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_automorphism, dynkin_node): + def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_automorphism, dynkin_node, category=None): """ Input is an affine Cartan type ``cartan_type``, a classical crystal ``classical_crystal``, and promotion automorphism and its inverse @@ -591,7 +604,7 @@ def __init__(self, cartan_type, classical_crystal, p_automorphism, p_inverse_aut sage: TestSuite(A).run() """ - AffineCrystalFromClassical.__init__(self, cartan_type, classical_crystal) + AffineCrystalFromClassical.__init__(self, cartan_type, classical_crystal, category) self.p_automorphism = p_automorphism self.p_inverse_automorphism = p_inverse_automorphism self.dynkin_node = dynkin_node diff --git a/src/sage/combinat/crystals/affine_factorization.py b/src/sage/combinat/crystals/affine_factorization.py index b486f14042f..274dd309ff4 100644 --- a/src/sage/combinat/crystals/affine_factorization.py +++ b/src/sage/combinat/crystals/affine_factorization.py @@ -373,7 +373,7 @@ def affine_factorizations(w, l, weight=None): - ``w`` -- an (affine) permutation or element of the (affine) Weyl group - - ``l`` -- nonegative integer + - ``l`` -- nonnegative integer - ``weight`` -- (default: None) tuple of nonnegative integers specifying the length of the factors diff --git a/src/sage/combinat/crystals/affinization.py b/src/sage/combinat/crystals/affinization.py index 8cced3900d1..e1dbce27e3b 100644 --- a/src/sage/combinat/crystals/affinization.py +++ b/src/sage/combinat/crystals/affinization.py @@ -92,8 +92,8 @@ def __init__(self, B): self._B = B self._cartan_type = B.cartan_type() Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) - mg_elt = lambda b: self.element_class(self, b, 0) - self.module_generators = tuple(map(mg_elt, B.module_generators)) + self.module_generators = tuple([self.element_class(self, b, 0) + for b in B.module_generators]) def _repr_(self): """ @@ -106,37 +106,6 @@ def _repr_(self): """ return "Affinization of {}".format(self._B) - def weight_lattice_realization(self): - """ - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: A = crystals.KirillovReshetikhin(['A',2,1], 1, 1).affinization() - sage: A.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 2, 1] - """ - return self.cartan_type().root_system().weight_lattice(extended=True) - - # TODO: This should become unnecessary once we have a proper category for KR crystals - def digraph(self, subset=None, index_set=None): - """ - Return the DiGraph associated with ``self``. See - :meth:`~sage.categories.crystals.ParentMethods.digraph()` for more - information. - - EXAMPLES:: - - sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() - sage: S = A.subcrystal(max_depth=3) - sage: G = A.digraph(subset=S) - """ - G = super(AffinizationOfCrystal, self).digraph(subset, index_set) - from sage.graphs.dot2tex_utils import have_dot2tex - if have_dot2tex(): - G.set_latex_options(edge_options=lambda u_v_label: ({})) - return G - class Element(Element): """ An element in an affinization crystal. @@ -195,9 +164,8 @@ def __hash__(self): sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: mg = A.module_generators[0] - sage: hash(mg) - -6948036233304877976 # 64-bit - -1420700568 # 32-bit + sage: hash(mg) == hash(mg._b) ^^ hash(mg._m) + True """ return hash(self._b) ^ hash(self._m) diff --git a/src/sage/combinat/crystals/alcove_path.py b/src/sage/combinat/crystals/alcove_path.py index edbb4b99d06..3a766a82c72 100644 --- a/src/sage/combinat/crystals/alcove_path.py +++ b/src/sage/combinat/crystals/alcove_path.py @@ -30,6 +30,7 @@ from sage.structure.sage_object import richcmp from sage.categories.finite_crystals import FiniteCrystals from sage.categories.classical_crystals import ClassicalCrystals +from sage.categories.loop_crystals import LoopCrystals from sage.graphs.all import DiGraph from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem @@ -329,11 +330,11 @@ def __init__(self, starting_weight, highest_weight_crystal): if cartan_type.is_finite() and highest_weight_crystal: - Parent.__init__(self, category=ClassicalCrystals() ) + Parent.__init__(self, category=ClassicalCrystals()) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True elif cartan_type.is_finite() and not highest_weight_crystal: - Parent.__init__(self, category=FiniteCrystals() ) + Parent.__init__(self, category=LoopCrystals().Finite()) self._R = RootsWithHeight(starting_weight) self._finite_cartan_type = True self._cartan_type = cartan_type.affine() @@ -476,31 +477,6 @@ def digraph_fast(self, depth=None): return super(CrystalOfAlcovePaths, self).digraph() return super(CrystalOfAlcovePaths, self).digraph(depth=depth) - def weight_lattice_realization(self): - r""" - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: B = crystals.AlcovePaths(['A',2,1],[1,0,0]) - sage: B.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 2, 1] - - 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=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() - class CrystalOfAlcovePathsElement(ElementWrapper): """ Crystal of alcove paths element. diff --git a/src/sage/combinat/crystals/catalog.py b/src/sage/combinat/crystals/catalog.py index d6e9cab5e7d..1141ed64bfc 100644 --- a/src/sage/combinat/crystals/catalog.py +++ b/src/sage/combinat/crystals/catalog.py @@ -45,6 +45,11 @@ * :class:`DirectSum ` * :class:`TensorProduct ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals) + False """ from __future__ import absolute_import @@ -78,3 +83,5 @@ from . import catalog_infinity_crystals as infinity from . import catalog_elementary_crystals as elementary +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_elementary_crystals.py b/src/sage/combinat/crystals/catalog_elementary_crystals.py index ed330816b43..5e83fe5fdb8 100644 --- a/src/sage/combinat/crystals/catalog_elementary_crystals.py +++ b/src/sage/combinat/crystals/catalog_elementary_crystals.py @@ -8,6 +8,11 @@ or :class:`B ` * :class:`R ` * :class:`T ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.elementary) + False """ from __future__ import absolute_import @@ -17,3 +22,4 @@ from .elementary_crystals import ElementaryCrystal as B from .elementary_crystals import ComponentCrystal as Component +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_infinity_crystals.py b/src/sage/combinat/crystals/catalog_infinity_crystals.py index 16543788962..573c34fa017 100644 --- a/src/sage/combinat/crystals/catalog_infinity_crystals.py +++ b/src/sage/combinat/crystals/catalog_infinity_crystals.py @@ -9,12 +9,19 @@ ` * :class:`LSPaths ` * :class:`Multisegments ` +* :class:`MVPolytopes ` * :class:`NakajimaMonomials ` +* :class:`PBW ` * :class:`PolyhedralRealization ` * :class:`RiggedConfigurations ` * :class:`Star ` * :class:`Tableaux ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.infinity) + False """ from __future__ import absolute_import @@ -24,7 +31,10 @@ from sage.combinat.rigged_configurations.rc_infinity import InfinityCrystalOfRiggedConfigurations as RiggedConfigurations from .infinity_crystals import InfinityCrystalOfTableaux as Tableaux from sage.combinat.crystals.polyhedral_realization import InfinityCrystalAsPolyhedralRealization as PolyhedralRealization +from sage.combinat.crystals.pbw_crystal import PBWCrystal as PBW +from sage.combinat.crystals.mv_polytopes import MVPolytopes from sage.combinat.crystals.star_crystal import StarCrystal as Star from sage.combinat.crystals.littelmann_path import InfinityCrystalOfLSPaths as LSPaths from sage.combinat.crystals.alcove_path import InfinityCrystalOfAlcovePaths as AlcovePaths +del absolute_import diff --git a/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py b/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py index 3d5d99a95de..00dd6ed9223 100644 --- a/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/catalog_kirillov_reshetikhin.py @@ -9,6 +9,11 @@ * :func:`LSPaths ` * :func:`RiggedConfigurations ` + +TESTS:: + + sage: 'absolute_import' in dir(crystals.kirillov_reshetikhin) + False """ from __future__ import absolute_import @@ -16,3 +21,6 @@ from .kirillov_reshetikhin import KirillovReshetikhinCrystalFromLSPaths as LSPaths from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux from sage.combinat.rigged_configurations.rigged_configurations import KirillovReshetikhinCrystal as RiggedConfigurations + +# remove from tab completion +del absolute_import diff --git a/src/sage/combinat/crystals/direct_sum.py b/src/sage/combinat/crystals/direct_sum.py index c0aef6c6479..59a9e2c49f6 100644 --- a/src/sage/combinat/crystals/direct_sum.py +++ b/src/sage/combinat/crystals/direct_sum.py @@ -53,7 +53,7 @@ class DirectSumOfCrystals(DisjointUnionEnumeratedSets): sage: [b.f(1) for b in B] [2, None, None, None, [[2], [3]], None] sage: B.module_generators - [1, [[1], [2]]] + (1, [[1], [2]]) :: @@ -61,7 +61,7 @@ class DirectSumOfCrystals(DisjointUnionEnumeratedSets): sage: B.list() [(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3)] sage: B.module_generators - [(0, 1), (1, 1)] + ((0, 1), (1, 1)) sage: b = B( tuple([0,C(1)]) ) sage: b (0, 1) @@ -105,10 +105,7 @@ def __classcall_private__(cls, crystals, facade=True, keepkey=False, category=No if not isinstance(facade, bool) or not isinstance(keepkey, bool): raise TypeError # Normalize the facade-keepkey by giving keepkey dominance - if keepkey: - facade = False - else: - facade = True + facade = not keepkey # We expand out direct sums of crystals ret = [] @@ -136,24 +133,21 @@ def __init__(self, crystals, facade, keepkey, category, **options): sage: isinstance(B, DirectSumOfCrystals) True """ - if facade: - Parent.__init__(self, facade=tuple(crystals), category=category) - else: - Parent.__init__(self, category=category) - DisjointUnionEnumeratedSets.__init__(self, crystals, keepkey=keepkey, facade=facade) + DisjointUnionEnumeratedSets.__init__(self, crystals, keepkey=keepkey, + facade=facade, category=category) self.rename("Direct sum of the crystals {}".format(crystals)) self._keepkey = keepkey self.crystals = crystals if len(crystals) == 0: - raise ValueError("The direct sum is empty") + raise ValueError("the direct sum is empty") else: assert(crystal.cartan_type() == crystals[0].cartan_type() for crystal in crystals) self._cartan_type = crystals[0].cartan_type() if keepkey: - self.module_generators = [ self(tuple([i,b])) for i in range(len(crystals)) - for b in crystals[i].module_generators ] + self.module_generators = tuple([ self((i,b)) for i,B in enumerate(crystals) + for b in B.module_generators ]) else: - self.module_generators = sum( (list(B.module_generators) for B in crystals), []) + self.module_generators = sum((tuple(B.module_generators) for B in crystals), ()) def weight_lattice_realization(self): r""" @@ -183,12 +177,11 @@ def weight_lattice_realization(self): class Element(ElementWrapper): r""" - A class for elements of direct sums of crystals + A class for elements of direct sums of crystals. """ - def e(self, i): r""" - Returns the action of `e_i` on self. + Return the action of `e_i` on ``self``. EXAMPLES:: @@ -206,7 +199,7 @@ def e(self, i): def f(self, i): r""" - Returns the action of `f_i` on self. + Return the action of `f_i` on ``self``. EXAMPLES:: @@ -224,7 +217,7 @@ def f(self, i): def weight(self): r""" - Returns the weight of self. + Return the weight of ``self``. EXAMPLES:: diff --git a/src/sage/combinat/crystals/elementary_crystals.py b/src/sage/combinat/crystals/elementary_crystals.py index 9f2d10eb0d1..a5153cafb5c 100644 --- a/src/sage/combinat/crystals/elementary_crystals.py +++ b/src/sage/combinat/crystals/elementary_crystals.py @@ -468,25 +468,13 @@ class RCrystal(UniqueRepresentation, Parent): sage: T = crystals.TensorProduct(R, B) sage: mg = T(R.highest_weight_vector(), B.highest_weight_vector()) sage: S = T.subcrystal(generators=[mg]) - sage: for x in S: x.weight() - (2, 1, 0) - (2, 0, 1) - (1, 2, 0) - (1, 1, 1) - (1, 1, 1) - (1, 0, 2) - (0, 2, 1) - (0, 1, 2) + sage: sorted([x.weight() for x in S], key=str) + [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 1, 1), + (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0)] sage: C = crystals.Tableaux("A2", shape=[2,1]) - sage: for x in C: x.weight() - (2, 1, 0) - (1, 2, 0) - (1, 1, 1) - (1, 0, 2) - (0, 1, 2) - (2, 0, 1) - (1, 1, 1) - (0, 2, 1) + sage: sorted([x.weight() for x in C], key=str) + [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 1, 1), + (1, 1, 1), (1, 2, 0), (2, 0, 1), (2, 1, 0)] sage: GT = T.digraph(subset=S) sage: GC = C.digraph() sage: GT.is_isomorphic(GC, edge_labels=True) diff --git a/src/sage/combinat/crystals/generalized_young_walls.py b/src/sage/combinat/crystals/generalized_young_walls.py index e0454309d1f..dee445ca766 100644 --- a/src/sage/combinat/crystals/generalized_young_walls.py +++ b/src/sage/combinat/crystals/generalized_young_walls.py @@ -787,18 +787,6 @@ def _repr_(self): """ return "Crystal of generalized Young walls of type {}".format(self._cartan_type) - def weight_lattice_realization(self): - r""" - Return the extended affine weight lattice of ``self``. - - EXAMPLES:: - - sage: Y = crystals.infinity.GeneralizedYoungWalls(3) - sage: Y.weight_lattice_realization() - Extended weight lattice of the Root system of type ['A', 3, 1] - """ - return RootSystem(self._cartan_type).weight_lattice(extended=True) - ######################## ## Highest weight GYW ## diff --git a/src/sage/combinat/crystals/infinity_crystals.py b/src/sage/combinat/crystals/infinity_crystals.py index a0202182eea..545b597261f 100644 --- a/src/sage/combinat/crystals/infinity_crystals.py +++ b/src/sage/combinat/crystals/infinity_crystals.py @@ -37,7 +37,9 @@ from sage.combinat.partition import Partition from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.crystals.letters import CrystalOfLetters -from sage.combinat.crystals.tensor_product import CrystalOfWords, CrystalOfTableauxElement +from sage.combinat.crystals.tensor_product import CrystalOfWords +from sage.combinat.crystals.tensor_product_element import (CrystalOfTableauxElement, + InfinityCrystalOfTableauxElement, InfinityCrystalOfTableauxElementTypeD) class InfinityCrystalOfTableaux(CrystalOfWords): @@ -292,100 +294,19 @@ def _coerce_map_from_(self, P): From: The infinity crystal of rigged configurations of type ['A', 3] To: The infinity crystal of tableaux of type ['A', 3] """ - from sage.combinat.rigged_configurations.rc_infinity import InfinityCrystalOfRiggedConfigurations - if isinstance(P, InfinityCrystalOfRiggedConfigurations): + from sage.combinat.rigged_configurations.rc_infinity import (InfinityCrystalOfRiggedConfigurations, + InfinityCrystalOfNonSimplyLacedRC) + if (isinstance(P, InfinityCrystalOfRiggedConfigurations) + and (self.cartan_type().is_simply_laced() + or isinstance(P, InfinityCrystalOfNonSimplyLacedRC))): from sage.combinat.rigged_configurations.bij_infinity import FromRCIsomorphism return FromRCIsomorphism(Hom(P, self)) return super(InfinityCrystalOfTableaux, self)._coerce_map_from_(P) - class Element(CrystalOfTableauxElement): + class Element(InfinityCrystalOfTableauxElement): r""" Elements in `\mathcal{B}(\infty)` crystal of tableaux. """ - - def e(self,i): - r""" - Return the action of `\widetilde{e}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['B',3]) - sage: b = B(rows=[[1,1,1,1,1,1,1,2,0,-3,-1,-1,-1,-1],[2,2,2,2,-2,-2],[3,-3,-3]]) - sage: b.e(3).pp() - 1 1 1 1 1 1 1 2 0 -3 -1 -1 -1 -1 - 2 2 2 2 -2 -2 - 3 0 -3 - sage: b.e(1).pp() - 1 1 1 1 1 1 1 0 -3 -1 -1 -1 -1 - 2 2 2 2 -2 -2 - 3 -3 -3 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - ret = self.set_index(k, self[k].e(i)) - if k+i > len(self): - return ret - for j in reversed(range(1, i+1)): - if ret[k+i-j].value != j: - return ret - # We've found a column, so we need to remove it - for j in range(i): - ret._list.pop(k) - return ret - - def f(self, i): - r""" - Return the action of `\widetilde{f}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['C',4]) - sage: b = B.highest_weight_vector() - sage: b.f(1).pp() - 1 1 1 1 2 - 2 2 2 - 3 3 - 4 - sage: b.f(3).pp() - 1 1 1 1 1 - 2 2 2 2 - 3 3 4 - 4 - sage: b.f(3).f(4).pp() - 1 1 1 1 1 - 2 2 2 2 - 3 3 -4 - 4 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_minus(i) - if position == []: - return None - k = position[len(position)-1] - ret = self.set_index(k, self[k].f(i)) - if k+i > len(self): - return ret - for j in reversed(range(1,i+1)): - if self[k+i-j].value != j: - return ret - # We've found a full column, so we'll need to add a new column - for j in range(i): - ret._list.insert(k,self.parent().letters(j+1)) - return ret - def phi(self,i): r""" Return `\varphi_i` of ``self``. @@ -719,79 +640,9 @@ def module_generator(self): module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(n-1)]) return self(list=[self.letters(x) for x in module_generator]) - class Element(InfinityCrystalOfTableaux.Element): + class Element(InfinityCrystalOfTableauxElementTypeD, InfinityCrystalOfTableaux.Element): r""" Elements in `\mathcal{B}(\infty)` crystal of tableaux for type `D_n`. """ - def e(self, i): - r""" - Return the action of `\widetilde{e}_i` on ``self``. - - INPUT: + pass - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['D',4]) - sage: b = B.highest_weight_vector().f_string([1,4,3,1,2]); b.pp() - 1 1 1 1 2 3 - 2 2 2 - 3 -3 - sage: b.e(2).pp() - 1 1 1 1 2 2 - 2 2 2 - 3 -3 - """ - if i not in self.index_set(): - raise ValueError('i is not in the index set.') - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - ret = self.set_index(k, self[k].e(i)) - if i == self.cartan_type().rank(): - i -= 1 - if k+i > len(self): - return ret - for j in reversed(range(1, i+1)): - if ret[k+i-j].value != j: - return ret - # We've found a column, so we need to remove it - for j in range(i): - ret._list.pop(k) - return ret - - def f(self, i): - r""" - Return the action of `\widetilde{f}_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux(['D',5]) - sage: b = B.highest_weight_vector().f_string([1,4,3,1,5]); b.pp() - 1 1 1 1 1 1 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 5 - sage: b.f(1).pp() - 1 1 1 1 1 1 2 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 5 - sage: b.f(5).pp() - 1 1 1 1 1 1 2 2 - 2 2 2 2 2 - 3 3 3 -5 - 4 -4 - """ - ret = InfinityCrystalOfTableaux.Element.f(self, i) - if ret._list[0].value == -self.cartan_type().rank(): - # Exceptional case for f_n where we need to add a new column - for j in range(i-1): - ret._list.insert(0,self.parent().letters(j+1)) - return ret diff --git a/src/sage/combinat/crystals/kirillov_reshetikhin.py b/src/sage/combinat/crystals/kirillov_reshetikhin.py index 8eac1105507..f66fe729df5 100644 --- a/src/sage/combinat/crystals/kirillov_reshetikhin.py +++ b/src/sage/combinat/crystals/kirillov_reshetikhin.py @@ -25,28 +25,27 @@ from six.moves import range from sage.misc.cachefunc import cached_method -from sage.misc.abstract_method import abstract_method from sage.misc.lazy_attribute import lazy_attribute from sage.misc.functional import is_even, is_odd -from sage.functions.other import floor, ceil +from sage.functions.other import floor from sage.combinat.combinat import CombinatorialObject from sage.structure.parent import Parent from sage.categories.crystals import CrystalMorphism -from sage.categories.regular_crystals import RegularCrystals -from sage.categories.finite_crystals import FiniteCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.categories.homset import Hom from sage.categories.map import Map from sage.rings.integer import Integer from sage.rings.all import QQ -from sage.combinat.crystals.affine import AffineCrystalFromClassical, \ - AffineCrystalFromClassicalElement, AffineCrystalFromClassicalAndPromotion, \ - AffineCrystalFromClassicalAndPromotionElement +from sage.combinat.crystals.affine import (AffineCrystalFromClassical, + AffineCrystalFromClassicalElement, + AffineCrystalFromClassicalAndPromotion, + AffineCrystalFromClassicalAndPromotionElement) from sage.combinat.crystals.highest_weight_crystals import HighestWeightCrystal from sage.combinat.crystals.littelmann_path import CrystalOfProjectedLevelZeroLSPaths from sage.combinat.crystals.direct_sum import DirectSumOfCrystals from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.root_system import RootSystem -from sage.combinat.crystals.tensor_product import CrystalOfTableaux, TensorProductOfCrystals +from sage.combinat.crystals.tensor_product import CrystalOfTableaux from sage.combinat.tableau import Tableau from sage.combinat.partition import Partition, Partitions from sage.combinat.integer_vector import IntegerVectors @@ -446,7 +445,7 @@ class KirillovReshetikhinGenericCrystal(AffineCrystalFromClassical): ``cartan_type``. """ - def __init__(self, cartan_type, r, s, dual = None): + def __init__(self, cartan_type, r, s, dual=None): r""" Initializes a generic Kirillov-Reshetikhin crystal. @@ -460,8 +459,8 @@ def __init__(self, cartan_type, r, s, dual = None): sage: K.s() 1 """ - # We need this here for the classical_decomposition() call - Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals())) + # We need this here for the classic al_decomposition() call + Parent.__init__(self, category=KirillovReshetikhinCrystals()) if dual is None: self._cartan_type = cartan_type else: @@ -469,7 +468,8 @@ def __init__(self, cartan_type, r, s, dual = None): self._r = r self._s = s self._dual = dual - AffineCrystalFromClassical.__init__(self, cartan_type, self.classical_decomposition()) + AffineCrystalFromClassical.__init__(self, cartan_type, self.classical_decomposition(), + KirillovReshetikhinCrystals()) def _repr_(self): """ @@ -487,7 +487,7 @@ def _element_constructor_(self, *args, **options): EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['A', 4, 1], 2, 1) - sage: K(columns=[[2,1]]) # indirect doctest + sage: K(columns=[[2,1]]) [[1], [2]] """ from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableauxElement @@ -496,7 +496,7 @@ def _element_constructor_(self, *args, **options): # Check to make sure it can be converted if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") + raise ValueError("the Kirillov-Reshetikhin tableau must have the same Cartan type and shape") to_hw = elt.to_classical_highest_weight() rows = [] @@ -509,21 +509,10 @@ def _element_constructor_(self, *args, **options): return hw_elt.f_string(f_str) return AffineCrystalFromClassical._element_constructor_(self, *args, **options) - @abstract_method - def classical_decomposition(self): - """ - Return the classical decomposition of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',3,1], 2,2) - sage: K.classical_decomposition() - The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] - """ - def module_generator(self): r""" - Returns the unique module generator of classical weight `s \Lambda_r` of a Kirillov-Reshetikhin crystal `B^{r,s}` + Return the unique module generator of classical weight + `s \Lambda_r` of a Kirillov-Reshetikhin crystal `B^{r,s}` EXAMPLES:: @@ -543,11 +532,11 @@ def module_generator(self): r = self.r() s = self.s() weight = s*Lambda[r] - s*Lambda[0] * Lambda[r].level() / Lambda[0].level() - return [ b for b in self.module_generators if b.weight() == weight][0] + return [b for b in self.module_generators if b.weight() == weight][0] def r(self): """ - Returns r of the underlying Kirillov-Reshetikhin crystal `B^{r,s}` + Return `r` of the underlying Kirillov-Reshetikhin crystal `B^{r,s}`. EXAMPLES:: @@ -559,7 +548,7 @@ def r(self): def s(self): """ - Returns s of the underlying Kirillov-Reshetikhin crystal `B^{r,s}` + Return `s` of the underlying Kirillov-Reshetikhin crystal `B^{r,s}`. EXAMPLES:: @@ -569,136 +558,20 @@ def s(self): """ return self._s - def is_perfect(self): - r""" - Returns True or False depending on whether ``self`` is a perfect crystal or not, respectively. - - If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, then it was proven in [FOS2010]_ - that it is perfect if and only if `s/c_r` is an integer (where `c_r` is a constant related to the - type of the crystal). - - REFERENCES: - - .. [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] ) - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.is_perfect() - True - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: K.is_perfect() - False - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K.is_perfect() - True + @cached_method + def classically_highest_weight_vectors(self): """ - x = self.s()/self.cartan_type().c()[self.r()] - return x - ceil(x) == 0 - - def level(self): - r""" - Returns the level of ``self`` assuming that it is a perfect crystal. - - If ``self`` is the Kirillov-Reshetikhin crystal `B^{r,s}`, then it was proven in [FOS2010]_ - that its level is `s/c_r` which is an integer if ``self`` is perfect - (here `c_r` is a constant related to the type of the crystal). + Return the classically highest weight vectors of ``self``. EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.level() - 1 - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K.level() - 1 - sage: K = crystals.KirillovReshetikhin(['D',4,1], 1, 3) - sage: K.level() - 3 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: K.level() - Traceback (most recent call last): - ... - ValueError: this crystal is not perfect + sage: K = crystals.KirillovReshetikhin(['D', 4, 1], 2, 2) + sage: K.classically_highest_weight_vectors() + ([], [[1], [2]], [[1, 1], [2, 2]]) """ - if not self.is_perfect(): - raise ValueError("this crystal is not perfect") - return self.s()/self.cartan_type().c()[self.r()] - - @cached_method - def R_matrix(self, K): - r""" - INPUT: + return tuple([self.retract(mg) + for mg in self.classical_decomposition().module_generators]) - - ``self`` -- a crystal `L` - - ``K`` -- a Kirillov-Reshetikhin crystal of the same type as `L` - - Returns the *combinatorial `R`-matrix* from `L \otimes K \to K - \otimes L`, where the combinatorial `R`-matrix is the affine - crystal isomorphism which maps `u_{L} \otimes u_K` to `u_K - \otimes u_{L}`, where `u_K` is the unique element in `K = - B^{r,s}` of weight `s\Lambda_r - s c \Lambda_0` (see - module_generator). - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: L = crystals.KirillovReshetikhin(['A',2,1],1,2) - sage: f = K.R_matrix(L) - sage: [[b,f(b)] for b in crystals.TensorProduct(K,L)] - [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]], - [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]], - [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]], - [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]], - [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]], - [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]], - [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]], - [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]], - [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]], - [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]], - [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]], - [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]], - [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]], - [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]], - [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]], - [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]], - [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]], - [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]] - - sage: K = crystals.KirillovReshetikhin(['D',4,1],1,1) - sage: L = crystals.KirillovReshetikhin(['D',4,1],2,1) - sage: f = K.R_matrix(L) - sage: T = crystals.TensorProduct(K,L) - sage: b = T( K(rows=[[1]]), L(rows=[]) ) - sage: f(b) - [[[2], [-2]], [[1]]] - - Alternatively, one can compute the combinatorial `R`-matrix using the isomorphism method - of digraphs:: - - sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) - sage: T1 = crystals.TensorProduct(K1,K2) - sage: T2 = crystals.TensorProduct(K2,K1) - sage: T1.digraph().is_isomorphic(T2.digraph(), edge_labels = True, certificate = True) #todo: not implemented (see #10904 and #10549) - (True, {[[[1]], [[2], [3]]]: [[[1], [3]], [[2]]], [[[3]], [[2], [3]]]: [[[2], [3]], [[3]]], - [[[3]], [[1], [3]]]: [[[1], [3]], [[3]]], [[[1]], [[1], [3]]]: [[[1], [3]], [[1]]], [[[1]], - [[1], [2]]]: [[[1], [2]], [[1]]], [[[2]], [[1], [2]]]: [[[1], [2]], [[2]]], [[[3]], - [[1], [2]]]: [[[2], [3]], [[1]]], [[[2]], [[1], [3]]]: [[[1], [2]], [[3]]], [[[2]], [[2], [3]]]: [[[2], [3]], [[2]]]}) - """ - T1 = TensorProductOfCrystals(self, K) - T2 = TensorProductOfCrystals(K, self) - gen1 = T1( self.module_generator(), K.module_generator() ) - gen2 = T2( K.module_generator(), self.module_generator() ) - g = { gen1 : gen2 } - return T1.crystal_morphism(g, check=False) - - @cached_method def kirillov_reshetikhin_tableaux(self): """ Return the corresponding set of @@ -713,97 +586,6 @@ def kirillov_reshetikhin_tableaux(self): from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux return KirillovReshetikhinTableaux(self.cartan_type(), self._r, self._s) - def affinization(self): - """ - Return the corresponding affinization crystal of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: K.affinization() - Affinization of Kirillov-Reshetikhin crystal of type ['A', 2, 1] with (r,s)=(1,1) - """ - from sage.combinat.crystals.affinization import AffinizationOfCrystal - return AffinizationOfCrystal(self) - - def q_dimension(self, q=None, prec=None, use_product=False): - """ - Return the `q`-dimension of ``self``. - - The `q`-dimension of a KR crystal is defined as the `q`-dimension of - the underlying classical crystal. - - EXAMPLES:: - - sage: KRC = crystals.KirillovReshetikhin(['A',2,1], 2,2) - sage: KRC.q_dimension() - q^4 + q^3 + 2*q^2 + q + 1 - sage: KRC = crystals.KirillovReshetikhin(['D',4,1], 2,1) - sage: KRC.q_dimension() - q^10 + q^9 + 3*q^8 + 3*q^7 + 4*q^6 + 4*q^5 + 4*q^4 + 3*q^3 + 3*q^2 + q + 2 - """ - return self.classical_decomposition().q_dimension(q, prec, use_product) - - @cached_method - def local_energy_function(self, B): - r""" - Return the local energy function of ``self`` and ``B``. - - See - :class:`~sage.combinat.crystals.tensor_product.LocalEnergyFunction` - for a definition. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) - sage: Kp = crystals.KirillovReshetikhin(['A',6,2], 1,1) - sage: H = K.local_energy_function(Kp); H - Local energy function of - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) - tensor - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) - """ - from sage.combinat.crystals.tensor_product import LocalEnergyFunction - return LocalEnergyFunction(self, B) - - @cached_method - def b_sharp(self): - r""" - Return the element `b^{\sharp}` of ``self``. - - Let `B` be a KR crystal. The element `b^{\sharp}` is the unique - element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with - `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',6,2], 2,1) - sage: K.b_sharp() - [] - sage: K.b_sharp().Phi() - Lambda[0] - - sage: K = crystals.KirillovReshetikhin(['C',3,1], 1,3) - sage: K.b_sharp() - [[-1]] - sage: K.b_sharp().Phi() - 2*Lambda[0] - - sage: K = crystals.KirillovReshetikhin(['D',6,2], 2,2) - sage: K.b_sharp() # long time - [] - sage: K.b_sharp().Phi() # long time - 2*Lambda[0] - """ - ell = float('inf') - bsharp = None - for b in self: - phi = b.Phi() - if phi.support() == [0] and phi[0] < ell: - bsharp = b - ell = phi[0] - return bsharp - class KirillovReshetikhinGenericCrystalElement(AffineCrystalFromClassicalElement): """ Abstract class for all Kirillov-Reshetikhin crystal elements. @@ -895,62 +677,23 @@ def lusztig_involution(self): li = self.lift().lusztig_involution() return self.parent().retract(li) - @cached_method - def energy_function(self): - r""" - Return the energy function of ``self``. - - Let `B` be a KR crystal. Let `b^{\sharp}` denote the unique - element such that `\varphi(b^{\sharp}) = \ell \Lambda_0` with - `\ell = \min \{ \langle c, \varphi(b) \mid b \in B \}`. Let - `u_B` denote the maximal element of `B`. The *energy* of - `b \in B` is given by - - .. MATH:: - - D(b) = H(b \otimes b^{\sharp}) - H(u_B \otimes b^{\sharp}), - - where `H` is the :meth:`local energy function - `. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['D',4,1], 2,1) - sage: for x in K: - ....: if x.is_highest_weight([1,2,3,4]): - ....: x, x.energy_function() - ([], 1) - ([[1], [2]], 0) - - sage: K = crystals.KirillovReshetikhin(['D',4,3], 1,2) - sage: for x in K: - ....: if x.is_highest_weight([1,2]): - ....: x, x.energy_function() - ([], 2) - ([[1]], 1) - ([[1, 1]], 0) - """ - B = self.parent() - bsharp = B.b_sharp() - T = B.tensor(B) - H = B.local_energy_function(B) - return H(T(self, bsharp)) - H(T(B.module_generator(), bsharp)) - KirillovReshetikhinGenericCrystal.Element = KirillovReshetikhinGenericCrystalElement class KirillovReshetikhinCrystalFromPromotion(KirillovReshetikhinGenericCrystal, AffineCrystalFromClassicalAndPromotion): r""" - This generic class assumes that the Kirillov-Reshetikhin crystal is constructed - from a classical crystal 'classical_decomposition' and an automorphism 'promotion' and its inverse - which corresponds to a Dynkin diagram automorphism 'dynkin_diagram_automorphism'. + This generic class assumes that the Kirillov-Reshetikhin crystal is + constructed from a classical crystal using the + ``classical_decomposition`` and an automorphism ``promotion`` + and its inverse, which corresponds to a Dynkin diagram automorphism + ``dynkin_diagram_automorphism``. Each instance using this class needs to implement the methods: - - classical_decomposition - - promotion - - promotion_inverse - - dynkin_diagram_automorphism + - ``classical_decomposition`` + - ``promotion`` + - ``promotion_inverse`` + - ``dynkin_diagram_automorphism`` """ def __init__(self, cartan_type, r, s): r""" @@ -962,9 +705,12 @@ def __init__(self, cartan_type, r, s): sage: TestSuite(K).run() """ KirillovReshetikhinGenericCrystal.__init__(self, cartan_type, r, s) - AffineCrystalFromClassicalAndPromotion.__init__(self, cartan_type, self.classical_decomposition(), - self.promotion(), self.promotion_inverse(), - self.dynkin_diagram_automorphism(0)) + AffineCrystalFromClassicalAndPromotion.__init__(self, cartan_type, + self.classical_decomposition(), + self.promotion(), + self.promotion_inverse(), + self.dynkin_diagram_automorphism(0), + KirillovReshetikhinCrystals()) class KirillovReshetikhinCrystalFromPromotionElement(AffineCrystalFromClassicalAndPromotionElement, KirillovReshetikhinGenericCrystalElement): @@ -997,7 +743,8 @@ def classical_decomposition(self): sage: K.classical_decomposition() The crystal of tableaux of type ['A', 3] and shape(s) [[2, 2]] """ - return CrystalOfTableaux(self.cartan_type().classical(), shape = [self.s() for i in range(1,self.r()+1)]) + return CrystalOfTableaux(self.cartan_type().classical(), + shape=[self.s()]*self.r()) @cached_method def promotion(self): @@ -1066,8 +813,9 @@ def dynkin_diagram_automorphism(self, i): class KR_type_vertical(KirillovReshetikhinCrystalFromPromotion): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `D_n^{(1)}` for `r\le n-2`, - `B_n^{(1)}` for `r0]) for r in b.to_tableau()]) outer = b.to_tableau().shape() @@ -1244,9 +1014,10 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): sage: K.from_pm_diagram_to_highest_weight_vector(pm) [[2], [-2]] """ - u = [b for b in self.classical_decomposition().module_generators if b.to_tableau().shape() == pm.outer_shape()][0] + u = [b for b in self.classical_decomposition().module_generators + if b.to_tableau().shape() == pm.outer_shape()][0] ct = self.cartan_type() - rank = ct.rank()-1 + rank = ct.rank() - 1 ct_type = ct.classical().type() assert ct_type in ['B', 'C', 'D'] ulist = [] @@ -1254,11 +1025,11 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): ulist += list(range(1, h + 1)) for h in pm.heights_of_minus(): if ct_type == 'D': - ulist += list(range(1,rank+1))+[rank-2-k for k in range(rank-1-h)] + ulist += list(range(1,rank+1)) + [rank-2-k for k in range(rank-1-h)] elif ct_type == 'B': - ulist += list(range(1,rank+1))+[rank-k for k in range(rank+1-h)] + ulist += list(range(1,rank+1)) + [rank-k for k in range(rank+1-h)] else: - ulist += list(range(1,rank+1))+[rank-1-k for k in range(rank-h)] + ulist += list(range(1,rank+1)) + [rank-1-k for k in range(rank-h)] for i in reversed(ulist): u = u.f(i) return u @@ -1331,19 +1102,22 @@ def classical_decomposition(self): """ La = self.cartan_type().classical().root_system().weight_lattice().fundamental_weights() if self.r() in [1,6]: - dw = [self.s()*La[self.r()]] + dw = [self.s() * La[self.r()]] elif self.r() == 2: - dw = sum( ([k*La[2]] for k in range(self.s()+1)), []) + dw = [k*La[2] for k in range(self.s()+1)] else: - raise ValueError - return DirectSumOfCrystals([HighestWeightCrystal(dominant_weight) for dominant_weight in dw], keepkey = False) + raise NotImplementedError + return DirectSumOfCrystals([HighestWeightCrystal(dominant_weight) + for dominant_weight in dw], + keepkey=False) def dynkin_diagram_automorphism(self, i): r""" - Specifies the Dynkin diagram automorphism underlying the promotion action on the crystal - elements. The automorphism needs to map node 0 to some other Dynkin node. + Specifies the Dynkin diagram automorphism underlying the promotion + action on the crystal elements. - Here we use the Dynkin diagram automorphism of order 3 which maps node 0 to node 1. + Here we use the Dynkin diagram automorphism of order 3 which maps + node 0 to node 1. EXAMPLES:: @@ -1356,41 +1130,45 @@ def dynkin_diagram_automorphism(self, i): def affine_weight(self, b): r""" - Returns the affine level zero weight corresponding to the element b of the classical - crystal underlying self. For the coefficients to calculate the level, see Kac pg. 48. + Return the affine level zero weight corresponding to the element + ``b`` of the classical crystal underlying ``self``. + + For the coefficients to calculate the level, see Table Aff 1 + in [Ka1990]_. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: [K.affine_weight(x.lift()) for x in K if all(x.epsilon(i) == 0 for i in [2,3,4,5])] + sage: [K.affine_weight(x.lift()) for x in K + ....: if all(x.epsilon(i) == 0 for i in [2,3,4,5])] [(0, 0, 0, 0, 0, 0, 0), - (-2, 0, 1, 0, 0, 0, 0), - (-1, -1, 0, 0, 0, 1, 0), - (0, 0, 0, 0, 0, 0, 0), - (0, 0, 0, 0, 0, 1, -2), - (0, -1, 1, 0, 0, 0, -1), - (-1, 0, 0, 1, 0, 0, -1), - (-1, -1, 0, 0, 1, 0, -1), - (0, 0, 0, 0, 0, 0, 0), - (0, -2, 0, 1, 0, 0, 0)] - """ - simple_roots = self.cartan_type().classical().root_system().ambient_space().simple_roots() - index_set = b.parent().index_set() - weight = [ Integer(b.weight().scalar( simple_roots[i] )) for i in index_set ] - E6_coeffs = [ 1, 2, 2, 3, 2, 1 ] - return tuple( [-sum([ weight[i-1] * E6_coeffs[i-1] for i in index_set ])] + weight ) - + (-2, 0, 1, 0, 0, 0, 0), + (-1, -1, 0, 0, 0, 1, 0), + (0, 0, 0, 0, 0, 0, 0), + (0, 0, 0, 0, 0, 1, -2), + (0, -1, 1, 0, 0, 0, -1), + (-1, 0, 0, 1, 0, 0, -1), + (-1, -1, 0, 0, 1, 0, -1), + (0, 0, 0, 0, 0, 0, 0), + (0, -2, 0, 1, 0, 0, 0)] + """ + cl = self.cartan_type().classical() + simple_roots = cl.root_system().ambient_space().simple_roots() + index_set = cl.index_set() + weight = [Integer(b.weight().scalar( simple_roots[i] )) for i in index_set] + E6_coeffs = [1, 2, 2, 3, 2, 1] + return tuple([-sum(weight[i] * coeff for i,coeff in enumerate(E6_coeffs))] + weight) @cached_method def hw_auxiliary(self): r""" - Returns the `{2,3,4,5}` highest weight elements of self. + Return the `{2,3,4,5}` highest weight elements of ``self``. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) sage: K.hw_auxiliary() - [[], [[(2, -1), (1,)]], + ([], [[(2, -1), (1,)]], [[(5, -3), (-1, 3)]], [[(6, -2), (-6, 2)]], [[(5, -2, -6), (-6, 2)]], @@ -1398,27 +1176,29 @@ def hw_auxiliary(self): [[(3, -1, -6), (1,)]], [[(4, -3, -6), (-1, 3)]], [[(1, -3), (-1, 3)]], - [[(-1,), (-1, 3)]]] + [[(-1,), (-1, 3)]]) """ - return [x for x in self.classical_decomposition() if all(x.epsilon(i) == 0 for i in [2,3,4,5])] + return tuple([x for x in self.classical_decomposition() + if all(x.epsilon(i) == 0 for i in [2,3,4,5])]) @cached_method def highest_weight_dict(self): r""" - Returns a dictionary between `{1,2,3,4,5}` highest weight elements, and a tuple of affine weights and its classical component. + Return a dictionary between `\{1,2,3,4,5\}`-highest weight elements, + and a tuple of affine weights and its classical component. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: K.highest_weight_dict() - {[[(2, -1), (1,)]]: ((-2, 0, 1, 0, 0, 0, 0), 1), - [[(3, -1, -6), (1,)]]: ((-1, 0, 0, 1, 0, 0, -1), 1), - [[(6, -2), (-6, 2)]]: ((0, 0, 0, 0, 0, 0, 0), 1), - [[(5, -2, -6), (-6, 2)]]: ((0, 0, 0, 0, 0, 1, -2), 1), - []: ((0, 0, 0, 0, 0, 0, 0), 0)} + sage: sorted(K.highest_weight_dict().items(), key=str) + [([[(2, -1), (1,)]], ((-2, 0, 1, 0, 0, 0, 0), 1)), + ([[(3, -1, -6), (1,)]], ((-1, 0, 0, 1, 0, 0, -1), 1)), + ([[(5, -2, -6), (-6, 2)]], ((0, 0, 0, 0, 0, 1, -2), 1)), + ([[(6, -2), (-6, 2)]], ((0, 0, 0, 0, 0, 0, 0), 1)), + ([], ((0, 0, 0, 0, 0, 0, 0), 0))] """ hw = [x for x in self.hw_auxiliary() if x.epsilon(1) == 0] - dic = dict( ( x, tuple( [self.affine_weight(x), len(x)] ) ) for x in hw ) + dic = {x: (self.affine_weight(x), len(x)) for x in hw} assert len(hw) == len(dic) return dic @@ -1426,7 +1206,7 @@ def highest_weight_dict(self): def highest_weight_dict_inv(self): r""" Return a dictionary between a tuple of affine weights and a classical - component, and `{2,3,4,5,6}` highest weight elements. + component, and `\{2,3,4,5,6\}`-highest weight elements. EXAMPLES:: @@ -1439,24 +1219,25 @@ def highest_weight_dict_inv(self): ((0, 0, 0, 0, 0, 0, 0), 1): [[(1, -3), (-1, 3)]]} """ hw = [x for x in self.hw_auxiliary() if x.epsilon(6) == 0] - dic = dict( ( tuple( [self.affine_weight(x), len(x)] ), x ) for x in hw ) + dic = {(self.affine_weight(x), len(x)): x for x in hw} assert len(hw) == len(dic) return dic def automorphism_on_affine_weight(self, weight): r""" - Acts with the Dynkin diagram automorphism on affine weights + Act with the Dynkin diagram automorphism on affine weights as outputted by the ``affine_weight`` method. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) - sage: [[x[0], K.automorphism_on_affine_weight(x[0])] for x in K.highest_weight_dict().values()] - [[(0, 0, 0, 0, 0, 1, -2), (-2, 0, 1, 0, 0, 0, 0)], - [(-1, 0, 0, 1, 0, 0, -1), (-1, -1, 0, 0, 0, 1, 0)], + sage: [[x[0], K.automorphism_on_affine_weight(x[0])] + ....: for x in K.highest_weight_dict().values()] + [[(-1, 0, 0, 1, 0, 0, -1), (-1, -1, 0, 0, 0, 1, 0)], + [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)], [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)], [(-2, 0, 1, 0, 0, 0, 0), (0, -2, 0, 1, 0, 0, 0)], - [(0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0)]] + [(0, 0, 0, 0, 0, 1, -2), (-2, 0, 1, 0, 0, 0, 0)]] """ f = self.dynkin_diagram_automorphism return tuple( [weight[f(f(i))] for i in self.index_set()] ) @@ -1464,19 +1245,20 @@ def automorphism_on_affine_weight(self, weight): @cached_method def promotion_on_highest_weight_vectors(self): r""" - Gives a dictionary of the promotion map on `{1,2,3,4,5}` highest - weight elements to `{2,3,4,5,6}` elements in ``self``. + Return a dictionary of the promotion map on `\{1,2,3,4,5\}`-highest + weight elements to `\{2,3,4,5,6\}`-highest weight elements + in ``self``. EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['E',6,1],2,1) + sage: K = crystals.KirillovReshetikhin(['E',6,1], 2, 1) sage: dic = K.promotion_on_highest_weight_vectors() - sage: dic - {[[(2, -1), (1,)]]: [[(-1,), (-1, 3)]], - [[(3, -1, -6), (1,)]]: [[(5, -3), (-1, 3)]], - [[(6, -2), (-6, 2)]]: [], - [[(5, -2, -6), (-6, 2)]]: [[(2, -1), (1,)]], - []: [[(1, -3), (-1, 3)]]} + sage: sorted(dic.items(), key=str) + [([[(2, -1), (1,)]], [[(-1,), (-1, 3)]]), + ([[(3, -1, -6), (1,)]], [[(5, -3), (-1, 3)]]), + ([[(5, -2, -6), (-6, 2)]], [[(2, -1), (1,)]]), + ([[(6, -2), (-6, 2)]], []), + ([], [[(1, -3), (-1, 3)]])] """ dic = self.highest_weight_dict() dic_inv = self.highest_weight_dict_inv() @@ -1484,8 +1266,8 @@ def promotion_on_highest_weight_vectors(self): for (weight, i) in dic.values(): dic_weight[weight] = dic_weight.get(weight, []) + [i] map_index = lambda i_list: max(i_list[1]) + min(i_list[1]) - i_list[0] - map_element = lambda x : tuple([ self.automorphism_on_affine_weight(dic[x][0]), - map_index((dic[x][1], dic_weight[dic[x][0]])) ]) + map_element = lambda x: ( self.automorphism_on_affine_weight(dic[x][0]), + map_index((dic[x][1], dic_weight[dic[x][0]])) ) return {x: dic_inv[map_element(x)] for x in dic} @cached_method @@ -1496,12 +1278,12 @@ def promotion_on_highest_weight_vectors_function(self): EXAMPLES:: - sage: K = crystals.KirillovReshetikhin(['E',6,1], 2,1) + sage: K = crystals.KirillovReshetikhin(['E',6,1], 2, 1) sage: f = K.promotion_on_highest_weight_vectors_function() sage: f(K.module_generator().lift()) [[(-1,), (-1, 3)]] """ - return lambda x : self.promotion_on_highest_weight_vectors()[x] + return self.promotion_on_highest_weight_vectors().__getitem__ @cached_method def promotion(self): @@ -1523,7 +1305,7 @@ def promotion(self): T = self.classical_decomposition() ind = [1,2,3,4,5] return CrystalDiagramAutomorphism(T, self.promotion_on_highest_weight_vectors(), ind, - automorphism=self.dynkin_diagram_automorphism) + automorphism=self.dynkin_diagram_automorphism) @cached_method def promotion_inverse(self): @@ -1545,7 +1327,8 @@ def promotion_inverse(self): class KR_type_C(KirillovReshetikhinGenericCrystal): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `C_n^{(1)}` for `r """ - def e0(self): r""" - Gives `e_0` on self by mapping self to the ambient crystal, calculating `e_1 e_0` there and - pulling the element back. + Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `e_1 e_0` there and pulling the element back. EXAMPLES:: @@ -1730,8 +1522,8 @@ def e0(self): def f0(self): r""" - Gives `f_0` on self by mapping self to the ambient crystal, calculating `f_1 f_0` there and - pulling the element back. + Return `f_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `f_1 f_0` there and pulling the element back. EXAMPLES:: @@ -1781,11 +1573,11 @@ def phi0(self): class KR_type_A2(KirillovReshetikhinGenericCrystal): r""" - Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `1\le r \le n` - in the realization with classical subalgebra `B_n`. The Cartan type in this case is inputted as - the dual of `A_{2n}^{(2)}`. + Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` + for `1 \leq r \leq n` in the realization with classical subalgebra `B_n`. + The Cartan type in this case is inputted as the dual of `A_{2n}^{(2)}`. - This is an alternative implementation to :class:`KR_type_box` which uses + This is an alternative implementation to :class:`KR_type_box` that uses the classical decomposition into type `C_n` crystals. EXAMPLES:: @@ -1799,8 +1591,9 @@ class KR_type_A2(KirillovReshetikhinGenericCrystal): [[1]] sage: b.e(0) - We can now check whether the two KR crystals of type `A_4^{(2)}` (namely the KR crystal and its dual - construction) are isomorphic up to relabelling of the edges:: + We can now check whether the two KR crystals of type `A_4^{(2)}` + (namely the KR crystal and its dual construction) are isomorphic + up to relabelling of the edges:: sage: C = CartanType(['A',4,2]) sage: K = crystals.KirillovReshetikhin(C,1,1) @@ -1815,13 +1608,15 @@ class KR_type_A2(KirillovReshetikhinGenericCrystal): def classical_decomposition(self): r""" - Specifies the classical crystal underlying the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}` - with `B_n` as classical subdiagram. + Return the classical crystal underlying the Kirillov-Reshetikhin + crystal of type `A_{2n}^{(2)}` with `B_n` as classical subdiagram. - It is given by `B^{r,s} \cong \bigoplus_\Lambda B(\Lambda)` where `B(\Lambda)` is a highest weight crystal of type - `B_n` of highest weight `\Lambda`. The sum is over all weights `\Lambda` obtained from - a rectangle of width `s` and height `r` by removing horizontal dominoes. Here we identify the fundamental - weight `\Lambda_i` with a column of height `i`. + It is given by `B^{r,s} \cong \bigoplus_{\Lambda} B(\Lambda)`, + where `B(\Lambda)` is a highest weight crystal of type `B_n` + of highest weight `\Lambda`. The sum is over all weights `\Lambda` + obtained from a rectangle of width `s` and height `r` by removing + horizontal dominoes. Here we identify the fundamental weight + `\Lambda_i` with a column of height `i`. EXAMPLES:: @@ -1835,8 +1630,11 @@ def classical_decomposition(self): def ambient_crystal(self): r""" - Returns the ambient crystal `B^{r,s}` of type `B_{n+1}^{(1)}` associated to the Kirillov-Reshetikhin - crystal of type `A_{2n}^{(2)}` dual. This ambient crystal is used to construct the zero arrows. + Return the ambient crystal `B^{r,s}` of type `B_{n+1}^{(1)}` + associated to the Kirillov-Reshetikhin crystal of type + `A_{2n}^{(2)}` dual. + + This ambient crystal is used to construct the zero arrows. EXAMPLES:: @@ -1850,8 +1648,8 @@ def ambient_crystal(self): @cached_method def ambient_dict_pm_diagrams(self): r""" - Gives a dictionary of all self-dual `\pm` diagrams for the ambient crystal. - Their key is their inner shape. + Return a dictionary of all self-dual `\pm` diagrams for the + ambient crystal whose keys are their inner shape. EXAMPLES:: @@ -1871,18 +1669,20 @@ def ambient_dict_pm_diagrams(self): ulist = [] s = self.s() r = self.r() - m = s//2 + m = s // 2 for i in range(m+1): for la in IntegerVectors(m-i, min_length=r, max_length=r): ulist.append(PMDiagram([[j,j] for j in la]+[[s-2*m+2*i]])) - return dict( (x.inner_shape(), x) for x in ulist ) + return {x.inner_shape(): x for x in ulist} @cached_method def ambient_highest_weight_dict(self): r""" - Gives a dictionary of all `{2,...,n+1}`-highest weight vectors in the ambient crystal. - Their key is the inner shape of their corresponding `\pm` diagram, or equivalently, their - `{2,...,n+1}` weight. + Return a dictionary of all `\{2,\ldots,n+1\}`-highest weight vectors + in the ambient crystal. + + The key is the inner shape of their corresponding `\pm` diagram, + or equivalently, their `\{2,\ldots,n+1\}` weight. EXAMPLES:: @@ -1893,13 +1693,14 @@ def ambient_highest_weight_dict(self): """ A = self.ambient_dict_pm_diagrams() ambient = self.ambient_crystal() - return dict( (key, ambient.retract(ambient.from_pm_diagram_to_highest_weight_vector(A[key]))) for key in A ) + return {key: ambient.retract(ambient.from_pm_diagram_to_highest_weight_vector(A[key])) + for key in A} @cached_method def highest_weight_dict(self): r""" - Gives a dictionary of the classical highest weight vectors of self. - Their key is their shape. + Return a dictionary of the classical highest weight vectors + of ``self`` whose keys are their shape. EXAMPLES:: @@ -1908,13 +1709,13 @@ def highest_weight_dict(self): sage: K.highest_weight_dict() {[]: [], [2]: [[1, 1]]} """ - return dict( (x.lift().to_tableau().shape(),x) for x in self.module_generators ) + return {x.lift().to_tableau().shape(): x for x in self.module_generators} @cached_method def to_ambient_crystal(self): r""" - Provides a map from the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}` to the - ambient crystal of type `B_{n+1}^{(1)}`. + Return a map from the Kirillov-Reshetikhin crystal of type + `A_{2n}^{(2)}` to the ambient crystal of type `B_{n+1}^{(1)}`. EXAMPLES:: @@ -1930,17 +1731,18 @@ def to_ambient_crystal(self): sage: K.to_ambient_crystal()(b).parent() Kirillov-Reshetikhin crystal of type ['B', 3, 1] with (r,s)=(2,2) """ - keys = self.highest_weight_dict() - pdict = dict( (self.highest_weight_dict()[key], self.ambient_highest_weight_dict()[key]) for key in keys ) + hwd = self.highest_weight_dict() + ahwd = self.ambient_highest_weight_dict() + pdict = {hwd[key]: ahwd[key] for key in hwd} classical = self.cartan_type().classical() - return self.crystal_morphism( pdict, index_set=classical.index_set(), - automorphism=lambda i: i+1, - cartan_type=classical, check=False ) + return self.crystal_morphism(pdict, index_set=classical.index_set(), + automorphism=lambda i: i+1, + cartan_type=classical, check=False) @cached_method def from_ambient_crystal(self): r""" - Provides a map from the ambient crystal of type `B_{n+1}^{(1)}` to + Return a map from the ambient crystal of type `B_{n+1}^{(1)}` to the Kirillov-Reshetikhin crystal of type `A_{2n}^{(2)}`. Note that this map is only well-defined on type `A_{2n}^{(2)}` @@ -1954,17 +1756,17 @@ def from_ambient_crystal(self): sage: K.from_ambient_crystal()(b) [[1, 1]] """ - keys = self.highest_weight_dict() - pdict_inv = dict( (self.ambient_highest_weight_dict()[key], self.highest_weight_dict()[key]) - for key in keys ) + hwd = self.highest_weight_dict() + ahwd = self.ambient_highest_weight_dict() + pdict_inv = {ahwd[key]: hwd[key] for key in hwd} ind = [j+1 for j in self.cartan_type().classical().index_set()] - return AmbientRetractMap( self, self.ambient_crystal(), pdict_inv, index_set=ind, - automorphism=lambda i : i-1 ) + return AmbientRetractMap(self, self.ambient_crystal(), pdict_inv, index_set=ind, + automorphism=lambda i: i-1) class KR_type_A2Element(KirillovReshetikhinGenericCrystalElement): r""" - Class for the elements in the Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `r """ def e0(self): r""" - Gives `e_0` on self by mapping self to the ambient crystal, calculating `e_0` there and - pulling the element back. + Return `e_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `e_0` there and pulling the element back. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['A',4,2],1,1) + sage: K = crystals.KirillovReshetikhin(['A',4,2],1,1) sage: b = K(rows=[]) sage: b.e(0) # indirect doctest [[-1]] @@ -2254,12 +2063,12 @@ def e0(self): def f0(self): r""" - Gives `f_0` on self by mapping self to the ambient crystal, calculating `f_0` there and - pulling the element back. + Return `f_0` on ``self`` by mapping ``self`` to the ambient crystal, + calculating `f_0` there and pulling the element back. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['A',4,2],1,1) + sage: K = crystals.KirillovReshetikhin(['A',4,2],1,1) sage: b = K(rows=[]) sage: b.f(0) # indirect doctest [[1]] @@ -2271,13 +2080,13 @@ def f0(self): def epsilon0(self): r""" - Calculate `\varepsilon_0` of ``self`` by mapping the element + Return `\varepsilon_0` of ``self`` by mapping the element to the ambient crystal and calculating `\varepsilon_0` there. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['A',4,2], 1,1) - sage: b=K(rows=[[1]]) + sage: b = K(rows=[[1]]) sage: b.epsilon(0) # indirect doctest 2 """ @@ -2286,13 +2095,13 @@ def epsilon0(self): def phi0(self): r""" - Calculate `\varphi_0` of ``self`` by mapping the element to + Return `\varphi_0` of ``self`` by mapping the element to the ambient crystal and calculating `\varphi_0` there. EXAMPLES:: sage: K = crystals.KirillovReshetikhin(['D',3,2], 1,1) - sage: b=K(rows=[[-1]]) + sage: b = K(rows=[[-1]]) sage: b.phi(0) # indirect doctest 2 """ @@ -2345,25 +2154,28 @@ def _element_constructor_(self, *args, **options): # Check to make sure it can be converted if elt.cartan_type() != self.cartan_type() \ or elt.parent().r() != self._r or elt.parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin tableau must have the same Cartan type and shape") + raise ValueError("the Kirillov-Reshetikhin tableau must have the same Cartan type and shape") to_hw = elt.to_classical_highest_weight() - wt = to_hw[0].classical_weight() / 2 + wt = to_hw[0].classical_weight() f_str = reversed(to_hw[1]) for x in self.module_generators: if x.classical_weight() == wt: return x.f_string(f_str) - raise ValueError("No matching highest weight element found") + raise ValueError("no matching highest weight element found") return KirillovReshetikhinGenericCrystal._element_constructor_(self, *args, **options) def classical_decomposition(self): r""" - Specifies the classical crystal underlying the Kirillov-Reshetikhin crystal `B^{n,s}` of type `B_n^{(1)}`. + Return the classical crystal underlying the Kirillov-Reshetikhin + crystal `B^{n,s}` of type `B_n^{(1)}`. - It is the same as for `r0]) for r in b.to_tableau()]) - inter = Partition([len([i for i in r if i>=0]) for r in b.to_tableau()]) + inter1 = Partition([len([i for i in r if i > 0]) for r in b.to_tableau()]) + inter = Partition([len([i for i in r if i >= 0]) for r in b.to_tableau()]) if inter != inter1: inner[n-1] += 2 inner = Partition(inner) @@ -2902,9 +2739,10 @@ def from_highest_weight_vector_to_pm_diagram(self, b): return PMDiagram([n, s, outer, inter, inner], from_shapes=True) def from_pm_diagram_to_highest_weight_vector(self, pm): - """ - This gives the bijection between a `\pm` diagram and an element b in the classical - decomposition of the KR crystal that is {2,3,..,n}-highest weight. + r""" + This gives the bijection between a `\pm` diagram and an element + ``b`` in the classical decomposition of the KR crystal that is + `\{2,3,\ldots,n\}`-highest weight. EXAMPLES:: @@ -2931,7 +2769,8 @@ def from_pm_diagram_to_highest_weight_vector(self, pm): class KR_type_Dn_twistedElement(KirillovReshetikhinGenericCrystalElement): r""" - Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` of type `D_{n+1}^{(2)}`. + Class for the elements in the Kirillov-Reshetikhin crystals `B^{n,s}` + of type `D_{n+1}^{(2)}`. EXAMPLES:: @@ -2942,8 +2781,9 @@ class KR_type_Dn_twistedElement(KirillovReshetikhinGenericCrystalElement): def e0(self): r""" - Gives `e_0` on self by going to the `\pm`-diagram corresponding to the `{2,...,n}`-highest weight - vector in the component of `self`, then applying [Definition 6.2, 4], and pulling back from + Return `e_0` on ``self`` by going to the `\pm`-diagram corresponding + to the `\{2,\ldots,n\}`-highest weight vector in the component of + ``self``, then applying [Definition 6.2, 4], and pulling back from `\pm`-diagrams. EXAMPLES:: @@ -2977,13 +2817,14 @@ def e0(self): def f0(self): r""" - Gives `e_0` on self by going to the `\pm`-diagram corresponding to the `{2,...,n}`-highest weight - vector in the component of `self`, then applying [Definition 6.2, 4], and pulling back from + Return `e_0` on ``self`` by going to the `\pm`-diagram corresponding + to the `\{2,\ldots,n\}`-highest weight vector in the component of + ``self``, then applying [Definition 6.2, 4], and pulling back from `\pm`-diagrams. EXAMPLES:: - sage: K=crystals.KirillovReshetikhin(['D',4,2],3,2) + sage: K = crystals.KirillovReshetikhin(['D',4,2],3,2) sage: b = K.module_generators[0] sage: b.f(0) # indirect doctest """ @@ -2993,7 +2834,7 @@ def f0(self): pm = self.parent().from_highest_weight_vector_to_pm_diagram(b) [l1,l2] = pm.pm_diagram[n-1] l3 = pm.pm_diagram[n-2][0] - if l1+l2+l3==s and l2==0: + if l1+l2+l3 == s and l2 == 0: return None if l1+l2+l3=0 for i in starting_weight.coefficients()): - Parent.__init__( self, category = (RegularCrystals(), - HighestWeightCrystals(), - InfiniteEnumeratedSets()) ) + Parent.__init__( self, category=(RegularCrystals(), + HighestWeightCrystals(), + InfiniteEnumeratedSets()) ) elif starting_weight.parent().is_extended(): - Parent.__init__(self, category = (RegularCrystals(), InfiniteEnumeratedSets())) + Parent.__init__(self, category=(RegularCrystals(), InfiniteEnumeratedSets())) else: - Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals())) + cl = self._cartan_type.classical().index_set() + if sum(self.weight[i] for i in cl) == 1: + cat = KirillovReshetikhinCrystals() + else: + cat = RegularLoopCrystals().Finite() + Parent.__init__(self, category=cat) else: - Parent.__init__(self, category = ClassicalCrystals()) + Parent.__init__(self, category=ClassicalCrystals()) if starting_weight == starting_weight.parent().zero(): initial_element = self(()) @@ -627,12 +633,14 @@ class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths): INPUT: - - ``weight`` -- a dominant weight of the weight space of an affine Kac-Moody root system + - ``weight`` -- a dominant weight of the weight space of an affine + Kac-Moody root system - When ``weight`` is just a single fundamental weight `\Lambda_r`, this crystal is - isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also + When ``weight`` is just a single fundamental weight `\Lambda_r`, this + crystal is isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also :meth:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhinFromLSPaths`. - For general weights, it is isomorphic to a tensor product of single-column KR crystals. + For general weights, it is isomorphic to a tensor product of + single-column KR crystals. EXAMPLES:: @@ -696,10 +704,42 @@ def __classcall_private__(cls, weight): if weight.parent().is_extended(): raise ValueError("The weight should be in the non-extended weight lattice!") La = weight.parent().basis() - weight = weight - (weight.level())*La[0]/(La[0].level()) + weight = weight - weight.level() * La[0] / La[0].level() return super(CrystalOfLSPaths, cls).__classcall__(cls, weight, starting_weight_parent = weight.parent()) - def one_dimensional_configuration_sum(self, q = None, group_components = True): + @cached_method + def maximal_vector(self): + """ + Return the maximal vector of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]+La[2]) + sage: LS.maximal_vector() + (-3*Lambda[0] + 2*Lambda[1] + Lambda[2],) + """ + return self.module_generators[0] + + @cached_method + def classically_highest_weight_vectors(self): + r""" + Return the classically highest weight vectors of ``self``. + + EXAMPLES:: + + sage: R = RootSystem(['A',2,1]) + sage: La = R.weight_space().basis() + sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) + sage: LS.classically_highest_weight_vectors() + ((-2*Lambda[0] + 2*Lambda[1],), + (-Lambda[0] + Lambda[1], -Lambda[1] + Lambda[2])) + """ + I0 = self.cartan_type().classical().index_set() + return tuple([x for x in self.list() if x.is_highest_weight(I0)]) + + def one_dimensional_configuration_sum(self, q=None, group_components=True): r""" Compute the one-dimensional configuration sum. @@ -710,10 +750,12 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): - ``group_components`` -- (default: ``True``) boolean; if ``True``, then the terms are grouped by classical component - The one-dimensional configuration sum is the sum of the weights of all elements in the crystal - weighted by the energy function. For untwisted types it uses the parabolic quantum Bruhat graph, see [LNSSS2013]_. - In the dual-of-untwisted case, the parabolic quantum Bruhat graph is defined by - exchanging the roles of roots and coroots (which is still conjectural at this point). + The one-dimensional configuration sum is the sum of the weights + of all elements in the crystal weighted by the energy function. + For untwisted types it uses the parabolic quantum Bruhat graph, + see [LNSSS2013]_. In the dual-of-untwisted case, the parabolic + quantum Bruhat graph is defined by exchanging the roles of roots + and coroots (which is still conjectural at this point). EXAMPLES:: @@ -722,11 +764,13 @@ def one_dimensional_configuration_sum(self, q = None, group_components = True): sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) sage: LS.one_dimensional_configuration_sum() # long time B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] - + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] + + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] sage: R. = ZZ[] sage: LS.one_dimensional_configuration_sum(t, False) # long time - B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] + B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + + (t+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] TESTS:: @@ -775,7 +819,7 @@ def weight(x): def is_perfect(self, level=1): r""" - Checks whether the crystal ``self`` is perfect (of level ``level``). + Check whether the crystal ``self`` is perfect (of level ``level``). INPUT: @@ -783,15 +827,18 @@ def is_perfect(self, level=1): A crystal `\mathcal{B}` is perfect of level `\ell` if: - #. `\mathcal{B}` is isomorphic to the crystal graph of a finite-dimensional `U_q^{'}(\mathfrak{g})`-module. + #. `\mathcal{B}` is isomorphic to the crystal graph of a + finite-dimensional `U_q^{'}(\mathfrak{g})`-module. #. `\mathcal{B}\otimes \mathcal{B}` is connected. - #. There exists a `\lambda\in X`, such that `\mathrm{wt}(\mathcal{B}) \subset \lambda - + \sum_{i\in I} \mathbb{Z}_{\le 0} \alpha_i` and there is a unique element in `\mathcal{B}` of classical - weight `\lambda`. - #. `\forall b \in \mathcal{B}, \mathrm{level}(\varepsilon (b)) \geq \ell`. - #. `\forall \Lambda` dominant weights of level `\ell`, there exist unique elements - `b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`, - such that `\varepsilon ( b_{\Lambda}) = \Lambda = \varphi( b^{\Lambda})`. + #. There exists a `\lambda\in X`, such that + `\mathrm{wt}(\mathcal{B}) \subset \lambda + \sum_{i\in I} \ZZ_{\le 0} \alpha_i` + and there is a unique element in + `\mathcal{B}` of classical weight `\lambda`. + #. For all `b \in \mathcal{B}`, + `\mathrm{level}(\varepsilon (b)) \geq \ell`. + #. For all `\Lambda` dominant weights of level `\ell`, there exist + unique elements `b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`, such + that `\varepsilon (b_{\Lambda}) = \Lambda = \varphi(b^{\Lambda})`. Points (1)-(3) are known to hold. This method checks points (4) and (5). @@ -887,11 +934,13 @@ def scalar_factors(self): s = 0 for c in self.value: supp = c.support() - if len(supp) > 0: - for w in weight.orbit(): - i = supp[0] + if supp: + i = supp[0] + for w in weight._orbit_iter(): # Check whether the vectors c and w are positive scalar multiples of each other - if i in w.support() and c[i]*w[i] > 0 and c[i]*w == w[i]*c: + # If i is not in the support of w, then the first + # product is 0 + if c[i] * w[i] > 0 and c[i] * w == w[i] * c: s += c[i] / w[i] l += [s] break @@ -920,11 +969,11 @@ def weyl_group_representation(self): sage: b = LS.module_generators[0] sage: c = b.f(1).f(3).f(2) sage: c.weyl_group_representation() - [s2*s3*s1, s3*s1] + [s2*s1*s3, s1*s3] """ cartan = self.parent().weight.parent().cartan_type().classical() I = cartan.index_set() - W = WeylGroup(cartan,prefix='s') + W = WeylGroup(cartan, prefix='s', implementation="permutation") return [W.from_reduced_word(x.to_dominant_chamber(index_set=I, reduced_word=True)[1]) for x in self.value] @cached_in_parent_method @@ -932,58 +981,70 @@ def energy_function(self): r""" Return the energy function of ``self``. - The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)` - requires a series of definitions; for simplicity the root system is assumed to be untwisted affine. + The energy function `D(\pi)` of the level zero LS path + `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)` requires a series + of definitions; for simplicity the root system is assumed to + be untwisted affine. - The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice. - It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors" - `x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the - stabilizer of `\lambda` in the finite Weyl group `W`. Precisely, + The LS path `\pi` is a piecewise linear map from the unit + interval `[0,1]` to the weight lattice. It is specified by + "times" `0 = \sigma_0 < \sigma_1 < \dotsm < \sigma_s = 1` and + "direction vectors" `x_u \lambda` where `x_u \in W / W_J` for + `1 \le u \le s`, and `W_J` is the stabilizer of `\lambda` in + the finite Weyl group `W`. Precisely, .. MATH:: - \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda + \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1}) + x_{u'} \lambda + (t-\sigma_{u-1}) x_{u} \lambda - for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`. + for `1 \le u \le s` and `\sigma_{u-1} \le t \le \sigma_{u}`. - For any `x,y\in W/W_J` let + For any `x,y \in W / W_J`, let .. MATH:: - d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow} + d: x = w_{0} \stackrel{\beta_{1}}{\leftarrow} w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots \stackrel{\beta_{n}}{\leftarrow} w_{n}=y - be a shortest directed path in the parabolic quantum Bruhat graph. Define + be a shortest directed path in the parabolic quantum + Bruhat graph. Define .. MATH:: - \mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\ \ell(w_{k-1})<\ell(w_k)}} - \beta_{k}^{\vee} + \mathrm{wt}(d) := \sum_{\substack{1 \le k \le n + \\ \ell(w_{k-1}) < \ell(w_k)}} + \beta_{k}^{\vee}. It can be shown that `\mathrm{wt}(d)` depends only on `x,y`; - call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by + call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` + is defined by .. MATH:: - D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle + D(\pi) = -\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda, + \mathrm{wt}(x_u,x_{u+1}) \rangle. For more information, see [LNSSS2013]_. REFERENCES: .. [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`}}} ) + *A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.* + DMTCS proc, to appear ( :arXiv:`1211.6019` ) .. NOTE:: - In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by - exchanging the roles of roots and coroots. Moreover, in the computation of the - pairing the short roots must be doubled (or tripled for type `G`). This factor - is determined by the translation factor of the corresponding root. - Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted. - Except for the untwisted cases, these formulas are currently still conjectural. + In the dual-of-untwisted case the parabolic quantum + Bruhat graph that is used is obtained by exchanging the + roles of roots and coroots. Moreover, in the computation + of the pairing the short roots must be doubled (or tripled + for type `G`). This factor is determined by the translation + factor of the corresponding root. Type `BC` is viewed as + untwisted type, whereas the dual of `BC` is viewed as twisted. + Except for the untwisted cases, these formulas are + currently still conjectural. EXAMPLES:: @@ -1017,7 +1078,8 @@ def energy_function(self): (Lambda[0] - Lambda[2], -Lambda[0] + Lambda[1]) 0 (Lambda[0] - Lambda[2], -Lambda[1] + Lambda[2]) 0 - The next test checks that the energy function is constant on classically connected components:: + The next test checks that the energy function is constant + on classically connected components:: sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() @@ -1071,7 +1133,7 @@ def energy_function(self): ct = P.cartan_type() cartan = ct.classical() Qv = RootSystem(cartan).coroot_lattice() - W = WeylGroup(cartan,prefix='s') + W = WeylGroup(cartan, prefix='s', implementation="permutation") J = tuple(weight.weyl_stabilizer()) L = self.weyl_group_representation() if ct.is_untwisted_affine() or ct.type() == 'BC': @@ -1080,7 +1142,7 @@ def energy_function(self): else: untwisted = False cartan_dual = cartan.dual() - Wd = WeylGroup(cartan_dual, prefix='s') + Wd = WeylGroup(cartan_dual, prefix='s', implementation="permutation") G = Wd.quantum_bruhat_graph(J) Qd = RootSystem(cartan_dual).root_lattice() dualize = lambda x: Qv.from_vector(x.to_vector()) diff --git a/src/sage/combinat/crystals/monomial_crystals.py b/src/sage/combinat/crystals/monomial_crystals.py index 5889a681b2f..55dd94a9bc4 100644 --- a/src/sage/combinat/crystals/monomial_crystals.py +++ b/src/sage/combinat/crystals/monomial_crystals.py @@ -982,30 +982,6 @@ def cardinality(self): """ return Infinity - def weight_lattice_realization(self): - r""" - Return the weight lattice realization of ``self``. - - EXAMPLES:: - - sage: M = crystals.infinity.NakajimaMonomials(['A',3,2]) - sage: M.weight_lattice_realization() - Extended weight lattice of the Root system of type ['B', 2, 1]^* - sage: M = crystals.infinity.NakajimaMonomials(['A',2]) - sage: M.weight_lattice_realization() - Ambient space of the Root system of type ['A', 2] - sage: A = CartanMatrix([[2,-3],[-3,2]]) - sage: M = crystals.infinity.NakajimaMonomials(A) - sage: M.weight_lattice_realization() - Weight lattice of the Root system of type Dynkin diagram of rank 2 - """ - F = self.cartan_type().root_system() - if self.cartan_type().is_finite() and F.ambient_space() is not None: - return F.ambient_space() - if self.cartan_type().is_affine(): - return F.weight_lattice(extended=True) - return F.weight_lattice() - def set_variables(self, letter): r""" Set the type of monomials to use for the element output. diff --git a/src/sage/combinat/crystals/mv_polytopes.py b/src/sage/combinat/crystals/mv_polytopes.py new file mode 100644 index 00000000000..7a0729f036b --- /dev/null +++ b/src/sage/combinat/crystals/mv_polytopes.py @@ -0,0 +1,469 @@ +# -*- coding: utf-8 -*- +r""" +Crystal Of Mirković-Vilonen (MV) Polytopes + +AUTHORS: + +- Dinakar Muthiah, Travis Scrimshaw (2015-05-11): initial version +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# 2015 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.crystals.pbw_crystal import PBWCrystalElement, PBWCrystal + +class MVPolytope(PBWCrystalElement): + """ + A Mirković-Vilonen (MV) polytope. + + EXAMPLES: + + We can create an animation showing how the MV polytope changes + under a string of crystal operators:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: u = MV.highest_weight_vector() + sage: L = RootSystem(['C',2,1]).ambient_space() + sage: s = [1,2,1,2,2,2,1,1,1,1,2,1,2,2,1,2] + sage: BB = [[-9, 2], [-10, 2]] + sage: p = L.plot(reflection_hyperplanes=False, bounding_box=BB) # long time + sage: frames = [p + L.plot_mv_polytope(u.f_string(s[:i]), # long time + ....: circle_size=0.1, + ....: wireframe='green', + ....: fill='purple', + ....: bounding_box=BB) + ....: for i in range(len(s))] + sage: for f in frames: # long time + ....: f.axes(False) + sage: animate(frames).show(delay=60) # optional -- ImageMagick # long time + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['E', 6]) + sage: b = MV.module_generators[0].f_string([1,2,6,4,3,2,5,2]) + sage: b + MV polytope with Lusztig datum (0, 1, ..., 1, 0, 0, 0, 0, 0, 0, 3, 1) + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + return "MV polytope with Lusztig datum {}".format(pbw_datum.lusztig_datum) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-1, 1) -- (-1, 1) -- (-2, 0) -- (-2, -2); + \draw (0, 0) -- (0, -2) -- (-1, -3) -- (-1, -3) -- (-2, -2); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (-2, -2) circle (0.1); + \end{tikzpicture} + sage: MV = crystals.infinity.MVPolytopes(['D',4]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: latex(b) + \text{\texttt{MV{ }polytope{ }...}} + + TESTS:: + + sage: MV = crystals.infinity.MVPolytopes(['A',2]) + sage: u = MV.highest_weight_vector() + sage: b = u.f_string([1,2,2,1]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (3/2, -989/1142) -- (3/2, -2967/1142) -- (0, -1978/571); + \draw (0, 0) -- (-3/2, -989/1142) -- (-3/2, -2967/1142) -- (0, -1978/571); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (0, -1978/571) circle (0.1); + \end{tikzpicture} + """ + latex_options = self.parent()._latex_options + P = latex_options['P'] + plot_options = P.plot_parse_options(projection=latex_options["projection"]) + proj = plot_options.projection + if proj(P.zero()).parent().dimension() != 2: + from sage.misc.latex import latex + return latex(repr(self)) + + # We need this to use tikz + from sage.graphs.graph_latex import setup_latex_preamble + setup_latex_preamble() + + pbw_data = self._pbw_datum.parent + W = pbw_data.weyl_group + w0 = W.long_element() + al = P.simple_roots() + ret = "\\begin{tikzpicture}\n" + + final = None + for red in w0.reduced_words(): + ret += "\\draw " + cur = proj(P.zero()) + red = tuple(red) + ret += str(cur) + roots = [proj(P.sum(c*al[a] for a,c in root)) + for root in pbw_data._root_list_from(red)] + datum = pbw_data.convert_to_new_long_word(self._pbw_datum, red) + for i in reversed(range(len(datum.lusztig_datum))): + cur -= roots[i] * datum.lusztig_datum[i] + ret += " -- " + str(cur) + final = cur + ret += ";\n" + + if latex_options["mark_endpoints"]: + circle_size = latex_options["circle_size"] + ret += "\\draw[fill=black] {} circle ({});\n".format(proj(P.zero()), circle_size) + ret += "\\draw[fill=black] {} circle ({});\n".format(final, circle_size) + ret += "\\end{tikzpicture}" + return ret + + def _polytope_vertices(self, P): + """ + Return a list of the vertices of ``self`` in ``P``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 3]) + sage: b = MV.module_generators[0].f_string([1,2,1,2]) + sage: sorted(b._polytope_vertices(MV.weight_lattice_realization()), key=list) + [(0, 0, 0), (2, 0, -2), (0, 2, -2)] + + sage: MV = crystals.infinity.MVPolytopes(['D', 4]) + sage: b = MV.module_generators[0].f_string([1,2,3,4]) + sage: P = RootSystem(['D',4]).weight_lattice() + sage: sorted(b._polytope_vertices(P), key=list) # long time + [0, + -Lambda[1] + Lambda[3] + Lambda[4], + Lambda[1] - Lambda[2] + Lambda[3] + Lambda[4], + -2*Lambda[2] + 2*Lambda[3] + 2*Lambda[4], + -Lambda[2] + 2*Lambda[3], + -Lambda[2] + 2*Lambda[4]] + """ + pbw_data = self._pbw_datum.parent + W = pbw_data.weyl_group + w0 = W.long_element() + al = P.simple_roots() + + vertices = set([P.zero()]) + for red in w0.reduced_words(): + cur = P.zero() + red = tuple(red) + roots = [P.sum(c*al[a] for a,c in root) + for root in pbw_data._root_list_from(red)] + datum = pbw_data.convert_to_new_long_word(self._pbw_datum, red) + for i,c in enumerate(datum.lusztig_datum): + cur = cur + roots[i] * c + vertices.add(cur) + return list(vertices) + + def polytope(self, P=None): + """ + Return a polytope of ``self``. + + INPUT: + + - ``P`` -- (optional) a space to realize the polytope; default is + the weight lattice realization of the crystal + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 3]) + sage: b = MV.module_generators[0].f_string([3,2,3,2,1]) + sage: P = b.polytope(); P + A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 6 vertices + sage: P.vertices() + (A vertex at (0, 0, 0), + A vertex at (0, 1, -1), + A vertex at (0, 1, 1), + A vertex at (1, -1, 0), + A vertex at (1, 1, -2), + A vertex at (1, 1, 2)) + """ + if P is None: + P = self.parent().weight_lattice_realization() + + from sage.geometry.polyhedron.constructor import Polyhedron + return Polyhedron([v.to_vector() for v in self._polytope_vertices(P)]) + + def plot(self, P=None, **options): + """ + Plot ``self``. + + INPUT: + + - ``P`` -- (optional) a space to realize the polytope; default is + the weight lattice realization of the crystal + + .. SEEALSO:: + + :meth:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations.ParentMethods.plot_mv_polytope` + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: b = MV.highest_weight_vector().f_string([1,2,1,2,2,2,1,1,1,1,2,1]) + sage: b.plot() + Graphics object consisting of 12 graphics primitives + + Here is the above example placed inside the ambient space + of type `C_2`: + + .. PLOT:: + :width: 300 px + + MV = crystals.infinity.MVPolytopes(['C', 2]) + b = MV.highest_weight_vector().f_string([1,2,1,2,2,2,1,1,1,1,2,1]) + L = RootSystem(['C', 2, 1]).ambient_space() + p = L.plot(reflection_hyperplanes=False, bounding_box=[[-8,2], [-8,2]]) + p += b.plot() + p.axes(False) + sphinx_plot(p) + """ + if P is None: + P = self.parent().weight_lattice_realization() + return P.plot_mv_polytope(self, **options) + +class MVPolytopes(PBWCrystal): + r""" + The crystal of Mirković-Vilonen (MV) polytopes. + + Let `W` denote the corresponding Weyl group and `P_{\RR} = \RR \otimes P`. + Let `\Gamma = \{ w \Lambda_i \mid w \in W, i \in I \}`. Consider + `M = (M_{\gamma} \in \ZZ)_{\gamma \in \Gamma}` that satisfy the + *tropical Plücker relations* (see Proposition 7.1 of [BZ01]_). + The *MV polytope* is defined as + + .. MATH:: + + P(M) = \{ \alpha \in P_{\RR} \mid + \langle \alpha, \gamma \rangle \geq M_{\gamma} + \text{ for all } \gamma \in \Gamma \}. + + The vertices `\{\mu_w\}_{w \in W}` are given by + + .. MATH:: + + \langle \mu_w, \gamma \rangle = M_{\gamma} + + and are known as the GGMS datum of the MV polytope. + + Each path from `\mu_e` to `\mu_{w_0}` corresponds to a reduced + expression `\mathbf{i} = (i_1, \ldots, i_m)` for `w_0` and the + corresponding edge lengths `(n_k)_{k=1}^m` from the Lusztig datum + with respect to `\mathbf{i}`. Explicitly, we have + + .. MATH:: + + \begin{aligned} + n_k & = -M_{w_{k-1} \Lambda_{i_k}} - M_{w_k \Lambda_{i_k}} + - \sum_{j \neq i} a_{ji} M_{w_k \Lambda_j}, + \\ \mu_{w_k} - \mu_{w_{k-1}} & = n_k w_{k-1} \alpha_{i_k}, + \end{aligned} + + where `w_k = s_{i_1} \cdots s_{i_k}` and `(a_{ji})` is the Cartan matrix. + + MV polytopes have a crystal structure that corresponds to the + crystal structure, which is isomorphic to `\mathcal{B}(\infty)` + with `\mu_{w_0} = 0`, on + :class:`PBW data `. + Specifically, we have `f_j P(M)` as being the unique MV polytope + given by shifting `\mu_e` by `-\alpha_j` and fixing the vertices + `\mu_w` when `s_j w < w` (in Bruhat order) and the weight is given by + `\mu_e`. Furthermore, the `*`-involution is given by negating `P(M)`. + + INPUT: + + - ``cartan_type`` -- a Cartan type + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['B', 3]) + sage: hw = MV.highest_weight_vector() + sage: x = hw.f_string([1,2,2,3,3,1,3,3,2,3,2,1,3,1,2,3,1,2,1,3,2]); x + MV polytope with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + Elements are expressed in terms of Lusztig datum for a fixed + reduced expression of `w_0`:: + + sage: MV.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: MV.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: x + MV polytope with Lusztig datum (3, 1, 1, 0, 1, 0, 1, 3, 4) + sage: MV.set_default_long_word([1, 3, 2, 3, 1, 2, 3, 1, 2]) + + We can construct elements by giving it Lusztig data (with respect + to the default long word reduced expression):: + + sage: MV([1,1,1,3,1,0,0,1,1]) + MV polytope with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + We can also construct elements by passing in a reduced expression + for a long word:: + + sage: x = MV([1,1,1,3,1,0,0,1,1], [3,2,1,3,2,3,2,1,2]); x + MV polytope with Lusztig datum (1, 1, 1, 0, 1, 0, 5, 1, 1) + sage: x.to_highest_weight()[1] + [1, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 2, 1, 3] + + The highest weight crystal `B(\lambda) \subseteq B(\infty)` is + characterized by the MV polytopes that sit inside of `W \lambda` + (translating `\mu_{w_0} \mapsto \lambda`):: + + sage: MV = crystals.infinity.MVPolytopes(['A',2]) + sage: La = MV.weight_lattice_realization().fundamental_weights() + sage: R = crystals.elementary.R(La[1]+La[2]) + sage: T = tensor([R, MV]) + sage: x = T(R.module_generators[0], MV.highest_weight_vector()) + sage: lw = x.to_lowest_weight()[0]; lw + [(2, 1, 0), MV polytope with Lusztig datum (1, 1, 1)] + sage: lw[1].polytope().vertices() + (A vertex at (0, 0, 0), + A vertex at (0, 1, -1), + A vertex at (1, -1, 0), + A vertex at (1, 1, -2), + A vertex at (2, -1, -1), + A vertex at (2, 0, -2)) + + .. PLOT:: + :width: 300 px + + MV = crystals.infinity.MVPolytopes(['A',2]) + x = MV.module_generators[0].f_string([1,2,2,1]) + L = RootSystem(['A',2,1]).ambient_space() + p = L.plot(bounding_box=[[-2,2],[-4,2]]) + x.plot() + p.axes(False) + sphinx_plot(x.plot()) + + REFERENCES: + + - [Kam2007]_ + - [Kam2010]_ + """ + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['B', 2]) + sage: TestSuite(MV).run() + """ + PBWCrystal.__init__(self, cartan_type) + self._latex_options = {"projection": True, + "mark_endpoints": True, + "P": self.weight_lattice_realization(), + "circle_size": 0.1} + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: crystals.infinity.MVPolytopes(['F', 4]) + MV polytopes of type ['F', 4] + """ + return "MV polytopes of type {}".format(self._cartan_type) + + def set_latex_options(self, **kwds): + r""" + Set the latex options for the elements of ``self``. + + INPUT: + + - ``projection`` -- the projection; set to ``True`` to use the + default projection of the specified weight lattice realization + (initial: ``True``) + - ``P`` -- the weight lattice realization to use (initial: the + weight lattice realization of ``self``) + - ``mark_endpoints`` -- whether to mark the endpoints (initial: ``True``) + - ``circle_size`` -- the size of the endpoint circles (initial: 0.1) + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['C', 2]) + sage: P = RootSystem(['C', 2]).weight_lattice() + sage: b = MV.highest_weight_vector().f_string([1,2,1,2]) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-1, 1) -- (-1, 1) -- (-2, 0) -- (-2, -2); + \draw (0, 0) -- (0, -2) -- (-1, -3) -- (-1, -3) -- (-2, -2); + \draw[fill=black] (0, 0) circle (0.1); + \draw[fill=black] (-2, -2) circle (0.1); + \end{tikzpicture} + sage: MV.set_latex_options(P=P, circle_size=float(0.2)) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-2, 1) -- (-2, 1) -- (-2, 0) -- (0, -2); + \draw (0, 0) -- (2, -2) -- (2, -3) -- (2, -3) -- (0, -2); + \draw[fill=black] (0, 0) circle (0.2); + \draw[fill=black] (0, -2) circle (0.2); + \end{tikzpicture} + sage: MV.set_latex_options(mark_endpoints=False) + sage: latex(b) + \begin{tikzpicture} + \draw (0, 0) -- (-2, 1) -- (-2, 1) -- (-2, 0) -- (0, -2); + \draw (0, 0) -- (2, -2) -- (2, -3) -- (2, -3) -- (0, -2); + \end{tikzpicture} + sage: MV.set_latex_options(P=MV.weight_lattice_realization(), + ....: circle_size=0.2, + ....: mark_endpoints=True) + """ + if "projection" in kwds: + self._latex_options["projection"] = True + del kwds["projection"] + + if 'P' in kwds: + self._latex_options['P'] = kwds['P'] + del kwds['P'] + + if "mark_endpoints" in kwds: + self._latex_options["mark_endpoints"] = kwds["mark_endpoints"] + del kwds["mark_endpoints"] + + if "circle_size" in kwds: + self._latex_options["circle_size"] = kwds["circle_size"] + del kwds["circle_size"] + + if kwds: + raise ValueError("invalid latex option") + + def latex_options(self): + """ + Return the latex options of ``self``. + + EXAMPLES:: + + sage: MV = crystals.infinity.MVPolytopes(['F', 4]) + sage: MV.latex_options() + {'P': Ambient space of the Root system of type ['F', 4], + 'circle_size': 0.1, + 'mark_endpoints': True, + 'projection': True} + """ + from copy import copy + return copy(self._latex_options) + + Element = MVPolytope + diff --git a/src/sage/combinat/crystals/pbw_crystal.py b/src/sage/combinat/crystals/pbw_crystal.py new file mode 100644 index 00000000000..4edb3576587 --- /dev/null +++ b/src/sage/combinat/crystals/pbw_crystal.py @@ -0,0 +1,509 @@ +# -*- coding: utf-8 -*- +r""" +`\mathcal{B}(\infty)` Crystal Of PBW Monomials. + +AUTHORS: + +- Dinakar Muthiah (2015-05-11): initial version + +.. SEEALSO:: + + For infromation on PBW datum, see + :ref:`sage.combinat.crystals.pbw_datum`. +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# +# 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.structure.element import Element +from sage.structure.parent import Parent +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.sage_object import richcmp +from sage.categories.highest_weight_crystals import HighestWeightCrystals +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + +class PBWCrystalElement(Element): + """ + A crystal element in the PBW model. + """ + def __init__(self, parent, lusztig_datum, long_word=None): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['F', 4]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + sage: TestSuite(b).run() + """ + Element.__init__(self, parent) + if long_word is None: + long_word = parent._default_word + self._pbw_datum = PBWDatum(parent._pbw_datum_parent, long_word, lusztig_datum) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 4]) + sage: u = B.highest_weight_vector() + sage: u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + PBW monomial with Lusztig datum + (0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 2) + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + return "PBW monomial with Lusztig datum {}".format(pbw_datum.lusztig_datum) + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['F', 4]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([1,2,3,4,2,3,2,3,4,1,2]) + sage: latex(b) + f_{\alpha_{4}}^{2} + f_{\alpha_{3}} + f_{\alpha_{1} + \alpha_{2} + 2\alpha_{3}} + f_{\alpha_{1} + \alpha_{2}} + f_{\alpha_{2}}^{2} + """ + pbw_datum = self._pbw_datum.convert_to_new_long_word(self.parent()._default_word) + lusztig_datum = list(pbw_datum.lusztig_datum) + al = self.parent()._pbw_datum_parent._root_list_from(self.parent()._default_word) + from sage.misc.latex import latex + ret_str = ' '.join("f_{%s}%s"%(latex(al[i]), "^{%s}"%latex(exp) if exp > 1 else "") + for i, exp in enumerate(lusztig_datum) if exp) + if ret_str == '': + return '1' + return ret_str + + def lusztig_datum(self, word=None): + """ + Return the Lusztig datum of ``self`` with respect to the reduced + expression of the long word ``word``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: b.lusztig_datum() + (6, 0, 10) + sage: b.lusztig_datum(word=[2,1,2]) + (4, 6, 0) + """ + if word is None: + word = self.parent()._default_word + else: + self.parent()._check_is_long_word(word) + word = tuple(word) + pbw_datum = self._pbw_datum.convert_to_new_long_word(word) + return tuple(pbw_datum.lusztig_datum) + + def __eq__(self, other): + """ + Check equality of ``self`` with ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: b == bp + True + """ + if other not in self.parent(): + return False + other_long_word = other._pbw_datum.long_word + other_lusztig_datum = other._pbw_datum.lusztig_datum + equiv_pbw_datum = self._pbw_datum.convert_to_new_long_word(other_long_word) + return equiv_pbw_datum.lusztig_datum == other_lusztig_datum + + def __ne__(self, other): + """ + Check inequality of ``self`` with ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: b != bp + False + """ + return not (self == other) + + # Necessary for displaying subcrystals + def _richcmp_(self, other, op): + """ + Return comparison of ``self`` and ``other``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2]) + sage: w = [1, 2, 1] + sage: (b < bp) == (b.lusztig_datum(w) < bp.lusztig_datum(w)) + True + sage: (b > bp) == (b.lusztig_datum(w) > bp.lusztig_datum(w)) + True + """ + i = self.parent().index_set()[0] + word = self.parent()._pbw_datum_parent._long_word_begin_with(i) + lusztig_datum = tuple(self._pbw_datum.convert_to_new_long_word(word).lusztig_datum) + other_lusztig_datum = tuple(other._pbw_datum.convert_to_new_long_word(word).lusztig_datum) + return richcmp(lusztig_datum, other_lusztig_datum, op) + + @cached_method + def __hash__(self): + """ + Return the hash of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: u = B.highest_weight_vector() + sage: b = u.f_string([2,1,2,2,2,2,1,1,2,1,2,1,2,1,2,2]) + sage: bp = u.f_string([2,1,2,2,1,1,2,2,2,1,2,1,2,2,1,2]) + sage: hash(b) == hash(bp) + True + """ + i = self.parent().index_set()[0] + word = self.parent()._pbw_datum_parent._long_word_begin_with(i) + pbw_datum = self._pbw_datum.convert_to_new_long_word(word) + return hash(tuple(pbw_datum.lusztig_datum)) + + def e(self, i): + """ + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 3]) + sage: b = B.highest_weight_vector() + sage: c = b.f_string([2,1,3,2,1,3,2,2]); c + PBW monomial with Lusztig datum (0, 1, 0, 1, 0, 0, 0, 1, 2) + sage: c.e(2) + PBW monomial with Lusztig datum (0, 1, 0, 1, 0, 0, 0, 1, 1) + sage: c.e_string([2,2,1,3,2,1,3,2]) == b + True + """ + equiv_pbw_datum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + new_long_word = equiv_pbw_datum.long_word + new_lusztig_datum = list(equiv_pbw_datum.lusztig_datum) + if new_lusztig_datum[0] == 0: + return None + new_lusztig_datum[0] -= 1 + return type(self)(self.parent(), tuple(new_lusztig_datum), new_long_word) + + def f(self, i): + """ + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW("D4") + sage: b = B.highest_weight_vector() + sage: c = b.f_string([1,2,3,1,2,3,4]); c + PBW monomial with Lusztig datum (0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0) + sage: c == b.f_string([1,2,4,1,2,3,3]) + True + """ + equiv_PBWDatum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + new_long_word = equiv_PBWDatum.long_word + new_lusztig_datum = list(equiv_PBWDatum.lusztig_datum) + new_lusztig_datum[0] += 1 + return type(self)(self.parent(), tuple(new_lusztig_datum), new_long_word) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(["A2"]) + sage: s = B((3,0,0), (1,2,1)) + sage: s.epsilon(1) + 3 + sage: s.epsilon(2) + 0 + """ + equiv_pbw_datum = self._pbw_datum.convert_to_long_word_with_first_letter(i) + return equiv_pbw_datum.lusztig_datum[0] + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: s = B((3,0,0), (1,2,1)) + sage: s.phi(1) + -3 + sage: s.phi(2) + 3 + """ + WLR = self.parent().weight_lattice_realization() + h = WLR.simple_coroots() + return self.epsilon(i) + self.weight().scalar(h[i]) + + def weight(self): + """ + Return weight of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 2]) + sage: s = B((2,2,2), (1,2,1)) + sage: s.weight() + (-4, 0, 4) + """ + WLR = self.parent().weight_lattice_realization() + al = WLR.simple_roots() + return WLR.sum(c*al[i] for i,c in self._pbw_datum.weight()) + + def star(self): + r""" + Return the starred crystal element corresponding + to ``self``. + + Let `b` be an element of ``self`` with Lusztig datum + `(b_1, \ldots, b_N)` with respect to `w_0 = s_{i_1} \cdots s_{i_N}`. + Then `b^*` is the element with Lusztig datum `(b_N, \ldots, b_1)` + with respect to `w_0 = s_{i_N^*} \cdots s_{i_1^*}`, where + `i_j^* = \omega(i_j)` with `\omega` being the :meth:`automorphism + ` + given by the action of `w_0` on the simple roots. + + EXAMPLES:: + + sage: P = crystals.infinity.PBW(['A', 2]) + sage: P((1,2,3), (1,2,1)).star() == P((3,2,1), (2,1,2)) + True + + sage: B = crystals.infinity.PBW(['E', 6]) + sage: b = B.highest_weight_vector() + sage: c = b.f_string([1,2,6,3,4,2,5,2,3,4,1,6]) + sage: c == c.star().star() + True + + TESTS:: + + sage: from itertools import product + sage: def test_star(PBW, depth): + ....: S = crystals.infinity.Star(PBW) + ....: for f_str in product(*([PBW.index_set()]*depth)): + ....: x = PBW.highest_weight_vector().f_string(f_str).star() + ....: y = S.highest_weight_vector().f_string(f_str) + ....: assert x.lusztig_datum() == y.value.lusztig_datum() + sage: P = crystals.infinity.PBW(['A', 2]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['A', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['B', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['C', 3]) + sage: test_star(P, 5) + sage: P = crystals.infinity.PBW(['D', 4]) + sage: test_star(P, 5) # long time + sage: P = crystals.infinity.PBW(['D', 5]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['E', 6]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['F', 4]) + sage: test_star(P, 4) # long time + sage: P = crystals.infinity.PBW(['G', 2]) + sage: test_star(P, 5) + """ + starred_pbw_datum = self._pbw_datum.star() + return type(self)(self.parent(), starred_pbw_datum.lusztig_datum, + starred_pbw_datum.long_word) + + +class PBWCrystal(Parent, UniqueRepresentation): + r""" + Crystal of `\mathcal{B}(\infty)` given by PBW monomials. + + A model of the crystal `\mathcal{B}(\infty)` whose elements are + PBW datum up to equivalence by the tropical Plücker relations. + The crystal structure on Lusztig data `x = (x_1, \ldots, x_m)` + for the reduced word `s_{i_1} \cdots s_{i_m} = w_0` is given as + follows. Suppose `i_1 = j`, then `f_j x = (x_1 + 1, x_2, \ldots, x_m)`. + If `i_1 \neq j`, then we use the tropical Plücker relations to + change the reduced expression such that `i_1' = j` and then we + change back to the original word. + + EXAMPLES:: + + sage: PBW = crystals.infinity.PBW(['B', 3]) + sage: hw = PBW.highest_weight_vector() + sage: x = hw.f_string([1,2,2,3,3,1,3,3,2,3,2,1,3,1,2,3,1,2,1,3,2]); x + PBW monomial with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + Elements are expressed in terms of Lusztig datum for a fixed + reduced expression of `w_0`:: + + sage: PBW.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: PBW.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: x + PBW monomial with Lusztig datum (3, 1, 1, 0, 1, 0, 1, 3, 4) + sage: PBW.set_default_long_word([1, 3, 2, 3, 1, 2, 3, 1, 2]) + + We can construct elements by giving it Lusztig data (with respect + to the default long word):: + + sage: PBW([1,1,1,3,1,0,0,1,1]) + PBW monomial with Lusztig datum (1, 1, 1, 3, 1, 0, 0, 1, 1) + + We can also construct elements by passing in a reduced expression + for a long word:: + + sage: x = PBW([1,1,1,3,1,0,0,1,1], [3,2,1,3,2,3,2,1,2]); x + PBW monomial with Lusztig datum (1, 1, 1, 0, 1, 0, 5, 1, 1) + sage: x.to_highest_weight()[1] + [1, 2, 2, 2, 2, 2, 1, 3, 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 2, 1, 3] + """ + @staticmethod + def __classcall__(cls, cartan_type): + """ + Normalize input to ensure a unique representation. + + EXAMPLES:: + + sage: B1 = crystals.infinity.PBW(['A', 2]) + sage: B2 = crystals.infinity.PBW("A2") + sage: B3 = crystals.infinity.PBW(CartanType("A2")) + sage: B1 is B2 and B2 is B3 + True + """ + cartan_type = CartanType(cartan_type) + if not cartan_type.is_finite(): + raise NotImplementedError("only implemented for finite types") + return super(PBWCrystal, cls).__classcall__(cls, cartan_type) + + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['B', 2]) + sage: TestSuite(B).run() + """ + self._cartan_type = cartan_type + self._pbw_datum_parent = PBWData(self._cartan_type) + category = (HighestWeightCrystals(), InfiniteEnumeratedSets()) + Parent.__init__(self, category=category) + + # There must be a better way to do the following + i = self._cartan_type.index_set()[0] + self._default_word = self._pbw_datum_parent._long_word_begin_with(i) + zero_lusztig_datum = [0]*len(self._default_word) + self.module_generators = (self.element_class(self, + zero_lusztig_datum, + self._default_word),) + + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: crystals.infinity.PBW(['C', 3]) + Crystal of PBW data of type ['C', 3] + """ + return "Crystal of PBW data of type {}".format(self._cartan_type) + + def default_long_word(self): + """ + Return the default long word used to express elements of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['E', 6]) + sage: B.default_long_word() + [1, 3, 4, 5, 6, 2, 4, 5, 3, 4, 1, 3, 2, 4, 5, 6, 2, 4, + 5, 3, 4, 1, 3, 2, 4, 5, 3, 4, 1, 3, 2, 4, 1, 3, 2, 1] + """ + return list(self._default_word) + + def _check_is_long_word(self, word): + """ + Check if ``word`` is a reduced expression of the long of the + Coxeter group of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['A', 3]) + sage: B._check_is_long_word([1,2,1,3,2,1]) + sage: B._check_is_long_word([1,3,2,3,2,1]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + sage: B._check_is_long_word([1,2,1,3,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + sage: B._check_is_long_word([1,2,1,3,2,1,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + """ + W = self._pbw_datum_parent.weyl_group + if (len(word) != len(self._default_word) + or W.from_reduced_word(word) != W.long_element()): + raise ValueError("not a reduced word of the long element") + + def set_default_long_word(self, word): + """ + Set the default long word used to express elements of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.PBW(['C', 3]) + sage: B.default_long_word() + [1, 3, 2, 3, 1, 2, 3, 1, 2] + sage: x = B.highest_weight_vector().f_string([2,1,3,2,3,1,2,3,3,1]) + sage: x + PBW monomial with Lusztig datum (1, 2, 2, 0, 0, 0, 0, 0, 1) + sage: B.set_default_long_word([2,1,3,2,1,3,2,3,1]) + sage: B.default_long_word() + [2, 1, 3, 2, 1, 3, 2, 3, 1] + sage: x + PBW monomial with Lusztig datum (2, 0, 0, 0, 0, 0, 1, 3, 2) + + TESTS:: + + sage: B = crystals.infinity.PBW(['A', 3]) + sage: B._check_is_long_word([1,2,1,3,2,1,2]) + Traceback (most recent call last): + ... + ValueError: not a reduced word of the long element + """ + self._check_is_long_word(word) + self._default_word = tuple(word) + + Element = PBWCrystalElement + diff --git a/src/sage/combinat/crystals/pbw_datum.pxd b/src/sage/combinat/crystals/pbw_datum.pxd new file mode 100644 index 00000000000..039c197ea90 --- /dev/null +++ b/src/sage/combinat/crystals/pbw_datum.pxd @@ -0,0 +1,4 @@ +cpdef tuple compute_new_lusztig_datum(list enhanced_braid_chain, initial_lusztig_datum) +cpdef tuple tropical_plucker_relation(tuple a, lusztig_datum) +cpdef list enhance_braid_move_chain(braid_move_chain, cartan_type) + diff --git a/src/sage/combinat/crystals/pbw_datum.pyx b/src/sage/combinat/crystals/pbw_datum.pyx new file mode 100644 index 00000000000..5eb504ad1db --- /dev/null +++ b/src/sage/combinat/crystals/pbw_datum.pyx @@ -0,0 +1,484 @@ +# -*- coding: utf-8 -*- +r""" +PBW Data + +This contains helper classes and functions which encode PBW data +in finite type. + +AUTHORS: + +- Dinakar Muthiah (2015-05): initial version +- Travis Scrimshaw (2016-06): simplfied code and converted to Cython +""" + +#***************************************************************************** +# Copyright (C) 2015 Dinakar Muthiah +# 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.lazy_attribute import lazy_attribute +from sage.misc.cachefunc import cached_method +from sage.combinat.root_system.cartan_type import CartanType +from sage.combinat.root_system.coxeter_group import CoxeterGroup +from sage.combinat.root_system.root_system import RootSystem +from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + +cimport cython + +class PBWDatum(object): + """ + Helper class which represents a PBW datum. + """ + def __init__(self, parent, long_word, lusztig_datum): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L = PBWDatum(P, (1,2,1), (1,4,7)) + sage: TestSuite(L).run(skip="_test_pickling") + """ + self.parent = parent + self.long_word = tuple(long_word) + self.lusztig_datum = tuple(lusztig_datum) + + def __repr__(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: PBWDatum(P, (1,2,1), (1,4,7)) + PBW Datum element of type ['A', 2] with long word (1, 2, 1) + and Lusztig datum (1, 4, 7) + """ + return_str = "PBW Datum element of type {cartan_type} with ".format( + cartan_type=self.parent.cartan_type) + return_str += "long word {long_word} and Lusztig datum {lusztig_datum}".format( + long_word=self.long_word, + lusztig_datum=self.lusztig_datum) + return return_str + + def __eq__(self, other_PBWDatum): + """ + Check equality. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,4,7)) + sage: L2 = PBWDatum(P, (1,2,1), (1,4,7)) + sage: L1 == L2 + True + """ + return (self.parent == other_PBWDatum.parent and + self.long_word == other_PBWDatum.long_word and + self.lusztig_datum == other_PBWDatum.lusztig_datum) + + def is_equivalent_to(self, other_pbw_datum): + r""" + Return whether ``self`` is equivalent to ``other_pbw_datum``. + modulo the tropical Plücker relations. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,0,1)) + sage: L2 = PBWDatum(P, (2,1,2), (0,1,0)) + sage: L1.is_equivalent_to(L2) + True + sage: L1 == L2 + False + """ + other_long_word = other_pbw_datum.long_word + other_lusztig_datum = other_pbw_datum.lusztig_datum + equiv_pbw_datum = self.convert_to_new_long_word(other_long_word) + return equiv_pbw_datum.lusztig_datum == other_lusztig_datum + + def convert_to_long_word_with_first_letter(self, i): + r""" + Return a new PBWDatum equivalent to ``self`` + whose long word begins with ``i``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A3") + sage: datum = PBWDatum(P, (1,2,1,3,2,1), (1,0,1,4,2,3)) + sage: datum.convert_to_long_word_with_first_letter(1) + PBW Datum element of type ['A', 3] with long word (1, 2, 3, 1, 2, 1) + and Lusztig datum (1, 0, 4, 1, 2, 3) + sage: datum.convert_to_long_word_with_first_letter(2) + PBW Datum element of type ['A', 3] with long word (2, 1, 2, 3, 2, 1) + and Lusztig datum (0, 1, 0, 4, 2, 3) + sage: datum.convert_to_long_word_with_first_letter(3) + PBW Datum element of type ['A', 3] with long word (3, 1, 2, 3, 1, 2) + and Lusztig datum (8, 1, 0, 4, 1, 2) + """ + return self.convert_to_new_long_word(self.parent._long_word_begin_with(i)) + + def convert_to_new_long_word(self, new_long_word): + r""" + Return a new PBWDatum equivalent to ``self`` + whose long word is ``new_long_word``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: datum = PBWDatum(P, (1,2,1), (1,0,1)) + sage: new_datum = datum.convert_to_new_long_word((2,1,2)) + sage: new_datum.long_word + (2, 1, 2) + sage: new_datum.lusztig_datum + (0, 1, 0) + """ + return self.parent.convert_to_new_long_word(self, new_long_word) + + def weight(self): + """ + Return the weight of ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L = PBWDatum(P, (1,2,1), (1,1,1)) + sage: L.weight() + -2*alpha[1] - 2*alpha[2] + """ + root_list = self.parent._root_list_from(tuple(self.long_word)) + R = self.parent.root_lattice + return R.linear_combination((root_list[i], -coeff) + for i, coeff in enumerate(self.lusztig_datum)) + + def star(self): + """ + Return the starred version of ``self``, i.e., + with reversed `long_word` and `lusztig_datum` + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: L1 = PBWDatum(P, (1,2,1), (1,2,3)) + sage: L1.star() == PBWDatum(P, (2,1,2), (3,2,1)) + True + """ + aut = self.parent.cartan_type.opposition_automorphism() + reversed_long_word = [aut[i] for i in reversed(self.long_word)] + reversed_lusztig_datum = reversed(self.lusztig_datum) + return PBWDatum(self.parent, reversed_long_word, reversed_lusztig_datum) + + +class PBWData(object): # UniqueRepresentation? + """ + Helper class for the set of PBW data. + """ + def __init__(self, cartan_type): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["A",2]) + sage: TestSuite(P).run(skip="_test_pickling") + """ + self.cartan_type = CartanType(cartan_type) + self.root_system = RootSystem(self.cartan_type) + self.root_lattice = self.root_system.root_lattice() + self.weyl_group = self.root_lattice.weyl_group() + self._braid_move_calc = BraidMoveCalculator(self.weyl_group) + + def convert_to_new_long_word(self, pbw_datum, new_long_word): + """ + Convert the PBW datum ``pbw_datum`` from its long word to + ``new_long_word``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData, PBWDatum + sage: P = PBWData("A2") + sage: datum = PBWDatum(P, (1,2,1), (1,0,1)) + sage: new_datum = P.convert_to_new_long_word(datum,(2,1,2)) + sage: new_datum + PBW Datum element of type ['A', 2] with long word (2, 1, 2) + and Lusztig datum (0, 1, 0) + sage: new_datum.long_word + (2, 1, 2) + sage: new_datum.lusztig_datum + (0, 1, 0) + """ + assert pbw_datum.parent is self + chain = self._braid_move_calc.chain_of_reduced_words(pbw_datum.long_word, + new_long_word) + cdef list enhanced_braid_chain = enhance_braid_move_chain(chain, self.cartan_type) + new_lusztig_datum = compute_new_lusztig_datum(enhanced_braid_chain, + pbw_datum.lusztig_datum) + return PBWDatum(self, new_long_word, new_lusztig_datum) + + @cached_method + def _root_list_from(self, reduced_word): + """ + Return the list of positive roots in the order determined by + ``reduced_word``. + + .. WARNING:: + + No error checking is done to verify that ``reduced_word`` + is reduced. + + INPUT: + + - ``reduced_word`` -- a tuple corresponding to a reduced word + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["A",2]) + sage: P._root_list_from((1,2,1)) + [alpha[1], alpha[1] + alpha[2], alpha[2]] + """ + al = self.root_lattice.simple_roots() + cur = [] + for i in reversed(reduced_word): + cur = [al[i]] + [x.simple_reflection(i) for x in cur] + return cur + + @cached_method + def _long_word_begin_with(self, i): + """ + Return a reduced expression of the long word which begins with ``i``. + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import PBWData + sage: P = PBWData(["C",3]) + sage: P._long_word_begin_with(1) + (1, 3, 2, 3, 1, 2, 3, 1, 2) + sage: P._long_word_begin_with(2) + (2, 3, 2, 3, 1, 2, 3, 2, 1) + sage: P._long_word_begin_with(3) + (3, 2, 3, 1, 2, 3, 1, 2, 1) + """ + si = self.weyl_group.simple_reflection(i) + w0 = self.weyl_group.long_element() + return tuple([i] + (si * w0).reduced_word()) + +#enhanced_braid_chain is an ugly data structure. +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef tuple compute_new_lusztig_datum(list enhanced_braid_chain, initial_lusztig_datum): + """ + Return the lusztig datum obtained by applying tropical Plücker + relations along ``enhanced_braid_chain`` starting with + ``initial_lusztig_datum``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: from sage.combinat.crystals.pbw_datum import compute_new_lusztig_datum + sage: ct = CartanType(['A', 2]) + sage: W = CoxeterGroup(ct) + sage: B = BraidMoveCalculator(W) + sage: chain = B.chain_of_reduced_words((1,2,1),(2,1,2)) + sage: enhanced_braid_chain = enhance_braid_move_chain(chain, ct) + sage: compute_new_lusztig_datum(enhanced_braid_chain,(1,0,1)) + (0, 1, 0) + + TESTS:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: from sage.combinat.crystals.pbw_datum import compute_new_lusztig_datum + sage: ct = CartanType(['A', 2]) + sage: W = CoxeterGroup(ct) + sage: B = BraidMoveCalculator(W) + sage: chain = B.chain_of_reduced_words((1,2,1), (2,1,2)) + sage: enhanced_braid_chain = enhance_braid_move_chain(chain, ct) + sage: compute_new_lusztig_datum(enhanced_braid_chain,(1,0,1)) == (0,1,0) + True + """ + cdef tuple interval_of_change + # Does not currently check that len(initial_lusztig_datum) is appropriate + cdef list new_lusztig_datum = list(initial_lusztig_datum) #shallow copy + cdef int i + for i in range(1, len(enhanced_braid_chain)): + interval_of_change, type_data = enhanced_braid_chain[i] + a,b = interval_of_change + old_interval_datum = new_lusztig_datum[a:b] + new_interval_datum = tropical_plucker_relation(type_data, old_interval_datum) + new_lusztig_datum[a:b] = new_interval_datum + return tuple(new_lusztig_datum) + +# The tropical plucker relations +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef tuple tropical_plucker_relation(tuple a, lusztig_datum): + r""" + Apply the tropical Plücker relation of type ``a`` to ``lusztig_datum``. + + The relations are obtained by tropicalizing the relations in + Proposition 7.1 of [BZ01]_. + + INPUT: + + - ``a`` -- a pair ``(x, y)`` of the off-diagonal entries of a + `2 \times 2` Cartan matrix + + EXAMPLES:: + + sage: from sage.combinat.crystals.pbw_datum import tropical_plucker_relation + sage: tropical_plucker_relation((0,0), (2,3)) + (3, 2) + sage: tropical_plucker_relation((-1,-1), (1,2,3)) + (4, 1, 2) + sage: tropical_plucker_relation((-1,-2), (1,2,3,4)) + (8, 1, 2, 3) + sage: tropical_plucker_relation((-2,-1), (1,2,3,4)) + (6, 1, 2, 3) + """ + if a == (0, 0): # A1xA1 + t1, t2 = lusztig_datum + return (t2, t1) + elif a == (-1, -1): # A2 + t1,t2,t3 = lusztig_datum + return (t2+t3-min(t1,t3), + min(t1,t3), + t1+t2-min(t1,t3)) + elif a == (-1, -2): # B2 + t1,t2,t3,t4 = lusztig_datum + pi1 = min(t1+t2,min(t1,t3)+t4) + pi2 = min(2*t1+t2,2*min(t1,t3)+t4) + return (t2+2*t3+t4-pi2, + pi2-pi1, + 2*pi1-pi2, + t1+t2+t3-pi1) + elif a == (-1, -3): # G2 + t1,t2,t3,t4,t5,t6 = lusztig_datum + pi1 = min(t1+t2+2*t3+t4, + t1+t2+2*min(t3,t5)+t6, + min(t1,t3)+t4+2*t5+t6) + pi2 = min(2*t1+2*t2+3*t3+t4, + 2*t1+2*t2+3*min(t3,t5)+t6, + 2*min(t1,t3)+2*t4+3*t5+t6, + t1+t2+t4+2*t5+t6+min(t1+t3,2*t3,t3+t5,t1+t5)) + pi3 = min(3*t1+2*t2+3*t3+t4, + 3*t1+2*t2+3*min(t3,t5)+t6, + 3*min(t1,t3)+2*t4+3*t5+t6, + 2*t1+t2+t4+2*t5+t6+min(t1+t3,2*t3,t3+t5,t1+t5)) + pi4 = min(2*t1+2*t2+3*t3+t4+min(t1+t2+3*t3+t4, + t1+t2+3*min(t3,t5)+t6, + min(t1+t3,2*t3,t3+t5,t1+t5)+t4+2*t5+t6), + 2*t6+3*min(t1+t2+2*min(t3,t5),min(t1,t3)+t4+2*t5)) + return (t2+3*t3+2*t4+3*t5+t6-pi3, + pi3-pi2, + 3*pi2-pi3-pi4, + pi4-pi1-pi2, + 3*pi1-pi4, + t1+t2+2*t3+t4+t5-pi1) + else: # (-1,-2) and (-1,-3) + reversed_lusztig_datum = tuple(reversed(lusztig_datum)) + return tuple(reversed(tropical_plucker_relation((a[1], a[0]), + reversed_lusztig_datum))) + +# Maybe we need to be more specific, and pass not the Cartan type, but the root lattice? +# TODO: Move to PBW_data? +@cython.boundscheck(False) +@cython.wraparound(False) +cpdef list enhance_braid_move_chain(braid_move_chain, cartan_type): + r""" + Return a list of tuples that records the data of the long words in + ``braid_move_chain`` plus the data of the intervals where the braid moves + occur and the data of the off-diagonal entries of the `2 \times 2` Cartan + submatrices of each braid move. + + INPUT: + + - ``braid_move_chain`` -- a chain of reduced words in the Weyl group + of ``cartan_type`` + - ``cartan_type`` -- a finite Cartan type + + OUTPUT: + + A list of 2-tuples + ``(interval_of_change, cartan_sub_matrix)`` where + + - ``interval_of_change`` is the (half-open) interval of indices where + the braid move occurs; this is `None` for the first tuple + - ``cartan_sub_matrix`` is the off-diagonal entries of the `2 \times 2` + submatrix of the cartan matrix corresponding to the braid move; + this is `None` for the first tuple + + For a matrix:: + + [2 a] + [b 2] + + the ``cartan_sub_matrix`` is the pair ``(a, b)``. + + TESTS:: + + sage: from sage.combinat.crystals.pbw_datum import enhance_braid_move_chain + sage: braid_chain = [(1, 2, 1, 3, 2, 1), + ....: (1, 2, 3, 1, 2, 1), + ....: (1, 2, 3, 2, 1, 2), + ....: (1, 3, 2, 3, 1, 2), + ....: (3, 1, 2, 3, 1, 2), + ....: (3, 1, 2, 1, 3, 2), + ....: (3, 2, 1, 2, 3, 2), + ....: (3, 2, 1, 3, 2, 3)] + sage: enhanced_chain = enhance_braid_move_chain(braid_chain, CartanType(["A",5])) + sage: enhanced_chain[0] + (None, None) + sage: enhanced_chain[7] + ((3, 6), (-1, -1)) + """ + cdef int i, j + cdef int k, pos, first, last + cdef tuple interval_of_change, cartan_sub_matrix + cdef list output_list = [] + output_list.append( (None, None) ) + cdef tuple previous_word = (braid_move_chain[0]) + cdef tuple current_word + cartan_matrix = cartan_type.cartan_matrix() + cdef int ell = len(previous_word) + # TODO - Optimize this by avoiding calls to here? + # This likely could be done when performing chain_of_reduced_words + # Things in here get called the most (about 50x more than enhance_braid_move_chain) + for pos in range(1, len(braid_move_chain)): + # This gets the smallest continguous half-open interval [a, b) + # that contains the indices where current_word and previous_word differ. + current_word = (braid_move_chain[pos]) + for k in range(ell): + i = previous_word[k] + j = current_word[k] + if i != j: + i -= 1 # -1 for indexing + j -= 1 # -1 for indexing + first = k + break + for k in range(ell-1, k-1, -1): + if previous_word[k] != current_word[k]: + last = k + 1 + break + + cartan_sub_matrix = (cartan_matrix[i,j], cartan_matrix[j,i]) + output_list.append( ((first, last), cartan_sub_matrix) ) + previous_word = current_word + return output_list + diff --git a/src/sage/combinat/crystals/polyhedral_realization.py b/src/sage/combinat/crystals/polyhedral_realization.py index 4707a0b7a1b..27775dd9926 100644 --- a/src/sage/combinat/crystals/polyhedral_realization.py +++ b/src/sage/combinat/crystals/polyhedral_realization.py @@ -266,7 +266,7 @@ def phi(self, i): sage: [elt.phi(i) for i in B.index_set()] [1, 1, 0] """ - P = self[-1].parent().weight_lattice_realization() + P = self.parent().weight_lattice_realization() h = P.simple_coroots() omega = P(self.weight()).scalar(h[i]) return self.epsilon(i) + omega @@ -297,7 +297,7 @@ def e(self, i): if pos is None or pos <= nf: return None - l = self._list[:] + l = list(self) l[-pos] = crystal if pos <= 2*nf and all(b._m == 0 for b in l[-2*nf:-nf]): return self.__class__(self.parent(), l[:-nf]) @@ -327,10 +327,10 @@ def f(self, i): nf = len(self.parent()._factors) if pos <= nf: - l = self._list[:] + l = list(self) l[-pos] = l[-pos].f(i) return self.__class__(self.parent(), l + self.parent()._tp) - return self.set_index(-pos, crystal) + return self._set_index(-pos, crystal) def truncate(self, k=None): r""" @@ -359,13 +359,13 @@ def truncate(self, k=None): [-1, -2, -1, 0, 0, 0, 0, 0, 0, 0] """ if k is None: - k = len(self._list) + k = len(self) P = self.parent().finite_tensor_product(k) - if k <= len(self._list): - l = self._list[:k] + if k <= len(self): + l = self[:k] else: - l = self._list[:] + l = list(self) N = len(self.parent()._tp) while len(l) < k: i = len(l) % N diff --git a/src/sage/combinat/crystals/subcrystal.py b/src/sage/combinat/crystals/subcrystal.py index 15f43642ad1..476fa40907c 100644 --- a/src/sage/combinat/crystals/subcrystal.py +++ b/src/sage/combinat/crystals/subcrystal.py @@ -82,10 +82,10 @@ class Subcrystal(UniqueRepresentation, Parent): 8 sage: list(T) [[[1, 1], [3]], - [[1, 2], [3]], [[1, 1], [2]], - [[1, 2], [2]], + [[1, 2], [3]], [[2, 2], [3]], + [[1, 2], [2]], [[2, 3], [3]], [[1, 3], [2]], [[1, 3], [3]]] @@ -321,10 +321,10 @@ def _richcmp_(self, other, op): [[1, 2]](0), [[1, -2]](0), [[2, 2]](0), - [[2, -1]](1), - [[-1, -1]](1), [](1), + [[2, -1]](1), [[-2, -1]](1), + [[-1, -1]](1), [[-1, -1]](2)] For != operator:: diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 4bd5194455e..cfd7856860e 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -32,26 +32,23 @@ from __future__ import absolute_import import operator -from sage.misc.latex import latex -from sage.misc.cachefunc import cached_method, cached_in_parent_method +from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent -from sage.structure.element import parent +from sage.structure.unique_representation import UniqueRepresentation from sage.structure.global_options import GlobalOptions from sage.categories.category import Category from sage.categories.cartesian_product import cartesian_product from sage.categories.classical_crystals import ClassicalCrystals from sage.categories.regular_crystals import RegularCrystals from sage.categories.sets_cat import Sets -from sage.categories.map import Map from sage.combinat.root_system.cartan_type import CartanType -from sage.combinat.combinat import CombinatorialElement from sage.combinat.partition import Partition -from sage.combinat.tableau import Tableau from .letters import CrystalOfLetters from .spins import CrystalOfSpins, CrystalOfSpinsMinus, CrystalOfSpinsPlus +from sage.combinat.crystals.tensor_product_element import (TensorProductOfCrystalsElement, + TensorProductOfRegularCrystalsElement, CrystalOfTableauxElement) from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model -from sage.rings.all import ZZ ############################################################################## # Until trunc gets implemented in sage.function.other @@ -78,222 +75,6 @@ def trunc(i): # Support classes ############################################################################## -from sage.structure.unique_representation import UniqueRepresentation - -class TestParent(UniqueRepresentation, Parent): - """ - A parent for tests. - """ - def _repr_(self): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import TestParent - sage: TestParent() - A parent for tests - """ - return "A parent for tests" - -class ImmutableListWithParent(CombinatorialElement): - r""" - A class for lists having a parent - - Specification: any subclass ``C`` should implement ``__init__`` which - accepts the following form ``C(parent, list = list)`` - - EXAMPLES: - - We create an immutable list whose parent is the class list:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l._list - [1, 2, 3] - sage: l.parent() - A parent for tests - sage: l.sibling([2,1]) == ImmutableListWithParent(TestParent(), [2,1]) - True - sage: l.reversed() - [3, 2, 1] - sage: l.set_index(1,4) - [1, 4, 3] - - TESTS:: - - sage: TestSuite(l).run(skip = "_test_category") - """ - def _repr_(self): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l._repr_() - '[1, 2, 3]' - """ - return repr(self._list) - - def __eq__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l == l - True - sage: l == m - False - sage: m == n - False - """ - return self.__class__ is other.__class__ and \ - self.parent() == other.parent() and \ - self._list == other._list - - def __ne__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l != l - False - sage: l != m - True - sage: m != n - True - """ - return not self == other - - def __lt__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l < l - False - sage: l < m - False - sage: m < n - True - """ - if parent(self) is not parent(other): - return NotImplemented - return self._list.__lt__(other._list) - - def __le__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l <= l - True - sage: l <= m - True - sage: m <= n - True - """ - return self == other or self.__lt__(other) - - def __gt__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l > l - False - sage: l > m - False - sage: m > n - False - """ - if parent(self) is not parent(other): - return NotImplemented - return other.__lt__(self) - - def __ge__(self, other): - """ - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = ImmutableListWithParent(ZZ, [1,2,3]) - sage: n = ImmutableListWithParent(ZZ, [2,3,4]) - sage: l >= l - True - sage: l >= m - True - sage: m >= n - False - """ - return self == other or self.__gt__(other) - - def sibling(self, l): - """ - Returns an :class:`ImmutableListWithParent` object whose list is - ``l`` and whose parent is the same as the parent of ``self``. - - Note that the implementation of this function makes an assumption - about the constructor for subclasses. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: m = l.sibling([2,3,4]); m - [2, 3, 4] - sage: m.parent() - A parent for tests - """ - return self.__class__(self.parent(), list=l) - - def reversed(self): - """ - Returns the sibling of ``self`` which is obtained by reversing the - elements of`` self``. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l.reversed() - [3, 2, 1] - """ - return self.sibling([ i for i in reversed(self._list)]) - - def set_index(self, k, value): - """ - Returns the sibling of ``self`` obtained by setting the - `k^{th}` entry of self to value. - - EXAMPLES:: - - sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent - sage: l = ImmutableListWithParent(TestParent(), [1,2,3]) - sage: l.set_index(0,2) - [2, 2, 3] - sage: l.set_index(1,4) - [1, 4, 3] - sage: _.parent() - A parent for tests - """ - l = [i for i in self._list] - l[k] = value - return self.sibling(l) - class CrystalOfWords(UniqueRepresentation, Parent): """ Auxiliary class to provide a call method to create tensor product elements. @@ -319,64 +100,8 @@ def _element_constructor_(self, *crystalElements): """ return self.element_class(self, list(crystalElements)) - def one_dimensional_configuration_sum(self, q=None, group_components=True): - r""" - Computes the one-dimensional configuration sum. - - INPUT: - - - ``q`` -- (default: ``None``) a variable or ``None``; if ``None``, - a variable `q` is set in the code - - ``group_components`` -- (default: ``True``) boolean; if ``True``, - then the terms are grouped by classical component - - The one-dimensional configuration sum is the sum of the weights of all - elements in the crystal weighted by the energy function. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: T.one_dimensional_configuration_sum() - B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]] + (q+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]] - sage: R. = ZZ[] - sage: T.one_dimensional_configuration_sum(t, False) - B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]] - + B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]] - - sage: R = RootSystem(['A',2,1]) - sage: La = R.weight_space().basis() - sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]) - sage: LS.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum() # long time - True - - TESTS:: - - sage: K1 = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: K2 = crystals.KirillovReshetikhin(['A',2,1],2,1) - sage: T = crystals.TensorProduct(K1,K2) - sage: T.one_dimensional_configuration_sum() == T.one_dimensional_configuration_sum(group_components=False) - True - - sage: RC = RiggedConfigurations(['A',3,1],[[1,1],[1,2]]) - sage: B = crystals.KirillovReshetikhin(['A',3,1],1,1) - sage: B1 = crystals.KirillovReshetikhin(['A',3,1],1,2) - sage: T = crystals.TensorProduct(B,B1) - sage: RC.fermionic_formula() == T.one_dimensional_configuration_sum() - True - """ - if q is None: - from sage.rings.all import QQ - q = QQ['q'].gens()[0] - P0 = self.weight_lattice_realization().classical() - B = P0.algebra(q.parent()) - if group_components: - G = self.digraph(index_set = self.cartan_type().classical().index_set()) - C = G.connected_components() - return sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C) - return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self) - + class Element(TensorProductOfCrystalsElement): + pass class TensorProductOfCrystals(CrystalOfWords): r""" @@ -518,7 +243,8 @@ class TensorProductOfCrystals(CrystalOfWords): It has `8` elements:: sage: T.list() - [[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]] + [[2, 1, 1], [2, 1, 2], [2, 1, 3], [3, 1, 3], + [3, 2, 3], [3, 1, 1], [3, 1, 2], [3, 2, 2]] One can also check the Cartan type of the crystal:: @@ -536,7 +262,7 @@ class TensorProductOfCrystals(CrystalOfWords): 24 sage: TestSuite(T).run() sage: T.module_generators - [[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]] + ([[[1], [2]], [[1]]], [[[2], [3]], [[1]]]) sage: [x.weight() for x in T.module_generators] [(2, 1, 0, 0), (1, 1, 1, 0)] @@ -766,7 +492,7 @@ def __init__(self, crystals, generators, cartan_type): Parent.__init__(self, category = category) self.crystals = crystals self._cartan_type = cartan_type - self.module_generators = [ self(*x) for x in generators ] + self.module_generators = tuple([self(*x) for x in generators]) def _repr_(self): """ @@ -890,803 +616,19 @@ def weight_lattice_realization(self): return cm.common_parent(*[crystal.weight_lattice_realization() for crystal in self.crystals]) -class TensorProductOfCrystalsElement(ImmutableListWithParent): - r""" - A class for elements of tensor products of crystals. - """ - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',3]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)) - [1, 2] - """ - if self.parent().options.convention == "Kashiwara": - return repr(list(reversed(self._list))) - return repr(self._list) - - def _latex_(self): - r""" - Return latex code for ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(["A",2]) - sage: D = crystals.Tableaux(["A",2], shape=[2]) - sage: E = crystals.TensorProduct(C,D) - sage: latex(E.module_generators[0]) - 1 \otimes {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} - \lr{1}&\lr{1}\\\cline{1-2} - \end{array}$} - } - """ - return ' \otimes '.join(latex(c) for c in self) - - def _ascii_art_(self): - """ - Return an ASCII art representation of ``self``. - - EXAMPLES:: - - sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) - sage: ascii_art(KT.module_generators[0]) - 1 1 1 - 2 2 2 # 1 # 1 1 - 3 3 3 2 - -4 -4 -4 - """ - from sage.typeset.ascii_art import ascii_art, AsciiArt - s = ascii_art(self[0]) - s._baseline = s._h // 2 - ret = s - for tableau in self[1:]: - s = ascii_art(tableau) - s._baseline = s._h // 2 - ret += AsciiArt([" # "]) + s - return ret - - def __lt__(self, other): - """ - Non elements of the crystal are incomparable with elements of the crystal - (or should it return NotImplemented?). - - Comparison of two elements of this crystal: - - - different length: incomparable - - otherwise lexicographically, considering ``self[i]`` and ``other[i]`` - as incomparable if ``self[i] < other[i]`` returns NotImplemented - """ - if parent(self) is not parent(other): - return False - if len(self) != len(other): - return False - for i in range(len(self)): - if (self[i] < other[i]): - return True - if (other[i] < self[i]): - return False - return False - - def _repr_diagram(self): - r""" - Return a string representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A',3], shape=[3,1]) - sage: D = crystals.Tableaux(['A',3], shape=[1]) - sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) - sage: T = crystals.TensorProduct(C,D,E) - sage: print(T.module_generators[0]._repr_diagram()) - 1 1 1 (X) 1 (X) 1 1 - 2 2 2 - 3 3 - """ - pplist = [] - max_widths = [] - num_cols = len(self) - for c in self: - try: - pplist.append(c._repr_diagram().split('\n')) - except AttributeError: - pplist.append(c._repr_().split('\n')) - max_widths.append(max(map(len, pplist[-1]))) - num_rows = max(map(len, pplist)) - ret = "" - for i in range(num_rows): - if i > 0: - ret += '\n' - for j in range(num_cols): - if j > 0: - if i == 0: - ret += ' (X) ' - else: - ret += ' ' - if i < len(pplist[j]): - ret += pplist[j][i] - ret += ' '*(max_widths[j] - len(pplist[j][i])) - else: - ret += ' '*max_widths[j] - return ret - - def pp(self): - """ - Pretty print ``self``. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A',3], shape=[3,1]) - sage: D = crystals.Tableaux(['A',3], shape=[1]) - sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) - sage: T = crystals.TensorProduct(C,D,E) - sage: T.module_generators[0].pp() - 1 1 1 (X) 1 (X) 1 1 - 2 2 2 - 3 3 - """ - print(self._repr_diagram()) - - def weight(self): - r""" - Return the weight of ``self``. - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("A3") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([2,1,3]) - sage: b2 = B.highest_weight_vector().f(1) - sage: t = T(b2, b1) - sage: t - [[[1, 1, 1, 2], [2, 2], [3]], [[1, 1, 1, 1, 2], [2, 2, 4], [3]]] - sage: t.weight() - (-2, 1, 0, 1) - """ - return sum(self[i].weight() for i in range(len(self))) - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("G2") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f(2) - sage: b2 = B.highest_weight_vector().f_string([2,2,1]) - sage: t = T(b2, b1) - sage: [t.epsilon(i) for i in B.index_set()] - [0, 3] - """ - return max(self._sig(i, k) for k in range(1, len(self)+1)) - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: B = crystals.GeneralizedYoungWalls(2,La[0]+La[1]) - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([1,0]) - sage: b2 = B.highest_weight_vector().f_string([0,1]) - sage: t = T(b2, b1) - sage: [t.phi(i) for i in B.index_set()] - [1, 1, 4] - - TESTS: - - Check that :trac:`15462` is fixed:: - - sage: B = crystals.Tableaux(['A',2], shape=[2,1]) - sage: La = RootSystem(['A',2]).ambient_space().fundamental_weights() - sage: T = crystals.TensorProduct(crystals.elementary.T(['A',2], La[1]+La[2]), B) - sage: t = T.an_element() - sage: t.phi(1) - 2 - sage: t.phi(2) - 2 - """ - P = self[-1].parent().weight_lattice_realization() - h = P.simple_coroots() - omega = P(self.weight()).scalar(h[i]) - return max([omega + self._sig(i, k) for k in range(1, len(self)+1)]) - - @cached_in_parent_method - def _sig(self,i,k): - r""" - Return `a_i(k)` of ``self``. - - The value `a_i(k)` of a crystal `b = b_N \otimes \cdots \otimes b_1` - is defined as: - - .. MATH:: - - a_i(k) = \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle h_i, - \mathrm{wt}(b_j) \rangle - - where `\mathrm{wt}` is the :meth:`weight` of `b_j`. - - INPUT: - - - ``i`` -- An element of the index set - - - ``k`` -- The (1-based) index of the tensor factor of ``self`` - - EXAMPLES:: - - sage: B = crystals.infinity.GeneralizedYoungWalls(3) - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([0,3,1]) - sage: b2 = B.highest_weight_vector().f_string([3,2,1,0,2,3]) - sage: t = T(b1, b2) - sage: [[t._sig(i,k) for k in range(1,len(t)+1)] for i in B.index_set()] - [[0, -1], [0, 0], [0, 1], [1, 2]] - - TESTS: - - Check that :trac:`18469` is fixed:: - - sage: E1 = crystals.elementary.B(['A',2], 1) - sage: E2 = crystals.elementary.B(['A',2], 2) - sage: T = crystals.TensorProduct(E1, E2) - sage: x = T(E1.module_generators[0], E2.module_generators[0]); x - [0, 0] - sage: [[x._sig(i,k) for k in range(1,3)] for i in T.index_set()] - [[-inf, 0], [0, -inf]] - sage: x.f(1) - [-1, 0] - sage: x.e(1) - [1, 0] - """ - if k == 1: - return self[-1].epsilon(i) - ep = self[-k].epsilon(i) - if ep == float("-inf"): - return ep - - P = self[-1].parent().weight_lattice_realization() - h = P.simple_coroots() - wt = sum(P(self[-j].weight()) for j in range(1, k)) - return ep - P(wt).scalar(h[i]) - - def e(self,i): - r""" - Return the action of `e_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: B = crystals.infinity.Tableaux("D4") - sage: T = crystals.TensorProduct(B,B) - sage: b1 = B.highest_weight_vector().f_string([1,4,3]) - sage: b2 = B.highest_weight_vector().f_string([2,2,3,1,4]) - sage: t = T(b2, b1) - sage: t.e(1) - [[[1, 1, 1, 1, 1], [2, 2, 3, -3], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - sage: t.e(2) - sage: t.e(3) - [[[1, 1, 1, 1, 1, 2], [2, 2, 3, -4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - sage: t.e(4) - [[[1, 1, 1, 1, 1, 2], [2, 2, 3, 4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] - """ - N = len(self) + 1 - for k in range(1, N): - if all(self._sig(i,k) > self._sig(i,j) for j in range(1, k)) and \ - all(self._sig(i,k) >= self._sig(i,j) for j in range(k+1, N)): - crystal = self[-k].e(i) - if crystal is None: - return None - return self.set_index(-k, crystal) - return None - - def f(self,i): - r""" - Return the action of `f_i` on ``self``. - - INPUT: - - - ``i`` -- An element of the index set - - EXAMPLES:: - - sage: La = RootSystem(['A',3,1]).weight_lattice(extended=True).fundamental_weights() - sage: B = crystals.GeneralizedYoungWalls(3,La[0]) - sage: T = crystals.TensorProduct(B,B,B) - sage: b1 = B.highest_weight_vector().f_string([0,3]) - sage: b2 = B.highest_weight_vector().f_string([0]) - sage: b3 = B.highest_weight_vector() - sage: t = T(b3, b2, b1) - sage: t.f(0) - [[[0]], [[0]], [[0, 3]]] - sage: t.f(1) - [[], [[0]], [[0, 3], [1]]] - sage: t.f(2) - [[], [[0]], [[0, 3, 2]]] - sage: t.f(3) - [[], [[0, 3]], [[0, 3]]] - """ - N = len(self) + 1 - for k in range(1, N): - if all(self._sig(i,k) >= self._sig(i,j) for j in range(1, k)) and \ - all(self._sig(i,k) > self._sig(i,j) for j in range(k+1, N)): - crystal = self[-k].f(i) - if crystal is None: - return None - return self.set_index(-k, crystal) - return None - -class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): - """ - Element class for a tensor product of regular crystals. - - TESTS:: - - sage: C = crystals.Letters(['A',2]) - sage: T = crystals.TensorProduct(C, C) - sage: elt = T(C(1), C(2)) - sage: from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement - sage: isinstance(elt, TensorProductOfRegularCrystalsElement) - True - """ - def e(self, i): - """ - Return the action of `e_i` on ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)).e(1) == T(C(1),C(1)) - True - sage: T(C(2),C(1)).e(1) is None - True - sage: T(C(2),C(2)).e(1) == T(C(1),C(2)) - True - """ - if i not in self.index_set(): - raise ValueError("i must be in the index set") - position = self.positions_of_unmatched_plus(i) - if position == []: - return None - k = position[0] - return self.set_index(k, self[k].e(i)) - - def weight(self): - """ - Return the weight of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',3]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(2)).weight() - (1, 1, 0, 0) - sage: T = crystals.Tableaux(['D',4],shape=[]) - sage: T.list()[0].weight() - (0, 0, 0, 0) - """ - return sum((self[j].weight() for j in range(len(self))), self.parent().weight_lattice_realization().zero()) - - def f(self, i): - """ - Return the action of `f_i` on ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).f(1) - [1, 2] - sage: T(C(1),C(2)).f(1) - [2, 2] - sage: T(C(2),C(1)).f(1) is None - True - """ - if i not in self.index_set(): - raise ValueError("i must be in the index set") - position = self.positions_of_unmatched_minus(i) - if position == []: - return None - k = position[len(position)-1] - return self.set_index(k, self[k].f(i)) - - def phi(self, i): - r""" - Return `\varphi_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).phi(1) - 2 - sage: T(C(1),C(2)).phi(1) - 1 - sage: T(C(2),C(1)).phi(1) - 0 - """ - self = self.reversed() - height = 0 - for j in range(len(self)): - plus = self[j].epsilon(i) - minus = self[j].phi(i) - if height-plus < 0: - height = minus - else: - height = height - plus + minus - return height - - def epsilon(self, i): - r""" - Return `\varepsilon_i` of ``self``. - - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(1),C(1)).epsilon(1) - 0 - sage: T(C(1),C(2)).epsilon(1) - 1 - sage: T(C(2),C(1)).epsilon(1) - 0 - """ - height = 0 - for j in range(len(self)): - minus = self[j].phi(i) - plus = self[j].epsilon(i) - if height-minus < 0: - height = plus - else: - height = height - minus + plus - return height - - def positions_of_unmatched_minus(self, i, dual=False, reverse=False): - """ - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(2),C(1)).positions_of_unmatched_minus(1) - [] - sage: T(C(1),C(2)).positions_of_unmatched_minus(1) - [0] - """ - unmatched_plus = [] - height = 0 - if reverse: - self = self.reversed() - if not dual: - for j in range(len(self)): - minus = self[j].phi(i) - plus = self[j].epsilon(i) - if height-minus < 0: - unmatched_plus.append(j) - height = plus - else: - height = height - minus + plus - else: - for j in range(len(self)): - plus = self[j].epsilon(i) - minus = self[j].phi(i) - if height-plus < 0: - unmatched_plus.append(j) - height = minus - else: - height = height - plus + minus - return unmatched_plus - - def positions_of_unmatched_plus(self, i): - """ - EXAMPLES:: - - sage: C = crystals.Letters(['A',5]) - sage: T = crystals.TensorProduct(C,C) - sage: T(C(2),C(1)).positions_of_unmatched_plus(1) - [] - sage: T(C(1),C(2)).positions_of_unmatched_plus(1) - [1] - """ - l = self.positions_of_unmatched_minus(i, dual=True, reverse=True) - l.reverse() - return [len(self)-1-l[j] for j in range(len(l))] - - def energy_function(self, algorithm=None): - r""" - Return the energy function of ``self``. - - ALGORITHM: - - .. RUBRIC:: definition - - Let `T` be a tensor product of Kirillov-Reshetikhin - crystals. Let `R_i` and `H_i` be the combinatorial - `R`-matrix and local energy functions, respectively, acting - on the `i` and `i+1` factors. Let `D_B` be the energy - function of a single Kirillov-Reshetikhin crystal. The - *energy function* is given by - - .. MATH:: - - D = \sum_{j > i} H_i R_{i+1} R_{i+2} \cdots R_{j-1} - + \sum_j D_B R_1 R_2 \cdots R_{j-1}, - - where `D_B` acts on the rightmost factor. - - .. RUBRIC:: grading - - If ``self`` is an element of `T`, a tensor product of - perfect crystals of the same level, then use the affine - grading to determine the energy. Specifically, let `g` - denote the affine grading of ``self`` and `d` the affine - grading of the maximal vector in `T`. Then the energy - of ``self`` is given by `d - g`. - - For more details, see Theorem 7.5 in [SchillingTingley2011]_. - - INPUT: - - - ``algorithm`` -- (default: ``None``) use one of the - following algorithms to determine the energy function: - - * ``'definition'`` - use the definition of the energy - function; - * ``'grading'`` - use the affine grading; - - if not specified, then this uses ``'grading'`` if all - factors are perfect of the same level and otherwise - this uses ``'definition'`` - - OUTPUT: an integer - - REFERENCES: - - .. [SchillingTingley2011] \A. Schilling, P. Tingley. - *Demazure crystals, Kirillov-Reshetikhin crystals, and - the energy function*. - Electronic Journal of Combinatorics. **19(2)**. 2012. - :arXiv:`1104.2359` - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = sorted([x for x in T if x.is_highest_weight([1,2])]) - sage: for b in hw: - ....: print("{} {}".format(b, b.energy_function())) - [[[1]], [[1]], [[1]]] 0 - [[[1]], [[2]], [[1]]] 2 - [[[2]], [[1]], [[1]]] 1 - [[[3]], [[2]], [[1]]] 3 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: T = crystals.TensorProduct(K,K) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.energy_function())) - [[], []] 4 - [[], [[1, 1]]] 1 - [[[1, 1]], []] 3 - [[[1, 1]], [[1, 1]]] 0 - [[[1, 2]], [[1, 1]]] 1 - [[[2, 2]], [[1, 1]]] 2 - [[[-1, -1]], [[1, 1]]] 2 - [[[1, -1]], [[1, 1]]] 2 - [[[2, -1]], [[1, 1]]] 2 - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 1) - sage: T = crystals.TensorProduct(K) - sage: t = T.module_generators[0] - sage: t.energy_function('grading') - Traceback (most recent call last): - ... - NotImplementedError: all crystals in the tensor product need to be perfect of the same level - - TESTS:: - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1, 2) - sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2, 2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: all(b.energy_function() == b.energy_function(algorithm='definition') - ....: for b in hw) - True - """ - C = self.parent().crystals[0] - ell = ceil(C.s()/C.cartan_type().c()[C.r()]) - is_perfect = all(ell == K.s()/K.cartan_type().c()[K.r()] - for K in self.parent().crystals) - if algorithm is None: - if is_perfect: - algorithm = 'grading' - else: - algorithm = 'definition' - - if algorithm == 'grading': - if not is_perfect: - raise NotImplementedError("all crystals in the tensor product need to be perfect of the same level") - t = self.parent()(*[K.module_generator() for K in self.parent().crystals]) - d = t.affine_grading() - return d - self.affine_grading() - - if algorithm == 'definition': - # Setup - energy = ZZ.zero() - R_mats = [[K.R_matrix(Kp) for Kp in self.parent().crystals[i+1:]] - for i,K in enumerate(self.parent().crystals)] - H_funcs = [[K.local_energy_function(Kp) for Kp in self.parent().crystals[i+1:]] - for i,K in enumerate(self.parent().crystals)] - - for i,b in enumerate(self): - for j,R in enumerate(R_mats[i]): - H = H_funcs[i][j] - bp = self[i+j+1] - T = R.domain() - t = T(b, bp) - energy += H(t) - b = R(t)[1] - energy += b.energy_function() # D contribution - return energy - else: - raise ValueError("invalid algorithm") - - def affine_grading(self): - r""" - Returns the affine grading of `self`. - - The affine grading is only defined when ``self`` is an element of a - tensor product of affine Kirillov-Reshetikhin crystals. It is - calculated by finding a path from ``self`` to a ground state path - using the helper method :meth:`e_string_to_ground_state` and counting - the number of affine Kashiwara operators `e_0` applied on the way. - - INPUT: - - - ``self`` -- an element of a tensor product of Kirillov-Reshetikhin - crystals - - OUTPUT: an integer - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0] - sage: t.affine_grading() - 1 - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.affine_grading())) - [[[1]], [[1]], [[1]]] 3 - [[[1]], [[2]], [[1]]] 1 - [[[2]], [[1]], [[1]]] 2 - [[[3]], [[2]], [[1]]] 0 - - sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) - sage: T = crystals.TensorProduct(K,K,K) - sage: hw = [b for b in T if all(b.epsilon(i)==0 for i in [1,2])] - sage: for b in hw: - ....: print("{} {}".format(b, b.affine_grading())) - [[[1]], [[1]], [[1]]] 2 - [[[1]], [[2]], [[1]]] 1 - [[[1]], [[-1]], [[1]]] 0 - [[[2]], [[1]], [[1]]] 1 - [[[-2]], [[2]], [[1]]] 0 - [[[-1]], [[1]], [[1]]] 1 - """ - return self.e_string_to_ground_state().count(0) - - @cached_method - def e_string_to_ground_state(self): - r""" - Returns a string of integers in the index set `(i_1,\ldots,i_k)` such - that `e_{i_k} \cdots e_{i_1}` of ``self`` is the ground state. - - This method is only defined when ``self`` is an element of a tensor - product of affine Kirillov-Reshetikhin crystals. It calculates a path - from ``self`` to a ground state path using Demazure arrows as defined - in Lemma 7.3 in [SchillingTingley2011]_. - - INPUT: - - - ``self`` -- an element of a tensor product of Kirillov-Reshetikhin - crystals - - OUTPUT: a tuple of integers `(i_1,\ldots,i_k)` - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0] - sage: t.e_string_to_ground_state() - (0, 2) - - sage: K = crystals.KirillovReshetikhin(['C',2,1],1,1) - sage: T = crystals.TensorProduct(K,K) - sage: t = T.module_generators[0]; t - [[[1]], [[1]]] - sage: t.e_string_to_ground_state() - (0,) - sage: x=t.e(0) - sage: x.e_string_to_ground_state() - () - sage: y=t.f_string([1,2,1,1,0]); y - [[[2]], [[1]]] - sage: y.e_string_to_ground_state() - () - - TESTS: - - Check that :trac:`22882` is fixed:: - - sage: K = crystals.KirillovReshetikhin(CartanType(['A',6,2]).dual(), 1,1) - sage: T = tensor([K,K,K]) - sage: hw = [x for x in T if x.is_highest_weight([1,2,3])] - sage: gs = T(K(0), K(0), K(0)) - sage: all(elt.e_string(elt.e_string_to_ground_state()) == gs - ....: for elt in hw) - True - sage: all(elt.energy_function() == elt.energy_function('definition') - ....: for elt in hw) - True - """ - from sage.combinat.rigged_configurations.kr_tableaux import KirillovReshetikhinTableaux - if self.parent().crystals[0].__module__ != 'sage.combinat.crystals.kirillov_reshetikhin' and \ - not isinstance(self.parent().crystals[0], KirillovReshetikhinTableaux): - raise ValueError("All crystals in the tensor product need to be Kirillov-Reshetikhin crystals") - ell = max(ceil(K.s()/K.cartan_type().c()[K.r()]) for K in self.parent().crystals) - if self.cartan_type().dual().type() == 'BC': - I = self.cartan_type().index_set() - for i in I[:-1]: - if self.epsilon(i) > 0: - return (i,) + (self.e(i)).e_string_to_ground_state() - if self.epsilon(I[-1]) > ell: - return (I[-1],) + (self.e(I[-1])).e_string_to_ground_state() - return () - - I = self.cartan_type().classical().index_set() - for i in I: - if self.epsilon(i) > 0: - return (i,) + (self.e(i)).e_string_to_ground_state() - if self.epsilon(0) > ell: - return (0,) + (self.e(0)).e_string_to_ground_state() - return () - -CrystalOfWords.Element = TensorProductOfCrystalsElement - class FullTensorProductOfRegularCrystals(FullTensorProductOfCrystals): """ Full tensor product of regular crystals. """ - Element = TensorProductOfRegularCrystalsElement + class Element(TensorProductOfRegularCrystalsElement): + pass class TensorProductOfRegularCrystalsWithGenerators(TensorProductOfCrystalsWithGenerators): """ Tensor product of regular crystals with a generating set. """ - Element = TensorProductOfRegularCrystalsElement + class Element(TensorProductOfRegularCrystalsElement): + pass ######################################################### ## Crystal of tableaux @@ -1753,7 +695,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T = crystals.Tableaux(['A',2], shape = [3,2]) sage: T.module_generators[0] [[1, 1, 1], [2, 2]] - sage: T.module_generators[0]._list + sage: list(T.module_generators[0]) [2, 1, 2, 1, 1] To create a tableau, one can use:: @@ -1790,7 +732,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T.cardinality() 48 sage: T.module_generators - [[+++, [[1]]]] + ([+++, [[1]]],) sage: TestSuite(T).run() sage: T = crystals.Tableaux(['D',3],shape=[3/2,1/2,-1/2]); T @@ -1798,7 +740,7 @@ class CrystalOfTableaux(CrystalOfWords): sage: T.cardinality() 20 sage: T.module_generators - [[++-, [[1]]]] + ([++-, [[1]]],) sage: TestSuite(T).run() TESTS: @@ -1829,18 +771,18 @@ class CrystalOfTableaux(CrystalOfWords): sage: T = crystals.Tableaux(['A',3], shape = [2,2]) sage: C = T.letters - sage: Tab(rows = [[1,2],[3,4]])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(rows = [[1,2],[3,4]])) == [C(3),C(1),C(4),C(2)] True - sage: Tab(columns = [[3,1],[4,2]])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(columns = [[3,1],[4,2]])) == [C(3),C(1),C(4),C(2)] True For compatibility with :func:`~sage.combinat.crystals.tensor_product.TensorProductOfCrystals` we need to accept as input the internal list or sequence of elements:: - sage: Tab(list = [3,1,4,2])._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(list = [3,1,4,2])) == [C(3),C(1),C(4),C(2)] True - sage: Tab(3,1,4,2)._list == [C(3),C(1),C(4),C(2)] + sage: list(Tab(3,1,4,2)) == [C(3),C(1),C(4),C(2)] True The next example checks whether a given tableau is in fact a valid @@ -1898,13 +840,13 @@ def __classcall_private__(cls, cartan_type, shapes = None, shape = None): elif all(shape[-1]<0 for shape in spin_shapes): S = CrystalOfSpinsMinus(cartan_type) else: - raise ValueError("In type D spins should all be positive or negative") + raise ValueError("in type D spins should all be positive or negative") else: if any( i < 0 for shape in spin_shapes for i in shape): raise ValueError("shapes should all be partitions") S = CrystalOfSpins(cartan_type) - B = CrystalOfTableaux(cartan_type, shapes = shapes) - T = TensorProductOfCrystals(S,B, generators=[[S.module_generators[0],x] for x in B.module_generators]) + B = CrystalOfTableaux(cartan_type, shapes=shapes) + T = TensorProductOfCrystals(S, B, generators=[[S.module_generators[0],x] for x in B.module_generators]) T.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in spin_shapes))) T.shapes = spin_shapes return T @@ -1972,12 +914,12 @@ def module_generator(self, shape): type = self.cartan_type() if type[0] == 'D' and len(shape) == type[1] and shape[type[1]-1] < 0: invert = True - shape = shape[:-1]+(-shape[type[1]-1],) + shape = shape[:-1] + (-shape[type[1]-1],) else: invert = False p = Partition(shape).conjugate() # The column canonical tableau, read by columns - module_generator = flatten([[p[j]-i for i in range(p[j])] for j in range(len(p))]) + module_generator = flatten([[val-i for i in range(val)] for val in p]) if invert: module_generator = [(-x if x == type[1] else x) for x in module_generator] return self(list=[self.letters(x) for x in module_generator]) @@ -1997,420 +939,11 @@ def _element_constructor_(self, *args, **options): """ return self.element_class(self, *args, **options) - - -class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): - """ - Element in a crystal of tableaux. - """ - def __init__(self, parent, *args, **options): - """ - There are several ways to input tableaux, by rows, - by columns, as the list of column elements, or as a sequence of numbers - in column reading. - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t - [[1, 2], [3, 4]] - sage: TestSuite(t).run() - - sage: t = T(columns=[[3,1],[4,2]]) - sage: t - [[1, 2], [3, 4]] - sage: TestSuite(t).run() - - sage: t = T(list=[3,1,4,2]) - sage: t - [[1, 2], [3, 4]] - - sage: t = T(3,1,4,2) - sage: t - [[1, 2], [3, 4]] - - Currently inputting the empty tableau as an empty sequence is - broken due to a bug in the generic __call__ method (see :trac:`8648`). - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape=[]) - sage: t = T() - sage: t._list - [0] - - TESTS: - - Integer types that are not a Sage ``Integer`` (such as a Python ``int`` - and typically arise from compiled code) were not converted into a - letter. This caused certain functions to fail. This is fixed in - :trac:`13204`:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(list=[int(3),1,4,2]) - sage: type(t[0]) - - sage: t = T(list=[3,int(1),4,2]) - sage: type(t[1]) - - sage: C = crystals.KirillovReshetikhin(['A',int(3),1], 1,1) - sage: C[0].e(0) - [[4]] - """ - if len(args) == 1: - if isinstance(args[0], Tableau): - options['rows'] = args[0] - if 'list' in options: - the_list = options['list'] - elif 'rows' in options: - rows = options['rows'] -# the_list=Tableau(rows).to_word_by_column() - rows = Tableau(rows).conjugate() - the_list = [] - for col in rows: - the_list += reversed(col) - elif 'columns' in options: - columns = options['columns'] - the_list = [] - for col in columns: - the_list += col - else: - the_list = [i for i in args] - TensorProductOfRegularCrystalsElement.__init__(self, parent, [parent.letters(_) for _ in the_list]) - - def _repr_(self): - """ - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t._repr_() - '[[1, 2], [3, 4]]' - """ - return repr(self.to_tableau()) - - def _repr_diagram(self): - """ - Return a string representation of ``self`` as a diagram. - - EXAMPLES:: - - sage: C = crystals.Tableaux(['A', 4], shape=[4,2,1]) - sage: elt = C(rows=[[1,1,1,2], [2,3], [4]]) - sage: print(elt._repr_diagram()) - 1 1 1 2 - 2 3 - 4 - """ - return self.to_tableau()._repr_diagram() - - def pp(self): - """ - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]) - sage: t.pp() - 1 2 - 3 4 - """ - return self.to_tableau().pp() - - def _ascii_art_(self): - """ - Return an ascii art version of ``self``. - - EXAMPLES: - - We check that :trac:`16486` is fixed:: - - sage: T = crystals.Tableaux(['B',6], shape=[1]*5) - sage: ascii_art(T.module_generators[0]) - 1 - 2 - 3 - 4 - 5 - sage: T = crystals.Tableaux(['D',4], shape=[2,1]) - sage: t = T.module_generators[0].f_string([1,2,3,4,2,2,3,4]) - sage: ascii_art(t) - 1 -2 - -3 - """ - return self.to_tableau()._ascii_art_() - - def _latex_(self): - r""" - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [4,2]) - sage: t = T(rows=[[1,1,2,3],[2,3]]) - sage: latex(t) # indirect doctest - {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} - \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} - \lr{1}&\lr{1}&\lr{2}&\lr{3}\\\cline{1-4} - \lr{2}&\lr{3}\\\cline{1-2} - \end{array}$} - } - """ - from sage.combinat.output import tex_from_array - # Modified version of to_tableau() to have the entries be letters - # rather than their values - if self._list == []: - return "{\\emptyset}" - - tab = [ [self[0]] ] - for i in range(1,len(self)): - if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): - tab.append([self[i]]) - else: - l = len(tab)-1 - tab[l].append(self[i]) - for x in tab: - x.reverse() - T = Tableau(tab).conjugate() - return tex_from_array([[letter._latex_() for letter in row] for row in T]) - - @cached_method - def to_tableau(self): - """ - Returns the Tableau object corresponding to self. - - EXAMPLES:: - - sage: T = crystals.Tableaux(['A',3], shape = [2,2]) - sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t - [[1, 2], [3, 4]] - sage: type(t) - - sage: type(t[0][0]) - <... 'int'> - sage: T = crystals.Tableaux(['D',3], shape = [1,1]) - sage: t=T(rows=[[-3],[3]]).to_tableau(); t - [[-3], [3]] - sage: t=T(rows=[[3],[-3]]).to_tableau(); t - [[3], [-3]] - sage: T = crystals.Tableaux(['B',2], shape = [1,1]) - sage: t = T(rows=[[0],[0]]).to_tableau(); t - [[0], [0]] - """ - if self._list == []: - return Tableau([]) - tab = [ [self[0].value] ] - for i in range(1,len(self)): - if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): - tab.append([self[i].value]) - else: - l = len(tab)-1 - tab[l].append(self[i].value) - for x in tab: - x.reverse() - return Tableau(tab).conjugate() - - def promotion(self): - """ - Promotion for type A crystals of tableaux of rectangular shape - - Returns the result of applying promotion on this tableau. - - This method only makes sense in type A with rectangular shapes. - - EXAMPLES:: - - sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) - sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) - sage: t - [[1, 1, 1], [2, 2, 3], [3, 4, 4]] - sage: t.promotion() - [[1, 1, 2], [2, 2, 3], [3, 4, 4]] - sage: t.promotion().parent() - The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] - """ - crystal = self.parent() - cartan_type = crystal.cartan_type() - assert cartan_type.type() == 'A' - return crystal(self.to_tableau().promotion(cartan_type.rank())) - - def promotion_inverse(self): - """ - Inverse promotion for type A crystals of tableaux of rectangular shape - - Returns the result of applying inverse promotion on this tableau. - - This method only makes sense in type A with rectangular shapes. - - EXAMPLES:: - - sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) - sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) - sage: t - [[1, 1, 1], [2, 2, 3], [3, 4, 4]] - sage: t.promotion_inverse() - [[1, 1, 2], [2, 3, 3], [4, 4, 4]] - sage: t.promotion_inverse().parent() - The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] - """ - crystal = self.parent() - cartan_type = crystal.cartan_type() - assert cartan_type.type() == 'A' - return crystal(self.to_tableau().promotion_inverse(cartan_type.rank())) - -CrystalOfTableaux.Element = CrystalOfTableauxElement - -##################################################################### -## Local energy function - -class LocalEnergyFunction(Map): - r""" - The local energy function. - - Let `B` and `B'` be Kirillov-Reshetikhin crystals with maximal - vectors `u_B` and `u_{B'}` respectively. The *local energy function* - `H : B \otimes B' \to \ZZ` is the function which satisfies - - .. MATH:: - - H(e_0(b \otimes b')) = H(b \otimes b') + \begin{cases} - 1 & \text{if } i = 0 \text{ and LL}, \\ - -1 & \text{if } i = 0 \text{ and RR}, \\ - 0 & \text{otherwise,} - \end{cases} - - where LL (resp. RR) denote `e_0` acts on the left (resp. right) - on both `b \otimes b'` and `R(b \otimes b')`, and - normalized by `H(u_B \otimes u_{B'}) = 0`. - - INPUT: - - - ``B`` -- a Kirillov-Reshetikhin crystal - - ``Bp`` -- a Kirillov-Reshetikhin crystal - - ``normalization`` -- (default: 0) the normalization value - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['C',2,1], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['C',2,1], 2,1) - sage: H = K.local_energy_function(K2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: for b in hw: - ....: b, H(b) - ([[], [[1], [2]]], 1) - ([[[1, 1]], [[1], [2]]], 0) - ([[[2, -2]], [[1], [2]]], 1) - ([[[1, -2]], [[1], [2]]], 1) - - REFERENCES: - - .. [KKMMNN92] S-J. Kang, M. Kashiwara, K. C. Misra, T. Miwa, - T. Nakashima, and A. Nakayashiki. - *Affine crystals and vertex models*. - Int. J. Mod. Phys. A, **7** (suppl. 1A), (1992) pp. 449-484. - """ - def __init__(self, B, Bp, normalization=0): - """ - Initialize ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',7,2], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['A',7,2], 2,1) - sage: H = K.local_energy_function(K2) - sage: TestSuite(H).run(skip=['_test_category', '_test_pickling']) - """ - self._B = B - self._Bp = Bp - self._R_matrix = self._B.R_matrix(self._Bp) - T = B.tensor(Bp) - self._known_values = {T(*[K.module_generator() for K in T.crystals]): - ZZ(normalization)} - self._I0 = T.cartan_type().classical().index_set() - from sage.categories.homset import Hom - Map.__init__(self, Hom(T, ZZ)) - - def _repr_(self): - """ - Return a string representation of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A', 6, 2], 2, 1) - sage: Kp = crystals.KirillovReshetikhin(['A', 6, 2], 1, 1) - sage: H = K.local_energy_function(Kp); H - Local energy function of - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(2,1) - tensor - Kirillov-Reshetikhin crystal of type ['BC', 3, 2] with (r,s)=(1,1) - """ - return "Local energy function of {} tensor {}".format(self._B, self._Bp) - - def _call_(self, x): - """ - Return the local energy of ``x``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['B',4,1], 1,2) - sage: K2 = crystals.KirillovReshetikhin(['B',4,1], 2,1) - sage: H = K.local_energy_function(K2) - sage: T = tensor([K, K2]) - sage: hw = [x for x in T if x.is_highest_weight([1,2])] - sage: H(hw[0]) - 1 - """ - # Setup variables - visited = {x: 0} - check0 = [x] - - # Helper function - def to_classical_hw(cur): - for i in self._I0: - b = cur.e(i) - if b is not None and b not in visited: - visited[b] = visited[cur] # No change - return b - return None # is classically HW or all have been visited - - cur = x - # Get the affine node (it might not be 0 if the type - # has been relabeled) - i0 = x.parent().cartan_type().special_node() - while cur not in self._known_values: - # We first go towards the classically highest weight since - # the maximal vector is classically highest weight - b = to_classical_hw(cur) - - # If classically HW, then try 0 arrows - while b is None: - b = check0.pop() - c = b.e(i0) - # If there is no 0 arrow or we have already seen c, move along - if c is None or c in visited: - b = None - continue - - bp = self._R_matrix(b) - cp = bp.e(i0) - if b[1] == c[1] and bp[1] == cp[1]: # LL case - visited[c] = visited[b] + 1 - elif b[0] == c[0] and bp[0] == cp[0]: # RR case - visited[c] = visited[b] - 1 - else: - visited[c] = visited[b] # Otherwise no change - b = c - - cur = b - check0.append(b) - - baseline = self._known_values[cur] - visited[cur] - for y in visited: - self._known_values[y] = baseline + visited[y] - - return self._known_values[x] - + class Element(CrystalOfTableauxElement): + pass # deprecations from trac:18555 from sage.misc.superseded import deprecated_function_alias TensorProductOfCrystals.global_options=deprecated_function_alias(18555, TensorProductOfCrystals.options) TensorProductOfCrystalsOptions=deprecated_function_alias(18555, TensorProductOfCrystals.options) + diff --git a/src/sage/combinat/crystals/tensor_product_element.pxd b/src/sage/combinat/crystals/tensor_product_element.pxd new file mode 100644 index 00000000000..df44135819f --- /dev/null +++ b/src/sage/combinat/crystals/tensor_product_element.pxd @@ -0,0 +1,20 @@ +from sage.structure.list_clone cimport ClonableArray + +cdef class ImmutableListWithParent(ClonableArray): + cpdef _set_index(self, k, value) + +cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): + pass + +cdef class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): + cpdef position_of_last_unmatched_minus(self, i) + cpdef position_of_first_unmatched_plus(self, i) + +cdef class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): + pass + +cdef class InfinityCrystalOfTableauxElement(CrystalOfTableauxElement): + pass + +cdef class InfinityCrystalOfTableauxElementTypeD(InfinityCrystalOfTableauxElement): + pass diff --git a/src/sage/combinat/crystals/tensor_product_element.pyx b/src/sage/combinat/crystals/tensor_product_element.pyx new file mode 100644 index 00000000000..71cfda0b418 --- /dev/null +++ b/src/sage/combinat/crystals/tensor_product_element.pyx @@ -0,0 +1,1118 @@ +""" +Tensor Products of Crystal Elements + +AUTHORS: + +- Anne Schilling, Nicolas Thiery (2007): Initial version +- Ben Salisbury, Travis Scrimshaw (2013): Refactored tensor products to handle + non-regular crystals and created new subclass to take advantage of + the regularity +- Travis Scrimshaw (2017): Cythonized element classes +""" +#***************************************************************************** +# Copyright (C) 2007 Anne Schilling +# Nicolas Thiery +# 2017 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: +# +# http://www.gnu.org/licenses/ +#**************************************************************************** +from __future__ import print_function, absolute_import + +from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE +from sage.structure.parent cimport Parent + +from sage.misc.cachefunc import cached_method, cached_in_parent_method +from sage.functions.other import ceil +from sage.combinat.tableau import Tableau +from sage.rings.all import ZZ + +############################################################################## +# Support classes +############################################################################## + +cdef class ImmutableListWithParent(ClonableArray): + r""" + A class for lists having a parent + + Specification: any subclass ``C`` should implement ``__init__`` which + accepts the following form ``C(parent, list=list)`` + """ + def __init__(self, Parent parent, list): + """ + Initialize ``self``. + + TESTS:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: TestSuite(b).run() + """ + ClonableArray.__init__(self, parent, list, check=False) + + cpdef long _hash_(self) except? -1: + """ + Return the hash of ``self``. + + TESTS:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: b._hash_() == hash(b) + True + """ + return hash(tuple(self._list)) + + def __setstate__(self, state): + """ + For unpickling old pickles. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',2], shape=[2,1]) + sage: b = T.module_generators[0] + sage: b.__setstate__([T, {'_list': list(b)}]) + """ + self._parent = state[0] + self._list = state[1]['_list'] + self._is_immutable = True + self._hash = 0 + + def reversed(self): + """ + Return a copy of ``self`` but in the reversed order. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[2,1]).module_generators[0] + sage: list(b) + [2, 1, 1] + sage: list(b.reversed()) + doctest:warning + ... + DeprecationWarning: reversed() is deprecated; use reversed(self) instead + See http://trac.sagemath.org/22642 for details. + [1, 1, 2] + """ + from sage.misc.superseded import deprecation + deprecation(22642, 'reversed() is deprecated; use reversed(self) instead') + return type(self)(self._parent, list=list(reversed(self._list))) + + def set_index(self, k, value): + """ + Return a sibling of ``self`` obtained by setting the + `k^{th}` entry of self to value. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[3]).module_generators[0] + sage: list(b.set_index(0, 2)) + doctest:warning + ... + DeprecationWarning: set_index is deprecated; use _set_index instead + See http://trac.sagemath.org/22642 for details. + [2, 1, 1] + """ + from sage.misc.superseded import deprecation + deprecation(22642, 'set_index is deprecated; use _set_index instead') + return self._set_index(int(k), value) + + cpdef _set_index(self, k, value): + r""" + Return a sibling of ``self`` obtained by setting the + `k^{th}` entry of self to value. + + EXAMPLES:: + + sage: b = crystals.Tableaux(['A',2], shape=[3]).module_generators[0] + sage: list(b._set_index(0, 2)) + [2, 1, 1] + sage: list(b._set_index(1, 4)) + [1, 4, 1] + """ + cdef list l = list(self._list) # Make a (shallow) copy + l[k] = value + return type(self)(self._parent, list=l) + +############################################################################## +# Primary classes +############################################################################## + +cdef class TensorProductOfCrystalsElement(ImmutableListWithParent): + r""" + A class for elements of tensor products of crystals. + """ + def _repr_(self): + """ + Return a string representation of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',3]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)) + [1, 2] + """ + if self._parent.options.convention == "Kashiwara": + return repr(list(reversed(self._list))) + return repr(self._list) + + def _latex_(self): + r""" + Return latex code for ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(["A",2]) + sage: D = crystals.Tableaux(["A",2], shape=[2]) + sage: E = crystals.TensorProduct(C,D) + sage: latex(E.module_generators[0]) + 1 \otimes {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{2}c}\cline{1-2} + \lr{1}&\lr{1}\\\cline{1-2} + \end{array}$} + } + """ + from sage.misc.latex import latex + if self._parent.options.convention == "Kashiwara": + return ' \otimes '.join(latex(c) for c in reversed(self)) + return ' \otimes '.join(latex(c) for c in self) + + def _ascii_art_(self): + """ + Return an ASCII art representation of ``self``. + + EXAMPLES:: + + sage: KT = crystals.TensorProductOfKirillovReshetikhinTableaux(['D',4,1],[[3,3],[2,1],[1,2]]) + sage: ascii_art(KT.module_generators[0]) + 1 1 1 + 2 2 2 # 1 # 1 1 + 3 3 3 2 + -4 -4 -4 + """ + if self._parent.options.convention == "Kashiwara": + lst = list(reversed(self)) + else: + lst = self + from sage.typeset.ascii_art import ascii_art, AsciiArt + s = ascii_art(lst[0]) + s._baseline = s._h // 2 + ret = s + for tableau in lst[1:]: + s = ascii_art(tableau) + s._baseline = s._h // 2 + ret += AsciiArt([" # "]) + s + return ret + + def _repr_diagram(self): + r""" + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3], shape=[3,1]) + sage: D = crystals.Tableaux(['A',3], shape=[1]) + sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) + sage: T = crystals.TensorProduct(C,D,E) + sage: print(T.module_generators[0]._repr_diagram()) + 1 1 1 (X) 1 (X) 1 1 + 2 2 2 + 3 3 + """ + pplist = [] + max_widths = [] + num_cols = len(self._list) + for c in self: + try: + pplist.append(c._repr_diagram().split('\n')) + except AttributeError: + pplist.append(c._repr_().split('\n')) + max_widths.append(max(map(len, pplist[-1]))) + num_rows = max(map(len, pplist)) + ret = "" + for i in range(num_rows): + if i > 0: + ret += '\n' + for j in range(num_cols): + if j > 0: + if i == 0: + ret += ' (X) ' + else: + ret += ' ' + if i < len(pplist[j]): + ret += pplist[j][i] + ret += ' '*(max_widths[j] - len(pplist[j][i])) + else: + ret += ' '*max_widths[j] + return ret + + def pp(self): + """ + Pretty print ``self``. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A',3], shape=[3,1]) + sage: D = crystals.Tableaux(['A',3], shape=[1]) + sage: E = crystals.Tableaux(['A',3], shape=[2,2,2]) + sage: T = crystals.TensorProduct(C,D,E) + sage: T.module_generators[0].pp() + 1 1 1 (X) 1 (X) 1 1 + 2 2 2 + 3 3 + """ + print(self._repr_diagram()) + + def weight(self): + r""" + Return the weight of ``self``. + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("A3") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([2,1,3]) + sage: b2 = B.highest_weight_vector().f(1) + sage: t = T(b2, b1) + sage: t + [[[1, 1, 1, 2], [2, 2], [3]], [[1, 1, 1, 1, 2], [2, 2, 4], [3]]] + sage: t.weight() + (-2, 1, 0, 1) + + :: + + sage: C = crystals.Letters(['A',3]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)).weight() + (1, 1, 0, 0) + sage: T = crystals.Tableaux(['D',4],shape=[]) + sage: T.list()[0].weight() + (0, 0, 0, 0) + """ + WLR = self._parent.weight_lattice_realization() + return WLR(sum(elt.weight() for elt in self)) + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("G2") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f(2) + sage: b2 = B.highest_weight_vector().f_string([2,2,1]) + sage: t = T(b2, b1) + sage: [t.epsilon(i) for i in B.index_set()] + [0, 3] + """ + return max(self._sig(i, k) for k in range(1, len(self._list)+1)) + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights() + sage: B = crystals.GeneralizedYoungWalls(2,La[0]+La[1]) + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([1,0]) + sage: b2 = B.highest_weight_vector().f_string([0,1]) + sage: t = T(b2, b1) + sage: [t.phi(i) for i in B.index_set()] + [1, 1, 4] + + TESTS: + + Check that :trac:`15462` is fixed:: + + sage: B = crystals.Tableaux(['A',2], shape=[2,1]) + sage: La = RootSystem(['A',2]).ambient_space().fundamental_weights() + sage: T = crystals.TensorProduct(crystals.elementary.T(['A',2], La[1]+La[2]), B) + sage: t = T.an_element() + sage: t.phi(1) + 2 + sage: t.phi(2) + 2 + """ + P = self._list[-1].parent().weight_lattice_realization() + h = P.simple_coroots() + omega = P(self.weight()).scalar(h[i]) + return max(omega + self._sig(i, k) for k in range(1, len(self._list)+1)) + + @cached_in_parent_method + def _sig(self, i, k): + r""" + Return `a_i(k)` of ``self``. + + The value `a_i(k)` of a crystal `b = b_N \otimes \cdots \otimes b_1` + is defined as: + + .. MATH:: + + a_i(k) = \varepsilon_i(b_k) - \sum_{j=1}^{k-1} \langle h_i, + \mathrm{wt}(b_j) \rangle + + where `\mathrm{wt}` is the :meth:`weight` of `b_j`. + + INPUT: + + - ``i`` -- an element of the index set + + - ``k`` -- the (1-based) index of the tensor factor of ``self`` + + EXAMPLES:: + + sage: B = crystals.infinity.GeneralizedYoungWalls(3) + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([0,3,1]) + sage: b2 = B.highest_weight_vector().f_string([3,2,1,0,2,3]) + sage: t = T(b1, b2) + sage: [[t._sig(i,k) for k in range(1,len(t)+1)] for i in B.index_set()] + [[0, -1], [0, 0], [0, 1], [1, 2]] + + TESTS: + + Check that :trac:`18469` is fixed:: + + sage: E1 = crystals.elementary.B(['A',2], 1) + sage: E2 = crystals.elementary.B(['A',2], 2) + sage: T = crystals.TensorProduct(E1, E2) + sage: x = T(E1.module_generators[0], E2.module_generators[0]); x + [0, 0] + sage: [[x._sig(i,k) for k in range(1,3)] for i in T.index_set()] + [[-inf, 0], [0, -inf]] + sage: x.f(1) + [-1, 0] + sage: x.e(1) + [1, 0] + """ + if k == 1: + return self._list[-1].epsilon(i) + ep = self._list[-k].epsilon(i) + if ep == float("-inf"): + return ep + + P = self._list[-1].parent().weight_lattice_realization() + h = P.simple_coroots() + wt = P.sum(P(self._list[-j].weight()) for j in range(1, k)) + return ep - wt.scalar(h[i]) + + def e(self, i): + r""" + Return the action of `e_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux("D4") + sage: T = crystals.TensorProduct(B,B) + sage: b1 = B.highest_weight_vector().f_string([1,4,3]) + sage: b2 = B.highest_weight_vector().f_string([2,2,3,1,4]) + sage: t = T(b2, b1) + sage: t.e(1) + [[[1, 1, 1, 1, 1], [2, 2, 3, -3], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + sage: t.e(2) + sage: t.e(3) + [[[1, 1, 1, 1, 1, 2], [2, 2, 3, -4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + sage: t.e(4) + [[[1, 1, 1, 1, 1, 2], [2, 2, 3, 4], [3]], [[1, 1, 1, 1, 2], [2, 2, 2], [3, -3]]] + """ + N = len(self._list) + 1 + for k in range(1, N): + if all(self._sig(i,k) > self._sig(i,j) for j in range(1, k)) and \ + all(self._sig(i,k) >= self._sig(i,j) for j in range(k+1, N)): + crystal = self._list[-k].e(i) + if crystal is None: + return None + return self._set_index(-k, crystal) + return None + + def f(self, i): + r""" + Return the action of `f_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: La = RootSystem(['A',3,1]).weight_lattice(extended=True).fundamental_weights() + sage: B = crystals.GeneralizedYoungWalls(3,La[0]) + sage: T = crystals.TensorProduct(B,B,B) + sage: b1 = B.highest_weight_vector().f_string([0,3]) + sage: b2 = B.highest_weight_vector().f_string([0]) + sage: b3 = B.highest_weight_vector() + sage: t = T(b3, b2, b1) + sage: t.f(0) + [[[0]], [[0]], [[0, 3]]] + sage: t.f(1) + [[], [[0]], [[0, 3], [1]]] + sage: t.f(2) + [[], [[0]], [[0, 3, 2]]] + sage: t.f(3) + [[], [[0, 3]], [[0, 3]]] + """ + N = len(self._list) + 1 + for k in range(1, N): + if all(self._sig(i,k) >= self._sig(i,j) for j in range(1, k)) and \ + all(self._sig(i,k) > self._sig(i,j) for j in range(k+1, N)): + crystal = self._list[-k].f(i) + if crystal is None: + return None + return self._set_index(-k, crystal) + return None + +cdef class TensorProductOfRegularCrystalsElement(TensorProductOfCrystalsElement): + """ + Element class for a tensor product of regular crystals. + + TESTS:: + + sage: C = crystals.Letters(['A',2]) + sage: T = crystals.TensorProduct(C, C) + sage: elt = T(C(1), C(2)) + sage: from sage.combinat.crystals.tensor_product import TensorProductOfRegularCrystalsElement + sage: isinstance(elt, TensorProductOfRegularCrystalsElement) + True + """ + def e(self, i): + """ + Return the action of `e_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(2)).e(1) == T(C(1),C(1)) + True + sage: T(C(2),C(1)).e(1) is None + True + sage: T(C(2),C(2)).e(1) == T(C(1),C(2)) + True + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + return self._set_index(k, self._list[k].e(i)) + + def f(self, i): + """ + Return the action of `f_i` on ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).f(1) + [1, 2] + sage: T(C(1),C(2)).f(1) + [2, 2] + sage: T(C(2),C(1)).f(1) is None + True + """ + if i not in self.index_set(): + raise ValueError("i must be in the index set") + k = self.position_of_last_unmatched_minus(i) + if k is None: + return None + return self._set_index(k, self._list[k].f(i)) + + def phi(self, i): + r""" + Return `\varphi_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).phi(1) + 2 + sage: T(C(1),C(2)).phi(1) + 1 + sage: T(C(2),C(1)).phi(1) + 0 + """ + height = 0 + for elt in reversed(self._list): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - plus < 0: + height = minus + else: + height = height - plus + minus + return height + + def epsilon(self, i): + r""" + Return `\varepsilon_i` of ``self``. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(1),C(1)).epsilon(1) + 0 + sage: T(C(1),C(2)).epsilon(1) + 1 + sage: T(C(2),C(1)).epsilon(1) + 0 + """ + height = 0 + for elt in self: + minus = elt.phi(i) + plus = elt.epsilon(i) + if height - minus < 0: + height = plus + else: + height = height - minus + plus + return height + + cpdef position_of_last_unmatched_minus(self, i): + """ + Return the position of the last unmatched `-` or ``None`` if + there is no unmatched `-`. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).position_of_last_unmatched_minus(1) + sage: T(C(1),C(2)).position_of_last_unmatched_minus(1) + 0 + """ + unmatched_minus = None + height = 0 + cdef int j + for j,elt in enumerate(self): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - minus < 0: + unmatched_minus = j + height = plus + else: + height = height - minus + plus + return unmatched_minus + + cpdef position_of_first_unmatched_plus(self, i): + """ + Return the position of the first unmatched `+` or ``None`` if + there is no unmatched `+`. + + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).position_of_first_unmatched_plus(1) + sage: T(C(1),C(2)).position_of_first_unmatched_plus(1) + 1 + """ + unmatched_plus = None + height = 0 + cdef int N = len(self._list) - 1 + cdef int j + for j, elt in enumerate(reversed(self._list)): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height - plus < 0: + unmatched_plus = N - j + height = minus + else: + height = height - plus + minus + return unmatched_plus + + # Legacy function + def positions_of_unmatched_minus(self, i, dual=False, reverse=False): + """ + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).positions_of_unmatched_minus(1) + [] + sage: T(C(1),C(2)).positions_of_unmatched_minus(1) + [0] + """ + cdef list unmatched_plus = [] + cdef int j + height = 0 + if reverse: + self = type(self)(self._parent, list(reversed(self._list))) + if not dual: + for j,elt in enumerate(self): + minus = elt.phi(i) + plus = elt.epsilon(i) + if height-minus < 0: + unmatched_plus.append(j) + height = plus + else: + height = height - minus + plus + else: + for j,elt in enumerate(self): + plus = elt.epsilon(i) + minus = elt.phi(i) + if height-plus < 0: + unmatched_plus.append(j) + height = minus + else: + height = height - plus + minus + return unmatched_plus + + # Legacy function + def positions_of_unmatched_plus(self, i): + """ + EXAMPLES:: + + sage: C = crystals.Letters(['A',5]) + sage: T = crystals.TensorProduct(C,C) + sage: T(C(2),C(1)).positions_of_unmatched_plus(1) + [] + sage: T(C(1),C(2)).positions_of_unmatched_plus(1) + [1] + """ + cdef list L = self.positions_of_unmatched_minus(i, dual=True, reverse=True) + L.reverse() + cdef int N = len(self._list) - 1 + return [N - val for val in L] + +cdef class CrystalOfTableauxElement(TensorProductOfRegularCrystalsElement): + """ + Element in a crystal of tableaux. + """ + def __init__(self, parent, *args, **options): + """ + There are several ways to input tableaux, by rows, by columns, + by columns, as the list of column elements, or as a sequence + of numbers in column reading. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t + [[1, 2], [3, 4]] + sage: TestSuite(t).run() + + sage: t = T(columns=[[3,1],[4,2]]) + sage: t + [[1, 2], [3, 4]] + sage: TestSuite(t).run() + + sage: t = T(list=[3,1,4,2]) + sage: t + [[1, 2], [3, 4]] + + sage: t = T(3,1,4,2) + sage: t + [[1, 2], [3, 4]] + + Currently inputting the empty tableau as an empty sequence is + broken due to a bug in the generic __call__ method (see :trac:`8648`). + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape=[]) + sage: t = T() + sage: list(t) + [0] + + TESTS: + + Integer types that are not a Sage ``Integer`` (such as a Python ``int`` + and typically arise from compiled code) were not converted into a + letter. This caused certain functions to fail. This is fixed in + :trac:`13204`:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(list=[int(3),1,4,2]) + sage: type(t[0]) + + sage: t = T(list=[3,int(1),4,2]) + sage: type(t[1]) + + sage: C = crystals.KirillovReshetikhin(['A',int(3),1], 1,1) + sage: C[0].e(0) + [[4]] + """ + if len(args) == 1: + if isinstance(args[0], Tableau): + options['rows'] = args[0] + if 'list' in options: + the_list = options['list'] + elif 'rows' in options: + rows = options['rows'] +# the_list=Tableau(rows).to_word_by_column() + rows = Tableau(rows).conjugate() + the_list = [] + for col in rows: + the_list += reversed(col) + elif 'columns' in options: + columns = options['columns'] + the_list = [] + for col in columns: + the_list += col + else: + the_list = [i for i in args] + TensorProductOfRegularCrystalsElement.__init__(self, parent, [parent.letters(_) for _ in the_list]) + + def _repr_(self): + """ + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t._repr_() + '[[1, 2], [3, 4]]' + """ + return repr(self.to_tableau()) + + def _repr_diagram(self): + """ + Return a string representation of ``self`` as a diagram. + + EXAMPLES:: + + sage: C = crystals.Tableaux(['A', 4], shape=[4,2,1]) + sage: elt = C(rows=[[1,1,1,2], [2,3], [4]]) + sage: print(elt._repr_diagram()) + 1 1 1 2 + 2 3 + 4 + """ + return self.to_tableau()._repr_diagram() + + def pp(self): + """ + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]) + sage: t.pp() + 1 2 + 3 4 + """ + return self.to_tableau().pp() + + def _ascii_art_(self): + """ + Return an ascii art version of ``self``. + + EXAMPLES: + + We check that :trac:`16486` is fixed:: + + sage: T = crystals.Tableaux(['B',6], shape=[1]*5) + sage: ascii_art(T.module_generators[0]) + 1 + 2 + 3 + 4 + 5 + sage: T = crystals.Tableaux(['D',4], shape=[2,1]) + sage: t = T.module_generators[0].f_string([1,2,3,4,2,2,3,4]) + sage: ascii_art(t) + 1 -2 + -3 + """ + return self.to_tableau()._ascii_art_() + + def _latex_(self): + r""" + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [4,2]) + sage: t = T(rows=[[1,1,2,3],[2,3]]) + sage: latex(t) # indirect doctest + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{4}c}\cline{1-4} + \lr{1}&\lr{1}&\lr{2}&\lr{3}\\\cline{1-4} + \lr{2}&\lr{3}\\\cline{1-2} + \end{array}$} + } + """ + from sage.combinat.output import tex_from_array + # Modified version of to_tableau() to have the entries be letters + # rather than their values + if not self._list: + return "{\\emptyset}" + + tab = [ [self[0]] ] + for i in range(1,len(self)): + if self[i-1] < self[i] or (self[i-1].value != 0 and self[i-1] == self[i]): + tab.append([self[i]]) + else: + l = len(tab)-1 + tab[l].append(self[i]) + for x in tab: + x.reverse() + T = Tableau(tab).conjugate() + return tex_from_array([[letter._latex_() for letter in row] for row in T]) + + @cached_method + def to_tableau(self): + """ + Return the :class:`Tableau` object corresponding to ``self``. + + EXAMPLES:: + + sage: T = crystals.Tableaux(['A',3], shape = [2,2]) + sage: t = T(rows=[[1,2],[3,4]]).to_tableau(); t + [[1, 2], [3, 4]] + sage: type(t) + + sage: type(t[0][0]) + <... 'int'> + sage: T = crystals.Tableaux(['D',3], shape = [1,1]) + sage: t=T(rows=[[-3],[3]]).to_tableau(); t + [[-3], [3]] + sage: t=T(rows=[[3],[-3]]).to_tableau(); t + [[3], [-3]] + sage: T = crystals.Tableaux(['B',2], shape = [1,1]) + sage: t = T(rows=[[0],[0]]).to_tableau(); t + [[0], [0]] + """ + if not self._list: + return Tableau([]) + cdef list lst = self._list + cdef list tab = [ [lst[0].value] ] + cdef int i + for i in range(1,len(self)): + if lst[i-1] < lst[i] or (lst[i-1].value != 0 and lst[i-1] == lst[i]): + tab.append([lst[i].value]) + else: + tab[len(tab)-1].append(lst[i].value) + for x in tab: + x.reverse() + return Tableau(tab).conjugate() + + def promotion(self): + """ + Return the result of applying promotion on ``self``. + + Promotion for type A crystals of tableaux of rectangular shape. + This method only makes sense in type A with rectangular shapes. + + EXAMPLES:: + + sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) + sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) + sage: t + [[1, 1, 1], [2, 2, 3], [3, 4, 4]] + sage: t.promotion() + [[1, 1, 2], [2, 2, 3], [3, 4, 4]] + sage: t.promotion().parent() + The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] + """ + crystal = self._parent + cartan_type = crystal.cartan_type() + assert cartan_type.type() == 'A' + return crystal(self.to_tableau().promotion(cartan_type.rank())) + + def promotion_inverse(self): + """ + Return the result of applying inverse promotion on ``self``. + + Inverse promotion for type A crystals of tableaux of rectangular shape. + This method only makes sense in type A with rectangular shapes. + + EXAMPLES:: + + sage: C = crystals.Tableaux(["A",3], shape = [3,3,3]) + sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]])) + sage: t + [[1, 1, 1], [2, 2, 3], [3, 4, 4]] + sage: t.promotion_inverse() + [[1, 1, 2], [2, 3, 3], [4, 4, 4]] + sage: t.promotion_inverse().parent() + The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]] + """ + crystal = self._parent + cartan_type = crystal.cartan_type() + assert cartan_type.type() == 'A' + return crystal(self.to_tableau().promotion_inverse(cartan_type.rank())) + +cdef class InfinityCrystalOfTableauxElement(CrystalOfTableauxElement): + def e(self,i): + r""" + Return the action of `\widetilde{e}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['B',3]) + sage: b = B(rows=[[1,1,1,1,1,1,1,2,0,-3,-1,-1,-1,-1],[2,2,2,2,-2,-2],[3,-3,-3]]) + sage: b.e(3).pp() + 1 1 1 1 1 1 1 2 0 -3 -1 -1 -1 -1 + 2 2 2 2 -2 -2 + 3 0 -3 + sage: b.e(1).pp() + 1 1 1 1 1 1 1 0 -3 -1 -1 -1 -1 + 2 2 2 2 -2 -2 + 3 -3 -3 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElement ret + ret = (self._set_index(k, self._list[k].e(i))) + if k+i > len(self._list): + return ret + for j in reversed(range(1, i+1)): + if ret._list[k+i-j].value != j: + return ret + # We've found a column, so we need to remove it + for j in range(i): + ret._list.pop(k) + return ret + + def f(self, i): + r""" + Return the action of `\widetilde{f}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['C',4]) + sage: b = B.highest_weight_vector() + sage: b.f(1).pp() + 1 1 1 1 2 + 2 2 2 + 3 3 + 4 + sage: b.f(3).pp() + 1 1 1 1 1 + 2 2 2 2 + 3 3 4 + 4 + sage: b.f(3).f(4).pp() + 1 1 1 1 1 + 2 2 2 2 + 3 3 -4 + 4 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_last_unmatched_minus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElement ret + ret = (self._set_index(k, self._list[k].f(i))) + if k+i > len(self._list): + return ret + for j in reversed(range(1,i+1)): + if self._list[k+i-j].value != j: + return ret + # We've found a full column, so we'll need to add a new column + for j in range(i): + ret._list.insert(k, self._parent.letters(j+1)) + return ret + +cdef class InfinityCrystalOfTableauxElementTypeD(InfinityCrystalOfTableauxElement): + def e(self, i): + r""" + Return the action of `\widetilde{e}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['D',4]) + sage: b = B.highest_weight_vector().f_string([1,4,3,1,2]); b.pp() + 1 1 1 1 2 3 + 2 2 2 + 3 -3 + sage: b.e(2).pp() + 1 1 1 1 2 2 + 2 2 2 + 3 -3 + """ + if i not in self.index_set(): + raise ValueError('i is not in the index set') + k = self.position_of_first_unmatched_plus(i) + if k is None: + return None + cdef InfinityCrystalOfTableauxElementTypeD ret + ret = (self._set_index(k, self._list[k].e(i))) + if i == self.cartan_type().rank(): + i -= 1 + if k+i > len(self._list): + return ret + for j in reversed(range(1, i+1)): + if ret._list[k+i-j].value != j: + return ret + # We've found a column, so we need to remove it + for j in range(i): + ret._list.pop(k) + return ret + + def f(self, i): + r""" + Return the action of `\widetilde{f}_i` on ``self``. + + INPUT: + + - ``i`` -- an element of the index set + + EXAMPLES:: + + sage: B = crystals.infinity.Tableaux(['D',5]) + sage: b = B.highest_weight_vector().f_string([1,4,3,1,5]); b.pp() + 1 1 1 1 1 1 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 5 + sage: b.f(1).pp() + 1 1 1 1 1 1 2 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 5 + sage: b.f(5).pp() + 1 1 1 1 1 1 2 2 + 2 2 2 2 2 + 3 3 3 -5 + 4 -4 + """ + cdef InfinityCrystalOfTableauxElementTypeD ret + ret = (InfinityCrystalOfTableauxElement.f(self, i)) + if ret._list[0].value == -self._parent.cartan_type().rank(): + # Exceptional case for f_n where we need to add a new column + for j in range(i-1): + ret._list.insert(0, self._parent.letters(j+1)) + return ret + +# for unpickling +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.combinat.crystals.tensor_product', 'ImmutableListWithParent', ImmutableListWithParent) + diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 4d3e001ee1a..0bb534f25d5 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -938,7 +938,7 @@ def v_5_1_BIBD(v, check=True): r""" Return a `(v,5,1)`-BIBD. - This method follows the constuction from [ClaytonSmith]_. + This method follows the construction from [ClaytonSmith]_. INPUT: diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 66fd7d01693..ed1d9d9586f 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -1703,7 +1703,7 @@ def OA_520_plus_x(x): r""" Return an `OA(10+x,520+x)`. - The consruction shared by Julian R. Abel works for :func:`OA(10,520) + The construction shared by Julian R. Abel works for :func:`OA(10,520) `, :func:`OA(12,522) `, and :func:`OA(14,524) `. diff --git a/src/sage/combinat/designs/design_catalog.py b/src/sage/combinat/designs/design_catalog.py index c3f7d71bb63..ed6d81ee982 100644 --- a/src/sage/combinat/designs/design_catalog.py +++ b/src/sage/combinat/designs/design_catalog.py @@ -72,7 +72,7 @@ .. [1] La Jolla Covering Repository, http://www.ccrwest.org/cover.html """ -from __future__ import absolute_import + from sage.combinat.designs.block_design import (BlockDesign, ProjectiveGeometryDesign, DesarguesianProjectivePlaneDesign, @@ -98,29 +98,8 @@ from sage.combinat.designs.difference_family import difference_family from .difference_matrices import difference_matrix -from sage.misc.superseded import deprecated_callable_import -deprecated_callable_import(19096, - 'sage.combinat.designs.incidence_structures', - globals(), - locals(), - ["IncidenceStructure"], - ("This alias will soon be removed. You can call the same object by removing 'designs.' in your command")) - -Hypergraph = BlockDesign = IncidenceStructure # just an alias from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system from sage.combinat.designs.resolvable_bibd import resolvable_balanced_incomplete_block_design, kirkman_triple_system from sage.combinat.designs.group_divisible_designs import group_divisible_design from .orthogonal_arrays import OAMainFunctions as orthogonal_arrays - -# When this deprecated function is removed, remove the handling of k=None in the -# function orthogonal_arrays.orthogonal_array() -deprecated_callable_import(17034, - 'sage.combinat.designs.orthogonal_arrays', - globals(), - locals(), - ["orthogonal_array"], - ("This function will soon be removed. Use the designs.orthogonal_arrays.* functions instead")) - -# We don't want this to appear in designs. -del deprecated_callable_import diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 13f2339b216..ac88ef785bb 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -426,7 +426,7 @@ def is_isomorphic(self, other, certificate=False): - ``other`` -- an incidence structure. - ``certificate`` (boolean) -- whether to return an - insomorphism from ``self`` to ``other`` instead of a boolean + isomorphism from ``self`` to ``other`` instead of a boolean answer. EXAMPLES:: diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 8e705557f0d..6f80263ee2a 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1424,7 +1424,7 @@ def OA_find_disjoint_blocks(OA,k,n,x): `x` blocks of an `OA` are said to be disjoint if they all have different values for a every given index, i.e. if they correspond to - disjoint blocks in the `TD` assciated with the `OA`. + disjoint blocks in the `TD` associated with the `OA`. INPUT: diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index cc1d3c821c3..73d2f028fa3 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -153,7 +153,7 @@ def complement(self): """ The two-graph which is the complement of ``self`` - That is, the two-graph constisting exactly of triples not in ``self``. + That is, the two-graph consisting exactly of triples not in ``self``. Note that this is different from :meth:`complement ` of the :class:`parent class @@ -161,7 +161,7 @@ def complement(self): EXAMPLES:: - sage: p=graphs.CompleteGraph(8).line_graph().twograph() + sage: p = graphs.CompleteGraph(8).line_graph().twograph() sage: pc = p.complement(); pc Incidence structure with 28 points and 1260 blocks diff --git a/src/sage/combinat/diagram_algebras.py b/src/sage/combinat/diagram_algebras.py index 099d9ffb841..f4d3f0d6394 100644 --- a/src/sage/combinat/diagram_algebras.py +++ b/src/sage/combinat/diagram_algebras.py @@ -26,8 +26,7 @@ from sage.categories.algebras import Algebras from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.structure.element import generic_power -from sage.combinat.free_module import (CombinatorialFreeModule, - CombinatorialFreeModuleElement) +from sage.combinat.free_module import CombinatorialFreeModule from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.combinat.combinat import bell_number, catalan_number @@ -1462,7 +1461,7 @@ def sgn(x): # The following subclass provides a few additional methods for # partition algebra elements. - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): r""" An element of a diagram algebra. diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index 7cf09b4ea6d..34ef799599c 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -8795,7 +8795,7 @@ def final_components(self): leaving ``C``. The final components are the only parts of a transducer which - influence the main terms of the asympotic behaviour of the sum + influence the main terms of the asymptotic behaviour of the sum of output labels of a transducer, see [HKP2015]_ and [HKW2015]_. EXAMPLES:: diff --git a/src/sage/combinat/free_module.py b/src/sage/combinat/free_module.py index 949b31f52e0..73bfcbb93b9 100644 --- a/src/sage/combinat/free_module.py +++ b/src/sage/combinat/free_module.py @@ -14,824 +14,25 @@ from six.moves import range from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.element import Element, have_same_parent from sage.structure.parent import Parent from sage.structure.indexed_generators import IndexedGenerators -from sage.misc.misc import repr_lincomb from sage.modules.module import Module from sage.rings.all import Integer -import sage.structure.element +from sage.structure.element import parent +from sage.modules.with_basis.indexed_element import IndexedFreeModuleElement from sage.sets.finite_enumerated_set import FiniteEnumeratedSet from sage.combinat.cartesian_product import CartesianProduct_iters from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute from sage.categories.all import Category, Sets, ModulesWithBasis +from sage.categories.tensor import tensor import sage.data_structures.blas_dict as blas -from sage.typeset.ascii_art import AsciiArt, empty_ascii_art -from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art +from sage.typeset.ascii_art import AsciiArt +from sage.typeset.unicode_art import UnicodeArt import six - -# TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element -class CombinatorialFreeModuleElement(Element): - def __init__(self, M, x): - """ - Create a combinatorial module element. This should never be - called directly, but only through the parent combinatorial - free module's :meth:`__call__` method. - - TESTS:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c']; f - B['a'] + 3*B['c'] - sage: f == loads(dumps(f)) - True - """ - Element.__init__(self, M) - self._monomial_coefficients = x - - def __iter__(self): - """ - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: [i for i in sorted(f)] - [('a', 1), ('c', 3)] - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: a = s([2,1]) + s([3]) - sage: [i for i in sorted(a)] - [([2, 1], 1), ([3], 1)] - """ - return six.iteritems(self._monomial_coefficients) - - def __contains__(self, x): - """ - Returns whether or not a combinatorial object x indexing a basis - element is in the support of self. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: 'a' in f - True - sage: 'b' in f - False - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: a = s([2,1]) + s([3]) - sage: Partition([2,1]) in a - True - sage: Partition([1,1,1]) in a - False - """ - return x in self._monomial_coefficients and self._monomial_coefficients[x] != 0 - - @cached_method - def __hash__(self): - """ - Return the hash value for ``self``. - - The result is cached. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: hash(f) - 6429418278783588506 # 64-bit - 726440090 # 32-bit - - sage: F = RootSystem(['A',2]).ambient_space() - sage: f = F.simple_root(0) - sage: hash(f) - 6920829894162680369 # 64-bit - -528971215 # 32-bit - - This uses the recipe that was proposed for frozendicts in `PEP - 0416 `_ (and adds - the hash of the parent). This recipe relies on the hash - function for frozensets which uses tricks to mix the hash - values of the items in case they are similar. - - .. TODO:: - - It would be desirable to make the hash value depend on the - hash value of the parent. See :trac:`15959`. - """ - return hash(frozenset(self._monomial_coefficients.items())) - - def monomial_coefficients(self, copy=True): - """ - Return the internal dictionary which has the combinatorial objects - indexing the basis as keys and their corresponding coefficients as - values. - - INPUT: - - - ``copy`` -- (default: ``True``) if ``self`` is internally - represented by a dictionary ``d``, then make a copy of ``d``; - if ``False``, then this can cause undesired behavior by - mutating ``d`` - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: d = f.monomial_coefficients() - sage: d['a'] - 1 - sage: d['c'] - 3 - - To run through the monomials of an element, it is better to - use the idiom:: - - sage: for (t,c) in f: - ....: print("{} {}".format(t,c)) - a 1 - c 3 - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: a = s([2,1])+2*s([3,2]) - sage: d = a.monomial_coefficients() - sage: type(d) - <... 'dict'> - sage: d[ Partition([2,1]) ] - 1 - sage: d[ Partition([3,2]) ] - 2 - """ - if copy: - return dict(self._monomial_coefficients) - return self._monomial_coefficients - - def _sorted_items_for_printing(self): - """ - Returns the items (i.e terms) of ``self``, sorted for printing - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 2*B['c'] + 3 * B['b'] - sage: f._sorted_items_for_printing() - [('a', 1), ('b', 3), ('c', 2)] - sage: F.print_options(sorting_reverse=True) - sage: f._sorted_items_for_printing() - [('c', 2), ('b', 3), ('a', 1)] - sage: F.print_options(sorting_reverse=False) #reset to original state - - .. SEEALSO:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` - """ - print_options = self.parent().print_options() - v = self._monomial_coefficients.items() - try: - v.sort(key=lambda monomial_coeff: - print_options['sorting_key'](monomial_coeff[0]), - reverse=print_options['sorting_reverse']) - except Exception: # Sorting the output is a plus, but if we can't, no big deal - pass - return v - - def _repr_(self): - """ - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='F') - sage: e = F.basis() - sage: e['a'] + 2*e['b'] # indirect doctest - F['a'] + 2*F['b'] - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='') - sage: e = F.basis() - sage: e['a'] + 2*e['b'] # indirect doctest - ['a'] + 2*['b'] - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='', scalar_mult=' ', bracket=False) - sage: e = F.basis() - sage: e['a'] + 2*e['b'] # indirect doctest - 'a' + 2 'b' - - Controling the order of terms by providing a comparison - function on elements of the support:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], - ....: sorting_reverse=True) - sage: e = F.basis() - sage: e['a'] + 3*e['b'] + 2*e['c'] - 2*B['c'] + 3*B['b'] + B['a'] - - sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], - ....: sorting_key=lambda x: x[1]) - sage: e = F.basis() - sage: e['ac'] + 3*e['ba'] + 2*e['cb'] - 3*B['ba'] + 2*B['cb'] + B['ac'] - """ - return repr_lincomb(self._sorted_items_for_printing(), - scalar_mult=self.parent()._print_options['scalar_mult'], - repr_monomial = self.parent()._repr_term, - strip_one = True) - - def _ascii_art_(self): - """ - TESTS:: - - sage: M = QuasiSymmetricFunctions(QQ).M() - sage: ascii_art(M[1,3]**2) # indirect doctest - 4*M + 2*M + 2*M + 2*M + 2*M + M - *** ****** *** *** *** ****** - *** * * **** *** ** - * * *** * ** - * * - sage: ascii_art(M.zero()) - 0 - """ - from sage.misc.misc import coeff_repr - terms = self._sorted_items_for_printing() - scalar_mult = self.parent()._print_options['scalar_mult'] - repr_monomial = self.parent()._ascii_art_term - strip_one = True - - if repr_monomial is None: - repr_monomial = str - - s = empty_ascii_art # "" - first = True - - if scalar_mult is None: - scalar_mult = "*" - - for (monomial,c) in terms: - b = repr_monomial(monomial) # PCR - if c != 0: - break_points = [] - coeff = coeff_repr(c, False) - if coeff != "0": - if coeff == "1": - coeff = "" - elif coeff == "-1": - coeff = "-" - elif b._l > 0: - if len(coeff) > 0 and monomial == 1 and strip_one: - b = empty_ascii_art # "" - else: - b = AsciiArt([scalar_mult]) + b - if not first: - if len(coeff) > 0 and coeff[0] == "-": - coeff = " - %s"%coeff[1:] - else: - coeff = " + %s"%coeff - break_points = [2] - else: - coeff = "%s"%coeff - s += AsciiArt([coeff], break_points) + b - first = False - if first: - return AsciiArt(["0"]) - elif s == empty_ascii_art: - return AsciiArt(["1"]) - else: - return s - - def _unicode_art_(self): - """ - TESTS:: - - sage: M = QuasiSymmetricFunctions(QQ).M() - sage: unicode_art(M[1,1]**2) # indirect doctest - 6*M + 2*M + 2*M + 2*M + M - ┌┐ ┌┬┐ ┌┐ ┌┐ ┌┬┐ - ├┤ ├┼┘ ┌┼┤ ├┤ ┌┼┼┘ - ├┤ ├┤ ├┼┘ ┌┼┤ └┴┘ - ├┤ └┘ └┘ └┴┘ - └┘ - """ - from sage.misc.misc import coeff_repr - terms = self._sorted_items_for_printing() - scalar_mult = self.parent()._print_options['scalar_mult'] - repr_monomial = self.parent()._unicode_art_term - strip_one = True - - if repr_monomial is None: - repr_monomial = str - - s = empty_unicode_art # "" - first = True - - if scalar_mult is None: - scalar_mult = "*" - - for (monomial, c) in terms: - b = repr_monomial(monomial) # PCR - if c != 0: - break_points = [] - coeff = coeff_repr(c, False) - if coeff != "0": - if coeff == "1": - coeff = "" - elif coeff == "-1": - coeff = "-" - elif b._l > 0: - if len(coeff) > 0 and monomial == 1 and strip_one: - b = empty_unicode_art # "" - else: - b = UnicodeArt([scalar_mult]) + b - if not first: - if len(coeff) > 0 and coeff[0] == "-": - coeff = " - %s" % coeff[1:] - else: - coeff = " + %s" % coeff - break_points = [2] - else: - coeff = "%s" % coeff - s += UnicodeArt([coeff], break_points) + b - first = False - if first: - return "0" - elif s == empty_unicode_art: - return UnicodeArt(["1"]) - else: - return s - - def _latex_(self): - r""" - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: latex(f) - B_{a} + 3B_{c} - - :: - - sage: QS3 = SymmetricGroupAlgebra(QQ,3) - sage: a = 2 + QS3([2,1,3]) - sage: latex(a) #indirect doctest - 2[1, 2, 3] + [2, 1, 3] - - :: - - sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='beta', latex_prefix='\\beta') - sage: x = F.an_element() - sage: x - 2*beta['a'] + 2*beta['b'] - sage: latex(x) - 2\beta_{a} + 2\beta_{b} - - Controling the order of terms by providing a comparison - function on elements of the support:: - - sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], - ....: sorting_reverse=True) - sage: e = F.basis() - sage: latex(e['a'] + 3*e['b'] + 2*e['c']) - 2B_{c} + 3B_{b} + B_{a} - - sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], - ....: sorting_key=lambda x: x[1]) - sage: e = F.basis() - sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb']) - 3B_{ba} + 2B_{cb} + B_{ac} - """ - return repr_lincomb(self._sorted_items_for_printing(), - scalar_mult = self.parent()._print_options['scalar_mult'], - latex_scalar_mult = self.parent()._print_options['latex_scalar_mult'], - repr_monomial = self.parent()._latex_term, - is_latex=True, strip_one = True) - - def __eq__(self, other): - """ - EXAMPLES:: - - sage: F1 = CombinatorialFreeModule(QQ, [1, 2, 3]) - sage: F2 = CombinatorialFreeModule(QQ, [1, 2, 3], prefix = "g") - sage: F1.zero() == F1.zero() - True - sage: F1.zero() == F1.an_element() - False - sage: F1.an_element() == F1.an_element() - True - sage: F1.an_element() is None - False - - .. TODO:: - - Currently, if ``self`` and ``other`` do not have the same parent, - seemingly equal elements do not evaluate equal, since conversions - between different modules have not been established. - - :: - - sage: F1.zero() == 0 - True - sage: F1(0) - 0 - - :: - - sage: F1.zero() == F2.zero() - False - sage: F1(F2.zero()) - Traceback (most recent call last): - ... - TypeError: do not know how to make x (= 0) an element of self (=Free module generated by {1, 2, 3} over Rational Field) - sage: F = AlgebrasWithBasis(QQ).example() - sage: F.one() == 1 - True - sage: 1 == F.one() - True - sage: 2 * F.one() == int(2) - True - sage: int(2) == 2 * F.one() - True - - sage: S = SymmetricFunctions(QQ); s = S.s(); p = S.p() - sage: p[2] == s[2] - s[1, 1] - True - sage: p[2] == s[2] - False - - This feature is disputable, in particular since it can make - equality testing costly. It may be removed at some point. - - Equality testing can be a bit tricky when the order of terms - can vary because their indices are incomparable with - ``cmp``. The following test did fail before :trac:`12489` :: - - sage: F = CombinatorialFreeModule(QQ, Subsets([1,2,3])) - sage: x = F.an_element() - sage: (x+F.zero()).terms() # random - [2*B[{1}], 3*B[{2}], B[{}]] - sage: x.terms() # random - [2*B[{1}], B[{}], 3*B[{2}]] - sage: x+F.zero() == x - True - - TESTS:: - - sage: TestSuite(F1).run() - sage: TestSuite(F).run() - """ - if have_same_parent(self, other): - return self._monomial_coefficients == other._monomial_coefficients - from sage.structure.element import get_coercion_model - import operator - try: - return get_coercion_model().bin_op(self, other, operator.eq) - except TypeError: - return False - - def __ne__(left, right): - """ - EXAMPLES:: - - sage: F1 = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: F1.an_element() != F1.an_element() - False - sage: F1.an_element() != F1.zero() - True - """ - return not left == right - - def __cmp__(left, right): - """ - The ordering is the one on the underlying sorted list of - (monomial,coefficients) pairs. - - EXAMPLES:: - - sage: s = SymmetricFunctions(QQ).schur() - sage: a = s([2,1]) - sage: b = s([1,1,1]) - sage: cmp(a,b) #indirect doctest - 1 - """ - if have_same_parent(left, right) and left._monomial_coefficients == right._monomial_coefficients: - return 0 - v = sorted(mc for mc in left._monomial_coefficients.items() if mc[1] != 0) - w = sorted(mc for mc in right._monomial_coefficients.items() if mc[1] != 0) - return cmp(v, w) - - def _add_(self, other): - """ - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: B['a'] + 3*B['c'] - B['a'] + 3*B['c'] - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: s([2,1]) + s([5,4]) # indirect doctest - s[2, 1] + s[5, 4] - sage: a = s([2,1]) + 0 - sage: len(a.monomial_coefficients()) - 1 - """ - F = self.parent() - return F._from_dict(blas.add(self._monomial_coefficients, - other._monomial_coefficients), - remove_zeros=False) - - def _neg_(self): - """ - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] + 3*B['c'] - sage: -f - -B['a'] - 3*B['c'] - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: -s([2,1]) # indirect doctest - -s[2, 1] - """ - F = self.parent() - return F._from_dict(blas.negate(self._monomial_coefficients), - remove_zeros=False) - - def _sub_(self, other): - """ - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: B['a'] - 3*B['c'] - B['a'] - 3*B['c'] - - :: - - sage: s = SymmetricFunctions(QQ).schur() - sage: s([2,1]) - s([5,4]) # indirect doctest - s[2, 1] - s[5, 4] - """ - F = self.parent() - return F._from_dict(blas.axpy(-1, - other._monomial_coefficients, - self._monomial_coefficients), - remove_zeros=False) - - def _coefficient_fast(self, m): - """ - Return the coefficient of ``m`` in ``self``, where ``m`` is key in - ``self._monomial_coefficients``. - - EXAMPLES:: - - sage: p = Partition([2,1]) - sage: q = Partition([1,1,1]) - sage: s = SymmetricFunctions(QQ).schur() - sage: a = s(p) - sage: a._coefficient_fast([2,1]) - Traceback (most recent call last): - ... - TypeError: unhashable type: 'list' - - :: - - sage: a._coefficient_fast(p) - 1 - sage: a._coefficient_fast(q) - 0 - sage: a[p] - 1 - sage: a[q] - 0 - """ - return self._monomial_coefficients.get(m, self.base_ring().zero()) - - __getitem__ = _coefficient_fast - - def _vector_(self, new_base_ring=None): - """ - Returns ``self`` as a dense vector - - INPUT: - - - ``new_base_ring`` -- a ring (default: ``None``) - - OUTPUT: a dense :func:`FreeModule` vector - - .. WARNING:: This will crash/run forever if ``self`` is infinite dimensional! - - .. SEEALSO:: - - - :func:`vector` - - :meth:`CombinatorialFreeModule.get_order` - - :meth:`CombinatorialFreeModule.from_vector` - - :meth:`CombinatorialFreeModule._dense_free_module` - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: f = B['a'] - 3*B['c'] - sage: f._vector_() - (1, 0, -3) - - One can use equivalently:: - - sage: f.to_vector() - (1, 0, -3) - sage: vector(f) - (1, 0, -3) - - More examples:: - - sage: QS3 = SymmetricGroupAlgebra(QQ, 3) - sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1]) - sage: a._vector_() - (2, 0, 0, 0, 0, 4) - sage: a.to_vector() - (2, 0, 0, 0, 0, 4) - sage: vector(a) - (2, 0, 0, 0, 0, 4) - sage: a == QS3.from_vector(a.to_vector()) - True - - If ``new_base_ring`` is specified, then a vector over - ``new_base_ring`` is returned:: - - sage: a._vector_(RDF) - (2.0, 0.0, 0.0, 0.0, 0.0, 4.0) - - .. NOTE:: - - :trac:`13406`: the current implementation has been optimized, at - the price of breaking the encapsulation for FreeModule - elements creation, with the following use case as metric, - on a 2008' Macbook Pro:: - - sage: F = CombinatorialFreeModule(QQ, range(10)) - sage: f = F.an_element() - sage: %timeit f._vector_() # not tested - 625 loops, best of 3: 17.5 micros per loop - - Other use cases may call for different or further - optimizations. - """ - parent = self.parent() - dense_free_module = parent._dense_free_module(new_base_ring) - d = self._monomial_coefficients - return dense_free_module.element_class(dense_free_module, - [d.get(m, 0) for m in parent.get_order()], - coerce=True, copy=False) - - to_vector = _vector_ - - def _acted_upon_(self, scalar, self_on_left=False): - """ - Return the action of ``scalar`` (an element of the base ring) on - ``self``. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) - sage: B = F.basis() - sage: B['a']*(1/2) # indirect doctest - 1/2*B['a'] - sage: B['a']/2 - 1/2*B['a'] - sage: B['a']*2 # indirect doctest - 2*B['a'] - sage: B['a']*int(2) # indirect doctest - 2*B['a'] - - sage: 1/2*B['a'] - 1/2*B['a'] - sage: 2*B['a'] # indirect doctest - 2*B['a'] - sage: int(2)*B['a'] # indirect doctest - 2*B['a'] - - TESTS:: - - sage: F.get_action(QQ, operator.mul, True) - Right action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field - sage: F.get_action(QQ, operator.mul, False) - Left action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field - sage: F.get_action(ZZ, operator.mul, True) - Right action by Integer Ring on Free module generated by {'a', 'b', 'c'} over Rational Field - sage: F.get_action(F, operator.mul, True) - sage: F.get_action(F, operator.mul, False) - - This also works when a coercion of the coefficient is needed, for - example with polynomials or fraction fields (:trac:`8832`):: - - sage: P. = QQ['q'] - sage: V = CombinatorialFreeModule(P, Permutations()) - sage: el = V(Permutation([3,1,2])) - sage: (3/2)*el - 3/2*B[[3, 1, 2]] - - sage: P. = QQ['q'] - sage: F = FractionField(P) - sage: V = CombinatorialFreeModule(F, Words()) - sage: w = Words()('abc') - sage: (1+q)*V(w) - (q+1)*B[word: abc] - sage: ((1+q)/q)*V(w) - ((q+1)/q)*B[word: abc] - - TODO: - - add non commutative tests - """ - # With the current design, the coercion model does not have - # enough information to detect a priori that this method only - # accepts scalars; so it tries on some elements(), and we need - # to make sure to report an error. - if isinstance(scalar, Element) and scalar.parent() is not self.base_ring(): - # Temporary needed by coercion (see Polynomial/FractionField tests). - if self.base_ring().has_coerce_map_from(scalar.parent()): - scalar = self.base_ring()( scalar ) - else: - return None - - F = self.parent() - D = self._monomial_coefficients - return F._from_dict(blas.scal(scalar, D, factor_on_left=not self_on_left), - remove_zeros=False) - - # For backward compatibility - _lmul_ = _acted_upon_ - _rmul_ = _acted_upon_ - - def __truediv__(self, x): - """ - Division by coefficients. - - EXAMPLES:: - - sage: F = CombinatorialFreeModule(QQ, [1,2,3]) - sage: x = F._from_dict({1:2, 2:3}) - sage: x/2 - B[1] + 3/2*B[2] - - :: - - sage: F = CombinatorialFreeModule(QQ, [1,2,3]) - sage: B = F.basis() - sage: f = 2*B[2] + 4*B[3] - sage: f/2 - B[2] + 2*B[3] - """ - if not self.base_ring().is_field(): - return self.map_coefficients(lambda c: _divide_if_possible(c, x)) - - F = self.parent() - x = self.base_ring()( x ) - x_inv = x**-1 - D = self._monomial_coefficients - return F._from_dict(blas.scal(x_inv, D), - remove_zeros=False) - - __div__ = __truediv__ - - -def _divide_if_possible(x, y): - """ - EXAMPLES:: - - sage: from sage.combinat.free_module import _divide_if_possible - sage: _divide_if_possible(4, 2) - 2 - sage: _.parent() - Integer Ring - - :: - - sage: _divide_if_possible(4, 3) - Traceback (most recent call last): - ... - ValueError: 4 is not divisible by 3 - """ - q, r = x.quo_rem(y) - if r != 0: - raise ValueError("%s is not divisible by %s"%(x, y)) - else: - return q - class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): r""" Class for free modules with a named basis @@ -846,7 +47,7 @@ class CombinatorialFreeModule(UniqueRepresentation, Module, IndexedGenerators): - ``element_class`` - the class of which elements of this module should be instances (optional, default None, in which case the elements are instances of - :class:`CombinatorialFreeModuleElement`) + :class:`~sage.modules.with_basis.indexed_element.IndexedFreeModuleElement`) - ``category`` - the category in which this module lies (optional, default None, in which case use the "category of modules with @@ -1084,7 +285,45 @@ def __classcall_private__(cls, base_ring, basis_keys, category = None, prefix="B keywords['latex_bracket'] = tuple(latex_bracket) return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category = category, prefix=prefix, **keywords) - Element = CombinatorialFreeModuleElement + # We make this explicitly a Python class so that the methods, + # specifically _mul_, from category framework still works. -- TCS + # We also need to deal with the old pickles too. -- TCS + Element = IndexedFreeModuleElement + + @lazy_attribute + def element_class(self): + """ + The (default) class for the elements of this parent + + Overrides :meth:`Parent.element_class` to force the + construction of Python class. This is currently needed to + inherit really all the features from categories, and in + particular the initialization of ``_mul_`` in + :meth:`Magmas.ParentMethods.__init_extra__`. + + EXAMPLES:: + + sage: A = Algebras(QQ).WithBasis().example(); A + An example of an algebra with basis: + the free algebra on the generators ('a', 'b', 'c') over Rational Field + + sage: A.element_class.mro() + [, + , + ...] + sage: a,b,c = A.algebra_generators() + sage: a * b + B[word: ab] + + TESTS:: + + sage: A.__class__.element_class.__module__ + 'sage.combinat.free_module' + """ + return self.__make_element_class__(self.Element, + name="%s.element_class"%self.__class__.__name__, + module=self.__class__.__module__, + inherit=True) def __init__(self, R, basis_keys, element_class = None, category = None, prefix="B", **kwds): r""" @@ -1199,7 +438,6 @@ def _ascii_art_term(self, m): sage: ascii_art(R.one()) # indirect doctest 1 """ - from sage.typeset.ascii_art import AsciiArt try: if m == self.one_basis(): return AsciiArt(["1"]) @@ -1291,7 +529,7 @@ def __contains__(self, x): sage: 5/3 in F False """ - return sage.structure.element.parent(x) == self # is self? + return parent(x) == self # is self? def _element_constructor_(self, x): """ @@ -1409,7 +647,7 @@ def _element_constructor_(self, x): elif ((hasattr(self._indices, 'element_class') and isinstance(self._indices.element_class, type) and isinstance(x, self._indices.element_class)) - or (sage.structure.element.parent(x) == self._indices)): + or (parent(x) == self._indices)): return self.monomial(x) elif x in self._indices: return self.monomial(self._indices(x)) @@ -1653,7 +891,7 @@ def from_vector(self, vector): True """ cc = self.get_order() - return self._from_dict(dict( (cc[index], coeff) for (index,coeff) in six.iteritems(vector))) + return self._from_dict({cc[index]: coeff for (index,coeff) in six.iteritems(vector)}) def __cmp__(self, other): """ @@ -1888,6 +1126,36 @@ def _from_dict(self, d, coerce=False, remove_zeros=True): d = {key: coeff for key, coeff in six.iteritems(d) if coeff} return self.element_class( self, d ) +class CombinatorialFreeModuleElement(CombinatorialFreeModule.Element): + """ + Deprecated. Use + :class:`sage.modules.with_basis.indexed_element.IndexedFreeModuleElement` + or :class:`CombinatorialFreeModule.Element` instead. + """ + def __init__(self, *args, **kwds): + """ + TESTS:: + + sage: from sage.combinat.free_module import CombinatorialFreeModuleElement + sage: class Test(CombinatorialFreeModule): + ....: class Element(CombinatorialFreeModuleElement): + ....: pass + sage: T = Test(QQ, (1,2)) + sage: T.an_element() + doctest:warning + ... + DeprecationWarning: CombinatorialFreeModuleElement is deprecated. + Use IndexedFreeModuleElement or CombinatorialFreeModule.Element instead. + See http://trac.sagemath.org/22632 for details. + 2*B[1] + 2*B[2] + """ + from sage.misc.superseded import deprecation + deprecation(22632, "CombinatorialFreeModuleElement is deprecated." + " Use IndexedFreeModuleElement" + " or CombinatorialFreeModule.Element instead.") + super(CombinatorialFreeModuleElement, self).__init__(*args, **kwds) + + class CombinatorialFreeModule_Tensor(CombinatorialFreeModule): """ Tensor Product of Free Modules @@ -2056,7 +1324,6 @@ def _ascii_art_(self, term): ## # ## """ - from sage.categories.tensor import tensor if hasattr(self, "_print_options"): symb = self._print_options['tensor_symbol'] if symb is None: @@ -2086,7 +1353,6 @@ def _unicode_art_(self, term): └┴┘ ├┼┐ └┴┘ """ - from sage.categories.tensor import tensor if hasattr(self, "_print_options"): symb = self._print_options['tensor_symbol'] if symb is None: @@ -2133,7 +1399,6 @@ def _repr_term(self, term): sage: tensor([f, g]) # indirect doctest 2*F[1] # G[3] + F[1] # G[4] + 4*F[2] # G[3] + 2*F[2] # G[4] """ - from sage.categories.tensor import tensor if hasattr(self, "_print_options"): symb = self._print_options['tensor_symbol'] if symb is None: @@ -2191,7 +1456,7 @@ def tensor_constructor(self, modules): 2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6] """ assert(module in ModulesWithBasis(self.base_ring()) for module in modules) - assert(sage.categories.tensor.tensor(modules) == self) + assert(tensor(modules) == self) # a list l such that l[i] is True if modules[i] is readily a tensor product is_tensor = [isinstance(module, CombinatorialFreeModule_Tensor) for module in modules] # the tensor_constructor, on basis elements @@ -2530,4 +1795,3 @@ class Element(CombinatorialFreeModule.Element): # TODO: get rid of this inherita pass CombinatorialFreeModule.CartesianProduct = CombinatorialFreeModule_CartesianProduct - diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 64f8b1d1003..8906c53bdd3 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -1215,7 +1215,7 @@ def szekeres_difference_set_pair(m, check=True): whenever `b+1 \in G`. See also Theorem 2.6 in [SWW72]_ (there the formula for `B` is correct, as opposed to (4.2) in [Sz69]_, where the sign before `1` is wrong. - In modern terminilogy, for `m>1` the sets `A` and `B` form a + In modern terminology, for `m>1` the sets `A` and `B` form a :func:`difference family` with parameters `(2m+1,m,1)`. I.e. each non-identity `g \in G` can be expressed uniquely as `xy^{-1}` for `x,y \in A` or `x,y \in B`. Other, specific to this construction, properties of `A` and `B` are: for `a` in `A` one has diff --git a/src/sage/combinat/ncsf_qsym/generic_basis_code.py b/src/sage/combinat/ncsf_qsym/generic_basis_code.py index 64bd73c8780..ccc043afd13 100644 --- a/src/sage/combinat/ncsf_qsym/generic_basis_code.py +++ b/src/sage/combinat/ncsf_qsym/generic_basis_code.py @@ -1013,7 +1013,7 @@ def __init__(self, domain, on_generators, position = 0, codomain = None, categor EXAMPLES: - We construct explicitly an algbera morphism:: + We construct explicitly an algebra morphism:: sage: from sage.combinat.ncsf_qsym.generic_basis_code import AlgebraMorphism sage: NCSF = NonCommutativeSymmetricFunctions(QQ) diff --git a/src/sage/combinat/ncsf_qsym/qsym.py b/src/sage/combinat/ncsf_qsym/qsym.py index 092d4c81c6f..fe850a03e37 100644 --- a/src/sage/combinat/ncsf_qsym/qsym.py +++ b/src/sage/combinat/ncsf_qsym/qsym.py @@ -52,6 +52,11 @@ non-commutative symmetric functions*, :arxiv:`1208.5191v3`. +.. [Hoff2015] Michael Hoffman. + *Quasi-symmetric functions and mod* `p` *multiple harmonic sums*. + Kyushu J. Math. **69** (2015), pp. 345-366. + :doi:`10.2206/kyushujm.69.345`, :arxiv:`math/0401319v3`. + AUTHOR: - Jason Bandlow @@ -610,7 +615,7 @@ def a_realization(self): """ return self.Monomial() - _shorthands = tuple(['M', 'F', 'dI', 'QS']) + _shorthands = tuple(['M', 'F', 'E', 'dI', 'QS']) def dual(self): r""" @@ -2574,6 +2579,200 @@ def star_involution(self): F = Fundamental + class Essential(CombinatorialFreeModule, BindableClass): + r""" + The Hopf algebra of quasi-symmetric functions in the Essential basis. + + The Essential quasi-symmetric functions are defined by + + .. MATH:: + + E_I = \sum_{J \geq I} M_J = \sum_{i_1 \leq \cdots \leq i_k} + x_{i_1}^{I_1} \cdots x_{i_k}^{I_k}, + + where `I = (I_1, \ldots, I_k)`. + + .. NOTE:: + + Our convention of `\leq` and `\geq` of compositions is + opposite that of [Hoff2015]_. + + EXAMPLES:: + + sage: QSym = QuasiSymmetricFunctions(QQ) + sage: E = QSym.E() + sage: M = QSym.M() + sage: E(M[2,2]) + E[2, 2] - E[4] + sage: s = SymmetricFunctions(QQ).s() + sage: E(s[3,2]) + 5*E[1, 1, 1, 1, 1] - 2*E[1, 1, 1, 2] - 2*E[1, 1, 2, 1] + - 2*E[1, 2, 1, 1] + E[1, 2, 2] - 2*E[2, 1, 1, 1] + + E[2, 1, 2] + E[2, 2, 1] + sage: (1 + E[1])^3 + E[] + 3*E[1] + 6*E[1, 1] + 6*E[1, 1, 1] - 3*E[1, 2] + - 3*E[2] - 3*E[2, 1] + E[3] + sage: E[1,2,1].coproduct() + E[] # E[1, 2, 1] + E[1] # E[2, 1] + E[1, 2] # E[1] + E[1, 2, 1] # E[] + + The following is an alias for this basis:: + + sage: QSym.Essential() + Quasisymmetric functions over the Rational Field in the Essential basis + + TESTS:: + + sage: E(M([])) + E[] + sage: E(M(0)) + 0 + sage: E(s([])) + E[] + sage: E(s(0)) + 0 + """ + def __init__(self, QSym): + """ + EXAMPLES:: + + sage: E = QuasiSymmetricFunctions(QQ).Essential() + sage: TestSuite(E).run() + + TESTS:: + + sage: E = QuasiSymmetricFunctions(QQ).E() + sage: M = QuasiSymmetricFunctions(QQ).M() + sage: all(E(M(E[c])) == E[c] for n in range(5) + ....: for c in Compositions(n)) + True + sage: all(M(E(M[c])) == M[c] for n in range(5) + ....: for c in Compositions(n)) + True + """ + CombinatorialFreeModule.__init__(self, QSym.base_ring(), Compositions(), + prefix='E', bracket=False, + category=QSym.Bases()) + + M = QSym.M() + category = self.realization_of()._category + # This changes Monomial into Essential + M.module_morphism(self.alternating_sum_of_fatter_compositions, + codomain=self, category=category + ).register_as_coercion() + # This changes Essential into Monomial + self.module_morphism(M.sum_of_fatter_compositions, + codomain=M, category=category + ).register_as_coercion() + + def antipode_on_basis(self, compo): + r""" + Return the result of the antipode applied to a quasi-symmetric + Essential basis element. + + INPUT: + + - ``compo`` -- composition + + OUTPUT: + + - The result of the antipode applied to the composition ``compo``, + expressed in the Essential basis. + + EXAMPLES:: + + sage: E = QuasiSymmetricFunctions(QQ).E() + sage: E.antipode_on_basis(Composition([2,1])) + E[1, 2] - E[3] + sage: E.antipode_on_basis(Composition([])) + E[] + + TESTS:: + + sage: E = QuasiSymmetricFunctions(QQ).E() + sage: M = QuasiSymmetricFunctions(QQ).M() + sage: all(E(M(E[c]).antipode()) == E[c].antipode() + ....: for n in range(5) for c in Compositions(n)) + True + + sage: all((-1)**len(I) * E[I] == M[I].star_involution().antipode() + ....: for k in [3,4] for I in Compositions(k)) + True + """ + return (-1)**len(compo) * self.alternating_sum_of_fatter_compositions(compo.reversed()) + + def coproduct_on_basis(self, compo): + r""" + Return the coproduct of a Essential basis element. + + Combinatorial rule: deconcatenation. + + INPUT: + + - ``compo`` -- composition + + OUTPUT: + + - The coproduct applied to the Essential quasi-symmetric function + indexed by ``compo``, expressed in the Essential basis. + + EXAMPLES:: + + sage: E = QuasiSymmetricFunctions(QQ).Essential() + sage: E[4,2,3].coproduct() + E[] # E[4, 2, 3] + E[4] # E[2, 3] + E[4, 2] # E[3] + E[4, 2, 3] # E[] + sage: E.coproduct_on_basis(Composition([])) + E[] # E[] + """ + return self.tensor_square().sum_of_monomials((self._indices(compo[:i]), + self._indices(compo[i:])) + for i in range(0,len(compo)+1)) + + def product_on_basis(self, I, J): + """ + The product on Essential basis elements. + + The product of the basis elements indexed by two compositions + `I` and `J` is the sum of the basis elements indexed by + compositions `K` in the stuffle product (also called the + overlapping shuffle product) of `I` and `J` with a + coefficient of `(-1)^{\ell(I) + \ell(J) - \ell(K)}`, + where `\ell(C)` is the length of the composition `C`. + + INPUT: + + - ``I``, ``J`` -- compositions + + OUTPUT: + + - The product of the Essential quasi-symmetric functions indexed + by ``I`` and ``J``, expressed in the Essential basis. + + EXAMPLES:: + + sage: E = QuasiSymmetricFunctions(QQ).E() + sage: c1 = Composition([2]) + sage: c2 = Composition([1,3]) + sage: E.product_on_basis(c1, c2) + E[1, 2, 3] + E[1, 3, 2] - E[1, 5] + E[2, 1, 3] - E[3, 3] + sage: E.product_on_basis(c1, Composition([])) + E[2] + sage: E.product_on_basis(c1, Composition([3])) + E[2, 3] + E[3, 2] - E[5] + + TESTS:: + + sage: E = QuasiSymmetricFunctions(QQ).E() + sage: M = QuasiSymmetricFunctions(QQ).M() + sage: all(E(M(E[cp])*M(E[c])) == E[cp]*E[c] # long time + ....: for c in Compositions(3) for cp in Compositions(5)) + True + """ + n = len(I) + len(J) + return self.sum_of_terms((K, (-1)**(n - len(K))) + for K in I.shuffle_product(J, overlap=True)) + + E = Essential + class Quasisymmetric_Schur(CombinatorialFreeModule, BindableClass): r""" The Hopf algebra of quasi-symmetric function in the Quasisymmetric diff --git a/src/sage/combinat/ncsf_qsym/tutorial.py b/src/sage/combinat/ncsf_qsym/tutorial.py index 5cf429b0d17..90bb9508685 100644 --- a/src/sage/combinat/ncsf_qsym/tutorial.py +++ b/src/sage/combinat/ncsf_qsym/tutorial.py @@ -3,7 +3,7 @@ Introduction to Quasisymmetric Functions In this document we briefly explain the quasisymmetric function bases and -related functionality in Sage. We assume the reader is familar with the +related functionality in Sage. We assume the reader is familiar with the package :class:`SymmetricFunctions`. Quasisymmetric functions, denoted `QSym`, form a subring of the power @@ -48,6 +48,8 @@ sage: QSym.inject_shorthands() Injecting M as shorthand for Quasisymmetric functions over the Rational Field in the Monomial basis Injecting F as shorthand for Quasisymmetric functions over the Rational Field in the Fundamental basis + Injecting E as shorthand for Quasisymmetric functions over the Rational Field in the Essential basis + doctest:...: RuntimeWarning: redefining global value `E` Injecting dI as shorthand for Quasisymmetric functions over the Rational Field in the dualImmaculate basis Injecting QS as shorthand for Quasisymmetric functions over the Rational Field in the Quasisymmetric Schur basis diff --git a/src/sage/combinat/posets/elements.py b/src/sage/combinat/posets/elements.py index 9075256de8b..2fa5aa1ac2a 100644 --- a/src/sage/combinat/posets/elements.py +++ b/src/sage/combinat/posets/elements.py @@ -82,7 +82,7 @@ def _latex_(self): sage: P = Poset(([m],[]), facade = False) sage: [e] = P sage: type(e) - + sage: latex(e) #indirect doctest \left(\begin{array}{rr} 1 & 2 \\ diff --git a/src/sage/combinat/posets/lattices.py b/src/sage/combinat/posets/lattices.py index d83e9c38aff..c49f2086fac 100644 --- a/src/sage/combinat/posets/lattices.py +++ b/src/sage/combinat/posets/lattices.py @@ -54,7 +54,8 @@ :meth:`~FiniteLatticePoset.is_sectionally_complemented` | Return ``True`` if every interval from the bottom is complemented. :meth:`~FiniteLatticePoset.is_cosectionally_complemented` | Return ``True`` if every interval to the top is complemented. :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_pseudocomplemented` | Return ``True`` if every element of the lattice has a (meet-)pseudocomplement. + :meth:`~FiniteLatticePoset.is_join_pseudocomplemented` | Return ``True`` if every element of the lattice has a join-pseudocomplement. :meth:`~FiniteLatticePoset.is_orthocomplemented` | Return ``True`` if the lattice has an orthocomplementation. :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. @@ -66,6 +67,7 @@ :meth:`~FiniteLatticePoset.is_uniform` | Return ``True`` if all congruences of the lattice consists of equal-sized blocks. :meth:`~FiniteLatticePoset.is_regular` | Return ``True`` if all congruences of lattice are determined by any of the congruence blocks. :meth:`~FiniteLatticePoset.is_subdirectly_reducible` | Return ``True`` if the lattice is a sublattice of the product of smaller lattices. + :meth:`~FiniteLatticePoset.is_constructible_by_doublings` | Return ``True`` if the lattice is constructible by doublings from the one-element lattice. :meth:`~FiniteLatticePoset.breadth` | Return the breadth of the lattice. **Specific elements** @@ -323,7 +325,7 @@ def atoms(self): .. SEEALSO:: - :meth:`~FiniteJoinSemilattice.coatoms()`. + - Dual function: :meth:`~FiniteJoinSemilattice.coatoms` EXAMPLES:: @@ -385,7 +387,7 @@ def pseudocomplement(self, element): sage: L.complements(2), L.pseudocomplement(2) ([3, 4], None) - .. SEEALSO:: :meth:`sage.combinat.posets.lattices.FiniteLatticePoset.is_pseudocomplemented()`. + .. SEEALSO:: :meth:`~sage.combinat.posets.lattices.FiniteLatticePoset.is_pseudocomplemented` TESTS:: @@ -575,7 +577,7 @@ def coatoms(self): .. SEEALSO:: - :meth:`~FiniteMeetSemilattice.atoms()`. + - Dual function: :meth:`~FiniteMeetSemilattice.atoms` EXAMPLES:: @@ -753,8 +755,8 @@ def join_primes(self): .. SEEALSO:: - :meth:`meet_primes`, - :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.join_irreducibles` + - Dual function: :meth:`meet_primes` + - Other: :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.join_irreducibles` EXAMPLES:: @@ -790,8 +792,8 @@ def meet_primes(self): .. SEEALSO:: - :meth:`join_primes`, - :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.meet_irreducibles` + - Dual function: :meth:`join_primes` + - Other: :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.meet_irreducibles` EXAMPLES:: @@ -874,7 +876,8 @@ def is_join_distributive(self, certificate=False): .. SEEALSO:: - Dual property: :meth:`is_meet_distributive` - - Weaker properties: :meth:`is_meet_semidistributive`, :meth:`is_upper_semimodular` + - Weaker properties: :meth:`is_meet_semidistributive`, + :meth:`is_upper_semimodular` - Stronger properties: :meth:`is_distributive` EXAMPLES:: @@ -963,7 +966,8 @@ def is_meet_distributive(self, certificate=False): .. SEEALSO:: - Dual property: :meth:`is_join_distributive` - - Weaker properties: :meth:`is_join_semidistributive`, :meth:`is_lower_semimodular` + - Weaker properties: :meth:`is_join_semidistributive`, + :meth:`is_lower_semimodular` - Stronger properties: :meth:`is_distributive` EXAMPLES:: @@ -1138,9 +1142,10 @@ def is_distributive(self, certificate=False): .. SEEALSO:: - - Weaker properties: :meth:`is_modular`, :meth:`is_semidistributive`, - :meth:`is_join_distributive`, :meth:`is_meet_distributive`, - :meth:`is_subdirectly_reducible` + - Weaker properties: :meth:`is_modular`, + :meth:`is_semidistributive`, :meth:`is_join_distributive`, + :meth:`is_meet_distributive`, :meth:`is_subdirectly_reducible`, + :meth:`is_constructible_by_doublings` (by interval doubling) - Stronger properties: :meth:`is_stone` EXAMPLES:: @@ -1190,7 +1195,8 @@ def is_semidistributive(self): .. SEEALSO:: - - Weaker properties: :meth:`is_join_semidistributive`, :meth:`is_meet_semidistributive` + - Weaker properties: :meth:`is_join_semidistributive`, + :meth:`is_meet_semidistributive` - Stronger properties: :meth:`is_distributive` EXAMPLES: @@ -1256,7 +1262,9 @@ def is_meet_semidistributive(self, certificate=False): - Dual property: :meth:`is_join_semidistributive` - Weaker properties: :meth:`is_pseudocomplemented` - - Stronger properties: :meth:`is_semidistributive`, :meth:`is_join_distributive` + - Stronger properties: :meth:`is_semidistributive`, + :meth:`is_join_distributive`, + :meth:`is_constructible_by_doublings` (by upper pseudo-intervals) EXAMPLES:: @@ -1344,7 +1352,9 @@ def is_join_semidistributive(self, certificate=False): .. SEEALSO:: - Dual property: :meth:`is_meet_semidistributive` - - Stronger properties: :meth:`is_semidistributive`, :meth:`is_meet_distributive` + - Stronger properties: :meth:`is_semidistributive`, + :meth:`is_meet_distributive`, + :meth:`is_constructible_by_doublings` (by lower pseudo-intervals) EXAMPLES:: @@ -1430,7 +1440,8 @@ def is_complemented(self, certificate=False): .. SEEALSO:: - Stronger properties: :meth:`is_sectionally_complemented`, - :meth:`is_cosectionally_complemented`, :meth:`is_orthocomplemented` + :meth:`is_cosectionally_complemented`, + :meth:`is_orthocomplemented` - Other: :meth:`complements` EXAMPLES:: @@ -1839,12 +1850,14 @@ def complements(self, element=None): complements for all elements having at least one complement is returned. + .. SEEALSO:: :meth:`is_complemented` + EXAMPLES:: sage: L=LatticePoset({0:['a','b','c'], 'a':[1], 'b':[1], 'c':[1]}) sage: C = L.complements() - Let us check that `'a'` and `'b'` are complements of each other:: + Let us check that 'a' and 'b' are complements of each other:: sage: 'a' in C['b'] True @@ -1853,11 +1866,11 @@ def complements(self, element=None): Full list of complements:: - sage: L.complements() # random + sage: L.complements() # random order {0: [1], 1: [0], 'a': ['b', 'c'], 'b': ['c', 'a'], 'c': ['b', 'a']} sage: L=LatticePoset({0:[1,2],1:[3],2:[3],3:[4]}) - sage: L.complements() # random + sage: L.complements() # random order {0: [4], 4: [0]} sage: L.complements(1) [] @@ -1945,6 +1958,7 @@ def is_pseudocomplemented(self, certificate=False): .. SEEALSO:: + - Dual property: :meth:`is_join_pseudocomplemented` - Stronger properties: :meth:`is_meet_semidistributive` - Other: :meth:`~sage.combinat.posets.lattices.FiniteMeetSemilattice.pseudocomplement()`. @@ -1972,6 +1986,65 @@ def is_pseudocomplemented(self, certificate=False): return (True, None) return True + def is_join_pseudocomplemented(self, certificate=False): + """ + Return ``True`` if the lattice is join-pseudocomplemented, and + ``False`` otherwise. + + A lattice is join-pseudocomplemented if every element `e` has a + join-pseudocomplement `e'`, i.e. the greatest element such that + the join of `e` and `e'` is the top element. + + INPUT: + + - ``certificate`` -- (default: ``False``) whether to return + a certificate + + OUTPUT: + + - If ``certificate=True`` return either ``(True, None)`` or + ``(False, e)``, where ``e`` is an element without a + join-pseudocomplement. If ``certificate=False`` return ``True`` + or ``False``. + + EXAMPLES:: + + sage: L = LatticePoset({1: [2, 5], 2: [3, 6], 3: [4], 4: [7], + ....: 5: [6], 6: [7]}) + sage: L.is_join_pseudocomplemented() + True + + sage: L = LatticePoset({1: [2, 3], 2: [4, 5, 6], 3: [6], 4: [7], + ....: 5: [7], 6: [7]}) + sage: L.is_join_pseudocomplemented() + False + sage: L.is_join_pseudocomplemented(certificate=True) + (False, 4) + + .. SEEALSO:: + + - Dual property: :meth:`is_pseudocomplemented` + - Stronger properties: :meth:`is_join_semidistributive` + + TESTS:: + + sage: LatticePoset({}).is_pseudocomplemented() + True + """ + H = self._hasse_diagram + if H.order() == 0: + if certificate: + return (True, None) + return True + for e in H.neighbor_in_iterator(H.order()-1): + if H.kappa_dual(e) is None: + if certificate: + return (False, self._vertex_to_element(e)) + return False + if certificate: + return (True, None) + return True + def skeleton(self): """ Return the skeleton of the lattice. @@ -2211,7 +2284,7 @@ def is_geometric(self): .. SEEALSO:: - - Weaker properties: :meth:`is_modular`, :meth:`is_relatively_complemented` + - Weaker properties: :meth:`is_upper_semimodular`, :meth:`is_relatively_complemented` EXAMPLES: @@ -2362,7 +2435,8 @@ def is_modular(self, L=None, certificate=False): .. SEEALSO:: - - Weaker properties: :meth:`is_upper_semimodular`, :meth:`is_lower_semimodular` + - Weaker properties: :meth:`is_upper_semimodular`, + :meth:`is_lower_semimodular` - Stronger properties: :meth:`is_distributive` - Other: :meth:`is_modular_element` @@ -2485,7 +2559,8 @@ def is_upper_semimodular(self, certificate=False): - Dual property: :meth:`is_lower_semimodular` - Weaker properties: :meth:`~sage.combinat.posets.posets.FinitePoset.is_graded` - - Stronger properties: :meth:`is_modular`, :meth:`is_join_distributive` + - Stronger properties: :meth:`is_modular`, + :meth:`is_join_distributive`, :meth:`is_geometric` See :wikipedia:`Semimodular_lattice` @@ -2544,7 +2619,8 @@ def is_lower_semimodular(self, certificate=False): - Dual property: :meth:`is_upper_semimodular` - Weaker properties: :meth:`~sage.combinat.posets.posets.FinitePoset.is_graded` - - Stronger properties: :meth:`is_modular`, :meth:`is_meet_distributive` + - Stronger properties: :meth:`is_modular`, + :meth:`is_meet_distributive` See :wikipedia:`Semimodular_lattice` @@ -3504,7 +3580,9 @@ def is_subdirectly_reducible(self, certificate=False): .. SEEALSO:: - - Stronger properties: :meth:`is_distributive`, :meth:`is_vertically_decomposable` + - Stronger properties: :meth:`is_distributive`, + :meth:`is_vertically_decomposable` + - Other: :meth:`subdirect_decomposition` EXAMPLES:: @@ -3527,8 +3605,6 @@ def is_subdirectly_reducible(self, certificate=False): sage: [Posets.ChainPoset(i).is_subdirectly_reducible() for i in range(5)] [False, False, False, True, True] """ - # Todo: Add seealso-link to subdirect_decomposition() when it is done. - H = self._hasse_diagram A = H.atoms_of_congruence_lattice() @@ -3703,41 +3779,74 @@ def is_constructible_by_doublings(self, type): subset of the lattice with a unique minimal element * ``'upper'`` - allow doublings of upper pseudo-interval; that is, a subset of the lattice with a unique maximal element - * ``'convex'`` - allow doubling of any convex set (not implemented) + * ``'convex'`` - allow doubling of any convex set + * ``'any'`` - allow doubling of any set .. SEEALSO:: :meth:`day_doubling` - EXAMPLES:: + EXAMPLES: + + The pentagon can be constructed by doubling intervals; the 5-element + diamond can not be constructed by any doublings:: sage: Posets.PentagonPoset().is_constructible_by_doublings('interval') True + + sage: Posets.DiamondPoset(5).is_constructible_by_doublings('any') + False + + After doubling both upper and lower pseudo-interval a lattice is + constructible by convex subset doubling:: + sage: L = Posets.BooleanLattice(2) sage: L = L.day_doubling([0, 1, 2]) # A lower pseudo-interval sage: L.is_constructible_by_doublings('interval') False sage: L.is_constructible_by_doublings('lower') True - sage: Posets.DiamondPoset(5).is_constructible_by_doublings('convex') # Not implemented + sage: L = L.day_doubling([(3,0), (1,1), (2,1)]) # An upper pseudo-interval + sage: L.is_constructible_by_doublings('upper') False + sage: L.is_constructible_by_doublings('convex') + True + + An example of a lattice that can be constructed by doublings + of a non-convex subsets:: + + sage: L = LatticePoset(DiGraph('OQC?a?@CO?G_C@?GA?O??_??@?BO?A_?G??C??_?@???')) + sage: L.is_constructible_by_doublings('convex') + False + sage: L.is_constructible_by_doublings('any') + True TESTS:: sage: LatticePoset().is_constructible_by_doublings('interval') True + The congruence lattice of this lattice has maximal chains satisfying the needed + property, but also maximal chains not satisfying that; this shows that the code + can't be optimized to test just some maximal chain:: + + sage: L = LatticePoset(DiGraph('QSO?I?_?_GBG??_??a???@?K??A??B???C??s??G??I??@??A??@???')) + sage: L.is_constructible_by_doublings('convex') + False + sage: L.is_constructible_by_doublings('any') + True + ALGORITHM: According to [HOLM2016]_ a lattice `L` is lower bounded if and only if `|\mathrm{Ji}(L)| = |\mathrm{Ji}(\mathrm{Con}\ L)|`, and so dually - `|\mathrm{Mi}(L)| = |\mathrm{Mi}(\mathrm{Con}\ L)|` in - upper bounded lattices. + `|\mathrm{Mi}(L)| = |\mathrm{Mi}(\mathrm{Con}\ L)|` in upper bounded + lattices. The same reference gives a test for being constructible by + convex or by any subset. """ - if type not in ['interval', 'lower', 'upper', 'convex']: - raise ValueError("type must be one on 'interval', 'lower', 'upper' or 'convex'") - if type == 'convex': - raise NotImplementedError("type 'convex' is not yet implemented") + if type not in ['interval', 'lower', 'upper', 'convex', 'any']: + raise ValueError("type must be one of 'interval', 'lower', 'upper', 'convex' or 'any'") + if self.cardinality() < 5: return True @@ -3751,12 +3860,35 @@ def is_constructible_by_doublings(self, type): if type == 'upper': return (len(self.meet_irreducibles()) == self._hasse_diagram.principal_congruences_poset()[0].cardinality()) + if type == 'convex': + return self._hasse_diagram.is_congruence_normal() + # type == 'any' + def splitting_depth_2(a, b): + """ + Return ``True`` if every block of `b` is made from + combining at most two blocks of `a`. + """ + return all(len([x for x in a if x.issubset(y)]) <= 2 for y in b) + + conL = self.congruences_lattice() + todo = [conL[0]] + reachable = [] + + while todo: + e = todo.pop() + for e_up in conL.upper_covers(e): + if e_up not in reachable and splitting_depth_2(e, e_up): + if len(e_up) == 1: # = the top of the cong. lattice + return True + reachable.append(e_up) + todo.append(e_up) + return False def is_isoform(self, certificate=False): """ Return ``True`` if the lattice is isoform and ``False`` otherwise. - A congruence is *isoform* if all blocks are isomorphic + A congruence is *isoform* (or *isotype*) if all blocks are isomorphic sublattices. A lattice is isoform if it has only isoform congruences. @@ -3775,7 +3907,8 @@ def is_isoform(self, certificate=False): .. SEEALSO:: - Weaker properties: :meth:`is_uniform` - - Stronger properties: :meth:`is_simple`, :meth:`is_relatively_complemented` + - Stronger properties: :meth:`is_simple`, + :meth:`is_relatively_complemented` - Other: :meth:`congruence` EXAMPLES:: @@ -3910,9 +4043,9 @@ def is_regular(self, certificate=False): .. SEEALSO:: - - Weaker properties: :meth:`is_sectionally_complemented`, + - Stronger properties: :meth:`is_uniform`, + :meth:`is_sectionally_complemented`, :meth:`is_cosectionally_complemented` - - Stronger properties: :meth:`is_uniform` - Other: :meth:`congruence` EXAMPLES:: diff --git a/src/sage/combinat/posets/linear_extensions.py b/src/sage/combinat/posets/linear_extensions.py index 5d018ee6969..00cdecfdb9e 100644 --- a/src/sage/combinat/posets/linear_extensions.py +++ b/src/sage/combinat/posets/linear_extensions.py @@ -450,18 +450,76 @@ def cardinality(self): 1 sage: Posets.ChainPoset(1).linear_extensions().cardinality() 1 + sage: Posets.BooleanLattice(4).linear_extensions().cardinality() + 1680384 """ from sage.rings.integer import Integer - H = self._poset.order_ideals_lattice(as_ideals=False)._hasse_diagram - L = H.level_sets() - c = [0] * H.order() - for l in L[0]: - c[l] = 1 - for lev in L[1:]: - for l in lev: - c[l] = sum(c[i] for i in H.lower_covers_iterator(l)) - return Integer(sum(c[i] for i in H.sinks())) + n = len(self._poset) + if not n: + return Integer(1) + + up = self._poset._hasse_diagram.to_dictionary() + # Convert to the Hasse diagram so our poset can be realized on + # the set {0,...,n-1} with a nice dictionary of edges + + for i in range(n): + up[n - 1 - i] = sorted(set(up[n - 1 - i] + + [item for x in up[n - 1 - i] + for item in up[x]])) + # Compute the principal order filter for each element. + + Jup = {1: []} + # Jup will be a dictionary giving up edges in J(P) + + # We will perform a loop where after k loops, we will have a + # list of up edges for the lattice of order ideals for P + # restricted to entries 0,...,k. + loc = [1] * n + + # This list will be indexed by entries in P. After k loops, + # the entry loc[i] will correspond to the element of J(P) that + # is the principal order ideal of i, restricted to the + # elements 0,...,k . + + m = 1 + # m keeps track of how many elements we currently have in J(P). + # We start with just the empty order ideal, and no relations. + for x in range(n): + # Use the existing Jup table to compute all covering + # relations in J(P) for things that are above loc(x). + K = [[loc[x]]] + j = 0 + while K[j]: + K.append([b for a in K[j] for b in Jup[a]]) + j += 1 + K = sorted(set(item for sublist in K for item in sublist)) + for j in range(len(K)): + i = m + j + 1 + Jup[i] = [m + K.index(a) + 1 for a in Jup[K[j]]] + # These are copies of the covering relations with + # elements from K, but now with the underlying + # elements containing x. + Jup[K[j]] = Jup[K[j]] + [i] + # There are the new covering relations we get between + # ideals that don't contain x and those that do. + for y in up[x]: + loc[y] = K.index(loc[y]) + m + 1 + # Updates loc[y] if y is above x. + m += len(K) + # Now we have a dictionary of covering relations for J(P). The + # following shortcut works to count maximal chains, since we + # made J(P) naturally labelled, and J(P) has a unique maximal + # element and minimum element. + + Jup[m] = Integer(1) + while m > 1: + m -= 1 + ct = Integer(0) + for j in Jup[m]: + ct += Jup[j] + Jup[m] = ct + return ct def __iter__(self): r""" diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 1454a4b71f9..5de15345f0c 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -72,7 +72,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six import add_metaclass +from six import add_metaclass, string_types from sage.misc.classcall_metaclass import ClasscallMetaclass import sage.categories.posets @@ -618,7 +618,8 @@ def RandomLattice(n, p, properties=None): * ``None``, no restrictions for lattices to create * ``'planar'``, the lattice has an upward planar drawing * ``'dismantlable'`` (implicated by ``'planar'``) - * ``'distributive'`` + * ``'distributive'`` (implicated by ``'stone'``) + * ``'stone'`` OUTPUT: @@ -703,12 +704,12 @@ def RandomLattice(n, p, properties=None): D.relabel([i-1 for i in Permutations(n).random_element()]) return LatticePoset(D, cover_relations=True) - if isinstance(properties, basestring): + if isinstance(properties, string_types): properties = set([properties]) else: properties = set(properties) - known_properties = set(['planar', 'dismantlable', 'distributive']) + known_properties = set(['planar', 'dismantlable', 'distributive', 'stone']) errors = properties.difference(known_properties) if errors: raise ValueError("unknown value %s for 'properties'" % errors.pop()) @@ -717,13 +718,17 @@ def RandomLattice(n, p, properties=None): # Change this, if property='complemented' is added return Posets.ChainPoset(n) - # Handling properties. Every planar lattice is also dismantlable. + # Handling properties: planar => dismantlable, stone => distributive if 'planar' in properties: properties.discard('dismantlable') + if 'stone' in properties: + properties.discard('distributive') # Test property combinations that are not implemented. if 'distributive' in properties and len(properties) > 1: raise NotImplementedError("combining 'distributive' with other properties is not implemented") + if 'stone' in properties and len(properties) > 1: + raise NotImplementedError("combining 'stone' with other properties is not implemented") if properties == set(['planar']): D = _random_planar_lattice(n) @@ -735,6 +740,11 @@ def RandomLattice(n, p, properties=None): D.relabel([i-1 for i in Permutations(n).random_element()]) return LatticePoset(D) + if properties == set(['stone']): + D = _random_stone_lattice(n) + D.relabel([i-1 for i in Permutations(n).random_element()]) + return LatticePoset(D) + if properties == set(['distributive']): tmp = Poset(_random_distributive_lattice(n)).order_ideals_lattice(as_ideals=False) D = copy(tmp._hasse_diagram) @@ -1535,6 +1545,10 @@ def _random_distributive_lattice(n): from sage.combinat.posets.hasse_diagram import HasseDiagram from copy import copy from sage.combinat.subset import Subsets + from sage.graphs.digraph_generators import digraphs + + if n < 4: + return digraphs.Path(n-1) H = HasseDiagram({0: []}) while sum(1 for _ in H.antichains_iterator()) < n: @@ -1559,4 +1573,53 @@ def _random_distributive_lattice(n): H = HasseDiagram(D) return D +def _random_stone_lattice(n): + """ + Return a random Stone lattice on `n` elements. + + INPUT: + + - ``n`` -- number of elements, a non-negative integer + + OUTPUT: + + A random lattice (as a digraph) of `n` elements. + + EXAMPLES:: + + sage: g = sage.combinat.posets.poset_examples._random_stone_lattice(10) + sage: LatticePoset(g).is_stone() + True + + ALGORITHM: + + Randomly split `n` to some factors. For every factor `p` generate + a random distributive lattice on `p-1` elements and add a new new bottom + element to it. Compute the cartesian product of those lattices. + """ + from sage.arith.misc import factor + from sage.combinat.partition import Partitions + from sage.misc.misc_c import prod + from copy import copy + + factors = sum([[f[0]]*f[1] for f in factor(n)], []) + sage.misc.prandom.shuffle(factors) + + part_lengths = list(Partitions(len(factors)).random_element()) + parts = [] + while part_lengths: + x = part_lengths.pop() + parts.append(prod(factors[:x])) + factors = factors[x:] + + result = DiGraph(1) + for p in parts: + g = _random_distributive_lattice(p-1) + g = copy(Poset(g).order_ideals_lattice(as_ideals=False)._hasse_diagram) + g.add_edge('bottom', 0) + result = result.cartesian_product(g) + result.relabel() + + return result + posets = Posets diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index ce3477e9bc8..dd30b18934c 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -104,6 +104,7 @@ :meth:`~FinitePoset.ordinal_product` | Return the ordinal product of the poset with other poset. :meth:`~FinitePoset.star_product` | Return the star product of the poset with other poset. :meth:`~FinitePoset.with_bounds` | Return the poset with bottom and top element adjoined. + :meth:`~FinitePoset.without_bounds` | Return the poset with bottom and top element removed. :meth:`~FinitePoset.dual` | Return the dual of the poset. :meth:`~FinitePoset.completion_by_cuts` | Return the Dedekind-MacNeille completion of the poset. :meth:`~FinitePoset.intervals_poset` | Return the poset of intervals of the poset. @@ -1015,7 +1016,7 @@ def _list(self): sage: L = P._list; L (a, b, c, d) sage: type(L[0]) - + sage: L[0].parent() is P True @@ -1651,7 +1652,7 @@ def list(self): sage: D.list() [0, 1, 2, 3, 4] sage: type(D.list()[0]) - + """ return list(self._list) @@ -4726,6 +4727,10 @@ def with_bounds(self, labels=('bottom', 'top')): ``'top'``. Either of them can be ``None``, and then a new bottom or top element will not be added. + .. SEEALSO:: + + :meth:`without_bounds` for the reverse operation + EXAMPLES:: sage: V = Poset({0: [1, 2]}) @@ -4850,6 +4855,57 @@ def with_bounds(self, labels=('bottom', 'top')): return constructor(D) + def without_bounds(self): + """ + Return the poset without its top and bottom elements. + + This is useful as an input for the method :meth:`order_complex`. + + If there is either no top or no bottom element, this + raises a ``TypeError``. + + .. SEEALSO:: + + :meth:`with_bounds` for the reverse operation + + EXAMPLES:: + + sage: P = posets.PentagonPoset() + sage: Q = P.without_bounds(); Q + Finite poset containing 3 elements + sage: Q.cover_relations() + [[2, 3]] + + sage: P = posets.DiamondPoset(5) + sage: Q = P.without_bounds(); Q + Finite poset containing 3 elements + sage: Q.cover_relations() + [] + + TESTS:: + + sage: P = Poset({1:[2],3:[2,4]}) + sage: P.without_bounds() + Traceback (most recent call last): + ... + TypeError: the poset is missing either top or bottom + + sage: P = Poset({1:[]}) + sage: P.without_bounds() + Finite poset containing 0 elements + + sage: P = Poset({}) + sage: P.without_bounds() + Traceback (most recent call last): + ... + TypeError: the poset is missing either top or bottom + """ + if self.is_bounded(): + top = self.top() + bottom = self.bottom() + return self.subposet(u for u in self if not u in (top, bottom)) + raise TypeError('the poset is missing either top or bottom') + def relabel(self, relabeling=None): r""" Return a copy of this poset with its elements relabeled. @@ -6992,6 +7048,10 @@ def completion_by_cuts(self): - a finite lattice + .. SEEALSO:: + + :meth:`~sage.categories.finite_lattice_posets.FiniteLatticePosets.ParentMethods.irreducibles_poset` + EXAMPLES:: sage: P = Posets.PentagonPoset() diff --git a/src/sage/combinat/rigged_configurations/bij_infinity.py b/src/sage/combinat/rigged_configurations/bij_infinity.py index eef0e31695e..04e7494805a 100644 --- a/src/sage/combinat/rigged_configurations/bij_infinity.py +++ b/src/sage/combinat/rigged_configurations/bij_infinity.py @@ -214,7 +214,8 @@ def run(self): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['B',4]) + sage: vct = CartanType(['B',4]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: T = crystals.infinity.Tableaux(['B',4]) sage: Psi = T.crystal_morphism({T.module_generators[0]: RC.module_generators[0]}) sage: TS = [x.value for x in T.subcrystal(max_depth=4)] @@ -251,7 +252,8 @@ def run(self): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['B',4]) + sage: vct = CartanType(['B',4]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: T = crystals.infinity.Tableaux(['B',4]) sage: Psi = RC.crystal_morphism({RC.module_generators[0]: T.module_generators[0]}) sage: RCS = [x.value for x in RC.subcrystal(max_depth=4)] diff --git a/src/sage/combinat/rigged_configurations/bij_type_B.py b/src/sage/combinat/rigged_configurations/bij_type_B.py index 21dab1dead1..20bf132e306 100644 --- a/src/sage/combinat/rigged_configurations/bij_type_B.py +++ b/src/sage/combinat/rigged_configurations/bij_type_B.py @@ -123,7 +123,9 @@ def run(self, verbose=False): self.ret_rig_con[-1] = RiggedPartition(self.ret_rig_con[-1]._list, self.ret_rig_con[-1].rigging, self.ret_rig_con[-1].vacancy_numbers) - bij = KRTToRCBijectionTypeA2Odd(KRT.module_generators[0]) # Placeholder element + # Placeholder element + elt = KRT(*[C.module_generators[0] for C in KRT.crystals]) + bij = KRTToRCBijectionTypeA2Odd(elt) bij.ret_rig_con = KRT.rigged_configurations()(*self.ret_rig_con, use_vacancy_numbers=True) bij.cur_path = self.cur_path bij.cur_dims = self.cur_dims diff --git a/src/sage/combinat/rigged_configurations/kleber_tree.py b/src/sage/combinat/rigged_configurations/kleber_tree.py index 121fba5b5c2..eedd0ad9d54 100644 --- a/src/sage/combinat/rigged_configurations/kleber_tree.py +++ b/src/sage/combinat/rigged_configurations/kleber_tree.py @@ -73,12 +73,14 @@ from sage.misc.latex import latex from sage.arith.all import binomial from sage.rings.integer import Integer +from sage.rings.all import ZZ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation from sage.structure.sage_object import richcmp_not_equal, richcmp from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.modules.free_module import FreeModule from sage.combinat.root_system.cartan_type import CartanType @@ -116,11 +118,11 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh \end{tikzpicture} """ draw_point = lambda point: '(%.3f, %.3f)'%(point[0],point[1]) - if len(tree_node.children) == 0: + if not tree_node.children: r = '' node_name = node_prefix + str(node_id) r = "\\node (%s) at %s"%(node_name, draw_point(start)) - if(node_label): + if node_label: r += "{$%s$};\n"%tree_node._latex_() else: r += "{};\n" @@ -178,7 +180,7 @@ def _draw_tree(tree_node, node_label=True, style_point=None, style_node='fill=wh rpos[1] = pos[1] point_str = '' node_str = "\\node%s (%s) at %s"%(style_node, node_name, draw_point(pos)) - if(node_label): + if node_label: node_str += "{$%s$};\n"%tree_node._latex_() else: node_str += "{};\n" @@ -221,8 +223,8 @@ def __init__(self, parent_obj, node_weight, dominant_root, parent_node=None): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: parent = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: parent @@ -250,8 +252,8 @@ def depth(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: n.depth @@ -303,11 +305,11 @@ def multiplicity(self): sage: KT = KleberTree(['A',3,1], [[3,2],[2,1],[1,1],[1,1]]) sage: for x in KT: x, x.multiplicity() (Kleber tree node with weight [2, 1, 2] and upwards edge root [0, 0, 0], 1) + (Kleber tree node with weight [3, 0, 1] and upwards edge root [0, 1, 1], 1) (Kleber tree node with weight [0, 2, 2] and upwards edge root [1, 0, 0], 1) (Kleber tree node with weight [1, 0, 3] and upwards edge root [1, 1, 0], 2) (Kleber tree node with weight [1, 1, 1] and upwards edge root [1, 1, 1], 4) (Kleber tree node with weight [0, 0, 2] and upwards edge root [2, 2, 1], 2) - (Kleber tree node with weight [3, 0, 1] and upwards edge root [0, 1, 1], 1) (Kleber tree node with weight [2, 0, 0] and upwards edge root [0, 1, 1], 2) (Kleber tree node with weight [0, 0, 2] and upwards edge root [1, 1, 0], 1) (Kleber tree node with weight [0, 1, 0] and upwards edge root [1, 1, 1], 2) @@ -355,8 +357,8 @@ def __hash__(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: hash(n) @@ -373,8 +375,8 @@ def _richcmp_(self, rhs, op): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: n = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()) sage: n2 = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero(), n) @@ -407,8 +409,8 @@ def _repr_(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: node = KT(WS.sum_of_terms([(1,2), (2,1), (3,1)]), R.sum_of_terms([(1,3), (3,3)])); node Kleber tree node with weight [2, 1, 1] and upwards edge root [3, 0, 3] @@ -431,8 +433,8 @@ def _latex_(self): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 3, 1], [[3,2], [1,1]]) sage: node = KT(WS.sum_of_terms([(1,4), (3,1)]), R.zero()) sage: latex(node) @@ -592,7 +594,7 @@ def __classcall_private__(cls, cartan_type, B, classical=None): raise ValueError("use VirtualKleberTree for non-simply-laced types") # Standardize B input into a tuple of tuples - B = tuple(map(tuple, B)) + B = tuple([tuple(rs) for rs in B]) if classical is None: classical = cartan_type.classical() @@ -619,7 +621,7 @@ def __init__(self, cartan_type, B, classical_ct): self._cartan_type = cartan_type self.B = B self._classical_ct = classical_ct - self._build_tree(B) + self._build_tree() self._latex_options = dict(edge_labels=True, use_vector_notation=False, hspace=2.5, vspace=min(-2.5, -0.75*self._classical_ct.rank())) @@ -647,7 +649,7 @@ def latex_options(self, **options): sage: sorted(KT.latex_options().items()) [('edge_labels', True), ('hspace', 2.5), ('use_vector_notation', True), ('vspace', -4)] """ - if len(options) == 0: + if not options: from copy import copy return copy(self._latex_options) for k in options: @@ -675,7 +677,7 @@ def _latex_(self): _draw_tree(self.root, **self._latex_options) \ + "\\end{tikzpicture}" - def _build_tree(self, B): + def _build_tree(self): """ Build the Kleber tree. @@ -686,9 +688,10 @@ def _build_tree(self, B): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: KT = KleberTree(['A',3,1], [[2,2]]) # indirect doctest """ + P = self._classical_ct.root_system().weight_lattice() # Create an empty node at first step - self.root = KleberTreeNode(self, self._classical_ct.root_system().weight_space().zero(), - self._classical_ct.root_system().root_space().zero()) + self.root = KleberTreeNode(self, P.zero(), + self._classical_ct.root_system().root_lattice().zero()) full_list = [self.root] # The list of tree nodes n = self._classical_ct.rank() @@ -699,18 +702,18 @@ def _build_tree(self, B): for i in range(n): L.append([0]) - for r,s in B: + for r,s in self.B: while len(L[0]) < s: # Add more columns if needed for row in L: row.append(0) L[I.index(r)][s - 1] += 1 # The -1 is for indexing # Perform a special case of the algorithm for the root node - weight_basis = self._classical_ct.root_system().weight_space().basis() + weight_basis = P.basis() for a in range(n): self.root.weight += sum(L[a]) * weight_basis[I[a]] new_children = [] - for new_child in self._children_root_iter(): + for new_child in self._children_iter(self.root): if not self._prune(new_child, 1): new_children.append(new_child) self.root.children.append(new_child) @@ -719,6 +722,12 @@ def _build_tree(self, B): depth = 1 growth = True + # self._has_normaliz is set by _children_iter + if self._classical_ct.rank() >= 7 or self._has_normaliz: + child_itr = self._children_iter + else: + child_itr = self._children_iter_vector + while growth: growth = False depth += 1 @@ -733,17 +742,17 @@ def _build_tree(self, B): x.weight += L[a][i] * weight_basis[I[a]] if x in leaves: - for new_child in self._children_iter(x): + for new_child in child_itr(x): if not self._prune(new_child, depth): new_children.append(new_child) else: for x in leaves: - for new_child in self._children_iter(x): + for new_child in child_itr(x): if not self._prune(new_child, depth): new_children.append(new_child) # Connect the new children into the tree - if len(new_children) > 0: + if new_children: growth = True for new_child in new_children: new_child.parent_node.children.append(new_child) @@ -751,16 +760,23 @@ def _build_tree(self, B): self._set = full_list - def _children_root_iter(self): - """ - Iterate over the children of the root node. + def _children_iter(self, node): + r""" + Iterate over the children of ``node``. Helper iterator to iterate over all children, by generating and/or - computing them, of the Kleber tree root. + computing them, of the Kleber tree node. + + We compute the children by computing integral points (expressed as + simple roots) in the polytope given by the intersection of the + negative root cone and shifted positive weight cone. More precisely, + we rewrite the condition `\lambda - \mu \in Q^+`, for `\mu \in P^+`, + as `\lambda - Q^+ = \mu \in P^+`. + + INPUT: - Right now we are just assuming that if a linear combination of positive - roots keeps us in the Weyl chamber, then a shorter linear combination - does as well. + - ``node`` -- the current node in the tree whose children we want + to generate TESTS:: @@ -770,65 +786,72 @@ def _children_root_iter(self): Kleber tree node with weight [2, 0, 0] and upwards edge root [0, 0, 0] Kleber tree node with weight [0, 1, 1] and upwards edge root [1, 0, 0] Kleber tree node with weight [0, 0, 0] and upwards edge root [2, 1, 1] - """ - pos_roots = list(self._classical_ct.root_system().root_space().positive_roots()) - WS = self._classical_ct.root_system().weight_space() - num_pos_roots = len(pos_roots) - roots_visited = [] - - for root in pos_roots: - # If we've already tried this root - if root in roots_visited: - continue - - # If not, then try it - - roots_visited.append(root) - - new_weight = self.root.weight - WS(root) - - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, root, self.root) - root_stack = [root] - index_stack = [0] - - # Now try all of its children - while len(root_stack) > 0: - # If we've tried all of the roots, then back up - if index_stack[-1] == num_pos_roots: - root_stack.pop() - index_stack.pop() - continue - - new_root = root_stack[-1] + pos_roots[index_stack[-1]] - index_stack[-1] += 1 - - # If we've already tried this root, move on to the next one - if new_root in roots_visited: - continue - - roots_visited.append(new_root) - new_weight = self.root.weight - WS(new_root) - - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, new_root, self.root) - root_stack.append(new_root) - index_stack.append(0) - - def _children_iter(self, node): + sage: KT = KleberTree(['D', 4, 1], [[2,2]]) + sage: KT[1] + Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] + sage: for x in KT: x + Kleber tree node with weight [0, 2, 0, 0] and upwards edge root [0, 0, 0, 0] + Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] + Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] + sage: for x in KT._children_iter(KT[1]): x + Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] """ - Iterate over all children nodes. + n = self._classical_ct.rank() + I = self._classical_ct.index_set() + Q = self._classical_ct.root_system().root_lattice() + P = self._classical_ct.root_system().weight_lattice() + + # Construct the polytope by inequalities + from sage.geometry.polyhedron.constructor import Polyhedron + # Construct the shifted weight cone + root_weight = node.weight.to_vector() + ieqs = [[root_weight[i]] + list(col) + for i,col in enumerate(self._classical_ct.cartan_matrix())] + # Construct the negative weight cone + for i in range(n): + v = [0] * (n+1) + v[i+1] = -1 + ieqs.append(v) + ieqs.append([-1]*(n+1)) # For avoiding the origin + # Construct the bounds for the non-root nodes + if node != self.root: + for i,c in enumerate(node.up_root.to_vector()): + v = [0] * (n+1) + v[0] = c + v[i+1] = 1 + ieqs.append(v) + + try: + poly = Polyhedron(ieqs=ieqs, backend='normaliz') + self._has_normaliz = True + except ImportError: + poly = Polyhedron(ieqs=ieqs) + self._has_normaliz = False + + # Build the nodes from the polytope + # Sort for a consistent ordering (it is typically a small list) + for pt in sorted(poly.integral_points(), reverse=True): + up_root = Q._from_dict({I[i]: -val for i,val in enumerate(pt) if val != 0}, + remove_zeros=False) + wt = node.weight + sum(val * P.simple_root(I[i]) for i,val in enumerate(pt)) + yield KleberTreeNode(self, wt, up_root, node) + + def _children_iter_vector(self, node): + r""" + Iterate over the children of ``node``. - This is a helper iterator to iterate over all children, by generating - and/or computing them, of a given Kleber tree node this isn't the root. + Helper iterator to iterate over all children, by generating and/or + computing them, of the Kleber tree node. This implementation + iterates over all possible uproot vectors. - We perform the dominance iteration by using the condition that that - new root must be smaller than the previous root. + .. SEEALSO:: + + :meth:`_children_iter` INPUT: - - ``node`` -- The current node in the tree whose children we want + - ``node`` -- the current node in the tree whose children we want to generate TESTS:: @@ -837,28 +860,29 @@ def _children_iter(self, node): sage: KT = KleberTree(['D', 4, 1], [[2,2]]) sage: KT[1] Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] - sage: for x in KT: x - Kleber tree node with weight [0, 2, 0, 0] and upwards edge root [0, 0, 0, 0] - Kleber tree node with weight [0, 1, 0, 0] and upwards edge root [1, 2, 1, 1] - Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] sage: for x in KT._children_iter(KT[1]): x Kleber tree node with weight [0, 0, 0, 0] and upwards edge root [1, 2, 1, 1] """ - RS = self._classical_ct.root_system().root_space() - WS = self._classical_ct.root_system().weight_space() + Q = self._classical_ct.root_system().root_lattice() + P = self._classical_ct.root_system().weight_lattice() I = self._classical_ct.index_set() + wt = node.weight.to_vector() + # Everything is dense at this point; moreover, ranks are relatively small + CM = self._classical_ct.cartan_matrix().dense_matrix() + F = FreeModule(ZZ, self._classical_ct.rank()) L = [range(val + 1) for val in node.up_root.to_vector()] it = itertools.product(*L) 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)]) + # Convert the list to the weight lattice + converted_root = CM * F(root) - new_weight = node.weight - WS(converted_root) - if new_weight.is_dominant(): - yield KleberTreeNode(self, new_weight, converted_root, node) + if all(wt[i] >= val for i,val in enumerate(converted_root)): + wd = {I[i]: wt[i] - val for i, val in enumerate(converted_root)} + rd = {I[i]: val for i, val in enumerate(root)} + yield KleberTreeNode(self, P._from_dict(wd), Q._from_dict(rd), node) def _prune(self, new_child, depth): r""" @@ -1015,8 +1039,8 @@ def _element_constructor_(self, node_weight, dominant_root, parent_node=None): sage: from sage.combinat.rigged_configurations.kleber_tree import KleberTree sage: RS = RootSystem(['A', 2]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = KleberTree(['A', 2, 1], [[1,1]]) sage: root = KT(WS.sum_of_terms([(1,5), (2,2)]), R.zero()); root # indirect doctest Kleber tree node with weight [5, 2] and upwards edge root [0, 0] @@ -1172,8 +1196,8 @@ def _prune(self, new_child, depth): sage: from sage.combinat.rigged_configurations.kleber_tree import VirtualKleberTree sage: RS = RootSystem(['A', 3]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = VirtualKleberTree(['C',2,1], [[1,2],[1,1],[2,1]]) sage: x = KT(WS.sum_of_terms([(1,1), (2,1), (3,3)]), R.sum_of_terms([(1,2),(2,2),(3,1)]), KT.root) sage: KT._prune(x, 1) @@ -1368,8 +1392,8 @@ def _prune(self, new_child, depth): sage: from sage.combinat.rigged_configurations.kleber_tree import VirtualKleberTree sage: RS = RootSystem(['A', 5]) - sage: WS = RS.weight_space() - sage: R = RS.root_space() + sage: WS = RS.weight_lattice() + sage: R = RS.root_lattice() sage: KT = VirtualKleberTree(['A',6,2], [[2,2]]) sage: x = KT(WS.sum_of_terms([(2,1), (4,1)]), R.sum_of_terms([(1,1),(2,2),(3,2),(4,2),(5,1)]), KT.root) sage: KT._prune(x, 1) diff --git a/src/sage/combinat/rigged_configurations/kr_tableaux.py b/src/sage/combinat/rigged_configurations/kr_tableaux.py index 7d9ec53d8fc..c5106104c67 100644 --- a/src/sage/combinat/rigged_configurations/kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/kr_tableaux.py @@ -5,7 +5,7 @@ `s` columns that naturally arise under the bijection between rigged configurations and tableaux [RigConBijection]_. They are in bijection with the elements of the Kirillov-Reshetikhin crystal `B^{r,s}` under the (inverse) -filling map [OSS13]_ [SchScr]_. They do not have to satisfy the semistandard row or column +filling map [OSS13]_ [SS2015]_. They do not have to satisfy the semistandard row or column restrictions. These tensor products are the result from the bijection from rigged configurations [RigConBijection]_. @@ -51,8 +51,7 @@ from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper -from sage.categories.finite_crystals import FiniteCrystals -from sage.categories.regular_crystals import RegularCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.combinat.crystals.letters import CrystalOfLetters, EmptyLetter from sage.combinat.root_system.cartan_type import CartanType @@ -283,7 +282,7 @@ def __init__(self, cartan_type, r, s): EXAMPLES:: sage: KRT = crystals.KirillovReshetikhin(['A', 4, 1], 2, 2, model='KR') - sage: TestSuite(KRT).run() # long time + sage: TestSuite(KRT).run() sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 2, 2, model='KR') sage: TestSuite(KRT).run() # long time sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 4, 1, model='KR'); KRT @@ -294,7 +293,7 @@ def __init__(self, cartan_type, r, s): self._s = s self._cartan_type = cartan_type - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals()) self.letters = CrystalOfLetters(cartan_type.classical()) self.module_generators = self._build_module_generators() @@ -463,7 +462,7 @@ def _element_constructor_(self, *lst, **options): # Check to make sure it can be converted if lst[0].cartan_type() != self.cartan_type() \ or lst[0].parent().r() != self._r or lst[0].parent().s() != self._s: - raise ValueError("The Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") + raise ValueError("the Kirillov-Reshetikhin crystal must have the same Cartan type and (r,s)") return self.from_kirillov_reshetikhin_crystal(lst[0]) return self.element_class(self, list(lst), **options) @@ -508,19 +507,6 @@ def kirillov_reshetikhin_crystal(self): """ return KashiwaraNakashimaTableaux(self._cartan_type, self._r, self._s) - def affinization(self): - """ - Return the corresponding affinization crystal of ``self``. - - EXAMPLES:: - - sage: K = crystals.KirillovReshetikhin(['A',2,1], 1, 1, model='KR') - sage: K.affinization() - Affinization of Kirillov-Reshetikhin tableaux of type ['A', 2, 1] and shape (1, 1) - """ - from sage.combinat.crystals.affinization import AffinizationOfCrystal - return AffinizationOfCrystal(self) - def classical_decomposition(self): """ Return the classical crystal decomposition of ``self``. @@ -1050,9 +1036,9 @@ def from_kirillov_reshetikhin_crystal(self, krc): f_str = reversed(to_hw[1]) wt = to_hw[0].weight() for x in self.module_generators: - if x.classical_weight() / 2 == wt: + if x.classical_weight() == wt: return x.f_string(f_str) - raise ValueError("No matching highest weight element found") + raise ValueError("no matching highest weight element found") class KirillovReshetikhinTableauxElement(TensorProductOfRegularCrystalsElement): r""" @@ -1326,8 +1312,7 @@ def classical_weight(self): WLR = F.weight_lattice() else: WLR = F.ambient_space() - weight = lambda x: x.weight() - return sum((weight(self[j]) for j in range(len(self))), WLR.zero()) + return sum((self[j].weight() for j in range(len(self))), WLR.zero()) def e(self, i): """ @@ -1408,28 +1393,6 @@ def phi(self, i): return self.to_kirillov_reshetikhin_crystal().phi0() return TensorProductOfRegularCrystalsElement.phi(self, i) - def lusztig_involution(self): - r""" - Return the result of the classical Lusztig involution on ``self``. - - EXAMPLES:: - - sage: KRT = crystals.KirillovReshetikhin(['D',4,1], 2, 3, model='KR') - sage: mg = KRT.module_generators[1] - sage: mg.lusztig_involution() - [[-2, -2, 1], [-1, -1, 2]] - sage: elt = mg.f_string([2,1,3,2]); elt - [[3, -2, 1], [4, -1, 2]] - sage: elt.lusztig_involution() - [[-4, -2, 1], [-3, -1, 2]] - """ - Cl = self.parent().cartan_type().classical() - I = Cl.index_set() - aut = Cl.opposition_automorphism() - hw = self.to_highest_weight(I)[1] - hw.reverse() - return self.to_lowest_weight(I)[0].e_string(aut[i] for i in hw) - def left_split(self): r""" Return the image of ``self`` under the left column splitting map. @@ -1651,6 +1614,26 @@ def left_split(self): rf = TP.crystals[1](*(self[h:])) return TP(lf, rf) + # FIXME: This is a copy of the above classical weight, and cached_method + # overwrites this method if it is called via super. + @cached_method + def classical_weight(self): + r""" + Return the classical weight of ``self``. + + EXAMPLES:: + + sage: KRT = crystals.KirillovReshetikhin(['D', 4, 1], 4, 1, model='KR') + sage: KRT.module_generators[0].classical_weight() + (1/2, 1/2, 1/2, 1/2) + """ + F = self.cartan_type().classical().root_system() + if F.ambient_space() is None: + WLR = F.weight_lattice() + else: + WLR = F.ambient_space() + return sum((self[j].weight() for j in range(len(self))), WLR.zero()) / 2 + KRTableauxBn.Element = KRTableauxSpinElement KRTableauxSpin.Element = KRTableauxSpinElement @@ -1776,10 +1759,11 @@ def phi(self, i): return rc.phi(0) return TensorProductOfRegularCrystalsElement.phi(self, i) + class KRTableauxTypeFromRC(KirillovReshetikhinTableaux): r""" Kirillov-Reshetikhin tableaux `B^{r,s}` constructed from rigged - configurations under the bijecton `\Phi`. + configurations under the bijection `\Phi`. .. WARNING:: @@ -1800,7 +1784,7 @@ def __init__(self, cartan_type, r, s): self._r = r self._s = s self._cartan_type = cartan_type - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals()) self.letters = CrystalOfLetters(cartan_type.classical()) @lazy_attribute diff --git a/src/sage/combinat/rigged_configurations/rc_crystal.py b/src/sage/combinat/rigged_configurations/rc_crystal.py index b7d3d932fa8..a97b7bb935f 100644 --- a/src/sage/combinat/rigged_configurations/rc_crystal.py +++ b/src/sage/combinat/rigged_configurations/rc_crystal.py @@ -45,12 +45,14 @@ class CrystalOfRiggedConfigurations(UniqueRepresentation, Parent): The crystal structure for finite simply-laced types is given in [CrysStructSchilling06]_. These were then shown to be the crystal - operators in all finite types in [SchScr]_ and all simply-laced and - a large class of foldings of simply-laced types in [SalScr]_. + operators in all finite types in [SS2015]_, all simply-laced and + a large class of foldings of simply-laced types in [SS2015II]_, + and all symmetrizable types (uniformly) in [SS2017]_. INPUT: - - ``cartan_type`` -- (optional) a Cartan type + - ``cartan_type`` -- (optional) a Cartan type or a Cartan type + given as a folding - ``wt`` -- the highest weight vector in the weight lattice @@ -78,18 +80,38 @@ class CrystalOfRiggedConfigurations(UniqueRepresentation, Parent): sage: RC.digraph().is_isomorphic(T.digraph(), edge_labels=True) True + We construct a non-simply-laced affine type:: + + sage: La = RootSystem(['C', 3]).weight_lattice().fundamental_weights() + sage: RC = crystals.RiggedConfigurations(La[2]) + sage: mg = RC.highest_weight_vector() + sage: mg.f_string([2,3]) + (/) 1[ ]1 -1[ ]-1 + sage: T = crystals.Tableaux(['C', 3], shape=[1,1]) + sage: RC.digraph().is_isomorphic(T.digraph(), edge_labels=True) + True + + We can construct rigged configurations using a diagram folding of + a simply-laced type. This yields an equivalent but distinct crystal:: + + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[2]) + sage: mg = RC.highest_weight_vector() + sage: mg.f_string([2,3]) + (/) 0[ ]0 -1[ ]-1 + sage: T = crystals.Tableaux(['C', 3], shape=[1,1]) + sage: RC.digraph().is_isomorphic(T.digraph(), edge_labels=True) + True + We reset the global options:: sage: RiggedConfigurations.options._reset() REFERENCES: - .. [SchScr] Anne Schilling and Travis Scrimshaw. - *Crystal structure on rigged configurations and the filling map*. - :arxiv:`1409.2920`. - - .. [SalScr] Ben Salisbury and Travis Scrimshaw. - *A rigged configuration model for* `B(\infty)`. :arxiv:`1404.6539`. + - [SS2015]_ + - [SS2015II]_ + - [SS2017]_ """ @staticmethod def __classcall_private__(cls, cartan_type, wt=None, WLR=None): @@ -112,20 +134,22 @@ def __classcall_private__(cls, cartan_type, wt=None, WLR=None): sage: RC is RCE False """ + from sage.combinat.root_system.type_folded import CartanTypeFolded + if wt is None: wt = cartan_type cartan_type = wt.parent().cartan_type() else: - cartan_type = CartanType(cartan_type) + if not isinstance(cartan_type, CartanTypeFolded): + cartan_type = CartanType(cartan_type) if WLR is None: WLR = wt.parent() else: wt = WLR(wt) - if not cartan_type.is_simply_laced(): - vct = cartan_type.as_folding() - return CrystalOfNonSimplyLacedRC(vct, wt, WLR) + if isinstance(cartan_type, CartanTypeFolded): + return CrystalOfNonSimplyLacedRC(cartan_type, wt, WLR) return super(CrystalOfRiggedConfigurations, cls).__classcall__(cls, wt, WLR=WLR) @@ -146,6 +170,7 @@ def __init__(self, wt, WLR): self._cartan_type = WLR.cartan_type() self._wt = wt self._rc_index = self._cartan_type.index_set() + self._rc_index_inverse = {i: ii for ii,i in enumerate(self._rc_index)} # We store the cartan matrix for the vacancy number calculations for speed self._cartan_matrix = self._cartan_type.cartan_matrix() if self._cartan_type.is_finite(): @@ -247,7 +272,9 @@ def _calc_vacancy_number(self, partitions, a, i, **options): vac_num = self._wt[self.index_set()[a]] for b in range(self._cartan_matrix.ncols()): - vac_num -= self._cartan_matrix[a,b] * partitions[b].get_num_cells_to_column(i) + val = self._cartan_matrix[a,b] + if val: + vac_num -= val * partitions[b].get_num_cells_to_column(i) return vac_num @@ -292,7 +319,8 @@ def virtual(self): EXAMPLES:: sage: La = RootSystem(['C', 2, 1]).weight_lattice().fundamental_weights() - sage: RC = crystals.RiggedConfigurations(La[0]) + sage: vct = CartanType(['C', 2, 1]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[0]) sage: RC Crystal of rigged configurations of type ['C', 2, 1] and weight Lambda[0] sage: RC.virtual @@ -319,7 +347,8 @@ def _calc_vacancy_number(self, partitions, a, i, **options): TESTS:: sage: La = RootSystem(['C', 3]).weight_lattice().fundamental_weights() - sage: RC = crystals.RiggedConfigurations(La[2]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[2]) sage: elt = RC(partition_list=[[], [1], [1]]) sage: RC._calc_vacancy_number(elt.nu(), 1, 1) 0 @@ -350,7 +379,8 @@ def to_virtual(self, rc): EXAMPLES:: sage: La = RootSystem(['C', 3]).weight_lattice().fundamental_weights() - sage: RC = crystals.RiggedConfigurations(La[2]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[2]) sage: elt = RC(partition_list=[[], [1], [1]]); elt (/) @@ -398,7 +428,8 @@ def from_virtual(self, vrc): EXAMPLES:: sage: La = RootSystem(['C', 3]).weight_lattice().fundamental_weights() - sage: RC = crystals.RiggedConfigurations(La[2]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[2]) sage: elt = RC(partition_list=[[0], [1], [1]]) sage: elt == RC.from_virtual(RC.to_virtual(elt)) True @@ -421,3 +452,4 @@ def from_virtual(self, vrc): # deprecations from trac:18555 from sage.misc.superseded import deprecated_function_alias CrystalOfRiggedConfigurations.global_options = deprecated_function_alias(18555, CrystalOfRiggedConfigurations.options) + diff --git a/src/sage/combinat/rigged_configurations/rc_infinity.py b/src/sage/combinat/rigged_configurations/rc_infinity.py index 60b5efacd9e..fd9b60042bc 100644 --- a/src/sage/combinat/rigged_configurations/rc_infinity.py +++ b/src/sage/combinat/rigged_configurations/rc_infinity.py @@ -80,11 +80,29 @@ class InfinityCrystalOfRiggedConfigurations(UniqueRepresentation, Parent): sage: RC = crystals.infinity.RiggedConfigurations(['C', 3]) sage: mg = RC.highest_weight_vector() sage: mg.f_string([2,1,3,2]) + 0[ ]0 -1[ ]0 0[ ]0 + -1[ ]-1 + sage: mg.f_string([2,3,2,1,3,2]) + 0[ ]-1 -1[ ][ ]-1 -1[ ][ ]0 + -1[ ]0 + + We can construct rigged configurations using a diagram folding of + a simply-laced type. This yields an equivalent but distinct crystal:: + + sage: vct = CartanType(['C', 3]).as_folding() + sage: VRC = crystals.infinity.RiggedConfigurations(vct) + sage: mg = VRC.highest_weight_vector() + sage: mg.f_string([2,1,3,2]) 0[ ]0 -2[ ]-1 0[ ]0 -2[ ]-1 sage: mg.f_string([2,3,2,1,3,2]) -1[ ]-1 -2[ ][ ][ ]-1 -1[ ][ ]0 + sage: G = RC.subcrystal(max_depth=5).digraph() + sage: VG = VRC.subcrystal(max_depth=5).digraph() + sage: G.is_isomorphic(VG, edge_labels=True) + True + We can also construct `B(\infty)` using rigged configurations in affine types:: @@ -97,14 +115,14 @@ class InfinityCrystalOfRiggedConfigurations(UniqueRepresentation, Parent): sage: RC = crystals.infinity.RiggedConfigurations(['C', 3, 1]) sage: mg = RC.highest_weight_vector() sage: mg.f_string([1,2,3,0,1,2,3,3,0]) - -2[ ][ ]-1 -1[ ]0 -1[ ]-1 -4[ ][ ][ ]-2 - -1[ ]0 -1[ ]-1 + -2[ ][ ]-1 0[ ]1 0[ ]0 -4[ ][ ][ ]-2 + 0[ ]0 0[ ]-1 sage: RC = crystals.infinity.RiggedConfigurations(['A', 6, 2]) sage: mg = RC.highest_weight_vector() sage: mg.f_string([1,2,3,0,1,2,3,3,0]) - 0[ ]-1 0[ ]1 -1[ ]-1 -4[ ][ ][ ]-2 - 0[ ]-1 0[ ]1 -1[ ]-1 + 0[ ]-1 0[ ]1 0[ ]0 -4[ ][ ][ ]-2 + 0[ ]-1 0[ ]1 0[ ]-1 We reset the global options:: @@ -122,11 +140,11 @@ def __classcall_private__(cls, cartan_type): sage: RC2 is RC1 True """ - cartan_type = CartanType(cartan_type) - if not cartan_type.is_simply_laced(): - vct = cartan_type.as_folding() - return InfinityCrystalOfNonSimplyLacedRC(vct) + from sage.combinat.root_system.type_folded import CartanTypeFolded + if isinstance(cartan_type, CartanTypeFolded): + return InfinityCrystalOfNonSimplyLacedRC(cartan_type) + cartan_type = CartanType(cartan_type) return super(InfinityCrystalOfRiggedConfigurations, cls).__classcall__(cls, cartan_type) def __init__(self, cartan_type): @@ -139,10 +157,15 @@ def __init__(self, cartan_type): sage: TestSuite(RC).run() sage: RC = crystals.infinity.RiggedConfigurations(['A', 2, 1]) sage: TestSuite(RC).run() # long time + sage: RC = crystals.infinity.RiggedConfigurations(['C', 2]) + sage: TestSuite(RC).run() # long time + sage: RC = crystals.infinity.RiggedConfigurations(['C', 2, 1]) + sage: TestSuite(RC).run() # long time """ self._cartan_type = cartan_type Parent.__init__(self, category=HighestWeightCrystals().Infinite()) self._rc_index = self._cartan_type.index_set() + self._rc_index_inverse = {i: ii for ii,i in enumerate(self._rc_index)} # We store the cartan matrix for the vacancy number calculations for speed self._cartan_matrix = self._cartan_type.cartan_matrix() self.module_generators = (self.element_class(self, rigging_list=[[]]*cartan_type.rank()),) @@ -172,9 +195,9 @@ def _element_constructor_(self, lst=None, **options): 0[ ]1 0[ ]0 0[ ]0 0[ ]-1 sage: RC = crystals.infinity.RiggedConfigurations(['C', 3]) - sage: ascii_art(RC(partition_list=[[1],[1,1],[1]], rigging_list=[[0],[-1,-1],[0]])) - 0[ ]0 -2[ ]-1 0[ ]0 - -2[ ]-1 + sage: ascii_art(RC(partition_list=[[1],[1,1],[1]], rigging_list=[[0],[0,-1],[0]])) + 0[ ]0 -1[ ]0 0[ ]0 + -1[ ]-1 TESTS: @@ -211,7 +234,8 @@ def _coerce_map_from_(self, P): """ if self.cartan_type().is_finite(): from sage.combinat.crystals.infinity_crystals import InfinityCrystalOfTableaux - if isinstance(P, InfinityCrystalOfTableaux): + if (isinstance(P, InfinityCrystalOfTableaux) + and self.cartan_type().is_simply_laced()): from sage.combinat.rigged_configurations.bij_infinity import FromTableauIsomorphism return FromTableauIsomorphism(Hom(P, self)) return super(InfinityCrystalOfRiggedConfigurations, self)._coerce_map_from_(P) @@ -299,14 +323,37 @@ def __init__(self, vct): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 2]) + sage: vct = CartanType(['C', 2]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: TestSuite(RC).run() # long time - sage: RC = crystals.infinity.RiggedConfigurations(['C', 2, 1]) + sage: vct = CartanType(['C', 2, 1]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: TestSuite(RC).run() # long time """ self._folded_ct = vct InfinityCrystalOfRiggedConfigurations.__init__(self, vct._cartan_type) + def _coerce_map_from_(self, P): + """ + Return ``True`` or the coerce map from ``P`` if a map exists. + + EXAMPLES:: + + sage: T = crystals.infinity.Tableaux(['C',3]) + sage: vct = CartanType(['C',3]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) + sage: RC._coerce_map_from_(T) + Crystal Isomorphism morphism: + From: The infinity crystal of tableaux of type ['C', 3] + To: The infinity crystal of rigged configurations of type ['C', 3] + """ + if self.cartan_type().is_finite(): + from sage.combinat.crystals.infinity_crystals import InfinityCrystalOfTableaux + if isinstance(P, InfinityCrystalOfTableaux): + from sage.combinat.rigged_configurations.bij_infinity import FromTableauIsomorphism + return FromTableauIsomorphism(Hom(P, self)) + return super(InfinityCrystalOfNonSimplyLacedRC, self)._coerce_map_from_(P) + def _calc_vacancy_number(self, partitions, a, i): r""" Calculate the vacancy number `p_i^{(a)}(\nu)` in ``self``. @@ -321,8 +368,9 @@ def _calc_vacancy_number(self, partitions, a, i): TESTS:: - sage: La = RootSystem(['C',2]).weight_lattice().fundamental_weights() - sage: RC = crystals.RiggedConfigurations(La[1]) + sage: La = RootSystem(['C', 2]).weight_lattice().fundamental_weights() + sage: vct = CartanType(['C', 2]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[1]) sage: elt = RC(partition_list=[[1], [1]]) sage: RC._calc_vacancy_number(elt.nu(), 0, 1) 0 @@ -349,7 +397,8 @@ def virtual(self): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 3]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: RC The infinity crystal of rigged configurations of type ['C', 3] sage: RC.virtual @@ -367,7 +416,8 @@ def to_virtual(self, rc): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 2]) + sage: vct = CartanType(['C', 2]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: mg = RC.highest_weight_vector() sage: elt = mg.f_string([1,2,2,1,1]); elt @@ -411,7 +461,8 @@ def from_virtual(self, vrc): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 2]) + sage: vct = CartanType(['C', 2]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC(partition_list=[[3],[2]], rigging_list=[[-2],[0]]) sage: vrc_elt = RC.to_virtual(elt) sage: ret = RC.from_virtual(vrc_elt); ret @@ -444,7 +495,8 @@ class Element(RCNonSimplyLacedElement): TESTS:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 3]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC(partition_list=[[1],[1,1],[1]]) sage: TestSuite(elt).run() """ @@ -454,12 +506,14 @@ def weight(self): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C', 3]) + sage: vct = CartanType(['C', 3]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC(partition_list=[[1],[1,1],[1]], rigging_list=[[0],[-1,-1],[0]]) sage: elt.weight() (-1, -1, 0) - sage: RC = crystals.infinity.RiggedConfigurations(['F', 4, 1]) + sage: vct = CartanType(['F', 4, 1]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: mg = RC.highest_weight_vector() sage: elt = mg.f_string([1,0,3,4,2,2]); ascii_art(elt) -1[ ]-1 0[ ]1 -2[ ][ ]-2 0[ ]1 -1[ ]-1 @@ -476,3 +530,4 @@ def weight(self): # deprecations from trac:18555 from sage.misc.superseded import deprecated_function_alias InfinityCrystalOfRiggedConfigurations.global_options = deprecated_function_alias(18555, InfinityCrystalOfRiggedConfigurations.options) + diff --git a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py index 2e4101a357b..441bde47c9e 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configuration_element.py +++ b/src/sage/combinat/rigged_configurations/rigged_configuration_element.py @@ -224,9 +224,6 @@ def __init__(self, parent, rigged_partitions=[], **options): # Parent's __call__ always passes at least 1 argument to the element constructor if options.get('use_vacancy_numbers', False): - # Special display case - if parent.cartan_type().type() == 'B': - rigged_partitions[-1] = RiggedPartitionTypeB(rigged_partitions[-1]) ClonableArray.__init__(self, parent, rigged_partitions) return nu = rigged_partitions @@ -260,10 +257,6 @@ def __init__(self, parent, rigged_partitions=[], **options): if partition.rigging[i] is None: partition.rigging[i] = partition.vacancy_numbers[i] - # Special display case - if parent.cartan_type().type() == 'B': - nu[-1] = RiggedPartitionTypeB(nu[-1]) - ClonableArray.__init__(self, parent, nu) def check(self): @@ -524,9 +517,9 @@ def e(self, a): -1[ ]-1 """ - if a not in self.parent()._rc_index: + if a not in self.parent()._rc_index_inverse: raise ValueError("{} is not in the index set".format(a)) - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] new_list = self[a][:] new_vac_nums = self[a].vacancy_numbers[:] @@ -594,7 +587,7 @@ def e(self, a): # Update that row's vacancy number ret_RC[a].vacancy_numbers[rigging_index] = \ self.parent()._calc_vacancy_number(nu, a, nu[a][rigging_index]) - return(ret_RC) + return ret_RC def _generate_partition_e(self, a, b, k): r""" @@ -623,12 +616,12 @@ def _generate_partition_e(self, a, b, k): if not self.parent()._cartan_matrix[a,b]: return self[b] - new_list = self[b][:] + new_list = self[b]._list new_vac_nums = self[b].vacancy_numbers[:] new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = self.parent()._cartan_matrix[a,b] + value = self.parent()._cartan_matrix[b,a] for i in range(len(new_vac_nums)): if new_list[i] < k: break @@ -672,9 +665,9 @@ def f(self, a): (/) """ - if a not in self.parent()._rc_index: + if a not in self.parent()._rc_index_inverse: raise ValueError("{} is not in the index set".format(a)) - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] new_list = self[a][:] new_vac_nums = self[a].vacancy_numbers[:] @@ -766,12 +759,12 @@ def _generate_partition_f(self, a, b, k): if not self.parent()._cartan_matrix[a,b]: return self[b] - new_list = self[b][:] + new_list = self[b]._list new_vac_nums = self[b].vacancy_numbers[:] new_rigging = self[b].rigging[:] # Update the vacancy numbers and the rigging - value = self.parent()._cartan_matrix[a,b] + value = self.parent()._cartan_matrix[b,a] for i in range(len(new_vac_nums)): if new_list[i] <= k: break @@ -800,7 +793,7 @@ def epsilon(self, a): [0 1] [0 2] """ - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] if not self[a]: return ZZ.zero() return Integer(-min(0, min(self[a].rigging))) @@ -824,7 +817,7 @@ def phi(self, a): [0 2] [1 1] """ - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] p_inf = self.parent()._calc_vacancy_number(self, a, float("inf")) if not self[a]: return Integer(p_inf) @@ -859,8 +852,8 @@ def vacancy_number(self, a, i): sage: x.vacancy_number(2,2) 1 """ - a = self.cartan_type().classical().index_set().index(a) - return self.parent()._calc_vacancy_number(self.nu(), a, i) + a = self.parent()._rc_index_inverse[a] + return self.parent()._calc_vacancy_number(self, a, i) def partition_rigging_lists(self): """ @@ -894,7 +887,8 @@ class RCNonSimplyLacedElement(RiggedConfigurationElement): TESTS:: - sage: RC = crystals.infinity.RiggedConfigurations(['C',2,1]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC.module_generators[0].f_string([1,0,2,2,0,1]); elt -2[ ][ ]-1 @@ -942,7 +936,8 @@ def e(self, a): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C',2,1]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC(partition_list=[[2],[1,1],[2]], rigging_list=[[-1],[-1,-1],[-1]]) sage: ascii_art(elt.e(0)) 0[ ]0 -2[ ]-1 -2[ ][ ]-1 @@ -977,7 +972,8 @@ def f(self, a): EXAMPLES:: - sage: RC = crystals.infinity.RiggedConfigurations(['C',2,1]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.infinity.RiggedConfigurations(vct) sage: elt = RC(partition_list=[[2],[1,1],[2]], rigging_list=[[-1],[-1,-1],[-1]]) sage: ascii_art(elt.f(0)) -4[ ][ ][ ]-2 -2[ ]-1 -2[ ][ ]-1 @@ -1034,7 +1030,7 @@ def check(self): sage: elt = RC(partition_list=[[1,1],[1],[2]]) sage: elt.check() """ - for partition in self: + for a, partition in enumerate(self): for i, vac_num in enumerate(partition.vacancy_numbers): if vac_num < partition.rigging[i]: raise ValueError("rigging can be at most the vacancy number") @@ -1114,7 +1110,8 @@ class RCHWNonSimplyLacedElement(RCNonSimplyLacedElement): TESTS:: sage: La = RootSystem(['C',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: RC = crystals.RiggedConfigurations(['C',2,1], La[0]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[0]) sage: elt = RC(partition_list=[[1,1],[2],[2]]); ascii_art(elt) -1[ ]-1 2[ ][ ]2 -2[ ][ ]-2 -1[ ]-1 @@ -1128,7 +1125,8 @@ def check(self): TESTS:: sage: La = RootSystem(['C',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: RC = crystals.RiggedConfigurations(['C',2,1], La[0]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[0]) sage: elt = RC(partition_list=[[1,1],[2],[2]]) sage: elt.check() """ @@ -1152,7 +1150,8 @@ def f(self, a): EXAMPLES:: sage: La = RootSystem(['C',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: RC = crystals.RiggedConfigurations(['C',2,1], La[0]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: RC = crystals.RiggedConfigurations(vct, La[0]) sage: elt = RC(partition_list=[[1,1],[2],[2]]) sage: elt.f(0) sage: ascii_art(elt.f(1)) @@ -1172,7 +1171,8 @@ def weight(self): EXAMPLES:: sage: La = RootSystem(['C',2,1]).weight_lattice(extended=True).fundamental_weights() - sage: B = crystals.RiggedConfigurations(['C',2,1], La[0]) + sage: vct = CartanType(['C',2,1]).as_folding() + sage: B = crystals.RiggedConfigurations(vct, La[0]) sage: mg = B.module_generators[0] sage: mg.f_string([0,1,2]).weight() 2*Lambda[1] - Lambda[2] - delta @@ -1249,7 +1249,7 @@ def __init__(self, parent, rigged_partitions=[], **options): sage: TestSuite(elt).run() """ - n = parent._cartan_type.classical().rank() + n = len(parent._rc_index) if "KT_constructor" in options: # Used only by the Kleber tree # Not recommended to be called by the user since it avoids safety @@ -1267,6 +1267,11 @@ def __init__(self, parent, rigged_partitions=[], **options): ClonableArray.__init__(self, parent, nu) return RiggedConfigurationElement.__init__(self, parent, rigged_partitions, n=n, **options) + # Special display case + if parent.cartan_type().type() == 'B': + self._set_mutable() + self[-1] = RiggedPartitionTypeB(self[-1]) + self.set_immutable() def check(self): """ @@ -1452,7 +1457,7 @@ def weight(self): """ WLR = self.parent().weight_lattice_realization() La = WLR.fundamental_weights() - cl_index = self.parent()._cartan_type.classical().index_set() + cl_index = self.parent()._rc_index wt = WLR.sum((self.phi(i) - self.epsilon(i)) * La[i] for i in cl_index) return -wt.level() * La[0] + wt @@ -1503,8 +1508,7 @@ def classical_weight(self): alpha = WLR.simple_roots() rc_index = self.parent()._rc_index for a, nu in enumerate(self): - a = rc_index[a] - wt -= sum(nu) * alpha[a] + wt -= sum(nu) * alpha[rc_index[a]] return wt @@ -1913,7 +1917,7 @@ def right_column_box(self): if P.dims[-1][1] > 1: return self.right_split().right_column_box() - rc, e_string = self.to_highest_weight(P.cartan_type().classical().index_set()) + rc, e_string = self.to_highest_weight(P._rc_index) B = P.dims[:-1] + ([r-1,1], [1,1]) from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations @@ -2001,7 +2005,7 @@ def complement_rigging(self, reverse_factors=False): from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations P = RiggedConfigurations(P._cartan_type, reversed(P.dims)) - mg, e_str = self.to_highest_weight(P._cartan_type.classical().index_set()) + mg, e_str = self.to_highest_weight(P._rc_index) nu = [] rig = [] for a,p in enumerate(mg): @@ -2272,12 +2276,12 @@ def epsilon(self, a): if a == self.parent()._cartan_type.special_node(): return self.to_tensor_product_of_kirillov_reshetikhin_tableaux().epsilon(a) - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] if not self[a]: epsilon = 0 else: epsilon = -min(0, min(self[a].rigging)) - n = self.parent().cartan_type().classical().rank() + n = len(self.parent()._rc_index) if a == n-1: # -1 for indexing epsilon *= 2 return Integer(epsilon) @@ -2305,13 +2309,13 @@ def phi(self, a): if a == self.parent()._cartan_type.special_node(): return self.to_tensor_product_of_kirillov_reshetikhin_tableaux().phi(a) - a = self.parent()._rc_index.index(a) + a = self.parent()._rc_index_inverse[a] p_inf = self.parent()._calc_vacancy_number(self, a, float("inf")) if not self[a]: phi = p_inf else: phi = p_inf - min(0, min(self[a].rigging)) - n = self.parent().cartan_type().classical().rank() + n = len(self.parent()._rc_index) if a == n-1: # -1 for indexing phi *= 2 return Integer(phi) diff --git a/src/sage/combinat/rigged_configurations/rigged_configurations.py b/src/sage/combinat/rigged_configurations/rigged_configurations.py index 2f9004a908d..5206f8047e7 100644 --- a/src/sage/combinat/rigged_configurations/rigged_configurations.py +++ b/src/sage/combinat/rigged_configurations/rigged_configurations.py @@ -31,8 +31,7 @@ from sage.combinat.misc import IterableFunctionCall import sage.combinat.tableau as tableau from sage.rings.all import QQ -from sage.categories.finite_crystals import FiniteCrystals -from sage.categories.regular_crystals import RegularCrystals +from sage.categories.loop_crystals import KirillovReshetikhinCrystals from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.rigged_configurations.kleber_tree import KleberTree, VirtualKleberTree from sage.combinat.rigged_configurations.rigged_configuration_element import ( @@ -345,6 +344,8 @@ def __classcall_private__(cls, cartan_type, B): # Standardize B input into a tuple of tuples B = tuple(tuple(factor) for factor in B) + if not B: + raise ValueError("must contain at least one factor") if cartan_type.type() == 'BC': # Type `A_{2n}^{(2)}` return RCTypeA2Even(cartan_type, B) @@ -380,9 +381,10 @@ def __init__(self, cartan_type, B): self.dims = B cl = cartan_type.classical() self._rc_index = cl.index_set() + self._rc_index_inverse = {i: ii for ii,i in enumerate(self._rc_index)} # We store the Cartan matrix for the vacancy number calculations for speed self._cartan_matrix = cl.cartan_matrix() - Parent.__init__(self, category=(RegularCrystals(), FiniteCrystals())) + Parent.__init__(self, category=KirillovReshetikhinCrystals().TensorProducts()) # add options to class options=GlobalOptions('RiggedConfigurations', @@ -484,7 +486,7 @@ def __iter__(self): sage: len(L) 24 """ - index_set = self._cartan_type.classical().index_set() + index_set = self._rc_index from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet(self.module_generators, lambda x: [x.f(i) for i in index_set], @@ -538,7 +540,7 @@ def module_generators(self): 2 """ module_gens = [] - n = self._cartan_type.classical().rank() + n = len(self._rc_index) for tree_node in self.kleber_tree(): shapes = [] @@ -666,6 +668,19 @@ def _blocks_to_values(self, blocks): values[-1].extend(block) return values + def classically_highest_weight_vectors(self): + """ + Return the classically highest weight elements of ``self``. + + TESTS:: + + sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 2]]) + sage: ascii_art(RC.classically_highest_weight_vectors()) + ( ) + ( (/) (/) (/) (/) ) + """ + return self.module_generators + def _element_constructor_(self, *lst, **options): """ Construct a ``RiggedConfigurationElement``. @@ -828,31 +843,6 @@ def tensor_product_of_kirillov_reshetikhin_tableaux(self): from sage.combinat.rigged_configurations.tensor_product_kr_tableaux import TensorProductOfKirillovReshetikhinTableaux return TensorProductOfKirillovReshetikhinTableaux(self._cartan_type, self.dims) - def cardinality(self): - """ - Return the cardinality of ``self``. - - EXAMPLES:: - - sage: RC = RiggedConfigurations(['A', 3, 1], [[3, 2], [1, 2]]) - sage: RC.cardinality() - 100 - sage: len(RC.list()) - 100 - - sage: RC = RiggedConfigurations(['E', 7, 1], [[1,1]]) - sage: RC.cardinality() - 134 - sage: len(RC.list()) - 134 - - sage: RC = RiggedConfigurations(['B', 3, 1], [[2,2],[1,2]]) - sage: RC.cardinality() - 5130 - """ - CWLR = self.cartan_type().classical().root_system().ambient_space() - return sum(CWLR.weyl_dimension(mg.classical_weight()) for mg in self.module_generators) - @cached_method def tensor_product_of_kirillov_reshetikhin_crystals(self): """ @@ -1226,13 +1216,13 @@ def module_generators(self): # Convert from the virtual rigged configuration # As a special case, we do not need to do anything for type `A_{2n}^{(2)}` sigma = self._folded_ct.folding_orbit() - vindex = self._folded_ct.folding_of().classical().index_set() + vindex = self.virtual._rc_index shapes = [shapes[vindex.index(sigma[a][0])] for a in self._rc_index] if self._cartan_type.type() != 'BC': gamma = self._folded_ct.scaling_factors() - for a in range(len(shapes)): - for i in range(len(shapes[a])): - shapes[a][i] = shapes[a][i] // gamma[self._rc_index[a]] + for a,shape in enumerate(shapes): + for i in range(len(shape)): + shape[i] = shape[i] // gamma[self._rc_index[a]] # Start with a base to calculate the vacancy numbers # Make a copy just to be safe @@ -1337,7 +1327,7 @@ def to_virtual(self, rc): """ gamma = self._folded_ct.scaling_factors() sigma = self._folded_ct.folding_orbit() - n = self._folded_ct._folding.classical().rank() + n = len(self.virtual._rc_index) # +/- 1 for indexing partitions = [None] * n for a,rp in enumerate(rc): @@ -1372,7 +1362,7 @@ def from_virtual(self, vrc): """ gamma = self._folded_ct.scaling_factors() sigma = self._folded_ct.folding_orbit() - n = self._cartan_type.classical().rank() + n = len(self._rc_index) partitions = [None] * n # +/- 1 for indexing for a in range(n): @@ -1537,7 +1527,7 @@ def to_virtual(self, rc): """ gamma = self._folded_ct.scaling_factors() sigma = self._folded_ct.folding_orbit() - n = self._folded_ct._folding.classical().rank() + n = len(self.virtual._rc_index) partitions = [None] * n for a,rp in enumerate(rc): g = gamma[a+1] @@ -1573,7 +1563,7 @@ def from_virtual(self, vrc): """ gamma = self._folded_ct.scaling_factors() sigma = self._folded_ct.folding_orbit() - n = self._cartan_type.classical().rank() + n = len(self._rc_index) partitions = [None] * n # +/- 1 for indexing for a in range(n): @@ -1628,7 +1618,7 @@ def _calc_vacancy_number(self, partitions, a, i, **options): sage: RC._calc_vacancy_number(elt.nu(), 0, 1) -1 """ - if a != self._cartan_type.classical().rank()-1: + if a != len(self._rc_index) - 1: return RCTypeA2Even._calc_vacancy_number(self, partitions, a, i, **options) vac_num = 0 @@ -1699,7 +1689,7 @@ def module_generators(self): # We are not simply-laced, so convert from the virtual rigged configuration sigma = self._folded_ct.folding_orbit() - vindex = self._folded_ct.folding_of().classical().index_set() + vindex = self.virtual._rc_index shapes = [shapes[vindex.index(sigma[a][0])] for a in self._rc_index] # Nothing more to do since gamma[i] == 1 for all i >= 1 @@ -1858,7 +1848,7 @@ def to_virtual(self, rc): gammatilde = list(self._folded_ct.scaling_factors()) gammatilde[-1] = 2 sigma = self._folded_ct.folding_orbit() - n = self._folded_ct._folding.classical().rank() + n = len(self.virtual._rc_index) partitions = [None] * n for a,rp in enumerate(rc): g = gammatilde[a+1] @@ -1893,7 +1883,7 @@ def from_virtual(self, vrc): gammatilde = list(self._folded_ct.scaling_factors()) gammatilde[-1] = QQ(2) sigma = self._folded_ct.folding_orbit() - n = self._cartan_type.classical().rank() + n = len(self._rc_index) partitions = [None] * n # +/- 1 for indexing for a in range(n): diff --git a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py index 5b4ac01976a..76932ed595d 100644 --- a/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py +++ b/src/sage/combinat/rigged_configurations/tensor_product_kr_tableaux.py @@ -112,8 +112,8 @@ def __getitem__(self, i): [[1], [2], [3]] (X) [[1], [2]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() - for x in self.tp_krt.rigged_configurations().module_generators] + self._cache = tuple([x.to_tensor_product_of_kirillov_reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators]) return self._cache[i] def __iter__(self): @@ -130,8 +130,8 @@ def __iter__(self): [[1], [-1]] """ if self._cache is None: - self._cache = [x.to_tensor_product_of_kirillov_reshetikhin_tableaux() - for x in self.tp_krt.rigged_configurations().module_generators] + self._cache = tuple([x.to_tensor_product_of_kirillov_reshetikhin_tableaux() + for x in self.tp_krt.rigged_configurations().module_generators]) for x in self._cache: yield x @@ -151,7 +151,8 @@ def __repr__(self): @cached_method def cardinality(self): """ - Return the cardinality of ``self`` which is the number of highest weight elements. + Return the cardinality of ``self``, which is the number of + highest weight elements. EXAMPLES:: diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index 5ae9b4841dd..95cb5c10883 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -56,6 +56,7 @@ - :ref:`sage.combinat.root_system.weyl_group` - :ref:`sage.combinat.root_system.extended_affine_weyl_group` - :ref:`sage.combinat.root_system.fundamental_group` +- :ref:`sage.combinat.root_system.braid_move_calculator` .. SEEALSO:: diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index 9cc64aa8c28..c53ed162a3b 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -10,7 +10,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from .weight_lattice_realizations import WeightLatticeRealizations from sage.rings.all import ZZ, QQ from sage.categories.homset import End @@ -360,7 +360,7 @@ def to_ambient_space_morphism(self): """ return End(self).identity() -class AmbientSpaceElement(CombinatorialFreeModuleElement): +class AmbientSpaceElement(CombinatorialFreeModule.Element): # For backward compatibility def _repr_(self): """ diff --git a/src/sage/combinat/root_system/braid_move_calculator.py b/src/sage/combinat/root_system/braid_move_calculator.py new file mode 100644 index 00000000000..f2315d2a424 --- /dev/null +++ b/src/sage/combinat/root_system/braid_move_calculator.py @@ -0,0 +1,139 @@ +""" +Braid Move Calculator + +AUTHORS: + +- Dinakar Muthiah (2014-06-03): initial version +""" + +#***************************************************************************** +# Copyright (C) 2014 Dinakar Muthiah +# +# 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 + +class BraidMoveCalculator(object): + """ + Helper class to compute braid moves. + """ + def __init__(self, coxeter_group): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: TestSuite(B).run(skip="_test_pickling") + """ + self.coxeter_matrix = coxeter_group.coxeter_matrix() + + def _apply_put_in_front_recur_step(self, k, input_word, coxeter_matrix_entry): + """ + Recurrence step for :meth:`put_in_front`. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: B.put_in_front(2, (3, 2, 3, 1, 2, 3, 1, 2, 1)) # indirect doctest + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 3, 1, 2, 1, 3, 2, 1), + (3, 2, 3, 2, 1, 2, 3, 2, 1), + (2, 3, 2, 3, 1, 2, 3, 2, 1)) + """ + i = input_word[0] + def partial_braid_word(length, swap=False, i=i, k=k): + if swap: + i,k = k,i + running_braid_word = [i,k]*(length//2) + if length % 2 == 1: + running_braid_word.append(i) + return tuple(running_braid_word) + + current_last_word = input_word + current_first_letter = k + output_word_list = [current_last_word] + for counter in range(1, coxeter_matrix_entry): + current_word_list = self.put_in_front(current_first_letter, current_last_word[1:]) + output_word_list += [partial_braid_word(counter) + word + for word in current_word_list[1:]] + if current_first_letter == k: + current_first_letter = i + else: + current_first_letter = k + current_last_word = current_word_list[-1] + if i != k: + output_word_list += [partial_braid_word(coxeter_matrix_entry, swap=True) + + current_last_word[1:]] + return tuple(output_word_list) + + def put_in_front(self, k, input_word): + """ + Return a list of reduced words starting with ``input_word`` + and ending with a reduced word whose first letter is ``k``. + There still remains an issue with 0 indices. + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['C',3]) + sage: B = BraidMoveCalculator(W) + sage: B.put_in_front(2, (3, 2, 3, 1, 2, 3, 1, 2, 1)) + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 3, 1, 2, 1, 3, 2, 1), + (3, 2, 3, 2, 1, 2, 3, 2, 1), + (2, 3, 2, 3, 1, 2, 3, 2, 1)) + sage: B.put_in_front(1, (3, 2, 3, 1, 2, 3, 1, 2, 1)) + ((3, 2, 3, 1, 2, 3, 1, 2, 1), + (3, 2, 1, 3, 2, 3, 1, 2, 1), + (3, 2, 1, 3, 2, 3, 2, 1, 2), + (3, 2, 1, 2, 3, 2, 3, 1, 2), + (3, 1, 2, 1, 3, 2, 3, 1, 2), + (1, 3, 2, 1, 3, 2, 3, 1, 2)) + sage: B.put_in_front(1, (1, 3, 2, 3, 2, 1, 2, 3, 2)) + ((1, 3, 2, 3, 2, 1, 2, 3, 2),) + """ + i = input_word[0] + if i == 0 or k == 0: # Is this for affine types? - Travis + raise NotImplementedError + entry = self.coxeter_matrix[i, k] + return self._apply_put_in_front_recur_step(k, input_word, entry) + + @cached_method + def chain_of_reduced_words(self, start_word, end_word): + """ + Compute the chain of reduced words from ``stard_word`` + to ``end_word``. + + INPUT: + + - ``start_word``, ``end_word`` -- two reduced expressions + for the long word + + EXAMPLES:: + + sage: from sage.combinat.root_system.braid_move_calculator import BraidMoveCalculator + sage: W = CoxeterGroup(['A',5]) + sage: B = BraidMoveCalculator(W) + sage: B.chain_of_reduced_words((1,2,1,3,2,1,4,3,2,1,5,4,3,2,1), # not tested + ....: (5,4,5,3,4,5,2,3,4,5,1,2,3,4,5)) + """ + if start_word == end_word: + return (start_word,) + k = end_word[0] + first_word_list = self.put_in_front(k, start_word) + first_last_word = first_word_list[-1] + return (first_word_list[:-1] + + tuple([ (k,) + word for word in + self.chain_of_reduced_words(first_last_word[1:], + end_word[1:]) ])) + 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 cb1faff3e21..cd89e7ece70 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -1814,7 +1814,7 @@ def symmetric_macdonald_polynomial(self, mu): # nonsymmetric Macdonald polynomial of the dominant weight mu # by searching the Weyl orbit of mu and remembering Torbit = {} - for c in mu.orbit(): + for c in mu._orbit_iter(): i = c.first_descent() if i is None: Torbit[c] = self[mu] # the nonsymmetric Macdonald polynomial of mu @@ -1822,3 +1822,4 @@ def symmetric_macdonald_polynomial(self, mu): Torbit[c] = v * self._T.Tw([i])(Torbit[c.simple_reflection(i)]) s = s + Torbit[c] return s + diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index ebe66cf847d..a439d468d6b 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -372,7 +372,8 @@ def maximal_elements(self): s = ct.translation_factors()[1] R = RootSystem(ct).weight_space() Lambda = R.fundamental_weights() - orbit = [ R.reduced_word_of_translation(x) for x in (s*(Lambda[1]-Lambda[1].level()*Lambda[0])).orbit() ] + orbit = [R.reduced_word_of_translation(x) + for x in (s*(Lambda[1]-Lambda[1].level()*Lambda[0]))._orbit_iter()] return [self.W.from_reduced_word(x) for x in orbit] diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 3cd701a1064..cb8aea11801 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -2050,6 +2050,7 @@ def plot(self, - :meth:`plot_alcoves` - :meth:`plot_alcove_walk` - :meth:`plot_ls_paths` + - :meth:`plot_mv_polytope` - :meth:`plot_crystal` """ plot_options = self.plot_parse_options(**options) @@ -2399,7 +2400,7 @@ def plot_fundamental_weights(self, **options): # We build the family of fundamental weights in this space, # indexed by the fundamental weights in the weight lattice. # - # To this end, we don't use the embdding of the weight + # To this end, we don't use the embedding of the weight # lattice into self as for the roots or coroots because # the ambient space can define the fundamental weights # slightly differently (the usual GL_n vs SL_n catch). @@ -3026,6 +3027,78 @@ def plot_ls_paths(self, paths, plot_labels=None, colored_labels=True, **options) G += plot_options.text(b, prev + prev.normalized()*plot_labels) return G + def plot_mv_polytope(self, mv_polytope, mark_endpoints=True, + circle_size=0.06, circle_thickness=1.6, + wireframe='blue', fill='green', alpha=1, + **options): + r""" + Plot an MV polytope. + + INPUT: + + - ``mv_polytope`` -- an MV polytope + - ``mark_endpoints`` -- (default: ``True``) mark the endpoints + of the MV polytope + - ``circle_size`` -- (default: 0.06) the size of the circles + - ``circle_thickness`` -- (default: 1.6) the thinkness of the + extra rings of circles + - ``wireframe`` -- (default: ``'blue'``) color to draw the + wireframe of the polytope with + - ``fill`` -- (default: ``'green'``) color to fill the polytope with + - ``alpha`` -- (default: 1) the alpha value (opacity) of the fill + - ``**options`` -- plotting options + + .. SEEALSO:: + + - :meth:`plot` for a description of the plotting options + - :ref:`sage.combinat.root_system.plot` for a tutorial + on root system plotting + + EXAMPLES:: + + sage: B = crystals.infinity.MVPolytopes(['C',2]) + sage: L = RootSystem(['C',2]).ambient_space() + sage: p = B.highest_weight_vector().f_string([1,2,1,2]) + sage: L.plot_fundamental_weights() + L.plot_mv_polytope(p) + Graphics object consisting of 14 graphics primitives + + This also works in 3 dimensions:: + + sage: B = crystals.infinity.MVPolytopes(['A',3]) + sage: L = RootSystem(['A',3]).ambient_space() + sage: p = B.highest_weight_vector().f_string([2,1,3,2]) + sage: L.plot_mv_polytope(p) + Graphics3d Object + """ + from sage.geometry.polyhedron.all import Polyhedron + plot_options = self.plot_parse_options(**options) + + # Setup the shift for plotting + pbw_data = mv_polytope._pbw_datum.parent + al = self.simple_roots() + red = tuple(mv_polytope._pbw_datum.long_word) + roots = [self.sum(c*al[a] for a,c in root) + for root in pbw_data._root_list_from(red)] + datum = mv_polytope._pbw_datum.lusztig_datum + end_pt = self.sum(roots[i] * c for i,c in enumerate(datum)) + shift = plot_options.projection(end_pt) + + vertices = [plot_options.projection(vertex) - shift + for vertex in mv_polytope._polytope_vertices(self)] + p = Polyhedron(vertices=vertices).plot(wireframe=wireframe, + fill=fill, alpha=alpha) + if mark_endpoints: + from sage.plot.circle import circle + + p += circle(plot_options.projection(self.zero()), + circle_size, fill=True, + thickness=circle_thickness, color=wireframe) + + p += circle(-shift, + circle_size, fill=True, + thickness=circle_thickness, color=wireframe) + return p + def plot_crystal(self, crystal, plot_labels=True, label_color='black', edge_labels=False, @@ -3351,18 +3424,37 @@ def simple_reflections(self): """ return [s(self) for s in self.parent().simple_reflections()] + def _orbit_iter(self): + """ + Iterate the orbit of ``self`` under the action of the Weyl group. + + Call this method when the orbit just needs to be iterated over. + + EXAMPLES:: + + sage: L = RootSystem(["A", 2]).ambient_lattice() + sage: sorted(L.rho()._orbit_iter()) # the output order is not specified + [(1, 2, 0), (1, 0, 2), (2, 1, 0), + (2, 0, 1), (0, 1, 2), (0, 2, 1)] + """ + R = RecursivelyEnumeratedSet([self], attrcall('simple_reflections'), + structure=None, enumeration='breadth') + return iter(R) + def orbit(self): r""" - The orbit of self under the action of the Weyl group + The orbit of ``self`` under the action of the Weyl group. EXAMPLES: - `\rho` is a regular element whose orbit is in bijection with the Weyl group. - In particular, it as 6 elements for the symmetric group `S_3`:: + `\rho` is a regular element whose orbit is in bijection + with the Weyl group. In particular, it has 6 elements for + the symmetric group `S_3`:: sage: L = RootSystem(["A", 2]).ambient_lattice() sage: sorted(L.rho().orbit()) # the output order is not specified - [(1, 2, 0), (1, 0, 2), (2, 1, 0), (2, 0, 1), (0, 1, 2), (0, 2, 1)] + [(1, 2, 0), (1, 0, 2), (2, 1, 0), + (2, 0, 1), (0, 1, 2), (0, 2, 1)] sage: L = RootSystem(["A", 3]).weight_lattice() sage: len(L.rho().orbit()) @@ -3372,9 +3464,7 @@ def orbit(self): sage: len(L.fundamental_weights()[2].orbit()) 6 """ - R = RecursivelyEnumeratedSet([self], attrcall('simple_reflections'), - structure=None, enumeration='breadth') - return list(R) + return list(self._orbit_iter()) ########################################################################## # diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index 68da6897a80..ca295870afe 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -12,7 +12,7 @@ from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.rings.all import ZZ -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from .root_lattice_realizations import RootLatticeRealizations from sage.misc.cachefunc import cached_in_parent_method import functools @@ -232,7 +232,7 @@ def basis_value(basis, i): return basis[i] return self.module_morphism(on_basis = functools.partial(basis_value, basis) , codomain=L) -class RootSpaceElement(CombinatorialFreeModuleElement): +class RootSpaceElement(CombinatorialFreeModule.Element): def scalar(self, lambdacheck): """ The scalar product between the root lattice and diff --git a/src/sage/combinat/root_system/weight_space.py b/src/sage/combinat/root_system/weight_space.py index 6dbacafab4e..9162fa3dc2e 100644 --- a/src/sage/combinat/root_system/weight_space.py +++ b/src/sage/combinat/root_system/weight_space.py @@ -11,7 +11,7 @@ from sage.misc.cachefunc import cached_method from sage.sets.family import Family -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from .weight_lattice_realizations import WeightLatticeRealizations import functools @@ -449,7 +449,7 @@ def basis_value(basis, i): return basis[i] return self.module_morphism(on_basis = functools.partial(basis_value, basis), codomain=L) -class WeightSpaceElement(CombinatorialFreeModuleElement): +class WeightSpaceElement(CombinatorialFreeModule.Element): def scalar(self, lambdacheck): """ diff --git a/src/sage/combinat/set_partition.py b/src/sage/combinat/set_partition.py index d09bda5a7c3..02609ac2dfb 100644 --- a/src/sage/combinat/set_partition.py +++ b/src/sage/combinat/set_partition.py @@ -27,6 +27,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import print_function, absolute_import, division from six.moves import range from six import add_metaclass @@ -142,6 +143,7 @@ def __init__(self, parent, s): sage: SetPartition([]) {} """ + self._latex_options = {} ClonableArray.__init__(self, parent, sorted(map(Set, s), key=min)) def check(self): @@ -412,6 +414,95 @@ def _repr_(self): """ return '{' + ', '.join(('{' + repr(sorted(x))[1:-1] + '}' for x in self)) + '}' + def set_latex_options(self, **kwargs): + r""" + Set the latex options for use in the ``_latex_`` function + + - ``tikz_scale`` -- (default: 1) scale for use with tikz package + + - ``plot`` -- (default: ``None``) ``None`` returns the set notation, + ``linear`` returns a linear plot, ``cyclic`` returns a cyclic + plot + + - ``color`` -- (default: ``'black'``) the arc colors + + - ``fill`` -- (default: ``False``) if ``True`` then fills ``color``, + else you can pass in a color to alter the fill color - + *only works with cyclic plot* + + - ``show_labels`` -- (default: ``True``) if ``True`` shows labels - + *only works with plots* + + - ``radius`` -- (default: ``"1cm"``) radius of circle for cyclic + plot - *only works with cyclic plot* + + - ``angle`` -- (default: 0) angle for linear plot + + EXAMPLES:: + + sage: SP = SetPartition([[1,6], [3,5,4]]) + sage: SP.set_latex_options(tikz_scale=2,plot='linear',fill=True,color='blue',angle=45) + sage: SP.set_latex_options(plot='cyclic') + sage: SP.latex_options() + {'angle': 45, + 'color': 'blue', + 'fill': True, + 'plot': 'cyclic', + 'radius': '1cm', + 'show_labels': True, + 'tikz_scale': 2} + + """ + valid_args = ['tikz_scale', 'plot', 'color', 'fill', 'show_labels', + 'radius', 'angle'] + + for key in kwargs: + if key not in valid_args: + raise ValueError("unknown keyword argument: %s"%key) + if key == 'plot': + if not (kwargs['plot'] == 'cyclic' + or kwargs['plot'] == 'linear' + or kwargs['plot'] is None): + raise ValueError("plot must be None, 'cyclic', or 'linear'") + + for opt in kwargs: + self._latex_options[opt] = kwargs[opt] + + def latex_options(self): + r""" + Return the latex options for use in the ``_latex_`` function as a + dictionary. The default values are set using the global options. + + Options can be found in :meth:`set_latex_options` + + EXAMPLES:: + + sage: SP = SetPartition([[1,6], [3,5,4]]); SP.latex_options() + {'angle': 0, + 'color': 'black', + 'fill': False, + 'plot': None, + 'radius': '1cm', + 'show_labels': True, + 'tikz_scale': 1} + """ + opts = self._latex_options.copy() + if "tikz_scale" not in opts: + opts["tikz_scale"] = 1 + if "plot" not in opts: + opts["plot"] = None + if "color" not in opts: + opts['color'] = 'black' + if "fill" not in opts: + opts["fill"] = False + if "show_labels" not in opts: + opts['show_labels'] = True + if "radius" not in opts: + opts['radius'] = "1cm" + if "angle" not in opts: + opts['angle'] = 0 + return opts + def _latex_(self): r""" Return a `\LaTeX` string representation of ``self``. @@ -421,8 +512,119 @@ def _latex_(self): sage: x = SetPartition([[1,2], [3,5,4]]) sage: latex(x) \{\{1, 2\}, \{3, 4, 5\}\} - """ - return repr(self).replace("{",r"\{").replace("}",r"\}") + + sage: x.set_latex_options(plot='linear', angle=25, color='red') + sage: latex(x) + \begin{tikzpicture}[scale=1] + \node[below=.05cm] at (0,0) {$1$}; + \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (0) at (0,0) {}; + \node[below=.05cm] at (1,0) {$2$}; + \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (1) at (1,0) {}; + \node[below=.05cm] at (2,0) {$3$}; + \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (2) at (2,0) {}; + \node[below=.05cm] at (3,0) {$4$}; + \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (3) at (3,0) {}; + \node[below=.05cm] at (4,0) {$5$}; + \node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] (4) at (4,0) {}; + \draw[color=red] (1) to [out=115,in=65] (0); + \draw[color=red] (3) to [out=115,in=65] (2); + \draw[color=red] (4) to [out=115,in=65] (3); + \end{tikzpicture} + + sage: p = SetPartition([['a','c'],['b',1],[20]]) + sage: p.set_latex_options(plot='cyclic', color='blue', fill=True, tikz_scale=2) + sage: latex(p) + \begin{tikzpicture}[scale=2] + \draw (0,0) circle [radius=1cm]; + \node[label=90:1] (0) at (90:1cm) {}; + \node[label=18:20] (1) at (18:1cm) {}; + \node[label=-54:a] (2) at (-54:1cm) {}; + \node[label=-126:b] (3) at (-126:1cm) {}; + \node[label=-198:c] (4) at (-198:1cm) {}; + \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] (2.center) -- (4.center) -- cycle; + \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] (0.center) -- (3.center) -- cycle; + \draw[-,thick,color=blue,fill=blue,fill opacity=0.1] (1.center) -- cycle; + \fill[color=black] (0) circle (1.5pt); + \fill[color=black] (1) circle (1.5pt); + \fill[color=black] (2) circle (1.5pt); + \fill[color=black] (3) circle (1.5pt); + \fill[color=black] (4) circle (1.5pt); + \end{tikzpicture} + """ + latex_options = self.latex_options() + if latex_options["plot"] is None: + return repr(self).replace("{",r"\{").replace("}",r"\}") + + from sage.misc.latex import latex + latex.add_package_to_preamble_if_available("tikz") + res = "\\begin{{tikzpicture}}[scale={}]\n".format(latex_options['tikz_scale']) + + cardinality = self.base_set_cardinality() + from sage.rings.integer_ring import ZZ + if all(x in ZZ for x in self.base_set()): + sort_key = ZZ + else: + sort_key = str + base_set = sorted(self.base_set(), key=sort_key) + color = latex_options['color'] + + # If we want cyclic plots + if latex_options['plot'] == 'cyclic': + degrees = 360 // cardinality + radius = latex_options['radius'] + + res += "\\draw (0,0) circle [radius={}];\n".format(radius) + + # Add nodes + for k,i in enumerate(base_set): + location = (cardinality - k) * degrees - 270 + if latex_options['show_labels']: + res += "\\node[label={}:{}]".format(location, i) + else: + res += "\\node" + res += " ({}) at ({}:{}) {{}};\n".format(k, location, radius) + + # Setup partitions + for partition in sorted(self, key=str): + res += "\\draw[-,thick,color="+color + if latex_options['fill'] is not False: + if isinstance(latex_options['fill'], str): + res += ",fill=" + latex_options['fill'] + else: + res += ",fill={},fill opacity=0.1".format(color) + res += "] " + res += " -- ".join("({}.center)".format(base_set.index(j)) + for j in sorted(partition, key=sort_key)) + res += " -- cycle;\n" + + # Draw the circles on top + for k in range(len(base_set)): + res += "\\fill[color=black] ({}) circle (1.5pt);\n".format(k) + + # If we want line plots + elif latex_options['plot'] == 'linear': + angle = latex_options['angle'] + # setup line + for k,i in enumerate(base_set): + if latex_options['show_labels']: + res += "\\node[below=.05cm] at ({},0) {{${}$}};\n".format(k, i) + res += "\\node[draw,circle, inner sep=0pt, minimum width=4pt, fill=black] " + res += "({k}) at ({k},0) {{}};\n".format(k=k) + + # setup arcs + for partition in sorted(self, key=str): + p = sorted(partition, key=sort_key) + if len(p) <= 1: + continue + for k in range(1, len(p)): + res += "\\draw[color={}] ({})".format(color, base_set.index(p[k])) + res += " to [out={},in={}] ".format(90+angle, 90-angle) + res += "({});\n".format(base_set.index(p[k-1])) + else: + raise ValueError("plot must be None, 'cyclic', or 'linear'") + + res += "\\end{tikzpicture}" + return res cardinality = ClonableArray.__len__ @@ -566,7 +768,7 @@ def apply_permutation(self, p): INPUT: - - ``p`` -- A permutation + - ``p`` -- a permutation EXAMPLES:: @@ -967,6 +1169,121 @@ def arcs(self): arcs.append((p[i], p[i+1])) return arcs + def plot(self, angle=None, color='black', base_set_dict=None): + r""" + Return a plot of ``self``. + + INPUT: + + - ``angle`` -- (default: `\pi/4`) the angle at which the arcs take off + (if angle is negative, the arcs are drawn below the horizontal line) + + - ``color`` -- (default: ``'black'``) color of the arcs + + - ``base_set_dict`` -- (optional) dictionary with keys elements + of :meth:`base_set()` and values as integer or float + + EXAMPLES:: + + sage: p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]]) + sage: p.plot() + Graphics object consisting of 29 graphics primitives + + .. PLOT:: + + p = SetPartition([[1,10,11],[2,3,7],[4,5,6],[8,9]]) + sphinx_plot(p.plot()) + + :: + + sage: p = SetPartition([[1,3,4],[2,5]]) + sage: print(p.plot().description()) + Point set defined by 1 point(s): [(0.0, 0.0)] + Point set defined by 1 point(s): [(1.0, 0.0)] + Point set defined by 1 point(s): [(2.0, 0.0)] + Point set defined by 1 point(s): [(3.0, 0.0)] + Point set defined by 1 point(s): [(4.0, 0.0)] + Text '1' at the point (0.0,-0.1) + Text '2' at the point (1.0,-0.1) + Text '3' at the point (2.0,-0.1) + Text '4' at the point (3.0,-0.1) + Text '5' at the point (4.0,-0.1) + Arc with center (1.0,-1.0) radii (1.41421356237,1.41421356237) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + Arc with center (2.5,-0.5) radii (0.707106781187,0.707106781187) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + Arc with center (2.5,-1.5) radii (2.12132034356,2.12132034356) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + sage: p = SetPartition([['a','c'],['b','d'],['e']]) + sage: print(p.plot().description()) + Point set defined by 1 point(s): [(0.0, 0.0)] + Point set defined by 1 point(s): [(1.0, 0.0)] + Point set defined by 1 point(s): [(2.0, 0.0)] + Point set defined by 1 point(s): [(3.0, 0.0)] + Point set defined by 1 point(s): [(4.0, 0.0)] + Text 'a' at the point (0.0,-0.1) + Text 'b' at the point (1.0,-0.1) + Text 'c' at the point (2.0,-0.1) + Text 'd' at the point (3.0,-0.1) + Text 'e' at the point (4.0,-0.1) + Arc with center (1.0,-1.0) radii (1.41421356237,1.41421356237) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + Arc with center (2.0,-1.0) radii (1.41421356237,1.41421356237) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + sage: p = SetPartition([['a','c'],['b','d'],['e']]) + sage: print(p.plot(base_set_dict={'a':0,'b':1,'c':2,'d':-2.3,'e':5.4}).description()) + Point set defined by 1 point(s): [(-2.3, 0.0)] + Point set defined by 1 point(s): [(0.0, 0.0)] + Point set defined by 1 point(s): [(1.0, 0.0)] + Point set defined by 1 point(s): [(2.0, 0.0)] + Point set defined by 1 point(s): [(5.4, 0.0)] + Text 'a' at the point (0.0,-0.1) + Text 'b' at the point (1.0,-0.1) + Text 'c' at the point (2.0,-0.1) + Text 'd' at the point (-2.3,-0.1) + Text 'e' at the point (5.4,-0.1) + Arc with center (-0.65,-1.65) radii (2.33345237792,2.33345237792) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + Arc with center (1.0,-1.0) radii (1.41421356237,1.41421356237) + angle 0.0 inside the sector (0.785398163397,2.35619449019) + """ + from sage.plot.graphics import Graphics + from sage.plot.point import point + from sage.plot.text import text + from sage.plot.arc import arc + from sage.functions.other import sqrt + from sage.symbolic.constants import pi + from sage.functions.trig import tan, sin + from sage.functions.generalized import sgn + + diag = Graphics() + sorted_vertices_list = list(self.base_set()) + sorted_vertices_list.sort() + + if angle is None: + angle = pi / 4 + + if base_set_dict is not None: + vertices_dict = base_set_dict + else: + vertices_dict = {val: pos for pos,val in enumerate(sorted_vertices_list)} + + for elt in vertices_dict: + pos = vertices_dict[elt] + diag += point((pos,0), size=30, color=color) + diag += text(elt, (pos, -sgn(angle)*0.1), color=color) + # TODO: change 0.1 to something proportional to the height of the picture + + for (k,j) in self.arcs(): + pos_k,pos_j = float(vertices_dict[k]),float(vertices_dict[j]) + center = ((pos_k+pos_j) / 2, -abs(pos_j-pos_k) / (2*tan(angle))) + r1 = abs((pos_j-pos_k) / (2*sin(angle))) + sector = (sgn(angle) * (pi/2 - angle), sgn(angle) * (pi/2 + angle)) + diag += arc(center=center, r1=r1, sector=sector, color=color) + + diag.axes(False) + return diag + class SetPartitions(UniqueRepresentation, Parent): r""" An (unordered) partition of a set `S` is a set of pairwise @@ -1700,7 +2017,6 @@ def cyclic_permutations_of_set_partition(set_part): """ return list(cyclic_permutations_of_set_partition_iterator(set_part)) - def cyclic_permutations_of_set_partition_iterator(set_part): """ Iterates over all combinations of cyclic permutations of each cell diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 02c6f39e462..e42fb4afdeb 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -219,7 +219,9 @@ def from_polynomial_exp(self, p): sage: m.from_polynomial_exp(f) 3*m[4] + 2*m[5, 5, 5, 3, 1, 1] - ..SEEALSO:: :func:`Partition`, :meth:`Partition.to_exp` + .. SEEALSO:: + + :func:`Partition`, :meth:`Partition.to_exp` """ assert self.base_ring() == p.parent().base_ring() return self.sum_of_terms((Partition(exp=monomial), coeff) diff --git a/src/sage/combinat/sf/witt.py b/src/sage/combinat/sf/witt.py index 5f9516a5fd1..46c591d16b4 100644 --- a/src/sage/combinat/sf/witt.py +++ b/src/sage/combinat/sf/witt.py @@ -994,7 +994,7 @@ def __init_extra__(self): # symmetric basis and the complete homogeneous basis (over the same base # ring as self), respectively (but they are only set if the respective # arguments ``coerce_p``, ``coerce_e`` and ``coerce_h`` are True). - # self._friendly will be the one avaliable basis which makes computations + # self._friendly will be the one available basis which makes computations # the easiest. self._friendly = None diff --git a/src/sage/combinat/similarity_class_type.py b/src/sage/combinat/similarity_class_type.py index 088a917c594..13939a26d33 100644 --- a/src/sage/combinat/similarity_class_type.py +++ b/src/sage/combinat/similarity_class_type.py @@ -691,7 +691,7 @@ class SimilarityClassType(CombinatorialElement): r""" A similarity class type. - A matrix type is a multiset of primary similairty class types. + A matrix type is a multiset of primary similarity class types. INPUT: @@ -1311,9 +1311,10 @@ def input_parsing(data): case = 'pri' output = data except(TypeError, ValueError): - raise ValueError("Expected a Partition, a SimiliarityClassType or a PrimarySimilarityClassType, got a %s"%(type(data))) + raise ValueError("Expected a Partition, a SimilarityClassType or a PrimarySimilarityClassType, got a %s" % type(data)) return case, data + def ext_orbits(input_data, q = None, selftranspose = False): r""" Return the number of orbits in `\mathrm{Ext}^1(M, M)` for the action of diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index 87b1afc7d16..adfab2a4daa 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -2309,7 +2309,7 @@ def dual_map(self, k=1): .. NOTE:: - It is acually implemented only for `k=1`. + It is actually implemented only for `k=1`. INPUT: diff --git a/src/sage/combinat/words/suffix_trees.py b/src/sage/combinat/words/suffix_trees.py index 5802b9b7909..54ac719108b 100644 --- a/src/sage/combinat/words/suffix_trees.py +++ b/src/sage/combinat/words/suffix_trees.py @@ -937,7 +937,7 @@ def transition_function(self, word, node=0): r""" Returns the node obtained by starting from ``node`` and following the edges labelled by the letters of ``word``. Returns ``("explicit", - end_node)`` if we end at ``end_node``, or ``("implicit", (edge, d))`` + end_node)`` if we end at ``end_node``, or ``("implicit", edge, d)`` if we end `d` spots along an edge. INPUT: diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index a19ec833efc..0eaecd2bb44 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -422,6 +422,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function, division +from six import string_types from sage.matrix.constructor import matrix from sage.matrix.constructor import column_matrix @@ -487,10 +488,10 @@ def __init__(self, Nb, Nk, state_chr='a', key_chr='k'): if Nk not in range(4, 9): msg = "Key length Nk must be in the range 4 - 8, not {0}" raise ValueError(msg.format(Nk)) - if not isinstance(state_chr, basestring): + if not isinstance(state_chr, string_types): msg = "state_chr must be a string, not {0}" raise TypeError(msg.format(state_chr)) - if not isinstance(key_chr, basestring): + if not isinstance(key_chr, string_types): msg = "key_chr must be a string, not {0}" raise TypeError(msg.format(key_chr)) @@ -707,8 +708,8 @@ def _hex_to_GF(self, H, matrix=True): sage: rgf._hex_to_GF('1a2b0f', matrix=False) [x^4 + x^3 + x, x^5 + x^3 + x + 1, x^3 + x^2 + x + 1] """ - if not isinstance(H, basestring) or \ - any([c not in '0123456789abcdefABCDEF' for c in H]): + if not isinstance(H, string_types) or \ + any(c not in '0123456789abcdefABCDEF' for c in H): raise TypeError("keyword 'H' must be a hex string") def hx_to_gf(h): @@ -835,8 +836,7 @@ def _bin_to_GF(self, B, matrix=True): x^7 + x^6 + x^4 + x^2 + x + 1, x^5 + x^4 + x^2 + 1] """ - if not isinstance(B, basestring) or \ - any([c not in '01' for c in B]): + if not isinstance(B, string_types) or any(c not in '01' for c in B): raise TypeError("keyword 'B' must be a binary string") def bn_to_gf(b): @@ -953,14 +953,14 @@ def encrypt(self, plain, key, format='hex'): True """ if format == 'hex': - if not isinstance(plain, basestring) or \ - any([c not in '0123456789abcdefABCDEF' for c in plain]): + if not isinstance(plain, string_types) or \ + any(c not in '0123456789abcdefABCDEF' for c in plain): raise TypeError("'plain' keyword must be a hex string") if len(plain) != 8 * self._Nb: msg = "'plain' keyword\'s length must be {0}, not{1}" raise ValueError(msg.format(8 * self._Nb, len(plain))) - if not isinstance(key, basestring) or \ - any([c not in '0123456789abcdefABCDEF' for c in key]): + if not isinstance(key, string_types) or \ + any(c not in '0123456789abcdefABCDEF' for c in key): raise TypeError("'key' keyword must be a hex string") if len(key) != 8 * self._Nk: msg = "'key' keyword's length must be {0}, not {1}" @@ -969,14 +969,14 @@ def encrypt(self, plain, key, format='hex'): key_state = self._hex_to_GF(key) roundKeys = self.expand_key(key_state) elif format == 'binary': - if not isinstance(plain, basestring) or \ - any([c not in '01' for c in plain]): + if not isinstance(plain, string_types) or \ + any(c not in '01' for c in plain): raise TypeError("'plain' keyword must be a binary string") if len(plain) != 32 * self._Nb: msg = "'plain' keyword's length must be {0}, not {1}" raise ValueError(msg.format(32 * self._Nb, len(plain))) - if not isinstance(key, basestring) or \ - any([c not in '01' for c in key]): + if not isinstance(key, string_types) or \ + any(c not in '01' for c in key): raise TypeError("'key' keyword must be a binary string") if len(key) != 32 * self._Nk: msg = "'key' keyword's length must be {0}, not {1}" @@ -1043,14 +1043,14 @@ def decrypt(self, ciphertext, key, format='hex'): True """ if format == 'hex': - if not isinstance(ciphertext, basestring) or \ - any([c not in '0123456789abcdefABCDEF' for c in ciphertext]): + if not isinstance(ciphertext, string_types) or \ + any(c not in '0123456789abcdefABCDEF' for c in ciphertext): raise TypeError("'ciphertext' keyword must be a hex string") if len(ciphertext) != 8 * self._Nb: msg = "'ciphertext' keyword's length must be {0}, not{1}" raise ValueError(msg.format(8 * self._Nb, len(ciphertext))) - if not isinstance(key, basestring) or \ - any([c not in '0123456789abcdefABCDEF' for c in key]): + if not isinstance(key, string_types) or \ + any(c not in '0123456789abcdefABCDEF' for c in key): raise TypeError("'key' keyword must be a hex string") if len(key) != 8 * self._Nk: msg = "'key' keyword's length must be {0}, not {1}" @@ -1059,15 +1059,15 @@ def decrypt(self, ciphertext, key, format='hex'): key_state = self._hex_to_GF(key) roundKeys = self.expand_key(key_state) elif format == 'binary': - if not isinstance(ciphertext, basestring) or \ - any([c not in '01' for c in ciphertext]): + if not isinstance(ciphertext, string_types) or \ + any(c not in '01' for c in ciphertext): raise TypeError(("'ciphertext' keyword must be a binary " "string")) if len(ciphertext) != 32 * self._Nb: msg = "'ciphertext' keyword's length must be {0}, not {1}" raise ValueError(msg.format(32 * self._Nb, len(ciphertext))) - if not isinstance(key, basestring) or \ - any([c not in '01' for c in key]): + if not isinstance(key, string_types) or \ + any(c not in '01' for c in key): raise TypeError("'key' keyword must be a binary string") if len(key) != 32 * self._Nk: msg = "'key' keyword\'s length must be {0}, not {1}" diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index aec5a624702..5d87c325bc7 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -168,7 +168,7 @@ def increasing_tree_shape(elt, compare=min): #***************************************************************************** from __future__ import print_function from six.moves import range -from six import iteritems, add_metaclass +from six import iteritems, add_metaclass, string_types from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.element import Element @@ -844,7 +844,8 @@ def __repr__(self): raise ValueError("FindStatStatistic._query should be either 'ID' or 'data', but is %s. This should not happen. Please send an email to the developers." %self._query) def __eq__(self, other): - """Return ``True`` if ``self`` is equal to ``other`` and ``False`` + """ + Return ``True`` if ``self`` is equal to ``other`` and ``False`` otherwise. INPUT: @@ -897,7 +898,8 @@ def __eq__(self, other): return False def __ne__(self, other): - """Determine whether ``other`` is a different query. + """ + Determine whether ``other`` is a different query. INPUT: @@ -908,9 +910,9 @@ def __ne__(self, other): A boolean. - SEEALSO: + .. SEEALSO:: - :meth:`__eq__` + :meth:`__eq__` EXAMPLES:: @@ -1853,10 +1855,9 @@ class FindStatCollection(Element): sage: FindStatCollection(DyckWords(2)) # optional -- internet Cc0005: Dyck paths - SEEALSO: - - :class:`FindStatCollections` + .. SEEALSO:: + :class:`FindStatCollections` """ @staticmethod def __classcall_private__(cls, entry): @@ -2441,7 +2442,7 @@ def _element_constructor_(self, entry): if isinstance(entry, FindStatCollection): return entry - if isinstance(entry, (str, unicode)): + if isinstance(entry, string_types): # find by name in _findstat_collections for (id, c) in iteritems(self._findstat_collections): if entry.upper() in (c[0].upper(), c[1].upper(), c[2].upper()): @@ -2837,7 +2838,7 @@ def _element_constructor_(self, entry): elif entry in self._findstat_maps: return self.element_class(self, entry) - elif isinstance(entry, (str, unicode)): + elif isinstance(entry, string_types): # find by name in _findstat_maps for c in self._findstat_maps: if entry.upper() == c[FINDSTAT_MAP_NAME].upper(): diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 90db37a02bc..01ac77d2a58 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -123,7 +123,7 @@ def _repr_(self): s += ")" return s - def __cmp__(self, other): + def __eq__(self, other): """ Comparison by __dict__. @@ -135,9 +135,23 @@ def __cmp__(self, other): sage: DD1 == DD2 True """ - c = cmp(type(self), type(other)) - if c: return c - return cmp(self.__dict__,other.__dict__) + if not isinstance(other, DocTestDefaults): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: DD1 = DocTestDefaults(long=True) + sage: DD2 = DocTestDefaults(long=True) + sage: DD1 != DD2 + False + """ + return not (self == other) def skipdir(dirname): diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index edc883ff791..48f6a7d19e9 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -218,9 +218,9 @@ def get_source(example): def reduce_hex(fingerprints): """ - Returns a symmetric function of the arguments as hex strings. + Return a symmetric function of the arguments as hex strings. - The arguments should be 32 character strings consiting of hex + The arguments should be 32 character strings consisting of hex digits: 0-9 and a-f. EXAMPLES:: @@ -441,7 +441,7 @@ def __init__(self, long=False, optional_tags=()): else: self.optional_only = True - def __cmp__(self, other): + def __eq__(self, other): """ Comparison. @@ -453,9 +453,23 @@ def __cmp__(self, other): sage: DTP == DTP2 False """ - c = cmp(type(self), type(other)) - if c: return c - return cmp(self.__dict__, other.__dict__) + if not isinstance(other, SageDocTestParser): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.doctest.parsing import SageDocTestParser + sage: DTP = SageDocTestParser(True, ('sage','magma','guava')) + sage: DTP2 = SageDocTestParser(False, ('sage','magma','guava')) + sage: DTP != DTP2 + True + """ + return not (self == other) def parse(self, string, *args): r""" @@ -464,7 +478,7 @@ def parse(self, string, *args): INPUT: - ``string`` -- the string to parse. - - ``name`` -- optional string giving the name indentifying string, + - ``name`` -- optional string giving the name identifying string, to be used in error messages. OUTPUT: diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index ecda271a11a..21c5fb12239 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -131,7 +131,7 @@ def __init__(self, options): """ self.options = options - def __cmp__(self, other): + def __eq__(self, other): """ Comparison is just by comparison of attributes. @@ -148,9 +148,28 @@ def __cmp__(self, other): sage: FDS == FDS2 True """ - c = cmp(type(self), type(other)) - if c: return c - return cmp(self.__dict__, other.__dict__) + if type(self) != type(other): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.sources import FileDocTestSource + sage: from sage.env import SAGE_SRC + sage: import os + sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') + sage: DD = DocTestDefaults() + sage: FDS = FileDocTestSource(filename,DD) + sage: FDS2 = FileDocTestSource(filename,DD) + sage: FDS != FDS2 + False + """ + return not (self == other) def _process_doc(self, doctests, doc, namespace, start): """ @@ -240,7 +259,7 @@ def _create_doctests(self, namespace, tab_okay=None): sage: FDS.qualified_name = NestedName('sage.doctest.sources') sage: doctests, extras = FDS._create_doctests({}) sage: len(doctests) - 40 + 41 sage: extras['tab'] False sage: extras['line_number'] @@ -633,16 +652,16 @@ def create_doctests(self, namespace): sage: FDS = FileDocTestSource(filename,DocTestDefaults()) sage: doctests, extras = FDS.create_doctests(globals()) sage: len(doctests) - 40 + 41 sage: extras['tab'] False We give a self referential example:: - sage: doctests[17].name + sage: doctests[18].name 'sage.doctest.sources.FileDocTestSource.create_doctests' - sage: doctests[17].examples[10].source - 'doctests[Integer(17)].examples[Integer(10)].source\n' + sage: doctests[18].examples[10].source + 'doctests[Integer(18)].examples[Integer(10)].source\n' TESTS: @@ -654,7 +673,7 @@ def create_doctests(self, namespace): sage: n = -920390823904823094890238490238484; hash(n) > 0 False # 32-bit True # 64-bit - sage: ex = doctests[17].examples[13] + sage: ex = doctests[18].examples[13] sage: (bitness == '64' and ex.want == 'True \n') or (bitness == '32' and ex.want == 'False \n') True diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index fd2cc1d934a..8cc394f5871 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -176,7 +176,7 @@ def __str__(self): """ return str(self.__dict__) - def __cmp__(self, other): + def __eq__(self, other): """ Comparison. @@ -189,9 +189,25 @@ def __cmp__(self, other): sage: loads(dumps(t)) == t True """ - c = cmp(type(self), type(other)) - if c: return c - return cmp(self.__dict__, other.__dict__) + if not isinstance(other, Timer): + return False + return self.__dict__ == other.__dict__ + + def __ne__(self, other): + """ + Test for unequality + + EXAMPLES:: + + sage: from sage.doctest.util import Timer + sage: Timer() == Timer() + True + sage: t = Timer().start() + sage: loads(dumps(t)) != t + False + """ + return not (self == other) + # Inheritance rather then delegation as globals() must be a dict class RecordingDict(dict): @@ -469,7 +485,7 @@ def __repr__(self): """ return '.'.join(a for a in self.all if a is not None) - def __cmp__(self, other): + def __eq__(self, other): """ Comparison is just comparison of the underlying lists. @@ -487,6 +503,26 @@ def __cmp__(self, other): sage: qname == qname2 False """ - c = cmp(type(self), type(other)) - if c: return c - return cmp(self.all, other.all) + if not isinstance(other, NestedName): + return False + return self.all == other.all + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.doctest.util import NestedName + sage: qname = NestedName('sage.categories.algebras') + sage: qname2 = NestedName('sage.categories.algebras') + sage: qname != qname2 + False + sage: qname[0] = 'Algebras' + sage: qname2[2] = 'Algebras' + sage: repr(qname) == repr(qname2) + True + sage: qname != qname2 + True + """ + return not (self == other) diff --git a/src/sage/dynamics/interval_exchanges/labelled.py b/src/sage/dynamics/interval_exchanges/labelled.py index 12c2f6d7bc6..7ebe7fce497 100644 --- a/src/sage/dynamics/interval_exchanges/labelled.py +++ b/src/sage/dynamics/interval_exchanges/labelled.py @@ -337,7 +337,7 @@ def _reversed(self): r""" .. TODO:: - resolve properly the mutablility problem with the + resolve properly the mutability problem with the :meth:`_twin` attribute. TESTS:: diff --git a/src/sage/dynamics/interval_exchanges/reduced.py b/src/sage/dynamics/interval_exchanges/reduced.py index a4a8174ebaa..2a19e53f88a 100644 --- a/src/sage/dynamics/interval_exchanges/reduced.py +++ b/src/sage/dynamics/interval_exchanges/reduced.py @@ -904,7 +904,7 @@ class ReducedPermutationLI(ReducedPermutation, PermutationLI): sage: decomposition (['a'], ['c', 'a'], [], ['c']) - Rauzy movavability and Rauzy move:: + Rauzy movability and Rauzy move:: sage: p = iet.GeneralizedPermutation('a b b', 'c c a', reduced = True) sage: p.has_rauzy_move(0) diff --git a/src/sage/functions/all.py b/src/sage/functions/all.py index 53b0cb654bf..1c04b124ea0 100644 --- a/src/sage/functions/all.py +++ b/src/sage/functions/all.py @@ -4,6 +4,7 @@ lazy_import('sage.functions.piecewise_old', 'Piecewise') # deprecated lazy_import('sage.functions.piecewise', 'piecewise') +lazy_import('sage.functions.error', ['erf', 'erfc', 'erfi', 'erfinv']) from .trig import ( sin, cos, sec, csc, cot, tan, asin, acos, atan, @@ -21,7 +22,7 @@ from .other import ( ceil, floor, gamma, psi, factorial, beta, binomial, - abs_symbolic, erf, sqrt, log_gamma, + abs_symbolic, sqrt, log_gamma, gamma_inc, incomplete_gamma, gamma_inc_lower, arg, real_part, real, frac, imag_part, imag, imaginary, conjugate) @@ -37,10 +38,9 @@ spherical_bessel_J, spherical_bessel_Y, spherical_hankel1, spherical_hankel2) -from .special import (spherical_harmonic, - error_fcn, elliptic_e, +from .special import (spherical_harmonic, elliptic_e, elliptic_f, elliptic_ec, elliptic_eu, - elliptic_kc, elliptic_pi, elliptic_j) + elliptic_kc, elliptic_pi, elliptic_j, error_fcn) from .jacobi import (jacobi, inverse_jacobi, jacobi_nd, jacobi_ns, jacobi_nc, jacobi_dn, jacobi_ds, jacobi_dc, jacobi_sn, jacobi_sd, @@ -84,3 +84,4 @@ exponential_integral_1, Ei, exp_integral_ei) from .hypergeometric import hypergeometric, hypergeometric_M, hypergeometric_U + diff --git a/src/sage/functions/error.py b/src/sage/functions/error.py new file mode 100644 index 00000000000..db523fd85fb --- /dev/null +++ b/src/sage/functions/error.py @@ -0,0 +1,548 @@ +r""" +Error Functions + +This module provides symbolic error functions. These functions use the +`mpmath library` for numerical evaluation and Maxima, Pynac for +symbolics. + +The main objects which are exported from this module are: + + * :meth:`erf ` -- The error function + * :meth:`erfc ` -- The complementary error function + * :meth:`erfi ` -- The imagnary error function + * :meth:`erfinv ` -- The inverse error function + +AUTHORS: + + * Original authors ``erf``/``error_fcn`` (c) 2006-2014: + Karl-Dieter Crisman, Benjamin Jones, Mike Hansen, William Stein, + Burcin Erocal, Jeroen Demeyer, W. D. Joyner, R. Andrew Ohana + * Reorganisation in new file, addition of ``erfi``/``erfinv``/``erfc`` + (c) 2016: Ralf Stephan + +REFERENCES: + +- [DLMF-Error]_ + +- [WP-Error]_ +""" + +#***************************************************************************** +# Copyright (C) 2016 Ralf Stephan +# +# 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.symbolic.function import BuiltinFunction +from sage.libs.mpmath import utils as mpmath_utils +from sage.symbolic.expression import Expression +from sage.functions.all import sqrt, exp +from sage.symbolic.constants import pi +from sage.rings.infinity import unsigned_infinity + +class Function_erf(BuiltinFunction): + r""" + The error function. + + The error function is defined for real values as + + .. MATH:: + + \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{-t^2} dt. + + This function is also defined for complex values, via analytic + continuation. + + EXAMPLES: + + We can evaluate numerically:: + + sage: erf(2) + erf(2) + sage: erf(2).n() + 0.995322265018953 + sage: erf(2).n(100) + 0.99532226501895273416206925637 + sage: erf(ComplexField(100)(2+3j)) + -20.829461427614568389103088452 + 8.6873182714701631444280787545*I + + Basic symbolic properties are handled by Sage and Maxima:: + + sage: x = var("x") + sage: diff(erf(x),x) + 2*e^(-x^2)/sqrt(pi) + sage: integrate(erf(x),x) + x*erf(x) + e^(-x^2)/sqrt(pi) + + ALGORITHM: + + Sage implements numerical evaluation of the error function via the + ``erf()`` function from mpmath. Symbolics are handled by Sage and Maxima. + + REFERENCES: + + - :wikipedia:`Error_function` + - http://mpmath.googlecode.com/svn/trunk/doc/build/functions/expintegrals.html#error-functions + + TESTS: + + Check limits:: + + sage: limit(erf(x),x=0) + 0 + sage: limit(erf(x),x=infinity) + 1 + + Check that it's odd:: + + sage: erf(1.0) + 0.842700792949715 + sage: erf(-1.0) + -0.842700792949715 + + Check against other implementations and against the definition:: + + sage: erf(3).n() + 0.999977909503001 + sage: maxima.erf(3).n() + 0.999977909503001 + sage: (1-pari(3).erfc()) + 0.999977909503001 + sage: RR(3).erf() + 0.999977909503001 + sage: (integrate(exp(-x**2),(x,0,3))*2/sqrt(pi)).n() + 0.999977909503001 + + :trac:`9044`:: + + sage: N(erf(sqrt(2)),200) + 0.95449973610364158559943472566693312505644755259664313203267 + + :trac:`11626`:: + + sage: n(erf(2),100) + 0.99532226501895273416206925637 + sage: erf(2).n(100) + 0.99532226501895273416206925637 + + Test (indirectly) :trac:`11885`:: + + sage: erf(float(0.5)) + 0.5204998778130465 + sage: erf(complex(0.5)) + (0.5204998778130465+0j) + + Ensure conversion from maxima elements works:: + + sage: merf = maxima(erf(x)).sage().operator() + sage: merf.parent() == erf.parent() + True + + Make sure we can dump and load it:: + + sage: loads(dumps(erf(2))) + erf(2) + + Special-case 0 for immediate evaluation:: + + sage: erf(0) + 0 + sage: solve(erf(x)==0,x) + [x == 0] + + Make sure that we can hold:: + + sage: erf(0,hold=True) + erf(0) + sage: simplify(erf(0,hold=True)) + 0 + + Check that high-precision ComplexField inputs work:: + + sage: CC(erf(ComplexField(1000)(2+3j))) + -20.8294614276146 + 8.68731827147016*I + """ + + def __init__(self): + r""" + See docstring for :meth:`Function_erf`. + + EXAMPLES:: + + sage: maxima(erf(2)) + erf(2) + sage: erf(2)._sympy_() + erf(2) + """ + BuiltinFunction.__init__(self, "erf", latex_name=r"\operatorname{erf}", + conversions=dict(maxima='erf', + sympy='erf', + fricas='erf')) + + def _eval_(self, x): + """ + EXAMPLES: + + Input is not an expression but is exact:: + + sage: erf(0) + 0 + sage: erf(1) + erf(1) + sage: erf(oo) + 1 + sage: erf(SR(-oo)) + -1 + sage: erf(unsigned_infinity) + Infinity + + Input is not an expression and is not exact:: + + sage: erf(0.0) + 0.000000000000000 + + Input is an expression but not a trivial zero:: + + sage: erf(x) + erf(x) + + Input is an expression which is trivially zero:: + + sage: erf(SR(0)) + 0 + """ + if isinstance(x, Expression): + if x.is_trivial_zero(): + return x + elif x.is_infinity(): + if x.is_positive_infinity(): + return 1 + elif x.is_negative_infinity(): + return -1 + else: + return unsigned_infinity + elif not x: + return x + + def _evalf_(self, x, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: erf(2).n() + 0.995322265018953 + sage: erf(2).n(200) + 0.99532226501895273416206925636725292861089179704006007673835 + sage: erf(pi - 1/2*I).n(100) + 1.0000111669099367825726058952 + 1.6332655417638522934072124547e-6*I + + TESTS: + + Check that PARI/GP through the GP interface gives the same answer:: + + sage: gp.set_real_precision(59) # random + 38 + sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)); + 0.84270079294971486934122063508260925929606699796630290845994 + 0.84270079294971486934122063508260925929606699796630290845994 + + Check that for an imaginary input, the output is also imaginary, see + :trac:`13193`:: + + sage: erf(3.0*I) + 1629.99462260157*I + sage: erf(33.0*I) + 1.51286977510409e471*I + """ + R = parent or s_parent(x) + import mpmath + return mpmath_utils.call(mpmath.erf, x, parent=R) + + def _derivative_(self, x, diff_param=None): + """ + Derivative of erf function. + + EXAMPLES:: + + sage: erf(x).diff(x) + 2*e^(-x^2)/sqrt(pi) + + TESTS: + + Check if :trac:`8568` is fixed:: + + sage: var('c,x') + (c, x) + sage: derivative(erf(c*x),x) + 2*c*e^(-c^2*x^2)/sqrt(pi) + sage: erf(c*x).diff(x)._maxima_init_() + '((%pi)^(-1/2))*(_SAGE_VAR_c)*(exp(((_SAGE_VAR_c)^(2))*((_SAGE_VAR_x)^(2))*(-1)))*(2)' + """ + return 2*exp(-x**2)/sqrt(pi) + +erf = Function_erf() + +class Function_erfi(BuiltinFunction): + """ + The imaginary error function. + + The imaginary error function is defined by + + .. MATH:: + + \operatorname{erfi}(x) = -i \operatorname{erf}(ix). + """ + def __init__(self): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: maxima(erfi(2)) + erfi(2) + sage: erfi(2)._sympy_() + erfi(2) + """ + BuiltinFunction.__init__(self, "erfi", + latex_name=r"\operatorname{erfi}", + conversions=dict(maxima='erfi', + sympy='erfi', + fricas='erfi')) + + def _eval_(self, x): + """ + EXAMPLES:: + + sage: erfi(0) + 0 + sage: erfi(SR(0)) + 0 + sage: erfi(oo) + Infinity + sage: erfi(SR(-oo)) + Infinity + """ + if isinstance(x, Expression): + if x.is_trivial_zero(): + return x + elif x.is_infinity(): + return unsigned_infinity + elif not x: + return x + + def _evalf_(self, x, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: erfi(2.) + 18.5648024145756 + sage: erfi(2).n(100) + 18.564802414575552598704291913 + sage: erfi(-2*I).n(100) + -0.99532226501895273416206925637*I + """ + R = parent or s_parent(x) + import mpmath + return mpmath_utils.call(mpmath.erfi, x, parent=R) + + def _derivative_(self, x, diff_param=None): + """ + Derivative of erfi function. + + EXAMPLES:: + + sage: erfi(x).diff(x) + 2*e^(x^2)/sqrt(pi) + + """ + return 2*exp(x**2)/sqrt(pi) + +erfi = Function_erfi() + +class Function_erfc(BuiltinFunction): + r""" + The complementary error function. + + The complementary error function is defined by + + .. MATH:: + + \frac{2}{\sqrt{\pi}} \int_t^\infty e^{-x^2} dx. + + EXAMPLES:: + + sage: erfc(6) + erfc(6) + sage: erfc(6).n() + 2.15197367124989e-17 + sage: erfc(RealField(100)(1/2)) + 0.47950012218695346231725334611 + + sage: 1 - erfc(0.5) + 0.520499877813047 + sage: erf(0.5) + 0.520499877813047 + """ + def __init__(self): + r""" + EXAMPLES:: + + sage: maxima(erfc(2)) + erfc(2) + sage: erfc(2)._sympy_() + erfc(2) + """ + BuiltinFunction.__init__(self, "erfc", + latex_name=r"\operatorname{erfc}", + conversions=dict(maxima='erfc', + sympy='erfc', + fricas='erfc')) + + def _eval_(self, x): + """ + EXAMPLES:: + + sage: erfc(0) + 1 + sage: erfc(SR(0)) + 1 + sage: erfc(oo) + 0 + sage: erfc(SR(-oo)) + 2 + """ + if isinstance(x, Expression): + if x.is_trivial_zero(): + return 1 + elif x.is_infinity(): + if x.is_positive_infinity(): + return 0 + elif x.is_negative_infinity(): + return 2 + else: + return unsigned_infinity + elif not x: + return 1 + + def _evalf_(self, x, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: erfc(4).n() + 1.54172579002800e-8 + sage: erfc(4).n(100) + 1.5417257900280018852159673487e-8 + sage: erfc(4*I).n(100) + 1.0000000000000000000000000000 - 1.2969597307176392315279409506e6*I + """ + R = parent or s_parent(x) + import mpmath + return mpmath_utils.call(mpmath.erfc, x, parent=R) + + def _derivative_(self, x, diff_param=None): + """ + Derivative of erfc function. + + EXAMPLES:: + + sage: erfc(x).diff(x) + -2*e^(-x^2)/sqrt(pi) + """ + return -2*exp(-x**2)/sqrt(pi) + +erfc = Function_erfc() + + +class Function_erfinv(BuiltinFunction): + """ + The inverse error function. + + The inverse error function is defined by: + + .. MATH:: + + \operatorname{erfinv}(x) = \operatorname{erf}^{-1}(x). + """ + def __init__(self): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: erfinv(2)._sympy_() + erfinv(2) + sage: maxima(erfinv(2)) + inverse_erf(2) + + TESTS: + + Check that :trac:`11349` is fixed:: + + sage: _ = var('z,t') + sage: PDF = exp(-x^2 /2)/sqrt(2*pi) + sage: integralExpr = integrate(PDF,x,z,oo).subs(z==log(t)) + sage: y = solve(integralExpr==z,t)[0].rhs().subs(z==1/4) + sage: y + e^(sqrt(2)*erfinv(1/2)) + sage: y.n() + 1.96303108415826 + """ + BuiltinFunction.__init__(self, "erfinv", + latex_name=r"\operatorname{erfinv}", + conversions=dict(sympy='erfinv', + maxima='inverse_erf')) + + def _eval_(self, x): + """ + EXAMPLES:: + + sage: erfinv(0) + 0 + sage: erfinv(SR(0)) + 0 + sage: erfinv(1) + Infinity + """ + if isinstance(x, Expression): + if x.is_trivial_zero(): + return x + elif (x-1).is_trivial_zero(): + return unsigned_infinity + elif not x: + return x + elif x == 1: + return unsigned_infinity + + def _evalf_(self, x, parent=None, algorithm=None): + """ + EXAMPLES:: + + sage: erfinv(0.2) + 0.179143454621292 + sage: erfinv(1/5).n(100) + 0.17914345462129167649274901663 + """ + R = parent or s_parent(x) + import mpmath + return mpmath_utils.call(mpmath.erfinv, x, parent=R) + + def _derivative_(self, x, diff_param=None): + """ + Derivative of inverse erf function. + + EXAMPLES:: + + sage: erfinv(x).diff(x) + 1/2*sqrt(pi)*e^(erfinv(x)^2) + """ + return sqrt(pi)*exp(erfinv(x)**2)/2 + +erfinv = Function_erfinv() + +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override('sage.functions.other', 'Function_erf', Function_erf) + diff --git a/src/sage/functions/hypergeometric.py b/src/sage/functions/hypergeometric.py index 8b19bad031c..eff8ff9db88 100644 --- a/src/sage/functions/hypergeometric.py +++ b/src/sage/functions/hypergeometric.py @@ -168,7 +168,7 @@ from sage.functions.log import exp, log from sage.functions.trig import sin from sage.functions.hyperbolic import cosh, sinh -from sage.functions.other import erf +from sage.functions.error import erf from sage.symbolic.constants import pi from sage.symbolic.all import I from sage.symbolic.function import BuiltinFunction diff --git a/src/sage/functions/log.py b/src/sage/functions/log.py index a908d77e58b..67972798f2f 100644 --- a/src/sage/functions/log.py +++ b/src/sage/functions/log.py @@ -74,6 +74,15 @@ def __init__(self): sage: exp(7*pi*I/2) -I + For the sake of simplification, the argument is reduced modulo the + period of the complex exponential function, `2\pi i`:: + + sage: k = var('k', domain='integer') + sage: exp(2*k*pi*I) + 1 + sage: exp(log(2) + 2*k*pi*I) + 2 + The precision for the result is deduced from the precision of the input. Convert the input to a higher precision explicitly if a result with higher precision is desired:: @@ -230,7 +239,8 @@ def __init__(self): polylog(1.414213562373095?, 3) """ GinacFunction.__init__(self, 'log', latex_name=r'\log', - conversions=dict(maxima='log', fricas='log')) + conversions=dict(maxima='log', fricas='log', + mathematica='Log')) def __call__(self, *args, **kwds): """ @@ -725,8 +735,9 @@ def _eval_(self, n, z): TESTS: - When automatic simplication occurs, the parent of the output value should be - either the same as the parent of the input, or a Sage type:: + When automatic simplification occurs, the parent of the output + value should be either the same as the parent of the input, or + a Sage type:: sage: parent(lambert_w(int(0))) <... 'int'> @@ -926,7 +937,7 @@ def __init__(self): 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: + .. SEEALSO:: `Examples in Sympy documentation `_, `Sympy source code of exp_polar `_ diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 29b2753fde6..7ea0a63c52b 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -29,231 +29,6 @@ one_half = ~SR(2) -class Function_erf(BuiltinFunction): - r""" - The error function, defined for real values as - - `\text{erf}(x) = \frac{2}{\sqrt{\pi}} \int_0^x e^{-t^2} dt`. - - This function is also defined for complex values, via analytic - continuation. - - - EXAMPLES: - - We can evaluate numerically:: - - sage: erf(2) - erf(2) - sage: erf(2).n() - 0.995322265018953 - sage: erf(2).n(100) - 0.99532226501895273416206925637 - sage: erf(ComplexField(100)(2+3j)) - -20.829461427614568389103088452 + 8.6873182714701631444280787545*I - - Basic symbolic properties are handled by Sage and Maxima:: - - sage: x = var("x") - sage: diff(erf(x),x) - 2*e^(-x^2)/sqrt(pi) - sage: integrate(erf(x),x) - x*erf(x) + e^(-x^2)/sqrt(pi) - - ALGORITHM: - - Sage implements numerical evaluation of the error function via the - ``erf()`` function from mpmath. Symbolics are handled by Sage and Maxima. - - REFERENCES: - - - http://en.wikipedia.org/wiki/Error_function - - http://mpmath.org/doc/current/functions/expintegrals.html#error-functions - - TESTS: - - Check limits:: - - sage: limit(erf(x),x=0) - 0 - sage: limit(erf(x),x=infinity) - 1 - - Check that it's odd:: - - sage: erf(1.0) - 0.842700792949715 - sage: erf(-1.0) - -0.842700792949715 - - Check against other implementations and against the definition:: - - sage: erf(3).n() - 0.999977909503001 - sage: maxima.erf(3).n() - 0.999977909503001 - sage: (1-pari(3).erfc()) - 0.999977909503001 - sage: RR(3).erf() - 0.999977909503001 - sage: (integrate(exp(-x**2),(x,0,3))*2/sqrt(pi)).n() - 0.999977909503001 - - :trac:`9044`:: - - sage: N(erf(sqrt(2)),200) - 0.95449973610364158559943472566693312505644755259664313203267 - - :trac:`11626`:: - - sage: n(erf(2),100) - 0.99532226501895273416206925637 - sage: erf(2).n(100) - 0.99532226501895273416206925637 - - Test (indirectly) :trac:`11885`:: - - sage: erf(float(0.5)) - 0.5204998778130465 - sage: erf(complex(0.5)) - (0.5204998778130465+0j) - - Ensure conversion from maxima elements works:: - - sage: merf = maxima(erf(x)).sage().operator() - sage: merf == erf - True - - Make sure we can dump and load it:: - - sage: loads(dumps(erf(2))) - erf(2) - - Special-case 0 for immediate evaluation:: - - sage: erf(0) - 0 - sage: solve(erf(x)==0,x) - [x == 0] - - Make sure that we can hold:: - - sage: erf(0,hold=True) - erf(0) - sage: simplify(erf(0,hold=True)) - 0 - - Check that high-precision ComplexField inputs work:: - - sage: CC(erf(ComplexField(1000)(2+3j))) - -20.8294614276146 + 8.68731827147016*I - """ - - def __init__(self): - r""" - See docstring for :meth:`Function_erf`. - - EXAMPLES:: - - sage: maxima(erf(2)) - erf(2) - sage: erf(2)._sympy_() - erf(2) - """ - BuiltinFunction.__init__(self, "erf", latex_name=r"\text{erf}", - conversions=dict(maxima='erf', - sympy='erf', - fricas='erf')) - - def _eval_(self, x): - """ - EXAMPLES: - - Input is not an expression but is exact:: - - sage: erf(0) - 0 - sage: erf(1) - erf(1) - - Input is not an expression and is not exact:: - - sage: erf(0.0) - 0.000000000000000 - - Input is an expression but not a trivial zero:: - - sage: erf(x) - erf(x) - - Input is an expression which is trivially zero:: - - sage: erf(SR(0)) - 0 - """ - if isinstance(x, Expression): - if x.is_trivial_zero(): - return x - elif not x: - return x - - def _evalf_(self, x, parent=None, algorithm=None): - """ - EXAMPLES:: - - sage: erf(2).n() - 0.995322265018953 - sage: erf(2).n(200) - 0.99532226501895273416206925636725292861089179704006007673835 - sage: erf(pi - 1/2*I).n(100) - 1.0000111669099367825726058952 + 1.6332655417638522934072124547e-6*I - - TESTS: - - Check that PARI/GP through the GP interface gives the same answer:: - - sage: gp.set_real_precision(59) # random - 38 - sage: print(gp.eval("1 - erfc(1)")); print(erf(1).n(200)); - 0.84270079294971486934122063508260925929606699796630290845994 - 0.84270079294971486934122063508260925929606699796630290845994 - - Check that for an imaginary input, the output is also imaginary, see - :trac:`13193`:: - - sage: erf(3.0*I) - 1629.99462260157*I - sage: erf(33.0*I) - 1.51286977510409e471*I - """ - R = parent or s_parent(x) - import mpmath - return mpmath_utils.call(mpmath.erf, x, parent=R) - - def _derivative_(self, x, diff_param=None): - """ - Derivative of erf function - - EXAMPLES:: - - sage: erf(x).diff(x) - 2*e^(-x^2)/sqrt(pi) - - TESTS: - - Check if :trac:`8568` is fixed:: - - sage: var('c,x') - (c, x) - sage: derivative(erf(c*x),x) - 2*c*e^(-c^2*x^2)/sqrt(pi) - sage: erf(c*x).diff(x)._maxima_init_() - '((%pi)^(-1/2))*(_SAGE_VAR_c)*(exp(((_SAGE_VAR_c)^(2))*((_SAGE_VAR_x)^(2))*(-1)))*(2)' - """ - return 2*exp(-x**2)/sqrt(pi) - -erf = Function_erf() - class Function_abs(GinacFunction): def __init__(self): r""" @@ -331,7 +106,8 @@ def __init__(self): (pi + e)*abs(x) """ GinacFunction.__init__(self, "abs", latex_name=r"\mathrm{abs}", - conversions=dict(sympy='Abs')) + conversions=dict(sympy='Abs', + mathematica='Abs')) abs = abs_symbolic = Function_abs() @@ -1107,6 +883,7 @@ def _eval_(self, x, y): if x == 0: return -Ei(-y) if x == Rational(1)/2: #only for x>0 + from sage.functions.error import erf return sqrt(pi)*(1-erf(sqrt(y))) return None @@ -2593,8 +2370,10 @@ class Function_sum(BuiltinFunction): EXAMPLES:: sage: from sage.functions.other import symbolic_sum as ssum - sage: ssum(x, x, 1, 10) + sage: r = ssum(x, x, 1, 10); r sum(x, x, 1, 10) + sage: r.unhold() + 55 """ def __init__(self): """ @@ -2607,4 +2386,163 @@ def __init__(self): BuiltinFunction.__init__(self, "sum", nargs=4, conversions=dict(maxima='sum')) + def _print_latex_(self, x, var, a, b): + r""" + EXAMPLES:: + + sage: from sage.functions.other import symbolic_sum as ssum + sage: latex(ssum(x^2, x, 1, 10)) + {\sum_{x=1}^{10} x^2} + """ + return r"{{\sum_{{{}={}}}^{{{}}} {}}}".format(var, a, b, x) + symbolic_sum = Function_sum() + + +class Function_prod(BuiltinFunction): + """ + Placeholder symbolic product function that is only accessible internally. + + EXAMPLES:: + + sage: from sage.functions.other import symbolic_product as sprod + sage: r = sprod(x, x, 1, 10); r + product(x, x, 1, 10) + sage: r.unhold() + 3628800 + """ + def __init__(self): + """ + EXAMPLES:: + + sage: from sage.functions.other import symbolic_product as sprod + sage: _ = var('m n', domain='integer') + sage: r = maxima(sprod(sin(m), m, 1, n)).sage(); r + product(sin(m), m, 1, n) + sage: isinstance(r.operator(), sage.functions.other.Function_prod) + True + sage: r = sympy(sprod(sin(m), m, 1, n)).sage(); r # known bug + product(sin(m), m, 1, n) + sage: isinstance(r.operator(), + ....: sage.functions.other.Function_prod) # known bug + True + sage: giac(sprod(m, m, 1, n)) + n! + """ + BuiltinFunction.__init__(self, "product", nargs=4, + conversions=dict(maxima='product', + sympy='Product', giac='product')) + + def _print_latex_(self, x, var, a, b): + r""" + EXAMPLES:: + + sage: from sage.functions.other import symbolic_product as sprod + sage: latex(sprod(x^2, x, 1, 10)) + {\prod_{x=1}^{10} x^2} + """ + return r"{{\prod_{{{}={}}}^{{{}}} {}}}".format(var, a, b, x) + +symbolic_product = Function_prod() + + +class Function_limit(BuiltinFunction): + """ + Placeholder symbolic limit function that is only accessible internally. + + This function is called to create formal wrappers of limits that + Maxima can't compute:: + + sage: a = lim(exp(x^2)*(1-erf(x)), x=infinity); a + -limit((erf(x) - 1)*e^(x^2), x, +Infinity) + + EXAMPLES:: + + sage: from sage.functions.other import symbolic_limit as slimit + sage: slimit(1/x, x, +oo) + limit(1/x, x, +Infinity) + sage: var('minus,plus') + (minus, plus) + sage: slimit(1/x, x, +oo) + limit(1/x, x, +Infinity) + sage: slimit(1/x, x, 0, plus) + limit(1/x, x, 0, plus) + sage: slimit(1/x, x, 0, minus) + limit(1/x, x, 0, minus) + """ + def __init__(self): + """ + EXAMPLES:: + + sage: from sage.functions.other import symbolic_limit as slimit + sage: maxima(slimit(1/x, x, +oo)) + 0 + """ + BuiltinFunction.__init__(self, "limit", nargs=0, + conversions=dict(maxima='limit')) + + def _latex_(self): + r""" + EXAMPLES:: + + sage: from sage.functions.other import symbolic_limit as slimit + sage: latex(slimit) + \lim + """ + return r'\lim' + + def _print_latex_(self, ex, var, to, direction=''): + r""" + EXAMPLES:: + + sage: from sage.functions.other import symbolic_limit as slimit + sage: var('x,a') + (x, a) + sage: f = function('f') + sage: latex(slimit(f(x), x, a)) + \lim_{x \to a}\, f\left(x\right) + sage: latex(limit(f(x), x=oo)) + \lim_{x \to +\infty}\, f\left(x\right) + + TESTS: + + When one-sided limits are converted back from maxima, the direction + argument becomes a symbolic variable. We check if typesetting these works:: + + sage: from sage.functions.other import symbolic_limit as slimit + sage: var('minus,plus') + (minus, plus) + sage: latex(slimit(f(x), x, a, minus)) + \lim_{x \to a^-}\, f\left(x\right) + sage: latex(slimit(f(x), x, a, plus)) + \lim_{x \to a^+}\, f\left(x\right) + sage: latex(limit(f(x),x=a,dir='+')) + \lim_{x \to a^+}\, f\left(x\right) + sage: latex(limit(f(x),x=a,dir='right')) + \lim_{x \to a^+}\, f\left(x\right) + sage: latex(limit(f(x),x=a,dir='-')) + \lim_{x \to a^-}\, f\left(x\right) + sage: latex(limit(f(x),x=a,dir='left')) + \lim_{x \to a^-}\, f\left(x\right) + + Check if :trac:`13181` is fixed:: + + sage: t = var('t') + sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x,dir='-')) + \lim_{t \to x^-}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) + sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x,dir='+')) + \lim_{t \to x^+}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) + sage: latex(limit(exp_integral_e(1/2, I*t - I*x)*sqrt(-t + x),t=x)) + \lim_{t \to x}\, \sqrt{-t + x} exp_integral_e\left(\frac{1}{2}, i \, t - i \, x\right) + """ + if repr(direction) == 'minus': + dir_str = '^-' + elif repr(direction) == 'plus': + dir_str = '^+' + else: + dir_str = '' + return r"\lim_{{{} \to {}{}}}\, {}".format(latex(var), + latex(to), dir_str, latex(ex)) + +symbolic_limit = Function_limit() + diff --git a/src/sage/functions/special.py b/src/sage/functions/special.py index d4ba3c93554..7c42730f97f 100644 --- a/src/sage/functions/special.py +++ b/src/sage/functions/special.py @@ -1013,34 +1013,21 @@ def _print_latex_(self, n, z, m): elliptic_pi = EllipticPi() -def error_fcn(t): - r""" - The complementary error function - `\frac{2}{\sqrt{\pi}}\int_t^\infty e^{-x^2} dx` (t belongs - to RR). This function is currently always - evaluated immediately. +def error_fcn(x): + """ + Deprecated in :trac:21819`. Please use ``erfc()``. EXAMPLES:: - sage: error_fcn(6) - 2.15197367124989e-17 - sage: error_fcn(RealField(100)(1/2)) - 0.47950012218695346231725334611 - - Note this is literally equal to `1 - erf(t)`:: - - sage: 1 - error_fcn(0.5) - 0.520499877813047 - sage: erf(0.5) - 0.520499877813047 + sage: error_fcn(x) + doctest:warning + ... + DeprecationWarning: error_fcn() is deprecated. Please use erfc() + See http://trac.sagemath.org/21819 for details. + erfc(x) """ - try: - return t.erfc() - except AttributeError: - try: - return RR(t).erfc() - except Exception: - raise NotImplementedError - - + from .error import erfc + from sage.misc.superseded import deprecation + deprecation(21819, "error_fcn() is deprecated. Please use erfc()") + return erfc(x) diff --git a/src/sage/functions/trig.py b/src/sage/functions/trig.py index 1159076ebcf..088a3218948 100644 --- a/src/sage/functions/trig.py +++ b/src/sage/functions/trig.py @@ -34,6 +34,16 @@ def __init__(self): sage: a = sin(0,hold=True); a.simplify() 0 + If possible, the argument is also reduced modulo the + period length `2\pi`, and well-known identities are + directly evaluated:: + + sage: k = var('k', domain='integer') + sage: sin(1 + 2*k*pi) + sin(1) + sage: sin(k*pi) + 0 + TESTS:: sage: conjugate(sin(x)) @@ -141,6 +151,18 @@ def __init__(self): sage: a = cos(0,hold=True); a.simplify() 1 + If possible, the argument is also reduced modulo the + period length `2\pi`, and well-known identities are + directly evaluated:: + + sage: k = var('k', domain='integer') + sage: cos(1 + 2*k*pi) + cos(1) + sage: cos(k*pi) + cos(pi*k) + sage: cos(pi/3 + 2*k*pi) + 1/2 + TESTS:: sage: conjugate(cos(x)) @@ -193,6 +215,16 @@ def __init__(self): sage: a = tan(pi/4,hold=True); a.simplify() 1 + If possible, the argument is also reduced modulo the + period length `\pi`, and well-known identities are + directly evaluated:: + + sage: k = var('k', domain='integer') + sage: tan(1 + 2*k*pi) + tan(1) + sage: tan(k*pi) + 0 + TESTS:: sage: tan(x)._sympy_() diff --git a/src/sage/game_theory/catalog.py b/src/sage/game_theory/catalog.py index aaefe760fba..9e47ab0c4bc 100644 --- a/src/sage/game_theory/catalog.py +++ b/src/sage/game_theory/catalog.py @@ -1,6 +1,13 @@ r""" Catalog Of Games + +TESTS:: + + sage: 'absolute_import' in dir(game_theory) + False """ from __future__ import absolute_import from . import catalog_normal_form_games as normal_form_games + +del absolute_import diff --git a/src/sage/games/quantumino.py b/src/sage/games/quantumino.py index 01d79ada28c..9d6dd90a929 100644 --- a/src/sage/games/quantumino.py +++ b/src/sage/games/quantumino.py @@ -7,7 +7,7 @@ made by Family Games America (see also `this video `_ on Youtube). This puzzle was left at the dinner room of the Laboratoire de Combinatoire Informatique -Mathematique in Montreal by Franco Saliola during winter 2011. +Mathématique in Montreal by Franco Saliola during winter 2011. The solution uses the dancing links code which is in Sage and is based on the more general code available in the module :mod:`sage.combinat.tiling`. @@ -27,7 +27,7 @@ AUTHOR: - - Sebastien Labbe, April 28th, 2011 +- Sébastien Labbé, April 28th, 2011 DESCRIPTION (from [1]): diff --git a/src/sage/geometry/fan.py b/src/sage/geometry/fan.py index 51f5b986708..2685d5f2de1 100644 --- a/src/sage/geometry/fan.py +++ b/src/sage/geometry/fan.py @@ -45,7 +45,7 @@ But keep in mind that in higher dimensions the cone data is essential and cannot be omitted. Instead of building a fan from scratch, for -this tutorial we will use an easy way to get two fans assosiated to +this tutorial we will use an easy way to get two fans associated to :class:`lattice polytopes `: :func:`FaceFan` and :func:`NormalFan`:: @@ -589,7 +589,7 @@ def FaceFan(polytope, lattice=None): ` over `\QQ` or a :class:`lattice polytope `. A (not - necessarily full-dimensional) polytope contaning the origin in + necessarily full-dimensional) polytope containing the origin in its :meth:`relative interior `. diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py index 5e7d78c7010..73dcee3fc50 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_interface.py @@ -22,7 +22,7 @@ in the hyperboloid model. Performing mapping this point to the upper half plane and performing computations there may return with vector whose components are unsimplified strings have several ``sqrt(2)``'s. - Presently, this drawback is outweighed by the rapidity with which new + Presently, this drawback is outweighted by the rapidity with which new models can be implemented. AUTHORS: diff --git a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py index 3654c66ba21..08511406a80 100644 --- a/src/sage/geometry/hyperbolic_space/hyperbolic_model.py +++ b/src/sage/geometry/hyperbolic_space/hyperbolic_model.py @@ -674,7 +674,7 @@ def coords(x): return self._dist_points(coords(p), coords(q)) raise NotImplementedError("can only compute distance between" - " ultra-parallel and interecting geodesics") + " ultra-parallel and intersecting geodesics") # If only one is a geodesic, make sure it's b to make things easier a,b = b,a diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index ebbefb1be32..77d3fb6e030 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3029,7 +3029,7 @@ def _element_constructor_(self, *args, **kwds): if h.A() == 0: raise ValueError('linear expression must be non-constant to define a hyperplane') if not_char2 and -h in hyperplane_set: - raise ValueError('arrangement cannot simultaneouly have h and -h as hyperplane') + raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') return self.element_class(self, hyperplanes) @cached_method diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index 354b8e26416..433a3243d61 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -34,7 +34,7 @@ def make_parent(base_ring, dimension, names=None): - ``base_ring`` -- a ring - - ``dimenison`` -- integer + - ``dimension`` -- integer - ``names`` -- ``None`` (default) or a list/tuple/iterable of strings diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index 785f9f54ed3..ed055ebf51d 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -886,6 +886,16 @@ cdef class Inequality_int: Traceback (most recent call last): ... OverflowError: ... + + TESTS: + + Check that :trac:`21993` is fixed:: + + sage: Inequality_int([18560500, -89466500], 108027, [178933, 37121]) + Traceback (most recent call last): + ... + OverflowError: ... + """ cdef int A[INEQ_INT_MAX_DIM] cdef int b @@ -928,8 +938,8 @@ cdef class Inequality_int: if self.dim > 0: self.coeff_next = self.A[1] # finally, make sure that there cannot be any overflow during the enumeration - self._to_int(ZZ(b) + sum( ZZ(A[i]) * ZZ(max_abs_coordinates[i]) - for i in range(self.dim) )) + self._to_int(abs(ZZ(b)) + sum( abs(ZZ(A[i])) * ZZ(max_abs_coordinates[i]) + for i in range(self.dim) )) def __repr__(self): """ diff --git a/src/sage/geometry/polyhedron/backend_cdd.py b/src/sage/geometry/polyhedron/backend_cdd.py index 76b537ff6a1..9e0ca53ba7b 100644 --- a/src/sage/geometry/polyhedron/backend_cdd.py +++ b/src/sage/geometry/polyhedron/backend_cdd.py @@ -357,7 +357,7 @@ def __init__(self, parent, Vrep, Hrep, **kwds): sage: p = Polyhedron(backend='cdd', base_ring=QQ) sage: type(p) - + sage: TestSuite(p).run() """ Polyhedron_cdd.__init__(self, parent, Vrep, Hrep, **kwds) @@ -399,7 +399,7 @@ def __init__(self, parent, Vrep, Hrep, **kwds): sage: p = Polyhedron(backend='cdd', base_ring=RDF) sage: type(p) - + sage: TestSuite(p).run() """ Polyhedron_cdd.__init__(self, parent, Vrep, Hrep, **kwds) diff --git a/src/sage/geometry/polyhedron/backend_field.py b/src/sage/geometry/polyhedron/backend_field.py index ca49decf889..7aac890ba42 100644 --- a/src/sage/geometry/polyhedron/backend_field.py +++ b/src/sage/geometry/polyhedron/backend_field.py @@ -210,9 +210,9 @@ def _init_Vrepresentation_backend(self, Vrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() - (A vertex at (0, 0.7071067811865475?), + (A vertex at (0.?e-15, 0.707106781186548?), A vertex at (1.414213562373095?, 0), - A vertex at (4, 0.372677996249965?)) + A vertex at (4.000000000000000?, 0.372677996249965?)) """ self._Vrepresentation = [] parent = self.parent() @@ -237,9 +237,9 @@ def _init_Hrepresentation_backend(self, Hrep): An inequality (-0.1419794359520263?, -1.698172434277148?) x + 1.200789243901438? >= 0, An inequality (0.3001973109753594?, 0.600394621950719?) x - 0.4245431085692869? >= 0) sage: p.Vrepresentation() - (A vertex at (0, 0.7071067811865475?), + (A vertex at (0.?e-15, 0.707106781186548?), A vertex at (1.414213562373095?, 0), - A vertex at (4, 0.372677996249965?)) + A vertex at (4.000000000000000?, 0.372677996249965?)) """ self._Hrepresentation = [] parent = self.parent() diff --git a/src/sage/geometry/polyhedron/backend_normaliz.py b/src/sage/geometry/polyhedron/backend_normaliz.py index 6c29099d45f..c5099ca8793 100644 --- a/src/sage/geometry/polyhedron/backend_normaliz.py +++ b/src/sage/geometry/polyhedron/backend_normaliz.py @@ -494,11 +494,11 @@ def integral_points(self, threshold=10000): be a very bad idea (note this is a rational (non-lattice) polytope, so the other backends use the bounding box method):: - sage: P = Polyhedron(vertices=((0, 0), (1789345,37121))) + 1/1000*polytopes.hypercube(2) + sage: P = Polyhedron(vertices=((0, 0), (178933,37121))) + 1/1000*polytopes.hypercube(2) sage: P = Polyhedron(vertices=P.vertices_list(), # optional - pynormaliz ....: backend='normaliz') sage: len(P.integral_points()) # optional - pynormaliz - 3654 + 434 Finally, the 3-d reflexive polytope number 4078:: @@ -551,6 +551,20 @@ def integral_points(self, threshold=10000): sage: P = Polyhedron([[]], backend='normaliz') # optional - pynormaliz sage: P.integral_points() # optional - pynormaliz ((),) + + A polytope with no integral points (:trac:`22938`):: + + sage: ieqs = [[1, 2, -1, 0], [0, -1, 2, -1], [0, 0, -1, 2], + ....: [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, -1], + ....: [-1, -1, -1, -1], [1, 1, 0, 0], [1, 0, 1, 0], + ....: [1, 0, 0, 1]] + sage: P = Polyhedron(ieqs=ieqs, backend='normaliz') # optional - pynormaliz + sage: P.bounding_box() # optional - pynormaliz + ((-3/4, -1/2, -1/4), (-1/2, -1/4, 0)) + sage: P.bounding_box(integral_hull=True) # optional - pynormaliz + (None, None) + sage: P.integral_points() # optional - pynormaliz + () """ import PyNormaliz if not self.is_compact(): @@ -567,6 +581,8 @@ def integral_points(self, threshold=10000): # for small bounding boxes, it is faster to naively iterate over the points of the box if threshold > 1: box_min, box_max = self.bounding_box(integral_hull=True) + if box_min is None: + return () box_points = prod(max_coord-min_coord+1 for min_coord, max_coord in zip(box_min, box_max)) if box_points + sage: p.polar().base_ring() Integer Ring """ diff --git a/src/sage/geometry/polyhedron/constructor.py b/src/sage/geometry/polyhedron/constructor.py index fbb8aa5c47c..9d2a4444000 100644 --- a/src/sage/geometry/polyhedron/constructor.py +++ b/src/sage/geometry/polyhedron/constructor.py @@ -487,7 +487,7 @@ def Polyhedron(vertices=None, rays=None, lines=None, base_ring = common_ring convert = True - # Add the origin if necesarry + # Add the origin if necessary if got_Vrep and len(vertices)==0: vertices = [ [0]*ambient_dim ] diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index a0714f32ee2..64d902ce5f3 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -483,7 +483,7 @@ def great_rhombicuboctahedron(self, exact=True, base_ring=None): """ Return the great rhombicuboctahedron. - The great rohombicuboctahedron (or truncated cuboctahedron) is an + The great rhombicuboctahedron (or truncated cuboctahedron) is an Archimedean solid with 48 vertices and 26 faces. For more information see the :wikipedia:`Truncated_cuboctahedron`. diff --git a/src/sage/geometry/polyhedron/palp_database.py b/src/sage/geometry/polyhedron/palp_database.py index 1cba7f96de4..fc74f17fda3 100644 --- a/src/sage/geometry/polyhedron/palp_database.py +++ b/src/sage/geometry/polyhedron/palp_database.py @@ -76,7 +76,7 @@ class PALPreader(SageObject): sage: next(iter(PALPreader(2, output='Polyhedron'))) A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices sage: type(_) - + sage: next(iter(PALPreader(2, output='PPL'))) A 2-dimensional lattice polytope in ZZ^2 with 3 vertices diff --git a/src/sage/geometry/ribbon_graph.py b/src/sage/geometry/ribbon_graph.py index 59b52933b3f..b2902917aa7 100644 --- a/src/sage/geometry/ribbon_graph.py +++ b/src/sage/geometry/ribbon_graph.py @@ -3,7 +3,7 @@ This file implements objects called *ribbon graphs*. These are graphs together with a cyclic ordering of the darts adjacent to each -vertex. This data allows us to unambiguosly "thicken" the ribbon +vertex. This data allows us to unambiguously "thicken" the ribbon graph to an orientable surface with boundary. Also, every orientable surface with non-empty boundary is the thickening of a ribbon graph. @@ -1140,14 +1140,14 @@ def make_ribbon(g, r): #We first generate the surface of genus g and 1 boundary component. #This is done by considering the usual planar representation of - #a surface as a poligon of 4*g+2 edges with identifications. (see + #a surface as a polygon of 4*g+2 edges with identifications. (see #any topology book on the classification of surfaces) for i in range(2*g): repr_sigma[0].append(i+2) repr_sigma[1].append(i+(2*g+2)+1) repr_rho += [[i+2,i+(2*g+2)+1]] - #finally we add an edge for each aditional boundary component. + #finally we add an edge for each additional boundary component. max_dart = 4*g+2 for j in range(r-1): repr_sigma[0].insert(0, max_dart+2*(j+1)-1) diff --git a/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py index c4195c8be65..d63551c42e4 100644 --- a/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py +++ b/src/sage/geometry/riemannian_manifolds/parametrized_surface3d.py @@ -842,7 +842,7 @@ def rotation(self,theta): [ 1/2 -1/2*sqrt(3)/cos(v)] [ 1/2*sqrt(3)*cos(v) 1/2] - We verify that three succesive rotations over $\pi/3$ yield minus the identity:: + We verify that three successive rotations over $\pi/3$ yield minus the identity:: sage: rotation^3 [-1 0] diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index bdafadbeade..8078b89ebd6 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -1846,7 +1846,7 @@ def contained_simplex(self, large=True, initial_point=None): A tuple of points that span a simplex of dimension :meth:`dim`. If ``large==True``, the simplex is constructed by sucessively picking the farthest point. This will ensure that - the simplex is not unneccessarily small, but will in general + the simplex is not unnecessarily small, but will in general not return a maximal simplex. EXAMPLES:: diff --git a/src/sage/graphs/all.py b/src/sage/graphs/all.py index c31a1aead8c..050339ebe14 100644 --- a/src/sage/graphs/all.py +++ b/src/sage/graphs/all.py @@ -19,3 +19,13 @@ lazy_import("sage.graphs.graph_editor", "graph_editor") from sage.graphs.isgci import graph_classes + +""" +TESTS: + +Test that sagenb.misc.support is not imported (see :trac:`22941`):: + + sage: import sage.graphs.graph_editor + sage: 'sagenb.misc.support' in sys.modules + False +""" diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index d2ac8db761e..d6a16a7c4fa 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -33,9 +33,11 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six import iteritems from six.moves import range from .graph import Graph +from sage.rings.integer import Integer class BipartiteGraph(Graph): r""" @@ -1281,3 +1283,135 @@ def reduced_adjacency_matrix(self, sparse=True): # now construct and return the matrix from the dictionary we created from sage.matrix.constructor import matrix return matrix(len(self.right), len(self.left), D, sparse=sparse) + + def matching(self, value_only=False, algorithm=None, + use_edge_labels=False, solver=None, verbose=0): + r""" + Return a maximum matching of the graph represented by the list of its + edges. + + Given a graph `G` such that each edge `e` has a weight `w_e`, a maximum + matching is a subset `S` of the edges of `G` of maximum weight such that + no two edges of `S` are incident with each other. + + INPUT: + + - ``value_only`` -- boolean (default: ``False``); when set to ``True``, + only the cardinal (or the weight) of the matching is returned + + - ``algorithm`` -- string (default: ``"Hopcroft-Karp"`` if + ``use_edge_labels==False``, otherwise ``"Edmonds"``) + + - ``"Hopcroft-Karp"`` selects the default bipartite graph algorithm as + implemented in NetworkX + + - ``"Eppstein"`` selects Eppstein's algorithm as implemented in + NetworkX + + - ``"Edmonds"`` selects Edmonds' algorithm as implemented in NetworkX + + - ``"LP"`` uses a Linear Program formulation of the matching problem + + - ``use_edge_labels`` -- boolean (default: ``False``) + + - when set to ``True``, computes a weighted matching where each edge + is weighted by its label (if an edge has no label, `1` is assumed); + only if ``algorithm`` is ``"Edmonds"``, ``"LP"`` + + - when set to ``False``, each edge has weight `1` + + - ``solver`` -- (default: ``None``) a specific Linear Program (LP) + solver to be used + + - ``verbose`` -- integer (default: ``0``); sets the level of verbosity: + set to 0 by default, which means quiet + + .. SEEALSO:: + + - :wikipedia:`Matching_(graph_theory)` + - :meth:`~Graph.matching` + + EXAMPLES: + + Maximum matching in a cycle graph:: + + sage: G = BipartiteGraph(graphs.CycleGraph(10)) + sage: G.matching() + [(0, 1, None), (2, 3, None), (4, 5, None), (6, 7, None), (8, 9, None)] + + The size of a maximum matching in a complete bipartite graph using + Eppstein:: + + sage: G = BipartiteGraph(graphs.CompleteBipartiteGraph(4,5)) + sage: G.matching(algorithm="Eppstein", value_only=True) + 4 + + TESTS: + + If ``algorithm`` is not set to one of the supported algorithms, an + exception is raised:: + + sage: G = BipartiteGraph(graphs.CompleteBipartiteGraph(4,5)) + sage: G.matching(algorithm="somethingdifferent") + Traceback (most recent call last): + ... + ValueError: algorithm must be "Hopcroft-Karp", "Eppstein", "Edmonds" or "LP" + + Maximum matching in a weighted bipartite graph:: + + sage: G = graphs.CycleGraph(4) + sage: B = BipartiteGraph([(u,v,2) for u,v in G.edges(labels=0)]) + sage: B.matching(use_edge_labels=True) + [(0, 3, 2), (1, 2, 2)] + sage: B.matching(use_edge_labels=True, value_only=True) + 4 + sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Edmonds') + 4 + sage: B.matching(use_edge_labels=True, value_only=True, algorithm='LP') + 4.0 + sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Eppstein') + Traceback (most recent call last): + ... + ValueError: use_edge_labels can not be used with "Hopcroft-Karp" or "Eppstein" + sage: B.matching(use_edge_labels=True, value_only=True, algorithm='Hopcroft-Karp') + Traceback (most recent call last): + ... + ValueError: use_edge_labels can not be used with "Hopcroft-Karp" or "Eppstein" + sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Hopcroft-Karp') + 2 + sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Eppstein') + 2 + sage: B.matching(use_edge_labels=False, value_only=True, algorithm='Edmonds') + 2 + sage: B.matching(use_edge_labels=False, value_only=True, algorithm='LP') + 2 + """ + self._scream_if_not_simple() + + if algorithm is None: + algorithm = "Edmonds" if use_edge_labels else "Hopcroft-Karp" + + if algorithm == "Hopcroft-Karp" or algorithm == "Eppstein": + if use_edge_labels: + raise ValueError('use_edge_labels can not be used with ' + + '"Hopcroft-Karp" or "Eppstein"') + import networkx + #this is necessary to call the methods for bipartite matchings + g = self.networkx_graph() + if algorithm == "Hopcroft-Karp": + d = networkx.bipartite.hopcroft_karp_matching(g) + else: + d = networkx.bipartite.eppstein_matching(g) + if value_only: + return Integer(len(d) // 2) + else: + return [(u, v, self.edge_label(u, v)) + for u, v in iteritems(d) if u < v] + elif algorithm == "Edmonds" or algorithm == "LP": + return Graph.matching(self, value_only=value_only, + algorithm=algorithm, + use_edge_labels=use_edge_labels, + solver=solver, verbose=verbose) + else: + raise ValueError('algorithm must be "Hopcroft-Karp", ' + + '"Eppstein", "Edmonds" or "LP"') diff --git a/src/sage/graphs/generators/families.py b/src/sage/graphs/generators/families.py index 39213551145..23f240f2c08 100644 --- a/src/sage/graphs/generators/families.py +++ b/src/sage/graphs/generators/families.py @@ -2501,6 +2501,99 @@ def WheelGraph(n): G = networkx.wheel_graph(n) return Graph(G, pos=pos_dict, name="Wheel graph") +def WindmillGraph(k, n): + r""" + Return the Windmill graph `Wd(k, n)`. + + The windmill graph `Wd(k, n)` is an undirected graph constructed for `k \geq + 2` and `n \geq 2` by joining `n` copies of the complete graph `K_k` at a + shared vertex. It has `(k-1)n+1` vertices and `nk(k-1)/2` edges, girth 3 (if + `k > 2`), radius 1 and diameter 2. It has vertex connectivity 1 because its + central vertex is an articulation point; however, like the complete graphs + from which it is formed, it is `(k-1)`-edge-connected. It is trivially + perfect and a block graph. + + .. SEEALSO:: + + - :wikipedia:`Windmill_graph` + - :meth:`GraphGenerators.StarGraph` + - :meth:`GraphGenerators.FriendshipGraph` + + EXAMPLES: + + The Windmill graph `Wd(2, n)` is a star graph:: + + sage: n = 5 + sage: W = graphs.WindmillGraph(2, n) + sage: W.is_isomorphic( graphs.StarGraph(n) ) + True + + The Windmill graph `Wd(3, n)` is the Friendship graph `F_n`:: + + sage: n = 5 + sage: W = graphs.WindmillGraph(3, n) + sage: W.is_isomorphic( graphs.FriendshipGraph(n) ) + True + + The Windmill graph `Wd(3, 2)` is the Butterfly graph:: + + sage: W = graphs.WindmillGraph(3, 2) + sage: W.is_isomorphic( graphs.ButterflyGraph() ) + True + + The Windmill graph `Wd(k, n)` has chromatic number `k`:: + + sage: n,k = 5,6 + sage: W = graphs.WindmillGraph(k, n) + sage: W.chromatic_number() == k + True + + TESTS: + + Giving too small parameters:: + + sage: graphs.WindmillGraph(1, 2) + Traceback (most recent call last): + ... + ValueError: parameters k and n must be >= 2 + sage: graphs.WindmillGraph(2, 1) + Traceback (most recent call last): + ... + ValueError: parameters k and n must be >= 2 + """ + if k < 2 or n < 2: + raise ValueError('parameters k and n must be >= 2') + + if k == 2: + from sage.graphs.generators.basic import StarGraph + G = StarGraph(n) + else: + sector = 2*pi/n + slide = 1/sin(sector/4) + + pos_dict = {} + for i in range(0,k): + x = float(cos(i*pi/(k-2))) + y = float(sin(i*pi/(k-2))) + slide + pos_dict[i] = (x,y) + + G = Graph() + pos = {0: [0, 0]} + for i in range(n): + V = list( range(i*(k-1)+1, (i+1)*(k-1)+1) ) + G.add_clique([0]+V) + for j,v in enumerate(V): + x,y = pos_dict[j] + xv = x*cos(i*sector) - y*sin(i*sector) + yv = x*sin(i*sector) + y*cos(i*sector) + pos[v] = [xv, yv] + + G.set_pos(pos) + + G.name("Windmill graph Wd({}, {})".format(k, n)) + return G + + def trees(vertices): r""" Returns a generator of the distinct trees on a fixed number of vertices. diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 1553f5884fa..7f3f41cd80f 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -22,10 +22,14 @@ :meth:`~GenericGraph.distance_matrix` | Return the distance matrix of the (strongly) connected (di)graph :meth:`~GenericGraph.weighted_adjacency_matrix` | Return the weighted adjacency matrix of the graph :meth:`~GenericGraph.kirchhoff_matrix` | Return the Kirchhoff matrix (a.k.a. the Laplacian) of the graph. - :meth:`~GenericGraph.has_loops` | Return whether there are loops in the (di)graph. - :meth:`~GenericGraph.allows_loops` | Return whether loops are permitted in the (di)graph. - :meth:`~GenericGraph.allow_loops` | Change whether loops are permitted in the (di)graph. - :meth:`~GenericGraph.loops` | Return any loops in the (di)graph. + :meth:`~GenericGraph.has_loops` | Return whether there are loops in the (di)graph + :meth:`~GenericGraph.allows_loops` | Return whether loops are permitted in the (di)graph + :meth:`~GenericGraph.allow_loops` | Change whether loops are permitted in the (di)graph + :meth:`~GenericGraph.loops` | Return a list of all loops in the (di)graph + :meth:`~GenericGraph.loop_edges` | Return a list of all loops in the (di)graph + :meth:`~GenericGraph.number_of_loops` | Return the number of edges that are loops + :meth:`~GenericGraph.loop_vertices` | Return a list of vertices with loops + :meth:`~GenericGraph.remove_loops` | Remove loops on vertices in vertices. If vertices is None, removes all loops. :meth:`~GenericGraph.has_multiple_edges` | Return whether there are multiple edges in the (di)graph. :meth:`~GenericGraph.allows_multiple_edges` | Return whether multiple edges are permitted in the (di)graph. :meth:`~GenericGraph.allow_multiple_edges` | Change whether multiple edges are permitted in the (di)graph. @@ -51,7 +55,6 @@ :meth:`~GenericGraph.set_vertex` | Associate an arbitrary object with a vertex. :meth:`~GenericGraph.get_vertex` | Retrieve the object associated with a given vertex. :meth:`~GenericGraph.get_vertices` | Return a dictionary of the objects associated to each vertex. - :meth:`~GenericGraph.loop_vertices` | Return a list of vertices with loops. :meth:`~GenericGraph.vertex_iterator` | Return an iterator over the vertices. :meth:`~GenericGraph.neighbor_iterator` | Return an iterator over neighbors of vertex. :meth:`~GenericGraph.vertices` | Return a list of the vertices. @@ -73,9 +76,6 @@ :meth:`~GenericGraph.edge_label` | Return the label of an edge. :meth:`~GenericGraph.edge_labels` | Return a list of edge labels. :meth:`~GenericGraph.remove_multiple_edges` | Remove all multiple edges, retaining one edge for each. - :meth:`~GenericGraph.remove_loops` | Remove loops on vertices in vertices. If vertices is None, removes all loops. - :meth:`~GenericGraph.loop_edges` | Returns a list of all loops in the graph. - :meth:`~GenericGraph.number_of_loops` | Return the number of edges that are loops. :meth:`~GenericGraph.clear` | Empty the graph of vertices and edges and removes name, associated objects, and position information. :meth:`~GenericGraph.degree` | Return the degree (in + out for digraphs) of a vertex or of vertices. :meth:`~GenericGraph.average_degree` | Return the average degree of the graph. @@ -979,9 +979,9 @@ def copy(self, weighted=None, implementation='c_graph', data_structure=None, sage: g is g.copy(data_structure='static_sparse') is g.copy(immutable=True) True - If a graph pretends to be immutable, but does not use the static sparse - backend, then the copy is not identic with the graph, even though it is - considered to be hashable:: + If a graph pretends to be immutable, but does not use the + static sparse backend, then the copy is not identical with the + graph, even though it is considered to be hashable:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension=True, facade = False) sage: H = P.hasse_diagram() @@ -2278,7 +2278,7 @@ def _check_embedding_validity(self, embedding=None, boolean=True): def has_loops(self): """ - Returns whether there are loops in the (di)graph. + Return whether there are loops in the (di)graph EXAMPLES:: @@ -2326,7 +2326,7 @@ def has_loops(self): def allows_loops(self): """ - Returns whether loops are permitted in the (di)graph. + Return whether loops are permitted in the (di)graph EXAMPLES:: @@ -2370,7 +2370,7 @@ def allows_loops(self): def allow_loops(self, new, check=True): """ - Changes whether loops are permitted in the (di)graph. + Change whether loops are permitted in the (di)graph INPUT: @@ -2418,13 +2418,14 @@ def allow_loops(self, new, check=True): self.remove_loops() self._backend.loops(new) - def loops(self, labels=True): + def loop_edges(self, labels=True): """ - Returns any loops in the (di)graph. + Return a list of all loops in the (di)graph INPUT: - - ``labels`` -- whether returned edges have labels ((u,v,l)) or not ((u,v)). + - ``labels`` -- whether returned edges have labels (``(u,v,l)``) or not + (``(u,v)``) EXAMPLES:: @@ -2434,17 +2435,22 @@ def loops(self, labels=True): False sage: G.allows_loops() True - sage: G.add_edge((0,0)) + sage: G.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) + sage: G.loop_edges() + [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] + sage: G.loop_edges(labels=False) + [(0, 0), (1, 1), (2, 2), (3, 3)] + sage: G.allows_loops() + True sage: G.has_loops() True - sage: G.loops() - [(0, 0, None)] - sage: G.allow_loops(False); G - Graph on 1 vertex + sage: G.allow_loops(False) sage: G.has_loops() False - sage: G.edges() + sage: G.loop_edges() [] + sage: G.edges() + [(2, 3, None)] sage: D = DiGraph(loops=True); D Looped digraph on 0 vertices @@ -2468,11 +2474,74 @@ def loops(self, labels=True): sage: G.loops() [] + :: + + sage: D = DiGraph(4, loops=True) + sage: D.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) + sage: D.loop_edges() + [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] + + :: + + sage: G = Graph(4, loops=True, multiedges=True, sparse=True) + sage: G.add_edges([(i,i) for i in range(4)]) + sage: G.loop_edges() + [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] + sage: G.add_edges([(0, 0), (1, 1)]) + sage: G.loop_edges(labels=False) + [(0, 0), (0, 0), (1, 1), (1, 1), (2, 2), (3, 3)] """ - loops = [] - for v in self: - loops += self.edge_boundary([v], [v], labels) - return loops + if self.allows_multiple_edges(): + if labels: + return [(v,v,l) for v in self.loop_vertices() for l in self.edge_label(v,v)] + else: + return [(v,v) for v in self.loop_vertices() for l in self.edge_label(v,v)] + elif labels: + return [(v,v,self.edge_label(v,v)) for v in self.loop_vertices()] + else: + return [(v,v) for v in self.loop_vertices()] + + # As discussed in trac 22911, we make method loops an alias for loop_edges + loops = loop_edges + + def number_of_loops(self): + """ + Return the number of edges that are loops + + EXAMPLES:: + + sage: G = Graph(4, loops=True) + sage: G.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) + sage: G.edges(labels=False) + [(0, 0), (1, 1), (2, 2), (2, 3), (3, 3)] + sage: G.number_of_loops() + 4 + + :: + + sage: D = DiGraph(4, loops=True) + sage: D.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) + sage: D.edges(labels=False) + [(0, 0), (1, 1), (2, 2), (2, 3), (3, 3)] + sage: D.number_of_loops() + 4 + """ + return len(self.loop_edges()) + + def loop_vertices(self): + """ + Return a list of vertices with loops + + EXAMPLES:: + + sage: G = Graph({0 : [0], 1: [1,2,3], 2: [3]}, loops=True) + sage: G.loop_vertices() + [0, 1] + """ + if self.allows_loops(): + return [v for v in self if self.has_edge(v,v)] + else: + return [] def has_multiple_edges(self, to_undirected=False): """ @@ -9891,21 +9960,6 @@ def get_vertices(self, verts=None): return output - def loop_vertices(self): - """ - Returns a list of vertices with loops. - - EXAMPLES:: - - sage: G = Graph({0 : [0], 1: [1,2,3], 2: [3]}, loops=True) - sage: G.loop_vertices() - [0, 1] - """ - if self.allows_loops(): - return [v for v in self if self.has_edge(v,v)] - else: - return [] - def vertex_iterator(self, vertices=None): """ Returns an iterator over the given vertices. @@ -11077,60 +11131,6 @@ def remove_loops(self, vertices=None): if self.has_edge(v,v): self.delete_multiedge(v,v) - def loop_edges(self): - """ - Returns a list of all loops in the graph. - - EXAMPLES:: - - sage: G = Graph(4, loops=True) - sage: G.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) - sage: G.loop_edges() - [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] - - :: - - sage: D = DiGraph(4, loops=True) - sage: D.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) - sage: D.loop_edges() - [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] - - :: - - sage: G = Graph(4, loops=True, multiedges=True, sparse=True) - sage: G.add_edges([(i,i) for i in range(4)]) - sage: G.loop_edges() - [(0, 0, None), (1, 1, None), (2, 2, None), (3, 3, None)] - """ - if self.allows_multiple_edges(): - return [(v,v,l) for v in self.loop_vertices() for l in self.edge_label(v,v)] - else: - return [(v,v,self.edge_label(v,v)) for v in self.loop_vertices()] - - def number_of_loops(self): - """ - Returns the number of edges that are loops. - - EXAMPLES:: - - sage: G = Graph(4, loops=True) - sage: G.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) - sage: G.edges(labels=False) - [(0, 0), (1, 1), (2, 2), (2, 3), (3, 3)] - sage: G.number_of_loops() - 4 - - :: - - sage: D = DiGraph(4, loops=True) - sage: D.add_edges( [ (0,0), (1,1), (2,2), (3,3), (2,3) ] ) - sage: D.edges(labels=False) - [(0, 0), (1, 1), (2, 2), (2, 3), (3, 3)] - sage: D.number_of_loops() - 4 - """ - return len(self.loop_edges()) - ### Modifications def clear(self): @@ -12510,7 +12510,7 @@ def is_chordal(self, certificate = False, algorithm = "B"): if not hole is None: # There was a bug there once, so it's better to check the - # answer is valid, especally when it is so cheap ;-) + # answer is valid, especially when it is so cheap ;-) if hole.order() <= 3 or not hole.is_regular(k=2): raise RuntimeError("the graph is not chordal, and something went wrong in the computation of the certificate. Please report this bug, providing the graph if possible!") @@ -15567,7 +15567,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, Computes the length of a shortest path from u to any other vertex. Returns a dictionary of shortest path lengths keyed by targets, - escluding all vertices that are not reachable from u. + excluding all vertices that are not reachable from u. For more information on the input variables and more examples, we refer to :meth:`~GenericGraph.shortest_paths` @@ -16723,7 +16723,8 @@ def add_clique(self, vertices, loops=False): INPUT: - - ``vertices`` -- a list of vertices for the clique to be added. + - ``vertices`` -- an iterable container of vertices for the clique to be + added, e.g. a list, set, graph, etc. - ``loops`` -- (boolean) whether to add loops or not, i.e., edges from a vertex to itself. Possible only if the (di)graph allows loops. @@ -16748,14 +16749,39 @@ def add_clique(self, vertices, loops=False): False sage: D.is_isomorphic(digraphs.Complete(4, loops=False)) True + + TESTS: + + Using different kinds of iterable container of vertices, :trac:`22906`:: + + sage: G = Graph(4) + sage: G.add_clique(G) + sage: G.is_clique() + True + sage: G = Graph() + sage: G.add_clique(set(range(4))) + sage: G.is_clique() + True + sage: G = Graph() + sage: G.add_clique({i:(i, i+1) for i in range(4)}) + sage: G.is_clique() + True + sage: G.vertices() + [0, 1, 2, 3] + sage: D = DiGraph(4, loops=True) + sage: D.add_clique(range(4), loops=True) + sage: D.is_clique(directed_clique=True) + True """ + import itertools if vertices: - n = len(vertices) - self.add_edges((vertices[i],vertices[j]) for i in range(n-1) for j in range(i+1,n)) + vertices = list(vertices) if self.is_directed(): - self.add_edges((vertices[j],vertices[i]) for i in range(n-1) for j in range(i+1,n)) + self.add_edges(itertools.permutations(vertices, 2)) + else: + self.add_edges(itertools.combinations(vertices, 2)) if loops and self.allows_loops(): - self.add_edges((vertices[i],vertices[i]) for i in range(n)) + self.add_edges((u, u) for u in vertices) def add_cycle(self, vertices): """ @@ -19507,6 +19533,13 @@ def graphviz_string(self, **options): node_4 -- node_5; } + Check that :trac:`22950` is fixed:: + + sage: D = DiGraph({1: [2]}) + sage: D.graphviz_string(edge_colors={'blue': [[1,2]]}) + 'digraph {\n node_0 [label="1"];\n node_1 [label="2"];\n\n + node_0 -> node_1 [color = "blue"];\n}' + REFERENCES: .. [dotspec] http://www.graphviz.org/doc/info/lang.html @@ -19541,7 +19574,7 @@ def graphviz_string(self, **options): color_by_edge = {} for color in options['edge_colors'].keys(): for edge in options['edge_colors'][color]: - assert isinstance(edge, tuple) and len(edge) >= 2 and len(edge) <= 3,\ + assert isinstance(edge, (list, tuple)) and len(edge) >= 2 and len(edge) <= 3,\ "%s is not a valid format for edge"%(edge) u = edge[0] v = edge[1] diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index af69556c94b..6847af92c46 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -1249,7 +1249,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, member[u] = True member[v] = True - #Initialize all the variables neccesary to start iterating + #Initialize all the variables necessary to start iterating cdef bint done = False cdef long counter = 0 cdef long bigcount = 0 diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 0669fc30693..2f1193fda9e 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -3762,7 +3762,7 @@ def orientations(self, implementation='c_graph', data_structure=None, sparse=Non .. WARNING:: - This always considers mutliple edges of graphs as + This always considers multiple edges of graphs as distinguishable, and hence, may have repeated digraphs. EXAMPLES:: @@ -4376,7 +4376,11 @@ def matching(self, value_only=False, algorithm="Edmonds", """ self._scream_if_not_simple(allow_loops=True) from sage.rings.real_mpfr import RR - weight = lambda x: x if x in RR else 1 + def weight(x): + if x in RR: + return x + else: + return 1 if algorithm == "Edmonds": import networkx @@ -4404,21 +4408,21 @@ def matching(self, value_only=False, algorithm="Edmonds", # returns the weight of an edge considering it may not be # weighted ... p = MixedIntegerLinearProgram(maximization=True, solver=solver) - b = p.new_variable(binary = True) + b = p.new_variable(binary=True) if use_edge_labels: p.set_objective( - p.sum(weight(w) * b[min(u, v),max(u, v)] - for u, v, w in g.edge_iterator())) + p.sum(weight(w) * b[min(u, v), max(u, v)] + for u, v, w in g.edge_iterator())) else: p.set_objective( - p.sum(b[min(u, v),max(u, v)] - for u, v in g.edge_iterator(labels=False))) + p.sum(b[min(u, v), max(u, v)] + for u, v in g.edge_iterator(labels=False))) # for any vertex v, there is at most one edge incident to v in # the maximum matching for v in g.vertex_iterator(): p.add_constraint( p.sum(b[min(u, v),max(u, v)] - for u in g.neighbors(v)), max=1) + for u in g.neighbors(v)), max=1) if value_only: if use_edge_labels: return p.solve(objective_only=True, log=verbose) @@ -7580,10 +7584,40 @@ def perfect_matchings(self, labels=False): yield [e] + mat @doc_index("Leftovers") - def has_perfect_matching(self): + def has_perfect_matching(self, algorithm="Edmonds", solver=None, verbose=0): r""" Return whether this graph has a perfect matching. + INPUT: + + - ``algorithm`` -- string (default: ``"Edmonds"``) + + - ``"Edmonds"`` uses Edmonds' algorithm as implemented in NetworkX to + find a matching of maximal cardinality, then check whether this + cardinality is half the number of vertices of the graph. + + - ``"LP_matching"`` uses uses a Linear Program to find a matching of + maximal cardinality, then check whether this cardinality is half the + number of vertices of the graph. + + - ``"LP"`` uses a Linear Program formulation of the perfect matching + problem: put a binary variable ``b[e]`` on each edge ``e``, and for + each vertex ``v``, require that the sum of the values of the edges + incident to ``v`` is 1. + + - ``solver`` -- (default: ``None``) specify a Linear Program (LP) + solver to be used; if set to ``None``, the default one is used + + - ``verbose`` -- integer (default: ``0``); sets the level of verbosity: + set to 0 by default, which means quiet (only useful when + ``algorithm == "LP_matching"`` or ``algorithm == "LP"``) + + For more information on LP solvers and which default solver is + used, see the method + :meth:`solve ` + of the class :class:`MixedIntegerLinearProgram + `. + OUTPUT: A boolean. @@ -7596,12 +7630,60 @@ def has_perfect_matching(self): True sage: graphs.WheelGraph(5).has_perfect_matching() False + sage: graphs.PetersenGraph().has_perfect_matching(algorithm="LP_matching") + True + sage: graphs.WheelGraph(6).has_perfect_matching(algorithm="LP_matching") + True + sage: graphs.WheelGraph(5).has_perfect_matching(algorithm="LP_matching") + False + sage: graphs.PetersenGraph().has_perfect_matching(algorithm="LP_matching") + True + sage: graphs.WheelGraph(6).has_perfect_matching(algorithm="LP_matching") + True + sage: graphs.WheelGraph(5).has_perfect_matching(algorithm="LP_matching") + False + + TESTS:: + + sage: G = graphs.EmptyGraph() + sage: all(G.has_perfect_matching(algorithm=algo) for algo in ['Edmonds', 'LP_matching', 'LP']) + True + + Be careful with isolated vertices:: + + sage: G = graphs.PetersenGraph() + sage: G.add_vertex(11) + sage: any(G.has_perfect_matching(algorithm=algo) for algo in ['Edmonds', 'LP_matching', 'LP']) + False """ - try: - next(self.perfect_matchings()) - except StopIteration: + if self.order() % 2: return False - return True + if algorithm == "Edmonds": + return len(self) == 2*self.matching(value_only=True, + use_edge_labels=False, + algorithm="Edmonds") + elif algorithm == "LP_matching": + return len(self) == 2*self.matching(value_only=True, + use_edge_labels=False, + algorithm="LP", + solver=solver, + verbose=verbose) + elif algorithm == "LP": + from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException + p = MixedIntegerLinearProgram(solver=solver) + b = p.new_variable(binary = True) + for v in self: + edges = self.edges_incident(v, labels=False) + if not edges: + return False + p.add_constraint(p.sum(b[e] for e in edges) == 1) + try: + p.solve(log=verbose) + return True + except MIPSolverException: + return False + else: + raise ValueError('algorithm must be set to "Edmonds", "LP_matching" or "LP"') # Aliases to functions defined in Cython modules diff --git a/src/sage/graphs/graph_editor.py b/src/sage/graphs/graph_editor.py index 2109406bb5e..b53178ade94 100644 --- a/src/sage/graphs/graph_editor.py +++ b/src/sage/graphs/graph_editor.py @@ -22,7 +22,7 @@ from sage.misc.html import html import sagenb.notebook.interact -from sagenb.misc.support import EMBEDDED_MODE +from sage.server.support import EMBEDDED_MODE def graph_to_js(g): diff --git a/src/sage/graphs/graph_generators.py b/src/sage/graphs/graph_generators.py index 85ae7c71b93..161234dbaa5 100644 --- a/src/sage/graphs/graph_generators.py +++ b/src/sage/graphs/graph_generators.py @@ -241,7 +241,8 @@ def __append_to_doc(methods): "trees", "triangulations", "TuranGraph", - "WheelGraph"]) + "WheelGraph", + "WindmillGraph"]) __doc__ += """ @@ -925,7 +926,7 @@ def cospectral_graphs(self, vertices, matrix_function=lambda g: g.adjacency_matr OUTPUT: A list of lists of graphs. Each sublist will be a list of - cospectral graphs (lists of cadinality 1 being omitted). + cospectral graphs (lists of cardinality 1 being omitted). .. SEEALSO:: @@ -2027,6 +2028,7 @@ def quadrangulations(self, order, minimum_degree=None, minimum_connectivity=None trees = staticmethod(sage.graphs.generators.families.trees) TuranGraph = staticmethod(sage.graphs.generators.families.TuranGraph) WheelGraph = staticmethod(sage.graphs.generators.families.WheelGraph) + WindmillGraph = staticmethod(sage.graphs.generators.families.WindmillGraph) ########################################################################### # Graphs from classical geometries over `F_q` diff --git a/src/sage/graphs/graph_input.py b/src/sage/graphs/graph_input.py index bf29d0f04aa..9202096441e 100644 --- a/src/sage/graphs/graph_input.py +++ b/src/sage/graphs/graph_input.py @@ -369,6 +369,15 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted sage: from_oriented_incidence_matrix(g, digraphs.Circuit(10).incidence_matrix()) sage: g.is_isomorphic(digraphs.Circuit(10)) True + + TESTS: + + Fix bug reported in :trac:`22985`:: + + sage: DiGraph(matrix ([[1,0,0,1],[0,0,1,1],[0,0,1,1]]).transpose()) + Traceback (most recent call last): + ... + ValueError: each column represents an edge: -1 goes to 1 """ from sage.matrix.matrix import is_Matrix assert is_Matrix(M) @@ -380,8 +389,7 @@ def from_oriented_incidence_matrix(G, M, loops=False, multiedges=False, weighted raise ValueError("There must be two nonzero entries (-1 & 1) per column.") L = sorted(set(c.list())) if L != [-1,0,1]: - msg += "Each column represents an edge: -1 goes to 1." - raise ValueError(msg) + raise ValueError("each column represents an edge: -1 goes to 1") if c[NZ[0]] == -1: positions.append(tuple(NZ)) else: diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 65546236528..a1485095ef7 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -122,7 +122,7 @@ def element(self): sage: T = EllipticCurve('65a').torsion_subgroup().gen(0) sage: T; type(T) (0 : 0 : 1) - + sage: T.element(); type(T.element()) (0 : 0 : 1) diff --git a/src/sage/groups/affine_gps/catalog.py b/src/sage/groups/affine_gps/catalog.py index 3ff92fe86a5..d78a1bdb2c4 100644 --- a/src/sage/groups/affine_gps/catalog.py +++ b/src/sage/groups/affine_gps/catalog.py @@ -1,6 +1,11 @@ r""" Type ``groups.affine.`` to access examples of groups implemented as affine groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.affine) + False """ from __future__ import absolute_import @@ -15,3 +20,6 @@ from .affine_group import AffineGroup as Affine from .euclidean_group import EuclideanGroup as Euclidean + +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index bc101a87edf..78622621a2f 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -80,7 +80,7 @@ class AffineGroupElement(MultiplicativeGroupElement): sage: G = AffineGroup(2, GF(3)) sage: g = G.random_element() sage: type(g) - + sage: G(g.matrix()) == g True sage: G(2) diff --git a/src/sage/groups/finitely_presented_catalog.py b/src/sage/groups/finitely_presented_catalog.py index acb1609e01a..dc934864556 100644 --- a/src/sage/groups/finitely_presented_catalog.py +++ b/src/sage/groups/finitely_presented_catalog.py @@ -2,6 +2,11 @@ Type ``groups.presentation.`` to access examples of groups implemented as finite presentations (quotients of free groups). + +TESTS:: + + sage: 'absolute_import' in dir(groups.presentation) + False """ from __future__ import absolute_import @@ -24,4 +29,5 @@ from .finitely_presented_named import AlternatingPresentation as Alternating from .finitely_presented_named import BinaryDihedralPresentation as BinaryDihedral - +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index 69c5e13840d..f31236bf74d 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -146,7 +146,7 @@ class ParentLibGAP(SageObject): Return whether the group was defined as a subgroup of a bigger group. - You can access the contaning group with :meth:`ambient`. + You can access the containing group with :meth:`ambient`. OUTPUT: diff --git a/src/sage/groups/matrix_gps/catalog.py b/src/sage/groups/matrix_gps/catalog.py index 525cb0248af..9febc470765 100644 --- a/src/sage/groups/matrix_gps/catalog.py +++ b/src/sage/groups/matrix_gps/catalog.py @@ -3,6 +3,11 @@ Type ``groups.matrix.`` to access examples of groups implemented as permutation groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.matrix) + False """ from __future__ import absolute_import @@ -19,3 +24,5 @@ from .all import QuaternionMatrixGroupGF3 as QuaternionGF3 from sage.groups.matrix_gps.binary_dihedral import BinaryDihedralGroup as BinaryDihedral +# We don't want this to appear tab completion +del absolute_import diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index b6e868153f7..d40d7b5af81 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -819,7 +819,7 @@ def molien_series(self, xi=None, return_series=True, prec=20, variable='t'): \frac{1}{|G|}\sum_{g \in G} \frac{\xi(g)}{\text{det}(I-tg)}, - where `I` is the indentity matrix and `t` an indeterminant. + where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index 7a8a0bebc85..237e37b3462 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -69,8 +69,8 @@ def finite_field_sqrt(ring): if not is_FiniteField(ring): raise ValueError('not a finite field') q, rem = ring.cardinality().sqrtrem() - if rem != 0: - raise ValueError('cardinatity not a square') + if rem: + raise ValueError('cardinality not a square') return q @@ -83,7 +83,7 @@ def GU(n, R, var='a'): Return the general unitary group. The general unitary group `GU( d, R )` consists of all `d \times - d` matrices that preserve a nondegenerate sequilinear form over + d` matrices that preserve a nondegenerate sesquilinear form over the ring `R`. .. note:: @@ -160,7 +160,7 @@ def GU(n, R, var='a'): def SU(n, R, var='a'): """ The special unitary group `SU( d, R )` consists of all `d \times d` - matrices that preserve a nondegenerate sequilinear form over the + matrices that preserve a nondegenerate sesquilinear form over the ring `R` and have determinant one. .. note:: diff --git a/src/sage/groups/old.pyx b/src/sage/groups/old.pyx index 31e77750763..07c524f3fbb 100644 --- a/src/sage/groups/old.pyx +++ b/src/sage/groups/old.pyx @@ -23,7 +23,7 @@ Base class for all groups import random -from sage.rings.infinity import infinity +from sage.rings.infinity import infinity import sage.rings.integer_ring cdef class Group(sage.structure.parent_gens.ParentWithGens): 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 01202b31b82..9d78b5def80 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx @@ -796,7 +796,7 @@ cdef class PartitionRefinement_generic: - if the inner group has changed -> sets ``inner_group_changed`` to True - if the partition has changed -> sets ``changed_partition`` to True - The string ``refine_name`` is only neccessary for printing within the + The string ``refine_name`` is only necessary for printing within the latex output (if activated). """ cdef tuple res = PS_refinement(self._part, self._refine_vals_scratch, best, begin, end, @@ -826,7 +826,7 @@ cdef class PartitionRefinement_generic: cdef void _leaf_computations(self): r""" - All neccessary computations which have to be performed in a leaf. + All necessary computations which have to be performed in a leaf. There are to possibilities depending on the flag ``self._is_candidate_initialized``: diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index c2dea51a6b5..4e91c4b23bf 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -45,22 +45,27 @@ degree. (1,30)(2,29)(3,28)(4,27)(5,26)(6,25)(7,24)(8,23)(9,22)(10,21)(11,20)(12,19)(13,18)(14,17)(15,16) """ -########################################################################### -# Copyright (C) 2006 William Stein -# Copyright (C) 2006 David Joyner +#***************************************************************************** +# Copyright (C) 2006 William Stein +# Copyright (C) 2006 David Joyner # -# 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 __future__ import print_function - +#***************************************************************************** + +from __future__ import absolute_import, print_function + import random import sage.groups.old as group -include "sage/ext/stdsage.pxi" +from cysignals.memory cimport sig_malloc, sig_realloc, sig_free from cpython.list cimport * +from sage.ext.stdsage cimport HAS_DICTIONARY from sage.rings.all import ZZ, Integer from sage.rings.polynomial.polynomial_element import is_Polynomial from sage.rings.polynomial.multi_polynomial import is_MPolynomial diff --git a/src/sage/groups/perm_gps/permutation_groups_catalog.py b/src/sage/groups/perm_gps/permutation_groups_catalog.py index b9ad040de7e..5c015376b49 100644 --- a/src/sage/groups/perm_gps/permutation_groups_catalog.py +++ b/src/sage/groups/perm_gps/permutation_groups_catalog.py @@ -3,6 +3,11 @@ Type ``groups.permutation.`` to access examples of groups implemented as permutation groups. + +TESTS:: + + sage: 'absolute_import' in dir(groups.permutation) + False """ from __future__ import absolute_import @@ -29,3 +34,6 @@ from .permgroup_named import (PGL, PSL, PSp,PSU,PGU,) from .permgroup_named import TransitiveGroup as Transitive from .cubegroup import CubeGroup as RubiksCube + +# We don't want this to appear in tab completion +del absolute_import diff --git a/src/sage/groups/perm_gps/symgp_conjugacy_class.py b/src/sage/groups/perm_gps/symgp_conjugacy_class.py index e5272d56ffe..65673006106 100644 --- a/src/sage/groups/perm_gps/symgp_conjugacy_class.py +++ b/src/sage/groups/perm_gps/symgp_conjugacy_class.py @@ -16,6 +16,7 @@ from sage.sets.set import Set import itertools + class SymmetricGroupConjugacyClassMixin(object): r""" Mixin class which contains methods for conjugacy classes of @@ -51,7 +52,7 @@ def _repr_(self): """ return "Conjugacy class of cycle type %s in %s"%(self._part, self._parent) - def __cmp__(self, other): + def __eq__(self, other): r""" Comparison of conjugacy classes is done by comparing the defining cycle types. @@ -65,10 +66,24 @@ def __cmp__(self, other): sage: C == Cp True """ - c = cmp(type(self), type(other)) - if c: - return c - return cmp(self._part, other._part) + if not isinstance(other, SymmetricGroupConjugacyClassMixin): + return False + return self._part == other._part + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + + sage: G = SymmetricGroup(5) + sage: g = G([(1,3), (2,4,5)]) + sage: C = G.conjugacy_class(Partition([3,2])) + sage: Cp = G.conjugacy_class(g) + sage: C != Cp + False + """ + return not (self == other) def partition(self): """ @@ -82,6 +97,7 @@ def partition(self): """ return self._part + class SymmetricGroupConjugacyClass(SymmetricGroupConjugacyClassMixin, ConjugacyClassGAP): """ A conjugacy class of the symmetric group. diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index e744a1cf6c6..b3f434ba94b 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -4,7 +4,7 @@ A *right-angled Artin group* (often abbreviated as RAAG) is a group which has a presentation whose only relations are commutators between generators. These are also known as graph groups, since they are (uniquely) encoded by -(simple) graphs, or partially commmutative groups. +(simple) graphs, or partially commutative groups. AUTHORS: diff --git a/src/sage/homology/chains.py b/src/sage/homology/chains.py index b1ab2a071ac..f18f067887c 100644 --- a/src/sage/homology/chains.py +++ b/src/sage/homology/chains.py @@ -21,8 +21,7 @@ from __future__ import absolute_import -from sage.combinat.free_module import CombinatorialFreeModule, \ - CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.rings.integer_ring import ZZ from sage.structure.element import coercion_model @@ -215,7 +214,7 @@ def chain_complex(self): cochain=False, ) - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): def to_complex(self): """ @@ -448,7 +447,7 @@ def cochain_complex(self): cochain=True, ) - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): def to_complex(self): """ diff --git a/src/sage/homology/examples.py b/src/sage/homology/examples.py index 958c76d9371..760669aa3fb 100644 --- a/src/sage/homology/examples.py +++ b/src/sage/homology/examples.py @@ -699,7 +699,7 @@ def RealProjectiveSpace(n): sends any subset `U` to its complement. One can show that modding out by this action results in a triangulation for `\Bold{R}P^n`. To find the facets in this triangulation, find - the facets in `S`. These are indentified in pairs to form + the facets in `S`. These are identified in pairs to form `\Bold{R}P^n`, so choose a representative from each pair: for each facet in `S`, replace any vertex in `S` containing 0 with its complement. diff --git a/src/sage/homology/hochschild_complex.py b/src/sage/homology/hochschild_complex.py index 7d71d01acb9..bd5f04b703a 100644 --- a/src/sage/homology/hochschild_complex.py +++ b/src/sage/homology/hochschild_complex.py @@ -14,13 +14,17 @@ from sage.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.category_object import CategoryObject +from sage.structure.parent import Parent +from sage.structure.element import ModuleElement, parent +from sage.structure.sage_object import richcmp 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 +from sage.homology.chain_complex import ChainComplex, Chain_class -class HochschildComplex(UniqueRepresentation, CategoryObject): +from sage.misc.superseded import deprecated_function_alias + +class HochschildComplex(UniqueRepresentation, Parent): r""" The Hochschild complex. @@ -94,14 +98,12 @@ def __init__(self, A, M): sage: H in ChainComplexes(QQ) True - Some methods required by the category are not implemented:: - - sage: TestSuite(H).run() # known bug (#21386) + sage: TestSuite(H).run() """ self._A = A self._M = M - CategoryObject.__init__(self, base=A.base_ring(), - category=ChainComplexes(A.base_ring())) + Parent.__init__(self, base=A.base_ring(), + category=ChainComplexes(A.base_ring())) def _repr_(self): """ @@ -162,21 +164,21 @@ def coefficients(self): """ return self._M - def free_module(self, d): + def module(self, d): """ - Return the free module in degree ``d``. + Return the 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) + sage: H.module(0) Trivial representation of Standard permutations of 3 over Rational Field - sage: H.free_module(1) + sage: H.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) + sage: H.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 @@ -185,6 +187,8 @@ def free_module(self, d): raise ValueError("only defined for non-negative degree") return tensor([self._M] + [self._A]*d) + free_module = deprecated_function_alias(21386, module) + @cached_method def trivial_module(self): """ @@ -238,8 +242,8 @@ def boundary(self, d): ....: 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()) + ....: zero = H.module(n-1).zero() + ....: return all(comp(b) == zero for b in H.module(n+1).basis()) sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: H = SGA.hochschild_complex(SGA) @@ -264,9 +268,9 @@ def boundary(self, d): 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) + return self.module(0).module_morphism(lambda x: zero, codomain=t) + Fd = self.module(d-1) + Fd1 = self.module(d) mone = -one def on_basis(k): p = self._M.monomial(k[0]) * self._A.monomial(k[1]) @@ -318,8 +322,8 @@ def coboundary(self, d): ....: 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()) + ....: zero = H.module(n+1).zero() + ....: return all(comp(b) == zero for b in H.module(n-1).basis()) sage: SGA = SymmetricGroupAlgebra(QQ, 3) sage: H = SGA.hochschild_complex(SGA) @@ -452,3 +456,299 @@ def cohomology(self, d): unitriangular=True) return ker.quotient_module(im_retract) + def _element_constructor_(self, vectors): + """ + Construct an element of ``self`` from ``vectors``. + + TESTS:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H(0) + Trivial chain + sage: H(2) + Chain(0: 2) + sage: H(x+2*y) + Chain(0: x + 2*y) + sage: H({0: H.module(0).an_element()}) + Chain(0: 2 + 2*x + 3*y) + sage: H({2: H.module(2).an_element()}) + Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y) + sage: H({0:x-y, 2: H.module(2).an_element()}) + Chain with 2 nonzero terms over Rational Field + sage: H([2]) + Traceback (most recent call last): + ... + ValueError: cannot construct an element from [2] + """ + if not vectors: # special case: the zero chain + return self.element_class(self, {}) + # special case: an element of the defining module + if self._M.has_coerce_map_from(parent(vectors)): + vectors = self._M(vectors) + if parent(vectors) is self._M: + mc = vectors.monomial_coefficients(copy=False) + vec = self.module(0)._from_dict({(k,): mc[k] for k in mc}) + return self.element_class(self, {0: vec}) + if isinstance(vectors, (Chain_class, self.element_class)): + vectors = vectors._vec + data = dict() + if not isinstance(vectors, dict): + raise ValueError("cannot construct an element from {}".format(vectors)) + # Special handling for the 0 free module + # FIXME: Allow coercions between the 0 free module and the defining module + if 0 in vectors: + vec = vectors.pop(0) + if parent(vec) is self._M: + mc = vec.monomial_coefficients(copy=False) + data[0] = self.module(0)._from_dict({(k,): mc[k] for k in mc}) + else: + data[0] = self.module(0)(vec) + for degree in vectors: + vec = self.module(degree)(vectors[degree]) + if not vec: + continue + data[degree] = vec + return self.element_class(self, data) + + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: v = H.an_element() + sage: [v.vector(i) for i in range(6)] + [2*F[1] + 2*F[x] + 3*F[y], + 2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y], + 2*F[1] # F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[1] # F[x] + + 3*F[1] # F[1] # F[1] # F[y], + 0, + 0] + """ + return self.element_class(self, {d: self.module(d).an_element() + for d in range(4)}) + + class Element(ModuleElement): + """ + A chain of the Hochschild complex. + + INPUT: + + Can be one of the following: + + - A dictionary whose keys are the degree and whose `d`-th + value is an element in the degree `d` module. + - An element in the coefficient module `M`. + + EXAMPLES:: + + sage: SGA = SymmetricGroupAlgebra(QQ, 3) + sage: T = SGA.trivial_representation() + sage: H = SGA.hochschild_complex(T) + sage: H(T.an_element()) + Chain(0: 2*B['v']) + sage: H({0: T.an_element()}) + Chain(0: 2*B['v']) + sage: H({1: H.module(1).an_element()}) + Chain(1: 2*B['v'] # [1, 2, 3] + 2*B['v'] # [1, 3, 2] + 3*B['v'] # [2, 1, 3]) + sage: H({0: H.module(0).an_element(), 3: H.module(3).an_element()}) + Chain with 2 nonzero terms over Rational Field + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: H(x + 2*y^2) + Chain(0: F[x] + 2*F[y^2]) + sage: H({0: x*y - x}) + Chain(0: -F[x] + F[x*y]) + sage: H(2) + Chain(0: 2*F[1]) + sage: H({0: x-y, 2: H.module(2).basis().an_element()}) + Chain with 2 nonzero terms over Integer Ring + """ + def __init__(self, parent, vectors): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x-y, 2: H.module(2).basis().an_element()}) + sage: TestSuite(a).run() + """ + self._vec = vectors + ModuleElement.__init__(self, parent) + + def vector(self, degree): + """ + Return the free module element in ``degree``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x-y, 2: H.module(2).basis().an_element()}) + sage: [a.vector(i) for i in range(3)] + [F[x] - F[y], 0, F[1] # F[1] # F[1]] + """ + try: + return self._vec[degree] + except KeyError: + return self.parent().module(degree).zero() + + def _repr_(self): + """ + Print representation. + + EXAMPLES:: + + sage: E. = ExteriorAlgebra(QQ) + sage: H = E.hochschild_complex(E) + sage: H(0) + Trivial chain + sage: H(x+2*y) + Chain(0: x + 2*y) + sage: H({2: H.module(2).an_element()}) + Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y) + sage: H({0:x-y, 2: H.module(2).an_element()}) + Chain with 2 nonzero terms over Rational Field + """ + n = len(self._vec) + if n == 0: + return 'Trivial chain' + + if n == 1: + deg, vec = self._vec.items()[0] + return 'Chain({0}: {1})'.format(deg, vec) + + return 'Chain with {0} nonzero terms over {1}'.format(n, + self.parent().base_ring()) + + def _ascii_art_(self): + """ + Return an ascii art representation. + + Note that arrows go to the left so that composition of + differentials is the usual matrix multiplication. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: ascii_art(a) + d_0 d_1 d_2 d_3 + 0 <---- F - F <---- 1 # 1 <---- 1 # 1 # 1 <---- 0 + x y + """ + from sage.typeset.ascii_art import AsciiArt, ascii_art + + if not self._vec: # 0 chain + return AsciiArt(['0']) + + def arrow_art(d): + d_str = [' d_{0} '.format(d)] + arrow = ' <' + '-'*(len(d_str[0])-3) + ' ' + d_str.append(arrow) + return AsciiArt(d_str, baseline=0) + + result = AsciiArt(['0']) + max_deg = max(self._vec) + for deg in range(min(self._vec), max_deg+1): + A = ascii_art(self.vector(deg)) + A._baseline = A.height() // 2 + result += arrow_art(deg) + A + return result + arrow_art(max_deg+1) + AsciiArt(['0']) + + def _add_(self, other): + """ + Module addition + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: [a.vector(i) for i in range(3)] + [F[x] - F[y], F[1] # F[1], F[1] # F[1] # F[1]] + sage: [H.an_element().vector(i) for i in range(3)] + [2*F[1] + 2*F[x] + 3*F[y], + 2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]] + + sage: v = a + H.an_element() + sage: [v.vector(i) for i in range(3)] + [2*F[1] + 3*F[x] + 2*F[y], + 3*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y], + 3*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]] + """ + vectors = dict(self._vec) # Make a (shallow) copy + for d in other._vec: + if d in vectors: + vectors[d] += other._vec[d] + if not vectors[d]: + del vectors[d] + else: + vectors[d] = other._vec + parent = self.parent() + return parent.element_class(parent, vectors) + + def _lmul_(self, scalar): + """ + Scalar multiplication + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: v = 3*a + sage: [v.vector(i) for i in range(3)] + [3*F[x] - 3*F[y], 3*F[1] # F[1], 3*F[1] # F[1] # F[1]] + """ + if scalar == 0: + return self.zero() + vectors = dict() + for d in self._vec: + vec = scalar * self._vec[d] + if vec: + vectors[d] = vec + return self.__class__(self.parent(), vectors) + + def _richcmp_(self, other, op): + """ + Rich comparison of ``self`` to ``other``. + + EXAMPLES:: + + sage: F. = FreeAlgebra(ZZ) + sage: H = F.hochschild_complex(F) + sage: a = H({0: x - y, + ....: 1: H.module(1).basis().an_element(), + ....: 2: H.module(2).basis().an_element()}) + sage: a == 3*a + False + sage: a + a == 2*a + True + sage: a == H.zero() + False + + sage: a != 3*a + True + sage: a + a != 2*a + False + sage: a != H.zero() + True + """ + return richcmp(self._vec, other._vec, op) + diff --git a/src/sage/homology/homology_vector_space_with_basis.py b/src/sage/homology/homology_vector_space_with_basis.py index 864523393d5..b1528aa8d48 100644 --- a/src/sage/homology/homology_vector_space_with_basis.py +++ b/src/sage/homology/homology_vector_space_with_basis.py @@ -30,7 +30,7 @@ from sage.misc.cachefunc import cached_method from sage.categories.algebras import Algebras from sage.categories.modules import Modules -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from sage.sets.family import Family from .simplicial_complex import SimplicialComplex from .simplicial_set import SimplicialSet_arbitrary @@ -375,7 +375,7 @@ def _to_cycle_on_basis(self, i): cochains=self._cohomology) return chains.from_vector(vec) - class Element(CombinatorialFreeModuleElement): + class Element(CombinatorialFreeModule.Element): def to_cycle(self): r""" (Co)cycle representative of this homogeneous (co)homology class. diff --git a/src/sage/homology/simplicial_complex.py b/src/sage/homology/simplicial_complex.py index c5447ee4154..aefe15563ae 100644 --- a/src/sage/homology/simplicial_complex.py +++ b/src/sage/homology/simplicial_complex.py @@ -1392,7 +1392,7 @@ def n_cells(self, n, subcomplex=None, sort=None): This list is sorted to provide reliable indexing for the rows and columns of the matrices of differentials in the - associateed chain complex. + associated chain complex. EXAMPLES:: diff --git a/src/sage/interfaces/ecm.py b/src/sage/interfaces/ecm.py index ea8fe4cda6e..52e34b7a2de 100644 --- a/src/sage/interfaces/ecm.py +++ b/src/sage/interfaces/ecm.py @@ -141,9 +141,6 @@ def __init__(self, B1=10, B2=None, **kwds): - ``ve`` -- integer `n`. Verbosely show short (`< n` character) expressions on each loop - - ``cofdec`` -- boolean. Force cofactor output in decimal - (even if expressions are used ) - - ``B2scale`` -- integer. Multiplies the default B2 value - ``go`` -- integer. Preload with group order val, which can @@ -396,15 +393,15 @@ def _parse_output(self, n, out): if m is not None: factor = m.group('factor') primality = m.group('primality') - assert primality in ['probable prime', 'composite'] - result += [(ZZ(factor), primality == 'probable prime')] + assert primality in ['prime', 'composite'] + result += [(ZZ(factor), primality == 'prime')] continue # cofactor on the next line m = self._found_cofactor_re.match(line) if m is not None: cofactor = m.group('cofactor') primality = m.group('primality') - assert primality in ['Probable prime', 'Composite'] - result += [(ZZ(cofactor), primality == 'Probable prime')] + assert primality in ['Prime', 'Composite'] + result += [(ZZ(cofactor), primality == 'Prime')] # assert len(result) == 2 return result raise ValueError('failed to parse ECM output') @@ -501,7 +498,6 @@ def _find_factor(self, n, factor_digits, B1, **kwds): if factor_digits is not None: B1 = self.recommended_B1(factor_digits) kwds['one'] = True - kwds['cofdec'] = True cmd = self._make_cmd(B1, None, kwds) out = self._run_ecm(cmd, n) return self._parse_output(n, out) @@ -833,8 +829,8 @@ def _validate(self, n): Step 1 took 12ms Step 2 took 17ms ********** Factor found in step 2: 79792266297612017 -Found probable prime factor of 17 digits: 79792266297612017 -Probable prime cofactor 6366805760909027985741435139224233 has 34 digits +Found prime factor of 17 digits: 79792266297612017 +Prime cofactor 6366805760909027985741435139224233 has 34 digits """ TEST_ECM_OUTPUT_2 = """ @@ -844,8 +840,8 @@ def _validate(self, n): Step 1 took 2ms Step 2 took 3ms ********** Factor found in step 2: 179424673 -Found probable prime factor of 9 digits: 179424673 -Probable prime cofactor 179424673 has 9 digits +Found prime factor of 9 digits: 179424673 +Prime cofactor 179424673 has 9 digits """ TEST_ECM_OUTPUT_3 = """ @@ -863,7 +859,7 @@ def _validate(self, n): Step 1 took 5ms Step 2 took 4ms ********** Factor found in step 2: 197002597249 -Found probable prime factor of 12 digits: 197002597249 +Found prime factor of 12 digits: 197002597249 Composite cofactor 339872432034468861533158743041639097889948066859 has 48 digits """ @@ -875,5 +871,5 @@ def _validate(self, n): Step 2 took 2ms ********** Factor found in step 2: 265748496095531068869578877937 Found composite factor of 30 digits: 265748496095531068869578877937 -Probable prime cofactor 251951573867253012259144010843 has 30 digits +Prime cofactor 251951573867253012259144010843 has 30 digits """ diff --git a/src/sage/interfaces/expect.py b/src/sage/interfaces/expect.py index 4f420eca2d3..b5de3868d8e 100644 --- a/src/sage/interfaces/expect.py +++ b/src/sage/interfaces/expect.py @@ -38,8 +38,8 @@ # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import +from six import string_types import os import signal @@ -49,7 +49,6 @@ import gc from . import quit from . import cleaner -import six from random import randrange import pexpect @@ -169,7 +168,7 @@ def __init__(self, name, prompt, command=None, server=None, self.__init_code = init_code #Handle the log file - if isinstance(logfile, six.string_types): + if isinstance(logfile, string_types): self.__logfile = None self.__logfilename = logfile else: @@ -773,9 +772,9 @@ def _eval_line_using_file(self, line, restart_if_needed=True): 3 """ - F = open(self._local_tmpfile(), 'w') - F.write(line+'\n') - F.close() + with open(self._local_tmpfile(), 'w') as F: + F.write(line + '\n') + tmp_to_use = self._local_tmpfile() if self.is_remote(): self._send_tmpfile_to_server() @@ -939,7 +938,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if if len(line)>0: try: - if isinstance(wait_for_prompt, six.string_types): + if isinstance(wait_for_prompt, string_types): E.expect(wait_for_prompt) else: E.expect(self._prompt) @@ -1139,16 +1138,14 @@ def _expect_expr(self, expr=None, timeout=None): self.interrupt() raise - def _sendstr(self, str): + def _sendstr(self, string): r""" Send a string to the pexpect interface, autorestarting the expect interface if anything goes wrong. INPUT: - - - ``str`` - a string - + - ``string`` -- a string EXAMPLES: We illustrate this function using the R interface:: @@ -1167,11 +1164,11 @@ def _sendstr(self, str): if self._expect is None: self._start() try: - os.write(self._expect.child_fd, str) + os.write(self._expect.child_fd, string) except OSError: self._crash_msg() self.quit() - self._sendstr(str) + self._sendstr(string) def _crash_msg(self): r""" @@ -1285,7 +1282,7 @@ def eval(self, code, strip=True, synchronize=False, locals=None, allow_use_file= except AttributeError: pass - if not isinstance(code, six.string_types): + if not isinstance(code, string_types): raise TypeError('input code must be a string.') #Remove extra whitespace @@ -1376,7 +1373,7 @@ def __init__(self, parent, value, is_name=False, name=None): # idea: Joe Wetherell -- try to find out if the output # is too long and if so get it using file, otherwise # don't. - if isinstance(value, six.string_types) and parent._eval_using_file_cutoff and \ + if isinstance(value, string_types) and parent._eval_using_file_cutoff and \ parent._eval_using_file_cutoff < len(value): self._get_using_file = True diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 2f104974fe9..4699c54051c 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -174,8 +174,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** - from __future__ import absolute_import, print_function +from six import string_types from .expect import Expect, ExpectElement, FunctionElement, ExpectFunction from .gap_workspace import gap_workspace_file, prepare_workspace_dir @@ -739,17 +739,17 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if (normal, error) = self._execute_line(line, wait_for_prompt=wait_for_prompt, expect_eof= (self._quit_string() in line)) - if len(error)> 0: + if len(error): if 'Error, Rebuild completion files!' in error: error += "\nRunning gap_reset_workspace()..." self.quit() gap_reset_workspace() error = error.replace('\r','') raise RuntimeError("%s produced error output\n%s\n executing %s"%(self, error,line)) - if len(normal) == 0: + if not len(normal): return '' - if isinstance(wait_for_prompt, str) and normal.ends_with(wait_for_prompt): + if isinstance(wait_for_prompt, string_types) and normal.ends_with(wait_for_prompt): n = len(wait_for_prompt) elif normal.endswith(self._prompt): n = len(self._prompt) @@ -758,7 +758,7 @@ def _eval_line(self, line, allow_use_file=True, wait_for_prompt=True, restart_if else: n = 0 out = normal[:-n] - if len(out) > 0 and out[-1] == "\n": + if len(out) and out[-1] == "\n": out = out[:-1] return out diff --git a/src/sage/interfaces/giac.py b/src/sage/interfaces/giac.py index ec56273b75f..ad232ef32e3 100644 --- a/src/sage/interfaces/giac.py +++ b/src/sage/interfaces/giac.py @@ -734,35 +734,34 @@ def _assign_symbol(self): """ return ":=" - def _help(self, str): + def _help(self, string): r""" - Returns the Giac help on ``str``. + Return the Giac help on ``string``. EXAMPLES:: sage: giac._help('gcd') # not tested ; output may vary (LANG) "...gcd - greatest common divisor of polynomials... """ - return os.popen('cas_help %s'%str).read() - # return os.popen('echo "?%s" | giac'%str).read() + return os.popen('cas_help %s' % string).read() + # return os.popen('echo "?%s" | giac' % string).read() - def help(self, str): + def help(self, string): """ - Display Giac help about str. This is the same as typing "?str" in - the Giac console. + Display Giac help about string. - INPUT: + This is the same as typing "?string" in the Giac console. - - ``str`` - a string to search for in the giac help - system + INPUT: + - ``string`` -- a string to search for in the giac help system EXAMPLES:: sage: giac.help('Psi') # not tested - depends of giac and $LANG Psi(a,n)=nth-derivative of the function DiGamma (=ln@Gamma) at point a (Psi(a,0)=Psi(a))... """ - pager()(self._help(str)) + pager()(self._help(string)) def clear(self, var): """ diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 3404d594585..e96d56bb5b1 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -38,9 +38,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function -from six import iteritems -from six import integer_types -import six +from six import iteritems, integer_types, string_types import operator @@ -49,6 +47,7 @@ from sage.structure.element import Element, parent import sage.misc.sage_eval +from sage.misc.fast_methods import WithEqualityById from sage.docs.instancedoc import instancedoc @@ -57,11 +56,33 @@ def __repr__(self): return str(self) -class Interface(ParentWithBase): +class Interface(WithEqualityById, ParentWithBase): """ Interface interface object. + + .. NOTE:: + + Two interfaces compare equal if and only if they are identical + objects (this is a critical constraint so that caching of + representations of objects in interfaces works + correctly). Otherwise they are never equal. """ def __init__(self, name): + """ + Initialize ``self``. + + EXAMPLES:: + + sage: Maxima() == maxima + False + sage: maxima == maxima + True + + sage: Maxima() != maxima + True + sage: maxima != maxima + False + """ self.__name = name self.__coerce_name = '_' + name.lower() + '_' self.__seq = -1 @@ -257,7 +278,7 @@ def __call__(self, x, name=None): except (NotImplementedError, TypeError): pass - if isinstance(x, six.string_types): + if isinstance(x, string_types): return cls(self, x, name=name) try: return self._coerce_from_special_method(x) @@ -587,28 +608,6 @@ def __getattr__(self, attrname): raise return self._function_class()(self, attrname) - def __cmp__(self, other): - """ - Compare two pseudo-tty interfaces. Two interfaces compare - equal if and only if they are identical objects (this is a - critical constraint so that caching of representations of - objects in interfaces works correctly). Otherwise they are - never equal. - - EXAMPLES:: - - sage: Maxima() == maxima - False - sage: maxima == maxima - True - """ - if self is other: - return 0 - c = cmp(type(self), type(other)) - if c: - return c - return -1 # sucky, but I can't think of anything better; it is important that different interfaces to the same system still compare differently; unfortunately there is nothing to distinguish them. - def console(self): raise NotImplementedError @@ -1098,7 +1097,7 @@ def __repr__(self): except ValueError as msg: return '(invalid {} object -- {})'.format(self.parent() or type(self), msg) cr = getattr(self, '_cached_repr', None) - if isinstance(cr, six.string_types): + if isinstance(cr, string_types): s = cr else: s = self._repr_() @@ -1354,7 +1353,7 @@ def name(self, new_name=None): 's5' """ if new_name is not None: - if not isinstance(new_name, str): + if not isinstance(new_name, string_types): raise TypeError("new_name must be a string") p = self.parent() p.set(new_name, self._name) diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index ca8d4cbf649..78f87d27998 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -213,6 +213,7 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six import string_types import re import sys @@ -457,7 +458,7 @@ def _post_process_from_file(self, s): sage: magma._post_process_from_file("Hello") '' """ - if not isinstance(s, str): + if not isinstance(s, string_types): raise RuntimeError("Error evaluating object in %s:\n%s" % (self, s)) # Chop off the annoying "Loading ... " message that Magma # always outputs no matter what. diff --git a/src/sage/interfaces/maple.py b/src/sage/interfaces/maple.py index 9ae7c5fd038..a2a901f8458 100644 --- a/src/sage/interfaces/maple.py +++ b/src/sage/interfaces/maple.py @@ -758,9 +758,9 @@ def source(self, s): except Exception: pager()('No source code could be found.') - def _help(self, str): + def _help(self, string): r""" - Return the Maple help on ``str``. + Return the Maple help on ``string``. EXAMPLES:: @@ -768,26 +768,26 @@ def _help(self, str): sage: txt.find('gcd - greatest common divisor') > 0 # optional - maple True """ - return os.popen('echo "?%s" | maple -q'%str).read() + return os.popen('echo "?%s" | maple -q' % string).read() - def help(self, str): + def help(self, string): """ - Display Maple help about ``str``. + Display Maple help about ``string``. - This is the same as typing "?str" in the Maple console. + This is the same as typing "?string" in the Maple console. INPUT: - - ``str`` - a string to search for in the maple help + - ``string`` - a string to search for in the maple help system EXAMPLES:: - sage: maple.help('digamma') #not tested + sage: maple.help('Psi') # not tested Psi - the Digamma and Polygamma functions ... """ - pager()(self._help(str)) + pager()(self._help(string)) def with_package(self, package): """ diff --git a/src/sage/interfaces/maxima.py b/src/sage/interfaces/maxima.py index 0da14f8786c..e5e7fef45e8 100644 --- a/src/sage/interfaces/maxima.py +++ b/src/sage/interfaces/maxima.py @@ -463,8 +463,8 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -from __future__ import absolute_import +from __future__ import print_function, absolute_import +from six import string_types import os import re @@ -639,7 +639,7 @@ def __reduce__(self): """ return reduce_load_Maxima, tuple([]) #(self.__init_code,) - def _sendline(self, str): + def _sendline(self, string): """ Send a string followed by a newline character. @@ -649,7 +649,7 @@ def _sendline(self, str): sage: maxima.get('t') '9' """ - self._sendstr(str) + self._sendstr(string) os.write(self._expect.child_fd, os.linesep) def _expect_expr(self, expr=None, timeout=None): @@ -995,7 +995,7 @@ def set(self, var, value): sage: maxima.get('xxxxx') '2' """ - if not isinstance(value, str): + if not isinstance(value, string_types): raise TypeError cmd = '%s : %s$'%(var, value.rstrip(';')) if len(cmd) > self.__eval_using_file_cutoff: diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 84f6a44dbfa..46aaa0c7a15 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -50,6 +50,7 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six import string_types import os import re @@ -637,13 +638,13 @@ def function(self, args, defn, rep=None, latex=None): name = self._next_var_name() if isinstance(defn, MaximaAbstractElement): defn = defn.str() - elif not isinstance(defn, str): + elif not isinstance(defn, string_types): defn = str(defn) if isinstance(args, MaximaAbstractElement): args = args.str() - elif not isinstance(args, str): + elif not isinstance(args, string_types): args = str(args) - cmd = '%s(%s) := %s'%(name, args, defn) + cmd = '%s(%s) := %s' % (name, args, defn) self._eval_line(cmd) if rep is None: rep = defn @@ -847,7 +848,7 @@ def de_solve(self, de, vars, ics=None): sage: maxima.de_solve('diff(y,x) + 3*x = y', ['x','y'],[1,1]) y=-%e^-1*(5*%e^x-3*%e*x-3*%e) """ - if not isinstance(vars, str): + if not isinstance(vars, string_types): str_vars = '%s, %s'%(vars[1], vars[0]) else: str_vars = vars diff --git a/src/sage/interfaces/maxima_lib.py b/src/sage/interfaces/maxima_lib.py index 552c96a0b91..cd7cf81795d 100644 --- a/src/sage/interfaces/maxima_lib.py +++ b/src/sage/interfaces/maxima_lib.py @@ -86,6 +86,7 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six import string_types from sage.symbolic.ring import SR @@ -232,6 +233,8 @@ max_integrate=EclObject("$INTEGRATE") max_sum=EclObject("$SUM") max_simplify_sum=EclObject("$SIMPLIFY_SUM") +max_prod=EclObject("$PRODUCT") +max_simplify_prod=EclObject("$SIMPLIFY_PRODUCT") max_ratsimp=EclObject("$RATSIMP") max_limit=EclObject("$LIMIT") max_tlimit=EclObject("$TLIMIT") @@ -502,7 +505,7 @@ def set(self, var, value): sage: maxima_lib.get('xxxxx') '2' """ - if not isinstance(value, str): + if not isinstance(value, string_types): raise TypeError cmd = '%s : %s$'%(var, value.rstrip(';')) self.eval(cmd) @@ -898,6 +901,22 @@ def sr_sum(self,*args): else: raise + def sr_prod(self,*args): + """ + Helper function to wrap calculus use of Maxima's product. + """ + try: + return max_to_sr(maxima_eval([[max_ratsimp],[[max_simplify_prod],([max_prod],[sr_to_max(SR(a)) for a in args])]])); + except RuntimeError as error: + s = str(error) + if "divergent" in s: + raise ValueError("Product is divergent.") + elif "Is" in s: # Maxima asked for a condition + self._missing_assumption(s) + else: + raise + + def sr_limit(self, expr, v, a, dir=None): """ Helper function to wrap calculus use of Maxima's limits. @@ -1208,7 +1227,7 @@ def reduce_load_MaximaLib(): sage.functions.log.log : "%LOG", sage.functions.log.lambert_w : "%LAMBERT_W", sage.functions.other.factorial : "MFACTORIAL", - sage.functions.other.erf : "%ERF", + sage.functions.error.erf : "%ERF", sage.functions.other.gamma_inc : "%GAMMA_INCOMPLETE", } #we compile the dictionary diff --git a/src/sage/interfaces/polymake.py b/src/sage/interfaces/polymake.py index c6ddcfc113e..55d1c5a4821 100644 --- a/src/sage/interfaces/polymake.py +++ b/src/sage/interfaces/polymake.py @@ -1639,7 +1639,7 @@ def __getattr__(self, attrname): If the attribute name is known as the name of a property, it is interpreted as such. Otherwise, if it is known as a function in - the currenty application, the function is returned with this + the current application, the function is returned with this element inserted as first argument, and potential further arguments, when called. Otherwise, it is assumed that it is a member function of this element, and treated as such. Note that member functions diff --git a/src/sage/interfaces/qepcad.py b/src/sage/interfaces/qepcad.py index 00488e99398..f920633084c 100644 --- a/src/sage/interfaces/qepcad.py +++ b/src/sage/interfaces/qepcad.py @@ -605,6 +605,7 @@ #***************************************************************************** from __future__ import print_function from __future__ import absolute_import +from six import string_types from sage.env import SAGE_LOCAL import pexpect @@ -836,12 +837,12 @@ def __init__(self, formula, varlist = None if vars is not None: - if isinstance(vars, str): + if isinstance(vars, string_types): varlist = vars.strip('()').split(',') else: varlist = [str(v) for v in vars] - if isinstance(formula, str): + if isinstance(formula, string_types): if varlist is None: raise ValueError("vars must be specified if formula is a string") @@ -917,7 +918,7 @@ def assume(self, assume): sage: qe.finish() # optional - qepcad 4 a c - b^2 <= 0 """ - if not isinstance(assume, str): + if not isinstance(assume, string_types): assume = qepcad_formula.formula(assume) if len(assume.qvars): raise ValueError("assumptions cannot be quantified") diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index a03b5ed8001..9db603f8eb3 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- r""" Interfaces to R diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index a8e969cbf35..4ba76ab0b80 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -15,7 +15,7 @@ # # http://www.gnu.org/licenses/ #***************************************************************************** -from six import iteritems +from six import iteritems, string_types from six.moves import cPickle import os @@ -232,7 +232,7 @@ def __call__(self, x): else: return self(x.sage()) - if isinstance(x, str): + if isinstance(x, string_types): return SageElement(self, x) if self.is_local(): diff --git a/src/sage/interfaces/singular.py b/src/sage/interfaces/singular.py index b2da07f9590..e7dfab1940d 100644 --- a/src/sage/interfaces/singular.py +++ b/src/sage/interfaces/singular.py @@ -320,7 +320,7 @@ from __future__ import print_function from __future__ import absolute_import from six.moves import range -from six import integer_types +from six import integer_types, string_types import os import re @@ -903,7 +903,7 @@ def ideal(self, *gens): x0*x1-x0*x2-x1*x2, x0^2*x2-x0*x2^2-x1*x2^2 """ - if isinstance(gens, str): + if isinstance(gens, string_types): gens = self(gens) if isinstance(gens, SingularElement): diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index 2b197e688e9..4a08558827a 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -17,12 +17,12 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -include 'sage/ext/stdsage.pxi' - -from sage.misc.long cimport pyobject_to_long from cpython.sequence cimport * +from cysignals.memory cimport sig_free + +from sage.misc.long cimport pyobject_to_long from sage.structure.sage_object cimport SageObject from sage.rings.integer cimport Integer from sage.libs.flint.fmpz_poly cimport * @@ -102,7 +102,7 @@ cdef class Fmpz_poly(SageObject): sage: f[200] 0 """ - cdef Integer res = PY_NEW(Integer) + cdef Integer res = Integer.__new__(Integer) fmpz_poly_get_coeff_mpz(res.value, self.poly, i) return res diff --git a/src/sage/libs/libecm.pyx b/src/sage/libs/libecm.pyx index c06b9b96048..d46154282c9 100644 --- a/src/sage/libs/libecm.pyx +++ b/src/sage/libs/libecm.pyx @@ -17,12 +17,16 @@ EXAMPLES:: sage: from sage.libs.libecm import ecmfactor sage: result = ecmfactor(999, 0.00) - sage: result[0] and (result[1] in [27, 37, 999]) + sage: result[0] + True + sage: result[1] in [3, 9, 27, 37, 111, 333, 999] or result[1] True sage: result = ecmfactor(999, 0.00, verbose=True) Performing one curve with B1=0 Found factor in step 1: ... - sage: result[0] and (result[1] in [27, 37, 999]) + sage: result[0] + True + sage: result[1] in [3, 9, 27, 37, 111, 333, 999] or result[1] True sage: ecmfactor(2^128+1,1000,sigma=227140902) (True, 5704689200685129054721, 227140902) diff --git a/src/sage/libs/linbox/linbox.pxd b/src/sage/libs/linbox/linbox.pxd index 09035553f1a..6903e89dafa 100644 --- a/src/sage/libs/linbox/linbox.pxd +++ b/src/sage/libs/linbox/linbox.pxd @@ -13,15 +13,3 @@ cdef class Linbox_modn_sparse: cdef object rank(self, int gauss) cdef void solve(self, c_vector_modint **x, c_vector_modint *b, int method) - -cdef class Linbox_integer_dense: - cdef mpz_t** matrix - cdef size_t nrows, ncols - - cdef set(self, mpz_t** matrix, size_t nrows, size_t ncols) - cdef matrix_matrix_multiply(self, - mpz_t **ans, - mpz_t **B, - size_t B_nr, size_t B_nc) - cdef unsigned long rank(self) except -1 - diff --git a/src/sage/libs/linbox/linbox.pyx b/src/sage/libs/linbox/linbox.pyx index c31d1fc8779..8c6f34a4bb1 100644 --- a/src/sage/libs/linbox/linbox.pyx +++ b/src/sage/libs/linbox/linbox.pyx @@ -53,123 +53,3 @@ cdef class Linbox_modn_sparse: for i from 0 <= i < X.size(): set_entry(x[0], i, X.get(i)) - -########################################################################## -## Sparse matrices over ZZ -########################################################################## - - -########################################################################## -## Dense matrices over ZZ -########################################################################## - -cdef extern from "linbox/linbox-sage.h": - void linbox_integer_dense_minpoly(mpz_t* &minpoly, size_t °ree, size_t n, mpz_t** matrix) - - void linbox_integer_dense_charpoly(mpz_t* &charpoly, size_t °ree, size_t n, mpz_t** matrix) - - void linbox_integer_dense_delete_array(mpz_t* f) - - int linbox_integer_dense_matrix_matrix_multiply(mpz_t** ans, mpz_t **A, mpz_t **B, size_t A_nr, size_t A_nc, size_t B_nc) - - unsigned long linbox_integer_dense_rank(mpz_t** matrix, size_t nrows, - size_t ncols) - - void linbox_integer_dense_det(mpz_t ans, mpz_t** matrix, - size_t nrows, size_t ncols) - - void linbox_integer_dense_smithform(mpz_t **v, - mpz_t **matrix, - size_t nrows, size_t ncols) - -cdef class Linbox_integer_dense: - cdef set(self, mpz_t** matrix, size_t nrows, size_t ncols): - self.nrows = nrows - self.ncols = ncols - self.matrix = matrix - - def minpoly(self): - """ - OUTPUT: - - coefficients of minpoly as a Python list - """ - cdef mpz_t* poly - cdef size_t degree - verbose("using linbox poly comp") - linbox_integer_dense_minpoly(poly, degree, self.nrows, self.matrix) - verbose("computed poly -- now converting back to Sage") - - v = [] - cdef Integer k - cdef size_t n - for n from 0 <= n <= degree: - k = Integer() - mpz_set(k.value, poly[n]) - mpz_clear(poly[n]) - v.append(k) - linbox_integer_dense_delete_array(poly) - return v - - def charpoly(self): - """ - OUTPUT: - - coefficients of charpoly or minpoly as a Python list - """ - cdef mpz_t* poly - cdef size_t degree - verbose("using linbox poly comp") - linbox_integer_dense_charpoly(poly, degree, self.nrows, self.matrix) - verbose("computed poly -- now converting back to Sage") - - v = [] - cdef Integer k - cdef size_t n - for n from 0 <= n <= degree: - k = Integer() - mpz_set(k.value, poly[n]) - mpz_clear(poly[n]) - v.append(k) - linbox_integer_dense_delete_array(poly) - return v - - cdef matrix_matrix_multiply(self, - mpz_t **ans, - mpz_t **B, - size_t B_nr, size_t B_nc): - cdef int e - e = linbox_integer_dense_matrix_matrix_multiply(ans, self.matrix, B, self.nrows, self.ncols, B_nc) - if e: - raise RuntimeError("error doing matrix matrix multiply over ZZ using linbox") - - - cdef unsigned long rank(self) except -1: - return linbox_integer_dense_rank(self.matrix, self.nrows, self.ncols) - - def det(self): - """ - OUTPUT: - - determinant as a sage Integer - """ - cdef Integer z - z = Integer() - linbox_integer_dense_det(z.value, self.matrix, self.nrows, self.ncols) - return z - - def smithform(self): - raise NotImplementedError - #cdef mpz_t* v - #linbox_integer_dense_smithform(&v, self.matrix, self.nrows, self.ncols) - #z = [] - #cdef Integer k - #cdef size_t n - #for n from 0 <= n < self.ncols: - # k = Integer() - # mpz_set(k.value, v[n]) - # mpz_clear(v[n]) - # z.append(k) - #linbox_integer_dense_delete_array(v) - #return z - diff --git a/src/sage/libs/linbox/linbox_flint_interface.pxd b/src/sage/libs/linbox/linbox_flint_interface.pxd new file mode 100644 index 00000000000..853fab63cbe --- /dev/null +++ b/src/sage/libs/linbox/linbox_flint_interface.pxd @@ -0,0 +1,16 @@ +from sage.libs.flint.types cimport fmpz_t, fmpz_mat_t, fmpz_poly_t + +# set C <- A * B +cdef void linbox_fmpz_mat_mul(fmpz_mat_t C, fmpz_mat_t A, fmpz_mat_t B) + +# set cp to the characteristic polynomial of A +cdef void linbox_fmpz_mat_charpoly(fmpz_poly_t cp, fmpz_mat_t A) + +# set mp to the minimal polynomial of A +cdef void linbox_fmpz_mat_minpoly(fmpz_poly_t mp, fmpz_mat_t A) + +# return the rank of A +cdef unsigned long linbox_fmpz_mat_rank(fmpz_mat_t A) + +# set det to the determinant of A +cdef void linbox_fmpz_mat_det(fmpz_t det, fmpz_mat_t A) diff --git a/src/sage/libs/linbox/linbox_flint_interface.pyx b/src/sage/libs/linbox/linbox_flint_interface.pyx new file mode 100644 index 00000000000..02a6151fcf0 --- /dev/null +++ b/src/sage/libs/linbox/linbox_flint_interface.pyx @@ -0,0 +1,255 @@ +# distutils: extra_compile_args = LINBOX_CFLAGS +# distutils: libraries = LINBOX_LIBRARIES +# distutils: library_dirs = LINBOX_LIBDIR +# distutils: language = c++ +r""" +Interface between flint matrices and linbox + +This module only contains C++ code (and the interface is fully C +compatible). It basically contains what used to be in the LinBox +source code under interfaces/sage/linbox-sage.C written by M. Albrecht +and C. Pernet. The functions available are: + +- ``void linbox_fmpz_mat_mul(fmpz_mat_t C, fmpz_mat_t A, fmpz_mat_t B)``: set + ``C`` to be the result of the multiplication ``A * B`` + +- ``void linbox_fmpz_mat_charpoly(fmpz_poly_t cp, fmpz_mat_t A)``: set ``cp`` + to be the characteristic polynomial of the square matrix ``A`` + +- ``void linbox_fmpz_mat_minpoly(fmpz_poly_t mp, fmpz_mat_t A)``: set ``mp`` + to be the minimal polynomial of the square matrix ``A`` + +- ``unsigned long linbox_fmpz_mat_rank(fmpz_mat_t A)``: return the rank of the + matrix ``A`` + +- ``void linbox_fmpz_mat_det(fmpz_t det, fmpz_mat_t A)``: set ``det`` to the + determinat of the square matrix ``A`` +""" +#***************************************************************************** +# Copyright (C) 2007 Martin Albrecht +# Copyright (C) 2008 Clement Pernet +# Copyright (C) 2017 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 +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.libs.gmp.types cimport mpz_t, mpz_srcptr, mpz_ptr +from sage.libs.gmp.mpz cimport mpz_set +from sage.libs.flint.types cimport fmpz, fmpz_t +from sage.libs.flint.fmpz cimport fmpz_get_mpz, fmpz_set_mpz +from sage.libs.flint.fmpz_mat cimport fmpz_mat_entry, fmpz_mat_nrows, fmpz_mat_ncols +from sage.libs.flint.fmpz_poly cimport fmpz_poly_set_coeff_mpz, fmpz_poly_fit_length, _fmpz_poly_set_length, fmpz_poly_one + +cdef extern from "givaro/givconfig.h": + pass +cdef extern from "linbox/linbox-config.h": + pass + +cdef extern from "gmp++/gmp++.h": + cdef cppclass GivaroInteger "Givaro::Integer": + mpz_ptr get_mpz() + mpz_srcptr get_mpz_const() + +cdef extern from "linbox/matrix/dense-matrix.h": + ## template + ## using DenseMatrix = BlasMatrix<_Field> ; + ## + # indirect include from linbox/matrix/densematrix/blas-matrix.h + ## + ## template + ## class BlasMatrix + cdef cppclass LinBoxIntegerDenseMatrix "LinBox::DenseMatrix>": + ctypedef GivaroIntegerRing Field + ctypedef GivaroInteger Element + size_t rowdim() + size_t coldim() + LinBoxIntegerDenseMatrix(Field &F, size_t m, size_t n) + void setEntry(size_t i, size_t j, Element &a) + Element &getEntry(size_t i, size_t j) + +cdef extern from "givaro/zring.h": + cdef cppclass GivaroIntegerRing "Givaro::ZRing": + ctypedef GivaroInteger Element + + +cdef extern from "givaro/givpoly1.h": + ## template < typename T, typename A=std::allocator > + ## class givvector : public __GIV_STANDARD_VECTOR + cdef cppclass givvector "Givaro::givvector" [T,ALLOCATOR=*]: + T& operator[](size_t i) + size_t size() + +cdef extern from "linbox/ring/givaro-polynomial.h": + ## template + ## class GivPolynomialRing : public Givaro::Poly1FactorDom< Domain,StorageTag> + cdef cppclass LinBoxIntegerPolynomialRing "LinBox::GivPolynomialRing, Givaro::Dense>": + ctypedef givvector[GivaroInteger] Element + ctypedef givvector[GivaroInteger] Polynomial + +cdef extern from "linbox/matrix/matrix-domain.h": + ## template + ## class MatrixDomain : public MVProductDomain { + cdef cppclass LinBoxIntegerDenseMatrixDomain "LinBox::MatrixDomain>": + LinBoxIntegerDenseMatrixDomain(GivaroIntegerRing&) + LinBoxIntegerDenseMatrix& mul(LinBoxIntegerDenseMatrix ans, + LinBoxIntegerDenseMatrix left, + LinBoxIntegerDenseMatrix right) + +cdef extern from "linbox/solutions/charpoly.h": + ## template + ## Polynomial &charpoly (Polynomial & P, const Blackbox & A) + LinBoxIntegerPolynomialRing.Element& LinBoxIntegerDense_charpoly "LinBox::charpoly" (LinBoxIntegerPolynomialRing.Element&, LinBoxIntegerDenseMatrix&) + +cdef extern from "linbox/solutions/minpoly.h": + ## template + ## Polynomial &minpoly (Polynomial & P, const Blackbox & A) + LinBoxIntegerPolynomialRing.Element& LinBoxIntegerDense_minpoly "LinBox::minpoly" (LinBoxIntegerPolynomialRing.Element&, LinBoxIntegerDenseMatrix&) + +cdef extern from "linbox/solutions/rank.h": + ## template + ## inline unsigned long &rank (unsigned long &r, const Blackbox &A, + ## const DomainCategory &tag, const Method &M); + unsigned long & LinBoxIntegerDense_rank "LinBox::rank" (unsigned long &, LinBoxIntegerDenseMatrix) + +cdef extern from "linbox/solutions/det.h": + GivaroInteger& LinBoxIntegerDense_det "LinBox::det" (GivaroInteger&, LinBoxIntegerDenseMatrix) + + + +############################################################################### +# end of LinBox declarations -- begining of the code # +############################################################################### + + +# set the entries of A from m (no allocation performed) +# NOTE: this function is not part of the interface (ie the .pxd file) to keep the +# module C-compatible +cdef void fmpz_mat_get_linbox(LinBoxIntegerDenseMatrix& A, fmpz_mat_t m): + cdef size_t i,j + cdef GivaroInteger t + + for i in range(fmpz_mat_nrows(m)): + for j in range(fmpz_mat_ncols(m)): + fmpz_get_mpz(t.get_mpz(), fmpz_mat_entry(m, i, j)) + A.setEntry(i, j, t) + + +# set the entries of m from A (no allocation performed) +# NOTE: this function is not part of the interface (ie the .pxd file) to keep the +# module C-compatible +cdef void fmpz_mat_set_linbox(fmpz_mat_t m, LinBoxIntegerDenseMatrix& A): + cdef size_t i,j + for i in range(A.rowdim()): + for j in range(A.coldim()): + fmpz_set_mpz(fmpz_mat_entry(m, i, j), A.getEntry(i, j).get_mpz_const()) + + +# set the entries of the polynomial p from q (no allocation performed) +# NOTE: this function is not part of the interface (ie the .pxd file) to keep the +# module C-compatible +cdef void fmpz_poly_set_linbox(fmpz_poly_t p, LinBoxIntegerPolynomialRing.Element& q): + cdef size_t i + + fmpz_poly_fit_length(p, q.size()) + + for i in range(q.size()): + fmpz_poly_set_coeff_mpz(p, i, q[i].get_mpz_const()) + + _fmpz_poly_set_length(p, q.size()) + + +# set C <- A * B +cdef void linbox_fmpz_mat_mul(fmpz_mat_t C, fmpz_mat_t A, fmpz_mat_t B): + cdef GivaroIntegerRing ZZ + cdef LinBoxIntegerDenseMatrix *LBA + cdef LinBoxIntegerDenseMatrix *LBB + cdef LinBoxIntegerDenseMatrix *LBC + cdef LinBoxIntegerDenseMatrixDomain * MD + + LBA = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(A)) + fmpz_mat_get_linbox(LBA[0], A) + + LBB = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(B), fmpz_mat_ncols(B)) + fmpz_mat_get_linbox(LBB[0], B) + + LBC = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(B)) + + MD = new LinBoxIntegerDenseMatrixDomain(ZZ) + MD.mul(LBC[0], LBA[0], LBB[0]) + + del MD + + fmpz_mat_set_linbox(C, LBC[0]) + + +# set cp to the characteristic polynomial of A +cdef void linbox_fmpz_mat_charpoly(fmpz_poly_t cp, fmpz_mat_t A): + cdef GivaroIntegerRing ZZ + cdef LinBoxIntegerDenseMatrix * LBA + cdef LinBoxIntegerPolynomialRing.Element m_A + + # FIXME: bug in LinBox + # see https://github.com/linbox-team/linbox/issues/51 + if fmpz_mat_nrows(A) == 0: + fmpz_poly_one(cp) + return + + LBA = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(A)) + fmpz_mat_get_linbox(LBA[0], A) + LinBoxIntegerDense_charpoly(m_A, LBA[0]) + fmpz_poly_set_linbox(cp, m_A) + + del LBA + + +# set mp to the minimal polynomial of A +cdef void linbox_fmpz_mat_minpoly(fmpz_poly_t mp, fmpz_mat_t A): + cdef GivaroIntegerRing ZZ + cdef LinBoxIntegerDenseMatrix * LBA + cdef LinBoxIntegerPolynomialRing.Element m_A + + # FIXME: bug in LinBox + # see https://github.com/linbox-team/linbox/issues/51 + if fmpz_mat_nrows(A) == 0: + fmpz_poly_one(mp) + return + + LBA = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(A)) + fmpz_mat_get_linbox(LBA[0], A) + LinBoxIntegerDense_minpoly(m_A, LBA[0]) + fmpz_poly_set_linbox(mp, m_A) + + del LBA + + +# return the rank of A +cdef unsigned long linbox_fmpz_mat_rank(fmpz_mat_t A): + cdef GivaroIntegerRing ZZ + cdef LinBoxIntegerDenseMatrix * LBA + cdef unsigned long r = 0 + + LBA = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(A)) + fmpz_mat_get_linbox(LBA[0], A) + LinBoxIntegerDense_rank(r, LBA[0]) + + del LBA + + return r + + +# set det to the determinant of A +cdef void linbox_fmpz_mat_det(fmpz_t det, fmpz_mat_t A): + cdef GivaroIntegerRing ZZ + cdef LinBoxIntegerDenseMatrix * LBA + cdef GivaroInteger d + + LBA = new LinBoxIntegerDenseMatrix(ZZ, fmpz_mat_nrows(A), fmpz_mat_ncols(A)) + fmpz_mat_get_linbox(LBA[0], A) + LinBoxIntegerDense_det(d, LBA[0]) + fmpz_set_mpz(det, d.get_mpz_const()) + + del LBA diff --git a/src/sage/libs/linkages/padics/API.pxi b/src/sage/libs/linkages/padics/API.pxi index f15c50740b1..576d66b492d 100644 --- a/src/sage/libs/linkages/padics/API.pxi +++ b/src/sage/libs/linkages/padics/API.pxi @@ -329,7 +329,7 @@ cdef inline int cdivunit(celement out, celement a, celement b, long prec, PowCom """ Division. - The inversion is perfomed modulo p^prec. Note that no reduction + The inversion is performed modulo p^prec. Note that no reduction is performed after the product. INPUT: diff --git a/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi index f5915a824be..830d09dd8ac 100644 --- a/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi +++ b/src/sage/libs/linkages/padics/fmpz_poly_unram.pxi @@ -17,10 +17,11 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off from cpython.list cimport PyList_Check, PyList_New, PyList_Append +from sage.ext.stdsage cimport PY_NEW + 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 @@ -370,7 +371,7 @@ cdef inline int cdivunit(celement out, celement a, celement b, long prec, PowCom """ Division. - The inversion is perfomed modulo p^prec. Note that no reduction + The inversion is performed modulo p^prec. Note that no reduction is performed after the product. INPUT: diff --git a/src/sage/libs/linkages/padics/mpz.pxi b/src/sage/libs/linkages/padics/mpz.pxi index 8bb4d23a232..5a1c11b3192 100644 --- a/src/sage/libs/linkages/padics/mpz.pxi +++ b/src/sage/libs/linkages/padics/mpz.pxi @@ -16,10 +16,10 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off from cpython.list cimport * +from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.mpz cimport * from sage.libs.gmp.pylong cimport mpz_pythonhash from sage.arith.rational_reconstruction cimport mpq_rational_reconstruction @@ -327,7 +327,7 @@ cdef inline int cdivunit(mpz_t out, mpz_t a, mpz_t b, long prec, PowComputer_ pr """ Division. - The inversion is perfomed modulo p^prec. Note that no reduction + The inversion is performed modulo p^prec. Note that no reduction is performed after the product. INPUT: diff --git a/src/sage/libs/meataxe.pxd b/src/sage/libs/meataxe.pxd index 68878f3fa19..780e2ea9b46 100644 --- a/src/sage/libs/meataxe.pxd +++ b/src/sage/libs/meataxe.pxd @@ -30,7 +30,7 @@ cdef extern from "meataxe.h": cdef extern size_t FfCurrentRowSize # The byte size of a single row in memory, # always a multiple of sizeof(long) cdef extern size_t FfCurrentRowSizeIo # The number of bytes actually used in a row. - cdef extern char MtxLibDir[250] # Where to search/create multiplication tables + cdef extern char MtxLibDir[1024] # Where to search/create multiplication tables # we only wrap MeatAxe for small fields (size < 255) cdef extern FEL mtx_tmult[256][256] diff --git a/src/sage/libs/mpmath/ext_impl.pyx b/src/sage/libs/mpmath/ext_impl.pyx index f143beab906..6a5a3469e37 100644 --- a/src/sage/libs/mpmath/ext_impl.pyx +++ b/src/sage/libs/mpmath/ext_impl.pyx @@ -16,10 +16,17 @@ See if :trac:`15118` is fixed:: ... ZeroDivisionError """ -from __future__ import print_function -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +#***************************************************************************** +# 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 __future__ import absolute_import, print_function + from cpython.int cimport * from cpython.long cimport * from cpython.float cimport * @@ -29,6 +36,9 @@ from cpython.number cimport * from libc.math cimport sqrt as fsqrt from libc.math cimport frexp +from cysignals.signals cimport sig_check + +from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.all cimport * from sage.libs.mpfr cimport * from sage.rings.integer cimport Integer diff --git a/src/sage/libs/mpmath/ext_main.pyx b/src/sage/libs/mpmath/ext_main.pyx index 77afb2b5bcb..a7a278be4ff 100644 --- a/src/sage/libs/mpmath/ext_main.pyx +++ b/src/sage/libs/mpmath/ext_main.pyx @@ -5,16 +5,27 @@ Implements mpf and mpc types, with binary operations and support for interaction with other types. Also implements the main context class, and related utilities. """ -from __future__ import print_function -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +#***************************************************************************** +# 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 __future__ import absolute_import, print_function + from cpython.int cimport * from cpython.long cimport * from cpython.float cimport * from cpython.complex cimport * from cpython.number cimport * +from cysignals.signals cimport sig_on, sig_off + +from sage.ext.stdsage cimport PY_NEW + from sage.libs.gmp.all cimport * from sage.rings.integer cimport Integer diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 4c29605ae68..17f143e2f79 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -14,8 +14,8 @@ #***************************************************************************** from __future__ import print_function -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +from cysignals.signals cimport sig_on, sig_off + include 'misc.pxi' include 'decl.pxi' @@ -167,7 +167,7 @@ cdef class ntl_ZZ(object): Agrees with the hash of the corresponding sage integer. """ - cdef Integer v = PY_NEW(Integer) + cdef Integer v = Integer.__new__(Integer) ZZ_to_mpz(v.value, &self.x) return v.hash_c() @@ -293,7 +293,7 @@ cdef class ntl_ZZ(object): AUTHOR: Joel B. Mohler """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) ZZ_to_mpz(ans.value, &self.x) return ans #return (ZZ_sage)._coerce_ZZ(&self.x) diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pxd b/src/sage/libs/ntl/ntl_ZZ_p.pxd index 51048ccb5f7..472587744db 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pxd +++ b/src/sage/libs/ntl/ntl_ZZ_p.pxd @@ -4,6 +4,6 @@ from .ntl_ZZ_pContext cimport ntl_ZZ_pContext_class cdef class ntl_ZZ_p(object): cdef ZZ_p_c x cdef ntl_ZZ_pContext_class c - cdef public int get_as_int(ntl_ZZ_p self) - cdef public void set_from_int(ntl_ZZ_p self, int value) + cdef int get_as_int(ntl_ZZ_p self) + cdef void set_from_int(ntl_ZZ_p self, int value) cdef ntl_ZZ_p _new(self) diff --git a/src/sage/libs/ntl/ntl_ZZ_pE.pxd b/src/sage/libs/ntl/ntl_ZZ_pE.pxd index 321e7bcb1e3..d7d265b437d 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pE.pxd +++ b/src/sage/libs/ntl/ntl_ZZ_pE.pxd @@ -5,6 +5,6 @@ from .ntl_ZZ_pX cimport ntl_ZZ_pX cdef class ntl_ZZ_pE(object): cdef ZZ_pE_c x cdef ntl_ZZ_pEContext_class c - cdef public ntl_ZZ_pX get_as_ZZ_pX(ntl_ZZ_pE self) - cdef public void set_from_ZZ_pX(ntl_ZZ_pE self, ntl_ZZ_pX value) + cdef ntl_ZZ_pX get_as_ZZ_pX(ntl_ZZ_pE self) + cdef void set_from_ZZ_pX(ntl_ZZ_pE self, ntl_ZZ_pX value) cdef ntl_ZZ_pE _new(self) diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index 9fe37f096f3..7fa70db4441 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -15,8 +15,8 @@ from __future__ import absolute_import, division, print_function -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +from cysignals.signals cimport sig_on, sig_off + include 'misc.pxi' include 'decl.pxi' @@ -1421,7 +1421,7 @@ cdef class ntl_ZZ_pX_Modulus(object): return "NTL ZZ_pXModulus %s (mod %s)"%(self.poly, self.poly.c.p) def degree(self): - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_ui(ans.value, ZZ_pX_Modulus_deg(self.x)) return ans diff --git a/src/sage/libs/pari/__init__.py b/src/sage/libs/pari/__init__.py index 59f05365bdd..5f7f18333cb 100644 --- a/src/sage/libs/pari/__init__.py +++ b/src/sage/libs/pari/__init__.py @@ -130,7 +130,7 @@ In these examples, we convert to Sage to ensure that PARI's real precision is not used when printing the numbers. As explained before, -this artificically increases the precision to a multiple of the +this artificially increases the precision to a multiple of the wordsize. :: sage: s = pari(1).sin(precision=180).sage(); print(s); print(parent(s)) diff --git a/src/sage/libs/ppl.pyx b/src/sage/libs/ppl.pyx index 749826f760b..6550429601c 100644 --- a/src/sage/libs/ppl.pyx +++ b/src/sage/libs/ppl.pyx @@ -82,7 +82,7 @@ documentation, in particular: 5 * PPL supports (topologically) closed polyhedra - (:class:`C_Polyhedron`) as well as not neccesarily closed polyhedra + (:class:`C_Polyhedron`) as well as not necessarily closed polyhedra (:class:`NNC_Polyhedron`). Only the latter allows closure points (=points of the closure but not of the actual polyhedron) and strict inequalities (``>`` and ``<``) @@ -2703,7 +2703,7 @@ cdef class Polyhedron(_mutable_or_immutable): .. NOTE:: - The modified polyhedron is not neccessarily a lattice + The modified polyhedron is not necessarily a lattice polyhedron; Some vertices will, in general, still be rational. Lattice points interior to the polyhedron may be lost in the process. diff --git a/src/sage/libs/pynac/pynac.pxd b/src/sage/libs/pynac/pynac.pxd index 17c9a355a22..20907bbdf84 100644 --- a/src/sage/libs/pynac/pynac.pxd +++ b/src/sage/libs/pynac/pynac.pxd @@ -421,7 +421,7 @@ cdef extern from "sage/libs/pynac/wrap.h": unsigned g_register_new "GiNaC::function::register_new" (GFunctionOpt opt) unsigned find_function "GiNaC::function::find_function" (char* name, - unsigned nargs) except +ValueError + unsigned nargs) except + bint has_symbol "GiNaC::has_symbol" (GEx ex) bint has_symbol_or_function "GiNaC::has_symbol_or_function" (GEx ex) diff --git a/src/sage/libs/pynac/pynac.pyx b/src/sage/libs/pynac/pynac.pyx index 68eff58c2cf..733dc6c2bfe 100644 --- a/src/sage/libs/pynac/pynac.pyx +++ b/src/sage/libs/pynac/pynac.pyx @@ -1791,19 +1791,40 @@ cdef py_atan2(x, y): 2.284887025407... sage: atan2(2.1000000000000000000000000000000000000, -1.20000000000000000000000000000000) 2.089942441041419571002776071... + + Check that :trac:`22877` is fixed:: + + sage: atan2(CC(I), CC(I+1)) + 0.553574358897045 + 0.402359478108525*I + sage: atan2(CBF(I), CBF(I+1)) + [0.55357435889705 +/- 5.75e-15] + [0.40235947810852 +/- 6.01e-15]*I """ from sage.symbolic.constants import pi, NaN + from sage.rings.real_arb import RealBallField + from sage.rings.real_mpfr import RealField_class P = coercion_model.common_parent(x, y) + is_real = False if P is ZZ: P = RR + if (P is float + or parent(P) is RealField_class + or isinstance(P, RealBallField)): + is_real = True if y != 0: - if x > 0: - res = py_atan(abs(y/x)) - elif x < 0: - res = P(pi) - py_atan(abs(y/x)) + try: + is_real = is_real or (x.is_real() and y.is_real()) + except AttributeError: + is_real = False + if is_real: + if x > 0: + res = py_atan(abs(y/x)) + elif x < 0: + res = P(pi) - py_atan(abs(y/x)) + else: + res = P(pi)/2 + return res if y > 0 else -res else: - res = P(pi)/2 - return res if y > 0 else -res + return -I*py_log((x + I*y)/py_sqrt(x**2 + y**2)) else: if x > 0: return P(0) diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 019f30887ae..d0c13b8d4cf 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -74,6 +74,7 @@ TESTS:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import from libc.string cimport memcpy @@ -515,7 +516,7 @@ cdef class Converter(SageObject): if ring is not None: self._singular_ring = access_singular_ring(ring) - from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense + from sage.matrix.matrix_mpolynomial_dense import Matrix_mpolynomial_dense from sage.matrix.matrix_integer_dense import Matrix_integer_dense from sage.matrix.matrix_generic_dense import Matrix_generic_dense for a in args: @@ -1397,7 +1398,7 @@ The Singular documentation for '%s' is given below. - ``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_mpolynomial_dense import Matrix_mpolynomial_dense from sage.matrix.matrix_integer_dense import Matrix_integer_dense ring2 = None for a in args: diff --git a/src/sage/libs/sirocco.pyx b/src/sage/libs/sirocco.pyx index 77e0484ed99..f3b4fabbc90 100644 --- a/src/sage/libs/sirocco.pyx +++ b/src/sage/libs/sirocco.pyx @@ -9,16 +9,13 @@ AUTHORS: - Miguel Marco (2016-07-19): initial version. """ -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +from cysignals.signals cimport sig_on, sig_off +from cysignals.memory cimport check_allocarray, sig_free as free from sage.libs.mpfr cimport * from sage.rings.real_mpfr cimport RealNumber from sage.rings.real_mpfr import RealField -cdef extern from "stdlib.h": - void free(void* ptr) - cdef extern from "sirocco.h": mpfr_t* homotopyPath_mp(int degree, mpfr_t *_coef, mpfr_t _y0R, mpfr_t _y0I, int prec) double* homotopyPath(int degree, double *_coef, double _y0R, double _y0I) @@ -29,12 +26,12 @@ cpdef list[list] contpath_mp(int deg, list values, RealNumber y0r, RealNumber y0 """ Mimics :func:`contpath`, but with the following differences: - - The floating point numbers can be arbitrary precission RealNumbers. + - The floating point numbers can be arbitrary precision RealNumbers. - - A extra argument is needed, indicating the bits of precission used + - A extra argument is needed, indicating the bits of precision used in the computations. """ - cdef mpfr_t* cvalues = sage_malloc(sizeof(mpfr_t)*len(values)) + cdef mpfr_t* cvalues = check_allocarray(len(values), sizeof(mpfr_t)) cdef mpfr_t* rop cdef int i, j cdef mpfr_t y0R @@ -98,7 +95,7 @@ cpdef list[list] contpath(int deg, list values, double y0r, double y0i): the piecewise linear approximation of the path tracked by the root. """ cdef double* rop - cdef double* c_values = sage_malloc(sizeof(double)*len(values)) + cdef double* c_values = check_allocarray(len(values), sizeof(double)) cdef int clen = len(values) cdef int i for i,v in enumerate(values): diff --git a/src/sage/logic/logic.py b/src/sage/logic/logic.py index 1fc28582cfd..db4eb281c46 100644 --- a/src/sage/logic/logic.py +++ b/src/sage/logic/logic.py @@ -278,7 +278,7 @@ def combine(self, statement1, statement2): OUTPUT: - A new staement which or'd the given statements together. + A new statement which or'd the given statements together. EXAMPLES:: diff --git a/src/sage/logic/propcalc.py b/src/sage/logic/propcalc.py index 6d17a5330b7..fafc7bfea5d 100644 --- a/src/sage/logic/propcalc.py +++ b/src/sage/logic/propcalc.py @@ -307,7 +307,7 @@ def consistent(*formulas): for formula in formulas[1:]: conjunction = conjunction & formula - # if conjnction is a contradiction, the formulas are inconsistent + # if conjunction is a contradiction, the formulas are inconsistent return not conjunction.is_contradiction() def valid_consequence(consequence, *formulas): diff --git a/src/sage/manifolds/continuous_map.py b/src/sage/manifolds/continuous_map.py index 08eb1ca432a..f74e3e9f4df 100644 --- a/src/sage/manifolds/continuous_map.py +++ b/src/sage/manifolds/continuous_map.py @@ -112,7 +112,7 @@ class ContinuousMap(Morphism): sage: Phi.parent() is Hom(M, N) True sage: type(Phi) - + sage: Phi.display() Phi: S^2 --> R^3 on U: (x, y) |--> (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), (x^2 + y^2 - 1)/(x^2 + y^2 + 1)) diff --git a/src/sage/manifolds/differentiable/curve.py b/src/sage/manifolds/differentiable/curve.py index 0bc6c4544bb..4c44dbcc919 100644 --- a/src/sage/manifolds/differentiable/curve.py +++ b/src/sage/manifolds/differentiable/curve.py @@ -82,7 +82,7 @@ class DifferentiableCurve(DiffMap): sage: c = M.curve({X: [sin(t), sin(2*t)/2]}, (t, 0, 2*pi), name='c') ; c Curve c in the 2-dimensional differentiable manifold M sage: type(c) - + Instead of declaring the parameter `t` as a symbolic variable by means of ``var('t')``, it is equivalent to get it as the canonical coordinate @@ -326,7 +326,7 @@ def __reduce__(self): sage: R. = RealLine() sage: c = M.curve([cos(t), sin(2*t)], (t, 0, 2*pi)) sage: c.__reduce__() - (, + (, (Set of Morphisms from Real interval (0, 2*pi) to 2-dimensional differentiable manifold M in Join of Category of subobjects of sets and Category of smooth manifolds over Real Field with 53 diff --git a/src/sage/manifolds/differentiable/diff_map.py b/src/sage/manifolds/differentiable/diff_map.py index 6740d021cdc..946c7eec565 100644 --- a/src/sage/manifolds/differentiable/diff_map.py +++ b/src/sage/manifolds/differentiable/diff_map.py @@ -117,7 +117,7 @@ class is sage: Phi.parent() is Hom(M, N) True sage: type(Phi) - + sage: Phi.display() Phi: S^2 --> R^3 on U: (x, y) |--> (X, Y, Z) = (2*x/(x^2 + y^2 + 1), 2*y/(x^2 + y^2 + 1), diff --git a/src/sage/manifolds/scalarfield.py b/src/sage/manifolds/scalarfield.py index 20f2485f034..c0bf02fd41e 100644 --- a/src/sage/manifolds/scalarfield.py +++ b/src/sage/manifolds/scalarfield.py @@ -946,7 +946,7 @@ def copy(self): sage: f = M.scalar_field(x*y^2) sage: g = f.copy() sage: type(g) - + sage: g.expr() x*y^2 sage: g == f diff --git a/src/sage/manifolds/utilities.py b/src/sage/manifolds/utilities.py index 43ec9eca893..5728fd31250 100644 --- a/src/sage/manifolds/utilities.py +++ b/src/sage/manifolds/utilities.py @@ -93,7 +93,7 @@ def simplify_sqrt_real(expr): # by Pynac symbols of the type D[0] or diff(...) # Lists to store the positions of all the top-level sqrt's in sexpr: pos_sqrts = [] # position of first character, i.e. 's' of 'sqrt(...)' - pos_after = [] # position of character immediatelty after 'sqrt(...)' + pos_after = [] # position of character immediately after 'sqrt(...)' the_sqrts = [] # the sqrt sub-expressions in sexpr, i.e. 'sqrt(...)' pos_max = len(sexpr) - 6 pos = 0 diff --git a/src/sage/matrix/docs.py b/src/sage/matrix/docs.py index d9add7956f1..97091b05d6d 100644 --- a/src/sage/matrix/docs.py +++ b/src/sage/matrix/docs.py @@ -365,8 +365,8 @@ 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 sig_malloc from ext/stdsage.pxi (only - needed if allocate memory) + * __cinit__ -- should use check_allocarray from cysignals.memory + (only needed if allocate memory) * __init__ -- this signature: 'def __init__(self, parent, entries, copy, coerce)' * __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 diff --git a/src/sage/matrix/matrix0.pyx b/src/sage/matrix/matrix0.pyx index 48d9980e1b5..9fb31698cf9 100644 --- a/src/sage/matrix/matrix0.pyx +++ b/src/sage/matrix/matrix0.pyx @@ -22,7 +22,7 @@ EXAMPLES:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function +from __future__ import print_function, absolute_import from cpython cimport * @@ -30,13 +30,13 @@ import sage.modules.free_module import sage.misc.latex import sage.rings.integer -from sage.misc.misc import verbose, get_verbose -from sage.structure.sequence import Sequence +from sage.misc.misc import verbose, get_verbose +from sage.structure.sequence import Sequence cimport sage.structure.element -from sage.structure.element cimport ModuleElement, Element, RingElement, Vector -from sage.structure.mutability cimport Mutability -from sage.misc.misc_c cimport normalize_index +from sage.structure.element cimport ModuleElement, Element, RingElement, Vector +from sage.structure.mutability cimport Mutability +from sage.misc.misc_c cimport normalize_index from sage.rings.ring cimport CommutativeRing from sage.rings.ring import is_Ring @@ -44,7 +44,7 @@ from sage.rings.finite_rings.integer_mod_ring import is_IntegerModRing import sage.modules.free_module -import matrix_misc +from .matrix_misc import row_iterator cdef class Matrix(sage.structure.element.Matrix): @@ -529,16 +529,15 @@ cdef class Matrix(sage.structure.element.Matrix): def __iter__(self): """ - Return an iterator for the rows of self + Return an iterator for the rows of self. EXAMPLES:: - sage: m=matrix(2,[1,2,3,4]) + sage: m = matrix(2,[1,2,3,4]) sage: next(m.__iter__()) (1, 2) """ - - return matrix_misc.row_iterator(self) + return row_iterator(self) def __getitem__(self, key): """ @@ -639,20 +638,20 @@ cdef class Matrix(sage.structure.element.Matrix): More examples:: - sage: M[range(2),:] + sage: M[list(range(2)),:] [ 1 -2 -1 -1 9] [ 1 8 6 2 2] - sage: M[range(2),4] + sage: M[list(range(2)),4] [9] [2] - sage: M[range(3),range(5)] + sage: M[list(range(3)),list(range(5))] [ 1 -2 -1 -1 9] [ 1 8 6 2 2] [ 1 1 -1 1 4] :: - sage: M[3,range(5)] + sage: M[3,list(range(5))] [-1 2 -2 -1 4] sage: M[3,:] [-1 2 -2 -1 4] @@ -701,14 +700,14 @@ cdef class Matrix(sage.structure.element.Matrix): :: sage: A= matrix(3,4,[1, 0, -3, -1, 3, 0, -2, 1, -3, -5, -1, -5]) - sage: A[range(2,-1,-1),:] + sage: A[list(range(2,-1,-1)),:] [-3 -5 -1 -5] [ 3 0 -2 1] [ 1 0 -3 -1] :: - sage: A[range(2,-1,-1),range(3,-1,-1)] + sage: A[list(range(2,-1,-1)),list(range(3,-1,-1))] [-5 -1 -5 -3] [ 1 -2 0 3] [-1 -3 0 1] @@ -729,7 +728,7 @@ cdef class Matrix(sage.structure.element.Matrix): [] sage: M[2:3, 3:3] [] - sage: M[range(2,2), :3] + sage: M[list(range(2,2)), :3] [] sage: M[(1,2), 3] [ 7] @@ -1157,26 +1156,26 @@ cdef class Matrix(sage.structure.element.Matrix): More examples:: - sage: M[range(2),:]=[[1..5], [6..10]]; M + sage: M[list(range(2)),:]=[[1..5], [6..10]]; M [ 1 2 3 4 5] [ 6 7 8 9 10] [30 -1 2 -2 4] [30 2 -2 -1 4] - sage: M[range(2),4]=0; M + sage: M[list(range(2)),4]=0; M [ 1 2 3 4 0] [ 6 7 8 9 0] [30 -1 2 -2 4] [30 2 -2 -1 4] - sage: M[range(3),range(5)]=M[range(1,4), :]; M + sage: M[list(range(3)),list(range(5))]=M[list(range(1,4)), :]; M [ 6 7 8 9 0] [30 -1 2 -2 4] [30 2 -2 -1 4] [30 2 -2 -1 4] - sage: M[3,range(5)]=vector([-2,3,4,-5,4]); M + sage: M[3,list(range(5))]=vector([-2,3,4,-5,4]); M [ 6 7 8 9 0] [30 -1 2 -2 4] [30 2 -2 -1 4] @@ -1204,12 +1203,12 @@ cdef class Matrix(sage.structure.element.Matrix): [ 3 0 -2 1] [-3 -5 -1 -5] - sage: A[range(2,-1,-1),:]=A; A + sage: A[list(range(2,-1,-1)),:]=A; A [-3 -5 -1 -5] [ 3 0 -2 1] [ 1 0 -3 -1] - sage: A[range(2,-1,-1),range(3,-1,-1)]=A; A + sage: A[list(range(2,-1,-1)),list(range(3,-1,-1))]=A; A [-1 -3 0 1] [ 1 -2 0 3] [-5 -1 -5 -3] @@ -1232,7 +1231,7 @@ cdef class Matrix(sage.structure.element.Matrix): [ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] - sage: M[range(2,2), :3]=20; M + sage: M[list(range(2,2)), :3]=20; M [ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] @@ -2149,7 +2148,7 @@ cdef class Matrix(sage.structure.element.Matrix): [ 5 25] [125 625] """ - from constructor import matrix + from .constructor import matrix return matrix(self.nrows(), self.ncols(), [e(*args, **kwargs) for e in self.list()]) ################################################### @@ -3283,6 +3282,67 @@ cdef class Matrix(sage.structure.element.Matrix): self.set_unsafe(i,l,-A.get_unsafe(r,k)) #self[i,l] = -A[r,k] l += 1 + def reverse_rows_and_columns(self): + r""" + Reverse the row order and column order of this matrix. + + This method transforms a matrix `m_{i,j}` with `0 \leq i < nrows` and + `0 \leq j < ncols` into `m_{nrows - i - 1, ncols - j - 1}`. + + EXAMPLES:: + + sage: m = matrix(ZZ, 2, 2, range(4)) + sage: m.reverse_rows_and_columns() + sage: m + [3 2] + [1 0] + + sage: m = matrix(ZZ, 2, 3, range(6), sparse=True) + sage: m.reverse_rows_and_columns() + sage: m + [5 4 3] + [2 1 0] + sage: m = matrix(ZZ, 3, 2, range(6), sparse=True) + sage: m.reverse_rows_and_columns() + sage: m + [5 4] + [3 2] + [1 0] + sage: m.reverse_rows_and_columns() + sage: m + [0 1] + [2 3] + [4 5] + + sage: m = matrix(QQ, 3, 2, [1/i for i in range(1,7)]) + sage: m.reverse_rows_and_columns() + sage: m + [1/6 1/5] + [1/4 1/3] + [1/2 1] + + sage: R. = ZZ['x,y'] + sage: m = matrix(R, 3, 3, lambda i,j: x**i*y**j, sparse=True) + sage: m.reverse_rows_and_columns() + sage: m + [x^2*y^2 x^2*y x^2] + [ x*y^2 x*y x] + [ y^2 y 1] + + If the matrix is immutable, the method raises an error:: + + sage: m = matrix(ZZ, 2, [1, 3, -2, 4]) + sage: m.set_immutable() + sage: m.reverse_rows_and_columns() + Traceback (most recent call last): + ... + ValueError: matrix is immutable; please change a copy + instead (i.e., use copy(M) to change a copy of M). + """ + self.check_mutability() + self.clear_cache() + self._reverse_unsafe() + ################################################### # Methods needed for quiver and cluster mutations # - mutate @@ -3479,7 +3539,7 @@ cdef class Matrix(sage.structure.element.Matrix): else: L.extend( L_prime ) if return_diag: - return [ d[i] for i in xrange(self._nrows) ] + return [d[i] for i in xrange(self._nrows)] else: return True @@ -3557,9 +3617,9 @@ cdef class Matrix(sage.structure.element.Matrix): """ if len(v) > self._nrows: raise ValueError("length of v must be at most the number of rows of self") - if self._nrows == 0: + if not self._nrows: return self.parent().row_space().zero_vector() - from constructor import matrix + from .constructor import matrix v = matrix(list(v)+[0]*(self._nrows-len(v))) return (v * self)[0] @@ -3634,9 +3694,9 @@ cdef class Matrix(sage.structure.element.Matrix): """ if len(v) > self._ncols: raise ValueError("length of v must be at most the number of columns of self") - if self._ncols == 0: + if not self._ncols: return self.parent().column_space().zero_vector() - from constructor import matrix + from .constructor import matrix v = matrix(self._ncols, 1, list(v)+[0]*(self._ncols-len(v))) return (self * v).column(0) diff --git a/src/sage/matrix/matrix1.pyx b/src/sage/matrix/matrix1.pyx index 31ad8b27ee5..b9c35be1aed 100644 --- a/src/sage/matrix/matrix1.pyx +++ b/src/sage/matrix/matrix1.pyx @@ -1724,8 +1724,10 @@ cdef class Matrix(matrix0.Matrix): * ``dcols`` - list of indices of columns to be deleted from self. * ``check`` - checks whether any index in ``dcols`` is out of range. Defaults to ``True``. - SEE ALSO: - The methods :meth:`delete_rows` and :meth:`matrix_from_columns` are related. + .. SEEALSO:: + + The methods :meth:`delete_rows` and :meth:`matrix_from_columns` + are related. EXAMPLES:: @@ -1824,8 +1826,10 @@ cdef class Matrix(matrix0.Matrix): * ``drows`` - list of indices of rows to be deleted from self. * ``check`` - checks whether any index in ``drows`` is out of range. Defaults to ``True``. - SEE ALSO: - The methods :meth:`delete_columns` and :meth:`matrix_from_rows` are related. + .. SEEALSO:: + + The methods :meth:`delete_columns` and :meth:`matrix_from_rows` + are related. EXAMPLES:: @@ -1968,12 +1972,12 @@ cdef class Matrix(matrix0.Matrix): take. If not provided, take all rows below and all columns to the right of the starting entry. - SEE ALSO: + .. SEEALSO:: - The functions :func:`matrix_from_rows`, - :func:`matrix_from_columns`, and - :func:`matrix_from_rows_and_columns` allow one to select - arbitrary subsets of rows and/or columns. + The functions :func:`matrix_from_rows`, + :func:`matrix_from_columns`, and + :func:`matrix_from_rows_and_columns` allow one to select + arbitrary subsets of rows and/or columns. EXAMPLES: diff --git a/src/sage/matrix/matrix_dense.pyx b/src/sage/matrix/matrix_dense.pyx index bf357c4ee23..7f71a383565 100644 --- a/src/sage/matrix/matrix_dense.pyx +++ b/src/sage/matrix/matrix_dense.pyx @@ -225,6 +225,33 @@ cdef class Matrix_dense(matrix.Matrix): [nr - t for t in reversed(row_divs)]) return atrans + def _reverse_unsafe(self): + r""" + TESTS:: + + sage: m = matrix(QQ, 2, 3, range(6)) + sage: m._reverse_unsafe() + sage: m + [5 4 3] + [2 1 0] + """ + cdef Py_ssize_t i, j + cdef Py_ssize_t nrows = self._nrows + cdef Py_ssize_t ncols = self._ncols + for i in range(nrows // 2): + for j in range(ncols): + e1 = self.get_unsafe(i, j) + e2 = self.get_unsafe(nrows - i - 1, ncols - j - 1) + self.set_unsafe(i, j, e2) + self.set_unsafe(nrows - i - 1, ncols - j - 1, e1) + if nrows % 2 == 1: + i = nrows // 2 + for j in range(ncols // 2): + e1 = self.get_unsafe(i, j) + e2 = self.get_unsafe(nrows - i - 1, ncols - j - 1) + self.set_unsafe(i, j, e2) + self.set_unsafe(nrows - i - 1, ncols - j - 1, e1) + def _elementwise_product(self, right): r""" Returns the elementwise product of two dense diff --git a/src/sage/matrix/matrix_generic_dense.pyx b/src/sage/matrix/matrix_generic_dense.pyx index ae2179b4ba4..9a18f876257 100644 --- a/src/sage/matrix/matrix_generic_dense.pyx +++ b/src/sage/matrix/matrix_generic_dense.pyx @@ -146,6 +146,19 @@ cdef class Matrix_generic_dense(matrix_dense.Matrix_dense): cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): return self._entries[i*self._ncols + j] + + def _reverse_unsafe(self): + r""" + TESTS:: + + sage: m = matrix(ZZ['x,y'], 2, 3, range(6)) + sage: m._reverse_unsafe() + sage: m + [5 4 3] + [2 1 0] + """ + self._entries.reverse() + def _pickle(self): """ EXAMPLES: diff --git a/src/sage/matrix/matrix_integer_dense.pxd b/src/sage/matrix/matrix_integer_dense.pxd index 90fc6922f87..03119c03584 100644 --- a/src/sage/matrix/matrix_integer_dense.pxd +++ b/src/sage/matrix/matrix_integer_dense.pxd @@ -8,10 +8,7 @@ from sage.ext.mod_int cimport * ctypedef long* GEN cdef class Matrix_integer_dense(Matrix_dense): - cdef fmpz_mat_t _matrix # Always initialized in __cinit__ - cdef bint _initialized_mpz - cdef mpz_t * _entries # Only used if _initialized_mpz - cdef mpz_t ** _rows # Only used if _initialized_mpz + cdef fmpz_mat_t _matrix cdef object _pivots cdef int mpz_height(self, mpz_t height) except -1 cdef _mod_int_c(self, mod_int modulus) @@ -19,10 +16,6 @@ cdef class Matrix_integer_dense(Matrix_dense): cdef _pickle_version0(self) cdef _unpickle_version0(self, data) cpdef _export_as_string(self, int base=?) - cdef inline int _init_mpz(self) except -1 - cdef int _init_mpz_impl(self) except -1 - cdef inline int _init_linbox(self) except -1 - cdef void _dealloc_mpz(self) cdef void set_unsafe_mpz(self, Py_ssize_t i, Py_ssize_t j, const mpz_t value) cdef void set_unsafe_si(self, Py_ssize_t i, Py_ssize_t j, long value) cdef void set_unsafe_double(self, Py_ssize_t i, Py_ssize_t j, double value) diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 5ef68fd7d2b..8ced59f7abd 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -15,6 +15,9 @@ AUTHORS: - Vincent Delecroix (February 2015): make it faster, see :trac:`17822`. +- Vincent Delecroix (May 2017): removed duplication of entries and + cleaner linbox interface + EXAMPLES:: sage: a = matrix(ZZ, 3,3, range(9)); a @@ -46,7 +49,7 @@ TESTS:: # Copyright (C) 2006,2007 William Stein # Copyright (C) 2014 Marc Masdeu # Copyright (C) 2014 Jeroen Demeyer -# Copyright (C) 2015,2016 Vincent Delecroix +# Copyright (C) 2015,2016,2017 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 @@ -59,6 +62,11 @@ from __future__ import absolute_import, print_function from libc.stdint cimport int64_t from libc.string cimport strcpy, strlen + +from sage.ext.stdsage cimport PY_NEW +from cysignals.signals cimport sig_check, sig_on, sig_str, sig_off +from cysignals.memory cimport sig_malloc, sig_free, check_malloc + from sage.libs.gmp.mpz cimport * from sage.modules.vector_integer_dense cimport Vector_integer_dense @@ -82,13 +90,11 @@ from cypari2.stack cimport clear_stack from cypari2.paridecl cimport * ######################################################### -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" - - from sage.arith.multi_modular cimport MultiModularBasis + from sage.libs.flint.fmpz cimport * from sage.libs.flint.fmpz_mat cimport * + from sage.rings.integer cimport Integer from sage.rings.rational_field import QQ from sage.rings.real_double import RDF @@ -123,10 +129,7 @@ from sage.rings.fast_arith cimport arith_int cdef arith_int ai = arith_int() ######### linbox interface ########## -from sage.libs.linbox.linbox cimport Linbox_integer_dense -cdef Linbox_integer_dense linbox = Linbox_integer_dense() -USE_LINBOX_POLY = True - +from sage.libs.linbox.linbox_flint_interface cimport * ########## iml -- integer matrix library ########### from sage.libs.iml cimport * @@ -137,7 +140,24 @@ fplll_fp_map = {None: None, 'xd': 'dpe', 'rr': 'mpfr'} -cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse +cdef inline mpz_t * fmpz_mat_to_mpz_array(fmpz_mat_t m): + cdef mpz_t * entries = check_malloc(sizeof(mpz_t) * fmpz_mat_nrows(m) * fmpz_mat_ncols(m)) + cdef size_t i, j + cdef size_t k = 0 + for i in range(fmpz_mat_nrows(m)): + for j in range(fmpz_mat_ncols(m)): + mpz_init(entries[k]) + fmpz_get_mpz(entries[k], fmpz_mat_entry(m, i, j)) + k += 1 + return entries + +cdef inline void mpz_array_clear(mpz_t * a, size_t length): + cdef size_t i + for i in range(length): + mpz_clear(a[i]) + sig_free(a) + +cdef class Matrix_integer_dense(Matrix_dense): r""" Matrix over the integers, implemented using FLINT. @@ -200,57 +220,10 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse self._nrows = parent.nrows() self._ncols = parent.ncols() self._pivots = None - self._initialized_mpz = False - self._entries = NULL - self._rows = NULL sig_str("FLINT exception") fmpz_mat_init(self._matrix, self._nrows, self._ncols) sig_off() - cdef inline int _init_mpz(self) except -1: - if self._initialized_mpz: - return 0 - else: - return self._init_mpz_impl() - - cdef inline int _init_linbox(self) except -1: - if not self._initialized_mpz: - self._init_mpz_impl() - linbox.set(self._rows, self._nrows, self._ncols) - return 0 - - cdef int _init_mpz_impl(self) except -1: - cdef Py_ssize_t i, j, k - - sig_on() - self._rows = sig_malloc(sizeof(mpz_t*) * self._nrows) - if not self._rows: - raise MemoryError - self._entries = sig_malloc(sizeof(mpz_t) * self._nrows * self._ncols) - if not self._entries: - sig_free(self._rows) - raise MemoryError - k = 0 - for i in range(self._nrows): - self._rows[i] = self._entries + k - for j in range(self._ncols): - mpz_init(self._entries[k]) - fmpz_get_mpz(self._entries[k],fmpz_mat_entry(self._matrix,i,j)) - k += 1 - sig_off() - self._initialized_mpz = True - return 1 - - cdef void _dealloc_mpz(self): - if not self._initialized_mpz: - return - cdef Py_ssize_t k - for k in range(self._nrows * self._ncols): - mpz_clear(self._entries[k]) - sig_free(self._rows) - sig_free(self._entries) - self._initialized_mpz = False - def __hash__(self): r""" Returns hash of self. @@ -283,7 +256,6 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: del a """ fmpz_mat_clear(self._matrix) - self._dealloc_mpz() def __init__(self, parent, entries, copy, coerce): r""" @@ -463,24 +435,18 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse [ 3 4 5] """ fmpz_set_mpz(fmpz_mat_entry(self._matrix,i,j), value) - if self._initialized_mpz: - mpz_set(self._entries[i*self._ncols + j], value) cdef void set_unsafe_si(self, Py_ssize_t i, Py_ssize_t j, long value): """ Set position i,j of this matrix to ``value``. """ fmpz_set_si(fmpz_mat_entry(self._matrix,i,j), value) - if self._initialized_mpz: - mpz_set_si(self._entries[i*self._ncols + j], value) cdef void set_unsafe_double(self, Py_ssize_t i, Py_ssize_t j, double value): """ Set position i,j of this matrix to ``value``. """ fmpz_set_d(fmpz_mat_entry(self._matrix,i,j), value) - if self._initialized_mpz: - mpz_set_d(self._entries[i*self._ncols + j], value) cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): """ @@ -506,7 +472,7 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: a[-1,0] 6 """ - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) self.get_unsafe_mpz(i, j, z.value) return z @@ -796,8 +762,8 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse EXAMPLES:: - sage: A = matrix(ZZ,2,3,range(6)) - sage: A*A.transpose() + sage: A = matrix(ZZ, 2, 3, range(6)) + sage: A * A.transpose() [ 5 14] [14 50] sage: A._multiply_linbox(A.transpose()) @@ -808,38 +774,21 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse This fixes a bug found in :trac:`17094`:: - sage: A = identity_matrix(ZZ,3) + sage: A = identity_matrix(ZZ, 3) sage: A._multiply_linbox(A) [1 0 0] [0 1 0] [0 0 1] """ - cdef int e - cdef long int i,j cdef Matrix_integer_dense ans - cdef Matrix_integer_dense left = self + cdef Matrix_integer_dense left = self - if self._nrows == right._nrows: - # self acts on the space of right - parent = right.parent() - if self._ncols == right._ncols: - # right acts on the space of self - parent = self.parent() - else: - parent = self.matrix_space(left._nrows, right._ncols) - - ans = self._new(parent.nrows(),parent.ncols()) - - left._init_linbox() - right._init_mpz() - ans._init_mpz() + ans = self._new(left._nrows, right._ncols) sig_on() - linbox.matrix_matrix_multiply(ans._rows, right._rows, right._nrows, right._ncols) - for i from 0 <= i < ans._nrows: - for j from 0 <= j < ans._ncols: - fmpz_set_mpz(fmpz_mat_entry(ans._matrix,i,j),ans._rows[i][j]) + linbox_fmpz_mat_mul(ans._matrix, left._matrix, right._matrix) sig_off() + return ans def _multiply_classical(self, Matrix_integer_dense right): @@ -853,6 +802,24 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse [ 18 21 24] [ 54 66 78] [ 90 111 132] + + TESTS:: + + sage: for _ in range(100): + ....: nrows = randint(0, 10) + ....: nmid = randint(0, 10) + ....: ncols = randint(0, 10) + ....: m1 = random_matrix(ZZ, nrows, nmid) + ....: m2 = random_matrix(ZZ, nmid, ncols) + ....: ans_flint = m1 * m2 + ....: ans_classical = m1._multiply_classical(m2) + ....: ans_linbox = m1._multiply_linbox(m2) + ....: if ans_flint != ans_classical: + ....: raise RuntimeError("ERROR\nm1=\n{}\nm2=\n{}\nans_flint=\n{}\nans_classical=\n{}".format( + ....: m1.str(), m2.str(), ans_flint.str(), ans_classical.str())) + ....: if ans_flint != ans_linbox: + ....: raise RuntimeError("ERROR\nm1=\n{}\nm2=\n{}\nans_flint=\n{}\nans_linbox=\n{}".format( + ....: m1.str(), m2.str(), ans_flint.str(), ans_linbox.str())) """ if self._ncols != right._nrows: raise IndexError("Number of columns of self must equal number of rows of right.") @@ -1194,14 +1161,15 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse """ return self, ZZ(1) - def charpoly(self, var='x', algorithm='generic'): + def charpoly(self, var='x', algorithm=None): """ INPUT: - ``var`` - a variable name - - ``algorithm`` - 'generic' (default), 'flint' or 'linbox' + - ``algorithm`` - (optional) either 'generic', 'flint' or 'linbox'. + Default is set to 'linbox'. EXAMPLES:: @@ -1219,6 +1187,13 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: A.minpoly() x^3 - 3990*x^2 - 266000*x + On non square matrices, this method raises an ArithmeticError:: + + sage: matrix(ZZ, 2, 1).charpoly() + Traceback (most recent call last): + ... + ArithmeticError: only valid for square matrix + TESTS: The cached polynomial should be independent of the ``var`` @@ -1237,106 +1212,156 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: A._cache['charpoly_linbox'] x^2 - 3*x - 2 + Test corner cases:: + + sage: matrix([5]).charpoly('z', 'flint') + z - 5 + sage: matrix([5]).charpoly('z', 'linbox') + z - 5 + sage: matrix([5]).charpoly('z', 'generic') + z - 5 + + + sage: matrix([]).charpoly('y', 'flint') + 1 + sage: matrix([]).charpoly('y', 'linbox') + 1 + sage: matrix([]).charpoly('y', 'generic') + 1 + + sage: matrix([0]).charpoly('x', 'flint') + x + sage: matrix([0]).charpoly('x', 'linbox') + x + sage: matrix([0]).charpoly('x', 'generic') + x + + Consistency on random inputs:: + + sage: for _ in range(100): + ....: dim = randint(1, 20) + ....: m = random_matrix(ZZ, dim) + ....: m._clear_cache(); ans_flint = m.charpoly(algorithm='flint') + ....: m._clear_cache(); ans_linbox = m.charpoly(algorithm='linbox') + ....: m._clear_cache(); ans_generic = m.charpoly(algorithm='generic') + ....: if ans_flint != ans_linbox or ans_flint != ans_generic: + ....: raise RuntimeError("ans_flint = {}, ans_linbox = {} and ans_generic = {} for\n{}".format( + ....: ans_flint, ans_linbox, ans_generic, m.str())) """ - cdef long i,n - cdef Integer z + if self._nrows != self._ncols: + raise ArithmeticError("only valid for square matrix") + cdef Polynomial_integer_dense_flint g - if algorithm == 'generic': + + if algorithm is None: algorithm = 'linbox' + cache_key = 'charpoly_%s' % algorithm g = self.fetch(cache_key) if g is not None: return g.change_variable_name(var) - if algorithm == 'flint' or (algorithm == 'linbox' and not USE_LINBOX_POLY): - g = ((PolynomialRing(ZZ,names = var).gen()))._new() + if algorithm == 'flint': + g = ( PolynomialRing(ZZ, names=var).gen())._new() sig_on() - fmpz_mat_charpoly(g.__poly,self._matrix) + fmpz_mat_charpoly(g.__poly, self._matrix) sig_off() elif algorithm == 'linbox': - g = self._charpoly_linbox(var) + g = ( PolynomialRing(ZZ, names=var).gen())._new() + sig_on() + linbox_fmpz_mat_charpoly(g.__poly, self._matrix) + sig_off() + elif algorithm == 'generic': + g = Matrix_dense.charpoly(self, var) else: raise ValueError("no algorithm '%s'"%algorithm) + self.cache(cache_key, g) return g - def minpoly(self, var='x', algorithm = 'linbox'): + def minpoly(self, var='x', algorithm=None): """ INPUT: - ``var`` - a variable name - - ``algorithm`` - 'linbox' (default) 'generic' - - - .. NOTE:: - - Linbox charpoly disabled on 64-bit machines, since it hangs - in many cases. + - ``algorithm`` - (optional) either 'linbox' (default) or 'generic' EXAMPLES:: - sage: A = matrix(ZZ,6, range(36)) + sage: A = matrix(ZZ, 6, range(36)) sage: A.minpoly() x^3 - 105*x^2 - 630*x - sage: n=6; A = Mat(ZZ,n)([k^2 for k in range(n^2)]) - sage: A.minpoly() + + sage: A = Mat(ZZ, 6)([k^2 for k in range(36)]) + sage: A.minpoly(algorithm='linbox') + x^4 - 2695*x^3 - 257964*x^2 + 1693440*x + sage: A.minpoly(algorithm='generic') x^4 - 2695*x^3 - 257964*x^2 + 1693440*x - """ - key = 'minpoly_%s_%s'%(algorithm, var) - x = self.fetch(key) - if x: return x + On non square matrices, this method raises an ArithmeticError:: - if algorithm == 'linbox' and not USE_LINBOX_POLY: - algorithm = 'generic' - if algorithm == 'linbox': - g = self._minpoly_linbox(var) - elif algorithm == 'generic': - g = Matrix_dense.minpoly(self, var) - else: - raise ValueError("no algorithm '%s'"%algorithm) - self.cache(key, g) - return g + sage: matrix(ZZ, 2, 1).minpoly() + Traceback (most recent call last): + ... + ArithmeticError: only valid for square matrix - def _minpoly_linbox(self, var='x'): - return self._poly_linbox(var=var, typ='minpoly') + TESTS: - def _charpoly_linbox(self, var='x'): - if self.is_zero(): # program around a bug in linbox on 32-bit linux - x = self.base_ring()[var].gen() - return x ** self._nrows - return self._poly_linbox(var=var, typ='charpoly') + Corner cases:: - def _poly_linbox(self, var='x', typ='minpoly'): - """ - INPUT: + sage: matrix([5]).minpoly('z', 'linbox') + z - 5 + sage: matrix([5]).minpoly('z', 'generic') + z - 5 + sage: matrix([]).minpoly('y', 'linbox') + 1 + sage: matrix([]).minpoly('y', 'generic') + 1 - - ``var`` - 'x' + sage: matrix(ZZ, 2).minpoly('x', 'linbox') + x + sage: matrix(ZZ, 2).minpoly('x', 'generic') + x - - ``typ`` - 'minpoly' or 'charpoly' + Consistency on random inputs:: + sage: for _ in range(100): + ....: dim = randint(1, 20) + ....: m = random_matrix(ZZ, dim) + ....: m._clear_cache(); ans_generic = m.minpoly(algorithm='generic') + ....: m._clear_cache(); ans_linbox = m.minpoly(algorithm='linbox') + ....: if ans_generic != ans_linbox: + ....: raise RuntimeError("ans_generic = {} and ans_linbox = {} for\n{}".format( + ....: ans_generic, ans_linbox, m.str())) """ - time = verbose('computing %s of %s x %s matrix using linbox'%(typ, self._nrows, self._ncols)) if self._nrows != self._ncols: - raise ArithmeticError("self must be a square matrix") - if self._nrows <= 1: - return Matrix_dense.charpoly(self, var) - self._init_linbox() - if typ == 'minpoly': + raise ArithmeticError("only valid for square matrix") + + cdef Polynomial_integer_dense_flint g + + if algorithm is None: + algorithm = 'linbox' + + key = 'minpoly_%s'%(algorithm) + g = self.fetch(key) + if g is not None: + return g.change_variable_name(var) + + if algorithm == 'linbox': + g = ( PolynomialRing(ZZ, names=var).gen())._new() sig_on() - v = linbox.minpoly() + linbox_fmpz_mat_minpoly(g.__poly, self._matrix) sig_off() + elif algorithm == 'generic': + g = Matrix_dense.minpoly(self, var) else: - sig_on() - v = linbox.charpoly() - sig_off() - R = self._base_ring[var] - verbose('finished computing %s'%typ, time) - return R(v) + raise ValueError("no algorithm '%s'"%algorithm) + self.cache(key, g) + return g def height(self): """ @@ -1356,7 +1381,7 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sage: a.height() 389 """ - cdef Integer x = PY_NEW(Integer) + cdef Integer x = Integer.__new__(Integer) self.mpz_height(x.value) return x @@ -1547,7 +1572,6 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse def _echelon_in_place_classical(self): cdef Matrix_integer_dense E - self._dealloc_mpz() E = self.echelon_form() sig_on() fmpz_mat_set(self._matrix,E._matrix) @@ -1840,9 +1864,9 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse [ 0 0 0 0 0 0 0 200 0 0] [ 0 0 0 0 0 0 0 0 200 0] [ 0 0 0 0 0 0 0 0 0 200] - + Check that the output is correct in corner cases, see :trac:`18613`:: - + sage: m = matrix(2, 0) sage: m.parent() Full MatrixSpace of 2 by 0 dense matrices over Integer Ring @@ -1915,7 +1939,6 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse U = U[:r] elif algorithm == "padic": from . import matrix_integer_dense_hnf - self._init_mpz() if transformation: H_m, U = matrix_integer_dense_hnf.hnf_with_transformation(self, proof=proof) if not include_zero_rows: @@ -2219,31 +2242,24 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse :meth:`smith_form` """ d = self.fetch('elementary_divisors') - if not d is None: + if d is not None: return d[:] if self._nrows == 0 or self._ncols == 0: d = [] + elif algorithm == 'linbox': + raise ValueError("linbox too broken -- currently Linbox SNF is disabled.") + elif algorithm == 'pari': + d = self.__pari__().matsnf(0).sage() + i = d.count(0) + d.sort() + if i > 0: + d = d[i:] + [d[0]]*i else: - if algorithm == 'linbox': - raise ValueError("linbox too broken -- currently Linbox SNF is disabled.") - if algorithm == 'pari': - d = self.__pari__().matsnf(0).sage() - i = d.count(0) - d.sort() - if i > 0: - d = d[i:] + [d[0]]*i - elif not (algorithm in ['pari', 'linbox']): - raise ValueError("algorithm (='%s') unknown"%algorithm) + raise ValueError("algorithm (='%s') unknown"%algorithm) + self.cache('elementary_divisors', d) return d[:] - def _elementary_divisors_linbox(self): - self._init_linbox() - sig_on() - d = linbox.smithform() - sig_off() - return d - def smith_form(self): r""" Returns matrices S, U, and V such that S = U\*self\*V, and S is in @@ -3210,8 +3226,7 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse raise IndexError("matrix column index out of range") fmpz_add(s, s, fmpz_mat_entry(self._matrix,row,c)) fmpz_mul(pr, pr, s) - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) fmpz_get_mpz(z.value, pr) fmpz_clear(s) fmpz_clear(pr) @@ -3458,10 +3473,18 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse def _rank_linbox(self): """ Compute the rank of this matrix using Linbox. + + TESTS:: + + sage: matrix(ZZ, 4, 6, 0)._rank_linbox() + 0 + sage: matrix(ZZ, 3, 4, range(12))._rank_linbox() + 2 + sage: matrix(ZZ, 5, 10, [1+i+i^2 for i in range(50)])._rank_linbox() + 3 """ - self._init_linbox() sig_on() - cdef unsigned long r = linbox.rank() + cdef unsigned long r = linbox_fmpz_mat_rank(self._matrix) sig_off() return Integer(r) @@ -3542,12 +3565,23 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse Traceback (most recent call last): ... RuntimeError: you must pass the proof=False option to the determinant command to use LinBox's det algorithm - sage: A.determinant(algorithm='linbox',proof=False) + sage: A.determinant(algorithm='linbox', proof=False) -21 sage: A._clear_cache() sage: A.determinant() -21 + Try the other algorithms on the same example:: + + sage: A._clear_cache(); A.determinant(algorithm='padic') + -21 + sage: A._clear_cache(); A.determinant(algorithm='pari') + -21 + sage: A._clear_cache(); A.determinant(algorithm='ntl') + -21 + sage: A._clear_cache(); A.determinant(algorithm='padic') + -21 + A bigger example:: sage: A = random_matrix(ZZ,30) @@ -3559,13 +3593,36 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse TESTS: This shows that we can compute determinants for all sizes up to - 80. The check that the determinant of a squared matrix is a + 80. The check that the determinant of a squared matrix is a square is a sanity check that the result is probably correct:: sage: for s in [1..80]: # long time ....: M = random_matrix(ZZ, s) ....: d = (M*M).determinant() ....: assert d.is_square() + + Check consistency:: + + sage: all(matrix(ZZ, 0).det(algorithm=algo).is_one() for algo in ['flint', 'padic', 'pari', 'ntl']) + True + sage: for _ in range(100): + ....: dim = randint(1, 10) + ....: m = random_matrix(ZZ, dim) + ....: det_flint = m.__copy__().det(algorithm='flint') + ....: det_padic = m.__copy__().det(algorithm='padic') + ....: det_pari = m.__copy__().det(algorithm='pari') + ....: det_ntl = m.__copy__().det(algorithm='ntl') + ....: if type(det_flint) is not Integer: + ....: raise RuntimeError("type(det_flint) = {}".format(type(det_flint))) + ....: if type(det_padic) is not Integer: + ....: raise RuntimeError("type(det_padic) = {}".format(type(det_padic))) + ....: if type(det_pari) is not Integer: + ....: raise RuntimeError("type(det_pari) = {}".format(type(det_pari))) + ....: if type(det_ntl) is not Integer: + ....: raise RuntimeError("type(det_ntl) = {}".format(type(det_ntl))) + ....: if det_flint != det_padic or det_flint != det_pari or det_flint != det_ntl: + ....: raise RuntimeError("ERROR\ndet_flint = {}\ndet_padic={}\ndet_pari={}\ndet_ntl={}\n{}".format( + ....: det_flint, det_padic, det_pari, det_ntl, self.str())) """ d = self.fetch('det') if d is not None: @@ -3589,7 +3646,7 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse fmpz_clear(e) d = det elif algorithm == 'padic': - import matrix_integer_dense_hnf + from . import matrix_integer_dense_hnf d = matrix_integer_dense_hnf.det_padic(self, proof=proof, stabilize=stabilize) elif algorithm == 'linbox': if proof: @@ -3609,12 +3666,27 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse def _det_linbox(self): """ Compute the determinant of this matrix using Linbox. + + TESTS:: + + sage: matrix(ZZ, 0)._det_linbox() + 1 """ - self._init_linbox() + if self._nrows != self._ncols: + raise ArithmeticError("self must be a square matrix") + if self._nrows == 0: + return ZZ.one() + + cdef fmpz_t tmp + fmpz_init(tmp) sig_on() - d = linbox.det() + linbox_fmpz_mat_det(tmp, self._matrix) sig_off() - return Integer(d) + + cdef Integer ans = PY_NEW(Integer) + fmpz_get_mpz(ans.value, tmp) + fmpz_clear(tmp) + return ans def _det_pari(self, int flag=0): """ @@ -3622,20 +3694,24 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse flag is set to 1, use classical Gaussian elimination. For efficiency purposes, this det is computed entirely on the - PARI stack then the PARI stack is cleared. This function is + PARI stack then the PARI stack is cleared. This function is most useful for very small matrices. EXAMPLES:: - sage: matrix(ZZ,3,[1..9])._det_pari() + sage: matrix(ZZ, 0)._det_pari() + 1 + sage: matrix(ZZ, 0)._det_pari(1) + 1 + sage: matrix(ZZ, 3, [1..9])._det_pari() 0 - sage: matrix(ZZ,3,[1..9])._det_pari(1) + sage: matrix(ZZ, 3, [1..9])._det_pari(1) 0 """ sig_on() cdef GEN d = det0(pari_GEN(self), flag) # now convert d to a Sage integer e - cdef Integer e = PY_NEW(Integer) + cdef Integer e = Integer.__new__(Integer) INT_to_mpz(e.value, d) clear_stack() return e @@ -3643,23 +3719,43 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse def _det_ntl(self): """ Compute the determinant of this matrix using NTL. + + EXAMPLES:: + + sage: matrix(ZZ, 0)._det_ntl() + 1 + sage: matrix(ZZ, 3, [1..9])._det_ntl() + 0 + sage: matrix(ZZ, 3, [1,3,6,2,7,8,2,1,0])._det_ntl() + -32 """ sig_on() d = self._ntl_().determinant() sig_off() return Integer(d) - #### Rational kernel, via IML def _rational_kernel_iml(self): """ - IML: Return the rational kernel of this matrix (acting from the - left), considered as a matrix over QQ. I.e., returns a matrix K - such that self\*K = 0, and the number of columns of K equals the - nullity of self. + Return the rational (left) kernel of this matrix - AUTHORS: + OUTPUT: - - William Stein + A matrix ``K`` such that ``self * K = 0``, and the number of columns of + K equals the nullity of self. + + EXAMPLES:: + + sage: m = matrix(ZZ, 5, 5, [1+i+i^2 for i in range(25)]) + sage: m._rational_kernel_iml() + [ 1 3] + [-3 -8] + [ 3 6] + [-1 0] + [ 0 -1] + + sage: V1 = m._rational_kernel_iml().column_space().change_ring(QQ) + sage: V2 = m._rational_kernel_flint().column_space().change_ring(QQ) + sage: assert V1 == V2 """ if self._nrows == 0 or self._ncols == 0: return self.matrix_space(self._ncols, 0).zero_matrix() @@ -3668,34 +3764,46 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse cdef unsigned long i,j,k cdef mpz_t *mp_N time = verbose('computing null space of %s x %s matrix using IML'%(self._nrows, self._ncols)) - self._init_mpz() + cdef mpz_t * m = fmpz_mat_to_mpz_array(self._matrix) sig_on() - dim = nullspaceMP(self._nrows, self._ncols, self._entries, &mp_N) + dim = nullspaceMP(self._nrows, self._ncols, m, &mp_N) sig_off() # Now read the answer as a matrix. cdef Matrix_integer_dense M - M = self._new(self._ncols,dim) + M = self._new(self._ncols, dim) k = 0 - for i from 0 <= i < self._ncols: - for j from 0 <= j < dim: - fmpz_set_mpz(fmpz_mat_entry(M._matrix,i,j), mp_N[k]) - mpz_clear(mp_N[k]) + for i in range(self._ncols): + for j in range(dim): + fmpz_set_mpz(fmpz_mat_entry(M._matrix, i, j), mp_N[k]) k += 1 - sig_free(mp_N) + mpz_array_clear(m, self._nrows * self._ncols) + mpz_array_clear(mp_N, dim * self._ncols) verbose("finished computing null space", time) return M - #### Rational kernel, via flint def _rational_kernel_flint(self): """ - Return the rational kernel of this matrix (acting from the - left), considered as a matrix over QQ. I.e., returns a matrix K - such that self\*K = 0, and the number of columns of K equals the - nullity of self. + Return the rational (left) kernel of this matrix - AUTHORS: + OUTPUT: - - Marc Masdeu + A matrix ``K`` such that ``self * K = 0``, and the number of columns of + K equals the nullity of self. + + EXAMPLES:: + + sage: m = matrix(ZZ, 4, 6, [i^2-2*i for i in range(24)]) + sage: m._rational_kernel_flint() + [ 1728 5184 10368] + [ -5184 -13824 -25920] + [ 5184 10368 17280] + [ -1728 0 0] + [ 0 -1728 0] + [ 0 0 -1728] + + sage: V1 = m._rational_kernel_iml().column_space().change_ring(QQ) + sage: V2 = m._rational_kernel_flint().column_space().change_ring(QQ) + sage: assert V1 == V2 """ if self._nrows == 0 or self._ncols == 0: return self.matrix_space(self._ncols, 0).zero_matrix() @@ -3703,15 +3811,15 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse cdef long dim cdef fmpz_mat_t M0 sig_on() - fmpz_mat_init(M0,self._ncols,self._ncols) + fmpz_mat_init(M0, self._ncols, self._ncols) dim = fmpz_mat_nullspace(M0, self._matrix) sig_off() # Now read the answer as a matrix. - cdef Matrix_integer_dense M - M = self._new(self._ncols,dim) - for i from 0 <= i < M._nrows: - for j from 0 <= j < M._ncols: - fmpz_set(fmpz_mat_entry(M._matrix,i,j),fmpz_mat_entry(M0,i,j)) + cdef Matrix_integer_dense M = self._new(self._ncols, dim) + cdef size_t i,j + for i in range(self._ncols): + for j in range(dim): + fmpz_set(fmpz_mat_entry(M._matrix, i, j), fmpz_mat_entry(M0, i, j)) fmpz_mat_clear(M0) return M @@ -4190,31 +4298,31 @@ cdef class Matrix_integer_dense(Matrix_dense): # dense or sparse sig_check() verbose("Initializing mp_N and mp_D") mp_N = sig_malloc( n * m * sizeof(mpz_t) ) - for i from 0 <= i < n * m: + for i in range(n * m): mpz_init(mp_N[i]) mpz_init(mp_D) verbose("Done with initializing mp_N and mp_D") - self._init_mpz() - B._init_mpz() + cdef mpz_t * mself = fmpz_mat_to_mpz_array(self._matrix) + cdef mpz_t * mB = fmpz_mat_to_mpz_array(B._matrix) try: verbose('Calling solver n = %s, m = %s'%(n,m)) sig_on() - nonsingSolvLlhsMM(solu_pos, n, m, self._entries, B._entries, mp_N, mp_D) + nonsingSolvLlhsMM(solu_pos, n, m, mself, mB, mp_N, mp_D) sig_off() - M = self._new(P.nrows(),P.ncols()) + M = self._new(P.nrows(), P.ncols()) k = 0 for i from 0 <= i < M._nrows: for j from 0 <= j < M._ncols: fmpz_set_mpz(fmpz_mat_entry(M._matrix,i,j), mp_N[k]) k += 1 - D = PY_NEW(Integer) + D = Integer.__new__(Integer) mpz_set(D.value, mp_D) return M, D finally: mpz_clear(mp_D) - for i from 0 <= i < n*m: - mpz_clear(mp_N[i]) - sig_free(mp_N) + mpz_array_clear(mself, self.nrows() * self.ncols()) + mpz_array_clear(mB, B.nrows() * B.ncols()) + mpz_array_clear(mp_N, n*m) def _solve_flint(self, Matrix_integer_dense B, right=True): """ diff --git a/src/sage/matrix/matrix_integer_sparse.pyx b/src/sage/matrix/matrix_integer_sparse.pyx index dba6f9ac643..bd4e0a00b5c 100644 --- a/src/sage/matrix/matrix_integer_sparse.pyx +++ b/src/sage/matrix/matrix_integer_sparse.pyx @@ -537,7 +537,9 @@ cdef class Matrix_integer_sparse(Matrix_sparse): sage: M.elementary_divisors() [1, 1, 6] - ..SEEALSO:: :meth:`smith_form` + .. SEEALSO:: + + :meth:`smith_form` """ return self.dense_matrix().elementary_divisors(algorithm=algorithm) diff --git a/src/sage/matrix/matrix_rational_dense.pyx b/src/sage/matrix/matrix_rational_dense.pyx index 73aec6b4a93..07219126f22 100644 --- a/src/sage/matrix/matrix_rational_dense.pyx +++ b/src/sage/matrix/matrix_rational_dense.pyx @@ -55,10 +55,10 @@ from __future__ import absolute_import, print_function from libc.string cimport strcpy, strlen -from sage.modules.vector_rational_dense cimport Vector_rational_dense +from cysignals.signals cimport sig_check, sig_on, sig_off +from cysignals.memory cimport sig_malloc, sig_free -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +from sage.modules.vector_rational_dense cimport Vector_rational_dense from sage.arith.rational_reconstruction cimport mpq_rational_reconstruction from sage.libs.gmp.randomize cimport * @@ -821,8 +821,7 @@ cdef class Matrix_rational_dense(Matrix_dense): sage: b.denominator() 293 """ - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) self.mpz_denom(z.value) return z @@ -872,7 +871,7 @@ cdef class Matrix_rational_dense(Matrix_dense): cdef fmpz_t aij fmpz_init(aij) mpz_init(tmp) - D = PY_NEW(Integer) + D = Integer.__new__(Integer) self.mpz_denom(D.value) from sage.matrix.matrix_space import MatrixSpace MZ = MatrixSpace(ZZ, self._nrows, self._ncols, sparse=self.is_sparse()) @@ -1121,8 +1120,7 @@ cdef class Matrix_rational_dense(Matrix_dense): sage: b.height() 5007 """ - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) self.mpz_height(z.value) return z diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index c4c34c26ad6..97081c5d9c5 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -14,20 +14,25 @@ TESTS:: [] """ -############################################################################## +#***************************************************************************** # Copyright (C) 2007 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/ -############################################################################## +#***************************************************************************** + from __future__ import absolute_import +from cysignals.signals cimport sig_on, sig_off +from cysignals.memory cimport sig_malloc, sig_free + from sage.data_structures.binary_search cimport * from sage.modules.vector_integer_sparse cimport * from sage.modules.vector_rational_sparse cimport * -include 'sage/ext/stdsage.pxi' -include "cysignals/signals.pxi" from cpython.sequence cimport * from sage.rings.rational cimport Rational @@ -375,8 +380,7 @@ cdef class Matrix_rational_sparse(Matrix_sparse): sage: b.height() 5007 """ - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) self.mpz_height(z.value) return z @@ -431,8 +435,7 @@ cdef class Matrix_rational_sparse(Matrix_sparse): sage: b.denominator() 293 """ - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) self.mpz_denom(z.value) return z diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 9fc0f8faf33..8b40094892b 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -985,7 +985,7 @@ def __getitem__(self, x): should eventually be implemented in the corresponding category rather than here.) - ..SEEALSO:: + .. SEEALSO:: :meth:`sage.categories.rings.Rings.ParentMethod.__getitem__`, :meth:`sage.structure.parent.Parent.__getitem__` diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index bb0ed441f8f..4cd99ca1788 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -423,6 +423,49 @@ cdef class Matrix_sparse(matrix.Matrix): list(reversed([self._nrows - t for t in row_divs]))) return A + + def _reverse_unsafe(self): + r""" + TESTS:: + + sage: m = matrix(QQ, 3, 3, {(2,2): 1}, sparse=True) + sage: m._reverse_unsafe() + sage: m + [1 0 0] + [0 0 0] + [0 0 0] + sage: m = matrix(QQ, 3, 3, {(2,2): 1, (0,0):2}, sparse=True) + sage: m._reverse_unsafe() + sage: m + [1 0 0] + [0 0 0] + [0 0 2] + sage: m = matrix(QQ, 3, 3, {(1,2): 1}, sparse=True) + sage: m._reverse_unsafe() + sage: m + [0 0 0] + [1 0 0] + [0 0 0] + sage: m = matrix(QQ, 3, 3, {(1,1): 1}, sparse=True) + sage: m._reverse_unsafe() + sage: m + [0 0 0] + [0 1 0] + [0 0 0] + """ + cdef Py_ssize_t i, j, ii, jj + for i,j in self.nonzero_positions(copy=False): + ii = self._nrows - i - 1 + jj = self._ncols - j - 1 + if (i > ii or (i == ii and j >= jj)) and self.get_unsafe(ii, jj): + # already swapped + continue + + e1 = self.get_unsafe(i, j) + e2 = self.get_unsafe(ii, jj) + self.set_unsafe(i, j, e2) + self.set_unsafe(ii, jj, e1) + def charpoly(self, var='x', **kwds): """ Return the characteristic polynomial of this matrix. diff --git a/src/sage/matroids/basis_exchange_matroid.pyx b/src/sage/matroids/basis_exchange_matroid.pyx index f7c8f8ab801..5c695086ccf 100644 --- a/src/sage/matroids/basis_exchange_matroid.pyx +++ b/src/sage/matroids/basis_exchange_matroid.pyx @@ -61,7 +61,7 @@ cdef class BasisExchangeMatroid(Matroid): adjacent in the graph if their symmetric difference has 2 members. This base exchange graph is not stored as such, but should be provided - implicity by the child class in the form of two methods + implicitly by the child class in the form of two methods ``__is_exchange_pair(x, y)`` and ``__exchange(x, y)``, as well as an initial basis. At any moment, BasisExchangeMatroid keeps a current basis `B`. The method ``__is_exchange_pair(x, y)`` should return a boolean diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 225af7d523b..ae2b4c14a5a 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -3334,7 +3334,7 @@ cdef class Matroid(SageObject): Boolean. - ..SEEALSO:: + .. SEEALSO:: :meth:`M.is_isomorphism() ` diff --git a/src/sage/matroids/matroids_plot_helpers.py b/src/sage/matroids/matroids_plot_helpers.py index 29c154cfc71..c8a45eba7af 100644 --- a/src/sage/matroids/matroids_plot_helpers.py +++ b/src/sage/matroids/matroids_plot_helpers.py @@ -22,7 +22,7 @@ curve containing the points in the specified line which inturn uses ``scipy.interpolate.splprep`` and ``scipy.interpolate.splev``. Then one can use sage's graphics primitives ``line``, ``point``, ``text`` and - ``points`` to produce graphics object containg points (ground set + ``points`` to produce graphics object containing points (ground set elements) and lines (for a rank 3 matroid, these are flats of rank 2 of size greater than equal to 3) of the geometric representation of the matroid. Loops and parallel elements are added as per conventions in diff --git a/src/sage/matroids/unpickling.pyx b/src/sage/matroids/unpickling.pyx index eca871caa96..70ab690629a 100644 --- a/src/sage/matroids/unpickling.pyx +++ b/src/sage/matroids/unpickling.pyx @@ -24,11 +24,13 @@ AUTHORS: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import + include 'sage/data_structures/bitset.pxi' import sage.matroids.matroid import sage.matroids.basis_exchange_matroid -from minor_matroid import MinorMatroid -from dual_matroid import DualMatroid +from .minor_matroid import MinorMatroid +from .dual_matroid import DualMatroid from .circuit_closures_matroid cimport CircuitClosuresMatroid from .basis_matroid cimport BasisMatroid from .linear_matroid cimport LinearMatroid, RegularMatroid, BinaryMatroid, TernaryMatroid, QuaternaryMatroid diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index b4568c4204b..19c9ac3792d 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -892,7 +892,8 @@ cpdef tuple C3_sorted_merge(list lists, key=identity): # last list. Later, we will make sure that it is actually # in the tail of the last list. if not last_list_non_empty: - # Reinstate the last list for the suggestion if it had disapeared before + # Reinstate the last list for the suggestion + # if it had disappeared before heads.append(O) tails.append([]) tailsets.append(set()) diff --git a/src/sage/misc/converting_dict.py b/src/sage/misc/converting_dict.py index 5be3be1f64f..2902e2f8c6f 100644 --- a/src/sage/misc/converting_dict.py +++ b/src/sage/misc/converting_dict.py @@ -205,7 +205,7 @@ def has_key(self, key): def pop(self, key, *args): r""" - Remove and retreive a given element from the dictionary + Remove and retrieve a given element from the dictionary. INPUT: diff --git a/src/sage/misc/dict_del_by_value.pxd b/src/sage/misc/dict_del_by_value.pxd new file mode 100644 index 00000000000..5d83af20d5d --- /dev/null +++ b/src/sage/misc/dict_del_by_value.pxd @@ -0,0 +1,13 @@ +from cpython.object cimport PyObject +cdef extern from "Python.h": + ctypedef struct PyDictObject + +cdef del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hash) + +#This is for compatibility: this routine is available in Py3 and we +#implement it ourselves in Py2. +IF PY_VERSION_HEX<=0x02ffffff: + cdef PyObject* PyDict_GetItemWithError(dict op, object key) except? NULL +ELSE: + cdef extern from "Python.h": + PyObject* PyDict_GetItemWithError(dict op, object key) except? NULL diff --git a/src/sage/misc/dict_del_by_value.pyx b/src/sage/misc/dict_del_by_value.pyx new file mode 100644 index 00000000000..967a67348a2 --- /dev/null +++ b/src/sage/misc/dict_del_by_value.pyx @@ -0,0 +1,443 @@ +""" +Delete item from PyDict by exact value and hash + +Beware that the implementation of the routine here relies on implementation +details of CPython's dict that go beyond the published API. This file depends +on python version when cythonized. It expects PY_VERSION_HEX to be available +in the cythonization and the result depends on it (and needs to match the +python version the C-compiler compiles it against). Usage should do something +along the lines of + + cythonize("dict_del_by_value.pyx", + compile_time_env({"PY_VERSION_HEX": sys.hexversion})) + +AUTHORS: + +- Nils Bruin (2017-05) +""" +######################################################################## +# Copyright (C) 2017 Nils Bruin +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# http://www.gnu.org/licenses/ +######################################################################## +from __future__ import print_function + +import weakref +from weakref import KeyedRef +from copy import deepcopy + +from cpython.list cimport PyList_New +from cpython cimport Py_XINCREF, Py_XDECREF + +IF PY_VERSION_HEX<=0x02ffffff: + cdef extern from "Python.h": + ctypedef struct PyDictEntry: + Py_ssize_t me_hash + PyObject* me_key + PyObject* me_value + ctypedef struct PyDictObject: + Py_ssize_t ma_fill + Py_ssize_t ma_used + Py_ssize_t ma_mask + PyDictEntry* ma_table + PyDictEntry* (*ma_lookup)(PyDictObject *mp, PyObject *key, Py_hash_t hash) except NULL + + PyObject* Py_None + #we need this redefinition because we want to be able to call + #PyWeakref_GetObject with borrowed references. This is the recommended + #strategy according to Cython/Includes/cpython/__init__.pxd + PyObject* PyWeakref_GetObject(PyObject * wr) + int PyList_SetItem(object list, Py_ssize_t index, PyObject * item) except -1 + + from cpython.object cimport PyObject_Hash + cdef PyObject* PyDict_GetItemWithError(dict op, object key) except? NULL: + cdef PyDictEntry* ep + cdef PyDictObject* mp = op + ep = mp.ma_lookup(mp, key, PyObject_Hash(key)) + if ep: + return ep.me_value + else: + return NULL + + #this routine extracts the "dummy" sentinel value that is used in dicts to mark + #"freed" slots. We need that to delete things ourselves. + + cdef PyObject* init_dummy() except NULL: + cdef dict D = dict() + cdef PyDictObject* mp = D + cdef size_t mask + cdef PyDictEntry* ep0 = mp.ma_table + cdef PyDictEntry* ep + cdef size_t i + global dummy + + D[0]=0; del D[0] #ensure that there is a "deleted" entry in the dict + mask = mp.ma_mask + #since our entry had hash 0, we should really succeed on the first iteration + for i in range(mask+1): + ep = &(ep0[i]) + if ep.me_key != NULL: + return ep.me_key + raise RuntimeError("Problem initializing dummy") + + #note that dummy here is a borrowed reference. That's not a problem because + #we're never giving it up and dictobject.c is also holding a permanent reference + #to this object + cdef PyObject* dummy = init_dummy() + + #this routine looks for the first entry in dict D with given hash of the + #key and given (identical!) value and deletes that entry. + cdef del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hash): + """ + This is used in callbacks for the weak values of :class:`WeakValueDictionary`. + + INPUT: + + - ``PyDictObject *mp`` -- pointer to a dict + - ``PyObject *value`` -- pointer to a value of the dictionary + - ``Py_hash_t hash`` -- hash of the key by which the value is stored in the dict + + The hash bucket determined by the given hash is searched for the item + containing the given value. If this item cannot be found, the function is + silently returning. Otherwise, the item is removed from the dict. + + TESTS: + + The following is an indirect doctest, as discussed on :trac:`13394`. + :: + + sage: from sage.misc.weak_dict import WeakValueDictionary + sage: V = [set(range(n)) for n in range(5)] + sage: D = WeakValueDictionary(enumerate(V)) + + The line ``V[k] = None`` triggers execution of the callback functions of + the dict values. However, the actual deletion is postponed till after the + iteration over the dictionary has finished. Hence, when the callbacks are + executed, the values which the callback belongs to has already been + overridded by a new value. Therefore, the callback does not delete the + item:: + + sage: for k in D: # indirect doctest + ....: V[k] = None + ....: D[k] = ZZ + sage: len(D) + 5 + sage: D[1] + Integer Ring + + TESTS: + + The following shows that the deletion of deeply nested structures does not + result in an error, by :trac:`15506`:: + + sage: class A: pass + sage: a = A(); prev = a + sage: M = WeakValueDictionary() + sage: for i in range(10^3+10): newA = A(); M[newA] = prev; prev = newA + sage: del a + + """ + cdef size_t i + cdef size_t perturb + cdef size_t mask = mp.ma_mask + cdef PyDictEntry* ep0 = mp.ma_table + cdef PyDictEntry* ep + i = hash & mask + ep = &(ep0[i]) + + if ep.me_key == NULL: + # key not found + return + + perturb = hash + while ((ep.me_value) != value or ep.me_hash != hash): + i = (i << 2) + i + perturb +1 + ep = &ep0[i & mask] + if ep.me_key == NULL: + # key not found + return + perturb = perturb >> 5 #this is the value of PERTURB_SHIFT + + T=PyList_New(2) + PyList_SetItem(T, 0, ep.me_key) + if dummy == NULL: + raise RuntimeError("dummy needs to be initialized") + Py_XINCREF(dummy) + ep.me_key = dummy + PyList_SetItem(T, 1, ep.me_value) + ep.me_value = NULL + mp.ma_used -= 1 + #We have transferred the to-be-deleted references to the list T + #we now delete the list so that the actual decref happens through a + #deallocation routine that uses the Python Trashcan macros to + #avoid stack overflow in deleting deep structures. + del T + +ELIF PY_VERSION_HEX>=0x03060000: + + from libc.stdint cimport int8_t,int16_t,int32_t,int64_t + cdef extern from "Python.h": + ctypedef struct PyDictObject: + Py_ssize_t ma_used + PyDictKeysObject * ma_keys + PyObject ** ma_values + + PyObject* Py_None + #we need this redefinition because we want to be able to call + #PyWeakref_GetObject with borrowed references. This is the recommended + #strategy according to Cython/Includes/cpython/__init__.pxd + PyObject* PyWeakref_GetObject(PyObject * wr) + int PyList_SetItem(object list, Py_ssize_t index, PyObject * item) except -1 + PyObject* PyDict_GetItemWithError(dict op, object key) except? NULL + int PyWeakref_Check(PyObject * ob) + + ctypedef struct PyDictKeysObject + #### + #definitions replicated from CPython's Objects/dict-common.h + #(this file is not exported fron CPython, so we need to be + #careful the definitions are in step with what happens there. + ctypedef union IndexBlock: + int8_t as_1[8] + int16_t as_2[4] + int32_t as_4[2] + int64_t as_8[1] + + ctypedef struct MyPyDictKeysObject: + Py_ssize_t dk_refcnt + Py_ssize_t dk_size + Py_ssize_t (*dk_lookup)(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject ** value_addr, Py_ssize_t *hashpos) + Py_ssize_t dk_usable + Py_ssize_t dk_nentries + IndexBlock dk_indices + + ctypedef struct PyDictKeyEntry: + Py_hash_t me_hash + PyObject * me_key + PyObject * me_value + + cdef Py_ssize_t DKIX_EMPTY = -1 + cdef Py_ssize_t DKIX_DUMMY = -2 + cdef Py_ssize_t DKIX_ERROR = -3 + + ##### + #These routines are copied from CPython's Object/dictobject.c + #in order to access PyDictKeysObject fields + + cdef inline int DK_IXSIZE(MyPyDictKeysObject *keys): + cdef Py_ssize_t s = keys.dk_size + cdef int i + if s <= 0xff: + i = 1 + elif s <= 0xffff: + i = 2 + elif s <= 0xffffffff: + i = 4 + else: + i = 8 + return i + + cdef inline PyDictKeyEntry * DK_ENTRIES(MyPyDictKeysObject *keys): + return &(keys.dk_indices.as_1[keys.dk_size * DK_IXSIZE(keys)]) + + cdef inline Py_ssize_t dk_get_index(MyPyDictKeysObject *keys, Py_ssize_t i): + cdef Py_ssize_t s = keys.dk_size + cdef Py_ssize_t ix + if s <= 0xff: + ix = keys.dk_indices.as_1[i] + elif s <= 0xffff: + ix = keys.dk_indices.as_2[i] + elif s <= 0xffffffff: + ix = keys.dk_indices.as_4[i] + else: + ix = keys.dk_indices.as_8[i] + return ix + + cdef inline dk_set_index(MyPyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix): + cdef Py_ssize_t s = keys.dk_size + if s <= 0xff: + keys.dk_indices.as_1[i] = ix + elif s <= 0xffff: + keys.dk_indices.as_2[i] = ix + elif s <= 0xffffffff: + keys.dk_indices.as_4[i] = ix + else: + keys.dk_indices.as_8[i] = ix + return ix + #End of replication of Object/dictobject.c + ###### + + cdef void * lookdict + + cdef void * DK_LOOKUP(PyDictObject *mp): + return (((mp.ma_keys)).dk_lookup) + + def init_lookdict(): + global lookdict + cdef dict D={} + #this should trigger the initialization of the general + #lookup function on the dict. + PyDict_GetItemWithError(D,None) + #which we store in a global variable + lookdict=DK_LOOKUP(D) + + init_lookdict() + + cdef ensure_allows_deletions(PyDictObject *mp): + if DK_LOOKUP(mp) != lookdict: + #on normal dictionaries (non-split table), looking up a key + #that is not a unicode object triggers installation of the + #general lookup function (which can deal with DKIX_DUMMY) + PyDict_GetItemWithError(mp,None) + #this can actually fail if mp is a dictionary with split table + assert DK_LOOKUP(mp) == lookdict + + cdef del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, Py_hash_t hash): + """ + This is used in callbacks for the weak values of :class:`WeakValueDictionary`. + + INPUT: + + - ``PyDictObject *mp`` -- pointer to a dict + - ``PyObject *value`` -- pointer to a value of the dictionary + - ``Py_hash_t hash`` -- hash of the key by which the value is stored in the dict + + The hash bucket determined by the given hash is searched for the item + containing the given value. If this item cannot be found, the function is + silently returning. Otherwise, the item is removed from the dict. + + TESTS: + + The following is an indirect doctest, as discussed on :trac:`13394`. + :: + + sage: from sage.misc.weak_dict import WeakValueDictionary + sage: V = [set(range(n)) for n in range(5)] + sage: D = WeakValueDictionary(enumerate(V)) + + The line ``V[k] = None`` triggers execution of the callback functions of + the dict values. However, the actual deletion is postponed till after the + iteration over the dictionary has finished. Hence, when the callbacks are + executed, the values which the callback belongs to has already been + overridded by a new value. Therefore, the callback does not delete the + item:: + + sage: for k in D: # indirect doctest + ....: V[k] = None + ....: D[k] = ZZ + sage: len(D) + 5 + sage: D[1] + Integer Ring + + TESTS: + + The following shows that the deletion of deeply nested structures does not + result in an error, by :trac:`15506`:: + + sage: class A: pass + sage: a = A(); prev = a + sage: M = WeakValueDictionary() + sage: for i in range(10^3+10): newA = A(); M[newA] = prev; prev = newA + sage: del a + + """ + cdef size_t i + cdef MyPyDictKeysObject * keys = (mp.ma_keys) + cdef size_t perturb + cdef size_t mask = keys.dk_size-1 + cdef PyDictKeyEntry *entries, *ep + entries = DK_ENTRIES(keys) + + if mp.ma_values != NULL: + print ("del_dictitem_by_exact_value cannot be applied to a shared key dict") + return + + i = hash & mask + ix = dk_get_index(keys, i) + + if ix == DKIX_EMPTY: + # key not found + return + + ep = &(entries[ix]) + perturb = hash + while (ep.me_value != value or ep.me_hash != hash): + perturb = perturb >> 5 #this is the value of PERTURB_SHIFT + i = ((i << 2) + i + perturb +1) & mask + ix = dk_get_index(keys, i) + if ix == DKIX_EMPTY: + # key not found + return + ep = &(entries[ix]) + + ensure_allows_deletions(mp) + T=PyList_New(2) + PyList_SetItem(T, 0, ep.me_key) + PyList_SetItem(T, 1, ep.me_value) + ep.me_key = NULL + ep.me_value = NULL + mp.ma_used -= 1 + dk_set_index(keys, i, DKIX_DUMMY) + #We have transferred the to-be-deleted references to the list T + #we now delete the list so that the actual decref happens through a + #deallocation routine that uses the Python Trashcan macros to + #avoid stack overflow in deleting deep structures. + del T + +#END IF PY_VERSION_HEX + +def test_del_dictitem_by_exact_value(D, value, h): + """ + This function helps testing some cdef function used to delete dictionary items. + + INPUT: + + - ``D`` -- a Python ````. + - ``value`` -- an object that is value ``D``. + - ``h`` -- the hash of the key under which to find ``value`` in ``D``. + + The underlying cdef function deletes an item from ``D`` that is in the + hash bucket determined by ``h`` and whose value is identic with + ``value``. Of course, this only makes sense if the pairs ``(h, value)`` + corresponding to items in ``D`` are pair-wise distinct. + + If a matching item can not be found, the function does nothing and + silently returns. + + TESTS: + + See :trac:`13394` for a discussion. + :: + + sage: from sage.misc.dict_del_by_value import test_del_dictitem_by_exact_value + sage: B=1000 + sage: L=list(range(B)) + sage: D1=dict() + sage: D2=dict() + sage: for i in range(100000): # long time + ....: ki=L[floor(random()*B)] + ....: vi=L[floor(random()*B)] + ....: D1[ki]=vi + ....: D2[ki]=vi + ....: ko=L[floor(random()*B)] + ....: if ko in D1: + ....: vo=D1[ko] + ....: del D1[ko] + ....: test_del_dictitem_by_exact_value(D2,vo,hash(ko)) + ....: assert D1 == D2 + + No action is taken if the item prescribed by key hash and value does not + exist in the dictionary:: + + sage: D = {1: ZZ} + sage: test_del_dictitem_by_exact_value(D, ZZ, 2) + sage: D + {1: Integer Ring} + sage: test_del_dictitem_by_exact_value(D, QQ, 1) + sage: D + {1: Integer Ring} + + """ + return del_dictitem_by_exact_value(D, value, h) diff --git a/src/sage/misc/mrange.py b/src/sage/misc/mrange.py index a653d72c170..08567bf5fd8 100644 --- a/src/sage/misc/mrange.py +++ b/src/sage/misc/mrange.py @@ -620,6 +620,9 @@ def cantor_product(*args, **kwds): - ``repeat`` -- an optional integer. If it is provided, the input is repeated ``repeat`` times. + Other keyword arguments are passed to + :class:`sage.combinat.integer_lists.invlex.IntegerListsLex`. + EXAMPLES:: sage: from sage.misc.mrange import cantor_product @@ -676,7 +679,13 @@ def cantor_product(*args, **kwds): sage: next(cantor_product(count(), toto='hey')) Traceback (most recent call last): ... - TypeError: 'toto' is an invalid keyword argument for this function + TypeError: __init__() got an unexpected keyword argument 'toto' + + :: + + sage: list(cantor_product(srange(5), repeat=2, min_slope=1)) + [(0, 1), (0, 2), (1, 2), (0, 3), (1, 3), + (0, 4), (2, 3), (1, 4), (2, 4), (3, 4)] """ from itertools import count from sage.combinat.integer_lists import IntegerListsLex @@ -691,8 +700,6 @@ def cantor_product(*args, **kwds): return elif repeat < 0: raise ValueError("repeat argument cannot be negative") - if kwds: - raise TypeError("'{}' is an invalid keyword argument for this function".format(list(kwds)[0])) mm = m * repeat for n in count(0): @@ -709,7 +716,7 @@ def cantor_product(*args, **kwds): # iterate through what we have ceiling = [n if lengths[i] is None else lengths[i]-1 for i in range(m)] * repeat - for v in IntegerListsLex(n, length=mm, ceiling=ceiling): + for v in IntegerListsLex(n, length=mm, ceiling=ceiling, **kwds): yield tuple(data[i%m][v[i]] for i in range(mm)) if all(l is not None for l in lengths) and repeat*sum(l-1 for l in lengths) == n: diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index d6fe1eb4d7b..143bad2f638 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -174,7 +174,7 @@ from __future__ import print_function, absolute_import -from six import itervalues, iteritems, integer_types +from six import itervalues, iteritems, integer_types, string_types def sage_input(x, preparse=True, verify=False, allow_locals=False): @@ -536,7 +536,7 @@ def __call__(self, x, coerced=False): return self.name('float')(self.int(ZZ(rrx))) return self.name('float')(RR(x)) - if isinstance(x, (str, unicode)): + if isinstance(x, string_types): return SIE_literal_stringrep(self, repr(x)) if isinstance(x, tuple): diff --git a/src/sage/misc/sage_unittest.py b/src/sage/misc/sage_unittest.py index 111dfeae0d7..c3269872770 100644 --- a/src/sage/misc/sage_unittest.py +++ b/src/sage/misc/sage_unittest.py @@ -269,7 +269,7 @@ def _test_b(self, tester): tester.fail() ... AssertionError: None - In conjonction with ``%pdb on``, this allows for the debbuger + In conjunction with ``%pdb on``, this allows for the debbuger to jump directly to the first failure location. """ if isinstance(skip, str): diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index bd7f38ded6d..91254e18395 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -114,7 +114,7 @@ def foo(unsigned int x=1, a=')"', b={not (2+1==3):'bar'}, *args, **kwds): return """ from __future__ import print_function, absolute_import from six.moves import range -from six import iteritems, string_types +from six import iteritems, string_types, class_types import ast import inspect @@ -409,7 +409,7 @@ def visit_Name(self, node): sage: [vis(n) for n in ['True', 'False', 'None', 'foo', 'bar']] [True, False, None, 'foo', 'bar'] sage: [type(vis(n)) for n in ['True', 'False', 'None', 'foo', 'bar']] - [<... 'bool'>, <... 'bool'>, , <... 'str'>, <... 'str'>] + [<... 'bool'>, <... 'bool'>, <... 'NoneType'>, <... 'str'>, <... 'str'>] """ what = node.id if what == 'None': @@ -1707,7 +1707,7 @@ def sage_getdoc_original(obj): """ # typ is the type corresponding to obj, which is obj itself if # that was a type or old-style class - if isinstance(obj, (type, types.ClassType) ): + if isinstance(obj, class_types): typ = obj else: typ = type(obj) diff --git a/src/sage/misc/superseded.py b/src/sage/misc/superseded.py index 348604d4ead..7cb1c225d46 100644 --- a/src/sage/misc/superseded.py +++ b/src/sage/misc/superseded.py @@ -508,79 +508,3 @@ def deprecated_function_alias(trac_number, func): if module_name is None: module_name = '__main__' return DeprecatedFunctionAlias(trac_number, func, module_name) - - -def deprecated_callable_import(trac_number, module_name, globs, locs, fromlist, message=None): - """ - Imports a list of callables into the namespace from - which it is called. These callables however give a deprecation - warning whenever they are called. This is primarily used from - deprecating things from Sage's ``all.py`` files. - - INPUT: - - - ``trac_number`` -- integer. The trac ticket number where the - deprecation is introduced. - - - ``param module_name`` -- string or ``None``. The name of the - module from which to import the callables or ``None``. - - - ``globs`` -- dictionary. The ``globals()`` from where this is being called. - - - ``locs`` -- dictionary. The ``locals()`` from where this is being called. - - - ``fromlist`` -- list of strings. The list the names of the - callables to deprecate - - - ``message`` -- string. Message to display when the deprecated functions are called. - - .. note:: - - If ``module_name`` is ``None``, then no importing will be done, and - it will be assumed that the functions have already been - imported and are present in ``globs`` - - .. warning:: - - This should really only be used for functions. - - EXAMPLES:: - - sage: from sage.misc.superseded import deprecated_callable_import - sage: is_prime(3) - True - sage: message = "Using %(name)s from here is deprecated." - sage: deprecated_callable_import(13109, None, globals(), locals(), ['is_prime'], message) - sage: is_prime(3) - doctest:...: DeprecationWarning: - Using is_prime from here is deprecated. - See http://trac.sagemath.org/13109 for details. - True - sage: del is_prime - sage: deprecated_callable_import(13109, 'sage.arith.all', globals(), locals(), ['is_prime']) - sage: is_prime(3) - doctest:...: DeprecationWarning: - Using is_prime from here is deprecated. If you need to use it, please import it directly from sage.arith.all. - See http://trac.sagemath.org/13109 for details. - True - """ - _check_trac_number(trac_number) - if message is None: - message = '\nUsing %(name)s from here is deprecated. ' + \ - 'If you need to use it, please import it directly from %(module_name)s.' - from functools import partial - from sage.misc.decorators import sage_wraps - if module_name is None: - mod_dict = globs - else: - mod_dict = __import__(module_name, globs, locs, fromlist).__dict__ - for name in fromlist: - func = mod_dict[name] - def wrapper(func, name, *args, **kwds): - from sage.misc.superseded import deprecation - deprecation(trac_number, message%{'name': name, 'module_name': module_name}) - return func(*args, **kwds) - wrapped_function = sage_wraps(func)(partial(wrapper, func, name)) - wrapped_function.__doc__ = message%{'name': name, 'module_name': module_name} - globs[name] = wrapped_function - del name diff --git a/src/sage/misc/trace.py b/src/sage/misc/trace.py index efcaa33cc5f..5d4d8bc293c 100644 --- a/src/sage/misc/trace.py +++ b/src/sage/misc/trace.py @@ -46,10 +46,13 @@ def trace(code, preparse=True): TESTS: - The only real way to test this is via pexpect spawning a - sage subprocess that uses IPython. + For tests we disable garbage collection, see :trac:`21258` :: - :: + sage: import gc + sage: gc.disable() + + The only real way to test this is via pexpect spawning a + sage subprocess that uses IPython:: sage: import pexpect sage: s = pexpect.spawn('sage') @@ -59,9 +62,7 @@ def trace(code, preparse=True): sage: _ = s.expect('100', timeout=90) Seeing the ipdb prompt and the 2 \* 5 in the output below is a - strong indication that the trace command worked correctly. - - :: + strong indication that the trace command worked correctly:: sage: print(s.before[s.before.find('--'):]) --... @@ -75,6 +76,10 @@ def trace(code, preparse=True): Traceback (most recent call last): ... NotImplementedError: the trace command is not implemented in the Sage notebook; you must use the command line. + + Re-enable garbage collection:: + + sage: gc.enable() """ from sage.plot.plot import EMBEDDED_MODE if EMBEDDED_MODE: diff --git a/src/sage/misc/weak_dict.pyx b/src/sage/misc/weak_dict.pyx index 3de8f54edde..3103c4977f5 100644 --- a/src/sage/misc/weak_dict.pyx +++ b/src/sage/misc/weak_dict.pyx @@ -123,22 +123,11 @@ from copy import deepcopy from cpython.dict cimport * from cpython.weakref cimport PyWeakref_NewRef -from cpython.list cimport PyList_New from cpython.object cimport PyObject_Hash from cpython cimport Py_XINCREF, Py_XDECREF +from dict_del_by_value cimport * cdef extern from "Python.h": - ctypedef struct PyDictEntry: - Py_ssize_t me_hash - PyObject* me_key - PyObject* me_value - ctypedef struct PyDictObject: - Py_ssize_t ma_fill - Py_ssize_t ma_used - Py_ssize_t ma_mask - PyDictEntry* ma_table - PyDictEntry* (*ma_lookup)(PyDictObject *mp, PyObject *key, long hash) except NULL - PyObject* Py_None #we need this redefinition because we want to be able to call #PyWeakref_GetObject with borrowed references. This is the recommended @@ -146,183 +135,6 @@ cdef extern from "Python.h": PyObject* PyWeakref_GetObject(PyObject * wr) int PyList_SetItem(object list, Py_ssize_t index, PyObject * item) except -1 -cdef PyObject* PyDict_GetItemWithError(dict op, object key) except? NULL: - cdef PyDictEntry* ep - cdef PyDictObject* mp = op - ep = mp.ma_lookup(mp, key, PyObject_Hash(key)) - if ep: - return ep.me_value - else: - return NULL - -#this routine extracts the "dummy" sentinel value that is used in dicts to mark -#"freed" slots. We need that to delete things ourselves. - -cdef PyObject* init_dummy() except NULL: - cdef dict D = dict() - cdef PyDictObject* mp = D - cdef size_t mask - cdef PyDictEntry* ep0 = mp.ma_table - cdef PyDictEntry* ep - cdef size_t i - global dummy - - D[0]=0; del D[0] #ensure that there is a "deleted" entry in the dict - mask = mp.ma_mask - #since our entry had hash 0, we should really succeed on the first iteration - for i in range(mask+1): - ep = &(ep0[i]) - if ep.me_key != NULL: - return ep.me_key - raise RuntimeError("Problem initializing dummy") - -#note that dummy here is a borrowed reference. That's not a problem because -#we're never giving it up and dictobject.c is also holding a permanent reference -#to this object -cdef PyObject* dummy = init_dummy() - -#this routine looks for the first entry in dict D with given hash of the -#key and given (identical!) value and deletes that entry. -cdef del_dictitem_by_exact_value(PyDictObject *mp, PyObject *value, long hash): - """ - This is used in callbacks for the weak values of :class:`WeakValueDictionary`. - - INPUT: - - - ``PyDictObject *mp`` -- pointer to a dict - - ``PyObject *value`` -- pointer to a value of the dictionary - - ``long hash`` -- hash of the key by which the value is stored in the dict - - The hash bucket determined by the given hash is searched for the item - containing the given value. If this item cannot be found, the function is - silently returning. Otherwise, the item is removed from the dict. - - TESTS: - - The following is an indirect doctest, as discussed on :trac:`13394`. - :: - - sage: from sage.misc.weak_dict import WeakValueDictionary - sage: V = [set(range(n)) for n in range(5)] - sage: D = WeakValueDictionary(enumerate(V)) - - The line ``V[k] = None`` triggers execution of the callback functions of - the dict values. However, the actual deletion is postponed till after the - iteration over the dictionary has finished. Hence, when the callbacks are - executed, the values which the callback belongs to has already been - overridded by a new value. Therefore, the callback does not delete the - item:: - - sage: for k in D: # indirect doctest - ....: V[k] = None - ....: D[k] = ZZ - sage: len(D) - 5 - sage: D[1] - Integer Ring - - TESTS: - - The following shows that the deletion of deeply nested structures does not - result in an error, by :trac:`15506`:: - - sage: class A: pass - sage: a = A(); prev = a - sage: M = WeakValueDictionary() - sage: for i in range(10^3+10): newA = A(); M[newA] = prev; prev = newA - sage: del a - - """ - cdef size_t i - cdef size_t perturb - cdef size_t mask = mp.ma_mask - cdef PyDictEntry* ep0 = mp.ma_table - cdef PyDictEntry* ep - i = hash & mask - ep = &(ep0[i]) - - if ep.me_key == NULL: - # key not found - return - - perturb = hash - while ((ep.me_value) != value or ep.me_hash != hash): - i = (i << 2) + i + perturb +1 - ep = &ep0[i & mask] - if ep.me_key == NULL: - # key not found - return - perturb = perturb >> 5 #this is the value of PERTURB_SHIFT - - T=PyList_New(2) - PyList_SetItem(T, 0, ep.me_key) - if dummy == NULL: - raise RuntimeError("dummy needs to be initialized") - Py_XINCREF(dummy) - ep.me_key = dummy - PyList_SetItem(T, 1, ep.me_value) - ep.me_value = NULL - mp.ma_used -= 1 - #We have transferred the to-be-deleted references to the list T - #we now delete the list so that the actual decref happens through a - #deallocation routine that uses the Python Trashcan macros to - #avoid stack overflow in deleting deep structures. - del T - -def test_del_dictitem_by_exact_value(D, value, h): - """ - This function helps testing some cdef function used to delete dictionary items. - - INPUT: - - - ``D`` -- a Python ````. - - ``value`` -- an object that is value ``D``. - - ``h`` -- the hash of the key under which to find ``value`` in ``D``. - - The underlying cdef function deletes an item from ``D`` that is in the - hash bucket determined by ``h`` and whose value is identic with - ``value``. Of course, this only makes sense if the pairs ``(h, value)`` - corresponding to items in ``D`` are pair-wise distinct. - - If a matching item can not be found, the function does nothing and - silently returns. - - TESTS: - - See :trac:`13394` for a discussion. - :: - - sage: from sage.misc.weak_dict import test_del_dictitem_by_exact_value - sage: B=1000 - sage: L=list(range(B)) - sage: D1=dict() - sage: D2=dict() - sage: for i in range(100000): # long time - ....: ki=L[floor(random()*B)] - ....: vi=L[floor(random()*B)] - ....: D1[ki]=vi - ....: D2[ki]=vi - ....: ko=L[floor(random()*B)] - ....: if ko in D1: - ....: vo=D1[ko] - ....: del D1[ko] - ....: test_del_dictitem_by_exact_value(D2,vo,hash(ko)) - ....: assert D1 == D2 - - No action is taken if the item prescribed by key hash and value does not - exist in the dictionary:: - - sage: D = {1: ZZ} - sage: test_del_dictitem_by_exact_value(D, ZZ, 2) - sage: D - {1: Integer Ring} - sage: test_del_dictitem_by_exact_value(D, QQ, 1) - sage: D - {1: Integer Ring} - - """ - return del_dictitem_by_exact_value(D, value, h) - cdef class WeakValueDictEraser: """ Erases items from a :class:`sage.misc.weak_dict.WeakValueDictionary` when @@ -924,9 +736,8 @@ cdef class WeakValueDictionary(dict): TypeError: mutable matrices are unhashable """ - cdef PyDictObject* mp=self - cdef PyDictEntry* ep=mp.ma_lookup(mp,k, PyObject_Hash(k)) - return (ep.me_value != NULL) and (PyWeakref_GetObject(ep.me_value) != Py_None) + cdef PyObject* wr = PyDict_GetItemWithError(self, k) + return (wr != NULL) and (PyWeakref_GetObject(wr) != Py_None) #def __len__(self): #since GC is not deterministic, neither is the length of a WeakValueDictionary, diff --git a/src/sage/modular/abvar/torsion_point.py b/src/sage/modular/abvar/torsion_point.py index 9286260b4b0..9bcf5fb2f4d 100644 --- a/src/sage/modular/abvar/torsion_point.py +++ b/src/sage/modular/abvar/torsion_point.py @@ -44,7 +44,7 @@ class TorsionPoint(ModuleElement): sage: G = J.finite_subgroup([[1/3,0], [0,1/5]]); G Finite subgroup with invariants [15] over QQbar of Abelian variety J0(11) of dimension 1 sage: type(G.0) - + """ def __init__(self, parent, element, check=True): """ diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp index 6e18266ef9f..142774d95c7 100644 --- a/src/sage/modular/arithgroup/farey.cpp +++ b/src/sage/modular/arithgroup/farey.cpp @@ -29,14 +29,8 @@ #include #include "farey.hpp" +#include "sage/modular/arithgroup/farey_symbol.h" -// The following functions are defined in farey_symbols.h, but direct inclusion -// of this file breaks compilation on cygwin, see trac #13336. -extern "C" long convert_to_long(PyObject *); -extern "C" PyObject *convert_to_Integer(mpz_class); -extern "C" PyObject *convert_to_rational(mpq_class); -extern "C" PyObject *convert_to_cusp(mpq_class); -extern "C" PyObject *convert_to_SL2Z(SL2Z); using namespace std; diff --git a/src/sage/modular/arithgroup/farey_symbol.pyx b/src/sage/modular/arithgroup/farey_symbol.pyx index cde53809c1e..7df08d4017a 100644 --- a/src/sage/modular/arithgroup/farey_symbol.pyx +++ b/src/sage/modular/arithgroup/farey_symbol.pyx @@ -1,3 +1,4 @@ +# distutils: sources = sage/modular/arithgroup/sl2z.cpp sage/modular/arithgroup/farey.cpp r""" Farey Symbol for arithmetic subgroups of `{\rm PSL}_2(\ZZ)` @@ -49,7 +50,7 @@ from sage.misc.cachefunc import cached_method from sage.structure.sage_object cimport richcmp_not_equal -cdef extern from "sage/modular/arithgroup/sl2z.cpp": +cdef extern from "sage/modular/arithgroup/sl2z.hpp": cppclass cpp_SL2Z "SL2Z": mpz_class a, b, c, d cpp_SL2Z(int, int, int, int) @@ -59,7 +60,7 @@ cdef extern from "sage/modular/arithgroup/sl2z.cpp": mpz_class c() mpz_class d() -cdef extern from "sage/modular/arithgroup/farey.cpp": +cdef extern from "sage/modular/arithgroup/farey.hpp": cppclass is_element_Gamma0: is_element_Gamma0(int) cppclass is_element_Gamma1: diff --git a/src/sage/modular/modform/eis_series_cython.pyx b/src/sage/modular/modform/eis_series_cython.pyx index b6c0e484055..f7e944cb055 100644 --- a/src/sage/modular/modform/eis_series_cython.pyx +++ b/src/sage/modular/modform/eis_series_cython.pyx @@ -1,8 +1,9 @@ """ Eisenstein Series (optimized compiled functions) """ -include 'sage/ext/stdsage.pxi' -include "cysignals/signals.pxi" + +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off from sage.rings.rational_field import QQ from sage.rings.power_series_ring import PowerSeriesRing @@ -60,8 +61,8 @@ cpdef Ek_ZZ(int k, int prec=10): # allocate the list for the result cdef list val = [] - for i from 0 <= i < prec: - pp = (PY_NEW(Integer)) + for i in range(prec): + pp = Integer.__new__(Integer) mpz_set_si(pp.value, 1) val.append(pp) @@ -71,7 +72,7 @@ cpdef Ek_ZZ(int k, int prec=10): max_power_sum = prec while max_power_sum: max_power_sum >>= 1 - pp = (PY_NEW(Integer)) + pp = Integer.__new__(Integer) mpz_set_si(pp.value, 1) power_sum_ls.append(pp) @@ -158,7 +159,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 = sig_malloc(prec * sizeof(mpz_t)) + cdef mpz_t *val = check_allocarray(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 diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 8b51d0bf2c5..1e95264a3b6 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -1271,7 +1271,7 @@ def element(self): q - 2*q^2 + (-a1 - 2)*q^3 + 4*q^4 + (2*a1 + 10)*q^5 + O(q^6), q + 2*q^2 + (1/2*a2 - 1)*q^3 + 4*q^4 + (-3/2*a2 + 12)*q^5 + O(q^6)] sage: type(ls2[0]) - + sage: ls2[2][3].minpoly() x^2 - 9*x + 2 """ diff --git a/src/sage/modular/modform_hecketriangle/constructor.py b/src/sage/modular/modform_hecketriangle/constructor.py index 5c2c915f5d2..db0974fd125 100644 --- a/src/sage/modular/modform_hecketriangle/constructor.py +++ b/src/sage/modular/modform_hecketriangle/constructor.py @@ -171,7 +171,7 @@ def rational_type(f, n=ZZ(3), base_ring=ZZ): weight = None ep = None - # Note that we intentially leave out the d-factor! + # Note that we intentionally leave out the d-factor! if (n == infinity): finf_pol = (x-y**2) else: 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 fc1019cb3b9..722bdb9e2cf 100644 --- a/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py +++ b/src/sage/modular/modform_hecketriangle/hecke_triangle_group_element.py @@ -124,7 +124,7 @@ def __init__(self, parent, M, check=True, **kwargs): sage: el == HeckeTriangleGroupElement(G, M) True sage: type(el) - + sage: el.category() Category of elements of Hecke triangle group for n = 10 sage: type(HeckeTriangleGroupElement(G, M)) diff --git a/src/sage/modular/modsym/ambient.py b/src/sage/modular/modsym/ambient.py index 2f4e04d8f66..cd7cdf1d152 100644 --- a/src/sage/modular/modsym/ambient.py +++ b/src/sage/modular/modsym/ambient.py @@ -412,7 +412,7 @@ def _element_constructor_(self, x, computed_with_hecke=False): sage: M(0) 0 sage: type(M(0)) - + From a vector of the correct dimension we construct the corresponding linear combination of the basis elements:: diff --git a/src/sage/modular/overconvergent/hecke_series.py b/src/sage/modular/overconvergent/hecke_series.py index 4922aa87dce..8590272dff2 100644 --- a/src/sage/modular/overconvergent/hecke_series.py +++ b/src/sage/modular/overconvergent/hecke_series.py @@ -670,12 +670,15 @@ def hecke_series_degree_bound(p,N,k,m): # Returns matrix A modulo p^m from Step 6 of Algorithm 2. -def higher_level_UpGj(p,N,klist,m,modformsring,bound): +def higher_level_UpGj(p, N, klist, m, modformsring, bound, extra_data=False): r""" - Returns a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` - parameterised by the weights k in ``klist``. The matrix `A_k` is the finite - square matrix which occurs on input p,k,N and m in Step 6 of Algorithm 2 in - [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined + Return a list ``[A_k]`` of square matrices over ``IntegerRing(p^m)`` + parameterised by the weights k in ``klist``. + + The matrix `A_k` is the finite square matrix which occurs on input + p, k, N and m in Step 6 of Algorithm 2 in [Lau2011]_. + + Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices.) @@ -685,12 +688,15 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): - ``N`` -- integer at least 2 and not divisible by p (level). - ``klist`` -- list of integers all congruent modulo (p-1) (the weights). - ``m`` -- positive integer. - - ``modformsring`` -- True or False. + - ``modformsring`` -- ``True`` or ``False``. - ``bound`` -- (even) positive integer. + - ``extra_data`` -- (default: ``False``) boolean. OUTPUT: - - list of square matrices. + - list of square matrices. If ``extra_data`` is ``True``, return also + extra intermediate data, namely the matrix `E` in [Lau2011]_ and + the integers ``elldash`` and ``mdash``. EXAMPLES:: @@ -704,7 +710,8 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): [ 0 7 20 0 20 0] [ 0 1 24 0 20 0] ] - + sage: len(higher_level_UpGj(5,3,[4],2,true,6,extra_data=True)) + 4 """ t = cputime() # Step 1 @@ -715,11 +722,12 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): elldashp = elldash*p mdash = m + ceil(n/(p+1)) - verbose("done step 1",t) + verbose("done step 1", t) t = cputime() # Steps 2 and 3 - e,Ep1 = higher_level_katz_exp(p,N,k0,m,mdash,elldash,elldashp,modformsring,bound) + e, Ep1 = higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, + modformsring, bound) ell = dimension(transpose(e)[0].parent()) S = e[0,0].parent() @@ -753,14 +761,14 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. - A = matrix(S,ell,ell) + A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell) verbose("elldash is %s" % elldash) - for i in range(0,ell): + for i in range(ell): Ti = T[i] - for j in range(0,ell): - ej = Ti.parent()([e[j][l] for l in range(0,elldash)]) + for j in range(ell): + ej = Ti.parent()([e[j][l] for l in range(elldash)]) ejleadpos = ej.nonzero_positions()[0] lj = ZZ(ej[ejleadpos]) A[i,j] = S(ZZ(Ti[j])/lj) @@ -769,7 +777,10 @@ def higher_level_UpGj(p,N,klist,m,modformsring,bound): Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A)) verbose("done step 6", t) - return Alist + if extra_data: + return Alist, e, elldash, mdash + else: + return Alist # *** LEVEL 1 CODE *** @@ -915,12 +926,15 @@ def katz_expansions(k0,p,ellp,mdash,n): # *** MAIN FUNCTION FOR LEVEL 1 *** -def level1_UpGj(p,klist,m): +def level1_UpGj(p, klist, m, extra_data=False): r""" - Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` - parameterised by the weights k in ``klist``. The matrix `A_k` is the finite - square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in - [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined + Return a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` + parameterised by the weights k in ``klist``. + + The matrix `A_k` is the finite square matrix which occurs on input + p, k and m in Step 6 of Algorithm 1 in [Lau2011]_. + + Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices. @@ -929,10 +943,13 @@ def level1_UpGj(p,klist,m): - ``p`` -- prime at least 5. - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights). - ``m`` -- positive integer. + - ``extra_data`` -- (default: ``False``) boolean OUTPUT: - - list of square matrices. + - list of square matrices. If ``extra_data`` is ``True``, return also + extra intermediate data, namely the matrix `E` in [Lau2011]_ and + the integers ``elldash`` and ``mdash``. EXAMPLES:: @@ -945,6 +962,9 @@ def level1_UpGj(p,klist,m): [ 0 1995 4802 0 0] [ 0 9212 14406 0 0] ] + sage: len(level1_UpGj(7,[100],5,extra_data=True)) + 4 + """ # Step 1 t = cputime() @@ -1012,7 +1032,10 @@ def level1_UpGj(p,klist,m): Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A)) verbose("done step 6", t) - return Alist + if extra_data: + return Alist, e, ell, mdash + else: + return Alist # *** CODE FOR GENERAL LEVEL *** diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index 945f6bf1a24..65c58d1bf12 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -52,9 +52,6 @@ from sage.rings.rational cimport Rational from sage.misc.misc import verbose, cputime from sage.rings.infinity import Infinity -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" - from sage.libs.flint.nmod_poly cimport (nmod_poly_init2_preinv, nmod_poly_set_coeff_ui, nmod_poly_inv_series, @@ -855,7 +852,7 @@ cdef class Dist_vector(Dist): sage: D = OverconvergentDistributions(3,5,4) # indirect doctest sage: v = D([1,1,1]) """ - cdef Dist_vector ans = PY_NEW(Dist_vector) + cdef Dist_vector ans = Dist_vector.__new__(Dist_vector) ans._parent = self._parent return ans @@ -1237,391 +1234,6 @@ cdef class Dist_vector(Dist): return ans -# cdef class Dist_long(Dist): -# r""" -# A class for distributions implemented using a C array of longs. - -# INPUT: - -# - ``moments`` -- the list of moments. If ``check == False`` it -# must be a vector in the appropriate approximation module. - -# - ``parent`` -- a :class:`distributions.OverconvergentDistributions_class` or -# :class:`distributions.Symk_class` instance - -# - ``check`` -- (default: True) boolean, whether to validate input - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# def __init__(self, moments, parent, ordp=0, check=True): -# """ -# Initialization. - -# TESTS:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# # if not hasattr(parent,'Element'): -# # parent, moments = moments, parent - -# Dist.__init__(self, parent) -# p = parent._p -# cdef int i -# if check: - -# # case 1: input is a distribution already -# if isinstance(moments, Dist): -# M = len(moments) -# moments = [ZZ(moments.moment(i)) for i in range(M)] -# # case 2: input is a vector, or something with a len -# elif hasattr(moments, '__len__'): -# M = len(moments) -# moments = [ZZ(a) for a in parent.approx_module(M)(moments)] -# # case 3: input is zero -# elif moments == 0: -# M = parent.precision_cap() -# moments = [ZZ(0)] * M -# else: -# M = 1 -# moments = [ZZ(moments)] -# if M > 100 or 7 * p ** M > ZZ(2) ** (4 * sizeof(long) - 1): # 6 is so that we don't overflow on gathers -# raise ValueError("moments too long") -# else: -# M = len(moments) - -# for i in range(len(moments)): -# self._moments[i] = moments[i] -# self.relprec = M -# self.prime_pow = parent.prime_pow -# self.normalize() - -# cdef Dist_long _new_c(self): -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# cdef Dist_long ans = PY_NEW(Dist_long) -# ans._parent = self._parent -# ans.prime_pow = self.prime_pow -# return ans - -# def _repr_(self): -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# valstr = "" -# if self.ordp == 1: -# valstr = "%s * " % (self.prime_pow.prime) -# elif self.ordp != 0: -# valstr = "%s^%s * " % (self.prime_pow.prime, self.ordp) -# if self.relprec == 1: -# return valstr + repr(self._moments[0]) -# else: -# return valstr + "(" + ", ".join([repr(self._moments[i]) for i in range(self.relprec)]) + ")" - -# cdef int quasi_normalize(self) except -1: -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# cdef int i -# for i in range(self.relprec): -# if self._moments[i] > overflow: -# self._moments[i] = self._moments[i] % self.prime_pow(self.relprec - i) -# elif self._moments[i] < underflow: -# self._moments[i] = self._moments[i] % self.prime_pow(self.relprec - i) -# self._moments[i] += self.prime_pow(self.relprec - i) - -# cpdef normalize(self): -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# cdef int i -# for i in range(1, self.relprec): # Don't normalize the zeroth moment -# if self._moments[i] < 0: -# self._moments[i] = self._moments[i] % self.prime_pow(self.relprec - i) -# self._moments[i] += self.prime_pow(self.relprec - i) -# elif self._moments[i] >= self.prime_pow(self.relprec - i): -# self._moments[i] = self._moments[i] % self.prime_pow(self.relprec - i) -# return self - -# cdef long _relprec(self): -# return self.relprec - -# cdef _unscaled_moment(self, long _n): -# r""" - - -# INPUT: - -# - ``_n`` -- an integer or slice giving an index into the -# moments. - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# if isinstance(_n, slice): -# a, b, c = _n.indices(self.relprec) -# return [self.moment(i) for i in range(a, b, c)] -# cdef int n = _n -# if n < 0: -# n += self.relprec -# if n < 0 or n >= self.relprec: -# raise IndexError("list index out of range") -# return self._moments[n] - -# cdef Dist_long _addsub(self, Dist_long right, bint negate): -# r""" -# Common code for the sum and the difference of two distributions -# """ -# cdef Dist_long ans = self._new_c() -# cdef long aprec = min(self.ordp + self.relprec, right.ordp + right.relprec) -# ans.ordp = min(self.ordp, right.ordp) -# ans.relprec = aprec - ans.ordp -# # In the case of symk, rprec will always be k -# cdef int i, n -# cdef long diff, cutoff -# # The following COULD overflow, but it would require 2^32 -# # additions (on a 64-bit machine), since we restrict p^k to be -# # less than 2^31/7. -# if self.ordp == right.ordp: -# n = min(self.relprec, right.relprec) -# for i in range(n): -# ans._moments[i] = self._moments[i] - right._moments[i] if negate else self._moments[i] + right._moments[i] -# if self.relprec < ans.relprec: -# for i in range(n, ans.relprec): -# ans._moments[i] = -right._moments[i] if negate else right._moments[i] -# elif ans.relprec < self.relprec: -# for i in range(n, ans.relprec): -# ans._moments[i] = self._moments[i] -# elif self.ordp < right.ordp: -# diff = right.ordp - self.ordp -# n = min(right.relprec, ans.relprec - diff) -# for i in range(n): -# ans._moments[i] = self.prime_pow(diff) * (right._moments[i] % self.prime_pow(ans.relprec - diff - i)) -# ans._moments[i] = self._moments[i] - ans._moments[i] if negate else self._moments[i] + ans._moments[i] -# if n < ans.relprec: -# for i in range(n, ans.relprec): -# ans._moments[i] = self._moments[i] -# else: # self.ordp > right.ordp -# diff = self.ordp - right.ordp -# n = min(self.relprec, ans.relprec - diff) -# for i in range(n): -# ans._moments[i] = self.prime_pow(diff) * (self._moments[i] % self.prime_pow(ans.relprec - diff - i)) -# ans._moments[i] += -right._moments[i] if negate else right._moments[i] -# if n < ans.relprec: -# for i in range(n, ans.relprec): -# ans._moments[i] = -right._moments[i] if negate else right._moments[i] -# return ans - -# cpdef _add_(self, ModuleElement right): -# r""" - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# return self._addsub( right, False) - -# cpdef _sub_(self, ModuleElement right): -# r""" - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# return self._addsub( right, True) - -# cpdef _lmul_(self, RingElement _right): -# r""" - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# cdef Dist_long ans = self._new_c() -# ans.relprec = self.relprec -# self.quasi_normalize() -# cdef long scalar, absprec, ordp -# cdef Integer iright, unit, ppow, p = self.prime_pow.prime -# cdef Rational qright, qunit -# cdef pAdicCappedAbsoluteElement pcaright -# cdef pAdicCappedRelativeElement pcrright -# cdef pAdicFixedModElement pfmright -# if isinstance(_right, Integer): -# iright = _right -# if mpz_sgn(iright.value) == 0: -# ans.ordp = maxordp -# ans.relprec = 0 -# return ans -# unit = PY_NEW(Integer) -# ordp = mpz_remove(unit.value, iright.value, p.value) -# if mpz_fits_slong_p(unit.value): -# scalar = mpz_get_si(iright.value) % self.prime_pow(self.relprec) -# else: -# scalar = mpz_fdiv_ui(iright.value, self.prime_pow(self.relprec)) -# elif isinstance(_right, Rational): -# qright = _right -# if mpq_sgn(qright.value) == 0: -# ans.ordp = maxordp -# ans.relprec = 0 -# return ans -# qunit = PY_NEW(Rational) -# ordp = mpz_remove(mpq_numref(qunit.value), mpq_numref(qright.value), p.value) -# if ordp == 0: -# ordp = -mpz_remove(mpq_denref(qunit.value), mpq_denref(qright.value), p.value) -# else: -# mpz_set(mpq_denref(qunit.value), mpq_denref(qright.value)) -# ppow = PY_NEW(Integer) -# mpz_set_ui(ppow.value, self.prime_pow(self.relprec)) -# # We reuse the pointers inside qunit, since we're going to discard it. -# mpz_invert(mpq_denref(qunit.value), mpq_denref(qunit.value), ppow.value) -# mpz_mul(mpq_numref(qunit.value), mpq_numref(qunit.value), mpq_denref(qunit.value)) -# scalar = mpz_fdiv_ui(mpq_numref(qunit.value), self.prime_pow(self.relprec)) -# # qunit should not be used now (it's unnormalized) -# elif isinstance(_right, pAdicCappedAbsoluteElement): -# pcaright = _right -# unit = PY_NEW(Integer) -# ordp = mpz_remove(unit.value, pcaright.value, p.value) -# if pcaright.absprec - ordp <= self.relprec: -# ans.relprec = pcaright.absprec - ordp -# scalar = mpz_get_si(unit.value) -# else: -# scalar = mpz_fdiv_ui(unit.value, self.prime_pow(self.relprec)) -# elif isinstance(_right, pAdicCappedRelativeElement): -# pcrright = _right -# ordp = pcrright.ordp -# if pcrright.relprec <= self.relprec: -# ans.relprec = pcrright.relprec -# scalar = mpz_get_si(pcrright.unit) -# else: -# scalar = mpz_fdiv_ui(pcrright.unit, self.prime_pow(self.relprec)) -# elif isinstance(_right, pAdicFixedModElement): -# pfmright = _right -# scalar = mpz_get_si(pfmright.value) -# ordp = 0 -# cdef int i -# for i in range(self.relprec): -# ans._moments[i] = self._moments[i] * scalar -# ans.ordp = self.ordp + ordp -# ans.quasi_normalize() -# return ans - -# def precision_relative(self): -# return Integer(self.relprec) - -# def precision_absolute(self): -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# return Integer(self.relprec + self.ordp) - -# def reduce_precision(self, M): -# r""" - - -# INPUT: - -# - ``M`` -- a positive integer less than the precision of this -# distribution. - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# if M > self.relprec: -# raise ValueError("not enough moments") -# if M < 0: -# raise ValueError("precision must be non-negative") -# cdef Dist_long ans = self._new_c() -# ans.relprec = M -# cdef int i -# for i in range(ans.relprec): -# ans._moments[i] = self._moments[i] -# ans.ordp = self.ordp -# return ans - -# def solve_diff_eqn(self): -# r""" - - -# OUTPUT: - -# - - -# EXAMPLES:: - -# sage: from sage.modular.pollack_stevens.distributions import OverconvergentDistributions, Symk -# """ -# raise NotImplementedError - -# def __reduce__(self): -# r""" -# Used in pickling. - -# EXAMPLES:: - -# sage: D = OverconvergentDistributions(0, 5, 10) -# sage: D([1,2,3,4]).__reduce__() -# (, ([1, 2, 3, 4], Space of 5-adic distributions with k=0 action and precision cap 10, 0, False)) -# """ -# return (self.__class__, ([self._moments[i] -# for i in xrange(self.relprec)], -# self.parent(), self.ordp, False)) - - cdef class WeightKAction(Action): r""" Encode the action of the monoid `\Sigma_0(N)` on the space of distributions. diff --git a/src/sage/modules/fg_pid/fgp_element.py b/src/sage/modules/fg_pid/fgp_element.py index 241fb3bbe80..1fd2a399652 100644 --- a/src/sage/modules/fg_pid/fgp_element.py +++ b/src/sage/modules/fg_pid/fgp_element.py @@ -43,7 +43,7 @@ class FGP_Element(ModuleElement): sage: isinstance(x, sage.modules.fg_pid.fgp_element.FGP_Element) True sage: type(x) - + sage: x is Q(x) True sage: x.parent() is Q @@ -70,7 +70,7 @@ def __init__(self, parent, x, check=DEBUG): sage: V = span([[1/2,1,1],[3/2,2,1],[0,0,1]],ZZ); W = V.span([2*V.0+4*V.1, 9*V.0+12*V.1, 4*V.2]) sage: Q = V/W sage: x = Q(V.0-V.1); type(x) - + sage: isinstance(x,sage.modules.fg_pid.fgp_element.FGP_Element) True diff --git a/src/sage/modules/fg_pid/fgp_module.py b/src/sage/modules/fg_pid/fgp_module.py index 37ce98f22ec..9ad0582e223 100644 --- a/src/sage/modules/fg_pid/fgp_module.py +++ b/src/sage/modules/fg_pid/fgp_module.py @@ -603,7 +603,7 @@ def _element_constructor_(self, x, check=True): sage: x = Q(V.0-V.1); x # indirect doctest (0, 3) sage: type(x) - + sage: x is Q(x) True sage: x.parent() is Q diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 19b2192c243..b30690de769 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -688,13 +688,13 @@ def __init__(self, base_ring, rank, degree, sparse=False, Ambient free module of rank 3 over the integral domain Multivariate Polynomial Ring in x0, x1, x2 over Rational Field sage: FreeModule(GF(7),3).category() - Category of finite enumerated finite dimensional vector spaces with basis over + Category of enumerated finite dimensional vector spaces with basis over (finite enumerated fields and subquotients of monoids and quotients of semigroups) sage: V = QQ^4; V.category() Category of finite dimensional vector spaces with basis over (quotient fields and metric spaces) sage: V = GF(5)**20; V.category() - Category of finite enumerated finite dimensional vector spaces with basis over (finite enumerated fields and subquotients of monoids and quotients of semigroups) + Category of enumerated finite dimensional vector spaces with basis over (finite enumerated fields and subquotients of monoids and quotients of semigroups) sage: FreeModule(ZZ,3).category() Category of finite dimensional modules with basis over (euclidean domains and infinite enumerated sets @@ -1170,8 +1170,17 @@ def cardinality(self): 144 sage: (QQ^3).cardinality() +Infinity + + TESTS: + + Check that :trac:`22987` is fixed:: + + sage: VectorSpace(QQ, 0).cardinality() + 1 """ - return (self.base_ring().cardinality())**self.rank() + if not self.rank(): + return sage.rings.integer.Integer(1) + return self.base_ring().cardinality() ** self.rank() __len__ = cardinality # for backward compatibility diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 71e81cdb443..91873f2a4ae 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -104,6 +104,7 @@ TESTS:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import cimport cython from cpython.slice cimport PySlice_GetIndicesEx @@ -429,7 +430,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None): # !! PLEASE DO NOT MOVE THIS CODE LOWER IN THIS FUNCTION !! if arg2 is None and is_Ring(arg0) and (isinstance(arg1, (int, long, Integer))): if sparse: - from free_module import FreeModule + from .free_module import FreeModule M = FreeModule(arg0, arg1, sparse=True) else: M = arg0 ** arg1 @@ -481,14 +482,14 @@ def vector(arg0, arg1=None, arg2=None, sparse=None): if isinstance(v, ndarray): if len(v.shape) != 1: raise TypeError("cannot convert %r-dimensional array to a vector" % len(v.shape)) - from free_module import VectorSpace + from .free_module import VectorSpace if (R is None or R is RDF) and v.dtype.kind == 'f': V = VectorSpace(RDF, v.shape[0]) - from vector_real_double_dense import Vector_real_double_dense + from .vector_real_double_dense import Vector_real_double_dense return Vector_real_double_dense(V, v) if (R is None or R is CDF) and v.dtype.kind == 'c': V = VectorSpace(CDF, v.shape[0]) - from vector_complex_double_dense import Vector_complex_double_dense + from .vector_complex_double_dense import Vector_complex_double_dense return Vector_complex_double_dense(V, v) # Use slower conversion via list v = list(v) @@ -506,7 +507,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None): v, R = prepare(v, R, degree) if sparse: - from free_module import FreeModule + from .free_module import FreeModule M = FreeModule(R, len(v), sparse=True) else: M = R ** len(v) @@ -1755,7 +1756,7 @@ cdef class FreeModuleElement(Vector): # abstract base class values = [] for n in range(slicelength): values.append(self.get_unsafe(start + n*step)) - from free_module import FreeModule + from .free_module import FreeModule M = FreeModule(self.coordinate_ring(), slicelength, sparse=self.is_sparse()) return M(values, coerce=False, copy=False) else: @@ -4013,7 +4014,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): sage: v = vector([-1,0,3,pi]) sage: type(v) - + sage: v.__copy__() (-1, 0, 3, pi) sage: v.__copy__() is v @@ -4309,7 +4310,7 @@ cdef class FreeModuleElement_generic_dense(FreeModuleElement): sage: g (2*x, 2*y) sage: type(g) - + sage: g(y=2, x=3) (6, 4) sage: f(x,y) = x^2 + y^2 @@ -4820,7 +4821,7 @@ cdef class FreeModuleElement_generic_sparse(FreeModuleElement): if min <= n <= max and n % step == mod: k = (n - start) // step newentries[k] = x - from free_module import FreeModule + from .free_module import FreeModule M = FreeModule(self.coordinate_ring(), slicelength, sparse=True) return M(newentries, coerce=False, copy=False) diff --git a/src/sage/modules/vector_double_dense.pyx b/src/sage/modules/vector_double_dense.pyx index f6e3817c742..6e7ff809215 100644 --- a/src/sage/modules/vector_double_dense.pyx +++ b/src/sage/modules/vector_double_dense.pyx @@ -40,10 +40,11 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import cimport numpy import numpy -import free_module_element +from .free_module_element import FreeModuleElement from sage.structure.element cimport Element, ModuleElement, RingElement, Vector @@ -467,8 +468,8 @@ cdef class Vector_double_dense(FreeModuleElement): else: self._vector_numpy = scipy.fftpack.ifft(self._vector_numpy, overwrite_x = True) else: - V = CDF**self._degree - from vector_complex_double_dense import Vector_complex_double_dense + V = CDF ** self._degree + from .vector_complex_double_dense import Vector_complex_double_dense if direction == 'forward': return Vector_complex_double_dense(V, scipy.fft(self._vector_numpy)) else: diff --git a/src/sage/modules/vector_integer_dense.pyx b/src/sage/modules/vector_integer_dense.pyx index 2424976cf35..f921da42f10 100644 --- a/src/sage/modules/vector_integer_dense.pyx +++ b/src/sage/modules/vector_integer_dense.pyx @@ -54,7 +54,8 @@ TESTS:: #***************************************************************************** from __future__ import absolute_import -include 'sage/ext/stdsage.pxi' +from cysignals.memory cimport check_allocarray, sig_free +from cysignals.signals cimport sig_on, sig_off from sage.structure.element cimport Element, ModuleElement, RingElement, Vector @@ -68,7 +69,7 @@ from sage.libs.gmp.mpz cimport * cdef inline _Integer_from_mpz(mpz_t e): - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) mpz_set(z.value, e) return z @@ -182,7 +183,7 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): sage: v[::-1] (3, 2, 1) """ - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) mpz_set(z.value, self._entries[i]) return z @@ -254,8 +255,7 @@ cdef class Vector_integer_dense(free_module_element.FreeModuleElement): 4 """ cdef Vector_integer_dense r = right - cdef Integer z - z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) cdef mpz_t t mpz_init(t) mpz_set_si(z.value, 0) diff --git a/src/sage/modules/vector_symbolic_dense.py b/src/sage/modules/vector_symbolic_dense.py index 5d771bc0265..74f3bfee004 100644 --- a/src/sage/modules/vector_symbolic_dense.py +++ b/src/sage/modules/vector_symbolic_dense.py @@ -15,7 +15,7 @@ sage: u = vector([sin(x)^2 + cos(x)^2, log(2*y) + log(3*y)]); u (cos(x)^2 + sin(x)^2, log(3*y) + log(2*y)) sage: type(u) - + sage: u.simplify_full() (1, log(3*y) + log(2*y)) @@ -27,15 +27,15 @@ sage: v = vector(SR, [1, 2]) sage: w = vector(SR, [sin(x), 0]) sage: type(v) - + sage: type(w) - + sage: type(v + w) - + sage: type(-v) - + sage: type(5*w) - + Test pickling/unpickling:: diff --git a/src/sage/modules/with_basis/indexed_element.pxd b/src/sage/modules/with_basis/indexed_element.pxd new file mode 100644 index 00000000000..c29caa26d62 --- /dev/null +++ b/src/sage/modules/with_basis/indexed_element.pxd @@ -0,0 +1,12 @@ +from sage.structure.element cimport Element, RingElement + +cdef class IndexedFreeModuleElement(Element): + cdef public dict _monomial_coefficients + cdef long _hash + cdef bint _hash_set + + cpdef dict monomial_coefficients(self, bint copy=*) + cpdef _coefficient_fast(self, m) + cpdef _lmul_(self, RingElement right) + cpdef _rmul_(self, RingElement left) + diff --git a/src/sage/modules/with_basis/indexed_element.pyx b/src/sage/modules/with_basis/indexed_element.pyx new file mode 100644 index 00000000000..7e93a8bc522 --- /dev/null +++ b/src/sage/modules/with_basis/indexed_element.pyx @@ -0,0 +1,891 @@ +# -*- coding: utf-8 -*- +r""" +An element in an indexed free module. + +AUTHORS: + +- Travis Scrimshaw (03-2017): Moved code from :mod:`sage.combinat.free_module`. +""" + +#***************************************************************************** +# Copyright (C) 2017 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from __future__ import print_function + +from six import iteritems + +from sage.structure.element cimport parent +from sage.structure.sage_object cimport richcmp, richcmp_not_equal, rich_to_bool +from cpython.object cimport Py_NE, Py_EQ + +from sage.misc.misc import repr_lincomb +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.typeset.ascii_art import AsciiArt, empty_ascii_art +from sage.typeset.unicode_art import UnicodeArt, empty_unicode_art +from sage.categories.all import Category, Sets, ModulesWithBasis +from sage.data_structures.blas_dict cimport add, negate, scal, axpy + +cdef class IndexedFreeModuleElement(Element): + def __init__(self, M, x): + """ + Create a combinatorial module element. This should never be + called directly, but only through the parent combinatorial + free module's :meth:`__call__` method. + + TESTS:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c']; f + B['a'] + 3*B['c'] + sage: f == loads(dumps(f)) + True + """ + Element.__init__(self, M) + self._monomial_coefficients = x + self._hash_set = False + + def __iter__(self): + """ + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: [i for i in sorted(f)] + [('a', 1), ('c', 3)] + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: a = s([2,1]) + s([3]) + sage: [i for i in sorted(a)] + [([2, 1], 1), ([3], 1)] + """ + return self._monomial_coefficients.iteritems() + + def __contains__(self, x): + """ + Returns whether or not a combinatorial object x indexing a basis + element is in the support of self. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: 'a' in f + True + sage: 'b' in f + False + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: a = s([2,1]) + s([3]) + sage: Partition([2,1]) in a + True + sage: Partition([1,1,1]) in a + False + """ + return x in self._monomial_coefficients and self._monomial_coefficients[x] != 0 + + def __hash__(self): + """ + Return the hash value for ``self``. + + The result is cached. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: hash(f) + 6429418278783588506 # 64-bit + 726440090 # 32-bit + + sage: F = RootSystem(['A',2]).ambient_space() + sage: f = F.simple_root(0) + sage: hash(f) + 6920829894162680369 # 64-bit + -528971215 # 32-bit + + This uses the recipe that was proposed for frozendicts in `PEP + 0416 `_ (and adds + the hash of the parent). This recipe relies on the hash + function for frozensets which uses tricks to mix the hash + values of the items in case they are similar. + + .. TODO:: + + It would be desirable to make the hash value depend on the + hash value of the parent. See :trac:`15959`. + """ + if not self._hash_set: + self._hash = hash(frozenset(self._monomial_coefficients.items())) + self._hash_set = True + return self._hash + + def __reduce__(self): + """ + For pickling. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: loads(dumps(F.an_element())) == F.an_element() + True + """ + return (_unpickle_element, (self._parent, self._monomial_coefficients)) + + def __setstate__(self, state): + r""" + For unpickling old ``CombinatorialFreeModuleElement`` classes. + See :trac:`22632` and register_unpickle_override below. + + EXAMPLES:: + + sage: loads('x\x9c\x95R\xcbn\x131\x14\xd5\x00\r\x89KK\xcb\xa3' + ....: '\xbc\xa1\xbc\xd3\xcd,\xe0\x0f\n\xad\xc4\xa2Y\x0c\xb2XZ' + ....: '\x8e\xe7N\xe6\x8a\xb1\xa7\xd7\x0f\x91,F\x82E&\xe2\xafq3' + ....: '\x13\xa4"X\xb0\xb1}\xae}\xce=\xf7\xc8\xdf\xaf(\'g\x90:o' + ....: '\x83\xf2\xc1B\x9a/\x8c\xd4\xa8\x84\xaa\xa4s\xec2\xa2d' + ....: '\xcc\xdf\x7f\xa8\xf5\x14\x8d\xf4\xb5EY\x9dZ\x80\xb3:' + ....: '\x0f\x15\x88o\xe8K\xa1\xa4\x87Ym\x17)T\xa0\xc1\xf8\x8eH}' + ....: '\x17\xd5S\xd3"\xd2\x84^\xf3\xd8?\xf4N:\x01FW\x95\x10\xd3' + ....: '\x80\x95G#\x04\x9b\x81\x97\xde[F\xd7:I\x8dN\xad\x17\xa6dU' + ....: '\t\r\xbe\xacsF[\xe5\xd6\x9f\x83\x05\x83\x14@X8\xb7\xe0' + ....: '\xa2\xb2\xf4X\x1b\x16\x8c\x85<(`4\xe8=v\x13 \xb8\xb43' + ....: '\xe8\xd8Y\xbf\xd3\xf5\xee\x89E3s)\x9a\xf8\x10\xac\xb8@' + ....: '\xecS\x07\xb2\x8b3\r\x8f2\x1a-\x1bb|\x98\xa3;\x97^\x95' + ....: '\xb4\xfd\xd3\xad\xe8FF;|\xbbKJ\xce\xb1\xd6\xb4\xcbG_":' + ....: '\x96\x0e\x1d\xdd\\e\xb4W\xee\xf2\xfdS4\xe8\xe1#\xc6\x00' + ....: '\\4)+\xda\x8fW\xb7\xf8\xce\xe5To\xb7\x19\xddi\xe9\xeed2' + ....: '\xf1\x19\x1d\x1c\xfd\xa0{\xe5\xe0\xff\x93ft\xbf\x1cm\x88' + ....: '\x0e\xbcK\x8bu\x7f\x01&h\xb01\x8f\\\xc42\xeb\\\x9d\xfc.~' + ....: '\x8e5z\xc0\x939O\x16-=\\6+z\x94\xd1\xe3\xb6\xa1\'c>\xdc' + ....: '\xfc\x04zZ\xee\xf1A\xcc\xbc\xc09=\xe3\xc9qX\xd1aF\xcf' + ....: '\x1bz\xc1\x0f\xa23S\xeb\xe8F\xa8\x1a\x8a\x02\x15\xc6\xe9' + ....: '\x1c\xbdl\xe8\xd58\xaa\xfe%n\xa6\xe5W\x10\x1b@\xafy\xf2n' + ....: '\x99\xd1\x9b\xe8\xa2\xec\xcfo\x83k\xa7\xe9/\xc1\xe1\t\x17') + 2*B['x'] + 2*B['y'] + """ + self._set_parent(state[0]) + for k, v in iteritems(state[1]): + setattr(self, k, v) + + cpdef dict monomial_coefficients(self, bint copy=True): + """ + Return the internal dictionary which has the combinatorial objects + indexing the basis as keys and their corresponding coefficients as + values. + + INPUT: + + - ``copy`` -- (default: ``True``) if ``self`` is internally + represented by a dictionary ``d``, then make a copy of ``d``; + if ``False``, then this can cause undesired behavior by + mutating ``d`` + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: d = f.monomial_coefficients() + sage: d['a'] + 1 + sage: d['c'] + 3 + + To run through the monomials of an element, it is better to + use the idiom:: + + sage: for (t,c) in f: + ....: print("{} {}".format(t,c)) + a 1 + c 3 + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: a = s([2,1])+2*s([3,2]) + sage: d = a.monomial_coefficients() + sage: type(d) + <... 'dict'> + sage: d[ Partition([2,1]) ] + 1 + sage: d[ Partition([3,2]) ] + 2 + """ + if copy: + return dict(self._monomial_coefficients) + return self._monomial_coefficients + + def _sorted_items_for_printing(self): + """ + Returns the items (i.e terms) of ``self``, sorted for printing + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 2*B['c'] + 3 * B['b'] + sage: f._sorted_items_for_printing() + [('a', 1), ('b', 3), ('c', 2)] + sage: F.print_options(sorting_reverse=True) + sage: f._sorted_items_for_printing() + [('c', 2), ('b', 3), ('a', 1)] + sage: F.print_options(sorting_reverse=False) #reset to original state + + .. SEEALSO:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options` + """ + print_options = self._parent.print_options() + v = self._monomial_coefficients.items() + try: + v.sort(key=lambda monomial_coeff: + print_options['sorting_key'](monomial_coeff[0]), + reverse=print_options['sorting_reverse']) + except Exception: # Sorting the output is a plus, but if we can't, no big deal + pass + return v + + def _repr_(self): + """ + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='F') + sage: e = F.basis() + sage: e['a'] + 2*e['b'] # indirect doctest + F['a'] + 2*F['b'] + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='') + sage: e = F.basis() + sage: e['a'] + 2*e['b'] # indirect doctest + ['a'] + 2*['b'] + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='', scalar_mult=' ', bracket=False) + sage: e = F.basis() + sage: e['a'] + 2*e['b'] # indirect doctest + 'a' + 2 'b' + + Controling the order of terms by providing a comparison + function on elements of the support:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], + ....: sorting_reverse=True) + sage: e = F.basis() + sage: e['a'] + 3*e['b'] + 2*e['c'] + 2*B['c'] + 3*B['b'] + B['a'] + + sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], + ....: sorting_key=lambda x: x[1]) + sage: e = F.basis() + sage: e['ac'] + 3*e['ba'] + 2*e['cb'] + 3*B['ba'] + 2*B['cb'] + B['ac'] + """ + return repr_lincomb(self._sorted_items_for_printing(), + scalar_mult=self._parent._print_options['scalar_mult'], + repr_monomial = self._parent._repr_term, + strip_one = True) + + def _ascii_art_(self): + """ + TESTS:: + + sage: M = QuasiSymmetricFunctions(QQ).M() + sage: ascii_art(M[1,3]**2) # indirect doctest + 4*M + 2*M + 2*M + 2*M + 2*M + M + *** ****** *** *** *** ****** + *** * * **** *** ** + * * *** * ** + * * + sage: ascii_art(M.zero()) + 0 + """ + from sage.misc.misc import coeff_repr + terms = self._sorted_items_for_printing() + scalar_mult = self._parent._print_options['scalar_mult'] + repr_monomial = self._parent._ascii_art_term + strip_one = True + + if repr_monomial is None: + repr_monomial = str + + s = empty_ascii_art # "" + first = True + + if scalar_mult is None: + scalar_mult = "*" + + for (monomial,c) in terms: + b = repr_monomial(monomial) # PCR + if c != 0: + break_points = [] + coeff = coeff_repr(c, False) + if coeff != "0": + if coeff == "1": + coeff = "" + elif coeff == "-1": + coeff = "-" + elif b._l > 0: + if len(coeff) > 0 and monomial == 1 and strip_one: + b = empty_ascii_art # "" + else: + b = AsciiArt([scalar_mult]) + b + if not first: + if len(coeff) > 0 and coeff[0] == "-": + coeff = " - %s"%coeff[1:] + else: + coeff = " + %s"%coeff + break_points = [2] + else: + coeff = "%s"%coeff + s += AsciiArt([coeff], break_points) + b + first = False + if first: + return AsciiArt(["0"]) + elif s == empty_ascii_art: + return AsciiArt(["1"]) + else: + return s + + def _unicode_art_(self): + """ + TESTS:: + + sage: M = QuasiSymmetricFunctions(QQ).M() + sage: unicode_art(M[1,1]**2) # indirect doctest + 6*M + 2*M + 2*M + 2*M + M + ┌┐ ┌┬┐ ┌┐ ┌┐ ┌┬┐ + ├┤ ├┼┘ ┌┼┤ ├┤ ┌┼┼┘ + ├┤ ├┤ ├┼┘ ┌┼┤ └┴┘ + ├┤ └┘ └┘ └┴┘ + └┘ + """ + from sage.misc.misc import coeff_repr + terms = self._sorted_items_for_printing() + scalar_mult = self._parent._print_options['scalar_mult'] + repr_monomial = self._parent._unicode_art_term + strip_one = True + + if repr_monomial is None: + repr_monomial = str + + s = empty_unicode_art # "" + first = True + + if scalar_mult is None: + scalar_mult = "*" + + for (monomial, c) in terms: + b = repr_monomial(monomial) # PCR + if c != 0: + break_points = [] + coeff = coeff_repr(c, False) + if coeff != "0": + if coeff == "1": + coeff = "" + elif coeff == "-1": + coeff = "-" + elif b._l > 0: + if len(coeff) > 0 and monomial == 1 and strip_one: + b = empty_unicode_art # "" + else: + b = UnicodeArt([scalar_mult]) + b + if not first: + if len(coeff) > 0 and coeff[0] == "-": + coeff = " - %s" % coeff[1:] + else: + coeff = " + %s" % coeff + break_points = [2] + else: + coeff = "%s" % coeff + s += UnicodeArt([coeff], break_points) + b + first = False + if first: + return "0" + elif s == empty_unicode_art: + return UnicodeArt(["1"]) + else: + return s + + def _latex_(self): + r""" + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: latex(f) + B_{a} + 3B_{c} + + :: + + sage: QS3 = SymmetricGroupAlgebra(QQ,3) + sage: a = 2 + QS3([2,1,3]) + sage: latex(a) #indirect doctest + 2[1, 2, 3] + [2, 1, 3] + + :: + + sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='beta', latex_prefix='\\beta') + sage: x = F.an_element() + sage: x + 2*beta['a'] + 2*beta['b'] + sage: latex(x) + 2\beta_{a} + 2\beta_{b} + + Controling the order of terms by providing a comparison + function on elements of the support:: + + sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], + ....: sorting_reverse=True) + sage: e = F.basis() + sage: latex(e['a'] + 3*e['b'] + 2*e['c']) + 2B_{c} + 3B_{b} + B_{a} + + sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'], + ....: sorting_key=lambda x: x[1]) + sage: e = F.basis() + sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb']) + 3B_{ba} + 2B_{cb} + B_{ac} + """ + return repr_lincomb(self._sorted_items_for_printing(), + scalar_mult = self._parent._print_options['scalar_mult'], + latex_scalar_mult = self._parent._print_options['latex_scalar_mult'], + repr_monomial = self._parent._latex_term, + is_latex=True, strip_one=True) + + cpdef _richcmp_(self, other, int op): + """ + Rich comparison for equal parents. + + EXAMPLES:: + + sage: F1 = CombinatorialFreeModule(QQ, [1, 2, 3]) + sage: F2 = CombinatorialFreeModule(QQ, [1, 2, 3], prefix = "g") + sage: F1.zero() == F1.zero() + True + sage: F1.zero() == F1.an_element() + False + sage: F1.an_element() == F1.an_element() + True + sage: F1.an_element() is None + False + + :: + + sage: F3 = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: F3.an_element() != F3.an_element() + False + sage: F3.an_element() != F3.zero() + True + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: a = s([2,1]) + sage: b = s([1,1,1]) + sage: cmp(a,b) #indirect doctest + 1 + + .. TODO:: + + Currently, if ``self`` and ``other`` do not have the same parent, + seemingly equal elements do not evaluate equal, since conversions + between different modules have not been established. + + :: + + sage: F1.zero() == 0 + True + sage: F1(0) + 0 + + :: + + sage: F1.zero() == F2.zero() + False + sage: F1(F2.zero()) + Traceback (most recent call last): + ... + TypeError: do not know how to make x (= 0) an element of self (=Free module generated by {1, 2, 3} over Rational Field) + sage: F = AlgebrasWithBasis(QQ).example() + sage: F.one() == 1 + True + sage: 1 == F.one() + True + sage: 2 * F.one() == int(2) + True + sage: int(2) == 2 * F.one() + True + + sage: S = SymmetricFunctions(QQ); s = S.s(); p = S.p() + sage: p[2] == s[2] - s[1, 1] + True + sage: p[2] == s[2] + False + + This feature is disputable, in particular since it can make + equality testing costly. It may be removed at some point. + + Equality testing can be a bit tricky when the order of terms + can vary because their indices are incomparable with + ``cmp``. The following test did fail before :trac:`12489` :: + + sage: F = CombinatorialFreeModule(QQ, Subsets([1,2,3])) + sage: x = F.an_element() + sage: (x+F.zero()).terms() # random + [2*B[{1}], 3*B[{2}], B[{}]] + sage: x.terms() # random + [2*B[{1}], B[{}], 3*B[{2}]] + sage: x+F.zero() == x + True + + TESTS:: + + sage: TestSuite(F1).run() + sage: TestSuite(F).run() + """ + cdef IndexedFreeModuleElement elt = other + if op in [Py_EQ, Py_NE]: + return richcmp(self._monomial_coefficients, elt._monomial_coefficients, op) + + if self._monomial_coefficients == elt._monomial_coefficients: + return rich_to_bool(op, 0) + + v = sorted(self._monomial_coefficients.items()) + w = sorted(elt._monomial_coefficients.items()) + return richcmp_not_equal(v, w, op) + + cdef _add_(self, other): + """ + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: B['a'] + 3*B['c'] + B['a'] + 3*B['c'] + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: s([2,1]) + s([5,4]) # indirect doctest + s[2, 1] + s[5, 4] + sage: a = s([2,1]) + 0 + sage: len(a.monomial_coefficients()) + 1 + """ + return type(self)(self._parent, + add(self._monomial_coefficients, + (other)._monomial_coefficients)) + + cdef _neg_(self): + """ + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] + 3*B['c'] + sage: -f + -B['a'] - 3*B['c'] + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: -s([2,1]) # indirect doctest + -s[2, 1] + """ + return type(self)(self._parent, negate(self._monomial_coefficients)) + + cdef _sub_(self, other): + """ + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: B['a'] - 3*B['c'] + B['a'] - 3*B['c'] + + :: + + sage: s = SymmetricFunctions(QQ).schur() + sage: s([2,1]) - s([5,4]) # indirect doctest + s[2, 1] - s[5, 4] + """ + return type(self)(self._parent, + axpy(-1, + (other)._monomial_coefficients, + self._monomial_coefficients)) + + cpdef _coefficient_fast(self, m): + """ + Return the coefficient of ``m`` in ``self``, where ``m`` is key in + ``self._monomial_coefficients``. + + EXAMPLES:: + + sage: p = Partition([2,1]) + sage: q = Partition([1,1,1]) + sage: s = SymmetricFunctions(QQ).schur() + sage: a = s(p) + sage: a._coefficient_fast([2,1]) + Traceback (most recent call last): + ... + TypeError: unhashable type: 'list' + + :: + + sage: a._coefficient_fast(p) + 1 + sage: a._coefficient_fast(q) + 0 + """ + return self._monomial_coefficients.get(m, self.base_ring().zero()) + + def __getitem__(self, m): + """ + EXAMPLES:: + + sage: s = SymmetricFunctions(QQ).schur() + sage: p = Partition([2,1]) + sage: q = Partition([1,1,1]) + sage: a = s(p) + sage: a[p] + 1 + sage: a[q] + 0 + """ + return self._coefficient_fast(m) + + def _vector_(self, new_base_ring=None): + """ + Returns ``self`` as a dense vector + + INPUT: + + - ``new_base_ring`` -- a ring (default: ``None``) + + OUTPUT: a dense :func:`FreeModule` vector + + .. WARNING:: This will crash/run forever if ``self`` is infinite dimensional! + + .. SEEALSO:: + + - :func:`vector` + - :meth:`CombinatorialFreeModule.get_order` + - :meth:`CombinatorialFreeModule.from_vector` + - :meth:`CombinatorialFreeModule._dense_free_module` + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: f = B['a'] - 3*B['c'] + sage: f._vector_() + (1, 0, -3) + + One can use equivalently:: + + sage: f.to_vector() + (1, 0, -3) + sage: vector(f) + (1, 0, -3) + + More examples:: + + sage: QS3 = SymmetricGroupAlgebra(QQ, 3) + sage: a = 2*QS3([1,2,3]) + 4*QS3([3,2,1]) + sage: a._vector_() + (2, 0, 0, 0, 0, 4) + sage: a.to_vector() + (2, 0, 0, 0, 0, 4) + sage: vector(a) + (2, 0, 0, 0, 0, 4) + sage: a == QS3.from_vector(a.to_vector()) + True + + If ``new_base_ring`` is specified, then a vector over + ``new_base_ring`` is returned:: + + sage: a._vector_(RDF) + (2.0, 0.0, 0.0, 0.0, 0.0, 4.0) + + .. NOTE:: + + :trac:`13406`: the current implementation has been optimized, at + the price of breaking the encapsulation for FreeModule + elements creation, with the following use case as metric, + on a 2008' Macbook Pro:: + + sage: F = CombinatorialFreeModule(QQ, range(10)) + sage: f = F.an_element() + sage: %timeit f._vector_() # not tested + 625 loops, best of 3: 17.5 micros per loop + + Other use cases may call for different or further + optimizations. + """ + dense_free_module = self._parent._dense_free_module(new_base_ring) + d = self._monomial_coefficients + zero = dense_free_module.base_ring().zero() + return dense_free_module.element_class(dense_free_module, + [d.get(m, zero) for m in self._parent.get_order()], + coerce=True, copy=False) + + to_vector = _vector_ + + cpdef _acted_upon_(self, scalar, bint self_on_left): + """ + Return the action of ``scalar`` (an element of the base ring) on + ``self``. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, ['a','b','c']) + sage: B = F.basis() + sage: B['a']*(1/2) # indirect doctest + 1/2*B['a'] + sage: B['a']/2 + 1/2*B['a'] + sage: B['a']*2 # indirect doctest + 2*B['a'] + sage: B['a']*int(2) # indirect doctest + 2*B['a'] + + sage: 1/2*B['a'] + 1/2*B['a'] + sage: 2*B['a'] # indirect doctest + 2*B['a'] + sage: int(2)*B['a'] # indirect doctest + 2*B['a'] + + TESTS:: + + sage: F.get_action(QQ, operator.mul, True) + Right action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field + sage: F.get_action(QQ, operator.mul, False) + Left action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field + sage: F.get_action(ZZ, operator.mul, True) + Right action by Integer Ring on Free module generated by {'a', 'b', 'c'} over Rational Field + sage: F.get_action(F, operator.mul, True) + sage: F.get_action(F, operator.mul, False) + + This also works when a coercion of the coefficient is needed, for + example with polynomials or fraction fields (:trac:`8832`):: + + sage: P. = QQ['q'] + sage: V = CombinatorialFreeModule(P, Permutations()) + sage: el = V(Permutation([3,1,2])) + sage: (3/2)*el + 3/2*B[[3, 1, 2]] + + sage: P. = QQ['q'] + sage: F = FractionField(P) + sage: V = CombinatorialFreeModule(F, Words()) + sage: w = Words()('abc') + sage: (1+q)*V(w) + (q+1)*B[word: abc] + sage: ((1+q)/q)*V(w) + ((q+1)/q)*B[word: abc] + + .. TODO:: + + Add non commutative tests. + """ + # With the current design, the coercion model does not have + # enough information to detect a priori that this method only + # accepts scalars; so it tries on some elements(), and we need + # to make sure to report an error. + if isinstance(scalar, Element) and parent(scalar) is not self.base_ring(): + # Temporary needed by coercion (see Polynomial/FractionField tests). + if self.base_ring().has_coerce_map_from(parent(scalar)): + scalar = self.base_ring()( scalar ) + else: + return None + + return type(self)(self._parent, + scal(scalar, self._monomial_coefficients, + factor_on_left=not self_on_left)) + + cpdef _lmul_(self, RingElement right): + """ + For backward compatibility. + + EXAMPLES:: + + sage: C = CombinatorialFreeModule(QQ, [1,2,3]) + sage: C.an_element()._lmul_(2) + 4*B[1] + 4*B[2] + 6*B[3] + """ + return self._acted_upon_(right, True) + + cpdef _rmul_(self, RingElement left): + """ + For backward compatibility. + + EXAMPLES:: + + sage: C = CombinatorialFreeModule(QQ, [1,2,3]) + sage: C.an_element()._rmul_(2) + 4*B[1] + 4*B[2] + 6*B[3] + """ + return self._acted_upon_(left, False) + + def __truediv__(self, x): + """ + Division by coefficients. + + EXAMPLES:: + + sage: F = CombinatorialFreeModule(QQ, [1,2,3]) + sage: x = F._from_dict({1:2, 2:3}) + sage: x/2 + B[1] + 3/2*B[2] + + :: + + sage: F = CombinatorialFreeModule(QQ, [1,2,3]) + sage: B = F.basis() + sage: f = 2*B[2] + 4*B[3] + sage: f/2 + B[2] + 2*B[3] + """ + F = parent(self) + if not self.base_ring().is_field(): + return type(self)(F, {k: c._divide_if_possible(x) + for k,c in self._monomial_coefficients.iteritems()}) + + x = self.base_ring()( x ) + x_inv = x ** -1 + D = self._monomial_coefficients + return type(self)(F, scal(x_inv, D)) + + __div__ = __truediv__ + +def _unpickle_element(C, d): + """ + Unpickle an element in ``C`` given by ``d``. + + EXAMPLES:: + + sage: from sage.modules.with_basis.indexed_element import _unpickle_element + sage: C = CombinatorialFreeModule(QQ, [1,2,3]) + sage: _unpickle_element(C, {1: -2, 3: -12}) + -2*B[1] - 12*B[3] + """ + return C._from_dict(d, coerce=False, remove_zeros=False) + +# Handle old CombinatorialFreeModuleElement pickles, see trac #22632 +from sage.structure.sage_object import register_unpickle_override +register_unpickle_override("sage.combinat.free_module", + "CombinatorialFreeModuleElement", + IndexedFreeModuleElement) diff --git a/src/sage/monoids/automatic_semigroup.py b/src/sage/monoids/automatic_semigroup.py index 4c5656c6ac4..2de54a47b73 100644 --- a/src/sage/monoids/automatic_semigroup.py +++ b/src/sage/monoids/automatic_semigroup.py @@ -146,7 +146,7 @@ class AutomaticSemigroup(UniqueRepresentation, Parent): [[1, 0], [3, 1], [2, 0], [2, 1]] We can also use it to get submonoids from groups. We check that in the - symmetric group, a transposition and a cyle generate the whole group:: + symmetric group, a transposition and a long cycle generate the whole group:: sage: G5 = SymmetricGroup(5) sage: N = AutomaticSemigroup(Family({1: G5([2,1,3,4,5]), 2: G5([2,3,4,5,1])}), one=G5.one()) diff --git a/src/sage/monoids/free_monoid.py b/src/sage/monoids/free_monoid.py index f4342adc54a..1dfcfd74b93 100644 --- a/src/sage/monoids/free_monoid.py +++ b/src/sage/monoids/free_monoid.py @@ -201,14 +201,28 @@ def __init__(self, n, names=None): #self._assign_names(names) Monoid_class.__init__(self,names) - def __cmp__(self, other): + def __eq__(self, other): + """ + Test for equality. + """ + if self is other: + return True if not isinstance(other, FreeMonoid_class): - return -1 - c = cmp(self.__ngens, other.__ngens) - if c: return c - if self.variable_names() == other.variable_names(): - return 0 - return 1 + return False + if self.__ngens != other.__ngens: + return False + try: + if self.variable_names() != other.variable_names(): + return False + except ValueError: + pass + return True + + def __ne__(self, other): + """ + Test for unequality. + """ + return not (self == other) def _repr_(self): return "Free monoid on %s generators %s"%(self.__ngens,self.gens()) diff --git a/src/sage/monoids/string_monoid_element.py b/src/sage/monoids/string_monoid_element.py index 1173827469d..a4d46bb38e9 100644 --- a/src/sage/monoids/string_monoid_element.py +++ b/src/sage/monoids/string_monoid_element.py @@ -26,6 +26,8 @@ from sage.rings.integer import Integer from sage.rings.all import RealField from .free_monoid_element import FreeMonoidElement +from sage.structure.sage_object import richcmp + def is_StringMonoidElement(x): r""" @@ -100,12 +102,12 @@ def __init__(self, S, x, check=True): else: raise TypeError("Argument x (= %s) is of the wrong type." % x) - def __cmp__(left, right): + def _richcmp_(left, right, op): """ Compare two free monoid elements with the same parents. The ordering is the one on the underlying sorted list of - (monomial,coefficients) pairs. + (monomial, coefficients) pairs. EXAMPLES:: @@ -116,7 +118,7 @@ def __cmp__(left, right): sage: S("01") < S("10") True """ - return cmp(left._element_list, right._element_list) + return richcmp(left._element_list, right._element_list, op) def _repr_(self): """ diff --git a/src/sage/numerical/all.py b/src/sage/numerical/all.py index f13906e1214..06ca1dd1a60 100644 --- a/src/sage/numerical/all.py +++ b/src/sage/numerical/all.py @@ -9,21 +9,3 @@ lazy_import("sage.numerical.interactive_simplex_method", ["InteractiveLPProblem", "InteractiveLPProblemStandardForm"]) - -from sage.misc.superseded import deprecated_callable_import - -# Deprecation (#17867) -- two lines at the end of -# sage.numerical.interactive_simplex_method should be removed too. -deprecated_callable_import(17867, - "sage.numerical.interactive_simplex_method", - globals(), - locals(), - ["LPProblem"], - ("This class meant for **educational purposes only** has been renamed to InteractiveLPProblem")) - -deprecated_callable_import(17867, - "sage.numerical.interactive_simplex_method", - globals(), - locals(), - ["LPProblemStandardForm"], - ("This class meant for **educational purposes only** has been renamed to InteractiveLPProblemStandardForm")) diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index 7944e8a6154..a4421c30341 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -570,7 +570,7 @@ cdef class GenericBackend: sage: p.nrows() # optional - Nonexistent_LP_solver 0 sage: p.add_linear_constraints(5, 0, None) # optional - Nonexistent_LP_solver - sage: p.add_col(range(5), range(5)) # optional - Nonexistent_LP_solver + sage: p.add_col(list(range(5)), list(range(5))) # optional - Nonexistent_LP_solver sage: p.nrows() # optional - Nonexistent_LP_solver 5 """ @@ -679,7 +679,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(5, 0, None) # optional - Nonexistent_LP_solver - sage: p.add_col(range(5), range(5)) # optional - Nonexistent_LP_solver + sage: p.add_col(list(range(5)), list(range(5))) # optional - Nonexistent_LP_solver sage: p.solve() # optional - Nonexistent_LP_solver 0 sage: p.objective_coefficient(0,1) # optional - Nonexistent_LP_solver @@ -711,7 +711,7 @@ cdef class GenericBackend: tester = p._tester(**options) # From doctest of GenericBackend.solve: tester.assertIsNone(p.add_linear_constraints(5, 0, None)) - tester.assertIsNone(p.add_col(range(5), range(5))) + tester.assertIsNone(p.add_col(list(xrange(5)), list(xrange(5)))) tester.assertEqual(p.solve(), 0) tester.assertIsNone(p.objective_coefficient(0,1)) from sage.numerical.mip import MIPSolverException @@ -1063,7 +1063,7 @@ cdef class GenericBackend: 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(range(5), range(5), 2, 2) # optional - Nonexistent_LP_solver + sage: p.add_linear_constraint(list(range(5)), list(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]) ## FIXME: Why backwards? sage: p.row_bounds(0) # optional - Nonexistent_LP_solver @@ -1275,7 +1275,7 @@ cdef class GenericBackend: p.add_linear_constraints(5, 0, None) try: # 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)) + p.add_col(list(xrange(5)), list(xrange(1, 6))) except NotImplementedError: # Gurobi does not implement add_col pass diff --git a/src/sage/numerical/backends/gurobi_backend.pyx b/src/sage/numerical/backends/gurobi_backend.pyx index a0c92bbbd76..b4e11c56a0e 100644 --- a/src/sage/numerical/backends/gurobi_backend.pyx +++ b/src/sage/numerical/backends/gurobi_backend.pyx @@ -221,7 +221,7 @@ cdef class GurobiBackend(GenericBackend): sage: p.nrows() 0 sage: p.add_linear_constraints(5, 0, None) - sage: p.add_col(range(5), range(5)) + sage: p.add_col(list(range(5)), list(range(5))) sage: p.nrows() 5 """ diff --git a/src/sage/numerical/interactive_simplex_method.py b/src/sage/numerical/interactive_simplex_method.py index 2d65b2aedee..0e15aea357a 100644 --- a/src/sage/numerical/interactive_simplex_method.py +++ b/src/sage/numerical/interactive_simplex_method.py @@ -5449,7 +5449,3 @@ def y(self): # Aliases for the standard notation x_B = basic_variables x_N = nonbasic_variables - -# DEPRECATION (those two lines should be removed when cleaning #17867) -LPProblem = InteractiveLPProblem -LPProblemStandardForm = InteractiveLPProblemStandardForm diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index 5976ceae2e8..fb1960a4b57 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -49,15 +49,12 @@ A mixed integer linear program can give you an answer: #. You have to create an instance of :class:`MixedIntegerLinearProgram` and -- in our case -- specify that it is a minimization. - #. Create a dictionary ``w`` of integer variables ``w`` via ``w = - p.new_variable(integer=True)`` (note that **by default all variables are - non-negative**, cf :meth:`~MixedIntegerLinearProgram.new_variable`). + #. Create a dictionary ``w`` of non-negative integer variables ``w`` via ``w = + p.new_variable(integer=True, nonnegative=True)``. #. Add those three equations as equality constraints via :meth:`add_constraint `. #. Also add the inequality constraint. #. Add an inequality constraint `w_3 \geq 1` to exclude the trivial solution. - #. By default, all variables are non-negative. We remove that constraint - via ``p.set_min(variable, None)``, see :meth:`set_min `. #. Specify the objective function via :meth:`set_objective `. In our case that is just `w_3`. If it is a pure constraint satisfaction problem, specify it as ``None``. @@ -74,7 +71,6 @@ The following example shows all these steps:: sage: p.add_constraint(2*w[2] - 3*w[3] == 0) sage: p.add_constraint(w[0] - w[1] - w[2] >= 0) sage: p.add_constraint(w[3] >= 1) - sage: _ = [ p.set_min(w[i], None) for i in range(1,4) ] sage: p.set_objective(w[3]) sage: p.show() Minimization: @@ -87,9 +83,9 @@ The following example shows all these steps:: - x_3 <= -1.0 Variables: x_0 is an integer variable (min=0.0, max=+oo) - x_1 is an integer variable (min=-oo, max=+oo) - x_2 is an integer variable (min=-oo, max=+oo) - x_3 is an integer variable (min=-oo, max=+oo) + x_1 is an integer variable (min=0.0, max=+oo) + x_2 is an integer variable (min=0.0, max=+oo) + x_3 is an integer variable (min=0.0, max=+oo) sage: print('Objective Value: {}'.format(p.solve())) Objective Value: 2.0 sage: for i, v in p.get_values(w).iteritems(): @@ -157,6 +153,12 @@ also allowed:: a[(4, 'string', Rational Field)] = x_2 is a continuous variable (min=-oo, max=+oo) b[2] = x_3 is a continuous variable (min=-oo, max=+oo) +Upper/lower bounds on a variable can be specified either as separate constraints +(see :meth:`add_constraint `) or +using the methods :meth:`set_max ` +and :meth:`set_min ` +respectively. + The default MIP variable ------------------------ @@ -298,11 +300,6 @@ cdef class MixedIntegerLinearProgram(SageObject): - Defaults to ``False``. - .. WARNING:: - - All LP variables are non-negative by default (see :meth:`new_variable` - and :meth:`set_min`). - .. SEEALSO:: - :func:`default_mip_solver` -- Returns/Sets the default MIP solver. @@ -710,7 +707,7 @@ cdef class MixedIntegerLinearProgram(SageObject): .. SEEALSO:: - :meth:`set_min`, :meth:`get_min` -- set/get the lower bound of a - variable. Note that by default, all variables are non-negative. + variable. - :meth:`set_max`, :meth:`get_max` -- set/get the upper bound of a variable. @@ -2185,8 +2182,8 @@ cdef class MixedIntegerLinearProgram(SageObject): .. WARNING:: - By default, all variables of a LP are assumed to be - non-negative. See :meth:`set_min` to change it. + By default, no additional assumption is made on the domain of an LP + variable. See :meth:`set_min` and :meth:`set_max` to change it. EXAMPLES: diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index a1df1e411c9..1b1d2d95503 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -452,7 +452,7 @@ 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 +a message raise the corresponding exception, stopping 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 @@ -1574,11 +1574,11 @@ def _thief(self): def steal(self): r""" - Steal some node from another worker + Steal some node from another worker. OUTPUT: - a node stolen from another worker choosed at random + a node stolen from another worker chosen at random EXAMPLES:: diff --git a/src/sage/parallel/multiprocessing_sage.py b/src/sage/parallel/multiprocessing_sage.py index cd271fd5499..10d7ac0c641 100644 --- a/src/sage/parallel/multiprocessing_sage.py +++ b/src/sage/parallel/multiprocessing_sage.py @@ -37,10 +37,11 @@ def pyprocessing(processes=0): sage: p_iter = pyprocessing(4) sage: P = parallel(p_iter=p_iter) sage: def f(x): return x+x - sage: v = list(P(f)(range(10))); v.sort(); v + sage: v = list(P(f)(list(range(10)))); v.sort(); v [(((0,), {}), 0), (((1,), {}), 2), (((2,), {}), 4), (((3,), {}), 6), (((4,), {}), 8), (((5,), {}), 10), (((6,), {}), 12), (((7,), {}), 14), (((8,), {}), 16), (((9,), {}), 18)] """ - if processes == 0: processes = ncpus.ncpus() + if processes == 0: + processes = ncpus.ncpus() return partial(parallel_iter, processes) def parallel_iter(processes, f, inputs): diff --git a/src/sage/plot/plot3d/platonic.py b/src/sage/plot/plot3d/platonic.py index e4da07ffd5c..4744ee3abe2 100644 --- a/src/sage/plot/plot3d/platonic.py +++ b/src/sage/plot/plot3d/platonic.py @@ -435,6 +435,7 @@ def octahedron(center=(0, 0, 0), size=1, **kwds): sphinx_plot(G) """ + kwds['enclosed'] = True if 'aspect_ratio' not in kwds: kwds['aspect_ratio'] = [1, 1, 1] return prep(Box(1,1,1).dual(**kwds), center, size, kwds) @@ -592,6 +593,7 @@ def icosahedron(center=(0, 0, 0), size=1, **kwds): sphinx_plot(p) """ + kwds['enclosed'] = True if 'aspect_ratio' not in kwds: kwds['aspect_ratio'] = [1, 1, 1] return prep(dodecahedron().dual(**kwds), center, size, kwds) diff --git a/src/sage/plot/plot3d/plot3d.py b/src/sage/plot/plot3d/plot3d.py index 3b1e7aaa541..097551ea9c7 100644 --- a/src/sage/plot/plot3d/plot3d.py +++ b/src/sage/plot/plot3d/plot3d.py @@ -756,7 +756,7 @@ def plot3d(f, urange, vrange, adaptive=False, transformation=None, **kwds): - ``adaptive`` - (default: False) whether to use adaptive refinement to draw the plot (slower, but may look better). - This option does NOT work in conjuction with a transformation + This option does NOT work in conjunction with a transformation (see below). - ``mesh`` - bool (default: False) whether to display diff --git a/src/sage/quadratic_forms/quadratic_form.py b/src/sage/quadratic_forms/quadratic_form.py index 2477451c30b..9fd853df2f5 100644 --- a/src/sage/quadratic_forms/quadratic_form.py +++ b/src/sage/quadratic_forms/quadratic_form.py @@ -1158,11 +1158,11 @@ def gcd(self): def polynomial(self,names='x'): r""" - Returns the polynomial in 'n' variables of the quadratic form in the ring 'R[names].' + Return the polynomial in 'n' variables of the quadratic form in the ring 'R[names].' INPUT: - -'self' - a quadratic form over a commatitive ring. + -'self' - a quadratic form over a commutative ring. -'names' - the name of the variables. Digits will be appended to the name for each different canonical variable e.g x1, x2, x3 etc. diff --git a/src/sage/quivers/algebra.py b/src/sage/quivers/algebra.py index 5d9b9e07b72..4d6345cebfd 100644 --- a/src/sage/quivers/algebra.py +++ b/src/sage/quivers/algebra.py @@ -22,7 +22,7 @@ import six from sage.misc.cachefunc import cached_method -from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement +from sage.combinat.free_module import CombinatorialFreeModule from .algebra_elements import PathAlgebraElement class PathAlgebra(CombinatorialFreeModule): diff --git a/src/sage/repl/rich_output/preferences.py b/src/sage/repl/rich_output/preferences.py index 5af0ff6d89f..f7d9a8fe79d 100644 --- a/src/sage/repl/rich_output/preferences.py +++ b/src/sage/repl/rich_output/preferences.py @@ -25,7 +25,7 @@ * supplemental_plot is not specified * text = ascii_art -Properties can be unset by deleting them or by assinging ``None``:: +Properties can be unset by deleting them or by assigning ``None``:: sage: prefs.text = 'ascii_art' sage: del prefs.text diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index ce474cf83da..0b87be89c8b 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -1,23 +1,6 @@ r""" Common Asymptotic Expansions -.. WARNING:: - - As this code is experimental, a warning is thrown when an - asymptotic ring (or an associated structure) is created for the - first time in a session (see - :class:`sage.misc.superseded.experimental`). - - TESTS:: - - sage: AsymptoticRing(growth_group='z^ZZ * log(z)^QQ', coefficient_ring=ZZ) - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/17601 for details. - Asymptotic Ring over Integer Ring - - Asymptotic expansions in SageMath can be built through the ``asymptotic_expansions`` object. It contains generators for common asymptotic expressions. For example, diff --git a/src/sage/rings/asymptotic/asymptotic_ring.py b/src/sage/rings/asymptotic/asymptotic_ring.py index c630c5570ca..8ac348e5438 100644 --- a/src/sage/rings/asymptotic/asymptotic_ring.py +++ b/src/sage/rings/asymptotic/asymptotic_ring.py @@ -89,23 +89,6 @@ see the top of the module :doc:`growth group `. -.. WARNING:: - - As this code is experimental, a warning is thrown when an - asymptotic ring (or an associated structure) is created for the - first time in a session (see - :class:`sage.misc.superseded.experimental`). - - TESTS:: - - sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: G = GrowthGroup('x^ZZ') - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/17601 for details. - - .. _asymptotic_ring_intro: Introductory Examples @@ -469,7 +452,6 @@ from sage.structure.element import CommutativeAlgebraElement from sage.structure.unique_representation import UniqueRepresentation from sage.misc.defaults import series_precision -from sage.misc.superseded import experimental from sage.rings.all import RIF @@ -3494,8 +3476,6 @@ def format_names(N): category=category, default_prec=default_prec) - - @experimental(trac_number=17601) def __init__(self, growth_group, coefficient_ring, category, default_prec): r""" See :class:`AsymptoticRing` for more information. diff --git a/src/sage/rings/asymptotic/growth_group.py b/src/sage/rings/asymptotic/growth_group.py index 4e23c3462e8..d2aaa6e78b7 100644 --- a/src/sage/rings/asymptotic/growth_group.py +++ b/src/sage/rings/asymptotic/growth_group.py @@ -13,23 +13,6 @@ examples of growth groups and elements are given as well. -.. WARNING:: - - As this code is experimental, warnings are thrown when a growth - group is created for the first time in a session (see - :class:`sage.misc.superseded.experimental`). - - TESTS:: - - sage: from sage.rings.asymptotic.growth_group import \ - ....: GenericGrowthGroup, GrowthGroup - sage: GenericGrowthGroup(ZZ) - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/17601 for details. - Growth Group Generic(ZZ) - .. _growth_group_description: Description of Growth Groups @@ -250,7 +233,6 @@ lazy_import('sage.rings.asymptotic.growth_group_cartesian', 'CartesianProductGrowthGroups') from sage.categories.pushout import ConstructionFunctor -from sage.misc.superseded import experimental from sage.structure.element import MultiplicativeGroupElement from sage.structure.factory import UniqueFactory from sage.structure.parent import Parent @@ -1568,7 +1550,6 @@ def __classcall__(cls, base, var=None, category=None, ignore_variables=None): return super(GenericGrowthGroup, cls).__classcall__( cls, base, var, category) - @experimental(trac_number=17601) def __init__(self, base, var, category): r""" See :class:`GenericGrowthElement` for more information. diff --git a/src/sage/rings/asymptotic/growth_group_cartesian.py b/src/sage/rings/asymptotic/growth_group_cartesian.py index 39aa8e0e97e..6e725460c89 100644 --- a/src/sage/rings/asymptotic/growth_group_cartesian.py +++ b/src/sage/rings/asymptotic/growth_group_cartesian.py @@ -16,21 +16,6 @@ - Benjamin Hackl is supported by the Google Summer of Code 2015. -.. WARNING:: - - As this code is experimental, warnings are thrown when a growth - group is created for the first time in a session (see - :class:`sage.misc.superseded.experimental`). - - TESTS:: - - sage: from sage.rings.asymptotic.growth_group import GenericGrowthGroup, GrowthGroup - sage: GenericGrowthGroup(ZZ) - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/17601 for details. - Growth Group Generic(ZZ) TESTS:: diff --git a/src/sage/rings/asymptotic/misc.py b/src/sage/rings/asymptotic/misc.py index a9afad2afb5..6c0eba0b9cb 100644 --- a/src/sage/rings/asymptotic/misc.py +++ b/src/sage/rings/asymptotic/misc.py @@ -609,7 +609,6 @@ def __init__(self, data=None, var=None): TESTS:: sage: A = AsymptoticRing('n^ZZ', ZZ) - doctest:...: FutureWarning: ... sage: from sage.rings.asymptotic.misc import NotImplementedOZero sage: raise NotImplementedOZero(A) Traceback (most recent call last): diff --git a/src/sage/rings/asymptotic/term_monoid.py b/src/sage/rings/asymptotic/term_monoid.py index 5d5ac36c3d5..6bf174083ed 100644 --- a/src/sage/rings/asymptotic/term_monoid.py +++ b/src/sage/rings/asymptotic/term_monoid.py @@ -31,21 +31,6 @@ `x^5`). Essentially, absorption can be interpreted as the addition of "compatible" terms (partial addition). -.. WARNING:: - - As this code is experimental, a warning is thrown when a term - monoid is created for the first time in a session (see - :class:`sage.misc.superseded.experimental`). - - TESTS:: - - sage: from sage.rings.asymptotic.growth_group import GrowthGroup - sage: from sage.rings.asymptotic.term_monoid import GenericTermMonoid - sage: G = GrowthGroup('x^ZZ * log(x)^ZZ') - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/17601 for details. .. _term_absorption: @@ -213,7 +198,6 @@ # ***************************************************************************** from __future__ import absolute_import -from sage.misc.superseded import experimental from sage.rings.big_oh import O from sage.structure.element import MultiplicativeGroupElement from sage.structure.factory import UniqueFactory @@ -1370,7 +1354,6 @@ def __classcall__(cls, growth_group, coefficient_ring, category=None): return super(GenericTermMonoid, cls).__classcall__( cls, growth_group, coefficient_ring, category) - @experimental(trac_number=17601) def __init__(self, growth_group, coefficient_ring, category): r""" See :class:`GenericTermMonoid` for more information. diff --git a/src/sage/rings/big_oh.py b/src/sage/rings/big_oh.py index 9801d7aff2c..c96afa6b285 100644 --- a/src/sage/rings/big_oh.py +++ b/src/sage/rings/big_oh.py @@ -83,8 +83,6 @@ def O(*x, **kwds): We can also work with `asymptotic expansions`_:: sage: A. = AsymptoticRing(growth_group='QQ^n * n^QQ * log(n)^QQ', coefficient_ring=QQ); A - doctest:...: FutureWarning: - This class/method/function is marked as experimental. ... Asymptotic Ring over Rational Field sage: O(n) O(n) diff --git a/src/sage/rings/complex_arb.pyx b/src/sage/rings/complex_arb.pyx index ae1597a6eaa..873ec883724 100644 --- a/src/sage/rings/complex_arb.pyx +++ b/src/sage/rings/complex_arb.pyx @@ -148,7 +148,7 @@ from sage.libs.arb.arb cimport * from sage.libs.arb.acb cimport * 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.arf cimport arf_init, arf_get_mpfr, arf_set_mpfr, arf_clear, arf_set_mag, arf_set, arf_is_nan 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, fmpz_abs from sage.libs.flint.fmpq cimport fmpq_t, fmpq_init, fmpq_set_mpq, fmpq_clear @@ -1357,6 +1357,25 @@ cdef class ComplexBall(RingElement): # Comparisons and predicates + def is_NaN(self): + """ + Return ``True`` iff either the real or the imaginary part + is not-a-number. + + EXAMPLES:: + + sage: CBF(NaN).is_NaN() + True + sage: CBF(-5).gamma().is_NaN() + True + sage: CBF(oo).is_NaN() + False + sage: CBF(42+I).is_NaN() + False + """ + return (arf_is_nan(arb_midref(acb_realref(self.value))) + or arf_is_nan(arb_midref(acb_imagref(self.value)))) + def is_zero(self): """ Return ``True`` iff the midpoint and radius of this ball are both zero. diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 51a0b20c295..2595e1515bb 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -1572,6 +1572,21 @@ cdef class ComplexDoubleElement(FieldElement): """ return self.real().is_infinity() or self.imag().is_infinity() + def is_NaN(self): + r""" + Check if ``self`` is not-a-number. + + EXAMPLES:: + + sage: CDF(1, 2).is_NaN() + False + sage: CDF(NaN).is_NaN() + True + sage: (1/CDF(0, 0)).is_NaN() + True + """ + return self.real().is_NaN() or self.imag().is_NaN() + def _pow_(self, ComplexDoubleElement a): """ The function returns the complex number `z` raised to the diff --git a/src/sage/rings/complex_interval.pyx b/src/sage/rings/complex_interval.pyx index ad1d47bf848..c0b40e11ff7 100644 --- a/src/sage/rings/complex_interval.pyx +++ b/src/sage/rings/complex_interval.pyx @@ -1839,6 +1839,21 @@ cdef class ComplexIntervalFieldElement(sage.structure.element.FieldElement): """ return True + def is_NaN(self): + r""" + Return ``True`` if this is not-a-number. + + EXAMPLES:: + + sage: CIF(2, 1).is_NaN() + False + sage: CIF(NaN).is_NaN() + True + sage: (1 / CIF(0, 0)).is_NaN() + True + """ + return mpfi_nan_p(self.__re) or mpfi_nan_p(self.__im) + def cos(self): r""" Compute the cosine of this complex interval. diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index 9b3270ffcbe..b4d34e4301c 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -1101,7 +1101,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: float(abs(ComplexNumber(1,1))) 1.4142135623730951 """ - if mpfr_zero_p(self.__im): + if mpfr_zero_p(self.__im) or mpfr_nan_p(self.__re): return mpfr_get_d(self.__re, rnd) else: raise TypeError("unable to convert {!r} to float; use abs() or real_part() as desired".format(self)) @@ -2341,6 +2341,21 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): """ return self.real().is_infinity() or self.imag().is_infinity() + def is_NaN(self): + r""" + Check if ``self`` is not-a-number. + + EXAMPLES:: + + sage: CC(1, 2).is_NaN() + False + sage: CC(NaN).is_NaN() + True + sage: CC(NaN,2).log().is_NaN() + True + """ + return mpfr_nan_p(self.__re) or mpfr_nan_p(self.__im) + def zeta(self): """ Return the Riemann zeta function evaluated at this complex number. diff --git a/src/sage/rings/fast_arith.pxd b/src/sage/rings/fast_arith.pxd index c84cf6d7d44..7a99e658b91 100644 --- a/src/sage/rings/fast_arith.pxd +++ b/src/sage/rings/fast_arith.pxd @@ -12,9 +12,9 @@ cdef class arith_llong: cdef long long abs_longlong(self, long long x) except -1 cdef long long sign_longlong(self, long long n) except -2 cdef long long c_gcd_longlong(self, long long a, long long b) except -1 - cdef public long long c_xgcd_longlong(self, long long a, long long b, - long long *ss, - long long *tt) except -1 + cdef long long c_xgcd_longlong(self, long long a, long long b, + long long *ss, + long long *tt) except -1 cdef long long c_inverse_mod_longlong(self, long long a, long long m) except -1 cdef long long c_rational_recon_longlong(self, long long a, long long m, long long *n, long long *d) except -1 diff --git a/src/sage/rings/fast_arith.pyx b/src/sage/rings/fast_arith.pyx index 543c94d61c6..91500f8a065 100644 --- a/src/sage/rings/fast_arith.pyx +++ b/src/sage/rings/fast_arith.pyx @@ -5,19 +5,13 @@ Basic arithmetic with C integers #***************************************************************************** # 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: -# +# 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/ #***************************************************************************** - ################################################################### # We define the following functions in this file, both # for int (up to bound = 2**31 - 1) and longlong (up to 2**63 - 1). @@ -41,8 +35,10 @@ Basic arithmetic with C integers # The int definitions +from libc.math cimport sqrt +from sage.libs.gmp.mpz cimport mpz_set_ui + from sage.ext.stdsage cimport PY_NEW -include "sage/ext/cdefs.pxi" from cypari2.paridecl cimport * from cypari2.gen cimport Gen as pari_gen @@ -172,18 +168,19 @@ cpdef prime_range(start, stop=None, algorithm="pari_primes", bint py_ints=False) raise ValueError("algorithm argument must be either ``pari_primes`` or ``pari_isprime``") return res + cdef class arith_int: - cdef public int abs_int(self, int x) except -1: + cdef int abs_int(self, int x) except -1: if x < 0: return -x return x - cdef public int sign_int(self, int n) except -2: + cdef int sign_int(self, int n) except -2: if n < 0: return -1 return 1 - cdef public int c_gcd_int(self, int a, int b) except -1: + cdef int c_gcd_int(self, int a, int b) except -1: cdef int c if a==0: return self.abs_int(b) @@ -197,12 +194,10 @@ cdef class arith_int: b = c return a - def gcd_int(self, int a, int b): return self.c_gcd_int(a,b) - - cdef public int c_xgcd_int(self, int a, int b, int* ss, int* tt) except -1: + cdef int c_xgcd_int(self, int a, int b, int* ss, int* tt) except -1: cdef int psign, qsign, p, q, r, s, c, quot, new_r, new_s if a == 0: @@ -239,7 +234,7 @@ cdef class arith_int: g = self.c_xgcd_int(a,b, &s, &t) return (g,s,t) - cdef public int c_inverse_mod_int(self, int a, int m) except -1: + cdef int c_inverse_mod_int(self, int a, int m) except -1: if a == 1 or m<=1: return a%m # common special case cdef int g, s, t g = self.c_xgcd_int(a,m, &s, &t) @@ -250,7 +245,6 @@ cdef class arith_int: s = s + m return s - def inverse_mod_int(self, int a, int m): return self.c_inverse_mod_int(a, m) @@ -309,17 +303,17 @@ cdef class arith_int: # The long long versions are next. cdef class arith_llong: - cdef public long long abs_longlong(self, long long x) except -1: + cdef long long abs_longlong(self, long long x) except -1: if x < 0: return -x return x - cdef public long long sign_longlong(self, long long n) except -2: + cdef long long sign_longlong(self, long long n) except -2: if n < 0: return -1 return 1 - cdef public long long c_gcd_longlong(self, long long a, long long b) except -1: + cdef long long c_gcd_longlong(self, long long a, long long b) except -1: cdef long long c if a==0: return self.abs_longlong(b) @@ -333,17 +327,14 @@ cdef class arith_llong: b = c return a - def gcd_longlong(self, long long a, long long b): return self.c_gcd_longlong(a,b) - - cdef public long long c_xgcd_longlong(self, long long a, long long b, - long long *ss, - long long *tt) except -1: + cdef long long c_xgcd_longlong(self, long long a, long long b, + long long *ss, + long long *tt) except -1: cdef long long psign, qsign, p, q, r, s, c, quot, new_r, new_s - if a == 0: ss[0] = 0 tt[0] = self.sign_longlong(b) @@ -371,10 +362,9 @@ cdef class arith_llong: ss[0] = p*psign tt[0] = q*qsign - return a - cdef public long long c_inverse_mod_longlong(self, long long a, long long m) except -1: + cdef long long c_inverse_mod_longlong(self, long long a, long long m) except -1: cdef long long g, s, t g = self.c_xgcd_longlong(a,m, &s, &t) if g != 1: @@ -438,7 +428,3 @@ cdef class arith_llong: cdef long long n, d self.c_rational_recon_longlong(a, m, &n, &d) return (n,d) - - - - diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index c0d70049fb4..c46207d8021 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -18,6 +18,7 @@ AUTHORS: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** +from __future__ import absolute_import include "cysignals/memory.pxi" include "cysignals/signals.pxi" @@ -41,9 +42,9 @@ from sage.interfaces.gap import is_GapElement from sage.misc.randstate import current_randstate -from element_ext_pari import FiniteField_ext_pariElement -from element_pari_ffelt import FiniteFieldElement_pari_ffelt -from finite_field_ntl_gf2e import FiniteField_ntl_gf2e +from .element_ext_pari import FiniteField_ext_pariElement +from .element_pari_ffelt import FiniteFieldElement_pari_ffelt +from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1259,7 +1260,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): AUTHOR: David Joyner and William Stein (2005-11) """ - from sage.groups.generic import discrete_log + from sage.groups.generic import discrete_log b = self.parent()(base) return discrete_log(self, b) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 750f4463b3b..0fe044d7af5 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -32,6 +32,7 @@ AUTHORS: from sage.categories.finite_fields import FiniteFields from sage.structure.parent cimport Parent +from sage.structure.sage_object import register_unpickle_override from sage.misc.cachefunc import cached_method from sage.misc.prandom import randrange @@ -1560,6 +1561,9 @@ def unpickle_FiniteField_prm(_type, order, variable_name, kwargs): """ return _type(order, variable_name, **kwargs) +register_unpickle_override( + 'sage.rings.ring', 'unpickle_FiniteField_prm', unpickle_FiniteField_prm) + def is_FiniteField(x): """ diff --git a/src/sage/rings/finite_rings/finite_field_prime_modn.py b/src/sage/rings/finite_rings/finite_field_prime_modn.py index d1cd765448c..c6183de81a7 100644 --- a/src/sage/rings/finite_rings/finite_field_prime_modn.py +++ b/src/sage/rings/finite_rings/finite_field_prime_modn.py @@ -35,8 +35,9 @@ from sage.rings.integer_ring import ZZ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing_generic -import sage.structure.factorization as factorization -from sage.structure.parent import Parent + +from sage.structure.sage_object import register_unpickle_override + class FiniteField_prime_modn(FiniteField_generic, integer_mod_ring.IntegerModRing_generic): r""" @@ -297,3 +298,7 @@ def degree(self): 1 """ return Integer(1) + +register_unpickle_override( + 'sage.rings.finite_field_prime_modn', 'FiniteField_prime_modn', + FiniteField_prime_modn) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 7bbf12bf5bd..be96bb7b9f0 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -69,8 +69,7 @@ TESTS:: #***************************************************************************** from __future__ import print_function, division -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" +from cysignals.signals cimport sig_on, sig_off from cpython.int cimport * from cpython.list cimport * @@ -4235,7 +4234,7 @@ cdef class IntegerMod_to_Integer(Map): Morphism.__init__(self, sage.categories.homset.Hom(R, integer_ring.ZZ, Sets())) cpdef Element _call_(self, x): - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) if isinstance(x, IntegerMod_gmp): mpz_set(ans.value, (x).value) elif isinstance(x, IntegerMod_int): diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 3075e5638d0..6190ed360d6 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -53,19 +53,25 @@ sage: M.base_field().base_field() Rational function field in x over Finite Field in a of size 5^2 +It is also possible to construct function fields over an imperfect base field:: + + sage: N. = FunctionField(K) + +and function fields as inseparable extensions:: + + sage: R. = K[] + sage: O. = K.extension(v^5 - x) + TESTS:: sage: TestSuite(K).run() sage: TestSuite(L).run() # long time (8s on sage.math, 2012) sage: TestSuite(M).run() # long time (52s on sage.math, 2012) + sage: TestSuite(N).run(skip = '_test_derivation') # long time + sage: TestSuite(O).run(skip = '_test_derivation') # long time -The following two test suites do not pass ``_test_elements`` yet since -``R.an_element()`` has a ``_test_category`` method which it should not have. -It is not the fault of the function field code so this will -be fixed in another ticket:: - - sage: TestSuite(R).run(skip = '_test_elements') - sage: TestSuite(S).run(skip = '_test_elements') + sage: TestSuite(R).run() + sage: TestSuite(S).run() """ from __future__ import absolute_import #***************************************************************************** @@ -180,7 +186,7 @@ def characteristic(self): sage: K.characteristic() 7 sage: R. = K[] - sage: L. = K.extension(y^2-x) + sage: L. = K.extension(y^2 - x) sage: L.characteristic() 7 """ @@ -346,6 +352,32 @@ def _coerce_map_from_(self, R): return True return False + def _test_derivation(self, **options): + """ + Test the correctness of the derivations of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: TestSuite(K).run() # indirect doctest + """ + tester = self._tester(**options) + S = tester.some_elements() + K = self.constant_base_field().some_elements() + d = self.derivation() + from itertools import product + # Leibniz's law + for x,y in tester.some_elements(product(S, S)): + tester.assert_(d(x*y) == x*d(y) + d(x)*y) + # Linearity + for x,y in tester.some_elements(product(S, S)): + tester.assert_(d(x+y) == d(x) + d(y)) + for c,x in tester.some_elements(product(K, S)): + tester.assert_(d(c*x) == c*d(x)) + # Constants map to zero + for c in tester.some_elements(K): + tester.assert_(d(c) == 0) + def _convert_map_from_(self, R): r""" Return a conversion from ``R`` to this function field or ``None`` if @@ -358,7 +390,7 @@ def _convert_map_from_(self, R): sage: L. = K.extension(y^3 + x^3 + 4*x + 1) sage: K(L(x)) # indirect doctest x - + """ if isinstance(R, FunctionField_polymod): base_conversion = self.convert_map_from(R.base_field()) @@ -554,7 +586,7 @@ def __reduce__(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L = K.extension(y^2-x) + sage: L = K.extension(y^2 - x) sage: clazz,args = L.__reduce__() sage: clazz(*args) Function field in y defined by y^2 - x @@ -1241,6 +1273,63 @@ def genus(self): else: raise NotImplementedError("Computation of genus over this rational function field not implemented yet") + @cached_method + def derivation(self): + r""" + Return a derivation of the function field over the constant base field. + + A derivation on `R` is a map `R\to R` satisfying + `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta + D(\alpha)+\alpha D(\beta)` for all `\alpha, \beta \in R`. For a + function field which is a finite extension of `K(x)` with `K` perfect, + the derivations form a one-dimensional `K`-vector space generated by + the derivation returned by this method. + + OUTPUT: + + - a derivation of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation(); d + Derivation map: + From: Function field in y defined by y^2 + 2*x + To: Function field in y defined by y^2 + 2*x + Defn: y |--> 2/x*y + sage: d(x) + 1 + sage: d(x^3) + 0 + sage: d(x*y) + 0 + sage: d(y) + 2/x*y + + Derivations are linear and satisfy Leibniz's law:: + + sage: d(x+y) == d(x) + d(y) + True + sage: d(x*y) == x*d(y) + y*d(x) + True + + If the field is a separable extension of the base field, the derivation + extending a derivation of the base function field is uniquely + determined. Proposition 11 of [GT1996]_ describes how to compute the + extension. We apply the formula described there to the generator + of the space of derivations on the base field. + + The general inseparable case is not implemented yet (see :trac:`16562`, + :trac:`16564`.)` + """ + from .maps import FunctionFieldDerivation_separable + if self.polynomial().gcd(self.polynomial().derivative()).is_one(): + return FunctionFieldDerivation_separable(self, self.base_ring().derivation()) + else: + raise NotImplementedError("construction of separable models not implemented") + def is_RationalFunctionField(x): """ Return ``True`` if ``x`` is of rational function field type. @@ -1851,35 +1940,24 @@ def vector_space(self, base=None): @cached_method def derivation(self): r""" - Return a generator of the space of derivations over the constant base - field of this function field. - - A derivation on `R` is a map `R \to R` with - `D(\alpha + \beta) = D(\alpha) + D(\beta)` and - `D(\alpha \beta) = \beta D(\alpha)+\alpha D(\beta)` - for all `\alpha, \beta \in R`. For a function - field `K(x)` with `K` perfect, the derivations form a one-dimensional - `K`-vector space generated by the extension of the usual derivation on - `K[x]` (cf. Proposition 10 in [GT1996]_.) + Return a derivation of the rational function field over the constant + base field. OUTPUT: - An endofunction on this function field. - - REFERENCES: + - a derivation of the rational function field - .. [GT1996] - Gianni, P., & Trager, B. (1996). Square-free algorithms in - positive characteristic. Applicable Algebra in Engineering, - Communication and Computing, 7(1), 1-14. + The derivation maps the generator of the rational function field to 1. EXAMPLES:: sage: K. = FunctionField(GF(3)) - sage: K.derivation() + sage: m = K.derivation(); m Derivation map: From: Rational function field in x over Finite Field of size 3 To: Rational function field in x over Finite Field of size 3 + sage: m(x) + 1 TESTS:: @@ -1888,7 +1966,6 @@ def derivation(self): Traceback (most recent call last): ... NotImplementedError: not implemented for non-perfect base fields - """ from .maps import FunctionFieldDerivation_rational if not self.constant_base_field().is_perfect(): diff --git a/src/sage/rings/function_field/function_field_element.pyx b/src/sage/rings/function_field/function_field_element.pyx index 876c3de0e3f..802390328a1 100644 --- a/src/sage/rings/function_field/function_field_element.pyx +++ b/src/sage/rings/function_field/function_field_element.pyx @@ -333,7 +333,7 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): sage: f.element() 1/x^2*y + x/(x^2 + 1) sage: type(f.element()) - + """ return self._x diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 12482f661e0..896c49b8fd3 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -14,7 +14,7 @@ sage: K.hom(1/x) Function Field endomorphism of Rational function field in x over Rational Field Defn: x |--> 1/x - sage: L. = K.extension(y^2-x) + sage: L. = K.extension(y^2 - x) sage: K.hom(y) Function Field morphism: From: Rational function field in x over Rational Field @@ -111,45 +111,41 @@ def is_injective(self): return False class FunctionFieldDerivation_rational(FunctionFieldDerivation): - r""" + """ A derivation on a rational function field. - INPUT: - - - ``K`` -- a rational function field - - - ``u`` -- an element of ``K``, the image of the generator of ``K`` under - the derivation. - EXAMPLES:: sage: K. = FunctionField(QQ) sage: d = K.derivation() sage: isinstance(d, sage.rings.function_field.maps.FunctionFieldDerivation_rational) True - """ def __init__(self, K, u): - r""" + """ Initialize a derivation of ``K`` which sends the generator of ``K`` to ``u``. + INPUT: + + - ``K`` -- a rational function field + + - ``u`` -- an element of ``K``, the image of the generator of ``K`` under + the derivation + EXAMPLES:: sage: K. = FunctionField(QQ) sage: d = K.derivation() # indirect doctest - + sage: type(d) + """ - from .function_field import is_RationalFunctionField - if not is_RationalFunctionField(K): - raise ValueError("K must be a rational function field") - if u.parent() is not K: - raise ValueError("u must be an element in K") FunctionFieldDerivation.__init__(self, K) + self._u = u def _call_(self, x): - r""" + """ Compute the derivation of ``x``. INPUT: @@ -166,18 +162,117 @@ def _call_(self, x): 3*x^2 sage: d(1/x) -1/x^2 + """ + f = x.numerator() + g = x.denominator() + numerator = f.derivative() * g - f * g.derivative() + if numerator.is_zero(): + return self.codomain().zero() + else: + return self._u * self.codomain()(numerator / g**2) + +class FunctionFieldDerivation_separable(FunctionFieldDerivation): + """ + The unique extension of the derivation ``d`` to ``L``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + """ + def __init__(self, L, d): """ - f,g = x.numerator(),x.denominator() + Initialization. + + INPUT: - if not f.gcd(g).is_one(): - raise NotImplementedError("derivations only implemented for rational functions with coprime numerator and denominator.") + - ``L`` -- a function field which is a separable extension of the domain of + ``d`` - numerator = f.derivative()*g - f*g.derivative() - if numerator.is_zero(): + - ``d`` -- a derivation on the base function field of ``L`` + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() # indirect doctest + sage: type(d) + + """ + FunctionFieldDerivation.__init__(self, L) + + f = self.domain().polynomial() + if not f.gcd(f.derivative()).is_one(): + raise ValueError("L must be a separable extension of its base field.") + + x = self.domain().gen() + + self._d = d + self._gen_image = - f.map_coefficients(lambda c:d(c))(x) / f.derivative()(x) + + def _call_(self, x): + r""" + Evaluate the derivation on ``x``. + + INPUT: + + - ``x`` -- an element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d(x) # indirect doctest + 1 + sage: d(y) + (-1/2/-x)*y + sage: d(y^2) + 1 + """ + if x.is_zero(): return self.codomain().zero() + + x = x._x + y = self.domain().gen() + + return x.map_coefficients(self._d) + x.derivative()(y) * self._gen_image + + def _repr_defn(self): + """ + Return the string representation of the map. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L.derivation() # indirect doctest + Derivation map: + From: Function field in y defined by y^2 - x + To: Function field in y defined by y^2 - x + Defn: y |--> (-1/2/-x)*y + + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.derivation() + Derivation map: + From: Function field in z defined by z^2 - y + To: Function field in z defined by z^2 - y + Defn: y |--> (-1/2/-x)*y + z |--> 1/4/x*z + """ + base = self._d._repr_defn() + ret = '{} |--> {}'.format(self.domain().gen(), self._gen_image) + if base: + return base + '\n' + ret else: - return self._u * self.codomain()( numerator / g**2 ) + return ret class FunctionFieldIsomorphism(Morphism): r""" diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 63fc39a1662..3f01782c078 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -139,17 +139,18 @@ cimport cython from libc.math cimport ldexp from libc.string cimport memcpy +from cysignals.memory cimport check_allocarray, check_malloc, sig_free +from cysignals.signals cimport sig_on, sig_off + import operator import sys +from sage.ext.stdsage cimport PY_NEW from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * from sage.misc.superseded import deprecated_function_alias from sage.misc.long cimport pyobject_to_long -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" -include "cysignals/memory.pxi" from cpython.list cimport * from cpython.number cimport * from cpython.int cimport * @@ -1235,11 +1236,11 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): This method just calls :func:`digits` with ``base=2``. - SEE ALSO: + .. SEEALSO:: - :func:`nbits` (number of bits; a faster way to compute - ``len(x.bits())``; and :func:`binary`, which returns a string in - more-familiar notation. + :func:`nbits` (number of bits; a faster way to compute + ``len(x.bits())``; and :func:`binary`, which returns a string in + more-familiar notation. EXAMPLES:: @@ -7053,7 +7054,7 @@ cdef hook_fast_tp_functions(): """ global global_dummy_Integer, sizeof_Integer, integer_pool - integer_pool = sig_malloc(integer_pool_size * sizeof(PyObject*)) + integer_pool = check_allocarray(integer_pool_size, sizeof(PyObject*)) cdef PyObject* o o = global_dummy_Integer diff --git a/src/sage/rings/integer_ring.pyx b/src/sage/rings/integer_ring.pyx index 9dec9d241bd..14639f74a08 100644 --- a/src/sage/rings/integer_ring.pyx +++ b/src/sage/rings/integer_ring.pyx @@ -45,13 +45,12 @@ other types will also coerce to the integers, when it makes sense. from __future__ import absolute_import, print_function -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" - from cpython.int cimport * from cpython.list cimport * from cpython.object cimport Py_NE +from cysignals.signals cimport sig_check, sig_on, sig_off + from sage.libs.gmp.mpz cimport * import sage.rings.infinity import sage.rings.rational @@ -474,7 +473,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): """ if end is None: end = start - start = PY_NEW(Integer) # 0 + start = Integer.__new__(Integer) if step is None: step = 1 if type(step) is not int: @@ -504,7 +503,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): sig_on() while mpz_cmp(a.value, b.value)*step_sign < 0: last = a - a = PY_NEW(Integer) + a = Integer.__new__(Integer) if type(step) is int: # count on branch prediction... if istep > 0: mpz_add_ui(a.value, last.value, istep) @@ -540,8 +539,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): n += 1 cdef Integer _coerce_ZZ(self, ZZ_c *z): - cdef integer.Integer i - i = PY_NEW(integer.Integer) + cdef Integer i = Integer.__new__(Integer) sig_on() ZZ_to_mpz(i.value, z) sig_off() @@ -737,8 +735,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain): 5 """ - cdef integer.Integer z - z = PY_NEW(integer.Integer) + cdef Integer z = Integer.__new__(Integer) if x is not None and y is None and x <= 0: raise TypeError("x must be > 0") if x is not None and y is not None and x >= y: diff --git a/src/sage/rings/multi_power_series_ring.py b/src/sage/rings/multi_power_series_ring.py index 521d21eece5..6231c9e024f 100644 --- a/src/sage/rings/multi_power_series_ring.py +++ b/src/sage/rings/multi_power_series_ring.py @@ -165,7 +165,7 @@ sage: type(x) sage: type(S(x)) - + sage: f = S(2/7 -100*x^2 + 1/3*x*y + y^2).O(3); f 5 - x^2 + 4*x*y + y^2 + O(x, y)^3 diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index f0cb64b4d42..4f8ff2b76ec 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -676,7 +676,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name from that string. - ``check`` - a boolean (default: ``True``), whether to check that the polynomials are irreducible - - ``embeddings`` - a list of elemenst or ``None`` (default: ``None``), + - ``embeddings`` - a list of elements or ``None`` (default: ``None``), embeddings of the relative number fields in an ambient field. - ``latex_names`` - a list of strings or ``None`` (default: ``None``), names used to print the generators for latex output. @@ -9497,7 +9497,7 @@ def _element_constructor_(self, x, check=True): ## d = sage.arith.all.gcd(m,n) ## r = n // d -## # Since we use the power basis for cyclomotic fields, if every +## # Since we use the power basis for cyclotomic fields, if every ## # v[i] with i not divisible by r is 0, then we're good. ## # If h generates self and has order m, then the element g^r diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index ecd6aa17ba6..772ceba3f95 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -27,14 +27,15 @@ AUTHORS: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function + +from __future__ import absolute_import, print_function import operator -include "cysignals/signals.pxi" from cpython.int cimport * -include "sage/ext/stdsage.pxi" + +from cysignals.signals cimport sig_on, sig_off + include "sage/libs/ntl/decl.pxi" from sage.libs.gmp.mpz cimport * @@ -886,8 +887,8 @@ cdef class NumberFieldElement(FieldElement): cdef int _randomize(self, num_bound, den_bound, distribution) except -1: cdef int i - cdef Integer denom_temp = PY_NEW(Integer) - cdef Integer tmp_integer = PY_NEW(Integer) + cdef Integer denom_temp = Integer.__new__(Integer) + cdef Integer tmp_integer = Integer.__new__(Integer) cdef ZZ_c ntl_temp cdef list coeff_list cdef Rational tmp_rational @@ -918,7 +919,7 @@ cdef class NumberFieldElement(FieldElement): else: coeff_list = [] mpz_set_si(denom_temp.value, 1) - tmp_integer = PY_NEW(Integer) + tmp_integer = Integer.__new__(Integer) for i from 0 <= i < ZZX_deg(self.__fld_numerator.x): tmp_rational = (QQ.random_element(num_bound=num_bound, @@ -2501,8 +2502,7 @@ cdef class NumberFieldElement(FieldElement): """ if ZZX_deg(self.__numerator) >= 1: raise TypeError("Unable to coerce %s to a rational"%self) - cdef Integer num - num = PY_NEW(Integer) + cdef Integer num = Integer.__new__(Integer) ZZX_getitem_as_mpz(num.value, &self.__numerator, 0) return num / (ZZ)._coerce_ZZ(&self.__denominator) @@ -2840,7 +2840,7 @@ cdef class NumberFieldElement(FieldElement): cdef Integer numCoeff cdef int i for i from 0 <= i <= ZZX_deg(self.__numerator): - numCoeff = PY_NEW(Integer) + numCoeff = Integer.__new__(Integer) ZZX_getitem_as_mpz(numCoeff.value, &self.__numerator, i) coeffs.append( numCoeff / den ) return coeffs 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 87997c15bfa..7ac8c88236d 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -29,11 +29,11 @@ AUTHORS: #***************************************************************************** from __future__ import absolute_import -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" include "sage/libs/ntl/decl.pxi" from cpython.object cimport Py_EQ, Py_NE, Py_LE, Py_GE, Py_LT, Py_GT +from cysignals.signals cimport sig_on, sig_off + from sage.libs.gmp.mpz cimport * from sage.libs.gmp.mpq cimport * from sage.libs.ntl.ntl_ZZ cimport ntl_ZZ @@ -332,9 +332,9 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): sage: loads(dumps(a/3+5)) == a/3+5 True """ - cdef Integer a = PY_NEW(Integer) - cdef Integer b = PY_NEW(Integer) - cdef Integer denom = PY_NEW(Integer) + cdef Integer a = Integer.__new__(Integer) + cdef Integer b = Integer.__new__(Integer) + cdef Integer denom = Integer.__new__(Integer) mpz_set(a.value, self.a) mpz_set(b.value, self.b) mpz_set(denom.value, self.denom) @@ -1399,7 +1399,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): if mpz_cmp_ui(self.b, 0) != 0 or mpz_cmp_ui(self.denom, 1) != 0: raise TypeError("Unable to coerce %s to an integer" % self) else: - res = PY_NEW(Integer) + res = Integer.__new__(Integer) mpz_set(res.value, self.a) return res @@ -1704,7 +1704,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): cdef NumberFieldElement_quadratic gen = self.number_field().gen() # should this be cached? cdef Integer denom if gen.is_sqrt_disc(): - denom = PY_NEW(Integer) + denom = Integer.__new__(Integer) mpz_set(denom.value, self.denom) return denom else: @@ -1974,7 +1974,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): if mpz_sgn(self.b) == 0: mpz_init_set(x,self.a) mpz_fdiv_q(x,x,self.denom) - result = PY_NEW(Integer) + result = Integer.__new__(Integer) mpz_set(result.value,x) mpz_clear(x) return result @@ -1996,7 +1996,7 @@ cdef class NumberFieldElement_quadratic(NumberFieldElement_absolute): mpz_add(x,x,self.a) # here x = a + floor(sqrt(b^2 D)) or a + floor(-sqrt(b^2 D)) mpz_fdiv_q(x,x,self.denom) - result = PY_NEW(Integer) + result = Integer.__new__(Integer) mpz_set(result.value,x) mpz_clear(x) return result diff --git a/src/sage/rings/number_field/totallyreal.pyx b/src/sage/rings/number_field/totallyreal.pyx index 4027eb8baa3..9414c9923d2 100644 --- a/src/sage/rings/number_field/totallyreal.pyx +++ b/src/sage/rings/number_field/totallyreal.pyx @@ -105,9 +105,10 @@ Authors # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -include 'sage/ext/stdsage.pxi' +from __future__ import absolute_import, print_function + +from cysignals.memory cimport check_calloc, sig_free import math import sys @@ -295,7 +296,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False ng = B_pari pari_tmp1 = B_pari - dB = PY_NEW(Integer) + dB = Integer.__new__(Integer) dB_odlyzko = odlyzko_bound_totallyreal(n_int) mpz_set_d(dB.value, dB_odlyzko) dB = 40000*((dB+1)**n_int) @@ -303,10 +304,7 @@ def enumerate_totallyreal_fields_prim(n, B, a = [], verbose=0, return_seqs=False counts[i] = 0 B_pari = pari(B) - 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 + f_out = check_calloc(n_int + 1, sizeof(int)) f_out[n_int] = 1 if keep_fields: diff --git a/src/sage/rings/padics/CA_template.pxi b/src/sage/rings/padics/CA_template.pxi index 047c67f6f65..c1c485892f2 100644 --- a/src/sage/rings/padics/CA_template.pxi +++ b/src/sage/rings/padics/CA_template.pxi @@ -922,7 +922,7 @@ cdef class CAElement(pAdicTemplateElement): sage: R = Zp(7,4,'capped-abs'); a = R(7); a.precision_absolute() 4 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.absprec) return ans @@ -938,7 +938,7 @@ cdef class CAElement(pAdicTemplateElement): sage: R = Zp(7,4,'capped-abs'); a = R(7); a.precision_relative() 3 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.absprec - self.valuation_c()) return ans @@ -1007,7 +1007,7 @@ cdef class CAElement(pAdicTemplateElement): (6, O(5^0)) """ cdef CAElement unit = self._new_c() - cdef Integer valuation = PY_NEW(Integer) + cdef Integer valuation = Integer.__new__(Integer) cdef long val = cremove(unit.value, self.value, self.absprec, self.prime_pow) mpz_set_si(valuation.value, val) unit.absprec = self.absprec - val @@ -1214,7 +1214,7 @@ cdef class pAdicConvert_CA_ZZ(RingMap): sage: f(ZpCA(5)(0)) 0 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) cdef CAElement x = _x cconv_mpz_t_out(ans.value, x.value, 0, x.absprec, x.prime_pow) return ans diff --git a/src/sage/rings/padics/CR_template.pxi b/src/sage/rings/padics/CR_template.pxi index c56fa547179..f1d2ef96d34 100644 --- a/src/sage/rings/padics/CR_template.pxi +++ b/src/sage/rings/padics/CR_template.pxi @@ -1403,7 +1403,7 @@ cdef class CRElement(pAdicTemplateElement): """ if exactzero(self.ordp): return infinity - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.ordp + self.relprec) return ans @@ -1429,8 +1429,8 @@ cdef class CRElement(pAdicTemplateElement): 0 sage: R(0,7).precision_relative() 0 - """ - cdef Integer ans = PY_NEW(Integer) + """ + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.relprec) return ans @@ -1528,7 +1528,7 @@ cdef class CRElement(pAdicTemplateElement): raise ValueError('Ring (%s) residue field of the wrong characteristic.'%self.parent()) if exactzero((self).ordp): raise ValueError("unit part of 0 not defined") - cdef Integer val = PY_NEW(Integer) + cdef Integer val = Integer.__new__(Integer) mpz_set_si(val.value, (self).ordp) cdef CRElement unit = (self)._new_c() unit.ordp = 0 @@ -1761,7 +1761,7 @@ cdef class pAdicConvert_CR_ZZ(RingMap): ... ValueError: negative valuation """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) cdef CRElement x = _x if x.relprec != 0: cconv_mpz_t_out(ans.value, x.unit, x.ordp, x.relprec, x.prime_pow) diff --git a/src/sage/rings/padics/FM_template.pxi b/src/sage/rings/padics/FM_template.pxi index 760ad0bebff..e852e9cd0a5 100644 --- a/src/sage/rings/padics/FM_template.pxi +++ b/src/sage/rings/padics/FM_template.pxi @@ -33,7 +33,6 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.ext.stdsage cimport PY_NEW include "padic_template_element.pxi" from cpython.int cimport * @@ -721,7 +720,7 @@ cdef class FMElement(pAdicTemplateElement): sage: R = Zp(7,4,'fixed-mod'); a = R(7); a.precision_absolute() 4 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.prime_pow.prec_cap) return ans @@ -736,7 +735,7 @@ cdef class FMElement(pAdicTemplateElement): sage: a = R(0); a.precision_relative() 0 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.prime_pow.prec_cap - self.valuation_c()) return ans @@ -813,7 +812,7 @@ cdef class FMElement(pAdicTemplateElement): (5, O(5^5)) """ cdef FMElement unit = self._new_c() - cdef Integer valuation = PY_NEW(Integer) + cdef Integer valuation = Integer.__new__(Integer) mpz_set_si(valuation.value, cremove(unit.value, self.value, self.prime_pow.prec_cap, self.prime_pow)) return valuation, unit @@ -1003,7 +1002,7 @@ cdef class pAdicConvert_FM_ZZ(RingMap): sage: f(ZpFM(5)(0)) 0 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) cdef FMElement x = _x cconv_mpz_t_out(ans.value, x.value, 0, x.prime_pow.prec_cap, x.prime_pow) return ans 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 176a9cbfccc..d77f6d38f35 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -119,8 +119,8 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" +from cysignals.signals cimport sig_on, sig_off + include "sage/libs/ntl/decl.pxi" from sage.structure.element cimport Element @@ -243,13 +243,13 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): ctx_prec = ZZ_remove(tmp_z, (x.modulus()).x, self.prime_pow.pow_ZZ_tmp(1)[0]) if ZZ_IsOne(tmp_z): x = x.lift() - tmp_Int = PY_NEW(Integer) + tmp_Int = Integer.__new__(Integer) ZZ_to_mpz(tmp_Int.value, &(x).x) x = tmp_Int else: raise TypeError("cannot coerce the given ntl_ZZ_p (modulus not a power of the same prime)") elif isinstance(x, ntl_ZZ): - tmp_Int = PY_NEW(Integer) + tmp_Int = Integer.__new__(Integer) ZZ_to_mpz(tmp_Int.value, &(x).x) x = tmp_Int elif isinstance(x, (int, long)): @@ -968,11 +968,10 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: ZZ(W(5)) 5 """ - cdef Integer ans cdef ZZ_c tmp_z if ZZ_pX_deg(self.value) > 0: raise ValueError("This element not well approximated by an integer.") - ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) tmp_z = ZZ_p_rep(ZZ_pX_ConstTerm(self.value)) ZZ_to_mpz(ans.value, &tmp_z) return ans @@ -1415,7 +1414,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.unit_part() 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + w^22 + 3*w^24 + O(w^25) """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_ui(ans.value, self.prime_pow.ram_prec_cap) return ans @@ -1441,16 +1440,10 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): sage: a.unit_part() 3 + 2*w^2 + w^4 + w^6 + w^7 + 3*w^8 + 3*w^9 + 2*w^11 + 3*w^12 + 3*w^13 + w^15 + 4*w^16 + 2*w^17 + w^18 + w^22 + 3*w^24 + O(w^25) """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_ui(ans.value, self.prime_pow.ram_prec_cap - self.valuation_c()) return ans -# def residue(self, n): -# """ -# Reduces this element modulo pi^n. -# """ -# raise NotImplementedError - cpdef pAdicZZpXFMElement unit_part(self): """ Returns the unit part of ``self``, ie diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index a3b68f8126e..c5454415af8 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -101,7 +101,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): sage: ZpCA(3,3)(1/4).lift() # indirect doctest 7 """ - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set(ans.value, self.value) return ans @@ -218,7 +218,6 @@ cdef class pAdicCappedAbsoluteElement(CAElement): :meth:`_mod_` """ - cdef Integer selfvalue, modulus if not isinstance(absprec, Integer): absprec = Integer(absprec) if mpz_cmp_si((absprec).value, self.absprec) > 0: @@ -226,9 +225,9 @@ cdef class pAdicCappedAbsoluteElement(CAElement): elif mpz_sgn((absprec).value) < 0: raise ValueError("cannot reduce modulo a negative power of p.") cdef long aprec = mpz_get_ui((absprec).value) - modulus = PY_NEW(Integer) + cdef Integer modulus = Integer.__new__(Integer) mpz_set(modulus.value, self.prime_pow.pow_mpz_t_tmp(aprec)) - selfvalue = PY_NEW(Integer) + cdef Integer selfvalue = Integer.__new__(Integer) mpz_set(selfvalue.value, self.value) return Mod(selfvalue, modulus) @@ -262,13 +261,13 @@ cdef class pAdicCappedAbsoluteElement(CAElement): if mpz_divisible_p(self.value, self.prime_pow.prime.value): return infinity if mpz_cmp_ui(self.value, 1) == 0: - ans = PY_NEW(Integer) + ans = Integer.__new__(Integer) mpz_set_ui(ans.value, 1) return ans mpz_init(ppow_minus_one) mpz_sub_ui(ppow_minus_one, self.prime_pow.pow_mpz_t_tmp(self.absprec), 1) if mpz_cmp(self.value, ppow_minus_one) == 0: - ans = PY_NEW(Integer) + ans = Integer.__new__(Integer) mpz_set_ui(ans.value, 2) mpz_clear(ppow_minus_one) return ans diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 01fae2b883f..413e89ab369 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -48,12 +48,13 @@ AUTHORS: # http://www.gnu.org/licenses/ #***************************************************************************** -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" -include "sage/libs/ntl/decl.pxi" from cpython.list cimport * from cpython.dict cimport * +from cysignals.signals cimport sig_on, sig_off + +include "sage/libs/ntl/decl.pxi" + import weakref from sage.misc.misc import cputime from sage.rings.infinity import infinity @@ -562,11 +563,11 @@ cdef class PowComputer_ext(PowComputer_class): PowComputer_ext for 5, with polynomial [9765620 0 1] """ cdef Integer cache_limit, prec_cap, ram_prec_cap - cache_limit = PY_NEW(Integer) + cache_limit = Integer.__new__(Integer) mpz_set_si(cache_limit.value, self.cache_limit) - prec_cap = PY_NEW(Integer) + prec_cap = Integer.__new__(Integer) mpz_set_si(prec_cap.value, self.prec_cap) - ram_prec_cap = PY_NEW(Integer) + ram_prec_cap = Integer.__new__(Integer) mpz_set_si(ram_prec_cap.value, self.ram_prec_cap) return PowComputer_ext_maker, (self.prime, cache_limit, prec_cap, ram_prec_cap, self.in_field, self._poly, self._prec_type, self._ext_type, self._shift_seed) @@ -1103,7 +1104,7 @@ cdef class PowComputer_ZZ_pX(PowComputer_ext): 4 """ cdef Integer _n = Integer(n) - cdef Integer ans = PY_NEW(Integer) + cdef Integer ans = Integer.__new__(Integer) mpz_set_si(ans.value, self.capdiv(mpz_get_si(_n.value))) return ans diff --git a/src/sage/rings/padics/pow_computer_flint.pyx b/src/sage/rings/padics/pow_computer_flint.pyx index 6d3c5370ca1..c8ddd3f2826 100644 --- a/src/sage/rings/padics/pow_computer_flint.pyx +++ b/src/sage/rings/padics/pow_computer_flint.pyx @@ -1,3 +1,5 @@ +from __future__ import absolute_import + include "cysignals/signals.pxi" include "cysignals/memory.pxi" @@ -622,11 +624,11 @@ def PowComputer_flint_maker(prime, cache_limit, prec_cap, ram_prec_cap, in_field """ if prec_type == 'capped-rel': - from qadic_flint_CR import PowComputer_ + from .qadic_flint_CR import PowComputer_ elif prec_type == 'capped-abs': - from qadic_flint_CA import PowComputer_ + from .qadic_flint_CA import PowComputer_ elif prec_type == 'fixed-mod': - from qadic_flint_FM import PowComputer_ + 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/polynomial/flatten.py b/src/sage/rings/polynomial/flatten.py index 86962ed44d4..6e8bee61c79 100644 --- a/src/sage/rings/polynomial/flatten.py +++ b/src/sage/rings/polynomial/flatten.py @@ -166,7 +166,7 @@ def __init__(self, domain): def _call_(self, p): r""" - Evaluate an flatenning morphism. + Evaluate an flattening morphism. EXAMPLES:: @@ -214,7 +214,7 @@ def _call_(self, p): @cached_method def section(self): """ - Inverse of this flattenning morphism. + Inverse of this flattening morphism. EXAMPLES:: diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 3669b5b7da1..1204e629147 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -2399,16 +2399,16 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial_generic): @coerce_binop def quo_rem(self, right): """ - Divide this laurent polynomial by ``right`` and return a quotient and + Divide this Laurent polynomial by ``right`` and return a quotient and a remainder. INPUT: - - ``right`` -- a laurent polynomial + - ``right`` -- a Laurent polynomial OUTPUT: - A pair of laurent polynomials. + A pair of Laurent polynomials. EXAMPLES:: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 411e84e44a2..4207fc0fcdf 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -470,7 +470,7 @@ def extract(T, indices): def _split_laurent_polynomial_dict_(P, M, d): r""" - Helper function for splitting a multivariate laurent polynomial + Helper function for splitting a multivariate Laurent polynomial during conversion. INPUT: @@ -620,7 +620,7 @@ def gen(self, i=0): def variable_names_recursive(self, depth=infinity): r""" Return the list of variable names of this ring and its base rings, - as if it were a single multi-variate laurent polynomial. + as if it were a single multi-variate Laurent polynomial. INPUT: diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index 656acc95ce1..cf5596875aa 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -2859,10 +2859,13 @@ cdef class MPolynomial_libsingular(sage.rings.polynomial.multi_polynomial.MPolyn - ``mon`` - a monomial OUTPUT: - coefficient in base ring - SEE ALSO: - For coefficients in a base ring of fewer variables, look at ``coefficient``. + coefficient in base ring + + .. SEEALSO:: + + For coefficients in a base ring of fewer variables, + look at ``coefficient``. EXAMPLES:: diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index f872cd0c3f9..c66c9d01529 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -267,8 +267,8 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): TESTS: - A PolynomialSequence can exist with elements in a infinite field of - characteristic 2 that is not (see :trac:`19452`):: + A PolynomialSequence can exist with elements in an infinite field of + characteristic 2 (see :trac:`19452`):: sage: from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence sage: F = GF(2) @@ -277,7 +277,6 @@ def PolynomialSequence(arg1, arg2=None, immutable=False, cr=False, cr_str=None): sage: PolynomialSequence([0], R) [0] """ - from sage.matrix.matrix import is_Matrix from sage.rings.polynomial.pbori import BooleanMonomialMonoid, BooleanMonomial diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 39631f0cccf..525dd2462b2 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -636,7 +636,7 @@ cdef class NCPolynomialRing_plural(Ring): """ return False - def is_field(self): + def is_field(self, *args, **kwargs): """ Return ``False``. @@ -646,6 +646,14 @@ cdef class NCPolynomialRing_plural(Ring): sage: P = A.g_algebra(relations={y*x:-x*y}, order = 'lex') sage: P.is_field() False + + TESTS: + + Make the method accept additional parameters, such as the flag ``proof``. + See :trac:`22910`:: + + sage: P.is_field(proof=False) + False """ return False diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 6e8f4f22e8d..b99a3fb5726 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -41,7 +41,7 @@ from sage.libs.pari.all import pari_gen from sage.structure.element import coerce_binop -from sage.rings.infinity import infinity +from sage.rings.infinity import infinity, Infinity from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer from sage.structure.factorization import Factorization @@ -61,7 +61,7 @@ class Polynomial_generic_sparse(Polynomial): sage: R. = PolynomialRing(PolynomialRing(QQ, 'y'), sparse=True) sage: f = x^3 - x + 17 sage: type(f) - + sage: loads(f.dumps()) == f True @@ -694,7 +694,7 @@ def shift(self, n): sage: R. = PolynomialRing(ZZ, sparse=True) sage: p = x^100000 + 2*x + 4 sage: type(p) - + sage: p.shift(0) x^100000 + 2*x + 4 sage: p.shift(-1) @@ -1031,7 +1031,7 @@ class Polynomial_generic_sparse_field(Polynomial_generic_sparse, Polynomial_gene sage: R. = PolynomialRing(Frac(RR['t']), sparse=True) sage: f = x^3 - x + 17 sage: type(f) - + sage: loads(f.dumps()) == f True """ @@ -1110,6 +1110,16 @@ def newton_polygon(self): ... PrecisionError: The coefficient of t^4 has not enough precision + TESTS: + + Check that :trac:`22936` is fixed:: + + sage: S. = PowerSeriesRing(GF(5)) + sage: R. = S[] + sage: p = x^2+y+x*y^2 + sage: p.newton_polygon() + Finite Newton polygon with 3 vertices: (0, 2), (1, 0), (2, 1) + AUTHOR: - Xavier Caruso (2013-03-20) @@ -1120,14 +1130,15 @@ def newton_polygon(self): 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)) + if len(vertices_prec) > 0: + 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): @@ -1204,6 +1215,14 @@ def _factor_of_degree(self, deg): ... KeyboardInterrupt: + TESTS:: + + sage: S. = PowerSeriesRing(GF(5)) + sage: R. = S[] + sage: p = x^2+y+x*y^2 + sage: p._factor_of_degree(1) + (1 + O(x^20))*y + x^2 + x^5 + 2*x^8 + 4*x^14 + 2*x^17 + 2*x^20 + O(x^22) + AUTHOR: - Xavier Caruso (2013-03-20) @@ -1213,8 +1232,15 @@ def _factor_of_degree(self, deg): Precision is not optimal, and can be improved. """ coeffs = self.list() - a = self.truncate(deg + 1) - b = v = self.parent()(1) + a = coeffs[:deg+1] + # The leading coefficient need to be known at finite precision + # in order to ensure that the while loop below terminates + if a[deg].precision_absolute() is Infinity: + a[deg] = a[deg].add_bigoh(self.base_ring().default_prec()) + + parent = self.parent() + a = parent(a) + b = v = parent(1) x = self % a while(not x.is_zero()): a += (v * x) % a @@ -1325,6 +1351,14 @@ def slope_factorization(self): [0, 0, 0] [1] + TESTS:: + + sage: S. = PowerSeriesRing(GF(5)) + sage: R. = S[] + sage: p = x^2+y+x*y^2 + sage: p.slope_factorization() + (x) * ((x + O(x^22))*y + 1 + 4*x^3 + 4*x^6 + 3*x^9 + x^15 + 3*x^18 + O(x^21)) * ((x^-1 + O(x^20))*y + x + x^4 + 2*x^7 + 4*x^13 + 2*x^16 + 2*x^19 + O(x^22)) + AUTHOR: - Xavier Caruso (2013-03-20) diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index 23867620a23..9a4f10f55c5 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -20,18 +20,22 @@ We check that the buggy gcd is fixed (see :trac:`17816`):: 1 """ -################################################################################ +#***************************************************************************** # Copyright (C) 2007 William Stein # Copyright (C) 2008-2010 Burcin Erocal # -# 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 __future__ import print_function +#***************************************************************************** + +from __future__ import absolute_import, print_function + +from cysignals.memory cimport sig_free +from cysignals.signals cimport sig_on, sig_off -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" include "sage/libs/ntl/decl.pxi" from cpython.int cimport PyInt_AS_LONG @@ -400,7 +404,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sig_off() return f if isinstance(x0, int): - z = PY_NEW(Integer) + z = Integer.__new__(Integer) sig_on() fmpz_init(a_fmpz) fmpz_init(z_fmpz) @@ -421,7 +425,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): if mpz_sgn(a.value) == 0: return self[0] - z = PY_NEW(Integer) + z = Integer.__new__(Integer) sig_on() fmpz_init(a_fmpz) @@ -484,7 +488,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_poly_content(c, self.__poly) - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) fmpz_get_mpz(z.value, c) fmpz_clear(c) return z if sign == 1 else -z @@ -529,7 +533,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): sage: f[:100] 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + 1 """ - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) fmpz_poly_get_coeff_mpz(z.value, self.__poly, n) return z @@ -550,7 +554,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): if name is None: name = self.parent().variable_name() cdef long i - cdef Integer coef = PY_NEW(Integer) + cdef Integer coef = Integer.__new__(Integer) cdef list all = [] for i from fmpz_poly_degree(self.__poly) >= i >= 0: fmpz_poly_get_coeff_mpz(coef.value, self.__poly, i) @@ -882,7 +886,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_poly_xgcd(r, ss.__poly, tt.__poly, self.__poly, (right).__poly) sig_off() - cdef Integer rr = PY_NEW(Integer) + cdef Integer rr = Integer.__new__(Integer) fmpz_get_mpz(rr.value, r) fmpz_clear(r) @@ -1384,11 +1388,10 @@ cdef class Polynomial_integer_dense_flint(Polynomial): """ cdef ZZX_c ntl_poly cdef ZZ_c* temp - cdef Integer x fmpz_poly_get_ZZX(ntl_poly, self.__poly) temp = ZZX_discriminant(&ntl_poly, proof) - x = PY_NEW(Integer) + cdef Integer x = Integer.__new__(Integer) ZZ_to_mpz(x.value, temp) del temp @@ -1710,7 +1713,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): cdef fmpz_t res fmpz_init(res) - cdef Integer x = PY_NEW(Integer) + cdef Integer x = Integer.__new__(Integer) sig_on() fmpz_poly_resultant(res, self.__poly, diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx index 6faf1fe71a7..2b0ed0ced0f 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_ntl.pyx @@ -31,10 +31,12 @@ do:: # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import print_function -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" +from __future__ import absolute_import, print_function + +from cysignals.memory cimport sig_free +from cysignals.signals cimport sig_on, sig_off + include "sage/libs/ntl/decl.pxi" from sage.rings.polynomial.polynomial_element cimport Polynomial @@ -252,7 +254,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): 0 """ cdef ZZ_c y - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) ZZX_content(y, self.__poly) ZZ_to_mpz(z.value, &y) return z @@ -356,7 +358,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): sage: f[:100] 5*x^5 + 4*x^4 + 3*x^3 + 2*x^2 + x + 1 """ - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) ZZ_to_mpz(z.value, &self.__poly.rep.elts()[n]) return z @@ -643,7 +645,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): cdef ZZ_c *r ZZX_xgcd(&self.__poly, &(right).__poly, &r, &s, &t, 1) # proof = 1 - cdef Integer rr = PY_NEW(Integer) + cdef Integer rr = Integer.__new__(Integer) ZZ_to_mpz(rr.value, r) cdef Polynomial_integer_dense_ntl ss = self._new() cdef Polynomial_integer_dense_ntl tt = self._new() @@ -836,7 +838,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): -339 """ cdef ZZ_c* temp = ZZX_discriminant(&self.__poly, proof) - cdef Integer x = PY_NEW(Integer) + cdef Integer x = Integer.__new__(Integer) ZZ_to_mpz(x.value, temp) del temp return x @@ -1120,7 +1122,7 @@ cdef class Polynomial_integer_dense_ntl(Polynomial): """ cdef Polynomial_integer_dense_ntl _other = (self.parent()._coerce_(other)) cdef ZZ_c* temp = ZZX_resultant(&self.__poly, &_other.__poly, proof) - cdef Integer x = PY_NEW(Integer) + cdef Integer x = Integer.__new__(Integer) ZZ_to_mpz(x.value, temp) del temp return x diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring.py b/src/sage/rings/polynomial/polynomial_quotient_ring.py index 28fa62bc430..c527a2b861c 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring.py @@ -823,8 +823,27 @@ def is_field(self, proof = True): sage: S = R.quotient(x^2 - 2) sage: S.is_field() True + + If proof is ``True``, requires the ``is_irreducible`` method of the + modulus to be implemented:: + + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2+x+1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2+x+1) + sage: F2.is_field() + Traceback (most recent call last): + ... + NotImplementedError + sage: F2.is_field(proof = False) + False """ - return self.base_ring().is_field(proof) and self.modulus().is_irreducible() + if proof: + return self.base_ring().is_field(True) and self.modulus().is_irreducible() + try: + return self.base_ring().is_field(False) and self.modulus().is_irreducible() + except NotImplementedError: + return False def krull_dimension(self): return self.base_ring().krull_dimension() @@ -1120,7 +1139,7 @@ def S_class_group(self, S, proof=True): sage: CG = S.S_class_group([]) sage: type(CG[0][0][1]) - + sage: type(CG[0][1]) @@ -1246,7 +1265,7 @@ def class_group(self, proof=True): sage: CG = S.class_group() sage: type(CG[0][0][1]) - + sage: type(CG[0][1]) @@ -1313,7 +1332,7 @@ def S_units(self, S, proof=True): sage: U = L.S_units([]) sage: type(U[0][0]) - + sage: type(U[0][1]) sage: type(U[1][1]) @@ -1399,7 +1418,7 @@ def units(self, proof=True): sage: L. = K['y'].quotient(y^3 + 5) sage: U = L.units() sage: type(U[0][0]) - + sage: type(U[0][1]) sage: type(U[1][1]) diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 99e4f3d82d5..2680215d099 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -371,7 +371,7 @@ def __invert__(self): TESTS: - An element is not invertable if the base ring is not a field + An element is not invertible if the base ring is not a field (see :trac:`13303`):: sage: Z16x. = Integers(16)[] diff --git a/src/sage/rings/polynomial/polynomial_rational_flint.pyx b/src/sage/rings/polynomial/polynomial_rational_flint.pyx index 731f9851ce8..1d5166272ec 100644 --- a/src/sage/rings/polynomial/polynomial_rational_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_rational_flint.pyx @@ -16,8 +16,8 @@ AUTHOR: # http://www.gnu.org/licenses/ #***************************************************************************** -include "sage/ext/stdsage.pxi" -include "cysignals/signals.pxi" +from cysignals.memory cimport check_allocarray, check_malloc, sig_free +from cysignals.signals cimport sig_on, sig_str, sig_off from cpython.int cimport PyInt_AS_LONG from sage.misc.long cimport pyobject_to_long @@ -240,7 +240,7 @@ 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 = sig_malloc(n * sizeof(mpq_t)) + L2 = check_allocarray(n, sizeof(mpq_t)) for deg from 0 <= deg < n: mpq_init(L2[deg]) mpq_set(L2[deg], ( L1[deg]).value) @@ -1419,7 +1419,7 @@ cdef class Polynomial_rational_flint(Polynomial): sage: f.denominator() 3 """ - cdef Integer den = PY_NEW(Integer) + cdef Integer den = Integer.__new__(Integer) if fmpq_poly_denref(self.__poly) is NULL: mpz_set_ui(den.value, 1) else: diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 353f249a841..c1b157c0e7a 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -1629,11 +1629,11 @@ def __init__(self, base_ring, name="x", sparse=False, element_class=None, catego sage: R = PRing(QQ, 'x', sparse=True); R Sparse Univariate Polynomial Ring in x over Rational Field sage: type(R.gen()) - + sage: R = PRing(CC, 'x'); R Univariate Polynomial Ring in x over Complex Field with 53 bits of precision sage: type(R.gen()) - + Demonstrate that :trac:`8762` is fixed:: @@ -2041,7 +2041,7 @@ def __init__(self, base_ring, name="x", element_class=None, implementation=None) sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field sage: R = PolynomialRing_dense_finite_field(GF(5), implementation='generic') sage: type(R(0)) - + sage: S = PolynomialRing_dense_finite_field(GF(25, 'a'), implementation='NTL') sage: type(S(0)) @@ -2468,7 +2468,7 @@ def __init__(self, base_ring, name=None, element_class=None, category=None): sage: R = PRing(Zp(13), name='t'); R Univariate Polynomial Ring in t over 13-adic Ring with capped relative precision 20 sage: type(R.gen()) - + """ if element_class is None: from sage.rings.polynomial.padics.\ @@ -2487,7 +2487,7 @@ def __init__(self, base_ring, name=None, element_class=None, category=None): sage: R = PRing(Zp(13, type='capped-abs'), name='t'); R Univariate Polynomial Ring in t over 13-adic Ring with capped absolute precision 20 sage: type(R.gen()) - + """ if element_class is None: from sage.rings.polynomial.padics.polynomial_padic_flat import \ @@ -2506,7 +2506,7 @@ def __init__(self, base_ring, name=None, element_class=None, category=None): Univariate Polynomial Ring in t over 13-adic Ring of fixed modulus 13^20 sage: type(R.gen()) - + """ if element_class is None: from sage.rings.polynomial.padics.polynomial_padic_flat import \ @@ -2543,7 +2543,7 @@ def __init__(self, base_ring, name=None, element_class=None, category=None): sage: R = PRing(Qp(13), name='t'); R Univariate Polynomial Ring in t over 13-adic Field with capped relative precision 20 sage: type(R.gen()) - + """ if element_class is None: from sage.rings.polynomial.padics.\ diff --git a/src/sage/rings/polynomial/real_roots.pyx b/src/sage/rings/polynomial/real_roots.pyx index 821ee40276c..f55d7f2d113 100644 --- a/src/sage/rings/polynomial/real_roots.pyx +++ b/src/sage/rings/polynomial/real_roots.pyx @@ -32,7 +32,7 @@ algorithm in that paper: The best description of the algorithms used (other than this source code itself) is in the slides for my Sage Days 4 talk, currently available -from http://www.sagemath.org:9001/days4schedule . +from https://wiki.sagemath.org/days4schedule . """ ################################################################################ diff --git a/src/sage/rings/qqbar.py b/src/sage/rings/qqbar.py index 65bcc819c86..2621249f1b5 100644 --- a/src/sage/rings/qqbar.py +++ b/src/sage/rings/qqbar.py @@ -510,7 +510,10 @@ 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.structure.sage_object import (SageObject, richcmp, + rich_to_bool, richcmp_not_equal, + op_EQ, op_NE, op_LE, op_LT, + op_GE, op_GT) from sage.rings.real_mpfr import RR from sage.rings.real_mpfi import RealIntervalField, RIF, is_RealIntervalFieldElement, RealIntervalField_class from sage.rings.complex_field import ComplexField @@ -2054,6 +2057,79 @@ def mk_algebraic(x): QQxy_x = QQxy.gen(0) QQxy_y = QQxy.gen(1) + +def cmp_elements_with_same_minpoly(a, b, p): + r""" + Compare the algebraic elements ``a`` and ``b`` knowing that they have the + same minimal polynomial ``p``. + + This is an helper function for comparison of algebraic elements (i.e. the + methods :meth:`AlgebraicNumber._richcmp_` and + :meth:`AlgebraicReal._richcmp_`). + + INPUT: + + - ``a`` and ``b`` -- elements of the algebraic or the real algebraic field + with same minimal polynomial + + - ``p`` -- the minimal polynomial + + OUTPUT: + + `-1`, `0`, `1`, `None` depending on whether `a < b`, `a = b` or `a > b` or + the function did not succeed with the given precision of `a` and `b`. + + EXAMPLES:: + + sage: from sage.rings.qqbar import cmp_elements_with_same_minpoly + sage: x = polygen(ZZ) + sage: p = x^2 - 2 + sage: a = AA.polynomial_root(p, RIF(1,2)) + sage: b = AA.polynomial_root(p, RIF(-2,-1)) + sage: cmp_elements_with_same_minpoly(a, b, p) + 1 + sage: cmp_elements_with_same_minpoly(-a, b, p) + 0 + """ + ar = a._value.real() + br = b._value.real() + if not ar.overlaps(br): + return -1 if richcmp_not_equal(ar, br, op_LT) else 1 + + ai = a._value.imag() + bi = b._value.imag() + + if a.parent() is AA or b.parent() is AA: + ring = AA + else: + ring = QQbar + roots = p.roots(ring, False) + + real = ar.union(br) + imag = ai.union(bi) + roots = [r for r in roots if r._value.real().overlaps(real) + and r._value.imag().abs().overlaps(imag)] + if len(roots) == 1: + # There is only a single (real) root matching both descriptors + # so they both must be that root and therefore equal. + return 0 + if (len(roots) == 2 and + not roots[0]._value.imag().contains_zero()): + # There is a complex conjugate pair of roots matching both + # descriptors, so compare by imaginary value. + while ai.contains_zero(): + a._more_precision() + ai = a._value.imag() + while bi.contains_zero(): + b._more_precision() + bi = b._value.imag() + if ai.overlaps(bi): + return 0 + return -1 if richcmp_not_equal(ai, bi, op_LT) else 1 + + return None + + class AlgebraicGeneratorRelation(SageObject): """ A simple class for maintaining relations in the lattice of algebraic @@ -2172,7 +2248,13 @@ def __cmp__(self, other): sage: gen.__cmp__(qq_generator) 1 """ - return cmp(self._index, other._index) + si = self._index + oi = other._index + if si < oi: + return -1 + if si > oi: + return 1 + return 0 def is_complex(self): r""" @@ -3022,6 +3104,146 @@ def __hash__(self): else: return hash((self + QQbar_hash_offset).interval_exact(CIF)) + def __bool__(self): + """ + Check whether ``self`` is nonzero. + + This is fast if interval arithmetic proves it and in many other cases. + Though, it might be slow in very particular cases where the number is + actually zero or very close to zero. + + EXAMPLES:: + + sage: bool(QQbar.zeta(2) + 1) + False + sage: bool(QQbar.zeta(7) / (2^500)) + True + + sage: bool(QQbar(I).imag()) + True + sage: bool(QQbar(I).real()) + False + + The following is very fast, even though the number is really small:: + + sage: a1 = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: a2 = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: bool(a1 + a2) + True + sage: bool(a1 - a2) + False + + sage: a = QQbar(2).sqrt() - 16616132878186749607/11749380235262596085 + sage: b = QQbar(2).sqrt() - 6882627592338442563/4866752642924153522 + sage: c = QQbar(3).sqrt() - 142437039878091970439/82236063316189858921 + sage: d = (59/2)**(1000/7) + sage: e = (a + b + c) * (a + b - c) * (a - b) * (a - b - c) / d + sage: bool(e) + True + sage: bool(e.abs() < 2**-500) + True + + An identity between roots of unity:: + + sage: z3 = QQbar.zeta(3) + sage: z4 = QQbar.zeta(4) + sage: z5 = QQbar.zeta(5) + sage: p1 = (z3 + z4 + z5)**2 + sage: p2 = (z3 - z4 - z5)**2 + sage: p3 = (z3 - z4 + z5)**2 + sage: p4 = (z3 + z4 - z5)**2 + sage: bool(p1 - p2 + p3 - p4 - 8 * QQbar.zeta(15)**8) + False + + Test some non-trivial zeros:: + + sage: x = polygen(ZZ) + sage: a = (AA(2).sqrt() + AA(3).sqrt() + AA(5).sqrt())^2 + sage: b = 10 + 2*max((x^4 - 62*x^2 - 240*x - 239).roots(AA, False)) + sage: bool(a - b) + False + + sage: d = sum(AA(k)**(1/k) for k in [2..100]) + sage: bool(d * (a - b)) + False + sage: bool((a - b) * d) + False + sage: bool(d * (a - b) * d) + False + sage: bool((a - b) / d) + False + + sage: d = sum(QQbar(-k)**(1/k) for k in [2..100]) + sage: bool(d * (a - b)) + False + sage: bool((a - b) * d) + False + sage: bool(d * (a - b) * d) + False + sage: bool((a - b) / d) + False + """ + # case 0: trivial tests + if not self._value.contains_zero(): + return True + elif self._value.is_zero(): + if not isinstance(self._descr, ANRational): + self._set_descr(ANRational(QQ.zero())) + return False + + # case 1: cheap tests + sd = self._descr + if isinstance(sd, ANExtensionElement): + # The ANExtensionElement returns an ANRational + # instead, if the number is zero. + return True + elif isinstance(sd, ANRational): + return bool(sd._value) + elif isinstance(sd, ANUnaryExpr) and sd._op != 'real' and sd._op != 'imag': + ans = bool(sd._arg) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + elif isinstance(sd, ANBinaryExpr) and sd._op is operator.mul: + ans = bool(sd._left) and bool(sd._right) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + elif isinstance(sd, ANBinaryExpr) and sd._op is operator.div: + ans = bool(sd._left) + if not ans: + self._set_descr(ANRational(QQ.zero())) + return ans + + # case 2: try more precision + if self._value.prec() < 128: + self._more_precision() + if not self._value.contains_zero(): + return True + + # case 3: try with minpoly in case of x+y or x-y + if isinstance(sd, ANBinaryExpr): + op = sd._op + left = sd._left + right = sd._right if op is operator.sub else -sd._right + + lp = left.minpoly() + rp = right.minpoly() + if lp != rp: + return True + + c = cmp_elements_with_same_minpoly(left, right, left.minpoly()) + if c is not None: + if c == 0: + self._set_descr(ANRational(QQ.zero())) + return bool(c) + + # Sigh... + self.exactify() + return bool(self) + + __nonzero__ = __bool__ + def is_square(self): """ Return whether or not this number is square. @@ -3613,11 +3835,14 @@ def radical_expression(self): roots = candidates interval_field = interval_field.to_prec(interval_field.prec()*2) + class AlgebraicNumber(AlgebraicNumber_base): r""" The class for algebraic numbers (complex numbers which are the roots of a polynomial with integer coefficients). Much of its functionality is inherited from ``AlgebraicNumber_base``. + + .. automethod:: _richcmp_ """ def __init__(self, x): r""" @@ -3643,8 +3868,8 @@ def __reduce__(self): """ return (AlgebraicNumber, (self._descr, )) - def __cmp__(self, other): - """ + def _richcmp_(self, other, op): + r""" Compare two algebraic numbers, lexicographically. (That is, first compare the real components; if the real components are equal, compare the imaginary components.) @@ -3673,8 +3898,8 @@ def __cmp__(self, other): [-0.0221204634374361? - 1.090991904211621?*I, -0.0221204634374361? + 1.090991904211621?*I, -0.8088604911480535?*I, - 0.?e-215 - 0.7598602580415435?*I, - 0.?e-229 + 0.7598602580415435?*I, + 0.?e-79 - 0.7598602580415435?*I, + 0.?e-79 + 0.7598602580415435?*I, 0.8088604911480535?*I, 0.0221204634374361? - 1.090991904211621?*I, 0.0221204634374361? + 1.090991904211621?*I] @@ -3707,63 +3932,8 @@ def __cmp__(self, other): 1.000000000000000? + 3.390564396412898?*I, 1.000000000000000? + 3.850538755978243?*I, 1.000000000000000? + 4.016778562562223?*I] - """ - # case 0: same object - if self is other: return 0 - # case 1: real parts are clearly distinct - ri1 = self._value.real() - ri2 = other._value.real() - if not ri1.overlaps(ri2): - return cmp(ri1, ri2) - - # case 2: possibly equal or conjugate values - # (this case happen a lot when sorting the roots of a real polynomial) - if is_RealIntervalFieldElement(self._value): - ci1 = ri1.parent().zero() - else: - ci1 = self._value.imag().abs() - if is_RealIntervalFieldElement(other._value): - ci2 = ri2.parent().zero() - else: - ci2 = other._value.imag().abs() - if ci1.overlaps(ci2) and self.minpoly() == other.minpoly(): - ri = ri1.union(ri2) - ci = ci1.union(ci2) - roots = self.minpoly().roots(QQbar, False) - roots = [r for r in roots if r._value.real().overlaps(ri) - and r._value.imag().abs().overlaps(ci)] - if len(roots) == 1: - # There is only a single (real) root matching both descriptors - # so they both must be that root and therefore equal. - return 0 - if (len(roots) == 2 and - not roots[0]._value.imag().contains_zero()): - # There is a complex conjugate pair of roots matching both - # descriptors, so compare by imaginary value. - ii1 = self._value.imag() - while ii1.contains_zero(): - self._more_precision() - ii1 = self._value.imag() - ii2 = other._value.imag() - while ii2.contains_zero(): - other._more_precision() - ii2 = other._value.imag() - if ii1.overlaps(ii2): - return 0 - return cmp(ii1, ii2) - - # case 3: try hard to compare real parts and imaginary parts - rcmp = cmp(self.real(), other.real()) - if rcmp != 0: - return rcmp - return cmp(self.imag(), other.imag()) - - def __eq__(self, other): - """ - Test two algebraic numbers for equality. - - EXAMPLES:: + TESTS:: sage: QQbar.zeta(6) == QQbar(1/2 + I*sqrt(3)/2) True @@ -3775,25 +3945,6 @@ def __eq__(self, other): False sage: GF(7)(2) in QQbar False - """ - if not isinstance(other, AlgebraicNumber): - try: - self, other = canonical_coercion(self, other) - return self == other - except TypeError: - return False - if self is other: return True - if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): - return not self - if isinstance(self._descr, ANRational) and self._descr._value.is_zero(): - return not other - return not self._sub_(other) - - def __ne__(self, other): - r""" - Test two algebraic numbers for inequality. - - EXAMPLES:: sage: QQbar.zeta(6) != QQbar(1/2 + I*sqrt(3)/2) False @@ -3803,39 +3954,59 @@ def __ne__(self, other): False sage: QQbar(2) != GF(7)(2) True - """ - return not self == other - def __bool__(self): - """ - Check whether self is equal is nonzero. This is fast if - interval arithmetic proves that self is nonzero, but may be - slow if the number actually is very close to zero. - - EXAMPLES:: - - sage: bool(QQbar.zeta(2) + 1) - False - sage: bool(QQbar.zeta(7) / (2^500)) + sage: QQbar.zeta(3).real() == -1/2 True """ - val = self._value - d = self._descr - if not val.contains_zero() or isinstance(d, ANExtensionElement): - return True - elif isinstance(d, ANRational): - return bool(d._value) + # note: we can assume that self is not other here + sd = self._descr + od = other._descr + + if isinstance(sd, ANRational) and isinstance(od, ANRational): + return richcmp(sd._value, od._value, op) + + if op == op_EQ or op == op_NE: + # some cheap and quite common tests where we can decide + # equality or difference + if not (self._value.real().overlaps(other._value.real()) and + self._value.imag().overlaps(other._value.imag())): + return op == op_NE + if isinstance(sd, ANRational) and not sd._value: + return bool(other) == (op == op_NE) + elif isinstance(od, ANRational) and not od._value: + return bool(self) == (op == op_NE) + elif (isinstance(sd, ANExtensionElement) and + isinstance(od, ANExtensionElement) and + sd._generator is od._generator): + return sd._value == od._value if op == op_EQ else sd._value != od._value + + # case 0: real parts are clearly distinct + ri1 = self._value.real() + ri2 = other._value.real() + if not ri1.overlaps(ri2): + return richcmp_not_equal(ri1, ri2, op) - while self._value.prec() < 128: - self._more_precision() - if not self._value.contains_zero(): - return True + # case 1: rationals + sd = self._descr + od = other._descr + if isinstance(sd, ANRational) and isinstance(od, ANRational): + return richcmp(sd._value, od._value, op) - # Sigh... - self.exactify() - return self.__bool__() + # case 2: possibly equal or conjugate values + # (this case happen a lot when sorting the roots of a real polynomial) + ci1 = self._value.imag().abs() + ci2 = other._value.imag().abs() + if ci1.overlaps(ci2) and self.minpoly() == other.minpoly(): + c = cmp_elements_with_same_minpoly(self, other, self.minpoly()) + if c is not None: + return rich_to_bool(op, c) - __nonzero__ = __bool__ + # case 3: try hard to compare real parts and imaginary parts + srp = self.real() + orp = other.real() + if srp != orp: + return richcmp_not_equal(srp, orp, op) + return richcmp(self.imag(), other.imag(), op) def __pow__(self, e): r""" ``self**p`` returns the `p`'th power of self (where `p` can @@ -3879,7 +4050,7 @@ def __pow__(self, e): sage: QQbar.zeta(7)^6 0.6234898018587335? - 0.7818314824680299?*I sage: (QQbar.zeta(7)^6)^(1/3) * QQbar.zeta(21) - 1.000000000000000? + 0.?e-18*I + 1.000000000000000? + 0.?e-17*I TESTS: @@ -4311,6 +4482,11 @@ def rational_argument(self): return self._descr.rational_argument(self) class AlgebraicReal(AlgebraicNumber_base): + r""" + A real algebraic number. + + .. automethod:: _richcmp_ + """ def __init__(self, x): """ Create an algebraic real from x, possibly taking the real part of x. @@ -4388,26 +4564,68 @@ def __reduce__(self): """ return (AlgebraicReal, (self._descr, )) - def __cmp__(self, other): + def _richcmp_(self, other, op): """ Compare two algebraic reals. EXAMPLES:: - sage: cmp(AA(golden_ratio), AA(sqrt(5))) - -1 - sage: cmp(AA(golden_ratio), AA((sqrt(5)+1)/2)) - 0 - sage: cmp(AA(7), AA(50/7)) - -1 + sage: AA(2).sqrt() < AA(3).sqrt() + True + sage: ((5+AA(5).sqrt())/2).sqrt() == 2*QQbar.zeta(5).imag() + True + sage: AA(3).sqrt() + AA(2).sqrt() < 3 + False + + TESTS:: + + sage: AA(golden_ratio) < AA(sqrt(5)) + True + sage: AA(golden_ratio) == AA((sqrt(5)+1)/2) + True + sage: AA(7) >= AA(50/7) + False """ - if self is other: return 0 - if isinstance(other._descr, ANRational) and other._descr._value.is_zero(): - return self.sign() - elif isinstance(self._descr, ANRational) and self._descr._value.is_zero(): - return -other.sign() - else: - return self._sub_(other).sign() + # note: we can assume that self is not other here + sd = self._descr + od = other._descr + + if type(sd) is ANRational and type(od) is ANRational: + return richcmp(sd._value, od._value, op) + + if op == op_EQ or op == op_NE: + # some cheap and quite common tests where we can decide equality or difference + if not self._value.real().overlaps(other._value.real()): + return op == op_NE + if type(sd) is ANRational and not sd._value: + return bool(other) == (op == op_NE) + elif type(od) is ANRational and not od._value: + return bool(self) == (op == op_NE) + elif (type(sd) is ANExtensionElement and + type(od) is ANExtensionElement and + sd._generator is od._generator): + return sd._value == od._value if op == op_EQ else sd._value != od._value + elif self.minpoly() != other.minpoly(): + return op == op_NE + + # case 0: real parts are clearly distinct + if not self._value.overlaps(other._value): + return richcmp(self._value, other._value, op) + + # case 1: rationals + sd = self._descr + od = other._descr + if type(sd) is ANRational and type(od) is ANRational: + return richcmp(sd._value, od._value, op) + + if self._value.prec() < 128: + self._more_precision() + if other._value.prec() < 128: + other._more_precision() + if not self._value.overlaps(other._value): + return richcmp(self._value, other._value, op) + + return rich_to_bool(op, (self-other).sign()) def __pow__(self, e): """ @@ -4719,28 +4937,82 @@ def sign(self): -1 sage: (AA(2).sqrt() - AA(2).sqrt()).sign() 0 + + sage: a = AA(2).sqrt() + AA(3).sqrt() - 58114382797550084497/18470915334626475921 + sage: a.sign() + 1 + sage: b = AA(2).sqrt() + AA(3).sqrt() - 2602510228533039296408/827174681630786895911 + sage: b.sign() + -1 + + sage: c = AA(5)**(1/3) - 1437624125539676934786/840727688792155114277 + sage: c.sign() + 1 + + sage: (((a+b)*(a+c)*(b+c))**9 / (a*b*c)).sign() + 1 + sage: (a-b).sign() + 1 + sage: (b-a).sign() + -1 + sage: (a*b).sign() + -1 + sage: ((a*b).abs() + a).sign() + 1 + sage: (a*b - b*a).sign() + 0 """ - if self._value.lower() > 0: - return 1 - elif self._value.upper() < 0: - return -1 - elif isinstance(self._descr, ANRational): - return self._descr._value.sign() + if not self._value.contains_zero(): + return self._value.unique_sign() + + sd = self._descr + if isinstance(self._descr, ANRational): + return sd._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). # An irrational number must eventually be different from 0 - self._more_precision() - return self.sign() - elif self._value.prec() < 128: + while self._value.contains_zero(): + self._more_precision() + return self._value.unique_sign() + elif type(sd) is ANBinaryExpr: + ls = sd._left.sign() + rs = sd._right.sign() + if sd._op is operator.mul or sd._op is operator.div: + return sd._left.sign() * sd._right.sign() + elif sd._op is operator.add: + if ls == rs: + return ls + else: + if ls == -rs: + return ls + elif not ls: + self._set_descr((-sd._right)._descr) + return -rs + elif not rs: + self._set_descr(sd._left._descr) + return ls + elif type(sd) is ANUnaryExpr: + if sd._op == 'abs': + c = 1 if bool(sd._arg) else 0 + if not c: + self._set_descr(ANRational(QQ.zero())) + return c + elif sd._op == '-': + return -(sd._arg.sign()) + elif sd._op == '~': + return sd._arg.sign() + + if self._value.prec() < 128: # OK, we'll try adding precision one more time self._more_precision() - return self.sign() - else: - # Sigh... - self.exactify() - return self.sign() + if not self._value.contains_zero(): + return self._value.unique_sign() + + # Sigh... + self.exactify() + return self.sign() def _interval_fast(self, prec): r""" diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index 17b95f8dd8d..b0ee6f634d5 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -102,7 +102,7 @@ def _reduce_(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a._reduce_() sage: a._QuotientRingElement__rep x @@ -118,7 +118,7 @@ def lift(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a.lift() x sage: (3/5*(a + a^2 + b^2)).lift() @@ -135,7 +135,7 @@ def __bool__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: bool(a) # indirect docteest True sage: bool(S(0)) @@ -161,7 +161,7 @@ def is_unit(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(1 - x*y); type(a) - + sage: a*b 1 sage: a.is_unit() @@ -185,7 +185,7 @@ def _repr_(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a-2*a*b # indirect doctest -2*a*b + a @@ -255,7 +255,7 @@ def _add_(self, right): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a + b a + b @@ -275,7 +275,7 @@ def _sub_(self, right): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a - b a - b @@ -295,7 +295,7 @@ def _mul_(self, right): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a * b a*b @@ -338,7 +338,7 @@ def _div_(self, right): Another really easy example:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a / S(2) 1/2*a sage: (a*b)._div_(b) @@ -461,7 +461,7 @@ def __int__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: int(S(-3)) # indirect doctest -3 sage: type(int(S(-3))) @@ -478,7 +478,7 @@ def _integer_(self, Z=None): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: ZZ(S(-3)) -3 @@ -497,7 +497,7 @@ def _rational_(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: QQ(S(-2/3)) -2/3 @@ -516,7 +516,7 @@ def __long__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: long(S(-3)) # indirect doctest -3L """ @@ -527,7 +527,7 @@ def __neg__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: -a # indirect doctest -a sage: -(a+b) @@ -540,7 +540,7 @@ def __pos__(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: (a+b).__pos__() a + b sage: c = a+b; c.__pos__() is c @@ -553,7 +553,7 @@ def __invert__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: ~S(2/3) 3/2 @@ -580,7 +580,7 @@ def __float__(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: float(S(2/3)) 0.6666666666666666 sage: float(a) @@ -607,7 +607,7 @@ def __cmp__(self, other): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a > b # indirect doctest True sage: b > a @@ -672,7 +672,7 @@ def lt(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: (a+3*a*b+b).lt() 3*a*b """ @@ -694,7 +694,7 @@ def lm(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: (a+3*a*b+b).lm() a*b @@ -717,7 +717,7 @@ def lc(self): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: (a+3*a*b+b).lc() 3 """ @@ -735,7 +735,7 @@ def variables(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a.variables() (a,) sage: b.variables() @@ -760,7 +760,7 @@ def monomials(self): EXAMPLES:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: a.monomials() [a] sage: (a+a*b).monomials() @@ -804,7 +804,7 @@ def _singular_(self, singular=singular_default): TESTS:: sage: R. = QQ[]; S. = R.quo(x^2 + y^2); type(a) - + sage: (a-2/3*b)._singular_() x-2/3*y sage: S((a-2/3*b)._singular_()) diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 27d6a23e6b8..b1acb90b174 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -41,12 +41,13 @@ TESTS:: # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** + from __future__ import absolute_import -include "cysignals/signals.pxi" -include "sage/ext/stdsage.pxi" from cpython cimport * +from cysignals.signals cimport sig_on, sig_off + import sys import operator @@ -205,7 +206,7 @@ cpdef Integer integer_rational_power(Integer a, Rational b): sage: integer_rational_power(0, QQ(0)) 1 """ - cdef Integer z = PY_NEW(Integer) + cdef Integer z = Integer.__new__(Integer) if mpz_sgn(mpq_numref(b.value)) < 0: raise ValueError("Only positive exponents supported.") cdef int sgn = mpz_sgn(a.value) @@ -698,21 +699,21 @@ cdef class Rational(sage.structure.element.FieldElement): if type == "std": while mpz_sgn(q) != 0: - z = PY_NEW(Integer) + z = Integer.__new__(Integer) mpz_fdiv_qr(z.value,tmp,p,q) mpz_set(p,q) mpz_set(q,tmp) res.append(z) elif type == "hj": while mpz_sgn(q) != 0: - z = PY_NEW(Integer) + z = Integer.__new__(Integer) mpz_cdiv_qr(z.value,tmp,p,q) mpz_set(p,q) mpz_set(q,tmp) res.append(z) if mpz_sgn(q) == 0: break - z = PY_NEW(Integer) + z = Integer.__new__(Integer) mpz_fdiv_qr(z.value,tmp,p,q) mpz_set(p,q) mpz_set(q,tmp) @@ -1639,7 +1640,6 @@ cdef class Rational(sage.structure.element.FieldElement): sage: (-4/17).val_unit(2) # indirect doctest (2, -1/17) """ - cdef integer.Integer v cdef Rational u if mpz_cmp_ui(p.value, 2) < 0: raise ValueError("p must be at least 2.") @@ -1648,7 +1648,7 @@ cdef class Rational(sage.structure.element.FieldElement): u = Rational.__new__(Rational) mpq_set_ui(u.value, 1, 1) return (sage.rings.infinity.infinity, u) - v = PY_NEW(integer.Integer) + cdef Integer v = Integer.__new__(Integer) u = Rational.__new__(Rational) sig_on() mpz_set_ui(v.value, mpz_remove(mpq_numref(u.value), mpq_numref(self.value), p.value)) @@ -2837,8 +2837,7 @@ cdef class Rational(sage.structure.element.FieldElement): """ if not mpz_cmp_si(mpq_denref(self.value), 1) == 0: raise TypeError("no conversion of this rational to integer") - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_numref(self.value)) return n @@ -2853,8 +2852,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: x.numer() -5 """ - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_numref(self.value)) return n @@ -2874,8 +2872,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: x.numerator() 3 """ - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_numref(self.value)) return n @@ -2932,8 +2929,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: x.denom() 1 """ - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_denref(self.value)) return n @@ -2950,8 +2946,7 @@ cdef class Rational(sage.structure.element.FieldElement): sage: x.denominator() 1 """ - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_denref(self.value)) return n @@ -4009,8 +4004,7 @@ cdef class Q_to_Z(Map): """ if not mpz_cmp_si(mpq_denref((x).value), 1) == 0: raise TypeError("no conversion of this rational to integer") - cdef integer.Integer n - n = PY_NEW(integer.Integer) + cdef Integer n = Integer.__new__(Integer) n.set_from_mpz(mpq_numref((x).value)) return n diff --git a/src/sage/rings/rational_field.py b/src/sage/rings/rational_field.py index 4c4c2750616..6fdc8b83453 100644 --- a/src/sage/rings/rational_field.py +++ b/src/sage/rings/rational_field.py @@ -974,7 +974,7 @@ def random_element(self, num_bound=None, den_bound=None, *args, **kwds): Return an random element of `\QQ`. Elements are constructed by randomly choosing integers - for the numerator and denominator, not neccessarily coprime. + for the numerator and denominator, not necessarily coprime. INPUT: diff --git a/src/sage/rings/real_arb.pyx b/src/sage/rings/real_arb.pyx index 9a2fe328ed4..1ea5d3841d0 100644 --- a/src/sage/rings/real_arb.pyx +++ b/src/sage/rings/real_arb.pyx @@ -2578,6 +2578,23 @@ cdef class RealBall(RingElement): """ return not self.is_finite() + def is_NaN(self): + """ + Return ``True`` if this ball is not-a-number. + + EXAMPLES:: + + sage: RBF(NaN).is_NaN() + True + sage: RBF(-5).gamma().is_NaN() + True + sage: RBF(infinity).is_NaN() + False + sage: RBF(42, rad=1.r).is_NaN() + False + """ + return arf_is_nan(arb_midref(self.value)) + # Arithmetic def __neg__(self): @@ -2957,6 +2974,8 @@ cdef class RealBall(RingElement): [1.098612288668110 +/- 6.63e-16] sage: RBF(3).log(2) [1.584962500721156 +/- 7.53e-16] + sage: log(RBF(5), 2) + [2.32192809488736 +/- 3.04e-15] sage: RBF(-1/3).log() nan diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 8cea6e38e84..a0a7d902a9e 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -44,9 +44,11 @@ from __future__ import print_function from cpython.object cimport * from cpython.float cimport * + +from cysignals.signals cimport sig_on, sig_off + include "sage/ext/python_debug.pxi" -include 'sage/ext/stdsage.pxi' -include "cysignals/signals.pxi" +from sage.ext.stdsage cimport PY_NEW from sage.libs.gsl.all cimport * cimport libc.math from libc.string cimport memcpy diff --git a/src/sage/rings/real_interval_absolute.pyx b/src/sage/rings/real_interval_absolute.pyx index ec9f72afc39..15dea4b5ad3 100644 --- a/src/sage/rings/real_interval_absolute.pyx +++ b/src/sage/rings/real_interval_absolute.pyx @@ -266,6 +266,8 @@ cdef class RealIntervalAbsoluteElement(FieldElement): 6.? sage: R100(R((5,6))) 6.? + sage: RIF(CIF(NaN)) + [.. NaN ..] """ Element.__init__(self, parent) diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index 439bc2013b6..3bad4a6f4ea 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -126,20 +126,12 @@ Comparison operations (``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``) return ``True`` if every value in the first interval has the given relation to every value in the second interval. -The ``cmp(a, b)`` function works -differently; it compares two intervals lexicographically. (However, the -behavior is not specified if given a non-interval and an interval.) -Note that ``cmp`` will disappear in Python3. - This convention for comparison operators has good and bad points. The good: - Expected transitivity properties hold (if ``a > b`` and ``b == c``, then ``a > c``; etc.) -- if ``a > b``, then ``cmp(a, b) == 1``; if ``a == b``, then ``cmp(a,b) == 0``; - if ``a < b``, then ``cmp(a, b) == -1`` - - ``a == 0`` is true if the interval contains only the floating-point number 0; similarly for ``a == 1`` @@ -151,17 +143,21 @@ The bad: - Trichotomy fails to hold: there are values ``(a,b)`` such that none of ``a < b``, ``a == b``, or ``a > b`` are true -- It is not the case that if ``cmp(a, b) == 0`` then ``a == b``, or that if - ``cmp(a, b) == 1`` then ``a > b``, or that if ``cmp(a, b) == -1`` then - ``a < b`` - - There are values ``a`` and ``b`` such that ``a <= b`` but neither ``a < b`` nor ``a == b`` hold. +- There are values ``a`` and ``b`` such that neither ``a != b`` + nor ``a == b`` hold. + .. NOTE:: Intervals ``a`` and ``b`` overlap iff ``not(a != b)``. +.. WARNING:: + + The ``cmp(a, b)`` function should not be used to compare real + intervals. Note that ``cmp`` will disappear in Python3. + EXAMPLES:: sage: 0 < RIF(1, 2) @@ -176,14 +172,6 @@ EXAMPLES:: True sage: not(0 < RIF(0, 1)) True - sage: cmp(RIF(0), RIF(0, 1)) - -1 - sage: cmp(RIF(0, 1), RIF(0)) - 1 - sage: cmp(RIF(0, 1), RIF(1)) - -1 - sage: cmp(RIF(0, 1), RIF(0, 1)) - 0 Comparison with infinity is defined through coercion to the infinity ring where semi-infinite intervals are sent to their central value @@ -213,6 +201,19 @@ interval coerces to plus infinity:: sage: RIF(-oo,oo) == oo True +If you want to compare two intervals lexicographically, you can use the +method ``lexico_cmp``. However, the behavior of this method is not +specified if given a non-interval and an interval:: + + sage: RIF(0).lexico_cmp(RIF(0, 1)) + -1 + sage: RIF(0, 1).lexico_cmp(RIF(0)) + 1 + sage: RIF(0, 1).lexico_cmp(RIF(1)) + -1 + sage: RIF(0, 1).lexico_cmp(RIF(0, 1)) + 0 + TESTS: Comparisons with numpy types are right (see :trac:`17758` and :trac:`18076`):: @@ -672,7 +673,7 @@ cdef class RealIntervalField_class(sage.rings.ring.Field): Type: RealIntervalField? for more information. """ if not y is None: - x = (x,y) + x = (x, y) return RealIntervalFieldElement(self, x, base) def algebraic_closure(self): @@ -1147,6 +1148,11 @@ cdef class RealIntervalFieldElement(RingElement): TypeError: Cannot convert sage.rings.integer_ring.IntegerRing_class to sage.rings.real_mpfi.RealIntervalField_class sage: RealIntervalFieldElement.__new__(RealIntervalFieldElement, RIF) [.. NaN ..] + + Equality ``x == x`` does not hold unless ``x`` is exact:: + + sage: x = RIF(4.5, 4.6) + sage: TestSuite(x).run(skip=["_test_eq", "_test_pickling"]) """ cdef RealIntervalField_class p = parent mpfi_init2(self.value, p.__prec) @@ -1256,11 +1262,11 @@ cdef class RealIntervalFieldElement(RingElement): EXAMPLES:: sage: a = RIF(5,5.5) - sage: cmp(loads(dumps(a)), a) + sage: loads(dumps(a)).lexico_cmp(a) 0 sage: R = RealIntervalField(sci_not=1, prec=200) sage: b = R('393.39203845902384098234098230948209384028340') - sage: cmp(loads(dumps(b)), b) + sage: loads(dumps(b)).lexico_cmp(b) 0 sage: b = R(1)/R(0); b # R(0) has no particular sign, thus 1/R(0) covers the whole reals [-infinity .. +infinity] @@ -1274,7 +1280,7 @@ cdef class RealIntervalFieldElement(RingElement): True sage: b = R('[2 .. 3]'); b.str(error_digits=1) '2.5?5e0' - sage: cmp(loads(dumps(b)), b) + sage: loads(dumps(b)).lexico_cmp(b) 0 sage: R = RealIntervalField(4000) sage: s = 1/R(3) @@ -3548,10 +3554,10 @@ cdef class RealIntervalFieldElement(RingElement): cpdef _richcmp_(left, right, int op): """ - Implements comparisons between intervals. + Implement comparisons between intervals. - (See the file header - comment for more information on interval comparison.) + See the file header comment for more information on interval + comparison. EXAMPLES:: @@ -3761,32 +3767,35 @@ cdef class RealIntervalFieldElement(RingElement): """ return not (mpfr_zero_p(&self.value.left) and mpfr_zero_p(&self.value.right)) - cpdef int _cmp_(left, right) except -2: + def lexico_cmp(left, right): """ Compare two intervals lexicographically. + This means that the left bounds are compared first and then + the right bounds are compared if the left bounds coincide. + Return 0 if they are the same interval, -1 if the second is larger, or 1 if the first is larger. EXAMPLES:: - sage: cmp(RIF(0), RIF(1)) + sage: RIF(0).lexico_cmp(RIF(1)) -1 - sage: cmp(RIF(0, 1), RIF(1)) + sage: RIF(0, 1).lexico_cmp(RIF(1)) -1 - sage: cmp(RIF(0, 1), RIF(1, 2)) + sage: RIF(0, 1).lexico_cmp(RIF(1, 2)) -1 - sage: cmp(RIF(0, 0.99999), RIF(1, 2)) + sage: RIF(0, 0.99999).lexico_cmp(RIF(1, 2)) -1 - sage: cmp(RIF(1, 2), RIF(0, 1)) + sage: RIF(1, 2).lexico_cmp(RIF(0, 1)) 1 - sage: cmp(RIF(1, 2), RIF(0)) + sage: RIF(1, 2).lexico_cmp(RIF(0)) 1 - sage: cmp(RIF(0, 1), RIF(0, 2)) + sage: RIF(0, 1).lexico_cmp(RIF(0, 2)) -1 - sage: cmp(RIF(0, 1), RIF(0, 1)) + sage: RIF(0, 1).lexico_cmp(RIF(0, 1)) 0 - sage: cmp(RIF(0, 1), RIF(0, 1/2)) + sage: RIF(0, 1).lexico_cmp(RIF(0, 1/2)) 1 """ cdef RealIntervalFieldElement lt, rt @@ -3808,6 +3817,22 @@ cdef class RealIntervalFieldElement(RingElement): else: return 0 + cpdef int _cmp_(self, other) except -2: + """ + Deprecated method (:trac:`22907`) + + EXAMPLES:: + + sage: a = RIF(1) + sage: a.__cmp__(a) + doctest:...: DeprecationWarning: for RIF elements, do not use cmp + See http://trac.sagemath.org/22907 for details. + 0 + """ + from sage.misc.superseded import deprecation + deprecation(22907, 'for RIF elements, do not use cmp') + return self.lexico_cmp(other) + def __contains__(self, other): """ Test whether one interval (or real number) is totally contained in diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 4fd2636d65e..e7fc8a28eeb 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -629,6 +629,8 @@ cdef class RealField_class(sage.rings.ring.Field): Traceback (most recent call last): ... ValueError: can only convert signed infinity to RR + sage: R(CIF(NaN)) + NaN """ if hasattr(x, '_mpfr_'): return x._mpfr_(self) @@ -1451,7 +1453,7 @@ cdef class RealNumber(sage.structure.element.RingElement): raise ValueError('can only convert signed infinity to RR') elif mpfr_set_str(self.value, s, base, parent.rnd) == 0: pass - elif s == 'NaN' or s == '@NaN@': + elif s == 'NaN' or s == '@NaN@' or s == '[..NaN..]' or s == 'NaN+NaN*I': mpfr_set_nan(self.value) elif s_lower == '+infinity': mpfr_set_inf(self.value, 1) diff --git a/src/sage/rings/ring.pyx b/src/sage/rings/ring.pyx index 22ac471cc12..383935431f4 100644 --- a/src/sage/rings/ring.pyx +++ b/src/sage/rings/ring.pyx @@ -994,8 +994,17 @@ cdef class Ring(ParentWithGens): Traceback (most recent call last): ... NotImplementedError + + Forward the proof flag to ``is_field``, see :trac:`22910`:: + + sage: R1. = GF(5)[] + sage: F1 = R1.quotient_ring(x^2+x+1) + sage: R2. = F1[] + sage: F2 = R2.quotient_ring(x^2+x+1) + sage: F2.is_integral_domain(False) + False """ - if self.is_field(): + if self.is_field(proof): return True if self.is_zero(): diff --git a/src/sage/sandpiles/all.py b/src/sage/sandpiles/all.py index 4e746e78966..ea997043368 100644 --- a/src/sage/sandpiles/all.py +++ b/src/sage/sandpiles/all.py @@ -1,13 +1,6 @@ from __future__ import absolute_import from sage.misc.lazy_import import lazy_import -from .sandpile import Sandpile, SandpileDivisor, SandpileConfig, firing_graph, parallel_firing_graph, wilmes_algorithm, random_digraph, random_DAG, triangle_sandpile +from .sandpile import Sandpile, SandpileDivisor, SandpileConfig, firing_graph, parallel_firing_graph, wilmes_algorithm, random_DAG, triangle_sandpile lazy_import('sage.sandpiles.examples', 'sandpiles') - -lazy_import('sage.sandpiles.sandpile', 'sandlib', deprecation=(18618,'sandlib() will soon be removed. Use sandpile() instead.')) -lazy_import('sage.sandpiles.sandpile', 'grid_sandpile', deprecation=(18618,'grid_sandpile() will soon be removed. Use sandpile.Grid() instead.')) -lazy_import('sage.sandpiles.sandpile', 'complete_sandpile', deprecation=(18618,'complete_sandpile() will soon be removed. Use sandpile.Complete() instead.')) -lazy_import('sage.sandpiles.sandpile', 'firing_vector', deprecation=(18618,'firing_vector() will soon be removed. Use SandpileDivisor.is_linearly_equivalent() instead.')) - -lazy_import('sage.sandpiles.sandpile', ['admissible_partitions','partition_sandpile','min_cycles','glue_graphs','aztec_sandpile','triangle_sandpile'], deprecation=18618) diff --git a/src/sage/sandpiles/examples.py b/src/sage/sandpiles/examples.py index 3e8c158898b..eb38b711700 100644 --- a/src/sage/sandpiles/examples.py +++ b/src/sage/sandpiles/examples.py @@ -65,11 +65,11 @@ def __call__(self): EXAMPLES:: sage: sandpiles() - Try sandpile.FOO() where FOO is in the list: + Try sandpiles.FOO() where FOO is in the list: Complete, Cycle, Diamond, Fan, Grid, House, Wheel """ - print('Try sandpile.FOO() where FOO is in the list:\n') + print('Try sandpiles.FOO() where FOO is in the list:\n') print(" " + ", ".join([str(i) for i in dir(sandpiles) if i[0] != '_'])) diff --git a/src/sage/sandpiles/sandpile.py b/src/sage/sandpiles/sandpile.py index ea1762268a2..d49850317cf 100644 --- a/src/sage/sandpiles/sandpile.py +++ b/src/sage/sandpiles/sandpile.py @@ -28,7 +28,7 @@ DEPRECATED -SandpileDivisor.linear_system, SandpileDivisor.r_of_D, sandlib method, complete_sandpile, grid_sandpile, triangle_sandpile, aztec_sandpile, random_digraph, random_tree, glue_graphs, admissible_partitions, firing_vector, min_cycles. +SandpileDivisor.linear_system, SandpileDivisor.r_of_D. MINOR CHANGES @@ -6203,10 +6203,8 @@ def sandlib(selector=None): EXAMPLES:: + sage: from sage.sandpiles.sandpile import sandlib sage: sandlib() - doctest:...: DeprecationWarning: sandlib() will soon be removed. Use sandpile() instead. - See http://trac.sagemath.org/18618 for details. - Sandpiles in the sandlib: kite : generic undirected graphs with 5 vertices generic : generic digraph with 6 vertices @@ -6302,10 +6300,8 @@ def triangle_sandpile(n): EXAMPLES:: + sage: from sage.sandpiles.sandpile import triangle_sandpile sage: T = triangle_sandpile(5) - doctest:...: DeprecationWarning: - Importing triangle_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. sage: T.group_order() 135418115000 """ @@ -6349,10 +6345,8 @@ def aztec_sandpile(n): EXAMPLES:: + sage: from sage.sandpiles.sandpile import aztec_sandpile sage: aztec_sandpile(2) - doctest:...: DeprecationWarning: - Importing aztec_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. {'sink': {(-3/2, -1/2): 2, (-3/2, 1/2): 2, (-1/2, -3/2): 2, @@ -6415,60 +6409,6 @@ def aztec_sandpile(n): aztec_sandpile['sink'][vert] = out_degree return aztec_sandpile -def random_digraph(num_verts, p=0.5, directed=True, weight_max=1): - """ - A random weighted digraph with a directed spanning tree rooted at `0`. If - ``directed = False``, the only difference is that if `(i,j,w)` is an edge with - tail `i`, head `j`, and weight `w`, then `(j,i,w)` appears also. The result - is returned as a Sage digraph. - - INPUT: - - - ``num_verts`` -- number of vertices - - - ``p`` -- (default: 0.5) probability edges occur - - - ``directed`` -- (default: ``True``) if directed - - - ``weight_max`` -- (default: 1) integer maximum for random weights - - OUTPUT: - - random graph - - EXAMPLES:: - - sage: g = random_digraph(6,0.2,True,3) - doctest:...: DeprecationWarning: random_digraph will be removed soon. Use any of the Random* methods - from graphs() and from digraphs() instead. - See http://trac.sagemath.org/18618 for details. - sage: S = Sandpile(g,0) - sage: S.show(edge_labels = True) - - TESTS: - - Check that we can construct a random digraph with the - default arguments (:trac:`12181`):: - - sage: random_digraph(5) - Digraph on 5 vertices - """ - deprecation(18618,'random_digraph will be removed soon. Use any of the Random* methods from graphs() and from digraphs() instead.') - a = digraphs.RandomDirectedGN(num_verts) - b = graphs.RandomGNP(num_verts,p) - a.add_edges(b.edges()) - if directed: - c = graphs.RandomGNP(num_verts,p) - # reverse the edges of c and add them in - a.add_edges([(j,i,None) for i,j,k in c.edges()]) - else: - a.add_edges([(j,i,None) for i,j,k in a.edges()]) - a.add_edges([(j,i,None) for i,j,k in b.edges()]) - # now handle the weights - for i,j,k in a.edge_iterator(): - a.set_edge_label(i,j,ZZ.random_element(weight_max)+1) - return a - def random_DAG(num_verts, p=0.5, weight_max=1): r""" A random directed acyclic graph with ``num_verts`` vertices. @@ -6547,16 +6487,12 @@ def glue_graphs(g, h, glue_g, glue_h): EXAMPLES:: + sage: from sage.sandpiles.sandpile import glue_graphs sage: x = {0: {}, 1: {0: 1}, 2: {0: 1, 1: 1}, 3: {0: 1, 1: 1, 2: 1}} sage: y = {0: {}, 1: {0: 2}, 2: {1: 2}, 3: {0: 1, 2: 1}} sage: glue_x = {1: 1, 3: 2} sage: glue_y = {0: 1, 1: 2, 3: 1} - sage: z = glue_graphs(x,y,glue_x,glue_y) - doctest:...: DeprecationWarning: - Importing glue_graphs from here is deprecated. If you need to use it, - please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. - sage: z + sage: z = glue_graphs(x,y,glue_x,glue_y); z {0: {}, 'x0': {0: 1, 'x1': 1, 'x3': 2, 'y1': 2, 'y3': 1}, 'x1': {'x0': 1}, @@ -6709,11 +6645,10 @@ def admissible_partitions(S, k): EXAMPLES:: + sage: from sage.sandpiles.sandpile import admissible_partitions + sage: from sage.sandpiles.sandpile import partition_sandpile sage: S = sandpiles.Cycle(4) sage: P = [admissible_partitions(S, i) for i in [2,3,4]] - doctest:...: DeprecationWarning: - Importing admissible_partitions from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. sage: P [[{{0}, {1, 2, 3}}, {{0, 2, 3}, {1}}, @@ -6728,9 +6663,6 @@ def admissible_partitions(S, k): [{{0}, {1}, {2}, {3}}]] sage: for p in P: ....: sum([partition_sandpile(S, i).betti(verbose=False)[-1] for i in p]) - doctest:...: DeprecationWarning: - Importing partition_sandpile from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. 6 8 3 @@ -6771,6 +6703,7 @@ def partition_sandpile(S, p): EXAMPLES:: + sage: from sage.sandpiles.sandpile import admissible_partitions, partition_sandpile sage: S = sandpiles.Cycle(4) sage: P = [admissible_partitions(S, i) for i in [2,3,4]] sage: for p in P: @@ -6799,50 +6732,6 @@ def partition_sandpile(S, p): if S.sink() in i: return Sandpile(g,i) -def firing_vector(S, D, E): - r""" - If `D` and `E` are linearly equivalent divisors, find the firing vector - taking `D` to `E`. - - INPUT: - - - ``S`` -- Sandpile - - - ``D``, ``E`` -- tuples (representing linearly equivalent divisors) - - OUTPUT: - - tuple (representing a firing vector from ``D`` to ``E``) - - EXAMPLES:: - - sage: S = sandpiles.Complete(4) - sage: D = SandpileDivisor(S, {0: 0, 1: 0, 2: 8, 3: 0}) - sage: E = SandpileDivisor(S, {0: 2, 1: 2, 2: 2, 3: 2}) - sage: v = firing_vector(S, D, E) - doctest:...: DeprecationWarning: firing_vector() will soon be removed. Use SandpileDivisor.is_linearly_equivalent() instead. - See http://trac.sagemath.org/18618 for details. - doctest:...: DeprecationWarning: May 25, 2015: Replaced by SandpileDivisor.is_linearly_equivalent. - See http://trac.sagemath.org/18618 for details. - sage: v - (0, 0, 2, 0) - - The divisors must be linearly equivalent:: - - sage: vector(D.values()) - S.laplacian()*vector(v) == vector(E.values()) - True - sage: firing_vector(S, D, S.zero_div()) - Error. Are the divisors linearly equivalent? - """ - deprecation(18618,'May 25, 2015: Replaced by SandpileDivisor.is_linearly_equivalent.') - try: - v = vector(D.values()) - w = vector(E.values()) - return tuple(S.laplacian().solve_left(v-w)) - except ValueError: - print("Error. Are the divisors linearly equivalent?") - return - def min_cycles(G, v): r""" Minimal length cycles in the digraph `G` starting at vertex `v`. @@ -6859,11 +6748,9 @@ def min_cycles(G, v): EXAMPLES:: + sage: from sage.sandpiles.sandpile import min_cycles, sandlib sage: T = sandlib('gor') sage: [min_cycles(T, i) for i in T.vertices()] - doctest:...: DeprecationWarning: - Importing min_cycles from here is deprecated. If you need to use it, please import it directly from sage.sandpiles.sandpile - See http://trac.sagemath.org/18618 for details. [[], [[1, 3]], [[2, 3, 1], [2, 3]], [[3, 1], [3, 2]]] """ pr = G.neighbors_in(v) diff --git a/src/sage/sat/converters/polybori.py b/src/sage/sat/converters/polybori.py index 48ec81e26c3..2c38f0e9363 100644 --- a/src/sage/sat/converters/polybori.py +++ b/src/sage/sat/converters/polybori.py @@ -613,6 +613,6 @@ def product(l): product = self.ring(1) for v in c: if phi[abs(v)] is None: - raise ValueError("Clause containst an XOR glueing variable.") + raise ValueError("clause contains an XOR glueing variable") product *= phi[abs(v)] + int(v>0) return product diff --git a/src/sage/schemes/affine/affine_morphism.py b/src/sage/schemes/affine/affine_morphism.py index 611b274b729..de697c1e3d7 100644 --- a/src/sage/schemes/affine/affine_morphism.py +++ b/src/sage/schemes/affine/affine_morphism.py @@ -928,7 +928,7 @@ def global_height(self, prec=None): def jacobian (self): r""" - Returns the Jacobian matrix of partial derivitive of this map. + Return the Jacobian matrix of partial derivative of this map. The `(i, j)` entry of the Jacobian matrix is the partial derivative `diff(functions[i], variables[j])`. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d75a58c1ed5..09efd68e204 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -238,7 +238,7 @@ def followstrand(f, x0, x1, y0a, prec=53): - ``x0`` -- a complex value, where the homotopy starts - ``x1`` -- a complex value, where the homotopy ends - ``y0a`` -- an approximate solution of the polynomial `F(y) = f(x_0, y)` - - ``prec`` -- the precission to use + - ``prec`` -- the precision to use OUTPUT: diff --git a/src/sage/schemes/elliptic_curves/cm.py b/src/sage/schemes/elliptic_curves/cm.py index dfbbaaf80e0..5606801aa05 100644 --- a/src/sage/schemes/elliptic_curves/cm.py +++ b/src/sage/schemes/elliptic_curves/cm.py @@ -545,7 +545,7 @@ def lb(f): @cached_function def is_cm_j_invariant(j): """ - Returns whether or not this is a CM `j`-invariant. + Return whether or not this is a CM `j`-invariant. INPUT: @@ -555,7 +555,7 @@ def is_cm_j_invariant(j): A pair (bool, (d,f)) which is either (False, None) if `j` is not a CM j-invariant or (True, (d,f)) if `j` is the `j`-invariant of the - immaginary quadratic order of discriminant `D=df^2` where `d` is + imaginary quadratic order of discriminant `D=df^2` where `d` is the associated fundamental discriminant and `f` the index. .. note:: @@ -568,7 +568,6 @@ def is_cm_j_invariant(j): primes dividing the discriminant of the minimal polynomial of `j`. - EXAMPLES:: sage: from sage.schemes.elliptic_curves.cm import is_cm_j_invariant diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index ea2fea8a411..3104d20c2da 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1717,7 +1717,7 @@ def is_supersingular(self, proof=True): - ``proof`` (boolean, default True) -- If True, returns a proved result. If False, then a return value of False is certain but a return value of True may be based on a - probabilistic test. See the documentaion of the function + probabilistic test. See the documentation of the function :meth:`is_j_supersingular` for more details. EXAMPLES:: @@ -1752,7 +1752,7 @@ def is_ordinary(self, proof=True): - ``proof`` (boolean, default True) -- If True, returns a proved result. If False, then a return value of True is certain but a return value of False may be based on a - probabilistic test. See the documentaion of the function + probabilistic test. See the documentation of the function :meth:`is_j_supersingular` for more details. EXAMPLES:: @@ -1923,7 +1923,7 @@ def supersingular_j_polynomial(p): First compute H(X) whose roots are the Legendre `\lambda`-invariants of supersingular curves (Silverman V.4.1(b)) - in charactersitic `p`. Then, using a resultant computation with + in characteristic `p`. Then, using a resultant computation with the polynomial relating `\lambda` and `j` (Silverman III.1.7(b)), we recover the polynomial (in variable ``j``) whose roots are the `j`-invariants. Factors of `j` and `j-1728` are removed if diff --git a/src/sage/schemes/elliptic_curves/ell_tate_curve.py b/src/sage/schemes/elliptic_curves/ell_tate_curve.py index 5e93556d066..c0b8d134c4f 100644 --- a/src/sage/schemes/elliptic_curves/ell_tate_curve.py +++ b/src/sage/schemes/elliptic_curves/ell_tate_curve.py @@ -631,14 +631,14 @@ def padic_regulator(self, prec=20): INPUT: - - ``prec`` - the `p`-adic precision, default is 20. + - ``prec`` -- the `p`-adic precision, default is 20. REFERENCES: [MTT]_ .. [Wer] Annette Werner, Local heights on abelian varieties and - rigid analytic unifomization, Doc. Math. 3 (1998), 301-319. + rigid analytic uniformization, Doc. Math. 3 (1998), 301-319. [SW]_ diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 19ea18fe000..cc919cf49dd 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -2759,9 +2759,11 @@ def reduced_quadratic_form(self): @cached_method def tau(self): """ - Return an element tau in the upper half plane that corresponds - to this particular Heegner point (actually, tau is in the - quadratic imagqinary field K associated to this Heegner point). + Return an element ``tau`` in the upper half plane that corresponds + to this particular Heegner point. + + Actually, ``tau`` is in the quadratic imaginary field K associated + to this Heegner point. EXAMPLES:: @@ -2773,10 +2775,9 @@ def tau(self): 37*x^2 + 11*x*y + 2*y^2 """ K = self.quadratic_field() - c = self.conductor() - d = K.gen()*c - A,B,_ = self.__f - return (-B + d)/(2*A) + d = K.gen() * self.conductor() + A, B, _ = self.__f + return (-B + d) / (2 * A) def map_to_curve(self, E): """ @@ -2862,6 +2863,7 @@ def plot(self, **kwds): from sage.plot.all import point return point(CDF(self.tau()), **kwds) + class HeegnerPointOnEllipticCurve(HeegnerPoint): """ A Heegner point on a curve associated to an order in a quadratic @@ -4678,11 +4680,11 @@ def optimal_embeddings(self, D, c, R): """ INPUT: - - `D` -- negative fundamental disriminant + - `D` -- negative fundamental discriminant - - `c` -- integer coprime + - `c` -- integer coprime - - `R` -- Eichler order + - `R` -- Eichler order EXAMPLES:: diff --git a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py index 5f749daef94..cbfb3088528 100644 --- a/src/sage/schemes/elliptic_curves/isogeny_small_degree.py +++ b/src/sage/schemes/elliptic_curves/isogeny_small_degree.py @@ -307,7 +307,7 @@ def isogenies_prime_degree_genus_0(E, l=None): t_list = sorted((f-j*t).roots(multiplicities=False)) # The generic kernel polynomial applies to a standard curve # E_t with the correct j-invariant; we must compute the - # appropriate twising factor to scale X by: + # appropriate twisting factor to scale X by: c4, c6 = E.c_invariants() T = c4/(3*c6) jt = Fricke_module(l) diff --git a/src/sage/schemes/elliptic_curves/padic_lseries.py b/src/sage/schemes/elliptic_curves/padic_lseries.py index 1c69e619209..0470a493667 100644 --- a/src/sage/schemes/elliptic_curves/padic_lseries.py +++ b/src/sage/schemes/elliptic_curves/padic_lseries.py @@ -544,8 +544,8 @@ def order_of_vanishing(self): Swinnerton-Dyer, Inventiones mathematicae 84, (1986), 1-48. - [Ka] Kayuza Kato, `p`-adic Hodge theory and values of zeta functions of modular - forms, Cohomologies `p`-adiques et applications arithmetiques III, - Asterisque vol 295, SMF, Paris, 2004. + forms, Cohomologies `p`-adiques et applications arithmétiques III, + Astérisque vol 295, SMF, Paris, 2004. EXAMPLES:: @@ -1120,7 +1120,8 @@ def series(self, n=3, quadratic_twist = +1, prec=5, eta = 0): ALIAS: power_series is identical to series. EXAMPLES: - A superingular example, where we must compute to higher precision to see anything:: + + A supersingular example, where we must compute to higher precision to see anything:: sage: e = EllipticCurve('37a') sage: L = e.padic_lseries(3); L @@ -1399,19 +1400,19 @@ def Dp_valued_series(self, n=3, quadratic_twist = +1, prec=5): @rename_keyword(deprecation=6094, method="algorithm") def frobenius(self, prec=20, algorithm = "mw"): r""" - This returns a geometric Frobenius `\varphi` on the Diedonne module `D_p(E)` + Return a geometric Frobenius `\varphi` on the Dieudonné module `D_p(E)` with respect to the basis `\omega`, the invariant differential, and `\eta=x\omega`. + It satisfies `\varphi^2 - a_p/p\, \varphi + 1/p = 0`. INPUT: - ``prec`` - (default: 20) a positive integer - - ``algorithm`` - either 'mw' (default) for Monsky-Washintzer + - ``algorithm`` - either 'mw' (default) for Monsky-Washnitzer or 'approx' for the algorithm described by Bernardi and Perrin-Riou (much slower and not fully tested) - EXAMPLES:: sage: E = EllipticCurve('14a') diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index e51fc13758a..eb6ae832954 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1051,7 +1051,7 @@ def ei(self): sage: abs(x1.real())+abs(x2.real())<1e-14 True sage: x1.imag(),x2.imag(),x3 - (-1.122462048309373?, 1.122462048309373?, -1) + (-1.122462048309373?, 1.122462048309373?, -1.000000000000000?) :: diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index 6e3540ae01a..7b21af4c0d9 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -914,7 +914,7 @@ def __call__(self, *args): sage: P = S(ZZ.ideal(3)); P Point on Spectrum of Integer Ring defined by the Principal ideal (3) of Integer Ring sage: type(P) - + sage: S(ZZ.ideal(next_prime(1000000))) Point on Spectrum of Integer Ring defined by the Principal ideal (1000003) of Integer Ring diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py index 858277e95be..0ae864851da 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_finite_field.py @@ -406,7 +406,7 @@ def frobenius_polynomial_matrix(self, M=None, algorithm='hypellfrob'): q = K.cardinality() g = self.genus() N = self._frobenius_coefficient_bound_charpoly() - # compute chapoly over ZZ and then reduce back + # compute charpoly over ZZ and then reduce back # (because charpoly of p-adic matrices sometimes loses precision) M = self.frobenius_matrix(N=N, algorithm=algorithm).change_ring(ZZ) diff --git a/src/sage/schemes/plane_conics/con_field.py b/src/sage/schemes/plane_conics/con_field.py index 642752e1d2c..6ad8f200f54 100644 --- a/src/sage/schemes/plane_conics/con_field.py +++ b/src/sage/schemes/plane_conics/con_field.py @@ -498,7 +498,7 @@ def has_rational_point(self, point = False, pass # The second attempt tries to split Magma elements into - # numerators and denominators first. This is neccessary + # numerators and denominators first. This is necessary # for the field of rational functions, because (at the moment of # writing) fraction field elements are not converted automatically # from Magma to Sage. diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 2b78e621507..4bf08108a7d 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -939,7 +939,7 @@ def dynatomic_polynomial(self, period): sage: f.dynatomic_polynomial(-1) Traceback (most recent call last): ... - TypeError: period must be a postive integer + TypeError: period must be a positive integer :: @@ -984,7 +984,7 @@ def dynatomic_polynomial(self, period): m = period[0] n = int(period[1]) if n < 0: - raise TypeError("period must be a postive integer") + raise TypeError("period must be a positive integer") if n == 0: return self[0].parent().zero() if m == 0 and n == 1: @@ -3597,7 +3597,7 @@ def multiplier_spectra(self, n, formal=True, embedding=None): 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-49*I, + 5.931851652578137? + 0.?e-47*I, 0.0681483474218635? - 1.930649271699173?*I, 0.0681483474218635? + 1.930649271699173?*I] diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index a6a6805cb33..d25b57622c5 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -568,7 +568,8 @@ def _linear_system_as_kernel(self, d, pt, m): [0] [0] - If the multiplcity ``m`` is 0, then the a matrix with zero rows is returned:: + If the multiplicity `m` is 0, then the a matrix with zero rows + is returned:: sage: P = ProjectiveSpace(GF(5), 2, names='x') sage: pt = P([1, 1, 1]) diff --git a/src/sage/schemes/toric/points.py b/src/sage/schemes/toric/points.py index a968d3b33f7..6d83d0af779 100644 --- a/src/sage/schemes/toric/points.py +++ b/src/sage/schemes/toric/points.py @@ -2,7 +2,7 @@ """ Enumerate Points of a Toric Variety -The classes here are not meant to be instatiated manually. Instead, +The classes here are not meant to be instantiated manually. Instead, you should always use the methods of the :class:`point set ` of the variety. diff --git a/src/sage/sets/primes.py b/src/sage/sets/primes.py index 08039c8b343..8681c615e24 100644 --- a/src/sage/sets/primes.py +++ b/src/sage/sets/primes.py @@ -54,7 +54,6 @@ class Primes(Set_generic, UniqueRepresentation): ... NotImplementedError: infinite set """ - @staticmethod def __classcall__(cls, proof=True): """ @@ -92,26 +91,19 @@ def __init__(self, proof): sage: P = Primes() sage: R = Primes() - sage: cmp(P,R) - 0 sage: P == R True sage: P != R False - sage: Q=[1,2,3] + sage: Q = [1,2,3] sage: Q != P # indirect doctest True - sage: R.=ZZ[] - sage: P!=x^2+x - True - - Make sure changing order changes the comparison with something - of a different type:: - - sage: cmp('foo', Primes()) != cmp(Primes(), 'foo') + sage: R. = ZZ[] + sage: P != x^2+x True """ - super(Primes, self).__init__(facade = ZZ, category = InfiniteEnumeratedSets()) + super(Primes, self).__init__(facade=ZZ, + category=InfiniteEnumeratedSets()) self.__proof = proof def _repr_(self): @@ -142,7 +134,7 @@ def __contains__(self, x): False """ try: - if not x in ZZ: + if x not in ZZ: return False return ZZ(x).is_prime() except TypeError: diff --git a/src/sage/stats/basic_stats.py b/src/sage/stats/basic_stats.py index 7bfd64f195b..3c532a62c75 100644 --- a/src/sage/stats/basic_stats.py +++ b/src/sage/stats/basic_stats.py @@ -84,22 +84,27 @@ def mean(v): return s/ZZ(len(v)) return s/len(v) + def mode(v): """ - Return the mode of `v`. The mode is the sorted list of the most - frequently occuring elements in `v`. If `n` is the most times - that any element occurs in `v`, then the mode is the sorted list - of elements of `v` that occur `n` times. + Return the mode of `v`. + + The mode is the list of the most frequently occuring + elements in `v`. If `n` is the most times that any element occurs + in `v`, then the mode is the list of elements of `v` that + occur `n` times. The list is sorted if possible. + + .. NOTE:: - NOTE: The elements of `v` must be hashable and comparable. + The elements of `v` must be hashable. INPUT: - - `v` -- a list + - `v` -- a list OUTPUT: - - a list + - a list (sorted if possible) EXAMPLES:: @@ -110,20 +115,28 @@ def mode(v): 3 sage: mode([]) [] + sage: mode([1,2,3,4,5]) [1, 2, 3, 4, 5] sage: mode([3,1,2,1,2,3]) [1, 2, 3] - sage: mode(['sage', 4, I, 3/5, 'sage', pi]) + sage: mode([0, 2, 7, 7, 13, 20, 2, 13]) + [2, 7, 13] + + sage: mode(['sage', 'four', 'I', 'three', 'sage', 'pi']) ['sage'] + sage: class MyClass: ....: def mode(self): ....: return [1] sage: stats.mode(MyClass()) [1] """ - if hasattr(v, 'mode'): return v.mode() - from operator import itemgetter + if hasattr(v, 'mode'): + return v.mode() + + if not v: + return v freq = {} for i in v: @@ -132,8 +145,12 @@ def mode(v): else: freq[i] = 1 - s = sorted(freq.items(), key=itemgetter(1), reverse=True) - return [i[0] for i in s if i[1]==s[0][1]] + n = max(freq.values()) + try: + return sorted(u for u, f in freq.items() if f == n) + except TypeError: + return [u for u, f in freq.items() if f == n] + def std(v, bias=False): """ diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 2e88998f2f9..3cf0012efb8 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -1881,9 +1881,13 @@ cdef class CoercionModel_cache_maps(CoercionModel): return res # If types are not equal: compare types - cdef int c = cmp(type(x), type(y)) - if c: - return rich_to_bool(op, c) + # avoiding call to cmp() for compatibility with python3 + cdef type tx = type(x) + cdef type ty = type(y) + if tx < ty: + return rich_to_bool(op, -1) + elif tx > ty: + return rich_to_bool(op, 1) # Final attempt: compare by id() if (x) >= (y): diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index 9355df10e45..81d06d2428c 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -1054,7 +1054,9 @@ cdef class Element(SageObject): if not isinstance(left, Element): assert type(left) is type(right) - return cmp(left, right) + raise NotImplementedError("old-style comparisons are not " + "supported anymore (see " + "https://trac.sagemath.org/ticket/22981)") # Now we have two Sage Elements with the same parent try: diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index d7d734c52a6..4f7de4deb79 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -130,21 +130,27 @@ AUTHORS: - Florent Hivert (2010-03): initial revision """ + #***************************************************************************** -# Copyright (C) 2009-2010 Florent Hivert +# Copyright (C) 2009-2010 Florent Hivert # -# 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 __future__ import print_function -include "sage/ext/stdsage.pxi" -include "cysignals/memory.pxi" +from __future__ import absolute_import, print_function + from cpython.list cimport * from cpython.int cimport * from cpython.ref cimport * +from cysignals.memory cimport check_reallocarray, sig_free + import sage +from sage.ext.stdsage cimport HAS_DICTIONARY from sage.structure.element cimport Element from sage.structure.parent cimport Parent from sage.structure.sage_object cimport richcmp @@ -1311,10 +1317,9 @@ cdef class ClonableIntArray(ClonableElement): self._len = size def __dealloc__(self): - if self._list is not NULL: - sig_free(self._list) - self._len = -1 - self._list = NULL + sig_free(self._list) + self._len = -1 + self._list = NULL def _repr_(self): """ diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 502818a2c93..a7ec72d0642 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -565,27 +565,30 @@ cdef class Parent(category_object.CategoryObject): - This should lookup for Element classes in all super classes """ try: #if hasattr(self, 'Element'): - return self.__make_element_class__(self.Element, "%s.element_class"%self.__class__.__name__) + return self.__make_element_class__(self.Element, + name="%s.element_class"%self.__class__.__name__, + module=self.__class__.__module__) except AttributeError: #else: return NotImplemented - def __make_element_class__(self, cls, name = None, inherit = None): + def __make_element_class__(self, cls, name = None, module=None, inherit = None): """ A utility to construct classes for the elements of this parent, with appropriate inheritance from the element class of the category (only for pure python types so far). """ - if name is None: - name = "%s_with_category"%cls.__name__ # By default, don't fiddle with extension types yet; inheritance from # categories will probably be achieved in a different way if inherit is None: inherit = not is_extension_type(cls) if inherit: - return dynamic_class(name, (cls, self._abstract_element_class)) - else: - return cls + if name is None: + name = "%s_with_category"%cls.__name__ + cls = dynamic_class(name, (cls, self._abstract_element_class)) + if module is not None: + cls.__module__ = module + return cls def _set_element_constructor(self): """ diff --git a/src/sage/structure/parent_base.pyx b/src/sage/structure/parent_base.pyx index 7a7543bd687..c5a3cbd7a35 100644 --- a/src/sage/structure/parent_base.pyx +++ b/src/sage/structure/parent_base.pyx @@ -1,16 +1,18 @@ r""" Base class for old-style parent objects with a base ring """ -############################################################################### -# 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/ -############################################################################### -from __future__ import absolute_import +#***************************************************************************** -include "sage/ext/stdsage.pxi" +from __future__ import absolute_import cimport sage.structure.parent as parent @@ -21,31 +23,6 @@ cdef inline check_old_coerce(parent.Parent p): raise RuntimeError("%s still using old coercion framework" % p) -# TODO: Unpickled parents with base sometimes have their base set to None. -# This causes a segfault in the module arithmetic architecture. -# -# sage: H = HomsetWithBase(QQ, RR, base=ZZ); H -# sage: H0 = loads(dumps(H)) -# sage: H.base_ring(), H0.base_ring() -# (Integer Ring, None) -# -# Perhaps the code below would help (why was it commented out?). - -## def make_parent_with_base_v0(_class, _dict, base, has_coerce_map_from): -## """ -## This should work for any Python class deriving from this, as long -## as it doesn't implement some screwy __new__() method. -## """ -## new_object = _class.__new__(_class) -## if base is None: -## (new_object)._base = new_object -## else: -## (new_object)._base = base -## (new_object)._has_coerce_map_from = has_coerce_map_from -## if not _dict is None: -## new_object.__dict__ = _dict -## return new_object - def is_ParentWithBase(x): """ Return True if x is a parent object with base. @@ -57,10 +34,6 @@ cdef class ParentWithBase(Parent_old): This class is being deprecated, see parent.Parent for the new model. """ def __init__(self, base, coerce_from=[], actions=[], embeddings=[], category=None): - # TODO: SymbolicExpressionRing has base RR, which makes this bad -# print type(self), "base", base, coerce_from -# if base != self and not base in coerce_from: -# coerce_from.append(base) Parent_old.__init__(self, coerce_from=coerce_from, actions=actions, embeddings=embeddings, category=category) self._base = base @@ -71,16 +44,6 @@ cdef class ParentWithBase(Parent_old): else: raise TypeError("No canonical coercion found.") -## def x__reduce__(self): -## if HAS_DICTIONARY(self): -## _dict = self.__dict__ -## else: -## _dict = None -## if self._base is self: -## return (make_parent_with_base_v0, (self.__class__, _dict, None, self._has_coerce_map_from)) -## else: -## return (make_parent_with_base_v0, (self.__class__, _dict, self._base, self._has_coerce_map_from)) - # Derived class *must* define base_extend. def base_extend(self, X): check_old_coerce(self) diff --git a/src/sage/structure/parent_gens.pyx b/src/sage/structure/parent_gens.pyx index 08bf0fc9387..f5c305e3f16 100644 --- a/src/sage/structure/parent_gens.pyx +++ b/src/sage/structure/parent_gens.pyx @@ -67,10 +67,8 @@ This example illustrates generators for a free module over `\ZZ`. # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** -from __future__ import absolute_import -from __future__ import print_function -include 'sage/ext/stdsage.pxi' +from __future__ import absolute_import, print_function import sage.misc.defaults from sage.misc.latex import latex_variable_name @@ -104,22 +102,6 @@ cdef class ParentWithGens(ParentWithBase): self._assign_names(names=names, normalize=normalize) ParentWithBase.__init__(self, base, category=category) - #if category is not None: - # self._init_category_(category) - -## def x__reduce__(self): -## if self._base is self: -## base = None -## else: -## base = self._base -## if HAS_DICTIONARY(self): -## _dict = self.__dict__ -## else: -## _dict = None -## return (make_parent_gens_v0, (self.__class__, -## _dict, base, -## self._has_coerce_map_from, -## self._names)) # Derived class *must* define ngens method. def ngens(self): diff --git a/src/sage/structure/parent_old.pyx b/src/sage/structure/parent_old.pyx index ca6325bdef6..de671181b47 100644 --- a/src/sage/structure/parent_old.pyx +++ b/src/sage/structure/parent_old.pyx @@ -18,43 +18,32 @@ This came up in some subtle bug once. 5 """ -############################################################################### -# 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/ -############################################################################### -from __future__ import absolute_import -from __future__ import print_function +#***************************************************************************** +from __future__ import absolute_import, print_function cimport sage.structure.sage_object as sage_object import operator from .parent import Set_PythonType, Set_PythonType_class from .coerce import py_scalar_parent +from sage.ext.stdsage cimport HAS_DICTIONARY from sage.structure.coerce_dict import MonoDict, TripleDict from cpython.object cimport * from cpython.bool cimport * -include 'sage/ext/stdsage.pxi' cdef inline check_old_coerce(Parent p): if p._element_constructor is not None: raise RuntimeError("%s still using old coercion framework" % p) -## def make_parent_v0(_class, _dict, has_coerce_map_from): -## """ -## This should work for any Python class deriving from this, as long -## as it doesn't implement some screwy __new__() method. -## """ -## cdef Parent new_object -## new_object = _class.__new__(_class) -## if not _dict is None: -## new_object.__dict__ = _dict -## new_object._has_coerce_map_from = has_coerce_map_from -## return new_object - cdef class Parent(parent.Parent): """ Parents are the SAGE/mathematical analogues of container objects diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 4205550777a..8517128d815 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -3737,11 +3737,11 @@ cdef class Expression(CommutativeRingElement): sage: None^pi Traceback (most recent call last): ... - TypeError: no canonical coercion from to Symbolic Ring + TypeError: no canonical coercion from <... 'NoneType'> to Symbolic Ring sage: sin(x)^None Traceback (most recent call last): ... - TypeError: no canonical coercion from to Symbolic Ring + TypeError: no canonical coercion from <... 'NoneType'> to Symbolic Ring Check that :trac:`18088` is fixed:: @@ -3822,7 +3822,28 @@ cdef class Expression(CommutativeRingElement): sage: f.derivative(2) x |--> 6*x - sin(x) - :: + Some expressions can't be cleanly differentiated by the + chain rule:: + + sage: _ = var('x', domain='real') + sage: _ = var('w z') + sage: (x^z).conjugate().diff(x) + conjugate(x^(z - 1))*conjugate(z) + sage: (w^z).conjugate().diff(w) + w^(z - 1)*z*D[0](conjugate)(w^z) + sage: atanh(x).real_part().diff(x) + -1/(x^2 - 1) + sage: atanh(x).imag_part().diff(x) + 0 + sage: atanh(w).real_part().diff(w) + -D[0](real_part)(arctanh(w))/(w^2 - 1) + sage: atanh(w).imag_part().diff(w) + -D[0](imag_part)(arctanh(w))/(w^2 - 1) + sage: abs(log(x)).diff(x) + 1/2*(conjugate(log(x))/x + log(x)/x)/abs(log(x)) + sage: abs(log(z)).diff(z) + 1/2*(conjugate(log(z))/z + log(z)/conjugate(z))/abs(log(z)) + sage: forget() sage: t = sin(x+y^2)*tan(x*y) sage: t.derivative(x) @@ -4174,6 +4195,15 @@ cdef class Expression(CommutativeRingElement): sage: zeta(s).residue(s == 1) 1 + We can also compute the residue at more general places, + given that the pole is recognized:: + + sage: k = var('k', domain='integer') + sage: (gamma(1+x)/(1 - exp(-x))).residue(x==2*I*pi*k) + gamma(2*I*pi*k + 1) + sage: csc(x).residue(x==2*pi*k) + 1 + TESTS:: sage: (exp(x)/sin(x)^4).residue(x == 0) @@ -4183,6 +4213,11 @@ cdef class Expression(CommutativeRingElement): sage: (1/(x^2 - x - 1)).residue(x == 1/2*sqrt(5) + 1/2) 1/5*sqrt(5) + + Check that :trac:`20084` is fixed:: + + sage: (1/(1 - 2^-x)).residue(x == 2*pi*I/log(2)) + 1/log(2) """ if symbol.is_relational(): x = symbol.lhs() @@ -4192,7 +4227,7 @@ cdef class Expression(CommutativeRingElement): a = 0 if a == infinity: return (-self.subs({x: 1/x}) / x**2).residue(x == 0) - return self.subs({x: x+a}).series(x == 0, 0).coefficient(x, -1) + return self.subs({x: x + a}).series(x == 0, 0).coefficient(x, -1) def taylor(self, *args): r""" @@ -6397,7 +6432,7 @@ cdef class Expression(CommutativeRingElement): def laurent_polynomial(self, base_ring=None, ring=None): r""" - Return this symbolic expression as an laurent polynomial + Return this symbolic expression as a Laurent polynomial over the given base ring, if possible. INPUT: @@ -6407,7 +6442,7 @@ cdef class Expression(CommutativeRingElement): - ``ring`` - (optional) the parent for the polynomial You can specify either the base ring (``base_ring``) you want - the output laurent polynomial to be over, or you can specify the full + the output Laurent polynomial to be over, or you can specify the full laurent polynomial ring (``ring``) you want the output laurent polynomial to be an element of. @@ -6455,7 +6490,7 @@ cdef class Expression(CommutativeRingElement): sage: a x^3 + y + sqrt(2) sage: type(a) - + sage: a.degree() 0 @@ -9481,8 +9516,6 @@ cdef class Expression(CommutativeRingElement): else: return self - - def simplify_real(self): r""" Simplify the given expression over the real numbers. This allows @@ -9791,7 +9824,7 @@ cdef class Expression(CommutativeRingElement): TESTS: - Check that the problem with applying `full_simplify()` to gamma + Check that the problem with applying ``full_simplify()`` to gamma functions (:trac:`9240`) has been fixed:: sage: gamma(1/3) @@ -10384,6 +10417,104 @@ cdef class Expression(CommutativeRingElement): log_expand = expand_log + def distribute(self, recursive=True): + """ + Distribute some indexed operators over similar operators in + order to allow further groupings or simplifications. + + Implemented cases (so far) : + + - Symbolic sum of a sum ==> sum of symbolic sums + + - Integral (definite or not) of a sum ==> sum of integrals. + + - Symbolic product of a product ==> product of symbolic products. + + INPUT: + + - ``recursive`` -- (default : True) the distribution proceeds + along the subtrees of the expression. + + TESTS: + + sage: var("j,k,p,q", domain="integer") + (j, k, p, q) + sage: X,Y,Z,f,g=function("X,Y,Z,f,g") + sage: var("x,a,b") + (x, a, b) + sage: sum(X(j)+Y(j),j,1,p) + sum(X(j) + Y(j), j, 1, p) + sage: sum(X(j)+Y(j),j,1,p).distribute() + sum(X(j), j, 1, p) + sum(Y(j), j, 1, p) + sage: integrate(f(x)+g(x),x) + integrate(f(x) + g(x), x) + sage: integrate(f(x)+g(x),x).distribute() + integrate(f(x), x) + integrate(g(x), x) + sage: integrate(f(x)+g(x),x,a,b) + integrate(f(x) + g(x), x, a, b) + sage: integrate(f(x)+g(x),x,a,b).distribute() + integrate(f(x), x, a, b) + integrate(g(x), x, a, b) + sage: sum(X(j)+sum(Y(k)+Z(k),k,1,q),j,1,p) + sum(X(j) + sum(Y(k) + Z(k), k, 1, q), j, 1, p) + sage: sum(X(j)+sum(Y(k)+Z(k),k,1,q),j,1,p).distribute() + sum(sum(Y(k), k, 1, q) + sum(Z(k), k, 1, q), j, 1, p) + sum(X(j), j, 1, p) + sage: sum(X(j)+sum(Y(k)+Z(k),k,1,q),j,1,p).distribute(recursive=False) + sum(X(j), j, 1, p) + sum(sum(Y(k) + Z(k), k, 1, q), j, 1, p) + sage: maxima("product(X(j)*Y(j),j,1,p)").sage() + product(X(j)*Y(j), j, 1, p) + sage: maxima("product(X(j)*Y(j),j,1,p)").sage().distribute() + product(X(j), j, 1, p)*product(Y(j), j, 1, p) + + + AUTHORS: + + - Emmanuel Charpentier, Ralf Stephan (05-2017) + """ + from sage.functions.other import symbolic_sum as opsum, \ + symbolic_product as opprod + from sage.symbolic.integration.integral \ + import indefinite_integral as opii, definite_integral as opdi + from sage.symbolic.operators import add_vararg as opadd, \ + mul_vararg as opmul + from sage.all import prod + + def treat_term(op, term, args): + l = sage.all.copy(args) + l.insert(0, term) + return op(*l) + + if self.parent() is not sage.all.SR: + return self + + op = self.operator() + if op is None: + return self + + if op in {opsum, opdi, opii}: + sa = self.operands()[0].expand() + op1 = sa.operator() + if op1 is opadd: + la = self.operands()[1:] + aa = sa.operands() + if recursive: + return sum(treat_term(op, t.distribute(), la) for t in aa) + return sum(treat_term(op, t, la) for t in aa) + return self + if op is opprod: + sa = self.operands()[0].expand() + op1 = sa.operator() + if op1 is opmul: + la = self.operands()[1:] + aa = sa.operands() + if recursive: + return prod(treat_term(op, t.distribute(), la) for t in aa) + return prod(treat_term(op, t, la) for t in aa) + return self + + if recursive: + done = [t.distribute() for t in self.operands()] + return op(*done) + return self def factor(self, dontfactor=[]): """ @@ -12423,4 +12554,3 @@ cdef operators compatible_relation(operators lop, operators rop) except
(.*)

", page).groups()[0] try: - ans = SR(mexpr.lower().replace('[', '(').replace(']', ')')) + from sage.libs.pynac.pynac import symbol_table + from sage.interfaces.mathematica import _un_camel as un_camel + from sage.symbolic.constants import constants_name_table as constants + from sage.calculus.calculus import symbolic_expression_from_string + from sage.calculus.calculus import _find_func as find_func + + expr = mexpr.replace('\n',' ').replace('\r', '') + expr = expr.replace('[', '(').replace(']', ')') + expr = expr.replace('{', '[').replace('}', ']') + lsymbols = symbol_table['mathematica'].copy() + autotrans = [str.lower, # Try it in lower case + un_camel, # Convert `CamelCase` to `camel_case` + lambda x: x # Try the original name + ] + # Find the MMA funcs/vars/constants - they start with a letter. + # Exclude exponents (e.g. 'e8' from 4.e8) + p = re.compile('(? = AsymptoticRing(growth_group='x^ZZ * y^QQ * log(y)^ZZ', coefficient_ring=ZZ) - doctest:...: FutureWarning: This class/method/function is - marked as experimental. - ... - See http://trac.sagemath.org/17601 for details. sage: s = SR(3*x^5 * log(y) + 4*y^(3/7) + O(x*log(y))); s 3*x^5*log(y) + 4*y^(3/7) + Order(x*log(y)) sage: s.operator(), s.operands() diff --git a/src/sage/symbolic/units.py b/src/sage/symbolic/units.py index f1d862023e7..f2939fa7ec7 100644 --- a/src/sage/symbolic/units.py +++ b/src/sage/symbolic/units.py @@ -1044,10 +1044,10 @@ class Units(ExtraTabCompletion): """ A collection of units of some type. - EXAMPLES:: + EXAMPLES:: - sage: units.power - Collection of units of power: cheval_vapeur horsepower watt + sage: units.power + Collection of units of power: cheval_vapeur horsepower watt """ def __init__(self, data, name=''): """ @@ -1092,9 +1092,10 @@ def __setstate__(self, state): self.__data = state[1] self.__units = {} - def __cmp__(self, other): + def __eq__(self, other): """ - Compare two collections of units, or a collection of units with some other object. + Compare two collections of units, or a collection of units + with some other object. EXAMPLES:: @@ -1106,9 +1107,24 @@ def __cmp__(self, other): False """ if not isinstance(other, Units): - return cmp(type(self), type(other)) - return cmp((self.__name, self.__data), (other.__name, other.__data)) + return False + return (self.__name, self.__data) == (other.__name, other.__data) + + def __ne__(self, other): + """ + Test for unequality. + + EXAMPLES:: + sage: units.length != 5 + True + sage: units.length != units.length + False + sage: units.length != units.mass + True + """ + return not (self == other) + def _tab_completion(self): """ Return tab completions. diff --git a/src/sage/tensor/modules/comp.py b/src/sage/tensor/modules/comp.py index 4d65c37b556..602e3a71531 100644 --- a/src/sage/tensor/modules/comp.py +++ b/src/sage/tensor/modules/comp.py @@ -1948,7 +1948,7 @@ def trace(self, pos1, pos2): """ if self._nid < 2: - raise ValueError("contraction can be perfomed only on " + + raise ValueError("contraction can be performed only on " + "components with at least 2 indices") if pos1 < 0 or pos1 > self._nid - 1: raise IndexError("pos1 out of range") @@ -3726,7 +3726,7 @@ def trace(self, pos1, pos2): """ if self._nid < 2: - raise TypeError("contraction can be perfomed only on " + + raise TypeError("contraction can be performed only on " + "components with at least 2 indices") if pos1 < 0 or pos1 > self._nid - 1: raise IndexError("pos1 out of range") diff --git a/src/sage/tensor/modules/free_module_alt_form.py b/src/sage/tensor/modules/free_module_alt_form.py index 3de40ee5916..4d7ef5321b9 100644 --- a/src/sage/tensor/modules/free_module_alt_form.py +++ b/src/sage/tensor/modules/free_module_alt_form.py @@ -75,7 +75,7 @@ class FreeModuleAltForm(FreeModuleTensor): Alternating form a of degree 2 on the Rank-3 free module M over the Integer Ring sage: type(a) - + sage: a.parent() 2nd exterior power of the dual of the Rank-3 free module M over the Integer Ring sage: a[1,2], a[2,3] = 4, -3 diff --git a/src/sage/tensor/modules/free_module_linear_group.py b/src/sage/tensor/modules/free_module_linear_group.py index 3a0254ff69b..c3e5abe7e62 100644 --- a/src/sage/tensor/modules/free_module_linear_group.py +++ b/src/sage/tensor/modules/free_module_linear_group.py @@ -124,7 +124,7 @@ class FreeModuleLinearGroup(UniqueRepresentation, Parent): sage: a.display(e) e_0*e^0 - e_1*e^1 + e_2*e^2 sage: type(a) - + As for any group, the identity element is obtained by the method :meth:`one`:: diff --git a/src/sage/tensor/modules/free_module_morphism.py b/src/sage/tensor/modules/free_module_morphism.py index bfdd00d829a..115eb53b44d 100644 --- a/src/sage/tensor/modules/free_module_morphism.py +++ b/src/sage/tensor/modules/free_module_morphism.py @@ -118,7 +118,7 @@ class FiniteRankFreeModuleMorphism(Morphism): is a derived class of :class:`FiniteRankFreeModuleMorphism`:: sage: type(phi) - + sage: isinstance(phi, sage.tensor.modules.free_module_morphism.FiniteRankFreeModuleMorphism) True diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index b81d1daa0d4..e63b20fa7ce 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -32,6 +32,7 @@ --optional --preparse --python +--python3 -q --R --root @@ -489,6 +490,14 @@ def test_executable(args, input="", timeout=100.0, **kwds): sage: ret 0 + sage: (out, err, ret) = test_executable(["sage", "--python3"], "print(3^33)\n") + sage: out + '34\n' + sage: err + '' + sage: ret + 0 + sage: (out, err, ret) = test_executable(["sage", "--cython"]) sage: print(err) Cython (http://cython.org) is a compiler for code written in the diff --git a/src/sage/tests/py3_syntax.py b/src/sage/tests/py3_syntax.py index e3dd733e0fa..e564860b485 100644 --- a/src/sage/tests/py3_syntax.py +++ b/src/sage/tests/py3_syntax.py @@ -77,7 +77,7 @@ def __iter__(self): sage: from sage.tests.py3_syntax import Python3SyntaxTest sage: test = Python3SyntaxTest('sage/tests/french_book') sage: next(iter(test)) - ('src/sage/tests/french_book', 'README', '') + ('...src/sage/tests/french_book', 'README', '') """ tree_walk = itertools.chain(*map(os.walk, self._directories)) for path, _, files in tree_walk: diff --git a/src/sage/typeset/character_art_factory.py b/src/sage/typeset/character_art_factory.py index e13ab8d0541..9a1c3d53fa2 100644 --- a/src/sage/typeset/character_art_factory.py +++ b/src/sage/typeset/character_art_factory.py @@ -16,10 +16,11 @@ # # http://www.gnu.org/licenses/ #******************************************************************************* -from six import iteritems +from six import iteritems, string_types from sage.structure.sage_object import SageObject + class CharacterArtFactory(SageObject): def __init__(self, @@ -60,7 +61,7 @@ def __init__(self, """ self.art_type = art_type - assert string_type in [str, unicode] + assert isinstance(string_type('a'), string_types) self.string_type = string_type assert magic_method_name in ['_ascii_art_', '_unicode_art_'] self.magic_method_name = magic_method_name diff --git a/src/sage/version.py b/src/sage/version.py index bc64af91cba..58f993d5ce4 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 = '8.0.beta5' -date = '2017-05-04' +version = '8.0.beta8' +date = '2017-05-24' diff --git a/src/sage_setup/docbuild/__init__.py b/src/sage_setup/docbuild/__init__.py index daa7dc6a556..43156631ed3 100644 --- a/src/sage_setup/docbuild/__init__.py +++ b/src/sage_setup/docbuild/__init__.py @@ -654,7 +654,7 @@ def get_all_documents(self, refdir): class ReferenceSubBuilder(DocBuilder): """ This class builds sub-components of the reference manual. It is - resposible for making sure the auto generated ReST files for the + responsible for making sure the auto generated ReST files for the Sage library are up to date. When building any output, we must first go through and check @@ -1208,7 +1208,8 @@ def get_builder(name): print("of documents, or 'sage --docbuild --help' for more help.") sys.exit(1) -def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): + +def format_columns(lst, align=u'<', cols=None, indent=4, pad=3, width=80): """ Utility function that formats a list as a simple table and returns a Unicode string representation. The number of columns is @@ -1223,13 +1224,14 @@ def format_columns(lst, align='<', cols=None, indent=4, pad=3, width=80): if cols is None: import math cols = math.trunc((width - indent) / size) - s = " " * indent + s = u" " * indent for i in range(len(lst)): if i != 0 and i % cols == 0: - s += "\n" + " " * indent - s += "{0:{1}{2}}".format(lst[i], align, size) - s += "\n" - return unicode(s) + s += u"\n" + u" " * indent + s += u"{0:{1}{2}}".format(lst[i], align, size) + s += u"\n" + return s + def help_usage(s=u"", compact=False): """ diff --git a/src/setup.py b/src/setup.py index c9e3fd7ea04..2215379c955 100755 --- a/src/setup.py +++ b/src/setup.py @@ -417,6 +417,7 @@ def run(self): force=self.force, aliases=aliases, compiler_directives=self.cython_directives, + compile_time_env={'PY_VERSION_HEX':sys.hexversion}, create_extension=sage_create_extension, # Debugging gdb_debug=self.debug,